• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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