Ejemplo n.º 1
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'))),
         ],
     )
Ejemplo n.º 2
0
    def setUp(self):
        # The following attributes must be overruled by child classes
        # The basics are tested with nested RelayForwardMessages
        self.packet_fixture = bytes.fromhex(
            '0c'  # message_type: MSG_RELAY_FORW
            '02'  # hop_count
            '20010db8000000000000000000020001'  # link_address
            '20010db8000000000000000000020002'  # peer_address
            '0009'  # option_type: OPTION_RELAY_MSG
            '0051'  # option_length
            '0c'  # message_type: MSG_RELAY_FORW
            '01'  # hop_count
            '20010db8000000000000000000010001'  # link_address
            '20010db8000000000000000000010002'  # peer_address
            '0009'  # option_type: OPTION_RELAY_MSG
            '002b'  # option_length
            '0c'  # message_type: MSG_RELAY_FORW
            '00'  # hop_count
            '20010db8000000000000000000000001'  # link_address
            '20010db8000000000000000000000002'  # peer_address
            '0009'  # option_type: OPTION_RELAY_MSG
            '0005'  # option_length
            'ff'  # some unknown  message_type: 255
            '41424344')  # some random message_data: 'ABCD'

        self.message_fixture = RelayForwardMessage(
            hop_count=2,
            link_address=IPv6Address('2001:db8::2:1'),
            peer_address=IPv6Address('2001:db8::2:2'),
            options=[
                RelayMessageOption(relayed_message=RelayForwardMessage(
                    hop_count=1,
                    link_address=IPv6Address('2001:db8::1:1'),
                    peer_address=IPv6Address('2001:db8::1:2'),
                    options=[
                        RelayMessageOption(relayed_message=RelayForwardMessage(
                            hop_count=0,
                            link_address=IPv6Address('2001:db8::1'),
                            peer_address=IPv6Address('2001:db8::2'),
                            options=[
                                RelayMessageOption(
                                    relayed_message=UnknownMessage(
                                        255, b'ABCD'))
                            ]))
                    ]))
            ])

        self.parse_packet()
Ejemplo n.º 3
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(),
     ]
    def setUp(self):
        self.option_bytes = bytes.fromhex(
            '002f'  # Option type: OPTION_LQ_RELAY_DATA
            '003b'  # Option length: 59
            '20010db8000000000000000000000002'  # Peer address: 2001:db8::2

            '0c'  # Message type: MSG_RELAY_FORW
            '00'  # Hop count: 0
            '20010db8000000000000000000000002'  # Link address: 2001:db8::2
            'fe800000000000000000000000000022'  # Peer address: fe80::22

            '0012'  # Option type: OPTION_INTERFACE_ID
            '0005'  # Option length: 5
            '4661322f33'  # Interface ID: 'Fa2/3'
        )
        self.option_object = LQRelayDataOption(
            peer_address=IPv6Address('2001:db8::2'),
            relay_message=RelayForwardMessage(
                hop_count=0,
                link_address=IPv6Address('2001:db8::2'),
                peer_address=IPv6Address('fe80::22'),
                options=[
                    InterfaceIdOption(interface_id=b'Fa2/3'),
                ]
            )
        )

        self.parse_option()
Ejemplo n.º 5
0
    def handle_relay(self, bundle: TransactionBundle,
                     relay_message_in: RelayForwardMessage,
                     relay_message_out: RelayReplyMessage):
        """
        Handle the options for each relay message pair.

        :param bundle: The transaction bundle
        :param relay_message_in: The incoming relay message
        :param relay_message_out: Thr outgoing relay message
        """
        # See if the relay message contains an ERO
        ero = relay_message_in.get_option_of_type(EchoRequestOption)
        if not ero:
            # Nothing to do
            return

        for option_type in ero.requested_options:
            # Don't do anything if the outgoing relay message already has this one
            if any(option.option_type == option_type
                   for option in relay_message_out.options):
                continue

            # Get the incoming options of the requested type
            incoming_options = [
                option for option in relay_message_in.options
                if option.option_type == option_type
            ]

            for option in incoming_options:
                # Make sure this option can go into this type of response
                if not relay_message_out.may_contain(option):
                    return

                # And append them to the outgoing message if possible
                relay_message_out.options.append(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)
