mirror of
https://github.com/espressif/openthread.git
synced 2026-08-10 04:37:47 +00:00
[nexus] implement test 1_4_DNS_TC_3 for upstream DNS resolver selection (#12835)
This commit implements the Nexus test specification 1_4_DNS_TC_3 for upstream DNS resolver selection in OpenThread. Nexus Platform Enhancements: - Added OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE and OPENTHREAD_CONFIG_PLATFORM_DNS_ENABLE to nexus config. - Implemented platform DNS APIs in nexus_dns.cpp, supporting upstream server selection based on prefix lifetimes and reachability. - Added UdpHook to Core to allow tests to intercept and simulate responses for backbone UDP traffic on port 53. - Updated InfraIf::Receive to call Core::HandleUdp for generic UDP interception. - Added raw buffer delivery overloads for InfraIf::SendUdp. Test Implementation: - Created test_1_4_DNS_TC_3.cpp which performs network formation, RA signaling (PIO/RIO/RDNSS), and DNS resolution triggers. - Created verify_1_4_DNS_TC_3.py to validate network behavior, RA contents, and correct upstream query routing using pktverify. - Integrated the new test into CMakeLists.txt and the default run_nexus_tests.sh suite.
This commit is contained in:
@@ -44,6 +44,7 @@ set(COMMON_COMPILE_OPTIONS
|
||||
add_library(ot-nexus-platform
|
||||
platform/nexus_alarm.cpp
|
||||
platform/nexus_core.cpp
|
||||
platform/nexus_dns.cpp
|
||||
platform/nexus_infra_if.cpp
|
||||
platform/nexus_logging.cpp
|
||||
platform/nexus_mdns.cpp
|
||||
@@ -293,6 +294,7 @@ ot_nexus_test(1_4_TREL_TC_4 "cert;nexus")
|
||||
ot_nexus_test(1_4_TREL_TC_5 "cert;nexus")
|
||||
ot_nexus_test(1_4_TREL_TC_6 "cert;nexus")
|
||||
ot_nexus_test(1_4_DNS_TC_1 "cert;nexus")
|
||||
ot_nexus_test(1_4_DNS_TC_3 "cert;nexus")
|
||||
ot_nexus_test(1_4_CS_TC_3 "cert;nexus")
|
||||
|
||||
# Misc tests
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_ENABLE 0
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE 1
|
||||
@@ -70,6 +71,7 @@
|
||||
#define OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_DNS_CLIENT_BIND_UDP_TO_THREAD_NETIF 1
|
||||
#define OPENTHREAD_CONFIG_DNS_DSO_ENABLE 0
|
||||
#define OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_ECDSA_ENABLE 1
|
||||
@@ -120,6 +122,7 @@
|
||||
#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION "OT-simul-nexus"
|
||||
#define OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 256
|
||||
#define OPENTHREAD_CONFIG_PLATFORM_DNS_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION 0
|
||||
#define OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE 0
|
||||
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
@@ -157,6 +160,4 @@
|
||||
#define OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL OT_LOG_LEVEL_INFO
|
||||
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE 1
|
||||
|
||||
#endif // OT_NEXUS_OPENTHREAD_CORE_NEXUS_CONFIG_H_
|
||||
|
||||
@@ -189,6 +189,29 @@ void Core::SaveTestInfo(const char *aFilename, Node *aLeaderNode)
|
||||
}
|
||||
fprintf(file, " },\n");
|
||||
|
||||
fprintf(file, " \"ipaddrs\": {\n");
|
||||
for (Node &node : mNodes)
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
fprintf(file, " \"%u\": [\n", node.GetInstance().GetId());
|
||||
|
||||
for (const Ip6::Netif::UnicastAddress &addr : node.Get<Ip6::Netif>().GetUnicastAddresses())
|
||||
{
|
||||
fprintf(file, "%s \"%s\"", first ? "" : ",\n", addr.GetAddress().ToString().AsCString());
|
||||
first = false;
|
||||
}
|
||||
|
||||
for (const Ip6::Address &infraAddr : node.mInfraIf.GetAddresses())
|
||||
{
|
||||
fprintf(file, "%s \"%s\"", first ? "" : ",\n", infraAddr.ToString().AsCString());
|
||||
first = false;
|
||||
}
|
||||
|
||||
fprintf(file, "\n ]%s\n", (&node == tail) ? "" : ",");
|
||||
}
|
||||
fprintf(file, " },\n");
|
||||
|
||||
fprintf(file, " \"ethaddrs\": {\n");
|
||||
for (Node &node : mNodes)
|
||||
{
|
||||
@@ -244,25 +267,6 @@ void Core::SaveTestInfo(const char *aFilename, Node *aLeaderNode)
|
||||
}
|
||||
fprintf(file, " },\n");
|
||||
|
||||
fprintf(file, " \"ipaddrs\": {\n");
|
||||
for (Node &node : mNodes)
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
fprintf(file, " \"%u\": [\n", node.GetInstance().GetId());
|
||||
for (const Ip6::Netif::UnicastAddress &addr : node.Get<ThreadNetif>().GetUnicastAddresses())
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
fprintf(file, ",\n");
|
||||
}
|
||||
fprintf(file, " \"%s\"", addr.GetAddress().ToString().AsCString());
|
||||
first = false;
|
||||
}
|
||||
fprintf(file, "\n ]%s\n", (&node == tail) ? "" : ",");
|
||||
}
|
||||
fprintf(file, " },\n");
|
||||
|
||||
fprintf(file, " \"extra_vars\": {\n");
|
||||
if (leaderNode != nullptr)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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 = node.Get<Ip6::Udp>().CloneMessage(aQuery);
|
||||
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
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef OT_NEXUS_PLATFORM_NEXUS_DNS_HPP_
|
||||
#define OT_NEXUS_PLATFORM_NEXUS_DNS_HPP_
|
||||
|
||||
#include <openthread/platform/dns.h>
|
||||
#include "common/heap_allocatable.hpp"
|
||||
#include "common/linked_list.hpp"
|
||||
#include "common/locator.hpp"
|
||||
#include "common/owning_list.hpp"
|
||||
#include "instance/instance.hpp"
|
||||
#include "net/dnssd_server.hpp"
|
||||
#include "net/ip6_address.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Nexus {
|
||||
|
||||
class Node;
|
||||
|
||||
class UpstreamDns : public InstanceLocator
|
||||
{
|
||||
public:
|
||||
static constexpr uint16_t kDnsPort = 53;
|
||||
|
||||
typedef Dns::ServiceDiscovery::Server::UpstreamQueryTransaction UpstreamQueryTransaction;
|
||||
|
||||
struct PendingQuery : public Heap::Allocatable<PendingQuery>, public LinkedListEntry<PendingQuery>
|
||||
{
|
||||
bool Matches(const UpstreamQueryTransaction *aTxn) const { return mTxn == aTxn; }
|
||||
bool Matches(uint16_t aMessageId, const Ip6::Address &aSrcAddr) const
|
||||
{
|
||||
return (mMessageId == aMessageId) && (mServerAddress == aSrcAddr);
|
||||
}
|
||||
|
||||
UpstreamQueryTransaction *mTxn;
|
||||
uint16_t mMessageId;
|
||||
Ip6::Address mServerAddress;
|
||||
PendingQuery *mNext;
|
||||
};
|
||||
|
||||
explicit UpstreamDns(Instance &aInstance);
|
||||
~UpstreamDns(void) { Reset(); }
|
||||
|
||||
void StartUpstreamQuery(UpstreamQueryTransaction &aTxn, const Message &aQuery);
|
||||
void CancelUpstreamQuery(UpstreamQueryTransaction &aTxn);
|
||||
bool HandleUpstreamDnsResponse(const Ip6::Address &aSrcAddress, Message &aMessage);
|
||||
bool IsUpstreamQueryAvailable(void) const;
|
||||
void Reset(void);
|
||||
|
||||
private:
|
||||
bool IsGua(const Ip6::Prefix &aPrefix) const;
|
||||
uint16_t CountGuaPrefixes(const Ip6::Address &aRouterAddress) const;
|
||||
Error SelectUpstreamDnsServer(Ip6::Address &aSelectedAddress) const;
|
||||
|
||||
OwningList<PendingQuery> mPendingQueries;
|
||||
};
|
||||
|
||||
} // namespace Nexus
|
||||
} // namespace ot
|
||||
|
||||
#endif // OT_NEXUS_PLATFORM_NEXUS_DNS_HPP_
|
||||
@@ -38,6 +38,7 @@ InfraIf::InfraIf(Instance &aInstance)
|
||||
: mNode(nullptr)
|
||||
, mNodeId(0)
|
||||
, mIfIndex(0)
|
||||
, mUdpHook(nullptr)
|
||||
, mHasRioPrefix(false)
|
||||
, mRaTimer(aInstance)
|
||||
{
|
||||
@@ -122,6 +123,34 @@ const Ip6::Address &InfraIf::FindMatchingAddress(const char *aPrefix) const
|
||||
return *matchedAddress;
|
||||
}
|
||||
|
||||
const Ip6::Address &InfraIf::SelectSourceAddress(const Ip6::Address &aDestination) const
|
||||
{
|
||||
const Ip6::Address *bestMatch = &GetLinkLocalAddress();
|
||||
|
||||
if (aDestination.IsLinkLocalUnicast())
|
||||
{
|
||||
// For link-local destinations, we always use the link-local address.
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
for (const Ip6::Address &address : mAddresses)
|
||||
{
|
||||
if (address.IsMulticast())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (address.GetScope() == aDestination.GetScope())
|
||||
{
|
||||
bestMatch = &address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return *bestMatch;
|
||||
}
|
||||
|
||||
void InfraIf::SendIcmp6Nd(const Ip6::Address &aDestAddress, const uint8_t *aBuffer, uint16_t aBufferLength)
|
||||
{
|
||||
Message *message = GetNode().Get<MessagePool>().Allocate(Message::kTypeIp6);
|
||||
@@ -340,20 +369,6 @@ void InfraIf::SendEchoRequest(const Ip6::Address &aSrcAddress,
|
||||
mPendingTxQueue.Enqueue(*message);
|
||||
}
|
||||
|
||||
void InfraIf::SendUdp(const Ip6::Address &aSrcAddress,
|
||||
const Ip6::Address &aDestAddress,
|
||||
uint16_t aSourcePort,
|
||||
uint16_t aDestPort,
|
||||
uint16_t aPayloadSize)
|
||||
{
|
||||
Message *message = GetNode().Get<Ip6::Ip6>().NewMessage();
|
||||
|
||||
VerifyOrQuit(message != nullptr);
|
||||
|
||||
SuccessOrQuit(message->IncreaseLength(aPayloadSize));
|
||||
SendUdp(aSrcAddress, aDestAddress, aSourcePort, aDestPort, *message);
|
||||
}
|
||||
|
||||
void InfraIf::SendUdp(const Ip6::Address &aSrcAddress,
|
||||
const Ip6::Address &aDestAddress,
|
||||
uint16_t aSourcePort,
|
||||
@@ -418,9 +433,8 @@ void InfraIf::Receive(Message &aMessage)
|
||||
|
||||
SuccessOrQuit(payload.SetFrom(aMessage, offset, aMessage.GetLength() - offset));
|
||||
|
||||
otPlatInfraIfRecvIcmp6Nd(&node.GetInstance(), mIfIndex,
|
||||
reinterpret_cast<const otIp6Address *>(&headers.GetSourceAddress()),
|
||||
payload.GetBytes(), payload.GetLength());
|
||||
otPlatInfraIfRecvIcmp6Nd(&node.GetInstance(), mIfIndex, &headers.GetSourceAddress(), payload.GetBytes(),
|
||||
payload.GetLength());
|
||||
node.mInfraIf.ProcessIcmp6Nd(headers.GetSourceAddress(), payload.GetBytes(), payload.GetLength());
|
||||
ExitNow();
|
||||
}
|
||||
@@ -479,11 +493,39 @@ void InfraIf::Receive(Message &aMessage)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
|
||||
if (headers.IsUdp() && headers.GetDestinationPort() == UpstreamDns::kDnsPort)
|
||||
{
|
||||
aMessage.SetOffset(sizeof(Ip6::Header) + sizeof(Ip6::Udp::Header));
|
||||
if (node.mUpstreamDns.HandleUpstreamDnsResponse(headers.GetSourceAddress(), aMessage))
|
||||
{
|
||||
ExitNow();
|
||||
}
|
||||
aMessage.SetOffset(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (headers.IsUdp() && node.mUdp.HandleReceive(aMessage, headers))
|
||||
{
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
if (headers.IsUdp() && mUdpHook != nullptr)
|
||||
{
|
||||
Ip6::MessageInfo messageInfo;
|
||||
messageInfo.SetPeerAddr(headers.GetSourceAddress());
|
||||
messageInfo.SetPeerPort(headers.GetSourcePort());
|
||||
messageInfo.SetSockAddr(headers.GetDestinationAddress());
|
||||
messageInfo.SetSockPort(headers.GetDestinationPort());
|
||||
|
||||
aMessage.SetOffset(sizeof(Ip6::Header) + sizeof(Ip6::Udp::Header));
|
||||
if (mUdpHook(node.GetInstance(), aMessage, messageInfo))
|
||||
{
|
||||
ExitNow();
|
||||
}
|
||||
aMessage.SetOffset(0);
|
||||
}
|
||||
|
||||
{
|
||||
// We also deliver generic IPv6 packets to the stack if they are NOT ICMPv6 ND packets.
|
||||
// (ND packets were already delivered via otPlatInfraIfRecvIcmp6Nd above).
|
||||
|
||||
@@ -58,6 +58,8 @@ public:
|
||||
const Ip6::Address &GetLinkLocalAddress(void) const { return mAddresses[0]; }
|
||||
const Heap::Array<Ip6::Address> &GetAddresses(void) const { return mAddresses; }
|
||||
|
||||
const Ip6::Address &SelectSourceAddress(const Ip6::Address &aDestination) const;
|
||||
|
||||
void SendIcmp6Nd(const Ip6::Address &aDestAddress, const uint8_t *aBuffer, uint16_t aBufferLength);
|
||||
void SendRouterAdvertisement(const Ip6::Address &aDestination,
|
||||
const Ip6::Prefix *aPioPrefix,
|
||||
@@ -70,11 +72,6 @@ public:
|
||||
uint16_t aIdentifier,
|
||||
uint16_t aPayloadSize,
|
||||
uint8_t aHopLimit = Ip6::kDefaultHopLimit);
|
||||
void SendUdp(const Ip6::Address &aSrcAddress,
|
||||
const Ip6::Address &aDestAddress,
|
||||
uint16_t aSourcePort,
|
||||
uint16_t aDestPort,
|
||||
uint16_t aPayloadSize);
|
||||
void SendUdp(const Ip6::Address &aSrcAddress,
|
||||
const Ip6::Address &aDestAddress,
|
||||
uint16_t aSourcePort,
|
||||
@@ -88,6 +85,9 @@ public:
|
||||
|
||||
void SetEchoReplyHandler(EchoReplyHandler aHandler, void *aContext) { mEchoReplyCallback.Set(aHandler, aContext); }
|
||||
|
||||
typedef bool (*UdpHook)(Instance &aInstance, Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
||||
void SetUdpHook(UdpHook aHook) { mUdpHook = aHook; }
|
||||
|
||||
Node &GetNode(void);
|
||||
const Node &GetNode(void) const;
|
||||
|
||||
@@ -108,6 +108,7 @@ private:
|
||||
uint32_t mIfIndex;
|
||||
Heap::Array<Ip6::Address> mAddresses;
|
||||
Callback<EchoReplyHandler> mEchoReplyCallback;
|
||||
UdpHook mUdpHook;
|
||||
|
||||
Ip6::Prefix mPioPrefix;
|
||||
Ip6::Prefix mRioPrefix;
|
||||
|
||||
@@ -40,6 +40,7 @@ void Node::Reset(void)
|
||||
mAlarmMilli.Reset();
|
||||
mAlarmMicro.Reset();
|
||||
mMdns.Reset();
|
||||
mUpstreamDns.Reset();
|
||||
mInfraIf.mPendingTxQueue.DequeueAndFreeAll();
|
||||
mPendingTasklet = false;
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include "nexus_alarm.hpp"
|
||||
#include "nexus_core.hpp"
|
||||
#include "nexus_dns.hpp"
|
||||
#include "nexus_infra_if.hpp"
|
||||
#include "nexus_logging.hpp"
|
||||
#include "nexus_mdns.hpp"
|
||||
@@ -48,14 +49,15 @@ namespace Nexus {
|
||||
class Platform
|
||||
{
|
||||
public:
|
||||
Radio mRadio;
|
||||
Alarm mAlarmMilli;
|
||||
Alarm mAlarmMicro;
|
||||
Logging mLogging;
|
||||
Mdns mMdns;
|
||||
InfraIf mInfraIf;
|
||||
Udp mUdp;
|
||||
Settings mSettings;
|
||||
Radio mRadio;
|
||||
Alarm mAlarmMilli;
|
||||
Alarm mAlarmMicro;
|
||||
Logging mLogging;
|
||||
Mdns mMdns;
|
||||
UpstreamDns mUpstreamDns;
|
||||
InfraIf mInfraIf;
|
||||
Udp mUdp;
|
||||
Settings mSettings;
|
||||
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
|
||||
Trel mTrel;
|
||||
#endif
|
||||
@@ -63,7 +65,8 @@ public:
|
||||
|
||||
protected:
|
||||
explicit Platform(Instance &aInstance)
|
||||
: mInfraIf(aInstance)
|
||||
: mUpstreamDns(aInstance)
|
||||
, mInfraIf(aInstance)
|
||||
, mUdp(aInstance)
|
||||
, mPendingTasklet(false)
|
||||
{
|
||||
@@ -155,6 +158,7 @@ public:
|
||||
using Platform::mRadio;
|
||||
using Platform::mSettings;
|
||||
using Platform::mUdp;
|
||||
using Platform::mUpstreamDns;
|
||||
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
|
||||
using Platform::mTrel;
|
||||
#endif
|
||||
|
||||
@@ -227,6 +227,7 @@ DEFAULT_TESTS=(
|
||||
"1_4_TREL_TC_5"
|
||||
"1_4_TREL_TC_6"
|
||||
"1_4_DNS_TC_1"
|
||||
"1_4_DNS_TC_3"
|
||||
"1_4_CS_TC_3"
|
||||
)
|
||||
|
||||
|
||||
@@ -262,7 +262,13 @@ void TestMatnTc5(void)
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 5: Host sends a UDP packet to the multicast address, MA1, port 5683.");
|
||||
host.mInfraIf.SendUdp(*hostUla, ma1, kCoapPort, kCoapPort, kUdpPayloadSize);
|
||||
{
|
||||
Message *message = host.Get<Ip6::Ip6>().NewMessage();
|
||||
|
||||
VerifyOrQuit(message != nullptr);
|
||||
SuccessOrQuit(message->SetLength(kUdpPayloadSize));
|
||||
host.mInfraIf.SendUdp(*hostUla, ma1, kCoapPort, kCoapPort, *message);
|
||||
}
|
||||
nexus.AdvanceTime(0);
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "nexus_core.hpp"
|
||||
#include "nexus_node.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Nexus {
|
||||
|
||||
namespace {
|
||||
|
||||
static constexpr char kGua1Prefix[] = "2005:1234:abcd:100::/64";
|
||||
|
||||
static constexpr char kEth1Addr[] = "2005:1234:abcd:100::E1";
|
||||
static constexpr char kEth2Addr[] = "2005:1234:abcd:100::E2";
|
||||
|
||||
static constexpr char kThreadGroup1[] = "threadgroup1.org";
|
||||
static constexpr char kThreadGroup2[] = "threadgroup2.org";
|
||||
static constexpr char kThreadGroup3[] = "threadgroup3.org";
|
||||
|
||||
static constexpr char kEth1Answer1[] = "2002:1234::E1:1";
|
||||
static constexpr char kEth1Answer2[] = "2002:1234::E1:2";
|
||||
static constexpr char kEth1Answer3[] = "2002:1234::E1:3";
|
||||
|
||||
static constexpr char kEth2Answer1[] = "2002:1234::E2:1";
|
||||
static constexpr char kEth2Answer2[] = "2002:1234::E2:2";
|
||||
static constexpr char kEth2Answer3[] = "2002:1234::E2:3";
|
||||
|
||||
struct Record
|
||||
{
|
||||
const char *mName;
|
||||
const char *mAddress;
|
||||
};
|
||||
|
||||
const Record kEth1Records[] = {
|
||||
{kThreadGroup1, kEth1Answer1},
|
||||
{kThreadGroup2, kEth1Answer2},
|
||||
{kThreadGroup3, kEth1Answer3},
|
||||
};
|
||||
|
||||
const Record kEth2Records[] = {
|
||||
{kThreadGroup1, kEth2Answer1},
|
||||
{kThreadGroup2, kEth2Answer2},
|
||||
{kThreadGroup3, kEth2Answer3},
|
||||
};
|
||||
|
||||
static Node *sEth1Node = nullptr;
|
||||
static Node *sEth2Node = nullptr;
|
||||
|
||||
bool HandleUdpHook(Instance &aInstance, Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||
{
|
||||
Node &node = AsNode(&aInstance);
|
||||
const Record *records = nullptr;
|
||||
uint16_t count = 0;
|
||||
Dns::Header dnsHeader;
|
||||
Dns::Question question;
|
||||
char name[Dns::Name::kMaxNameSize];
|
||||
uint16_t readPos;
|
||||
Message *response;
|
||||
bool handled = false;
|
||||
|
||||
if (&node == sEth1Node)
|
||||
{
|
||||
records = kEth1Records;
|
||||
count = GetArrayLength(kEth1Records);
|
||||
}
|
||||
else if (&node == sEth2Node)
|
||||
{
|
||||
records = kEth2Records;
|
||||
count = GetArrayLength(kEth2Records);
|
||||
}
|
||||
|
||||
VerifyOrExit(records != nullptr);
|
||||
VerifyOrExit(aMessageInfo.GetSockPort() == UpstreamDns::kDnsPort);
|
||||
|
||||
SuccessOrExit(aMessage.Read(aMessage.GetOffset(), dnsHeader));
|
||||
VerifyOrExit(dnsHeader.GetType() == Dns::Header::kTypeQuery);
|
||||
VerifyOrExit(dnsHeader.GetQuestionCount() == 1);
|
||||
|
||||
readPos = aMessage.GetOffset() + sizeof(dnsHeader);
|
||||
SuccessOrExit(Dns::Name::ReadName(aMessage, readPos, name));
|
||||
SuccessOrExit(aMessage.Read(readPos, question));
|
||||
|
||||
Log("Node %u (%s) received DNS query for %s", node.GetInstance().GetId(), node.GetName(), name);
|
||||
|
||||
response = node.Get<MessagePool>().Allocate(Message::kTypeIp6);
|
||||
VerifyOrExit(response != nullptr);
|
||||
|
||||
dnsHeader.SetType(Dns::Header::kTypeResponse);
|
||||
dnsHeader.SetAnswerCount(0);
|
||||
dnsHeader.SetRecursionDesiredFlag();
|
||||
dnsHeader.SetRecursionAvailableFlag();
|
||||
|
||||
for (uint16_t i = 0; i < count; i++)
|
||||
{
|
||||
if (StringMatch(name, records[i].mName, kStringCaseInsensitiveMatch))
|
||||
{
|
||||
Ip6::Address address;
|
||||
Dns::ResourceRecord rr;
|
||||
|
||||
SuccessOrQuit(address.FromString(records[i].mAddress));
|
||||
|
||||
dnsHeader.SetAnswerCount(1);
|
||||
SuccessOrQuit(response->Append(dnsHeader));
|
||||
SuccessOrQuit(Dns::Name::AppendName(name, *response));
|
||||
SuccessOrQuit(response->Append(question));
|
||||
|
||||
SuccessOrQuit(Dns::Name::AppendName(name, *response));
|
||||
rr.Init(Dns::ResourceRecord::kTypeAaaa);
|
||||
rr.SetTtl(1800);
|
||||
rr.SetLength(sizeof(Ip6::Address));
|
||||
SuccessOrQuit(response->Append(rr));
|
||||
SuccessOrQuit(response->Append(address));
|
||||
|
||||
Log("Node %u (%s) sending DNS response for %s with address %s", node.GetInstance().GetId(), node.GetName(),
|
||||
name, records[i].mAddress);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dnsHeader.GetAnswerCount() == 0)
|
||||
{
|
||||
dnsHeader.SetResponseCode(Dns::Header::kResponseNameError);
|
||||
SuccessOrQuit(response->Append(dnsHeader));
|
||||
SuccessOrQuit(Dns::Name::AppendName(name, *response));
|
||||
SuccessOrQuit(response->Append(question));
|
||||
Log("Node %u (%s) sending DNS NXDOMAIN for %s", node.GetInstance().GetId(), node.GetName(), name);
|
||||
}
|
||||
|
||||
node.mInfraIf.SendUdp(aMessageInfo.GetSockAddr(), aMessageInfo.GetPeerAddr(), UpstreamDns::kDnsPort,
|
||||
aMessageInfo.GetPeerPort(), *response);
|
||||
handled = true;
|
||||
|
||||
exit:
|
||||
return handled;
|
||||
}
|
||||
|
||||
void HandleDnsResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aError);
|
||||
OT_UNUSED_VARIABLE(aResponse);
|
||||
OT_UNUSED_VARIABLE(aContext);
|
||||
}
|
||||
|
||||
static void SendRa(Node &aNode, const Ip6::Prefix &aPrefix, const Ip6::Address &aDnsAddr)
|
||||
{
|
||||
Ip6::Nd::RouterAdvert::TxMessage ra;
|
||||
Ip6::Nd::RouterAdvert::Header header;
|
||||
Ip6::Nd::Icmp6Packet packet;
|
||||
|
||||
header.SetToDefault();
|
||||
header.SetRouterLifetime(1800);
|
||||
SuccessOrQuit(ra.Append(header));
|
||||
SuccessOrQuit(ra.AppendPrefixInfoOption(
|
||||
aPrefix, 1800, 1800, Ip6::Nd::PrefixInfoOption::kOnLinkFlag | Ip6::Nd::PrefixInfoOption::kAutoConfigFlag));
|
||||
SuccessOrQuit(ra.AppendRecursiveDnsServerOption(&aDnsAddr, 1, 1800));
|
||||
ra.GetAsPacket(packet);
|
||||
aNode.mInfraIf.SendIcmp6Nd(Ip6::Address::GetLinkLocalAllNodesMulticast(), packet.GetBytes(), packet.GetLength());
|
||||
}
|
||||
|
||||
/**
|
||||
* 11.3. [1.4] [CERT] Selection of upstream DNS resolver
|
||||
*
|
||||
* 11.3.1. Purpose
|
||||
* To verify that the BR DUT:
|
||||
* - Selects the upstream DNS resolver based on ND RA RDNSS Option, sent by the infrastructure network, pointing to the
|
||||
* default DNS resolver.
|
||||
* - Reacts to change of the upstream DNS resolver, e.g. due to a configuration change, new ISP, or other reasons.
|
||||
* - Can handle link-local and global IPv6 address pointing to upstream DNS resolver.
|
||||
*
|
||||
* 11.3.2. Topology
|
||||
* - BR_1 (DUT) - Border Router
|
||||
* - Router_1 - Thread Router Reference Device, attached to BR_1
|
||||
* - ED_1 - Thread Reference Device, End Device (e.g. FED/REED) role, attached to Router_1
|
||||
* - Eth_1 - Adjacent Infrastructure Link Linux Reference Device with DNS server
|
||||
* - Eth_2 - Adjacent Infrastructure Link Linux Reference Device with DNS server and RA daemon
|
||||
*/
|
||||
void Test_1_4_DNS_TC_3(const char *aJsonFileName)
|
||||
{
|
||||
Core nexus;
|
||||
Node &br1 = nexus.CreateNode();
|
||||
Node &router1 = nexus.CreateNode();
|
||||
Node &ed1 = nexus.CreateNode();
|
||||
Node ð1 = nexus.CreateNode();
|
||||
Node ð2 = nexus.CreateNode();
|
||||
Ip6::Prefix gua1Prefix;
|
||||
Ip6::Address eth1Addr;
|
||||
Ip6::Address eth2Addr;
|
||||
|
||||
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelNote));
|
||||
|
||||
br1.SetName("BR", 1);
|
||||
router1.SetName("Router", 1);
|
||||
ed1.SetName("ED", 1);
|
||||
eth1.SetName("Eth", 1);
|
||||
eth2.SetName("Eth", 2);
|
||||
|
||||
sEth1Node = ð1;
|
||||
sEth2Node = ð2;
|
||||
|
||||
eth1.mInfraIf.SetUdpHook(HandleUdpHook);
|
||||
eth2.mInfraIf.SetUdpHook(HandleUdpHook);
|
||||
|
||||
br1.AllowList(router1);
|
||||
router1.AllowList(br1);
|
||||
router1.AllowList(ed1);
|
||||
ed1.AllowList(router1);
|
||||
|
||||
SuccessOrQuit(gua1Prefix.FromString(kGua1Prefix));
|
||||
SuccessOrQuit(eth1Addr.FromString(kEth1Addr));
|
||||
SuccessOrQuit(eth2Addr.FromString(kEth2Addr));
|
||||
|
||||
/**
|
||||
* Step 1
|
||||
* - Device: Eth_1, Eth_2
|
||||
* - Description (DNS-11.3): Enable
|
||||
* - Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 1: Enable Eth_1, Eth_2");
|
||||
eth1.Get<ThreadNetif>().Up();
|
||||
eth2.Get<ThreadNetif>().Up();
|
||||
eth1.mInfraIf.AddAddress(eth1Addr);
|
||||
eth2.mInfraIf.AddAddress(eth2Addr);
|
||||
|
||||
/**
|
||||
* Step 2
|
||||
* - Device: Eth_2
|
||||
* - Description (DNS-11.3): Harness instructs device to: Configure router
|
||||
* advertisement daemon (radvd) to multicast ND RA with: Prefix Information
|
||||
* Option (PIO) with a global on-link prefix GUA_1 (SLAAC is enabled (A=1),
|
||||
* on-link (L=1)), Recursive DNS Server Option (25), with Addresses of IPv6
|
||||
* Recursive DNS Servers field contains a single global IPv6 address of Eth_1.
|
||||
* Configure DNS server with records: threadgroup1.org AAAA 2002:1234::E2:1,
|
||||
* threadgroup2.org AAAA 2002:1234::E2:2, threadgroup3.org AAAA 2002:1234::E2:3.
|
||||
* Note: prefix GUA_1 may be 2005:1234:abcd:100::/64.
|
||||
* - Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 2: Eth_2 sends RA with global IPv6 address of Eth_1");
|
||||
SendRa(eth2, gua1Prefix, eth1Addr);
|
||||
|
||||
/**
|
||||
* Step 3
|
||||
* - Device: Eth_1
|
||||
* - Description (DNS-11.3): Harness instructs device to: Configure DNS server
|
||||
* with records: threadgroup1.org AAAA 2002:1234::E1:1, threadgroup2.org AAAA
|
||||
* 2002:1234::E1:2, threadgroup3.org AAAA 2002:1234::E1:3
|
||||
* - Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 3: Configure DNS records on Eth_1");
|
||||
// Handled by HandleUdpHook using kEth1Records.
|
||||
|
||||
/**
|
||||
* Step 4
|
||||
* - Device: BR_1 (DUT), Router_1, ED_1
|
||||
* - Description (DNS-11.3): Enable
|
||||
* - Pass Criteria:
|
||||
* - Single Thread Network forms.
|
||||
*/
|
||||
Log("Step 4: Enable BR_1, Router_1, ED_1");
|
||||
br1.Form();
|
||||
nexus.AdvanceTime(10000);
|
||||
|
||||
router1.Join(br1, Node::kAsFtd);
|
||||
ed1.Join(br1, Node::kAsFed);
|
||||
|
||||
nexus.AdvanceTime(20000);
|
||||
|
||||
VerifyOrQuit(br1.Get<Mle::Mle>().IsAttached());
|
||||
VerifyOrQuit(router1.Get<Mle::Mle>().IsRouter());
|
||||
VerifyOrQuit(ed1.Get<Mle::Mle>().IsChild());
|
||||
|
||||
/**
|
||||
* Step 5
|
||||
* - Device: BR_1 (DUT)
|
||||
* - Description (DNS-11.3): Automatically obtains / configures an OMR prefix for
|
||||
* the Thread Network, and assigns an address for its AIL interface using SLAAC.
|
||||
* - Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 5: BR_1 configures OMR prefix and AIL address");
|
||||
br1.Get<BorderRouter::InfraIf>().Init(1, true);
|
||||
br1.Get<BorderRouter::RoutingManager>().Init();
|
||||
SuccessOrQuit(br1.Get<BorderRouter::RoutingManager>().SetEnabled(true));
|
||||
SuccessOrQuit(br1.Get<Dns::ServiceDiscovery::Server>().Start());
|
||||
br1.Get<Dns::ServiceDiscovery::Server>().SetUpstreamQueryEnabled(true);
|
||||
|
||||
{
|
||||
Dns::Client::QueryConfig dnsConfig;
|
||||
dnsConfig.Clear();
|
||||
AsCoreType(&dnsConfig.mServerSockAddr.mAddress) = br1.Get<Mle::Mle>().GetMeshLocalEid();
|
||||
dnsConfig.mServerSockAddr.mPort = UpstreamDns::kDnsPort;
|
||||
ed1.Get<Dns::Client>().SetDefaultConfig(dnsConfig);
|
||||
}
|
||||
|
||||
// Resend RA to ensure BR_1 (which was just initialized) receives it.
|
||||
SendRa(eth2, gua1Prefix, eth1Addr);
|
||||
nexus.AdvanceTime(5000);
|
||||
|
||||
/**
|
||||
* Step 6
|
||||
* - Device: ED_1
|
||||
* - Description (DNS-11.3): Harness instructs device to perform DNS query
|
||||
* Qtype=AAAA, name threadgroup1.org. Automatically, the DNS query gets
|
||||
* routed to the DUT
|
||||
* - Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 6: ED_1 performs DNS query for threadgroup1.org");
|
||||
ed1.Get<Dns::Client>().Stop();
|
||||
SuccessOrQuit(ed1.Get<Dns::Client>().Start());
|
||||
SuccessOrQuit(ed1.Get<Dns::Client>().ResolveAddress(kThreadGroup1, HandleDnsResponse, nullptr));
|
||||
|
||||
/**
|
||||
* Step 7
|
||||
* - Device: BR_1 (DUT)
|
||||
* - Description (DNS-11.3): Automatically processes the DNS query by requesting
|
||||
* upstream to the Eth_1 DNS server. Then, it responds back with the answer
|
||||
* to ED_1.
|
||||
* - Pass Criteria:
|
||||
* - N/A
|
||||
*
|
||||
* Step 8
|
||||
* - Device: ED_1
|
||||
* - Description (DNS-11.3): Successfully receives DNS query result:
|
||||
* threadgroup1.org AAAA 2002:1234::E1:1
|
||||
* - Pass Criteria:
|
||||
* - ED_1 MUST receive correct DNS query answer from the DUT.
|
||||
*/
|
||||
Log("Step 7 & 8: Verify BR_1 requests upstream to Eth_1 and ED_1 receives correct answer");
|
||||
nexus.AdvanceTime(5000);
|
||||
|
||||
/**
|
||||
* Step 9
|
||||
* - Device: Eth_2
|
||||
* - Description (DNS-11.3): Harness instructs device to configure router
|
||||
* advertisement daemon (radvd) to multicast ND RA with updated information,
|
||||
* pointing to another DNS server: Prefix Information Option (PIO) - as
|
||||
* before, Recursive DNS Server Option (25), with Addresses of IPv6
|
||||
* Recursive DNS Servers field contains a single global IPv6 address of
|
||||
* Eth_2. Harness waits for a time of at least RA_PERIOD + 1 seconds, where
|
||||
* RA_PERIOD is the max period between multicast ND RA transmissions by
|
||||
* Eth_2, to ensure that the new ND RA is received by DUT.
|
||||
* - Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 9: Eth_2 sends RA with global IPv6 address of Eth_2");
|
||||
SendRa(eth2, gua1Prefix, eth2Addr);
|
||||
nexus.AdvanceTime(10000); // Wait for RA_PERIOD + 1 seconds
|
||||
|
||||
/**
|
||||
* Step 10
|
||||
* - Device: ED_1
|
||||
* - Description (DNS-11.3): Harness instructs device to perform DNS query
|
||||
* Qtype=AAAA, name threadgroup2.org. Automatically, the DNS query gets
|
||||
* routed to the DUT.
|
||||
* - Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 10: ED_1 performs DNS query for threadgroup2.org");
|
||||
SuccessOrQuit(ed1.Get<Dns::Client>().ResolveAddress(kThreadGroup2, HandleDnsResponse, nullptr));
|
||||
|
||||
/**
|
||||
* Step 11
|
||||
* - Device: BR_1 (DUT)
|
||||
* - Description (DNS-11.3): Automatically processes the DNS query by requesting
|
||||
* upstream to the Eth_2 DNS server. Then, it responds back with the answer
|
||||
* to ED_1.
|
||||
* - Pass Criteria:
|
||||
* - N/A
|
||||
*
|
||||
* Step 12
|
||||
* - Device: ED_1
|
||||
* - Description (DNS-11.3): Successfully receives DNS query result:
|
||||
* threadgroup2.org AAAA 2002:1234::E2:2
|
||||
* - Pass Criteria:
|
||||
* - ED_1 MUST receive correct DNS query answer from the DUT.
|
||||
*/
|
||||
Log("Step 11 & 12: Verify BR_1 requests upstream to Eth_2 and ED_1 receives correct answer");
|
||||
nexus.AdvanceTime(5000);
|
||||
|
||||
/**
|
||||
* Step 13
|
||||
* - Device: Eth_2
|
||||
* - Description (DNS-11.3): Harness instructs device to configure router
|
||||
* advertisement daemon (radvd) to multicast ND RA with updated information,
|
||||
* pointing to the first DNS server using link-local address: Prefix
|
||||
* Information Option (PIO) - as before, Recursive DNS Server Option (25),
|
||||
* with Addresses of IPv6 Recursive DNS Servers field contains a single
|
||||
* link-local IPv6 address of Eth_1. Harness waits for a time of at least
|
||||
* RA_PERIOD + 1 seconds, where RA_PERIOD is the max period between
|
||||
* multicast ND RA transmissions by Eth_2, to ensure that the new RA is
|
||||
* received by DUT.
|
||||
* - Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 13: Eth_2 sends RA with link-local IPv6 address of Eth_1");
|
||||
{
|
||||
const Ip6::Address ð1Lla = eth1.mInfraIf.GetLinkLocalAddress();
|
||||
SendRa(eth2, gua1Prefix, eth1Lla);
|
||||
}
|
||||
nexus.AdvanceTime(10000); // Wait for RA_PERIOD + 1 seconds
|
||||
|
||||
/**
|
||||
* Step 14
|
||||
* - Device: ED_1
|
||||
* - Description (DNS-11.3): Harness instructs device to perform DNS query
|
||||
* Qtype=AAAA, name threadgroup3.org. Automatically, the DNS query gets
|
||||
* routed to the DUT
|
||||
* - Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 14: ED_1 performs DNS query for threadgroup3.org");
|
||||
SuccessOrQuit(ed1.Get<Dns::Client>().ResolveAddress(kThreadGroup3, HandleDnsResponse, nullptr));
|
||||
|
||||
/**
|
||||
* Step 15
|
||||
* - Device: BR_1 (DUT)
|
||||
* - Description (DNS-11.3): Automatically processes the DNS query by requesting
|
||||
* upstream to the Eth_1 DNS server. Then, it responds back with the answer
|
||||
* to ED_1.
|
||||
* - Pass Criteria:
|
||||
* - N/A
|
||||
*
|
||||
* Step 16
|
||||
* - Device: ED_1
|
||||
* - Description (DNS-11.3): Successfully receives DNS query result:
|
||||
* threadgroup3.org AAAA 2002:1234::E1:3
|
||||
* - Pass Criteria:
|
||||
* - ED_1 MUST receive correct DNS query answer from the DUT.
|
||||
*/
|
||||
Log("Step 15 & 16: Verify BR_1 requests upstream to Eth_1 and ED_1 receives correct answer");
|
||||
nexus.AdvanceTime(5000);
|
||||
|
||||
nexus.SaveTestInfo(aJsonFileName);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace Nexus
|
||||
} // namespace ot
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
ot::Nexus::Test_1_4_DNS_TC_3((argc > 2) ? argv[2] : "test_1_4_dns_tc_3.json");
|
||||
ot::Nexus::Log("All tests passed");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add the current directory to sys.path to find verify_utils
|
||||
CUR_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.append(CUR_DIR)
|
||||
|
||||
import verify_utils
|
||||
from pktverify import consts
|
||||
from pktverify.addrs import Ipv6Addr
|
||||
|
||||
GUA1_PREFIX = "2005:1234:abcd:100::/64"
|
||||
ETH1_ADDR = "2005:1234:abcd:100::e1"
|
||||
ETH2_ADDR = "2005:1234:abcd:100::e2"
|
||||
|
||||
THREADGROUP1 = "threadgroup1.org"
|
||||
THREADGROUP2 = "threadgroup2.org"
|
||||
THREADGROUP3 = "threadgroup3.org"
|
||||
|
||||
ANSWER1_ETH1 = "2002:1234::e1:1"
|
||||
ANSWER2_ETH2 = "2002:1234::e2:2"
|
||||
ANSWER3_ETH1 = "2002:1234::e1:3"
|
||||
|
||||
# ICMPv6 RA Option Types
|
||||
ND_OPTION_PIO = 3
|
||||
ND_OPTION_RDNSS = 25
|
||||
|
||||
|
||||
def verify(pv):
|
||||
pkts = pv.pkts
|
||||
|
||||
BR_1 = pv.vars['BR_1']
|
||||
Router_1 = pv.vars['Router_1']
|
||||
ED_1 = pv.vars['ED_1']
|
||||
Eth_1 = pv.vars['Eth_1']
|
||||
Eth_2 = pv.vars['Eth_2']
|
||||
|
||||
BR_1_ETH_ADDR = pv.vars['BR_1_ETH']
|
||||
Eth_1_ETH_ADDR = pv.vars['Eth_1_ETH']
|
||||
Eth_2_ETH_ADDR = pv.vars['Eth_2_ETH']
|
||||
|
||||
# Step 1
|
||||
# - Device: Eth_1, Eth_2
|
||||
# - Description (DNS-11.3): Enable
|
||||
# - Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 1: Enable Eth_1, Eth_2")
|
||||
|
||||
# Step 2
|
||||
# - Device: Eth_2
|
||||
# - Description (DNS-11.3): Harness instructs device to: Configure router
|
||||
# advertisement daemon (radvd) to multicast ND RA with: Prefix Information
|
||||
# Option (PIO) with a global on-link prefix GUA_1 (SLAAC is enabled (A=1),
|
||||
# on-link (L=1)), Recursive DNS Server Option (25), with Addresses of IPv6
|
||||
# Recursive DNS Servers field contains a single global IPv6 address of Eth_1.
|
||||
# Configure DNS server with records: threadgroup1.org AAAA 2002:1234::E2:1,
|
||||
# threadgroup2.org AAAA 2002:1234::E2:2, threadgroup3.org AAAA 2002:1234::E2:3.
|
||||
# Note: prefix GUA_1 may be 2005:1234:abcd:100::/64.
|
||||
# - Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 2: Eth_2 sends RA with global IPv6 address of Eth_1")
|
||||
pkts.filter_eth_src(Eth_2_ETH_ADDR).\
|
||||
filter_icmpv6_nd_ra().\
|
||||
filter(lambda p: ND_OPTION_PIO in p.icmpv6.opt.type).\
|
||||
filter(lambda p: ND_OPTION_RDNSS in p.icmpv6.opt.type).\
|
||||
must_next()
|
||||
|
||||
# Step 3
|
||||
# - Device: Eth_1
|
||||
# - Description (DNS-11.3): Harness instructs device to: Configure DNS server
|
||||
# with records: threadgroup1.org AAAA 2002:1234::E1:1, threadgroup2.org AAAA
|
||||
# 2002:1234::E1:2, threadgroup3.org AAAA 2002:1234::E1:3
|
||||
# - Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 3: Configure DNS records on Eth_1")
|
||||
|
||||
# Step 4
|
||||
# - Device: BR_1 (DUT), Router_1, ED_1
|
||||
# - Description (DNS-11.3): Enable
|
||||
# - Pass Criteria:
|
||||
# - Single Thread Network forms.
|
||||
print("Step 4: Enable BR_1, Router_1, ED_1")
|
||||
pkts.filter_wpan_src64(BR_1).\
|
||||
filter_mle_cmd(consts.MLE_ADVERTISEMENT).\
|
||||
must_next()
|
||||
|
||||
pkts.filter_wpan_src64(Router_1).\
|
||||
filter_mle_cmd(consts.MLE_CHILD_ID_REQUEST).\
|
||||
must_next()
|
||||
|
||||
pkts.filter_wpan_src64(ED_1).\
|
||||
filter_mle_cmd(consts.MLE_CHILD_ID_REQUEST).\
|
||||
must_next()
|
||||
|
||||
# Step 5
|
||||
# - Device: BR_1 (DUT)
|
||||
# - Description (DNS-11.3): Automatically obtains / configures an OMR prefix for
|
||||
# the Thread Network, and assigns an address for its AIL interface using SLAAC.
|
||||
# - Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 5: BR_1 configures OMR prefix and AIL address")
|
||||
|
||||
# Step 6
|
||||
# - Device: ED_1
|
||||
# - Description (DNS-11.3): Harness instructs device to perform DNS query
|
||||
# Qtype=AAAA, name threadgroup1.org. Automatically, the DNS query gets
|
||||
# routed to the DUT
|
||||
# - Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 6: ED_1 performs DNS query for threadgroup1.org")
|
||||
pkts.filter(lambda p: 'udp' in p.layer_names and p.udp.dstport == 53).\
|
||||
must_next()
|
||||
|
||||
# Step 7
|
||||
# - Device: BR_1 (DUT)
|
||||
# - Description (DNS-11.3): Automatically processes the DNS query by requesting
|
||||
# upstream to the Eth_1 DNS server. Then, it responds back with the answer
|
||||
# to ED_1.
|
||||
# - Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 7: BR_1 requests upstream to Eth_1")
|
||||
pkts.filter_eth_src(BR_1_ETH_ADDR).\
|
||||
filter(lambda p: p.eth.dst == Eth_1_ETH_ADDR).\
|
||||
filter_ipv6_dst(ETH1_ADDR).\
|
||||
filter(lambda p: 'udp' in p.layer_names and p.udp.dstport == 53).\
|
||||
must_next()
|
||||
|
||||
# Step 8
|
||||
# - Device: ED_1
|
||||
# - Description (DNS-11.3): Successfully receives DNS query result:
|
||||
# threadgroup1.org AAAA 2002:1234::E1:1
|
||||
# - Pass Criteria:
|
||||
# - ED_1 MUST receive correct DNS query answer from the DUT.
|
||||
print("Step 8: ED_1 receives correct answer")
|
||||
pkts.filter_eth_src(Eth_1_ETH_ADDR).\
|
||||
filter(lambda p: p.eth.dst == BR_1_ETH_ADDR).\
|
||||
must_next()
|
||||
|
||||
# Step 9
|
||||
# - Device: Eth_2
|
||||
# - Description (DNS-11.3): Harness instructs device to configure router
|
||||
# advertisement daemon (radvd) to multicast ND RA with updated information,
|
||||
# pointing to another DNS server: Prefix Information Option (PIO) - as
|
||||
# before, Recursive DNS Server Option (25), with Addresses of IPv6
|
||||
# Recursive DNS Servers field contains a single global IPv6 address of
|
||||
# Eth_2. Harness waits for a time of at least RA_PERIOD + 1 seconds, where
|
||||
# RA_PERIOD is the max period between multicast ND RA transmissions by
|
||||
# Eth_2, to ensure that the new ND RA is received by DUT.
|
||||
# - Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 9: Eth_2 sends RA with global IPv6 address of Eth_2")
|
||||
pkts.filter_eth_src(Eth_2_ETH_ADDR).\
|
||||
filter_icmpv6_nd_ra().\
|
||||
filter(lambda p: ND_OPTION_PIO in p.icmpv6.opt.type).\
|
||||
filter(lambda p: ND_OPTION_RDNSS in p.icmpv6.opt.type).\
|
||||
must_next()
|
||||
|
||||
# Step 10
|
||||
# - Device: ED_1
|
||||
# - Description (DNS-11.3): Harness instructs device to perform DNS query
|
||||
# Qtype=AAAA, name threadgroup2.org. Automatically, the DNS query gets
|
||||
# routed to the DUT.
|
||||
# - Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 10: ED_1 performs DNS query for threadgroup2.org")
|
||||
pkts.filter(lambda p: 'udp' in p.layer_names and p.udp.dstport == 53).\
|
||||
must_next()
|
||||
|
||||
# Step 11
|
||||
# - Device: BR_1 (DUT)
|
||||
# - Description (DNS-11.3): Automatically processes the DNS query by requesting
|
||||
# upstream to the Eth_2 DNS server. Then, it responds back with the answer
|
||||
# to ED_1.
|
||||
# - Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 11: BR_1 requests upstream to Eth_2")
|
||||
pkts.filter_eth_src(BR_1_ETH_ADDR).\
|
||||
filter(lambda p: p.eth.dst == Eth_2_ETH_ADDR).\
|
||||
filter_ipv6_dst(ETH2_ADDR).\
|
||||
filter(lambda p: 'udp' in p.layer_names and p.udp.dstport == 53).\
|
||||
must_next()
|
||||
|
||||
# Step 12
|
||||
# - Device: ED_1
|
||||
# - Description (DNS-11.3): Successfully receives DNS query result:
|
||||
# threadgroup2.org AAAA 2002:1234::E2:2
|
||||
# - Pass Criteria:
|
||||
# - ED_1 MUST receive correct DNS query answer from the DUT.
|
||||
print("Step 12: ED_1 receives correct answer")
|
||||
pkts.filter_eth_src(Eth_2_ETH_ADDR).\
|
||||
filter(lambda p: p.eth.dst == BR_1_ETH_ADDR).\
|
||||
must_next()
|
||||
|
||||
# Step 13
|
||||
# - Device: Eth_2
|
||||
# - Description (DNS-11.3): Harness instructs device to configure router
|
||||
# advertisement daemon (radvd) to multicast ND RA with updated information,
|
||||
# pointing to the first DNS server using link-local address: Prefix
|
||||
# Information Option (PIO) - as before, Recursive DNS Server Option (25),
|
||||
# with Addresses of IPv6 Recursive DNS Servers field contains a single
|
||||
# link-local IPv6 address of Eth_1. Harness waits for a time of at least
|
||||
# RA_PERIOD + 1 seconds, where RA_PERIOD is the max period between
|
||||
# multicast ND RA transmissions by Eth_2, to ensure that the new RA is
|
||||
# received by DUT.
|
||||
# - Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 13: Eth_2 sends RA with link-local IPv6 address of Eth_1")
|
||||
pkts.filter_eth_src(Eth_2_ETH_ADDR).\
|
||||
filter_icmpv6_nd_ra().\
|
||||
filter(lambda p: ND_OPTION_PIO in p.icmpv6.opt.type).\
|
||||
filter(lambda p: ND_OPTION_RDNSS in p.icmpv6.opt.type).\
|
||||
must_next()
|
||||
|
||||
# Step 14
|
||||
# - Device: ED_1
|
||||
# - Description (DNS-11.3): Harness instructs device to perform DNS query
|
||||
# Qtype=AAAA, name threadgroup3.org. Automatically, the DNS query gets
|
||||
# routed to the DUT
|
||||
# - Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 14: ED_1 performs DNS query for threadgroup3.org")
|
||||
pkts.filter(lambda p: 'udp' in p.layer_names and p.udp.dstport == 53).\
|
||||
must_next()
|
||||
|
||||
# Step 15
|
||||
# - Device: BR_1 (DUT)
|
||||
# - Description (DNS-11.3): Automatically processes the DNS query by requesting
|
||||
# upstream to the Eth_1 DNS server. Then, it responds back with the answer
|
||||
# to ED_1.
|
||||
# - Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 15: BR_1 requests upstream to Eth_1")
|
||||
pkts.filter_eth_src(BR_1_ETH_ADDR).\
|
||||
filter(lambda p: p.eth.dst == Eth_1_ETH_ADDR).\
|
||||
filter(lambda p: 'udp' in p.layer_names and p.udp.dstport == 53).\
|
||||
must_next()
|
||||
|
||||
# Step 16
|
||||
# - Device: ED_1
|
||||
# - Description (DNS-11.3): Successfully receives DNS query result:
|
||||
# threadgroup3.org AAAA 2002:1234::E1:3
|
||||
# - Pass Criteria:
|
||||
# - ED_1 MUST receive correct DNS query answer from the DUT.
|
||||
print("Step 16: ED_1 receives correct answer")
|
||||
pkts.filter_eth_src(Eth_1_ETH_ADDR).\
|
||||
filter(lambda p: p.eth.dst == BR_1_ETH_ADDR).\
|
||||
must_next()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
verify_utils.run_main(verify)
|
||||
Reference in New Issue
Block a user