diff --git a/tests/nexus/CMakeLists.txt b/tests/nexus/CMakeLists.txt index 93ffca220..6c24faaa9 100644 --- a/tests/nexus/CMakeLists.txt +++ b/tests/nexus/CMakeLists.txt @@ -274,6 +274,7 @@ ot_nexus_test(1_3_SRP_TC_8 "cert;nexus") ot_nexus_test(1_3_SRP_TC_11 "cert;nexus") ot_nexus_test(1_3_SRP_TC_12 "cert;nexus") ot_nexus_test(1_3_SRP_TC_13 "cert;nexus") +ot_nexus_test(1_3_SRP_TC_15 "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 73ad43b4c..6e90d16b5 100755 --- a/tests/nexus/run_nexus_tests.sh +++ b/tests/nexus/run_nexus_tests.sh @@ -209,6 +209,7 @@ DEFAULT_TESTS=( "1_3_SRP_TC_11" "1_3_SRP_TC_12" "1_3_SRP_TC_13" + "1_3_SRP_TC_15" ) # Use provided arguments or the default test list diff --git a/tests/nexus/test_1_3_SRP_TC_15.cpp b/tests/nexus/test_1_3_SRP_TC_15.cpp new file mode 100644 index 000000000..fa5b41d76 --- /dev/null +++ b/tests/nexus/test_1_3_SRP_TC_15.cpp @@ -0,0 +1,646 @@ +/* + * 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[] = "_test-type._tcp"; +static const char kSrpInstanceName[] = "service-test-1"; +static const char kSrpHostName[] = "host-test-1"; +static const char kSrpFullServiceType[] = "_test-type._tcp.default.service.arpa."; +static const char kSrpSubtype1[] = "_test-subtype"; +static const char kSrpSubtype2[] = "_other-subtype"; +static const char kSrpSubtypeInvalid[] = "_test-subtypee"; +static constexpr uint16_t kSrpServicePort = 55555; + +static const char *const kSubTypes1[] = {kSrpSubtype1, nullptr}; +static const char *const kSubTypes2[] = {kSrpSubtype1, kSrpSubtype2, nullptr}; +static const char *const kSubTypes3[] = {kSrpSubtype2, nullptr}; + +void Test_1_3_SRP_TC_15(const char *aJsonFileName) +{ + Srp::Client::Service service; + + /** + * 2.15. [1.3] [CERT] Validation of SRP subtypes + * + * 2.15.1 Purpose + * To test the following: + * - 1. Handle service that includes additional subtypes of the basic service type. + * - 2. Respond to the DNS queries for the basic service type. + * - 3. Respond to the DNS queries for the service subtype. + * - 4. Handle addition of new subtype to the service. + * - 5. Handle removal of a subtype from the service. + * - 6. Test service types using 'top' in part of name. + * + * 2.15.2 Topology + * - 3. BR 1 (DUT) - Thread Border Router, and the Leader + * - 4. ED 1-Test Bed device operating as a Thread End Device, attached to BR_1 + * - 5. Eth 1-Test Bed border router device on an Adjacent Infrastructure Link + * + * Spec Reference | V1.1 Section | V1.3.0 Section + * -----------------|--------------|--------------- + * SRP Server | N/A | 1.3 + */ + + Core nexus; + + Node &br1 = nexus.CreateNode(); + Node &ed1 = nexus.CreateNode(); + Node ð1 = nexus.CreateNode(); + + br1.SetName("BR_1"); + ed1.SetName("ED_1"); + eth1.SetName("Eth_1"); + + br1.Form(); + nexus.AdvanceTime(0); + + SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelNote)); + + Log("---------------------------------------------------------------------------------------"); + + /** + * Step 1 + * Device: BR 1 (DUT) + * Description (SRP-2.15): Enable + * Pass Criteria: + * - N/A + */ + Log("Step 1: BR 1 (DUT) Enable"); + 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: Eth 1, ED 1 + * Description (SRP-2.15): Enable + * Pass Criteria: + * - N/A + */ + Log("Step 2: Eth 1, ED 1 Enable"); + ed1.Join(br1, Node::kAsFed); + SuccessOrQuit(eth1.Get().SetEnabled(true, kInfraIfIndex)); + nexus.AdvanceTime(kJoinNetworkTime); + + // Wait for BR to advertise prefixes and ED to get an OMR address + nexus.AdvanceTime(kBrActionTime); + + /** + * Step 3 + * Device: BR 1 (DUT) + * Description (SRP-2.15): Automatically adds SRP Server information in the Thread Network Data. + * Pass Criteria: + * - The DUT's SRP Server information MUST appear in the Thread Network Data. + */ + Log("Step 3: BR 1 (DUT) adds SRP Server information in Network Data"); + nexus.AdvanceTime(kSrpServerInfoUpdateTime); + + /** + * Step 4 + * Device: ED 1 + * Description (SRP-2.15): Harness instructs the device to send SRP Update to the DUT that includes a subtype: + * $ORIGIN default.service.arpa. _test-subtype._sub._test-type._tcp ( PTR service-test-1._test-type._tcp) + * _test-type._tcp ( PTR service-test-1._test-type._tcp) service-test-1._test-type._tcp ( SRV 8 55555 + * host-test-1) host-test-1 AAAA + * Pass Criteria: + * - N/A + */ + Log("Step 4: ED 1 sends SRP Update with a subtype"); + { + ed1.Get().EnableAutoStartMode(nullptr, nullptr); + + SuccessOrQuit(ed1.Get().SetHostName(kSrpHostName)); + SuccessOrQuit(ed1.Get().EnableAutoHostAddress()); + + ClearAllBytes(service); + service.mName = kSrpServiceType; + service.mInstanceName = kSrpInstanceName; + service.mPort = kSrpServicePort; + service.mSubTypeLabels = kSubTypes1; + SuccessOrQuit(ed1.Get().AddService(service)); + } + nexus.AdvanceTime(kSrpRegistrationTime); + + /** + * Step 5 + * Device: BR 1 (DUT) + * Description (SRP-2.15): 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"); + + /** + * Step 6 + * Device: ED 1 + * Description (SRP-2.15): Harness instructs the device to unicast DNS query QType = PTR for + * "_test-type._tcp.default.service.arpa" service type to the DUT. + * Pass Criteria: + * - N/A + */ + Log("Step 6: ED 1 sends unicast DNS query QType PTR for service type"); + { + SuccessOrQuit(ed1.Get().Browse(kSrpFullServiceType, nullptr, nullptr)); + } + nexus.AdvanceTime(kDnsQueryTime); + + /** + * Step 7 + * Device: BR_1 (DUT) + * Description (SRP-2.15): Automatically sends DNS response with service-test-1. + * Pass Criteria: + * - The DUT MUST send a valid DNS Response and contain service-test-1 as follows in the Answer section: + * $ORIGIN default.service.arpa. _test-type._tcp ( PTR service-test-1. test-type._tcp) + * - It MAY also contain in the Additional records section: $ORIGIN default.service.arpa. + * service-test-1._test-type._tcp ( SRV 8 8 55555 host-test-1) + * - host-test-1 AAAA + */ + Log("Step 7: BR 1 (DUT) sends DNS response with service-test-1"); + + /** + * Step 8 + * Device: Eth 1 + * Description (SRP-2.15): Harness instructs the device to send mDNS query QType PTR for _test-type._tcp.local." + * service type. + * Pass Criteria: + * - N/A + */ + Log("Step 8: Eth 1 sends mDNS query QType PTR for service type"); + { + 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.15): Automatically sends mDNS Response with service-test-1. + * Pass Criteria: + * - The DUT MUST send a valid mDNS Response with RCODE=0 (NoError) and contain service-test-1 as follows in the + * Answer section: $ORIGIN local. _test-type._tcp ( PTR service-test-1. test-type._tcp) + * - It MAY also contain in the Additional records section: $ORIGIN local. Service-test-1._test-type. tcp ( SRV 8 + * 55555 host-test-1) + * - host-test-1 AAAA + */ + Log("Step 9: BR 1 (DUT) sends mDNS Response with service-test-1"); + + /** + * Step 10 + * Device: ED 1 + * Description (SRP-2.15): Harness instructs the device to unicast DNS query QType PTR for + * _test-subtype._sub._test-type._tcp.default.service.arpa" service type to the DUT. + * Pass Criteria: + * - N/A + */ + Log("Step 10: ED 1 sends unicast DNS query QType PTR for service subtype"); + { + char subServiceType[Dns::Name::kMaxNameSize]; + snprintf(subServiceType, sizeof(subServiceType), "%s._sub.%s", kSrpSubtype1, kSrpFullServiceType); + SuccessOrQuit(ed1.Get().Browse(subServiceType, nullptr, nullptr)); + } + nexus.AdvanceTime(kDnsQueryTime); + + /** + * Step 11 + * Device: BR_1 (DUT) + * Description (SRP-2.15): Automatically sends DNS response with service-test-1. + * Pass Criteria: + * - The DUT MUST send a valid DNS Response and contain service-test-1 as follows in the Answer records section: + * $ORIGIN default.service.arpa. _test-subtype._sub._test-type._tcp ( PTR service-test-1._test-type._tcp) + * - It MAY also contain in the Additional records section: $ORIGIN default.service.arpa. + * service-test-1._test-type. tcp ( SRV 55555 host-test-1) + * - host-test-1 AAAA + */ + Log("Step 11: BR 1 (DUT) sends DNS response with service-test-1"); + + /** + * Step 12 + * Device: Eth 1 + * Description (SRP-2.15): Harness instructs the device to mDNS query QType PTR for + * "_test-subtype._sub._test-type._tcp.local" service type. + * Pass Criteria: + * - N/A + */ + Log("Step 12: Eth 1 sends mDNS query QType PTR for service subtype"); + { + char subServiceType[Dns::Name::kMaxNameSize]; + snprintf(subServiceType, sizeof(subServiceType), "%s._sub.%s", kSrpSubtype1, kSrpServiceType); + + Dns::Multicast::Core::Browser browser; + ClearAllBytes(browser); + browser.mCallback = [](otInstance *, const otPlatDnssdBrowseResult *) {}; + browser.mServiceType = subServiceType; + 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.15): Automatically sends mDNS Response with service-test-1. + * Pass Criteria: + * - The DUT MUST send a valid mDNS Response with RCODE=0 (NoError) and contain service-test-1 as follows in the + * Answer records: $ORIGIN local. _test-subtype._sub._test-type._tcp ( PTR service-test-1._test-type._tcp) + * - It MAY also contain in the Additional records section: $ORIGIN local. Service-test-1. test-type._tcp ( SRV 8 + * 55555 host-test-1) + * - host-test-1 AAAA OMR address of ED_1> + */ + Log("Step 13: BR 1 (DUT) sends mDNS Response with service-test-1"); + + /** + * Step 14 + * Device: ED 1 + * Description (SRP-2.15): Harness instructs the device to send SRP Update to the DUT to add a new subtype to the + * existing service: $ORIGIN default.service.arpa. _other-subtype._sub._test-type._tcp ( PTR service-test-1. + * test-type._tcp) _test-subtype._sub._test-type._tcp ( PTR service-test-1._test-type._tcp) _test-type._tcp ( + * PTR service-test-1. test-type._tcp) service-test-1. test-type._tcp ( SRV 8 8 55555 host-test-1) host-test-1 AAAA + * OMR address of ED_1> + * Pass Criteria: + * - N/A + */ + Log("Step 14: ED 1 adds a new subtype to the existing service"); + { + SuccessOrQuit(ed1.Get().ClearService(service)); + service.mName = kSrpServiceType; + service.mInstanceName = kSrpInstanceName; + service.mPort = kSrpServicePort; + service.mSubTypeLabels = kSubTypes2; + SuccessOrQuit(ed1.Get().AddService(service)); + } + nexus.AdvanceTime(kSrpRegistrationTime); + + /** + * Step 15 + * Device: BR 1 (DUT) + * Description (SRP-2.15): 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"); + + /** + * Step 16 + * Device: Eth_1 + * Description (SRP-2.15): Harness instructs the device to mDNS query QType PTR for + * "_other-subtype._sub._test-type._tcp.local" service type, to check that the new subtype is present. + * Pass Criteria: + * - N/A + */ + Log("Step 16: Eth 1 sends mDNS query QType PTR for the new subtype"); + { + char subServiceType[Dns::Name::kMaxNameSize]; + snprintf(subServiceType, sizeof(subServiceType), "%s._sub.%s", kSrpSubtype2, kSrpServiceType); + + Dns::Multicast::Core::Browser browser; + ClearAllBytes(browser); + browser.mCallback = [](otInstance *, const otPlatDnssdBrowseResult *) {}; + browser.mServiceType = subServiceType; + 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.15): Automatically sends mDNS Response with service-test-1. + * Pass Criteria: + * - The DUT MUST send a valid mDNS Response with RCODE=0 (NoError) and contain service-test-1 as follows in the + * Answer records section: $ORIGIN local. _other-subtype._sub._test-type._tcp ( PTR + * service-test-1._test-type._tcp) + * - It MAY also contain in the Additional records section: $ORIGIN local. Service-test-1. test-type._tcp ( SRV + * 55555 host-test-1) + * - host-test-1 AAAA OMR address of ED_1> + */ + Log("Step 17: BR 1 (DUT) sends mDNS Response with service-test-1"); + + /** + * Step 18 + * Device: Eth_1 + * Description (SRP-2.15): Harness instructs the device to mDNS query QType=PTR for + * "_test-subtype._sub_test-type._tcp.local" service type, to check that the originally registered subtype is + * still present. + * Pass Criteria: + * - N/A + */ + Log("Step 18: Eth 1 sends mDNS query QType PTR for the original subtype"); + { + char subServiceType[Dns::Name::kMaxNameSize]; + snprintf(subServiceType, sizeof(subServiceType), "%s._sub.%s", kSrpSubtype1, kSrpServiceType); + + Dns::Multicast::Core::Browser browser; + ClearAllBytes(browser); + browser.mCallback = [](otInstance *, const otPlatDnssdBrowseResult *) {}; + browser.mServiceType = subServiceType; + browser.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartBrowser(browser)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopBrowser(browser)); + } + + /** + * Step 19 + * Device: BR 1 (DUT) + * Description (SRP-2.15): Automatically sends mDNS Response with service-test-1. + * Pass Criteria: + * - Repeat Step 13 + */ + Log("Step 19: BR 1 (DUT) sends mDNS Response (Repeat Step 13)"); + + /** + * Step 20 + * Device: Eth_1 + * Description (SRP-2.15): Harness instructs the device to send mDNS query QType PTR for _test-type._tcp.local." + * service type, to check that the query for the parent type also works after new subtype registration. + * Pass Criteria: + * - N/A + */ + Log("Step 20: Eth 1 sends mDNS query QType PTR for the parent service type"); + { + 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 21 + * Device: BR 1 (DUT) + * Description (SRP-2.15): Automatically sends mDNS Response with service-test-1. + * Pass Criteria: + * - Repeat Step 9 + */ + Log("Step 21: BR 1 (DUT) sends mDNS Response (Repeat Step 9)"); + + /** + * Step 22 + * Device: Eth 1 + * Description (SRP-2.15): Harness instructs the device to send mDNS query QType PTR for non-existent subtype + * (name with extra character added): "_test-subtypee._sub._test-type_tcp.local." service type, to check that no + * service answer is given by the DUT. + * Pass Criteria: + * - N/A + */ + Log("Step 22: Eth 1 sends mDNS query QType PTR for a non-existent subtype"); + { + char subServiceType[Dns::Name::kMaxNameSize]; + snprintf(subServiceType, sizeof(subServiceType), "%s._sub.%s", kSrpSubtypeInvalid, kSrpServiceType); + + Dns::Multicast::Core::Browser browser; + ClearAllBytes(browser); + browser.mCallback = [](otInstance *, const otPlatDnssdBrowseResult *) {}; + browser.mServiceType = subServiceType; + browser.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartBrowser(browser)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopBrowser(browser)); + } + + /** + * Step 23 + * Device: BR_1 (DUT) + * Description (SRP-2.15): Does not send an mDNS Response. + * Pass Criteria: + * - The DUT MUST NOT send an mDNS response. + */ + Log("Step 23: BR 1 (DUT) does not send an mDNS Response"); + + /** + * Step 24 + * Device: ED 1 + * Description (SRP-2.15): Harness instructs the device to send SRP Update to the DUT to re-register (i.e., + * update), its service with the subtype "_test-subtype" removed from the service: $ORIGIN + * default.service.arpa. _other-subtype._sub._test-type._tcp ( PTR service-test-1._test-type._tcp) + * _test-type._tcp ( PTR service-test-1._test-type._tcp) service-test-1._test-type._tcp ( SRV 55555 host-test-1) + * host-test-1 AAAA OMR address of ED_1> Note: such an update would automatically delete any other, + * pre-existing subtypes on the SRP server for this service. + * Pass Criteria: + * - N/A + */ + Log("Step 24: ED 1 removes a subtype from the service"); + { + SuccessOrQuit(ed1.Get().ClearService(service)); + service.mName = kSrpServiceType; + service.mInstanceName = kSrpInstanceName; + service.mPort = kSrpServicePort; + service.mSubTypeLabels = kSubTypes3; + SuccessOrQuit(ed1.Get().AddService(service)); + } + nexus.AdvanceTime(kSrpRegistrationTime); + + /** + * Step 25 + * Device: BR 1 (DUT) + * Description (SRP-2.15): Automatically sends SRP Update Response. + * Pass Criteria: + * - The DUT MUST send a valid SRP Update Response with RCODE=0 (NoError). + */ + Log("Step 25: BR 1 (DUT) sends SRP Update Response"); + + /** + * Step 26 + * Device: Eth 1 + * Description (SRP-2.15): Harness instructs the device to send mDNS query QType PTR for + * _other-subtype._sub._test-type._tcp.local" service type, to check that this subtype is still present. + * Pass Criteria: + * - N/A + */ + Log("Step 26: Eth 1 sends mDNS query QType PTR for the remaining subtype"); + { + char subServiceType[Dns::Name::kMaxNameSize]; + snprintf(subServiceType, sizeof(subServiceType), "%s._sub.%s", kSrpSubtype2, kSrpServiceType); + + Dns::Multicast::Core::Browser browser; + ClearAllBytes(browser); + browser.mCallback = [](otInstance *, const otPlatDnssdBrowseResult *) {}; + browser.mServiceType = subServiceType; + browser.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartBrowser(browser)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopBrowser(browser)); + } + + /** + * Step 27 + * Device: BR 1 (DUT) + * Description (SRP-2.15): Automatically sends mDNS Response with service-test-1. + * Pass Criteria: + * - Repeat Step 17 + */ + Log("Step 27: BR 1 (DUT) sends mDNS Response (Repeat Step 17)"); + + /** + * Step 28 + * Device: Eth 1 + * Description (SRP-2.15): Harness instructs the device to send mDNS QType=PTR query for + * _test-subtype._sub._test-type._tcp.local." service type to check that the removed subtype is no longer + * present. Pass Criteria: + * - N/A + */ + Log("Step 28: Eth 1 sends mDNS query QType PTR for the removed subtype"); + { + char subServiceType[Dns::Name::kMaxNameSize]; + snprintf(subServiceType, sizeof(subServiceType), "%s._sub.%s", kSrpSubtype1, kSrpServiceType); + + Dns::Multicast::Core::Browser browser; + ClearAllBytes(browser); + browser.mCallback = [](otInstance *, const otPlatDnssdBrowseResult *) {}; + browser.mServiceType = subServiceType; + browser.mInfraIfIndex = kInfraIfIndex; + SuccessOrQuit(eth1.Get().StartBrowser(browser)); + nexus.AdvanceTime(kDnsQueryTime); + SuccessOrQuit(eth1.Get().StopBrowser(browser)); + } + + /** + * Step 29 + * Device: BR 1 (DUT) + * Description (SRP-2.15): Does not send an mDNS Response. + * Pass Criteria: + * - The DUT MUST NOT send an mDNS response. + */ + Log("Step 29: BR 1 (DUT) does not send an mDNS Response"); + + /** + * Step 30 + * Device: ED 1 + * Description (SRP-2.15): Harness instructs the device to send SRP Update to the DUT which isn't a valid SRP + * Update. This invalid update attempts to add a subtype to the service but is missing the Service Description + * Instruction (SRV record): $ORIGIN default.service.arpa. _test-subtype-2._sub._test-type._tcp ( PTR + * service-test-1._test-type._tcp) _test-type._tcp ( PTR service-test-1._test-type._tcp) host-test-1 AAAA OMR + * address of ED_1> Note (TBD): sending an invalid SRP Update of this form is not supported by current reference + * devices. As a future extension, the Harness should construct the required DNS Update message and send it + * using the UDP-send API. + * Pass Criteria: + * - N/A + */ + Log("Step 30: ED 1 sends an invalid SRP Update (skipped)"); + + /** + * Step 31 + * Device: BR 1 (DUT) + * Description (SRP-2.15): Rejects the invalid SRP Update. + * Pass Criteria: + * - N/A (skipping for 1.3.0 cert due to API issue - TBD) + * - (Future requirement TBD: MUST send SRP Update Response with RCODE=5, REFUSED.) + */ + Log("Step 31: BR 1 (DUT) rejects the invalid SRP Update (skipped)"); + + 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()); + + { + Ip6::Prefix omrPrefix; + SuccessOrQuit(br1.Get().GetOmrPrefix(omrPrefix)); + nexus.AddTestVar("ED_1_OMR_ADDR", + ed1.FindMatchingAddress(omrPrefix.ToString().AsCString()).ToString().AsCString()); + } + + { + String<10> srpPortString; + srpPortString.Append("%u", br1.Get().GetPort()); + nexus.AddTestVar("BR_1_SRP_PORT", srpPortString.AsCString()); + } + + nexus.SaveTestInfo(aJsonFileName); +} + +} // namespace Nexus +} // namespace ot + +int main(int argc, char *argv[]) +{ + ot::Nexus::Test_1_3_SRP_TC_15((argc > 2) ? argv[2] : "test_1_3_SRP_TC_15.json"); + + printf("All tests passed\n"); + return 0; +} diff --git a/tests/nexus/verify_1_3_SRP_TC_15.py b/tests/nexus/verify_1_3_SRP_TC_15.py new file mode 100644 index 000000000..73473254d --- /dev/null +++ b/tests/nexus/verify_1_3_SRP_TC_15.py @@ -0,0 +1,555 @@ +#!/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.15. [1.3] [CERT] Validation of SRP subtypes + # + # 2.15.1 Purpose + # To test the following: + # - 1. Handle service that includes additional subtypes of the basic service type. + # - 2. Respond to the DNS queries for the basic service type. + # - 3. Respond to the DNS queries for the service subtype. + # - 4. Handle addition of new subtype to the service. + # - 5. Handle removal of a subtype from the service. + # - 6. Test service types using 'tcp' in part of name. + # + # 2.15.2 Topology + # - 3. BR 1 (DUT) - Thread Border Router, and the Leader + # - 4. ED 1-Test Bed device operating as a Thread End Device, attached to BR_1 + # - 5. Eth 1-Test Bed border router device on an Adjacent Infrastructure Link + # + # Spec Reference | V1.1 Section | V1.3.0 Section + # -----------------|--------------|--------------- + # SRP Server | N/A | 1.3 + + pkts = pv.pkts + pv.summary.show() + + ED_1_MLEID = Ipv6Addr(pv.vars['ED_1_MLEID_ADDR']) + BR_1_MLEID = Ipv6Addr(pv.vars['BR_1_MLEID_ADDR']) + br1_srp_port = int(pv.vars['BR_1_SRP_PORT']) + + SRP_SERVICE_NAME = '_test-type._tcp.default.service.arpa' + SRP_SUBTYPE1_NAME = '_test-subtype._sub._test-type._tcp.default.service.arpa' + SRP_SUBTYPE2_NAME = '_other-subtype._sub._test-type._tcp.default.service.arpa' + SRP_INSTANCE_NAME = 'service-test-1._test-type._tcp.default.service.arpa' + SRP_HOST_NAME = 'host-test-1.default.service.arpa' + SRP_SERVICE_PORT = 55555 + + MDNS_SERVICE_NAME = '_test-type._tcp.local' + MDNS_SUBTYPE1_NAME = '_test-subtype._sub._test-type._tcp.local' + MDNS_SUBTYPE2_NAME = '_other-subtype._sub._test-type._tcp.local' + MDNS_SUBTYPE_INVALID_NAME = '_test-subtypee._sub._test-type._tcp.local' + MDNS_INSTANCE_NAME = 'service-test-1._test-type._tcp.local' + + SERVICE_ID_SRP_SERVER = 93 + DNS_PORT = 53 + MDNS_PORT = 5353 + + # Step 1 + # Device: BR 1 (DUT) + # Description (SRP-2.15): Enable + # Pass Criteria: + # - N/A + print("Step 1: BR 1 (DUT) Enable") + + # Step 2 + # Device: Eth 1, ED 1 + # Description (SRP-2.15): Enable + # Pass Criteria: + # - N/A + print("Step 2: Eth 1, ED 1 Enable") + + # Step 3 + # Device: BR 1 (DUT) + # Description (SRP-2.15): Automatically adds SRP Server information in the Thread Network Data. + # Pass Criteria: + # - The DUT's SRP Server information MUST appear in the Thread Network Data. + print("Step 3: BR 1 (DUT) adds SRP Server information in 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.15): Harness instructs the device to send SRP Update to the DUT that includes a subtype: + # $ORIGIN default.service.arpa. _test-subtype._sub._test-type._tcp ( PTR service-test-1._test-type._tcp) + # _test-type._tcp ( PTR service-test-1._test-type._tcp) service-test-1._test-type._tcp ( SRV 8 55555 + # host-test-1) host-test-1 AAAA + # Pass Criteria: + # - N/A + print("Step 4: ED 1 sends SRP Update with a subtype") + 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_SUBTYPE1_NAME in verify_utils.as_list(p.dns.resp.name)).\ + filter(lambda p: SRP_SERVICE_NAME in verify_utils.as_list(p.dns.resp.name)).\ + filter(lambda p: SRP_INSTANCE_NAME in verify_utils.as_list(p.dns.resp.name)).\ + must_next() + + # Step 5 + # Device: BR 1 (DUT) + # Description (SRP-2.15): 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") + 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 1 + # Description (SRP-2.15): Harness instructs the device to unicast DNS query QType = PTR for + # "_test-type._tcp.default.service.arpa" service type to the DUT. + # Pass Criteria: + # - N/A + print("Step 6: ED 1 sends unicast DNS query QType PTR for service type") + pkts.\ + filter_ipv6_dst(BR_1_MLEID).\ + filter_ipv6_src(ED_1_MLEID).\ + filter(lambda p: p.udp.dstport == DNS_PORT).\ + filter(lambda p: p.dns.flags.response == 0).\ + filter(lambda p: SRP_SERVICE_NAME in verify_utils.as_list(p.dns.qry.name)).\ + must_next() + + # Step 7 + # Device: BR_1 (DUT) + # Description (SRP-2.15): Automatically sends DNS response with service-test-1. + # Pass Criteria: + # - The DUT MUST send a valid DNS Response and contain service-test-1 as follows in the Answer section: + # $ORIGIN default.service.arpa. _test-type._tcp ( PTR service-test-1. test-type._tcp) + # - It MAY also contain in the Additional records section: $ORIGIN default.service.arpa. + # service-test-1._test-type._tcp ( SRV 8 8 55555 host-test-1) + # - host-test-1 AAAA + print("Step 7: BR 1 (DUT) sends DNS response with service-test-1") + pkts.\ + filter_ipv6_dst(ED_1_MLEID).\ + filter_ipv6_src(BR_1_MLEID).\ + filter(lambda p: p.udp.srcport == DNS_PORT).\ + filter(lambda p: p.dns.flags.response == 1).\ + filter(lambda p: p.dns.flags.rcode == 0).\ + filter(lambda p: SRP_SERVICE_NAME in verify_utils.as_list(p.dns.resp.name)).\ + filter(lambda p: SRP_INSTANCE_NAME in verify_utils.as_list(p.dns.ptr.domain_name)).\ + filter(lambda p: SRP_INSTANCE_NAME in verify_utils.as_list(p.dns.resp.name)).\ + filter(lambda p: SRP_HOST_NAME in verify_utils.as_list(p.dns.srv.target)).\ + filter(lambda p: SRP_SERVICE_PORT in verify_utils.as_list(p.dns.srv.port)).\ + must_next() + + # Step 8 + # Device: Eth 1 + # Description (SRP-2.15): Harness instructs the device to send mDNS query QType PTR for _test-type._tcp.local." + # service type. + # Pass Criteria: + # - N/A + print("Step 8: Eth 1 sends mDNS query QType PTR for service type") + pkts.\ + filter(lambda p: p.udp.dstport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 0).\ + filter(lambda p: MDNS_SERVICE_NAME in verify_utils.as_list(p.mdns.qry.name)).\ + must_next() + + # Step 9 + # Device: BR 1 (DUT) + # Description (SRP-2.15): Automatically sends mDNS Response with service-test-1. + # Pass Criteria: + # - The DUT MUST send a valid mDNS Response with RCODE=0 (NoError) and contain service-test-1 as follows in the + # Answer section: $ORIGIN local. _test-type._tcp ( PTR service-test-1. test-type._tcp) + # - It MAY also contain in the Additional records section: $ORIGIN local. Service-test-1._test-type. tcp ( SRV 8 + # 55555 host-test-1) + # - host-test-1 AAAA + print("Step 9: BR 1 (DUT) sends mDNS Response with service-test-1") + pkts.\ + filter(lambda p: p.udp.srcport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 1).\ + filter(lambda p: p.mdns.flags.rcode == 0).\ + filter(lambda p: MDNS_SERVICE_NAME in verify_utils.as_list(p.mdns.resp.name)).\ + filter(lambda p: MDNS_INSTANCE_NAME in verify_utils.as_list(p.mdns.ptr.domain_name)).\ + filter(lambda p: MDNS_INSTANCE_NAME in verify_utils.as_list(p.mdns.resp.name)).\ + filter(lambda p: SRP_SERVICE_PORT in verify_utils.as_list(p.mdns.srv.port)).\ + must_next() + + # Step 10 + # Device: ED 1 + # Description (SRP-2.15): Harness instructs the device to unicast DNS query QType PTR for + # _test-subtype._sub._test-type._tcp.default.service.arpa" service type to the DUT. + # Pass Criteria: + # - N/A + print("Step 10: ED 1 sends unicast DNS query QType PTR for service subtype") + pkts.\ + filter_ipv6_dst(BR_1_MLEID).\ + filter_ipv6_src(ED_1_MLEID).\ + filter(lambda p: p.udp.dstport == DNS_PORT).\ + filter(lambda p: p.dns.flags.response == 0).\ + filter(lambda p: SRP_SUBTYPE1_NAME in verify_utils.as_list(p.dns.qry.name)).\ + must_next() + + # Step 11 + # Device: BR_1 (DUT) + # Description (SRP-2.15): Automatically sends DNS response with service-test-1. + # Pass Criteria: + # - The DUT MUST send a valid DNS Response and contain service-test-1 as follows in the Answer records section: + # $ORIGIN default.service.arpa. _test-subtype._sub._test-type._tcp ( PTR service-test-1. test-type._tcp) + # - It MAY also contain in the Additional records section: $ORIGIN default.service.arpa. + # service-test-1._test-type. tcp ( SRV 55555 host-test-1) + # - host-test-1 AAAA + print("Step 11: BR 1 (DUT) sends DNS response with service-test-1") + pkts.\ + filter_ipv6_dst(ED_1_MLEID).\ + filter_ipv6_src(BR_1_MLEID).\ + filter(lambda p: p.udp.srcport == DNS_PORT).\ + filter(lambda p: p.dns.flags.response == 1).\ + filter(lambda p: p.dns.flags.rcode == 0).\ + filter(lambda p: SRP_SUBTYPE1_NAME in verify_utils.as_list(p.dns.resp.name)).\ + filter(lambda p: SRP_INSTANCE_NAME in verify_utils.as_list(p.dns.ptr.domain_name)).\ + filter(lambda p: SRP_INSTANCE_NAME in verify_utils.as_list(p.dns.resp.name)).\ + filter(lambda p: SRP_HOST_NAME in verify_utils.as_list(p.dns.srv.target)).\ + filter(lambda p: SRP_SERVICE_PORT in verify_utils.as_list(p.dns.srv.port)).\ + must_next() + + # Step 12 + # Device: Eth 1 + # Description (SRP-2.15): Harness instructs the device to mDNS query QType PTR for + # "_test-subtype._sub._test-type._tcp.local" service type. + # Pass Criteria: + # - N/A + print("Step 12: Eth 1 sends mDNS query QType PTR for service subtype") + pkts.\ + filter(lambda p: p.udp.dstport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 0).\ + filter(lambda p: MDNS_SUBTYPE1_NAME in verify_utils.as_list(p.mdns.qry.name)).\ + must_next() + + # Step 13 + # Device: BR 1 (DUT) + # Description (SRP-2.15): Automatically sends mDNS Response with service-test-1. + # Pass Criteria: + # - The DUT MUST send a valid mDNS Response with RCODE=0 (NoError) and contain service-test-1 as follows in the + # Answer records: $ORIGIN local. _test-subtype._sub._test-type._tcp ( PTR service-test-1. test-type._tcp) + # - It MAY also contain in the Additional records section: $ORIGIN local. Service-test-1. test-type._tcp ( SRV 8 + # 55555 host-test-1) + # - host-test-1 AAAA OMR address of ED_1> + print("Step 13: BR 1 (DUT) sends mDNS Response with service-test-1") + pkts.\ + filter(lambda p: p.udp.srcport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 1).\ + filter(lambda p: p.mdns.flags.rcode == 0).\ + filter(lambda p: MDNS_SUBTYPE1_NAME in verify_utils.as_list(p.mdns.resp.name)).\ + filter(lambda p: MDNS_INSTANCE_NAME in verify_utils.as_list(p.mdns.ptr.domain_name)).\ + filter(lambda p: MDNS_INSTANCE_NAME in verify_utils.as_list(p.mdns.resp.name)).\ + filter(lambda p: SRP_SERVICE_PORT in verify_utils.as_list(p.mdns.srv.port)).\ + must_next() + + # Step 14 + # Device: ED 1 + # Description (SRP-2.15): Harness instructs the device to send SRP Update to the DUT to add a new subtype to the + # existing service: $ORIGIN default.service.arpa. _other-subtype._sub._test-type._tcp ( PTR service-test-1. + # test-type._tcp) _test-subtype._sub._test-type._tcp ( PTR service-test-1._test-type._tcp) _test-type._tcp ( PTR + # service-test-1. test-type._tcp) service-test-1. test-type._tcp ( SRV 8 8 55555 host-test-1) host-test-1 AAAA + # OMR address of ED_1> + # Pass Criteria: + # - N/A + print("Step 14: ED 1 adds a new subtype to the existing service") + 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_SUBTYPE1_NAME in verify_utils.as_list(p.dns.resp.name)).\ + filter(lambda p: SRP_SUBTYPE2_NAME in verify_utils.as_list(p.dns.resp.name)).\ + filter(lambda p: SRP_SERVICE_NAME in verify_utils.as_list(p.dns.resp.name)).\ + filter(lambda p: SRP_INSTANCE_NAME in verify_utils.as_list(p.dns.resp.name)).\ + must_next() + + # Step 15 + # Device: BR 1 (DUT) + # Description (SRP-2.15): 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") + 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 16 + # Device: Eth_1 + # Description (SRP-2.15): Harness instructs the device to mDNS query QType PTR for + # "_other-subtype._sub._test-type._tcp.local" service type, to check that the new subtype is present. + # Pass Criteria: + # - N/A + print("Step 16: Eth 1 sends mDNS query QType PTR for the new subtype") + pkts.\ + filter(lambda p: p.udp.dstport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 0).\ + filter(lambda p: MDNS_SUBTYPE2_NAME in verify_utils.as_list(p.mdns.qry.name)).\ + must_next() + + # Step 17 + # Device: BR 1 (DUT) + # Description (SRP-2.15): Automatically sends mDNS Response with service-test-1. + # Pass Criteria: + # - The DUT MUST send a valid mDNS Response with RCODE=0 (NoError) and contain service-test-1 as follows in the + # Answer records section: $ORIGIN local. _other-subtype._sub._test-type._tcp ( PTR + # service-test-1._test-type._tcp) + # - It MAY also contain in the Additional records section: $ORIGIN local. Service-test-1. test-type._tcp ( SRV + # 55555 host-test-1) + # - host-test-1 AAAA OMR address of ED_1> + print("Step 17: BR 1 (DUT) sends mDNS Response with service-test-1") + pkts.\ + filter(lambda p: p.udp.srcport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 1).\ + filter(lambda p: p.mdns.flags.rcode == 0).\ + filter(lambda p: MDNS_SUBTYPE2_NAME in verify_utils.as_list(p.mdns.resp.name)).\ + filter(lambda p: MDNS_INSTANCE_NAME in verify_utils.as_list(p.mdns.ptr.domain_name)).\ + filter(lambda p: MDNS_INSTANCE_NAME in verify_utils.as_list(p.mdns.resp.name)).\ + filter(lambda p: SRP_SERVICE_PORT in verify_utils.as_list(p.mdns.srv.port)).\ + must_next() + + # Step 18 + # Device: Eth_1 + # Description (SRP-2.15): Harness instructs the device to mDNS query QType=PTR for + # "_test-subtype._sub._test-type._tcp.local" service type, to check that the originally registered subtype is + # still present. + # Pass Criteria: + # - N/A + print("Step 18: Eth 1 sends mDNS query QType PTR for the original subtype") + pkts.\ + filter(lambda p: p.udp.dstport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 0).\ + filter(lambda p: MDNS_SUBTYPE1_NAME in verify_utils.as_list(p.mdns.qry.name)).\ + must_next() + + # Step 19 + # Device: BR 1 (DUT) + # Description (SRP-2.15): Automatically sends mDNS Response with service-test-1. + # Pass Criteria: + # - Repeat Step 13 + print("Step 19: BR 1 (DUT) sends mDNS Response (Repeat Step 13)") + pkts.\ + filter(lambda p: p.udp.srcport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 1).\ + filter(lambda p: p.mdns.flags.rcode == 0).\ + filter(lambda p: MDNS_SUBTYPE1_NAME in verify_utils.as_list(p.mdns.resp.name)).\ + filter(lambda p: MDNS_INSTANCE_NAME in verify_utils.as_list(p.mdns.ptr.domain_name)).\ + filter(lambda p: MDNS_INSTANCE_NAME in verify_utils.as_list(p.mdns.resp.name)).\ + filter(lambda p: SRP_SERVICE_PORT in verify_utils.as_list(p.mdns.srv.port)).\ + must_next() + + # Step 20 + # Device: Eth_1 + # Description (SRP-2.15): Harness instructs the device to send mDNS query QType PTR for _test-type._tcp.local." + # service type, to check that the query for the parent type also works after new subtype registration. + # Pass Criteria: + # - N/A + print("Step 20: Eth 1 sends mDNS query QType PTR for the parent service type") + pkts.\ + filter(lambda p: p.udp.dstport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 0).\ + filter(lambda p: MDNS_SERVICE_NAME in verify_utils.as_list(p.mdns.qry.name)).\ + must_next() + + # Step 21 + # Device: BR 1 (DUT) + # Description (SRP-2.15): Automatically sends mDNS Response with service-test-1. + # Pass Criteria: + # - Repeat Step 9 + print("Step 21: BR 1 (DUT) sends mDNS Response (Repeat Step 9)") + pkts.\ + filter(lambda p: p.udp.srcport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 1).\ + filter(lambda p: p.mdns.flags.rcode == 0).\ + filter(lambda p: MDNS_SERVICE_NAME in verify_utils.as_list(p.mdns.resp.name)).\ + filter(lambda p: MDNS_INSTANCE_NAME in verify_utils.as_list(p.mdns.ptr.domain_name)).\ + filter(lambda p: MDNS_INSTANCE_NAME in verify_utils.as_list(p.mdns.resp.name)).\ + filter(lambda p: SRP_SERVICE_PORT in verify_utils.as_list(p.mdns.srv.port)).\ + must_next() + + # Step 22 + # Device: Eth 1 + # Description (SRP-2.15): Harness instructs the device to send mDNS query QType PTR for non-existent subtype (name + # with extra character added): "_test-subtypee._sub._test-type._tcp.local." service type, to check that no + # service answer is given by the DUT. + # Pass Criteria: + # - N/A + print("Step 22: Eth 1 sends mDNS query QType PTR for a non-existent subtype") + pkts.\ + filter(lambda p: p.udp.dstport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 0).\ + filter(lambda p: MDNS_SUBTYPE_INVALID_NAME in verify_utils.as_list(p.mdns.qry.name)).\ + must_next() + + # Step 23 + # Device: BR_1 (DUT) + # Description (SRP-2.15): Does not send an mDNS Response. + # Pass Criteria: + # - The DUT MUST NOT send an mDNS response. + print("Step 23: BR 1 (DUT) does not send an mDNS Response") + pkts.\ + filter(lambda p: p.udp.srcport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 1).\ + filter(lambda p: MDNS_SUBTYPE_INVALID_NAME in verify_utils.as_list(p.mdns.resp.name)).\ + must_not_next() + + # Step 24 + # Device: ED 1 + # Description (SRP-2.15): Harness instructs the device to send SRP Update to the DUT to re-register (i.e., + # update), its service with the subtype "_test-subtype" removed from the service: $ORIGIN + # default.service.arpa. _other-subtype._sub._test-type._tcp ( PTR service-test-1._test-type._tcp) + # _test-type._tcp ( PTR service-test-1._test-type._tcp) service-test-1._test-type._tcp ( SRV 55555 host-test-1) + # host-test-1 AAAA OMR address of ED_1> Note: such an update would automatically delete any other, + # pre-existing subtypes on the SRP server for this service. + # Pass Criteria: + # - N/A + print("Step 24: ED 1 removes a subtype from the service") + 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_SUBTYPE2_NAME in verify_utils.as_list(p.dns.resp.name)).\ + filter(lambda p: SRP_SERVICE_NAME in verify_utils.as_list(p.dns.resp.name)).\ + filter(lambda p: SRP_INSTANCE_NAME in verify_utils.as_list(p.dns.resp.name)).\ + must_next() + + # Step 25 + # Device: BR 1 (DUT) + # Description (SRP-2.15): Automatically sends SRP Update Response. + # Pass Criteria: + # - The DUT MUST send a valid SRP Update Response with RCODE=0 (NoError). + print("Step 25: BR 1 (DUT) sends SRP Update Response") + 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 26 + # Device: Eth 1 + # Description (SRP-2.15): Harness instructs the device to send mDNS query QType PTR for + # _other-subtype._sub._test-type._tcp.local" service type, to check that this subtype is still present. + # Pass Criteria: + # - N/A + print("Step 26: Eth 1 sends mDNS query QType PTR for the remaining subtype") + pkts.\ + filter(lambda p: p.udp.dstport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 0).\ + filter(lambda p: MDNS_SUBTYPE2_NAME in verify_utils.as_list(p.mdns.qry.name)).\ + must_next() + + # Step 27 + # Device: BR 1 (DUT) + # Description (SRP-2.15): Automatically sends mDNS Response with service-test-1. + # Pass Criteria: + # - Repeat Step 17 + print("Step 27: BR 1 (DUT) sends mDNS Response (Repeat Step 17)") + pkts.\ + filter(lambda p: p.udp.srcport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 1).\ + filter(lambda p: p.mdns.flags.rcode == 0).\ + filter(lambda p: MDNS_SUBTYPE2_NAME in verify_utils.as_list(p.mdns.resp.name)).\ + filter(lambda p: MDNS_INSTANCE_NAME in verify_utils.as_list(p.mdns.ptr.domain_name)).\ + filter(lambda p: MDNS_INSTANCE_NAME in verify_utils.as_list(p.mdns.resp.name)).\ + filter(lambda p: SRP_SERVICE_PORT in verify_utils.as_list(p.mdns.srv.port)).\ + must_next() + + # Step 28 + # Device: Eth 1 + # Description (SRP-2.15): Harness instructs the device to send mDNS QType=PTR query for + # _test-subtype._sub._test-type._tcp.local." service type to check that the removed subtype is no longer present. + # Pass Criteria: + # - N/A + print("Step 28: Eth 1 sends mDNS query QType PTR for the removed subtype") + pkts.\ + filter(lambda p: p.udp.dstport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 0).\ + filter(lambda p: MDNS_SUBTYPE1_NAME in verify_utils.as_list(p.mdns.qry.name)).\ + must_next() + + # Step 29 + # Device: BR 1 (DUT) + # Description (SRP-2.15): Does not send an mDNS Response. + # Pass Criteria: + # - The DUT MUST NOT send an mDNS response. + print("Step 29: BR 1 (DUT) does not send an mDNS Response") + pkts.\ + filter(lambda p: p.udp.srcport == MDNS_PORT).\ + filter(lambda p: p.mdns.flags.response == 1).\ + filter(lambda p: MDNS_SUBTYPE1_NAME in verify_utils.as_list(p.mdns.resp.name)).\ + must_not_next() + + # Step 30 + # Device: ED 1 + # Description (SRP-2.15): Harness instructs the device to send SRP Update to the DUT which isn't a valid SRP + # Update. This invalid update attempts to add a subtype to the service but is missing the Service Description + # Instruction (SRV record): $ORIGIN default.service.arpa. _test-subtype-2._sub._test-type._tcp ( PTR + # service-test-1._test-type._tcp) _test-type._tcp ( PTR service-test-1._test-type._tcp) host-test-1 AAAA OMR + # address of ED_1> Note (TBD): sending an invalid SRP Update of this form is not supported by current reference + # devices. As a future extension, the Harness should construct the required DNS Update message and send it + # using the UDP-send API. + # Pass Criteria: + # - N/A + print("Step 30: ED 1 sends an invalid SRP Update (skipped)") + + # Step 31 + # Device: BR 1 (DUT) + # Description (SRP-2.15): Rejects the invalid SRP Update. + # Pass Criteria: + # - N/A (skipping for 1.3.0 cert due to API issue - TBD) + # - (Future requirement TBD: MUST send SRP Update Response with RCODE=5, REFUSED.) + print("Step 31: BR 1 (DUT) rejects the invalid SRP Update (skipped)") + + +if __name__ == '__main__': + verify_utils.run_main(verify)