Modules in progress

There are several modules in the very initial development state, and the help with them will be particularly valuable. You are more than just welcome to help with:

IPSet module

ipset support.

This module is tested with hash:ip, hash:net, list:set and several other ipset structures (like hash:net,iface). There is no guarantee that this module is working with all available ipset modules.

It supports almost all kernel commands (create, destroy, flush, rename, swap, test…)

class pyroute2.ipset.IPSet(version=6, attr_revision=None, nfgen_family=2)

NFNetlink socket (family=NETLINK_NETFILTER).

Implements API to the ipset functionality.

add(name, entry, family=<AddressFamily.AF_INET: 2>, exclusive=True, comment=None, timeout=None, etype='ip', skbmark=None, skbprio=None, skbqueue=None, **kwargs)

Add a member to the ipset.

etype is the entry type that you add to the ipset. It’s related to the ipset type. For example, use “ip” for one hash:ip or bitmap:ip ipset.

When your ipset store a tuple, like “hash:net,iface”, you must use a comma a separator (etype=”net,iface”)

entry is a string for “ip” and “net” objects. For ipset with several dimensions, you must use a tuple (or a list) of objects.

“port” type is specific, since you can use integer of specialized containers like PortEntry and PortRange

Examples:

ipset = IPSet()
ipset.create("foo", stype="hash:ip")
ipset.add("foo", "198.51.100.1", etype="ip")

ipset = IPSet()
ipset.create("bar", stype="bitmap:port",
             bitmap_ports_range=(1000, 2000))
ipset.add("bar", 1001, etype="port")
ipset.add("bar", PortRange(1500, 2000), etype="port")

ipset = IPSet()
import socket
protocol = socket.getprotobyname("tcp")
ipset.create("foobar", stype="hash:net,port")
port_entry = PortEntry(80, protocol=protocol)
ipset.add("foobar", ("198.51.100.0/24", port_entry),
          etype="net,port")
create(name, stype='hash:ip', family=<AddressFamily.AF_INET: 2>, exclusive=True, counters=False, comment=False, maxelem=65536, forceadd=False, hashsize=None, timeout=None, bitmap_ports_range=None, size=None, skbinfo=False)

Create an ipset name of type stype, by default hash:ip.

Common ipset options are supported:

  • exclusive – if set, raise an error if the ipset exists
  • counters – enable data/packets counters
  • comment – enable comments capability
  • maxelem – max size of the ipset
  • forceadd – you should refer to the ipset manpage
  • hashsize – size of the hashtable (if any)
  • timeout – enable and set a default value for entries (if not None)
  • bitmap_ports_range – set the specified inclusive portrange for
    the bitmap ipset structure (0, 65536)
  • size – Size of the list:set, the default is 8
  • skbinfo – enable skbinfo capability
delete(name, entry, family=<AddressFamily.AF_INET: 2>, exclusive=True, etype='ip')

Delete a member from the ipset.

See add() method for more information on etype.

destroy(name=None)

Destroy one (when name is set) or all ipset (when name is None)

flush(name=None)

Flush all ipsets. When name is set, flush only this ipset.

get_supported_revisions(stype, family=<AddressFamily.AF_INET: 2>)

Return minimum and maximum of revisions supported by the kernel.

Each ipset module (like hash:net, hash:ip, etc) has several revisions. Newer revisions often have more features or more performances. Thanks to this call, you can ask the kernel the list of supported revisions.

You can manually set/force revisions used in IPSet constructor.

Example:

ipset = IPSet()
ipset.get_supported_revisions("hash:net")

ipset.get_supported_revisions("hash:net,port,net")
headers(name, **kwargs)

Get headers of the named ipset. It can be used to test if one ipset exists, since it returns a no such file or directory.

list(*argv, **kwargs)

List installed ipsets. If name is provided, list the named ipset or return an empty list.

Be warned: netlink does not return an error if given name does not exit, you will receive an empty list.

rename(name_src, name_dst)

Rename the ipset.

swap(set_a, set_b)

