mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
05ad9803d8
This commit adds a new Nexus test that implements the test specification in test-1-4-PIC-TC-1.md. The test verifies Border Router functionality including: - DHCPv6-PD client to obtain OMR prefix - Advertising route to OMR prefix on AIL (Stub Router) - DNS recursive resolver for public internet addresses - Connectivity (ICMPv6, UDP, TCP/HTTP) to internet and local servers New files: - tests/nexus/test_1_4_PIC_TC_1.cpp: C++ test execution - tests/nexus/verify_1_4_PIC_TC_1.py: Python pcap verification Nexus platform enhancements: - Enabled DHCPv6-PD client in openthread-core-nexus-config.h - Implemented DHCPv6-PD platform APIs in nexus_infra_if.cpp - Added RDNSS option to RA in nexus_infra_if.cpp - Improved packet delivery on infrastructure interface in nexus_core.cpp - Fixed upstream DNS query matching in nexus_dns.cpp
215 lines
7.2 KiB
C++
215 lines
7.2 KiB
C++
/*
|
|
* Copyright (c) 2026, The OpenThread Authors.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* This file implements the platform DNS APIs for Nexus.
|
|
*/
|
|
|
|
#include "nexus_dns.hpp"
|
|
|
|
#include <openthread/platform/dns.h>
|
|
|
|
#include "nexus_core.hpp"
|
|
#include "nexus_node.hpp"
|
|
#include "border_router/rx_ra_tracker.hpp"
|
|
#include "common/as_core_type.hpp"
|
|
#include "common/code_utils.hpp"
|
|
#include "common/debug.hpp"
|
|
#include "net/dns_types.hpp"
|
|
|
|
namespace ot {
|
|
namespace Nexus {
|
|
|
|
bool UpstreamDns::IsGua(const Ip6::Prefix &aPrefix) const { return !aPrefix.IsLinkLocal() && !aPrefix.IsUniqueLocal(); }
|
|
|
|
uint16_t UpstreamDns::CountGuaPrefixes(const Ip6::Address &aRouterAddress) const
|
|
{
|
|
const BorderRouter::RxRaTracker &rxRaTracker = Get<BorderRouter::RxRaTracker>();
|
|
BorderRouter::PrefixTableIterator iter;
|
|
BorderRouter::PrefixTableEntry entry;
|
|
uint16_t count = 0;
|
|
|
|
rxRaTracker.InitIterator(iter);
|
|
while (rxRaTracker.GetNextPrefixTableEntry(iter, entry) == kErrorNone)
|
|
{
|
|
if (entry.mValidLifetime > 0 && IsGua(AsCoreType(&entry.mPrefix)) &&
|
|
AsCoreType(&entry.mRouter.mAddress) == aRouterAddress)
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
Error UpstreamDns::SelectUpstreamDnsServer(Ip6::Address &aSelectedAddress) const
|
|
{
|
|
const BorderRouter::RxRaTracker &rxRaTracker = Get<BorderRouter::RxRaTracker>();
|
|
BorderRouter::PrefixTableIterator iter;
|
|
BorderRouter::RdnssAddrEntry entry;
|
|
bool found = false;
|
|
bool bestHasGua = false;
|
|
|
|
rxRaTracker.InitIterator(iter);
|
|
while (rxRaTracker.GetNextRdnssAddrEntry(iter, entry) == kErrorNone)
|
|
{
|
|
if (!entry.mRouter.mIsReachable)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool hasGua = (CountGuaPrefixes(AsCoreType(&entry.mRouter.mAddress)) > 0);
|
|
|
|
if (!found || (hasGua && !bestHasGua))
|
|
{
|
|
aSelectedAddress = AsCoreType(&entry.mAddress);
|
|
bestHasGua = hasGua;
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
return found ? kErrorNone : kErrorNotFound;
|
|
}
|
|
|
|
UpstreamDns::UpstreamDns(Instance &aInstance)
|
|
: InstanceLocator(aInstance)
|
|
{
|
|
}
|
|
|
|
void UpstreamDns::StartUpstreamQuery(UpstreamQueryTransaction &aTxn, const Message &aQuery)
|
|
{
|
|
Error error = kErrorNone;
|
|
Node &node = AsNode(&GetInstance());
|
|
Ip6::Address serverAddress;
|
|
ot::Dns::Header dnsHeader;
|
|
PendingQuery *pendingQuery;
|
|
Message *message = nullptr;
|
|
|
|
// We use kDnsPort (53) as both source and destination port for upstream queries to simplify interception
|
|
// and response matching in this simulation environment.
|
|
|
|
SuccessOrExit(error = SelectUpstreamDnsServer(serverAddress));
|
|
|
|
SuccessOrExit(error = aQuery.Read(0, dnsHeader));
|
|
|
|
message = aQuery.Clone<kSameReservedHeader>();
|
|
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
|
|
|
|
pendingQuery = PendingQuery::Allocate();
|
|
VerifyOrExit(pendingQuery != nullptr, error = kErrorNoBufs);
|
|
|
|
pendingQuery->mTxn = &aTxn;
|
|
pendingQuery->mMessageId = dnsHeader.GetMessageId();
|
|
pendingQuery->mServerAddress = serverAddress;
|
|
mPendingQueries.Push(*pendingQuery);
|
|
|
|
node.mInfraIf.SendUdp(node.mInfraIf.SelectSourceAddress(serverAddress), serverAddress, kDnsPort, kDnsPort,
|
|
*message);
|
|
message = nullptr;
|
|
|
|
exit:
|
|
if (error != kErrorNone)
|
|
{
|
|
if (message != nullptr)
|
|
{
|
|
message->Free();
|
|
}
|
|
otPlatDnsUpstreamQueryDone(&GetInstance(), &aTxn, nullptr);
|
|
}
|
|
}
|
|
|
|
void UpstreamDns::CancelUpstreamQuery(UpstreamQueryTransaction &aTxn)
|
|
{
|
|
mPendingQueries.RemoveAndFreeAllMatching(&aTxn);
|
|
}
|
|
|
|
void UpstreamDns::Reset(void) { mPendingQueries.Clear(); }
|
|
|
|
bool UpstreamDns::IsUpstreamQueryAvailable(void) const
|
|
{
|
|
Ip6::Address address;
|
|
return SelectUpstreamDnsServer(address) == kErrorNone;
|
|
}
|
|
|
|
bool UpstreamDns::HandleUpstreamDnsResponse(const Ip6::Address &aSrcAddress, Message &aMessage)
|
|
{
|
|
Instance &instance = GetInstance();
|
|
Node &node = AsNode(&instance);
|
|
ot::Dns::Header dnsHeader;
|
|
OwnedPtr<PendingQuery> query;
|
|
bool handled = false;
|
|
|
|
SuccessOrExit(aMessage.Read(aMessage.GetOffset(), dnsHeader));
|
|
|
|
query = mPendingQueries.RemoveMatching(dnsHeader.GetMessageId(), aSrcAddress);
|
|
|
|
if (query != nullptr)
|
|
{
|
|
// Found matching query
|
|
// We use kTypeOther as it is appropriate for a payload-only message.
|
|
Message *response = node.Get<MessagePool>().Allocate(Message::kTypeOther);
|
|
|
|
if (response != nullptr)
|
|
{
|
|
if (response->AppendBytesFromMessage(aMessage, aMessage.GetOffset(),
|
|
aMessage.GetLength() - aMessage.GetOffset()) != kErrorNone)
|
|
{
|
|
response->Free();
|
|
response = nullptr;
|
|
}
|
|
}
|
|
|
|
otPlatDnsUpstreamQueryDone(&instance, query->mTxn, response);
|
|
handled = true;
|
|
}
|
|
|
|
exit:
|
|
return handled;
|
|
}
|
|
|
|
extern "C" bool otPlatDnsIsUpstreamQueryAvailable(otInstance *aInstance)
|
|
{
|
|
return AsNode(aInstance).mUpstreamDns.IsUpstreamQueryAvailable();
|
|
}
|
|
|
|
extern "C" void otPlatDnsStartUpstreamQuery(otInstance *aInstance,
|
|
otPlatDnsUpstreamQuery *aTxn,
|
|
const otMessage *aQuery)
|
|
{
|
|
AsNode(aInstance).mUpstreamDns.StartUpstreamQuery(AsCoreType(aTxn), AsCoreType(aQuery));
|
|
}
|
|
|
|
extern "C" void otPlatDnsCancelUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn)
|
|
{
|
|
AsNode(aInstance).mUpstreamDns.CancelUpstreamQuery(AsCoreType(aTxn));
|
|
}
|
|
|
|
} // namespace Nexus
|
|
} // namespace ot
|