Ejemplo n.º 7
0
    def encode_relay_messages(
            self, relay_chain: Optional[RelayForwardMessage]) -> bytes:
        """
        Encode a chain of relay messages as bytes.

        :param relay_chain: The incoming relay messages
        :return: The bytes
        """
        if not relay_chain:
            return b''

        current_in = relay_chain
        current_out = None
        out = None
        while isinstance(current_in, RelayForwardMessage):
            new_relay_message = RelayForwardMessage(
                hop_count=current_in.hop_count,
                link_address=current_in.link_address,
                peer_address=current_in.peer_address,
                options=self.filter_storable_options(current_in.options))
            if not current_out:
                out = new_relay_message
            else:
                current_out.relayed_message = new_relay_message

            current_in = current_in.relayed_message
            current_out = new_relay_message

        # Save the resulting chain
        if not out:
            return b''

        return out.save()
    def setUp(self):
        self.option_bytes = bytes.fromhex(
            '002d'  # Option type 45: OPTION_CLIENT_DATA
            '0099'  # Option length: 153

            '0001'  # Option type 1: OPTION_CLIENT_ID
            '0015'  # Option length: 21
            '0002'  # DUID type: DUID_EN
            '00009d10'  # Enterprise ID: 40208
            '303132333435363738396162636465'  # Identifier: '0123456789abcde'

            '0005'  # Option type: OPTION_IAADDR
            '0018'  # Option length: 24
            '20010db800000000000000000000cafe'  # IPv6 address: 2001:db8::cafe
            '00000708'  # Preferred lifetime: 1800
            '00000e10'  # Valid lifetime: 3600

            '001a'  # Option type: OPTION_IAPREFIX
            '0019'  # Option length: 25
            '00000708'  # Preferred lifetime: 1800
            '00000e10'  # Valid lifetime: 3600
            '30'  # Prefix length: 48
            '20010db8000100000000000000000000'

            '002e'  # Option type: OPTION_CLT_TIME
            '0004'  # Option length: 4
            '00000384'  # Client-Last-Transaction time: 900

            '002f'  # Option type: OPTION_LQ_RELAY_DATA
            '003b'  # Option length: 59
            '20010db8000000000000000000000002'  # Peer address: 2001:db8::2

            '0c'  # Message type: MSG_RELAY_FORW
            '00'  # Hop count: 0
            '20010db8000000000000000000000002'  # Link address: 2001:db8::2
            'fe800000000000000000000000000022'  # Peer address: fe80::22

            '0012'  # Option type: OPTION_INTERFACE_ID
            '0005'  # Option length: 5
            '4661322f33'  # Interface ID: 'Fa2/3'
        )
        self.option_object = ClientDataOption(options=[
            ClientIdOption(EnterpriseDUID(40208, b'0123456789abcde')),
            IAAddressOption(address=IPv6Address('2001:db8::cafe'), preferred_lifetime=1800, valid_lifetime=3600),
            IAPrefixOption(prefix=IPv6Network('2001:db8:1::/48'), preferred_lifetime=1800, valid_lifetime=3600),
            CLTTimeOption(clt_time=900),
            LQRelayDataOption(peer_address=IPv6Address('2001:db8::2'), relay_message=RelayForwardMessage(
                hop_count=0,
                link_address=IPv6Address('2001:db8::2'),
                peer_address=IPv6Address('fe80::22'),
                options=[
                    InterfaceIdOption(interface_id=b'Fa2/3'),
                ]
            ))
        ])

        self.parse_option()
Ejemplo n.º 9
0
    def test_empty_message(self):
        with self.assertLogs(level=logging.WARNING) as cm:
            bundle = TransactionBundle(incoming_message=RelayForwardMessage(),
                                       received_over_multicast=True)
            result = self.message_handler.handle(bundle, StatisticsSet())
            self.assertIsNone(result)

        self.assertEqual(len(cm.output), 1)
        self.assertRegex(cm.output[0],
                         '^WARNING:.*:A server should not receive')
