IPRoute in two words:
$ sudo pip install pyroute2
$ cat example.py
from pyroute2 import IPRoute
ip = IPRoute()
print([x.get_attr('IFLA_IFNAME') for x in ip.get_links()])
$ python example.py
['lo', 'p6p1', 'wlan0', 'virbr0', 'virbr0-nic']
Since v0.3.2, IPRoute class is threadless by default. It spawns no additional threads, and receives only responses to own requests, no broadcast messages. So, if you prefer not to cope with implicit threading, you can safely use this module.
To get broadcast messages, use IPRoute.bind() call. Please notice, that after calling IPRoute.bind() you MUST get all the messages in time. In the case of the kernel buffer overflow, you will have to restart the socket.
With IPRoute.bind(async=True) one can launch async message receiver thread with Queue-based buffer. The buffer is thread-safe and completely transparent from the programmer’s perspective. Please read also NetlinkSocket documentation to know more about async mode.
If you plan to regularly fetch loads of objects, think about IPDB also. Unlike to IPRoute, IPDB does not fetch all the objects from OS every time you request them, but keeps a cache that is asynchronously updated by the netlink broadcasts. For a long-term running programs, that often retrieve info about hundreds or thousands of objects, it can be better to use IPDB as it will load CPU significantly less.
Production class that provides iproute API over normal Netlink socket.
You can think of this class in some way as of plain old iproute2 utility.
IPRouteMixin should not be instantiated by itself. It is intended to be used as a mixin class that provides iproute2-like API. You should use IPRoute or NetNS classes.
All following info you can consider as IPRoute info as well.
It is an old-school API, that provides access to rtnetlink as is. It helps you to retrieve and change almost all the data, available through rtnetlink:
from pyroute2 import IPRoute
ipr = IPRoute()
# create an interface
ipr.link_create(ifname='brx', kind='bridge')
# lookup the index
dev = ipr.link_lookup(ifname='brx')[0]
# bring it down
ipr.link('set', index=dev, state='down')
# change the interface MAC address and rename it just for fun
ipr.link('set', index=dev,
address='00:11:22:33:44:55',
ifname='br-ctrl')
# add primary IP address
ipr.addr('add', index=dev,
address='10.0.0.1', mask=24,
broadcast='10.0.0.255')
# add secondary IP address
ipr.addr('add', index=dev,
address='10.0.0.2', mask=24,
broadcast='10.0.0.255')
# bring it up
ipr.link('set', index=dev, state='up')
Address operations
Later the method signature will be changed to:
def addr(self, command, match=None, **kwarg):
# the method body
So only keyword arguments (except of the command) will be accepted. The reason for this change is an unification of API.
Example:
idx = 62
ip.addr('add', index=idx, address='10.0.0.1', mask=24)
ip.addr('add', index=idx, address='10.0.0.2', mask=24)
With more NLAs:
# explicitly set broadcast address
ip.addr('add', index=idx,
address='10.0.0.3',
broadcast='10.0.0.255',
prefixlen=24)
# make the secondary address visible to ifconfig: add label
ip.addr('add', index=idx,
address='10.0.0.4',
broadcast='10.0.0.255',
prefixlen=24,
label='eth0:1')
Flush addresses.
Examples:
# flush all addresses on the interface with index 2:
ipr.flush_addr(index=2)
# flush all addresses with IFA_LABEL='eth0':
ipr.flush_addr(label='eth0')
Flush routes – purge route records from a table. Arguments are the same as for get_routes() routine. Actually, this routine implements a pipe from get_routes() to nlm_request().
Flush rules. Please keep in mind, that by default the function operates on all rules of all families. To work only on IPv4 rules, one should explicitly specify family=AF_INET.
Examples:
# flush all IPv4 rule with priorities above 5 and below 32000
ipr.flush_rules(family=AF_INET, priority=lambda x: 5 < x < 32000)
# flush all IPv6 rules that point to table 250:
ipr.flush_rules(family=socket.AF_INET6, table=250)
Dump addresses.
If family is not specified, both AF_INET and AF_INET6 addresses will be dumped:
# get all addresses
ip.get_addr()
It is possible to apply filters on the results:
# get addresses for the 2nd interface
ip.get_addr(index=2)
# get addresses with IFA_LABEL == 'eth0'
ip.get_addr(label='eth0')
# get all the subnet addresses on the interface, identified
# by broadcast address (should be explicitly specified upon
# creation)
ip.get_addr(index=2, broadcast='192.168.1.255')
A custom predicate can be used as a filter:
ip.get_addr(match=lambda x: x['index'] == 1)
Get classes for specified interface.
Get default routes
Get filters for specified interface, handle and parent.
Get network interfaces.
By default returns all interfaces. Arguments vector can contain interface indices or a special keyword ‘all’:
ip.get_links()
ip.get_links('all')
ip.get_links(1, 2, 3)
interfaces = [1, 2, 3]
ip.get_links(*interfaces)
Alias of get_neighbours(), deprecated.
Dump ARP cache records.
The family keyword sets the family for the request: e.g. AF_INET or AF_INET6 for arp cache, AF_BRIDGE for fdb.
If other keyword arguments not empty, they are used as filter. Also, one can explicitly set filter as a function with the match parameter.
Examples:
# get neighbours on the 3rd link:
ip.get_neighbours(ifindex=3)
# get a particular record by dst:
ip.get_neighbours(dst='172.16.0.1')
# get fdb records:
ip.get_neighbours(AF_BRIDGE)
# and filter them by a function:
ip.get_neighbours(AF_BRIDGE, match=lambda x: x['state'] == 2)
Get neighbour tables
Get all queue disciplines for all interfaces or for specified one.
Get all routes. You can specify the table. There are 255 routing classes (tables), and the kernel returns all the routes on each request. So the routine filters routes from full output.
Example:
ip.get_routes() # get all the routes for all families
ip.get_routes(family=AF_INET6) # get only IPv6 routes
ip.get_routes(table=254) # get routes from 254 table
Get all rules. By default return all rules. To explicitly request the IPv4 rules use family=AF_INET.
Link operations.
Example:
x = 62 # interface index
ip.link("set", index=x, state="down")
ip.link("set", index=x, address="00:11:22:33:44:55", name="bala")
ip.link("set", index=x, mtu=1000, txqlen=2000)
ip.link("set", index=x, state="up")
Keywords “state”, “flags” and “mask” are reserved. State can be “up” or “down”, it is a shortcut:
state="up": flags=1, mask=1
state="down": flags=0, mask=0
For more flags grep IFF in the kernel code, until we write human-readable flag resolver.
Other keywords are from ifinfmsg.nla_map, look into the corresponding module. You can use the form “ifname” as well as “IFLA_IFNAME” and so on, so that’s equal:
ip.link("set", index=x, mtu=1000)
ip.link("set", index=x, IFLA_MTU=1000)
You can also delete interface with:
ip.link("delete", index=x)
Create a link. The method parameters will be passed to the IPLinkRequest() constructor as a dictionary.
Examples:
ip.link_create(ifname='very_dummy', kind='dummy')
ip.link_create(ifname='br0', kind='bridge')
ip.link_create(ifname='v101', kind='vlan', vlan_id=101, link=1)
Switch an interface down unconditilnally.
Lookup interface index (indeces) by first level NLA value.
Example:
ip.link_lookup(address="52:54:00:9d:4e:3d")
ip.link_lookup(ifname="lo")
ip.link_lookup(operstate="UP")
Please note, that link_lookup() returns list, not one value.
Remove an interface
Rename an interface. Please note, that the interface must be in the DOWN state in order to be renamed, otherwise you will get an error.
Switch an interface up unconditionally.
Neighbours operations, same as ip neigh or bridge fdb
Example:
pass
Route operations.
pyroute2/netlink/rtnl/rtmsg.py rtmsg.nla_map:
etc.
Example:
ip.route("add", dst="10.0.0.0", mask=24, gateway="192.168.0.1")
Commands change and replace have the same meanings, as in ip-route(8): change modifies only existing route, while replace creates a new one, if there is no such route yet.
It is possible to set also route metrics. There are two ways to do so. The first is to use ‘raw’ NLA notation:
ip.route("add",
dst="10.0.0.0",
mask=24,
gateway="192.168.0.1",
metrics={"attrs": [["RTAX_MTU", 1400],
["RTAX_HOPLIMIT", 16]]})
The second way is to use IPRouteRequest helper:
from pyroute2.netlink.rtnl.req import IPRouteRequest
...
ip.route("add", **IPRouteRequest({"dst": "10.0.0.0/24",
"gateway": "192.168.0.1",
"metrics": {"mtu": 1400,
"hoplimit": 16}}))
The IPRouteRequest helper is useful also to manage mulptipath routes:
from pyroute2.netlink.rtnl.req import IPRouteRequest
...
request = {"dst": "10.0.0.0/24",
"multipath": [{"gateway": "192.168.0.1",
"hops": 2},
{"gateway": "192.168.0.2",
"hops": 1},
{"gateway": "192.168.0.3"}]}
ip.route("add", **IPRouteRequest(request))
Rule operations
command — add, delete
table — 0 < table id < 253
priority — 0 < rule’s priority < 32766
action — type of rule, default ‘FR_ACT_NOP’ (see fibmsg.py)
- rtscope — routing scope, default RT_SCOPE_UNIVERSE
(RT_SCOPE_UNIVERSE|RT_SCOPE_SITE| RT_SCOPE_LINK|RT_SCOPE_HOST|RT_SCOPE_NOWHERE)
- family — rule’s family (socket.AF_INET (default) or
socket.AF_INET6)
src — IP source for Source Based (Policy Based) routing’s rule
dst — IP for Destination Based (Policy Based) routing’s rule
src_len — Mask for Source Based (Policy Based) routing’s rule
- dst_len — Mask for Destination Based (Policy Based) routing’s
rule
- iifname — Input interface for Interface Based (Policy Based)
routing’s rule
- oifname — Output interface for Interface Based (Policy Based)
routing’s rule
“Swiss knife” for traffic control. With the method you can add, delete or modify qdiscs, classes and filters.
Command can be one of (“add”, “del”, “add-class”, “del-class”, “add-filter”, “del-filter”) (see commands dict in the code).
Handle notice: traditional iproute2 notation, like “1:0”, actually represents two parts in one four-bytes integer:
1:0 -> 0x10000
1:1 -> 0x10001
ff:0 -> 0xff0000
ffff:1 -> 0xffff0001
For pyroute2 tc() you can use both forms: integer like 0xffff0000 or string like ‘ffff:0000’. By default, handle is 0, so you can add simple classless queues w/o need to specify handle. Ingress queue causes handle to be 0xffff0000.
So, to set up sfq queue on interface 1, the function call will be like that:
ip = IPRoute()
ip.tc("add", "sfq", 1)
Instead of string commands (“add”, “del”...), you can use also module constants, RTM_NEWQDISC, RTM_DELQDISC and so on:
ip = IPRoute()
ip.tc(RTM_NEWQDISC, "sfq", 1)
More complex example with htb qdisc, lets assume eth0 == 2:
# u32 --> +--> htb 1:10 --> sfq 10:0
# | |
# | |
# eth0 -- htb 1:0 -- htb 1:1
# | |
# | |
# u32 --> +--> htb 1:20 --> sfq 20:0
eth0 = 2
# add root queue 1:0
ip.tc("add", "htb", eth0, 0x10000, default=0x200000)
# root class 1:1
ip.tc("add-class", "htb", eth0, 0x10001,
parent=0x10000,
rate="256kbit",
burst=1024 * 6)
# two branches: 1:10 and 1:20
ip.tc("add-class", "htb", eth0, 0x10010,
parent=0x10001,
rate="192kbit",
burst=1024 * 6,
prio=1)
ip.tc("add-class", "htb", eht0, 0x10020,
parent=0x10001,
rate="128kbit",
burst=1024 * 6,
prio=2)
# two leaves: 10:0 and 20:0
ip.tc("add", "sfq", eth0, 0x100000,
parent=0x10010,
perturb=10)
ip.tc("add", "sfq", eth0, 0x200000,
parent=0x10020,
perturb=10)
# two filters: one to load packets into 1:10 and the
# second to 1:20
ip.tc("add-filter", "u32", eth0,
parent=0x10000,
prio=10,
protocol=socket.AF_INET,
target=0x10010,
keys=["0x0006/0x00ff+8", "0x0000/0xffc0+2"])
ip.tc("add-filter", "u32", eth0,
parent=0x10000,
prio=10,
protocol=socket.AF_INET,
target=0x10020,
keys=["0x5/0xf+0", "0x10/0xff+33"])