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