Ejemplo n.º 10
0
    def setUp(self):
        # The following attributes must be overruled by child classes
        # The basics are tested with nested RelayForwardMessages
        self.packet_fixture = bytes.fromhex('0c'  # message_type: MSG_RELAY_FORW
                                            '02'  # hop_count
                                            '20010db8000000000000000000020001'  # link_address
                                            '20010db8000000000000000000020002'  # peer_address
                                            '0009'  # option_type: OPTION_RELAY_MSG
                                            '0051'  # option_length
                                            '0c'  # message_type: MSG_RELAY_FORW
                                            '01'  # hop_count
                                            '20010db8000000000000000000010001'  # link_address
                                            '20010db8000000000000000000010002'  # peer_address
                                            '0009'  # option_type: OPTION_RELAY_MSG
                                            '002b'  # option_length
                                            '0c'  # message_type: MSG_RELAY_FORW
                                            '00'  # hop_count
                                            '20010db8000000000000000000000001'  # link_address
                                            '20010db8000000000000000000000002'  # peer_address
                                            '0009'  # option_type: OPTION_RELAY_MSG
                                            '0005'  # option_length
                                            'ff'  # some unknown  message_type: 255
                                            '41424344')  # some random message_data: 'ABCD'

        self.message_fixture = RelayForwardMessage(
            hop_count=2,
            link_address=IPv6Address('2001:db8::2:1'),
            peer_address=IPv6Address('2001:db8::2:2'),
            options=[
                RelayMessageOption(
                    relayed_message=RelayForwardMessage(
                        hop_count=1,
                        link_address=IPv6Address('2001:db8::1:1'),
                        peer_address=IPv6Address('2001:db8::1:2'),
                        options=[
                            RelayMessageOption(
                                relayed_message=RelayForwardMessage(
                                    hop_count=0,
                                    link_address=IPv6Address('2001:db8::1'),
                                    peer_address=IPv6Address('2001:db8::2'),
                                    options=[
                                        RelayMessageOption(relayed_message=UnknownMessage(255, b'ABCD'))
                                    ]))
                        ]))
            ])

        self.parse_packet()
Ejemplo n.º 11
0
    def handle_relay(self, bundle: TransactionBundle,
                     relay_message_in: RelayForwardMessage, relay_message_out: RelayReplyMessage):
        """
        Copy the options for each relay message pair.

        :param bundle: The transaction bundle
        :param relay_message_in: The incoming relay message
        :param relay_message_out: Thr outgoing relay message
        """
        # Make sure this option can go into this type of response
        if not relay_message_out.may_contain(self.option_class):
            return

        # Make sure this option isn't present and then copy those from the request
        relay_message_out.options = [existing_option for existing_option in relay_message_out.options
                                     if not isinstance(existing_option, self.option_class)]
        relay_message_out.options[:0] = relay_message_in.get_options_of_type(self.option_class)
Ejemplo n.º 12
0
def parse_incoming_request(
        incoming_packet: IncomingPacketBundle) -> TransactionBundle:
    """
    Parse the incoming packet and add a RelayServerMessage around it containing the meta-data received from the
    listener.

    :param incoming_packet: The received packet
    :return: The parsed message in a transaction bundle
    """
    # Parse message and validate
    length, incoming_message = Message.parse(incoming_packet.data)
    incoming_message.validate()

    # Determine the next hop count and construct useful log messages
    if isinstance(incoming_message, RelayForwardMessage):
        next_hop_count = incoming_message.hop_count + 1
    else:
        next_hop_count = 0

    # Collect the relay options
    relay_options = []
    """:type: List[Option]"""

    relay_options.append(
        InterfaceIdOption(
            interface_id=incoming_packet.interface_name.encode('utf-8')))
    relay_options.extend(incoming_packet.extra_options)
    relay_options.append(RelayMessageOption(relayed_message=incoming_message))

    # Pretend to be an internal relay and wrap the message like a relay would
    wrapped_message = RelayForwardMessage(
        hop_count=next_hop_count,
        link_address=incoming_packet.link_address,
        peer_address=incoming_packet.sender,
        options=relay_options)

    # Create the transaction bundle
    return TransactionBundle(
        incoming_message=wrapped_message,
        received_over_multicast=incoming_packet.received_over_multicast,
        marks=incoming_packet.marks)
