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