예제 #1
0
def duid_en(section) -> EnterpriseDUID:
    """
    Create a EnterpriseDUID from the data provided in the config section.

    :param section: The section data
    :return: The DUID object
    """
    duid = EnterpriseDUID(enterprise_number=section.enterprise_number,
                          identifier=section.identifier)
    duid.validate()
    return duid
예제 #2
0
    def test_validate_enterprise_number(self):
        good_duid_object = EnterpriseDUID(0, b'demo')
        good_duid_object.validate()

        bad_duid_object = EnterpriseDUID(-1, b'demo')
        with self.assertRaisesRegex(ValueError, 'unsigned 32 bit integer'):
            bad_duid_object.validate()

        bad_duid_object = EnterpriseDUID(2 ** 32, b'demo')
        with self.assertRaisesRegex(ValueError, 'unsigned 32 bit integer'):
            bad_duid_object.validate()
예제 #3
0
    def test_validate_length(self):
        good_duid_object = EnterpriseDUID(0, 122 * b'x')
        good_duid_object.validate()

        bad_duid_object = EnterpriseDUID(0, 123 * b'x')
        with self.assertRaisesRegex(ValueError,
                                    'cannot be longer than 122 bytes'):
            bad_duid_object.validate()
    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()
예제 #5
0
    def test_validate_length(self):
        good_duid_object = EnterpriseDUID(0, 124 * b'x')
        good_duid_object.validate()

        bad_duid_object = EnterpriseDUID(0, 125 * b'x')
        with self.assertRaisesRegex(ValueError, 'cannot be longer than 124 bytes'):
            bad_duid_object.validate()
예제 #6
0
def parse_duid(duid_str: str) -> DUID:
    """
    Parse a string representing a DUID into a real DUID

    :param duid_str: The string representation
    :return: The DUID object
    """
    duid_parts = duid_str.split(':')
    duid_type = duid_parts[0]
    if duid_type == 'enterprise':
        if len(duid_parts) == 3:
            hardware_type = int(duid_parts[1], 10)
            if duid_parts[2][:2] == '0x':
                identifier = bytes.fromhex(duid_parts[2][2:])
            else:
                identifier = duid_parts[2].encode('utf-8')
            return EnterpriseDUID(hardware_type, identifier)
        else:
            logger.critical(
                "Enterprise DUIDs must have format 'enterprise:<enterprise-nr>:<identifier>'"
            )
            raise ValueError

    elif duid_type == 'linklayer':
        if len(duid_parts) == 3:
            hardware_type = int(duid_parts[1], 10)
            address = bytes.fromhex(duid_parts[2])
            return LinkLayerDUID(hardware_type, address)
        else:
            logger.critical(
                "Link Layer DUIDs must have format 'linklayer:<hardware-type>:<address-hex>'"
            )
            raise ValueError

    elif duid_type == 'linklayer-time':
        if len(duid_parts) == 4:
            hardware_type = int(duid_parts[1], 10)
            timestamp = int(duid_parts[2], 10)
            address = bytes.fromhex(duid_parts[3])
            return LinkLayerTimeDUID(hardware_type, timestamp, address)
        else:
            logger.critical(
                "Link Layer + Time DUIDs must have format "
                "'linklayer-time:<hardware-type>:<time>:<address-hex>'")
            raise ValueError

    else:
        logger.critical("Unknown DUID type: {}".format(duid_type))
        raise ValueError
 def setUp(self):
     # The following attributes must be overruled by child classes
     # The basics are tested with a simple SolicitMessage
     self.packet_fixture = bytes.fromhex(
         '01'  # message_type
         '58595a'  # transaction_id
         '0001'  # option_type: OPTION_CLIENTID
         '0015'  # option_length
         '0002'  # duid_type: DUID_EN
         '00009d10'  # enterprise_number
         '444843504b6974556e697454657374'  # "DHCPKitUnitTest"
         '0008'  # option_type: OPTION_ELAPSED_TIME
         '0002'  # option_length
         '0000')  # elapsed_time
     self.message_fixture = SolicitMessage(
         transaction_id=b'XYZ',
         options=[
             ClientIdOption(duid=EnterpriseDUID(
                 enterprise_number=40208, identifier=b'DHCPKitUnitTest')),
             ElapsedTimeOption(elapsed_time=0)
         ])
     self.parse_packet()
