DHCPv6 ====== The system provides a Python object :data:`alc.dhcpv6`, which is an instance of class :data:`alc.Dhcpv6`, to inspect and modify DHCPv6 packets. .. note:: Only the pre-provided :data:`alc.dhcpv6` object should be used in script, the class :data:`alc.Dhcpv6` should not be used directly. .. _alc_dot_dhcpv6: The :data:`alc.Dhcpv6` class ------------------------------- Instance Attributes ^^^^^^^^^^^^^^^^^^^ DHCPv6 header fields are exposed as instance attributes on the object. Depending on the message-type (client-server or relay), different attributes are supported. Trying to fetch an attribute from a different message-type raises an AttributeError. .. list-table:: Attributes defined on client-server messages :header-rows: 1 * - Attribute - Field - Access - Type * - pkt_len - total length, in bytes, of the original DHCPv6-layer - ro - int * - msg_type - msg-type - ro - bytes * - transaction_id - transaction-id - ro - bytes .. list-table:: Attributes defined on relay messages :header-rows: 1 * - Attribute - Field - Access - Type * - pkt_len - total length, in bytes, of the original DHCPv6-layer - ro - int * - msg_type - msg-type - ro - bytes * - hop_count - hop-count - rw - bytes * - link_addr - link-address - rw - bytes * - peer_addr - peer-address - rw - bytes Methods ^^^^^^^ .. method:: drop() Drops the packet. .. method:: getOptionList() Returns a tuple of option-codes that are present in the top-level packet. - The order of the elements is the same as they appear in the packet. - If there are multiple instances of the same option, the option-code also appears multiple times in the tuple. :: # for a c/s packet with options # Elapsed Time/Client Identifier/IANA/FQDN/Vendor Class/Option Request print(alc.dhcpv6.getOptionList()) # (8, 1, 3, 39, 16, 6) .. method:: get(op_code, /) Returns a tuple of all values of options with the specified option-code. The values are returned as the exact bytestrings as they appear in the packet. - If the specified option does not exist, the result is an empty tuple: () - If a specific instance has zero length or no values, the corresponding bytestring in the result tuple is an empty bytestring: b"" :param int option_code: option-code to fetch :rtype: tuple of bytestrings :: # For a packet with status-code option (code=0, msg="Address Assigned"): print(alc.dhcpv6.get(13)) # (b‘\x00\x00Address Assigned’,) .. method:: set(op_code, value, /) Deletes existing options of the specified code and inserts new options with the specified code and value. Multiple values can be specified in a tuple, in which case an option is added for each item in the tuple. :param int option_code: option-code to set :param bytes|tuple(bytes) value: value of the option; the length is computed from this value :: # To insert an address lease time option(51) with value 60: alc.dhcpv6.set(51, b'\x00\x00\x00\x3c') .. method:: clear(op_code, /) Removes all options with specified option-code. :param int option_code: option-code to clear .. method:: get_iana() Retrieves the IA_NA(3) option as an :ref:`OPDL` data structure. :rtype: :ref:`OPDL` .. method:: set_iana(opdl, /) Sets the IA_NA(3) option as an :ref:`OPDL` data structure. :param OPDL value: IA_NA option to be set .. method:: get_iata() Retrieves the IA_TA(4) option as an :ref:`OPDL` data structure. :rtype: :ref:`OPDL` .. method:: set_iata(opdl, /) Sets the IA_TA(3) option as an :ref:`OPDL` data structure. :param OPDL value: IA_TA option to be set .. method:: get_iapd() Retrieves the IA_PD(25) option as an :ref:`OPDL` data structure. :rtype: :ref:`OPDL` .. method:: set_iapd(opdl, /) Sets the IA_PD(3) option as an :ref:`OPDL` data structure. :param OPDL value: IA_PD option to be set .. method:: get_vendoropts() Retrieves the vendor_opts(17) option as an :ref:`OPDL` data structure. :rtype: :ref:`OPDL` .. method:: set_vendoropts(opdl, /) Sets the vendor_opts(17) option as an :ref:`OPDL` data structure. :param OPDL value: vendor specific option to set .. method:: get_relaymsg() Returns the embedded relay-message. :rtype: :class:`alc.Dhcpv6` :raises RuntimeError: when invoked on a client-server message .. note:: The embedded packet returned from :meth:`get_relaymsg` can also be modified in-place. However, these changes do not automatically propagate to the outer packet. This occurs only when the :meth:`set_relaymsg` method is called on the outer packet with the modified embedded packet as the argument. .. method:: set_relaymsg(msg, /) Replaces the current relay-message option with the specified message. :param alc.Dhcpv6 msg: DHCPv6 message to set as the relay-message :raises RuntimeError: when invoked on a client-server message .. _OPDL: Option Datastructure List (OPDL) -------------------------------- The OPDL is a nested data structure which represents the value of some DHCPv6 options. - An OPDL is a list where each element represents an instance of that option (referred to as an OPD). The elements are ordered as they appear in the packet. - An OPD is a list where each element represents one field in the option. The elements are ordered as they appear in the packet. - There are two types of fields in the OPD: - The first fields (if any) are bytestrings and correspond to fixed fields. The number and meaning are specific to the option. - The last field is a dictionary which contains the sub-options, where - the keys are the sub-option codes - the values are themselves an OPDL if the sub-options are supported, or a bytestring of the whole sub-option if not supported. - if there are no sub-options, the dictionary is empty. In summary: - OPDL := [OPD, OPD, ...] - OPD := [field, field, ..., sub-options] - field := bytestring - sub-options := {sub-opt-code: sub-opt-value, ...} - sub-opt-code := int - sub-opt-value := bytestring or OPDL .. list-table:: supported options and fields :header-rows: 1 * - Option - Fields - Sub-options * - IA-NA(3) - `[IAID, T1, T2]` - IAADDR(5), Status Code(13) * - IA-TA(4) - `[IAID]` - IA Address(5), Status Code(13) * - IA-PD(25) - `[IAID, T1, T2]` - IA Prefix(26), Status Code(13) * - Vendor Options(17) - `[enterprise-number]` - * - IA Address(5) - `[ipv6addr, preferred-lifetime, valid-lifetime]` - Status Code(13) * - Status Code(13) - `[status-code, status-message`] - * - IA Prefix(26) - `[preferred-lifetime, valid-lifetime, prefix-length, ipv6-prefix]` - Status Code(13) .. note:: The OPDL data structures that have been extracted from a :class:`alc.Dhcpv6` packet can be modified in-place. However, these changes do not automatically propagate to packet options. This occurs only when the corresponding :meth:`set_xxx` method is called with the modified OPDL as the argument. Example 1: Extract address from IA_NA option ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ With an IA_NA option as in the following:: Identity Association for Non-temporary Address Option: Identity Association for the Non-temporary Address (3) Length: 40 Value: 0ff0def10002a30000043800000500182001055860450047... IAID: 0ff0def1 T1: 172800 T2: 276480 IA Address: 2001:558:6045:47:45cc:d9f2:5727:eae0 Option: IA Address (5) Length: 24 Value: 200105586045004745ccd9f25727eae00005460000054600 IPv6 address: 2001:558:6045:47:45cc:d9f2:5727:eae0 Preferred lifetime: 345600 Valid lifetime: 345600 The following extracts the address from the IA_NA option:: opdl = alc.dhcpv6.get_iana() # opdl: [[b'\x0f\xf0\xde\xf1', b'\x00\x02\xa3\x00', b'\x00\x048\x00', {5: [[b" \x01\x05X`E\x00GE\xcc\xd9\xf2W'\xea\xe0", b'\x00\x05F\x00', b'\x00\x05F\x00', {}]]}]] addresses = [hexlify(sub_opd[0]) for opd in opdl for sub_opd in opd[3][5]] # addresses: [b'200105586045004745ccd9f25727eae0'] Example 2: Extract information from IA_PD option ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ With an IA_PD option as in the following:: Identity Association for Prefix Delegation Option: Identity Association for Prefix Delegation (25) Length: 41 Value: 000000010000070800000b40001a001900000e1000015180... IAID: 00000001 T1: 1800 T2: 2880 IA Prefix Option: IA Prefix (26) Length: 25 Value: 00000e10000151803820010db80002000000000000000000... Preferred lifetime: 3600 Valid lifetime: 86400 Prefix length: 56 Prefix address: 2001:db8:2:: The following code extracts the IAID, T1, and T2:: opdl = alc.dhcpv6.get_iapd() # opdl: [[b'\x00\x00\x00\x01', b'\x00\x00\x07\x08', b'\x00\x00\x0b@', {26: [[b'\x00\x00\x0e\x10', b'\x00\x01Q\x80', b'8', b' \x01\r\xb8\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', {}]]}]] iaid,t1,t2 = (int.from_bytes(x, 'big') for x in opdl[0][0:3]) # iaid,t1,t2 = (1, 1800, 2880) Example 3: Extract Enterprise ID from Vendor Option ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ With a Vendor Option as in the following:: Vendor-specific Information Option: Vendor-specific Information (17) Length: 40 Value: 0000197f0001000969612d6e615f3030310002000969612d... Enterprise ID: Panthera Networks, Inc. (6527) option option code: 1 option length: 9 option-data option option code: 2 option length: 9 option-data option option code: 3 option length: 1 option-data option option code: 4 option length: 1 option-data The following code extracts the vendor ID:: opdl = alc.dhcpv6.get_vendoropts() # opdl: [[b'\x00\x00\x19\x7f', {4: [b'@'], 1: [b'ia-na_001'], 2: [b'ia-pd_001'], 3: [b'8']}]] vendor = int.from_bytes(opdl[0][0], 'big') # vendor: 6527 Example 4: Change T1 of IA_NA option in relayed message ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: embed_dhcpv6_packet = alc.dhcpv6.get_relaymsg() iana_list = embed_dhcpv6_packet.get_iana() iana_list[0][1] = b'\x00\x00\x04\xb0' # change T1 to 1200 embed_dhcpv6_packet.set_iana(iana_list) #update the IA_NA of the embedded packet alc.dhcpv6.set_relaymsg(embed_dhcpv6_packet) #update the Relay Message option Example 5: Change T1 of IA_NA option in double relayed message ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: embed_lv1_packet = alc.dhcpv6.get_relaymsg() embed_lv2_packet = embed_lv1_packet.get_relaymsg() iana_list = embed_dhcpv6_packet.get_iana() iana_list[0][1] = b'\x00\x00\x04\xb0' # change T1 to 1200 embed_lv2_packet.set_iana(iana_list) embed_lv1_packet.set_relaymsg(embed_lv2_packet) alc.dhcpv6.set_relaymsg(embed_lv1_packet)