arp.c revision 201282
1250003Sadrian/*
2250003Sadrian * Copyright (c) 1984, 1993
3250003Sadrian *	The Regents of the University of California.  All rights reserved.
4250003Sadrian *
5250003Sadrian * This code is derived from software contributed to Berkeley by
6250003Sadrian * Sun Microsystems, Inc.
7250003Sadrian *
8250003Sadrian * Redistribution and use in source and binary forms, with or without
9250003Sadrian * modification, are permitted provided that the following conditions
10250003Sadrian * are met:
11250003Sadrian * 1. Redistributions of source code must retain the above copyright
12250003Sadrian *    notice, this list of conditions and the following disclaimer.
13250003Sadrian * 2. Redistributions in binary form must reproduce the above copyright
14250003Sadrian *    notice, this list of conditions and the following disclaimer in the
15250003Sadrian *    documentation and/or other materials provided with the distribution.
16250003Sadrian * 4. Neither the name of the University nor the names of its contributors
17250003Sadrian *    may be used to endorse or promote products derived from this software
18250003Sadrian *    without specific prior written permission.
19250003Sadrian *
20250003Sadrian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21250003Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22250003Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23250003Sadrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24250003Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25250003Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26250003Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27250003Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28250003Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29250003Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30250003Sadrian * SUCH DAMAGE.
31250003Sadrian */
32250003Sadrian
33250003Sadrian#if 0
34250003Sadrian#ifndef lint
35250003Sadrianstatic char const copyright[] =
36250003Sadrian"@(#) Copyright (c) 1984, 1993\n\
37250003Sadrian	The Regents of the University of California.  All rights reserved.\n";
38250003Sadrian#endif /* not lint */
39250003Sadrian
40250003Sadrian#ifndef lint
41250003Sadrianstatic char const sccsid[] = "@(#)from: arp.c	8.2 (Berkeley) 1/2/94";
42250003Sadrian#endif /* not lint */
43250003Sadrian#endif
44250003Sadrian#include <sys/cdefs.h>
45250003Sadrian__FBSDID("$FreeBSD: head/usr.sbin/arp/arp.c 201282 2009-12-30 21:35:34Z qingli $");
46250003Sadrian
47250003Sadrian/*
48250003Sadrian * arp - display, set, and delete arp table entries
49250003Sadrian */
50250003Sadrian
51250003Sadrian
52250003Sadrian#include <sys/param.h>
53250003Sadrian#include <sys/file.h>
54250003Sadrian#include <sys/socket.h>
55250003Sadrian#include <sys/sockio.h>
56250003Sadrian#include <sys/sysctl.h>
57250003Sadrian#include <sys/ioctl.h>
58250003Sadrian#include <sys/time.h>
59250003Sadrian
60250003Sadrian#include <net/if.h>
61250003Sadrian#include <net/if_dl.h>
62250003Sadrian#include <net/if_types.h>
63250003Sadrian#include <net/route.h>
64250003Sadrian#include <net/iso88025.h>
65250003Sadrian
66250003Sadrian#include <netinet/in.h>
67250003Sadrian#include <netinet/if_ether.h>
68250003Sadrian
69250003Sadrian#include <arpa/inet.h>
70250003Sadrian
71250003Sadrian#include <ctype.h>
72250003Sadrian#include <err.h>
73250003Sadrian#include <errno.h>
74250003Sadrian#include <netdb.h>
75250003Sadrian#include <nlist.h>
76250003Sadrian#include <paths.h>
77250003Sadrian#include <stdio.h>
78250003Sadrian#include <stdlib.h>
79250003Sadrian#include <string.h>
80250003Sadrian#include <strings.h>
81250003Sadrian#include <unistd.h>
82250003Sadrian
83250003Sadriantypedef void (action_fn)(struct sockaddr_dl *sdl,
84250003Sadrian	struct sockaddr_inarp *s_in, struct rt_msghdr *rtm);
85250003Sadrian
86250003Sadrianstatic int search(u_long addr, action_fn *action);
87250003Sadrianstatic action_fn print_entry;
88250003Sadrianstatic action_fn nuke_entry;
89250003Sadrian
90250003Sadrianstatic int delete(char *host, int do_proxy);
91250003Sadrianstatic void usage(void);
92250003Sadrianstatic int set(int argc, char **argv);
93250003Sadrianstatic int get(char *host);
94250003Sadrianstatic int file(char *name);
95250003Sadrianstatic struct rt_msghdr *rtmsg(int cmd,
96250003Sadrian    struct sockaddr_inarp *dst, struct sockaddr_dl *sdl);
97250003Sadrianstatic int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr);
98250003Sadrianstatic struct sockaddr_inarp *getaddr(char *host);
99250003Sadrianstatic int valid_type(int type);
100250003Sadrian
101250003Sadrianstatic int nflag;	/* no reverse dns lookups */
102250003Sadrianstatic char *rifname;
103250003Sadrian
104250003Sadrianstatic int	expire_time, flags, doing_proxy, proxy_only;
105250003Sadrian
106250003Sadrian/* which function we're supposed to do */
107250003Sadrian#define F_GET		1
108250003Sadrian#define F_SET		2
109250003Sadrian#define F_FILESET	3
110250003Sadrian#define F_REPLACE	4
111250003Sadrian#define F_DELETE	5
112250003Sadrian
113250003Sadrian#define SETFUNC(f)	{ if (func) usage(); func = (f); }
114250003Sadrian
115250003Sadrianint
116250003Sadrianmain(int argc, char *argv[])
117250003Sadrian{
118250003Sadrian	int ch, func = 0;
119250003Sadrian	int rtn = 0;
120250003Sadrian	int aflag = 0;	/* do it for all entries */
121250003Sadrian
122250003Sadrian	while ((ch = getopt(argc, argv, "andfsSi:")) != -1)
123250003Sadrian		switch(ch) {
124250003Sadrian		case 'a':
125250003Sadrian			aflag = 1;
126250003Sadrian			break;
127250003Sadrian		case 'd':
128250003Sadrian			SETFUNC(F_DELETE);
129250003Sadrian			break;
130250003Sadrian		case 'n':
131250003Sadrian			nflag = 1;
132250003Sadrian			break;
133250003Sadrian		case 'S':
134250003Sadrian			SETFUNC(F_REPLACE);
135250003Sadrian			break;
136250003Sadrian		case 's':
137250003Sadrian			SETFUNC(F_SET);
138250003Sadrian			break;
139250003Sadrian		case 'f' :
140250003Sadrian			SETFUNC(F_FILESET);
141250003Sadrian			break;
142250003Sadrian		case 'i':
143250003Sadrian			rifname = optarg;
144250003Sadrian			break;
145250003Sadrian		case '?':
146250003Sadrian		default:
147250003Sadrian			usage();
148250003Sadrian		}
149250003Sadrian	argc -= optind;
150250003Sadrian	argv += optind;
151250003Sadrian
152250003Sadrian	if (!func)
153250003Sadrian		func = F_GET;
154250003Sadrian	if (rifname) {
155250003Sadrian		if (func != F_GET && !(func == F_DELETE && aflag))
156250003Sadrian			errx(1, "-i not applicable to this operation");
157250003Sadrian		if (if_nametoindex(rifname) == 0) {
158250003Sadrian			if (errno == ENXIO)
159250003Sadrian				errx(1, "interface %s does not exist", rifname);
160250003Sadrian			else
161250003Sadrian				err(1, "if_nametoindex(%s)", rifname);
162250008Sadrian		}
163250008Sadrian	}
164250003Sadrian	switch (func) {
165250003Sadrian	case F_GET:
166250003Sadrian		if (aflag) {
167250003Sadrian			if (argc != 0)
168250003Sadrian				usage();
169250003Sadrian			search(0, print_entry);
170250003Sadrian		} else {
171250003Sadrian			if (argc != 1)
172250003Sadrian				usage();
173250008Sadrian			rtn = get(argv[0]);
174250003Sadrian		}
175250003Sadrian		break;
176250003Sadrian	case F_SET:
177250003Sadrian	case F_REPLACE:
178250003Sadrian		if (argc < 2 || argc > 6)
179250003Sadrian			usage();
180250003Sadrian		if (func == F_REPLACE)
181250003Sadrian			(void)delete(argv[0], 0);
182250003Sadrian		rtn = set(argc, argv) ? 1 : 0;
183250003Sadrian		break;
184250003Sadrian	case F_DELETE:
185250003Sadrian		if (aflag) {
186250003Sadrian			if (argc != 0)
187250003Sadrian				usage();
188250003Sadrian			search(0, nuke_entry);
189250003Sadrian		} else {
190250003Sadrian			if (argc == 2 && strncmp(argv[1], "pub", 3) == 0)
191250003Sadrian				ch = SIN_PROXY;
192250003Sadrian			else if (argc == 1)
193250003Sadrian				ch = 0;
194250003Sadrian			else
195250008Sadrian				usage();
196250003Sadrian			rtn = delete(argv[0], ch);
197250003Sadrian		}
198250003Sadrian		break;
199250003Sadrian	case F_FILESET:
200250003Sadrian		if (argc != 1)
201250003Sadrian			usage();
202250003Sadrian		rtn = file(argv[0]);
203250003Sadrian		break;
204250003Sadrian	}
205250003Sadrian
206250003Sadrian	return (rtn);
207250003Sadrian}
208250003Sadrian
209250003Sadrian/*
210250003Sadrian * Process a file to set standard arp entries
211250003Sadrian */
212250003Sadrianstatic int
213250008Sadrianfile(char *name)
214250008Sadrian{
215250003Sadrian	FILE *fp;
216250003Sadrian	int i, retval;
217250003Sadrian	char line[100], arg[5][50], *args[5], *p;
218250003Sadrian
219250003Sadrian	if ((fp = fopen(name, "r")) == NULL)
220250003Sadrian		err(1, "cannot open %s", name);
221250003Sadrian	args[0] = &arg[0][0];
222250003Sadrian	args[1] = &arg[1][0];
223250003Sadrian	args[2] = &arg[2][0];
224250003Sadrian	args[3] = &arg[3][0];
225250003Sadrian	args[4] = &arg[4][0];
226250003Sadrian	retval = 0;
227250003Sadrian	while(fgets(line, sizeof(line), fp) != NULL) {
228250003Sadrian		if ((p = strchr(line, '#')) != NULL)
229250003Sadrian			*p = '\0';
230250003Sadrian		for (p = line; isblank(*p); p++);
231250003Sadrian		if (*p == '\n' || *p == '\0')
232250003Sadrian			continue;
233250003Sadrian		i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1],
234250003Sadrian		    arg[2], arg[3], arg[4]);
235250003Sadrian		if (i < 2) {
236250003Sadrian			warnx("bad line: %s", line);
237250003Sadrian			retval = 1;
238250003Sadrian			continue;
239250003Sadrian		}
240250003Sadrian		if (set(i, args))
241250003Sadrian			retval = 1;
242250003Sadrian	}
243250003Sadrian	fclose(fp);
244250003Sadrian	return (retval);
245250003Sadrian}
246250003Sadrian
247250003Sadrian/*
248250003Sadrian * Given a hostname, fills up a (static) struct sockaddr_inarp with
249250003Sadrian * the address of the host and returns a pointer to the
250250003Sadrian * structure.
251250003Sadrian */
252250003Sadrianstatic struct sockaddr_inarp *
253250003Sadriangetaddr(char *host)
254250003Sadrian{
255250003Sadrian	struct hostent *hp;
256250003Sadrian	static struct sockaddr_inarp reply;
257250003Sadrian
258250003Sadrian	bzero(&reply, sizeof(reply));
259250003Sadrian	reply.sin_len = sizeof(reply);
260250003Sadrian	reply.sin_family = AF_INET;
261250003Sadrian	reply.sin_addr.s_addr = inet_addr(host);
262250003Sadrian	if (reply.sin_addr.s_addr == INADDR_NONE) {
263250003Sadrian		if (!(hp = gethostbyname(host))) {
264250003Sadrian			warnx("%s: %s", host, hstrerror(h_errno));
265250003Sadrian			return (NULL);
266250003Sadrian		}
267250003Sadrian		bcopy((char *)hp->h_addr, (char *)&reply.sin_addr,
268250003Sadrian			sizeof reply.sin_addr);
269250003Sadrian	}
270250003Sadrian	return (&reply);
271250003Sadrian}
272250003Sadrian
273250003Sadrian/*
274250003Sadrian * Returns true if the type is a valid one for ARP.
275250003Sadrian */
276250003Sadrianstatic int
277250003Sadrianvalid_type(int type)
278250003Sadrian{
279250003Sadrian
280250003Sadrian	switch (type) {
281250003Sadrian	case IFT_ETHER:
282250003Sadrian	case IFT_FDDI:
283250003Sadrian	case IFT_ISO88023:
284250003Sadrian	case IFT_ISO88024:
285250003Sadrian	case IFT_ISO88025:
286250003Sadrian	case IFT_L2VLAN:
287250003Sadrian	case IFT_BRIDGE:
288250003Sadrian		return (1);
289250003Sadrian	default:
290250003Sadrian		return (0);
291250003Sadrian	}
292250003Sadrian}
293250003Sadrian
294250003Sadrian/*
295250003Sadrian * Set an individual arp entry
296250003Sadrian */
297250003Sadrianstatic int
298250003Sadrianset(int argc, char **argv)
299250003Sadrian{
300250003Sadrian	struct sockaddr_inarp *addr;
301250003Sadrian	struct sockaddr_inarp *dst;	/* what are we looking for */
302250003Sadrian	struct sockaddr_dl *sdl;
303250003Sadrian	struct rt_msghdr *rtm;
304250003Sadrian	struct ether_addr *ea;
305250003Sadrian	char *host = argv[0], *eaddr = argv[1];
306250003Sadrian	struct sockaddr_dl sdl_m;
307250003Sadrian
308250003Sadrian	argc -= 2;
309250003Sadrian	argv += 2;
310250003Sadrian
311250003Sadrian	bzero(&sdl_m, sizeof(sdl_m));
312250003Sadrian	sdl_m.sdl_len = sizeof(sdl_m);
313250003Sadrian	sdl_m.sdl_family = AF_LINK;
314250003Sadrian
315250003Sadrian	dst = getaddr(host);
316250003Sadrian	if (dst == NULL)
317250003Sadrian		return (1);
318250003Sadrian	doing_proxy = flags = proxy_only = expire_time = 0;
319250008Sadrian	while (argc-- > 0) {
320250003Sadrian		if (strncmp(argv[0], "temp", 4) == 0) {
321250003Sadrian			struct timeval tv;
322250003Sadrian			gettimeofday(&tv, 0);
323250003Sadrian			expire_time = tv.tv_sec + 20 * 60;
324250003Sadrian		} else if (strncmp(argv[0], "pub", 3) == 0) {
325250008Sadrian			flags |= RTF_ANNOUNCE;
326250003Sadrian			doing_proxy = 1;
327250003Sadrian			if (argc && strncmp(argv[1], "only", 3) == 0) {
328250003Sadrian				proxy_only = 1;
329250003Sadrian				argc--; argv++;
330250003Sadrian			}
331250003Sadrian		} else if (strncmp(argv[0], "blackhole", 9) == 0) {
332250003Sadrian			if (flags & RTF_REJECT) {
333250003Sadrian				printf("Choose one of blackhole or reject, not both.\n");
334250003Sadrian			}
335250003Sadrian			flags |= RTF_BLACKHOLE;
336250003Sadrian		} else if (strncmp(argv[0], "reject", 6) == 0) {
337250003Sadrian			if (flags & RTF_BLACKHOLE) {
338250003Sadrian				printf("Choose one of blackhole or reject, not both.\n");
339250003Sadrian			}
340250008Sadrian			flags |= RTF_REJECT;
341250003Sadrian		} else if (strncmp(argv[0], "trail", 5) == 0) {
342250003Sadrian			/* XXX deprecated and undocumented feature */
343250003Sadrian			printf("%s: Sending trailers is no longer supported\n",
344250008Sadrian				host);
345250003Sadrian		}
346250003Sadrian		argv++;
347250003Sadrian	}
348250003Sadrian	ea = (struct ether_addr *)LLADDR(&sdl_m);
349250003Sadrian	if (doing_proxy && !strcmp(eaddr, "auto")) {
350250003Sadrian		if (!get_ether_addr(dst->sin_addr.s_addr, ea)) {
351250003Sadrian			printf("no interface found for %s\n",
352250003Sadrian			       inet_ntoa(dst->sin_addr));
353250003Sadrian			return (1);
354250003Sadrian		}
355250003Sadrian		sdl_m.sdl_alen = ETHER_ADDR_LEN;
356250003Sadrian	} else {
357250003Sadrian		struct ether_addr *ea1 = ether_aton(eaddr);
358250003Sadrian
359250003Sadrian		if (ea1 == NULL) {
360250003Sadrian			warnx("invalid Ethernet address '%s'", eaddr);
361250003Sadrian			return (1);
362250003Sadrian		} else {
363250003Sadrian			*ea = *ea1;
364250003Sadrian			sdl_m.sdl_alen = ETHER_ADDR_LEN;
365250003Sadrian		}
366250003Sadrian	}
367250003Sadrian
368250003Sadrian	/*
369250003Sadrian	 * In the case a proxy-arp entry is being added for
370250003Sadrian	 * a remote end point, the RTF_ANNOUNCE flag in the
371250003Sadrian	 * RTM_GET command is an indication to the kernel
372250003Sadrian	 * routing code that the interface associated with
373250003Sadrian	 * the prefix route covering the local end of the
374250003Sadrian	 * PPP link should be returned, on which ARP applies.
375250003Sadrian	 */
376250003Sadrian	rtm = rtmsg(RTM_GET, dst, &sdl_m);
377250003Sadrian	if (rtm == NULL) {
378250003Sadrian		warn("%s", host);
379250003Sadrian		return (1);
380250003Sadrian	}
381250003Sadrian	addr = (struct sockaddr_inarp *)(rtm + 1);
382250003Sadrian	sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
383250003Sadrian	if (addr->sin_addr.s_addr == dst->sin_addr.s_addr) {
384250003Sadrian		printf("set: proxy entry exists for non 802 device\n");
385250003Sadrian		return (1);
386250003Sadrian	}
387250003Sadrian
388250003Sadrian	if ((sdl->sdl_family != AF_LINK) ||
389250003Sadrian	    (rtm->rtm_flags & RTF_GATEWAY) ||
390250003Sadrian	    !valid_type(sdl->sdl_type)) {
391250003Sadrian		printf("cannot intuit interface index and type for %s\n", host);
392250003Sadrian		return (1);
393250003Sadrian	}
394250003Sadrian	sdl_m.sdl_type = sdl->sdl_type;
395250003Sadrian	sdl_m.sdl_index = sdl->sdl_index;
396250003Sadrian	return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL);
397250003Sadrian}
398250003Sadrian
399250003Sadrian/*
400250003Sadrian * Display an individual arp entry
401250003Sadrian */
402250003Sadrianstatic int
403250003Sadrianget(char *host)
404250003Sadrian{
405250003Sadrian	struct sockaddr_inarp *addr;
406250003Sadrian
407250003Sadrian	addr = getaddr(host);
408250003Sadrian	if (addr == NULL)
409250003Sadrian		return (1);
410250003Sadrian	if (0 == search(addr->sin_addr.s_addr, print_entry)) {
411250003Sadrian		printf("%s (%s) -- no entry",
412250003Sadrian		    host, inet_ntoa(addr->sin_addr));
413250003Sadrian		if (rifname)
414250003Sadrian			printf(" on %s", rifname);
415250003Sadrian		printf("\n");
416250003Sadrian		return (1);
417250003Sadrian	}
418250003Sadrian	return (0);
419250003Sadrian}
420250003Sadrian
421250003Sadrian/*
422250003Sadrian * Delete an arp entry
423250003Sadrian */
424250003Sadrianstatic int
425250003Sadriandelete(char *host, int do_proxy)
426250003Sadrian{
427250003Sadrian	struct sockaddr_inarp *addr, *dst;
428250003Sadrian	struct rt_msghdr *rtm;
429250003Sadrian	struct sockaddr_dl *sdl;
430250003Sadrian	struct sockaddr_dl sdl_m;
431250003Sadrian
432250003Sadrian	dst = getaddr(host);
433250003Sadrian	if (dst == NULL)
434250003Sadrian		return (1);
435250003Sadrian
436250003Sadrian	/*
437250003Sadrian	 * Perform a regular entry delete first.
438250003Sadrian	 */
439250003Sadrian	flags &= ~RTF_ANNOUNCE;
440250003Sadrian
441250003Sadrian	/*
442250003Sadrian	 * setup the data structure to notify the kernel
443250003Sadrian	 * it is the ARP entry the RTM_GET is interested
444250003Sadrian	 * in
445250003Sadrian	 */
446250003Sadrian	bzero(&sdl_m, sizeof(sdl_m));
447250003Sadrian	sdl_m.sdl_len = sizeof(sdl_m);
448250003Sadrian	sdl_m.sdl_family = AF_LINK;
449250003Sadrian
450250003Sadrian	for (;;) {	/* try twice */
451250003Sadrian		rtm = rtmsg(RTM_GET, dst, &sdl_m);
452250003Sadrian		if (rtm == NULL) {
453250003Sadrian			warn("%s", host);
454250003Sadrian			return (1);
455250003Sadrian		}
456250003Sadrian		addr = (struct sockaddr_inarp *)(rtm + 1);
457250003Sadrian		sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
458250003Sadrian
459250003Sadrian		/*
460250003Sadrian		 * With the new L2/L3 restructure, the route
461250003Sadrian		 * returned is a prefix route. The important
462250003Sadrian		 * piece of information from the previous
463250003Sadrian		 * RTM_GET is the interface index. In the
464250003Sadrian		 * case of ECMP, the kernel will traverse
465250003Sadrian		 * the route group for the given entry.
466250003Sadrian		 */
467250003Sadrian		if (sdl->sdl_family == AF_LINK &&
468250003Sadrian		    !(rtm->rtm_flags & RTF_GATEWAY) &&
469250003Sadrian		    valid_type(sdl->sdl_type) ) {
470250003Sadrian			addr->sin_addr.s_addr = dst->sin_addr.s_addr;
471250003Sadrian			break;
472250003Sadrian		}
473250003Sadrian
474250003Sadrian		/*
475250003Sadrian		 * Regualar entry delete failed, now check if there
476250003Sadrian		 * is a proxy-arp entry to remove.
477250003Sadrian		 */
478250003Sadrian		if (flags & RTF_ANNOUNCE) {
479250003Sadrian			fprintf(stderr, "delete: cannot locate %s\n",host);
480250003Sadrian			return (1);
481250003Sadrian		}
482250003Sadrian
483250003Sadrian		flags |= RTF_ANNOUNCE;
484250003Sadrian	}
485250003Sadrian	rtm->rtm_flags |= RTF_LLDATA;
486250003Sadrian	if (rtmsg(RTM_DELETE, dst, NULL) != NULL) {
487250003Sadrian		printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr));
488250003Sadrian		return (0);
489250003Sadrian	}
490250003Sadrian	return (1);
491250003Sadrian}
492250003Sadrian
493250003Sadrian
494250003Sadrian/*
495250003Sadrian * Search the arp table and do some action on matching entries
496250003Sadrian */
497250003Sadrianstatic int
498250003Sadriansearch(u_long addr, action_fn *action)
499250003Sadrian{
500250003Sadrian	int mib[6];
501250003Sadrian	size_t needed;
502250003Sadrian	char *lim, *buf, *next;
503250003Sadrian	struct rt_msghdr *rtm;
504250003Sadrian	struct sockaddr_inarp *sin2;
505250003Sadrian	struct sockaddr_dl *sdl;
506250003Sadrian	char ifname[IF_NAMESIZE];
507250008Sadrian	int st, found_entry = 0;
508250003Sadrian
509250003Sadrian	mib[0] = CTL_NET;
510250003Sadrian	mib[1] = PF_ROUTE;
511250008Sadrian	mib[2] = 0;
512250003Sadrian	mib[3] = AF_INET;
513250003Sadrian	mib[4] = NET_RT_FLAGS;
514250003Sadrian#ifdef RTF_LLINFO
515250003Sadrian	mib[5] = RTF_LLINFO;
516250003Sadrian#else
517250003Sadrian	mib[5] = 0;
518250003Sadrian#endif
519250003Sadrian	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
520250003Sadrian		err(1, "route-sysctl-estimate");
521250003Sadrian	if (needed == 0)	/* empty table */
522250003Sadrian		return 0;
523250003Sadrian	buf = NULL;
524250003Sadrian	for (;;) {
525250003Sadrian		buf = reallocf(buf, needed);
526250003Sadrian		if (buf == NULL)
527250003Sadrian			errx(1, "could not reallocate memory");
528250003Sadrian		st = sysctl(mib, 6, buf, &needed, NULL, 0);
529250003Sadrian		if (st == 0 || errno != ENOMEM)
530250003Sadrian			break;
531250003Sadrian		needed += needed / 8;
532250003Sadrian	}
533250003Sadrian	if (st == -1)
534250003Sadrian		err(1, "actual retrieval of routing table");
535250008Sadrian	lim = buf + needed;
536250003Sadrian	for (next = buf; next < lim; next += rtm->rtm_msglen) {
537250003Sadrian		rtm = (struct rt_msghdr *)next;
538250003Sadrian		sin2 = (struct sockaddr_inarp *)(rtm + 1);
539250003Sadrian		sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
540250003Sadrian		if (rifname && if_indextoname(sdl->sdl_index, ifname) &&
541250003Sadrian		    strcmp(ifname, rifname))
542250003Sadrian			continue;
543250003Sadrian		if (addr) {
544250003Sadrian			if (addr != sin2->sin_addr.s_addr)
545250003Sadrian				continue;
546250003Sadrian			found_entry = 1;
547250003Sadrian		}
548250003Sadrian		(*action)(sdl, sin2, rtm);
549250003Sadrian	}
550250003Sadrian	free(buf);
551250003Sadrian	return (found_entry);
552250003Sadrian}
553250003Sadrian
554250003Sadrian/*
555250003Sadrian * Display an arp entry
556250003Sadrian */
557250003Sadrianstatic void
558250003Sadrianprint_entry(struct sockaddr_dl *sdl,
559250003Sadrian	struct sockaddr_inarp *addr, struct rt_msghdr *rtm)
560250003Sadrian{
561250003Sadrian	const char *host;
562250003Sadrian	struct hostent *hp;
563250003Sadrian	struct iso88025_sockaddr_dl_data *trld;
564250003Sadrian	char ifname[IF_NAMESIZE];
565250008Sadrian	int seg;
566250003Sadrian
567250003Sadrian	if (nflag == 0)
568250003Sadrian		hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
569250003Sadrian		    sizeof addr->sin_addr, AF_INET);
570250003Sadrian	else
571250003Sadrian		hp = 0;
572250003Sadrian	if (hp)
573250003Sadrian		host = hp->h_name;
574250003Sadrian	else {
575250003Sadrian		host = "?";
576250003Sadrian		if (h_errno == TRY_AGAIN)
577250003Sadrian			nflag = 1;
578250003Sadrian	}
579250003Sadrian	printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr));
580250003Sadrian	if (sdl->sdl_alen) {
581250003Sadrian		if ((sdl->sdl_type == IFT_ETHER ||
582250003Sadrian		    sdl->sdl_type == IFT_L2VLAN ||
583250003Sadrian		    sdl->sdl_type == IFT_BRIDGE) &&
584250003Sadrian		    sdl->sdl_alen == ETHER_ADDR_LEN)
585250003Sadrian			printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl)));
586250003Sadrian		else {
587250003Sadrian			int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
588250003Sadrian
589250003Sadrian			printf("%s", link_ntoa(sdl) + n);
590250003Sadrian		}
591250003Sadrian	} else
592250003Sadrian		printf("(incomplete)");
593250003Sadrian	if (if_indextoname(sdl->sdl_index, ifname) != NULL)
594250003Sadrian		printf(" on %s", ifname);
595250003Sadrian	if (rtm->rtm_rmx.rmx_expire == 0)
596250003Sadrian		printf(" permanent");
597250003Sadrian	if (addr->sin_other & SIN_PROXY)
598250003Sadrian		printf(" published (proxy only)");
599250003Sadrian	if (rtm->rtm_flags & RTF_ANNOUNCE)
600250003Sadrian		printf(" published");
601250003Sadrian	switch(sdl->sdl_type) {
602250003Sadrian	case IFT_ETHER:
603250003Sadrian                printf(" [ethernet]");
604250003Sadrian                break;
605250003Sadrian	case IFT_ISO88025:
606250003Sadrian                printf(" [token-ring]");
607250003Sadrian		trld = SDL_ISO88025(sdl);
608250003Sadrian		if (trld->trld_rcf != 0) {
609250003Sadrian			printf(" rt=%x", ntohs(trld->trld_rcf));
610250003Sadrian			for (seg = 0;
611250003Sadrian			     seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2);
612250003Sadrian			     seg++)
613250003Sadrian				printf(":%x", ntohs(*(trld->trld_route[seg])));
614250003Sadrian		}
615250003Sadrian                break;
616250003Sadrian	case IFT_FDDI:
617250003Sadrian                printf(" [fddi]");
618250003Sadrian                break;
619250003Sadrian	case IFT_ATM:
620250003Sadrian                printf(" [atm]");
621250003Sadrian                break;
622250003Sadrian	case IFT_L2VLAN:
623250003Sadrian		printf(" [vlan]");
624250003Sadrian		break;
625250003Sadrian	case IFT_IEEE1394:
626250003Sadrian                printf(" [firewire]");
627250003Sadrian                break;
628250003Sadrian	case IFT_BRIDGE:
629250003Sadrian		printf(" [bridge]");
630250003Sadrian		break;
631250003Sadrian	default:
632250003Sadrian		break;
633250003Sadrian        }
634250003Sadrian
635250003Sadrian	printf("\n");
636250003Sadrian
637250003Sadrian}
638250003Sadrian
639250003Sadrian/*
640250003Sadrian * Nuke an arp entry
641250003Sadrian */
642250003Sadrianstatic void
643250003Sadriannuke_entry(struct sockaddr_dl *sdl __unused,
644250003Sadrian	struct sockaddr_inarp *addr, struct rt_msghdr *rtm __unused)
645250003Sadrian{
646250003Sadrian	char ip[20];
647250003Sadrian
648250003Sadrian	snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
649250003Sadrian	(void)delete(ip, 0);
650250003Sadrian}
651250003Sadrian
652250003Sadrianstatic void
653250003Sadrianusage(void)
654250003Sadrian{
655250003Sadrian	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
656250003Sadrian		"usage: arp [-n] [-i interface] hostname",
657250003Sadrian		"       arp [-n] [-i interface] -a",
658250003Sadrian		"       arp -d hostname [pub]",
659250003Sadrian		"       arp -d [-i interface] -a",
660250003Sadrian		"       arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
661250003Sadrian		"       arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
662250003Sadrian		"       arp -f filename");
663250003Sadrian	exit(1);
664250003Sadrian}
665250003Sadrian
666250003Sadrianstatic struct rt_msghdr *
667250003Sadrianrtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl)
668250003Sadrian{
669250003Sadrian	static int seq;
670250003Sadrian	int rlen;
671250003Sadrian	int l;
672250003Sadrian	struct sockaddr_in so_mask, *som = &so_mask;
673250003Sadrian	static int s = -1;
674250003Sadrian	static pid_t pid;
675250003Sadrian
676250003Sadrian	static struct	{
677250003Sadrian		struct	rt_msghdr m_rtm;
678250003Sadrian		char	m_space[512];
679250003Sadrian	}	m_rtmsg;
680250003Sadrian
681250003Sadrian	struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
682250003Sadrian	char *cp = m_rtmsg.m_space;
683250003Sadrian
684250003Sadrian	if (s < 0) {	/* first time: open socket, get pid */
685250003Sadrian		s = socket(PF_ROUTE, SOCK_RAW, 0);
686250003Sadrian		if (s < 0)
687250003Sadrian			err(1, "socket");
688250003Sadrian		pid = getpid();
689250003Sadrian	}
690250003Sadrian	bzero(&so_mask, sizeof(so_mask));
691250003Sadrian	so_mask.sin_len = 8;
692250003Sadrian	so_mask.sin_addr.s_addr = 0xffffffff;
693250003Sadrian
694250003Sadrian	errno = 0;
695250003Sadrian	/*
696250003Sadrian	 * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer
697250003Sadrian	 * appropriately.
698250003Sadrian	 */
699250003Sadrian	if (cmd == RTM_DELETE)
700250003Sadrian		goto doit;
701250003Sadrian	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
702250003Sadrian	rtm->rtm_flags = flags;
703250003Sadrian	rtm->rtm_version = RTM_VERSION;
704250003Sadrian
705250003Sadrian	switch (cmd) {
706250003Sadrian	default:
707250003Sadrian		errx(1, "internal wrong cmd");
708250003Sadrian	case RTM_ADD:
709250003Sadrian		rtm->rtm_addrs |= RTA_GATEWAY;
710250003Sadrian		rtm->rtm_rmx.rmx_expire = expire_time;
711250003Sadrian		rtm->rtm_inits = RTV_EXPIRE;
712250003Sadrian		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
713250003Sadrian		dst->sin_other = 0;
714250003Sadrian		if (doing_proxy) {
715250003Sadrian			if (proxy_only)
716250003Sadrian				dst->sin_other = SIN_PROXY;
717250003Sadrian			else {
718250003Sadrian				rtm->rtm_addrs |= RTA_NETMASK;
719250003Sadrian				rtm->rtm_flags &= ~RTF_HOST;
720250003Sadrian			}
721250003Sadrian		}
722250003Sadrian		/* FALLTHROUGH */
723250003Sadrian	case RTM_GET:
724250003Sadrian		rtm->rtm_addrs |= RTA_DST;
725250003Sadrian	}
726250003Sadrian#define NEXTADDR(w, s)					   \
727250003Sadrian	do {						   \
728250003Sadrian		if ((s) != NULL && rtm->rtm_addrs & (w)) { \
729250003Sadrian			bcopy((s), cp, sizeof(*(s)));	   \
730250003Sadrian			cp += SA_SIZE(s);		   \
731250003Sadrian		}					   \
732250003Sadrian	} while (0)
733250003Sadrian
734250003Sadrian	NEXTADDR(RTA_DST, dst);
735250003Sadrian	NEXTADDR(RTA_GATEWAY, sdl);
736250003Sadrian	NEXTADDR(RTA_NETMASK, som);
737250003Sadrian
738250003Sadrian	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
739250003Sadriandoit:
740250003Sadrian	l = rtm->rtm_msglen;
741250003Sadrian	rtm->rtm_seq = ++seq;
742250003Sadrian	rtm->rtm_type = cmd;
743250003Sadrian	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
744250003Sadrian		if (errno != ESRCH || cmd != RTM_DELETE) {
745250003Sadrian			warn("writing to routing socket");
746250003Sadrian			return (NULL);
747250003Sadrian		}
748250003Sadrian	}
749250003Sadrian	do {
750250003Sadrian		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
751250003Sadrian	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
752250003Sadrian	if (l < 0)
753250003Sadrian		warn("read from routing socket");
754250003Sadrian	return (rtm);
755250003Sadrian}
756250003Sadrian
757250003Sadrian/*
758250003Sadrian * get_ether_addr - get the hardware address of an interface on the
759250003Sadrian * the same subnet as ipaddr.
760250003Sadrian */
761250003Sadrian#define MAX_IFS		32
762250003Sadrian
763250003Sadrianstatic int
764250003Sadrianget_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr)
765250003Sadrian{
766250003Sadrian	struct ifreq *ifr, *ifend, *ifp;
767250003Sadrian	in_addr_t ina, mask;
768250003Sadrian	struct sockaddr_dl *dla;
769250003Sadrian	struct ifreq ifreq;
770250003Sadrian	struct ifconf ifc;
771250003Sadrian	struct ifreq ifs[MAX_IFS];
772250003Sadrian	int sock;
773250003Sadrian	int retval = 0;
774250003Sadrian
775250003Sadrian	sock = socket(AF_INET, SOCK_DGRAM, 0);
776250003Sadrian	if (sock < 0)
777250003Sadrian		err(1, "socket");
778250003Sadrian
779250003Sadrian	ifc.ifc_len = sizeof(ifs);
780250003Sadrian	ifc.ifc_req = ifs;
781250003Sadrian	if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
782250003Sadrian		warnx("ioctl(SIOCGIFCONF)");
783250003Sadrian		goto done;
784250003Sadrian	}
785250003Sadrian
786250003Sadrian#define NEXTIFR(i)						\
787250003Sadrian    ((struct ifreq *)((char *)&(i)->ifr_addr			\
788250003Sadrian	+ MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) )
789250003Sadrian
790250003Sadrian	/*
791250003Sadrian	 * Scan through looking for an interface with an Internet
792250003Sadrian	 * address on the same subnet as `ipaddr'.
793250003Sadrian	 */
794250003Sadrian	ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
795250003Sadrian	for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) {
796250003Sadrian		if (ifr->ifr_addr.sa_family != AF_INET)
797250003Sadrian			continue;
798250003Sadrian		strncpy(ifreq.ifr_name, ifr->ifr_name,
799250003Sadrian			sizeof(ifreq.ifr_name));
800250003Sadrian		ifreq.ifr_addr = ifr->ifr_addr;
801250003Sadrian		/*
802250003Sadrian		 * Check that the interface is up,
803250003Sadrian		 * and not point-to-point or loopback.
804250003Sadrian		 */
805250003Sadrian		if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0)
806250003Sadrian			continue;
807250003Sadrian		if ((ifreq.ifr_flags &
808250003Sadrian		     (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
809250003Sadrian				IFF_LOOPBACK|IFF_NOARP))
810250003Sadrian		     != (IFF_UP|IFF_BROADCAST))
811250003Sadrian			continue;
812250003Sadrian		/*
813250003Sadrian		 * Get its netmask and check that it's on
814250003Sadrian		 * the right subnet.
815250003Sadrian		 */
816250003Sadrian		if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0)
817250003Sadrian			continue;
818250003Sadrian		mask = ((struct sockaddr_in *)
819250003Sadrian			&ifreq.ifr_addr)->sin_addr.s_addr;
820250003Sadrian		ina = ((struct sockaddr_in *)
821250003Sadrian			&ifr->ifr_addr)->sin_addr.s_addr;
822250003Sadrian		if ((ipaddr & mask) == (ina & mask))
823250003Sadrian			break; /* ok, we got it! */
824250003Sadrian	}
825250003Sadrian
826250003Sadrian	if (ifr >= ifend)
827250003Sadrian		goto done;
828250003Sadrian
829250003Sadrian	/*
830250003Sadrian	 * Now scan through again looking for a link-level address
831250003Sadrian	 * for this interface.
832250003Sadrian	 */
833250003Sadrian	ifp = ifr;
834250003Sadrian	for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr))
835250003Sadrian		if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 &&
836250003Sadrian		    ifr->ifr_addr.sa_family == AF_LINK)
837250003Sadrian			break;
838250003Sadrian	if (ifr >= ifend)
839250003Sadrian		goto done;
840250003Sadrian	/*
841250003Sadrian	 * Found the link-level address - copy it out
842250003Sadrian	 */
843250003Sadrian	dla = (struct sockaddr_dl *) &ifr->ifr_addr;
844250003Sadrian	memcpy(hwaddr,  LLADDR(dla), dla->sdl_alen);
845250003Sadrian	printf("using interface %s for proxy with address ",
846250003Sadrian		ifp->ifr_name);
847250003Sadrian	printf("%s\n", ether_ntoa(hwaddr));
848250003Sadrian	retval = dla->sdl_alen;
849250003Sadriandone:
850250003Sadrian	close(sock);
851250003Sadrian	return (retval);
852250003Sadrian}
853250003Sadrian