Ejemplo n.º 13
0
    def handle_relay(self, bundle: TransactionBundle,
                     relay_message_in: RelayForwardMessage,
                     relay_message_out: RelayReplyMessage):
        """
        Copy the options for each relay message pair.

        :param bundle: The transaction bundle
        :param relay_message_in: The incoming relay message
        :param relay_message_out: Thr outgoing relay message
        """
        # Make sure this option can go into this type of response
        if not relay_message_out.may_contain(self.option_class):
            return

        # Make sure this option isn't present and then copy those from the request
        relay_message_out.options = [
            existing_option for existing_option in relay_message_out.options
            if not isinstance(existing_option, self.option_class)
        ]
        relay_message_out.options[:0] = relay_message_in.get_options_of_type(
            self.option_class)
Ejemplo n.º 14
0
class RelayServerMessageTestCase(test_message.MessageTestCase):
    def setUp(self):
        # The following attributes must be overruled by child classes
        # The basics are tested with nested RelayForwardMessages
        self.packet_fixture = bytes.fromhex('0c'  # message_type: MSG_RELAY_FORW
                                            '02'  # hop_count
                                            '20010db8000000000000000000020001'  # link_address
                                            '20010db8000000000000000000020002'  # peer_address
                                            '0009'  # option_type: OPTION_RELAY_MSG
                                            '0051'  # option_length
                                            '0c'  # message_type: MSG_RELAY_FORW
                                            '01'  # hop_count
                                            '20010db8000000000000000000010001'  # link_address
                                            '20010db8000000000000000000010002'  # peer_address
                                            '0009'  # option_type: OPTION_RELAY_MSG
                                            '002b'  # option_length
                                            '0c'  # message_type: MSG_RELAY_FORW
                                            '00'  # hop_count
                                            '20010db8000000000000000000000001'  # link_address
                                            '20010db8000000000000000000000002'  # peer_address
                                            '0009'  # option_type: OPTION_RELAY_MSG
                                            '0005'  # option_length
                                            'ff'  # some unknown  message_type: 255
                                            '41424344')  # some random message_data: 'ABCD'

        self.message_fixture = RelayForwardMessage(
            hop_count=2,
            link_address=IPv6Address('2001:db8::2:1'),
            peer_address=IPv6Address('2001:db8::2:2'),
            options=[
                RelayMessageOption(
                    relayed_message=RelayForwardMessage(
                        hop_count=1,
                        link_address=IPv6Address('2001:db8::1:1'),
                        peer_address=IPv6Address('2001:db8::1:2'),
                        options=[
                            RelayMessageOption(
                                relayed_message=RelayForwardMessage(
                                    hop_count=0,
                                    link_address=IPv6Address('2001:db8::1'),
                                    peer_address=IPv6Address('2001:db8::2'),
                                    options=[
                                        RelayMessageOption(relayed_message=UnknownMessage(255, b'ABCD'))
                                    ]))
                        ]))
            ])

        self.parse_packet()

    def parse_packet(self):
        super().parse_packet()
        self.assertIsInstance(self.message, RelayServerMessage)

    def test_validate_hop_count(self):
        self.message.hop_count = -1
        with self.assertRaisesRegex(ValueError, 'unsigned 8 bit integer'):
            self.message.validate()

        self.message.hop_count = 255
        self.message.validate()

        self.message.hop_count = 256
        with self.assertRaisesRegex(ValueError, 'unsigned 8 bit integer'):
            self.message.validate()

    def test_validate_link_address(self):
        self.message.link_address = bytes.fromhex('20010db8000000000000000000000001')
        with self.assertRaisesRegex(ValueError, 'Link-address .* IPv6 address'):
            self.message.validate()

        self.message.link_address = IPv6Address('ff02::1')
        with self.assertRaisesRegex(ValueError, 'Link-address .* non-multicast IPv6 address'):
            self.message.validate()

    def test_validate_peer_address(self):
        self.message.peer_address = bytes.fromhex('20010db8000000000000000000000001')
        with self.assertRaisesRegex(ValueError, 'Peer-address .* IPv6 address'):
            self.message.validate()

        self.message.peer_address = IPv6Address('ff02::1')
        with self.assertRaisesRegex(ValueError, 'Peer-address .* non-multicast IPv6 address'):
            self.message.validate()

    def test_get_relayed_message(self):
        self.assertEqual(self.message.relayed_message,
                         self.message_fixture.get_option_of_type(RelayMessageOption).relayed_message)

    def test_set_relayed_message(self):
        # Start with empty options
        self.message.options = []
        self.assertEqual(len(self.message.get_options_of_type(RelayMessageOption)), 0)

        self.message.relayed_message = UnknownMessage(255, b'ThisIsAnUnknownMessage')
        self.assertEqual(len(self.message.get_options_of_type(RelayMessageOption)), 1)
        self.assertEqual(self.message.relayed_message.message_data, b'ThisIsAnUnknownMessage')

        self.message.relayed_message = UnknownMessage(255, b'ThisIsADifferentUnknownMessage')
        self.assertEqual(len(self.message.get_options_of_type(RelayMessageOption)), 1)
        self.assertEqual(self.message.relayed_message.message_data, b'ThisIsADifferentUnknownMessage')

    def test_inner_message(self):
        # Make sure the inner message is a message that is not a RelayServerMessage
        self.assertIsInstance(self.message.inner_message, Message)
        self.assertNotIsInstance(self.message.inner_message, RelayServerMessage)

    def test_inner_relay_message(self):
        # Make sure the inner relay message is a RelayServerMessage
        self.assertIsInstance(self.message.inner_relay_message, RelayServerMessage)

        # that contains a message that is not a RelayServerMessage
        self.assertIsInstance(self.message.inner_relay_message.relayed_message, Message)
        self.assertNotIsInstance(self.message.inner_relay_message.relayed_message, RelayServerMessage)

    def test_missing_inner_message(self):
        # Remove the final message and check that it's being handled correctly
        self.message.inner_relay_message.options = []
        self.assertIsNone(self.message.inner_message)
        self.assertIsNone(self.message.inner_relay_message)
