How to implement a new network device type¶
This guide will explain how to extend the LNST with a new network device type.
Each device type supported by LNST is implemented as a separate class in
User can import such device using the following code:
from lnst.Devices import VlanDevice
To implement a class for new network device type several steps need to be done.
This guide will use gre tunnel device as an example.
All virtual devices should inherit from the
from lnst.Devices.SoftDevice import SoftDevice class GreDevice(SoftDevice): pass
Device name template¶
The new device class should define the name template for the created devices.
If the template is not defined a generic one defined by the
class will be used instead.
from lnst.Devices.SoftDevice import SoftDevice class GreDevice(SoftDevice): _name_template = "t_gre"
User must also define the device type through the
_link_type class attribute.
The value matches the link type that is used by iproute2’s
ip link utility.
For example, the gre device is created by following command:
ip link add new_gre_device type gre remote 192.168.200.2
The type gre part of the command above should be used in the
For other link types user can check the output of the following command:
ip link help ip -6 link help # for ipv6 specific devices
So, the device code will look like this:
from lnst.Devices.SoftDevice import SoftDevice class GreDevice(SoftDevice): _name_template = "t_gre" _link_type = "gre"
The device class may define any parameters available for the network device type.
For example, the gre device uses the local and remote parameters as in the following command:
ip link add new_gre_device type gre local 192.168.100.1 remote 192.168.200.2
These parameters have to be defined as class properties with their setters.
They have to use
SoftDevice methods to configure the device’s
parameters through kernel’s netlink API:
_set_linkinfo_data_attr()for the property setters
The code extended with the remote parameter would look like this:
@property def remote(self): try: return ipaddress(self._get_linkinfo_data_attr("IFLA_GRE_LOCAL")) except: return None @remote.setter def remote(self, val): self._set_linkinfo_data_attr("IFLA_GRE_LOCAL", str(ipaddress(val))) self._nl_link_sync("set")
In the code above the
remote property returns an IP address retrieved
through the netlink by calling the
_get_linkinfo_data_attr() with the
netlink’s representation of the remote parameter, that is IFLA_GRE_LOCAL.
remote.setter configures the remote device parameter by calling
_set_linkinfo_data_attr() with the netlink’s representation of the
parameter IFLA_GRE_LOCAL and the IP address as the value of the parameter.
The setters must always include call of the
_nl_link_sync() to commit
the changes through netlink.
For the device specific
IFLA_* strings refer to Finding out the IFLA_* strings
With the code above the user can now use the device class in a recipe, for example:
from lnst.Controller import Controller, HostReq, DeviceReq, BaseRecipe from lnst.Devices.GreDevice import GreDevice class GreRecipe(BaseRecipe): machine1 = HostReq() machine1.nic1 = DeviceReq(label="net1") def test(self): machine1.gre = GreDevice(remote="192.168.200.2") ctl = Controller() recipe_instance = GreRecipe() ctl.run(recipe_instance)
Explanation of the netlink update bulking in LNST¶
Feel free to skip this section if you’re not interested in the deeper understanding of the device configuration in LNST.
In the previous section I stated that the device parameter’s setters must
_nl_link_sync() method call to propagate the changes to
the kernel through netlink.
We might assume that configuration of a device parameter is done instantly, by immediately sending the update to the netlink. This however does not work when multiple parameters are required while the device is created. Additionally we want to avoid unnecessary multiple calls to the netlink.
LNST solves this problem by using a bulk mode for the netlink updates.
In short, the bulk mode is postponing of sending the netlink updates for a
device until a bulk transfer is explicitly requested with
SoftDevice has bulk mode automatically enabled in the
phase, so that specifying of multiple device parameters during instantiation
would generate just one netlink message to create the device.
The bulk mode is disabled once the device is created. After that changing any of the properties would immediately result in propagation through the netlink.
More information about the bulking concept is described in this commit
Mandatory device parameters¶
A device may require some parameters to be specified, for example a gre device requires remote parameter.
You can specify such parameters in the class attribute
class GreDevice(SoftDevice): _name_template = "t_gre" _link_type = "gre" _mandatory_opts = ["remote"]
LNST will automatically check if the mandatory parameters where specified in the device instance and report back a failure.
Finding out the IFLA_* strings¶
The device parameters are configured through the kernel’s netlink API.
In the code above we have mentioned two
SoftDevice methods used for
setting or retrieving the device parameters, the
_set_linkinfo_data_attr(). Both methods takes a name of the device
parameter as an argument, these are prefixed with IFLA_ string.
What becomes challenging is to find out the corresponding
for a specific network device. These are unique for each device.
Here the pyroute2 comes handy. The pyroute2 is a Python module that provides also an API for interacting with the kernel’s netlink. LNST uses this module for the device management.
To find out what parameters are needed to configure the test device simply follow the procedure below.
Save the following code to file named watch_netlink.py
from pyroute2 import IPRoute from pprint import pprint with IPRoute() as ipr: while True: ipr.bind() for message in ipr.get(): pprint(message)
Run the script in background and run an iproute command to create a gre tunnel device (or any other device of your interest).
./watch_netlink.py & ip link add mygre type gre local 192.168.200.1 remote 192.168.200.2
watch_netlink.py script should print the complete netlink message
including the details of
IFLA_LINKINFO. So simply find the message that
('IFLA_IFNAME', 'mygre') (mygre matches the device name used
ip link command above).
So, inspecting the output above, the relevant
IFLA_* strings are found
under IFLA_LINKINFO / IFLA_INFO_DATA (in highlighted lines):