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 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.
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")
-
class
pyroute2.ipset.
PortEntry
(port, protocol=None)¶ A simple container for port entry with optional protocol
-
class
pyroute2.ipset.
IPSet
(version=None, attr_revision=None, nfgen_family=2)¶ NFNetlink socket (family=NETLINK_NETFILTER).
Implements API to the ipset functionality.
-
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.
-
get_proto_version
(version=6)¶ Get supported protocol version by kernel.
version parameter allow to set mandatory (but unused?) IPSET_ATTR_PROTOCOL netlink attribute in the request.
-
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.
-
destroy
(name=None)¶ Destroy one (when name is set) or all ipset (when name is None)
-
create
(name, stype='hash:ip', family=<AddressFamily.AF_INET: 2>, exclusive=True, counters=False, comment=False, maxelem=None, 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
-
add
(name, entry, family=<AddressFamily.AF_INET: 2>, exclusive=True, comment=None, timeout=None, etype='ip', skbmark=None, skbprio=None, skbqueue=None, wildcard=False, **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
andPortRange
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")
wildcard option enable kernel wildcard matching on interface name for net,iface entries.
-
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.
-
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.
-
swap
(set_a, set_b)¶ Swap two ipsets. They must have compatible content type.
-
flush
(name=None)¶ Flush all ipsets. When name is set, flush only this ipset.
-
rename
(name_src, name_dst)¶ Rename the ipset.
-
get_set_byname
(name)¶ Get a set by its name
-
get_set_byindex
(index)¶ Get a set by its index
-
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")
-
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)¶ -
del_interface
(dev)¶ Delete a virtual interface
dev — device index
-
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:
adhoc
station
ap
ap_vlan
wds
monitor
mesh_point
p2p_client
p2p_go
p2p_device
ocb
-
list_dev
()¶ Get list of all wifi network interfaces
-
list_wiphy
()¶ Get list of all phy devices
-
get_interfaces_dict
()¶ Get interfaces dictionary
-
get_interfaces_dump
()¶ Get interfaces dump
-
get_interface_by_phy
(attr)¶ Get interface by phy ( use x.get_attr(‘NL80211_ATTR_WIPHY’) )
-
get_interface_by_ifindex
(ifindex)¶ Get interface by ifindex ( use x.get_attr(‘NL80211_ATTR_IFINDEX’)
-
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:
20_noht
20
40
80
80p80
160
5
10
-
leave_ibss
(ifindex)¶ Leave the IBSS – the IBSS is determined by the network interface
-
authenticate
(ifindex, bssid, ssid, freq, auth_type=0)¶ Send an Authentication management frame.
-
deauthenticate
(ifindex, bssid, reason_code=1)¶ Send a Deauthentication management frame.
-
associate
(ifindex, bssid, ssid, freq, info_elements=None)¶ Send an Association request frame.
-
disassociate
(ifindex, bssid, reason_code=3)¶ Send a Disassociation management frame.
-
connect
(ifindex, ssid, bssid=None)¶ Connect to the ap with ssid and bssid
-
disconnect
(ifindex)¶ Disconnect the device
-
scan
(ifindex, ssids=None, flush_cache=False)¶ Trigger scan and get results.
Triggering scan usually requires root, and can take a couple of seconds.
-
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.
-
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.