From e11d4136260cf4260e70ed06562b83f890d32487 Mon Sep 17 00:00:00 2001 From: Michael Washer Date: Tue, 2 Jul 2019 15:13:47 +1200 Subject: [PATCH 1/3] Add Kill On Exception flag to API for Chewie --- faucet/faucet.py | 2 +- faucet/faucet_dot1x.py | 15 ++++++++++----- tests/unit/faucet/test_valve.py | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/faucet/faucet.py b/faucet/faucet.py index 4425d929e9..b6918f0218 100644 --- a/faucet/faucet.py +++ b/faucet/faucet.py @@ -110,7 +110,7 @@ def __init__(self, *args, **kwargs): self.bgp = faucet_bgp.FaucetBgp( self.logger, self.exc_logname, self.metrics, self._send_flow_msgs) self.dot1x = faucet_dot1x.FaucetDot1x( - self.logger, self.metrics, self._send_flow_msgs) + self.logger, self.exc_logname, self.metrics, self._send_flow_msgs) self.notifier = faucet_event.FaucetEventNotifier( self.get_setting('EVENT_SOCK'), self.metrics, self.logger) self.valves_manager = valves_manager.ValvesManager( diff --git a/faucet/faucet_dot1x.py b/faucet/faucet_dot1x.py index a72fed6b88..ea40d540f1 100644 --- a/faucet/faucet_dot1x.py +++ b/faucet/faucet_dot1x.py @@ -22,7 +22,7 @@ from ryu.lib import hub # pylint: disable=wrong-import-position from chewie import chewie # pylint: disable=wrong-import-position - +from faucet.valve_util import kill_on_exception def get_mac_str(valve_index, port_num): """Gets the mac address string for the valve/port combo @@ -38,12 +38,15 @@ def get_mac_str(valve_index, port_num): return '00:00:00:%02x:%s' % (valve_index, two_byte_port_num_formatted) -class FaucetDot1x: +class FaucetDot1x: # pylint: disable=too-many-instance-attributes """Wrapper for experimental Chewie 802.1x authenticator.""" - def __init__(self, logger, metrics, send_flow_msgs): + exc_logname = None + + def __init__(self, logger, exc_logname, metrics, send_flow_msgs): self.logger = logger self.metrics = metrics + self.exc_logname = exc_logname self.mac_to_port = {} # {"00:00:00:00:00:02" : (valve_0, port_1)} self.dp_id_to_valve_index = {} self.thread = None @@ -100,12 +103,13 @@ def log_auth_event(self, valve, port_num, mac_str, status): 'eth_src': mac_str, 'status': status}}) - def log_port_event(self, event_type, port_type, valve, port_num): + def log_port_event(self, event_type, port_type, valve, port_num): # pylint: disable=no-self-use """Log a dot1x port event""" valve.dot1x_event({event_type: {'dp_id': valve.dp.dp_id, 'port': port_num, 'port_type': port_type}}) + @kill_on_exception(exc_logname) def auth_handler(self, address, port_id, *args, **kwargs): # pylint: disable=unused-argument """Callback for when a successful auth happens.""" address_str = str(address) @@ -119,6 +123,7 @@ def auth_handler(self, address, port_id, *args, **kwargs): # pylint: disable=un if flowmods: self._send_flow_msgs(valve, flowmods) + @kill_on_exception(exc_logname) def logoff_handler(self, address, port_id): """Callback for when an EAP logoff happens.""" address_str = str(address) @@ -132,6 +137,7 @@ def logoff_handler(self, address, port_id): if flowmods: self._send_flow_msgs(valve, flowmods) + @kill_on_exception(exc_logname) def failure_handler(self, address, port_id): """Callback for when a EAP failure happens.""" address_str = str(address) @@ -348,7 +354,6 @@ def _add_authenticated_flowmod(self, dot1x_port, valve, mac_str, vlan_name): if vlan_name: flowmods.extend(valve.add_dot1x_native_vlan(port_num, vlan_name)) - return flowmods def _del_authenticated_flowmod(self, dot1x_port, valve, mac_str): diff --git a/tests/unit/faucet/test_valve.py b/tests/unit/faucet/test_valve.py index f0f2204042..f5667623c2 100755 --- a/tests/unit/faucet/test_valve.py +++ b/tests/unit/faucet/test_valve.py @@ -387,7 +387,7 @@ def setup_valve(self, config): self.bgp = faucet_bgp.FaucetBgp( self.logger, logfile, self.metrics, self.send_flows_to_dp_by_id) self.dot1x = faucet_dot1x.FaucetDot1x( - self.logger, self.metrics, self.send_flows_to_dp_by_id) + self.logger, logfile, self.metrics, self.send_flows_to_dp_by_id) self.valves_manager = valves_manager.ValvesManager( self.LOGNAME, self.logger, self.metrics, self.notifier, self.bgp, self.dot1x, self.send_flows_to_dp_by_id) From 05192e1b602ddb8c9c1ed3e2928a6b3e8020feef Mon Sep 17 00:00:00 2001 From: Michael Washer Date: Wed, 3 Jul 2019 21:21:08 +1200 Subject: [PATCH 2/3] BUGFIX: Enforce output_only restraint on nfv_sw_port BUGFIX: Enforce output_only restraint on nfv_sw_port Add tests for restraint Change Mininet dot1x tests to use restraint --- faucet/dp.py | 6 ++++++ faucet/valve.py | 18 +++++++++--------- tests/integration/mininet_tests.py | 9 ++++----- tests/unit/faucet/test_config.py | 25 +++++++++++++++++++++++++ tests/unit/faucet/test_valve.py | 3 --- 5 files changed, 44 insertions(+), 17 deletions(-) diff --git a/faucet/dp.py b/faucet/dp.py index 695354356a..a36deff724 100644 --- a/faucet/dp.py +++ b/faucet/dp.py @@ -414,6 +414,12 @@ def _generate_acl_tables(self): all_acls.setdefault('port_acl', []) all_acls['port_acl'].extend(port.acls_in) + if self.dot1x and port.number == self.dot1x['nfv_sw_port']: + test_config_condition(not port.output_only, ( + 'NFV Ports must have output_only set to True.' + )) + + table_config = {} for table_name, acls in all_acls.items(): matches = {} diff --git a/faucet/valve.py b/faucet/valve.py index 8a7dcfc258..955f08e4b2 100644 --- a/faucet/valve.py +++ b/faucet/valve.py @@ -737,6 +737,15 @@ def ports_add(self, port_nums, cold_start=False, log_msg='up'): for manager in self._get_managers(): ofmsgs.extend(manager.add_port(port)) + if self.dp.dot1x: + nfv_sw_port = self.dp.ports[self.dp.dot1x['nfv_sw_port']] + if port == nfv_sw_port: + ofmsgs.extend(self.dot1x.nfv_sw_port_up( + self.dp.dp_id, self.dp.dot1x_ports(), nfv_sw_port)) + elif port.dot1x: + ofmsgs.extend(self.dot1x.port_up( + self.dp.dp_id, port, nfv_sw_port)) + if port.output_only: ofmsgs.append(vlan_table.flowdrop( match=vlan_table.match(in_port=port_num), @@ -758,15 +767,6 @@ def ports_add(self, port_nums, cold_start=False, log_msg='up'): if port.lacp_active: ofmsgs.extend(self._lacp_actions(port.dyn_last_lacp_pkt, port)) - if self.dp.dot1x: - nfv_sw_port = self.dp.ports[self.dp.dot1x['nfv_sw_port']] - if port == nfv_sw_port: - ofmsgs.extend(self.dot1x.nfv_sw_port_up( - self.dp.dp_id, self.dp.dot1x_ports(), nfv_sw_port)) - elif port.dot1x: - ofmsgs.extend(self.dot1x.port_up( - self.dp.dp_id, port, nfv_sw_port)) - port_vlans = port.vlans() # If this is a stacking port, accept all VLANs (came from another FAUCET) diff --git a/tests/integration/mininet_tests.py b/tests/integration/mininet_tests.py index dc24249a59..378dcc1fac 100644 --- a/tests/integration/mininet_tests.py +++ b/tests/integration/mininet_tests.py @@ -209,7 +209,7 @@ class Faucet8021XBaseTest(FaucetTest): native_vlan: 100 # ping host. %(port_4)d: - native_vlan: 100 + output_only: True # "NFV host - interface used by controller." """ @@ -911,8 +911,7 @@ class Faucet8021XCustomACLLoginTest(Faucet8021XBaseTest): %(port_4)d: name: b4 description: "b4" - - native_vlan: 100 + output_only: True # "NFV host - interface used by controller." """ @@ -981,7 +980,7 @@ class Faucet8021XMABTest(Faucet8021XSuccessTest): native_vlan: 100 # ping host. %(port_4)d: - native_vlan: 100 + output_only: True # "NFV host - interface used by controller." """ @@ -1056,7 +1055,7 @@ class Faucet8021XVLANTest(Faucet8021XSuccessTest): native_vlan: radiusassignedvlan1 # ping host. %(port_4)d: - native_vlan: 100 + output_only: True # "NFV host - interface used by controller." """ diff --git a/tests/unit/faucet/test_config.py b/tests/unit/faucet/test_config.py index 4d21fe475c..06a9bf54ba 100755 --- a/tests/unit/faucet/test_config.py +++ b/tests/unit/faucet/test_config.py @@ -3080,6 +3080,31 @@ def test_dot1x_config_valid(self): """ self.check_config_success(config, cp.dp_parser) + def test_dot1x_nfv_port_config_invalid(self): + """Test valid dot1x.""" + config = """ +vlans: + office: + vid: 100 +dps: + sw1: + dp_id: 0x1 + dot1x: + nfv_intf: lo + nfv_sw_port: 2 + radius_ip: ::1 + radius_port: 123 + radius_secret: SECRET + interfaces: + 1: + native_vlan: office + dot1x: True + 2: + output_only: False + """ + self.check_config_failure(config, cp.dp_parser) + + def test_rule_acl_parse(self): """Test simple allow ACL.""" config = """ diff --git a/tests/unit/faucet/test_valve.py b/tests/unit/faucet/test_valve.py index f5667623c2..f635aa612a 100755 --- a/tests/unit/faucet/test_valve.py +++ b/tests/unit/faucet/test_valve.py @@ -1631,9 +1631,6 @@ class ValveDot1xMABSmokeTestCase(ValveDot1xSmokeTestCase): vlans: v100: vid: 0x100 - student: - vid: 0x200 - dot1x_assigned: True """.format(DOT1X_CONFIG) From add8aed15194279071aeed2a24e0990fbef78dc7 Mon Sep 17 00:00:00 2001 From: Michael Washer Date: Wed, 3 Jul 2019 23:29:59 +1200 Subject: [PATCH 3/3] Remove Invalid Usage of NFV Port from 8021XPortFlapTest and 8021XVLANTest --- tests/integration/mininet_tests.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/tests/integration/mininet_tests.py b/tests/integration/mininet_tests.py index 378dcc1fac..fb28570c90 100644 --- a/tests/integration/mininet_tests.py +++ b/tests/integration/mininet_tests.py @@ -748,7 +748,7 @@ def test_untagged(self): self.try_8021x( self.eapol1_host, port_no1, self.wpasupplicant_conf_1, and_logoff=False) self.one_ipv4_ping( - self.eapol1_host, self.nfv_host.IP(), + self.eapol1_host, self.ping_host.IP(), require_host_learned=False, expected_result=False) wpa_status = self.get_wpa_status(self.eapol1_host, self.get_wpa_ctrl_path(self.eapol1_host)) self.assertNotEqual('SUCCESS', wpa_status) @@ -1126,19 +1126,16 @@ def test_untagged(self): self.wait_until_matching_flow( {'vlan_vid': vid}, table_id=self._FLOOD_TABLE, - actions=['POP_VLAN', 'OUTPUT:%s' % port_no2, 'OUTPUT:%s' % port_no4]) + actions=['POP_VLAN', 'OUTPUT:%s' % port_no2]) self.wait_until_no_matching_flow( {'vlan_vid': radius_vid2}, table_id=self._FLOOD_TABLE, - actions=['POP_VLAN', 'OUTPUT:%s' % port_no1, 'OUTPUT:%s' % port_no2, 'OUTPUT:%s' % port_no4]) + actions=['POP_VLAN', 'OUTPUT:%s' % port_no1, 'OUTPUT:%s' % port_no2]) self.one_ipv4_ping( self.eapol1_host, self.ping_host.IP(), require_host_learned=False, expected_result=True) - self.one_ipv4_ping(self.eapol1_host, self.nfv_host.IP(), - require_host_learned=False, expected_result=False) - tcpdump_txt = self.try_8021x( self.eapol1_host, port_no1, self.wpasupplicant_conf_1, and_logoff=True) self.assertIn('Success', tcpdump_txt) @@ -1147,9 +1144,6 @@ def test_untagged(self): self.eapol1_host, self.ping_host.IP(), require_host_learned=False, expected_result=False) - self.one_ipv4_ping(self.eapol1_host, self.nfv_host.IP(), - require_host_learned=False, expected_result=False) - # check ports are back in the right vlans. self.wait_until_no_matching_flow( {'in_port': port_no1}, @@ -1169,7 +1163,7 @@ def test_untagged(self): self.wait_until_matching_flow( {'vlan_vid': vid}, table_id=self._FLOOD_TABLE, - actions=['POP_VLAN', 'OUTPUT:%s' % port_no1, 'OUTPUT:%s' % port_no2, 'OUTPUT:%s' % port_no4]) + actions=['POP_VLAN', 'OUTPUT:%s' % port_no1, 'OUTPUT:%s' % port_no2]) # check two 1x hosts play nicely. (same dyn vlan) tcpdump_txt = self.try_8021x( @@ -1279,7 +1273,7 @@ def test_untagged(self): self.wait_until_matching_flow( {'vlan_vid': vid}, table_id=self._FLOOD_TABLE, - actions=['POP_VLAN', 'OUTPUT:%s' % port_no2, 'OUTPUT:%s' % port_no4]) + actions=['POP_VLAN', 'OUTPUT:%s' % port_no2]) class FaucetUntaggedRandomVidTest(FaucetUntaggedTest):