Navigation

  • index
  • modules |
  • next |
  • previous |
  • pyroute2 0.3.14-18-g70051b2 documentation »

Netlink basics¶

General netlink packet structure:

nlmsg packet:
    header
    data

Generic netlink message header:

nlmsg header:
    uint32 length
    uint16 type
    uint16 flags
    uint32 sequence number
    uint32 pid

The length field is the length of all the packet, including data and header. The type field is used to distinguish different message types, commands etc. Please note, that there is no explicit protocol field – you choose a netlink protocol, when you create a socket.

The sequence number is very important. Netlink is an asynchronous protocol – it means, that the packet order doesn’t matter and is not guaranteed. But responses to a request are always marked with the same sequence number, so you can treat it as a cookie.

Please keep in mind, that a netlink request can initiate a cascade of events, and netlink messages from these events can carry sequence number == 0. E.g., it is so when you remove a primary IP addr from an interface, when promote_secondaries sysctl is set.

Beside of incapsulated headers and other protocol-specific data, netlink messages can carry NLA (netlink attributes). NLA structure is as follows:

NLA header:
    uint16 length
    uint16 type
NLA data:
    data-specific struct
    # optional:
    NLA
    NLA
    ...

So, NLA structures can be nested, forming a tree.

Complete structure of a netlink packet:

nlmsg header:
    uint32 length
    uint16 type
    uint16 flags
    uint32 sequence number
    uint32 pid
[ optional protocol-specific data ]
[ optional NLA tree ]

More information about netlink protocol you can find in the man pages.

Pyroute2 and netlink¶

packets¶

To simplify the development, pyroute2 provides an easy way to describe packet structure. As an example, you can take the ifaddrmsg description – pyroute2/netlink/rtnl/ifaddrmsg.py.

To describe a packet, you need to inherit from nlmsg class:

from pyroute2.netlink import nlmsg

class foo_msg(nlmsg):
    fields = ( ... )
    nla_map = ( ... )

NLA are described in the same way, but the parent class should be nla, instead of nlmsg. And yes, it is important to use the proper parent class – it affects the header structure.

fields attribute¶

The fields attribute describes the structure of the protocol-specific data. It is a tuple of tuples, where each member contains a field name and its data format.

Field data format should be specified as for Python struct module. E.g., ifaddrmsg structure:

struct ifaddrmsg {
    __u8  ifa_family;
    __u8  ifa_prefixlen;
    __u8  ifa_flags;
    __u8  ifa_scope;
    __u32 ifa_index;
};

should be described as follows:

class ifaddrmsg(nlmsg):
    fields = (('family', 'B'),
              ('prefixlen', 'B'),
              ('flags', 'B'),
              ('scope', 'B'),
              ('index', 'I'))

Format strings are passed directly to the struct module, so you can use all the notations like >I, 16s etc. All fields are parsed from the stream separately, so if you want to explicitly fix alignemt, as if it were C struct, use the pack attribute:

class tstats(nla):
    pack = 'struct'
    fields = (('version', 'H'),
              ('ac_exitcode', 'I'),
              ('ac_flag', 'B'),
              ...)

Explicit padding bytes also can be used, when struct packing doesn’t work well:

class ipq_mode_msg(nlmsg):
    pack = 'struct'
    fields = (('value', 'B'),
              ('__pad', '7x'),
              ('range', 'I'),
              ('__pad', '12x'))

nla_map attribute¶

The nla_map attribute is a tuple of NLA descriptions. Each description is also a tuple in two different forms: either two fields, name and format, or three fields: type, name and format.

Please notice, that the format field is a string name of corresponding NLA class:

class ifaddrmsg(nlmsg):
    ...
    nla_map = (('IFA_UNSPEC',  'hex'),
               ('IFA_ADDRESS', 'ipaddr'),
               ('IFA_LOCAL', 'ipaddr'),
               ...)

This code will create mapping, where IFA_ADDRESS NLA will be of type 1 and IFA_LOCAL – of type 2, etc. Both NLA will be decoded as IP addresses (class ipaddr). IFA_UNSPEC will be of type 0, and if it will be in the NLA tree, it will be just dumped in hex.

NLA class names are should be specified as strings, since they are resolved in runtime.

There are several pre-defined NLA types, that you will get with nla class:

  • none – ignore this NLA
  • flag – boolean flag NLA (no payload; NLA exists = True)
  • uint8, uint16, uint32, uint64 – unsigned int
  • be8, be16, be32, be64 – big-endian unsigned int
  • ipaddr – IP address, IPv4 or IPv6
  • ip4addr – only IPv4 address type
  • ip6addr – only IPv6 address type
  • target – a univeral target (IPv4, IPv6, MPLS)
  • l2addr – MAC address
  • hex – hex dump as a string – useful for debugging
  • cdata – a binary data
  • string – UTF-8 string
  • asciiz – zero-terminated ASCII string, no decoding
  • array – array of simple types (uint8, uint16 etc.)

