#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):
struct ifreq ifr;
d = socket (...);
ifr.ifr_ifindex=2;
ioctl (d, SIOCGIFNAME, &ifr);
printf ("Interfejs nr. %i to %s.\n",ifr.ifr_ifindex,ifr.ifr_name);