The NETLINK_SOCKET provided by the Linux kernel can be used to detect the link down and link up events of the network cable connection in a non-query mode.
NETLINK_SOCKET is a mechanism provided by the Linux kernel for inter-process communication, which allows two-way communication between user processes and kernel processes. You can subscribe and receive kernel events through NETLINK_SOCKET, such as changes in the status of network cable connections.
In a C program, you can use NETLINK_SOCKET to subscribe to kernel events and be notified when the event occurs. The specific implementation steps are as follows:
- Create a NETLINK_SOCKET socket;
- Bind to the NETLINK_KOBJECT_UEVENT message type;
- Subscribe to events "add" and "remove";
- Receive kernel events cyclically;
- In the event processing function, determine the event type and perform corresponding operations.
Here is a simple sample code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>
#define NETLINK_KOBJECT_UEVENT 15
#define UEVENT_BUFFER_SIZE 2048
void handle_uevent(int sock) {
char buffer[UEVENT_BUFFER_SIZE];
ssize_t len;
while ((len = recv(sock, buffer, sizeof(buffer), 0)) > 0) {
int i;
for (i = 0; i < len; i++) {
if (buffer[i] == '\0') {
printf("%s\n", &buffer[i] + 1); // 事件信息在'\0'后面
}
}
}
}
int main(int argc, char *argv[]) {
struct sockaddr_nl addr;
int sock;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
if (sock == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = 1 << (NETLINK_KOBJECT_UEVENT - 1);
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("bind");
close(sock);
exit(EXIT_FAILURE);
}
while (1) {
handle_uevent(sock);
}
close(sock);
return 0;
}
In the above code, first create a NETLINK_SOCKET socket and bind it to the NETLINK_KOBJECT_UEVENT message type, then subscribe to the events "add" and "remove", and finally receive kernel events in a loop and judge the event type in the event processing function and perform corresponding operations.
When the connection state of the network cable changes, the kernel will generate a UEVENT event and send the event information to the NETLINK_KOBJECT_UEVENT socket. The C program subscribes to and receives this event through NETLINK_SOCKET, and can obtain event information in the event processing function and determine the change type of the network cable connection status.
#include <sys/types.h>
#include <sys/socket.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <string.h>
#define BUFLEN 20480
int main(int argc, char *argv[])
{
int fd, retval;
char buf[BUFLEN] = { 0 };
int len = BUFLEN;
struct sockaddr_nl addr;
struct nlmsghdr *nh;
struct ifinfomsg *ifinfo;
struct rtattr *attr;
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len));
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTNLGRP_LINK;
bind(fd, (struct sockaddr *)&addr, sizeof(addr));
retval = read(fd, buf, BUFLEN);
printf("retval = %d \n", retval);
while ((retval = read(fd, buf, BUFLEN)) > 0) {
for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, retval); nh = NLMSG_NEXT(nh, retval)) {
if (nh->nlmsg_type == NLMSG_DONE)
break;
else if (nh->nlmsg_type == NLMSG_ERROR)
return;
else if (nh->nlmsg_type != RTM_NEWLINK)
continue;
ifinfo = NLMSG_DATA(nh);
printf("%u: %s", ifinfo->ifi_index, (ifinfo->ifi_flags & IFF_LOWER_UP) ? "up" : "down");
attr = (struct rtattr *)(((char *)nh) + NLMSG_SPACE(sizeof(*ifinfo)));
len = nh->nlmsg_len - NLMSG_SPACE(sizeof(*ifinfo));
for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
if (attr->rta_type == IFLA_IFNAME) {
printf(" %s", (char *)RTA_DATA(attr));
break;
}
}
printf("\n");
}
}
return 0;
}