From 10201d97cc0348554a4d3d08a116bc4371db38ab Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Mon, 4 Mar 2024 10:37:00 -0700 Subject: [PATCH 1/2] Add dhcp snooping and arp inspection to ovs flows --- .../src/net_orc/network_orchestrator.py | 1 + framework/python/src/net_orc/ovs_control.py | 57 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/framework/python/src/net_orc/network_orchestrator.py b/framework/python/src/net_orc/network_orchestrator.py index 8126d0738..14415367c 100644 --- a/framework/python/src/net_orc/network_orchestrator.py +++ b/framework/python/src/net_orc/network_orchestrator.py @@ -193,6 +193,7 @@ def _device_discovered(self, mac_addr): LOGGER.info( f'Device with mac addr {device.mac_addr} has obtained IP address ' f'{device.ip_addr}') + self._ovs.add_arp_inspection_filter(ip_address=device.ip_addr,mac_address=device.mac_addr) self._start_device_monitor(device) diff --git a/framework/python/src/net_orc/ovs_control.py b/framework/python/src/net_orc/ovs_control.py index 80f76e85f..24ac4b217 100644 --- a/framework/python/src/net_orc/ovs_control.py +++ b/framework/python/src/net_orc/ovs_control.py @@ -51,6 +51,12 @@ def add_port(self, port, bridge_name): add-port {bridge_name} {port}""") return success + def delete_flow(self, bridge_name, flow): + # Delete a flow from the bridge using ovs-ofctl commands + LOGGER.debug(f'Deleting flow {flow} from bridge: {bridge_name}') + success = util.run_command(f'ovs-ofctl del-flows {bridge_name} \'{flow}\'') + return success + def get_bridge_ports(self, bridge_name): # Get a list of all the ports on a bridge response = util.run_command(f'ovs-vsctl list-ports {bridge_name}', @@ -125,6 +131,13 @@ def create_baseline_net(self, verify=True): self.add_flow(bridge_name=DEVICE_BRIDGE, flow='table=0, dl_dst=01:80:c2:00:00:03, actions=flood') + # Add a DHCP snooping equivalent to the device bridge + # ToDo Define these IP's dynamically + dhcp_server_primary_ip = '10.10.10.2' + dhcp_server_secondary_ip = '10.10.10.3' + self.add_dhcp_filters(dhcp_server_primary_ip=dhcp_server_primary_ip, + dhcp_server_secondary_ip=dhcp_server_secondary_ip) + # Set ports up self.set_bridge_up(DEVICE_BRIDGE) self.set_bridge_up(INTERNET_BRIDGE) @@ -136,6 +149,50 @@ def create_baseline_net(self, verify=True): else: return None + def add_dhcp_filters(self,dhcp_server_primary_ip,dhcp_server_secondary_ip): + + # Allow DHCP traffic from primary server + allow_primary_dhcp_server = ( + f'table=0, dl_type=0x800, priority=65535, tp_src=67, tp_dst=68, nw_src={dhcp_server_primary_ip}, actions=normal') + self.add_flow(bridge_name=DEVICE_BRIDGE,flow=allow_primary_dhcp_server) + + # Allow DHCP traffic from secondary server + allow_secondary_dhcp_server = ( + f'table=0, dl_type=0x800, priority=65535, tp_src=67, tp_dst=68, nw_src={dhcp_server_secondary_ip}, actions=normal') + self.add_flow(bridge_name=DEVICE_BRIDGE,flow=allow_secondary_dhcp_server) + + # Drop DHCP packets not associated with known servers + drop_dhcp_flow = 'table=0, dl_type=0x800, priority=0, tp_src=67, tp_dst=68, actions=drop' + self.add_flow(bridge_name=DEVICE_BRIDGE,flow=drop_dhcp_flow) + + def add_arp_inspection_filter(self,ip_address,mac_address): + # Combine IP address and MAC address + combined_str = ip_address + mac_address + + # Convert combined string to integer + cookie_value = int(combined_str.replace(':', '').replace('.', '')) + + # Allow ARP packets with known MAC-to-IP mappings + drop_bad_arp= f'table=0, cookie={cookie_value}, priority=65535, arp, arp_tpa={ip_address}, arp_tha={mac_address}, action=normal' + self.add_flow(bridge_name=DEVICE_BRIDGE,flow=drop_bad_arp) + + # Drop ARP packets with unknown MAC-to-IP mappings + drop_unknown_arps = ( + f'table=0, priority=0, arp, ' + f'action=drop' + ) + self.add_flow(bridge_name=DEVICE_BRIDGE,flow=drop_unknown_arps) + + def delete_arp_inspection_filter(self,ip_address,mac_address): + # Combine IP address and MAC address + combined_str = ip_address + mac_address + + # Convert combined string to integer + cookie_value = int(combined_str.replace(':', '').replace('.', '')) + + self.delete_flow(bridge_name=DEVICE_BRIDGE,flow=f'cookie={cookie_value}') + + def delete_bridge(self, bridge_name): LOGGER.debug('Deleting OVS Bridge: ' + bridge_name) # Delete the bridge using ovs-vsctl commands From 74ba26aa64f19132179a38945ba6f1dfb00b5da8 Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Mon, 4 Mar 2024 16:05:28 -0700 Subject: [PATCH 2/2] Update and disable arp inspection flows --- .../src/net_orc/network_orchestrator.py | 6 +++- framework/python/src/net_orc/ovs_control.py | 35 ++++++++++--------- .../python/src/test_orc/test_orchestrator.py | 3 ++ 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/framework/python/src/net_orc/network_orchestrator.py b/framework/python/src/net_orc/network_orchestrator.py index 14415367c..b83feffe4 100644 --- a/framework/python/src/net_orc/network_orchestrator.py +++ b/framework/python/src/net_orc/network_orchestrator.py @@ -193,7 +193,7 @@ def _device_discovered(self, mac_addr): LOGGER.info( f'Device with mac addr {device.mac_addr} has obtained IP address ' f'{device.ip_addr}') - self._ovs.add_arp_inspection_filter(ip_address=device.ip_addr,mac_address=device.mac_addr) + # self._ovs.add_arp_inspection_filter(ip_address=device.ip_addr,mac_address=device.mac_addr) self._start_device_monitor(device) @@ -662,6 +662,10 @@ def _attach_service_to_network(self, net_module): ' to internet bridge ' + DEVICE_BRIDGE + '. Exiting.') sys.exit(1) + def remove_arp_filters(self): + LOGGER.info('Removing ARP inspection filters') + self._ovs.delete_arp_inspection_filter() + def restore_net(self): LOGGER.info('Clearing baseline network') diff --git a/framework/python/src/net_orc/ovs_control.py b/framework/python/src/net_orc/ovs_control.py index 24ac4b217..015d6b7a2 100644 --- a/framework/python/src/net_orc/ovs_control.py +++ b/framework/python/src/net_orc/ovs_control.py @@ -18,7 +18,9 @@ DEVICE_BRIDGE = 'tr-d' INTERNET_BRIDGE = 'tr-c' LOGGER = logger.get_logger('ovs_ctrl') - +DEVICER_ARP_COOKIE = '1000' +UNKNOWN_ARP_COOKIE = '1183' +CONTAINER_MAC_PREFIX = '9a:02:57:1e:8f' class OVSControl: """OVS Control""" @@ -166,31 +168,30 @@ def add_dhcp_filters(self,dhcp_server_primary_ip,dhcp_server_secondary_ip): self.add_flow(bridge_name=DEVICE_BRIDGE,flow=drop_dhcp_flow) def add_arp_inspection_filter(self,ip_address,mac_address): - # Combine IP address and MAC address - combined_str = ip_address + mac_address + # Allow ARP packets with known MAC-to-IP mappings + allow_known_arps= f'table=0, cookie={DEVICER_ARP_COOKIE}, priority=65535, arp, arp_tpa={ip_address}, arp_tha={mac_address}, action=normal' + self.add_flow(bridge_name=DEVICE_BRIDGE,flow=allow_known_arps) - # Convert combined string to integer - cookie_value = int(combined_str.replace(':', '').replace('.', '')) + DHCP1_MAC = f'{CONTAINER_MAC_PREFIX}:02' + DHCP2_MAC = f'{CONTAINER_MAC_PREFIX}:03' + DHCP1_IP = '10.10.10.2' + DHCP2_IP = '10.10.10.3' - # Allow ARP packets with known MAC-to-IP mappings - drop_bad_arp= f'table=0, cookie={cookie_value}, priority=65535, arp, arp_tpa={ip_address}, arp_tha={mac_address}, action=normal' - self.add_flow(bridge_name=DEVICE_BRIDGE,flow=drop_bad_arp) + dhcp_1_arps= f'table=0, priority=65535, arp, arp_tpa={DHCP1_IP}, arp_tha={DHCP1_MAC}, action=normal' + dhcp_2_arps= f'table=0, priority=65535, arp, arp_tpa={DHCP2_IP}, arp_tha={DHCP2_MAC}, action=normal' + self.add_flow(bridge_name=DEVICE_BRIDGE,flow=dhcp_1_arps) + self.add_flow(bridge_name=DEVICE_BRIDGE,flow=dhcp_2_arps) # Drop ARP packets with unknown MAC-to-IP mappings drop_unknown_arps = ( - f'table=0, priority=0, arp, ' + f'table=0, cookie={UNKNOWN_ARP_COOKIE} priority=100, arp, ' f'action=drop' ) self.add_flow(bridge_name=DEVICE_BRIDGE,flow=drop_unknown_arps) - def delete_arp_inspection_filter(self,ip_address,mac_address): - # Combine IP address and MAC address - combined_str = ip_address + mac_address - - # Convert combined string to integer - cookie_value = int(combined_str.replace(':', '').replace('.', '')) - - self.delete_flow(bridge_name=DEVICE_BRIDGE,flow=f'cookie={cookie_value}') + def delete_arp_inspection_filter(self): + self.delete_flow(bridge_name=DEVICE_BRIDGE,flow=f'cookie={DEVICER_ARP_COOKIE}/-1') + self.delete_flow(bridge_name=DEVICE_BRIDGE,flow=f'cookie={UNKNOWN_ARP_COOKIE}/-1') def delete_bridge(self, bridge_name): diff --git a/framework/python/src/test_orc/test_orchestrator.py b/framework/python/src/test_orc/test_orchestrator.py index a81fca4fd..143243d56 100644 --- a/framework/python/src/test_orc/test_orchestrator.py +++ b/framework/python/src/test_orc/test_orchestrator.py @@ -326,6 +326,9 @@ def _run_test_module(self, module): client = docker.from_env() + # if module.name == 'connection': + # self._net_orc.remove_arp_filters() + module.container = client.containers.run( module.image_name, auto_remove=True,