Пример #1
0
class ChewieTestCase(unittest.TestCase):
    """Main chewie.py test class"""

    no_radius_replies = []

    header = "0000000000010242ac17006f888e"
    sup_replies_success = [
        bytes.fromhex(header + "01000009027400090175736572"),
        bytes.fromhex(header +
                      "010000160275001604103abcadc86714b2d75d09dd7ff53edf6b")
    ]

    radius_replies_success = [
        bytes.fromhex(
            "0b000050066300262c8ee8a33f43ad4e837e63c54f180175001604101a16a3baa37a0238f33384f6c"
            "11067425012a3bf83bea0eeb69645088527dc491eed18126aa866456add628e3a55a4737872cad6"
        ),
        bytes.fromhex(
            "02010032ec22830bb2fbde37f635e91690410b334"
            "f060375000450129a08d246ec3da2f371ec4ff16eed9310010675736572")
    ]

    radius_replies_success_mab = [
        bytes.fromhex(
            "02000034187d6f5dc540a24efa738d2ba242ac5c50128f4b859e01a85b919b848421d0d369"
            "ad010e303234326163313730303666")
    ]

    radius_replies_failure_mab = [
        bytes.fromhex(
            "030000340b0a0f4ec71ebd0573180eb45161c2b7010e303234326163313730303666"
        )
    ]

    sup_replies_logoff = [
        bytes.fromhex(header + "01000009027400090175736572"),
        bytes.fromhex(header +
                      "010000160275001604103abcadc86714b2d75d09dd7ff53edf6b"),
        bytes.fromhex("0000000000010242ac17006f888e01020000")
    ]

    # packet id (0x84 is incorrect)
    sup_replies_failure_message_id = [
        bytes.fromhex(header + "01000009028400090175736572"),
        bytes.fromhex(header + "01000009029400090175736572"),
        bytes.fromhex(header + "01000009026400090175736572"),
        bytes.fromhex(header + "01000009025400090175736572")
    ]

    # the first response has correct code, second is wrong and will be dropped by radius
    sup_replies_failure2_response_code = [
        bytes.fromhex(header + "01000009027400090175736572"),
        bytes.fromhex(header + "01000009037400090175736572")
    ]

    def setUp(self):
        logger = logging.getLogger()
        logger.level = logging.DEBUG
        self.log_file = tempfile.NamedTemporaryFile()

        logger.addHandler(logging.FileHandler(self.log_file.name))
        logger.addHandler(logging.StreamHandler(sys.stdout))

        self.chewie = Chewie('lo', logger, auth_handler, failure_handler,
                             logoff_handler, '127.0.0.1', 1812, 'SECRET',
                             '44:44:44:44:44:44')
        self.fake_scheduler = FakeTimerScheduler()
        self.chewie.timer_scheduler = self.fake_scheduler

        global FROM_SUPPLICANT  # pylint: disable=global-statement
        global TO_SUPPLICANT  # pylint: disable=global-statement
        global FROM_RADIUS  # pylint: disable=global-statement
        global TO_RADIUS  # pylint: disable=global-statement
        global FROM_SUPPLICANT_ACTIVITY  # pylint: disable=global-statement

        FROM_SUPPLICANT = Queue()
        FROM_SUPPLICANT_ACTIVITY = Queue()
        TO_SUPPLICANT = Queue()
        FROM_RADIUS = Queue()
        TO_RADIUS = Queue()

    def tearDown(self):
        self.chewie.shutdown()

    def test_get_state_machine(self):
        """Tests Chewie.get_state_machine()"""
        self.assertEqual(len(self.chewie.state_machines), 0)
        # creates the state_machine if it doesn't exist
        state_machine = self.chewie.get_state_machine(
            '12:34:56:78:9a:bc',
            # pylint: disable=invalid-name
            '00:00:00:00:00:01')
        self.assertEqual(len(self.chewie.state_machines), 1)

        self.assertIs(
            state_machine,
            self.chewie.get_state_machine('12:34:56:78:9a:bc',
                                          '00:00:00:00:00:01'))

        self.assertIsNot(
            state_machine,
            self.chewie.get_state_machine('12:34:56:78:9a:bc',
                                          '00:00:00:00:00:02'))
        self.assertIsNot(
            state_machine,
            self.chewie.get_state_machine('ab:cd:ef:12:34:56',
                                          '00:00:00:00:00:01'))

        # 2 ports
        self.assertEqual(len(self.chewie.state_machines), 2)
        # port 1 has 2 macs
        self.assertEqual(len(self.chewie.state_machines['00:00:00:00:00:01']),
                         2)
        # port 2 has 1 mac
        self.assertEqual(len(self.chewie.state_machines['00:00:00:00:00:02']),
                         1)

    # TODO Stop Test from touching internal get_state_machine_from_radius_packet
    def test_get_state_machine_by_packet_id(self):
        """Tests Chewie.get_state_machine_by_packet_id()"""
        self.chewie.radius_lifecycle.packet_id_to_mac[56] = {
            'src_mac': '12:34:56:78:9a:bc',
            'port_id': '00:00:00:00:00:01'
        }
        state_machine = self.chewie.get_state_machine(
            '12:34:56:78:9a:bc',
            # pylint: disable=invalid-name
            '00:00:00:00:00:01')

        self.assertIs(self.chewie._get_state_machine_from_radius_packet_id(56),
                      state_machine)
        with self.assertRaises(KeyError):
            self.chewie._get_state_machine_from_radius_packet_id(20)

    @patch_things
    @setup_generators(sup_replies_success, radius_replies_success)
    def test_success_dot1x(self):
        """Test success api"""
        FROM_SUPPLICANT.put_nowait(
            bytes.fromhex("0000000000010242ac17006f888e01010000"))

        pool = eventlet.GreenPool()
        pool.spawn(self.chewie.run)

        eventlet.sleep(1)

        self.assertEqual(
            self.chewie.get_state_machine('02:42:ac:17:00:6f',
                                          '00:00:00:00:00:01').state,
            FullEAPStateMachine.SUCCESS2)

    @patch_things
    @setup_generators(sup_replies_success, radius_replies_success)
    def test_chewie_identity_response_dot1x(self):
        """test port status api and that identity request is sent after port up"""

        global TO_SUPPLICANT
        pool = eventlet.GreenPool()
        pool.spawn(self.chewie.run)
        eventlet.sleep(1)
        self.chewie.port_down("00:00:00:00:00:01")

        self.chewie.port_up("00:00:00:00:00:01")

        while not self.fake_scheduler.jobs:
            eventlet.sleep(SHORT_SLEEP)
        self.fake_scheduler.run_jobs(num_jobs=1)

        # check preemptive sent directly after port up
        out_packet = TO_SUPPLICANT.get()
        self.assertEqual(
            out_packet,
            bytes.fromhex('0180C2000003000000000001888e010000050167000501'))

        # Send a response to the request
        FROM_SUPPLICANT.put_nowait(
            bytes.fromhex("0000000000010242ac17006f888e010000050167000501"))
        eventlet.sleep(2)

        self.assertEqual(
            self.chewie.get_state_machine('02:42:ac:17:00:6f',
                                          '00:00:00:00:00:01').state,
            FullEAPStateMachine.AAA_IDLE)

    @patch_things
    def test_port_status_changes(self):
        """test port status api and that identity request is sent after port up"""

        global TO_SUPPLICANT
        pool = eventlet.GreenPool()
        pool.spawn(self.chewie.run)
        eventlet.sleep(1)
        self.chewie.port_down("00:00:00:00:00:01")

        self.chewie.port_up("00:00:00:00:00:01")

        while not self.fake_scheduler.jobs:
            eventlet.sleep(SHORT_SLEEP)
        self.fake_scheduler.run_jobs(num_jobs=1)
        # check preemptive sent directly after port up
        out_packet = TO_SUPPLICANT.get()
        self.assertEqual(
            out_packet,
            bytes.fromhex('0180C2000003000000000001888e010000050167000501'))

        # check there is a new job in the queue for sending the next id request.
        # This will keep adding jobs forever.
        self.assertEqual(len(self.fake_scheduler.jobs), 1)
        self.assertEqual(self.fake_scheduler.jobs[0].function.__name__,
                         ManagedPort.send_preemptive_identity_request.__name__)

    @patch_things
    def test_delete_state_on_port_down(self):
        """test port status api and that identity request is sent after port up"""
        self.test_chewie_identity_response_dot1x()
        self.chewie.port_down("00:00:00:00:00:01")
        self.assertIsNone(self.chewie.state_machines.get("00:00:00:00:00:01"),
                          "Not Removing state machines on port_down")

    @patch_things
    @setup_generators(sup_replies_logoff, radius_replies_success)
    def test_logoff_dot1x(self):
        """Test logoff"""
        self.chewie.get_state_machine(
            MacAddress.from_string('02:42:ac:17:00:6f'),
            MacAddress.from_string('00:00:00:00:00:01'))
        FROM_SUPPLICANT.put_nowait(
            bytes.fromhex("0000000000010242ac17006f888e01010000"))

        pool = eventlet.GreenPool()
        pool.spawn(self.chewie.run)

        eventlet.sleep(SHORT_SLEEP)

        self.assertEqual(
            self.chewie.get_state_machine('02:42:ac:17:00:6f',
                                          '00:00:00:00:00:01').state,
            FullEAPStateMachine.LOGOFF2)

    @patch_things
    @setup_generators(sup_replies_failure_message_id, no_radius_replies)
    def test_failure_message_id_dot1x(self):
        """Test incorrect message id results in timeout_failure"""
        # TODO not convinced this is transitioning through the correct states.
        # (should be discarding all packets)
        # But end result is correct (both packets sent/received, and end state)
        # self.chewie.get_state_machine(MacAddress.from_string('02:42:ac:17:00:6f'),
        self.chewie.get_state_machine(
            MacAddress.from_string('02:42:ac:17:00:6f'),
            MacAddress.from_string('00:00:00:00:00:01')).DEFAULT_TIMEOUT = 0.5

        FROM_SUPPLICANT.put_nowait(
            bytes.fromhex("0000000000010242ac17006f888e01010000"))

        pool = eventlet.GreenPool()
        pool.spawn(self.chewie.run)

        while not self.fake_scheduler.jobs:
            eventlet.sleep(SHORT_SLEEP)
        self.fake_scheduler.run_jobs()

        self.assertEqual(
            self.chewie.get_state_machine('02:42:ac:17:00:6f',
                                          '00:00:00:00:00:01').state,
            FullEAPStateMachine.TIMEOUT_FAILURE)

    @patch_things
    @setup_generators(sup_replies_failure2_response_code, no_radius_replies)
    def test_failure2_resp_code_dot1x(self):
        """Test incorrect eap.code results in timeout_failure2. RADIUS Server drops it.
        It is up to the supplicant to send another request - this supplicant doesnt"""
        self.chewie.get_state_machine(
            MacAddress.from_string('02:42:ac:17:00:6f'),
            MacAddress.from_string('00:00:00:00:00:01')).DEFAULT_TIMEOUT = 0.5

        FROM_SUPPLICANT.put_nowait(
            bytes.fromhex("0000000000010242ac17006f888e01010000"))

        pool = eventlet.GreenPool()
        pool.spawn(self.chewie.run)

        while not self.fake_scheduler.jobs:
            eventlet.sleep(SHORT_SLEEP)
        self.fake_scheduler.run_jobs()

        self.assertEqual(
            self.chewie.get_state_machine('02:42:ac:17:00:6f',
                                          '00:00:00:00:00:01').state,
            FullEAPStateMachine.TIMEOUT_FAILURE2)

    @patch_things
    def test_mab_receive(self):
        """Test ETH Receive Ability of MAB Functionality"""
        mab_sm = self.chewie.get_state_machine(
            MacAddress.from_string('02:42:ac:17:00:6f'),
            MacAddress.from_string('00:00:00:00:00:01'), -2)
        mab_sm.DEFAULT_TIMEOUT = 0.5

        FROM_SUPPLICANT_ACTIVITY.put_nowait(
            bytes.fromhex(
                "0000000000010242ac17006f08004500001c0001000040117cce7f0000017f0000010044004300080155"
            ))

        pool = eventlet.GreenPool()
        pool.spawn(self.chewie.run)

        eventlet.sleep(SHORT_SLEEP)

        self.assertEqual(
            self.chewie.get_state_machine('02:42:ac:17:00:6f',
                                          '00:00:00:00:00:01').state,
            MacAuthenticationBypassStateMachine.AAA_IDLE)

    @patch_things
    @setup_generators(None, radius_replies_success_mab)
    def test_mab_success_auth(self):
        """Test Successful MAB Attempt"""
        mab_sm = self.chewie.get_state_machine(
            MacAddress.from_string('02:42:ac:17:00:6f'),
            MacAddress.from_string('00:00:00:00:00:01'), -2)
        mab_sm.DEFAULT_TIMEOUT = 0.5

        FROM_SUPPLICANT_ACTIVITY.put_nowait(
            bytes.fromhex(
                "0000000000010242ac17006f08004500001c0001000040117cce7f0000017f0000010044004300080155"
            ))

        pool = eventlet.GreenPool()
        pool.spawn(self.chewie.run)

        eventlet.sleep(SHORT_SLEEP)

        self.assertEqual(
            self.chewie.get_state_machine('02:42:ac:17:00:6f',
                                          '00:00:00:00:00:01').state,
            MacAuthenticationBypassStateMachine.AAA_SUCCESS)

    @patch_things
    @setup_generators(None, radius_replies_failure_mab)
    def test_mab_failure_auth(self):
        """Test unsuccessful MAB Attempt"""
        mab_sm = self.chewie.get_state_machine(
            MacAddress.from_string('02:42:ac:17:00:6f'),
            MacAddress.from_string('00:00:00:00:00:01'), -2)
        mab_sm.DEFAULT_TIMEOUT = 0.5

        FROM_SUPPLICANT_ACTIVITY.put_nowait(
            bytes.fromhex(
                "0000000000010242ac17006f08004500001c0001000040117cce7f0000017f0000010044004300080155"
            ))

        pool = eventlet.GreenPool()
        pool.spawn(self.chewie.run)

        eventlet.sleep(SHORT_SLEEP)

        self.assertEqual(
            self.chewie.get_state_machine('02:42:ac:17:00:6f',
                                          '00:00:00:00:00:01').state,
            MacAuthenticationBypassStateMachine.AAA_FAILURE)

    @patch_things
    @setup_generators(sup_replies_success, radius_replies_success)
    def test_smoke_test_clients(self):
        """Test success api"""
        FROM_SUPPLICANT.put_nowait(
            bytes.fromhex("0000000000010242ac17006f888e01010000"))

        pool = eventlet.GreenPool()
        pool.spawn(self.chewie.run)

        eventlet.sleep(1)
        self.assertIsNotNone(self.chewie.clients)
        self.assertEqual(len(self.chewie.clients), 1)
