]> git.saurik.com Git - apple/network_cmds.git/blob - arp.tproj/arp.c
a152574b225b228b0c51f1694d2f1e977f25e6a4
[apple/network_cmds.git] / arp.tproj / arp.c
1 /*
2 * Copyright (c) 2003-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /*
30 * Copyright (c) 1984, 1993
31 * The Regents of the University of California. All rights reserved.
32 *
33 * This code is derived from software contributed to Berkeley by
34 * Sun Microsystems, Inc.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 4. Neither the name of the University nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 */
60
61 #if 0
62 #ifndef lint
63 static char const copyright[] =
64 "@(#) Copyright (c) 1984, 1993\n\
65 The Regents of the University of California. All rights reserved.\n";
66 #endif /* not lint */
67
68 #ifndef lint
69 static char const sccsid[] = "@(#)from: arp.c 8.2 (Berkeley) 1/2/94";
70 #endif /* not lint */
71 #include <sys/cdefs.h>
72 __FBSDID("$FreeBSD: src/usr.sbin/arp/arp.c,v 1.65.2.1 2008/04/25 16:38:14 sam Exp $");
73 #endif
74
75 /*
76 * arp - display, set, and delete arp table entries
77 */
78
79
80 #include <sys/param.h>
81 #include <sys/file.h>
82 #include <sys/socket.h>
83 #include <sys/sockio.h>
84 #include <sys/sysctl.h>
85 #include <sys/ioctl.h>
86 #include <sys/time.h>
87
88 #include <net/if.h>
89 #include <net/if_dl.h>
90 #include <net/if_types.h>
91 #include <net/route.h>
92 #if 0
93 #include <net/iso88025.h>
94 #endif
95
96 #include <netinet/in.h>
97 #include <netinet/if_ether.h>
98
99 #include <arpa/inet.h>
100
101 #include <ctype.h>
102 #include <err.h>
103 #include <errno.h>
104 #include <netdb.h>
105 #include <nlist.h>
106 #include <paths.h>
107 #include <stdio.h>
108 #include <stdlib.h>
109 #include <string.h>
110 #include <strings.h>
111 #include <unistd.h>
112
113 typedef void (action_fn)(struct sockaddr_dl *sdl,
114 struct sockaddr_inarp *s_in, struct rt_msghdr *rtm);
115 typedef void (action_ext_fn)(struct sockaddr_dl *sdl,
116 struct sockaddr_inarp *s_in, struct rt_msghdr_ext *rtm);
117
118 static int search(in_addr_t addr, action_fn *action);
119 static int search_ext(in_addr_t addr, action_ext_fn *action);
120 static action_fn print_entry;
121 static action_fn nuke_entry;
122 static action_ext_fn print_entry_ext;
123
124 static char *print_lladdr(struct sockaddr_dl *);
125 static int delete(char *host, int do_proxy);
126 static void usage(void);
127 static int set(int argc, char **argv);
128 static int get(char *host);
129 static int file(char *name);
130 static struct rt_msghdr *rtmsg(int cmd,
131 struct sockaddr_inarp *dst, struct sockaddr_dl *sdl);
132 static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr);
133 static struct sockaddr_inarp *getaddr(char *host);
134 static int valid_type(int type);
135 static char *sec2str(time_t);
136
137 static int nflag; /* no reverse dns lookups */
138 static int xflag; /* extended link-layer reachability information */
139 static char *rifname;
140
141 static int expire_time, flags, doing_proxy, proxy_only;
142
143 static char *boundif = NULL;
144 static unsigned int ifscope = 0;
145
146 /* which function we're supposed to do */
147 #define F_GET 1
148 #define F_SET 2
149 #define F_FILESET 3
150 #define F_REPLACE 4
151 #define F_DELETE 5
152
153 #ifndef SA_SIZE
154 #define SA_SIZE(sa) \
155 ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
156 sizeof(uint32_t) : \
157 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(uint32_t) - 1) ) )
158 #endif
159
160 #define SETFUNC(f) { if (func) usage(); func = (f); }
161
162
163 int
164 main(int argc, char *argv[])
165 {
166 int ch, func = 0;
167 int rtn = 0;
168 int aflag = 0; /* do it for all entries */
169 int lflag = 0;
170 uint32_t ifindex = 0;
171
172 while ((ch = getopt(argc, argv, "andflsSi:x")) != -1)
173 switch((char)ch) {
174 case 'a':
175 aflag = 1;
176 break;
177 case 'd':
178 SETFUNC(F_DELETE);
179 break;
180 case 'n':
181 nflag = 1;
182 break;
183 case 'l':
184 lflag = 1;
185 break;
186 case 'S':
187 SETFUNC(F_REPLACE);
188 break;
189 case 's':
190 SETFUNC(F_SET);
191 break;
192 case 'f' :
193 SETFUNC(F_FILESET);
194 break;
195 case 'i':
196 rifname = optarg;
197 break;
198 case 'x':
199 xflag = 1;
200 lflag = 1;
201 break;
202 case '?':
203 default:
204 usage();
205 }
206 argc -= optind;
207 argv += optind;
208
209 if (!func)
210 func = F_GET;
211 if (rifname) {
212 if (func != F_GET && !(func == F_DELETE && aflag))
213 errx(1, "-i not applicable to this operation");
214 if ((ifindex = if_nametoindex(rifname)) == 0) {
215 if (errno == ENXIO)
216 errx(1, "interface %s does not exist", rifname);
217 else
218 err(1, "if_nametoindex(%s)", rifname);
219 }
220 }
221 switch (func) {
222 case F_GET:
223 if (aflag) {
224 if (argc != 0)
225 usage();
226 if (lflag) {
227 printf("%-23s %-17s %-9.9s %-9.9s %8.8s %4s "
228 "%4s", "Neighbor",
229 "Linklayer Address", "Expire(O)",
230 "Expire(I)", "Netif", "Refs", "Prbs");
231 if (xflag)
232 printf(" %-7.7s %-7.7s %-7.7s",
233 "RSSI", "LQM", "NPM");
234 printf("\n");
235 search_ext(0, print_entry_ext);
236 } else {
237 search(0, print_entry);
238 }
239 } else {
240 if (argc != 1)
241 usage();
242 rtn = get(argv[0]);
243 }
244 break;
245 case F_SET:
246 case F_REPLACE:
247 if (argc < 2 || argc > 6)
248 usage();
249 if (func == F_REPLACE)
250 (void)delete(argv[0], 0);
251 rtn = set(argc, argv) ? 1 : 0;
252 break;
253 case F_DELETE:
254 if (aflag) {
255 if (argc != 0)
256 usage();
257 search(0, nuke_entry);
258 } else {
259 int do_proxy = 0;
260 int i;
261
262 for (i = 1; i < argc; i++) {
263 if (strncmp(argv[i], "pub", sizeof("pub")) == 0) {
264 do_proxy = SIN_PROXY;
265 } else if (strncmp(argv[i], "ifscope", sizeof("ifscope")) == 0) {
266 if (i + 1 >= argc) {
267 printf("ifscope needs an interface parameter\n");
268 return (1);
269 }
270 boundif = argv[++i];
271 if ((ifscope = if_nametoindex(boundif)) == 0)
272 errx(1, "ifscope has bad interface name: %s", boundif);
273 } else {
274 usage();
275 }
276 }
277 if (i > argc)
278 usage();
279 rtn = delete(argv[0], do_proxy);
280 }
281 break;
282 case F_FILESET:
283 if (argc != 1)
284 usage();
285 rtn = file(argv[0]);
286 break;
287 }
288
289 return (rtn);
290 }
291
292 /*
293 * Process a file to set standard arp entries
294 */
295 static int
296 file(char *name)
297 {
298 FILE *fp;
299 int i, retval;
300 char line[128], arg[7][50], *args[7], *p;
301
302 if ((fp = fopen(name, "r")) == NULL)
303 err(1, "cannot open %s", name);
304 args[0] = &arg[0][0];
305 args[1] = &arg[1][0];
306 args[2] = &arg[2][0];
307 args[3] = &arg[3][0];
308 args[4] = &arg[4][0];
309 args[5] = &arg[5][0];
310 args[6] = &arg[6][0];
311 retval = 0;
312 while(fgets(line, sizeof(line), fp) != NULL) {
313 if ((p = strchr(line, '#')) != NULL)
314 *p = '\0';
315 for (p = line; isblank(*p); p++);
316 if (*p == '\n' || *p == '\0')
317 continue;
318 i = sscanf(p, "%49s %49s %49s %49s %49s %49s %49s", arg[0], arg[1],
319 arg[2], arg[3], arg[4], arg[5], arg[6]);
320 if (i < 2) {
321 warnx("bad line: %s", line);
322 retval = 1;
323 continue;
324 }
325 if (set(i, args))
326 retval = 1;
327 }
328 fclose(fp);
329 return (retval);
330 }
331
332 /*
333 * Given a hostname, fills up a (static) struct sockaddr_inarp with
334 * the address of the host and returns a pointer to the
335 * structure.
336 */
337 static struct sockaddr_inarp *
338 getaddr(char *host)
339 {
340 struct hostent *hp;
341 static struct sockaddr_inarp reply;
342
343 bzero(&reply, sizeof(reply));
344 reply.sin_len = sizeof(reply);
345 reply.sin_family = AF_INET;
346 reply.sin_addr.s_addr = inet_addr(host);
347 if (reply.sin_addr.s_addr == INADDR_NONE) {
348 if (!(hp = gethostbyname(host))) {
349 warnx("%s: %s", host, hstrerror(h_errno));
350 return (NULL);
351 }
352 bcopy((char *)hp->h_addr, (char *)&reply.sin_addr,
353 sizeof reply.sin_addr);
354 }
355 return (&reply);
356 }
357
358 /*
359 * Returns true if the type is a valid one for ARP.
360 */
361 static int
362 valid_type(int type)
363 {
364
365 switch (type) {
366 case IFT_ETHER:
367 case IFT_FDDI:
368 case IFT_ISO88023:
369 case IFT_ISO88024:
370 #if 0
371 case IFT_ISO88025:
372 #endif
373 case IFT_L2VLAN:
374 #ifdef IFT_BRIDGE
375 case IFT_BRIDGE:
376 #endif
377 return (1);
378 default:
379 return (0);
380 }
381 }
382
383 /*
384 * Set an individual arp entry
385 */
386 static int
387 set(int argc, char **argv)
388 {
389 struct sockaddr_inarp *addr;
390 struct sockaddr_inarp *dst; /* what are we looking for */
391 struct sockaddr_dl *sdl;
392 struct rt_msghdr *rtm;
393 struct ether_addr *ea;
394 char *host = argv[0], *eaddr = argv[1];
395 struct sockaddr_dl sdl_m;
396
397 argc -= 2;
398 argv += 2;
399
400 bzero(&sdl_m, sizeof(sdl_m));
401 sdl_m.sdl_len = sizeof(sdl_m);
402 sdl_m.sdl_family = AF_LINK;
403
404 dst = getaddr(host);
405 if (dst == NULL)
406 return (1);
407 doing_proxy = flags = proxy_only = expire_time = 0;
408 boundif = NULL;
409 ifscope = 0;
410 while (argc-- > 0) {
411 if (strncmp(argv[0], "temp", sizeof("temp")) == 0) {
412 struct timeval tv;
413 gettimeofday(&tv, 0);
414 expire_time = tv.tv_sec + 20 * 60;
415 } else if (strncmp(argv[0], "pub", sizeof("pub")) == 0) {
416 flags |= RTF_ANNOUNCE;
417 doing_proxy = 1;
418 if (argc && strncmp(argv[1], "only", sizeof("only")) == 0) {
419 proxy_only = 1;
420 dst->sin_other = SIN_PROXY;
421 argc--; argv++;
422 }
423 } else if (strncmp(argv[0], "blackhole", sizeof("blackhole")) == 0) {
424 flags |= RTF_BLACKHOLE;
425 } else if (strncmp(argv[0], "reject", sizeof("reject")) == 0) {
426 flags |= RTF_REJECT;
427 } else if (strncmp(argv[0], "trail", sizeof("trail")) == 0) {
428 /* XXX deprecated and undocumented feature */
429 printf("%s: Sending trailers is no longer supported\n",
430 host);
431 } else if (strncmp(argv[0], "ifscope", sizeof("ifscope")) == 0) {
432 if (argc < 1) {
433 printf("ifscope needs an interface parameter\n");
434 return (1);
435 }
436 boundif = argv[1];
437 if ((ifscope = if_nametoindex(boundif)) == 0)
438 errx(1, "ifscope has bad interface name: %s", boundif);
439 argc--; argv++;
440 }
441 argv++;
442 }
443 ea = (struct ether_addr *)LLADDR(&sdl_m);
444 if (doing_proxy && !strcmp(eaddr, "auto")) {
445 if (!get_ether_addr(dst->sin_addr.s_addr, ea)) {
446 printf("no interface found for %s\n",
447 inet_ntoa(dst->sin_addr));
448 return (1);
449 }
450 sdl_m.sdl_alen = ETHER_ADDR_LEN;
451 } else {
452 struct ether_addr *ea1 = ether_aton(eaddr);
453
454 if (ea1 == NULL) {
455 warnx("invalid Ethernet address '%s'", eaddr);
456 return (1);
457 } else {
458 *ea = *ea1;
459 sdl_m.sdl_alen = ETHER_ADDR_LEN;
460 }
461 }
462 for (;;) { /* try at most twice */
463 rtm = rtmsg(RTM_GET, dst, &sdl_m);
464 if (rtm == NULL) {
465 warn("%s", host);
466 return (1);
467 }
468 addr = (struct sockaddr_inarp *)(rtm + 1);
469 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
470 if (addr->sin_addr.s_addr != dst->sin_addr.s_addr)
471 break;
472 if (sdl->sdl_family == AF_LINK &&
473 (rtm->rtm_flags & RTF_LLINFO) &&
474 !(rtm->rtm_flags & RTF_GATEWAY) &&
475 valid_type(sdl->sdl_type) )
476 break;
477 /*
478 * If we asked for a scope entry and did not get one or
479 * did not asked for a scope entry and got one, we can
480 * proceed.
481 */
482 if ((ifscope != 0) != (rtm->rtm_flags & RTF_IFSCOPE))
483 break;
484 if (doing_proxy == 0) {
485 printf("set: can only proxy for %s\n", host);
486 return (1);
487 }
488 if (dst->sin_other & SIN_PROXY) {
489 printf("set: proxy entry exists for non 802 device\n");
490 return (1);
491 }
492 dst->sin_other = SIN_PROXY;
493 proxy_only = 1;
494 }
495
496 if (sdl->sdl_family != AF_LINK) {
497 printf("cannot intuit interface index and type for %s\n", host);
498 return (1);
499 }
500 sdl_m.sdl_type = sdl->sdl_type;
501 sdl_m.sdl_index = sdl->sdl_index;
502 return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL);
503 }
504
505 /*
506 * Display an individual arp entry
507 */
508 static int
509 get(char *host)
510 {
511 struct sockaddr_inarp *addr;
512
513 addr = getaddr(host);
514 if (addr == NULL)
515 return (1);
516 if (0 == search(addr->sin_addr.s_addr, print_entry)) {
517 printf("%s (%s) -- no entry",
518 host, inet_ntoa(addr->sin_addr));
519 if (rifname)
520 printf(" on %s", rifname);
521 printf("\n");
522 return (1);
523 }
524 return (0);
525 }
526
527 /*
528 * Delete an arp entry
529 */
530 static int
531 delete(char *host, int do_proxy)
532 {
533 struct sockaddr_inarp *addr, *dst;
534 struct rt_msghdr *rtm;
535 struct sockaddr_dl *sdl;
536
537 dst = getaddr(host);
538 if (dst == NULL)
539 return (1);
540 dst->sin_other = do_proxy;
541 for (;;) { /* try twice */
542 rtm = rtmsg(RTM_GET, dst, NULL);
543 if (rtm == NULL) {
544 warn("%s", host);
545 return (1);
546 }
547 addr = (struct sockaddr_inarp *)(rtm + 1);
548 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
549 if (addr->sin_addr.s_addr == dst->sin_addr.s_addr &&
550 sdl->sdl_family == AF_LINK &&
551 (rtm->rtm_flags & RTF_LLINFO) &&
552 !(rtm->rtm_flags & RTF_GATEWAY) &&
553 valid_type(sdl->sdl_type) )
554 break; /* found it */
555 if (dst->sin_other & SIN_PROXY) {
556 fprintf(stderr, "delete: cannot locate %s\n",host);
557 return (1);
558 }
559 dst->sin_other = SIN_PROXY;
560 }
561 if (rtmsg(RTM_DELETE, dst, NULL) != NULL) {
562 printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr));
563 return (0);
564 }
565 return (1);
566 }
567
568 /*
569 * Search the arp table and do some action on matching entries
570 */
571 static int
572 search(in_addr_t addr, action_fn *action)
573 {
574 int mib[6];
575 size_t needed;
576 char *lim, *buf, *newbuf, *next;
577 struct rt_msghdr *rtm;
578 struct sockaddr_inarp *sin2;
579 struct sockaddr_dl *sdl;
580 char ifname[IF_NAMESIZE];
581 int st, found_entry = 0;
582
583 mib[0] = CTL_NET;
584 mib[1] = PF_ROUTE;
585 mib[2] = 0;
586 mib[3] = AF_INET;
587 mib[4] = NET_RT_FLAGS;
588 mib[5] = RTF_LLINFO;
589 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
590 err(1, "route-sysctl-estimate");
591 if (needed == 0) /* empty table */
592 return 0;
593 buf = NULL;
594 for (;;) {
595 newbuf = realloc(buf, needed);
596 if (newbuf == NULL) {
597 if (buf != NULL)
598 free(buf);
599 errx(1, "could not reallocate memory");
600 }
601 buf = newbuf;
602 st = sysctl(mib, 6, buf, &needed, NULL, 0);
603 if (st == 0 || errno != ENOMEM)
604 break;
605 needed += needed / 8;
606 }
607 if (st == -1)
608 err(1, "actual retrieval of routing table");
609 lim = buf + needed;
610 for (next = buf; next < lim; next += rtm->rtm_msglen) {
611 rtm = (struct rt_msghdr *)next;
612 sin2 = (struct sockaddr_inarp *)(rtm + 1);
613 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
614 if (rifname && if_indextoname(sdl->sdl_index, ifname) &&
615 strcmp(ifname, rifname))
616 continue;
617 if (addr) {
618 if (addr != sin2->sin_addr.s_addr)
619 continue;
620 found_entry = 1;
621 }
622 (*action)(sdl, sin2, rtm);
623 }
624 free(buf);
625 return (found_entry);
626 }
627
628 /*
629 * Stolen and adapted from ifconfig
630 */
631 static char *
632 print_lladdr(struct sockaddr_dl *sdl)
633 {
634 static char buf[256];
635 char *cp;
636 int n, bufsize = sizeof (buf), p = 0;
637
638 bzero(buf, sizeof (buf));
639 cp = (char *)LLADDR(sdl);
640 if ((n = sdl->sdl_alen) > 0) {
641 while (--n >= 0)
642 p += snprintf(buf + p, bufsize - p, "%x%s",
643 *cp++ & 0xff, n > 0 ? ":" : "");
644 }
645 return (buf);
646 }
647
648 /*
649 * Display an arp entry
650 */
651 static void
652 print_entry(struct sockaddr_dl *sdl,
653 struct sockaddr_inarp *addr, struct rt_msghdr *rtm)
654 {
655 const char *host;
656 struct hostent *hp;
657 char ifname[IF_NAMESIZE];
658 #if 0
659 struct iso88025_sockaddr_dl_data *trld;
660 int seg;
661 #endif
662
663 if (nflag == 0)
664 hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
665 sizeof addr->sin_addr, AF_INET);
666 else
667 hp = 0;
668 if (hp)
669 host = hp->h_name;
670 else {
671 host = "?";
672 if (h_errno == TRY_AGAIN)
673 nflag = 1;
674 }
675 printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr));
676 if (sdl->sdl_alen) {
677 #if 1
678 printf("%s", print_lladdr(sdl));
679 #else
680 if ((sdl->sdl_type == IFT_ETHER ||
681 sdl->sdl_type == IFT_L2VLAN ||
682 sdl->sdl_type == IFT_BRIDGE) &&
683 sdl->sdl_alen == ETHER_ADDR_LEN)
684 printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl)));
685 else {
686 int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
687
688 printf("%s", link_ntoa(sdl) + n);
689 }
690 #endif
691 } else
692 printf("(incomplete)");
693 if (if_indextoname(sdl->sdl_index, ifname) != NULL)
694 printf(" on %s", ifname);
695 if ((rtm->rtm_flags & RTF_IFSCOPE))
696 printf(" ifscope");
697 if (rtm->rtm_rmx.rmx_expire == 0)
698 printf(" permanent");
699 if (addr->sin_other & SIN_PROXY)
700 printf(" published (proxy only)");
701 if (rtm->rtm_addrs & RTA_NETMASK) {
702 addr = (struct sockaddr_inarp *)
703 (SA_SIZE(sdl) + (char *)sdl);
704 if (addr->sin_addr.s_addr == 0xffffffff)
705 printf(" published");
706 if (addr->sin_len != 8)
707 printf("(weird)");
708 }
709 switch(sdl->sdl_type) {
710 case IFT_ETHER:
711 printf(" [ethernet]");
712 break;
713 #if 0
714 case IFT_ISO88025:
715 printf(" [token-ring]");
716 trld = SDL_ISO88025(sdl);
717 if (trld->trld_rcf != 0) {
718 printf(" rt=%x", ntohs(trld->trld_rcf));
719 for (seg = 0;
720 seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2);
721 seg++)
722 printf(":%x", ntohs(*(trld->trld_route[seg])));
723 }
724 break;
725 #endif
726 case IFT_FDDI:
727 printf(" [fddi]");
728 break;
729 case IFT_ATM:
730 printf(" [atm]");
731 break;
732 case IFT_L2VLAN:
733 printf(" [vlan]");
734 break;
735 case IFT_IEEE1394:
736 printf(" [firewire]");
737 break;
738 #ifdef IFT_BRIDGE
739 case IFT_BRIDGE:
740 printf(" [bridge]");
741 break;
742 #endif
743 default:
744 break;
745 }
746
747 printf("\n");
748
749 }
750
751 /*
752 * Nuke an arp entry
753 */
754 static void
755 nuke_entry(struct sockaddr_dl *sdl __unused,
756 struct sockaddr_inarp *addr, struct rt_msghdr *rtm)
757 {
758 char ip[20];
759
760 snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
761 /*
762 * When deleting all entries, specify the interface scope of each entry
763 */
764 if ((rtm->rtm_flags & RTF_IFSCOPE))
765 ifscope = rtm->rtm_index;
766 (void)delete(ip, 0);
767 ifscope = 0;
768 }
769
770 static void
771 usage(void)
772 {
773 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
774 "usage: arp [-n] [-i interface] hostname",
775 " arp [-n] [-i interface] [-l] -a",
776 " arp -d hostname [pub] [ifscope interface]",
777 " arp -d [-i interface] -a",
778 " arp -s hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]",
779 " arp -S hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]",
780 " arp -f filename");
781 exit(1);
782 }
783
784 static struct rt_msghdr *
785 rtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl)
786 {
787 static int seq;
788 int rlen;
789 int l;
790 struct sockaddr_in so_mask, *so_mask_ptr = &so_mask;
791 static int s = -1;
792 static pid_t pid;
793
794 static struct {
795 struct rt_msghdr m_rtm;
796 char m_space[512];
797 } m_rtmsg;
798
799 struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
800 char *cp = m_rtmsg.m_space;
801
802 if (s < 0) { /* first time: open socket, get pid */
803 s = socket(PF_ROUTE, SOCK_RAW, 0);
804 if (s < 0)
805 err(1, "socket");
806 pid = getpid();
807 }
808 bzero(&so_mask, sizeof(so_mask));
809 so_mask.sin_len = 8;
810 so_mask.sin_addr.s_addr = 0xffffffff;
811
812 errno = 0;
813 /*
814 * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer
815 * appropriately (except for the mask set just above).
816 */
817 if (cmd == RTM_DELETE)
818 goto doit;
819 bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
820 rtm->rtm_flags = flags;
821 rtm->rtm_version = RTM_VERSION;
822
823 /*
824 * Note: On RTM_GET the kernel will return a scoped route when both a scoped route and
825 * a unscoped route exist. That means we cannot delete a unscoped route if there is
826 * also a matching scope route
827 */
828 if (ifscope) {
829 rtm->rtm_index = ifscope;
830 rtm->rtm_flags |= RTF_IFSCOPE;
831 }
832
833 switch (cmd) {
834 default:
835 errx(1, "internal wrong cmd");
836 case RTM_ADD:
837 rtm->rtm_addrs |= RTA_GATEWAY;
838 rtm->rtm_rmx.rmx_expire = expire_time;
839 rtm->rtm_inits = RTV_EXPIRE;
840 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
841 dst->sin_other = 0;
842 if (doing_proxy) {
843 if (proxy_only)
844 dst->sin_other = SIN_PROXY;
845 else {
846 rtm->rtm_addrs |= RTA_NETMASK;
847 rtm->rtm_flags &= ~RTF_HOST;
848 }
849 }
850 /* FALLTHROUGH */
851 case RTM_GET:
852 rtm->rtm_addrs |= RTA_DST;
853 }
854 #define NEXTADDR(w, s) \
855 if ((s) != NULL && rtm->rtm_addrs & (w)) { \
856 bcopy((s), cp, sizeof(*(s))); cp += SA_SIZE(s);}
857
858 NEXTADDR(RTA_DST, dst);
859 NEXTADDR(RTA_GATEWAY, sdl);
860 NEXTADDR(RTA_NETMASK, so_mask_ptr);
861
862 rtm->rtm_msglen = cp - (char *)&m_rtmsg;
863 doit:
864 l = rtm->rtm_msglen;
865 rtm->rtm_seq = ++seq;
866 rtm->rtm_type = cmd;
867 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
868 if (errno != ESRCH || cmd != RTM_DELETE) {
869 warn("writing to routing socket");
870 return (NULL);
871 }
872 }
873 do {
874 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
875 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
876 if (l < 0)
877 warn("read from routing socket");
878 return (rtm);
879 }
880
881 /*
882 * get_ether_addr - get the hardware address of an interface on the
883 * the same subnet as ipaddr.
884 */
885 #define MAX_IFS 32
886
887 static int
888 get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr)
889 {
890 struct ifreq *ifr, *ifend, *ifp;
891 in_addr_t ina, mask;
892 struct sockaddr_dl *dla;
893 struct ifreq ifreq;
894 struct ifconf ifc;
895 struct ifreq ifs[MAX_IFS];
896 int sock;
897 int retval = 0;
898
899 sock = socket(AF_INET, SOCK_DGRAM, 0);
900 if (sock < 0)
901 err(1, "socket");
902
903 ifc.ifc_len = sizeof(ifs);
904 ifc.ifc_req = ifs;
905 if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
906 warnx("ioctl(SIOCGIFCONF)");
907 goto done;
908 }
909
910 #define NEXTIFR(i) \
911 ((struct ifreq *)((char *)&(i)->ifr_addr \
912 + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) )
913
914 /*
915 * Scan through looking for an interface with an Internet
916 * address on the same subnet as `ipaddr'.
917 */
918 ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
919 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) {
920 if (ifr->ifr_addr.sa_family != AF_INET)
921 continue;
922 strncpy(ifreq.ifr_name, ifr->ifr_name,
923 sizeof(ifreq.ifr_name));
924 ifreq.ifr_addr = ifr->ifr_addr;
925 /*
926 * Check that the interface is up,
927 * and not point-to-point or loopback.
928 */
929 if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0)
930 continue;
931 if ((ifreq.ifr_flags &
932 (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
933 IFF_LOOPBACK|IFF_NOARP))
934 != (IFF_UP|IFF_BROADCAST))
935 continue;
936 /*
937 * Get its netmask and check that it's on
938 * the right subnet.
939 */
940 if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0)
941 continue;
942 mask = ((struct sockaddr_in *)
943 &ifreq.ifr_addr)->sin_addr.s_addr;
944 ina = ((struct sockaddr_in *)
945 &ifr->ifr_addr)->sin_addr.s_addr;
946 if ((ipaddr & mask) == (ina & mask))
947 break; /* ok, we got it! */
948 }
949
950 if (ifr >= ifend)
951 goto done;
952
953 /*
954 * Now scan through again looking for a link-level address
955 * for this interface.
956 */
957 ifp = ifr;
958 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr))
959 if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 &&
960 ifr->ifr_addr.sa_family == AF_LINK)
961 break;
962 if (ifr >= ifend)
963 goto done;
964 /*
965 * Found the link-level address - copy it out
966 */
967 dla = (struct sockaddr_dl *) &ifr->ifr_addr;
968 memcpy(hwaddr, LLADDR(dla), dla->sdl_alen);
969 printf("using interface %s for proxy with address ",
970 ifp->ifr_name);
971 printf("%s\n", ether_ntoa(hwaddr));
972 retval = dla->sdl_alen;
973 done:
974 close(sock);
975 return (retval);
976 }
977
978 static char *
979 sec2str(total)
980 time_t total;
981 {
982 static char result[256];
983 int days, hours, mins, secs;
984 int first = 1;
985 char *p = result;
986
987 days = total / 3600 / 24;
988 hours = (total / 3600) % 24;
989 mins = (total / 60) % 60;
990 secs = total % 60;
991
992 if (days) {
993 first = 0;
994 p += snprintf(p, sizeof(result) - (p - result), "%dd", days);
995 }
996 if (!first || hours) {
997 first = 0;
998 p += snprintf(p, sizeof(result) - (p - result), "%dh", hours);
999 }
1000 if (!first || mins) {
1001 first = 0;
1002 p += snprintf(p, sizeof(result) - (p - result), "%dm", mins);
1003 }
1004 snprintf(p, sizeof(result) - (p - result), "%ds", secs);
1005
1006 return(result);
1007 }
1008
1009 static int
1010 search_ext(in_addr_t addr, action_ext_fn *action)
1011 {
1012 int mib[6];
1013 size_t needed;
1014 char *lim, *buf, *newbuf, *next;
1015 struct rt_msghdr_ext *ertm;
1016 struct sockaddr_inarp *sin2;
1017 struct sockaddr_dl *sdl;
1018 char ifname[IF_NAMESIZE];
1019 int st, found_entry = 0;
1020
1021 mib[0] = CTL_NET;
1022 mib[1] = PF_ROUTE;
1023 mib[2] = 0;
1024 mib[3] = AF_INET;
1025 mib[4] = NET_RT_DUMPX_FLAGS;
1026 mib[5] = RTF_LLINFO;
1027 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
1028 err(1, "route-sysctl-estimate");
1029 if (needed == 0) /* empty table */
1030 return 0;
1031 buf = NULL;
1032 for (;;) {
1033 newbuf = realloc(buf, needed);
1034 if (newbuf == NULL) {
1035 if (buf != NULL)
1036 free(buf);
1037 errx(1, "could not reallocate memory");
1038 }
1039 buf = newbuf;
1040 st = sysctl(mib, 6, buf, &needed, NULL, 0);
1041 if (st == 0 || errno != ENOMEM)
1042 break;
1043 needed += needed / 8;
1044 }
1045 if (st == -1)
1046 err(1, "actual retrieval of routing table");
1047 lim = buf + needed;
1048 for (next = buf; next < lim; next += ertm->rtm_msglen) {
1049 ertm = (struct rt_msghdr_ext *)next;
1050 sin2 = (struct sockaddr_inarp *)(ertm + 1);
1051 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
1052 if (rifname && if_indextoname(sdl->sdl_index, ifname) &&
1053 strcmp(ifname, rifname))
1054 continue;
1055 if (addr) {
1056 if (addr != sin2->sin_addr.s_addr)
1057 continue;
1058 found_entry = 1;
1059 }
1060 (*action)(sdl, sin2, ertm);
1061 }
1062 free(buf);
1063 return (found_entry);
1064 }
1065
1066 static void
1067 print_entry_ext(struct sockaddr_dl *sdl, struct sockaddr_inarp *addr,
1068 struct rt_msghdr_ext *ertm)
1069 {
1070 const char *host;
1071 struct hostent *hp;
1072 char ifname[IF_NAMESIZE];
1073 struct timeval time;
1074
1075 if (nflag == 0)
1076 hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
1077 sizeof (addr->sin_addr), AF_INET);
1078 else
1079 hp = 0;
1080
1081 if (hp)
1082 host = hp->h_name;
1083 else
1084 host = inet_ntoa(addr->sin_addr);
1085
1086 printf("%-23s ", host);
1087
1088 if (sdl->sdl_alen)
1089 printf("%-17s ", print_lladdr(sdl));
1090 else
1091 printf("%-17s ", "(incomplete)");
1092
1093 gettimeofday(&time, 0);
1094
1095 if (ertm->rtm_ri.ri_refcnt == 0 || ertm->rtm_ri.ri_snd_expire == 0)
1096 printf("%-9.9s ", "(none)");
1097 else if (ertm->rtm_ri.ri_snd_expire > time.tv_sec)
1098 printf("%-9.9s ",
1099 sec2str(ertm->rtm_ri.ri_snd_expire - time.tv_sec));
1100 else
1101 printf("%-9.9s ", "expired");
1102
1103 if (ertm->rtm_ri.ri_refcnt == 0 || ertm->rtm_ri.ri_rcv_expire == 0)
1104 printf("%-9.9s", "(none)");
1105 else if (ertm->rtm_ri.ri_rcv_expire > time.tv_sec)
1106 printf("%-9.9s",
1107 sec2str(ertm->rtm_ri.ri_rcv_expire - time.tv_sec));
1108 else
1109 printf("%-9.9s", "expired");
1110
1111 if (if_indextoname(sdl->sdl_index, ifname) == NULL)
1112 snprintf(ifname, sizeof (ifname), "%s", "?");
1113 printf(" %8.8s", ifname);
1114
1115 if (ertm->rtm_ri.ri_refcnt) {
1116 printf(" %4d", ertm->rtm_ri.ri_refcnt);
1117 if (ertm->rtm_ri.ri_probes)
1118 printf(" %4d", ertm->rtm_ri.ri_probes);
1119
1120 if (xflag) {
1121 if (!ertm->rtm_ri.ri_probes)
1122 printf(" %-4.4s", "none");
1123
1124 if (ertm->rtm_ri.ri_rssi != IFNET_RSSI_UNKNOWN)
1125 printf(" %7d", ertm->rtm_ri.ri_rssi);
1126 else
1127 printf(" %-7.7s", "unknown");
1128
1129 switch (ertm->rtm_ri.ri_lqm)
1130 {
1131 case IFNET_LQM_THRESH_OFF:
1132 printf(" %-7.7s", "off");
1133 break;
1134 case IFNET_LQM_THRESH_UNKNOWN:
1135 printf(" %-7.7s", "unknown");
1136 break;
1137 case IFNET_LQM_THRESH_POOR:
1138 printf(" %-7.7s", "poor");
1139 break;
1140 case IFNET_LQM_THRESH_GOOD:
1141 printf(" %-7.7s", "good");
1142 break;
1143 default:
1144 printf(" %7d", ertm->rtm_ri.ri_lqm);
1145 break;
1146 }
1147
1148 switch (ertm->rtm_ri.ri_npm)
1149 {
1150 case IFNET_NPM_THRESH_UNKNOWN:
1151 printf(" %-7.7s", "unknown");
1152 break;
1153 case IFNET_NPM_THRESH_NEAR:
1154 printf(" %-7.7s", "near");
1155 break;
1156 case IFNET_NPM_THRESH_GENERAL:
1157 printf(" %-7.7s", "general");
1158 break;
1159 case IFNET_NPM_THRESH_FAR:
1160 printf(" %-7.7s", "far");
1161 break;
1162 default:
1163 printf(" %7d", ertm->rtm_ri.ri_npm);
1164 break;
1165 }
1166 }
1167 }
1168 printf("\n");
1169 }