7

I write a program which needs to get notified if the network interfaces have changed, in particular new one appeared or existed one gone. My research brought to the netlink and its RTMGRP_LINK signal. This manpage gives an example which is not clear for me yet.

It has this code:

memset(&sa, 0, sizeof(sa));
snl.nl_family = AF_NETLINK;
snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;

fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
bind(fd, (struct sockaddr*)&sa, sizeof(sa));

My understading is this is an initialization part to enable some signals. I guess there must be a handler subroutine which processes the event.

My questions are:

  • Is this a correct code snippet for enabling the event?

  • How to process the event: do I need to have some handler?

  • What are the data structures that contains a relevant information (about network interfaces and their changes)?

Thanks for any help.

1

2 Answers 2

9

The following code has been taken from here and I have added the logic to get the name of the concerned interface.

    #include <asm/types.h>
    #include <sys/socket.h>
    #include <unistd.h>
    #include <errno.h>
    #include <stdio.h>
    #include <string.h>
    #include <net/if.h>
    #include <netinet/in.h>

    #include <linux/netlink.h>
    #include <linux/rtnetlink.h>

    #define MYPROTO NETLINK_ROUTE
    #define MYMGRP RTMGRP_IPV4_ROUTE

    int open_netlink()
    {
        int sock = socket(AF_NETLINK,SOCK_RAW,MYPROTO);
        struct sockaddr_nl addr;

        memset((void *)&addr, 0, sizeof(addr));

        if (sock<0)
            return sock;
        addr.nl_family = AF_NETLINK;
        addr.nl_pid = getpid();
        addr.nl_groups = RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR;
        if (bind(sock,(struct sockaddr *)&addr,sizeof(addr))<0)
            return -1;
        return sock;
    }


    int read_event(int sockint, int (*msg_handler)(struct sockaddr_nl *,
                                                   struct nlmsghdr *))
    {
        int status;
        int ret = 0;
        char buf[4096];
        struct iovec iov = { buf, sizeof buf };
        struct sockaddr_nl snl;
        struct msghdr msg = { (void*)&snl, sizeof snl, &iov, 1, NULL, 0, 0};
        struct nlmsghdr *h;

        status = recvmsg(sockint, &msg, 0);

        if(status < 0)
        {
            /* Socket non-blocking so bail out once we have read everything */
            if (errno == EWOULDBLOCK || errno == EAGAIN)
                return ret;

            /* Anything else is an error */
            printf("read_netlink: Error recvmsg: %d\n", status);
            perror("read_netlink: Error: ");
            return status;
        }

        if(status == 0)
        {
            printf("read_netlink: EOF\n");
        }

        /* We need to handle more than one message per 'recvmsg' */
        for(h = (struct nlmsghdr *) buf; NLMSG_OK (h, (unsigned int)status); 
        h = NLMSG_NEXT (h, status))
        {
            /* Finish reading */
            if (h->nlmsg_type == NLMSG_DONE)
                return ret;

            /* Message is some kind of error */
            if (h->nlmsg_type == NLMSG_ERROR)
            {
                printf("read_netlink: Message is an error - decode TBD\n");
                return -1; // Error
            }

            /* Call message handler */
            if(msg_handler)
            {
                ret = (*msg_handler)(&snl, h);
                if(ret < 0)
                {
                    printf("read_netlink: Message hander error %d\n", ret);
                    return ret;
                }
            }
            else
            {
                printf("read_netlink: Error NULL message handler\n");
                return -1;
            }
        }



        return ret;
    }

    static int netlink_link_state(struct sockaddr_nl *nl, struct nlmsghdr *msg)
    {
        int len;
        struct ifinfomsg *ifi;

        nl = nl;

        ifi = NLMSG_DATA(msg);
        char ifname[1024];if_indextoname(ifi->ifi_index,ifname);

        printf("netlink_link_state: Link %s %s\n", 
                /*(ifi->ifi_flags & IFF_RUNNING)?"Up":"Down");*/
            ifname,(ifi->ifi_flags & IFF_UP)?"Up":"Down");
        return 0;
    }

    static int msg_handler(struct sockaddr_nl *nl, struct nlmsghdr *msg)
    {
        struct ifinfomsg *ifi=NLMSG_DATA(msg); /* for RTM_{NEW,DEL}ADDR */
        struct ifaddrmsg *ifa=NLMSG_DATA(msg); /* for RTM_{NEW,DEL}LINK */
        char ifname[1024];
        switch (msg->nlmsg_type)
        {
            case RTM_NEWADDR:
                if_indextoname(ifa->ifa_index,ifname);
                printf("msg_handler: RTM_NEWADDR : %s\n",ifname);
                break;
            case RTM_DELADDR:
                if_indextoname(ifa->ifa_index,ifname);
                printf("msg_handler: RTM_DELADDR : %s\n",ifname);
                break;
            case RTM_NEWLINK:
                if_indextoname(ifi->ifi_index,ifname);
                printf("msg_handler: RTM_NEWLINK\n");
                netlink_link_state(nl, msg);
                break;
            case RTM_DELLINK:
                if_indextoname(ifi->ifi_index,ifname);
                printf("msg_handler: RTM_DELLINK : %s\n",ifname);
                break;
            default:
                printf("msg_handler: Unknown netlink nlmsg_type %d\n",
                        msg->nlmsg_type);
                break;
        }
        return 0;
    }
    int main(int argc, char *argv[])
    {
        int nls = open_netlink();
        printf("Started watching:\n");
        if (nls<0) {
            printf("Open Error!");
        }
        while (1)
            read_event(nls, msg_handler);
        return 0;
    }