Swap two ipsets. They must have compatible content type.

test(name, entry, family=<AddressFamily.AF_INET: 2>, etype='ip')

Test if entry is part of an ipset

See add() method for more information on etype.

class pyroute2.ipset.PortEntry(port, protocol=None)

A simple container for port entry with optional protocol

class pyroute2.ipset.PortRange(begin, end, protocol=None)

A simple container for port range with optional protocol

Note that optional protocol parameter is not supported by all kernel ipset modules using ports. On the other hand, it’s sometimes mandatory to set it (like for hash:net,port ipsets)

Example:

udp_proto = socket.getprotobyname("udp")
port_range = PortRange(1000, 2000, protocol=udp_proto)
ipset.create("foo", stype="hash:net,port")
ipset.add("foo", ("192.0.2.0/24", port_range), etype="net,port")
ipset.test("foo", ("192.0.2.0/24", port_range), etype="net,port")

IW module

Experimental wireless module — nl80211 support.

Disclaimer

Unlike IPRoute, which is mostly usable, though is far from complete yet, the IW module is in the very initial state. Neither the module itself, nor the message class cover the nl80211 functionality reasonably enough. So if you’re going to use it, brace yourself — debug is coming.

Messages

nl80211 messages are defined here:

pyroute2/netlink/nl80211/__init__.py

Pls notice NLAs of type hex. On the early development stage hex allows to inspect incoming data as a hex dump and, occasionally, even make requests with such NLAs. But it’s not a production way.

The type hex in the NLA definitions means that this particular NLA is not handled yet properly. If you want to use some NLA which is defined as hex yet, pls find out a specific type, patch the message class and submit your pull request on github.

If you’re not familiar with NLA types, take a look at RTNL definitions:

pyroute2/netlink/rtnl/ndmsg.py

and so on.

Communication with the kernel

There are several methods of the communication with the kernel.

  • sendto() — lowest possible, send a raw binary data
  • put() — send a netlink message
  • nlm_request() — send a message, return the response
  • get() — get a netlink message
  • recv() — get a raw binary data from the kernel

There are no errors on put() usually. Any permission denied, any invalid value errors are returned from the kernel with netlink also. So if you do put(), but don’t do get(), be prepared to miss errors.

The preferred method for the communication is nlm_request(). It tracks the message ID, returns the corresponding response. In the case of errors nlm_request() raises an exception. To get the response on any operation with nl80211, use flag NLM_F_ACK.

Reverse it

If you’re too lazy to read the kernel sources, but still need something not implemented here, you can use reverse engineering on a reference implementation. E.g.:

# strace -e trace=network -f -x -s 4096 \
        iw phy phy0 interface add test type monitor

Will dump all the netlink traffic between the program iw and the kernel. Three first packets are the generic netlink protocol discovery, you can ignore them. All that follows, is the nl80211 traffic:

sendmsg(3, {msg_name(12)={sa_family=AF_NETLINK, ... },
    msg_iov(1)=[{"\x30\x00\x00\x00\x1b\x00\x05 ...", 48}],
    msg_controllen=0, msg_flags=0}, 0) = 48
recvmsg(3, {msg_name(12)={sa_family=AF_NETLINK, ... },
    msg_iov(1)=[{"\x58\x00\x00\x00\x1b\x00\x00 ...", 16384}],
    msg_controllen=0, msg_flags=0}, 0) = 88
...

With -s 4096 you will get the full dump. Then copy the strings from msg_iov to a file, let’s say data, and run the decoder:

$ pwd
/home/user/Projects/pyroute2
$ export PYTHONPATH=`pwd`
$ python scripts/decoder.py pyroute2.netlink.nl80211.nl80211cmd data

You will get the session decoded:

