1#!/usr/bin/env python3 2# 3# Copyright (c) 2020, 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# This test verifies PBBR's ND Proxy feature: 30# 1. Sends ICMPv6 Neighbor Advertisement (NA) for ND Proxy 31# 2. Receive and handle ICMPv6 Neighbor Solicitation (NS) and sends NA accordingly. 32# 33import ipaddress 34import unittest 35 36import config 37import thread_cert 38from pktverify.packet_verifier import PacketVerifier 39from pktverify.consts import DUA_RECENT_TIME 40 41PBBR = 1 42SBBR = 2 43ROUTER1 = 3 44HOST = 4 45 46REREG_DELAY = 5 # Seconds 47MLR_TIMEOUT = 300 # Seconds 48WAIT_REDUNDANCE = 3 49 50 51class TestNdProxy(thread_cert.TestCase): 52 USE_MESSAGE_FACTORY = False 53 54 # Topology: 55 # ---- -(eth)------------ 56 # | | | 57 # PBBR----SBBR HOST 58 # \ / 59 # Router1 60 # 61 TOPOLOGY = { 62 PBBR: { 63 'name': 'PBBR', 64 'allowlist': [SBBR, ROUTER1], 65 'is_otbr': True, 66 'version': '1.2', 67 }, 68 SBBR: { 69 'name': 'SBBR', 70 'allowlist': [PBBR, ROUTER1], 71 'is_otbr': True, 72 'version': '1.2', 73 }, 74 ROUTER1: { 75 'name': 'ROUTER1', 76 'allowlist': [PBBR, SBBR], 77 'version': '1.2', 78 }, 79 HOST: { 80 'name': 'HOST', 81 'is_host': True 82 }, 83 } 84 85 def test(self): 86 # Bring up HOST 87 self.nodes[HOST].start() 88 89 # Bring up PBBR 90 self.nodes[PBBR].start() 91 self.simulator.go(config.LEADER_STARTUP_DELAY) 92 self.assertEqual('leader', self.nodes[PBBR].get_state()) 93 self.wait_node_state(PBBR, 'leader', 10) 94 95 self.nodes[PBBR].set_backbone_router(reg_delay=REREG_DELAY, mlr_timeout=MLR_TIMEOUT) 96 self.nodes[PBBR].enable_backbone_router() 97 self.nodes[PBBR].set_domain_prefix(config.DOMAIN_PREFIX, 'prosD') 98 self.simulator.go(5) 99 self.assertTrue(self.nodes[PBBR].is_primary_backbone_router) 100 self.assertIsNotNone(self.nodes[PBBR].get_ip6_address(config.ADDRESS_TYPE.DUA)) 101 102 # Bring up SBBR 103 self.nodes[SBBR].start() 104 self.simulator.go(5) 105 self.assertEqual('router', self.nodes[SBBR].get_state()) 106 107 self.nodes[SBBR].set_backbone_router(reg_delay=REREG_DELAY, mlr_timeout=MLR_TIMEOUT) 108 self.nodes[SBBR].enable_backbone_router() 109 self.simulator.go(5) 110 self.assertFalse(self.nodes[SBBR].is_primary_backbone_router) 111 self.assertIsNotNone(self.nodes[SBBR].get_ip6_address(config.ADDRESS_TYPE.DUA)) 112 113 # Bring up ROUTER1 114 self.nodes[ROUTER1].start() 115 self.simulator.go(5) 116 self.assertEqual('router', self.nodes[ROUTER1].get_state()) 117 self.assertIsNotNone(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.DUA)) 118 119 self.collect_ipaddrs() 120 self.collect_rloc16s() 121 122 # Wait for DUA_RECENT_TIME 123 self.simulator.go(DUA_RECENT_TIME + WAIT_REDUNDANCE) 124 125 self._test_neighbor_solicitation(PBBR) 126 self._test_neighbor_solicitation(SBBR) 127 self._test_neighbor_solicitation(ROUTER1) 128 129 def _test_neighbor_solicitation(self, nodeid): 130 dua = self.nodes[nodeid].get_ip6_address(config.ADDRESS_TYPE.DUA) 131 print('Node DUA: ', dua) 132 self.nodes[HOST].ip_neighbors_flush() 133 self.nodes[HOST].ping(dua, backbone=True) 134 self.simulator.go(WAIT_REDUNDANCE) 135 136 def verify(self, pv: PacketVerifier): 137 pkts = pv.pkts 138 pv.add_common_vars() 139 pv.summary.show() 140 141 PBBR_DUA = pv.vars['PBBR_DUA'] 142 SBBR_DUA = pv.vars['SBBR_DUA'] 143 ROUTER1_DUA = pv.vars['ROUTER1_DUA'] 144 PBBR_ETH = pv.vars['PBBR_ETH'] 145 SBBR_ETH = pv.vars['SBBR_ETH'] 146 HOST_ETH = pv.vars['HOST_ETH'] 147 HOST_BGUA = pv.vars['HOST_BGUA'] 148 149 # SBBR must not send NA for any DUA 150 pkts.filter_eth_src(SBBR_ETH).filter_LLANMA().filter_icmpv6_nd_na(PBBR_DUA).must_not_next() 151 pkts.filter_eth_src(SBBR_ETH).filter_LLANMA().filter_icmpv6_nd_na(SBBR_DUA).must_not_next() 152 pkts.filter_eth_src(SBBR_ETH).filter_LLANMA().filter_icmpv6_nd_na(ROUTER1_DUA).must_not_next() 153 154 # PBBR must send unsolicited NA for PBBR's DUA 155 pkts.filter_eth_src(PBBR_ETH).filter_LLANMA().filter_icmpv6_nd_na(PBBR_DUA).filter( 156 'icmpv6.nd.na.flag.s == 0').must_next().must_verify(""" 157 icmpv6.opt.linkaddr == {PBBR_ETH} 158 and icmpv6.nd.na.flag.r == 1 159 and icmpv6.nd.na.flag.o == 1 160 and icmpv6.nd.na.flag.rsv == 0 161 """, 162 PBBR_ETH=PBBR_ETH) 163 164 # PBBR must send unsolicited NA for SBBR's DUA 165 pkts.filter_eth_src(PBBR_ETH).filter_LLANMA().filter_icmpv6_nd_na(SBBR_DUA).filter( 166 'icmpv6.nd.na.flag.s == 0').must_next().must_verify(""" 167 icmpv6.opt.linkaddr == {PBBR_ETH} 168 and icmpv6.nd.na.flag.r == 1 169 and icmpv6.nd.na.flag.o == 1 170 and icmpv6.nd.na.flag.rsv == 0 171 """, 172 PBBR_ETH=PBBR_ETH) 173 174 # PBBR must send unsolicited NA for ROUTER1's DUA 175 pkts.filter_eth_src(PBBR_ETH).filter_LLANMA().filter_icmpv6_nd_na(ROUTER1_DUA).filter( 176 'icmpv6.nd.na.flag.s == 0').must_next().must_verify(""" 177 icmpv6.opt.linkaddr == {PBBR_ETH} 178 and icmpv6.nd.na.flag.r == 1 179 and icmpv6.nd.na.flag.o == 1 180 and icmpv6.nd.na.flag.rsv == 0 181 """, 182 PBBR_ETH=PBBR_ETH) 183 184 # HOST should send NS for PBBR's DUA 185 pkts.filter_eth_src(HOST_ETH).filter_icmpv6_nd_ns(PBBR_DUA).must_next() 186 # PBBR must send solicited NA for PBBR's DUA 187 pkts.filter_eth_src(PBBR_ETH).filter_ipv6_dst(HOST_BGUA).filter_icmpv6_nd_na(PBBR_DUA).filter( 188 'icmpv6.nd.na.flag.s == 1').must_next().must_verify(""" 189 icmpv6.opt.linkaddr == {PBBR_ETH} 190 and icmpv6.nd.na.flag.r == 1 191 and icmpv6.nd.na.flag.o == 0 192 and icmpv6.nd.na.flag.rsv == 0 193 """, 194 PBBR_ETH=PBBR_ETH) 195 196 # Host should send NS for SBBR's DUA 197 pkts.filter_eth_src(HOST_ETH).filter_icmpv6_nd_ns(SBBR_DUA).must_next() 198 # PBBR must send solicited NA for SBBR's DUA 199 pkts.filter_eth_src(PBBR_ETH).filter_ipv6_dst(HOST_BGUA).filter_icmpv6_nd_na(SBBR_DUA).filter( 200 'icmpv6.nd.na.flag.s == 1').must_next().must_verify(""" 201 icmpv6.opt.linkaddr == {PBBR_ETH} 202 and icmpv6.nd.na.flag.r == 1 203 and icmpv6.nd.na.flag.o == 0 204 and icmpv6.nd.na.flag.rsv == 0 205 """, 206 PBBR_ETH=PBBR_ETH) 207 208 # Host should send NS for ROUTER1's DUA 209 pkts.filter_eth_src(HOST_ETH).filter_icmpv6_nd_ns(ROUTER1_DUA).must_next() 210 # PBBR must send solicited NA for ROUTER1's DUA 211 pkts.filter_eth_src(PBBR_ETH).filter_ipv6_dst(HOST_BGUA).filter_icmpv6_nd_na(ROUTER1_DUA).filter( 212 'icmpv6.nd.na.flag.s == 1').must_next().must_verify(""" 213 icmpv6.opt.linkaddr == {PBBR_ETH} 214 and icmpv6.nd.na.flag.r == 1 215 and icmpv6.nd.na.flag.o == 0 216 and icmpv6.nd.na.flag.rsv == 0 217 """, 218 PBBR_ETH=PBBR_ETH) 219 220 221if __name__ == '__main__': 222 unittest.main() 223