arp.c revision 93952
160484Sobrien/*
2130561Sobrien * Copyright (c) 1984, 1993
378828Sobrien *	The Regents of the University of California.  All rights reserved.
460484Sobrien *
560484Sobrien * This code is derived from software contributed to Berkeley by
660484Sobrien * Sun Microsystems, Inc.
7130561Sobrien *
860484Sobrien * Redistribution and use in source and binary forms, with or without
9130561Sobrien * modification, are permitted provided that the following conditions
10130561Sobrien * are met:
11130561Sobrien * 1. Redistributions of source code must retain the above copyright
12130561Sobrien *    notice, this list of conditions and the following disclaimer.
1360484Sobrien * 2. Redistributions in binary form must reproduce the above copyright
14130561Sobrien *    notice, this list of conditions and the following disclaimer in the
15130561Sobrien *    documentation and/or other materials provided with the distribution.
16130561Sobrien * 3. All advertising materials mentioning features or use of this software
17130561Sobrien *    must display the following acknowledgement:
1860484Sobrien *	This product includes software developed by the University of
19130561Sobrien *	California, Berkeley and its contributors.
20130561Sobrien * 4. Neither the name of the University nor the names of its contributors
21130561Sobrien *    may be used to endorse or promote products derived from this software
2260484Sobrien *    without specific prior written permission.
2360484Sobrien *
2460484Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2560484Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2660484Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2760484Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2860484Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2960484Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30130561Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3160484Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32104834Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3360484Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3460484Sobrien * SUCH DAMAGE.
3560484Sobrien */
3660484Sobrien
3760484Sobrien#ifndef lint
3860484Sobrienstatic char const copyright[] =
3960484Sobrien"@(#) Copyright (c) 1984, 1993\n\
4060484Sobrien	The Regents of the University of California.  All rights reserved.\n";
4160484Sobrien#endif /* not lint */
4260484Sobrien
4360484Sobrien#ifndef lint
4460484Sobrien#if 0
4560484Sobrienstatic char const sccsid[] = "@(#)from: arp.c	8.2 (Berkeley) 1/2/94";
4660484Sobrien#endif
4760484Sobrienstatic const char rcsid[] =
4860484Sobrien  "$FreeBSD: head/usr.sbin/arp/arp.c 93952 2002-04-06 09:01:44Z ru $";
4960484Sobrien#endif /* not lint */
5060484Sobrien
5160484Sobrien/*
5260484Sobrien * arp - display, set, and delete arp table entries
5360484Sobrien */
5460484Sobrien
5560484Sobrien
5660484Sobrien#include <sys/param.h>
5760484Sobrien#include <sys/file.h>
5860484Sobrien#include <sys/socket.h>
5960484Sobrien#include <sys/sockio.h>
6060484Sobrien#include <sys/sysctl.h>
6160484Sobrien#include <sys/ioctl.h>
6260484Sobrien#include <sys/time.h>
6360484Sobrien
6460484Sobrien#include <net/if.h>
6577298Sobrien#include <net/if_dl.h>
6677298Sobrien#include <net/if_types.h>
6760484Sobrien#include <net/route.h>
6860484Sobrien
6960484Sobrien#include <netinet/in.h>
7060484Sobrien#include <netinet/if_ether.h>
7160484Sobrien
7260484Sobrien#include <arpa/inet.h>
7360484Sobrien
74130561Sobrien#include <err.h>
75130561Sobrien#include <errno.h>
76130561Sobrien#include <netdb.h>
77130561Sobrien#include <nlist.h>
78130561Sobrien#include <paths.h>
7960484Sobrien#include <stdio.h>
8060484Sobrien#include <stdlib.h>
81130561Sobrien#include <string.h>
82130561Sobrien#include <strings.h>
83130561Sobrien#include <unistd.h>
84130561Sobrien
85130561Sobrienvoid search(u_long addr, void (*action)(struct sockaddr_dl *sdl,
86130561Sobrien	struct sockaddr_inarp *sin, struct rt_msghdr *rtm));
87130561Sobrienvoid print_entry(struct sockaddr_dl *sdl,
88130561Sobrien	struct sockaddr_inarp *addr, struct rt_msghdr *rtm);
89130561Sobrienvoid nuke_entry(struct sockaddr_dl *sdl,
90130561Sobrien	struct sockaddr_inarp *addr, struct rt_msghdr *rtm);
9177298Sobrienint delete(char *host, char *info);
9260484Sobrienvoid usage(void);
9360484Sobrienint set(int argc, char **argv);
9460484Sobrienint get(char *host);
9560484Sobrienint file(char *name);
9660484Sobrienvoid getsocket(void);
97130561Sobrienint my_ether_aton(char *a, struct ether_addr *n);
9860484Sobrienint rtmsg(int cmd);
9960484Sobrienint get_ether_addr(u_int32_t ipaddr, struct ether_addr *hwaddr);
10060484Sobrien
10160484Sobrienstatic int pid;
102130561Sobrienstatic int nflag;	/* no reverse dns lookups */
10360484Sobrienstatic int aflag;	/* do it for all entries */
10460484Sobrienstatic int s = -1;
10560484Sobrien
106130561Sobrienstruct	sockaddr_in so_mask;
107130561Sobrienstruct	sockaddr_inarp blank_sin, sin_m;
108130561Sobrienstruct	sockaddr_dl blank_sdl, sdl_m;
109130561Sobrienint	expire_time, flags, doing_proxy, proxy_only, found_entry;
110130561Sobrienstruct	{
111130561Sobrien	struct	rt_msghdr m_rtm;
112130561Sobrien	char	m_space[512];
113130561Sobrien}	m_rtmsg;
114130561Sobrien
115130561Sobrien/* which function we're supposed to do */
116130561Sobrien#define F_GET		1
117130561Sobrien#define F_SET		2
118130561Sobrien#define F_FILESET	3
119130561Sobrien#define F_REPLACE	4
120130561Sobrien#define F_DELETE	5
121130561Sobrien
122130561Sobrien#define ROUNDUP(a) \
123130561Sobrien	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
124130561Sobrien#define SETFUNC(f)	{ if (func) usage(); func = (f); }
12560484Sobrien
126104834Sobrienint
12760484Sobrienmain(int argc, char *argv[])
128104834Sobrien{
12960484Sobrien	int ch, func = 0;
13060484Sobrien	int rtn = 0;
13160484Sobrien
13260484Sobrien	pid = getpid();
13360484Sobrien	while ((ch = getopt(argc, argv, "andfsS")) != -1)
134104834Sobrien		switch((char)ch) {
135104834Sobrien		case 'a':
13660484Sobrien			aflag = 1;
13760484Sobrien			break;
13860484Sobrien		case 'd':
13960484Sobrien			SETFUNC(F_DELETE);
14060484Sobrien			break;
14160484Sobrien		case 'n':
14260484Sobrien			nflag = 1;
143104834Sobrien			break;
144104834Sobrien		case 'S':
145104834Sobrien			SETFUNC(F_REPLACE);
146104834Sobrien			break;
147104834Sobrien		case 's':
14860484Sobrien			SETFUNC(F_SET);
14960484Sobrien			break;
15060484Sobrien		case 'f' :
15160484Sobrien			SETFUNC(F_FILESET);
15260484Sobrien			break;
15360484Sobrien		case '?':
15460484Sobrien		default:
15560484Sobrien			usage();
15660484Sobrien		}
15760484Sobrien	argc -= optind;
15860484Sobrien	argv += optind;
15960484Sobrien
16060484Sobrien	bzero(&so_mask, sizeof(so_mask));
16160484Sobrien	so_mask.sin_len = 8;
162130561Sobrien	so_mask.sin_addr.s_addr = 0xffffffff;
16360484Sobrien	bzero(&blank_sin, sizeof(blank_sin));
16460484Sobrien	blank_sin.sin_len = sizeof(blank_sin);
16560484Sobrien	blank_sin.sin_family = AF_INET;
16660484Sobrien	bzero(&blank_sdl, sizeof(blank_sdl));
16760484Sobrien	blank_sdl.sdl_len = sizeof(blank_sdl);
16860484Sobrien	blank_sdl.sdl_family = AF_LINK;
169130561Sobrien
17060484Sobrien	if (!func)
17160484Sobrien		func = F_GET;
17260484Sobrien	switch (func) {
17360484Sobrien	case F_GET:
17460484Sobrien		if (aflag) {
17560484Sobrien			if (argc != 0)
17660484Sobrien				usage();
177130561Sobrien			search(0, print_entry);
17860484Sobrien		} else {
17960484Sobrien			if (argc != 1)
180130561Sobrien				usage();
18160484Sobrien			get(argv[0]);
18260484Sobrien		}
18360484Sobrien		break;
18460484Sobrien	case F_SET:
18560484Sobrien	case F_REPLACE:
18660484Sobrien		if (argc < 2 || argc > 6)
18760484Sobrien			usage();
18860484Sobrien		if (func == F_REPLACE)
189130561Sobrien			(void) delete(argv[0], NULL);
190130561Sobrien		rtn = set(argc, argv) ? 1 : 0;
191130561Sobrien		break;
192130561Sobrien	case F_DELETE:
193130561Sobrien		if (aflag) {
194130561Sobrien			if (argc != 0)
195130561Sobrien				usage();
196130561Sobrien			search(0, nuke_entry);
197130561Sobrien		} else {
198130561Sobrien			if (argc < 1 || argc > 2)
199130561Sobrien				usage();
200130561Sobrien			rtn = delete(argv[0], argv[1]);
201130561Sobrien		}
202130561Sobrien		break;
203130561Sobrien	case F_FILESET:
204130561Sobrien		if (argc != 1)
205130561Sobrien			usage();
206130561Sobrien		rtn = file(argv[0]);
20760484Sobrien		break;
20860484Sobrien	}
209104834Sobrien
21060484Sobrien	return(rtn);
21160484Sobrien}
212130561Sobrien
213130561Sobrien/*
214130561Sobrien * Process a file to set standard arp entries
21560484Sobrien */
216130561Sobrienint
217130561Sobrienfile(char *name)
218130561Sobrien{
219130561Sobrien	FILE *fp;
22060484Sobrien	int i, retval;
22160484Sobrien	char line[100], arg[5][50], *args[5];
22260484Sobrien
223130561Sobrien	if ((fp = fopen(name, "r")) == NULL)
224130561Sobrien		errx(1, "cannot open %s", name);
225130561Sobrien	args[0] = &arg[0][0];
226130561Sobrien	args[1] = &arg[1][0];
227130561Sobrien	args[2] = &arg[2][0];
22860484Sobrien	args[3] = &arg[3][0];
22960484Sobrien	args[4] = &arg[4][0];
23060484Sobrien	retval = 0;
231130561Sobrien	while(fgets(line, 100, fp) != NULL) {
23260484Sobrien		i = sscanf(line, "%49s %49s %49s %49s %49s", arg[0], arg[1],
23360484Sobrien		    arg[2], arg[3], arg[4]);
23460484Sobrien		if (i < 2) {
23560484Sobrien			warnx("bad line: %s", line);
23660484Sobrien			retval = 1;
23760484Sobrien			continue;
23860484Sobrien		}
23960484Sobrien		if (set(i, args))
24060484Sobrien			retval = 1;
24160484Sobrien	}
24260484Sobrien	fclose(fp);
24360484Sobrien	return (retval);
24460484Sobrien}
24560484Sobrien
24660484Sobrienvoid
247130561Sobriengetsocket(void)
24860484Sobrien{
249130561Sobrien	if (s < 0) {
25060484Sobrien		s = socket(PF_ROUTE, SOCK_RAW, 0);
25160484Sobrien		if (s < 0)
25260484Sobrien			err(1, "socket");
25360484Sobrien	}
254130561Sobrien}
255104834Sobrien
25680016Sobrien/*
25760484Sobrien * Set an individual arp entry
25860484Sobrien */
25960484Sobrienint
26060484Sobrienset(int argc, char **argv)
26160484Sobrien{
26260484Sobrien	struct hostent *hp;
26360484Sobrien	register struct sockaddr_inarp *addr = &sin_m;
26460484Sobrien	register struct sockaddr_dl *sdl;
26560484Sobrien	register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
26660484Sobrien	struct ether_addr *ea;
26760484Sobrien	char *host = argv[0], *eaddr = argv[1];
26860484Sobrien
26960484Sobrien	getsocket();
27080016Sobrien	argc -= 2;
27160484Sobrien	argv += 2;
272104834Sobrien	sdl_m = blank_sdl;
273104834Sobrien	sin_m = blank_sin;
27460484Sobrien	addr->sin_addr.s_addr = inet_addr(host);
275130561Sobrien	if (addr->sin_addr.s_addr == INADDR_NONE) {
27660484Sobrien		if (!(hp = gethostbyname(host))) {
27760484Sobrien			warnx("%s: %s", host, hstrerror(h_errno));
27860484Sobrien			return (1);
27960484Sobrien		}
28060484Sobrien		bcopy((char *)hp->h_addr, (char *)&addr->sin_addr,
281130561Sobrien		    sizeof addr->sin_addr);
28260484Sobrien	}
28360484Sobrien	doing_proxy = flags = proxy_only = expire_time = 0;
28460484Sobrien	while (argc-- > 0) {
28560484Sobrien		if (strncmp(argv[0], "temp", 4) == 0) {
28660484Sobrien			struct timeval tv;
28760484Sobrien			gettimeofday(&tv, 0);
28860484Sobrien			expire_time = tv.tv_sec + 20 * 60;
289130561Sobrien		}
29060484Sobrien		else if (strncmp(argv[0], "pub", 3) == 0) {
29160484Sobrien			flags |= RTF_ANNOUNCE;
29260484Sobrien			doing_proxy = 1;
29360484Sobrien			if (argc && strncmp(argv[1], "only", 3) == 0) {
29460484Sobrien				proxy_only = 1;
29560484Sobrien				sin_m.sin_other = SIN_PROXY;
29660484Sobrien				argc--; argv++;
29760484Sobrien			}
29860484Sobrien		} else if (strncmp(argv[0], "trail", 5) == 0) {
29960484Sobrien			printf("%s: Sending trailers is no longer supported\n",
30060484Sobrien				host);
301130561Sobrien		}
30260484Sobrien		argv++;
30360484Sobrien	}
30460484Sobrien	ea = (struct ether_addr *)LLADDR(&sdl_m);
30560484Sobrien	if (doing_proxy && !strcmp(eaddr, "auto")) {
30660484Sobrien		if (!get_ether_addr(addr->sin_addr.s_addr, ea)) {
30760484Sobrien			printf("no interface found for %s\n",
30860484Sobrien			       inet_ntoa(addr->sin_addr));
30960484Sobrien			return (1);
310130561Sobrien		}
31160484Sobrien		sdl_m.sdl_alen = ETHER_ADDR_LEN;
31260484Sobrien	} else {
313130561Sobrien		if (my_ether_aton(eaddr, ea) == 0)
31460484Sobrien			sdl_m.sdl_alen = ETHER_ADDR_LEN;
31560484Sobrien	}
31660484Sobrientryagain:
31760484Sobrien	if (rtmsg(RTM_GET) < 0) {
31860484Sobrien		warn("%s", host);
319130561Sobrien		return (1);
32060484Sobrien	}
32160484Sobrien	addr = (struct sockaddr_inarp *)(rtm + 1);
32260484Sobrien	sdl = (struct sockaddr_dl *)(ROUNDUP(addr->sin_len) + (char *)addr);
32360484Sobrien	if (addr->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
32460484Sobrien		if (sdl->sdl_family == AF_LINK &&
32560484Sobrien		    (rtm->rtm_flags & RTF_LLINFO) &&
32660484Sobrien		    !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
32760484Sobrien		case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
32860484Sobrien		case IFT_ISO88024: case IFT_ISO88025: case IFT_L2VLAN:
32960484Sobrien			goto overwrite;
33060484Sobrien		}
33160484Sobrien		if (doing_proxy == 0) {
332130561Sobrien			printf("set: can only proxy for %s\n", host);
33360484Sobrien			return (1);
33460484Sobrien		}
335130561Sobrien		if (sin_m.sin_other & SIN_PROXY) {
33680016Sobrien			printf("set: proxy entry exists for non 802 device\n");
337130561Sobrien			return(1);
33860484Sobrien		}
33960484Sobrien		sin_m.sin_other = SIN_PROXY;
34060484Sobrien		proxy_only = 1;
34160484Sobrien		goto tryagain;
34260484Sobrien	}
343130561Sobrienoverwrite:
34460484Sobrien	if (sdl->sdl_family != AF_LINK) {
34560484Sobrien		printf("cannot intuit interface index and type for %s\n", host);
34660484Sobrien		return (1);
34760484Sobrien	}
34860484Sobrien	sdl_m.sdl_type = sdl->sdl_type;
34960484Sobrien	sdl_m.sdl_index = sdl->sdl_index;
35060484Sobrien	return (rtmsg(RTM_ADD));
35160484Sobrien}
35260484Sobrien
353130561Sobrien/*
35460484Sobrien * Display an individual arp entry
35560484Sobrien */
35660484Sobrienint
35760484Sobrienget(char *host)
35860484Sobrien{
35960484Sobrien	struct hostent *hp;
36060484Sobrien	struct sockaddr_inarp *addr = &sin_m;
36160484Sobrien
36260484Sobrien	sin_m = blank_sin;
36360484Sobrien	addr->sin_addr.s_addr = inet_addr(host);
36460484Sobrien	if (addr->sin_addr.s_addr == INADDR_NONE) {
36560484Sobrien		if (!(hp = gethostbyname(host)))
366130561Sobrien			errx(1, "%s: %s", host, hstrerror(h_errno));
36760484Sobrien		bcopy((char *)hp->h_addr, (char *)&addr->sin_addr,
36860484Sobrien		    sizeof addr->sin_addr);
36960484Sobrien	}
37060484Sobrien	search(addr->sin_addr.s_addr, print_entry);
37160484Sobrien	if (found_entry == 0) {
37260484Sobrien		printf("%s (%s) -- no entry\n",
37360484Sobrien		    host, inet_ntoa(addr->sin_addr));
37460484Sobrien		return(1);
37560484Sobrien	}
37660484Sobrien	return(0);
377130561Sobrien}
37860484Sobrien
37960484Sobrien/*
380130561Sobrien * Delete an arp entry
38160484Sobrien */
38260484Sobrienint
38360484Sobriendelete(char *host, char *info)
38460484Sobrien{
38560484Sobrien	struct hostent *hp;
38660484Sobrien	register struct sockaddr_inarp *addr = &sin_m;
38760484Sobrien	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
38860484Sobrien	struct sockaddr_dl *sdl;
38960484Sobrien
39060484Sobrien	getsocket();
39160484Sobrien	sin_m = blank_sin;
39260484Sobrien	if (info) {
39360484Sobrien		if (strncmp(info, "pub", 3) == 0)
394130561Sobrien			sin_m.sin_other = SIN_PROXY;
39560484Sobrien		else
39660484Sobrien			usage();
39760484Sobrien	}
39860484Sobrien	addr->sin_addr.s_addr = inet_addr(host);
39960484Sobrien	if (addr->sin_addr.s_addr == INADDR_NONE) {
40060484Sobrien		if (!(hp = gethostbyname(host))) {
40160484Sobrien			warnx("%s: %s", host, hstrerror(h_errno));
40260484Sobrien			return (1);
40360484Sobrien		}
40460484Sobrien		bcopy((char *)hp->h_addr, (char *)&addr->sin_addr,
40560484Sobrien		    sizeof addr->sin_addr);
40660484Sobrien	}
40760484Sobrientryagain:
40860484Sobrien	if (rtmsg(RTM_GET) < 0) {
40960484Sobrien		warn("%s", host);
41060484Sobrien		return (1);
41160484Sobrien	}
41260484Sobrien	addr = (struct sockaddr_inarp *)(rtm + 1);
41360484Sobrien	sdl = (struct sockaddr_dl *)(ROUNDUP(addr->sin_len) + (char *)addr);
41460484Sobrien	if (addr->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
41560484Sobrien		if (sdl->sdl_family == AF_LINK &&
41660484Sobrien		    (rtm->rtm_flags & RTF_LLINFO) &&
41760484Sobrien		    !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
41860484Sobrien		case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
41960484Sobrien		case IFT_ISO88024: case IFT_ISO88025: case IFT_L2VLAN:
42060484Sobrien			goto delete;
42160484Sobrien		}
42260484Sobrien	}
42360484Sobrien	if (sin_m.sin_other & SIN_PROXY) {
42460484Sobrien		fprintf(stderr, "delete: can't locate %s\n",host);
42560484Sobrien		return (1);
42660484Sobrien	} else {
42760484Sobrien		sin_m.sin_other = SIN_PROXY;
42860484Sobrien		goto tryagain;
42960484Sobrien	}
43060484Sobriendelete:
43160484Sobrien	if (sdl->sdl_family != AF_LINK) {
43260484Sobrien		printf("cannot locate %s\n", host);
43360484Sobrien		return (1);
43460484Sobrien	}
43560484Sobrien	if (rtmsg(RTM_DELETE) == 0) {
43660484Sobrien		printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr));
43760484Sobrien		return (0);
43860484Sobrien	}
43960484Sobrien	return (1);
44060484Sobrien}
44160484Sobrien
44260484Sobrien/*
44360484Sobrien * Search the arp table and do some action on matching entries
44460484Sobrien */
44560484Sobrienvoid
44660484Sobriensearch(u_long addr, void (*action)(struct sockaddr_dl *sdl,
44760484Sobrien	struct sockaddr_inarp *sin, struct rt_msghdr *rtm))
448130561Sobrien{
449130561Sobrien	int mib[6];
45060484Sobrien	size_t needed;
45160484Sobrien	char *lim, *buf, *next;
452130561Sobrien	struct rt_msghdr *rtm;
45360484Sobrien	struct sockaddr_inarp *sin2;
454130561Sobrien	struct sockaddr_dl *sdl;
45560484Sobrien
45660484Sobrien	mib[0] = CTL_NET;
45760484Sobrien	mib[1] = PF_ROUTE;
45860484Sobrien	mib[2] = 0;
45960484Sobrien	mib[3] = AF_INET;
46060484Sobrien	mib[4] = NET_RT_FLAGS;
46160484Sobrien	mib[5] = RTF_LLINFO;
46260484Sobrien	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
46360484Sobrien		errx(1, "route-sysctl-estimate");
464130561Sobrien	if ((buf = malloc(needed)) == NULL)
465130561Sobrien		errx(1, "malloc");
466130561Sobrien	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
467130561Sobrien		errx(1, "actual retrieval of routing table");
468130561Sobrien	lim = buf + needed;
469130561Sobrien	for (next = buf; next < lim; next += rtm->rtm_msglen) {
470130561Sobrien		rtm = (struct rt_msghdr *)next;
471130561Sobrien		sin2 = (struct sockaddr_inarp *)(rtm + 1);
472130561Sobrien		(char *)sdl = (char *)sin2 + ROUNDUP(sin2->sin_len);
473130561Sobrien		if (addr) {
47460484Sobrien			if (addr != sin2->sin_addr.s_addr)
475130561Sobrien				continue;
47660484Sobrien			found_entry = 1;
47760484Sobrien		}
47860484Sobrien		(*action)(sdl, sin2, rtm);
47977298Sobrien	}
48077298Sobrien	free(buf);
48177298Sobrien}
48277298Sobrien
48377298Sobrien/*
484130561Sobrien * Display an arp entry
48577298Sobrien */
48677298Sobrienvoid
48777298Sobrienprint_entry(struct sockaddr_dl *sdl,
48877298Sobrien	struct sockaddr_inarp *addr, struct rt_msghdr *rtm)
48977298Sobrien{
49077298Sobrien	const char *host;
49177298Sobrien	struct hostent *hp;
49277298Sobrien	char ifname[IF_NAMESIZE];
493130561Sobrien	int seg;
49477298Sobrien
49577298Sobrien	if (nflag == 0)
49677298Sobrien		hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
49777298Sobrien		    sizeof addr->sin_addr, AF_INET);
49877298Sobrien	else
49977298Sobrien		hp = 0;
50077298Sobrien	if (hp)
50177298Sobrien		host = hp->h_name;
50289857Sobrien	else {
50389857Sobrien		host = "?";
50489857Sobrien		if (h_errno == TRY_AGAIN)
50589857Sobrien			nflag = 1;
50689857Sobrien	}
50789857Sobrien	printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr));
50889857Sobrien	if (sdl->sdl_alen)
50989857Sobrien		printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl)));
51089857Sobrien	else
51189857Sobrien		printf("(incomplete)");
51289857Sobrien	if (if_indextoname(sdl->sdl_index, ifname) != NULL)
51389857Sobrien		printf(" on %s", ifname);
51489857Sobrien	if (rtm->rtm_rmx.rmx_expire == 0)
51589857Sobrien		printf(" permanent");
51689857Sobrien	if (addr->sin_other & SIN_PROXY)
51789857Sobrien		printf(" published (proxy only)");
51889857Sobrien	if (rtm->rtm_addrs & RTA_NETMASK) {
51989857Sobrien		addr = (struct sockaddr_inarp *)
52089857Sobrien			(ROUNDUP(sdl->sdl_len) + (char *)sdl);
52160484Sobrien		if (addr->sin_addr.s_addr == 0xffffffff)
52277298Sobrien			printf(" published");
52377298Sobrien		if (addr->sin_len != 8)
52477298Sobrien			printf("(weird)");
52577298Sobrien	}
52677298Sobrien        switch(sdl->sdl_type) {
52777298Sobrien            case IFT_ETHER:
52877298Sobrien                printf(" [ethernet]");
52977298Sobrien                break;
53077298Sobrien            case IFT_ISO88025:
53160484Sobrien                printf(" [token-ring]");
53260484Sobrien                break;
53360484Sobrien            case IFT_FDDI:
53460484Sobrien                printf(" [fddi]");
53560484Sobrien                break;
53660484Sobrien            case IFT_ATM:
53760484Sobrien                printf(" [atm]");
53860484Sobrien                break;
53960484Sobrien	    case IFT_L2VLAN:
54060484Sobrien		printf(" [vlan]");
54160484Sobrien		break;
54260484Sobrien            default:
54360484Sobrien        }
54460484Sobrien	if (sdl->sdl_rcf != NULL) {
54560484Sobrien		printf(" rt=%x", ntohs(sdl->sdl_rcf));
54660484Sobrien		for (seg = 0; seg < ((((ntohs(sdl->sdl_rcf) & 0x1f00) >> 8) - 2 ) / 2); seg++)
54760484Sobrien			printf(":%x", ntohs(sdl->sdl_route[seg]));
54860484Sobrien	}
549130561Sobrien
55060484Sobrien	printf("\n");
55160484Sobrien
55260484Sobrien}
55360484Sobrien
55460484Sobrien/*
55560484Sobrien * Nuke an arp entry
55660484Sobrien */
55760484Sobrienvoid
55860484Sobriennuke_entry(struct sockaddr_dl *sdl __unused,
55960484Sobrien	struct sockaddr_inarp *addr, struct rt_msghdr *rtm __unused)
56060484Sobrien{
56160484Sobrien	char ip[20];
56260484Sobrien
56360484Sobrien	snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
56460484Sobrien	delete(ip, NULL);
56560484Sobrien}
56660484Sobrien
56760484Sobrienint
56860484Sobrienmy_ether_aton(char *a, struct ether_addr *n)
56960484Sobrien{
57060484Sobrien	struct ether_addr *ea;
57160484Sobrien
57260484Sobrien	if ((ea = ether_aton(a)) == NULL) {
57360484Sobrien		warnx("invalid Ethernet address '%s'", a);
57460484Sobrien		return (1);
57560484Sobrien	}
57660484Sobrien	*n = *ea;
57760484Sobrien	return (0);
57860484Sobrien}
57960484Sobrien
58060484Sobrienvoid
58160484Sobrienusage(void)
58260484Sobrien{
58360484Sobrien	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
58460484Sobrien		"usage: arp [-n] hostname",
58560484Sobrien		"       arp [-n] -a",
58660484Sobrien		"       arp -d hostname [pub]",
58760484Sobrien		"       arp -d -a",
58860484Sobrien		"       arp -s hostname ether_addr [temp] [pub]",
58960484Sobrien		"       arp -S hostname ether_addr [temp] [pub]",
59060484Sobrien		"       arp -f filename");
59160484Sobrien	exit(1);
59260484Sobrien}
59360484Sobrien
59460484Sobrienint
59560484Sobrienrtmsg(int cmd)
59660484Sobrien{
59760484Sobrien	static int seq;
59860484Sobrien	int rlen;
59960484Sobrien	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
60060484Sobrien	register char *cp = m_rtmsg.m_space;
601130561Sobrien	register int l;
60260484Sobrien
60360484Sobrien	errno = 0;
60460484Sobrien	if (cmd == RTM_DELETE)
60560484Sobrien		goto doit;
60660484Sobrien	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
60760484Sobrien	rtm->rtm_flags = flags;
60860484Sobrien	rtm->rtm_version = RTM_VERSION;
60960484Sobrien
61060484Sobrien	switch (cmd) {
61160484Sobrien	default:
61260484Sobrien		errx(1, "internal wrong cmd");
613130561Sobrien	case RTM_ADD:
61460484Sobrien		rtm->rtm_addrs |= RTA_GATEWAY;
61560484Sobrien		rtm->rtm_rmx.rmx_expire = expire_time;
616130561Sobrien		rtm->rtm_inits = RTV_EXPIRE;
61760484Sobrien		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
61860484Sobrien		sin_m.sin_other = 0;
619130561Sobrien		if (doing_proxy) {
62060484Sobrien			if (proxy_only)
62160484Sobrien				sin_m.sin_other = SIN_PROXY;
62260484Sobrien			else {
62360484Sobrien				rtm->rtm_addrs |= RTA_NETMASK;
62460484Sobrien				rtm->rtm_flags &= ~RTF_HOST;
625130561Sobrien			}
62660484Sobrien		}
62760484Sobrien		/* FALLTHROUGH */
628130561Sobrien	case RTM_GET:
62960484Sobrien		rtm->rtm_addrs |= RTA_DST;
63060484Sobrien	}
63160484Sobrien#define NEXTADDR(w, s) \
63260484Sobrien	if (rtm->rtm_addrs & (w)) { \
63360484Sobrien		bcopy((char *)&s, cp, sizeof(s)); cp += ROUNDUP(sizeof(s));}
63460484Sobrien
635130561Sobrien	NEXTADDR(RTA_DST, sin_m);
636130561Sobrien	NEXTADDR(RTA_GATEWAY, sdl_m);
637130561Sobrien	NEXTADDR(RTA_NETMASK, so_mask);
638130561Sobrien
639130561Sobrien	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
640130561Sobriendoit:
641130561Sobrien	l = rtm->rtm_msglen;
642130561Sobrien	rtm->rtm_seq = ++seq;
643130561Sobrien	rtm->rtm_type = cmd;
644130561Sobrien	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
64560484Sobrien		if (errno != ESRCH || cmd != RTM_DELETE) {
64660484Sobrien			warn("writing to routing socket");
64760484Sobrien			return (-1);
64860484Sobrien		}
649130561Sobrien	}
65060484Sobrien	do {
65160484Sobrien		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
65260484Sobrien	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
65360484Sobrien	if (l < 0)
65460484Sobrien		warn("read from routing socket");
655130561Sobrien	return (0);
65660484Sobrien}
65760484Sobrien
658130561Sobrien/*
65960484Sobrien * get_ether_addr - get the hardware address of an interface on the
660130561Sobrien * the same subnet as ipaddr.
66160484Sobrien */
66260484Sobrien#define MAX_IFS		32
66360484Sobrien
66460484Sobrienint
66560484Sobrienget_ether_addr(u_int32_t ipaddr, struct ether_addr *hwaddr)
66660484Sobrien{
66760484Sobrien	struct ifreq *ifr, *ifend, *ifp;
66860484Sobrien	u_int32_t ina, mask;
66960484Sobrien	struct sockaddr_dl *dla;
67060484Sobrien	struct ifreq ifreq;
67160484Sobrien	struct ifconf ifc;
672130561Sobrien	struct ifreq ifs[MAX_IFS];
67360484Sobrien	int sock;
67460484Sobrien
675130561Sobrien	sock = socket(AF_INET, SOCK_DGRAM, 0);
67660484Sobrien	if (sock < 0)
67760484Sobrien		err(1, "socket");
67860484Sobrien
67960484Sobrien	ifc.ifc_len = sizeof(ifs);
68060484Sobrien	ifc.ifc_req = ifs;
68160484Sobrien	if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
682130561Sobrien		warnx("ioctl(SIOCGIFCONF)");
68360484Sobrien		close(sock);
68460484Sobrien		return 0;
685130561Sobrien	}
68660484Sobrien
68760484Sobrien	/*
68860484Sobrien	* Scan through looking for an interface with an Internet
68960484Sobrien	* address on the same subnet as `ipaddr'.
69060484Sobrien	*/
69160484Sobrien	ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
69260484Sobrien	for (ifr = ifc.ifc_req; ifr < ifend; ) {
693130561Sobrien		if (ifr->ifr_addr.sa_family == AF_INET) {
694130561Sobrien			ina = ((struct sockaddr_in *)
695130561Sobrien				&ifr->ifr_addr)->sin_addr.s_addr;
696130561Sobrien			strncpy(ifreq.ifr_name, ifr->ifr_name,
697130561Sobrien				sizeof(ifreq.ifr_name));
698130561Sobrien			/*
699130561Sobrien			 * Check that the interface is up,
700130561Sobrien			 * and not point-to-point or loopback.
701130561Sobrien			 */
702130561Sobrien			if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0)
703130561Sobrien				continue;
704130561Sobrien			if ((ifreq.ifr_flags &
705130561Sobrien			     (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
706130561Sobrien					IFF_LOOPBACK|IFF_NOARP))
707130561Sobrien			     != (IFF_UP|IFF_BROADCAST))
708130561Sobrien				goto nextif;
709130561Sobrien			/*
710130561Sobrien			 * Get its netmask and check that it's on
711130561Sobrien			 * the right subnet.
712130561Sobrien			 */
713130561Sobrien			if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0)
714130561Sobrien				continue;
715130561Sobrien			mask = ((struct sockaddr_in *)
716130561Sobrien				&ifreq.ifr_addr)->sin_addr.s_addr;
717130561Sobrien			if ((ipaddr & mask) != (ina & mask))
718130561Sobrien				goto nextif;
719130561Sobrien			break;
720130561Sobrien		}
721130561Sobriennextif:
722130561Sobrien		ifr = (struct ifreq *) ((char *)&ifr->ifr_addr
723130561Sobrien		    + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)));
724130561Sobrien	}
725130561Sobrien
726130561Sobrien	if (ifr >= ifend) {
727130561Sobrien		close(sock);
728130561Sobrien		return 0;
729130561Sobrien	}
730130561Sobrien
731130561Sobrien	/*
732130561Sobrien	* Now scan through again looking for a link-level address
733130561Sobrien	* for this interface.
734130561Sobrien	*/
735130561Sobrien	ifp = ifr;
736130561Sobrien	for (ifr = ifc.ifc_req; ifr < ifend; ) {
737130561Sobrien		if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
738130561Sobrien		    && ifr->ifr_addr.sa_family == AF_LINK) {
739130561Sobrien			/*
740130561Sobrien			 * Found the link-level address - copy it out
741130561Sobrien			 */
742130561Sobrien		 	dla = (struct sockaddr_dl *) &ifr->ifr_addr;
743130561Sobrien			memcpy(hwaddr,  LLADDR(dla), dla->sdl_alen);
744130561Sobrien			close (sock);
745130561Sobrien			printf("using interface %s for proxy with address ",
746130561Sobrien				ifp->ifr_name);
747130561Sobrien			printf("%s\n", ether_ntoa(hwaddr));
748130561Sobrien			return dla->sdl_alen;
749130561Sobrien		}
75060484Sobrien		ifr = (struct ifreq *) ((char *)&ifr->ifr_addr
75160484Sobrien		    + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)));
75260484Sobrien	}
75360484Sobrien	return 0;
75489857Sobrien}
75589857Sobrien