Ejemplo n.º 15
0
class RelayServerMessageTestCase(test_message.MessageTestCase):
    def setUp(self):
        # The following attributes must be overruled by child classes
        # The basics are tested with nested RelayForwardMessages
        self.packet_fixture = bytes.fromhex(
            '0c'  # message_type: MSG_RELAY_FORW
            '02'  # hop_count
            '20010db8000000000000000000020001'  # link_address
            '20010db8000000000000000000020002'  # peer_address
            '0009'  # option_type: OPTION_RELAY_MSG
            '0051'  # option_length
            '0c'  # message_type: MSG_RELAY_FORW
            '01'  # hop_count
            '20010db8000000000000000000010001'  # link_address
            '20010db8000000000000000000010002'  # peer_address
            '0009'  # option_type: OPTION_RELAY_MSG
            '002b'  # option_length
            '0c'  # message_type: MSG_RELAY_FORW
            '00'  # hop_count
            '20010db8000000000000000000000001'  # link_address
            '20010db8000000000000000000000002'  # peer_address
            '0009'  # option_type: OPTION_RELAY_MSG
            '0005'  # option_length
            'ff'  # some unknown  message_type: 255
            '41424344')  # some random message_data: 'ABCD'

        self.message_fixture = RelayForwardMessage(
            hop_count=2,
            link_address=IPv6Address('2001:db8::2:1'),
            peer_address=IPv6Address('2001:db8::2:2'),
            options=[
                RelayMessageOption(relayed_message=RelayForwardMessage(
                    hop_count=1,
                    link_address=IPv6Address('2001:db8::1:1'),
                    peer_address=IPv6Address('2001:db8::1:2'),
                    options=[
                        RelayMessageOption(relayed_message=RelayForwardMessage(
                            hop_count=0,
                            link_address=IPv6Address('2001:db8::1'),
                            peer_address=IPv6Address('2001:db8::2'),
                            options=[
                                RelayMessageOption(
                                    relayed_message=UnknownMessage(
                                        255, b'ABCD'))
                            ]))
                    ]))
            ])

        self.parse_packet()

    def parse_packet(self):
        super().parse_packet()
        self.assertIsInstance(self.message, RelayServerMessage)

    def test_validate_hop_count(self):
        self.check_unsigned_integer_property('hop_count', size=8)

    def test_validate_link_address(self):
        self.message.link_address = bytes.fromhex(
            '20010db8000000000000000000000001')
        with self.assertRaisesRegex(ValueError,
                                    'Link-address .* IPv6 address'):
            self.message.validate()

        self.message.link_address = IPv6Address('ff02::1')
        with self.assertRaisesRegex(
                ValueError, 'Link-address .* non-multicast IPv6 address'):
            self.message.validate()

    def test_validate_peer_address(self):
        self.message.peer_address = bytes.fromhex(
            '20010db8000000000000000000000001')
        with self.assertRaisesRegex(ValueError,
                                    'Peer-address .* IPv6 address'):
            self.message.validate()

        self.message.peer_address = IPv6Address('ff02::1')
        with self.assertRaisesRegex(
                ValueError, 'Peer-address .* non-multicast IPv6 address'):
            self.message.validate()

    def test_get_relayed_message(self):
        self.assertEqual(
            self.message.relayed_message,
            self.message_fixture.get_option_of_type(
                RelayMessageOption).relayed_message)

    def test_set_relayed_message(self):
        # Start with empty options
        self.message.options = []
        self.assertEqual(
            len(self.message.get_options_of_type(RelayMessageOption)), 0)

        self.message.relayed_message = UnknownMessage(
            255, b'ThisIsAnUnknownMessage')
        self.assertEqual(
            len(self.message.get_options_of_type(RelayMessageOption)), 1)
        self.assertEqual(self.message.relayed_message.message_data,
                         b'ThisIsAnUnknownMessage')

        self.message.relayed_message = UnknownMessage(
            255, b'ThisIsADifferentUnknownMessage')
        self.assertEqual(
            len(self.message.get_options_of_type(RelayMessageOption)), 1)
        self.assertEqual(self.message.relayed_message.message_data,
                         b'ThisIsADifferentUnknownMessage')

    def test_inner_message(self):
        # Make sure the inner message is a message that is not a RelayServerMessage
        self.assertIsInstance(self.message.inner_message, Message)
        self.assertNotIsInstance(self.message.inner_message,
                                 RelayServerMessage)

    def test_inner_relay_message(self):
        # Make sure the inner relay message is a RelayServerMessage
        self.assertIsInstance(self.message.inner_relay_message,
                              RelayServerMessage)

        # that contains a message that is not a RelayServerMessage
        self.assertIsInstance(self.message.inner_relay_message.relayed_message,
                              Message)
        self.assertNotIsInstance(
            self.message.inner_relay_message.relayed_message,
            RelayServerMessage)

    def test_missing_inner_message(self):
        # Remove the final message and check that it's being handled correctly
        self.message.inner_relay_message.options = []
        self.assertIsNone(self.message.inner_message)
        self.assertIsInstance(self.message.inner_relay_message,
                              RelayServerMessage)

    def test_empty_relayed_message(self):
        # This uses a getter/setter, so test that code

        # Make sure we have a message to start with
        self.assertIsNotNone(self.message.relayed_message)

        # This sets the message in the RelayMessageOption
        self.message.relayed_message = None
        self.assertIsNone(self.message.relayed_message)
        with self.assertRaisesRegex(ValueError,
                                    'must be an IPv6 DHCP message'):
            # Validation will complain about the missing message
            self.message.validate()

        # This removes the RelayMessageOption altogether
        option = self.message.get_option_of_type(RelayMessageOption)
        self.message.options.remove(option)

        # This still returns None
        self.assertIsNone(self.message.relayed_message)
