#include <netlink/route/link.h>
int rtnl_link_alloc_cache(struct nl_sock *sk, int family, struct nl_cache **result)
The cache will contain link objects (struct rtnl_link
, see Link Object)
and can be accessed using the standard cache functions. By setting the
family
parameter to an address familly other than AF_UNSPEC
, the resulting
cache will only contain links supporting the specified address family.
The following direct search functions are provided to search by interface
index and by link name:
#include <netlink/route/link.h>
struct rtnl_link *rtnl_link_get(struct nl_cache *cache, int ifindex);
struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache, const char *name);
3.2.2. Lookup Single Link (Direct Lookup)
If only a single link is of interest, the link can be looked up directly
without the use of a link cache using the function rtnl_link_get_kernel()
.
#include <netlink/route/link.h>
int rtnl_link_get_kernel(struct nl_sock *sk, int ifindex, const char *name, struct rtnl_link **result);
It will construct and send a RTM_GETLINK
request using the parameters
provided and wait for a RTM_NEWLINK
or netlink error message sent in
return. If the link exists, the link is returned as link object
(see Link Object).
Example: Direct link lookup
struct rtnl_link *link;
if (rtnl_link_get_kernel(sock, 0, "eth1", &link) < 0)
/* error */
/* do something with link */
rtnl_link_put(link);
While using this function can save a substantial amount of bandwidth
on the netlink socket, the result will not be cached, subsequent calls
to rtnl_link_get_kernel() will always trigger sending a RTM_GETLINK
request.
3.2.3. Translating interface index to link name
Applications which require to translate interface index to a link name or
vice verase may use the following functions to do so. Both functions require
a filled link cache to work with.
char *rtnl_link_i2name (struct nl_cache *cache, int ifindex, char *dst, size_t len);
int rtnl_link_name2i (struct nl_cache *cache, const char *name);
3.3. Add / Modify
Several types of virtual link can be added on the fly using the function
rtnl_link_add()
.
#include <netlink/route/link.h>
int rtnl_link_add(struct nl_sock *sk, struct rtnl_link *link, int flags);
The deletion of virtual links such as VLAN devices or dummy devices is done
using the function rtnl_link_delete()
. The link passed on to the function
can be a link from a link cache or it can be construct with the minimal
attributes needed to identify the link.
#include <netlink/route/link.h>
int rtnl_link_delete(struct nl_sock *sk, const struct rtnl_link *link);
The function will construct and send a RTM_DELLINK
request message and
returns any errors returned by the kernel.
Example: Delete link by name
struct rtnl_link *link;
if (!(link = rtnl_link_alloc()))
/* error */
rtnl_link_set_name(link, "my_vlan");
if (rtnl_link_delete(sock, link) < 0)
/* error */
rtnl_link_put(link);
3.5. Link Object
#include <netlink/route/link.h>
struct rtnl_link *rtnl_link_alloc(void);
void rtnl_link_put(struct rtnl_link *link);
The name serves as unique, human readable description of the link. By
default, links are named based on their type and then enumerated, e.g.
eth0, eth1, ethn but they may be renamed at any time.
Kernels >= 2.6.11 support identification by link name.
#include <netlink/route/link.h>
void rtnl_link_set_name(struct rtnl_link *link, const char *name);
char *rtnl_link_get_name(struct rtnl_link *link);
Accepted link name format: [^ /]*
(maximum length: 15 characters)
3.5.2. Interface Index (Identifier)
The interface index is an integer uniquely identifying a link. If present
in any link message, it will be used to identify an existing link.
#include <netlink/route/link.h>
void rtnl_link_set_ifindex(struct rtnl_link *link, int ifindex);
int rtnl_link_get_ifindex(struct rtnl_link *link);
3.5.3. Group
Each link can be assigned a numeric group identifier to group a bunch of links
together and apply a set of changes to a group instead of just a single link.
#include <netlink/route/link.h>
void rtnl_link_set_group(struct rtnl_link *link, uint32_t group);
uint32_t rtnl_link_get_group(struct rtnl_link *link);
3.5.6. MTU (Maximum Transmission Unit)
The maximum transmission unit specifies the maximum packet size a network
device can transmit or receive. This value may be lower than the capability
of the physical network device.
#include <netlink/route/link.h>
void rtnl_link_set_mtu(struct rtnl_link *link, unsigned int mtu);
unsigned int rtnl_link_get_mtu(struct rtnl_link *link);
void rtnl_link_set_flags(struct rtnl_link *link, unsigned int flags);
void rtnl_link_unset_flags(struct rtnl_link *link, unsigned int flags);
unsigned int rtnl_link_get_flags(struct rtnl_link *link);
3.5.8. Transmission Queue Length
The transmission queue holds packets before packets are delivered to
the driver for transmission. It is usually specified in number of
packets but the unit may be specific to the link type.
#include <netlink/route/link.h>
void rtnl_link_set_txqlen(struct rtnl_link *link, unsigned int txqlen);
unsigned int rtnl_link_get_txqlen(struct rtnl_link *link);
3.5.9. Operational Status
The operational status has been introduced to provide extended information
on the link status. Traditionally the link state has been described using
the link flags IFF_UP, IFF_RUNNING, IFF_LOWER_UP
, and IFF_DORMANT
which
was no longer sufficient for some link types.
#include <netlink/route/link.h>
void rtnl_link_set_operstate(struct rtnl_link *link, uint8_t state);
uint8_t rtnl_link_get_operstate(struct rtnl_link *link);
The name of the queueing discipline used by the link is of informational
nature only. It is a read-only attribute provided by the kernel and cannot
be modified. The set function is provided solely for the purpose of creating
link objects to be used for comparison.
For more information on how to modify the qdisc of a link, see section
Traffic Control.
#include <netlink/route/link.h>
void rtnl_link_set_qdisc(struct rtnl_link *link, const char *name);
char *rtnl_link_get_qdisc(struct rtnl_link *link);
3.5.14. Promiscuity
The number of subsystem currently depending on the link being promiscuous mode.
A value of 0 indicates that the link is not in promiscuous mode. It is a
read-only attribute provided by the kernel and cannot be modified. The set
function is provided solely for the purpose of creating link objects to be
used for comparison.
#include <netlink/route/link.h>
void rtnl_link_set_promiscuity(struct rtnl_link *link, uint32_t count);
uint32_t rtnl_link_get_promiscuity(struct rtnl_link *link);
3.5.15. RX/TX Queues
The number of RX/TX queues the link provides. The attribute is writable but
will only be considered when creating a new network device via netlink.
#include <netlink/route/link.h>
void rtnl_link_set_num_tx_queues(struct rtnl_link *link, uint32_t nqueues);
uint32_t rtnl_link_get_num_tx_queues(struct rtnl_link *link);
void rtnl_link_set_num_rx_queues(struct rtnl_link *link, uint32_t nqueues);
uint32_t rtnl_link_get_num_rx_queues(struct rtnl_link *link);
uint32_t, int);
extern struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *,
int *);
/* lookup interface index of eth0 */
if (!(master_index = rtnl_link_name2i(link_cache, "eth0")))
/* error */
/* allocate new link object of type vlan */
link = rtnl_link_vlan_alloc();
/* set eth0 to be our master device */
rtnl_link_set_link(link, master_index);
rtnl_link_vlan_set_id(link, 10);
if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0)
/* error */
rtnl_link_put(link);
/* lookup interface index of eth0 */
if (!(master_index = rtnl_link_name2i(link_cache, "eth0")))
/* error */
/* allocate new link object of type macvlan */
link = rtnl_link_macvlan_alloc();
/* set eth0 to be our master device */
rtnl_link_set_link(link, master_index);
/* set address of virtual interface */
addr = nl_addr_build(AF_LLC, ether_aton("00:11:22:33:44:55"), ETH_ALEN);
rtnl_link_set_addr(link, addr);
nl_addr_put(addr);
/* set mode of virtual interface */
rtnl_link_macvlan_set_mode(link, rtnl_link_macvlan_str2mode("bridge"));
if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0)
/* error */
rtnl_link_put(link);
extern int rtnl_link_vxlan_set_learning(struct rtnl_link *, uint8_t);
extern int rtnl_link_vxlan_get_learning(struct rtnl_link *);
extern int rtnl_link_vxlan_enable_learning(struct rtnl_link *);
extern int rtnl_link_vxlan_disable_learning(struct rtnl_link *);
extern int rtnl_link_vxlan_set_ageing(struct rtnl_link *, uint32_t);
extern int rtnl_link_vxlan_get_ageing(struct rtnl_link *, uint32_t *);
extern int rtnl_link_vxlan_set_limit(struct rtnl_link *, uint32_t);
extern int rtnl_link_vxlan_get_limit(struct rtnl_link
*, uint32_t *);
extern int rtnl_link_vxlan_set_port_range(struct rtnl_link *,
struct ifla_vxlan_port_range *);
extern int rtnl_link_vxlan_get_port_range(struct rtnl_link *,
struct ifla_vxlan_port_range *);
extern int rtnl_link_vxlan_set_proxy(struct rtnl_link *, uint8_t);
extern int rtnl_link_vxlan_get_proxy(struct rtnl_link *);
extern int rtnl_link_vxlan_enable_proxy(struct rtnl_link *);
extern int rtnl_link_vxlan_disable_proxy(struct rtnl_link *);
extern int rtnl_link_vxlan_set_rsc(struct rtnl_link *, uint8_t);
extern int rtnl_link_vxlan_get_rsc(struct rtnl_link *);
extern int rtnl_link_vxlan_enable_rsc(struct rtnl_link *);
extern int rtnl_link_vxlan_disable_rsc(struct rtnl_link *);
extern int rtnl_link_vxlan_set_l2miss(struct rtnl_link *, uint8_t);
extern int rtnl_link_vxlan_get_l2miss(struct rtnl_link *);
extern int rtnl_link_vxlan_enable_l2miss(struct rtnl_link *);
extern int rtnl_link_vxlan_disable_l2miss(struct rtnl_link *);
extern int rtnl_link_vxlan_set_l3miss(struct rtnl_link *, uint8_t);
extern int rtnl_link_vxlan_get_l3miss(struct rtnl_link *);
extern int rtnl_link_vxlan_enable_l3miss(struct rtnl_link *);
extern int rtnl_link_vxlan_disable_l3miss(struct rtnl_link *);
/* set VXLAN network identifier */
if ((err = rtnl_link_vxlan_set_id(link, 128)) < 0)
/* error */
/* set multicast address to join */
if ((err = nl_addr_parse("239.0.0.1", AF_INET, &addr)) < 0)
/* error */
if ((err = rtnl_link_set_group(link, addr)) < 0)
/* error */
nl_addr_put(addr);
if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0)
/* error */
rtnl_link_put(link);
The traffic control architecture allows the queueing and
prioritization of packets before they are enqueued to the network
driver. To a limited degree it is also possible to take control of
network traffic as it enters the network stack.
The architecture consists of three different types of modules:
Queueing disciplines (qdisc) provide a mechanism to enqueue packets
in different forms. They may be used to implement fair queueing,
prioritization of differentiated services, enforce bandwidth
limitations, or even to simulate network behaviour such as packet
loss and packet delay. Qdiscs can be classful in which case they
allow traffic classes described in the next paragraph to be attached
to them.
Traffic classes (class) are supported by several qdiscs to build
a tree structure for different types of traffic. Each class may be
assigned its own set of attributes such as bandwidth limits or
queueing priorities. Some qdiscs even allow borrowing of bandwidth
between classes.
Classifiers (cls) are used to decide which qdisc/class the packet
should be enqueued to. Different types of classifiers exists,
ranging from classification based on protocol header values to
classification based on packet priority or firewall marks.
Additionally most classifiers support extended matches (ematch)
which allow extending classifiers by a set of matcher modules, and
actions which allow classifiers to take actions such as mangling,
mirroring, or even rerouting of packets.
Default Qdisc
The default qdisc used on all network devices is pfifo_fast
.
Network devices which do not require a transmit queue such as the
loopback device do not have a default qdisc attached. The pfifo_fast
qdisc provides three bands to prioritize interactive traffic over bulk
traffic. Classification is based on the packet priority (diffserv).
Multiqueue Default Qdisc
If the network device provides multiple transmit queues the mq
qdisc is used by default. It will automatically create a separate
class for each transmit queue available and will also replace
the single per device tx lock with a per queue lock.
Example of a customized classful qdisc setup
The following figure illustrates a possible combination of different
queueing and classification modules to implement quality of service
needs.
6.1. Traffic Control Object
Each type traffic control module (qdisc, class, classifier) is
represented by its own structure. All of them are based on the traffic
control object represented by struct rtnl_tc
which itself is based
on the generic object struct nl_object
to make it cacheable. The
traffic control object contains all attributes, implementation details
and statistics that are shared by all of the traffic control object
types.
It is not possible to allocate a struct rtnl_tc
object, instead the
actual tc object types must be allocated directly using
rtnl_qdisc_alloc()
, rtnl_class_alloc()
, rtnl_cls_alloc()
and
then casted to struct rtnl_tc
using the TC_CAST()
macro.
Usage Example: Allocation, Casting, Freeing
#include <netlink/route/tc.h>
#include <netlink/route/qdisc.h>
struct rtnl_qdisc *qdisc;
/* Allocation of a qdisc object */
qdisc = rtnl_qdisc_alloc();
/* Cast the qdisc to a tc object using TC_CAST() to use rtnl_tc_ functions. */
rtnl_tc_set_mpu(TC_CAST(qdisc), 64);
/* Free the qdisc object */
rtnl_qdisc_put(qdisc);
The interface index specifies the network device the traffic object
is attached to. The function rtnl_tc_set_link()
should be preferred
when setting the interface index. It stores the reference to the link
object in the tc object and allows retrieving the mtu
and linktype
automatically.
void rtnl_tc_set_ifindex(struct rtnl_tc *tc, int ifindex);
void rtnl_tc_set_link(struct rtnl_tc *tc, struct rtnl_link *link);
int rtnl_tc_get_ifindex(struct rtnl_tc *tc);
The link type specifies the kind of link that is used by the network
device (e.g. ethernet, ATM, …). It is derived automatically when
the network device is specified with rtnl_tc_set_link()
.
The default fallback is ARPHRD_ETHER
(ethernet).
The kind character string specifies the type of qdisc, class,
classifier. Setting the kind results in the module specific
structure being allocated. Therefore it is imperative to call
rtnl_tc_set_kind()
before using any type specific API functions
such as rtnl_htb_set_rate()
.
ever be seen by this traffic control object. This value is used for
rate calculations. Not all object implementations will make use of
this value. The default value is 0.
The Maximum Transmission Unit specifies the maximum packet size which
will be transmitted. The value is derived from the link specified
with rtnl_tc_set_link()
if not overwritten with rtnl_tc_set_mtu()
.
If no link and MTU is specified, the value defaults to 1500
(ethernet).
The overhead specifies the additional overhead per packet caused by
the network layer. This value can be used to correct packet size
calculations if the packet size on the wire does not match the packet
size seen by the kernel. The default value is 0.
6.1.2. Accessing Statistics
The traffic control object holds a set of generic statistics. Not all
traffic control modules will make use of all of these statistics. Some
modules may provide additional statistics via their own APIs.
6.2. Queueing Discipline (qdisc)
Classless Qdisc
The queueing discipline (qdisc) is used to implement fair queueing,
priorization or rate control. It provides a enqueue() and
dequeue() operation. Whenever a network packet leaves the networking
stack over a network device, be it a physical or virtual device, it
will be enqueued to a qdisc unless the device is queueless. The
enqueue() operation is followed by an immediate call to dequeue()
for the same qdisc to eventually retrieve a packet which can be
scheduled for transmission by the driver. Additionally, the networking
stack runs a watchdog which polls the qdisc regularly to dequeue and
send packets even if no new packets are being enqueued.
This additional watchdog is required due to the fact that qdiscs may
hold on to packets and not return any packets upon dequeue() in
order to enforce bandwidth restrictions.
The figure illustrates a trivial example of a classless qdisc
consisting of three bands (queues). Use of multiple bands is a common
technique in qdiscs to implement fair queueing between flows or
prioritize differentiated services.
Classless qdiscs can be regarded as a blackbox, their inner workings
can only be steered using the configuration parameters provided by the
qdisc. There is no way of taking influence on the structure of its
internal queues itself.
Classful Qdisc
Classful qdiscs allow for the queueing structure and classification
process to be created by the user.
The figure above shows a classful qdisc with a classifier attached to
it which will make the decision whether to enqueue a packet to traffic
class 1:1
or 1:2
. Unlike with classless qdiscs, classful qdiscs
allow the classification process and the structure of the queues to be
defined by the user. This allows for complex traffic class rules to
be applied.
The CBQ (Class Based Queueing) is a classful qdisc which allows
creating traffic classes and enforce bandwidth limitations for each
class.
The DRR (Deficit Round Robin) scheduler is a classful qdisc
impelemting fair queueing. Each class is assigned a quantum specyfing
the maximum number of bytes that can be served per round. Unused
quantum at the end of the round is carried over to the next round.
int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
struct nl_msg **result);
int rtnl_qdisc_add(struct nl_sock *sock, struct rtnl_qdisc *qdisc,
int flags);
int rtnl_qdisc_build_change_request(struct rtnl_qdisc *old,
struct rtnl_qdisc *new,
struct nl_msg **result);
int rtnl_qdisc_change(struct nl_sock *sock, struct rtnl_qdisc *old,
struct rtnl_qdisc *new);
int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
struct nl_msg **result);
int rtnl_qdisc_delete(struct nl_sock *sock, struct rtnl_qdisc *qdisc);
6.2.1. Retrieving Qdisc Configuration
The function rtnl_qdisc_alloc_cache() is used to retrieve the current
qdisc configuration in the kernel. It will construct a RTM_GETQDISC
netlink message, requesting the complete list of qdiscs configured in
the kernel.
#include <netlink/route/qdisc.h>
struct nl_cache *all_qdiscs;
if (rtnl_link_alloc_cache(sock, &all_qdiscs) < 0)
/* error while retrieving qdisc cfg */
The cache can be accessed using the following functions:
/* search for qdisc on eth0 with handle 1:0 */
if (!(qdisc = rtnl_qdisc_get(all_qdiscs, ifindex, TC_HANDLE(1, 0))))
/* no such qdisc found */
nl_object_dump(OBJ_CAST(qdisc), NULL);
rtnl_qdisc_put(qdisc);
6.2.2. Adding a Qdisc
In order to add a new qdisc to the kernel, a qdisc object needs to be
allocated. It will hold all attributes of the new qdisc.
#include <netlink/route/qdisc.h>
struct rtnl_qdisc *qdisc;
if (!(qdisc = rtnl_qdisc_alloc()))
/* OOM error */
The next step is to specify all generic qdisc attributes using the tc
object interface described in the section Attributes.
The following attributes must be specified:
- IfIndex
- Parent
- Kind
/* Attach qdisc to device eth0 */
rtnl_tc_set_link(TC_CAST(qdisc), eth0_obj);
/* Make this the root qdisc */
rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT);
/* Set qdisc identifier to 1:0, if left unspecified, a handle will be generated by the kernel. */
rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(1, 0));
/* Make this a HTB qdisc */
rtnl_tc_set_kind(TC_CAST(qdisc), "htb");
After specyfing the qdisc kind (rtnl_tc_set_kind()) the qdisc type
specific interface can be used to set attributes which are specific
to the respective qdisc implementations:
/* HTB feature: Make unclassified packets go to traffic class 1:5 */
rtnl_htb_set_defcls(qdisc, TC_HANDLE(1, 5));
Finally, the qdisc is ready to be added and can be passed on to the
function rntl_qdisc_add() which takes care of constructing a netlink
message requesting the addition of the new qdisc, sends the message to
the kernel and waits for the response by the kernel. The function
returns 0 if the qdisc has been added or updated successfully or a
negative error code if an error occured.
The kernel operation for updating and adding a qdisc is the
same. Therefore when calling rtnl_qdisc_add() any existing
qdisc with matching handle will be updated unless the flag
NLM_F_EXCL is specified.
If another qdisc is already attached to the same
parent and their handles mismatch, replace the qdisc
instead of returning -EEXIST.
if (err < 0) {
fprintf(stderr, "Unable to add qdisc: %s\n", nl_geterror(err));
return err;
The default class is the fallback class to which all traffic which
remained unclassified is directed to. If no default class or an
invalid default class is specified, packets are transmitted directly
to the next layer (direct transmissions).
The rate (bytes/s) specifies the maximum bandwidth an invidivual class
can use without borrowing. The rate of a class should always be greater
or erqual than the rate of its children.
The ceil rate specifies the maximum bandwidth an invidivual class
can use. This includes bandwidth that is being borrowed from other
classes. Ceil defaults to the class rate implying that by default
the class will not borrow. The ceil rate of a class should always
be greater or erqual than the ceil rate of its children.