2009-02-23 2 views
25

C'è un modo per rilevare le modifiche agli indirizzi IP sulla macchina locale in Linux a livello di programmazione usando C++?Come rilevare automaticamente la modifica dell'indirizzo IP in Linux?

+2

+1: domanda comune, felice di vedere che ha chiesto qui. –

+0

Danny, IMHO se la tua soluzione non sta sondando, allora che cos'è ?? chiaramente attendi di ricevere un particolare messaggio dallo zoccolo, anche silenzioso quando puoi semplicemente eseguire il polling dell'output di ifconfig o usare getifaddrs come altri suggeriscono – user3529114

risposta

6

In C, per ottenere il IP corrente che uso:

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)); 

sostituire "eth0" con l'interfaccia che stai guardando. Tutto quello che devi fare ora è un sondaggio per una modifica.

+0

Questo è esattamente come fa "ifconfig" per ottenere l'indirizzo IP corrente di un'interfaccia. Per scoprire quali nomi di interfaccia esistono, in realtà si apre e analizza "/ proc/net/dev". Ci dovrebbe essere un modo per ottenere un evento dal kernel anche se l'IP cambia. La mia ipotesi sta usando gli eventi. – nly

+3

Purtroppo, questo non è affidabile per i sistemi moderni, in cui una singola interfaccia può avere più indirizzi senza l'uso di sottointerfacce. –

+1

c'è un esempio funzionante qui: http://stackoverflow.com/a/701989/386557 –

1

Un modo sarebbe quello di scrivere un processo cron che contiene una chiamata a una famiglia di funzioni della libreria gethost. Se si utilizza gethostbyname() è possibile confrontare i valori di ritorno di h_addr_list. Vedi man gethostbyname.

Se vuoi farlo dal tuo programma, genera un pthread che fa la stessa cosa, poi dorme per qualche periodo di tempo arbitrario.

4

Non è semplice in alcun modo. Ogni distribuzione linux utilizza diverse posizioni per archiviare gli indirizzi IP, ecc. (Più variazioni se si considerano altre varianti UNIX). È possibile uso, ad esempio, /sbin/ifconfig per ottenere gli indirizzi IP delle interfacce, ma non si può nemmeno essere sicuro se la troverete in questo luogo, o affatto, ecc

Inoltre, data avete quell'eseguibile, devi impostare un thread chiamandolo per ottenere i dati con un determinato periodo (diciamo 5 secondi) e interpretare l'output. Può variare, ad esempio, se hai ponti, ecc. Ecc. Cioè, non è facile.

Una soluzione che mi viene in mente è, se hai l'opportunità di utilizzare GNOME o qualche altra distribuzione diffusa come KDE, puoi fare affidamento sui messaggi/informazioni che danno. Ad esempio, NetworkManager emette un segnale su DBUS standard bus quando un dispositivo cambia. Devi implementare un ascoltatore per quel segnale. Informazioni here (non funziona al momento, quindi ecco un cache). Notare i diversi messaggi quando viene aggiunta una nuova interfaccia o quando uno di questi cambia l'indirizzo IP. Questo è il modo migliore in cui posso pensare adesso.

2

Se gli utenti utilizzano NetworkManager, è possibile eseguire il polling su NetworkManager.Connection.Active e NetworkManager.IP4Config tramite D-Bus per ottenere un metodo di distribuzione più incrociato per determinare queste informazioni.

2

Se iproute2 è installato e siete su un kernel 2.6,

/sbin/ip monitor 

cambiamenti uscita sarà in stato dell'interfaccia locale e gli indirizzi a stdout. Il tuo programma può leggerlo.

Si potrebbe anche utilizzare lo stesso meccanismo di basso livello di quello che fa lo strumento iproute2 (penso che sia un socket netlink).

0

Da pagina man di rtnetlink:

DESCRIZIONE

Rtnetlink permette tabelle di routing del kernel per essere letti e modificati. Viene usato all'interno del kernel per comunicare tra vari sottosistemi, sebbene questo uso non sia documentato qui, e per la comunicazione con i programmi dello spazio utente. Percorsi di rete, indirizzi IP, parametri di collegamento, configurazioni vicine, discipline di accodamento, classi di traffico e classificatori di pacchetti possono essere controllati tramite socket NETLINK_ROUTE. Si basa sui messaggi netlink, vedi netlink (7) per maggiori informazioni.

2

suggerimento di ste per utilizzare ioctl SIOCGIFADDR utilizzato per essere tecnicamente corretto, purtroppo non è affidabile per i moderni sistemi Linux, in cui una singola interfaccia può avere più indirizzi senza utilizzare sub-interfacce (ad esempio eth0: 1) come è stato fatto con l'ora -obsolete ifconfig.

La cosa migliore è quella di utilizzare getifaddrs (3), che è presente da glibc 2.3: http://www.kernel.org/doc/man-pages/online/pages/man3/getifaddrs.3.html

Purtroppo è un po 'inefficiente (si torna una lista concatenata di tutti gli indirizzi di tutte le interfacce e dovrà scorrere per trovare quelli che ti interessano), ma nella maggior parte dei casi probabilmente non lo controlli più di una volta al minuto, rendendo tollerabile l'overhead.

42

qui vai .. questo lo fa senza polling.

si ascolta solo per RTM_NEWADDR ma dovrebbe essere facile da cambiare per sostenere RTM_DELADDR se avete bisogno di

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

Grande codice. C'è un modo per guardare solo un'interfaccia specifica per un cambiamento? – watain

+5

@watain 'struct ifaddrmsg' ha un membro' ifa_index', che è l'indice dell'interfaccia con cui l'indirizzo è associato. –

+1

Si dovrebbe notare nella manpage per queste API netlink, si raccomanda di non usare questa interfaccia di basso livello, ma piuttosto la [libnetlink lib] (http://man7.org/linux/man-pages/man3/libnetlink. 3.html) ... Che a sua volta elenca l'uso di questa lib come un bug e punta all'uso di [libmnl] (http://www.netfilter.org/projects/libmnl/) invece – Joakim