RTNL classes

Arguments to the constructors:

port: Optional[int]

An integer to be used together with pid in the bind() call, epid = pid + (port << 22).

pid: Optional[int]

Value to be used as the base in while calling bind()

fileno: Optional[int]

An open file descriptor to construct the socket from.

sndbuf: int

Send buffer limit in bytes.

rcvbuf: int

Receive buffer limit in bytes.

rcvsize: int

Maximum recieve packet size.

all_ns: bool

Turns on NETLINK_LISTEN_ALL_NSID on the socket.

async_qsize

Deprecated.

nlm_generator

Deprecated.

target: str

Target field (string) to be provided in the header. Useful when working with sockets in multiple network namespaces.

ext_ack: bool

Extended ACK controls reporting additional error or warning info in NLMSG_ERROR and NLMSG_DONE messages.

strict_check: bool

Controls strict input field checking. By default kernel does not validate the input fields, silently ignoring possible issues, that may lead to regressions in the future.

groups: int (default: pyroute2.netlink.rtnl.RTMGRP_DEFAULTS)

Groups to subscribe when calling bind(), see pyroute2.netlink.rtnl

nlm_echo: bool

Return the request fields in the response.

use_socket: Optional[socket.socket]

An existing socket object to run the protocol on.

netns: str

Network namespace to use.

flags: int (default: os.O_CREAT)

Flags to use when calling netns.create(). By default the library will create netns if it doesn't exist, and reuse if it does. In order to fail when the network namespace already exists, you should provide flags=0.

libc: Optional[ctypes.CDLL]

If you want the socket to use specific libc object when managing network namespaces, you can use this argument.

use_event_loop: Optional[asyncio.AbstractEventLoop]

Use an existing asyncio event loop.

RTNL classes:

class pyroute2.AsyncIPRSocket(port=None, pid=None, fileno=None, sndbuf=1048576, rcvbuf=1048576, rcvsize=16384, all_ns=False, async_qsize=None, nlm_generator=None, target='localhost', ext_ack=False, strict_check=False, groups=67372509, nlm_echo=False, netns=None, netns_path=None, flags=64, libc=None, use_socket=None, use_event_loop=None, telemetry=None)

A low-level class to provide RTNL socket.

This is a low-level class designed to provide an RTNL asyncio-controlled socket. It does not include high-level methods like those found in AsyncIPRoute. Instead, it provides only common netlink methods such as get() and put(). For more details, refer to the AsyncNetlinkSocket documentation.

Since the underlying socket is controlled by asyncio, it is not possible to use it in poll/select loops. If you want such API, consider using synchronous IPRSocket.

Warning

Your code must process incoming messages quickly enough to prevent the RCVBUF from overflowing. If the RCVBUF overflows, all subsequent socket operations will raise an OSError:

>>> [ x async for x in ipr.get() ]
Traceback (most recent call last):
  File ".../python3.13/futures/_base.py", line 456, in result
    return self.__get_result()
           ~~~~~~~~~~~~~~~~~^^
  ...
OSError: [Errno 105] No buffer space available

If this exception occurs, the only solution is to close the socket and create a new one.

This class does not handle protocol-level error propagation; it only provides socket-level error handling. It is the user's responsibility to catch and manage protocol-level errors:

if msg.get(('header', 'type')) == NLMSG_ERROR:
    # prints error code and the request that
    # triggered the error
    print(
        msg.get('error'),
        msg.get('msg'),
    )
class pyroute2.IPRSocket(port=None, pid=None, fileno=None, sndbuf=1048576, rcvbuf=1048576, rcvsize=16384, all_ns=False, async_qsize=None, nlm_generator=None, target='localhost', ext_ack=False, strict_check=False, groups=67372509, nlm_echo=False, netns=None, netns_path=None, flags=64, libc=None, use_socket=None, use_event_loop=None)

Synchronous select-compatible netlink socket.

IPRSocket is the synchronous counterpart to AsyncIPRSocket. A key feature of IPRSocket is that the underlying netlink socket operates out of asyncio control, allowing it to be used in poll/select loops.

Warning

Your code must process incoming messages quickly enough to prevent the RCVBUF from overflowing. If the RCVBUF overflows, all subsequent socket operations will raise an OSError:

>>> iprsock.get()
Traceback (most recent call last):
  File "<python-input-12>", line 1, in <module>
    iprsock.get()
    ~~~~~~~~^^
  File ".../pyroute2/netlink/rtnl/iprsocket.py", line 276, in get
    data = self.socket.recv(16384)
OSError: [Errno 105] No buffer space available
>>>

If this exception occurs, the only solution is to close the socket and create a new one.

Some usage examples:

import select

from pyroute2 import IPRSocket
from pyroute2.netlink import NLM_F_DUMP, NLM_F_REQUEST
from pyroute2.netlink.rtnl import RTM_GETLINK
from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg

