Example #1
0
 def setUp(self):
     self.bundle = TransactionBundle(relayed_solicit_message,
                                     received_over_multicast=False)
     self.shallow_bundle = TransactionBundle(solicit_message,
                                             received_over_multicast=True)
     self.deep_bundle = TransactionBundle(RelayForwardMessage(
         hop_count=0,
         link_address=IPv6Address('2001:db8:ffff:2::1'),
         peer_address=IPv6Address('fe80::3631:c4ff:fe3c:b2f1'),
         options=[
             RelayMessageOption(relayed_message=relayed_solicit_message),
         ]),
                                          received_over_multicast=False,
                                          marks=['some', 'marks'])
     self.ia_bundle = TransactionBundle(SolicitMessage(options=[
         IANAOption(b'0001'),
         IANAOption(b'0002'),
         IATAOption(b'0003'),
         IATAOption(b'0004'),
         IAPDOption(b'0005'),
         IAPDOption(b'0006'),
     ]),
                                        received_over_multicast=False)
     self.option_handlers = [
         InterfaceIdOptionHandler(),
     ]
Example #2
0
 def test_get_unhandled_options(self):
     unanswered_options = self.ia_bundle.get_unhandled_options((IANAOption, IATAOption))
     self.assertEqual(len(unanswered_options), 4)
     self.assertIn(IANAOption(b'0001'), unanswered_options)
     self.assertIn(IANAOption(b'0002'), unanswered_options)
     self.assertIn(IATAOption(b'0003'), unanswered_options)
     self.assertIn(IATAOption(b'0004'), unanswered_options)
Example #3
0
    def test_bad_option_length(self):
        with self.assertRaisesRegex(ValueError,
                                    'shorter than the minimum length'):
            IANAOption.parse(bytes.fromhex('0003000041424344000000290000002a'))

        with self.assertRaisesRegex(ValueError, 'length does not match'):
            IANAOption.parse(
                bytes.fromhex('0003000d41424344000000290000002a00140000'))
Example #4
0
 def setUp(self):
     self.option_bytes = bytes.fromhex(
         '0003'  # option_type: OPTION_IA_NA
         '0044'  # option_length
         '41424344'  # iaid: ABCD
         '00000029'  # t1: 41
         '0000002a'  # t2: 42
         '0005'  # option_type: OPTION_IAADDR
         '0018'  # option_length
         '20010db8000000000000000000000001'  # address: 2001:db8::1
         '00000000'  # preferred_lifetime
         '00000000'  # valid_lifetime
         '000d'  # option_type: OPTION_STATUS_CODE
         '0018'  # option_length
         '0000'  # status_code
         '45766572797468696e6720697320617765736f6d6521')  # status_message
     self.option_object = IANAOption(
         iaid=b'ABCD',
         t1=41,
         t2=42,
         options=[
             IAAddressOption(address=IPv6Address('2001:db8::1')),
             StatusCodeOption(status_code=STATUS_SUCCESS,
                              status_message='Everything is awesome!')
         ])
     self.parse_option()
    def test_absent_option_echo_request(self):
        relayed_solicit_message = RelayForwardMessage(
            hop_count=1,
            link_address=IPv6Address('2001:db8:ffff:1::1'),
            peer_address=IPv6Address('fe80::3631:c4ff:fe3c:b2f1'),
            options=[
                RelayMessageOption(relayed_message=SolicitMessage(
                    transaction_id=bytes.fromhex('f350d6'),
                    options=[
                        ElapsedTimeOption(elapsed_time=0),
                        ClientIdOption(duid=LinkLayerDUID(hardware_type=1,
                                                          link_layer_address=bytes.fromhex('3431c43cb2f1'))),
                        IANAOption(iaid=bytes.fromhex('c43cb2f1')),
                    ],
                )),
                EchoRequestOption(requested_options=[OPTION_SUBSCRIBER_ID]),
                UnknownOption(option_type=65535),
                InterfaceIdOption(interface_id=b'Fa2/3'),
                RemoteIdOption(enterprise_number=9,
                               remote_id=bytes.fromhex('020023000001000a0003000100211c7d486e')),
            ]
        )

        bundle = TransactionBundle(incoming_message=relayed_solicit_message, received_over_multicast=True)
        self.message_handler.handle(bundle, StatisticsSet())

        self.assertIsInstance(bundle.outgoing_message, RelayReplyMessage)
        self.assertEqual(len(bundle.outgoing_message.options), 2)
        self.assertIsInstance(bundle.outgoing_message.options[0], InterfaceIdOption)
        self.assertIsInstance(bundle.outgoing_message.options[1], RelayMessageOption)
