1#!/usr/bin/env python3
2#
3#  Copyright (c) 2021, 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 logging
31import unittest
32
33import pktverify
34from pktverify import packet_verifier
35from pktverify.consts import MA1, MA1g, MA2
36import config
37import thread_cert
38
39# Test description:
40# The purpose of this test is to verify the functionality of ping command.
41#
42# Topology:
43#
44#
45#  ROUTER_2 ----- ROUTER_1 ---- ROUTER_3
46#
47#
48
49ROUTER_1 = 1
50ROUTER_2 = 2
51ROUTER_3 = 3
52
53
54class TestPing(thread_cert.TestCase):
55    USE_MESSAGE_FACTORY = False
56    SUPPORT_NCP = False
57
58    TOPOLOGY = {
59        ROUTER_1: {
60            'name': 'Router_1',
61            'allowlist': [ROUTER_2, ROUTER_3],
62        },
63        ROUTER_2: {
64            'name': 'Router_2',
65            'allowlist': [ROUTER_1],
66        },
67        ROUTER_3: {
68            'name': 'Router_3',
69            'allowlist': [ROUTER_1],
70        },
71    }
72
73    def test(self):
74        router1 = self.nodes[ROUTER_1]
75        router2 = self.nodes[ROUTER_2]
76        router3 = self.nodes[ROUTER_3]
77
78        router1.start()
79        self.simulator.go(5)
80        self.assertEqual('leader', router1.get_state())
81
82        router2.start()
83        self.simulator.go(5)
84        self.assertEqual('router', router2.get_state())
85
86        router3.start()
87        self.simulator.go(5)
88        self.assertEqual('router', router3.get_state())
89
90        # 1. ROUTER_1 pings ROUTER_2.
91        self.assertTrue(router1.ping(router2.get_ip6_address(config.ADDRESS_TYPE.RLOC)))
92
93        # 2. ROUTER_1 pings ROUTER_2 multiple times.
94        self.assertTrue(router1.ping(router2.get_ip6_address(config.ADDRESS_TYPE.RLOC), count=5))
95
96        # 3. ROUTER_2 pings ROUTER_1 from the link-local address to the
97        # link-local address.
98        self.assertTrue(
99            router2.ping(router1.get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL),
100                         interface=router2.get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL)))
101
102        # 4. ROUTER_2 pings ROUTER_3 using the RLOC.
103        self.assertTrue(router2.ping(router3.get_ip6_address(config.ADDRESS_TYPE.RLOC)))
104
105        # 5. ROUTER_2 pings ROUTER_3's link-local address. The ping should fail.
106        self.assertFalse(router2.ping(router3.get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL)))
107
108        # 6. ROUTER_2 pings ROUTER_3's RLOC from the link-local address. The
109        # ping should fail.
110        self.assertFalse(
111            router2.ping(router3.get_ip6_address(config.ADDRESS_TYPE.RLOC),
112                         interface=router2.get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL)))
113
114        # 7. ROUTER_2 pings ROUTER_3's RLOC from an non-existent address. The
115        # ping command should be rejected by CLI.
116        self.assertFalse(router2.ping(router3.get_ip6_address(config.ADDRESS_TYPE.RLOC), interface='1::1'))
117
118        self.collect_ipaddrs()
119        self.collect_rloc16s()
120        self.collect_rlocs()
121        self.collect_extra_vars()
122
123    def verify(self, pv: pktverify.packet_verifier.PacketVerifier):
124        pkts = pv.pkts
125        vars = pv.vars
126        pv.summary.show()
127
128        logging.info(f'vars = {vars}')
129
130        # Ensure the topology is formed correctly
131        pv.verify_attached('Router_2', 'Router_1')
132        pv.verify_attached('Router_3', 'Router_1')
133
134        # 1. Router_1 pings Router_2.
135        _pkt = pkts.filter_wpan_src64(vars['Router_1']) \
136            .filter_ipv6_2dsts(vars['Router_2_RLOC'], vars['Router_2_LLA']) \
137            .filter_ping_request() \
138            .must_next()
139
140        pkts.filter_wpan_src64(vars['Router_2']) \
141            .filter_ipv6_dst(_pkt.ipv6.src) \
142            .filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier) \
143            .must_next()
144
145        # 2. Router_1 pings Router_2 multiple times.
146        for i in range(5):
147            _pkt = pkts.filter_wpan_src64(vars['Router_1']) \
148                .filter_ipv6_2dsts(vars['Router_2_RLOC'], vars['Router_2_LLA']) \
149                .filter_ping_request() \
150                .must_next()
151            pkts.filter_wpan_src64(vars['Router_2']) \
152                .filter_ipv6_dst(_pkt.ipv6.src) \
153                .filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier) \
154                .must_next()
155
156        # 3. Router_2 pings Router_1 from the link-local address to the
157        # link-local address.
158        _pkt = pkts.filter_wpan_src64(vars['Router_2']) \
159            .filter_ipv6_src_dst(vars['Router_2_LLA'], vars['Router_1_LLA']) \
160            .filter_ping_request() \
161            .must_next()
162
163        pkts.filter_wpan_src64(vars['Router_1']) \
164            .filter_ipv6_dst(_pkt.ipv6.src) \
165            .filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier) \
166            .must_next()
167
168        # 4. Router_2 pings Router_3 using the RLOC.
169        _pkt = pkts.filter_wpan_src64(vars['Router_2']) \
170            .filter_ipv6_dst(vars['Router_3_RLOC']) \
171            .filter_ping_request() \
172            .must_next()
173
174        pkts.filter_wpan_src64(vars['Router_3']) \
175            .filter_ipv6_dst(_pkt.ipv6.src) \
176            .filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier) \
177            .must_next()
178
179        # 5. Router_2 pings Router_3's link-local address. The ping should fail.
180        _pkt = pkts.filter_wpan_src64(vars['Router_2']) \
181            .filter_ipv6_dst(vars['Router_3_LLA']) \
182            .filter_ping_request() \
183            .must_next()
184
185        pkts.filter_wpan_src64(vars['Router_3']) \
186            .filter_ipv6_dst(_pkt.ipv6.src) \
187            .filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier) \
188            .must_not_next()
189
190        # 5. Router_2 pings Router_3's RLOC from the link-local address. The
191        # ping should fail.
192        _pkt = pkts.filter_wpan_src64(vars['Router_2']) \
193            .filter_ipv6_src_dst(vars['Router_2_LLA'], vars['Router_3_RLOC']) \
194            .filter_ping_request() \
195            .must_next()
196
197
198if __name__ == '__main__':
199    unittest.main()
200