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