14

Ora ho bisogno di ottenere lo stato della NIC (su o giù) in tempo reale. Ciò significa che devo prendere l'interrupt del kernel quando la NIC su o giù in un ciclo bloccato.Come posso monitorare lo stato della NIC (su/giù) in un programma C senza eseguire il polling del kernel?

Il primo metodo stupido da mio è quello di controllo sulla /sys/class/net/eth0/operstate o utilizzare ioctl per ottenere l'ifflag ogni 100 ms in un ciclo. Ma 100 ms è troppo lungo perché l'app possa reindirizzare il traffico e anche il polling del kernel ogni 100 ms non è una buona idea.

Una volta ho notato la funzione diinotify in grado di monitorare i file in una modalità di blocco. Ma sfortunatamente, non può monitorare il file/sys/class/net/eth0/operstate poiché/sys si trova nella RAM non nel disco.

Quindi, c'è qualche metodo tranne la scrittura di un modulo del kernel per catturare l'interrupt NIC (su/giù) nel programma C con una modalità di blocco?

+0

possibile duplicato del [c'è un meccanismo di notifica per quando getifaddrs() risultati cambiano?] (Http://stackoverflow.com/questions/1270186/is-there-a-notification-mechanism-for-when -getifaddrs-results-change) –

risposta

11

Sì, aprire un socket netlink e ascoltare i gruppi multicast RTMGRP_LINK (network interface create/delete/up/down events).

La pagina di manuale netlink here ha un esempio specifico per eseguire questa operazione.

+0

un milione grazie! – victor

+0

prego! è consuetudine contrassegnare la mia risposta come corretta se ritieni di aver risolto il tuo problema (fai clic sulla V a sinistra della domanda) – gby

+0

Ho provato RTMGET_LINK come tipo di messaggio e ho ottenuto le informazioni dal kernel, le informazioni sul dispositivo sono state costruite in una ** ifinfomsg struct' **. Il processo è come utente-> kernel e kernel-> utente. Ma quello che voglio è eseguire un ciclo nello spazio utente che quando lo stato della NIC cambia, il kernel comunicherà automaticamente allo spazio dell'utente senza la richiesta di invio dello spazio dell'utente. Puoi presentare un breve esempio usando RTMGRP_LINK? – victor

-1

Hai provato a monitorare il file /sys/class/net/eth0/operstate con la funzione select o poll? Per quanto posso dire, i file sysfs dovrebbero comportarsi come nel caso del polling dei file normali: ogni volta che si verifica una modifica, si dovrebbe ricevere una notifica sul file che gestisce che qualcosa è cambiato e si dovrebbe essere in grado di rispondere di conseguenza.

+0

Non ho idea se il 'select' o' poll' notificherà lo spazio dell'utente quando il contenuto del file è stato modificato. **/sys/class/net/eth0/operstate ** è il risultato del kernel per indicare la NIC su o giù. – victor

+0

È un handle di file: dovrebbe funzionare bene. https://bugzilla.redhat.com/show_bug.cgi?id=604887 è un vecchio bug RHEL che mostra l'uso di 'select' su un file sysfs: sembra ragionevole aspettarsi che funzioni. – Femi

+0

Non tutti i file sysfs implementano il supporto 'poll'. Sfortunatamente 'operstate' è uno di quelli che non lo fa. – hetman

6

Dopo aver fatto una piccola ricerca/lettura sul web, sono riuscito a preparare un codice funzionante per monitorare lo stato della 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

Potrebbe esserti persa la parte nella domanda che chiedeva "senza polling", che è ciò che la tua soluzione sembra fare. –

+0

Questa è la stessa soluzione accettata da @victor e questo codice riceve una notifica tramite socket quando viene visualizzata un'interfaccia. –

Problemi correlati