Netlink实现网卡上下线监控

本文介绍如何利用Netlink技术实时捕获并打印网络接口的状态信息,包括序列号、上下线状态和接口名称,通过C语言实现了一个完整的监控程序。

http://www.cpplive.com/html/1542.html



之前有一篇文章《Netlink实现Linux内核与用户空间通信》专门介绍了Netlink相比其他内核交互方式的优点以及Netlink的调用方法,并以NETLINK_KOBJECT_UEVENT(内核事件向用户态通知)为例演示了U盘热插拔信息的捕捉,衍生出另一篇文章《Linux下自动检测USB热插拔》,今天尝试用Netlink来捕捉一下网络接口信息,实现的主要功能是实时打印发生变化的网络接口的序列号、上下线状态和接口名称。    

为了创建一个 netlink socket,用户需要使用如下参数调用 socket():

  1. 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 的地址初始化及绑定如下:

  1. addr.nl_family = AF_NETLINK;  
  2. addr.nl_groups = RTNLGRP_LINK; //指定接收路由多播组消息  
  3. bind(fd, (struct sockaddr*)&addr, sizeof(addr));  

 
Netlink实现网卡上下线监控的整个过程,完整的C语言实现如下:

  1. #include <sys/types.h>  
  2. #include <sys/socket.h>  
  3. #include <asm/types.h>  
  4. #include <linux/netlink.h>  
  5. #include <linux/rtnetlink.h>  
  6. #include <stdlib.h>  
  7. #include <stdio.h>  
  8. #include <sys/ioctl.h>  
  9. #include <linux/if.h>  
  10. #include <string.h>  
  11.   
  12. #define BUFLEN 20480  
  13.   
  14. int main(int argc, char *argv[])  
  15. {  
  16.     int fd, retval;  
  17.     char buf[BUFLEN] = {0};  
  18.     int len = BUFLEN;  
  19.     struct sockaddr_nl addr;  
  20.     struct nlmsghdr *nh;  
  21.     struct ifinfomsg *ifinfo;  
  22.     struct rtattr *attr;  
  23.   
  24.     fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);  
  25.     setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len));  
  26.     memset(&addr, 0, sizeof(addr));  
  27.     addr.nl_family = AF_NETLINK;  
  28.     addr.nl_groups = RTNLGRP_LINK;  
  29.     bind(fd, (struct sockaddr*)&addr, sizeof(addr));  
  30.     while ((retval = read(fd, buf, BUFLEN)) > 0)  
  31.     {  
  32.         for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, retval); nh = NLMSG_NEXT(nh, retval))  
  33.         {  
  34.             if (nh->nlmsg_type == NLMSG_DONE)  
  35.                 break;  
  36.             else if (nh->nlmsg_type == NLMSG_ERROR)  
  37.                 return;  
  38.             else if (nh->nlmsg_type != RTM_NEWLINK)  
  39.                 continue;  
  40.             ifinfo = NLMSG_DATA(nh);  
  41.             printf("%u: %s", ifinfo->ifi_index,  
  42.                     (ifinfo->ifi_flags & IFF_LOWER_UP) ? "up" : "down" );  
  43.             attr = (struct rtattr*)(((char*)nh) + NLMSG_SPACE(sizeof(*ifinfo)));  
  44.             len = nh->nlmsg_len - NLMSG_SPACE(sizeof(*ifinfo));  
  45.             for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len))  
  46.             {  
  47.                 if (attr->rta_type == IFLA_IFNAME)  
  48.                 {  
  49.                     printf(" %s", (char*)RTA_DATA(attr));  
  50.                     break;  
  51.                 }  
  52.             }  
  53.             printf("\n");  
  54.         }  
  55.     }  
  56.   
  57.     return 0;  
  58. }  

 
运行程序,测试网线拔除/插入,输出如下:

  1. 2: down eth0  
  2. 2: up eth0 