with IPRSocket() as iprsock:
    iprsock.put(
        ifinfmsg(),
        msg_type=RTM_GETLINK,
        msg_flags=NLM_F_REQUEST | NLM_F_DUMP
    )

    ret = []

    while True:
        rl, wl, xl = select.select([iprsock], [], [], 0)
        if not len(rl):
            break
        ret.extend(iprsock.get())

    for link in ret:
        if link.get('event') == 'RTM_NEWLINK':
            print(
                link.get('ifname'),
                link.get('state'),
                link.get('address'),
            )
lo up 00:00:00:00:00:00
eth0 up 52:54:00:72:58:b2

Threadless RT netlink monitoring with blocking I/O calls:

>>> from pyroute2 import IPRSocket
>>> from pprint import pprint
>>> s = IPRSocket()
>>> s.bind()
>>> pprint(s.get())
[{'attrs': [('RTA_TABLE', 254),
            ('RTA_OIF', 2),
            ('RTA_GATEWAY', '192.168.122.1')],
  'dst_len': 0,
  'event': 'RTM_NEWROUTE',
  'family': 2,
  'flags': 0,
  'header': {'error': None,
             'flags': 2,
             'length': 52,
             'pid': 325359,
             'sequence_number': 255,
             'type': 24},
  'proto': 2,
  'scope': 0,
  'src_len': 0,
  'table': 254,
  'tos': 0,
  'type': 2}]
>>>

Like AsyncIPRSocket, it does not perform response reassembly, protocol-level error propagation, or packet buffering.

class pyroute2.AsyncIPRoute(port=None, pid=None, fileno=None, sndbuf=1048576, rcvbuf=1048576, rcvsize=16384, all_ns=False, async_qsize=None, nlm_generator=None, target='localhost', ext_ack=False, strict_check=False, groups=67372509, nlm_echo=False, netns=None, netns_path=None, flags=64, libc=None, use_socket=None, use_event_loop=None, telemetry=None)

Regular ordinary async utility class, provides RTNL API using AsyncIPRSocket as the transport level.

Warning

The project core is currently undergoing refactoring, so some methods may still use the old synchronous API. This will be addressed in future updates.

The main RTNL API class is built on an asyncio core. All methods that send netlink requests are asynchronous and return awaitables. Dump requests return asynchronous generators, while other requests return iterables, such as tuples or lists.

This design choice addresses the fact that RTNL dumps, such as routes or neighbors, can return an extremely large number of objects. Buffering the entire response in memory could lead to performance issues.

import asyncio

from pyroute2 import AsyncIPRoute


async def main():
    async with AsyncIPRoute() as ipr:
        # create a link: immediate evaluation
        await ipr.link("add", ifname="test0", kind="dummy")

        # dump links: lazy evaluation
        async for link in await ipr.link("dump"):
            print(link.get("ifname"))

asyncio.run(main())
lo
eth0
test0
class pyroute2.IPRoute(port=None, pid=None, fileno=None, sndbuf=1048576, rcvbuf=1048576, rcvsize=16384, all_ns=False, async_qsize=None, nlm_generator=None, target='localhost', ext_ack=False, strict_check=False, groups=67372509, nlm_echo=False, netns=None, flags=64, libc=None, use_socket=None, use_event_loop=None, telemetry=None)

A synchronous version of AsyncIPRoute. All the same API, but sync. Provides a legacy API for the old code that is not using asyncio.

This API is designed to be compatible with the old synchronous IPRoute from version 0.8.x and earlier:

from pyroute2 import IPRoute

with IPRoute() as ipr:
    for msg in ipr.addr("dump"):
        addr = msg.get("address")
        mask = msg.get("prefixlen")
        print(f"{addr}/{mask}")
127.0.0.1/8
192.168.122.28/24
from pyroute2 import IPRoute

with IPRoute() as ipr:

    # this request returns one match, one interface index
    eth0 = ipr.link_lookup(ifname="eth0")
    assert len(eth0) == 1  # 1 if exists else 0

    # this requests uses a lambda to filter interfaces
    # and returns all interfaces that are up
    nics_up = set(ipr.link_lookup(lambda x: x.get("flags") & 1))
    assert len(nics_up) == 2
    assert nics_up == {1, 2}
class pyroute2.NetNS(netns=None, flags=64, target='localhost', libc=None, groups=67372509)

The NetNS class, prior to version 0.9.1, was used to run the RTNL API in a network namespace. Starting with pyroute2 version 0.9.1, the network namespace functionality has been integrated into the library core. To run an IPRoute or AsyncIPRoute instance in a network namespace, simply use the netns argument:

from pyroute2 import IPRoute

with IPRoute(netns="test") as ipr:
    assert ipr.status["netns"] == "test"

After initialization, the netns name is available as .status["netns"].

The old synchronous NetNS class is still available for compatibility but now serves as a wrapper around IPRoute.

from pyroute2 import NetNS

with NetNS("test") as ns:
    assert ns.status["netns"] == "test"