{'attrs': [['NL80211_ATTR_WIPHY', 0],
           ['NL80211_ATTR_IFNAME', 'test'],
           ['NL80211_ATTR_IFTYPE', 6]],
 'cmd': 7,
 'header': {'flags': 5,
            'length': 48,
            'pid': 3292542647,
            'sequence_number': 1430426434,
            'type': 27},
 'reserved': 0,
 'version': 0}
{'attrs': [['NL80211_ATTR_IFINDEX', 23811],
           ['NL80211_ATTR_IFNAME', 'test'],
           ['NL80211_ATTR_WIPHY', 0],
           ['NL80211_ATTR_IFTYPE', 6],
           ['NL80211_ATTR_WDEV', 4],
           ['NL80211_ATTR_MAC', 'a4:4e:31:43:1c:7c'],
           ['NL80211_ATTR_GENERATION', '02:00:00:00']],
 'cmd': 7,
 'header': {'flags': 0,
            'length': 88,
            'pid': 3292542647,
            'sequence_number': 1430426434,
            'type': 27},
 'reserved': 0,
 'version': 1}

Now you know, how to do a request and what you will get as a response. Sample collected data is in the scripts directory.

Submit changes

Please do not hesitate to submit the changes on github. Without your patches this module will not evolve.

class pyroute2.iwutil.IW(*argv, **kwarg)
add_interface(ifname, iftype, dev=None, phy=0)

Create a virtual interface

  • ifname — name of the interface to create
  • iftype — interface type to create
  • dev — device index
  • phy — phy index

One should specify dev (device index) or phy (phy index). If no one specified, phy == 0.

iftype can be integer or string:

  1. adhoc
  2. station
  3. ap
  4. ap_vlan
  5. wds
  6. monitor
  7. mesh_point
  8. p2p_client
  9. p2p_go
  10. p2p_device
  11. ocb
associate(ifindex, bssid, ssid, freq, info_elements=None)

Send an Association request frame.

authenticate(ifindex, bssid, ssid, freq, auth_type=0)

Send an Authentication management frame.

connect(ifindex, ssid, bssid=None)

Connect to the ap with ssid and bssid

deauthenticate(ifindex, bssid, reason_code=1)

Send a Deauthentication management frame.

del_interface(dev)

Delete a virtual interface

  • dev — device index
disassociate(ifindex, bssid, reason_code=3)

Send a Disassociation management frame.

disconnect(ifindex)

Disconnect the device

get_associated_bss(ifindex)

Returns the same info like scan() does, but only about the currently associated BSS.

Unlike scan(), it returns immediately and doesn’t require root.

get_interface_by_ifindex(ifindex)

Get interface by ifindex ( use x.get_attr(‘NL80211_ATTR_IFINDEX’)

get_interface_by_phy(attr)

Get interface by phy ( use x.get_attr(‘NL80211_ATTR_WIPHY’) )

get_interfaces_dict()

Get interfaces dictionary

get_interfaces_dump()

Get interfaces dump

get_stations(ifindex)

Get stations by ifindex

join_ibss(ifindex, ssid, freq, bssid=None, channel_fixed=False, width=None, center=None, center2=None)
Connect to network by ssid
  • ifindex - IFINDEX of the interface to perform the connection
  • ssid - Service set identification
  • freq - Frequency in MHz
  • bssid - The MAC address of target interface
  • channel_fixed: Boolean flag
  • width - Channel width
  • center - Central frequency of the 40/80/160 MHz channel
  • center2 - Center frequency of second segment if 80P80

If the flag of channel_fixed is True, one should specify both the width and center of the channel

width can be integer of string:

  1. 20_noht
  2. 20
  3. 40
  4. 80
  5. 80p80
  6. 160
  7. 5
  8. 10
leave_ibss(ifindex)

Leave the IBSS – the IBSS is determined by the network interface

list_dev()

Get list of all wifi network interfaces

list_wiphy()

Get list of all phy devices

scan(ifindex, ssids=None, flush_cache=False)

Trigger scan and get results.

Triggering scan usually requires root, and can take a couple of seconds.

Network settings daemon – pyrouted

Pyrouted is a standalone project of a system service, that utilizes the pyroute2 library. It consists of a daemon controlled by systemd and a CLI utility that communicates with the daemon via UNIX socket.

It is an extremely simple and basic network interface setup tool.