Example #6
0
 def setUp(self):
     self.relayed_solicit_message = RelayForwardMessage(
         hop_count=1,
         link_address=IPv6Address('2001:db8:ffff:1::1'),
         peer_address=IPv6Address('fe80::3631:c4ff:fe3c:b2f1'),
         options=[
             RelayMessageOption(relayed_message=RelayForwardMessage(
                 hop_count=0,
                 link_address=IPv6Address('::'),
                 peer_address=IPv6Address('fe80::3631:c4ff:fe3c:b2f1'),
                 options=[
                     RelayMessageOption(relayed_message=SolicitMessage(
                         transaction_id=bytes.fromhex('f350d6'),
                         options=[
                             ElapsedTimeOption(elapsed_time=0),
                             ClientIdOption(duid=LinkLayerDUID(hardware_type=1,
                                                               link_layer_address=bytes.fromhex('3431c43cb2f1'))),
                             IANAOption(iaid=bytes.fromhex('c43cb2f1')),
                             IAPDOption(iaid=bytes.fromhex('c43cb2f1')),
                             OptionRequestOption(requested_options=[
                                 OPTION_DNS_SERVERS,
                             ]),
                         ],
                     )),
                     InterfaceIdOption(interface_id=b'Fa2/3'),
                     RemoteIdOption(enterprise_number=9,
                                    remote_id=bytes.fromhex('020023000001000a0003000100211c7d486e')),
                 ])
             ),
             InterfaceIdOption(interface_id=b'Gi0/0/0'),
             RemoteIdOption(enterprise_number=9, remote_id=bytes.fromhex('020000000000000a0003000124e9b36e8100')),
             RelayIdOption(duid=LinkLayerDUID(hardware_type=1, link_layer_address=bytes.fromhex('121212121212'))),
         ],
     )
Example #7
0
    def handle_request(self, bundle: TransactionBundle):
        """
        Handle a client requesting addresses (also handles SolicitMessage)

        :param bundle: The request bundle
        """
        # Get the assignment
        assignment = self.get_assignment(bundle)

        # Try to assign the prefix first: it's not dependent on the link
        if assignment.prefix:
            unanswered_iapd_options = bundle.get_unhandled_options(IAPDOption)
            found_option = self.find_iapd_option_for_prefix(
                unanswered_iapd_options, assignment.prefix)
            if found_option:
                # Answer to this option
                logger.log(DEBUG_HANDLING,
                           "Assigning prefix {}".format(assignment.prefix))
                response_option = IAPDOption(
                    found_option.iaid,
                    options=[
                        IAPrefixOption(
                            prefix=assignment.prefix,
                            preferred_lifetime=self.prefix_preferred_lifetime,
                            valid_lifetime=self.prefix_valid_lifetime)
                    ])
                bundle.response.options.append(response_option)
                bundle.mark_handled(found_option)
            else:
                logger.log(
                    DEBUG_HANDLING,
                    "Prefix {} reserved, but client did not ask for it".format(
                        assignment.prefix))

        if assignment.address:
            unanswered_iana_options = bundle.get_unhandled_options(IANAOption)
            found_option = self.find_iana_option_for_address(
                unanswered_iana_options, assignment.address)
            if found_option:
                # Answer to this option
                logger.log(DEBUG_HANDLING,
                           "Assigning address {}".format(assignment.address))
                response_option = IANAOption(
                    found_option.iaid,
                    options=[
                        IAAddressOption(
                            address=assignment.address,
                            preferred_lifetime=self.address_preferred_lifetime,
                            valid_lifetime=self.address_valid_lifetime)
                    ])
                bundle.response.options.append(response_option)
                bundle.mark_handled(found_option)
            else:
                logger.log(
                    DEBUG_HANDLING,
                    "Address {} reserved, but client did not ask for it".
                    format(assignment.address))
    def test_validate_IAID_uniqueness(self):
        # The first one should be fine
        self.message.options.append(IANAOption(iaid=b'test'))
        self.message.validate()

        # Adding a different type with the same IAID is allowed
        self.message.options.append(IATAOption(iaid=b'test'))
        self.message.validate()

        # But adding another one with the same IAID is not allowed
        self.message.options.append(IATAOption(iaid=b'test'))
        with self.assertRaisesRegex(ValueError, 'not unique'):
            self.message.validate()
#     Reconfigure Accept
#         Option: Reconfigure Accept (20)
#         Length: 0
#     DNS recursive name server
#         Option: DNS recursive name server (23)
#         Length: 16
#         Value: 20014860486000000000000000008888
#          1 DNS server address: 2001:4860:4860::8888 (2001:4860:4860::8888)