Ejemplo n.º 16
0
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'))),
                        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,
                        ]),
                        VendorClassOption(enterprise_number=872),
                    ],
                )),
                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')),
    ],
)
Ejemplo n.º 17
0
    def load_from(self, buffer: bytes, offset: int = 0, length: int = None) -> int:
        """
        Load the internal state of this object from the given buffer. The buffer may contain more data after the
        structured element is parsed. This data is ignored.

        :param buffer: The buffer to read data from
        :param offset: The offset in the buffer where to start reading
        :param length: The amount of data we are allowed to read from the buffer
        :return: The number of bytes used from the buffer
        """
        my_offset, option_len = self.parse_option_header(buffer, offset, length)

        self.subscriber_id = buffer[offset + my_offset : offset + my_offset + option_len]
        my_offset += option_len

        return my_offset

    def save(self) -> bytes:
        """
        Save the internal state of this object as a buffer.

        :return: The buffer with the data from this element
        """
        return pack("!HH", self.option_type, len(self.subscriber_id)) + self.subscriber_id


RelayForwardMessage.add_may_contain(SubscriberIdOption)

# The RFC says there is no requirement for servers to include this option in replies, but it is not forbidden
RelayReplyMessage.add_may_contain(SubscriberIdOption)
Ejemplo n.º 18
0
        structured element is parsed. This data is ignored.

        :param buffer: The buffer to read data from
        :param offset: The offset in the buffer where to start reading
        :param length: The amount of data we are allowed to read from the buffer
        :return: The number of bytes used from the buffer
        """
        my_offset, option_len = self.parse_option_header(
            buffer, offset, length)

        self.subscriber_id = buffer[offset + my_offset:offset + my_offset +
                                    option_len]
        my_offset += option_len

        return my_offset

    def save(self) -> Union[bytes, bytearray]:
        """
        Save the internal state of this object as a buffer.

        :return: The buffer with the data from this element
        """
        return pack('!HH', self.option_type, len(
            self.subscriber_id)) + self.subscriber_id


RelayForwardMessage.add_may_contain(SubscriberIdOption)

# The RFC says there is no requirement for servers to include this option in replies, but it is not forbidden
RelayReplyMessage.add_may_contain(SubscriberIdOption)
Ejemplo n.º 19
0
        my_offset, option_len = self.parse_option_header(
            buffer, offset, length)

        self.enterprise_number = unpack_from('!I',
                                             buffer,
                                             offset=offset + my_offset)[0]
        my_offset += 4

        remote_id_length = option_len - 4
        self.remote_id = buffer[offset + my_offset:offset + my_offset +
                                remote_id_length]
        my_offset += remote_id_length

        return my_offset

    def save(self) -> Union[bytes, bytearray]:
        """
        Save the internal state of this object as a buffer.

        :return: The buffer with the data from this element
        """
        return pack('!HHI', self.option_type,
                    len(self.remote_id) + 4,
                    self.enterprise_number) + self.remote_id


RelayForwardMessage.add_may_contain(RemoteIdOption)

# The RFC says there is no requirement for servers to include this option in replies, but it is not forbidden
RelayReplyMessage.add_may_contain(RemoteIdOption)
Ejemplo n.º 20
0
        :param buffer: The buffer to read data from
        :param offset: The offset in the buffer where to start reading
        :param length: The amount of data we are allowed to read from the buffer
        :return: The number of bytes used from the buffer
        """
        my_offset, option_len = self.parse_option_header(buffer, offset, length)

        self.enterprise_number = unpack_from('!I', buffer, offset=offset + my_offset)[0]
        my_offset += 4

        remote_id_length = option_len - 4
        self.remote_id = buffer[offset + my_offset:offset + my_offset + remote_id_length]
        my_offset += remote_id_length

        return my_offset

    def save(self) -> bytes:
        """
        Save the internal state of this object as a buffer.

        :return: The buffer with the data from this element
        """
        return pack('!HHI', self.option_type, len(self.remote_id) + 4, self.enterprise_number) + self.remote_id


RelayForwardMessage.add_may_contain(RemoteIdOption)

# The RFC says there is no requirement for servers to include this option in replies, but it is not forbidden
RelayReplyMessage.add_may_contain(RemoteIdOption)