Please refer to pyroute2/netlink/__init__.py for details.

You can also make your own NLA descriptions:

class ifaddrmsg(nlmsg):
    ...
    nla_map = (...
               ('IFA_CACHEINFO', 'cacheinfo'),
               ...)

    class cacheinfo(nla):
        fields = (('ifa_prefered', 'I'),
                  ('ifa_valid', 'I'),
                  ('cstamp', 'I'),
                  ('tstamp', 'I'))

Custom NLA descriptions should be defined in the same class, where they are used.

Also, it is possible to use not autogenerated type numbers, as for ifaddrmsg, but specify them explicitly:

class iw_event(nla):
    ...
    nla_map = ((0x8B00, 'SIOCSIWCOMMIT', 'hex'),
               (0x8B01, 'SIOCGIWNAME', 'hex'),
               (0x8B02, 'SIOCSIWNWID', 'hex'),
               (0x8B03, 'SIOCGIWNWID', 'hex'),
               ...)

Here you can see custom NLA type numbers – 0x8B00, 0x8B01 etc. It is not permitted to mix these two forms in one class: you should use ether autogenerated type numbers (two fields tuples), or explicit numbers (three fields typles).

array types¶

There are different array-like NLA types in the kernel, and some of them are covered by pyroute2. An array of simple type elements:

# declaration
nla_map = (('NLA_TYPE', 'array(uint8)'), ...)

# data layout
+======+======+----------------------------
| len  | type | uint8 | uint8 | uint 8 | ...
+======+======+----------------------------

# decoded
{'attrs': [['NLA_TYPE', (2, 3, 4, 5, ...)], ...], ...}

An array of NLAs:

# declaration
nla_map = (('NLA_TYPE', '*type'), ...)

# data layout
+=======+=======+-----------------------+-----------------------+--
| len   | type* | len  | type | payload | len  | type | payload | ...
+=======+=======+-----------------------+-----------------------+--
# type* -- in that case the type is OR'ed with NLA_F_NESTED

# decoded
{'attrs': [['NLA_TYPE', [payload, payload, ...]], ...], ...}

parsed netlink message¶

Netlink messages are represented by pyroute2 as dictionaries as follows:

