17.8 ARP Cache Operations
On some systems, the ARP cache is also manipulated with the ioctl function. Systems that use routing sockets (Chapter 18) usually use routing sockets instead of ioctl to access the ARP cache. These requests use an arpreq structure, shown in Figure 17.12 and defined by including the <net/if_arp.h> header.
Figure 17.12 arpreq structure used with ioctl requests for ARP cache.
<net/if_arp.h>
struct arpreq {
struct sockaddr arp_pa; /* protocol address */
struct sockaddr arp_ha; /* hardware address */
int arp_flags; /* flags */
};
#define ATF_INUSE 0x01 /* entry in use */
#define ATF_COM 0x02 /* completed entry (hardware addr valid) */
#define ATF_PERM 0x04 /* permanent entry */
#define ATF_PUBL 0x08 /* published entry (respond for other host) */
The third argument to ioctl must point to one of these structures. The following three requests are supported:
SIOCSARP | Add a new entry to the ARP cache or modify an existing entry. arp_pa is an Internet socket address structure containing the IP address, and arp_ha is a generic socket address structure with sa_family set to AF_UNSPEC and sa_data containing the hardware address (e.g., the 6-byte Ethernet address). The two flags, ATF_PERM and ATF_PUBL, can be specified by the application. The other two flags, ATF_INUSE and ATF_COM, are set by the kernel. |
SIOCDARP | Delete an entry from the ARP cache. The caller specifies the Internet address for the entry to be deleted. |
SIOCGARP | Get an entry from the ARP cache. The caller specifies the Internet address, and the corresponding Ethernet address is returned along with the flags. |
These ARP-related ioctl requests are not supported on some newer systems, which use routing sockets for these ARP operations.
Notice that there is no way with ioctl to list all the entries in the ARP cache. On many systems, the arp command, when invoked with the -a flag (list all entries in the ARP cache), reads the kernel's memory (/dev/kmem) to obtain the current contents of the ARP cache. We will see an easier (and better) way to do this using sysctl, which only works on some systems (Section 18.4).
Example: Print Hardware Addresses of Host
We now use our get_ifi_info function to return all of a host's IP addresses, followed by an ioctl of SIOCGARP for each IP address to obtain and print the hardware addresses. We show our program in Figure 17.13.
Get List of Addresses and Loop Through Each One
12 We call get_ifi_info to obtain the host's IP addresses and then loop through each address.
Print IP Address
13 We print the IP address using sock_ntop. We asked get_ifi_info to only return IPv4 addresses, since ARP is not used with IPv6.
Issue ioctl and Check for Error
1419 We fill in the arp_pa structure as an IPv4 socket address structure containing the IPv4 address. ioctl is called, and if it returns an error (e.g., because the address supplied isn't on an interface that supports ARP), we print the error and loop to the next address.
Print Hardware Address
2022 The hardware address returned from the ioctl is printed.
Figure 17.13 Print a host's hardware addresses.
ioctl/prmac.c
1 #include "unpifi.h"
2 #include <net/if_arp.h>
3 int
4 main(int argc, char **argv)
5 {
6 int sockfd;
7 struct ifi_info *ifi;
8 unsigned char *ptr;
9 struct arpreq arpreq;
10 struct sockaddr_in *sin;
11 sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
12 for (ifi = get_ifi_info(AF_INET, 0); ifi != NULL; ifi = ifi->ifi_next) {
13 printf("%s: ", Sock_ntop(ifi->ifi_addr, sizeof(struct sockaddr_in)));
14 sin = (struct sockaddr_in *) &arpreq.arp_pa;
15 memcpy(sin, ifi->ifi_addr, sizeof(struct sockaddr_in));
16 if (ioctl(sockfd, SIOCGARP, &arpreq) < 0) {
17 err_ret("ioctl SIOCGARP");
18 continue;
19 }
20 ptr = &arpreq.arp_ha.sa_data[0];
21 printf("%x:%x:%x:%x:%x:%x\n", *ptr, *(ptr + 1),
22 *(ptr + 2), *(ptr + 3), *(ptr + 4), *(ptr + 5));
23 }
24 exit(0);
25 }
Running this program on our hpux host gives
hpux % prmac
192.6.38.100: 0:60:b0:c2:68:9b
192.168.1.1: 0:60:b0:b2:28:2b
127.0.0.1: ioctl SIOCGARP: Invalid argument