reply_message = ReplyMessage(
    transaction_id=bytes.fromhex('f350d6'),
    options=[
        IANAOption(iaid=bytes.fromhex('c43cb2f1'),
                   options=[
                       IAAddressOption(
                           address=IPv6Address('2001:db8:ffff:1:c::e09c'),
                           preferred_lifetime=375,
                           valid_lifetime=600),
                   ]),
        IAPDOption(iaid=bytes.fromhex('c43cb2f1'),
                   options=[
                       IAPrefixOption(
                           prefix=IPv6Network('2001:db8:ffcc:fe00::/56'),
                           preferred_lifetime=375,
                           valid_lifetime=600),
                   ]),
        ClientIdOption(duid=LinkLayerDUID(hardware_type=1,
                                          link_layer_address=bytes.fromhex(
                                              '3431c43cb2f1'))),
        ServerIdOption(duid=LinkLayerTimeDUID(hardware_type=1,
                                              time=488458703,
Example #10
0
 def test_unanswered_iana_options(self):
     unanswered_options = self.ia_bundle.get_unhandled_options(IANAOption)
     self.assertEqual(len(unanswered_options), 2)
     self.assertIn(IANAOption(b'0001'), unanswered_options)
     self.assertIn(IANAOption(b'0002'), unanswered_options)
Example #11
0
 peer_address=IPv6Address('fe80::3631:c4ff:fe3c:b2f1'),
 options=[
     RelayMessageOption(relayed_message=RelayForwardMessage(
         hop_count=0,
         link_address=IPv6Address('::'),
         peer_address=IPv6Address('fe80::3631:c4ff:fe3c:b2f1'),
         options=[
             RelayMessageOption(relayed_message=SolicitMessage(
                 transaction_id=bytes.fromhex('f350d6'),
                 options=[
                     ElapsedTimeOption(elapsed_time=0),
                     ClientIdOption(duid=LinkLayerDUID(
                         hardware_type=1,
                         link_layer_address=bytes.fromhex('3431c43cb2f1'))),
                     RapidCommitOption(),
                     IANAOption(iaid=bytes.fromhex('c43cb2f1')),
                     IAPDOption(iaid=bytes.fromhex('c43cb2f1'),
                                options=[
                                    IAPrefixOption(
                                        prefix=IPv6Network('::/0')),
                                ]),
                     ReconfigureAcceptOption(),
                     OptionRequestOption(requested_options=[
                         OPTION_DNS_SERVERS,
                         OPTION_NTP_SERVER,
                         OPTION_SNTP_SERVERS,
                         OPTION_IA_PD,
                         OPTION_IA_NA,
                         OPTION_VENDOR_OPTS,
                         OPTION_SOL_MAX_RT,
                         OPTION_INF_MAX_RT,
Example #12
0
    def handle_renew_rebind(self, bundle: TransactionBundle):
        """
        Handle a client renewing/rebinding addresses

        :param bundle: The request bundle
        """
        # Get the assignment
        assignment = self.get_assignment(bundle)

        # Collect unanswered options
        unanswered_iana_options = bundle.get_unhandled_options(IANAOption)
        unanswered_iapd_options = bundle.get_unhandled_options(IAPDOption)

        for option in unanswered_iapd_options:
            if assignment.prefix and prefix_overlaps_prefixes(
                    assignment.prefix, option.get_prefixes()):
                # Overlap with our assigned prefix: take responsibility
                response_suboptions = []
                for suboption in option.get_options_of_type(IAPrefixOption):
                    if suboption.prefix == assignment.prefix:
                        # This is the correct option, renew it
                        logger.log(
                            DEBUG_HANDLING,
                            "Renewing prefix {}".format(assignment.prefix))
                        response_suboptions.append(
                            IAPrefixOption(
                                prefix=assignment.prefix,
                                preferred_lifetime=self.
                                prefix_preferred_lifetime,
                                valid_lifetime=self.prefix_valid_lifetime))
                    else:
                        # This isn't right
                        logger.log(
                            DEBUG_HANDLING,
                            "Withdrawing prefix {}".format(suboption.prefix))
                        response_suboptions.append(
                            IAPrefixOption(prefix=suboption.prefix,
                                           preferred_lifetime=0,
                                           valid_lifetime=0))

                response_option = IAPDOption(option.iaid,
                                             options=response_suboptions)
                bundle.response.options.append(response_option)
                bundle.mark_handled(option)

        for option in unanswered_iana_options:
            response_suboptions = []
            for suboption in option.get_options_of_type(IAAddressOption):
                if suboption.address == assignment.address:
                    # This is the correct option, renew it
                    logger.log(
                        DEBUG_HANDLING,
                        "Renewing address {}".format(assignment.address))
                    response_suboptions.append(
                        IAAddressOption(
                            address=assignment.address,
                            preferred_lifetime=self.address_preferred_lifetime,
                            valid_lifetime=self.address_valid_lifetime))
                else:
                    # This isn't right
                    logger.log(
                        DEBUG_HANDLING,
                        "Withdrawing address {}".format(suboption.address))
                    response_suboptions.append(
                        IAAddressOption(address=suboption.address,
                                        preferred_lifetime=0,
                                        valid_lifetime=0))

            response_option = IANAOption(option.iaid,
                                         options=response_suboptions)
            bundle.response.options.append(response_option)
            bundle.mark_handled(option)
Example #13
0
 def test_bad_option_length(self):
     with self.assertRaisesRegex(ValueError, 'length does not match'):
         IANAOption.parse(bytes.fromhex('0003000041424344000000290000002a'))
Example #14
0
    def test_bad_option_length(self):
        with self.assertRaisesRegex(ValueError, 'shorter than the minimum length'):
            IANAOption.parse(bytes.fromhex('0003000041424344000000290000002a'))

        with self.assertRaisesRegex(ValueError, 'length does not match'):
            IANAOption.parse(bytes.fromhex('0003000d41424344000000290000002a00140000'))