xref: /netbsd/usr.sbin/arp/arp.c (revision c4a72b64)
1 /*	$NetBSD: arp.c,v 1.35 2002/11/08 03:40:28 rafal Exp $ */
2 
3 /*
4  * Copyright (c) 1984, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Sun Microsystems, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __COPYRIGHT("@(#) Copyright (c) 1984, 1993\n\
42 	The Regents of the University of California.  All rights reserved.\n");
43 #endif /* not lint */
44 
45 #ifndef lint
46 #if 0
47 static char sccsid[] = "@(#)arp.c	8.3 (Berkeley) 4/28/95";
48 #else
49 __RCSID("$NetBSD: arp.c,v 1.35 2002/11/08 03:40:28 rafal Exp $");
50 #endif
51 #endif /* not lint */
52 
53 /*
54  * arp - display, set, and delete arp table entries
55  */
56 
57 /* Roundup the same way rt_xaddrs does */
58 #define ROUNDUP(a) \
59 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
60 
61 #include <sys/param.h>
62 #include <sys/file.h>
63 #include <sys/socket.h>
64 #include <sys/sysctl.h>
65 #include <sys/ioctl.h>
66 
67 #include <net/if.h>
68 #include <net/if_dl.h>
69 #include <net/if_ether.h>
70 #include <net/if_types.h>
71 #include <net/route.h>
72 #include <netinet/in.h>
73 #include <netinet/if_inarp.h>
74 #include <arpa/inet.h>
75 
76 #include <err.h>
77 #include <errno.h>
78 #include <netdb.h>
79 #include <nlist.h>
80 #include <paths.h>
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <unistd.h>
85 #include <ifaddrs.h>
86 
87 int	delete __P((const char *, const char *));
88 void	dump __P((u_long));
89 void	delete_all __P((void));
90 void	sdl_print __P((const struct sockaddr_dl *));
91 int	getifname __P((u_int16_t, char *));
92 int	atosdl __P((const char *s, struct sockaddr_dl *sdl));
93 int	file __P((char *));
94 void	get __P((const char *));
95 int	getinetaddr __P((const char *, struct in_addr *));
96 void	getsocket __P((void));
97 int	main __P((int, char **));
98 int	rtmsg __P((int));
99 int	set __P((int, char **));
100 void	usage __P((void));
101 
102 static int pid;
103 static int aflag, nflag, vflag;
104 static int s = -1;
105 static struct ifaddrs* ifaddrs = NULL;
106 
107 int
108 main(argc, argv)
109 	int argc;
110 	char **argv;
111 {
112 	int ch;
113 	int op = 0;
114 
115 	setprogname(argv[0]);
116 
117 	pid = getpid();
118 
119 	while ((ch = getopt(argc, argv, "andsfv")) != -1)
120 		switch((char)ch) {
121 		case 'a':
122 			aflag = 1;
123 			break;
124 		case 'd':
125 		case 's':
126 		case 'f':
127 			if (op)
128 				usage();
129 			op = ch;
130 			break;
131 		case 'n':
132 			nflag = 1;
133 			break;
134 		case 'v':
135 			vflag = 1;
136 			break;
137 		default:
138 			usage();
139 		}
140 	argc -= optind;
141 	argv += optind;
142 
143 	if (!op && aflag)
144 		op = 'a';
145 
146 	switch((char)op) {
147 	case 'a':
148 		dump(0);
149 		break;
150 	case 'd':
151 		if (aflag && argc == 0)
152 			delete_all();
153 		else {
154 			if (aflag || argc < 1 || argc > 2)
155 				usage();
156 			(void)delete(argv[0], argv[1]);
157 		}
158 		break;
159 	case 's':
160 		if (argc < 2 || argc > 5)
161 			usage();
162 		return (set(argc, argv) ? 1 : 0);
163 	case 'f':
164 		if (argc != 1)
165 			usage();
166 		return (file(argv[0]));
167 	default:
168 		if (argc != 1)
169 			usage();
170 		get(argv[0]);
171 		break;
172 	}
173 	return (0);
174 }
175 
176 /*
177  * Process a file to set standard arp entries
178  */
179 int
180 file(name)
181 	char *name;
182 {
183 	char line[100], arg[5][50], *args[5];
184 	int i, retval;
185 	FILE *fp;
186 
187 	if ((fp = fopen(name, "r")) == NULL)
188 		err(1, "cannot open %s", name);
189 	args[0] = &arg[0][0];
190 	args[1] = &arg[1][0];
191 	args[2] = &arg[2][0];
192 	args[3] = &arg[3][0];
193 	args[4] = &arg[4][0];
194 	retval = 0;
195 	while (fgets(line, 100, fp) != NULL) {
196 		i = sscanf(line, "%s %s %s %s %s", arg[0], arg[1], arg[2],
197 		    arg[3], arg[4]);
198 		if (i < 2) {
199 			warnx("bad line: %s", line);
200 			retval = 1;
201 			continue;
202 		}
203 		if (set(i, args))
204 			retval = 1;
205 	}
206 	fclose(fp);
207 	return (retval);
208 }
209 
210 void
211 getsocket()
212 {
213 	if (s >= 0)
214 		return;
215 	s = socket(PF_ROUTE, SOCK_RAW, 0);
216 	if (s < 0)
217 		err(1, "socket");
218 }
219 
220 struct	sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}};
221 struct	sockaddr_inarp blank_sin = {sizeof(blank_sin), AF_INET }, sin_m;
222 struct	sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
223 int	expire_time, flags, export_only, doing_proxy, found_entry;
224 struct	{
225 	struct	rt_msghdr m_rtm;
226 	char	m_space[512];
227 }	m_rtmsg;
228 
229 /*
230  * Set an individual arp entry
231  */
232 int
233 set(argc, argv)
234 	int argc;
235 	char **argv;
236 {
237 	struct sockaddr_inarp *sin;
238 	struct sockaddr_dl *sdl;
239 	struct rt_msghdr *rtm;
240 	char *host = argv[0], *eaddr;
241 	int rval;
242 
243 	sin = &sin_m;
244 	rtm = &(m_rtmsg.m_rtm);
245 	eaddr = argv[1];
246 
247 	getsocket();
248 	argc -= 2;
249 	argv += 2;
250 	sdl_m = blank_sdl;		/* struct copy */
251 	sin_m = blank_sin;		/* struct copy */
252 	if (getinetaddr(host, &sin->sin_addr) == -1)
253 		return (1);
254 	if (atosdl(eaddr, &sdl_m))
255 		warnx("invalid link-level address '%s'", eaddr);
256 	doing_proxy = flags = export_only = expire_time = 0;
257 	while (argc-- > 0) {
258 		if (strncmp(argv[0], "temp", 4) == 0) {
259 			struct timeval time;
260 			(void)gettimeofday(&time, 0);
261 			expire_time = time.tv_sec + 20 * 60;
262 		}
263 		else if (strncmp(argv[0], "pub", 3) == 0) {
264 			flags |= RTF_ANNOUNCE;
265 			doing_proxy = SIN_PROXY;
266 		} else if (strncmp(argv[0], "trail", 5) == 0) {
267 			(void)printf(
268 			    "%s: Sending trailers is no longer supported\n",
269 			     host);
270 		}
271 		argv++;
272 	}
273 tryagain:
274 	if (rtmsg(RTM_GET) < 0) {
275 		warn("%s", host);
276 		return (1);
277 	}
278 	sin = (struct sockaddr_inarp *)(rtm + 1);
279 	sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin_len) + (char *)sin);
280 	if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
281 		if (sdl->sdl_family == AF_LINK &&
282 		    (rtm->rtm_flags & RTF_LLINFO) &&
283 		    !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
284 		case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
285 		case IFT_ISO88024: case IFT_ISO88025: case IFT_ARCNET:
286 			goto overwrite;
287 		}
288 		if (doing_proxy == 0) {
289 			(void)printf("set: can only proxy for %s\n", host);
290 			return (1);
291 		}
292 		if (sin_m.sin_other & SIN_PROXY) {
293 			(void)printf(
294 			    "set: proxy entry exists for non 802 device\n");
295 			return (1);
296 		}
297 		sin_m.sin_other = SIN_PROXY;
298 		export_only = 1;
299 		goto tryagain;
300 	}
301 overwrite:
302 	if (sdl->sdl_family != AF_LINK) {
303 		(void)printf("cannot intuit interface index and type for %s\n",
304 		    host);
305 		return (1);
306 	}
307 	sdl_m.sdl_type = sdl->sdl_type;
308 	sdl_m.sdl_index = sdl->sdl_index;
309 	rval = rtmsg(RTM_ADD);
310 	if (vflag)
311 		(void)printf("%s (%s) added\n", host, eaddr);
312 	return (rval);
313 }
314 
315 /*
316  * Display an individual arp entry
317  */
318 void
319 get(host)
320 	const char *host;
321 {
322 	struct sockaddr_inarp *sin;
323 
324 	sin = &sin_m;
325 	sin_m = blank_sin;		/* struct copy */
326 	if (getinetaddr(host, &sin->sin_addr) == -1)
327 		exit(1);
328 	dump(sin->sin_addr.s_addr);
329 	if (found_entry == 0) {
330 		(void)printf("%s (%s) -- no entry\n", host,
331 		    inet_ntoa(sin->sin_addr));
332 		exit(1);
333 	}
334 }
335 
336 /*
337  * Delete an arp entry
338  */
339 int
340 delete(host, info)
341 	const char *host;
342 	const char *info;
343 {
344 	struct sockaddr_inarp *sin;
345 	struct rt_msghdr *rtm;
346 	struct sockaddr_dl *sdl;
347 
348 	sin = &sin_m;
349 	rtm = &m_rtmsg.m_rtm;
350 
351 	if (info && strncmp(info, "pro", 3) )
352 		export_only = 1;
353 	getsocket();
354 	sin_m = blank_sin;		/* struct copy */
355 	if (getinetaddr(host, &sin->sin_addr) == -1)
356 		return (1);
357 tryagain:
358 	if (rtmsg(RTM_GET) < 0) {
359 		warn("%s", host);
360 		return (1);
361 	}
362 	sin = (struct sockaddr_inarp *)(rtm + 1);
363 	sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin_len) + (char *)sin);
364 	if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
365 		if (sdl->sdl_family == AF_LINK &&
366 		    (rtm->rtm_flags & RTF_LLINFO) &&
367 		    !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
368 		case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
369 		case IFT_ISO88024: case IFT_ISO88025: case IFT_ARCNET:
370 			goto delete;
371 		}
372 	}
373 	if (sin_m.sin_other & SIN_PROXY) {
374 		warnx("delete: can't locate %s", host);
375 		return (1);
376 	} else {
377 		sin_m.sin_other = SIN_PROXY;
378 		goto tryagain;
379 	}
380 delete:
381 	if (sdl->sdl_family != AF_LINK) {
382 		(void)printf("cannot locate %s\n", host);
383 		return (1);
384 	}
385 	if (rtmsg(RTM_DELETE))
386 		return (1);
387 	if (vflag)
388 		(void)printf("%s (%s) deleted\n", host,
389 		    inet_ntoa(sin->sin_addr));
390 	return (0);
391 }
392 
393 /*
394  * Dump the entire arp table
395  */
396 void
397 dump(addr)
398 	u_long addr;
399 {
400 	int mib[6];
401 	size_t needed;
402 	char ifname[IFNAMSIZ];
403 	char *host, *lim, *buf, *next;
404 	struct rt_msghdr *rtm;
405 	struct sockaddr_inarp *sin;
406 	struct sockaddr_dl *sdl;
407 	struct hostent *hp;
408 
409 	mib[0] = CTL_NET;
410 	mib[1] = PF_ROUTE;
411 	mib[2] = 0;
412 	mib[3] = AF_INET;
413 	mib[4] = NET_RT_FLAGS;
414 	mib[5] = RTF_LLINFO;
415 	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
416 		err(1, "route-sysctl-estimate");
417 	if (needed == 0)
418 		return;
419 	if ((buf = malloc(needed)) == NULL)
420 		err(1, "malloc");
421 	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
422 		err(1, "actual retrieval of routing table");
423 	lim = buf + needed;
424 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
425 		rtm = (struct rt_msghdr *)next;
426 		sin = (struct sockaddr_inarp *)(rtm + 1);
427 		sdl = (struct sockaddr_dl *)
428 		    (ROUNDUP(sin->sin_len) + (char *)sin);
429 		if (addr) {
430 			if (addr != sin->sin_addr.s_addr)
431 				continue;
432 			found_entry = 1;
433 		}
434 		if (nflag == 0)
435 			hp = gethostbyaddr((caddr_t)&(sin->sin_addr),
436 			    sizeof sin->sin_addr, AF_INET);
437 		else
438 			hp = NULL;
439 
440 		host = hp ? hp->h_name : "?";
441 
442 		(void)printf("%s (%s) at ", host, inet_ntoa(sin->sin_addr));
443 		if (sdl->sdl_alen)
444 			sdl_print(sdl);
445 		else
446 			(void)printf("(incomplete)");
447 
448 		if (sdl->sdl_index) {
449 			if (getifname(sdl->sdl_index, ifname) == 0)
450 				printf(" on %s", ifname);
451 		}
452 
453 		if (rtm->rtm_rmx.rmx_expire == 0)
454 			(void)printf(" permanent");
455 		if (sin->sin_other & SIN_PROXY)
456 			(void)printf(" published (proxy only)");
457 		if (rtm->rtm_addrs & RTA_NETMASK) {
458 			sin = (struct sockaddr_inarp *)
459 				(ROUNDUP(sdl->sdl_len) + (char *)sdl);
460 			if (sin->sin_addr.s_addr == 0xffffffff)
461 				(void)printf(" published");
462 			if (sin->sin_len != 8)
463 				(void)printf("(weird)");
464 		}
465 		(void)printf("\n");
466 	}
467 }
468 
469 /*
470  * Delete the entire arp table
471  */
472 void
473 delete_all(void)
474 {
475 	int mib[6];
476 	size_t needed;
477 	char addr[sizeof("000.000.000.000\0")];
478 	char *lim, *buf, *next;
479 	struct rt_msghdr *rtm;
480 	struct sockaddr_inarp *sin;
481 	struct sockaddr_dl *sdl;
482 
483 	mib[0] = CTL_NET;
484 	mib[1] = PF_ROUTE;
485 	mib[2] = 0;
486 	mib[3] = AF_INET;
487 	mib[4] = NET_RT_FLAGS;
488 	mib[5] = RTF_LLINFO;
489 	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
490 		err(1, "route-sysctl-estimate");
491 	if (needed == 0)
492 		return;
493 	if ((buf = malloc(needed)) == NULL)
494 		err(1, "malloc");
495 	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
496 		err(1, "actual retrieval of routing table");
497 	lim = buf + needed;
498 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
499 		rtm = (struct rt_msghdr *)next;
500 		sin = (struct sockaddr_inarp *)(rtm + 1);
501 		sdl = (struct sockaddr_dl *)
502 		    (ROUNDUP(sin->sin_len) + (char *)sin);
503 		snprintf(addr, sizeof(addr), "%s", inet_ntoa(sin->sin_addr));
504 		delete(addr, NULL);
505 	}
506 }
507 
508 void
509 sdl_print(sdl)
510 	const struct sockaddr_dl *sdl;
511 {
512 	char hbuf[NI_MAXHOST];
513 
514 	if (getnameinfo((struct sockaddr *)sdl, sdl->sdl_len,
515 	    hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
516 		printf("<invalid>");
517 	else
518 		printf("%s", hbuf);
519 }
520 
521 int
522 atosdl(s, sdl)
523 	const char *s;
524 	struct sockaddr_dl *sdl;
525 {
526 	int i;
527 	long b;
528 	caddr_t endp;
529 	caddr_t p;
530 	char *t, *r;
531 
532 	p = LLADDR(sdl);
533 	endp = ((caddr_t)sdl) + sdl->sdl_len;
534 	i = 0;
535 
536 	b = strtol(s, &t, 16);
537 	if (t == s)
538 		return 1;
539 
540 	*p++ = b;
541 	++i;
542 	while ((p < endp) && (*t++ == ':')) {
543 		b = strtol(t, &r, 16);
544 		if (r == t)
545 			break;
546 		*p++ = b;
547 		++i;
548 		t = r;
549 	}
550 	sdl->sdl_alen = i;
551 
552 	return 0;
553 }
554 
555 void
556 usage()
557 {
558 	const char *progname;
559 
560 	progname = getprogname();
561 	(void)fprintf(stderr, "usage: %s [-n] hostname\n", progname);
562 	(void)fprintf(stderr, "usage: %s [-n] -a\n", progname);
563 	(void)fprintf(stderr, "usage: %s -d [-a|hostname]\n", progname);
564 	(void)fprintf(stderr,
565 	    "usage: %s -s hostname ether_addr [temp] [pub]\n", progname);
566 	(void)fprintf(stderr, "usage: %s -f filename\n", progname);
567 	exit(1);
568 }
569 
570 int
571 rtmsg(cmd)
572 	int cmd;
573 {
574 	static int seq;
575 	int rlen;
576 	struct rt_msghdr *rtm;
577 	char *cp;
578 	int l;
579 
580 	rtm = &m_rtmsg.m_rtm;
581 	cp = m_rtmsg.m_space;
582 	errno = 0;
583 
584 	if (cmd == RTM_DELETE)
585 		goto doit;
586 	(void)memset(&m_rtmsg, 0, sizeof(m_rtmsg));
587 	rtm->rtm_flags = flags;
588 	rtm->rtm_version = RTM_VERSION;
589 
590 	switch (cmd) {
591 	default:
592 		errx(1, "internal wrong cmd");
593 		/*NOTREACHED*/
594 	case RTM_ADD:
595 		rtm->rtm_addrs |= RTA_GATEWAY;
596 		rtm->rtm_rmx.rmx_expire = expire_time;
597 		rtm->rtm_inits = RTV_EXPIRE;
598 		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
599 		sin_m.sin_other = 0;
600 		if (doing_proxy) {
601 			if (export_only)
602 				sin_m.sin_other = SIN_PROXY;
603 			else {
604 				rtm->rtm_addrs |= RTA_NETMASK;
605 				rtm->rtm_flags &= ~RTF_HOST;
606 			}
607 		}
608 		/* FALLTHROUGH */
609 	case RTM_GET:
610 		rtm->rtm_addrs |= RTA_DST;
611 	}
612 
613 #define NEXTADDR(w, s) \
614 	if (rtm->rtm_addrs & (w)) { \
615 		(void)memcpy(cp, &s, ((struct sockaddr *)&s)->sa_len); \
616 		cp += ROUNDUP(((struct sockaddr *)&s)->sa_len); \
617 	}
618 
619 	NEXTADDR(RTA_DST, sin_m);
620 	NEXTADDR(RTA_GATEWAY, sdl_m);
621 	NEXTADDR(RTA_NETMASK, so_mask);
622 
623 	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
624 doit:
625 	l = rtm->rtm_msglen;
626 	rtm->rtm_seq = ++seq;
627 	rtm->rtm_type = cmd;
628 	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
629 		if (errno != ESRCH || cmd != RTM_DELETE) {
630 			warn("writing to routing socket");
631 			return (-1);
632 		}
633 	}
634 	do {
635 		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
636 	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
637 	if (l < 0)
638 		warn("read from routing socket");
639 	return (0);
640 }
641 
642 int
643 getinetaddr(host, inap)
644 	const char *host;
645 	struct in_addr *inap;
646 {
647 	struct hostent *hp;
648 
649 	if (inet_aton(host, inap) == 1)
650 		return (0);
651 	if ((hp = gethostbyname(host)) == NULL) {
652 		warnx("%s: %s", host, hstrerror(h_errno));
653 		return (-1);
654 	}
655 	(void)memcpy(inap, hp->h_addr, sizeof(*inap));
656 	return (0);
657 }
658 
659 int
660 getifname(ifindex, ifname)
661 	u_int16_t ifindex;
662 	char* ifname;
663 {
664 	int i;
665 	struct ifaddrs *addr;
666 	const struct sockaddr_dl *sdl = NULL;
667 
668 	if (ifaddrs == NULL) {
669 		i = getifaddrs(&ifaddrs);
670 		if (i != 0)
671 			err(1, "getifaddrs");
672 	}
673 
674 	for (addr = ifaddrs; addr; addr = addr->ifa_next) {
675 		if (addr->ifa_addr == NULL ||
676 		    addr->ifa_addr->sa_family != AF_LINK)
677 			continue;
678 
679 		sdl = (const struct sockaddr_dl *) addr->ifa_addr;
680 		if (sdl && sdl->sdl_index == ifindex) {
681 			(void) strncpy(ifname, addr->ifa_name, IFNAMSIZ);
682 			return 0;
683 		}
684 	}
685 
686 	return -1;
687 }
688