{'header': {'pid': ...,
            'length: ...,
            'flags': ...,
            'error': None,  # if you are lucky
            'type': ...,
            'sequence_number': ...},

 # fields attributes
 'field_name1': value,
 ...
 'field_nameX': value,

 # nla tree
 'attrs': [['NLA_NAME1', value],
           ...
           ['NLA_NAMEX', value],
           ['NLA_NAMEY', {'field_name1': value,
                          ...
                          'field_nameX': value,
                          'attrs': [['NLA_NAME.... ]]}]]}

As an example, a message from the wireless subsystem about new scan event:

{'index': 4,
 'family': 0,
 '__align': 0,
 'header': {'pid': 0,
            'length': 64,
            'flags': 0,
            'error': None,
            'type': 16,
            'sequence_number': 0},
 'flags': 69699,
 'ifi_type': 1,
 'event': 'RTM_NEWLINK',
 'change': 0,
 'attrs': [['IFLA_IFNAME', 'wlp3s0'],
           ['IFLA_WIRELESS',
            {'attrs': [['SIOCGIWSCAN',
                        '00:00:00:00:00:00:00:00:00:00:00:00']]}]]}

One important detail is that NLA chain is represented as a list of elements [‘NLA_TYPE’, value], not as a dictionary. The reason is that though in the kernel usually NLA chain is a dictionary, the netlink protocol by itself doesn’t require elements of each type to be unique. In a message there may be several NLA of the same type.

encoding and decoding algo¶

The message encoding works as follows:

  1. Reserve space for the message header (if there is)
  2. Iterate defined fields, encoding values with struct.pack()
  3. Iterate NLA from the attrs field, looking up types in nla_map
  4. Encode the header

Since every NLA is also an nlmsg object, there is a recursion.

The decoding process is a bit simpler:

  1. Decode the header
  2. Iterate fields, decoding values with struct.unpack()
  3. Iterate NLA until the message ends

If the fields attribute is an empty list, the step 2 will be skipped. The step 3 will be skipped in the case of the empty nla_map. If both attributes are empty lists, only the header will be encoded/decoded.

create and send messages¶

Using high-level interfaces like IPRoute or IPDB, you will never need to manually construct and send netlink messages. But in the case you really need it, it is simple as well.

Having a description class, like ifaddrmsg from above, you need to:

  • instantiate it
  • fill the fields
  • encode the packet
  • send the encoded data

The code:

from pyroute2.netlink import NLM_F_REQUEST
from pyroute2.netlink import NLM_F_ACK
from pyroute2.netlink import NLM_F_CREATE
from pyroute2.netlink import NLM_F_EXCL
from pyroute2.iproute import RTM_NEWADDR
from pyroute2.netlink.rtnl.ifaddrmsg import ifaddrmsg

##
# add an addr to an interface
#

# create the message
msg = ifaddrmsg()

# fill the protocol-specific fields
msg['index'] = index  # index of the interface
msg['family'] = AF_INET  # address family
msg['prefixlen'] = 24  # the address mask
msg['scope'] = scope  # see /etc/iproute2/rt_scopes

# attach NLA -- it MUST be a list / mutable
msg['attrs'] = [['IFA_LOCAL', '192.168.0.1'],
                ['IFA_ADDRESS', '192.162.0.1']]

# fill generic netlink fields
msg['header']['sequence_number'] = nonce  # an unique seq number
msg['header']['pid'] = os.getpid()
msg['header']['type'] = RTM_NEWADDR
msg['header']['flags'] = NLM_F_REQUEST |\
                         NLM_F_ACK |\
                         NLM_F_CREATE |\
                         NLM_F_EXCL

# encode the packet
msg.encode()

# send the buffer
nlsock.sendto(msg.buf.getvalue(), (0, 0))

Please notice, that NLA list MUST be mutable.


Module contents:

exception pyroute2.netlink.NetlinkDataDecodeError(exception)¶

The error occured while decoding the message fields

exception pyroute2.netlink.NetlinkDecodeError(exception)¶

Base decoding error class.

Incapsulates underlying error for the following analysis

exception pyroute2.netlink.NetlinkError(code, msg=None)¶

Base netlink error

exception pyroute2.netlink.NetlinkHeaderDecodeError(exception)¶

The error occured while decoding a header

exception pyroute2.netlink.NetlinkNLADecodeError(exception)¶

The error occured while decoding NLA chain

class pyroute2.netlink.ctrlmsg(buf=None, length=None, parent=None, debug=False, init=None)¶

Netlink control message

class pyroute2.netlink.genlmsg(buf=None, length=None, parent=None, debug=False, init=None)¶

Generic netlink message

class pyroute2.netlink.nla(buf=None, length=None, parent=None, debug=False, init=None)¶

Main NLA class

class pyroute2.netlink.nla_base(buf=None, length=None, parent=None, debug=False, init=None)¶

The NLA base class. Use nla_header class as the header.

header¶

alias of nla_header

class pyroute2.netlink.nla_header(buf=None, length=None, parent=None, debug=False, init=None)¶

The NLA header structure: uin16 length and uint16 type.

class pyroute2.netlink.nlmsg(buf=None, length=None, parent=None, debug=False, init=None)¶

Main netlink message class

header¶

alias of nlmsg_header

class pyroute2.netlink.nlmsg_atoms(buf=None, length=None, parent=None, debug=False, init=None)¶

A collection of base NLA types

class array(buf=None, length=None, parent=None, debug=False, init=None)¶

Array of simple data type

class nlmsg_atoms.asciiz(buf=None, length=None, parent=None, debug=False, init=None)¶

Zero-terminated string.

nlmsg_atoms.binary¶

alias of cdata

class nlmsg_atoms.cdata(buf=None, length=None, parent=None, debug=False, init=None)¶

Binary data

class nlmsg_atoms.flag(buf=None, length=None, parent=None, debug=False, init=None)¶

‘flag’ type is used to denote attrs that have no payload

class nlmsg_atoms.hex(buf=None, length=None, parent=None, debug=False, init=None)¶

Represent NLA’s content with header as hex string.

class nlmsg_atoms.ip4addr(buf=None, length=None, parent=None, debug=False, init=None)¶

Explicit IPv4 address type class.

class nlmsg_atoms.ip6addr(buf=None, length=None, parent=None, debug=False, init=None)¶

Explicit IPv6 address type class.

class nlmsg_atoms.ipaddr(buf=None, length=None, parent=None, debug=False, init=None)¶

This class is used to decode IP addresses according to the family. Socket library currently supports only two families, AF_INET and AF_INET6.

We do not specify here the string size, it will be calculated in runtime.

class nlmsg_atoms.l2addr(buf=None, length=None, parent=None, debug=False, init=None)¶

Decode MAC address.

class nlmsg_atoms.none(buf=None, length=None, parent=None, debug=False, init=None)¶

‘none’ type is used to skip decoding of NLA. You can also use ‘hex’ type to dump NLA’s content.

nlmsg_atoms.nul_string¶

alias of asciiz

class nlmsg_atoms.string(buf=None, length=None, parent=None, debug=False, init=None)¶

UTF-8 string.

class nlmsg_atoms.target(buf=None, length=None, parent=None, debug=False, init=None)¶

A universal target class. The target type depends on the msg family:

  • AF_INET: IPv4 addr, string: “127.0.0.1”
  • AF_INET6: IPv6 addr, string: ”::1”
  • AF_MPLS: MPLS labels, 0 .. k: [{“label”: 0x20, “ttl”: 16}, ...]
class pyroute2.netlink.nlmsg_base(buf=None, length=None, parent=None, debug=False, init=None)¶

Netlink base class. You do not need to inherit it directly, unless you’re inventing completely new protocol structure.

Use nlmsg or nla classes.

The class provides several methods, but often one need to customize only decode() and encode().

copy()¶

Return a decoded copy of the netlink message. Works correctly only if the message was encoded, or is received from the socket.

decode()¶

Decode the message. The message should have the buf attribute initialized. e.g.:

data = sock.recv(16384)
msg = ifinfmsg(data)

If you want to customize the decoding process, override the method, but don’t forget to call parent’s decode():

class CustomMessage(nlmsg):

    def decode(self):
        nlmsg.decode(self)
        ...  # do some custom data tuning
decode_nlas()¶

Decode the NLA chain. Should not be called manually, since it is called from decode() routine.

encode()¶

Encode the message into the binary buffer:

msg.encode()
sock.send(msg.buf.getvalue())

If you want to customize the encoding process, override the method:

class CustomMessage(nlmsg):

    def encode(self):
        ...  # do some custom data tuning
        nlmsg.encode(self)
encode_nlas()¶

Encode the NLA chain. Should not be called manually, since it is called from encode() routine.

get_attr(attr, default=None, fmt='raw')¶

Return the first attr by name or None

get_attrs(attr, fmt='raw')¶

Return attrs by name

get_encoded(attr, default=None)¶

Return the first encoded NLA by name

getvalue()¶

Atomic NLAs return their value in the ‘value’ field, not as a dictionary. Complex NLAs return whole dictionary.

classmethod name2nla(name)¶

Convert human-friendly name into NLA name

Example: address -> IFLA_ADDRESS

Requires self.prefix to be set

classmethod nla2name(name)¶

Convert NLA name into human-friendly name

Example: IFLA_ADDRESS -> address

Requires self.prefix to be set

register_nlas()¶

Convert ‘nla_map’ tuple into two dictionaries for mapping and reverse mapping of NLA types.

ex: given:

nla_map = (('TCA_HTB_UNSPEC', 'none'),
           ('TCA_HTB_PARMS', 'htb_parms'),
           ('TCA_HTB_INIT', 'htb_glob'))

creates:

t_nla_map = {0: (<class 'pyroute2...none'>, 'TCA_HTB_UNSPEC'),
             1: (<class 'pyroute2...htb_parms'>, 'TCA_HTB_PARMS'),
             2: (<class 'pyroute2...htb_glob'>, 'TCA_HTB_INIT')}
r_nla_map = {'TCA_HTB_UNSPEC': (<class 'pyroute2...none'>, 0),
             'TCA_HTB_PARMS': (<class 'pyroute2...htb_parms'>, 1),
             'TCA_HTB_INIT': (<class 'pyroute2...htb_glob'>, 2)}

nla_map format:

nla_map = (([ID, ] NAME, TYPE[, FLAGS]), ...)

Items in [...] are optional. If ID is not given, then the map will be autonumerated from 0. If flags are not given, they are 0 by default.

reserve()¶

Reserve space in the buffer for data. This can be used to skip encoding of the header until some fields will be known.

reset(buf=None)¶

Reset the message buffer. Optionally, set the message from the buf parameter. This parameter can be either string, or io.BytesIO, or dict instance.

strip(attrs)¶

Remove an NLA from the attrs chain. The attrs parameter can be either string, or iterable. In the latter case, will be stripped NLAs, specified in the provided list.

class pyroute2.netlink.nlmsg_header(buf=None, length=None, parent=None, debug=False, init=None)¶

Common netlink message header

Table Of Contents

  • Netlink basics
  • Pyroute2 and netlink
    • packets
    • fields attribute
    • nla_map attribute
    • array types
    • parsed netlink message
    • encoding and decoding algo
    • create and send messages

Previous topic

Module architecture

Next topic

Base netlink socket and marshal

This Page

  • Show Source

Quick search

Enter search terms or a module, class or function name.

Navigation

  • index
  • modules |
  • next |
  • previous |
  • pyroute2 0.3.14-18-g70051b2 documentation »
© Copyright 2013, Peter V. Saveliev. Created using Sphinx 1.2.3.