14

現在我需要實時獲取NIC的狀態(向上或向下)。這意味着當NIC在阻塞循環中上下移動時,我必須捕獲內核中斷。如何在不查詢內核的情況下監控C程序中的NIC狀態(向上/向下)?

從我的第一個笨方法是該檢查的/SYS /班/網/的eth0/operstate或使用IOCTL得到ifflag每100ms的循環。但是100ms對於應用程序重新路由流量來說太長了,並且每100ms輪詢內核也不是個好主意。

一旦我注意到inotify的功能,可以監視塊模式的文件。但不幸的是,它不能監視/ sys/class/net/eth0/operstate文件,因爲/ sys位於不在磁盤中的RAM中。

那麼,有沒有其他的方法,除了編寫一個內核模塊來捕捉塊程序模式下的C程序中的NIC中斷(上/下)?

+0

可能重複[是否有當getifaddrs()業績變動通知機制?](http://stackoverflow.com/questions/1270186/is-there-a-notification-mechanism-for-when -getifaddrs-results-change) –

回答

11

是的,打開一個netlink套接字並監聽RTMGRP_LINK(網絡接口創建/刪除/向上/向下事件)多播組。

netlink手冊頁here有一個具體的例子來做到這一點。

+0

百萬謝謝! – victor

+0

不客氣!如果您認爲它解決了您的問題,請按照慣例將我的答案標記爲正確的答案(單擊問題左側的V) – gby

+0

我已嘗試RTMGET_LINK作爲消息類型並從內核獲取信息,設備信息已構建在**'ifinfomsg結構**中。該過程就像用戶 - >內核和內核 - >用戶。但我想要的是在用戶空間中運行一個循環,當NIC狀態發生變化時,內核將自動與用戶空間進行通信,而無需用戶空間發送請求。你能提出一個簡單的例如使用RTMGRP_LINK嗎?行! – victor

-1

您是否試過用selectpoll函數監視/sys/class/net/eth0/operstate文件?至於我可以告訴sysfs文件應該像普通文件一樣在輪詢方面表現相同:每當發生更改時,您應該在文件句柄上收到通知,指出某些內容已發生更改,並且您應該能夠做出相應的響應。

+0

我不知道'select'或'poll'是否會在文件內容被修改時通知用戶空間。 **/sys/class/net/eth0/operstate **是內核向上或向下指示NIC的結果。 – victor

+0

它的文件句柄:它應該工作得很好。 https://bugzilla.redhat.com/show_bug.cgi?id=604887是一箇舊的RHEL錯誤,它顯示了在sysfs文件中使用'select':似乎有理由期望它能夠正常工作。 – Femi

+0

並非所有的sysfs文件都實現「輪詢」支持。不幸的是'operstate'是其中的一個。 – hetman

6

在網上做了一些研究/閱讀後,我設法制作了一個工作代碼來監視NIC狀態。

#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; 
} 
+0

您可能錯過了要求「不進行輪詢」的問題中的部分,這是您的解決方案似乎要做的事情。 –

+0

這與@victor接受的解決方案相同,並且此代碼在啓用接口時通過套接字獲取通知。 –

相關問題