next up previous contents
Next: Konfigurowanie interfejsów Up: Programowe konfigurowanie sieci - Previous: Programowe konfigurowanie sieci -   Contents

Zbieranie danych o dostępnych interfejsach

Bardzo często zachodzi potrzeba pobrania listy wszystkich interfejsów sieciowych dostępnych na danej maszynie. Zadanie takie wykona ten kawałek kodu:
  #include <linux/if.h>
  
  #define MAXIFN         16    /* maksymalna liczba interfejsów */
  
  main ()
  {
    struct ifreq ifr[MAXIFN];
    struct ifconf ifc;
    int sd, n, i;
  
    sd = socket (PF_INET, SOCK_STREAM, 0);
  
Żeby operować na jakimś urządzeniu za pomocą ioctl() potrzebujemy deskryptora pliku wskazującego na to urządzenie. Interfejsy sieciowe nie mają ze sobą skojarzonych żadnych plików w /dev. W takim razie w jaki sposób można dobrać się do interfejsu ?? Otóż robi się to za pośrednictwem deskryptora gniazda. Nie musimy koniecznie tworzyć gniazda dokładnie takiego samego, jak w przykładzie - możemy się posłużyć dowolnym typem gniazda.
    ifc.ifc_len = MAXIFN * sizeof (struct ifreq);
    ifc.ifc_req = ifr;
  
Następnym krokiem jest przygotowanie struktury, którą przekażemy jako parametr dla późniejszego wywołania ioctl(). Ogromna większość poleceń ioctl() przeznaczonych do operacji na interfejsach sieciowych oczekuje jako parametru wskaźnika do struktury ifreq. Jest jednak kilka wyjątków. Jednym z nich jest polecenie SIOCGIFCONF (Socket I/O Control Get Interface Configuration ;) - jego parametrem jest wskaźnik do struktury ifconf (linux/if.h):
   struct ifconf
   {
        int     ifc_len;                           /* wielkość bufora       */
        union
        {
                char *                  ifcu_buf;  /* adres bufora          */
                struct  ifreq           *ifcu_req;
        } ifc_ifcu;
   };
  
Polecenie SIOCGIFCONF wypełni podany bufor pewną ilością (zależnie od ilości interfejsów) struktur ifreq, które opisują pojedyncze interfejsy. Jak widać buforem może być zarówno tablica znaków (char) jak i tablica struktur ifreq. Bardziej intuicyjne jest korzystanie z tej drugiej możliwości. Po wykonaniu SIOCGIFCONF w pole ifc_len wstawiona zostanie ilość bajtów umieszczonych w buforze. Pozostało już tylko wywołać ioctl():
    ioctl (sd, SIOCGIFCONF, &ifc);
    n = ifc.ifc_len / sizeof (struct ifreq);
    printf ("Liczba interfejsów: %i\n", n);
  
Jak widać liczbę znalezionych interfejsów obliczamy dzieląc wielkość zwróconego bufora przez wielkość pojedynczej struktury ifreq.
    for (i = 0; i < ifc.ifc_len / sizeof (struct ifreq); i++)
      printf ("nr.%i : %s\n", i + 1, ifr[i].ifr_name);
  }
  

W pętli for wyświetlimy nazwy odnalezionych interfejsów. Informacje te pobierzemy ze struktury ifreq (linux/if.h):

  struct ifreq
  {
  #define IFHWADDRLEN     6
  #define IFNAMSIZ        16
          union
          {
                  char    ifrn_name[IFNAMSIZ];     /* nazwa interfejsu" */
          } ifr_ifrn;
  
          union {
                  struct  sockaddr ifru_addr;
                  struct  sockaddr ifru_dstaddr;
                  struct  sockaddr ifru_broadaddr;
                  struct  sockaddr ifru_netmask;
                  struct  sockaddr ifru_hwaddr;
                  short   ifru_flags;
                  int     ifru_ivalue;
                  int     ifru_mtu;
                  struct  ifmap ifru_map;
                  char    ifru_slave[IFNAMSIZ];   
                  char    ifru_newname[IFNAMSIZ];
                  char *  ifru_data;
          } ifr_ifru;
  };
  
  #define ifr_name        ifr_ifrn.ifrn_name      /* nazwa interfejsu         */
  #define ifr_hwaddr      ifr_ifru.ifru_hwaddr    /* adres MAC                */
  #define ifr_addr        ifr_ifru.ifru_addr      /* adres warstwy sieciowej  */
  ...
  #define ifr_broadaddr   ifr_ifru.ifru_broadaddr /* adres broadcast          */
  #define ifr_netmask     ifr_ifru.ifru_netmask   /* maska sieci              */
  #define ifr_flags       ifr_ifru.ifru_flags     /* flagi                    */
  ...
  #define ifr_map         ifr_ifru.ifru_map       /* I/O, IRQ, DMA itp        */
  ...
  #define ifr_ifindex     ifr_ifru.ifru_ivalue    /* numer interfejsu         */
  ...
  #define ifr_newname     ifr_ifru.ifru_newname   /* nowa nazwa (SIOCSIFNAME) */
  

Ważne jest aby pamietać, że SIOCGIFCONF wypełnia tylko pole ifr_name - reszta pól ma zupełnie przypadkową wartość ! Do uzyskania reszty informacji o danym interfejsie slużą oddzielne polecenia ioctl() (linux/sockios.h):


next up previous contents
Next: Konfigurowanie interfejsów Up: Programowe konfigurowanie sieci - Previous: Programowe konfigurowanie sieci -   Contents
Paweł Niewiadomski
2000-10-17