Using rtnetlink for detection of NICs status changes in C

I had a task once, where I had to update a configuration when a change was detected in the network interfaces. While the major part of the task was the configuration part, it’s interesting to see how we can detect networking changes in C, and it’s also a nice Netlink exercise.

Netlink is a socket-based IPC between the kernel and user-space, using packets communication (a datagram-oriented service). Netlink communication in user-space is done with the standard and familiar sockets API, similar to unix-domain sockets and INET sockets (See Netlink’s man page).

Netlink has a lot of families, which are sort of sub-protocols. Each family is designed for communication with a different kernel service. It also has a generic family, for user-customized communication.

What I’m going to show here is the usage of “RTNL” – Routing Netlink. This Netlink family is used for managing the routing services of the kernel. We can send commands to change routing, get routing information or “subscribe” for routing notification as (RTNL man page).

The complete code for this post can be downloaded from github.

Netlink messages

An incoming buffer from a netlink socket can contain one or more netlink messages. Each message consists of a header and payload. It looks something like this:

This is the header structure, I think it is pretty self-documenting:

struct nlmsghdr {
    __u32 nlmsg_len;    /* Length of message including header. */
    __u16 nlmsg_type;   /* Type of message content. */
    __u16 nlmsg_flags;  /* Additional flags. */
    __u32 nlmsg_seq;    /* Sequence number. */
    __u32 nlmsg_pid;    /* Sender port ID. */
};

The message payload itself depends on the type, as we’ll soon see with RT messages.

Netlink API offers convenient macros to iterate the messages, extract the payload, and such. You should use them instead of doing the casting yourself, since netlink uses strict alignments, which the macros already take into account.

More on netlink can be found in it’s man page. Or here, if you want to go deeper into Netlink.

RT Netlink

If we are receiving RT Netlink messages, nlmsg_type will indicate which routing message it is, and the payload will match the type. RT Netlink payload look something like this:

The “Message data” is a struct (as I said, it depends on the netlink message type), and the attributes struct looks like this:

struct rtattr {
    unsigned short rta_len;    /* Length of option */
    unsigned short rta_type;   /* Type of option */
    /* Data follows */
};

And here, again, the attribute’s data depends on the attribute’s type.

In our example, we wait for “RTM_NEWLINK” netlink message type, which payload is this:

struct ifinfomsg {
    unsigned char  ifi_family; /* AF_UNSPEC */
    unsigned short ifi_type;   /* Device type */
    int            ifi_index;  /* Interface index */
    unsigned int   ifi_flags;  /* Device flags  */
    unsigned int   ifi_change; /* change mask */
};
  • We will be interested only in messages that inform a change in network interfaces, that is, only messages with non-zero ifi_change.
  • ifi_flags will indicate if the device was taken up, or down.
  • In order to know which interface has changed its state, we’ll look for an attribute with rta_type “IFLA_IFNAME”, and read its payload, which type is char*.

Taking it into practice

Opening a netlink socket with RT netlink family is shown in the man page, and looks like this:

fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

Binding the socket to a specific “Netlink address” – Here we specify which RT groups we want to “subscribe” to. We can subscribe to more than one by OR’ing them together. Here we subscribe only to “RTMGRP_LINK”:

sa.nl_family = AF_NETLINK;
sa.nl_groups = RTMGRP_LINK;
err = bind(fd, (struct sockaddr *) &sa, sizeof(sa));

Waiting for messages and receiving data is done with the regular sockets API. In my code I used “poll” and “recv”, but any other API will do. You can see this part in the code itself.

When we get the message buffer, we need to iterate the netlink messages it contains by iterating the header:

for (nh = (struct nlmsghdr *)buff; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
    .
    .
    .

And for each message we look for nlmsg_type “RTM_NEWLINK” with change indication:

    ifi = NLMSG_DATA(nh);
    if (nh->nlmsg_type != RTM_NEWLINK || !ifi->ifi_change)
        continue;

If we do find such a message, we iterate its attributes in order to find the interface’s name:

char *if_name = NULL;

for ( ; RTA_OK(rta, rta_len) ; rta = RTA_NEXT(rta, rta_len)) {
    if (rta->rta_type == IFLA_IFNAME) {
        if_name = (char*)RTA_DATA(rta);
        break;
    }
}

Where rta and rta_len is taken from the netlink message:

struct rtattr *rta = IFLA_RTA(ifi);
int rta_len = IFLA_PAYLOAD(nh);

Again, you can check out the code itself for the complete picture, I believe it’s clear enough.

Amnon.

Amnon

Advantures in Embeddedland · Post

Update Revert to draft Preview Close

ComposeHTML

Normal
Labels
C, C++, devices, linux, netlink, network interfaces, networking, nic, programming, rtnetlink, sockets, unix
Published on

5/20/16, 10:36 PM

Israel Daylight Time

Permalink
Location
Search Description
Options

Amnon

Advantures in Embeddedland · Post

Update Revert to draft Preview Close

ComposeHTML

Normal
Labels
C, C++, devices, linux, netlink, network interfaces, networking, nic, programming, rtnetlink, sockets, unix
Published on

5/20/16, 10:36 PM

Israel Daylight Time

Permalink
Location
Search Description
Options

Amnon

Advantures in Embeddedland · Post

Update Revert to draft Preview Close

ComposeHTML

Normal
Labels
C, C++, devices, linux, netlink, network interfaces, networking, nic, programming, rtnetlink, sockets, unix
Published on

5/20/16, 10:36 PM

Israel Daylight Time

Permalink
Location
Search Description
Options

Amnon

Advantures in Embeddedland · Post

Update Revert to draft Preview Close

ComposeHTML

Normal
Labels
C, C++, devices, linux, netlink, network interfaces, networking, nic, programming, rtnetlink, sockets, unix
Published on

5/20/16, 10:36 PM

Israel Daylight Time

Permalink
Location
Search Description
Options

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s