<think>我们使用C语言通过Netlink套接字来监控网卡状态。NetlinkLinux内核与用户空间进程通信的一种机制。我们可以通过Netlink来获网络接口(网卡)的状态变化。 具体步骤: 1. 创建Netlink套接字。 2. 绑定套接字到Netlink协议族(这里使用RTMGRP_LINK来监听链接状态变化)。 3. 接收并解析来自内核的消息,特别是链接状态变化的消息(RTM_NEWLINK, RTM_DELLINK等)。 注意:需要包含必要的头文件,并且需要root权限运行。 代码实现: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> #define NETLINK_GROUP RTMGRP_LINK // 监听链接事件 int main() { struct sockaddr_nl addr; int sock_fd, ret; char buf[4096]; struct nlmsghdr *nlh; // 创建Netlink套接字 sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock_fd < 0) { perror("socket"); return -1; } // 设置地址结构 memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_pid = getpid(); // 当前进程的pid addr.nl_groups = NETLINK_GROUP; // 加入多播组 // 绑定套接字 ret = bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)); if (ret < 0) { perror("bind"); close(sock_fd); return -1; } printf("Listening for netlink events...\n"); // 循环接收并处理消息 while (1) { int len = recv(sock_fd, buf, sizeof(buf), 0); if (len < 0) { perror("recv"); continue; } // 处理每个接收到的消息 for (nlh = (struct nlmsghdr *)buf; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) { // 如果消息是结束消息,则退出循环 if (nlh->nlmsg_type == NLMSG_DONE) { break; } // 处理链接事件:新链接、删除链接、链接属性改变 if (nlh->nlmsg_type == RTM_NEWLINK || nlh->nlmsg_type == RTM_DELLINK || nlh->nlmsg_type == RTM_SETLINK) { struct ifinfomsg *ifinfo = (struct ifinfomsg *) NLMSG_DATA(nlh); char *status = (ifinfo->ifi_flags & IFF_UP) ? "UP" : "DOWN"; char *action; if (nlh->nlmsg_type == RTM_NEWLINK) { action = "NEWLINK"; } else if (nlh->nlmsg_type == RTM_DELLINK) { action = "DELLINK"; } else { action = "SETLINK"; } printf("Interface %d: %s, state: %s\n", ifinfo->ifi_index, action, status); } } } close(sock_fd); return 0; } ``` 代码解释: 1. 创建了一个Netlink套接字,协议为`NETLINK_ROUTE`,用于路由相关的消息(包括网络接口事件)。 2. 绑定到指定的多播组`RTMGRP_LINK`,这样内核会发送网络接口状态变化的消息。 3. 在一个无限循环中接收消息,每次接收后遍历消息(因为可能一次收到多个消息)。 4. 对于每个消息,检查其类型:`RTM_NEWLINK`(新接口或接口属性变化)、`RTM_DELLINK`(接口删除)、`RTM_SETLINK`(设置接口属性)。然后从消息体中提`ifinfomsg`结构,其中包含接口索引和标志(如`IFF_UP`表示接口是否启动)。 5. 打印出接口的索引、事件类型和状态(UP或DOWN)。 注意:运行此程序需要root权限,因为普通用户无法访问Netlink路由套接字。 相关问题: §§相关问题§§: 1. 如何通过Netlink路由表信息? 2. 如何使用Netlink监控网络地址(IP地址)的变化? 3. 除了Netlink,还有哪些方法可以监控网络接口状态? 4. 如何通过Netlink修改网络接口的状态(例如启用或禁用一个网卡)?</think>### 使用C语言实现通过Netlink监控网卡状态 NetlinkLinux内核与用户空间进程通信的机制,可以高效获网络接口状态变化。以下是完整的实现代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> #include <net/if.h> // 创建Netlink套接字并加入多播组 int create_netlink_socket() { struct sockaddr_nl sa; int sock; int group = RTMGRP_LINK; // 监听网络接口变化 // 创建原始Netlink套接字 sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { perror("socket"); exit(EXIT_FAILURE); } // 配置地址结构 memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; sa.nl_pid = getpid(); // 当前进程ID sa.nl_groups = group; // 加入多播组 // 绑定套接字 if (bind(sock, (struct sockaddr*)&sa, sizeof(sa)) < 0) { perror("bind"); close(sock); exit(EXIT_FAILURE); } return sock; } // 解析并打印接口信息 void parse_interface_info(struct nlmsghdr *nlh) { struct ifinfomsg *ifinfo = NLMSG_DATA(nlh); char *action = "UNKNOWN"; char *status = (ifinfo->ifi_flags & IFF_UP) ? "UP" : "DOWN"; // 确定事件类型 switch (nlh->nlmsg_type) { case RTM_NEWLINK: action = "ADDED/UPDATED"; break; case RTM_DELLINK: action = "REMOVED"; break; } // 获接口名称 char ifname[IF_NAMESIZE]; if_indextoname(ifinfo->ifi_index, ifname); printf("[Event] Interface: %-6s Action: %-12s Status: %-4s Index: %d\n", ifname, action, status, ifinfo->ifi_index); } int main() { int sock = create_netlink_socket(); char buffer[4096]; struct nlmsghdr *nlh; printf("Monitoring network interface changes...\n"); printf("Press Ctrl+C to exit\n\n"); while (1) { // 接收Netlink消息 ssize_t len = recv(sock, buffer, sizeof(buffer), 0); if (len < 0) { perror("recv"); continue; } // 解析每个消息头 for (nlh = (struct nlmsghdr *)buffer; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) { // 处理网络接口事件 if (nlh->nlmsg_type == RTM_NEWLINK || nlh->nlmsg_type == RTM_DELLINK) { parse_interface_info(nlh); } } } close(sock); return 0; } ``` ### 代码解释 #### 1. 创建Netlink套接字 ```c int create_netlink_socket() { struct sockaddr_nl sa; int sock; int group = RTMGRP_LINK; // 监听网络接口变化 sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); ... sa.nl_groups = group; // 加入多播组 bind(sock, (struct sockaddr*)&sa, sizeof(sa)); } ``` - 使用`NETLINK_ROUTE`协议族获路由信息 - 加入`RTMGRP_LINK`多播组监听网络接口变化 - 绑定当前进程ID作为通信地址 #### 2. 消息解析核心 ```c void parse_interface_info(struct nlmsghdr *nlh) { struct ifinfomsg *ifinfo = NLMSG_DATA(nlh); char *status = (ifinfo->ifi_flags & IFF_UP) ? "UP" : "DOWN"; switch (nlh->nlmsg_type) { case RTM_NEWLINK: ... // 接口新增/更新 case RTM_DELLINK: ... // 接口移除 } if_indextoname(ifinfo->ifi_index, ifname); // 获接口名 } ``` - `ifinfomsg`结构包含接口索引和标志位 - `IFF_UP`标志位判断接口启用状态 - `if_indextoname`将索引转换为接口名 #### 3. 主循环处理 ```c while (1) { ssize_t len = recv(sock, buffer, sizeof(buffer), 0); for (nlh = (struct nlmsghdr *)buffer; NLMSG_OK(nlh, len); ...) { if (nlh->nlmsg_type == RTM_NEWLINK || ...) { parse_interface_info(nlh); } } } ``` - 持续监听套接字获内核通知 - `NLMSG_OK`宏确保消息完整性 - 过滤网络接口相关事件(RTM_NEWLINK/RTM_DELLINK) ### 编译与运行 ```bash # 编译 gcc netlink_monitor.c -o netlink_monitor # 运行(需要root权限) sudo ./netlink_monitor ``` ### 测试效果 当执行以下操作时,程序会实时输出: ```text [Event] Interface: eth0 Action: ADDED/UPDATED Status: UP Index: 2 [Event] Interface: wlan0 Action: ADDED/UPDATED Status: UP Index: 3 [Event] Interface: eth0 Action: REMOVED Status: DOWN Index: 2 ``` ### 工作原理 1. 内核网卡状态变化时发送RTM_NEWLINK/RTM_DELLINK消息 2. 用户空间程序通过Netlink套接字接收这些消息 3. 解析消息中的`ifinfomsg`结构获接口详情 4. 使用`if_indextoname`将接口索引转换为可读名称
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值