mirror of
https://github.com/espressif/openthread.git
synced 2026-08-04 18:07:47 +00:00
[nexus] implement test 1_4_DNS_TC_5 for DNS record types (#12836)
This commit implements the Nexus test specification 1_4_DNS_TC_5 for DNS record types and special cases in OpenThread 1.4. The test verifies that the Border Router: - Can resolve A and AAAA records from upstream DNS servers. - Does not perform IPv6 AAAA synthesis from A records when not specifically requested or configured. - Can resolve mDNS records on the Adjacent Infrastructure Link (AIL). - Supports non-typical record types (RRTypes) and "Private Use" ranges (0xFF00-0xFFFE). - Correctly blocks and responds with NXDomain for "ipv4only.arpa" queries, ensuring they are not forwarded upstream. Test Implementation: - Created test_1_4_DNS_TC_5.cpp to simulate the network topology and DNS query/response sequences. - Created verify_1_4_DNS_TC_5.py to perform packet-level verification of the DNS interactions and BR behavior. - Integrated the new test into the Nexus build and test execution scripts.
This commit is contained in:
@@ -295,6 +295,7 @@ 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_DNS_TC_5 "cert;nexus")
|
||||
ot_nexus_test(1_4_CS_TC_3 "cert;nexus")
|
||||
|
||||
# Misc tests
|
||||
|
||||
@@ -117,7 +117,7 @@ void UpstreamDns::StartUpstreamQuery(UpstreamQueryTransaction &aTxn, const Messa
|
||||
|
||||
SuccessOrExit(error = aQuery.Read(0, dnsHeader));
|
||||
|
||||
message = node.Get<Ip6::Udp>().CloneMessage(aQuery);
|
||||
message = aQuery.Clone<kSameReservedHeader>();
|
||||
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
|
||||
|
||||
pendingQuery = PendingQuery::Allocate();
|
||||
|
||||
@@ -228,6 +228,7 @@ DEFAULT_TESTS=(
|
||||
"1_4_TREL_TC_6"
|
||||
"1_4_DNS_TC_1"
|
||||
"1_4_DNS_TC_3"
|
||||
"1_4_DNS_TC_5"
|
||||
"1_4_CS_TC_3"
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,525 @@
|
||||
/*
|
||||
* 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 uint32_t kFormNetworkTime = 10 * 1000;
|
||||
static constexpr uint32_t kJoinNetworkTime = 20 * 1000;
|
||||
static constexpr uint32_t kStabilizationTime = 10 * 1000;
|
||||
|
||||
static constexpr char kGuaPrefix[] = "2005:1234:abcd:100::/64";
|
||||
static constexpr char kEth1Addr[] = "2005:1234:abcd:100::E1";
|
||||
static constexpr char kBr1AilAddr[] = "2005:1234:abcd:100::B1";
|
||||
|
||||
static constexpr char kThreadGroup1[] = "threadgroup1.org";
|
||||
static constexpr char kThreadGroup2[] = "threadgroup2.org";
|
||||
|
||||
static constexpr char kThreadGroup1Aaaa[] = "2002:1234::E1:1";
|
||||
static constexpr char kThreadGroup1A[] = "192.168.217.1";
|
||||
static constexpr char kThreadGroup2A[] = "192.168.217.2";
|
||||
|
||||
static constexpr uint16_t kTypePrivateUse = 65285; // 0xFF05
|
||||
static constexpr uint32_t kPrivateData = 0x12345678;
|
||||
|
||||
static Node *sEth1Node = nullptr;
|
||||
|
||||
bool HandleUdpHook(Instance &aInstance, Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||
{
|
||||
Node &node = AsNode(&aInstance);
|
||||
Dns::Header dnsHeader;
|
||||
char name[Dns::Name::kMaxNameSize];
|
||||
uint16_t readPos;
|
||||
uint16_t qType;
|
||||
uint16_t qClass;
|
||||
Message *response;
|
||||
bool handled = false;
|
||||
|
||||
VerifyOrExit(&node == sEth1Node);
|
||||
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, qType));
|
||||
qType = BigEndian::HostSwap16(qType);
|
||||
readPos += sizeof(uint16_t);
|
||||
SuccessOrExit(aMessage.Read(readPos, qClass));
|
||||
qClass = BigEndian::HostSwap16(qClass);
|
||||
readPos += sizeof(uint16_t);
|
||||
|
||||
Log("Eth_1 received DNS query for '%s' (type %u)", name, qType);
|
||||
|
||||
response = node.Get<MessagePool>().Allocate(Message::kTypeIp6);
|
||||
VerifyOrExit(response != nullptr);
|
||||
|
||||
dnsHeader.SetType(Dns::Header::kTypeResponse);
|
||||
dnsHeader.SetAnswerCount(0);
|
||||
dnsHeader.SetRecursionDesiredFlag();
|
||||
dnsHeader.SetRecursionAvailableFlag();
|
||||
|
||||
if (Dns::Name::IsSameDomain(name, kThreadGroup1))
|
||||
{
|
||||
if (qType == Dns::ResourceRecord::kTypeAaaa)
|
||||
{
|
||||
Ip6::Address address;
|
||||
Dns::ResourceRecord rr;
|
||||
|
||||
SuccessOrQuit(address.FromString(kThreadGroup1Aaaa));
|
||||
dnsHeader.SetAnswerCount(1);
|
||||
SuccessOrQuit(response->Append(dnsHeader));
|
||||
SuccessOrQuit(Dns::Name::AppendName(name, *response));
|
||||
SuccessOrQuit(response->Append(BigEndian::HostSwap16(qType)));
|
||||
SuccessOrQuit(response->Append(BigEndian::HostSwap16(qClass)));
|
||||
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("Eth_1 sending AAAA response for %s: %s", name, kThreadGroup1Aaaa);
|
||||
}
|
||||
else if (qType == Dns::ResourceRecord::kTypeA)
|
||||
{
|
||||
Ip4::Address address;
|
||||
Dns::ResourceRecord rr;
|
||||
|
||||
SuccessOrQuit(address.FromString(kThreadGroup1A));
|
||||
dnsHeader.SetAnswerCount(1);
|
||||
SuccessOrQuit(response->Append(dnsHeader));
|
||||
SuccessOrQuit(Dns::Name::AppendName(name, *response));
|
||||
SuccessOrQuit(response->Append(BigEndian::HostSwap16(qType)));
|
||||
SuccessOrQuit(response->Append(BigEndian::HostSwap16(qClass)));
|
||||
SuccessOrQuit(Dns::Name::AppendName(name, *response));
|
||||
rr.Init(Dns::ResourceRecord::kTypeA);
|
||||
rr.SetTtl(1800);
|
||||
rr.SetLength(sizeof(Ip4::Address));
|
||||
SuccessOrQuit(response->Append(rr));
|
||||
SuccessOrQuit(response->Append(address));
|
||||
Log("Eth_1 sending A response for %s: %s", name, kThreadGroup1A);
|
||||
}
|
||||
}
|
||||
else if (Dns::Name::IsSameDomain(name, kThreadGroup2))
|
||||
{
|
||||
if (qType == Dns::ResourceRecord::kTypeA)
|
||||
{
|
||||
Ip4::Address address;
|
||||
Dns::ResourceRecord rr;
|
||||
|
||||
SuccessOrQuit(address.FromString(kThreadGroup2A));
|
||||
dnsHeader.SetAnswerCount(1);
|
||||
SuccessOrQuit(response->Append(dnsHeader));
|
||||
SuccessOrQuit(Dns::Name::AppendName(name, *response));
|
||||
SuccessOrQuit(response->Append(BigEndian::HostSwap16(qType)));
|
||||
SuccessOrQuit(response->Append(BigEndian::HostSwap16(qClass)));
|
||||
SuccessOrQuit(Dns::Name::AppendName(name, *response));
|
||||
rr.Init(Dns::ResourceRecord::kTypeA);
|
||||
rr.SetTtl(1800);
|
||||
rr.SetLength(sizeof(Ip4::Address));
|
||||
SuccessOrQuit(response->Append(rr));
|
||||
SuccessOrQuit(response->Append(address));
|
||||
Log("Eth_1 sending A response for %s: %s", name, kThreadGroup2A);
|
||||
}
|
||||
else if (qType == kTypePrivateUse)
|
||||
{
|
||||
Dns::ResourceRecord rr;
|
||||
uint32_t data = BigEndian::HostSwap32(kPrivateData);
|
||||
|
||||
dnsHeader.SetAnswerCount(1);
|
||||
SuccessOrQuit(response->Append(dnsHeader));
|
||||
SuccessOrQuit(Dns::Name::AppendName(name, *response));
|
||||
SuccessOrQuit(response->Append(BigEndian::HostSwap16(qType)));
|
||||
SuccessOrQuit(response->Append(BigEndian::HostSwap16(qClass)));
|
||||
SuccessOrQuit(Dns::Name::AppendName(name, *response));
|
||||
rr.Init(kTypePrivateUse);
|
||||
rr.SetTtl(1800);
|
||||
rr.SetLength(sizeof(uint32_t));
|
||||
SuccessOrQuit(response->Append(rr));
|
||||
SuccessOrQuit(response->Append(data));
|
||||
Log("Eth_1 sending PrivateUse response for %s", name);
|
||||
}
|
||||
}
|
||||
|
||||
if (dnsHeader.GetAnswerCount() == 0)
|
||||
{
|
||||
Log("Eth_1 sending empty response for %s", name);
|
||||
SuccessOrQuit(response->Append(dnsHeader));
|
||||
SuccessOrQuit(Dns::Name::AppendName(name, *response));
|
||||
SuccessOrQuit(response->Append(BigEndian::HostSwap16(qType)));
|
||||
SuccessOrQuit(response->Append(BigEndian::HostSwap16(qClass)));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void HandleDnsRecordResponse(otError aError, const otDnsRecordResponse *aResponse, void *aContext)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aError);
|
||||
OT_UNUSED_VARIABLE(aResponse);
|
||||
OT_UNUSED_VARIABLE(aContext);
|
||||
}
|
||||
|
||||
void SendQuery(Node &aNode, const char *aName, uint16_t aType)
|
||||
{
|
||||
if (aType == Dns::ResourceRecord::kTypeAaaa)
|
||||
{
|
||||
SuccessOrQuit(aNode.Get<Dns::Client>().ResolveAddress(aName, HandleDnsResponse, nullptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
SuccessOrQuit(aNode.Get<Dns::Client>().QueryRecord(aType, nullptr, aName, HandleDnsRecordResponse, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
void SendRa(Node &aNode, const Ip6::Address &aDnsAddr, const Ip6::Prefix &aPrefix)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
* 11.5. [1.4] [CERT] DNS record types and special cases
|
||||
*
|
||||
* 11.5.1. Purpose
|
||||
* To verify that the BR DUT:
|
||||
* - Can resolve A records
|
||||
* - Can resolve A records without doing IPv6 AAAA synthesis
|
||||
* - Can resolve AAAA records
|
||||
* - Can resolve records contributed by different sources (on-mesh, on-AIL, and upstream DNS server)
|
||||
* - Supports non-typical record types (RRTypes) for queries
|
||||
* - Supports queries using RR type 0xFF00-0xFFFE (65280-65534) “Private Use”
|
||||
* - Blocks the “ipv4only.arpa” query
|
||||
*/
|
||||
void Test_1_4_DNS_TC_5(const char *aJsonFileName)
|
||||
{
|
||||
Core nexus;
|
||||
Node &br1 = nexus.CreateNode();
|
||||
Node &ed1 = nexus.CreateNode();
|
||||
Node ð1 = nexus.CreateNode();
|
||||
Ip6::Address eth1Addr;
|
||||
Ip6::Address br1AilAddr;
|
||||
Ip6::Prefix guaPrefix;
|
||||
|
||||
br1.SetName("BR", 1);
|
||||
ed1.SetName("ED", 1);
|
||||
eth1.SetName("Eth", 1);
|
||||
|
||||
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelNote));
|
||||
|
||||
sEth1Node = ð1;
|
||||
eth1.mInfraIf.SetUdpHook(HandleUdpHook);
|
||||
|
||||
br1.AllowList(ed1);
|
||||
ed1.AllowList(br1);
|
||||
|
||||
SuccessOrQuit(eth1Addr.FromString(kEth1Addr));
|
||||
SuccessOrQuit(br1AilAddr.FromString(kBr1AilAddr));
|
||||
SuccessOrQuit(guaPrefix.FromString(kGuaPrefix));
|
||||
|
||||
/**
|
||||
* Step 1
|
||||
* Device: Eth_1, BR_1, ED_1
|
||||
* Description (DNS-11.5): Form topology. Thread network is formed.
|
||||
* Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 1: Form topology. Thread network is formed.");
|
||||
|
||||
eth1.Get<ThreadNetif>().Up();
|
||||
eth1.mInfraIf.AddAddress(eth1Addr);
|
||||
|
||||
br1.Form();
|
||||
nexus.AdvanceTime(kFormNetworkTime);
|
||||
|
||||
ed1.Join(br1);
|
||||
nexus.AdvanceTime(kJoinNetworkTime);
|
||||
|
||||
br1.Get<BorderRouter::InfraIf>().Init(1, true);
|
||||
br1.mInfraIf.AddAddress(br1AilAddr);
|
||||
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 = 53;
|
||||
ed1.Get<Dns::Client>().SetDefaultConfig(dnsConfig);
|
||||
}
|
||||
|
||||
// Send RA for BR to adopt the DNS server address and OMR prefix
|
||||
SendRa(eth1, eth1Addr, guaPrefix);
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
|
||||
/**
|
||||
* Step 2
|
||||
* Device: Eth_1
|
||||
* Description (DNS-11.5): Harness instructs device to configure DNS server with
|
||||
* records: threadgroup1.org AAAA 2002:1234::E1:1, threadgroup1.org A 192.168.217.1,
|
||||
* threadgroup2.org A 192.168.217.2, threadgroup2.org TYPE65285 \# 4 12345678.
|
||||
* Note: the record type 0xFF05 is a private range record type, hence it has the
|
||||
* symbolic name TYPE65285 associated.
|
||||
* Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 2: Eth_1 configures DNS server with records.");
|
||||
|
||||
/**
|
||||
* Step 3
|
||||
* Device: Eth_1
|
||||
* Description (DNS-11.5): Harness instructs device to start advertising mDNS
|
||||
* services on the AIL: Note: the PTR records for services are not shown below;
|
||||
* however these are present as usual. $ORIGIN local. Service-test-1.
|
||||
* _infra-test._udp ( SRV 0 0 55551 host-test-eth TXT dummy=abcd;thread-test=a49 )
|
||||
* service-test-2._infra-test._udp ( SRV 0 0 55552 host-test-eth TXT a=2;thread-test
|
||||
* =b50_test ) ) host-test-eth A <IPv4 address of Eth_1>.
|
||||
* Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 3: Eth_1 starts advertising mDNS services on the AIL.");
|
||||
{
|
||||
Dns::Multicast::Core::Service mdnsService;
|
||||
Ip6::Address eth1Ip4Addr;
|
||||
Ip4::Address ip4Addr;
|
||||
|
||||
SuccessOrQuit(eth1.Get<Dns::Multicast::Core>().SetLocalHostName("host-test-eth"));
|
||||
SuccessOrQuit(eth1.Get<Dns::Multicast::Core>().SetEnabled(true, 1));
|
||||
|
||||
SuccessOrQuit(ip4Addr.FromString("192.168.217.100"));
|
||||
eth1Ip4Addr.SetToIp4Mapped(ip4Addr);
|
||||
eth1.mInfraIf.AddAddress(eth1Ip4Addr);
|
||||
eth1.mMdns.SignalIfAddresses(eth1.GetInstance());
|
||||
|
||||
ClearAllBytes(mdnsService);
|
||||
mdnsService.mServiceInstance = "service-test-1";
|
||||
mdnsService.mServiceType = "_infra-test._udp";
|
||||
mdnsService.mHostName = "host-test-eth";
|
||||
static const uint8_t kTxtData1[] = "\x0adummy=abcd\x0fthread-test=a49";
|
||||
mdnsService.mTxtData = kTxtData1;
|
||||
mdnsService.mTxtDataLength = sizeof(kTxtData1) - 1;
|
||||
mdnsService.mPort = 55551;
|
||||
SuccessOrQuit(eth1.Get<Dns::Multicast::Core>().RegisterService(mdnsService, 1, nullptr));
|
||||
|
||||
mdnsService.mServiceInstance = "service-test-2";
|
||||
mdnsService.mPort = 55552;
|
||||
static const uint8_t kTxtData2[] = "\x03a=2\x14thread-test=b50_test";
|
||||
mdnsService.mTxtData = kTxtData2;
|
||||
mdnsService.mTxtDataLength = sizeof(kTxtData2) - 1;
|
||||
SuccessOrQuit(eth1.Get<Dns::Multicast::Core>().RegisterService(mdnsService, 2, nullptr));
|
||||
}
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
|
||||
/**
|
||||
* Step 4
|
||||
* Device: ED_1
|
||||
* Description (DNS-11.5): Harness instructs device to send a DNS query to the DUT
|
||||
* with Qtype=AAAA for name “threadgroup1.org”.
|
||||
* Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 4: ED_1 sends a DNS query to the DUT with Qtype=AAAA for name threadgroup1.org.");
|
||||
SendRa(eth1, eth1Addr, guaPrefix);
|
||||
SendQuery(ed1, kThreadGroup1, Dns::ResourceRecord::kTypeAaaa);
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
|
||||
/**
|
||||
* Step 5
|
||||
* Device: BR_1 (DUT)
|
||||
* Description (DNS-11.5): Automatically performs the DNS query via the Eth_1 DNS
|
||||
* server, and provides the answer back to ED_1.
|
||||
* Pass Criteria:
|
||||
* - The DUT MUST respond Rcode=0 (NoError) with answer record: threadgroup1.org
|
||||
* AAAA 2002:1234::E1:1
|
||||
*/
|
||||
Log("Step 5: BR_1 performs the DNS query via Eth_1 and provides the answer to ED_1.");
|
||||
|
||||
/**
|
||||
* Step 6
|
||||
* Device: ED_1
|
||||
* Description (DNS-11.5): Harness instructs device to send a DNS query to the DUT,
|
||||
* with Qtype=A for name “threadgroup1.org”.
|
||||
* Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 6: ED_1 sends a DNS query to the DUT with Qtype=A for name threadgroup1.org.");
|
||||
SendRa(eth1, eth1Addr, guaPrefix);
|
||||
SendQuery(ed1, kThreadGroup1, Dns::ResourceRecord::kTypeA);
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
|
||||
/**
|
||||
* Step 7
|
||||
* Device: BR_1 (DUT)
|
||||
* Description (DNS-11.5): Automatically performs the DNS query via the Eth_1 DNS
|
||||
* server, and provides the answer back to ED_1.
|
||||
* Pass Criteria:
|
||||
* - The DUT MUST respond Rcode=0 (NoError) with answer record: threadgroup1.org A
|
||||
* 192.168.217.1
|
||||
*/
|
||||
Log("Step 7: BR_1 performs the DNS query via Eth_1 and provides the answer to ED_1.");
|
||||
|
||||
/**
|
||||
* Step 8
|
||||
* Device: ED_1
|
||||
* Description (DNS-11.5): Harness instructs device to send a DNS query to the DUT,
|
||||
* with Qtype=AAAA for name “threadgroup2.org”.
|
||||
* Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 8: ED_1 sends a DNS query to the DUT with Qtype=AAAA for name threadgroup2.org.");
|
||||
SendRa(eth1, eth1Addr, guaPrefix);
|
||||
SendQuery(ed1, kThreadGroup2, Dns::ResourceRecord::kTypeAaaa);
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
|
||||
/**
|
||||
* Step 9
|
||||
* Device: BR_1 (DUT)
|
||||
* Description (DNS-11.5): Automatically performs the DNS query via the Eth_1 DNS
|
||||
* server, and provides the error answer back to ED_1. Note: this checks that the
|
||||
* DUT does not perform AAAA synthesis from the A record.
|
||||
* Pass Criteria:
|
||||
* - The DUT MUST respond Rcode=0 (NoError) with zero answer records (ANCOUNT=0).
|
||||
*/
|
||||
Log("Step 9: BR_1 performs the DNS query via Eth_1 and provides the error answer to ED_1.");
|
||||
|
||||
/**
|
||||
* Step 10
|
||||
* Device: ED_1
|
||||
* Description (DNS-11.5): Harness instructs device to send a DNS query, with
|
||||
* Qtype=A for name “host-test-eth.default.service.arpa”.
|
||||
* Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 10: ED_1 sends a DNS query with Qtype=A for host-test-eth.default.service.arpa.");
|
||||
SendQuery(ed1, "host-test-eth.default.service.arpa", Dns::ResourceRecord::kTypeA);
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
|
||||
/**
|
||||
* Step 11
|
||||
* Device: BR_1 (DUT)
|
||||
* Description (DNS-11.5): Automatically performs the mDNS query on AIL
|
||||
* (optionally, if still needed), and provides the answer back to ED_1.
|
||||
* Pass Criteria:
|
||||
* - The DUT MUST respond Rcode=0 (NoError) with answer record: host-test-eth.
|
||||
* default.service.arpa A <IPv4 address of Eth_1>
|
||||
*/
|
||||
Log("Step 11: BR_1 performs mDNS query on AIL and provides the answer to ED_1.");
|
||||
|
||||
/**
|
||||
* Step 12
|
||||
* Device: ED_1
|
||||
* Description (DNS-11.5): Harness instructs device to send DNS query Qtype=A for
|
||||
* name “ipv4only.arpa”
|
||||
* Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 12: ED_1 sends DNS query Qtype=A for name ipv4only.arpa.");
|
||||
SendQuery(ed1, "ipv4only.arpa", Dns::ResourceRecord::kTypeA);
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
|
||||
/**
|
||||
* Step 13
|
||||
* Device: BR_1 (DUT)
|
||||
* Description (DNS-11.5): Automatically blocks the ipv4only.arpa query and
|
||||
* responds NXDomain error.
|
||||
* Pass Criteria:
|
||||
* - The DUT MUST respond Rcode=3 (NXDomain) with zero answer records (ANCOUNT=0).
|
||||
* - The DUT MUST NOT send a DNS query to Eth_1.
|
||||
*/
|
||||
Log("Step 13: BR_1 blocks the ipv4only.arpa query and responds NXDomain error.");
|
||||
|
||||
/**
|
||||
* Step 14
|
||||
* Device: ED_1
|
||||
* Description (DNS-11.5): Harness instructs device to send DNS query with RR type
|
||||
* in the private use range Qtype=0xFF05 for name “threadgroup2.org”. To do this
|
||||
* with OT CLI, use the following: udp send <BR_1 ML-EID address> 53 -x
|
||||
* e13c010000010000000000000c74687265616467726f757032036f726700ff050001 “”
|
||||
* Pass Criteria:
|
||||
* - N/A
|
||||
*/
|
||||
Log("Step 14: ED_1 sends DNS query with RR type in private use range Qtype=0xFF05.");
|
||||
SendRa(eth1, eth1Addr, guaPrefix);
|
||||
SendQuery(ed1, kThreadGroup2, kTypePrivateUse);
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
|
||||
/**
|
||||
* Step 15
|
||||
* Device: BR_1 (DUT)
|
||||
* Description (DNS-11.5): Automatically responds with the requested record.
|
||||
* Pass Criteria:
|
||||
* - The DUT MUST respond Rcode=0 (NoError) with answer record with binary data
|
||||
* (4 bytes): threadgroup2.org 0xFF05 0x12345678
|
||||
*/
|
||||
Log("Step 15: BR_1 responds with the requested record.");
|
||||
|
||||
nexus.SaveTestInfo(aJsonFileName);
|
||||
}
|
||||
|
||||
} // namespace Nexus
|
||||
} // namespace ot
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
ot::Nexus::Test_1_4_DNS_TC_5((argc > 2) ? argv[2] : "test_1_4_dns_tc_5.json");
|
||||
ot::Nexus::Log("All tests passed");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
#!/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
|
||||
|
||||
|
||||
def verify(pv):
|
||||
# 11.5. [1.4] [CERT] DNS record types and special cases
|
||||
#
|
||||
# 11.5.1. Purpose
|
||||
# To verify that the BR DUT:
|
||||
# - Can resolve A records
|
||||
# - Can resolve A records without doing IPv6 AAAA synthesis
|
||||
# - Can resolve AAAA records
|
||||
# - Can resolve records contributed by different sources (on-mesh, on-AIL, and upstream DNS server)
|
||||
# - Supports non-typical record types (RRTypes) for queries
|
||||
# - Supports queries using RR type 0xFF00-0xFFFE (65280-65534) “Private Use”
|
||||
# - Blocks the “ipv4only.arpa” query
|
||||
|
||||
pkts = pv.pkts
|
||||
pv.summary.show()
|
||||
|
||||
# Step 1
|
||||
# Device: Eth_1, BR_1, ED_1
|
||||
# Description (DNS-11.5): Form topology. Thread network is formed.
|
||||
# Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 1: Form topology. Thread network is formed.")
|
||||
|
||||
# Step 2
|
||||
# Device: Eth_1
|
||||
# Description (DNS-11.5): Harness instructs device to configure DNS server with records:
|
||||
# threadgroup1.org AAAA 2002:1234::E1:1, threadgroup1.org A 192.168.217.1,
|
||||
# threadgroup2.org A 192.168.217.2, threadgroup2.org TYPE65285 \# 4 12345678.
|
||||
# Note: the record type 0xFF05 is a private range record type, hence it has the
|
||||
# symbolic name TYPE65285 associated.
|
||||
# Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 2: Eth_1 configures DNS server with records.")
|
||||
|
||||
# Step 3
|
||||
# Device: Eth_1
|
||||
# Description (DNS-11.5): Harness instructs device to start advertising mDNS services on the AIL:
|
||||
# Note: the PTR records for services are not shown below; however these are present as usual.
|
||||
# $ORIGIN local. Service-test-1._infra-test._udp ( SRV 0 0 55551 host-test-eth TXT
|
||||
# dummy=abcd;thread-test=a49 ) service-test-2._infra-test._udp ( SRV 0 0 55552
|
||||
# host-test-eth TXT a=2;thread-test=b50_test ) ) host-test-eth A <IPv4 address of Eth_1>.
|
||||
# Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 3: Eth_1 starts advertising mDNS services on the AIL.")
|
||||
|
||||
# Step 4
|
||||
# Device: ED_1
|
||||
# Description (DNS-11.5): Harness instructs device to send a DNS query to the DUT with
|
||||
# Qtype=AAAA for name “threadgroup1.org”.
|
||||
# Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 4: ED_1 sends a DNS query to the DUT with Qtype=AAAA for name threadgroup1.org.")
|
||||
pkts.filter(lambda p: getattr(p, 'dns', None) and p.dns.flags.response == 0).\
|
||||
filter(lambda p: any(name in ("threadgroup1.org", "threadgroup1.org.") for name in verify_utils.as_list(p.dns.qry.name))).\
|
||||
filter(lambda p: consts.DNS_TYPE_AAAA in verify_utils.as_list(p.dns.qry.type)).\
|
||||
must_next()
|
||||
|
||||
# Step 5
|
||||
# Device: BR_1 (DUT)
|
||||
# Description (DNS-11.5): Automatically performs the DNS query via the Eth_1 DNS server,
|
||||
# and provides the answer back to ED_1.
|
||||
# Pass Criteria:
|
||||
# - The DUT MUST respond Rcode=0 (NoError) with answer record: threadgroup1.org
|
||||
# AAAA 2002:1234::E1:1
|
||||
print("Step 5: BR_1 performs the DNS query via Eth_1 and provides the answer to ED_1.")
|
||||
pkts.filter(lambda p: getattr(p, 'dns', None) and p.dns.flags.response == 1).\
|
||||
filter(lambda p: consts.DNS_TYPE_AAAA in verify_utils.as_list(p.dns.qry.type)).\
|
||||
filter(lambda p: p.dns.flags.rcode == consts.DNS_RCODE_NOERROR).\
|
||||
filter(lambda p: int(p.dns.count.answers) > 0).\
|
||||
filter(lambda p: any(verify_utils.Ipv6Addr("2002:1234::e1:1") == addr
|
||||
for addr in verify_utils.as_list(p.dns.aaaa))).\
|
||||
must_next()
|
||||
|
||||
# Step 6
|
||||
# Device: ED_1
|
||||
# Description (DNS-11.5): Harness instructs device to send a DNS query to the DUT, with
|
||||
# Qtype=A for name “threadgroup1.org”.
|
||||
# Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 6: ED_1 sends a DNS query to the DUT with Qtype=A for name threadgroup1.org.")
|
||||
pkts.filter(lambda p: getattr(p, 'dns', None) and p.dns.flags.response == 0).\
|
||||
filter(lambda p: any(name in ("threadgroup1.org", "threadgroup1.org.") for name in verify_utils.as_list(p.dns.qry.name))).\
|
||||
filter(lambda p: consts.DNS_TYPE_A in verify_utils.as_list(p.dns.qry.type)).\
|
||||
must_next()
|
||||
|
||||
# Step 7
|
||||
# Device: BR_1 (DUT)
|
||||
# Description (DNS-11.5): Automatically performs the DNS query via the Eth_1 DNS server,
|
||||
# and provides the answer back to ED_1.
|
||||
# Pass Criteria:
|
||||
# - The DUT MUST respond Rcode=0 (NoError) with answer record: threadgroup1.org A 192.168.217.1
|
||||
print("Step 7: BR_1 performs the DNS query via Eth_1 and provides the answer to ED_1.")
|
||||
pkts.filter(lambda p: getattr(p, 'dns', None) and p.dns.flags.response == 1).\
|
||||
filter(lambda p: consts.DNS_TYPE_A in verify_utils.as_list(p.dns.qry.type)).\
|
||||
filter(lambda p: p.dns.flags.rcode == consts.DNS_RCODE_NOERROR).\
|
||||
filter(lambda p: int(p.dns.count.answers) > 0).\
|
||||
filter(lambda p: any("192.168.217.1" == str(a) for a in verify_utils.as_list(p.dns.a))).\
|
||||
must_next()
|
||||
|
||||
# Step 8
|
||||
# Device: ED_1
|
||||
# Description (DNS-11.5): Harness instructs device to send a DNS query to the DUT, with
|
||||
# Qtype=AAAA for name “threadgroup2.org”.
|
||||
# Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 8: ED_1 sends a DNS query to the DUT with Qtype=AAAA for name threadgroup2.org.")
|
||||
pkts.filter(lambda p: getattr(p, 'dns', None) and p.dns.flags.response == 0).\
|
||||
filter(lambda p: any(name in ("threadgroup2.org", "threadgroup2.org.") for name in verify_utils.as_list(p.dns.qry.name))).\
|
||||
filter(lambda p: consts.DNS_TYPE_AAAA in verify_utils.as_list(p.dns.qry.type)).\
|
||||
must_next()
|
||||
|
||||
# Step 9
|
||||
# Device: BR_1 (DUT)
|
||||
# Description (DNS-11.5): Automatically performs the DNS query via the Eth_1 DNS server,
|
||||
# and provides the error answer back to ED_1. Note: this checks that the DUT does not
|
||||
# perform AAAA synthesis from the A record.
|
||||
# Pass Criteria:
|
||||
# - The DUT MUST respond Rcode=0 (NoError) with zero answer records (ANCOUNT=0).
|
||||
print("Step 9: BR_1 performs the DNS query via Eth_1 and provides the error answer to ED_1.")
|
||||
pkts.filter(lambda p: getattr(p, 'dns', None) and p.dns.flags.response == 1).\
|
||||
filter(lambda p: consts.DNS_TYPE_AAAA in verify_utils.as_list(p.dns.qry.type)).\
|
||||
filter(lambda p: p.dns.flags.rcode == consts.DNS_RCODE_NOERROR).\
|
||||
filter(lambda p: int(p.dns.count.answers) == 0).\
|
||||
must_next()
|
||||
|
||||
# Step 10
|
||||
# Device: ED_1
|
||||
# Description (DNS-11.5): Harness instructs device to send a DNS query, with Qtype=A for
|
||||
# name “host-test-eth.default.service.arpa”.
|
||||
# Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 10: ED_1 sends a DNS query with Qtype=A for host-test-eth.default.service.arpa.")
|
||||
pkts.filter(lambda p: getattr(p, 'dns', None) and p.dns.flags.response == 0).\
|
||||
filter(lambda p: any(name in ("host-test-eth.default.service.arpa", "host-test-eth.default.service.arpa.") for name in verify_utils.as_list(p.dns.qry.name))).\
|
||||
filter(lambda p: consts.DNS_TYPE_A in verify_utils.as_list(p.dns.qry.type)).\
|
||||
must_next()
|
||||
|
||||
# Step 11
|
||||
# Device: BR_1 (DUT)
|
||||
# Description (DNS-11.5): Automatically performs the mDNS query on AIL (optionally, if
|
||||
# still needed), and provides the answer back to ED_1.
|
||||
# Pass Criteria:
|
||||
# - The DUT MUST respond Rcode=0 (NoError) with answer record:
|
||||
# host-test-eth.default.service.arpa A <IPv4 address of Eth_1>
|
||||
print("Step 11: BR_1 performs mDNS query on AIL and provides the answer to ED_1.")
|
||||
pkts.filter(lambda p: getattr(p, 'dns', None) and p.dns.flags.response == 1).\
|
||||
filter(lambda p: consts.DNS_TYPE_A in verify_utils.as_list(p.dns.qry.type)).\
|
||||
filter(lambda p: p.dns.flags.rcode == consts.DNS_RCODE_NOERROR).\
|
||||
filter(lambda p: int(p.dns.count.answers) > 0).\
|
||||
filter(lambda p: any("192.168.217.100" == str(a) for a in verify_utils.as_list(p.dns.a))).\
|
||||
must_next()
|
||||
|
||||
# Step 12
|
||||
# Device: ED_1
|
||||
# Description (DNS-11.5): Harness instructs device to send DNS query Qtype=A for name “ipv4only.arpa”
|
||||
# Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 12: ED_1 sends DNS query Qtype=A for name ipv4only.arpa.")
|
||||
pkts.filter(lambda p: getattr(p, 'dns', None) and p.dns.flags.response == 0).\
|
||||
filter(lambda p: any(name in ("ipv4only.arpa", "ipv4only.arpa.") for name in verify_utils.as_list(p.dns.qry.name))).\
|
||||
filter(lambda p: consts.DNS_TYPE_A in verify_utils.as_list(p.dns.qry.type)).\
|
||||
must_next()
|
||||
|
||||
# Step 13
|
||||
# Device: BR_1 (DUT)
|
||||
# Description (DNS-11.5): Automatically blocks the ipv4only.arpa query and responds NXDomain error.
|
||||
# Pass Criteria:
|
||||
# - The DUT MUST respond Rcode=3 (NXDomain) with zero answer records (ANCOUNT=0).
|
||||
# - The DUT MUST NOT send a DNS query to Eth_1.
|
||||
print("Step 13: BR_1 blocks the ipv4only.arpa query and responds NXDomain error.")
|
||||
pkts.filter(lambda p: getattr(p, 'dns', None) and p.dns.flags.response == 1).\
|
||||
filter(lambda p: any(name in ("ipv4only.arpa", "ipv4only.arpa.") for name in verify_utils.as_list(p.dns.qry.name))).\
|
||||
filter(lambda p: p.dns.flags.rcode == consts.DNS_RCODE_NXDOMAIN).\
|
||||
filter(lambda p: int(p.dns.count.answers) == 0).\
|
||||
must_next()
|
||||
|
||||
# Check that no query was sent to Eth_1 (upstream)
|
||||
pkts.copy().filter(lambda p: getattr(p, 'dns', None) and p.dns.flags.response == 0).\
|
||||
filter(lambda p: any(name in ("ipv4only.arpa", "ipv4only.arpa.") for name in verify_utils.as_list(p.dns.qry.name))).\
|
||||
filter(lambda p: p.eth.dst == "02:00:00:00:00:02").\
|
||||
must_not_next()
|
||||
|
||||
# Step 14
|
||||
# Device: ED_1
|
||||
# Description (DNS-11.5): Harness instructs device to send DNS query with RR type in the
|
||||
# private use range Qtype=0xFF05 for name “threadgroup2.org”. To do this with OT CLI,
|
||||
# use the following: udp send <BR_1 ML-EID address> 53 -x
|
||||
# e13c010000010000000000000c74687265616467726f757032036f726700ff050001 “”
|
||||
# Pass Criteria:
|
||||
# - N/A
|
||||
print("Step 14: ED_1 sends DNS query with RR type in private use range Qtype=0xFF05.")
|
||||
pkts.filter(lambda p: getattr(p, 'dns', None) and p.dns.flags.response == 0).\
|
||||
filter(lambda p: any(name in ("threadgroup2.org", "threadgroup2.org.") for name in verify_utils.as_list(p.dns.qry.name))).\
|
||||
filter(lambda p: any(65285 == t for t in verify_utils.as_list(p.dns.qry.type))).\
|
||||
must_next()
|
||||
|
||||
# Step 15
|
||||
# Device: BR_1 (DUT)
|
||||
# Description (DNS-11.5): Automatically responds with the requested record.
|
||||
# Pass Criteria:
|
||||
# - The DUT MUST respond Rcode=0 (NoError) with answer record with binary data (4 bytes):
|
||||
# threadgroup2.org 0xFF05 0x12345678
|
||||
print("Step 15: BR_1 responds with the requested record.")
|
||||
pkts.filter(lambda p: getattr(p, 'dns', None) and p.dns.flags.response == 1).\
|
||||
filter(lambda p: any(65285 == t for t in verify_utils.as_list(p.dns.qry.type))).\
|
||||
filter(lambda p: p.dns.flags.rcode == consts.DNS_RCODE_NOERROR).\
|
||||
filter(lambda p: int(p.dns.count.answers) > 0).\
|
||||
filter(lambda p: any("12345678" in str(getattr(p.dns, 'data', '')) for _ in [0])).\
|
||||
must_next()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
verify_utils.run_main(verify)
|
||||
@@ -640,6 +640,10 @@ def apply_patches():
|
||||
layer_fields._layer_containers.add('dns.opt')
|
||||
layer_fields._layer_containers.add('dns.count')
|
||||
layer_fields._LAYER_FIELDS['mdns.nsec'] = layer_fields._list(layer_fields._bytes)
|
||||
layer_fields._LAYER_FIELDS['dns.a'] = layer_fields._list(layer_fields._str)
|
||||
layer_fields._LAYER_FIELDS['mdns.a'] = layer_fields._list(layer_fields._str)
|
||||
layer_fields._LAYER_FIELDS['dns.data'] = layer_fields._list(layer_fields._bytes)
|
||||
layer_fields._LAYER_FIELDS['mdns.data'] = layer_fields._list(layer_fields._bytes)
|
||||
|
||||
def which_tshark_patch():
|
||||
default_path = '/tmp/thread-wireshark/tshark'
|
||||
|
||||
Reference in New Issue
Block a user