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# 29import logging 30import time 31import unittest 32import ipaddress 33 34import config 35import pktverify 36import pktverify.packet_verifier 37from pktverify.consts import MA1 38import thread_cert 39 40# Test description: 41# This test verifies the functionality of firewall. OTBR will only 42# forward specific kinds of packets between the infra interface and the thread 43# interface. 44# 45# Topology: 46# ----------------(eth)---------------------- 47# | | 48# BR1 (Leader) HOST 49# | \ 50# ROUTER1 ROUTER2 51 52BR1 = 1 53ROUTER1 = 2 54ROUTER2 = 3 55HOST = 4 56 57 58class Firewall(thread_cert.TestCase): 59 USE_MESSAGE_FACTORY = False 60 61 TOPOLOGY = { 62 BR1: { 63 'name': 'BR_1', 64 'allowlist': [ROUTER1, ROUTER2], 65 'is_otbr': True, 66 'version': '1.4', 67 }, 68 ROUTER1: { 69 'name': 'Router_1', 70 'allowlist': [BR1], 71 'version': '1.4', 72 }, 73 ROUTER2: { 74 'name': 'Router_2', 75 'allowlist': [BR1], 76 'version': '1.4', 77 }, 78 HOST: { 79 'name': 'Host', 80 'is_host': True, 81 } 82 } 83 84 def test(self): 85 br1 = self.nodes[BR1] 86 self.br1 = br1 87 router1 = self.nodes[ROUTER1] 88 router2 = self.nodes[ROUTER2] 89 host = self.nodes[HOST] 90 91 br1.start() 92 self.simulator.go(config.LEADER_STARTUP_DELAY) 93 self.assertEqual('leader', br1.get_state()) 94 br1.set_log_level(5) 95 96 router1.start() 97 router2.start() 98 host.start(start_radvd=True) 99 self.simulator.go(config.ROUTER_STARTUP_DELAY) 100 self.assertEqual('router', router1.get_state()) 101 self.assertEqual('router', router2.get_state()) 102 103 br1.set_domain_prefix(config.DOMAIN_PREFIX, 'prosD') 104 br1.register_netdata() 105 106 router1.add_ipmaddr(MA1) 107 router1.register_netdata() 108 109 self.simulator.go(20) 110 111 def host_ping_ether(dest, interface, ttl=10, add_interface=False, add_route=False, gateway=None): 112 if add_interface: 113 host.bash(f'ip -6 addr add {interface}/64 dev {host.ETH_DEV} scope global') 114 route = str(ipaddress.IPv6Network(f'{interface}/64', strict=False)) 115 host.bash(f'ip -6 route del {route} dev {host.ETH_DEV}') 116 if add_route: 117 host.bash(f'ip -6 route add {dest} dev {host.ETH_DEV} via {gateway}') 118 self.simulator.go(2) 119 result = host.ping_ether(dest, ttl=ttl, interface=interface) 120 if add_route: 121 host.bash(f'ip -6 route del {dest}') 122 if add_interface: 123 host.bash(f'ip -6 addr del {interface}/64 dev {host.ETH_DEV} scope global') 124 self.simulator.go(1) 125 return result 126 127 # 1. Host pings router1's OMR from host's infra address. 128 self.assertTrue(host_ping_ether(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], interface=host.ETH_DEV)) 129 130 # 2. Host pings router1's DUA from host's infra address. 131 self.assertTrue(host_ping_ether(router1.get_ip6_address(config.ADDRESS_TYPE.DUA), interface=host.ETH_DEV)) 132 133 # 3. Host pings router1's OMR from router1's RLOC. 134 self.assertFalse( 135 host_ping_ether(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], 136 interface=router1.get_rloc(), 137 add_interface=True)) 138 139 # 4. Host pings router1's OMR from router2's OMR. 140 self.assertFalse( 141 host_ping_ether(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], 142 interface=router2.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], 143 add_interface=True)) 144 145 # 5. Host pings router1's OMR from router1's ML-EID. 146 self.assertFalse( 147 host_ping_ether(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], 148 interface=router1.get_mleid(), 149 add_interface=True)) 150 151 host.bash(f'ip -6 route add {config.MESH_LOCAL_PREFIX} dev {host.ETH_DEV}') 152 self.simulator.go(5) 153 154 # 6. Host pings router1's RLOC from host's ULA address. 155 self.assertFalse( 156 host_ping_ether(router1.get_rloc(), 157 interface=host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0], 158 add_route=True, 159 gateway=br1.get_ip6_address(config.ADDRESS_TYPE.BACKBONE_GUA))) 160 161 # 7. Host pings router1's ML-EID from host's ULA address. 162 self.assertFalse( 163 host_ping_ether(router1.get_mleid(), 164 interface=host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0], 165 add_route=True, 166 gateway=br1.get_ip6_address(config.ADDRESS_TYPE.BACKBONE_GUA))) 167 168 # 8. Host pings router1's link-local address from host's infra address. 169 self.assertFalse( 170 host_ping_ether(router1.get_linklocal(), 171 interface=host.ETH_DEV, 172 add_route=True, 173 gateway=br1.get_ip6_address(config.ADDRESS_TYPE.BACKBONE_GUA))) 174 175 # 9. Host pings MA1 from host's ULA address. 176 self.assertTrue(host_ping_ether(MA1, ttl=10, 177 interface=host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) 178 179 # 10. Host pings MA1 from router1's RLOC. 180 self.assertFalse(host_ping_ether(MA1, ttl=10, interface=router1.get_rloc(), add_interface=True)) 181 182 # 11. Host pings MA1 from router1's OMR. 183 self.assertFalse( 184 host_ping_ether(MA1, 185 ttl=10, 186 interface=router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], 187 add_interface=True)) 188 189 # 12. Host pings MA1 from router1's ML-EID. 190 self.assertFalse(host_ping_ether(MA1, ttl=10, interface=router1.get_mleid(), add_interface=True)) 191 192 # 13. Router1 pings Host from router1's ML-EID. 193 self.assertFalse( 194 router1.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0], interface=router1.get_mleid())) 195 196 # 14. BR pings router1's ML-EID from BR's ML-EID. 197 self.assertTrue(br1.ping_ether(router1.get_mleid(), interface=br1.get_mleid())) 198 199 # 15. BR pings router1's OMR from BR's infra interface. 200 self.assertTrue( 201 br1.ping_ether(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], 202 interface=br1.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) 203 204 # 16. Host sends a UDP packet to router1's OMR address's TMF port. 205 host.udp_send_host(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], config.TMF_PORT, "HELLO") 206 207 # 17. BR1 sends a UDP packet to router1's ML-EID's TMF port. 208 br1.udp_send_host(router1.get_mleid(), config.TMF_PORT, "BYE") 209 210 # 18. BR1 sends a UDP packet to its own ML-EID's TMF port. 211 br1.udp_send_host(br1.get_mleid(), config.TMF_PORT, "SELF") 212 213 self.collect_ipaddrs() 214 self.collect_rlocs() 215 self.collect_rloc16s() 216 self.collect_extra_vars() 217 self.collect_omrs() 218 self.collect_duas() 219 220 def verify(self, pv: pktverify.packet_verifier.PacketVerifier): 221 pkts = pv.pkts 222 vars = pv.vars 223 pv.summary.show() 224 logging.info(f'vars = {vars}') 225 226 pv.verify_attached('Router_1', 'BR_1') 227 228 # 1. Host pings router1's OMR from host's infra address. 229 _pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_dst( 230 vars['Router_1_OMR'][0]).filter_ping_request().must_next() 231 pkts.filter_wpan_src64(vars['BR_1']).filter_wpan_dst16( 232 vars['Router_1_RLOC16']).filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_next() 233 234 # 2. Host pings router1's DUA from host's infra address. 235 _pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_dst( 236 vars['Router_1_DUA']).filter_ping_request().must_next() 237 pkts.filter_wpan_src64(vars['BR_1']).filter_wpan_dst16( 238 vars['Router_1_RLOC16']).filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_next() 239 240 # 3. Host pings router1's OMR from router1's RLOC. 241 _pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_src_dst( 242 vars['Router_1_RLOC'], vars['Router_1_OMR'][0]).filter_ping_request().must_next() 243 pkts.filter_wpan_src64(vars['BR_1']).filter_wpan_dst16( 244 vars['Router_1_RLOC16']).filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next() 245 246 # 4. Host pings router1's OMR from router2's OMR. 247 _pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_src_dst( 248 vars['Router_2_OMR'][0], vars['Router_1_OMR'][0]).filter_ping_request().must_next() 249 pkts.filter_wpan_src64(vars['BR_1']).filter_wpan_dst64( 250 vars['Router_1']).filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next() 251 252 # 5. Host pings router1's OMR from router1's ML-EID. 253 _pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_src_dst( 254 vars['Router_1_MLEID'], vars['Router_1_OMR'][0]).filter_ping_request().must_next() 255 pkts.filter_wpan_src64(vars['BR_1']).filter_wpan_dst16( 256 vars['Router_1_RLOC16']).filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next() 257 258 # 6. Host pings router1's RLOC from host's ULA address. 259 _pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_dst( 260 vars['Router_1_RLOC']).filter_ping_request().must_next() 261 pkts.filter_wpan_src64(vars['BR_1']).filter_wpan_dst16( 262 vars['Router_1_RLOC16']).filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next() 263 264 # 7. Host pings router1's ML-EID from host's ULA address. 265 _pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_dst( 266 vars['Router_1_MLEID']).filter_ping_request().must_next() 267 pkts.filter_wpan_src64(vars['BR_1']).filter_wpan_dst16( 268 vars['Router_1_RLOC16']).filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next() 269 270 # 8. Host pings router1's link-local address from host's infra address. 271 # Skip this scenario as for now 272 # _pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_dst( 273 # vars['Router_1_LLA']).filter_ping_request().must_next() 274 pkts.filter_wpan_src64(vars['BR_1']).filter_wpan_dst16( 275 vars['Router_1_RLOC16']).filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next() 276 277 # 9. Host pings MA1 from host's ULA address. 278 _pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_dst(MA1).filter_ping_request().must_next() 279 pkts.filter_wpan_src64( 280 vars['BR_1']).filter_AMPLFMA().filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_next() 281 282 # 10. Host pings MA1 from router1's RLOC. 283 _pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_src_dst(vars['Router_1_RLOC'], 284 MA1).filter_ping_request().must_next() 285 pkts.filter_wpan_src64( 286 vars['BR_1']).filter_AMPLFMA().filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next() 287 288 # 11. Host pings MA1 from router1's OMR. 289 _pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_src_dst(vars['Router_1_OMR'][0], 290 MA1).filter_ping_request().must_next() 291 pkts.filter_wpan_src64( 292 vars['BR_1']).filter_AMPLFMA().filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next() 293 294 # 12. Host pings MA1 from router1's ML-EID. 295 _pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_src_dst(vars['Router_1_MLEID'], 296 MA1).filter_ping_request().must_next() 297 pkts.filter_wpan_src64( 298 vars['BR_1']).filter_AMPLFMA().filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next() 299 300 # 13. Router1 pings Host from router1's ML-EID. 301 pkts.filter_eth_src(vars['BR_1_ETH']).filter_ipv6_src_dst( 302 vars['Router_1_MLEID'], vars['Host_BGUA']).filter_ping_request().must_not_next() 303 304 # 14. BR pings router1's ML-EID from BR's ML-EID. 305 _pkt = pkts.filter_wpan_src64(vars['BR_1']).filter_ipv6_src_dst( 306 vars['BR_1_MLEID'], vars['Router_1_MLEID']).filter_ping_request().must_next() 307 pkts.filter_wpan_src64(vars['Router_1']).filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).must_next() 308 309 # 15. BR pings router1's OMR from BR's infra interface. 310 _pkt = pkts.filter_wpan_src64(vars['BR_1']).filter_ping_request().must_next() 311 pkts.filter_wpan_src64(vars['Router_1']).filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).must_next() 312 313 # 16. Host sends a UDP packet to router1's OMR address's TMF port (61631). 314 # The packet should be able to reach BR1 but BR1 won't forward it to Thread. 315 pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_dst(vars['Router_1_OMR'][0]).filter( 316 lambda p: p.udp.dstport == config.TMF_PORT and p.udp.length == len("HELLO") + 8).must_next() 317 pkts.filter_wpan_src64(vars['BR_1']).filter_ipv6_dst(vars['Router_1_OMR'][0]).filter( 318 lambda p: p.udp.dstport == config.TMF_PORT and p.udp.length == len("HELLO") + 8).must_not_next() 319 320 # 17. BR1 sends a UDP packet to router1's ML-EID's TMF port (61631). 321 # The packet should be dropped by BR1, so it should not be present in Thread. 322 pkts.filter_wpan_src64(vars['BR_1']).filter_ipv6_dst(vars['Router_1_MLEID']).filter( 323 lambda p: p.udp.dstport == config.TMF_PORT and p.udp.length == len("BYE") + 8).must_not_next() 324 325 # 18. BR1 sends a UDP packet to its own ML-EID's TMF port (61631). 326 # The packet should be dropped by BR1, so it should not be present anywhere. 327 pkts.filter_wpan_src64(vars['BR_1']).filter_ipv6_dst(vars['BR_1_MLEID']).filter( 328 lambda p: p.udp.dstport == config.TMF_PORT and p.udp.length == len("SELF") + 8).must_not_next() 329 330 331if __name__ == '__main__': 332 unittest.main() 333