14

Ahora necesito obtener el estado de la NIC (arriba o abajo) en tiempo real. Eso significa que tengo que atrapar la interrupción del kernel cuando la NIC sube o baja en un bucle bloqueado.¿Cómo puedo controlar el estado de NIC (arriba/abajo) en un programa en C sin sondear el kernel?

El primer método estúpida de la mía es que el registro en el /sys/class/net/eth0/operstate o utilizar ioctl para obtener el ifflag cada 100 ms en un bucle. Pero 100 ms es demasiado largo para que la aplicación vuelva a enrutar el tráfico y también interrogar al kernel cada 100 ms no es una buena idea.

Una vez noto la inotify función que puede supervisar los archivos en un modo de bloque. Pero desafortunadamente, no puede monitorear el archivo/sys/class/net/eth0/operstate ya que/sys está ubicado en la RAM que no está en el disco.

Entonces, ¿hay algún método excepto escribir un módulo kernel para capturar la interrupción NIC (arriba/abajo) en el programa C con un modo de bloque?

+0

posible duplicado de [¿Existe un mecanismo de notificación para cuando getifaddrs() resultados cambian?] (Http://stackoverflow.com/questions/1270186/is-there-a-notification-mechanism-for-when -getifaddrs-results-change) –

Respuesta

11

Sí, abra un socket de netlink y escuche los grupos de multidifusión RTMGRP_LINK (interfaz de red crear/eliminar/subir/bajar).

La página man de netlink here tiene un ejemplo específico para hacer esto.

+0

¡un millón gracias! – victor

+0

¡De nada! es costumbre marcar mi respuesta como la correcta si crees que resolvió tu problema (haz clic en la V a la izquierda de la pregunta) – gby

+0

He intentado con RTMGET_LINK como el tipo de mensaje y obtuve la información del kernel, la información del dispositivo está construida en una ** 'ifinfomsg struct' **. El proceso es como usuario-> kernel y kernel-> usuario. Pero lo que quiero es ejecutar un ciclo en el espacio de usuario que cuando el estado de la NIC cambia, el kernel se comunicará automáticamente con el espacio de usuario sin la solicitud de envío de espacio del usuario. ¿Puede presentar un breve, por ejemplo, usando RTMGRP_LINK? – victor

-1

¿Ha intentado controlar el archivo /sys/class/net/eth0/operstate con la función select o poll? Por lo que puedo decir, sysfs los archivos deben comportarse de la misma manera con los sondeos como archivos regulares: cada vez que se produce un cambio, debe recibir una notificación en el archivo de que algo ha cambiado y debería poder responder en consecuencia.

+0

No tengo idea de si 'select' o' poll' notificarán al espacio del usuario cuando se haya modificado el contenido del archivo. **/sys/class/net/eth0/operstate ** es el resultado del kernel para indicar que la NIC sube o baja. – victor

+0

Es un controlador de archivo: debería funcionar bien. https://bugzilla.redhat.com/show_bug.cgi?id=604887 es un antiguo error de RHEL que muestra el uso de 'select' en un archivo sysfs: parece razonable esperar que funcione. – Femi

+0

No todos los archivos sysfs implementan el soporte 'encuesta '. Desafortunadamente, 'operstate' es uno de los que no. – hetman

6

Después de hacer un poco de investigación/lectura en la web, logré preparar un código de trabajo para monitorear el estado de la 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

Es posible que haya omitido la parte en la pregunta que solicitó "sin votación", que es lo que parece que hace su solución. –

+0

Esta es la misma solución aceptada por @victor y este código recibe una notificación a través del socket cuando se abre una interfaz. –

Cuestiones relacionadas