struct arpreq {
struct sockaddr arp_pa; /* adres sieciowy */
struct sockaddr arp_ha; /* adres MAC */
int arp_flags; /* flagi */
struct sockaddr arp_netmask; /* maska sieciowa (proxy ARP) */
char arp_dev[16]; /* nazwa interfejsu */
};
Dwie najważniejsze flagi to:
struct arpreq arq;
struct in_addr inaddr; /* adres IP */
char h[] = "\x11\x22\x33\x44\x55\x66"; /* adres MAC */
int d;
d = socket (PF_PACKET, SOCK_RAW, 0);
bzero ((char *) &arq, sizeof (arq));
/* W inaddr umieszczamy adres IP */
inet_aton ("192.192.192.192", &inaddr);
/* Trzeba też podać odpowiednią domenę adresową */
arq.arp_pa.sa_family = PF_INET;
/* Kopiujemy adres IP w pole arp_pa, które jest strukturą */
/* sockaddr. */
memcpy (arq.arp_pa.sa_data + 2, (char *) &inaddr, 4);
/* Adres MAC umieszczamy w arp.ha ... */
sprintf (arq.arp_ha.sa_data, "%s", h);
/* ... a w arp_dev nazwę interfejsu */
sprintf (arq.arp_dev, "%s", "eth0");
/* Jeszcze flaga, oznaczająca, że dany wpis jest kompletny */
arq.arp_flags = ATF_COM;
ioctl (d, SIOCSARP , &arq);
perror("ioctl()");
struct arpreq arq;
struct in_addr inaddr;
struct ether_addr eaddr; /* net/ethernet.h */
int d;
d = socket (PF_PACKET, SOCK_RAW, 0);
bzero ((char *) &arq, sizeof (arq));
arq.arp_pa.sa_family = PF_INET;
sprintf (arq.arp_dev, "%s", "eth0");
inet_aton ("192.168.1.2", &inaddr);
memcpy (arq.arp_pa.sa_data + 2, (char *) &inaddr, 4);
ioctl (d, SIOCGARP, &arq);
memcpy ((char *) &eaddr, arq.arp_ha.sa_data, ETH_ALEN);
printf ("adr: %s\n", ether_ntoa (&eaddr)); /* netinet/ether.h */
struct rtentry
{
...
struct sockaddr rt_dst; /* adres docelowy */
struct sockaddr rt_gateway; /* adres bramki */
struct sockaddr rt_genmask; /* maska sieci docelowej) */
unsigned short rt_flags; /* flagi */
...
char *rt_dev; /* interfejs sieciowy */
...
};
Niektóre flagi:
#define RTF_UP 0x0001 /* ścieżka aktywna */
#define RTF_GATEWAY 0x0002 /* pole rt_gateway jest znaczące */
#define RTF_HOST 0x0004 /* ścieżka do hosta (jeśli ta flaga */
/* jest wyłączona to wpis oznacza */
/* ścieżkę do sieci */
#define RTF_DYNAMIC 0x0010 /* utworzona poprzez ICMP redirect */
#define RTF_MODIFIED 0x0020 /* zmieniona przez ICMP redirect */
Poniższy przykład robi to samo co polecenie 'route add -net
192.168.0.0 netmask 255.255.0.0 gw 192.168.1.1':
struct rtentry rte; /* linux/route.h */
struct in_addr daddr, gaddr, naddr;
int d;
d = socket (PF_PACKET, SOCK_RAW, 0);
bzero ((char *) &rte, sizeof (rte));
/* Wypełniamy odpowiednio: adres docelowej sieci, adres bramki */
/* i maskę docelowej sieci. */
inet_aton ("192.168.0.0", &daddr);
inet_aton ("192.168.1.1", &gaddr);
inet_aton ("255.255.0.0", &naddr);
/* Oba adresy oraz maska należą do domeny PF_INET */
rte.rt_dst.sa_family = PF_INET;
rte.rt_gateway.sa_family = PF_INET;
rte.rt_genmask.sa_family = PF_INET;
/* Kopiujemy adres i maskę do odpowiednich pol struktury */
memcpy (rte.rt_dst.sa_data + 2, (char *) &daddr, 4);
memcpy (rte.rt_gateway.sa_data + 2, (char *) &gaddr, 4);
memcpy (rte.rt_genmask.sa_data + 2, (char *) &naddr, 4);
/* Ustawiamy dwie flagi mówiące, że dana scieżka jest aktywna */
/* (RTF_UP) i że należy skorzystać z bramki (RTF_GATEWAY) */
rte.rt_flags = RTF_UP | RTF_GATEWAY;
perror ("ioctl()");
Zanim zabierzemy się za niskopoziomowe manipulowanie tablicą routingu warto poćwiczyć dodawanie/usuwanie ścieżek za pomocą systemowej komendy route(8). Dzięki temu dowiemy się, jakie pola należy zawsze wypełnić, a jakie są opcjonalne. Parametry polecenia route mają bardzo dokładne przełożenie na pola struktury rtentry.