Linux How can I monitor the NIC status(up/down) in a C program without polling the kernel?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7225888/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 05:55:27  来源:igfitidea点击:

How can I monitor the NIC status(up/down) in a C program without polling the kernel?

linuxnetworkingnetwork-programminglinux-kernellinux-device-driver

提问by victor

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.

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/operstateor use ioctlto 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.

The first stupid method from mine is that check on the /sys/class/net/eth0/operstateor use ioctlto 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 inotifyfunction 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.

Once I notice the inotifyfunction 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?

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?

采纳答案by gby

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

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

The netlink man page herehas a specific example to do this.

The netlink man page herehas a specific example to do this.

回答by Femi

Have you tried monitoring the /sys/class/net/eth0/operstatefile with selector pollfunction? As far as I can tell sysfsfiles 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.

Have you tried monitoring the /sys/class/net/eth0/operstatefile with selector pollfunction? As far as I can tell sysfsfiles 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.

回答by Betran Jacob

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

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;
}