NLMSG_ERROR responses¶
Some kernel subsystems return NLMSG_ERROR in response to any request. This is acceptable as long as nlmsg["header"]["error"] is None. If it is not None, an exception will be raised by the parser.
If you receive an NLMSG_ERROR message instead of an exception, it means error == 0, which is equivalent to $? == 0 in bash.
How to work with messages¶
Every netlink message contains a header, fields, and NLAs (netlink attributes). Each NLA is itself a netlink message (see "recursion").
The library parses messages according to this structure. Each RTNL message includes the following:
nlmsg['header'] -- parsed header
nlmsg['attrs'] -- NLA chain (parsed on demand)
data fields, e.g. nlmsg['flags'] etc.
nlmsg.header -- the header fields spec
nlmsg.fields -- the data fields spec
nlmsg.nla_map -- NLA spec
One key feature of the parser is that NLAs are parsed only on demand, i.e., when accessed. This prevents unnecessary CPU usage.
The NLA chain is a list-like structure rather than a dictionary because the netlink standard does not require NLAs to be unique within a single message:
{'attrs': [('IFLA_IFNAME', 'lo'), # [1]
('IFLA_TXQLEN', 1),
('IFLA_OPERSTATE', 'UNKNOWN'),
('IFLA_LINKMODE', 0),
('IFLA_MTU', 65536),
('IFLA_GROUP', 0),
('IFLA_PROMISCUITY', 0),
('IFLA_NUM_TX_QUEUES', 1),
('IFLA_NUM_RX_QUEUES', 1),
('IFLA_CARRIER', 1),
...],
'change': 0,
'event': 'RTM_NEWLINK', # [2]
'family': 0,
'flags': 65609,
'header': {'error': None, # [3]
'flags': 2,
'length': 1180,
'pid': 28233,
'sequence_number': 257, # [4]
'type': 16}, # [5]
'ifi_type': 772,
'index': 1}
# [1] every NLA is parsed upon access
# [2] this field is injected by the RTNL parser
# [3] if not None, an exception will be raised
# [4] more details in the netlink description
# [5] 16 == RTM_NEWLINK
To access fields or NLAs, use the .get() method. To retrieve nested NLAs, pass a tuple of NLA names to the .get() call to navigate through the hierarchy:
from pyroute2 import IPRoute
with IPRoute() as ipr:
lo = tuple(ipr.link("get", index=1))[0]
# get a field
assert lo.get("index") == 1
# get an NLA
assert lo.get("ifname") == "lo"
# get a nested NLA
assert lo.get(("stats64", "rx_bytes")) == 43309665
If an NLA with the specified name is not present in the chain, .get() returns None. To retrieve a list of all NLAs with the specified name, use .get_attrs().
Below is an example demonstrating the usage of .get() and .get_attrs() with an NLA hierarchy:
# for macvlan interfaces there may be several
# IFLA_MACVLAN_MACADDR NLA provided, so use
# get_attrs() to get all the list, not only
# the first one
(msg
.get('IFLA_LINKINFO') # one NLA
.get('IFLA_INFO_DATA') # one NLA
.get_attrs('IFLA_MACVLAN_MACADDR')) # a list of
The protocol itself does not impose a limit on the number of NLAs of the same type within a single message. This is why we cannot represent them as a dictionary, unlike with PF_ROUTE messages.