arp.c revision 147170
11553Srgrimes/*
21553Srgrimes * Copyright (c) 1984, 1993
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes * This code is derived from software contributed to Berkeley by
61553Srgrimes * Sun Microsystems, Inc.
71553Srgrimes *
81553Srgrimes * Redistribution and use in source and binary forms, with or without
91553Srgrimes * modification, are permitted provided that the following conditions
101553Srgrimes * are met:
111553Srgrimes * 1. Redistributions of source code must retain the above copyright
121553Srgrimes *    notice, this list of conditions and the following disclaimer.
131553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141553Srgrimes *    notice, this list of conditions and the following disclaimer in the
151553Srgrimes *    documentation and/or other materials provided with the distribution.
161553Srgrimes * 4. Neither the name of the University nor the names of its contributors
171553Srgrimes *    may be used to endorse or promote products derived from this software
181553Srgrimes *    without specific prior written permission.
191553Srgrimes *
201553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301553Srgrimes * SUCH DAMAGE.
311553Srgrimes */
321553Srgrimes
33114601Sobrien#if 0
341553Srgrimes#ifndef lint
3513977Sphkstatic char const copyright[] =
361553Srgrimes"@(#) Copyright (c) 1984, 1993\n\
371553Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381553Srgrimes#endif /* not lint */
391553Srgrimes
401553Srgrimes#ifndef lint
4113977Sphkstatic char const sccsid[] = "@(#)from: arp.c	8.2 (Berkeley) 1/2/94";
42114601Sobrien#endif /* not lint */
4329060Scharnier#endif
44114601Sobrien#include <sys/cdefs.h>
45114601Sobrien__FBSDID("$FreeBSD: head/usr.sbin/arp/arp.c 147170 2005-06-09 15:00:31Z glebius $");
461553Srgrimes
471553Srgrimes/*
481553Srgrimes * arp - display, set, and delete arp table entries
491553Srgrimes */
501553Srgrimes
511553Srgrimes
521553Srgrimes#include <sys/param.h>
531553Srgrimes#include <sys/file.h>
541553Srgrimes#include <sys/socket.h>
5513977Sphk#include <sys/sockio.h>
561553Srgrimes#include <sys/sysctl.h>
5713977Sphk#include <sys/ioctl.h>
5820287Swollman#include <sys/time.h>
591553Srgrimes
601553Srgrimes#include <net/if.h>
611553Srgrimes#include <net/if_dl.h>
621553Srgrimes#include <net/if_types.h>
6396235Swpaul#include <net/route.h>
6496202Skbyanc#include <net/iso88025.h>
651553Srgrimes
661553Srgrimes#include <netinet/in.h>
671553Srgrimes#include <netinet/if_ether.h>
681553Srgrimes
691553Srgrimes#include <arpa/inet.h>
701553Srgrimes
71108314Sru#include <ctype.h>
7229060Scharnier#include <err.h>
7329060Scharnier#include <errno.h>
741553Srgrimes#include <netdb.h>
751553Srgrimes#include <nlist.h>
7629060Scharnier#include <paths.h>
771553Srgrimes#include <stdio.h>
7813977Sphk#include <stdlib.h>
7993590Smike#include <string.h>
8029060Scharnier#include <strings.h>
8113977Sphk#include <unistd.h>
821553Srgrimes
83128181Sluigitypedef void (action_fn)(struct sockaddr_dl *sdl,
84128192Sluigi	struct sockaddr_inarp *s_in, struct rt_msghdr *rtm);
8513977Sphk
86128181Sluigistatic int search(u_long addr, action_fn *action);
87128181Sluigistatic action_fn print_entry;
88128181Sluigistatic action_fn nuke_entry;
89128181Sluigi
90128192Sluigistatic int delete(char *host, int do_proxy);
91128181Sluigistatic void usage(void);
92128181Sluigistatic int set(int argc, char **argv);
93128192Sluigistatic void get(char *host);
94128181Sluigistatic int file(char *name);
95128192Sluigistatic struct rt_msghdr *rtmsg(int cmd,
96128192Sluigi	struct sockaddr_inarp *dst, struct sockaddr_dl *sdl);
97128181Sluigistatic int get_ether_addr(u_int32_t ipaddr, struct ether_addr *hwaddr);
98128181Sluigi
9931145Sjulianstatic int nflag;	/* no reverse dns lookups */
100117729Syarstatic char *rifname;
1011553Srgrimes
102128181Sluigistatic int	expire_time, flags, doing_proxy, proxy_only;
103128192Sluigi
10431145Sjulian/* which function we're supposed to do */
10531145Sjulian#define F_GET		1
10631145Sjulian#define F_SET		2
10731145Sjulian#define F_FILESET	3
10831145Sjulian#define F_REPLACE	4
10931145Sjulian#define F_DELETE	5
11031145Sjulian
11131145Sjulian#define SETFUNC(f)	{ if (func) usage(); func = (f); }
11231145Sjulian
11313977Sphkint
11477870Srumain(int argc, char *argv[])
1151553Srgrimes{
11631145Sjulian	int ch, func = 0;
11731145Sjulian	int rtn = 0;
118128181Sluigi	int aflag = 0;	/* do it for all entries */
1191553Srgrimes
120117729Syar	while ((ch = getopt(argc, argv, "andfsSi:")) != -1)
1211553Srgrimes		switch((char)ch) {
1221553Srgrimes		case 'a':
12331145Sjulian			aflag = 1;
12431145Sjulian			break;
1251553Srgrimes		case 'd':
12631145Sjulian			SETFUNC(F_DELETE);
12731145Sjulian			break;
1281553Srgrimes		case 'n':
1291553Srgrimes			nflag = 1;
13031145Sjulian			break;
13113977Sphk		case 'S':
13231145Sjulian			SETFUNC(F_REPLACE);
13331145Sjulian			break;
1341553Srgrimes		case 's':
13531145Sjulian			SETFUNC(F_SET);
13631145Sjulian			break;
1379874Sjkh		case 'f' :
13831145Sjulian			SETFUNC(F_FILESET);
13931145Sjulian			break;
140117729Syar		case 'i':
141117729Syar			rifname = optarg;
142117729Syar			break;
1431553Srgrimes		case '?':
1441553Srgrimes		default:
1451553Srgrimes			usage();
1461553Srgrimes		}
14731145Sjulian	argc -= optind;
14831145Sjulian	argv += optind;
14931145Sjulian
15031145Sjulian	if (!func)
15131145Sjulian		func = F_GET;
152117729Syar	if (rifname) {
153117729Syar		if (func != F_GET)
154117729Syar			errx(1, "-i not applicable to this operation");
155117729Syar		if (if_nametoindex(rifname) == 0) {
156117729Syar			if (errno == ENXIO)
157117729Syar				errx(1, "interface %s does not exist", rifname);
158117729Syar			else
159117729Syar				err(1, "if_nametoindex(%s)", rifname);
160117729Syar		}
161117729Syar	}
16231145Sjulian	switch (func) {
16331145Sjulian	case F_GET:
16431145Sjulian		if (aflag) {
16531145Sjulian			if (argc != 0)
16631145Sjulian				usage();
16731145Sjulian			search(0, print_entry);
16831145Sjulian		} else {
16931145Sjulian			if (argc != 1)
17031145Sjulian				usage();
17131145Sjulian			get(argv[0]);
17231145Sjulian		}
17331145Sjulian		break;
17431145Sjulian	case F_SET:
17531145Sjulian	case F_REPLACE:
17677870Sru		if (argc < 2 || argc > 6)
17731145Sjulian			usage();
17831145Sjulian		if (func == F_REPLACE)
179128192Sluigi			delete(argv[0], 0);
18031145Sjulian		rtn = set(argc, argv) ? 1 : 0;
18131145Sjulian		break;
18231145Sjulian	case F_DELETE:
18331145Sjulian		if (aflag) {
18431145Sjulian			if (argc != 0)
18531145Sjulian				usage();
18631145Sjulian			search(0, nuke_entry);
18731145Sjulian		} else {
188128192Sluigi			if (argc == 2 && strncmp(argv[1], "pub", 3) == 0)
189128192Sluigi				ch = SIN_PROXY;
190128192Sluigi			else if (argc == 1)
191128192Sluigi				ch = 0;
192128192Sluigi			else
19331145Sjulian				usage();
194128192Sluigi			rtn = delete(argv[0], ch);
19531145Sjulian		}
19631145Sjulian		break;
19731145Sjulian	case F_FILESET:
19831145Sjulian		if (argc != 1)
19931145Sjulian			usage();
20031145Sjulian		rtn = file(argv[0]);
20131145Sjulian		break;
20231145Sjulian	}
20331145Sjulian
20431145Sjulian	return(rtn);
2051553Srgrimes}
2061553Srgrimes
2071553Srgrimes/*
2081553Srgrimes * Process a file to set standard arp entries
2091553Srgrimes */
210128181Sluigistatic int
21113977Sphkfile(char *name)
2121553Srgrimes{
2131553Srgrimes	FILE *fp;
2141553Srgrimes	int i, retval;
215109413Sru	char line[100], arg[5][50], *args[5], *p;
2161553Srgrimes
21729060Scharnier	if ((fp = fopen(name, "r")) == NULL)
218128054Smux		err(1, "cannot open %s", name);
2191553Srgrimes	args[0] = &arg[0][0];
2201553Srgrimes	args[1] = &arg[1][0];
2211553Srgrimes	args[2] = &arg[2][0];
2221553Srgrimes	args[3] = &arg[3][0];
2231553Srgrimes	args[4] = &arg[4][0];
2241553Srgrimes	retval = 0;
2251553Srgrimes	while(fgets(line, 100, fp) != NULL) {
226109413Sru		if ((p = strchr(line, '#')) != NULL)
227109413Sru			*p = '\0';
228109413Sru		for (p = line; isblank(*p); p++);
229111910Sru		if (*p == '\n' || *p == '\0')
230108314Sru			continue;
231109413Sru		i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1],
23256134Sjkh		    arg[2], arg[3], arg[4]);
2331553Srgrimes		if (i < 2) {
23429060Scharnier			warnx("bad line: %s", line);
2351553Srgrimes			retval = 1;
2361553Srgrimes			continue;
2371553Srgrimes		}
2381553Srgrimes		if (set(i, args))
2391553Srgrimes			retval = 1;
2401553Srgrimes	}
2411553Srgrimes	fclose(fp);
2421553Srgrimes	return (retval);
2431553Srgrimes}
2441553Srgrimes
2451553Srgrimes/*
246128192Sluigi * Given a hostname, fills up a (static) struct sockaddr_inarp with
247128192Sluigi * the address of the host and returns a pointer to the
248128192Sluigi * structure.
249128192Sluigi */
250128192Sluigistatic struct sockaddr_inarp *
251128192Sluigigetaddr(char *host)
252128192Sluigi{
253128192Sluigi	struct hostent *hp;
254128192Sluigi	static struct sockaddr_inarp reply;
255128192Sluigi
256128192Sluigi	bzero(&reply, sizeof(reply));
257128192Sluigi	reply.sin_len = sizeof(reply);
258128192Sluigi	reply.sin_family = AF_INET;
259128192Sluigi	reply.sin_addr.s_addr = inet_addr(host);
260128192Sluigi	if (reply.sin_addr.s_addr == INADDR_NONE) {
261128192Sluigi		if (!(hp = gethostbyname(host))) {
262128192Sluigi			warnx("%s: %s", host, hstrerror(h_errno));
263128192Sluigi			return NULL;
264128192Sluigi		}
265128192Sluigi		bcopy((char *)hp->h_addr, (char *)&reply.sin_addr,
266128192Sluigi			sizeof reply.sin_addr);
267128192Sluigi	}
268128192Sluigi	return &reply;
269128192Sluigi}
270128192Sluigi
271128192Sluigi/*
272128192Sluigi * returns true if the type is a valid one for ARP
273128192Sluigi */
274128192Sluigistatic int
275128192Sluigivalid_type(int type)
276128192Sluigi{
277128192Sluigi	switch (type) {
278128192Sluigi	default:
279128192Sluigi		return 0;
280128192Sluigi	case IFT_ETHER:
281128192Sluigi	case IFT_FDDI:
282128192Sluigi	case IFT_ISO88023:
283128192Sluigi	case IFT_ISO88024:
284128192Sluigi	case IFT_ISO88025:
285128192Sluigi	case IFT_L2VLAN:
286128192Sluigi		return 1;
287128192Sluigi	}
288128192Sluigi}
289128192Sluigi
290128192Sluigi/*
2918857Srgrimes * Set an individual arp entry
2921553Srgrimes */
293128192Sluigistatic int
29413977Sphkset(int argc, char **argv)
2951553Srgrimes{
296128192Sluigi	struct sockaddr_inarp *addr;
297128192Sluigi	struct sockaddr_inarp *dst;	/* what are we looking for */
298128054Smux	struct sockaddr_dl *sdl;
299128181Sluigi	struct rt_msghdr *rtm;
30093952Sru	struct ether_addr *ea;
3011553Srgrimes	char *host = argv[0], *eaddr = argv[1];
302128192Sluigi	struct	sockaddr_dl sdl_m;
3031553Srgrimes
3041553Srgrimes	argc -= 2;
3051553Srgrimes	argv += 2;
306128181Sluigi
307128181Sluigi	bzero(&sdl_m, sizeof(sdl_m));
308128181Sluigi	sdl_m.sdl_len = sizeof(sdl_m);
309128181Sluigi	sdl_m.sdl_family = AF_LINK;
310128181Sluigi
311128192Sluigi	dst = getaddr(host);
312128192Sluigi	if (dst == NULL)
313128192Sluigi		return (1);
31477870Sru	doing_proxy = flags = proxy_only = expire_time = 0;
3151553Srgrimes	while (argc-- > 0) {
3161553Srgrimes		if (strncmp(argv[0], "temp", 4) == 0) {
31777870Sru			struct timeval tv;
31877870Sru			gettimeofday(&tv, 0);
31977870Sru			expire_time = tv.tv_sec + 20 * 60;
3201553Srgrimes		}
3211553Srgrimes		else if (strncmp(argv[0], "pub", 3) == 0) {
3221553Srgrimes			flags |= RTF_ANNOUNCE;
32377870Sru			doing_proxy = 1;
32477870Sru			if (argc && strncmp(argv[1], "only", 3) == 0) {
32577870Sru				proxy_only = 1;
326128192Sluigi				dst->sin_other = SIN_PROXY;
32777870Sru				argc--; argv++;
32877870Sru			}
3291553Srgrimes		} else if (strncmp(argv[0], "trail", 5) == 0) {
330128192Sluigi			/* XXX deprecated and undocumented feature */
3311553Srgrimes			printf("%s: Sending trailers is no longer supported\n",
3321553Srgrimes				host);
3331553Srgrimes		}
3341553Srgrimes		argv++;
3351553Srgrimes	}
33693952Sru	ea = (struct ether_addr *)LLADDR(&sdl_m);
33713977Sphk	if (doing_proxy && !strcmp(eaddr, "auto")) {
338128192Sluigi		if (!get_ether_addr(dst->sin_addr.s_addr, ea)) {
33973135Sru			printf("no interface found for %s\n",
340128192Sluigi			       inet_ntoa(dst->sin_addr));
34113977Sphk			return (1);
34213977Sphk		}
34393952Sru		sdl_m.sdl_alen = ETHER_ADDR_LEN;
34413977Sphk	} else {
345128192Sluigi		struct ether_addr *ea1 = ether_aton(eaddr);
346128192Sluigi
347128192Sluigi		if (ea1 == NULL)
348128192Sluigi			warnx("invalid Ethernet address '%s'", eaddr);
349128192Sluigi		else {
350128192Sluigi			*ea = *ea1;
35193952Sru			sdl_m.sdl_alen = ETHER_ADDR_LEN;
352128192Sluigi		}
35313977Sphk	}
354128192Sluigi	for (;;) {	/* try at most twice */
355128192Sluigi		rtm = rtmsg(RTM_GET, dst, &sdl_m);
356128192Sluigi		if (rtm == NULL) {
357128192Sluigi			warn("%s", host);
358128192Sluigi			return (1);
359128192Sluigi		}
360128192Sluigi		addr = (struct sockaddr_inarp *)(rtm + 1);
361128192Sluigi		sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
362128192Sluigi		if (addr->sin_addr.s_addr != dst->sin_addr.s_addr)
363128192Sluigi			break;
3641553Srgrimes		if (sdl->sdl_family == AF_LINK &&
3651553Srgrimes		    (rtm->rtm_flags & RTF_LLINFO) &&
366128192Sluigi		    !(rtm->rtm_flags & RTF_GATEWAY) &&
367128192Sluigi		    valid_type(sdl->sdl_type) )
368128192Sluigi			break;
3691553Srgrimes		if (doing_proxy == 0) {
3701553Srgrimes			printf("set: can only proxy for %s\n", host);
3711553Srgrimes			return (1);
3721553Srgrimes		}
373128192Sluigi		if (dst->sin_other & SIN_PROXY) {
3741553Srgrimes			printf("set: proxy entry exists for non 802 device\n");
3751553Srgrimes			return(1);
3761553Srgrimes		}
377128192Sluigi		dst->sin_other = SIN_PROXY;
37877870Sru		proxy_only = 1;
3791553Srgrimes	}
380128192Sluigi
3811553Srgrimes	if (sdl->sdl_family != AF_LINK) {
3821553Srgrimes		printf("cannot intuit interface index and type for %s\n", host);
3831553Srgrimes		return (1);
3841553Srgrimes	}
3851553Srgrimes	sdl_m.sdl_type = sdl->sdl_type;
3861553Srgrimes	sdl_m.sdl_index = sdl->sdl_index;
387147170Sglebius	return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL);
3881553Srgrimes}
3891553Srgrimes
3901553Srgrimes/*
3911553Srgrimes * Display an individual arp entry
3921553Srgrimes */
393128192Sluigistatic void
39413977Sphkget(char *host)
3951553Srgrimes{
396128192Sluigi	struct sockaddr_inarp *addr;
3971553Srgrimes
398128192Sluigi	addr = getaddr(host);
399128192Sluigi	if (addr == NULL)
400128192Sluigi		exit(1);
401128181Sluigi	if (0 == search(addr->sin_addr.s_addr, print_entry)) {
402117729Syar		printf("%s (%s) -- no entry",
40387598Smikeh		    host, inet_ntoa(addr->sin_addr));
404117729Syar		if (rifname)
405117729Syar			printf(" on %s", rifname);
406117729Syar		printf("\n");
4071553Srgrimes	}
4081553Srgrimes}
4091553Srgrimes
4101553Srgrimes/*
4118857Srgrimes * Delete an arp entry
4121553Srgrimes */
413128181Sluigistatic int
414128192Sluigidelete(char *host, int do_proxy)
4151553Srgrimes{
416128192Sluigi	struct sockaddr_inarp *addr, *dst;
417128181Sluigi	struct rt_msghdr *rtm;
4181553Srgrimes	struct sockaddr_dl *sdl;
4191553Srgrimes
420128192Sluigi	dst = getaddr(host);
421128192Sluigi	if (dst == NULL)
422128192Sluigi		return 1;
423128192Sluigi	dst->sin_other = do_proxy;
424128192Sluigi	for (;;) {	/* try twice */
425128192Sluigi		rtm = rtmsg(RTM_GET, dst, NULL);
426128192Sluigi		if (rtm == NULL) {
427128192Sluigi			warn("%s", host);
4281553Srgrimes			return (1);
4291553Srgrimes		}
430128192Sluigi		addr = (struct sockaddr_inarp *)(rtm + 1);
431128192Sluigi		sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
432128192Sluigi		if (addr->sin_addr.s_addr == dst->sin_addr.s_addr &&
433128192Sluigi		    sdl->sdl_family == AF_LINK &&
4341553Srgrimes		    (rtm->rtm_flags & RTF_LLINFO) &&
435128192Sluigi		    !(rtm->rtm_flags & RTF_GATEWAY) &&
436128192Sluigi		    valid_type(sdl->sdl_type) )
437128192Sluigi			break;	/* found it */
438128192Sluigi		if (dst->sin_other & SIN_PROXY) {
439128192Sluigi			fprintf(stderr, "delete: cannot locate %s\n",host);
440128192Sluigi			return (1);
441128192Sluigi		}
442128192Sluigi		dst->sin_other = SIN_PROXY;
4431553Srgrimes	}
444128192Sluigi	if (rtmsg(RTM_DELETE, dst, NULL) != NULL) {
44587598Smikeh		printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr));
44613977Sphk		return (0);
44713977Sphk	}
44813977Sphk	return (1);
4491553Srgrimes}
4501553Srgrimes
4511553Srgrimes/*
45231145Sjulian * Search the arp table and do some action on matching entries
4531553Srgrimes */
454128181Sluigistatic int
455128181Sluigisearch(u_long addr, action_fn *action)
4561553Srgrimes{
4571553Srgrimes	int mib[6];
4581553Srgrimes	size_t needed;
459140735Smaxim	char *lim, *buf, *newbuf, *next;
4601553Srgrimes	struct rt_msghdr *rtm;
46187598Smikeh	struct sockaddr_inarp *sin2;
4621553Srgrimes	struct sockaddr_dl *sdl;
463117729Syar	char ifname[IF_NAMESIZE];
464140735Smaxim	int st, found_entry = 0;
4651553Srgrimes
4661553Srgrimes	mib[0] = CTL_NET;
4671553Srgrimes	mib[1] = PF_ROUTE;
4681553Srgrimes	mib[2] = 0;
4691553Srgrimes	mib[3] = AF_INET;
4701553Srgrimes	mib[4] = NET_RT_FLAGS;
4711553Srgrimes	mib[5] = RTF_LLINFO;
4721553Srgrimes	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
473128054Smux		err(1, "route-sysctl-estimate");
474128192Sluigi	if (needed == 0)	/* empty table */
475128181Sluigi		return 0;
476140735Smaxim	buf = NULL;
477140748Smaxim	for (;;) {
478140735Smaxim		newbuf = realloc(buf, needed);
479140735Smaxim		if (newbuf == NULL) {
480140735Smaxim			if (buf != NULL)
481140735Smaxim				free(buf);
482140735Smaxim			errx(1, "could not reallocate memory");
483140735Smaxim		}
484140735Smaxim		buf = newbuf;
485140735Smaxim		st = sysctl(mib, 6, buf, &needed, NULL, 0);
486140748Smaxim		if (st == 0 || errno != ENOMEM)
487140748Smaxim			break;
488140748Smaxim		needed += needed / 8;
489140748Smaxim	}
490140735Smaxim	if (st == -1)
491128054Smux		err(1, "actual retrieval of routing table");
4921553Srgrimes	lim = buf + needed;
4931553Srgrimes	for (next = buf; next < lim; next += rtm->rtm_msglen) {
4941553Srgrimes		rtm = (struct rt_msghdr *)next;
49587598Smikeh		sin2 = (struct sockaddr_inarp *)(rtm + 1);
496130246Sstefanf		sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
497117729Syar		if (rifname && if_indextoname(sdl->sdl_index, ifname) &&
498117729Syar		    strcmp(ifname, rifname))
499117729Syar			continue;
5001553Srgrimes		if (addr) {
50187598Smikeh			if (addr != sin2->sin_addr.s_addr)
5021553Srgrimes				continue;
5031553Srgrimes			found_entry = 1;
5041553Srgrimes		}
50587598Smikeh		(*action)(sdl, sin2, rtm);
5061553Srgrimes	}
50776216Syar	free(buf);
508128181Sluigi	return found_entry;
5091553Srgrimes}
5101553Srgrimes
51131145Sjulian/*
51231145Sjulian * Display an arp entry
51331145Sjulian */
514128181Sluigistatic void
51531145Sjulianprint_entry(struct sockaddr_dl *sdl,
51687598Smikeh	struct sockaddr_inarp *addr, struct rt_msghdr *rtm)
51731145Sjulian{
51877870Sru	const char *host;
51931145Sjulian	struct hostent *hp;
52096202Skbyanc	struct iso88025_sockaddr_dl_data *trld;
52184666Sru	char ifname[IF_NAMESIZE];
52244627Sjulian	int seg;
52331145Sjulian
52431145Sjulian	if (nflag == 0)
52587598Smikeh		hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
52687598Smikeh		    sizeof addr->sin_addr, AF_INET);
52731145Sjulian	else
52831145Sjulian		hp = 0;
52931145Sjulian	if (hp)
53031145Sjulian		host = hp->h_name;
53131145Sjulian	else {
53231145Sjulian		host = "?";
53331145Sjulian		if (h_errno == TRY_AGAIN)
53431145Sjulian			nflag = 1;
53531145Sjulian	}
53687598Smikeh	printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr));
537130408Sdfr	if (sdl->sdl_alen) {
538139270Sru		if ((sdl->sdl_type == IFT_ETHER ||
539139270Sru		    sdl->sdl_type == IFT_L2VLAN) &&
540130408Sdfr		    sdl->sdl_alen == ETHER_ADDR_LEN)
541130408Sdfr			printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl)));
542130408Sdfr		else {
543130408Sdfr			int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
544130408Sdfr
545130408Sdfr			printf("%s", link_ntoa(sdl) + n);
546130408Sdfr		}
547130408Sdfr	} else
54831145Sjulian		printf("(incomplete)");
54984666Sru	if (if_indextoname(sdl->sdl_index, ifname) != NULL)
55084666Sru		printf(" on %s", ifname);
55131145Sjulian	if (rtm->rtm_rmx.rmx_expire == 0)
55231145Sjulian		printf(" permanent");
55387598Smikeh	if (addr->sin_other & SIN_PROXY)
55431145Sjulian		printf(" published (proxy only)");
55531145Sjulian	if (rtm->rtm_addrs & RTA_NETMASK) {
55687598Smikeh		addr = (struct sockaddr_inarp *)
557128186Sluigi			(SA_SIZE(sdl) + (char *)sdl);
55887598Smikeh		if (addr->sin_addr.s_addr == 0xffffffff)
55931145Sjulian			printf(" published");
56087598Smikeh		if (addr->sin_len != 8)
56151250Sru			printf("(weird)");
56231145Sjulian	}
56344627Sjulian        switch(sdl->sdl_type) {
564128181Sluigi	case IFT_ETHER:
56544627Sjulian                printf(" [ethernet]");
56644627Sjulian                break;
567128181Sluigi	case IFT_ISO88025:
56844627Sjulian                printf(" [token-ring]");
56996202Skbyanc		trld = SDL_ISO88025(sdl);
57096202Skbyanc		if (trld->trld_rcf != 0) {
57196202Skbyanc			printf(" rt=%x", ntohs(trld->trld_rcf));
57296202Skbyanc			for (seg = 0;
57396202Skbyanc			     seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2);
57496202Skbyanc			     seg++)
57596235Swpaul				printf(":%x", ntohs(*(trld->trld_route[seg])));
57696202Skbyanc		}
57744627Sjulian                break;
578128181Sluigi	case IFT_FDDI:
57985123Smdodd                printf(" [fddi]");
58085123Smdodd                break;
581128181Sluigi	case IFT_ATM:
58285123Smdodd                printf(" [atm]");
58385123Smdodd                break;
584128181Sluigi	case IFT_L2VLAN:
58575180Syar		printf(" [vlan]");
58675180Syar		break;
587130408Sdfr	case IFT_IEEE1394:
588130408Sdfr                printf(" [firewire]");
589130408Sdfr                break;
590128181Sluigi	default:
59194075Smurray		break;
59244627Sjulian        }
59344627Sjulian
59431145Sjulian	printf("\n");
59544627Sjulian
59631145Sjulian}
59731145Sjulian
59831145Sjulian/*
59931145Sjulian * Nuke an arp entry
60031145Sjulian */
601128181Sluigistatic void
60287598Smikehnuke_entry(struct sockaddr_dl *sdl __unused,
60387598Smikeh	struct sockaddr_inarp *addr, struct rt_msghdr *rtm __unused)
60431145Sjulian{
60531145Sjulian	char ip[20];
60631145Sjulian
60787598Smikeh	snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
608128192Sluigi	delete(ip, 0);
60931145Sjulian}
61031145Sjulian
611128181Sluigistatic void
61213977Sphkusage(void)
6131553Srgrimes{
61431145Sjulian	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
615117729Syar		"usage: arp [-n] [-i interface] hostname",
616117729Syar		"       arp [-n] [-i interface] -a",
61777870Sru		"       arp -d hostname [pub]",
61831145Sjulian		"       arp -d -a",
61929060Scharnier		"       arp -s hostname ether_addr [temp] [pub]",
62029060Scharnier		"       arp -S hostname ether_addr [temp] [pub]",
62129060Scharnier		"       arp -f filename");
6221553Srgrimes	exit(1);
6231553Srgrimes}
6241553Srgrimes
625128181Sluigistatic struct rt_msghdr *
626128192Sluigirtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl)
6271553Srgrimes{
6281553Srgrimes	static int seq;
6291553Srgrimes	int rlen;
630128181Sluigi	int l;
631128181Sluigi	struct sockaddr_in so_mask;
632128181Sluigi	static int s = -1;
633128181Sluigi	static pid_t pid;
634128181Sluigi
635128181Sluigi	static struct	{
636128181Sluigi		struct	rt_msghdr m_rtm;
637128181Sluigi		char	m_space[512];
638128181Sluigi	}	m_rtmsg;
639128181Sluigi
640128054Smux	struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
641128054Smux	char *cp = m_rtmsg.m_space;
6421553Srgrimes
643128181Sluigi	if (s < 0) {	/* first time: open socket, get pid */
644128181Sluigi		s = socket(PF_ROUTE, SOCK_RAW, 0);
645128181Sluigi		if (s < 0)
646128181Sluigi			err(1, "socket");
647128181Sluigi		pid = getpid();
648128181Sluigi	}
649128181Sluigi	bzero(&so_mask, sizeof(so_mask));
650128181Sluigi	so_mask.sin_len = 8;
651128181Sluigi	so_mask.sin_addr.s_addr = 0xffffffff;
652128181Sluigi
6531553Srgrimes	errno = 0;
654128192Sluigi	/*
655128192Sluigi	 * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer
656128192Sluigi	 * appropriately.
657128192Sluigi	 */
6581553Srgrimes	if (cmd == RTM_DELETE)
6591553Srgrimes		goto doit;
6601553Srgrimes	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
6611553Srgrimes	rtm->rtm_flags = flags;
6621553Srgrimes	rtm->rtm_version = RTM_VERSION;
6631553Srgrimes
6641553Srgrimes	switch (cmd) {
6651553Srgrimes	default:
66629060Scharnier		errx(1, "internal wrong cmd");
6671553Srgrimes	case RTM_ADD:
6681553Srgrimes		rtm->rtm_addrs |= RTA_GATEWAY;
6691553Srgrimes		rtm->rtm_rmx.rmx_expire = expire_time;
6701553Srgrimes		rtm->rtm_inits = RTV_EXPIRE;
6711553Srgrimes		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
672128192Sluigi		dst->sin_other = 0;
6731553Srgrimes		if (doing_proxy) {
67477870Sru			if (proxy_only)
675128192Sluigi				dst->sin_other = SIN_PROXY;
6761553Srgrimes			else {
6771553Srgrimes				rtm->rtm_addrs |= RTA_NETMASK;
6781553Srgrimes				rtm->rtm_flags &= ~RTF_HOST;
6791553Srgrimes			}
6801553Srgrimes		}
6811553Srgrimes		/* FALLTHROUGH */
6821553Srgrimes	case RTM_GET:
6831553Srgrimes		rtm->rtm_addrs |= RTA_DST;
6841553Srgrimes	}
6851553Srgrimes#define NEXTADDR(w, s) \
686128192Sluigi	if (s && rtm->rtm_addrs & (w)) { \
687128192Sluigi		bcopy(s, cp, sizeof(*s)); cp += SA_SIZE(s);}
6881553Srgrimes
689128192Sluigi	NEXTADDR(RTA_DST, dst);
690128192Sluigi	NEXTADDR(RTA_GATEWAY, sdl);
691128192Sluigi	NEXTADDR(RTA_NETMASK, &so_mask);
6921553Srgrimes
6931553Srgrimes	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
6941553Srgrimesdoit:
6951553Srgrimes	l = rtm->rtm_msglen;
6961553Srgrimes	rtm->rtm_seq = ++seq;
6971553Srgrimes	rtm->rtm_type = cmd;
6981553Srgrimes	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
6991553Srgrimes		if (errno != ESRCH || cmd != RTM_DELETE) {
70029060Scharnier			warn("writing to routing socket");
701128181Sluigi			return NULL;
7021553Srgrimes		}
7031553Srgrimes	}
7041553Srgrimes	do {
7051553Srgrimes		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
7061553Srgrimes	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
7071553Srgrimes	if (l < 0)
70829060Scharnier		warn("read from routing socket");
709128181Sluigi	return rtm;
7101553Srgrimes}
7111553Srgrimes
71213977Sphk/*
71313977Sphk * get_ether_addr - get the hardware address of an interface on the
71413977Sphk * the same subnet as ipaddr.
71513977Sphk */
71613977Sphk#define MAX_IFS		32
71713977Sphk
718128181Sluigistatic int
71993952Sruget_ether_addr(u_int32_t ipaddr, struct ether_addr *hwaddr)
72013977Sphk{
72113977Sphk	struct ifreq *ifr, *ifend, *ifp;
722128192Sluigi	uint32_t ina, mask;
72313977Sphk	struct sockaddr_dl *dla;
72413977Sphk	struct ifreq ifreq;
72513977Sphk	struct ifconf ifc;
72613977Sphk	struct ifreq ifs[MAX_IFS];
72787598Smikeh	int sock;
728128192Sluigi	int retval = 0;
72913977Sphk
73087598Smikeh	sock = socket(AF_INET, SOCK_DGRAM, 0);
73187598Smikeh	if (sock < 0)
73229060Scharnier		err(1, "socket");
73313977Sphk
73413977Sphk	ifc.ifc_len = sizeof(ifs);
73513977Sphk	ifc.ifc_req = ifs;
73692735Smikeh	if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
73729060Scharnier		warnx("ioctl(SIOCGIFCONF)");
738128192Sluigi		goto done;
73913977Sphk	}
74013977Sphk
741128192Sluigi#define NEXTIFR(i)						\
742128192Sluigi    ((struct ifreq *)((char *)&(i)->ifr_addr			\
743128192Sluigi	+ MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) )
744128192Sluigi
74513977Sphk	/*
746128192Sluigi	 * Scan through looking for an interface with an Internet
747128192Sluigi	 * address on the same subnet as `ipaddr'.
748128192Sluigi	 */
749128192Sluigi	ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
750128192Sluigi	for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) {
751128192Sluigi		if (ifr->ifr_addr.sa_family != AF_INET)
752128192Sluigi			continue;
753128192Sluigi		/* XXX can't we use *ifr instead of ifreq ? */
754128192Sluigi		strncpy(ifreq.ifr_name, ifr->ifr_name,
755128192Sluigi			sizeof(ifreq.ifr_name));
756128192Sluigi		/*
757128192Sluigi		 * Check that the interface is up,
758128192Sluigi		 * and not point-to-point or loopback.
759128192Sluigi		 */
760128192Sluigi		if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0)
761128192Sluigi			continue;
762128192Sluigi		if ((ifreq.ifr_flags &
763128192Sluigi		     (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
764128192Sluigi				IFF_LOOPBACK|IFF_NOARP))
765128192Sluigi		     != (IFF_UP|IFF_BROADCAST))
766128192Sluigi			continue;
767128192Sluigi		/*
768128192Sluigi		 * Get its netmask and check that it's on
769128192Sluigi		 * the right subnet.
770128192Sluigi		 */
771128192Sluigi		if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0)
772128192Sluigi			continue;
773128192Sluigi		mask = ((struct sockaddr_in *)
774128192Sluigi			&ifreq.ifr_addr)->sin_addr.s_addr;
775128192Sluigi		ina = ((struct sockaddr_in *)
776128192Sluigi			&ifr->ifr_addr)->sin_addr.s_addr;
777128192Sluigi		if ((ipaddr & mask) == (ina & mask))
778128192Sluigi			break; /* ok, we got it! */
77913977Sphk	}
78013977Sphk
781128192Sluigi	if (ifr >= ifend)
782128192Sluigi		goto done;
78313977Sphk
78413977Sphk	/*
785128192Sluigi	 * Now scan through again looking for a link-level address
786128192Sluigi	 * for this interface.
787128192Sluigi	 */
78813977Sphk	ifp = ifr;
789128192Sluigi	for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr))
790128192Sluigi		if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 &&
791128192Sluigi		    ifr->ifr_addr.sa_family == AF_LINK)
792128192Sluigi			break;
793128192Sluigi	if (ifr >= ifend)
794128192Sluigi		goto done;
795128192Sluigi	/*
796128192Sluigi	 * Found the link-level address - copy it out
797128192Sluigi	 */
798128192Sluigi	dla = (struct sockaddr_dl *) &ifr->ifr_addr;
799128192Sluigi	memcpy(hwaddr,  LLADDR(dla), dla->sdl_alen);
800128192Sluigi	printf("using interface %s for proxy with address ",
801128192Sluigi		ifp->ifr_name);
802128192Sluigi	printf("%s\n", ether_ntoa(hwaddr));
803128192Sluigi	retval = dla->sdl_alen;
804128192Sluigidone:
805128192Sluigi	close(sock);
806128192Sluigi	return retval;
80713977Sphk}
808