How can I monitor the NIC status(up/down) in a C program without polling the kernel?
Solution 1
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.
Solution 2
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;
}
victor
Updated on July 05, 2022Comments
-
victor almost 2 years
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 over 12 yearsI have no idea whether the
select
orpoll
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. -
Kho Dam over 12 yearsyou'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)
-
Femi over 12 yearsIts a file handle: it should work just fine. 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. -
victor over 12 yearsI 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 over 12 yearsOK! I figure out! Thanks again with the 'V'!!
-
Andrew Barber almost 10 yearsYou may have missed the part in the question that asked for "without polling", which is what your solution appears to do.
-
suchitra nair over 9 yearsThis is the same solution accepted by @victor and this code gets a notification via socket when an interface is brought up.
-
RootPhoenix about 8 yearsCould you please share your solution..just the essential code to detect few events on the Interface.
-
hetman over 6 yearsNot all sysfs files implement
poll
support. Unfortunatelyoperstate
is one of the ones that doesn't.