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$");
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,
84246143Sglebius	struct sockaddr_in *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
90246143Sglebiusstatic int delete(char *host);
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,
96246143Sglebius    struct sockaddr_in *dst, struct sockaddr_dl *sdl);
97147172Srustatic int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr);
98246143Sglebiusstatic struct sockaddr_in *getaddr(char *host);
99147172Srustatic int valid_type(int type);
100128181Sluigi
10131145Sjulianstatic int nflag;	/* no reverse dns lookups */
102117729Syarstatic char *rifname;
1031553Srgrimes
104203919Srustatic time_t	expire_time;
105246143Sglebiusstatic int	flags, doing_proxy;
106128192Sluigi
10731145Sjulian/* which function we're supposed to do */
10831145Sjulian#define F_GET		1
10931145Sjulian#define F_SET		2
11031145Sjulian#define F_FILESET	3
11131145Sjulian#define F_REPLACE	4
11231145Sjulian#define F_DELETE	5
11331145Sjulian
11431145Sjulian#define SETFUNC(f)	{ if (func) usage(); func = (f); }
11531145Sjulian
11613977Sphkint
11777870Srumain(int argc, char *argv[])
1181553Srgrimes{
11931145Sjulian	int ch, func = 0;
12031145Sjulian	int rtn = 0;
121128181Sluigi	int aflag = 0;	/* do it for all entries */
1221553Srgrimes
123117729Syar	while ((ch = getopt(argc, argv, "andfsSi:")) != -1)
124196216Sremko		switch(ch) {
1251553Srgrimes		case 'a':
12631145Sjulian			aflag = 1;
12731145Sjulian			break;
1281553Srgrimes		case 'd':
12931145Sjulian			SETFUNC(F_DELETE);
13031145Sjulian			break;
1311553Srgrimes		case 'n':
1321553Srgrimes			nflag = 1;
13331145Sjulian			break;
13413977Sphk		case 'S':
13531145Sjulian			SETFUNC(F_REPLACE);
13631145Sjulian			break;
1371553Srgrimes		case 's':
13831145Sjulian			SETFUNC(F_SET);
13931145Sjulian			break;
1409874Sjkh		case 'f' :
14131145Sjulian			SETFUNC(F_FILESET);
14231145Sjulian			break;
143117729Syar		case 'i':
144117729Syar			rifname = optarg;
145117729Syar			break;
1461553Srgrimes		case '?':
1471553Srgrimes		default:
1481553Srgrimes			usage();
1491553Srgrimes		}
15031145Sjulian	argc -= optind;
15131145Sjulian	argv += optind;
15231145Sjulian
15331145Sjulian	if (!func)
15431145Sjulian		func = F_GET;
155117729Syar	if (rifname) {
156154162Sbrooks		if (func != F_GET && !(func == F_DELETE && aflag))
157117729Syar			errx(1, "-i not applicable to this operation");
158117729Syar		if (if_nametoindex(rifname) == 0) {
159117729Syar			if (errno == ENXIO)
160117729Syar				errx(1, "interface %s does not exist", rifname);
161117729Syar			else
162117729Syar				err(1, "if_nametoindex(%s)", rifname);
163117729Syar		}
164117729Syar	}
16531145Sjulian	switch (func) {
16631145Sjulian	case F_GET:
16731145Sjulian		if (aflag) {
16831145Sjulian			if (argc != 0)
16931145Sjulian				usage();
17031145Sjulian			search(0, print_entry);
17131145Sjulian		} else {
17231145Sjulian			if (argc != 1)
17331145Sjulian				usage();
174147172Sru			rtn = get(argv[0]);
17531145Sjulian		}
17631145Sjulian		break;
17731145Sjulian	case F_SET:
17831145Sjulian	case F_REPLACE:
17977870Sru		if (argc < 2 || argc > 6)
18031145Sjulian			usage();
18131145Sjulian		if (func == F_REPLACE)
182246143Sglebius			(void)delete(argv[0]);
18331145Sjulian		rtn = set(argc, argv) ? 1 : 0;
18431145Sjulian		break;
18531145Sjulian	case F_DELETE:
18631145Sjulian		if (aflag) {
18731145Sjulian			if (argc != 0)
18831145Sjulian				usage();
18931145Sjulian			search(0, nuke_entry);
190255821Sglebius		} else {
191255821Sglebius			if (argc != 1)
192255821Sglebius				usage();
193246143Sglebius			rtn = delete(argv[0]);
194255821Sglebius		}
19531145Sjulian		break;
19631145Sjulian	case F_FILESET:
19731145Sjulian		if (argc != 1)
19831145Sjulian			usage();
19931145Sjulian		rtn = file(argv[0]);
20031145Sjulian		break;
20131145Sjulian	}
20231145Sjulian
203147172Sru	return (rtn);
2041553Srgrimes}
2051553Srgrimes
2061553Srgrimes/*
2071553Srgrimes * Process a file to set standard arp entries
2081553Srgrimes */
209128181Sluigistatic int
21013977Sphkfile(char *name)
2111553Srgrimes{
2121553Srgrimes	FILE *fp;
2131553Srgrimes	int i, retval;
214109413Sru	char line[100], arg[5][50], *args[5], *p;
2151553Srgrimes
21629060Scharnier	if ((fp = fopen(name, "r")) == NULL)
217128054Smux		err(1, "cannot open %s", name);
2181553Srgrimes	args[0] = &arg[0][0];
2191553Srgrimes	args[1] = &arg[1][0];
2201553Srgrimes	args[2] = &arg[2][0];
2211553Srgrimes	args[3] = &arg[3][0];
2221553Srgrimes	args[4] = &arg[4][0];
2231553Srgrimes	retval = 0;
224167260Skevlo	while(fgets(line, sizeof(line), fp) != NULL) {
225109413Sru		if ((p = strchr(line, '#')) != NULL)
226109413Sru			*p = '\0';
227109413Sru		for (p = line; isblank(*p); p++);
228111910Sru		if (*p == '\n' || *p == '\0')
229108314Sru			continue;
230109413Sru		i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1],
23156134Sjkh		    arg[2], arg[3], arg[4]);
2321553Srgrimes		if (i < 2) {
23329060Scharnier			warnx("bad line: %s", line);
2341553Srgrimes			retval = 1;
2351553Srgrimes			continue;
2361553Srgrimes		}
2371553Srgrimes		if (set(i, args))
2381553Srgrimes			retval = 1;
2391553Srgrimes	}
2401553Srgrimes	fclose(fp);
2411553Srgrimes	return (retval);
2421553Srgrimes}
2431553Srgrimes
2441553Srgrimes/*
245246143Sglebius * Given a hostname, fills up a (static) struct sockaddr_in with
246128192Sluigi * the address of the host and returns a pointer to the
247128192Sluigi * structure.
248128192Sluigi */
249246143Sglebiusstatic struct sockaddr_in *
250128192Sluigigetaddr(char *host)
251128192Sluigi{
252128192Sluigi	struct hostent *hp;
253246143Sglebius	static struct sockaddr_in reply;
254128192Sluigi
255128192Sluigi	bzero(&reply, sizeof(reply));
256128192Sluigi	reply.sin_len = sizeof(reply);
257128192Sluigi	reply.sin_family = AF_INET;
258128192Sluigi	reply.sin_addr.s_addr = inet_addr(host);
259128192Sluigi	if (reply.sin_addr.s_addr == INADDR_NONE) {
260128192Sluigi		if (!(hp = gethostbyname(host))) {
261128192Sluigi			warnx("%s: %s", host, hstrerror(h_errno));
262147172Sru			return (NULL);
263128192Sluigi		}
264128192Sluigi		bcopy((char *)hp->h_addr, (char *)&reply.sin_addr,
265128192Sluigi			sizeof reply.sin_addr);
266128192Sluigi	}
267147172Sru	return (&reply);
268128192Sluigi}
269128192Sluigi
270128192Sluigi/*
271147172Sru * Returns true if the type is a valid one for ARP.
272128192Sluigi */
273128192Sluigistatic int
274128192Sluigivalid_type(int type)
275128192Sluigi{
276147172Sru
277128192Sluigi	switch (type) {
278128192Sluigi	case IFT_ETHER:
279128192Sluigi	case IFT_FDDI:
280128192Sluigi	case IFT_ISO88023:
281128192Sluigi	case IFT_ISO88024:
282128192Sluigi	case IFT_ISO88025:
283128192Sluigi	case IFT_L2VLAN:
284151989Sthompsa	case IFT_BRIDGE:
285147172Sru		return (1);
286147172Sru	default:
287147172Sru		return (0);
288128192Sluigi	}
289128192Sluigi}
290128192Sluigi
291128192Sluigi/*
2928857Srgrimes * Set an individual arp entry
2931553Srgrimes */
294128192Sluigistatic int
29513977Sphkset(int argc, char **argv)
2961553Srgrimes{
297246143Sglebius	struct sockaddr_in *addr;
298246143Sglebius	struct sockaddr_in *dst;	/* what are we looking for */
299128054Smux	struct sockaddr_dl *sdl;
300128181Sluigi	struct rt_msghdr *rtm;
30193952Sru	struct ether_addr *ea;
3021553Srgrimes	char *host = argv[0], *eaddr = argv[1];
303147172Sru	struct sockaddr_dl sdl_m;
3041553Srgrimes
3051553Srgrimes	argc -= 2;
3061553Srgrimes	argv += 2;
307128181Sluigi
308128181Sluigi	bzero(&sdl_m, sizeof(sdl_m));
309128181Sluigi	sdl_m.sdl_len = sizeof(sdl_m);
310128181Sluigi	sdl_m.sdl_family = AF_LINK;
311128181Sluigi
312128192Sluigi	dst = getaddr(host);
313128192Sluigi	if (dst == NULL)
314128192Sluigi		return (1);
315246143Sglebius	doing_proxy = flags = expire_time = 0;
3161553Srgrimes	while (argc-- > 0) {
3171553Srgrimes		if (strncmp(argv[0], "temp", 4) == 0) {
318216076Sglebius			struct timespec tp;
319216078Sglebius			int max_age;
320216078Sglebius			size_t len = sizeof(max_age);
321216078Sglebius
322216076Sglebius			clock_gettime(CLOCK_MONOTONIC, &tp);
323216078Sglebius			if (sysctlbyname("net.link.ether.inet.max_age",
324216078Sglebius			    &max_age, &len, NULL, 0) != 0)
325216078Sglebius				err(1, "sysctlbyname");
326216078Sglebius			expire_time = tp.tv_sec + max_age;
327177362Ssam		} else if (strncmp(argv[0], "pub", 3) == 0) {
3281553Srgrimes			flags |= RTF_ANNOUNCE;
32977870Sru			doing_proxy = 1;
33077870Sru			if (argc && strncmp(argv[1], "only", 3) == 0) {
331246143Sglebius				/*
332246143Sglebius				 * Compatibility: in pre FreeBSD 8 times
333246143Sglebius				 * the "only" keyword used to mean that
334246143Sglebius				 * an ARP entry should be announced, but
335246143Sglebius				 * not installed into routing table.
336246143Sglebius				 */
33777870Sru				argc--; argv++;
33877870Sru			}
339177362Ssam		} else if (strncmp(argv[0], "blackhole", 9) == 0) {
340186485Strhodes			if (flags & RTF_REJECT) {
341186485Strhodes				printf("Choose one of blackhole or reject, not both.\n");
342186485Strhodes			}
343177362Ssam			flags |= RTF_BLACKHOLE;
344177362Ssam		} else if (strncmp(argv[0], "reject", 6) == 0) {
345186485Strhodes			if (flags & RTF_BLACKHOLE) {
346186485Strhodes				printf("Choose one of blackhole or reject, not both.\n");
347186485Strhodes			}
348177362Ssam			flags |= RTF_REJECT;
3491553Srgrimes		} else if (strncmp(argv[0], "trail", 5) == 0) {
350128192Sluigi			/* XXX deprecated and undocumented feature */
3511553Srgrimes			printf("%s: Sending trailers is no longer supported\n",
3521553Srgrimes				host);
3531553Srgrimes		}
3541553Srgrimes		argv++;
3551553Srgrimes	}
35693952Sru	ea = (struct ether_addr *)LLADDR(&sdl_m);
35713977Sphk	if (doing_proxy && !strcmp(eaddr, "auto")) {
358128192Sluigi		if (!get_ether_addr(dst->sin_addr.s_addr, ea)) {
35973135Sru			printf("no interface found for %s\n",
360128192Sluigi			       inet_ntoa(dst->sin_addr));
36113977Sphk			return (1);
36213977Sphk		}
36393952Sru		sdl_m.sdl_alen = ETHER_ADDR_LEN;
36413977Sphk	} else {
365128192Sluigi		struct ether_addr *ea1 = ether_aton(eaddr);
366128192Sluigi
367155471Sglebius		if (ea1 == NULL) {
368128192Sluigi			warnx("invalid Ethernet address '%s'", eaddr);
369155471Sglebius			return (1);
370155471Sglebius		} else {
371128192Sluigi			*ea = *ea1;
37293952Sru			sdl_m.sdl_alen = ETHER_ADDR_LEN;
373128192Sluigi		}
37413977Sphk	}
375201282Sqingli
376201282Sqingli	/*
377201282Sqingli	 * In the case a proxy-arp entry is being added for
378201282Sqingli	 * a remote end point, the RTF_ANNOUNCE flag in the
379201282Sqingli	 * RTM_GET command is an indication to the kernel
380201282Sqingli	 * routing code that the interface associated with
381201282Sqingli	 * the prefix route covering the local end of the
382201282Sqingli	 * PPP link should be returned, on which ARP applies.
383201282Sqingli	 */
384201282Sqingli	rtm = rtmsg(RTM_GET, dst, &sdl_m);
385201282Sqingli	if (rtm == NULL) {
386201282Sqingli		warn("%s", host);
387201282Sqingli		return (1);
3881553Srgrimes	}
389246143Sglebius	addr = (struct sockaddr_in *)(rtm + 1);
390201282Sqingli	sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
391128192Sluigi
392201282Sqingli	if ((sdl->sdl_family != AF_LINK) ||
393201282Sqingli	    (rtm->rtm_flags & RTF_GATEWAY) ||
394201282Sqingli	    !valid_type(sdl->sdl_type)) {
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{
409246143Sglebius	struct sockaddr_in *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
429246143Sglebiusdelete(char *host)
4301553Srgrimes{
431246143Sglebius	struct sockaddr_in *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);
439186119Sqingli
440186119Sqingli	/*
441201282Sqingli	 * Perform a regular entry delete first.
442201282Sqingli	 */
443201282Sqingli	flags &= ~RTF_ANNOUNCE;
444201282Sqingli
445201282Sqingli	/*
446186119Sqingli	 * setup the data structure to notify the kernel
447186119Sqingli	 * it is the ARP entry the RTM_GET is interested
448186119Sqingli	 * in
449186119Sqingli	 */
450186119Sqingli	bzero(&sdl_m, sizeof(sdl_m));
451186119Sqingli	sdl_m.sdl_len = sizeof(sdl_m);
452186119Sqingli	sdl_m.sdl_family = AF_LINK;
453186119Sqingli
454128192Sluigi	for (;;) {	/* try twice */
455186119Sqingli		rtm = rtmsg(RTM_GET, dst, &sdl_m);
456128192Sluigi		if (rtm == NULL) {
457128192Sluigi			warn("%s", host);
4581553Srgrimes			return (1);
4591553Srgrimes		}
460246143Sglebius		addr = (struct sockaddr_in *)(rtm + 1);
461128192Sluigi		sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
462186119Sqingli
463186119Sqingli		/*
464186119Sqingli		 * With the new L2/L3 restructure, the route
465186119Sqingli		 * returned is a prefix route. The important
466186119Sqingli		 * piece of information from the previous
467186119Sqingli		 * RTM_GET is the interface index. In the
468186119Sqingli		 * case of ECMP, the kernel will traverse
469186119Sqingli		 * the route group for the given entry.
470186119Sqingli		 */
471186119Sqingli		if (sdl->sdl_family == AF_LINK &&
472128192Sluigi		    !(rtm->rtm_flags & RTF_GATEWAY) &&
473186119Sqingli		    valid_type(sdl->sdl_type) ) {
474186119Sqingli			addr->sin_addr.s_addr = dst->sin_addr.s_addr;
475186119Sqingli			break;
476186119Sqingli		}
477186119Sqingli
478201282Sqingli		/*
479201282Sqingli		 * Regualar entry delete failed, now check if there
480201282Sqingli		 * is a proxy-arp entry to remove.
481201282Sqingli		 */
482201282Sqingli		if (flags & RTF_ANNOUNCE) {
483128192Sluigi			fprintf(stderr, "delete: cannot locate %s\n",host);
484128192Sluigi			return (1);
485128192Sluigi		}
486201282Sqingli
487201282Sqingli		flags |= RTF_ANNOUNCE;
4881553Srgrimes	}
489186500Sqingli	rtm->rtm_flags |= RTF_LLDATA;
490128192Sluigi	if (rtmsg(RTM_DELETE, dst, NULL) != NULL) {
49187598Smikeh		printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr));
49213977Sphk		return (0);
49313977Sphk	}
49413977Sphk	return (1);
4951553Srgrimes}
4961553Srgrimes
497201282Sqingli
4981553Srgrimes/*
49931145Sjulian * Search the arp table and do some action on matching entries
5001553Srgrimes */
501128181Sluigistatic int
502128181Sluigisearch(u_long addr, action_fn *action)
5031553Srgrimes{
5041553Srgrimes	int mib[6];
5051553Srgrimes	size_t needed;
506201202Sjhb	char *lim, *buf, *next;
5071553Srgrimes	struct rt_msghdr *rtm;
508246143Sglebius	struct sockaddr_in *sin2;
5091553Srgrimes	struct sockaddr_dl *sdl;
510117729Syar	char ifname[IF_NAMESIZE];
511140735Smaxim	int st, found_entry = 0;
5121553Srgrimes
5131553Srgrimes	mib[0] = CTL_NET;
5141553Srgrimes	mib[1] = PF_ROUTE;
5151553Srgrimes	mib[2] = 0;
5161553Srgrimes	mib[3] = AF_INET;
5171553Srgrimes	mib[4] = NET_RT_FLAGS;
518186119Sqingli#ifdef RTF_LLINFO
5191553Srgrimes	mib[5] = RTF_LLINFO;
520186119Sqingli#else
521186119Sqingli	mib[5] = 0;
522186119Sqingli#endif
5231553Srgrimes	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
524128054Smux		err(1, "route-sysctl-estimate");
525128192Sluigi	if (needed == 0)	/* empty table */
526128181Sluigi		return 0;
527140735Smaxim	buf = NULL;
528140748Smaxim	for (;;) {
529201202Sjhb		buf = reallocf(buf, needed);
530201202Sjhb		if (buf == NULL)
531140735Smaxim			errx(1, "could not reallocate memory");
532140735Smaxim		st = sysctl(mib, 6, buf, &needed, NULL, 0);
533140748Smaxim		if (st == 0 || errno != ENOMEM)
534140748Smaxim			break;
535140748Smaxim		needed += needed / 8;
536140748Smaxim	}
537140735Smaxim	if (st == -1)
538128054Smux		err(1, "actual retrieval of routing table");
5391553Srgrimes	lim = buf + needed;
5401553Srgrimes	for (next = buf; next < lim; next += rtm->rtm_msglen) {
5411553Srgrimes		rtm = (struct rt_msghdr *)next;
542246143Sglebius		sin2 = (struct sockaddr_in *)(rtm + 1);
543130246Sstefanf		sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
544117729Syar		if (rifname && if_indextoname(sdl->sdl_index, ifname) &&
545117729Syar		    strcmp(ifname, rifname))
546117729Syar			continue;
5471553Srgrimes		if (addr) {
54887598Smikeh			if (addr != sin2->sin_addr.s_addr)
5491553Srgrimes				continue;
5501553Srgrimes			found_entry = 1;
5511553Srgrimes		}
55287598Smikeh		(*action)(sdl, sin2, rtm);
5531553Srgrimes	}
55476216Syar	free(buf);
555147172Sru	return (found_entry);
5561553Srgrimes}
5571553Srgrimes
55831145Sjulian/*
55931145Sjulian * Display an arp entry
56031145Sjulian */
561209063Smlaierstatic char lifname[IF_NAMESIZE];
562209063Smlaierstatic int64_t lifindex = -1;
563209063Smlaier
564128181Sluigistatic void
56531145Sjulianprint_entry(struct sockaddr_dl *sdl,
566246143Sglebius	struct sockaddr_in *addr, struct rt_msghdr *rtm)
56731145Sjulian{
56877870Sru	const char *host;
56931145Sjulian	struct hostent *hp;
57096202Skbyanc	struct iso88025_sockaddr_dl_data *trld;
57144627Sjulian	int seg;
57231145Sjulian
57331145Sjulian	if (nflag == 0)
57487598Smikeh		hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
57587598Smikeh		    sizeof addr->sin_addr, AF_INET);
57631145Sjulian	else
57731145Sjulian		hp = 0;
57831145Sjulian	if (hp)
57931145Sjulian		host = hp->h_name;
58031145Sjulian	else {
58131145Sjulian		host = "?";
58231145Sjulian		if (h_errno == TRY_AGAIN)
58331145Sjulian			nflag = 1;
58431145Sjulian	}
58587598Smikeh	printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr));
586130408Sdfr	if (sdl->sdl_alen) {
587139270Sru		if ((sdl->sdl_type == IFT_ETHER ||
588151989Sthompsa		    sdl->sdl_type == IFT_L2VLAN ||
589151989Sthompsa		    sdl->sdl_type == IFT_BRIDGE) &&
590130408Sdfr		    sdl->sdl_alen == ETHER_ADDR_LEN)
591130408Sdfr			printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl)));
592130408Sdfr		else {
593130408Sdfr			int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
594130408Sdfr
595130408Sdfr			printf("%s", link_ntoa(sdl) + n);
596130408Sdfr		}
597130408Sdfr	} else
59831145Sjulian		printf("(incomplete)");
599209063Smlaier	if (sdl->sdl_index != lifindex &&
600209063Smlaier	    if_indextoname(sdl->sdl_index, lifname) != NULL) {
601209063Smlaier        	lifindex = sdl->sdl_index;
602209063Smlaier		printf(" on %s", lifname);
603209063Smlaier        } else if (sdl->sdl_index == lifindex)
604209063Smlaier		printf(" on %s", lifname);
60531145Sjulian	if (rtm->rtm_rmx.rmx_expire == 0)
60631145Sjulian		printf(" permanent");
607203919Sru	else {
608216075Sglebius		static struct timespec tp;
609216075Sglebius		if (tp.tv_sec == 0)
610216075Sglebius			clock_gettime(CLOCK_MONOTONIC, &tp);
611216075Sglebius		if ((expire_time = rtm->rtm_rmx.rmx_expire - tp.tv_sec) > 0)
612203919Sru			printf(" expires in %d seconds", (int)expire_time);
613203919Sru		else
614203919Sru			printf(" expired");
615203919Sru	}
616186119Sqingli	if (rtm->rtm_flags & RTF_ANNOUNCE)
617186119Sqingli		printf(" published");
618186119Sqingli	switch(sdl->sdl_type) {
619128181Sluigi	case IFT_ETHER:
62044627Sjulian                printf(" [ethernet]");
62144627Sjulian                break;
622128181Sluigi	case IFT_ISO88025:
62344627Sjulian                printf(" [token-ring]");
62496202Skbyanc		trld = SDL_ISO88025(sdl);
62596202Skbyanc		if (trld->trld_rcf != 0) {
62696202Skbyanc			printf(" rt=%x", ntohs(trld->trld_rcf));
62796202Skbyanc			for (seg = 0;
62896202Skbyanc			     seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2);
62996202Skbyanc			     seg++)
63096235Swpaul				printf(":%x", ntohs(*(trld->trld_route[seg])));
63196202Skbyanc		}
63244627Sjulian                break;
633128181Sluigi	case IFT_FDDI:
63485123Smdodd                printf(" [fddi]");
63585123Smdodd                break;
636128181Sluigi	case IFT_ATM:
63785123Smdodd                printf(" [atm]");
63885123Smdodd                break;
639128181Sluigi	case IFT_L2VLAN:
64075180Syar		printf(" [vlan]");
64175180Syar		break;
642130408Sdfr	case IFT_IEEE1394:
643130408Sdfr                printf(" [firewire]");
644130408Sdfr                break;
645151989Sthompsa	case IFT_BRIDGE:
646151989Sthompsa		printf(" [bridge]");
647151989Sthompsa		break;
648128181Sluigi	default:
64994075Smurray		break;
65044627Sjulian        }
65144627Sjulian
65231145Sjulian	printf("\n");
65344627Sjulian
65431145Sjulian}
65531145Sjulian
65631145Sjulian/*
65731145Sjulian * Nuke an arp entry
65831145Sjulian */
659128181Sluigistatic void
66087598Smikehnuke_entry(struct sockaddr_dl *sdl __unused,
661246143Sglebius	struct sockaddr_in *addr, struct rt_msghdr *rtm __unused)
66231145Sjulian{
66331145Sjulian	char ip[20];
66431145Sjulian
66587598Smikeh	snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
666246143Sglebius	delete(ip);
66731145Sjulian}
66831145Sjulian
669128181Sluigistatic void
67013977Sphkusage(void)
6711553Srgrimes{
67231145Sjulian	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
673117729Syar		"usage: arp [-n] [-i interface] hostname",
674117729Syar		"       arp [-n] [-i interface] -a",
67577870Sru		"       arp -d hostname [pub]",
676154191Sbrooks		"       arp -d [-i interface] -a",
677186485Strhodes		"       arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
678186485Strhodes		"       arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
67929060Scharnier		"       arp -f filename");
6801553Srgrimes	exit(1);
6811553Srgrimes}
6821553Srgrimes
683128181Sluigistatic struct rt_msghdr *
684246143Sglebiusrtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl)
6851553Srgrimes{
6861553Srgrimes	static int seq;
6871553Srgrimes	int rlen;
688128181Sluigi	int l;
689175206Ssam	struct sockaddr_in so_mask, *som = &so_mask;
690128181Sluigi	static int s = -1;
691128181Sluigi	static pid_t pid;
692128181Sluigi
693128181Sluigi	static struct	{
694128181Sluigi		struct	rt_msghdr m_rtm;
695128181Sluigi		char	m_space[512];
696128181Sluigi	}	m_rtmsg;
697128181Sluigi
698128054Smux	struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
699128054Smux	char *cp = m_rtmsg.m_space;
7001553Srgrimes
701128181Sluigi	if (s < 0) {	/* first time: open socket, get pid */
702128181Sluigi		s = socket(PF_ROUTE, SOCK_RAW, 0);
703128181Sluigi		if (s < 0)
704128181Sluigi			err(1, "socket");
705128181Sluigi		pid = getpid();
706128181Sluigi	}
707128181Sluigi	bzero(&so_mask, sizeof(so_mask));
708128181Sluigi	so_mask.sin_len = 8;
709128181Sluigi	so_mask.sin_addr.s_addr = 0xffffffff;
710128181Sluigi
7111553Srgrimes	errno = 0;
712128192Sluigi	/*
713128192Sluigi	 * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer
714128192Sluigi	 * appropriately.
715128192Sluigi	 */
7161553Srgrimes	if (cmd == RTM_DELETE)
7171553Srgrimes		goto doit;
7181553Srgrimes	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
7191553Srgrimes	rtm->rtm_flags = flags;
7201553Srgrimes	rtm->rtm_version = RTM_VERSION;
7211553Srgrimes
7221553Srgrimes	switch (cmd) {
7231553Srgrimes	default:
72429060Scharnier		errx(1, "internal wrong cmd");
7251553Srgrimes	case RTM_ADD:
7261553Srgrimes		rtm->rtm_addrs |= RTA_GATEWAY;
7271553Srgrimes		rtm->rtm_rmx.rmx_expire = expire_time;
7281553Srgrimes		rtm->rtm_inits = RTV_EXPIRE;
729186500Sqingli		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
7301553Srgrimes		if (doing_proxy) {
731246143Sglebius			rtm->rtm_addrs |= RTA_NETMASK;
732246143Sglebius			rtm->rtm_flags &= ~RTF_HOST;
7331553Srgrimes		}
7341553Srgrimes		/* FALLTHROUGH */
7351553Srgrimes	case RTM_GET:
7361553Srgrimes		rtm->rtm_addrs |= RTA_DST;
7371553Srgrimes	}
738193976Sdes#define NEXTADDR(w, s)					   \
739193976Sdes	do {						   \
740193976Sdes		if ((s) != NULL && rtm->rtm_addrs & (w)) { \
741193976Sdes			bcopy((s), cp, sizeof(*(s)));	   \
742193976Sdes			cp += SA_SIZE(s);		   \
743193976Sdes		}					   \
744193976Sdes	} while (0)
7451553Srgrimes
746128192Sluigi	NEXTADDR(RTA_DST, dst);
747128192Sluigi	NEXTADDR(RTA_GATEWAY, sdl);
748175206Ssam	NEXTADDR(RTA_NETMASK, som);
7491553Srgrimes
7501553Srgrimes	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
7511553Srgrimesdoit:
7521553Srgrimes	l = rtm->rtm_msglen;
7531553Srgrimes	rtm->rtm_seq = ++seq;
7541553Srgrimes	rtm->rtm_type = cmd;
7551553Srgrimes	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
7561553Srgrimes		if (errno != ESRCH || cmd != RTM_DELETE) {
75729060Scharnier			warn("writing to routing socket");
758147172Sru			return (NULL);
7591553Srgrimes		}
7601553Srgrimes	}
7611553Srgrimes	do {
7621553Srgrimes		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
7631553Srgrimes	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
7641553Srgrimes	if (l < 0)
76529060Scharnier		warn("read from routing socket");
766147172Sru	return (rtm);
7671553Srgrimes}
7681553Srgrimes
76913977Sphk/*
77013977Sphk * get_ether_addr - get the hardware address of an interface on the
77113977Sphk * the same subnet as ipaddr.
77213977Sphk */
77313977Sphk#define MAX_IFS		32
77413977Sphk
775128181Sluigistatic int
776147172Sruget_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr)
77713977Sphk{
77813977Sphk	struct ifreq *ifr, *ifend, *ifp;
779147172Sru	in_addr_t ina, mask;
78013977Sphk	struct sockaddr_dl *dla;
78113977Sphk	struct ifreq ifreq;
78213977Sphk	struct ifconf ifc;
78313977Sphk	struct ifreq ifs[MAX_IFS];
78487598Smikeh	int sock;
785128192Sluigi	int retval = 0;
78613977Sphk
78787598Smikeh	sock = socket(AF_INET, SOCK_DGRAM, 0);
78887598Smikeh	if (sock < 0)
78929060Scharnier		err(1, "socket");
79013977Sphk
79113977Sphk	ifc.ifc_len = sizeof(ifs);
79213977Sphk	ifc.ifc_req = ifs;
79392735Smikeh	if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
79429060Scharnier		warnx("ioctl(SIOCGIFCONF)");
795128192Sluigi		goto done;
79613977Sphk	}
79713977Sphk
798128192Sluigi#define NEXTIFR(i)						\
799128192Sluigi    ((struct ifreq *)((char *)&(i)->ifr_addr			\
800128192Sluigi	+ MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) )
801128192Sluigi
80213977Sphk	/*
803128192Sluigi	 * Scan through looking for an interface with an Internet
804128192Sluigi	 * address on the same subnet as `ipaddr'.
805128192Sluigi	 */
806128192Sluigi	ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
807128192Sluigi	for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) {
808128192Sluigi		if (ifr->ifr_addr.sa_family != AF_INET)
809128192Sluigi			continue;
810128192Sluigi		strncpy(ifreq.ifr_name, ifr->ifr_name,
811128192Sluigi			sizeof(ifreq.ifr_name));
812163305Sglebius		ifreq.ifr_addr = ifr->ifr_addr;
813128192Sluigi		/*
814128192Sluigi		 * Check that the interface is up,
815128192Sluigi		 * and not point-to-point or loopback.
816128192Sluigi		 */
817128192Sluigi		if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0)
818128192Sluigi			continue;
819128192Sluigi		if ((ifreq.ifr_flags &
820128192Sluigi		     (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
821128192Sluigi				IFF_LOOPBACK|IFF_NOARP))
822128192Sluigi		     != (IFF_UP|IFF_BROADCAST))
823128192Sluigi			continue;
824128192Sluigi		/*
825128192Sluigi		 * Get its netmask and check that it's on
826128192Sluigi		 * the right subnet.
827128192Sluigi		 */
828128192Sluigi		if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0)
829128192Sluigi			continue;
830128192Sluigi		mask = ((struct sockaddr_in *)
831128192Sluigi			&ifreq.ifr_addr)->sin_addr.s_addr;
832128192Sluigi		ina = ((struct sockaddr_in *)
833128192Sluigi			&ifr->ifr_addr)->sin_addr.s_addr;
834128192Sluigi		if ((ipaddr & mask) == (ina & mask))
835128192Sluigi			break; /* ok, we got it! */
83613977Sphk	}
83713977Sphk
838128192Sluigi	if (ifr >= ifend)
839128192Sluigi		goto done;
84013977Sphk
84113977Sphk	/*
842128192Sluigi	 * Now scan through again looking for a link-level address
843128192Sluigi	 * for this interface.
844128192Sluigi	 */
84513977Sphk	ifp = ifr;
846128192Sluigi	for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr))
847128192Sluigi		if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 &&
848128192Sluigi		    ifr->ifr_addr.sa_family == AF_LINK)
849128192Sluigi			break;
850128192Sluigi	if (ifr >= ifend)
851128192Sluigi		goto done;
852128192Sluigi	/*
853128192Sluigi	 * Found the link-level address - copy it out
854128192Sluigi	 */
855128192Sluigi	dla = (struct sockaddr_dl *) &ifr->ifr_addr;
856128192Sluigi	memcpy(hwaddr,  LLADDR(dla), dla->sdl_alen);
857128192Sluigi	printf("using interface %s for proxy with address ",
858128192Sluigi		ifp->ifr_name);
859128192Sluigi	printf("%s\n", ether_ntoa(hwaddr));
860128192Sluigi	retval = dla->sdl_alen;
861128192Sluigidone:
862128192Sluigi	close(sock);
863147172Sru	return (retval);
86413977Sphk}
865