DHCP support

DHCP support in pyroute2 is in very initial state, so it is in the «Development section» yet. DHCP protocol has nothing to do with netlink, but pyroute2 slowly moving from netlink-only library to some more general networking framework.

DHCP protocol

The DHCP implementation here is far from complete, but already provides some basic functionality. Later it will be extended with IPv6 support and more DHCP options will be added.

Right now it can be interesting mostly to developers, but not users and/or system administrators. So, the development hints first.

The packet structure description is intentionally implemented as for netlink packets. Later these two parsers, netlink and generic, can be merged, so the syntax is more or less compatible.

Packet fields

There are two big groups of items within any DHCP packet. First, there are BOOTP/DHCP packet fields, they’re defined with the fields attribute:

class dhcp4msg(msg):
    fields = ((name, format, policy),
              (name, format, policy),
              ...
              (name, format, policy))

The name can be any literal. Format should be specified as for the struct module, like B for uint8, or i for int32, or >Q for big-endian uint64. There are also aliases defined, so one can write uint8 or be16, or like that. Possible aliases can be seen in the pyroute2.protocols module.

The policy is a bit complicated. It can be a number or literal, and it will mean that it is a default value, that should be encoded if no other value is given.

But when the policy is a dictionary, it can contain keys as follows:

'l2addr': {'format': '6B',
           'decode': ...,
           'encode': ...}

Keys encode and decode should contain filters to be used in decoding and encoding procedures. The encoding filter should accept the value from user’s definition and should return a value that can be packed using format. The decoding filter should accept a value, decoded according to format, and should return value that can be used by a user.

The struct module can not decode IP addresses etc, so they should be decoded as 4s, e.g. Further transformation from 4 bytes string to a string like ‘10.0.0.1’ performs the filter.

DHCP options

DHCP options are described in a similar way:

options = ((code, name, format),
           (code, name, format),
           ...
           (code, name, format))

Code is a uint8 value, name can be any string literal. Format is a string, that must have a corresponding class, inherited from pyroute2.dhcp.option. One can find these classes in pyroute2.dhcp (more generic) or in pyroute2.dhcp.dhcp4msg (IPv4-specific). The option class must reside within dhcp message class.

Every option class can be decoded in two ways. If it has fixed width fields, it can be decoded with ordinary msg routines, and in this case it can look like that:

class client_id(option):
    fields = (('type', 'uint8'),
              ('key', 'l2addr'))

If it must be decoded by some custom rules, one can define the policy just like for the fields above:

class array8(option):
    policy = {'format': 'string',
              'encode': lambda x: array('B', x).tobytes(),
              'decode': lambda x: array('B', x).tolist()}

In the corresponding modules, like in pyroute2.dhcp.dhcp4msg, one can define as many custom DHCP options, as one need. Just be sure, that they are compatible with the DHCP server and all fit into 1..254 (uint8) – the 0 code is used for padding and the code 255 is the end of options code.

class pyroute2.dhcp.dhcpmsg(content=None, buf=b'', offset=0, value=None)
class array8(content=None, buf=b'', offset=0, value=None, code=0)
class be16(content=None, buf=b'', offset=0, value=None, code=0)
class be32(content=None, buf=b'', offset=0, value=None, code=0)
class client_id(content=None, buf=b'', offset=0, value=None, code=0)
class none(content=None, buf=b'', offset=0, value=None, code=0)
class string(content=None, buf=b'', offset=0, value=None, code=0)
class uint8(content=None, buf=b'', offset=0, value=None, code=0)
class pyroute2.dhcp.option(content=None, buf=b'', offset=0, value=None, code=0)

IPv4 DHCP socket

class pyroute2.dhcp.dhcp4socket.DHCP4Socket(ifname, port=68)

Parameters:

  • ifname – interface name to work on

This raw socket binds to an interface and installs BPF filter to get only its UDP port. It can be used in poll/select and provides also the context manager protocol, so can be used in with statements.

It does not provide any DHCP state machine, and does not inspect DHCP packets, it is totally up to you. No default values are provided here, except xid – DHCP transaction ID. If xid is not provided, DHCP4Socket generates it for outgoing messages.

get()

Get the next incoming packet from the socket and try to decode it as IPv4 DHCP. No analysis is done here, only MAC/IPv4/UDP headers are stripped out, and the rest is interpreted as DHCP.

put(msg=None, dport=67)

Put DHCP message. Parameters:

  • msg – dhcp4msg instance
  • dport – DHCP server port

If msg is not provided, it is constructed as default BOOTREQUEST + DHCPDISCOVER.

Examples:

sock.put(dhcp4msg({'op': BOOTREQUEST,
                   'chaddr': 'ff:11:22:33:44:55',
                   'options': {'message_type': DHCPREQUEST,
                               'parameter_list': [1, 3, 6, 12, 15],
                               'requested_ip': '172.16.101.2',
                               'server_id': '172.16.101.1'}}))

The method returns dhcp4msg that was sent, so one can get from there xid (transaction id) and other details.