ndp.c revision 1.57 1 /* $NetBSD: ndp.c,v 1.57 2020/06/12 21:08:02 roy Exp $ */
2 /* $KAME: ndp.c,v 1.121 2005/07/13 11:30:13 keiichi Exp $ */
3
4 /*
5 * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32 /*
33 * Copyright (c) 1984, 1993
34 * The Regents of the University of California. All rights reserved.
35 *
36 * This code is derived from software contributed to Berkeley by
37 * Sun Microsystems, Inc.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 * 3. Neither the name of the University nor the names of its contributors
48 * may be used to endorse or promote products derived from this software
49 * without specific prior written permission.
50 *
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE.
62 */
63
64 /*
65 * Based on:
66 * "@(#) Copyright (c) 1984, 1993\n\
67 * The Regents of the University of California. All rights reserved.\n";
68 *
69 * "@(#)arp.c 8.2 (Berkeley) 1/2/94";
70 */
71
72 /*
73 * ndp - display, set, delete and flush neighbor cache
74 */
75
76
77 #include <sys/param.h>
78 #include <sys/file.h>
79 #include <sys/ioctl.h>
80 #include <sys/socket.h>
81 #include <sys/sysctl.h>
82 #include <sys/time.h>
83
84 #include <net/if.h>
85 #include <net/if_dl.h>
86 #include <net/if_types.h>
87 #include <net/route.h>
88
89 #include <netinet/in.h>
90
91 #include <netinet/icmp6.h>
92 #include <netinet6/in6_var.h>
93 #include <netinet6/nd6.h>
94
95 #include <arpa/inet.h>
96
97 #include <netdb.h>
98 #include <errno.h>
99 #include <nlist.h>
100 #include <stdio.h>
101 #include <string.h>
102 #include <paths.h>
103 #include <err.h>
104 #include <stdlib.h>
105 #include <fcntl.h>
106 #include <unistd.h>
107
108 #include "gmt2local.h"
109 #include "prog_ops.h"
110
111 static pid_t pid;
112 static int nflag;
113 static int tflag;
114 static int32_t thiszone; /* time difference with gmt */
115 static int my_s = -1;
116 static unsigned int repeat = 0;
117
118
119 static char host_buf[NI_MAXHOST]; /* getnameinfo() */
120 static char ifix_buf[IFNAMSIZ]; /* if_indextoname() */
121
122 static void getsocket(void);
123 static int set(int, char **);
124 static void get(char *);
125 static int delete(struct rt_msghdr *, char *);
126 static void delete_one(char *);
127 static void do_foreach(struct in6_addr *, char *, int);
128 static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, unsigned int, int);
129 static char *ether_str(struct sockaddr_dl *);
130 static int ndp_ether_aton(char *, u_char *);
131 __dead static void usage(void);
132 static int rtmsg(int, struct rt_msghdr *);
133 static void ifinfo(char *, int, char **);
134 static const char *sec2str(time_t);
135 static char *ether_str(struct sockaddr_dl *);
136 static void ts_print(const struct timeval *);
137
138 #define NDP_F_CLEAR 1
139 #define NDP_F_DELETE 2
140
141 static int mode = 0;
142 static char *arg = NULL;
143
144 int
145 main(int argc, char **argv)
146 {
147 int ch;
148
149 while ((ch = getopt(argc, argv, "acd:f:i:nstA:")) != -1)
150 switch (ch) {
151 case 'a':
152 case 'c':
153 case 's':
154 case 'd':
155 case 'f':
156 case 'i' :
157 if (mode) {
158 usage();
159 /*NOTREACHED*/
160 }
161 mode = ch;
162 arg = optarg;
163 break;
164 case 'n':
165 nflag = 1;
166 break;
167 case 't':
168 tflag = 1;
169 break;
170 case 'A':
171 if (mode) {
172 usage();
173 /*NOTREACHED*/
174 }
175 mode = 'a';
176 repeat = atoi(optarg);
177 break;
178 default:
179 usage();
180 }
181
182 argc -= optind;
183 argv += optind;
184
185 if (prog_init && prog_init() == -1)
186 err(1, "init failed");
187
188 pid = prog_getpid();
189 thiszone = gmt2local(0L);
190
191 switch (mode) {
192 case 'a':
193 case 'c':
194 if (argc != 0) {
195 usage();
196 /*NOTREACHED*/
197 }
198 do_foreach(0, NULL, mode == 'c' ? NDP_F_CLEAR : 0);
199 break;
200 case 'd':
201 if (argc != 0) {
202 usage();
203 /*NOTREACHED*/
204 }
205 delete_one(arg);
206 break;
207 case 'i':
208 ifinfo(arg, argc, argv);
209 break;
210 case 's':
211 if (argc < 2 || argc > 4)
212 usage();
213 return(set(argc, argv) ? 1 : 0);
214 case 0:
215 if (argc != 1) {
216 usage();
217 /*NOTREACHED*/
218 }
219 get(argv[0]);
220 break;
221 }
222 return(0);
223 }
224
225 static void
226 makeaddr(struct sockaddr_in6 *mysin, const void *resp)
227 {
228 const struct sockaddr_in6 *res = resp;
229 mysin->sin6_addr = res->sin6_addr;
230 mysin->sin6_scope_id = res->sin6_scope_id;
231 inet6_putscopeid(mysin, INET6_IS_ADDR_LINKLOCAL);
232 }
233
234 static void
235 getsocket(void)
236 {
237 if (my_s < 0) {
238 my_s = prog_socket(PF_ROUTE, SOCK_RAW, 0);
239 if (my_s < 0)
240 err(1, "socket");
241 }
242 }
243
244 #ifdef notdef
245 static struct sockaddr_in6 so_mask = {
246 .sin6_len = sizeof(so_mask),
247 .sin6_family = AF_INET6
248 };
249 #endif
250 static struct sockaddr_in6 blank_sin = {
251 .sin6_len = sizeof(blank_sin),
252 .sin6_family = AF_INET6
253 };
254 static struct sockaddr_in6 sin_m;
255 static struct sockaddr_dl blank_sdl = {
256 .sdl_len = sizeof(blank_sdl),
257 .sdl_family = AF_LINK,
258 };
259 static struct sockaddr_dl sdl_m;
260 static int expire_time, flags, found_entry;
261 static struct {
262 struct rt_msghdr m_rtm;
263 char m_space[512];
264 } m_rtmsg;
265
266 /*
267 * Set an individual neighbor cache entry
268 */
269 static int
270 set(int argc, char **argv)
271 {
272 register struct sockaddr_in6 *mysin = &sin_m;
273 register struct sockaddr_dl *sdl;
274 register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
275 struct addrinfo hints, *res;
276 int gai_error;
277 u_char *ea;
278 char *host = argv[0], *eaddr = argv[1];
279
280 getsocket();
281 argc -= 2;
282 argv += 2;
283 sdl_m = blank_sdl;
284 sin_m = blank_sin;
285
286 (void)memset(&hints, 0, sizeof(hints));
287 hints.ai_family = AF_INET6;
288 gai_error = getaddrinfo(host, NULL, &hints, &res);
289 if (gai_error) {
290 warnx("%s: %s", host, gai_strerror(gai_error));
291 return 1;
292 }
293 makeaddr(mysin, res->ai_addr);
294 freeaddrinfo(res);
295 ea = (u_char *)LLADDR(&sdl_m);
296 if (ndp_ether_aton(eaddr, ea) == 0)
297 sdl_m.sdl_alen = 6;
298 flags = expire_time = 0;
299 while (argc-- > 0) {
300 if (strncmp(argv[0], "temp", 4) == 0) {
301 struct timeval tim;
302
303 (void)gettimeofday(&tim, 0);
304 expire_time = tim.tv_sec + 20 * 60;
305 } else if (strncmp(argv[0], "proxy", 5) == 0)
306 flags |= RTF_ANNOUNCE;
307 argv++;
308 }
309 if (rtmsg(RTM_GET, NULL) < 0) {
310 errx(1, "RTM_GET(%s) failed", host);
311 /* NOTREACHED */
312 }
313 mysin = (struct sockaddr_in6 *)(void *)(rtm + 1);
314 sdl = (struct sockaddr_dl *)(void *)(RT_ROUNDUP(mysin->sin6_len) + (char *)(void *)mysin);
315 if (IN6_ARE_ADDR_EQUAL(&mysin->sin6_addr, &sin_m.sin6_addr)) {
316 if (sdl->sdl_family == AF_LINK &&
317 !(rtm->rtm_flags & RTF_GATEWAY)) {
318 switch (sdl->sdl_type) {
319 case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
320 case IFT_ISO88024: case IFT_ISO88025:
321 goto overwrite;
322 }
323 }
324 /*
325 * IPv4 arp command retries with sin_other = SIN_PROXY here.
326 */
327 (void)fprintf(stderr, "set: cannot configure a new entry\n");
328 return 1;
329 }
330
331 overwrite:
332 if (sdl->sdl_family != AF_LINK) {
333 warnx("cannot intuit interface index and type for %s", host);
334 return (1);
335 }
336 sdl_m.sdl_type = sdl->sdl_type;
337 sdl_m.sdl_index = sdl->sdl_index;
338 return (rtmsg(RTM_ADD, NULL));
339 }
340
341 /*
342 * Display an individual neighbor cache entry
343 */
344 static void
345 get(char *host)
346 {
347 struct sockaddr_in6 *mysin = &sin_m;
348 struct addrinfo hints, *res;
349 int gai_error;
350
351 sin_m = blank_sin;
352 (void)memset(&hints, 0, sizeof(hints));
353 hints.ai_family = AF_INET6;
354 gai_error = getaddrinfo(host, NULL, &hints, &res);
355 if (gai_error) {
356 warnx("%s: %s", host, gai_strerror(gai_error));
357 return;
358 }
359 makeaddr(mysin, res->ai_addr);
360 freeaddrinfo(res);
361 do_foreach(&mysin->sin6_addr, host, 0);
362 if (found_entry == 0) {
363 (void)getnameinfo((struct sockaddr *)(void *)mysin,
364 (socklen_t)mysin->sin6_len,
365 host_buf, sizeof(host_buf), NULL ,0,
366 (nflag ? NI_NUMERICHOST : 0));
367 errx(1, "%s (%s) -- no entry", host, host_buf);
368 }
369 }
370
371 static void
372 delete_one(char *host)
373 {
374 struct sockaddr_in6 *mysin = &sin_m;
375 struct addrinfo hints, *res;
376 int gai_error;
377
378 sin_m = blank_sin;
379 (void)memset(&hints, 0, sizeof(hints));
380 hints.ai_family = AF_INET6;
381 gai_error = getaddrinfo(host, NULL, &hints, &res);
382 if (gai_error) {
383 warnx("%s: %s", host, gai_strerror(gai_error));
384 return;
385 }
386 makeaddr(mysin, res->ai_addr);
387 freeaddrinfo(res);
388 do_foreach(&mysin->sin6_addr, host, NDP_F_DELETE);
389 }
390
391 /*
392 * Delete a neighbor cache entry
393 */
394 static int
395 delete(struct rt_msghdr *rtm, char *host)
396 {
397 char delete_host_buf[NI_MAXHOST];
398 struct sockaddr_in6 *mysin = &sin_m;
399 struct sockaddr_dl *sdl;
400
401 getsocket();
402 mysin = (struct sockaddr_in6 *)(void *)(rtm + 1);
403 sdl = (struct sockaddr_dl *)(void *)(RT_ROUNDUP(mysin->sin6_len) +
404 (char *)(void *)mysin);
405
406 if (sdl->sdl_family != AF_LINK) {
407 (void)printf("cannot locate %s\n", host);
408 return (1);
409 }
410 if (rtmsg(RTM_DELETE, rtm) == 0) {
411 struct sockaddr_in6 s6 = *mysin; /* XXX: for safety */
412
413 s6.sin6_scope_id = 0;
414 inet6_putscopeid(&s6, INET6_IS_ADDR_LINKLOCAL);
415 (void)getnameinfo((struct sockaddr *)(void *)&s6,
416 (socklen_t)s6.sin6_len, delete_host_buf,
417 sizeof(delete_host_buf), NULL, 0,
418 (nflag ? NI_NUMERICHOST : 0));
419 (void)printf("%s (%s) deleted\n", host, delete_host_buf);
420 }
421
422 return 0;
423 }
424
425 #define W_ADDR (8 * 4 + 7)
426 #define W_LL 17
427 #define W_IF 6
428
429 /*
430 * Iterate on neighbor caches and do
431 * - dump all caches,
432 * - clear all caches (NDP_F_CLEAR) or
433 * - remove matched caches (NDP_F_DELETE)
434 */
435 static void
436 do_foreach(struct in6_addr *addr, char *host, int _flags)
437 {
438 int mib[6];
439 size_t needed;
440 char *lim, *buf, *next;
441 struct rt_msghdr *rtm;
442 struct sockaddr_in6 *mysin;
443 struct sockaddr_dl *sdl;
444 struct in6_nbrinfo *nbi;
445 struct timeval tim;
446 int addrwidth;
447 int llwidth;
448 int ifwidth;
449 char flgbuf[8], *fl;
450 const char *ifname;
451 int cflag = _flags == NDP_F_CLEAR;
452 int dflag = _flags == NDP_F_DELETE;
453
454 /* Print header */
455 if (!tflag && !cflag)
456 (void)printf("%-*.*s %-*.*s %*.*s %-9.9s %1s %2s\n",
457 W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address",
458 W_IF, W_IF, "Netif", "Expire", "S", "Fl");
459
460 again:;
461 mib[0] = CTL_NET;
462 mib[1] = PF_ROUTE;
463 mib[2] = 0;
464 mib[3] = AF_INET6;
465 mib[4] = NET_RT_FLAGS;
466 mib[5] = RTF_LLDATA;
467 if (prog_sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
468 err(1, "sysctl(PF_ROUTE estimate)");
469 if (needed > 0) {
470 if ((buf = malloc(needed)) == NULL)
471 err(1, "malloc");
472 if (prog_sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
473 free(buf);
474 if (errno == ENOBUFS)
475 goto again;
476 err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)");
477 }
478 lim = buf + needed;
479 } else
480 buf = lim = NULL;
481
482 for (next = buf; next && next < lim; next += rtm->rtm_msglen) {
483 int isrouter = 0, prbs = 0;
484
485 rtm = (struct rt_msghdr *)(void *)next;
486 mysin = (struct sockaddr_in6 *)(void *)(rtm + 1);
487 sdl = (struct sockaddr_dl *)(void *)((char *)(void *)mysin + RT_ROUNDUP(mysin->sin6_len));
488
489 /*
490 * Some OSes can produce a route that has the LINK flag but
491 * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD
492 * and BSD/OS, where xx is not the interface identifier on
493 * lo0). Such routes entry would annoy getnbrinfo() below,
494 * so we skip them.
495 * XXX: such routes should have the GATEWAY flag, not the
496 * LINK flag. However, there is rotten routing software
497 * that advertises all routes that have the GATEWAY flag.
498 * Thus, KAME kernel intentionally does not set the LINK flag.
499 * What is to be fixed is not ndp, but such routing software
500 * (and the kernel workaround)...
501 */
502 if (sdl->sdl_family != AF_LINK)
503 continue;
504
505 if (!(rtm->rtm_flags & RTF_HOST))
506 continue;
507
508 if (addr) {
509 if (!IN6_ARE_ADDR_EQUAL(addr, &mysin->sin6_addr))
510 continue;
511 found_entry = 1;
512 } else if (IN6_IS_ADDR_MULTICAST(&mysin->sin6_addr))
513 continue;
514 if (dflag) {
515 (void)delete(rtm, host_buf);
516 continue;
517 }
518 if (IN6_IS_ADDR_LINKLOCAL(&mysin->sin6_addr) ||
519 IN6_IS_ADDR_MC_LINKLOCAL(&mysin->sin6_addr)) {
520 uint16_t scopeid = mysin->sin6_scope_id;
521 inet6_getscopeid(mysin, INET6_IS_ADDR_LINKLOCAL|
522 INET6_IS_ADDR_MC_LINKLOCAL);
523 if (scopeid == 0)
524 mysin->sin6_scope_id = sdl->sdl_index;
525 }
526 (void)getnameinfo((struct sockaddr *)(void *)mysin,
527 (socklen_t)mysin->sin6_len,
528 host_buf, sizeof(host_buf), NULL, 0,
529 (nflag ? NI_NUMERICHOST : 0));
530 if (cflag) {
531 /* Restore scopeid */
532 if (IN6_IS_ADDR_LINKLOCAL(&mysin->sin6_addr) ||
533 IN6_IS_ADDR_MC_LINKLOCAL(&mysin->sin6_addr))
534 inet6_putscopeid(mysin, INET6_IS_ADDR_LINKLOCAL|
535 INET6_IS_ADDR_MC_LINKLOCAL);
536 if ((rtm->rtm_flags & RTF_STATIC) == 0)
537 (void)delete(rtm, host_buf);
538 continue;
539 }
540 (void)gettimeofday(&tim, 0);
541 if (tflag)
542 ts_print(&tim);
543
544 addrwidth = strlen(host_buf);
545 if (addrwidth < W_ADDR)
546 addrwidth = W_ADDR;
547 llwidth = strlen(ether_str(sdl));
548 if (W_ADDR + W_LL - addrwidth > llwidth)
549 llwidth = W_ADDR + W_LL - addrwidth;
550 ifname = if_indextoname((unsigned int)sdl->sdl_index, ifix_buf);
551 if (!ifname)
552 ifname = "?";
553 ifwidth = strlen(ifname);
554 if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
555 ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
556
557 (void)printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth,
558 host_buf, llwidth, llwidth, ether_str(sdl), ifwidth,
559 ifwidth, ifname);
560
561 /* Print neighbor discovery specific informations */
562 nbi = getnbrinfo(&mysin->sin6_addr,
563 (unsigned int)sdl->sdl_index, 1);
564 if (nbi) {
565 if (nbi->expire > tim.tv_sec) {
566 (void)printf(" %-9.9s",
567 sec2str(nbi->expire - tim.tv_sec));
568 } else if (nbi->expire == 0)
569 (void)printf(" %-9.9s", "permanent");
570 else
571 (void)printf(" %-9.9s", "expired");
572
573 switch (nbi->state) {
574 case ND6_LLINFO_NOSTATE:
575 (void)printf(" N");
576 break;
577 #ifdef ND6_LLINFO_WAITDELETE
578 case ND6_LLINFO_WAITDELETE:
579 (void)printf(" W");
580 break;
581 #endif
582 case ND6_LLINFO_INCOMPLETE:
583 (void)printf(" I");
584 break;
585 case ND6_LLINFO_REACHABLE:
586 (void)printf(" R");
587 break;
588 case ND6_LLINFO_STALE:
589 (void)printf(" S");
590 break;
591 case ND6_LLINFO_DELAY:
592 (void)printf(" D");
593 break;
594 case ND6_LLINFO_PROBE:
595 (void)printf(" P");
596 break;
597 default:
598 (void)printf(" ?");
599 break;
600 }
601
602 isrouter = nbi->isrouter;
603 prbs = nbi->asked;
604 } else {
605 warnx("failed to get neighbor information");
606 (void)printf(" ");
607 }
608
609 /*
610 * other flags. R: router, P: proxy, W: ??
611 */
612 fl = flgbuf;
613 if (isrouter)
614 *fl++ = 'R';
615 if (rtm->rtm_flags & RTF_ANNOUNCE)
616 *fl++ = 'p';
617 *fl++ = '\0';
618 (void)printf(" %s", flgbuf);
619
620 if (prbs)
621 (void)printf(" %d", prbs);
622
623 (void)printf("\n");
624 }
625 if (buf != NULL)
626 free(buf);
627
628 if (repeat) {
629 (void)printf("\n");
630 (void)fflush(stdout);
631 (void)sleep(repeat);
632 goto again;
633 }
634 }
635
636 static struct in6_nbrinfo *
637 getnbrinfo(struct in6_addr *addr, unsigned int ifindex, int warning)
638 {
639 static struct in6_nbrinfo nbi;
640 int s;
641
642 if ((s = prog_socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
643 err(1, "socket");
644
645 (void)memset(&nbi, 0, sizeof(nbi));
646 (void)if_indextoname(ifindex, nbi.ifname);
647 nbi.addr = *addr;
648 if (prog_ioctl(s, SIOCGNBRINFO_IN6, &nbi) < 0) {
649 if (warning)
650 warn("ioctl(SIOCGNBRINFO_IN6)");
651 (void)prog_close(s);
652 return(NULL);
653 }
654
655 (void)prog_close(s);
656 return(&nbi);
657 }
658
659 static char *
660 ether_str(struct sockaddr_dl *sdl)
661 {
662 static char hbuf[NI_MAXHOST];
663
664 if (sdl->sdl_alen) {
665 if (getnameinfo((struct sockaddr *)(void *)sdl,
666 (socklen_t)sdl->sdl_len,
667 hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
668 (void)snprintf(hbuf, sizeof(hbuf), "<invalid>");
669 } else
670 (void)snprintf(hbuf, sizeof(hbuf), "(incomplete)");
671
672 return(hbuf);
673 }
674
675 static int
676 ndp_ether_aton(char *a, u_char *n)
677 {
678 int i, o[6];
679
680 i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
681 &o[3], &o[4], &o[5]);
682 if (i != 6) {
683 warnx("invalid Ethernet address '%s'", a);
684 return (1);
685 }
686 for (i = 0; i < 6; i++)
687 n[i] = o[i];
688 return (0);
689 }
690
691 static void
692 usage(void)
693 {
694 const char *pn = getprogname();
695
696 (void)fprintf(stderr, "Usage: %s [-nt] hostname\n", pn);
697 (void)fprintf(stderr,
698 " %s [-nt] -a | -c\n", pn);
699 (void)fprintf(stderr, " %s [-nt] -A wait\n", pn);
700 (void)fprintf(stderr, " %s [-nt] -d hostname\n", pn);
701 (void)fprintf(stderr, " %s [-nt] -f filename\n", pn);
702 (void)fprintf(stderr, " %s [-nt] -i interface [flags...]\n", pn);
703 (void)fprintf(stderr,
704 " %s [-nt] -s nodename etheraddr [temp] [proxy]\n", pn);
705 exit(1);
706 }
707
708 static int
709 rtmsg(int cmd, struct rt_msghdr *_rtm)
710 {
711 static int seq;
712 register struct rt_msghdr *rtm = _rtm;
713 register char *cp = m_rtmsg.m_space;
714 register int l;
715
716 errno = 0;
717 if (rtm != NULL) {
718 memcpy(&m_rtmsg, rtm, rtm->rtm_msglen);
719 rtm = &m_rtmsg.m_rtm;
720 goto doit;
721 }
722 (void)memset(&m_rtmsg, 0, sizeof(m_rtmsg));
723 rtm = &m_rtmsg.m_rtm;
724 rtm->rtm_flags = flags;
725 rtm->rtm_version = RTM_VERSION;
726
727 switch (cmd) {
728 default:
729 errx(1, "internal wrong cmd");
730 /*NOTREACHED*/
731 case RTM_ADD:
732 rtm->rtm_addrs |= RTA_GATEWAY;
733 if (expire_time) {
734 rtm->rtm_rmx.rmx_expire = expire_time;
735 rtm->rtm_inits = RTV_EXPIRE;
736 }
737 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
738 #ifdef notdef /* we don't support ipv6addr/128 type proxying. */
739 if (rtm->rtm_flags & RTF_ANNOUNCE) {
740 rtm->rtm_flags &= ~RTF_HOST;
741 rtm->rtm_addrs |= RTA_NETMASK;
742 }
743 #endif
744 rtm->rtm_addrs |= RTA_DST;
745 break;
746 case RTM_GET:
747 rtm->rtm_flags |= RTF_LLDATA;
748 rtm->rtm_addrs |= RTA_DST | RTA_GATEWAY;
749 }
750 #define NEXTADDR(w, s) \
751 if (rtm->rtm_addrs & (w)) { \
752 (void)memcpy(cp, &s, sizeof(s)); \
753 RT_ADVANCE(cp, (struct sockaddr *)(void *)&s); \
754 }
755
756 NEXTADDR(RTA_DST, sin_m);
757 NEXTADDR(RTA_GATEWAY, sdl_m);
758 #ifdef notdef /* we don't support ipv6addr/128 type proxying. */
759 (void)memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr));
760 NEXTADDR(RTA_NETMASK, so_mask);
761 #endif
762
763 rtm->rtm_msglen = cp - (char *)(void *)&m_rtmsg;
764 doit:
765 l = rtm->rtm_msglen;
766 rtm->rtm_seq = ++seq;
767 rtm->rtm_type = cmd;
768 if (prog_write(my_s, &m_rtmsg, (size_t)l) == -1) {
769 if (errno != ESRCH || cmd != RTM_DELETE)
770 err(1, "writing to routing socket");
771 }
772 do {
773 l = prog_read(my_s, &m_rtmsg, sizeof(m_rtmsg));
774 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
775 if (l < 0)
776 warn("read from routing socket");
777 return (0);
778 }
779
780 static void
781 ifinfo(char *ifname, int argc, char **argv)
782 {
783 struct in6_ndireq nd;
784 int i, s;
785 u_int32_t newflags;
786 bool valset = false, flagset = false;
787
788 if ((s = prog_socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
789 err(1, "socket");
790 (void)memset(&nd, 0, sizeof(nd));
791 (void)strlcpy(nd.ifname, ifname, sizeof(nd.ifname));
792 if (prog_ioctl(s, SIOCGIFINFO_IN6, &nd) < 0)
793 err(1, "ioctl(SIOCGIFINFO_IN6)");
794 #define ND nd.ndi
795 newflags = ND.flags;
796 for (i = 0; i < argc; i++) {
797 int clear = 0;
798 char *cp = argv[i];
799
800 if (*cp == '-') {
801 clear = 1;
802 cp++;
803 }
804
805 #define SETFLAG(s, f) \
806 do {\
807 if (strcmp(cp, (s)) == 0) {\
808 if (clear)\
809 newflags &= ~(f);\
810 else\
811 newflags |= (f);\
812 flagset = true; \
813 }\
814 } while (/*CONSTCOND*/0)
815 /*
816 * XXX: this macro is not 100% correct, in that it matches "nud" against
817 * "nudbogus". But we just let it go since this is minor.
818 */
819 #define SETVALUE(f, v) \
820 do { \
821 char *valptr; \
822 unsigned long newval; \
823 v = 0; /* unspecified */ \
824 if (strncmp(cp, f, strlen(f)) == 0) { \
825 valptr = strchr(cp, '='); \
826 if (valptr == NULL) \
827 err(1, "syntax error in %s field", (f)); \
828 errno = 0; \
829 newval = strtoul(++valptr, NULL, 0); \
830 if (errno) \
831 err(1, "syntax error in %s's value", (f)); \
832 v = newval; \
833 valset = true; \
834 } \
835 } while (/*CONSTCOND*/0)
836
837 #ifdef ND6_IFF_IFDISABLED
838 SETFLAG("disabled", ND6_IFF_IFDISABLED);
839 #endif
840 SETFLAG("nud", ND6_IFF_PERFORMNUD);
841 #ifdef ND6_IFF_ACCEPT_RTADV
842 SETFLAG("accept_rtadv", ND6_IFF_ACCEPT_RTADV);
843 #endif
844 #ifdef ND6_IFF_OVERRIDE_RTADV
845 SETFLAG("override_rtadv", ND6_IFF_OVERRIDE_RTADV);
846 #endif
847 #ifdef ND6_IFF_AUTO_LINKLOCAL
848 SETFLAG("auto_linklocal", ND6_IFF_AUTO_LINKLOCAL);
849 #endif
850 #ifdef ND6_IFF_PREFER_SOURCE
851 SETFLAG("prefer_source", ND6_IFF_PREFER_SOURCE);
852 #endif
853 #ifdef ND6_IFF_DONT_SET_IFROUTE
854 SETFLAG("dont_set_ifroute", ND6_IFF_DONT_SET_IFROUTE);
855 #endif
856 SETVALUE("basereachable", ND.basereachable);
857 SETVALUE("retrans", ND.retrans);
858 SETVALUE("curhlim", ND.chlim);
859
860 ND.flags = newflags;
861 #ifdef SIOCSIFINFO_IN6
862 if (valset && prog_ioctl(s, SIOCSIFINFO_IN6, &nd) < 0)
863 err(1, "ioctl(SIOCSIFINFO_IN6)");
864 #endif
865 if (flagset && prog_ioctl(s, SIOCSIFINFO_FLAGS, &nd) < 0)
866 err(1, "ioctl(SIOCSIFINFO_FLAGS)");
867 #undef SETFLAG
868 #undef SETVALUE
869 }
870
871 if (prog_ioctl(s, SIOCGIFINFO_IN6, &nd) < 0)
872 err(1, "ioctl(SIOCGIFINFO_IN6)");
873 (void)printf("curhlim=%d", ND.chlim);
874 (void)printf(", basereachable=%ds%dms",
875 ND.basereachable / 1000, ND.basereachable % 1000);
876 (void)printf(", retrans=%ds%dms", ND.retrans / 1000, ND.retrans % 1000);
877 if (ND.flags) {
878 (void)printf("\nFlags: ");
879 if ((ND.flags & ND6_IFF_PERFORMNUD))
880 (void)printf("nud ");
881 #ifdef ND6_IFF_IFDISABLED
882 if ((ND.flags & ND6_IFF_IFDISABLED))
883 (void)printf("disabled ");
884 #endif
885 #ifdef ND6_IFF_AUTO_LINKLOCAL
886 if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL))
887 (void)printf("auto_linklocal ");
888 #endif
889 #ifdef ND6_IFF_PREFER_SOURCE
890 if ((ND.flags & ND6_IFF_PREFER_SOURCE))
891 (void)printf("prefer_source ");
892 #endif
893 }
894 (void)putc('\n', stdout);
895 #undef ND
896
897 (void)prog_close(s);
898 }
899
900 static const char *
901 sec2str(time_t total)
902 {
903 static char result[256];
904 int days, hours, mins, secs;
905 int first = 1;
906 char *p = result;
907 char *ep = &result[sizeof(result)];
908 int n;
909
910 days = total / 3600 / 24;
911 hours = (total / 3600) % 24;
912 mins = (total / 60) % 60;
913 secs = total % 60;
914
915 if (days) {
916 first = 0;
917 n = snprintf(p, (size_t)(ep - p), "%dd", days);
918 if (n < 0 || n >= ep - p)
919 return "?";
920 p += n;
921 }
922 if (!first || hours) {
923 first = 0;
924 n = snprintf(p, (size_t)(ep - p), "%dh", hours);
925 if (n < 0 || n >= ep - p)
926 return "?";
927 p += n;
928 }
929 if (!first || mins) {
930 first = 0;
931 n = snprintf(p, (size_t)(ep - p), "%dm", mins);
932 if (n < 0 || n >= ep - p)
933 return "?";
934 p += n;
935 }
936 (void)snprintf(p, (size_t)(ep - p), "%ds", secs);
937
938 return(result);
939 }
940
941 /*
942 * Print the timestamp
943 * from tcpdump/util.c
944 */
945 static void
946 ts_print(const struct timeval *tvp)
947 {
948 int s;
949
950 /* Default */
951 s = (tvp->tv_sec + thiszone) % 86400;
952 (void)printf("%02d:%02d:%02d.%06u ",
953 s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tvp->tv_usec);
954 }
955