2009-02-23 129 views
25

有沒有辦法在Linux上以編程方式使用C++來檢測本地計算機上的IP地址更改?如何在Linux中以編程方式檢測IP地址更改?

+2

+1:常見問題,很高興看到它在這裏問。 – 2009-02-24 10:51:45

+0

丹尼,恕我直言,如果你的解決方案沒有投票,那麼什麼是?你顯然會等待從套接字收到一個特定的消息,當一個人可以簡單地輪詢ifconfig的輸出或使用其他人建議的getifaddrs時,安靜地複雜化 – user3529114 2014-04-13 13:54:27

回答

6

在C,以獲得當前IP使用:

int s; 
    struct ifreq ifr = {}; 

    s = socket(PF_INET, SOCK_DGRAM, 0); 

    strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name)); 

    if (ioctl(s, SIOCGIFADDR, &ifr) >= 0) 
     printf("%s\n", 
      inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr)); 

替換 「eth0的」 與你在尋找的接口。您現在需要做的就是輪詢變更。

+0

這正是「ifconfig」如何獲取接口的當前IP地址。要找出存在哪些接口名稱,它實際上會打開並解析「/ proc/net/dev」。 儘管知識產權發生變化,應該有辦法從內核獲取事件。我的猜測是使用uevents。 – nly 2009-02-24 11:20:56

1

一種方法是編寫一個cron作業,其中包含對庫函數的gethost系列調用。如果使用gethostbyname(),則可以比較h_addr_list的返回值。請參閱man gethostbyname。

如果你想在你的程序中做到這一點,產生一個pthread,做同樣的事情,然後睡一段任意時間。

4

這並不容易。每個Linux發行版使用不同的地方來存儲IP地址等(如果考慮其他UNIX變體,則會有更多的變化)。您可以使用,例如,/sbin/ifconfig獲得接口的IP地址,但你甚至不能肯定,如果你在這個地方找到它,或者根本等

此外,由於你有那個可執行文件,你必須建立一個線程來調用它來獲得給定週期(比如說5秒)的數據,然後解釋輸出。例如,如果您有橋樑等,這可能會有所不同。也就是說,這並不容易。

我想到的一個解決方案是,如果您有機會使用GNOME或其他廣泛分佈的KDE,則可以依賴它們提供的消息/信息。例如,當設備發生變化時,NetworkManagerDBUS standard bus輸出一個信號。你必須爲這些信號實現一個​​監聽器。信息here(現在不工作,所以這裏是cache)。請注意添加新接口時或當其中一個接口更改IP地址時的不同消息。這是我現在能想到的最好的方式。

2

如果您的用戶使用NetworkManager,則可以通過D-Bus輪詢NetworkManager.Connection.Active和NetworkManager.IP4Config,以獲得更多的確定此信息的交叉分配方式。

2

如果iproute2是安裝,您使用的是2.6內核,在本地接口狀態和地址到stdout

/sbin/ip monitor 

將輸出的變化。你的程序可以閱讀這個。

您也可以使用與iproute2工具相同的低級別機制(我認爲它是netlink套接字)。

0

從的rtnetlink的手冊頁:

說明

RTNETLINK允許內核的路由表來讀取和修改。它在內核中用於在各種子系統之間進行通信,儘管這裏沒有記錄這種用法,並且用於與用戶空間程序的通信。網絡路由,IP地址,鏈路參數,鄰居設置,排隊規則,流量類別和分組分類都可以通過NETLINK_ROUTE套接字來控制。它基於netlink消息,請參閱netlink(7)以獲取更多信息。

2

ste的建議是使用ioctl SIOCGIFADDR在技術上是正確的,但不幸的是,對於現代Linux系統來說,單個接口可以有多個地址而不使用子接口(如eth0:1) -obsolete ifconfig。

最好的辦法是使用getifaddrs(3),這是由glibc的2.3存在:http://www.kernel.org/doc/man-pages/online/pages/man3/getifaddrs.3.html

可惜這有點低效(你找回所有接口上的所有地址的鏈接列表,將不得不遍歷找到你感興趣的人),但是在大多數情況下,你可能不會每分鐘檢查一次以上,這使得開銷可以忍受。

42

在這裏你去..這沒有投票。

只監聽RTM_NEWADDR但它應該是很容易改變,以支持RTM_DELADDR如果你需要

#include <stdio.h> 
#include <string.h> 
#include <netinet/in.h> 
#include <linux/netlink.h> 
#include <linux/rtnetlink.h> 
#include <net/if.h> 

int 
main() 
{ 
    struct sockaddr_nl addr; 
    int sock, len; 
    char buffer[4096]; 
    struct nlmsghdr *nlh; 

    if ((sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) { 
     perror("couldn't open NETLINK_ROUTE socket"); 
     return 1; 
    } 

    memset(&addr, 0, sizeof(addr)); 
    addr.nl_family = AF_NETLINK; 
    addr.nl_groups = RTMGRP_IPV4_IFADDR; 

    if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { 
     perror("couldn't bind"); 
     return 1; 
    } 

    nlh = (struct nlmsghdr *)buffer; 
    while ((len = recv(sock, nlh, 4096, 0)) > 0) { 
     while ((NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE)) { 
      if (nlh->nlmsg_type == RTM_NEWADDR) { 
       struct ifaddrmsg *ifa = (struct ifaddrmsg *) NLMSG_DATA(nlh); 
       struct rtattr *rth = IFA_RTA(ifa); 
       int rtl = IFA_PAYLOAD(nlh); 

       while (rtl && RTA_OK(rth, rtl)) { 
        if (rth->rta_type == IFA_LOCAL) { 
         uint32_t ipaddr = htonl(*((uint32_t *)RTA_DATA(rth))); 
         char name[IFNAMSIZ]; 
         if_indextoname(ifa->ifa_index, name); 
         printf("%s is now %d.%d.%d.%d\n", 
           name, 
           (ipaddr >> 24) & 0xff, 
           (ipaddr >> 16) & 0xff, 
           (ipaddr >> 8) & 0xff, 
           ipaddr & 0xff); 
        } 
        rth = RTA_NEXT(rth, rtl); 
       } 
      } 
      nlh = NLMSG_NEXT(nlh, len); 
     } 
    } 
    return 0; 
}