Пример #2
0
class ChewieTestCase(unittest.TestCase):
    """Main chewie.py test class"""

    no_radius_replies = []

    header = "0000000000010242ac17006f888e"
    sup_replies_success = [
        bytes.fromhex(header + "01000009027400090175736572"),
        bytes.fromhex(header +
                      "010000160275001604103abcadc86714b2d75d09dd7ff53edf6b")
    ]

    radius_replies_success = [
        bytes.fromhex(
            "0b000050066300262c8ee8a33f43ad4e837e63c54f180175001604101a16a3baa37a0238f33384f6c11067425012a3bf83bea0eeb69645088527dc491eed18126aa866456add628e3a55a4737872cad6"
        ),
        bytes.fromhex(
            "02010032ec22830bb2fbde37f635e91690410b334f060375000450129a08d246ec3da2f371ec4ff16eed9310010675736572"
        )
    ]

    sup_replies_logoff = [
        bytes.fromhex(header + "01000009027400090175736572"),
        bytes.fromhex(header +
                      "010000160275001604103abcadc86714b2d75d09dd7ff53edf6b"),
        bytes.fromhex("0000000000010242ac17006f888e01020000")
    ]

    # packet id (0x84 is incorrect)
    sup_replies_failure_message_id = [
        bytes.fromhex(header + "01000009028400090175736572"),
        bytes.fromhex(header + "01000009029400090175736572"),
        bytes.fromhex(header + "01000009026400090175736572"),
        bytes.fromhex(header + "01000009025400090175736572")
    ]

    # the first response has correct code, second is wrong and will be dropped by radius
    sup_replies_failure2_response_code = [
        bytes.fromhex(header + "01000009027400090175736572"),
        bytes.fromhex(header + "01000009037400090175736572")
    ]

    def setUp(self):
        logger = logging.getLogger()
        logger.level = logging.DEBUG
        self.log_file = tempfile.NamedTemporaryFile()

        logger.addHandler(logging.FileHandler(self.log_file.name))
        logger.addHandler(logging.StreamHandler(sys.stdout))

        self.chewie = Chewie('lo', logger, auth_handler, failure_handler,
                             logoff_handler, '127.0.0.1', 1812, 'SECRET',
                             '44:44:44:44:44:44')
        self.fake_scheduler = FakeTimerScheduler()
        self.chewie.timer_scheduler = self.fake_scheduler

        global FROM_SUPPLICANT  # pylint: disable=global-statement
        global TO_SUPPLICANT  # pylint: disable=global-statement
        global FROM_RADIUS  # pylint: disable=global-statement
        global TO_RADIUS  # pylint: disable=global-statement

        FROM_SUPPLICANT = Queue()
        TO_SUPPLICANT = Queue()
        FROM_RADIUS = Queue()
        TO_RADIUS = Queue()

    def tearDown(self):
        self.chewie.shutdown()

    def test_get_state_machine(self):
        """Tests Chewie.get_state_machine()"""
        self.assertEqual(len(self.chewie.state_machines), 0)
        # creates the state_machine if it doesn't exist
        state_machine = self.chewie.get_state_machine(
            '12:34:56:78:9a:bc',  # pylint: disable=invalid-name
            '00:00:00:00:00:01')

        self.assertEqual(len(self.chewie.state_machines), 1)

        self.assertIs(
            state_machine,
            self.chewie.get_state_machine('12:34:56:78:9a:bc',
                                          '00:00:00:00:00:01'))

        self.assertIsNot(
            state_machine,
            self.chewie.get_state_machine('12:34:56:78:9a:bc',
                                          '00:00:00:00:00:02'))
        self.assertIsNot(
            state_machine,
            self.chewie.get_state_machine('ab:cd:ef:12:34:56',
                                          '00:00:00:00:00:01'))

        # 2 ports
        self.assertEqual(len(self.chewie.state_machines), 2)
        # port 1 has 2 macs
        self.assertEqual(len(self.chewie.state_machines['00:00:00:00:00:01']),
                         2)
        # port 2 has 1 mac
        self.assertEqual(len(self.chewie.state_machines['00:00:00:00:00:02']),
                         1)

    def test_get_state_machine_by_packet_id(self):
        """Tests Chewie.get_state_machine_by_packet_id()"""
        self.chewie.radius_lifecycle.packet_id_to_mac[56] = {
            'src_mac': '12:34:56:78:9a:bc',
            'port_id': '00:00:00:00:00:01'
        }
        state_machine = self.chewie.get_state_machine(
            '12:34:56:78:9a:bc',  # pylint: disable=invalid-name
            '00:00:00:00:00:01')

        self.assertIs(self.chewie.get_state_machine_from_radius_packet_id(56),
                      state_machine)
        with self.assertRaises(KeyError):
            self.chewie.get_state_machine_from_radius_packet_id(20)

    @patch_things
    @setup_generators(sup_replies_success, radius_replies_success)
    def test_success_dot1x(self):
        """Test success api"""
        FROM_SUPPLICANT.put_nowait(
            bytes.fromhex("0000000000010242ac17006f888e01010000"))

        pool = eventlet.GreenPool()
        pool.spawn(self.chewie.run)

        eventlet.sleep(1)

        self.assertEqual(
            self.chewie.get_state_machine('02:42:ac:17:00:6f',
                                          '00:00:00:00:00:01').state,
            FullEAPStateMachine.SUCCESS2)

    def test_port_status_changes(self):
        """test port status api"""
        # TODO what can actually be checked here?
        # the state machine tests already check the statemachine
        # could check that the preemptive identity request packet is sent. (once implemented)
        # for now just check api works under python version.

        self.chewie.port_down("00:00:00:00:00:01")

        self.chewie.port_up("00:00:00:00:00:01")

        self.chewie.port_down("00:00:00:00:00:01")

    @patch_things
    @setup_generators(sup_replies_logoff, radius_replies_success)
    def test_logoff_dot1x(self):
        """Test logoff"""
        self.chewie.get_state_machine(
            MacAddress.from_string('02:42:ac:17:00:6f'),
            MacAddress.from_string('00:00:00:00:00:01'))
        FROM_SUPPLICANT.put_nowait(
            bytes.fromhex("0000000000010242ac17006f888e01010000"))

        pool = eventlet.GreenPool()
        pool.spawn(self.chewie.run)

        eventlet.sleep(0.1)

        self.assertEqual(
            self.chewie.get_state_machine('02:42:ac:17:00:6f',
                                          '00:00:00:00:00:01').state,
            FullEAPStateMachine.LOGOFF2)

    @patch_things
    @setup_generators(sup_replies_failure_message_id, no_radius_replies)
    def test_failure_message_id_dot1x(self):
        """Test incorrect message id results in timeout_failure"""
        # TODO not convinced this is transitioning through the correct states.
        # (should be discarding all packets)
        # But end result is correct (both packets sent/received, and end state)self.chewie.get_state_machine(MacAddress.from_string('02:42:ac:17:00:6f'),
        self.chewie.get_state_machine(
            MacAddress.from_string('02:42:ac:17:00:6f'),
            MacAddress.from_string('00:00:00:00:00:01')).DEFAULT_TIMEOUT = 0.5

        FROM_SUPPLICANT.put_nowait(
            bytes.fromhex("0000000000010242ac17006f888e01010000"))

        pool = eventlet.GreenPool()
        pool.spawn(self.chewie.run)

        while not self.fake_scheduler.jobs:
            eventlet.sleep(0.1)
        self.fake_scheduler.run_jobs()

        self.assertEqual(
            self.chewie.get_state_machine('02:42:ac:17:00:6f',
                                          '00:00:00:00:00:01').state,
            FullEAPStateMachine.TIMEOUT_FAILURE)

    @patch_things
    @setup_generators(sup_replies_failure2_response_code, no_radius_replies)
    def test_failure2_resp_code_dot1x(self):
        """Test incorrect eap.code results in timeout_failure2. RADIUS Server drops it.
        It is up to the supplicant to send another request - this supplicant doesnt"""
        self.chewie.get_state_machine(
            MacAddress.from_string('02:42:ac:17:00:6f'),
            MacAddress.from_string('00:00:00:00:00:01')).DEFAULT_TIMEOUT = 0.5

        FROM_SUPPLICANT.put_nowait(
            bytes.fromhex("0000000000010242ac17006f888e01010000"))

        pool = eventlet.GreenPool()
        pool.spawn(self.chewie.run)

        while not self.fake_scheduler.jobs:
            eventlet.sleep(0.1)
        self.fake_scheduler.run_jobs()

        self.assertEqual(
            self.chewie.get_state_machine('02:42:ac:17:00:6f',
                                          '00:00:00:00:00:01').state,
            FullEAPStateMachine.TIMEOUT_FAILURE2)