arp.c revision 186500
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 186500 2008-12-26 19:45:24Z qingli $");
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);
93147172Srustatic int get(char *host);
94128181Sluigistatic int file(char *name);
95128192Sluigistatic struct rt_msghdr *rtmsg(int cmd,
96147172Sru    struct sockaddr_inarp *dst, struct sockaddr_dl *sdl);
97147172Srustatic int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr);
98147172Srustatic struct sockaddr_inarp *getaddr(char *host);
99147172Srustatic int valid_type(int type);
100128181Sluigi
10131145Sjulianstatic int nflag;	/* no reverse dns lookups */
102117729Syarstatic char *rifname;
1031553Srgrimes
104128181Sluigistatic int	expire_time, flags, doing_proxy, proxy_only;
105128192Sluigi
10631145Sjulian/* which function we're supposed to do */
10731145Sjulian#define F_GET		1
10831145Sjulian#define F_SET		2
10931145Sjulian#define F_FILESET	3
11031145Sjulian#define F_REPLACE	4
11131145Sjulian#define F_DELETE	5
11231145Sjulian
11331145Sjulian#define SETFUNC(f)	{ if (func) usage(); func = (f); }
11431145Sjulian
11513977Sphkint
11677870Srumain(int argc, char *argv[])
1171553Srgrimes{
11831145Sjulian	int ch, func = 0;
11931145Sjulian	int rtn = 0;
120128181Sluigi	int aflag = 0;	/* do it for all entries */
1211553Srgrimes
122117729Syar	while ((ch = getopt(argc, argv, "andfsSi:")) != -1)
1231553Srgrimes		switch((char)ch) {
1241553Srgrimes		case 'a':
12531145Sjulian			aflag = 1;
12631145Sjulian			break;
1271553Srgrimes		case 'd':
12831145Sjulian			SETFUNC(F_DELETE);
12931145Sjulian			break;
1301553Srgrimes		case 'n':
1311553Srgrimes			nflag = 1;
13231145Sjulian			break;
13313977Sphk		case 'S':
13431145Sjulian			SETFUNC(F_REPLACE);
13531145Sjulian			break;
1361553Srgrimes		case 's':
13731145Sjulian			SETFUNC(F_SET);
13831145Sjulian			break;
1399874Sjkh		case 'f' :
14031145Sjulian			SETFUNC(F_FILESET);
14131145Sjulian			break;
142117729Syar		case 'i':
143117729Syar			rifname = optarg;
144117729Syar			break;
1451553Srgrimes		case '?':
1461553Srgrimes		default:
1471553Srgrimes			usage();
1481553Srgrimes		}
14931145Sjulian	argc -= optind;
15031145Sjulian	argv += optind;
15131145Sjulian
15231145Sjulian	if (!func)
15331145Sjulian		func = F_GET;
154117729Syar	if (rifname) {
155154162Sbrooks		if (func != F_GET && !(func == F_DELETE && aflag))
156117729Syar			errx(1, "-i not applicable to this operation");
157117729Syar		if (if_nametoindex(rifname) == 0) {
158117729Syar			if (errno == ENXIO)
159117729Syar				errx(1, "interface %s does not exist", rifname);
160117729Syar			else
161117729Syar				err(1, "if_nametoindex(%s)", rifname);
162117729Syar		}
163117729Syar	}
16431145Sjulian	switch (func) {
16531145Sjulian	case F_GET:
16631145Sjulian		if (aflag) {
16731145Sjulian			if (argc != 0)
16831145Sjulian				usage();
16931145Sjulian			search(0, print_entry);
17031145Sjulian		} else {
17131145Sjulian			if (argc != 1)
17231145Sjulian				usage();
173147172Sru			rtn = get(argv[0]);
17431145Sjulian		}
17531145Sjulian		break;
17631145Sjulian	case F_SET:
17731145Sjulian	case F_REPLACE:
17877870Sru		if (argc < 2 || argc > 6)
17931145Sjulian			usage();
18031145Sjulian		if (func == F_REPLACE)
181147172Sru			(void)delete(argv[0], 0);
18231145Sjulian		rtn = set(argc, argv) ? 1 : 0;
18331145Sjulian		break;
18431145Sjulian	case F_DELETE:
18531145Sjulian		if (aflag) {
18631145Sjulian			if (argc != 0)
18731145Sjulian				usage();
18831145Sjulian			search(0, nuke_entry);
18931145Sjulian		} else {
190128192Sluigi			if (argc == 2 && strncmp(argv[1], "pub", 3) == 0)
191128192Sluigi				ch = SIN_PROXY;
192128192Sluigi			else if (argc == 1)
193128192Sluigi				ch = 0;
194128192Sluigi			else
19531145Sjulian				usage();
196128192Sluigi			rtn = delete(argv[0], ch);
19731145Sjulian		}
19831145Sjulian		break;
19931145Sjulian	case F_FILESET:
20031145Sjulian		if (argc != 1)
20131145Sjulian			usage();
20231145Sjulian		rtn = file(argv[0]);
20331145Sjulian		break;
20431145Sjulian	}
20531145Sjulian
206147172Sru	return (rtn);
2071553Srgrimes}
2081553Srgrimes
2091553Srgrimes/*
2101553Srgrimes * Process a file to set standard arp entries
2111553Srgrimes */
212128181Sluigistatic int
21313977Sphkfile(char *name)
2141553Srgrimes{
2151553Srgrimes	FILE *fp;
2161553Srgrimes	int i, retval;
217109413Sru	char line[100], arg[5][50], *args[5], *p;
2181553Srgrimes
21929060Scharnier	if ((fp = fopen(name, "r")) == NULL)
220128054Smux		err(1, "cannot open %s", name);
2211553Srgrimes	args[0] = &arg[0][0];
2221553Srgrimes	args[1] = &arg[1][0];
2231553Srgrimes	args[2] = &arg[2][0];
2241553Srgrimes	args[3] = &arg[3][0];
2251553Srgrimes	args[4] = &arg[4][0];
2261553Srgrimes	retval = 0;
227167260Skevlo	while(fgets(line, sizeof(line), fp) != NULL) {
228109413Sru		if ((p = strchr(line, '#')) != NULL)
229109413Sru			*p = '\0';
230109413Sru		for (p = line; isblank(*p); p++);
231111910Sru		if (*p == '\n' || *p == '\0')
232108314Sru			continue;
233109413Sru		i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1],
23456134Sjkh		    arg[2], arg[3], arg[4]);
2351553Srgrimes		if (i < 2) {
23629060Scharnier			warnx("bad line: %s", line);
2371553Srgrimes			retval = 1;
2381553Srgrimes			continue;
2391553Srgrimes		}
2401553Srgrimes		if (set(i, args))
2411553Srgrimes			retval = 1;
2421553Srgrimes	}
2431553Srgrimes	fclose(fp);
2441553Srgrimes	return (retval);
2451553Srgrimes}
2461553Srgrimes
2471553Srgrimes/*
248128192Sluigi * Given a hostname, fills up a (static) struct sockaddr_inarp with
249128192Sluigi * the address of the host and returns a pointer to the
250128192Sluigi * structure.
251128192Sluigi */
252128192Sluigistatic struct sockaddr_inarp *
253128192Sluigigetaddr(char *host)
254128192Sluigi{
255128192Sluigi	struct hostent *hp;
256128192Sluigi	static struct sockaddr_inarp reply;
257128192Sluigi
258128192Sluigi	bzero(&reply, sizeof(reply));
259128192Sluigi	reply.sin_len = sizeof(reply);
260128192Sluigi	reply.sin_family = AF_INET;
261128192Sluigi	reply.sin_addr.s_addr = inet_addr(host);
262128192Sluigi	if (reply.sin_addr.s_addr == INADDR_NONE) {
263128192Sluigi		if (!(hp = gethostbyname(host))) {
264128192Sluigi			warnx("%s: %s", host, hstrerror(h_errno));
265147172Sru			return (NULL);
266128192Sluigi		}
267128192Sluigi		bcopy((char *)hp->h_addr, (char *)&reply.sin_addr,
268128192Sluigi			sizeof reply.sin_addr);
269128192Sluigi	}
270147172Sru	return (&reply);
271128192Sluigi}
272128192Sluigi
273128192Sluigi/*
274147172Sru * Returns true if the type is a valid one for ARP.
275128192Sluigi */
276128192Sluigistatic int
277128192Sluigivalid_type(int type)
278128192Sluigi{
279147172Sru
280128192Sluigi	switch (type) {
281128192Sluigi	case IFT_ETHER:
282128192Sluigi	case IFT_FDDI:
283128192Sluigi	case IFT_ISO88023:
284128192Sluigi	case IFT_ISO88024:
285128192Sluigi	case IFT_ISO88025:
286128192Sluigi	case IFT_L2VLAN:
287151989Sthompsa	case IFT_BRIDGE:
288147172Sru		return (1);
289147172Sru	default:
290147172Sru		return (0);
291128192Sluigi	}
292128192Sluigi}
293128192Sluigi
294128192Sluigi/*
2958857Srgrimes * Set an individual arp entry
2961553Srgrimes */
297128192Sluigistatic int
29813977Sphkset(int argc, char **argv)
2991553Srgrimes{
300128192Sluigi	struct sockaddr_inarp *addr;
301128192Sluigi	struct sockaddr_inarp *dst;	/* what are we looking for */
302128054Smux	struct sockaddr_dl *sdl;
303128181Sluigi	struct rt_msghdr *rtm;
30493952Sru	struct ether_addr *ea;
3051553Srgrimes	char *host = argv[0], *eaddr = argv[1];
306147172Sru	struct sockaddr_dl sdl_m;
3071553Srgrimes
3081553Srgrimes	argc -= 2;
3091553Srgrimes	argv += 2;
310128181Sluigi
311128181Sluigi	bzero(&sdl_m, sizeof(sdl_m));
312128181Sluigi	sdl_m.sdl_len = sizeof(sdl_m);
313128181Sluigi	sdl_m.sdl_family = AF_LINK;
314128181Sluigi
315128192Sluigi	dst = getaddr(host);
316128192Sluigi	if (dst == NULL)
317128192Sluigi		return (1);
31877870Sru	doing_proxy = flags = proxy_only = expire_time = 0;
3191553Srgrimes	while (argc-- > 0) {
3201553Srgrimes		if (strncmp(argv[0], "temp", 4) == 0) {
32177870Sru			struct timeval tv;
32277870Sru			gettimeofday(&tv, 0);
32377870Sru			expire_time = tv.tv_sec + 20 * 60;
324177362Ssam		} else if (strncmp(argv[0], "pub", 3) == 0) {
3251553Srgrimes			flags |= RTF_ANNOUNCE;
32677870Sru			doing_proxy = 1;
32777870Sru			if (argc && strncmp(argv[1], "only", 3) == 0) {
32877870Sru				proxy_only = 1;
329128192Sluigi				dst->sin_other = SIN_PROXY;
33077870Sru				argc--; argv++;
33177870Sru			}
332177362Ssam		} else if (strncmp(argv[0], "blackhole", 9) == 0) {
333186485Strhodes			if (flags & RTF_REJECT) {
334186485Strhodes				printf("Choose one of blackhole or reject, not both.\n");
335186485Strhodes			}
336177362Ssam			flags |= RTF_BLACKHOLE;
337177362Ssam		} else if (strncmp(argv[0], "reject", 6) == 0) {
338186485Strhodes			if (flags & RTF_BLACKHOLE) {
339186485Strhodes				printf("Choose one of blackhole or reject, not both.\n");
340186485Strhodes			}
341177362Ssam			flags |= RTF_REJECT;
3421553Srgrimes		} else if (strncmp(argv[0], "trail", 5) == 0) {
343128192Sluigi			/* XXX deprecated and undocumented feature */
3441553Srgrimes			printf("%s: Sending trailers is no longer supported\n",
3451553Srgrimes				host);
3461553Srgrimes		}
3471553Srgrimes		argv++;
3481553Srgrimes	}
34993952Sru	ea = (struct ether_addr *)LLADDR(&sdl_m);
35013977Sphk	if (doing_proxy && !strcmp(eaddr, "auto")) {
351128192Sluigi		if (!get_ether_addr(dst->sin_addr.s_addr, ea)) {
35273135Sru			printf("no interface found for %s\n",
353128192Sluigi			       inet_ntoa(dst->sin_addr));
35413977Sphk			return (1);
35513977Sphk		}
35693952Sru		sdl_m.sdl_alen = ETHER_ADDR_LEN;
35713977Sphk	} else {
358128192Sluigi		struct ether_addr *ea1 = ether_aton(eaddr);
359128192Sluigi
360155471Sglebius		if (ea1 == NULL) {
361128192Sluigi			warnx("invalid Ethernet address '%s'", eaddr);
362155471Sglebius			return (1);
363155471Sglebius		} else {
364128192Sluigi			*ea = *ea1;
36593952Sru			sdl_m.sdl_alen = ETHER_ADDR_LEN;
366128192Sluigi		}
36713977Sphk	}
368128192Sluigi	for (;;) {	/* try at most twice */
369128192Sluigi		rtm = rtmsg(RTM_GET, dst, &sdl_m);
370128192Sluigi		if (rtm == NULL) {
371128192Sluigi			warn("%s", host);
372128192Sluigi			return (1);
373128192Sluigi		}
374128192Sluigi		addr = (struct sockaddr_inarp *)(rtm + 1);
375128192Sluigi		sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
376128192Sluigi		if (addr->sin_addr.s_addr != dst->sin_addr.s_addr)
377128192Sluigi			break;
3781553Srgrimes		if (sdl->sdl_family == AF_LINK &&
379128192Sluigi		    !(rtm->rtm_flags & RTF_GATEWAY) &&
380128192Sluigi		    valid_type(sdl->sdl_type) )
381128192Sluigi			break;
3821553Srgrimes		if (doing_proxy == 0) {
3831553Srgrimes			printf("set: can only proxy for %s\n", host);
3841553Srgrimes			return (1);
3851553Srgrimes		}
386128192Sluigi		if (dst->sin_other & SIN_PROXY) {
3871553Srgrimes			printf("set: proxy entry exists for non 802 device\n");
388147172Sru			return (1);
3891553Srgrimes		}
390128192Sluigi		dst->sin_other = SIN_PROXY;
39177870Sru		proxy_only = 1;
3921553Srgrimes	}
393128192Sluigi
3941553Srgrimes	if (sdl->sdl_family != AF_LINK) {
3951553Srgrimes		printf("cannot intuit interface index and type for %s\n", host);
3961553Srgrimes		return (1);
3971553Srgrimes	}
3981553Srgrimes	sdl_m.sdl_type = sdl->sdl_type;
3991553Srgrimes	sdl_m.sdl_index = sdl->sdl_index;
400147170Sglebius	return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL);
4011553Srgrimes}
4021553Srgrimes
4031553Srgrimes/*
4041553Srgrimes * Display an individual arp entry
4051553Srgrimes */
406147172Srustatic int
40713977Sphkget(char *host)
4081553Srgrimes{
409128192Sluigi	struct sockaddr_inarp *addr;
4101553Srgrimes
411128192Sluigi	addr = getaddr(host);
412128192Sluigi	if (addr == NULL)
413147172Sru		return (1);
414128181Sluigi	if (0 == search(addr->sin_addr.s_addr, print_entry)) {
415117729Syar		printf("%s (%s) -- no entry",
41687598Smikeh		    host, inet_ntoa(addr->sin_addr));
417117729Syar		if (rifname)
418117729Syar			printf(" on %s", rifname);
419117729Syar		printf("\n");
420147172Sru		return (1);
4211553Srgrimes	}
422147172Sru	return (0);
4231553Srgrimes}
4241553Srgrimes
4251553Srgrimes/*
4268857Srgrimes * Delete an arp entry
4271553Srgrimes */
428128181Sluigistatic int
429128192Sluigidelete(char *host, int do_proxy)
4301553Srgrimes{
431128192Sluigi	struct sockaddr_inarp *addr, *dst;
432128181Sluigi	struct rt_msghdr *rtm;
4331553Srgrimes	struct sockaddr_dl *sdl;
434186119Sqingli	struct sockaddr_dl sdl_m;
4351553Srgrimes
436128192Sluigi	dst = getaddr(host);
437128192Sluigi	if (dst == NULL)
438147172Sru		return (1);
439128192Sluigi	dst->sin_other = do_proxy;
440186119Sqingli
441186119Sqingli	/*
442186119Sqingli	 * setup the data structure to notify the kernel
443186119Sqingli	 * it is the ARP entry the RTM_GET is interested
444186119Sqingli	 * in
445186119Sqingli	 */
446186119Sqingli	bzero(&sdl_m, sizeof(sdl_m));
447186119Sqingli	sdl_m.sdl_len = sizeof(sdl_m);
448186119Sqingli	sdl_m.sdl_family = AF_LINK;
449186119Sqingli
450128192Sluigi	for (;;) {	/* try twice */
451186119Sqingli		rtm = rtmsg(RTM_GET, dst, &sdl_m);
452128192Sluigi		if (rtm == NULL) {
453128192Sluigi			warn("%s", host);
4541553Srgrimes			return (1);
4551553Srgrimes		}
456128192Sluigi		addr = (struct sockaddr_inarp *)(rtm + 1);
457128192Sluigi		sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
458186119Sqingli
459186119Sqingli		/*
460186119Sqingli		 * With the new L2/L3 restructure, the route
461186119Sqingli		 * returned is a prefix route. The important
462186119Sqingli		 * piece of information from the previous
463186119Sqingli		 * RTM_GET is the interface index. In the
464186119Sqingli		 * case of ECMP, the kernel will traverse
465186119Sqingli		 * the route group for the given entry.
466186119Sqingli		 */
467186119Sqingli		if (sdl->sdl_family == AF_LINK &&
468128192Sluigi		    !(rtm->rtm_flags & RTF_GATEWAY) &&
469186119Sqingli		    valid_type(sdl->sdl_type) ) {
470186119Sqingli			addr->sin_addr.s_addr = dst->sin_addr.s_addr;
471186119Sqingli			break;
472186119Sqingli		}
473186119Sqingli
474128192Sluigi		if (dst->sin_other & SIN_PROXY) {
475128192Sluigi			fprintf(stderr, "delete: cannot locate %s\n",host);
476128192Sluigi			return (1);
477128192Sluigi		}
478128192Sluigi		dst->sin_other = SIN_PROXY;
4791553Srgrimes	}
480186500Sqingli	rtm->rtm_flags |= RTF_LLDATA;
481128192Sluigi	if (rtmsg(RTM_DELETE, dst, NULL) != NULL) {
48287598Smikeh		printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr));
48313977Sphk		return (0);
48413977Sphk	}
48513977Sphk	return (1);
4861553Srgrimes}
4871553Srgrimes
4881553Srgrimes/*
48931145Sjulian * Search the arp table and do some action on matching entries
4901553Srgrimes */
491128181Sluigistatic int
492128181Sluigisearch(u_long addr, action_fn *action)
4931553Srgrimes{
4941553Srgrimes	int mib[6];
4951553Srgrimes	size_t needed;
496140735Smaxim	char *lim, *buf, *newbuf, *next;
4971553Srgrimes	struct rt_msghdr *rtm;
49887598Smikeh	struct sockaddr_inarp *sin2;
4991553Srgrimes	struct sockaddr_dl *sdl;
500117729Syar	char ifname[IF_NAMESIZE];
501140735Smaxim	int st, found_entry = 0;
5021553Srgrimes
5031553Srgrimes	mib[0] = CTL_NET;
5041553Srgrimes	mib[1] = PF_ROUTE;
5051553Srgrimes	mib[2] = 0;
5061553Srgrimes	mib[3] = AF_INET;
5071553Srgrimes	mib[4] = NET_RT_FLAGS;
508186119Sqingli#ifdef RTF_LLINFO
5091553Srgrimes	mib[5] = RTF_LLINFO;
510186119Sqingli#else
511186119Sqingli	mib[5] = 0;
512186119Sqingli#endif
5131553Srgrimes	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
514128054Smux		err(1, "route-sysctl-estimate");
515128192Sluigi	if (needed == 0)	/* empty table */
516128181Sluigi		return 0;
517140735Smaxim	buf = NULL;
518140748Smaxim	for (;;) {
519140735Smaxim		newbuf = realloc(buf, needed);
520140735Smaxim		if (newbuf == NULL) {
521140735Smaxim			if (buf != NULL)
522140735Smaxim				free(buf);
523140735Smaxim			errx(1, "could not reallocate memory");
524140735Smaxim		}
525140735Smaxim		buf = newbuf;
526140735Smaxim		st = sysctl(mib, 6, buf, &needed, NULL, 0);
527140748Smaxim		if (st == 0 || errno != ENOMEM)
528140748Smaxim			break;
529140748Smaxim		needed += needed / 8;
530140748Smaxim	}
531140735Smaxim	if (st == -1)
532128054Smux		err(1, "actual retrieval of routing table");
5331553Srgrimes	lim = buf + needed;
5341553Srgrimes	for (next = buf; next < lim; next += rtm->rtm_msglen) {
5351553Srgrimes		rtm = (struct rt_msghdr *)next;
53687598Smikeh		sin2 = (struct sockaddr_inarp *)(rtm + 1);
537130246Sstefanf		sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
538117729Syar		if (rifname && if_indextoname(sdl->sdl_index, ifname) &&
539117729Syar		    strcmp(ifname, rifname))
540117729Syar			continue;
5411553Srgrimes		if (addr) {
54287598Smikeh			if (addr != sin2->sin_addr.s_addr)
5431553Srgrimes				continue;
5441553Srgrimes			found_entry = 1;
5451553Srgrimes		}
54687598Smikeh		(*action)(sdl, sin2, rtm);
5471553Srgrimes	}
54876216Syar	free(buf);
549147172Sru	return (found_entry);
5501553Srgrimes}
5511553Srgrimes
55231145Sjulian/*
55331145Sjulian * Display an arp entry
55431145Sjulian */
555128181Sluigistatic void
55631145Sjulianprint_entry(struct sockaddr_dl *sdl,
55787598Smikeh	struct sockaddr_inarp *addr, struct rt_msghdr *rtm)
55831145Sjulian{
55977870Sru	const char *host;
56031145Sjulian	struct hostent *hp;
56196202Skbyanc	struct iso88025_sockaddr_dl_data *trld;
56284666Sru	char ifname[IF_NAMESIZE];
56344627Sjulian	int seg;
56431145Sjulian
56531145Sjulian	if (nflag == 0)
56687598Smikeh		hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
56787598Smikeh		    sizeof addr->sin_addr, AF_INET);
56831145Sjulian	else
56931145Sjulian		hp = 0;
57031145Sjulian	if (hp)
57131145Sjulian		host = hp->h_name;
57231145Sjulian	else {
57331145Sjulian		host = "?";
57431145Sjulian		if (h_errno == TRY_AGAIN)
57531145Sjulian			nflag = 1;
57631145Sjulian	}
57787598Smikeh	printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr));
578130408Sdfr	if (sdl->sdl_alen) {
579139270Sru		if ((sdl->sdl_type == IFT_ETHER ||
580151989Sthompsa		    sdl->sdl_type == IFT_L2VLAN ||
581151989Sthompsa		    sdl->sdl_type == IFT_BRIDGE) &&
582130408Sdfr		    sdl->sdl_alen == ETHER_ADDR_LEN)
583130408Sdfr			printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl)));
584130408Sdfr		else {
585130408Sdfr			int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
586130408Sdfr
587130408Sdfr			printf("%s", link_ntoa(sdl) + n);
588130408Sdfr		}
589130408Sdfr	} else
59031145Sjulian		printf("(incomplete)");
59184666Sru	if (if_indextoname(sdl->sdl_index, ifname) != NULL)
59284666Sru		printf(" on %s", ifname);
59331145Sjulian	if (rtm->rtm_rmx.rmx_expire == 0)
59431145Sjulian		printf(" permanent");
59587598Smikeh	if (addr->sin_other & SIN_PROXY)
59631145Sjulian		printf(" published (proxy only)");
597186119Sqingli	if (rtm->rtm_flags & RTF_ANNOUNCE)
598186119Sqingli		printf(" published");
599186119Sqingli	switch(sdl->sdl_type) {
600128181Sluigi	case IFT_ETHER:
60144627Sjulian                printf(" [ethernet]");
60244627Sjulian                break;
603128181Sluigi	case IFT_ISO88025:
60444627Sjulian                printf(" [token-ring]");
60596202Skbyanc		trld = SDL_ISO88025(sdl);
60696202Skbyanc		if (trld->trld_rcf != 0) {
60796202Skbyanc			printf(" rt=%x", ntohs(trld->trld_rcf));
60896202Skbyanc			for (seg = 0;
60996202Skbyanc			     seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2);
61096202Skbyanc			     seg++)
61196235Swpaul				printf(":%x", ntohs(*(trld->trld_route[seg])));
61296202Skbyanc		}
61344627Sjulian                break;
614128181Sluigi	case IFT_FDDI:
61585123Smdodd                printf(" [fddi]");
61685123Smdodd                break;
617128181Sluigi	case IFT_ATM:
61885123Smdodd                printf(" [atm]");
61985123Smdodd                break;
620128181Sluigi	case IFT_L2VLAN:
62175180Syar		printf(" [vlan]");
62275180Syar		break;
623130408Sdfr	case IFT_IEEE1394:
624130408Sdfr                printf(" [firewire]");
625130408Sdfr                break;
626151989Sthompsa	case IFT_BRIDGE:
627151989Sthompsa		printf(" [bridge]");
628151989Sthompsa		break;
629128181Sluigi	default:
63094075Smurray		break;
63144627Sjulian        }
63244627Sjulian
63331145Sjulian	printf("\n");
63444627Sjulian
63531145Sjulian}
63631145Sjulian
63731145Sjulian/*
63831145Sjulian * Nuke an arp entry
63931145Sjulian */
640128181Sluigistatic void
64187598Smikehnuke_entry(struct sockaddr_dl *sdl __unused,
64287598Smikeh	struct sockaddr_inarp *addr, struct rt_msghdr *rtm __unused)
64331145Sjulian{
64431145Sjulian	char ip[20];
64531145Sjulian
64687598Smikeh	snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
647147172Sru	(void)delete(ip, 0);
64831145Sjulian}
64931145Sjulian
650128181Sluigistatic void
65113977Sphkusage(void)
6521553Srgrimes{
65331145Sjulian	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
654117729Syar		"usage: arp [-n] [-i interface] hostname",
655117729Syar		"       arp [-n] [-i interface] -a",
65677870Sru		"       arp -d hostname [pub]",
657154191Sbrooks		"       arp -d [-i interface] -a",
658186485Strhodes		"       arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
659186485Strhodes		"       arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
66029060Scharnier		"       arp -f filename");
6611553Srgrimes	exit(1);
6621553Srgrimes}
6631553Srgrimes
664128181Sluigistatic struct rt_msghdr *
665128192Sluigirtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl)
6661553Srgrimes{
6671553Srgrimes	static int seq;
6681553Srgrimes	int rlen;
669128181Sluigi	int l;
670175206Ssam	struct sockaddr_in so_mask, *som = &so_mask;
671128181Sluigi	static int s = -1;
672128181Sluigi	static pid_t pid;
673128181Sluigi
674128181Sluigi	static struct	{
675128181Sluigi		struct	rt_msghdr m_rtm;
676128181Sluigi		char	m_space[512];
677128181Sluigi	}	m_rtmsg;
678128181Sluigi
679128054Smux	struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
680128054Smux	char *cp = m_rtmsg.m_space;
6811553Srgrimes
682128181Sluigi	if (s < 0) {	/* first time: open socket, get pid */
683128181Sluigi		s = socket(PF_ROUTE, SOCK_RAW, 0);
684128181Sluigi		if (s < 0)
685128181Sluigi			err(1, "socket");
686128181Sluigi		pid = getpid();
687128181Sluigi	}
688128181Sluigi	bzero(&so_mask, sizeof(so_mask));
689128181Sluigi	so_mask.sin_len = 8;
690128181Sluigi	so_mask.sin_addr.s_addr = 0xffffffff;
691128181Sluigi
6921553Srgrimes	errno = 0;
693128192Sluigi	/*
694128192Sluigi	 * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer
695128192Sluigi	 * appropriately.
696128192Sluigi	 */
6971553Srgrimes	if (cmd == RTM_DELETE)
6981553Srgrimes		goto doit;
6991553Srgrimes	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
7001553Srgrimes	rtm->rtm_flags = flags;
7011553Srgrimes	rtm->rtm_version = RTM_VERSION;
7021553Srgrimes
7031553Srgrimes	switch (cmd) {
7041553Srgrimes	default:
70529060Scharnier		errx(1, "internal wrong cmd");
7061553Srgrimes	case RTM_ADD:
7071553Srgrimes		rtm->rtm_addrs |= RTA_GATEWAY;
7081553Srgrimes		rtm->rtm_rmx.rmx_expire = expire_time;
7091553Srgrimes		rtm->rtm_inits = RTV_EXPIRE;
710186500Sqingli		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
711128192Sluigi		dst->sin_other = 0;
7121553Srgrimes		if (doing_proxy) {
71377870Sru			if (proxy_only)
714128192Sluigi				dst->sin_other = SIN_PROXY;
7151553Srgrimes			else {
7161553Srgrimes				rtm->rtm_addrs |= RTA_NETMASK;
7171553Srgrimes				rtm->rtm_flags &= ~RTF_HOST;
7181553Srgrimes			}
7191553Srgrimes		}
7201553Srgrimes		/* FALLTHROUGH */
7211553Srgrimes	case RTM_GET:
7221553Srgrimes		rtm->rtm_addrs |= RTA_DST;
7231553Srgrimes	}
7241553Srgrimes#define NEXTADDR(w, s) \
725147172Sru	if ((s) != NULL && rtm->rtm_addrs & (w)) { \
726147172Sru		bcopy((s), cp, sizeof(*(s))); cp += SA_SIZE(s);}
7271553Srgrimes
728128192Sluigi	NEXTADDR(RTA_DST, dst);
729128192Sluigi	NEXTADDR(RTA_GATEWAY, sdl);
730175206Ssam	NEXTADDR(RTA_NETMASK, som);
7311553Srgrimes
7321553Srgrimes	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
7331553Srgrimesdoit:
7341553Srgrimes	l = rtm->rtm_msglen;
7351553Srgrimes	rtm->rtm_seq = ++seq;
7361553Srgrimes	rtm->rtm_type = cmd;
7371553Srgrimes	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
7381553Srgrimes		if (errno != ESRCH || cmd != RTM_DELETE) {
73929060Scharnier			warn("writing to routing socket");
740147172Sru			return (NULL);
7411553Srgrimes		}
7421553Srgrimes	}
7431553Srgrimes	do {
7441553Srgrimes		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
7451553Srgrimes	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
7461553Srgrimes	if (l < 0)
74729060Scharnier		warn("read from routing socket");
748147172Sru	return (rtm);
7491553Srgrimes}
7501553Srgrimes
75113977Sphk/*
75213977Sphk * get_ether_addr - get the hardware address of an interface on the
75313977Sphk * the same subnet as ipaddr.
75413977Sphk */
75513977Sphk#define MAX_IFS		32
75613977Sphk
757128181Sluigistatic int
758147172Sruget_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr)
75913977Sphk{
76013977Sphk	struct ifreq *ifr, *ifend, *ifp;
761147172Sru	in_addr_t ina, mask;
76213977Sphk	struct sockaddr_dl *dla;
76313977Sphk	struct ifreq ifreq;
76413977Sphk	struct ifconf ifc;
76513977Sphk	struct ifreq ifs[MAX_IFS];
76687598Smikeh	int sock;
767128192Sluigi	int retval = 0;
76813977Sphk
76987598Smikeh	sock = socket(AF_INET, SOCK_DGRAM, 0);
77087598Smikeh	if (sock < 0)
77129060Scharnier		err(1, "socket");
77213977Sphk
77313977Sphk	ifc.ifc_len = sizeof(ifs);
77413977Sphk	ifc.ifc_req = ifs;
77592735Smikeh	if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
77629060Scharnier		warnx("ioctl(SIOCGIFCONF)");
777128192Sluigi		goto done;
77813977Sphk	}
77913977Sphk
780128192Sluigi#define NEXTIFR(i)						\
781128192Sluigi    ((struct ifreq *)((char *)&(i)->ifr_addr			\
782128192Sluigi	+ MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) )
783128192Sluigi
78413977Sphk	/*
785128192Sluigi	 * Scan through looking for an interface with an Internet
786128192Sluigi	 * address on the same subnet as `ipaddr'.
787128192Sluigi	 */
788128192Sluigi	ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
789128192Sluigi	for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) {
790128192Sluigi		if (ifr->ifr_addr.sa_family != AF_INET)
791128192Sluigi			continue;
792128192Sluigi		strncpy(ifreq.ifr_name, ifr->ifr_name,
793128192Sluigi			sizeof(ifreq.ifr_name));
794163305Sglebius		ifreq.ifr_addr = ifr->ifr_addr;
795128192Sluigi		/*
796128192Sluigi		 * Check that the interface is up,
797128192Sluigi		 * and not point-to-point or loopback.
798128192Sluigi		 */
799128192Sluigi		if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0)
800128192Sluigi			continue;
801128192Sluigi		if ((ifreq.ifr_flags &
802128192Sluigi		     (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
803128192Sluigi				IFF_LOOPBACK|IFF_NOARP))
804128192Sluigi		     != (IFF_UP|IFF_BROADCAST))
805128192Sluigi			continue;
806128192Sluigi		/*
807128192Sluigi		 * Get its netmask and check that it's on
808128192Sluigi		 * the right subnet.
809128192Sluigi		 */
810128192Sluigi		if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0)
811128192Sluigi			continue;
812128192Sluigi		mask = ((struct sockaddr_in *)
813128192Sluigi			&ifreq.ifr_addr)->sin_addr.s_addr;
814128192Sluigi		ina = ((struct sockaddr_in *)
815128192Sluigi			&ifr->ifr_addr)->sin_addr.s_addr;
816128192Sluigi		if ((ipaddr & mask) == (ina & mask))
817128192Sluigi			break; /* ok, we got it! */
81813977Sphk	}
81913977Sphk
820128192Sluigi	if (ifr >= ifend)
821128192Sluigi		goto done;
82213977Sphk
82313977Sphk	/*
824128192Sluigi	 * Now scan through again looking for a link-level address
825128192Sluigi	 * for this interface.
826128192Sluigi	 */
82713977Sphk	ifp = ifr;
828128192Sluigi	for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr))
829128192Sluigi		if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 &&
830128192Sluigi		    ifr->ifr_addr.sa_family == AF_LINK)
831128192Sluigi			break;
832128192Sluigi	if (ifr >= ifend)
833128192Sluigi		goto done;
834128192Sluigi	/*
835128192Sluigi	 * Found the link-level address - copy it out
836128192Sluigi	 */
837128192Sluigi	dla = (struct sockaddr_dl *) &ifr->ifr_addr;
838128192Sluigi	memcpy(hwaddr,  LLADDR(dla), dla->sdl_alen);
839128192Sluigi	printf("using interface %s for proxy with address ",
840128192Sluigi		ifp->ifr_name);
841128192Sluigi	printf("%s\n", ether_ntoa(hwaddr));
842128192Sluigi	retval = dla->sdl_alen;
843128192Sluigidone:
844128192Sluigi	close(sock);
845147172Sru	return (retval);
84613977Sphk}
847