예제 #8
0
 def setUp(self):
     self.duid_object = EnterpriseDUID(
         enterprise_number=40208, identifier=b'DHCPKitUnitTestIdentifier')
     self.duid_bytes = bytes.fromhex(
         '000200009d10444843504b6974556e6974546573744964656e746966696572')
예제 #9
0
 def test_wrong_parser(self):
     with self.assertRaisesRegex(ValueError,
                                 'does not contain EnterpriseDUID'):
         duid = EnterpriseDUID()
         duid.load_from(self.duid_bytes, length=len(self.duid_bytes))
예제 #10
0
 def test_validate_identifier(self):
     # noinspection PyTypeChecker
     bad_duid_object = EnterpriseDUID(0, 'demo')
     with self.assertRaisesRegex(ValueError, 'sequence of bytes'):
         bad_duid_object.validate()
예제 #11
0
    def test_validate_enterprise_number(self):
        good_duid_object = EnterpriseDUID(0, b'demo')
        good_duid_object.validate()

        bad_duid_object = EnterpriseDUID(-1, b'demo')
        with self.assertRaisesRegex(ValueError, 'unsigned 32 bit integer'):
            bad_duid_object.validate()

        bad_duid_object = EnterpriseDUID(2**32, b'demo')
        with self.assertRaisesRegex(ValueError, 'unsigned 32 bit integer'):
            bad_duid_object.validate()
예제 #12
0
 def setUp(self):
     self.option_bytes = b'\x00\x01\x00\x15\x00\x02\x00\x00\x9d\x100123456789abcde'
     self.option_object = ClientIdOption(EnterpriseDUID(40208, b'0123456789abcde'))
     self.parse_option()
예제 #13
0
 def test_wrong_parser(self):
     with self.assertRaisesRegex(ValueError, 'does not contain EnterpriseDUID'):
         duid = EnterpriseDUID()
         duid.load_from(self.duid_bytes, length=len(self.duid_bytes))
예제 #14
0
 def test_validate_identifier(self):
     # noinspection PyTypeChecker
     bad_duid_object = EnterpriseDUID(0, 'demo')
     with self.assertRaisesRegex(ValueError, 'sequence of bytes'):
         bad_duid_object.validate()
예제 #15
0
def main(args: Iterable[str]) -> int:
    """
    The main program

    :param args: Command line arguments
    :return: The program exit code
    """
    # Handle command line arguments
    options = handle_args(args)
    set_verbosity_logger(logger, options.verbosity)

    query = options.create(options)

    # Add ORO for relay data
    if options.relay_data:
        query.options.append(OptionRequestOption([OPTION_LQ_RELAY_DATA]))

    # Generate the outgoing message
    transaction_id = random.getrandbits(24).to_bytes(3, 'big')
    message_out = LeasequeryMessage(
        transaction_id,
        [ClientIdOption(EnterpriseDUID(40208, b'LeaseQueryTester')), query])

    # Create client socket
    if options.tcp:
        client = TCPClientSocket(options)
    else:
        # Check permission
        if os.getuid() != 0:
            raise RuntimeError("This tool needs to be run as root")

        client = UDPClientSocket(options)

    destination = client.send(message_out)
    logger.info("Sent to {}:\n{}".format(destination, message_out))

    # Wait for responses
    wait_for_multiple = options.server.is_multicast or options.tcp

    start = time.time()
    deadline = start + 3

    received = 0

    while time.time() < deadline:
        client.set_timeout(deadline - time.time())
        try:
            sender, message_in = client.recv()
            received += 1

            logger.info("Received from {}:\n{}".format(sender, message_in))

            if options.tcp:
                # Check bulk leasequery ending
                if isinstance(message_in, LeasequeryReplyMessage):
                    if not message_in.get_option_of_type(ClientDataOption):
                        # Reply without data, the end
                        break

                if isinstance(message_in, LeasequeryDoneMessage):
                    break

            if not wait_for_multiple:
                break
        except socket.timeout:
            pass

    logger.info(
        gettext.ngettext("{} response received", "{} responses received",
                         received).format(received))

    return 0