arp.c revision 1.41 1 /* $NetBSD: arp.c,v 1.41 2005/03/16 02:04:51 xtraeme 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.41 2005/03/16 02:04:51 xtraeme 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 int delete(const char *, const char *);
84 void dump(u_long);
85 void delete_all(void);
86 void sdl_print(const struct sockaddr_dl *);
87 int getifname(u_int16_t, char *, size_t);
88 int atosdl(const char *s, struct sockaddr_dl *sdl);
89 int file(char *);
90 void get(const char *);
91 int getinetaddr(const char *, struct in_addr *);
92 void getsocket(void);
93 int rtmsg(int);
94 int set(int, char **);
95 void usage(void);
96
97 static int pid;
98 static int aflag, nflag, vflag;
99 static int s = -1;
100 static struct ifaddrs* ifaddrs = NULL;
101
102 int
103 main(int argc, char **argv)
104 {
105 int ch;
106 int op = 0;
107
108 setprogname(argv[0]);
109
110 pid = getpid();
111
112 while ((ch = getopt(argc, argv, "andsfv")) != -1)
113 switch((char)ch) {
114 case 'a':
115 aflag = 1;
116 break;
117 case 'd':
118 case 's':
119 case 'f':
120 if (op)
121 usage();
122 op = ch;
123 break;
124 case 'n':
125 nflag = 1;
126 break;
127 case 'v':
128 vflag = 1;
129 break;
130 default:
131 usage();
132 }
133 argc -= optind;
134 argv += optind;
135
136 if (!op && aflag)
137 op = 'a';
138
139 switch((char)op) {
140 case 'a':
141 dump(0);
142 break;
143 case 'd':
144 if (aflag && argc == 0)
145 delete_all();
146 else {
147 if (aflag || argc < 1 || argc > 2)
148 usage();
149 (void)delete(argv[0], argv[1]);
150 }
151 break;
152 case 's':
153 if (argc < 2 || argc > 5)
154 usage();
155 return (set(argc, argv) ? 1 : 0);
156 case 'f':
157 if (argc != 1)
158 usage();
159 return (file(argv[0]));
160 default:
161 if (argc != 1)
162 usage();
163 get(argv[0]);
164 break;
165 }
166 return (0);
167 }
168
169 /*
170 * Process a file to set standard arp entries
171 */
172 int
173 file(char *name)
174 {
175 char line[100], arg[5][50], *args[5];
176 int i, retval;
177 FILE *fp;
178
179 if ((fp = fopen(name, "r")) == NULL)
180 err(1, "cannot open %s", name);
181 args[0] = &arg[0][0];
182 args[1] = &arg[1][0];
183 args[2] = &arg[2][0];
184 args[3] = &arg[3][0];
185 args[4] = &arg[4][0];
186 retval = 0;
187 while (fgets(line, 100, fp) != NULL) {
188 i = sscanf(line, "%49s %49s %49s %49s %49s",
189 arg[0], arg[1], arg[2], arg[3], arg[4]);
190 if (i < 2) {
191 warnx("bad line: %s", line);
192 retval = 1;
193 continue;
194 }
195 if (set(i, args))
196 retval = 1;
197 }
198 fclose(fp);
199 return (retval);
200 }
201
202 void
203 getsocket(void)
204 {
205 if (s >= 0)
206 return;
207 s = socket(PF_ROUTE, SOCK_RAW, 0);
208 if (s < 0)
209 err(1, "socket");
210 }
211
212 struct sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}};
213 struct sockaddr_inarp blank_sin = {sizeof(blank_sin), AF_INET }, sin_m;
214 struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
215 int expire_time, flags, export_only, doing_proxy, found_entry;
216 struct {
217 struct rt_msghdr m_rtm;
218 char m_space[512];
219 } m_rtmsg;
220
221 /*
222 * Set an individual arp entry
223 */
224 int
225 set(int argc, char **argv)
226 {
227 struct sockaddr_inarp *sina;
228 struct sockaddr_dl *sdl;
229 struct rt_msghdr *rtm;
230 char *host = argv[0], *eaddr;
231 int rval;
232
233 sina = &sin_m;
234 rtm = &(m_rtmsg.m_rtm);
235 eaddr = argv[1];
236
237 getsocket();
238 argc -= 2;
239 argv += 2;
240 sdl_m = blank_sdl; /* struct copy */
241 sin_m = blank_sin; /* struct copy */
242 if (getinetaddr(host, &sina->sin_addr) == -1)
243 return (1);
244 if (atosdl(eaddr, &sdl_m))
245 warnx("invalid link-level address '%s'", eaddr);
246 doing_proxy = flags = export_only = expire_time = 0;
247 while (argc-- > 0) {
248 if (strncmp(argv[0], "temp", 4) == 0) {
249 struct timeval timev;
250 (void)gettimeofday(&timev, 0);
251 expire_time = timev.tv_sec + 20 * 60;
252 }
253 else if (strncmp(argv[0], "pub", 3) == 0) {
254 flags |= RTF_ANNOUNCE;
255 doing_proxy = SIN_PROXY;
256 } else if (strncmp(argv[0], "trail", 5) == 0) {
257 (void)printf(
258 "%s: Sending trailers is no longer supported\n",
259 host);
260 }
261 argv++;
262 }
263 tryagain:
264 if (rtmsg(RTM_GET) < 0) {
265 warn("%s", host);
266 return (1);
267 }
268 sina = (struct sockaddr_inarp *)(rtm + 1);
269 sdl = (struct sockaddr_dl *)(ROUNDUP(sina->sin_len) + (char *)sina);
270 if (sina->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
271 if (sdl->sdl_family == AF_LINK &&
272 (rtm->rtm_flags & RTF_LLINFO) &&
273 !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
274 case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
275 case IFT_ISO88024: case IFT_ISO88025: case IFT_ARCNET:
276 goto overwrite;
277 }
278 if (doing_proxy == 0) {
279 (void)printf("set: can only proxy for %s\n", host);
280 return (1);
281 }
282 if (sin_m.sin_other & SIN_PROXY) {
283 (void)printf(
284 "set: proxy entry exists for non 802 device\n");
285 return (1);
286 }
287 sin_m.sin_other = SIN_PROXY;
288 export_only = 1;
289 goto tryagain;
290 }
291 overwrite:
292 if (sdl->sdl_family != AF_LINK) {
293 (void)printf("cannot intuit interface index and type for %s\n",
294 host);
295 return (1);
296 }
297 sdl_m.sdl_type = sdl->sdl_type;
298 sdl_m.sdl_index = sdl->sdl_index;
299 rval = rtmsg(RTM_ADD);
300 if (vflag)
301 (void)printf("%s (%s) added\n", host, eaddr);
302 return (rval);
303 }
304
305 /*
306 * Display an individual arp entry
307 */
308 void
309 get(const char *host)
310 {
311 struct sockaddr_inarp *sina;
312
313 sina = &sin_m;
314 sin_m = blank_sin; /* struct copy */
315 if (getinetaddr(host, &sina->sin_addr) == -1)
316 exit(1);
317 dump(sina->sin_addr.s_addr);
318 if (found_entry == 0) {
319 (void)printf("%s (%s) -- no entry\n", host,
320 inet_ntoa(sina->sin_addr));
321 exit(1);
322 }
323 }
324
325 /*
326 * Delete an arp entry
327 */
328 int
329 delete(const char *host, const char *info)
330 {
331 struct sockaddr_inarp *sina;
332 struct rt_msghdr *rtm;
333 struct sockaddr_dl *sdl;
334
335 sina = &sin_m;
336 rtm = &m_rtmsg.m_rtm;
337
338 if (info && strncmp(info, "pro", 3) )
339 export_only = 1;
340 getsocket();
341 sin_m = blank_sin; /* struct copy */
342 if (getinetaddr(host, &sina->sin_addr) == -1)
343 return (1);
344 tryagain:
345 if (rtmsg(RTM_GET) < 0) {
346 warn("%s", host);
347 return (1);
348 }
349 sina = (struct sockaddr_inarp *)(rtm + 1);
350 sdl = (struct sockaddr_dl *)(ROUNDUP(sina->sin_len) + (char *)sina);
351 if (sina->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
352 if (sdl->sdl_family == AF_LINK &&
353 (rtm->rtm_flags & RTF_LLINFO) &&
354 !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
355 case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
356 case IFT_ISO88024: case IFT_ISO88025: case IFT_ARCNET:
357 goto delete;
358 }
359 }
360 if (sin_m.sin_other & SIN_PROXY) {
361 warnx("delete: can't locate %s", host);
362 return (1);
363 } else {
364 sin_m.sin_other = SIN_PROXY;
365 goto tryagain;
366 }
367 delete:
368 if (sdl->sdl_family != AF_LINK) {
369 (void)printf("cannot locate %s\n", host);
370 return (1);
371 }
372 if (rtmsg(RTM_DELETE))
373 return (1);
374 if (vflag)
375 (void)printf("%s (%s) deleted\n", host,
376 inet_ntoa(sina->sin_addr));
377 return (0);
378 }
379
380 /*
381 * Dump the entire arp table
382 */
383 void
384 dump(u_long addr)
385 {
386 int mib[6];
387 size_t needed;
388 char ifname[IFNAMSIZ];
389 char *lim, *buf, *next;
390 const char *host;
391 struct rt_msghdr *rtm;
392 struct sockaddr_inarp *sina;
393 struct sockaddr_dl *sdl;
394 struct hostent *hp;
395
396 mib[0] = CTL_NET;
397 mib[1] = PF_ROUTE;
398 mib[2] = 0;
399 mib[3] = AF_INET;
400 mib[4] = NET_RT_FLAGS;
401 mib[5] = RTF_LLINFO;
402 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
403 err(1, "route-sysctl-estimate");
404 if (needed == 0)
405 return;
406 if ((buf = malloc(needed)) == NULL)
407 err(1, "malloc");
408 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
409 err(1, "actual retrieval of routing table");
410 lim = buf + needed;
411 for (next = buf; next < lim; next += rtm->rtm_msglen) {
412 rtm = (struct rt_msghdr *)next;
413 sina = (struct sockaddr_inarp *)(rtm + 1);
414 sdl = (struct sockaddr_dl *)
415 (ROUNDUP(sina->sin_len) + (char *)sina);
416 if (addr) {
417 if (addr != sina->sin_addr.s_addr)
418 continue;
419 found_entry = 1;
420 }
421 if (nflag == 0)
422 hp = gethostbyaddr((caddr_t)&(sina->sin_addr),
423 sizeof sina->sin_addr, AF_INET);
424 else
425 hp = NULL;
426
427 host = hp ? hp->h_name : "?";
428
429 (void)printf("%s (%s) at ", host, inet_ntoa(sina->sin_addr));
430 if (sdl->sdl_alen)
431 sdl_print(sdl);
432 else
433 (void)printf("(incomplete)");
434
435 if (sdl->sdl_index) {
436 if (getifname(sdl->sdl_index, ifname, sizeof(ifname)) == 0)
437 printf(" on %s", ifname);
438 }
439
440 if (rtm->rtm_rmx.rmx_expire == 0)
441 (void)printf(" permanent");
442 if (sina->sin_other & SIN_PROXY)
443 (void)printf(" published (proxy only)");
444 if (rtm->rtm_addrs & RTA_NETMASK) {
445 sina = (struct sockaddr_inarp *)
446 (ROUNDUP(sdl->sdl_len) + (char *)sdl);
447 if (sina->sin_addr.s_addr == 0xffffffff)
448 (void)printf(" published");
449 if (sina->sin_len != 8)
450 (void)printf("(weird)");
451 }
452 (void)printf("\n");
453 }
454 free(buf);
455 }
456
457 /*
458 * Delete the entire arp table
459 */
460 void
461 delete_all(void)
462 {
463 int mib[6];
464 size_t needed;
465 char addr[sizeof("000.000.000.000\0")];
466 char *lim, *buf, *next;
467 struct rt_msghdr *rtm;
468 struct sockaddr_inarp *sina;
469 struct sockaddr_dl *sdl;
470
471 mib[0] = CTL_NET;
472 mib[1] = PF_ROUTE;
473 mib[2] = 0;
474 mib[3] = AF_INET;
475 mib[4] = NET_RT_FLAGS;
476 mib[5] = RTF_LLINFO;
477 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
478 err(1, "route-sysctl-estimate");
479 if (needed == 0)
480 return;
481 if ((buf = malloc(needed)) == NULL)
482 err(1, "malloc");
483 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
484 err(1, "actual retrieval of routing table");
485 lim = buf + needed;
486 for (next = buf; next < lim; next += rtm->rtm_msglen) {
487 rtm = (struct rt_msghdr *)next;
488 sina = (struct sockaddr_inarp *)(rtm + 1);
489 sdl = (struct sockaddr_dl *)
490 (ROUNDUP(sina->sin_len) + (char *)sina);
491 snprintf(addr, sizeof(addr), "%s", inet_ntoa(sina->sin_addr));
492 delete(addr, NULL);
493 }
494 free(buf);
495 }
496
497 void
498 sdl_print(const struct sockaddr_dl *sdl)
499 {
500 char hbuf[NI_MAXHOST];
501
502 if (getnameinfo((const struct sockaddr *)sdl, sdl->sdl_len,
503 hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
504 printf("<invalid>");
505 else
506 printf("%s", hbuf);
507 }
508
509 int
510 atosdl(const char *ss, struct sockaddr_dl *sdl)
511 {
512 int i;
513 long b;
514 caddr_t endp;
515 caddr_t p;
516 char *t, *r;
517
518 p = LLADDR(sdl);
519 endp = ((caddr_t)sdl) + sdl->sdl_len;
520 i = 0;
521
522 b = strtol(ss, &t, 16);
523 if (t == ss)
524 return 1;
525
526 *p++ = b;
527 ++i;
528 while ((p < endp) && (*t++ == ':')) {
529 b = strtol(t, &r, 16);
530 if (r == t)
531 break;
532 *p++ = b;
533 ++i;
534 t = r;
535 }
536 sdl->sdl_alen = i;
537
538 return 0;
539 }
540
541 void
542 usage(void)
543 {
544 const char *progname;
545
546 progname = getprogname();
547 (void)fprintf(stderr, "usage: %s [-n] hostname\n", progname);
548 (void)fprintf(stderr, "usage: %s [-nv] -a\n", progname);
549 (void)fprintf(stderr, "usage: %s [-v] -d [-a|hostname]\n", progname);
550 (void)fprintf(stderr,
551 "usage: %s -s hostname ether_addr [temp] [pub]\n", progname);
552 (void)fprintf(stderr, "usage: %s -f filename\n", progname);
553 exit(1);
554 }
555
556 int
557 rtmsg(int cmd)
558 {
559 static int seq;
560 int rlen;
561 struct rt_msghdr *rtm;
562 char *cp;
563 int l;
564
565 rtm = &m_rtmsg.m_rtm;
566 cp = m_rtmsg.m_space;
567 errno = 0;
568
569 if (cmd == RTM_DELETE)
570 goto doit;
571 (void)memset(&m_rtmsg, 0, sizeof(m_rtmsg));
572 rtm->rtm_flags = flags;
573 rtm->rtm_version = RTM_VERSION;
574
575 switch (cmd) {
576 default:
577 errx(1, "internal wrong cmd");
578 /*NOTREACHED*/
579 case RTM_ADD:
580 rtm->rtm_addrs |= RTA_GATEWAY;
581 rtm->rtm_rmx.rmx_expire = expire_time;
582 rtm->rtm_inits = RTV_EXPIRE;
583 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
584 sin_m.sin_other = 0;
585 if (doing_proxy) {
586 if (export_only)
587 sin_m.sin_other = SIN_PROXY;
588 else {
589 rtm->rtm_addrs |= RTA_NETMASK;
590 rtm->rtm_flags &= ~RTF_HOST;
591 }
592 }
593 /* FALLTHROUGH */
594 case RTM_GET:
595 rtm->rtm_addrs |= RTA_DST;
596 }
597
598 #define NEXTADDR(w, s) \
599 if (rtm->rtm_addrs & (w)) { \
600 (void)memcpy(cp, &s, ((struct sockaddr *)&s)->sa_len); \
601 cp += ROUNDUP(((struct sockaddr *)&s)->sa_len); \
602 }
603
604 NEXTADDR(RTA_DST, sin_m);
605 NEXTADDR(RTA_GATEWAY, sdl_m);
606 NEXTADDR(RTA_NETMASK, so_mask);
607
608 rtm->rtm_msglen = cp - (char *)&m_rtmsg;
609 doit:
610 l = rtm->rtm_msglen;
611 rtm->rtm_seq = ++seq;
612 rtm->rtm_type = cmd;
613 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
614 if (errno != ESRCH || cmd != RTM_DELETE) {
615 warn("writing to routing socket");
616 return (-1);
617 }
618 }
619 do {
620 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
621 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
622 if (l < 0)
623 warn("read from routing socket");
624 return (0);
625 }
626
627 int
628 getinetaddr(const char *host, struct in_addr *inap)
629 {
630 struct hostent *hp;
631
632 if (inet_aton(host, inap) == 1)
633 return (0);
634 if ((hp = gethostbyname(host)) == NULL) {
635 warnx("%s: %s", host, hstrerror(h_errno));
636 return (-1);
637 }
638 (void)memcpy(inap, hp->h_addr, sizeof(*inap));
639 return (0);
640 }
641
642 int
643 getifname(u_int16_t ifindex, char *ifname, size_t l)
644 {
645 int i;
646 struct ifaddrs *addr;
647 const struct sockaddr_dl *sdl = NULL;
648
649 if (ifaddrs == NULL) {
650 i = getifaddrs(&ifaddrs);
651 if (i != 0)
652 err(1, "getifaddrs");
653 }
654
655 for (addr = ifaddrs; addr; addr = addr->ifa_next) {
656 if (addr->ifa_addr == NULL ||
657 addr->ifa_addr->sa_family != AF_LINK)
658 continue;
659
660 sdl = (const struct sockaddr_dl *) addr->ifa_addr;
661 if (sdl && sdl->sdl_index == ifindex) {
662 (void) strlcpy(ifname, addr->ifa_name, l);
663 return 0;
664 }
665 }
666
667 return -1;
668 }
669