diff --git a/tests/nexus/CMakeLists.txt b/tests/nexus/CMakeLists.txt index 548f163d2..2606cb16f 100644 --- a/tests/nexus/CMakeLists.txt +++ b/tests/nexus/CMakeLists.txt @@ -264,6 +264,7 @@ ot_nexus_test(1_3_DBR_TC_7C "cert;nexus") ot_nexus_test(1_3_DBR_TC_8 "cert;nexus") ot_nexus_test(1_3_DBR_TC_10 "cert;nexus") ot_nexus_test(1_3_SRP_TC_1 "cert;nexus") +ot_nexus_test(1_3_SRP_TC_2 "cert;nexus") # Misc tests ot_nexus_test(border_admitter "core;nexus") diff --git a/tests/nexus/run_nexus_tests.sh b/tests/nexus/run_nexus_tests.sh index ee37c0643..9b44ffff8 100755 --- a/tests/nexus/run_nexus_tests.sh +++ b/tests/nexus/run_nexus_tests.sh @@ -200,6 +200,7 @@ DEFAULT_TESTS=( "1_3_DBR_TC_8" "1_3_DBR_TC_10" "1_3_SRP_TC_1" + "1_3_SRP_TC_2" ) # Use provided arguments or the default test list diff --git a/tests/nexus/test_1_3_SRP_TC_2.cpp b/tests/nexus/test_1_3_SRP_TC_2.cpp new file mode 100644 index 000000000..1da9948f4 --- /dev/null +++ b/tests/nexus/test_1_3_SRP_TC_2.cpp @@ -0,0 +1,608 @@ +/* + * 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 +#include + +#include "platform/nexus_core.hpp" +#include "platform/nexus_node.hpp" + +namespace ot { +namespace Nexus { + +/** + * Time to advance for a node to form a network and become leader, in milliseconds. + */ +static constexpr uint32_t kFormNetworkTime = 13 * 1000; + +/** + * Time to advance for a node to join a network, in milliseconds. + */ +static constexpr uint32_t kJoinNetworkTime = 10 * 1000; + +/** + * Time to advance for the BR to perform automatic actions (RA, Network Data), in milliseconds. + */ +static constexpr uint32_t kBrActionTime = 20 * 1000; + +/** + * Time to advance for SRP server information to be updated in Network Data. + */ +static constexpr uint32_t kSrpServerInfoUpdateTime = 20 * 1000; + +/** + * Time to advance for SRP registration, in milliseconds. + */ +static constexpr uint32_t kSrpRegistrationTime = 10 * 1000; + +/** + * Time to advance for DNS query processing, in milliseconds. + */ +static constexpr uint32_t kDnsQueryTime = 5 * 1000; + +/** + * Infrastructure interface index. + */ +static constexpr uint32_t kInfraIfIndex = 1; + +/** + * SRP service registration parameters. + */ +static const char kSrpServiceType[] = "_thread-test._udp"; +static const char kSrpInstance1[] = "service-test-1"; +static const char kSrpInstance2[] = "service-test-2"; +static const char kSrpHost1[] = "host-test-1"; +static const char kSrpHost2[] = "host-test-2"; +static constexpr uint16_t kSrpServicePort333 = 33333; +static constexpr uint16_t kSrpServicePort444 = 44444; + +void Test_1_3_SRP_TC_2(const char *aJsonFileName) +{ + Srp::Client::Service service1; + Srp::Client::Service service2; + + /** + * 2.2. [1.3] [CERT] Name Conflicts - Single Thread Network + * + * 2.2.1. Purpose + * To test the following: + * - 1. Handle name conflict in Host Description record. + * - 2. Handle name conflict in Service Description record. + * - 3. Verify that original service is seen when discovering, and conflicting + * service is not seen on the AIL. + * + * 2.2.2. Topology + * - 1. BR_1 (DUT) - Border Router and the Leader. + * - 2. ED 1-Test Bed device operating as a Thread End Device, attached to BR_1 + * - 3. ED 2-Test Bed device operating as a Thread End Device, attached to BR_1 + * - 4. Eth 1-Test Bed border router device on an Adjacent Infrastructure Link + * + * 2.2.3. Initial Conditions + * - BR_1 (DUT) should automatically start SRP server capabilities at the + * beginning of the test. + */ + + Core nexus; + + Node &br1 = nexus.CreateNode(); + Node &ed1 = nexus.CreateNode(); + Node &ed2 = nexus.CreateNode(); + Node ð1 = nexus.CreateNode(); + + br1.SetName("BR_1"); + ed1.SetName("ED_1"); + ed2.SetName("ED_2"); + eth1.SetName("Eth_1"); + + br1.Form(); + nexus.AdvanceTime(0); + + Instance::SetLogLevel(kLogLevelNote); + + Log("---------------------------------------------------------------------------------------"); + + /** + * Step 1 + * - Device: BR 1 (DUT) + * - Description (SRP-2.2): Enable: switch on. + * - Pass Criteria: + * - N/A + */ + Log("Step 1: BR 1 (DUT) switch on."); + SuccessOrQuit(br1.Get().SetAddressMode(Srp::Server::kAddressModeUnicast)); + br1.Get().SetEnabled(true); + br1.Get().Init(kInfraIfIndex, true); + br1.Get().Init(); + SuccessOrQuit(br1.Get().SetEnabled(true)); + nexus.AdvanceTime(kFormNetworkTime); + + /** + * Step 2 + * - Device: ED 1, ED 2, Eth 1 + * - Description (SRP-2.2): Enable + * - Pass Criteria: + * - N/A + */ + Log("Step 2: ED 1, ED 2, Eth 1 Enable."); + ed1.Join(br1, Node::kAsFed); + ed2.Join(br1, Node::kAsFed); + SuccessOrQuit(eth1.Get().SetEnabled(true, kInfraIfIndex)); + nexus.AdvanceTime(kJoinNetworkTime); + + // Wait for BR to advertise prefixes and EDs to get OMR addresses + nexus.AdvanceTime(kBrActionTime); + + /** + * Step 3 + * - Device: BR 1 (DUT) + * - Description (SRP-2.2): Automatically adds its SRP Server information in the + * Thread Network Data. + * - Pass Criteria: + * - The DUT's SRP Server information MUST appear in the Thread Network Data. + * This can be verified as specified in test case 2.1 step 3. + * - Specifically, it MUST include a DNS-SD Unicast Dataset. + */ + Log("Step 3: BR 1 (DUT) adds its SRP Server information in the Thread Network Data."); + nexus.AdvanceTime(kSrpServerInfoUpdateTime); + + /** + * Step 4 + * - Device: ED 1 + * - Description (SRP-2.2): Harness instructs the device to send SRP Update with + * initial service registration: $ORIGIN default.service.arpa. + * service-test-1._thread-test._udp ( SRV 0 0 33333 host-test-1 ) host-test-1 + * AAAA + * - Pass Criteria: + * - N/A + */ + Log("Step 4: ED 1 sends SRP Update with initial service registration."); + { + ed1.Get().EnableAutoStartMode(nullptr, nullptr); + SuccessOrQuit(ed1.Get().SetHostName(kSrpHost1)); + + SuccessOrQuit(ed1.Get().EnableAutoHostAddress()); + + ClearAllBytes(service1); + service1.mName = kSrpServiceType; + service1.mInstanceName = kSrpInstance1; + service1.mPort = kSrpServicePort333; + service1.mTxtEntries = nullptr; + service1.mNumTxtEntries = 0; + SuccessOrQuit(ed1.Get().AddService(service1)); + } + nexus.AdvanceTime(kSrpRegistrationTime); + + /** + * Step 5 + * - Device: BR 1 (DUT) + * - Description (SRP-2.2): Automatically sends SRP Update Response. + * - Pass Criteria: + * - The DUT MUST send a valid SRP Update Response, with RCODE=0 (NoError). + */ + Log("Step 5: BR 1 (DUT) sends SRP Update Response (NoError)."); + + /** + * Step 6 + * - Device: ED 2 + * - Description (SRP-2.2): Harness instructs the device to send SRP Update with + * same-name (conflicting) service registration: $ORIGIN + * default.service.arpa. service-test-1._thread-test._udp ( SRV 0 0 33333 + * host-test-2 ) host-test-2 AAAA + * - Pass Criteria: + * - N/A + */ + Log("Step 6: ED 2 sends SRP Update with same-name (conflicting) service registration."); + { + ed2.Get().EnableAutoStartMode(nullptr, nullptr); + SuccessOrQuit(ed2.Get().SetHostName(kSrpHost2)); + + SuccessOrQuit(ed2.Get().EnableAutoHostAddress()); + + ClearAllBytes(service2); + service2.mName = kSrpServiceType; + service2.mInstanceName = kSrpInstance1; // Conflict + service2.mPort = kSrpServicePort333; + SuccessOrQuit(ed2.Get().AddService(service2)); + } + nexus.AdvanceTime(kSrpRegistrationTime); + + /** + * Step 7 + * - Device: BR 1 (DUT) + * - Description (SRP-2.2): Automatically sends SRP Update Response. + * - Pass Criteria: + * - The DUT MUST send a valid SRP Update Response, with RCODE=6 (YXDOMAIN). + */ + Log("Step 7: BR 1 (DUT) sends SRP Update Response (YXDOMAIN)."); + + /** + * Step 8 + * - Device: Eth 1 + * - Description (SRP-2.2): Harness instructs device to perform mDNS query QType + * PTR for any services of type _thread-test._udp.local.. + * - Pass Criteria: + * - N/A + */ + Log("Step 8: Eth 1 performs mDNS query QType PTR."); + { + Dns::Multicast::Core::Browser browser; + ClearAllBytes(browser); + browser.mCallback = [](otInstance *, const otPlatDnssdBrowseResult *) {}; + browser.mServiceType = kSrpServiceType; + browser.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartBrowser(browser)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopBrowser(browser)); + } + + /** + * Step 9 + * - Device: BR 1 (DUT) + * - Description (SRP-2.2): Automatically sends mDNS response with initial + * registered service. + * - Pass Criteria: + * - The DUT MUST send a valid mDNS Response and contain answer record: + * _thread-test._udp.local PTR ( service-test-1._thread-test._udp.local. ) + * - It MAY also contain in the Additional records section: + * service-test-1._thread-test._udp.local. ( SRV 0 0 33333 host-test-1.local. ) + * - It MAY also contain in the Additional records section: host-test-1.local. + * AAAA + * - The mDNS Response MUST NOT contain any answer record or additional + * record like: AAAA + * - The mDNS Response MUST NOT contain any answer record or additional + * record like: host-test-1.local. AAAA + * - The mDNS Response MUST NOT contain any answer record or additional + * record like: service-test-2._thread-test._udp.local. SRV + * - The mDNS Response MUST NOT contain any answer record or additional + * record like: PTR service-test-2._thread-test_udp.local. + */ + Log("Step 9: BR 1 (DUT) sends mDNS response with initial registered service."); + + /** + * Step 9b + * - Device: Eth 1 + * - Description (SRP-2.2): Harness instructs the device to send mDNS query + * QTYPE SRV QNAME service-test-1._thread-test._udp.local + * - Pass Criteria: + * - The DUT MUST respond RCODE=0 with Answer section record: + * service-test-1._thread-test._udp.local ( SRV 0 0 33333 host-test-1.local ) + */ + Log("Step 9b: Eth 1 sends mDNS query QTYPE SRV."); + { + Dns::Multicast::Core::SrvResolver resolver; + ClearAllBytes(resolver); + resolver.mCallback = [](otInstance *, const otPlatDnssdSrvResult *) {}; + resolver.mServiceInstance = kSrpInstance1; + resolver.mServiceType = kSrpServiceType; + resolver.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartSrvResolver(resolver)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopSrvResolver(resolver)); + } + + /** + * Step 9c + * - Device: Eth 1 + * - Description (SRP-2.2): Harness instructs the device to send mDNS query + * QTYPE AAAA QNAME host-test-1.local + * - Pass Criteria: + * - The DUT MUST respond RCODE=0 with Answer section record: + * host-test-1.local AAAA + */ + Log("Step 9c: Eth 1 sends mDNS query QTYPE AAAA."); + { + Dns::Multicast::Core::AddressResolver resolver; + ClearAllBytes(resolver); + resolver.mCallback = [](otInstance *, const otPlatDnssdAddressResult *) {}; + resolver.mHostName = kSrpHost1; + resolver.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartIp6AddressResolver(resolver)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopIp6AddressResolver(resolver)); + } + + /** + * Step 10 + * - Device: ED 2 + * - Description (SRP-2.2): Harness instructs the device to send SRP Update with + * same-hostname (conflicting) service registration: $ORIGIN + * default.service.arpa. service-test-2._thread-test._udp ( SRV 0 0 33333 + * host-test-1 ) host-test-1 AAAA + * - Pass Criteria: + * - N/A + */ + Log("Step 10: ED 2 sends SRP Update with same-hostname (conflicting) service registration."); + { + ed2.Get().ClearHostAndServices(); + SuccessOrQuit(ed2.Get().SetHostName(kSrpHost1)); // Conflict + + SuccessOrQuit(ed2.Get().EnableAutoHostAddress()); + + ClearAllBytes(service2); + service2.mName = kSrpServiceType; + service2.mInstanceName = kSrpInstance2; + service2.mPort = kSrpServicePort333; + service2.mTxtEntries = nullptr; + service2.mNumTxtEntries = 0; + SuccessOrQuit(ed2.Get().AddService(service2)); + } + nexus.AdvanceTime(kSrpRegistrationTime); + + /** + * Step 11 + * - Device: BR 1 (DUT) + * - Description (SRP-2.2): Automatically sends SRP Update Response. + * - Pass Criteria: + * - The DUT MUST send a valid SRP Update Response, with RCODE=6 (YXDOMAIN). + */ + Log("Step 11: BR 1 (DUT) sends SRP Update Response (YXDOMAIN)."); + + /** + * Step 12 + * - Device: Eth 1 + * - Description (SRP-2.2): Repeat Step 8 + * - Pass Criteria: + * - Repeat Step 8 + */ + Log("Step 12: Eth 1 repeats mDNS query QType PTR."); + { + Dns::Multicast::Core::Browser browser; + ClearAllBytes(browser); + browser.mCallback = [](otInstance *, const otPlatDnssdBrowseResult *) {}; + browser.mServiceType = kSrpServiceType; + browser.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartBrowser(browser)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopBrowser(browser)); + } + + /** + * Step 13 + * - Device: BR 1 (DUT) + * - Description (SRP-2.2): Repeat Step 9 (including 9b, 9c, etc) + * - Pass Criteria: + * - Repeat Step 9 (including 9b, 9c, etc) + */ + Log("Step 13: BR 1 (DUT) repeats mDNS response verification."); + { + Dns::Multicast::Core::SrvResolver resolver; + ClearAllBytes(resolver); + resolver.mCallback = [](otInstance *, const otPlatDnssdSrvResult *) {}; + resolver.mServiceInstance = kSrpInstance1; + resolver.mServiceType = kSrpServiceType; + resolver.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartSrvResolver(resolver)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopSrvResolver(resolver)); + } + { + Dns::Multicast::Core::AddressResolver resolver; + ClearAllBytes(resolver); + resolver.mCallback = [](otInstance *, const otPlatDnssdAddressResult *) {}; + resolver.mHostName = kSrpHost1; + resolver.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartIp6AddressResolver(resolver)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopIp6AddressResolver(resolver)); + } + + /** + * Step 14 + * - Device: ED 2 + * - Description (SRP-2.2): Harness instructs the device to send SRP Update with + * non-conflicting service registration: $ORIGIN default.service.arpa. + * service-test-2._thread-test._udp ( SRV 1 0 44444 host-test-2 ) host-test-2 + * AAAA + * - Pass Criteria: + * - N/A + */ + Log("Step 14: ED 2 sends SRP Update with non-conflicting service registration."); + { + ed2.Get().ClearHostAndServices(); + SuccessOrQuit(ed2.Get().SetHostName(kSrpHost2)); + + SuccessOrQuit(ed2.Get().EnableAutoHostAddress()); + + ClearAllBytes(service2); + service2.mName = kSrpServiceType; + service2.mInstanceName = kSrpInstance2; + service2.mPort = kSrpServicePort444; + service2.mPriority = 1; + service2.mTxtEntries = nullptr; + service2.mNumTxtEntries = 0; + SuccessOrQuit(ed2.Get().AddService(service2)); + } + nexus.AdvanceTime(kSrpRegistrationTime); + + /** + * Step 15 + * - Device: BR_1 (DUT) + * - Description (SRP-2.2): Automatically sends SRP Update Response. + * - Pass Criteria: + * - The DUT MUST send a valid SRP Update Response, with RCODE=0 (NoError). + */ + Log("Step 15: BR 1 (DUT) sends SRP Update Response (NoError)."); + + /** + * Step 16 + * - Device: Eth 1 + * - Description (SRP-2.2): Harness instructs the device to perform mDNS query + * QType PTR for any services of type _thread-test_udp.local.. + * - Pass Criteria: + * - N/A + */ + Log("Step 16: Eth 1 performs mDNS query QType PTR."); + { + Dns::Multicast::Core::Browser browser; + ClearAllBytes(browser); + browser.mCallback = [](otInstance *, const otPlatDnssdBrowseResult *) {}; + browser.mServiceType = kSrpServiceType; + browser.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartBrowser(browser)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopBrowser(browser)); + } + + /** + * Step 17 + * - Device: BR 1 (DUT) + * - Description (SRP-2.2): Automatically sends mDNS response with both + * registered services. + * - Pass Criteria: + * - The DUT MUST send a valid mDNS Response and MUST contain 2 answer + * records: _thread-test._udp.local ( PTR service-test-1._thread-test._udp.local ) + * AND _thread-test._udp.local ( PTR service-test-2._thread-test._udp.local ) + * - It MAY also contain in the Additional records section: + * service-test-1._thread-test._udp.local. ( SRV 0 0 33333 host-test-1.local. ) + * - It MAY also contain in the Additional records section: + * service-test-2._thread-test._udp.local. ( SRV 1 0 44444 host-test-2.local. ) + * - It MAY also contain in the Additional records section: host-test-1.local. + * AAAA + * - It MAY also contain in the Additional records section: host-test-2.local. + * AAAA + * - The response MAY be sent as two separate mDNS response messages, + * splitting over the 2 services. + */ + Log("Step 17: BR 1 (DUT) sends mDNS response with both registered services."); + + /** + * Step 17b + * - Device: Eth 1 + * - Description (SRP-2.2): Harness instructs the device to send mDNS query + * QTYPE=SRV QNAME service-test-1._thread-test._udp.local + * - Pass Criteria: + * - The DUT MUST respond RCODE=0 with Answer section record: + * service-test-1._thread-test._udp.local ( SRV 0 0 33333 host-test-1.local ) + */ + Log("Step 17b: Eth 1 sends mDNS query QTYPE=SRV QNAME service-test-1."); + { + Dns::Multicast::Core::SrvResolver resolver; + ClearAllBytes(resolver); + resolver.mCallback = [](otInstance *, const otPlatDnssdSrvResult *) {}; + resolver.mServiceInstance = kSrpInstance1; + resolver.mServiceType = kSrpServiceType; + resolver.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartSrvResolver(resolver)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopSrvResolver(resolver)); + } + + /** + * Step 17c + * - Device: Eth_1 + * - Description (SRP-2.2): Harness instructs the device to send mDNS query + * QTYPE SRV QNAME service-test-2._thread-test_udp.local + * - Pass Criteria: + * - The DUT MUST respond RCODE=0 with Answer section record: + * service-test-2._thread-test._udp.local ( SRV 1 0 44444 host-test-2.local ) + */ + Log("Step 17c: Eth 1 sends mDNS query QTYPE=SRV QNAME service-test-2."); + { + Dns::Multicast::Core::SrvResolver resolver; + ClearAllBytes(resolver); + resolver.mCallback = [](otInstance *, const otPlatDnssdSrvResult *) {}; + resolver.mServiceInstance = kSrpInstance2; + resolver.mServiceType = kSrpServiceType; + resolver.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartSrvResolver(resolver)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopSrvResolver(resolver)); + } + + /** + * Step 17d + * - Device: Eth 1 + * - Description (SRP-2.2): Harness instructs the device to send mDNS query + * QTYPE=AAAA QNAME host-test-1.local + * - Pass Criteria: + * - The DUT MUST respond RCODE=0 with Answer section record: + * host-test-1.local AAAA + */ + Log("Step 17d: Eth 1 sends mDNS query QTYPE=AAAA QNAME host-test-1."); + { + Dns::Multicast::Core::AddressResolver resolver; + ClearAllBytes(resolver); + resolver.mCallback = [](otInstance *, const otPlatDnssdAddressResult *) {}; + resolver.mHostName = kSrpHost1; + resolver.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartIp6AddressResolver(resolver)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopIp6AddressResolver(resolver)); + } + + /** + * Step 17e + * - Device: Eth_1 + * - Description (SRP-2.2): Harness instructs the device to send mDNS query + * QTYPE AAAA QNAME host-test-2.local + * - Pass Criteria: + * - THE DUT MUST respond RCODE=0 with Answer section record: + * host-test-2.local AAAA + */ + Log("Step 17e: Eth 1 sends mDNS query QTYPE=AAAA QNAME host-test-2."); + { + Dns::Multicast::Core::AddressResolver resolver; + ClearAllBytes(resolver); + resolver.mCallback = [](otInstance *, const otPlatDnssdAddressResult *) {}; + resolver.mHostName = kSrpHost2; + resolver.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartIp6AddressResolver(resolver)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopIp6AddressResolver(resolver)); + } + + Log("All steps completed."); + + nexus.AddTestVar("BR_1_MLEID_ADDR", br1.Get().GetMeshLocalEid().ToString().AsCString()); + nexus.AddTestVar("ED_1_MLEID_ADDR", ed1.Get().GetMeshLocalEid().ToString().AsCString()); + nexus.AddTestVar("ED_2_MLEID_ADDR", ed2.Get().GetMeshLocalEid().ToString().AsCString()); + + { + String<10> srpPortString; + srpPortString.Append("%u", br1.Get().GetPort()); + nexus.AddTestVar("BR_1_SRP_PORT", srpPortString.AsCString()); + } + + Ip6::Prefix omrPrefix; + SuccessOrQuit(br1.Get().GetOmrPrefix(omrPrefix)); + nexus.AddTestVar("ED_1_OMR_ADDR", ed1.FindMatchingAddress(omrPrefix.ToString().AsCString()).ToString().AsCString()); + nexus.AddTestVar("ED_2_OMR_ADDR", ed2.FindMatchingAddress(omrPrefix.ToString().AsCString()).ToString().AsCString()); + + nexus.SaveTestInfo(aJsonFileName); +} + +} // namespace Nexus +} // namespace ot + +int main(int argc, char *argv[]) +{ + ot::Nexus::Test_1_3_SRP_TC_2((argc > 2) ? argv[2] : "test_1_3_SRP_TC_2.json"); + + printf("All tests passed\n"); + return 0; +} diff --git a/tests/nexus/verify_1_3_SRP_TC_2.py b/tests/nexus/verify_1_3_SRP_TC_2.py new file mode 100644 index 000000000..c4e9438e7 --- /dev/null +++ b/tests/nexus/verify_1_3_SRP_TC_2.py @@ -0,0 +1,487 @@ +#!/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 + + +def verify(pv): + # 2.2. [1.3] [CERT] Name Conflicts - Single Thread Network + # + # 2.2.1. Purpose + # To test the following: + # - 1. Handle name conflict in Host Description record. + # - 2. Handle name conflict in Service Description record. + # - 3. Verify that original service is seen when discovering, and conflicting + # service is not seen on the AIL. + # + # 2.2.2. Topology + # - 1. BR_1 (DUT) - Border Router and the Leader. + # - 2. ED 1-Test Bed device operating as a Thread End Device, attached to BR_1 + # - 3. ED 2-Test Bed device operating as a Thread End Device, attached to BR_1 + # - 4. Eth 1-Test Bed border router device on an Adjacent Infrastructure Link + # + # 2.2.3. Initial Conditions + # - BR_1 (DUT) should automatically start SRP server capabilities at the + # beginning of the test. + pkts = pv.pkts + pv.summary.show() + + BR_1_MLEID = Ipv6Addr(pv.vars['BR_1_MLEID_ADDR']) + ED_1_MLEID = Ipv6Addr(pv.vars['ED_1_MLEID_ADDR']) + ED_2_MLEID = Ipv6Addr(pv.vars['ED_2_MLEID_ADDR']) + ED_1_OMR = Ipv6Addr(pv.vars['ED_1_OMR_ADDR']) + ED_2_OMR = Ipv6Addr(pv.vars['ED_2_OMR_ADDR']) + br1_srp_port = int(pv.vars['BR_1_SRP_PORT']) + + SRP_SERVICE_NAME = '_thread-test._udp.default.service.arpa' + SRP_INSTANCE_1 = 'service-test-1.' + SRP_SERVICE_NAME + SRP_INSTANCE_2 = 'service-test-2.' + SRP_SERVICE_NAME + SRP_HOST_1 = 'host-test-1.default.service.arpa' + SRP_HOST_2 = 'host-test-2.default.service.arpa' + MDNS_SERVICE_NAME = '_thread-test._udp.local' + MDNS_INSTANCE_1 = 'service-test-1.' + MDNS_SERVICE_NAME + MDNS_INSTANCE_2 = 'service-test-2.' + MDNS_SERVICE_NAME + MDNS_HOST_1 = 'host-test-1.local' + MDNS_HOST_2 = 'host-test-2.local' + + SRP_PORT_33333 = 33333 + SRP_PORT_44444 = 44444 + + SERVICE_ID_SRP_SERVER = 93 + + # Step 1 + # - Device: BR 1 (DUT) + # - Description (SRP-2.2): Enable: switch on. + # - Pass Criteria: + # - N/A + print("Step 1: BR 1 (DUT) switch on.") + + # Step 2 + # - Device: ED 1, ED 2, Eth 1 + # - Description (SRP-2.2): Enable + # - Pass Criteria: + # - N/A + print("Step 2: ED 1, ED 2, Eth 1 Enable.") + + # Step 3 + # - Device: BR 1 (DUT) + # - Description (SRP-2.2): Automatically adds its SRP Server information in the + # Thread Network Data. + # - Pass Criteria: + # - The DUT's SRP Server information MUST appear in the Thread Network Data. + # This can be verified as specified in test case 2.1 step 3. + # - Specifically, it MUST include a DNS-SD Unicast Dataset. + print("Step 3: BR 1 (DUT) adds its SRP Server information in the Thread Network Data.") + pkts. \ + filter_mle_cmd(consts.MLE_DATA_RESPONSE). \ + filter(lambda p: SERVICE_ID_SRP_SERVER in verify_utils.as_list(p.thread_nwd.tlv.service.srp_dataset_identifier)). \ + filter(lambda p: any(l in (1, 19) for l in verify_utils.as_list(p.thread_nwd.tlv.service.s_data_len))). \ + must_next() + + # Step 4 + # - Device: ED 1 + # - Description (SRP-2.2): Harness instructs the device to send SRP Update with + # initial service registration: $ORIGIN default.service.arpa. + # service-test-1._thread-test._udp ( SRV 0 0 33333 host-test-1 ) host-test-1 + # AAAA + # - Pass Criteria: + # - N/A + print("Step 4: ED 1 sends SRP Update with initial service registration.") + pkts. \ + filter_ipv6_dst(BR_1_MLEID). \ + filter_ipv6_src(ED_1_MLEID). \ + filter(lambda p: p.udp.dstport == br1_srp_port). \ + filter(lambda p: p.dns.flags.opcode == 5). \ + filter(lambda p: SRP_INSTANCE_1 in verify_utils.as_list(p.dns.resp.name)). \ + filter(lambda p: SRP_PORT_33333 in verify_utils.as_list(p.dns.srv.port)). \ + filter(lambda p: SRP_HOST_1 in verify_utils.as_list(p.dns.srv.target)). \ + filter(lambda p: ED_1_OMR in verify_utils.as_list(p.dns.aaaa)). \ + must_next() + + # Step 5 + # - Device: BR 1 (DUT) + # - Description (SRP-2.2): Automatically sends SRP Update Response. + # - Pass Criteria: + # - The DUT MUST send a valid SRP Update Response, with RCODE=0 (NoError). + print("Step 5: BR 1 (DUT) sends SRP Update Response (NoError).") + pkts. \ + filter_ipv6_dst(ED_1_MLEID). \ + filter_ipv6_src(BR_1_MLEID). \ + filter(lambda p: p.udp.srcport == br1_srp_port). \ + filter(lambda p: p.dns.flags.response == 1). \ + filter(lambda p: p.dns.flags.rcode == 0). \ + must_next() + + # Step 6 + # - Device: ED 2 + # - Description (SRP-2.2): Harness instructs the device to send SRP Update with + # same-name (conflicting) service registration: $ORIGIN + # default.service.arpa. service-test-1._thread-test._udp ( SRV 0 0 33333 + # host-test-2 ) host-test-2 AAAA + # - Pass Criteria: + # - N/A + print("Step 6: ED 2 sends SRP Update with same-name (conflicting) service registration.") + pkts. \ + filter_ipv6_dst(BR_1_MLEID). \ + filter_ipv6_src(ED_2_MLEID). \ + filter(lambda p: p.udp.dstport == br1_srp_port). \ + filter(lambda p: p.dns.flags.opcode == 5). \ + filter(lambda p: SRP_INSTANCE_1 in verify_utils.as_list(p.dns.resp.name)). \ + filter(lambda p: SRP_PORT_33333 in verify_utils.as_list(p.dns.srv.port)). \ + filter(lambda p: SRP_HOST_2 in verify_utils.as_list(p.dns.srv.target)). \ + filter(lambda p: ED_2_OMR in verify_utils.as_list(p.dns.aaaa)). \ + must_next() + + # Step 7 + # - Device: BR 1 (DUT) + # - Description (SRP-2.2): Automatically sends SRP Update Response. + # - Pass Criteria: + # - The DUT MUST send a valid SRP Update Response, with RCODE=6 (YXDOMAIN). + print("Step 7: BR 1 (DUT) sends SRP Update Response (YXDOMAIN).") + pkts. \ + filter_ipv6_dst(ED_2_MLEID). \ + filter_ipv6_src(BR_1_MLEID). \ + filter(lambda p: p.udp.srcport == br1_srp_port). \ + filter(lambda p: p.dns.flags.response == 1). \ + filter(lambda p: p.dns.flags.rcode == 6). \ + must_next() + + # mDNS checks helper + def verify_no_ed2_info(p): + assert ED_2_OMR not in verify_utils.as_list(p.mdns.aaaa), f"Packet {p.index} contains ED_2_OMR" + assert MDNS_INSTANCE_2 not in verify_utils.as_list( + p.mdns.ptr.domain_name), f"Packet {p.index} contains MDNS_INSTANCE_2 in PTR" + assert MDNS_INSTANCE_2 not in verify_utils.as_list( + p.mdns.resp.name), f"Packet {p.index} contains MDNS_INSTANCE_2 in resp.name" + + # Step 8 era + index_step8 = pkts.index + + # Step 8 + # - Device: Eth 1 + # - Description (SRP-2.2): Harness instructs device to perform mDNS query QType + # PTR for any services of type _thread-test._udp.local.. + # - Pass Criteria: + # - N/A + print("Step 8: Eth 1 performs mDNS query QType PTR.") + + # Step 9 + # - Device: BR 1 (DUT) + # - Description (SRP-2.2): Automatically sends mDNS response with initial + # registered service. + # - Pass Criteria: + # - The DUT MUST send a valid mDNS Response and contain answer record: + # _thread-test._udp.local PTR ( service-test-1._thread-test._udp.local. ) + # - It MAY also contain in the Additional records section: + # service-test-1._thread-test._udp.local. ( SRV 0 0 33333 host-test-1.local. ) + # - It MAY also contain in the Additional records section: host-test-1.local. + # AAAA + # - The mDNS Response MUST NOT contain any answer record or additional + # record like: AAAA + # - The mDNS Response MUST NOT contain any answer record or additional + # record like: host-test-1.local. AAAA + # - The mDNS Response MUST NOT contain any answer record or additional + # record like: service-test-2._thread-test._udp.local. SRV + # - The mDNS Response MUST NOT contain any answer record or additional + # record like: PTR service-test-2._thread-test_udp.local. + print("Step 9: BR 1 (DUT) sends mDNS response with initial registered service.") + with pkts.save_index(): + pkts.index = index_step8 + res = pkts.\ + filter(lambda p: p.udp.dstport == 5353). \ + filter(lambda p: p.mdns.flags.response == 1). \ + filter(lambda p: MDNS_INSTANCE_1 in verify_utils.as_list(p.mdns.ptr.domain_name)). \ + must_next() + verify_no_ed2_info(res) + + # Step 9b + # - Device: Eth 1 + # - Description (SRP-2.2): Harness instructs the device to send mDNS query + # QTYPE SRV QNAME service-test-1._thread-test._udp.local + # - Pass Criteria: + # - The DUT MUST respond RCODE=0 with Answer section record: + # service-test-1._thread-test._udp.local ( SRV 0 0 33333 host-test-1.local ) + print("Step 9b: Eth 1 sends mDNS query QTYPE SRV.") + with pkts.save_index(): + pkts.index = index_step8 + pkts.\ + filter(lambda p: p.udp.dstport == 5353). \ + filter(lambda p: p.mdns.flags.response == 1). \ + filter(lambda p: MDNS_INSTANCE_1 in verify_utils.as_list(p.mdns.resp.name)). \ + filter(lambda p: SRP_PORT_33333 in verify_utils.as_list(p.mdns.srv.port)). \ + must_next() + + # Step 9c + # - Device: Eth 1 + # - Description (SRP-2.2): Harness instructs the device to send mDNS query + # QTYPE AAAA QNAME host-test-1.local + # - Pass Criteria: + # - The DUT MUST respond RCODE=0 with Answer section record: + # host-test-1.local AAAA + print("Step 9c: Eth 1 sends mDNS query QTYPE AAAA.") + with pkts.save_index(): + pkts.index = index_step8 + pkts.\ + filter(lambda p: p.udp.dstport == 5353). \ + filter(lambda p: p.mdns.flags.response == 1). \ + filter(lambda p: MDNS_HOST_1 in verify_utils.as_list(p.mdns.resp.name)). \ + filter(lambda p: ED_1_OMR in verify_utils.as_list(p.mdns.aaaa)). \ + must_next() + + # Step 10 + # - Device: ED 2 + # - Description (SRP-2.2): Harness instructs the device to send SRP Update with + # same-hostname (conflicting) service registration: $ORIGIN + # default.service.arpa. service-test-2._thread-test._udp ( SRV 0 0 33333 + # host-test-1 ) host-test-1 AAAA + # - Pass Criteria: + # - N/A + print("Step 10: ED 2 sends SRP Update with same-hostname (conflicting) service registration.") + pkts. \ + filter_ipv6_dst(BR_1_MLEID). \ + filter_ipv6_src(ED_2_MLEID). \ + filter(lambda p: p.udp.dstport == br1_srp_port). \ + filter(lambda p: p.dns.flags.opcode == 5). \ + filter(lambda p: SRP_INSTANCE_2 in verify_utils.as_list(p.dns.resp.name)). \ + filter(lambda p: SRP_PORT_33333 in verify_utils.as_list(p.dns.srv.port)). \ + filter(lambda p: SRP_HOST_1 in verify_utils.as_list(p.dns.srv.target)). \ + filter(lambda p: ED_2_OMR in verify_utils.as_list(p.dns.aaaa)). \ + must_next() + + # Step 11 + # - Device: BR 1 (DUT) + # - Description (SRP-2.2): Automatically sends SRP Update Response. + # - Pass Criteria: + # - The DUT MUST send a valid SRP Update Response, with RCODE=6 (YXDOMAIN). + print("Step 11: BR 1 (DUT) sends SRP Update Response (YXDOMAIN).") + pkts. \ + filter_ipv6_dst(ED_2_MLEID). \ + filter_ipv6_src(BR_1_MLEID). \ + filter(lambda p: p.udp.srcport == br1_srp_port). \ + filter(lambda p: p.dns.flags.response == 1). \ + filter(lambda p: p.dns.flags.rcode == 6). \ + must_next() + + # Step 12 + # - Device: Eth 1 + # - Description (SRP-2.2): Repeat Step 8 + # - Pass Criteria: + # - Repeat Step 8 + print("Step 12: Eth 1 repeats mDNS query QType PTR.") + + # Step 13 + # - Device: BR 1 (DUT) + # - Description (SRP-2.2): Repeat Step 9 (including 9b, 9c, etc) + # - Pass Criteria: + # - Repeat Step 9 (including 9b, 9c, etc) + print("Step 13: BR 1 (DUT) repeats mDNS response verification.") + with pkts.save_index(): + pkts.index = index_step8 + res = pkts.\ + filter(lambda p: p.udp.dstport == 5353). \ + filter(lambda p: p.mdns.flags.response == 1). \ + filter(lambda p: MDNS_INSTANCE_1 in verify_utils.as_list(p.mdns.ptr.domain_name)). \ + must_next() + assert ED_1_OMR in verify_utils.as_list(res.mdns.aaaa) + verify_no_ed2_info(res) + + with pkts.save_index(): + pkts.index = index_step8 + pkts.\ + filter(lambda p: p.udp.dstport == 5353). \ + filter(lambda p: p.mdns.flags.response == 1). \ + filter(lambda p: MDNS_INSTANCE_1 in verify_utils.as_list(p.mdns.resp.name)). \ + filter(lambda p: SRP_PORT_33333 in verify_utils.as_list(p.mdns.srv.port)). \ + must_next() + + with pkts.save_index(): + pkts.index = index_step8 + pkts.\ + filter(lambda p: p.udp.dstport == 5353). \ + filter(lambda p: p.mdns.flags.response == 1). \ + filter(lambda p: MDNS_HOST_1 in verify_utils.as_list(p.mdns.resp.name)). \ + filter(lambda p: ED_1_OMR in verify_utils.as_list(p.mdns.aaaa)). \ + must_next() + + # Step 14 + # - Device: ED 2 + # - Description (SRP-2.2): Harness instructs the device to send SRP Update with + # non-conflicting service registration: $ORIGIN default.service.arpa. + # service-test-2._thread-test._udp ( SRV 1 0 44444 host-test-2 ) host-test-2 + # AAAA + # - Pass Criteria: + # - N/A + print("Step 14: ED 2 sends SRP Update with non-conflicting service registration.") + pkts. \ + filter_ipv6_dst(BR_1_MLEID). \ + filter_ipv6_src(ED_2_MLEID). \ + filter(lambda p: p.udp.dstport == br1_srp_port). \ + filter(lambda p: p.dns.flags.opcode == 5). \ + filter(lambda p: SRP_INSTANCE_2 in verify_utils.as_list(p.dns.resp.name)). \ + filter(lambda p: SRP_PORT_44444 in verify_utils.as_list(p.dns.srv.port)). \ + filter(lambda p: SRP_HOST_2 in verify_utils.as_list(p.dns.srv.target)). \ + filter(lambda p: ED_2_OMR in verify_utils.as_list(p.dns.aaaa)). \ + must_next() + + # Step 15 + # - Device: BR_1 (DUT) + # - Description (SRP-2.2): Automatically sends SRP Update Response. + # - Pass Criteria: + # - The DUT MUST send a valid SRP Update Response, with RCODE=0 (NoError). + print("Step 15: BR 1 (DUT) sends SRP Update Response (NoError).") + pkts. \ + filter_ipv6_dst(ED_2_MLEID). \ + filter_ipv6_src(BR_1_MLEID). \ + filter(lambda p: p.udp.srcport == br1_srp_port). \ + filter(lambda p: p.dns.flags.response == 1). \ + filter(lambda p: p.dns.flags.rcode == 0). \ + must_next() + + # Step 16 + # - Device: Eth 1 + # - Description (SRP-2.2): Harness instructs the device to perform mDNS query + # QType PTR for any services of type _thread-test_udp.local.. + # - Pass Criteria: + # - N/A + print("Step 16: Eth 1 performs mDNS query QType PTR.") + + # Step 17 + # - Device: BR 1 (DUT) + # - Description (SRP-2.2): Automatically sends mDNS response with both + # registered services. + # - Pass Criteria: + # - The DUT MUST send a valid mDNS Response and MUST contain 2 answer + # records: _thread-test._udp.local ( PTR service-test-1._thread-test._udp.local ) + # AND _thread-test._udp.local ( PTR service-test-2._thread-test._udp.local ) + # - It MAY also contain in the Additional records section: + # service-test-1._thread-test._udp.local. ( SRV 0 0 33333 host-test-1.local. ) + # - It MAY also contain in the Additional records section: + # service-test-2._thread-test._udp.local. ( SRV 1 0 44444 host-test-2.local. ) + # - It MAY also contain in the Additional records section: host-test-1.local. + # AAAA + # - It MAY also contain in the Additional records section: host-test-2.local. + # AAAA + # - The response MAY be sent as two separate mDNS response messages, + # splitting over the 2 services. + print("Step 17: BR 1 (DUT) sends mDNS response with both registered services.") + with pkts.save_index(): + pkts.index = index_step8 + pkts.\ + filter(lambda p: p.udp.dstport == 5353). \ + filter(lambda p: p.mdns.flags.response == 1). \ + filter(lambda p: MDNS_INSTANCE_1 in verify_utils.as_list(p.mdns.ptr.domain_name)). \ + must_next() + with pkts.save_index(): + pkts.index = index_step8 + pkts.\ + filter(lambda p: p.udp.dstport == 5353). \ + filter(lambda p: p.mdns.flags.response == 1). \ + filter(lambda p: MDNS_INSTANCE_2 in verify_utils.as_list(p.mdns.ptr.domain_name)). \ + must_next() + + # Step 17b + # - Device: Eth 1 + # - Description (SRP-2.2): Harness instructs the device to send mDNS query + # QTYPE=SRV QNAME service-test-1._thread-test._udp.local + # - Pass Criteria: + # - The DUT MUST respond RCODE=0 with Answer section record: + # service-test-1._thread-test._udp.local ( SRV 0 0 33333 host-test-1.local ) + print("Step 17b: Eth 1 sends mDNS query QTYPE SRV instance 1.") + with pkts.save_index(): + pkts.index = index_step8 + pkts.\ + filter(lambda p: p.udp.dstport == 5353). \ + filter(lambda p: p.mdns.flags.response == 1). \ + filter(lambda p: MDNS_INSTANCE_1 in verify_utils.as_list(p.mdns.resp.name)). \ + filter(lambda p: SRP_PORT_33333 in verify_utils.as_list(p.mdns.srv.port)). \ + must_next() + + # Step 17c + # - Device: Eth_1 + # - Description (SRP-2.2): Harness instructs the device to send mDNS query + # QTYPE SRV QNAME service-test-2._thread-test_udp.local + # - Pass Criteria: + # - The DUT MUST respond RCODE=0 with Answer section record: + # service-test-2._thread-test._udp.local ( SRV 1 0 44444 host-test-2.local ) + print("Step 17c: Eth 1 sends mDNS query QTYPE SRV instance 2.") + with pkts.save_index(): + pkts.index = index_step8 + pkts.\ + filter(lambda p: p.udp.dstport == 5353). \ + filter(lambda p: p.mdns.flags.response == 1). \ + filter(lambda p: MDNS_INSTANCE_2 in verify_utils.as_list(p.mdns.resp.name)). \ + filter(lambda p: SRP_PORT_44444 in verify_utils.as_list(p.mdns.srv.port)). \ + must_next() + + # Step 17d + # - Device: Eth 1 + # - Description (SRP-2.2): Harness instructs the device to send mDNS query + # QTYPE=AAAA QNAME host-test-1.local + # - Pass Criteria: + # - The DUT MUST respond RCODE=0 with Answer section record: + # host-test-1.local AAAA + print("Step 17d: Eth 1 sends mDNS query QTYPE AAAA host 1.") + with pkts.save_index(): + pkts.index = index_step8 + pkts.\ + filter(lambda p: p.udp.dstport == 5353). \ + filter(lambda p: p.mdns.flags.response == 1). \ + filter(lambda p: MDNS_HOST_1 in verify_utils.as_list(p.mdns.resp.name)). \ + filter(lambda p: ED_1_OMR in verify_utils.as_list(p.mdns.aaaa)). \ + must_next() + + # Step 17e + # - Device: Eth_1 + # - Description (SRP-2.2): Harness instructs the device to send mDNS query + # QTYPE AAAA QNAME host-test-2.local + # - Pass Criteria: + # - THE DUT MUST respond RCODE=0 with Answer section record: + # host-test-2.local AAAA + print("Step 17e: Eth 1 sends mDNS query QTYPE AAAA host 2.") + with pkts.save_index(): + pkts.index = index_step8 + pkts.\ + filter(lambda p: p.udp.dstport == 5353). \ + filter(lambda p: p.mdns.flags.response == 1). \ + filter(lambda p: MDNS_HOST_2 in verify_utils.as_list(p.mdns.resp.name)). \ + filter(lambda p: ED_2_OMR in verify_utils.as_list(p.mdns.aaaa)). \ + must_next() + + +if __name__ == '__main__': + verify_utils.run_main(verify)