Last active
January 8, 2025 00:32
-
-
Save userid/9edfae91647479de3e5790fbbdba36c2 to your computer and use it in GitHub Desktop.
使用rtnetlink监听和判断网卡的状态变化
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
http://guochongxin.github.io/c/c/c++/linux/netlink/rj45/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E7%BD%91%E5%8D%A1/2014/12/05/tong_guo_netlink_jian_ce_wang_xian_cha_ba | |
最近有个需求需要检测RJ45网卡的网线有没有接上,而最近正在了解Netlink相关资料,刚好也看下通过Netlink可以进行检测,故在此做下粗略笔记: | |
1.首先要创建一个Netlink Socket,在用户层使用如下参数来调用socket()函数: | |
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
上面这个函数第一个参数必须是AF_NETLINK或PF_NETLINK,这两个标志在Linux下是一样的,第二个参数可以是SOCK_RAW或SOCK_DGRAM(对应用到TCP或UDP协议),而最后一个参数NETLINK_ROUTE为“路由守护进程”,用于接收来自内核的路由通知事件。 | |
2.将上面创建的Socket绑定 | |
addr.nl_family = AF_NETLINK; | |
addr.nl_groups = RTNLGRP_LINK; //指定接收路由多播组消息 | |
bind(fd, (struct sockaddr*)&addr, sizeof(addr)); | |
上面将创建的socket与相应的协议族和组进行绑定,接下来通过读fd这个socket来获得相应的消息数据struct nlmsghdr,再对该结构体数据进行判断来获得网线是接上或是拔掉,相应的源码如下: | |
*/ | |
#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)); | |
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; | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
https://stackoverflow.com/questions/7225888/how-can-i-monitor-the-nic-statusup-down-in-a-c-program-without-polling-the-ker | |
https://stackoverflow.com/questions/10340145/get-event-for-nic-ethernet-card-link-status-on-linux | |
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; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
https://blog.csdn.net/sourthstar/article/details/7975999 | |
http://www.cpplive.com/html/1542.html | |
之前有一篇文章《Netlink实现Linux内核与用户空间通信》专门介绍了Netlink相比其他内核交互方式的优点以及Netlink的调用方法,并以NETLINK_KOBJECT_UEVENT(内核事件向用户态通知)为例演示了U盘热插拔信息的捕捉,衍生出另一篇文章《Linux下自动检测USB热插拔》,今天尝试用Netlink来捕捉一下网络接口信息,实现的主要功能是实时打印发生变化的网络接口的序列号、上下线状态和接口名称。 | |
为了创建一个 netlink socket,用户需要使用如下参数调用 socket(): | |
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
第一个参数必须是 AF_NETLINK 或 PF_NETLINK,在 Linux 中,它们俩实际为一个东西,它表示要使用netlink,第二个参数必须是SOCK_RAW或SOCK_DGRAM,第三个参数指定netlink协议类型,NETLINK_ROUTE意为“路由守护进程”,绑定该协议创建的fd可以接收到来自内核的路由通知事件(如网路接口eth0上线)。 | |
函数 bind() 用于把一个打开的 netlink socket 与 netlink 源 socket 地址绑定在一起。netlink socket 的地址初始化及绑定如下: | |
addr.nl_family = AF_NETLINK; | |
addr.nl_groups = RTNLGRP_LINK; //指定接收路由多播组消息 | |
bind(fd, (struct sockaddr*)&addr, sizeof(addr)); | |
*/ | |
#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)); | |
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; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
https://stackoverflow.com/questions/40788161/how-to-receive-kernel-uevents-with-netlink-socket | |
*/ | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <linux/netlink.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <string.h> | |
#define NL_MAX_PAYLOAD 8192 | |
int main () { | |
int nl_socket; | |
struct sockaddr_nl src_addr; | |
char msg[NL_MAX_PAYLOAD]; | |
int ret; | |
// Prepare source address | |
memset(&src_addr, 0, sizeof(src_addr)); | |
src_addr.nl_family = AF_NETLINK; | |
src_addr.nl_pid = getpid(); | |
src_addr.nl_groups = -1; // ==> add this so can receive from kernel broadcast | |
nl_socket = socket(AF_NETLINK, (SOCK_DGRAM | SOCK_CLOEXEC), NETLINK_KOBJECT_UEVENT); | |
if (nl_socket < 0) { | |
printf("Failed to create socket for DeviceFinder"); | |
exit(1); | |
} | |
ret = bind(nl_socket, (struct sockaddr*) &src_addr, sizeof(src_addr)); | |
if (ret) { | |
printf("Failed to bind netlink socket.."); | |
close(nl_socket); | |
return 1; | |
} | |
printf("Waiting for events now...\n"); | |
while (1) { | |
int r = recv(nl_socket, msg, sizeof(msg), MSG_DONTWAIT); | |
if (r == -1) | |
continue; | |
if (r < 0) { | |
perror(""); | |
continue; | |
} | |
printf("length:%i\n msg:%s", r, msg); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
https://stackoverflow.com/questions/42264101/does-libevent-support-netlink-socket | |
使用libevent | |
Yes, libevent supports netlink socket. | |
There is https://github.com/libevent/libevent/blob/master/sample/hello-world.c, it is modified below to listen to netlink socket. | |
The basic example listens to Linux network interface creation/deletion and can be executed with sudo to gain privilege needed. | |
It listens to same events as ip monitor link. | |
Another example of listening to RAW sockets with libevent is here | |
https://github.com/bodgit/libevent-natpmp/blob/master/natpmp.c. | |
*/ | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <arpa/inet.h> | |
#include <net/if.h> | |
#include <net/route.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <event.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <linux/netlink.h> | |
#include <linux/rtnetlink.h> | |
#define BUF_SIZE 10240 | |
static void link_recvmsg(int fd, short event, void *arg) | |
{ | |
char buf[NLMSG_SPACE(BUF_SIZE)] = {0}; | |
socklen_t socklen; | |
struct iovec iov = {.iov_base = buf, .iov_len = sizeof(buf)}; | |
struct sockaddr addr; | |
memset(&addr, 0, sizeof(struct sockaddr)); | |
if (!fd || -1 == fd) | |
return; | |
int status = getsockname(fd, &addr, &socklen); | |
if(-1 == status) | |
return; | |
struct msghdr mh = {.msg_name = NULL, .msg_namelen = 0, .msg_iov = &iov, .msg_iovlen = 1, | |
.msg_flags = 0, .msg_name = &addr, .msg_namelen = sizeof(struct sockaddr)}; | |
status = recvmsg(fd, &mh, 0); | |
if ((-1 == status) && ((EINTR == errno) || (EAGAIN == errno))) | |
return; | |
if(-1 == status) | |
return; | |
if ((mh.msg_flags & MSG_TRUNC) == MSG_TRUNC) | |
return; | |
if ((mh.msg_flags & MSG_CTRUNC) == MSG_CTRUNC) | |
return; | |
for (const struct nlmsghdr *h = (struct nlmsghdr *)buf; NLMSG_OK(h, status); h = NLMSG_NEXT(h, status)) { | |
switch (h->nlmsg_type) { | |
case RTM_NEWLINK: | |
fprintf(stderr, "got RTM_NEWLINK\n"); | |
break; | |
case RTM_DELLINK: | |
fprintf(stderr, "got RTM_DELLINK\n"); | |
break; | |
default: | |
fprintf(stderr, "unexpected case in swtch statement\n"); | |
break; | |
} | |
} | |
} | |
int main(int argc, char **argv) | |
{ | |
/* some init code here */ | |
/* NETLINK socket */ | |
int status; | |
int buf_size = BUF_SIZE; | |
struct sockaddr_nl src_addr; | |
struct event_base *base; | |
base = event_base_new(); | |
int socket_nl = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK, NETLINK_ROUTE); | |
if(-1 == socket_nl) return -1; | |
memset(&src_addr, 0, sizeof(struct sockaddr_nl)); | |
src_addr.nl_family = AF_NETLINK; | |
src_addr.nl_pid = getpid(); | |
src_addr.nl_groups |= RTNLGRP_LINK; | |
status = setsockopt(socket_nl, SOL_SOCKET, SO_RCVBUF, | |
&buf_size, sizeof(buf_size)); | |
if(-1 == status) return -1; | |
status = bind(socket_nl, (struct sockaddr *)&src_addr, sizeof(struct sockaddr_nl)); | |
if(status < 0) return -1; | |
static struct event nl_ev; | |
event_set(&nl_ev, socket_nl, EV_READ|EV_PERSIST, link_recvmsg, | |
NULL); | |
if (base) { | |
event_base_set(base, &nl_ev); | |
} | |
event_add(&nl_ev, NULL); | |
/* some other code, dispatch event and deinit */ | |
event_base_dispatch(base); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment