mirror of
https://github.com/espressif/openthread.git
synced 2026-09-01 14:59:54 +00:00
[firewall] implement packet filtering in OT core (#9402)
This commit implements the packet logic in OT core. It aims to have the same effect as what's already achieved by our iptables-based firewall. Instead of leveraging iptables, this commit filters the border routing packets in user space by checking the source/destination addresses of a packet. This commit also adds a job to do BR regression test when this feature is enabled and iptables-based firewall is disabled.
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
import logging
|
||||
import time
|
||||
import unittest
|
||||
import ipaddress
|
||||
|
||||
@@ -45,12 +46,13 @@ import thread_cert
|
||||
# ----------------(eth)----------------------
|
||||
# | |
|
||||
# BR1 (Leader) HOST
|
||||
# |
|
||||
# ROUTER1
|
||||
# | \
|
||||
# ROUTER1 ROUTER2
|
||||
|
||||
BR1 = 1
|
||||
ROUTER1 = 2
|
||||
HOST = 3
|
||||
ROUTER2 = 3
|
||||
HOST = 4
|
||||
|
||||
|
||||
class Firewall(thread_cert.TestCase):
|
||||
@@ -59,14 +61,19 @@ class Firewall(thread_cert.TestCase):
|
||||
TOPOLOGY = {
|
||||
BR1: {
|
||||
'name': 'BR_1',
|
||||
'allowlist': [ROUTER1],
|
||||
'allowlist': [ROUTER1, ROUTER2],
|
||||
'is_otbr': True,
|
||||
'version': '1.2',
|
||||
'version': '1.3',
|
||||
},
|
||||
ROUTER1: {
|
||||
'name': 'Router_1',
|
||||
'allowlist': [BR1],
|
||||
'version': '1.2',
|
||||
'version': '1.3',
|
||||
},
|
||||
ROUTER2: {
|
||||
'name': 'Router_2',
|
||||
'allowlist': [BR1],
|
||||
'version': '1.3',
|
||||
},
|
||||
HOST: {
|
||||
'name': 'Host',
|
||||
@@ -78,16 +85,20 @@ class Firewall(thread_cert.TestCase):
|
||||
br1 = self.nodes[BR1]
|
||||
self.br1 = br1
|
||||
router1 = self.nodes[ROUTER1]
|
||||
router2 = self.nodes[ROUTER2]
|
||||
host = self.nodes[HOST]
|
||||
|
||||
br1.start()
|
||||
self.simulator.go(config.LEADER_STARTUP_DELAY)
|
||||
self.assertEqual('leader', br1.get_state())
|
||||
br1.set_log_level(5)
|
||||
|
||||
router1.start()
|
||||
router2.start()
|
||||
host.start(start_radvd=True)
|
||||
self.simulator.go(config.ROUTER_STARTUP_DELAY)
|
||||
self.assertEqual('router', router1.get_state())
|
||||
self.assertEqual('router', router2.get_state())
|
||||
|
||||
br1.set_domain_prefix(config.DOMAIN_PREFIX, 'prosD')
|
||||
br1.register_netdata()
|
||||
@@ -125,13 +136,13 @@ class Firewall(thread_cert.TestCase):
|
||||
interface=router1.get_rloc(),
|
||||
add_interface=True))
|
||||
|
||||
# 4. Host pings router1's OMR from BR1's OMR.
|
||||
# 4. Host pings router1's OMR from router2's OMR.
|
||||
self.assertFalse(
|
||||
host_ping_ether(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
|
||||
interface=br1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
|
||||
interface=router2.get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
|
||||
add_interface=True))
|
||||
|
||||
# 5. Host pings router1's OMR from router1's MLE-ID.
|
||||
# 5. Host pings router1's OMR from router1's ML-EID.
|
||||
self.assertFalse(
|
||||
host_ping_ether(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
|
||||
interface=router1.get_mleid(),
|
||||
@@ -147,7 +158,7 @@ class Firewall(thread_cert.TestCase):
|
||||
add_route=True,
|
||||
gateway=br1.get_ip6_address(config.ADDRESS_TYPE.BACKBONE_GUA)))
|
||||
|
||||
# 7. Host pings router1's MLE-ID from host's ULA address.
|
||||
# 7. Host pings router1's ML-EID from host's ULA address.
|
||||
self.assertFalse(
|
||||
host_ping_ether(router1.get_mleid(),
|
||||
interface=host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0],
|
||||
@@ -175,13 +186,21 @@ class Firewall(thread_cert.TestCase):
|
||||
interface=router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
|
||||
add_interface=True))
|
||||
|
||||
# 12. Host pings MA1 from router1's MLE-ID.
|
||||
# 12. Host pings MA1 from router1's ML-EID.
|
||||
self.assertFalse(host_ping_ether(MA1, ttl=10, interface=router1.get_mleid(), add_interface=True))
|
||||
|
||||
# 13. Router1 pings Host from router1's MLE-ID.
|
||||
# 13. Router1 pings Host from router1's ML-EID.
|
||||
self.assertFalse(
|
||||
router1.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0], interface=router1.get_mleid()))
|
||||
|
||||
# 14. BR pings router1's ML-EID from BR's ML-EID.
|
||||
self.assertTrue(br1.ping_ether(router1.get_mleid(), interface=br1.get_mleid()))
|
||||
|
||||
# 15. BR pings router1's OMR from BR's infra interface.
|
||||
self.assertTrue(
|
||||
br1.ping_ether(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
|
||||
interface=br1.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
|
||||
|
||||
self.collect_ipaddrs()
|
||||
self.collect_rlocs()
|
||||
self.collect_rloc16s()
|
||||
@@ -215,13 +234,13 @@ class Firewall(thread_cert.TestCase):
|
||||
pkts.filter_wpan_src64(vars['BR_1']).filter_wpan_dst16(
|
||||
vars['Router_1_RLOC16']).filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next()
|
||||
|
||||
# 4. Host pings router1's OMR from BR1's OMR.
|
||||
# 4. Host pings router1's OMR from router2's OMR.
|
||||
_pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_src_dst(
|
||||
vars['BR_1_OMR'][0], vars['Router_1_OMR'][0]).filter_ping_request().must_next()
|
||||
vars['Router_2_OMR'][0], vars['Router_1_OMR'][0]).filter_ping_request().must_next()
|
||||
pkts.filter_wpan_src64(vars['BR_1']).filter_wpan_dst64(
|
||||
vars['Router_1']).filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next()
|
||||
|
||||
# 5. Host pings router1's OMR from router1's MLE-ID.
|
||||
# 5. Host pings router1's OMR from router1's ML-EID.
|
||||
_pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_src_dst(
|
||||
vars['Router_1_MLEID'], vars['Router_1_OMR'][0]).filter_ping_request().must_next()
|
||||
pkts.filter_wpan_src64(vars['BR_1']).filter_wpan_dst16(
|
||||
@@ -233,7 +252,7 @@ class Firewall(thread_cert.TestCase):
|
||||
pkts.filter_wpan_src64(vars['BR_1']).filter_wpan_dst16(
|
||||
vars['Router_1_RLOC16']).filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next()
|
||||
|
||||
# 7. Host pings router1's MLE-ID from host's ULA address.
|
||||
# 7. Host pings router1's ML-EID from host's ULA address.
|
||||
_pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_dst(
|
||||
vars['Router_1_MLEID']).filter_ping_request().must_next()
|
||||
pkts.filter_wpan_src64(vars['BR_1']).filter_wpan_dst16(
|
||||
@@ -263,16 +282,25 @@ class Firewall(thread_cert.TestCase):
|
||||
pkts.filter_wpan_src64(
|
||||
vars['BR_1']).filter_AMPLFMA().filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next()
|
||||
|
||||
# 12. Host pings MA1 from router1's MLE-ID.
|
||||
# 12. Host pings MA1 from router1's ML-EID.
|
||||
_pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_src_dst(vars['Router_1_MLEID'],
|
||||
MA1).filter_ping_request().must_next()
|
||||
pkts.filter_wpan_src64(
|
||||
vars['BR_1']).filter_AMPLFMA().filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next()
|
||||
|
||||
# 13. Router1 pings Host from router1's MLE-ID.
|
||||
# 13. Router1 pings Host from router1's ML-EID.
|
||||
pkts.filter_eth_src(vars['BR_1_ETH']).filter_ipv6_src_dst(
|
||||
vars['Router_1_MLEID'], vars['Host_BGUA']).filter_ping_request().must_not_next()
|
||||
|
||||
# 14. BR pings router1's ML-EID from BR's infra interface.
|
||||
_pkt = pkts.filter_wpan_src64(vars['BR_1']).filter_ipv6_src_dst(
|
||||
vars['BR_1_MLEID'], vars['Router_1_MLEID']).filter_ping_request().must_next()
|
||||
pkts.filter_wpan_src64(vars['Router_1']).filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).must_next()
|
||||
|
||||
# 15. BR pings router1's OMR from BR's infra interface.
|
||||
_pkt = pkts.filter_wpan_src64(vars['BR_1']).filter_ping_request().must_next()
|
||||
pkts.filter_wpan_src64(vars['Router_1']).filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).must_next()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -3630,6 +3630,24 @@ class LinuxHost():
|
||||
|
||||
return resp_count
|
||||
|
||||
def get_ip6_address(self, address_type: config.ADDRESS_TYPE):
|
||||
"""Get specific type of IPv6 address configured on thread device.
|
||||
|
||||
Args:
|
||||
address_type: the config.ADDRESS_TYPE type of IPv6 address.
|
||||
|
||||
Returns:
|
||||
IPv6 address string.
|
||||
"""
|
||||
if address_type == config.ADDRESS_TYPE.BACKBONE_GUA:
|
||||
return self._getBackboneGua()
|
||||
elif address_type == config.ADDRESS_TYPE.ONLINK_ULA:
|
||||
return self._getInfraUla()
|
||||
elif address_type == config.ADDRESS_TYPE.ONLINK_GUA:
|
||||
return self._getInfraGua()
|
||||
else:
|
||||
raise ValueError(f'unsupported address type: {address_type}')
|
||||
|
||||
def _getBackboneGua(self) -> Optional[str]:
|
||||
for addr in self.get_ether_addrs():
|
||||
if re.match(config.BACKBONE_PREFIX_REGEX_PATTERN, addr, re.I):
|
||||
@@ -3885,6 +3903,12 @@ class OtbrNode(LinuxHost, NodeImpl, OtbrDocker):
|
||||
cmd = f'python3 /app/third_party/openthread/repo/tests/scripts/thread-cert/mcast6.py {self.TUN_DEV} {ip} &'
|
||||
self.bash(cmd)
|
||||
|
||||
def get_ip6_address(self, address_type: config.ADDRESS_TYPE):
|
||||
try:
|
||||
return super(OtbrNode, self).get_ip6_address(address_type)
|
||||
except Exception as e:
|
||||
return super(LinuxHost, self).get_ip6_address(address_type)
|
||||
|
||||
|
||||
class HostNode(LinuxHost, OtbrDocker):
|
||||
is_host = True
|
||||
@@ -3922,25 +3946,6 @@ class HostNode(LinuxHost, OtbrDocker):
|
||||
|
||||
return addrs
|
||||
|
||||
def get_ip6_address(self, address_type: config.ADDRESS_TYPE):
|
||||
"""Get specific type of IPv6 address configured on thread device.
|
||||
|
||||
Args:
|
||||
address_type: the config.ADDRESS_TYPE type of IPv6 address.
|
||||
|
||||
Returns:
|
||||
IPv6 address string.
|
||||
"""
|
||||
|
||||
if address_type == config.ADDRESS_TYPE.BACKBONE_GUA:
|
||||
return self._getBackboneGua()
|
||||
elif address_type == config.ADDRESS_TYPE.ONLINK_ULA:
|
||||
return self._getInfraUla()
|
||||
elif address_type == config.ADDRESS_TYPE.ONLINK_GUA:
|
||||
return self._getInfraGua()
|
||||
else:
|
||||
raise ValueError(f'unsupported address type: {address_type}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user