|
|
ARP caches Internet-IEEE address mappings. When an interface requests a mapping for an address not in the cache, ARP queues the message which requires the mapping and broadcasts a message on the associated network requesting the address mapping. If a response is provided, the new mapping is cached and any pending message is transmitted. ARP will queue at most one packet while waiting for a mapping request to be responded to; only the most recently ``transmitted'' packet is kept. The ARP protocol is implemented by a STREAMS driver to do the protocol negotiation.
To facilitate communications with systems which do not use ARP, a mechanism is provided for manipulating the entries in the arp cache. Since the arp cache is stored in the routing table, this requires use of the routing stream driver (see route(ADMP).
An arp ``route'' consists of an IP destination and a link-level next hop address. To add an ARP entry, issue an RTM_ADD message to the routing stream interface. Both the destination and gateway address fields must be present. The destination address is defined as follows (see sys/netinet/if_ether.h):
struct sockaddr_inarp { u_char sin_len; /* not used */ u_char sin_family; /* AF_INET */ u_short sin_port; /* not used */ struct in_addr sin_addr; /* IP address */ struct in_addr sin_srcaddr; /* not used */ u_short sin_tos; /* not used */ u_short sin_other; /* set to SIN_PROXY to publish */ #define SIN_PROXY 1 };The gateway is a link-level address and is defined using the following structure (see sys/net/if_dl.h):
/* * Structure of a Link-Level sockaddr: */ struct sockaddr_dl { u_short sdl_family; /* AF_DL */ u_short sdl_index; /* if != 0, index for interface */ u_char sdl_type; /* if type (see net/if_types.h) */ u_char sdl_alen; /* link address length (normally 6) */ char sdl_data[10]; /* address */ }; #define LLADDR(s) ((caddr_t)((s)->sdl_data))To retrieve all ARP entries, code similar to that shown in route(ADMP) can be used. The gi_arg field of the rt_giarg structure should be set to RTF_LLINFO. This ensures that only routes with valid link-level information (i.e. ARP entries) are retrieved.
The following sample code shows how to add an ARP entry:
#include <sys/types.h> #include <sys/socket.h> #include <sys/stream.h> #include <sys/time.h> #include <sys/stropts.h> #include <netinet/in.h> #include <net/if.h> #include <net/if_dl.h> #include <net/route.h> #include <net/if_arp.h> #include <netinet/if_ether.h> #include <errno.h> #include <fcntl.h> #include <paths.h>The RTSTR_USELOOPBACK ioctl indicates that the process does not desire to receive routing messages generated as a result of its own operations.struct sockaddr_inarp blank_sin = {AF_INET}, sin_m; struct sockaddr_dl blank_sdl = {AF_LINK}, sdl_m; struct { struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg;
setarp(ia, ha, len) struct in_addr *ia; /* IP address */ u_char *ha; /* hardware address */ int len; /* hardware address length (6) */ { int s; int r; struct rt_msghdr *rtm = &m_rtmsg.m_rtm ; struct timeval time; struct strioctl si;
s = open(_PATH_ROUTE, O_WRONLY, 0); /* open routing stream */ if (s < 0) { perror("_PATH_ROUTE"); return s; } (void) ioctl(s, I_SRDOPT, RMSGD); /* message discard mode */
si.ic_cmd = RTSTR_USELOOPBACK; /* don't want my msgs */ si.ic_dp = (char *)0; si.ic_len = 0; si.ic_timout = -1; (void) ioctl(s, I_STR, &si);
/* copy in IP and arp addresses */ sin_m = blank_sin; sdl_m = blank_sdl; bcopy((char *)ia, &sin_m.sin_addr.s_addr, sizeof(struct in_addr)); bcopy((char *)ha, sdl_m.sdl_data, len); sdl_m.sdl_alen = len;
/* add the entry */ (void) gettimeofday(&time, (struct timezone *)0); r = rtmsg(s, RTM_ADD, &sin_m, &sdl_m, time.tv_sec + 20 * 60); if (r < 0 && errno != EEXIST) { perror("RTM_ADD"); close(s); return r; } close(s); return 0; }
static int seq = 0;
rtmsg(s, msg, sin, sdl, expire) int s; int msg; struct sockaddr_inarp *sin; struct sockaddr_dl *sdl; int expire; { char *cp = (char *)m_rtmsg.m_space; struct rt_msghdr *rtm = &m_rtmsg.m_rtm ; int r = 0; struct strioctl si;
/* initialize message header */ bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); rtm->rtm_flags = 0; rtm->rtm_version = RTM_VERSION; rtm->rtm_addrs |= RTA_GATEWAY|RTA_DST; rtm->rtm_rmx.rmx_expire = expire; rtm->rtm_inits = RTV_EXPIRE; rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); sin->sin_other = 0;
#define NEXTADDR(w, s) \ if (rtm->rtm_addrs & (w)) { \ bcopy((char *)s, cp, sizeof(*s)); cp += sizeof(*s);}
NEXTADDR(RTA_DST, sin); NEXTADDR(RTA_GATEWAY, sdl); rtm->rtm_msglen = cp - (char *)&m_rtmsg ; rtm->rtm_seq = ++seq; rtm->rtm_type = msg;
si.ic_cmd = RTSTR_SEND; si.ic_dp = (char *)&m_rtmsg ; si.ic_len = rtm->rtm_msglen; si.ic_timout = 0;
r = ioctl(s, I_STR, (char *)&si); if (r < 0) return r; return 0; }
To denote an ARP entry as being permanent, the
rt_expire
field in the route structure
should be set to zero. A non-zero value is treated as the
time in seconds that the entry should be considered valid.
An ARP entry may be ``published.'' This will
cause ARP to answer requests for the entry even
if its IP address is not one of the local
addresses of the system. An entry is published by setting
the sin_other
field of the
sockaddr_inarp structure to the value
SIN_PROXY. ARP watches passively for
hosts impersonating the local host (that is, a host that
responds to an ARP mapping request for the local
host's address).
arp: duplicate IP address %x sent from ethernet address:
addressarp: invalid opcode %d
arp: broadcast IP address
arp: info overwritten for %x by %s
RFC 826 (STD 37), RFC 1042 (STD 43)