3
  • Thanks for the answer. It looks like the right thing but this code is not something ready to use or completed, it is even not compiled (something missed). Nov 28, 2014 at 8:35
  • 1
    I had extracted the relevant portion for your use. There were remnants of my code in it. They were just additions and could easily be removed. I have edited the code. You can use it now. Dec 1, 2014 at 6:36
  • it's nearly there, but when I plugged my usb modem the program said wwan0 Downwwan0 Down and when I disconnected the modem it said msg_handler: RTM_NEWLINK netlink_link_state: Link wlan0 Up. Besided I can see periodically printed msg_handler: RTM_NEWLINK netlink_link_state: Link wlan0 Up . I expected Up instead of Down at the first place, and then I expected wwan0 not wlan0 at RTM_DELLINK. But thanks again. You are very close. Dec 1, 2014 at 19:09
2
#include<stdio.h>
#include <asm/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <netinet/in.h>

#include <linux/netlink.h>
#include <linux/rtnetlink.h>
static void network_loop(void *data)
{
    struct sockaddr_nl addr;
    int nls, len, rtl;
    char buffer[4096];
    struct nlmsghdr *nlh;
    struct ifaddrmsg *ifa;
    struct rtattr *rth;

    if ((nls = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
        printf("MY_PROG: socket failure\n");

    memset (&addr,0,sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_groups = RTMGRP_IPV4_IFADDR;

    if (bind(nls, (struct sockaddr *)&addr, sizeof(addr)) == -1)
        printf("MY_PROG:network_loop bind failure\n");

    nlh = (struct nlmsghdr *)buffer;
    while ((len = recv(nls,nlh,4096,0)) > 0)
    {
        for (; (NLMSG_OK (nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE); nlh = NLMSG_NEXT(nlh, len))
        {
            if (nlh->nlmsg_type == RTM_NEWADDR)
                printf("MY_PROG:network_loop RTM_NEWADDR\n");
            else if (nlh->nlmsg_type == RTM_DELADDR)
                printf("MY_PROG:network_loop RTM_DELADDR\n");
            else
                printf("MY_PROG:network_loop nlmsg_type=%d\n", nlh->nlmsg_type);

            if ((nlh->nlmsg_type != RTM_NEWADDR) && (nlh->nlmsg_type != RTM_DELADDR)) continue; /* some other kind of announcement */

            ifa = (struct ifaddrmsg *) NLMSG_DATA (nlh);

            rth = IFA_RTA (ifa);
            rtl = IFA_PAYLOAD (nlh);
            for (; rtl && RTA_OK (rth, rtl); rth = RTA_NEXT (rth,rtl))
            {
                char name[IFNAMSIZ];
                uint32_t ipaddr;

                if (rth->rta_type != IFA_LOCAL) continue;

                ipaddr = * ((uint32_t *)RTA_DATA(rth));
                ipaddr = htonl(ipaddr);

                printf("MY_PROG:network_loop %s is now %X\n",if_indextoname(ifa->ifa_index,name),ipaddr);
            }
        }
    }

}

main()
{
    network_loop("MYPROG");
    printf("EXIT main()\n");
}

Simple program to know network change notification in Linux system and tested.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.