2009-02-23 11 views
25

¿Hay alguna manera de detectar cambios de direcciones IP en la máquina local en Linux mediante programación mediante C++?¿Cómo detectar el cambio de dirección IP programáticamente en Linux?

+2

+1: pregunta común, contento de ver que pidió aquí. –

+0

Danny, en mi humilde opinión, si su solución no es una encuesta, ¿entonces qué es? esperas claramente recibir un mensaje particular del zócalo, también tranquilo complicado cuando uno simplemente puede sondear la salida de ifconfig o usar getifaddrs como otros sugirieron – user3529114

Respuesta

6

En C, para obtener el IP actual que utilizo:

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

reemplazar "eth0" con la interfaz que está viendo. Todo lo que debes hacer ahora es sondear para un cambio.

+0

Esto es exactamente lo que hace "ifconfig" para obtener la dirección IP actual de una interfaz. Para averiguar qué nombres de interfaz existen, en realidad se abre y analiza "/ proc/net/dev". Sin embargo, debería haber una manera de obtener un evento del kernel cuando cambia la IP. Mi suposición es usar uevents. – nly

+3

Lamentablemente, esto no es confiable para sistemas modernos, donde una única interfaz puede tener múltiples direcciones sin el uso de subinterfaces. –

+1

hay un ejemplo de trabajo aquí: http://stackoverflow.com/a/701989/386557 –

1

Una forma sería escribir un trabajo cron que contenga una llamada a uno de la familia gethost de las funciones de la biblioteca. Si usa gethostbyname(), puede comparar los valores de retorno de h_addr_list. Ver hombre gethostbyname.

Si quieres hacer esto desde dentro de tu programa, engendra un pthread que hace lo mismo, luego duerme durante un período de tiempo arbitrario.

4

No es fácil de ninguna manera. Cada distribución de Linux utiliza diferentes lugares para almacenar direcciones IP, etc. (más variación si considera otras variantes de UNIX). Usted puede uso, por ejemplo, /sbin/ifconfig para obtener las direcciones IP de las interfaces, pero ni siquiera se puede estar seguro de si lo encontrará en este lugar, o en absoluto, etc.

Además, dado que tiene ese ejecutable, tiene que configurar un hilo que lo llame para obtener los datos con un período determinado (digamos 5 segundos), e interpretar el resultado. Puede variar, por ejemplo, si tiene puentes, etc. etc. Es decir, no es fácil.

Una solución que me viene a la mente es que, si tiene la oportunidad de usar GNOME o alguna otra distribución generalizada como KDE, puede confiar en los mensajes/información que brindan. Por ejemplo, NetworkManager emite una señal al DBUS standard bus cuando cambia un dispositivo. Tienes que implementar un oyente para esas señales. Información here (no funciona en este momento, así que aquí está un cache). Tenga en cuenta los diferentes mensajes cuando se agrega una nueva interfaz, o cuando uno de ellos cambia la dirección IP. Esta es la mejor manera en que puedo pensar en este momento.

2

Si sus usuarios usan NetworkManager, puede sondear NetworkManager.Connection.Active y NetworkManager.IP4Config a través de D-Bus para obtener una forma más de distribución cruzada para determinar esta información.

2

Si iproute2 está instalado y ya está en un kernel 2.6,

/sbin/ip monitor 

cambios de salida será en el estado de la interfaz local y direcciones a la salida estándar. Tu programa puede leer esto.

También podría usar el mismo mecanismo de bajo nivel que la herramienta iproute2 (creo que es una conexión de red).

0

De página del manual de rtnetlink:

DESCRIPCIÓN

Rtnetlink permite tablas de enrutamiento del kernel para leer y alterar. Se usa dentro del kernel para comunicarse entre varios subsistemas, aunque este uso no está documentado aquí, y para la comunicación con programas de espacio de usuario. Las rutas de red, las direcciones IP, los parámetros de enlace, las configuraciones vecinas, las disciplinas de colas, las clases de tráfico y los clasificadores de paquetes pueden controlarse a través de los zócalos NETLINK_ROUTE. Se basa en mensajes netlink, ver netlink (7) para más información.

2

sugerencia de ste para usar ioctl SIOCGIFADDR solía ser técnicamente correcto, desafortunadamente no es confiable para los sistemas Linux modernos, donde una sola interfaz puede tener múltiples direcciones sin usar subinterfaces (por ej. Eth0: 1) como se hizo con el ahora -obsolete ifconfig.

Su mejor opción es utilizar getifaddrs (3), que está presente desde glibc 2.3: http://www.kernel.org/doc/man-pages/online/pages/man3/getifaddrs.3.html

Por desgracia, es algo ineficaz (que obtener una lista enlazada de todas las direcciones de todas las interfaces y tendrá que recorrer para encontrar las que le interesan), pero en la mayoría de los casos probablemente no lo verifique más de una vez por minuto, lo que hace que la sobrecarga sea tolerable.

42

aquí tienes ... esto lo hace sin sondeo.

sólo escucha los RTM_NEWADDR pero debe ser fácil de cambiar para apoyar RTM_DELADDR si necesita

#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

Gran código. ¿Hay alguna forma de mirar solo una interfaz específica para un cambio? – watain

+5

@watain the 'struct ifaddrmsg' tiene un miembro' ifa_index', que es el índice de interfaz de la interfaz a la que está asociada la dirección. –

+1

Debe tenerse en cuenta en la página de manual para estas API de netlink, se recomienda no utilizar esta interfaz de bajo nivel, sino más bien la [libnetlink lib] (http://man7.org/linux/man-pages/man3/libnetlink. 3.html) ... que a su vez enumera el uso de esta lib como un error y señala el uso de [libmnl] (http://www.netfilter.org/projects/libmnl/) en su lugar – Joakim

Cuestiones relacionadas