1#!/usr/bin/env python3 2# 3# Copyright (c) 2016, The OpenThread Authors. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 1. Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# 2. Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in the 12# documentation and/or other materials provided with the distribution. 13# 3. Neither the name of the copyright holder nor the 14# names of its contributors may be used to endorse or promote products 15# derived from this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27# POSSIBILITY OF SUCH DAMAGE. 28# 29 30import unittest 31 32import config 33import thread_cert 34from pktverify.consts import MLE_PARENT_RESPONSE, MLE_CHILD_ID_RESPONSE, SOURCE_ADDRESS_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, CONNECTIVITY_TLV, LINK_MARGIN_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV 35from pktverify.packet_verifier import PacketVerifier 36from pktverify.null_field import nullField 37 38LEADER = 1 39ROUTER = 2 40SED1 = 7 41 42# Test Purpose and Description: 43# ----------------------------- 44# The purpose of this test case is to validate the minimum 45# conformance requirements for router-capable devices: 46# a)Minimum number of supported children. 47# b)Minimum MTU requirement when sending/forwarding an 48# IPv6 datagram to a SED. 49# c)Minimum number of sent/forwarded IPv6 datagrams to 50# SED children. 51# 52# Test Topology: 53# ------------- 54# 55# Leader 56# | 57# Router[DUT] 58# / \ 59# MED1 - MED4 SED1 - SED6 60# 61# DUT Types: 62# ---------- 63# Router 64 65 66class Cert_5_1_07_MaxChildCount(thread_cert.TestCase): 67 USE_MESSAGE_FACTORY = False 68 69 TOPOLOGY = { 70 LEADER: { 71 'name': 'LEADER', 72 'mode': 'rdn', 73 'allowlist': [ROUTER] 74 }, 75 ROUTER: { 76 'name': 'ROUTER', 77 'max_children': 10, 78 'mode': 'rdn', 79 'allowlist': [LEADER, 3, 4, 5, 6, SED1, 8, 9, 10, 11, 12] 80 }, 81 3: { 82 'name': 'MED1', 83 'is_mtd': True, 84 'mode': 'rn', 85 'timeout': config.DEFAULT_CHILD_TIMEOUT, 86 'allowlist': [ROUTER] 87 }, 88 4: { 89 'name': 'MED2', 90 'is_mtd': True, 91 'mode': 'rn', 92 'timeout': config.DEFAULT_CHILD_TIMEOUT, 93 'allowlist': [ROUTER] 94 }, 95 5: { 96 'name': 'MED3', 97 'is_mtd': True, 98 'mode': 'rn', 99 'timeout': config.DEFAULT_CHILD_TIMEOUT, 100 'allowlist': [ROUTER] 101 }, 102 6: { 103 'name': 'MED4', 104 'is_mtd': True, 105 'mode': 'rn', 106 'timeout': config.DEFAULT_CHILD_TIMEOUT, 107 'allowlist': [ROUTER] 108 }, 109 SED1: { 110 'name': 'SED1', 111 'is_mtd': True, 112 'mode': '-', 113 'timeout': config.DEFAULT_CHILD_TIMEOUT, 114 'allowlist': [ROUTER] 115 }, 116 8: { 117 'name': 'SED2', 118 'is_mtd': True, 119 'mode': '-', 120 'timeout': config.DEFAULT_CHILD_TIMEOUT, 121 'allowlist': [ROUTER] 122 }, 123 9: { 124 'name': 'SED3', 125 'is_mtd': True, 126 'mode': '-', 127 'timeout': config.DEFAULT_CHILD_TIMEOUT, 128 'allowlist': [ROUTER] 129 }, 130 10: { 131 'name': 'SED4', 132 'is_mtd': True, 133 'mode': '-', 134 'timeout': config.DEFAULT_CHILD_TIMEOUT, 135 'allowlist': [ROUTER] 136 }, 137 11: { 138 'name': 'SED5', 139 'is_mtd': True, 140 'mode': '-', 141 'timeout': config.DEFAULT_CHILD_TIMEOUT, 142 'allowlist': [ROUTER] 143 }, 144 12: { 145 'name': 'SED6', 146 'is_mtd': True, 147 'mode': '-', 148 'timeout': config.DEFAULT_CHILD_TIMEOUT, 149 'allowlist': [ROUTER] 150 }, 151 } 152 153 def test(self): 154 self.nodes[LEADER].start() 155 self.simulator.go(config.LEADER_STARTUP_DELAY) 156 self.assertEqual(self.nodes[LEADER].get_state(), 'leader') 157 158 self.nodes[ROUTER].start() 159 self.simulator.go(config.ROUTER_STARTUP_DELAY) 160 self.assertEqual(self.nodes[ROUTER].get_state(), 'router') 161 162 for i in range(3, 13): 163 self.nodes[i].start() 164 self.simulator.go(7) 165 self.assertEqual(self.nodes[i].get_state(), 'child') 166 167 self.collect_rloc16s() 168 self.collect_ipaddrs() 169 170 ipaddrs = self.nodes[SED1].get_addrs() 171 for addr in ipaddrs: 172 if addr[0:4] != 'fe80' and 'ff:fe00' not in addr: 173 self.assertTrue(self.nodes[LEADER].ping(addr, size=1232)) 174 break 175 176 for i in range(3, 13): 177 ipaddrs = self.nodes[i].get_addrs() 178 for addr in ipaddrs: 179 if addr[0:4] != 'fe80' and 'ff:fe00' not in addr: 180 self.assertTrue(self.nodes[LEADER].ping(addr, size=106)) 181 break 182 183 def verify(self, pv: PacketVerifier): 184 pkts = pv.pkts 185 pv.summary.show() 186 187 ROUTER = pv.vars['ROUTER'] 188 router_pkts = pkts.filter_wpan_src64(ROUTER) 189 190 # Step 1: The DUT MUST send properly formatted MLE Parent Response 191 # and MLE Child ID Response to each child. 192 for i in range(1, 7): 193 _pkts = router_pkts.copy().filter_wpan_dst64(pv.vars['SED%d' % i]) 194 _pkts.filter_mle_cmd(MLE_PARENT_RESPONSE).\ 195 filter(lambda p: { 196 CHALLENGE_TLV, 197 CONNECTIVITY_TLV, 198 LEADER_DATA_TLV, 199 LINK_LAYER_FRAME_COUNTER_TLV, 200 LINK_MARGIN_TLV, 201 RESPONSE_TLV, 202 SOURCE_ADDRESS_TLV, 203 VERSION_TLV 204 } <= set(p.mle.tlv.type) 205 ).\ 206 must_next() 207 _pkts.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\ 208 filter(lambda p: { 209 SOURCE_ADDRESS_TLV, 210 LEADER_DATA_TLV, 211 ADDRESS16_TLV, 212 NETWORK_DATA_TLV, 213 ADDRESS_REGISTRATION_TLV 214 } <= set(p.mle.tlv.type) and\ 215 p.mle.tlv.addr16 is not nullField and\ 216 p.thread_nwd.tlv.type is not None and\ 217 p.thread_meshcop.tlv.type is not None 218 ).\ 219 must_next() 220 221 for i in range(1, 5): 222 _pkts = router_pkts.copy().filter_wpan_dst64(pv.vars['MED%d' % i]) 223 _pkts.filter_mle_cmd(MLE_PARENT_RESPONSE).\ 224 filter(lambda p: { 225 CHALLENGE_TLV, 226 CONNECTIVITY_TLV, 227 LEADER_DATA_TLV, 228 LINK_LAYER_FRAME_COUNTER_TLV, 229 LINK_MARGIN_TLV, 230 RESPONSE_TLV, 231 SOURCE_ADDRESS_TLV, 232 VERSION_TLV 233 } <= set(p.mle.tlv.type) 234 ).\ 235 must_next() 236 237 _pkts.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\ 238 filter(lambda p: { 239 SOURCE_ADDRESS_TLV, 240 LEADER_DATA_TLV, 241 ADDRESS16_TLV, 242 NETWORK_DATA_TLV, 243 ADDRESS_REGISTRATION_TLV 244 } <= set(p.mle.tlv.type) and\ 245 p.mle.tlv.addr16 is not nullField and\ 246 p.thread_meshcop.tlv.type is not None 247 ).\ 248 must_next() 249 250 # Step 2: The DUT MUST properly forward ICMPv6 Echo Requests to all MED children 251 # The DUT MUST properly forward ICMPv6 Echo Replies to the Leader 252 leader_rloc16 = pv.vars['LEADER_RLOC16'] 253 for i in range(1, 5): 254 rloc16 = pv.vars['MED%d_RLOC16' % i] 255 _pkts = router_pkts.copy() 256 p = _pkts.filter('wpan.dst16 == {rloc16}', rloc16=rloc16).\ 257 filter_ping_request().\ 258 must_next() 259 _pkts.filter('wpan.dst16 == {rloc16}', 260 rloc16=leader_rloc16).\ 261 filter_ping_reply(identifier=p.icmpv6.echo.identifier).\ 262 must_next() 263 264 # Step 3: The DUT MUST properly forward ICMPv6 Echo Requests to all SED children 265 # The DUT MUST properly forward ICMPv6 Echo Replies to the Leader 266 for i in range(1, 7): 267 rloc16 = pv.vars['SED%d_RLOC16' % i] 268 _pkts = router_pkts.copy() 269 p = _pkts.filter('wpan.dst16 == {rloc16}', rloc16=rloc16).\ 270 filter_ping_request().\ 271 must_next() 272 _pkts.filter('wpan.dst16 == {rloc16}', 273 rloc16=leader_rloc16).\ 274 filter_ping_reply(identifier=p.icmpv6.echo.identifier).\ 275 must_next() 276 277 278if __name__ == '__main__': 279 unittest.main() 280