def test_auth_learn(self): """Test to validate MAC authentication status""" device_placement = DevicePlacement(switch='switch', port=1, connected=True) self._forchestrator._process_device_placement('00:11:22:33:44:55', device_placement) self.assertEqual(self._get_auth_sm_state('00:11:22:33:44:55'), 'RADIUS Request') self._forchestrator._authenticator.process_radius_result( '00:11:22:33:44:55', 'ACCEPT', 'ACCEPT', None) self.assertEqual(self._get_auth_sm_state('00:11:22:33:44:55'), 'Authorized') device_placement = DevicePlacement(switch='switch', port=1, connected=False) self._forchestrator._process_device_placement('00:11:22:33:44:55', device_placement, expired_vlan=200) self.assertEqual(self._get_auth_sm_state('00:11:22:33:44:55'), 'Authorized') self._forchestrator._process_device_placement('00:11:22:33:44:55', device_placement, expired_vlan=100) self.assertEqual(self._get_auth_sm_state('00:11:22:33:44:55'), None)
def process_device_placement(self, eth_src, placement, static=False): """Process device placement""" if not placement.switch or not placement.port: raise Exception(f'Incomplete placement for {eth_src}: {placement}') devices_state = self._static_devices if static else self._dynamic_devices device_type = "static" if static else "dynamic" eth_src = eth_src.lower() with self._lock: device_placements = devices_state.device_mac_placements if placement.connected: device_placement = device_placements.setdefault( eth_src, DevicePlacement()) device_placement.CopyFrom(placement) self._logger.info('Received %s placement: %s, %s, %s', device_type, eth_src, device_placement.switch, device_placement.port) else: removed = device_placements.pop(eth_src, None) if removed: self._logger.info('Removed %s placement: %s, %s, %s', device_type, eth_src, removed.switch, removed.port) self.flush_behavioral_config()
def _handle_learned_device(self, mac, device_placement, static=False): # if device is learned old_mac = self._placement_to_mac.get( (device_placement.switch, device_placement.port)) stale_mac = old_mac if old_mac and old_mac != mac else None if stale_mac: switch = device_placement.switch port = device_placement.port self._logger.warning('Cleaning stale device placement: %s, %s, %s', old_mac, switch, port) stale_placement = DevicePlacement(switch=switch, port=port, connected=False) self._handle_disconnected_device(stale_placement) self._placement_to_mac[(device_placement.switch, device_placement.port)] = mac self._process_device_placement(mac, device_placement, static=static) if mac not in self._state_machines: self._state_machines[mac] = PortStateMachine( mac, PortStateMachine.UNAUTHENTICATED, self._handle_unauthenticated_state, self._set_port_sequestered, self._set_port_operational, self._handle_infracted_state) device_behavior = (self._static_device_behaviors.get(mac) or self._dynamic_device_behaviors.get(mac)) if device_behavior: static = mac in self._static_device_behaviors self.handle_device_behavior(mac, device_behavior, static=static) return True, None, stale_mac
def _handle_learned_device(self, mac, device_placement, static=False): old_mac = self._placement_to_mac.get( (device_placement.switch, device_placement.port)) stale_mac = old_mac if old_mac and old_mac != mac else None if stale_mac: switch = device_placement.switch port = device_placement.port self._logger.warning('Cleaning stale device placement: %s, %s, %s', old_mac, switch, port) stale_placement = DevicePlacement(switch=switch, port=port, connected=False) self._handle_disconnected_device(stale_placement) self._placement_to_mac[(device_placement.switch, device_placement.port)] = mac self._process_device_placement(mac, device_placement, static=static) if mac not in self._state_machines: self._state_machines[mac] = PortStateMachine( mac, PortStateMachine.UNAUTHENTICATED, self._state_callbacks, state_overwrites=self._state_overwrites) device_behavior = (self._static_device_behaviors.get(mac) or self._dynamic_device_behaviors.get(mac)) if device_behavior: static_behavior = mac in self._static_device_behaviors self.handle_device_behavior(mac, device_behavior, static=static_behavior) return True, None, stale_mac
def _expire_devices(self, expired_device_placements, expected_device_placements): for expired_device_placement in expired_device_placements: mac = None switch = expired_device_placement[1][0] port = expired_device_placement[1][1] self._port_state_manager.handle_device_placement( mac, DevicePlacement(switch=switch, port=port), False) expected_device_placements.extend([ # mac, device_placement.connected, static ('00:0X:00:00:00:01', False, False), ('00:0B:00:00:00:05', False, False) ]) self._verify_received_device_placements(expected_device_placements)
def _process_device_placement(self, eth_src, device_placement, static=False): """Call device placement API for faucetizer/authenticator""" propagate_placement, mac, stale_mac = self._port_state_manager.handle_device_placement( eth_src, device_placement, static) src_mac = mac if mac else eth_src if self._authenticator and propagate_placement: if stale_mac: self._authenticator.process_device_placement( stale_mac, DevicePlacement(connected=False)) self._authenticator.process_device_placement( src_mac, device_placement) else: self._logger.info('Ignored deauthentication for %s on %s:%s', src_mac, device_placement.switch, device_placement.port)
def _update_device_placements(self): try: metrics = self._varz_state_collector.retry_get_faucet_metrics( [self._DEVICE_LEARNING_METRIC]) except Exception as e: LOGGER.error('Could not get %s metric: %s', self._DEVICE_LEARNING_METRIC, e) device_learning_metric = metrics.get(self._DEVICE_LEARNING_METRIC) if not device_learning_metric: LOGGER.info('No devices are learned') self._device_placements = {} for sample in device_learning_metric.samples: if sample.labels.get('dp_name') != self._SEC_SWITCH: continue mac = sample.labels.get('eth_src') if mac not in self._duts: continue port = int(sample.value) self._device_placements[mac] = DevicePlacement( switch=self._SEC_SWITCH, port=port)
{ 'radius_info': { 'server_ip': ARGS.server_ip, 'server_port': ARGS.server_port, 'source_port': ARGS.source_port, 'radius_secret_helper': f'echo {ARGS.radius_secret}' } }, OrchestrationConfig.AuthConfig) MOCK_RADIUS_QUERY = MockRadiusQuery() if ARGS.mab: AUTHENTICATOR = Authenticator(AUTH_CONFIG, mock_auth_callback) AUTHENTICATOR.do_mab_request(ARGS.src_mac, ARGS.port_id) input('Press any key to exit.') else: # test radius query call for device placement AUTHENTICATOR = Authenticator(AUTH_CONFIG, mock_auth_callback, MOCK_RADIUS_QUERY) TEST_MAC = '00:aa:bb:cc:dd:ee' DEV_PLACEMENT = DevicePlacement(switch='t2s2', port=1, connected=True) AUTHENTICATOR.process_device_placement(TEST_MAC, DEV_PLACEMENT) assert MOCK_RADIUS_QUERY.get_last_mac_queried() == TEST_MAC # test positive RADIUS response CODE = radius_query.ACCEPT SEGMENT = 'test' ROLE = 'test' EXPECTED_MAB_RESULT[TEST_MAC] = {'segment': SEGMENT, 'role': ROLE} AUTHENTICATOR.process_radius_result(TEST_MAC, CODE, SEGMENT, ROLE) EXPECTED_MAB_RESULT.pop(TEST_MAC)
def test_ports_states(self): """Test the port states with different signals""" static_device_behaviors = { '00:0X:00:00:00:01': {'segment': 'SEG_A', 'port_behavior': 'cleared'}, '00:0Y:00:00:00:02': {'port_behavior': 'cleared'} } authentication_results = { '00:0X:00:00:00:01': {'segment': 'SEG_X'}, '00:0Z:00:00:00:03': {'segment': 'SEG_C'}, '00:0A:00:00:00:04': {'segment': 'SEG_D'}, '00:0B:00:00:00:05': {'segment': 'SEG_E'} } testing_results = [ ('00:0X:00:00:00:01', 'failed'), ('00:0Y:00:00:00:02', 'passed'), ('00:0Z:00:00:00:03', 'failed'), ('00:0A:00:00:00:04', 'passed') ] unauthenticated_devices = ['00:0X:00:00:00:01', '00:0A:00:00:00:04'] expired_device_vlans = [ ('00:0B:00:00:00:05', 600), ('00:0B:00:00:00:05', 500), ] # load static device behaviors for mac, device_behavior_map in static_device_behaviors.items(): self._port_state_manager.handle_static_device_behavior( mac, dict_proto(device_behavior_map, DeviceBehavior)) # devices are authenticated for mac, device_behavior_map in authentication_results.items(): self._port_state_manager.handle_device_behavior( mac, dict_proto(device_behavior_map, DeviceBehavior)) expected_states = { '00:0X:00:00:00:01': self.OPERATIONAL, '00:0Z:00:00:00:03': self.SEQUESTERED, '00:0A:00:00:00:04': self.SEQUESTERED, '00:0B:00:00:00:05': self.SEQUESTERED } self._verify_ports_states(expected_states) expected_received_device_behaviors = [ ('00:0X:00:00:00:01', 'SEG_A', True), ('00:0X:00:00:00:01', 'SEG_A', True), ('00:0Z:00:00:00:03', 'TESTING', False), ('00:0A:00:00:00:04', 'TESTING', False), ('00:0B:00:00:00:05', 'TESTING', False) ] self._verify_received_device_behaviors(expected_received_device_behaviors) # received testing results for devices for testing_result in testing_results: self._port_state_manager.handle_testing_result( self._encapsulate_testing_result(*testing_result)) expected_states = { '00:0X:00:00:00:01': self.OPERATIONAL, '00:0Z:00:00:00:03': self.INFRACTED, '00:0A:00:00:00:04': self.OPERATIONAL, '00:0B:00:00:00:05': self.SEQUESTERED } self._verify_ports_states(expected_states) expected_received_device_behaviors.extend([('00:0A:00:00:00:04', 'SEG_D', False)]) self._verify_received_device_behaviors(expected_received_device_behaviors) # devices are unauthenticated for mac in unauthenticated_devices: self._port_state_manager.handle_device_behavior(mac, DeviceBehavior()) expected_states = { '00:0X:00:00:00:01': self.OPERATIONAL, '00:0Z:00:00:00:03': self.INFRACTED, '00:0B:00:00:00:05': self.SEQUESTERED } self._verify_ports_states(expected_states) expected_received_device_behaviors.extend([('00:0A:00:00:00:04', '', False)]) self._verify_received_device_behaviors(expected_received_device_behaviors) # devices are expired for expired_device_vlan in expired_device_vlans: mac = expired_device_vlan[0] expired_vlan = expired_device_vlan[1] self._port_state_manager.handle_device_placement( mac, DevicePlacement(switch='switch', port=1), False, expired_vlan) expired_received_device_placements = [('00:0B:00:00:00:05', False, False)] self._verify_received_device_placements(expired_received_device_placements)