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 copy
31import unittest
32
33import command
34import config
35import copy
36import ipv6
37import thread_cert
38from pktverify.consts import WIRESHARK_OVERRIDE_PREFS, ADDR_QRY_URI, ADDR_NTF_URI, NL_ML_EID_TLV, NL_RLOC16_TLV, NL_TARGET_EID_TLV
39from pktverify.packet_verifier import PacketVerifier
40from pktverify.bytes import Bytes
41from pktverify.addrs import Ipv6Addr
42
43LEADER = 1
44BR = 2
45ROUTER1 = 3
46DUT_ROUTER2 = 4
47MED1 = 5
48
49PREFIX_1 = '2003::/64'
50GUA_1_START = '2003'
51PREFIX_2 = '2004::/64'
52
53# Test Purpose and Description:
54# -----------------------------
55# The purpose of this test case is to validate that the DUT is able to generate
56# Address Query and Address Notification messages
57# The Border Router is configured as a SLAAC server for prefixes 2003:: & 2004::
58#
59# Test Topology:
60# -------------
61# BorderRouter - Leader
62#               /     \
63#         Router_1 - Router_2(DUT)
64#                       |
65#                      MED
66#
67# DUT Types:
68# ----------
69#  Router
70
71
72class Cert_5_3_10_AddressQuery(thread_cert.TestCase):
73    USE_MESSAGE_FACTORY = False
74    SUPPORT_NCP = False
75
76    TOPOLOGY = {
77        LEADER: {
78            'name': 'LEADER',
79            'mode': 'rdn',
80            'allowlist': [BR, ROUTER1, DUT_ROUTER2]
81        },
82        BR: {
83            'name': 'BR',
84            'mode': 'rdn',
85            'allowlist': [LEADER]
86        },
87        ROUTER1: {
88            'name': 'ROUTER_1',
89            'mode': 'rdn',
90            'allowlist': [LEADER, DUT_ROUTER2]
91        },
92        DUT_ROUTER2: {
93            'name': 'ROUTER_2',
94            'mode': 'rdn',
95            'allowlist': [LEADER, ROUTER1, MED1]
96        },
97        MED1: {
98            'name': 'MED',
99            'is_mtd': True,
100            'mode': 'rn',
101            'allowlist': [DUT_ROUTER2]
102        },
103    }
104    # override wireshark preferences with case needed parameters
105    CASE_WIRESHARK_PREFS = copy.deepcopy(WIRESHARK_OVERRIDE_PREFS)
106    CASE_WIRESHARK_PREFS['6lowpan.context1'] = PREFIX_1
107    CASE_WIRESHARK_PREFS['6lowpan.context2'] = PREFIX_2
108
109    def test(self):
110        # 1 & 2
111        self.nodes[LEADER].start()
112        self.simulator.go(config.LEADER_STARTUP_DELAY)
113        self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
114
115        self.nodes[BR].start()
116        self.simulator.go(config.ROUTER_STARTUP_DELAY)
117        self.assertEqual(self.nodes[BR].get_state(), 'router')
118
119        # Configure two On-Mesh Prefixes on the BR
120        self.nodes[BR].add_prefix(PREFIX_1, 'paros')
121        self.nodes[BR].add_prefix(PREFIX_2, 'paros')
122        self.nodes[BR].register_netdata()
123
124        self.nodes[DUT_ROUTER2].start()
125        self.simulator.go(config.ROUTER_STARTUP_DELAY)
126        self.assertEqual(self.nodes[DUT_ROUTER2].get_state(), 'router')
127
128        self.nodes[ROUTER1].start()
129        self.simulator.go(config.ROUTER_STARTUP_DELAY)
130        self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
131
132        self.nodes[MED1].start()
133        self.simulator.go(5)
134        self.assertEqual(self.nodes[MED1].get_state(), 'child')
135
136        self.collect_rlocs()
137        self.collect_rloc16s()
138        self.collect_ipaddrs()
139
140        # 3 MED1: MED1 sends an ICMPv6 Echo Request to Router1 using GUA
141        # PREFIX_1 address
142        router1_addr = self.nodes[ROUTER1].get_addr(PREFIX_1)
143        self.assertTrue(router1_addr is not None)
144        self.assertTrue(self.nodes[MED1].ping(router1_addr))
145        self.simulator.go(1)
146
147        # 4 BR: BR sends an ICMPv6 Echo Request to MED1 using GUA PREFIX_1
148        # address
149        med1_addr = self.nodes[MED1].get_addr(PREFIX_1)
150        self.assertTrue(med1_addr is not None)
151        self.assertTrue(self.nodes[BR].ping(med1_addr))
152        self.simulator.go(1)
153
154        # 5 MED1: MED1 sends an ICMPv6 Echo Request to ROUTER1 using GUA PREFIX_1
155        # address
156        self.assertTrue(self.nodes[MED1].ping(router1_addr))
157        self.simulator.go(1)
158
159        # 6 DUT_ROUTER2: Power off ROUTER1 and wait 580 seconds to allow the
160        # LEADER to expire its Router ID
161        router1_id = self.nodes[ROUTER1].get_router_id()
162        self.nodes[ROUTER1].stop()
163        self.simulator.go(580)
164
165        # Send an ICMPv6 Echo Request from MED1 to ROUTER1 GUA PREFIX_1 address
166        self.assertFalse(self.nodes[MED1].ping(router1_addr))
167        self.simulator.go(1)
168
169        # 7 MED1: Power off MED1 and wait to allow DUT_ROUTER2 to timeout the
170        # child
171        self.nodes[MED1].stop()
172        self.simulator.go(config.MLE_END_DEVICE_TIMEOUT)
173
174        # BR sends two ICMPv6 Echo Requests to MED1 GUA PREFIX_1 address
175        self.assertFalse(self.nodes[BR].ping(med1_addr))
176        self.assertFalse(self.nodes[BR].ping(med1_addr))
177
178    def verify(self, pv):
179        pkts = pv.pkts
180        pv.summary.show()
181
182        LEADER = pv.vars['LEADER']
183        ROUTER_1 = pv.vars['ROUTER_1']
184        ROUTER_2 = pv.vars['ROUTER_2']
185        ROUTER_2_RLOC = pv.vars['ROUTER_2_RLOC']
186        ROUTER_2_RLOC16 = pv.vars['ROUTER_2_RLOC16']
187        BR = pv.vars['BR']
188        BR_RLOC = pv.vars['BR_RLOC']
189        MED = pv.vars['MED']
190        MED_RLOC16 = pv.vars['MED_RLOC16']
191        MM = pv.vars['MM_PORT']
192        GUA1 = {}
193
194        for node in ('ROUTER_1', 'BR', 'MED'):
195            for addr in pv.vars['%s_IPADDRS' % node]:
196                if addr.startswith(Bytes(GUA_1_START)):
197                    GUA1[node] = addr
198
199        # Step 2: Build the topology as described
200
201        pv.verify_attached('BR', 'LEADER')
202        for i in (2, 1):
203            pv.verify_attached('ROUTER_%d' % i, 'LEADER')
204        pv.verify_attached('MED', 'ROUTER_2', 'MTD')
205
206        # Step 3: MED sends an ICMPv6 Echo Request to Router_1 using GUA 2003::
207        #         address
208        #         The DUT MUST generate an Address Query Request on MED’s behalf
209        #         to find each node’s RLOC.
210        #         The Address Query Request MUST be sent to the Realm-Local
211        #         All-Routers address (FF03::2)
212        #             CoAP URI-Path
213        #                 - NON POST coap://<FF03::2>
214        #             CoAP Payload
215        #                 - Target EID TLV
216        #         The DUT MUST receive and process the incoming Address Query
217        #         Response and forward the ICMPv6 Echo Request packet to Router_1
218
219        _pkt = pkts.filter_ping_request().\
220            filter_wpan_src64(MED).\
221            filter_ipv6_dst(GUA1['ROUTER_1']).\
222            must_next()
223        pkts.filter_wpan_src64(ROUTER_2).\
224            filter_RLARMA().\
225            filter_coap_request(ADDR_QRY_URI, port=MM).\
226            filter(lambda p: p.thread_address.tlv.target_eid == GUA1['ROUTER_1']).\
227            must_next()
228        pkts.filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).\
229            filter_wpan_src64(ROUTER_2).\
230            filter_ipv6_dst(GUA1['ROUTER_1']).\
231            must_next()
232        pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
233            filter_wpan_src64(ROUTER_1).\
234            filter_ipv6_dst(GUA1['MED']).\
235            must_next()
236        pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
237            filter_wpan_src64(ROUTER_2).\
238            filter_wpan_dst16(MED_RLOC16).\
239            must_next()
240
241        # Step 4: Border Router sends an ICMPv6 Echo Request to MED using GUA 2003::
242        #         address
243        #         The DUT MUST respond to the Address Query Request with a properly
244        #         formatted Address Notification Message:
245        #             CoAP URI-Path
246        #                 - CON POST coap://[<Address Query Source>]:MM/a/an
247        #             CoAP Payload
248        #                 - ML-EID TLV
249        #                 - RLOC16 TLV
250        #                 - Target EID TLV
251        #         The IPv6 Source address MUST be the RLOC of the originator
252        #         The IPv6 Destination address MUST be the RLOC of the destination
253
254        pkts.filter_wpan_src64(BR).\
255            filter_RLARMA().\
256            filter_coap_request(ADDR_QRY_URI, port=MM).\
257            filter(lambda p: p.thread_address.tlv.target_eid == GUA1['MED']).\
258            must_next()
259        pkts.filter_ipv6_src_dst(ROUTER_2_RLOC, BR_RLOC).\
260            filter_coap_request(ADDR_NTF_URI, port=MM).\
261            filter(lambda p: {
262                                 NL_ML_EID_TLV,
263                                 NL_RLOC16_TLV,
264                                 NL_TARGET_EID_TLV
265                             } <= set(p.coap.tlv.type) and\
266                             p.thread_address.tlv.target_eid == GUA1['MED'] and\
267                             p.thread_address.tlv.rloc16 == ROUTER_2_RLOC16
268                   ).\
269            must_next()
270        _pkt = pkts.filter_ping_request().\
271            filter_wpan_src64(BR).\
272            filter_ipv6_dst(GUA1['MED']).\
273            must_next()
274        pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
275            filter_wpan_src64(MED).\
276            filter_ipv6_dst(GUA1['BR']).\
277            must_next()
278
279        # Step 5: MED sends an ICMPv6 Echo Request to Router_1 using GUA 2003::
280        #         address
281        #         The DUT MUST not send an Address Query as Router_1 address should
282        #         be cached.
283        #         The DUT MUST forward the ICMPv6 Echo Reply to MED
284
285        _pkt = pkts.filter_ping_request().\
286            filter_wpan_src64(MED).\
287            filter_ipv6_dst(GUA1['ROUTER_1']).\
288            must_next()
289        lstart = pkts.index
290        pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
291            filter_wpan_src64(ROUTER_1).\
292            filter_ipv6_dst(GUA1['MED']).\
293            must_next()
294        pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
295            filter_wpan_src64(ROUTER_2).\
296            filter_wpan_dst16(MED_RLOC16).\
297            must_next()
298        lend = pkts.index
299        pkts.range(lstart, lend).filter_wpan_src64(ROUTER_2).\
300            filter_RLARMA().\
301            filter_coap_request(ADDR_QRY_URI, port=MM).\
302            must_not_next()
303
304        # Step 6: MED sends an ICMPv6 Echo Request to Router_1 using GUA 2003::
305        #         address
306        #         The DUT MUST update its address cache and remove all entries
307        #         based on Router_1’s Router ID.
308        #         The DUT MUST send an Address Query to discover Router_1’s RLOC address.
309
310        pkts.filter_ping_request().\
311            filter_wpan_src64(MED).\
312            filter_ipv6_dst(GUA1['ROUTER_1']).\
313            must_next()
314        pkts.filter_wpan_src64(ROUTER_2).\
315            filter_RLARMA().\
316            filter_coap_request(ADDR_QRY_URI, port=MM).\
317            filter(lambda p: p.thread_address.tlv.target_eid == GUA1['ROUTER_1']).\
318            must_next()
319
320        # Step 7: Border Router sends two ICMPv6 Echo Requests to MED using GUA 2003::
321        #         address
322        #         The DUT MUST NOT respond with an Address Notification message
323
324        pkts.filter_wpan_src64(ROUTER_2).\
325            filter_ipv6_dst(BR_RLOC).\
326            filter_coap_request(ADDR_NTF_URI, port=MM).\
327            must_not_next()
328        pkts.filter_ping_request().\
329            filter_wpan_src64(BR).\
330            filter_ipv6_dst(GUA1['MED']).\
331            must_next()
332
333
334if __name__ == '__main__':
335    unittest.main()
336