20

Now I need to get the status of the NIC(up or down) in the real time. That means I have to catch the kernel interrupt when the NIC up or down in a blocked loop.

The first stupid method from mine is that check on the /sys/class/net/eth0/operstate or use ioctl to get the ifflag every 100ms in a loop. But 100ms is too long for the app to reroute the traffic and also polling kernel every 100ms is not good idea.

Once I notice the inotify function that can monitor the files in a block mode. But unfortunately, it can't monitor the /sys/class/net/eth0/operstate file since /sys is located in the RAM not in the disk.

So, is there any methods except writing a kernel module to catch the NIC interrupt(up/down) in the C program with a block mode?

victor
  • 655
  • 2
  • 6
  • 7
  • possible duplicate of [Is there a notification mechanism for when getifaddrs() results change?](http://stackoverflow.com/questions/1270186/is-there-a-notification-mechanism-for-when-getifaddrs-results-change) – Steve-o Aug 29 '11 at 04:40
  • related: [get notified with netlist and RTMGRP_LINK signal](https://stackoverflow.com/questions/27008067/how-to-get-notified-about-network-interface-changes-with-netlist-and-rtmgrp-link) – minghua Aug 23 '18 at 23:09

3 Answers3

19

Yes, open a netlink socket and listen to the RTMGRP_LINK (network interface create/delete/up/down events) multicast groups.

The netlink man page here has a specific example to do this.

gby
  • 14,900
  • 40
  • 57
  • you're welcome! it's customary to mark my answer as the correct one if you think it solved your problem (click the V to the left of the question) – gby Aug 29 '11 at 06:08
  • I have tried the RTMGET_LINK as the message type and got the information from kernel, the device info is constructed in a **`ifinfomsg struct`**. The process is like user->kernel and kernel->user. But what I want is to running a loop in the user space that when the NIC status changed, the kernel will communicate to the user space automatically without the user space send request. Can u present a brief e.g using RTMGRP_LINK? – victor Aug 29 '11 at 07:04
  • OK! I figure out! Thanks again with the 'V'!! – victor Aug 29 '11 at 07:54
  • 1
    Could you please share your solution..just the essential code to detect few events on the Interface. – RootPhoenix Mar 30 '16 at 09:06
10

After doing a little research/reading on the web, I managed to cook up a working code to monitor NIC status.

#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>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>

int
read_event (int sockint)
{
  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;
  struct ifinfomsg *ifi;

  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
        }

      if (h->nlmsg_type == RTM_NEWLINK)
        {
        ifi = NLMSG_DATA (h);
            printf ("NETLINK::%s\n", (ifi->ifi_flags & IFF_RUNNING) ? "Up" : "Down");
    }
    }

  return ret;
}

int
main (int argc, char *argv[])
{
  fd_set rfds, wfds;
  struct timeval tv;
  int retval;
  struct sockaddr_nl addr;

  int nl_socket = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
  if (nl_socket < 0)
    {
      printf ("Socket Open Error!");
      exit (1);
    }

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

  addr.nl_family = AF_NETLINK;
  addr.nl_pid = getpid ();
  addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
//  addr.nl_groups = RTMGRP_LINK;

  if (bind (nl_socket, (struct sockaddr *) &addr, sizeof (addr)) < 0)
    {
      printf ("Socket bind failed!");
      exit (1);
    }

  while (1)
    {
      FD_ZERO (&rfds);
      FD_CLR (nl_socket, &rfds);
      FD_SET (nl_socket, &rfds);

      tv.tv_sec = 10;
      tv.tv_usec = 0;

      retval = select (FD_SETSIZE, &rfds, NULL, NULL, &tv);
      if (retval == -1)
        printf ("Error select() \n");
      else if (retval)
        {
          printf ("Event recieved >> ");
          read_event (nl_socket);
        }
      else
        printf ("## Select TimedOut ## \n");
    }
  return 0;
}
B Jacob
  • 389
  • 3
  • 9
  • 1
    You may have missed the part in the question that asked for "without polling", which is what your solution appears to do. – Andrew Barber Aug 25 '14 at 17:14
  • 1
    This is the same solution accepted by @victor and this code gets a notification via socket when an interface is brought up. – suchitra nair Nov 27 '14 at 11:14
-1

Have you tried monitoring the /sys/class/net/eth0/operstate file with select or poll function? As far as I can tell sysfs files should behave the same with respect to polling as regular files: whenever a change occurs you should get a notification on the file handle that something has changed and you should be able to respond accordingly.

Femi
  • 64,273
  • 8
  • 118
  • 148
  • 1
    I have no idea whether the `select` or `poll` will notify the user space when the file's content has been modified. **/sys/class/net/eth0/operstate** is the result from the kernel to indicate the NIC up or down. – victor Aug 29 '11 at 05:24
  • 2
    Its a file handle: it should work just fine. https://bugzilla.redhat.com/show_bug.cgi?id=604887 is an old RHEL bug that shows use of `select` on a sysfs file: seems reasonable to expect it to work. – Femi Aug 29 '11 at 06:55
  • 2
    Not all sysfs files implement `poll` support. Unfortunately `operstate` is one of the ones that doesn't. – hetman Dec 14 '17 at 07:24