xref: /freebsd/usr.sbin/ndp/ndp.c (revision 66fd12cf4896eb08ad8e7a2627537f84ead84dd3)
1 /*	$FreeBSD$	*/
2 /*	$KAME: ndp.c,v 1.104 2003/06/27 07:48:39 itojun Exp $	*/
3 
4 /*-
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
8  * All rights reserved.
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 project 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 PROJECT 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 PROJECT 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  * Copyright (c) 1984, 1993
36  *	The Regents of the University of California.  All rights reserved.
37  *
38  * This code is derived from software contributed to Berkeley by
39  * Sun Microsystems, Inc.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. Neither the name of the University nor the names of its contributors
50  *    may be used to endorse or promote products derived from this software
51  *    without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63  * SUCH DAMAGE.
64  */
65 
66 /*
67  * Based on:
68  * "@(#) Copyright (c) 1984, 1993\n\
69  *	The Regents of the University of California.  All rights reserved.\n";
70  *
71  * "@(#)arp.c	8.2 (Berkeley) 1/2/94";
72  */
73 
74 /*
75  * ndp - display, set, delete and flush neighbor cache
76  */
77 
78 
79 #include <sys/param.h>
80 #include <sys/file.h>
81 #include <sys/ioctl.h>
82 #include <sys/socket.h>
83 #include <sys/sysctl.h>
84 #include <sys/time.h>
85 #include <sys/queue.h>
86 
87 #include <net/if.h>
88 #include <net/if_dl.h>
89 #include <net/if_types.h>
90 #include <net/route.h>
91 
92 #include <netinet/in.h>
93 #include <netinet/if_ether.h>
94 
95 #include <netinet/icmp6.h>
96 #include <netinet6/in6_var.h>
97 #include <netinet6/nd6.h>
98 
99 #include <arpa/inet.h>
100 
101 #include <assert.h>
102 #include <ctype.h>
103 #include <netdb.h>
104 #include <errno.h>
105 #include <nlist.h>
106 #include <stdbool.h>
107 #include <stdio.h>
108 #include <string.h>
109 #include <paths.h>
110 #include <err.h>
111 #include <stdint.h>
112 #include <stdlib.h>
113 #include <fcntl.h>
114 #include <unistd.h>
115 #include <libxo/xo.h>
116 #include <time.h>
117 
118 #include "ndp.h"
119 
120 #define	NEXTADDR(w, s)					\
121 	if (rtm->rtm_addrs & (w)) {			\
122 		bcopy((char *)&s, cp, sizeof(s));	\
123 		cp += SA_SIZE(&s);			\
124 	}
125 
126 static pid_t pid;
127 static int32_t thiszone;	/* time difference with gmt */
128 static int s = -1;
129 static int repeat = 0;
130 
131 static char host_buf[NI_MAXHOST];	/* getnameinfo() */
132 static char ifix_buf[IFNAMSIZ];		/* if_indextoname() */
133 
134 static int file(char *);
135 static int set(int, char **);
136 static void get(char *);
137 static int delete(char *);
138 static int dump(struct sockaddr_in6 *, int);
139 static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int);
140 static int ndp_ether_aton(char *, u_char *);
141 static void usage(void) __dead2;
142 static void ifinfo(char *, int, char **);
143 static void rtrlist(void);
144 static void plist(void);
145 static void pfx_flush(void);
146 static void rtr_flush(void);
147 static void harmonize_rtr(void);
148 #ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
149 static void getdefif(void);
150 static void setdefif(char *);
151 #endif
152 
153 #ifdef WITHOUT_NETLINK
154 static void getsocket(void);
155 static int rtmsg(int);
156 #endif
157 
158 static const char *rtpref_str[] = {
159 	"medium",		/* 00 */
160 	"high",			/* 01 */
161 	"rsv",			/* 10 */
162 	"low"			/* 11 */
163 };
164 
165 struct ndp_opts opts = {};
166 
167 #define NDP_XO_VERSION	"1"
168 
169 bool
170 valid_type(int if_type)
171 {
172 	switch (if_type) {
173 	case IFT_ETHER:
174 	case IFT_FDDI:
175 	case IFT_ISO88023:
176 	case IFT_ISO88024:
177 	case IFT_ISO88025:
178 	case IFT_L2VLAN:
179 	case IFT_BRIDGE:
180 		return (true);
181 		break;
182 	}
183 	return (false);
184 }
185 
186 static int32_t
187 utc_offset(void)
188 {
189 	time_t t;
190 	struct tm *tm;
191 
192 	t = time(NULL);
193 	tm = localtime(&t);
194 
195 	assert(tm->tm_gmtoff > INT32_MIN && tm->tm_gmtoff < INT32_MAX);
196 
197 	return (tm->tm_gmtoff);
198 }
199 
200 int
201 main(int argc, char **argv)
202 {
203 	int ch, mode = 0;
204 	char *arg = NULL;
205 
206 	pid = getpid();
207 	thiszone = utc_offset();
208 
209 	argc = xo_parse_args(argc, argv);
210 	if (argc < 0)
211 		exit(1);
212 	xo_set_version(NDP_XO_VERSION);
213 	xo_open_container("ndp");
214 
215 	while ((ch = getopt(argc, argv, "acd:f:Ii:nprstA:HPR")) != -1)
216 		switch (ch) {
217 		case 'a':
218 		case 'c':
219 		case 'p':
220 		case 'r':
221 		case 'H':
222 		case 'P':
223 		case 'R':
224 		case 's':
225 		case 'I':
226 			if (mode) {
227 				usage();
228 				/*NOTREACHED*/
229 			}
230 			mode = ch;
231 			arg = NULL;
232 			break;
233 		case 'f':
234 			exit(file(optarg) ? 1 : 0);
235 		case 'd':
236 		case 'i':
237 			if (mode) {
238 				usage();
239 				/*NOTREACHED*/
240 			}
241 			mode = ch;
242 			arg = optarg;
243 			break;
244 		case 'n':
245 			opts.nflag = true;
246 			break;
247 		case 't':
248 			opts.tflag = true;
249 			break;
250 		case 'A':
251 			if (mode) {
252 				usage();
253 				/*NOTREACHED*/
254 			}
255 			mode = 'a';
256 			repeat = atoi(optarg);
257 			if (repeat < 0) {
258 				usage();
259 				/*NOTREACHED*/
260 			}
261 			break;
262 		default:
263 			usage();
264 		}
265 
266 	argc -= optind;
267 	argv += optind;
268 
269 	switch (mode) {
270 	case 'a':
271 	case 'c':
272 		if (argc != 0) {
273 			usage();
274 			/*NOTREACHED*/
275 		}
276 		dump(0, mode == 'c');
277 		break;
278 	case 'd':
279 		if (argc != 0) {
280 			usage();
281 			/*NOTREACHED*/
282 		}
283 		xo_open_list("neighbor-cache");
284 		delete(arg);
285 		xo_close_list("neighbor-cache");
286 		break;
287 	case 'I':
288 #ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
289 		if (argc > 1) {
290 			usage();
291 			/*NOTREACHED*/
292 		} else if (argc == 1) {
293 			if (strcmp(*argv, "delete") == 0 ||
294 			    if_nametoindex(*argv))
295 				setdefif(*argv);
296 			else
297 				xo_errx(1, "invalid interface %s", *argv);
298 		}
299 		getdefif(); /* always call it to print the result */
300 		break;
301 #else
302 		xo_errx(1, "not supported yet");
303 		/*NOTREACHED*/
304 #endif
305 	case 'p':
306 		if (argc != 0) {
307 			usage();
308 			/*NOTREACHED*/
309 		}
310 		plist();
311 		break;
312 	case 'i':
313 		ifinfo(arg, argc, argv);
314 		break;
315 	case 'r':
316 		if (argc != 0) {
317 			usage();
318 			/*NOTREACHED*/
319 		}
320 		rtrlist();
321 		break;
322 	case 's':
323 		if (argc < 2 || argc > 4)
324 			usage();
325 		exit(set(argc, argv) ? 1 : 0);
326 	case 'H':
327 		if (argc != 0) {
328 			usage();
329 			/*NOTREACHED*/
330 		}
331 		harmonize_rtr();
332 		break;
333 	case 'P':
334 		if (argc != 0) {
335 			usage();
336 			/*NOTREACHED*/
337 		}
338 		pfx_flush();
339 		break;
340 	case 'R':
341 		if (argc != 0) {
342 			usage();
343 			/*NOTREACHED*/
344 		}
345 		rtr_flush();
346 		break;
347 	case 0:
348 		if (argc != 1) {
349 			usage();
350 			/*NOTREACHED*/
351 		}
352 		get(argv[0]);
353 		break;
354 	}
355 	xo_close_container("ndp");
356 	xo_finish();
357 	exit(0);
358 }
359 
360 /*
361  * Process a file to set standard ndp entries
362  */
363 static int
364 file(char *name)
365 {
366 	FILE *fp;
367 	int i, retval;
368 	char line[100], arg[5][50], *args[5], *p;
369 
370 	if ((fp = fopen(name, "r")) == NULL)
371 		xo_err(1, "cannot open %s", name);
372 	args[0] = &arg[0][0];
373 	args[1] = &arg[1][0];
374 	args[2] = &arg[2][0];
375 	args[3] = &arg[3][0];
376 	args[4] = &arg[4][0];
377 	retval = 0;
378 	while (fgets(line, sizeof(line), fp) != NULL) {
379 		if ((p = strchr(line, '#')) != NULL)
380 			*p = '\0';
381 		for (p = line; isblank(*p); p++);
382 		if (*p == '\n' || *p == '\0')
383 			continue;
384 		i = sscanf(line, "%49s %49s %49s %49s %49s",
385 		    arg[0], arg[1], arg[2], arg[3], arg[4]);
386 		if (i < 2) {
387 			xo_warnx("bad line: %s", line);
388 			retval = 1;
389 			continue;
390 		}
391 		if (set(i, args))
392 			retval = 1;
393 	}
394 	fclose(fp);
395 	return (retval);
396 }
397 
398 static void
399 getsocket(void)
400 {
401 	if (s < 0) {
402 		s = socket(PF_ROUTE, SOCK_RAW, 0);
403 		if (s < 0) {
404 			xo_err(1, "socket");
405 			/* NOTREACHED */
406 		}
407 	}
408 }
409 
410 static struct sockaddr_in6 so_mask = {
411 	.sin6_len = sizeof(so_mask),
412 	.sin6_family = AF_INET6
413 };
414 static struct sockaddr_in6 blank_sin = {
415 	.sin6_len = sizeof(blank_sin),
416 	.sin6_family = AF_INET6
417 };
418 static struct sockaddr_in6 sin_m;
419 static struct sockaddr_dl blank_sdl = {
420 	.sdl_len = sizeof(blank_sdl),
421 	.sdl_family = AF_LINK
422 };
423 static struct sockaddr_dl sdl_m;
424 #ifdef WITHOUT_NETLINK
425 static struct {
426 	struct	rt_msghdr m_rtm;
427 	char	m_space[512];
428 } m_rtmsg;
429 #endif
430 
431 /*
432  * Set an individual neighbor cache entry
433  */
434 static int
435 set(int argc, char **argv)
436 {
437 	struct sockaddr_in6 *sin = &sin_m;
438 	int gai_error;
439 	u_char *ea;
440 	char *host = argv[0], *eaddr = argv[1];
441 
442 	argc -= 2;
443 	argv += 2;
444 	sdl_m = blank_sdl;
445 	sin_m = blank_sin;
446 
447 	gai_error = getaddr(host, sin);
448 	if (gai_error) {
449 		xo_warnx("%s: %s", host, gai_strerror(gai_error));
450 		return 1;
451 	}
452 
453 	ea = (u_char *)LLADDR(&sdl_m);
454 	if (ndp_ether_aton(eaddr, ea) == 0)
455 		sdl_m.sdl_alen = 6;
456 	while (argc-- > 0) {
457 		if (strncmp(argv[0], "temp", 4) == 0) {
458 			struct timeval now;
459 
460 			gettimeofday(&now, 0);
461 			opts.expire_time = now.tv_sec + 20 * 60;
462 		} else if (strncmp(argv[0], "proxy", 5) == 0)
463 			opts.flags |= RTF_ANNOUNCE;
464 		argv++;
465 	}
466 
467 #ifndef WITHOUT_NETLINK
468 	return (set_nl(0, sin, &sdl_m, host));
469 #else
470 	struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
471 	struct sockaddr_dl *sdl;
472 
473 	getsocket();
474 
475 	if (rtmsg(RTM_GET) < 0) {
476 		xo_errx(1, "RTM_GET(%s) failed", host);
477 		/* NOTREACHED */
478 	}
479 	sin = (struct sockaddr_in6 *)(rtm + 1);
480 	sdl = (struct sockaddr_dl *)(ALIGN(sin->sin6_len) + (char *)sin);
481 	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
482 		if (sdl->sdl_family == AF_LINK &&
483 		    !(rtm->rtm_flags & RTF_GATEWAY)) {
484 			if (valid_type(sdl->sdl_type))
485 				goto overwrite;
486 		}
487 		xo_warnx("cannot configure a new entry");
488 		return 1;
489 	}
490 
491 overwrite:
492 	if (sdl->sdl_family != AF_LINK) {
493 		xo_warnx("cannot intuit interface index and type for %s", host);
494 		return (1);
495 	}
496 	sdl_m.sdl_type = sdl->sdl_type;
497 	sdl_m.sdl_index = sdl->sdl_index;
498 	return (rtmsg(RTM_ADD));
499 #endif
500 }
501 
502 int
503 getaddr(char *host, struct sockaddr_in6 *sin6)
504 {
505 	struct addrinfo hints = { .ai_family = AF_INET6 };
506 	struct addrinfo *res;
507 
508 	int gai_error = getaddrinfo(host, NULL, &hints, &res);
509 	if (gai_error != 0)
510 		return (gai_error);
511 	sin6->sin6_family = AF_INET6;
512 	sin6->sin6_len = sizeof(*sin6);
513 	sin6->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
514 	sin6->sin6_scope_id =
515 	    ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
516 	return (0);
517 }
518 
519 /*
520  * Display an individual neighbor cache entry
521  */
522 static void
523 get(char *host)
524 {
525 	struct sockaddr_in6 *sin = &sin_m;
526 	int gai_error;
527 
528 	sin_m = blank_sin;
529 
530 	gai_error = getaddr(host, sin);
531 	if (gai_error) {
532 		xo_warnx("%s: %s", host, gai_strerror(gai_error));
533 		return;
534 	}
535 	if (dump(sin, 0) == 0) {
536 		getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
537 		    sizeof(host_buf), NULL ,0,
538 		    (opts.nflag ? NI_NUMERICHOST : 0));
539 		xo_errx(1, "%s (%s) -- no entry", host, host_buf);
540 	}
541 }
542 
543 #ifdef WITHOUT_NETLINK
544 /*
545  * Delete a neighbor cache entry
546  */
547 static int
548 delete_rtsock(char *host)
549 {
550 	struct sockaddr_in6 *sin = &sin_m;
551 	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
552 	register char *cp = m_rtmsg.m_space;
553 	struct sockaddr_dl *sdl;
554 	int gai_error;
555 
556 	getsocket();
557 	sin_m = blank_sin;
558 
559 	gai_error = getaddr(host, sin);
560 	if (gai_error) {
561 		xo_warnx("%s: %s", host, gai_strerror(gai_error));
562 		return 1;
563 	}
564 
565 	if (rtmsg(RTM_GET) < 0) {
566 		xo_errx(1, "RTM_GET(%s) failed", host);
567 		/* NOTREACHED */
568 	}
569 	sin = (struct sockaddr_in6 *)(rtm + 1);
570 	sdl = (struct sockaddr_dl *)(ALIGN(sin->sin6_len) + (char *)sin);
571 	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
572 		if (sdl->sdl_family == AF_LINK &&
573 		    !(rtm->rtm_flags & RTF_GATEWAY)) {
574 			goto delete;
575 		}
576 		xo_warnx("delete: cannot delete non-NDP entry");
577 		return 1;
578 	}
579 
580 delete:
581 	if (sdl->sdl_family != AF_LINK) {
582 		xo_warnx("cannot locate %s", host);
583 		return (1);
584 	}
585 	/*
586 	 * need to reinit the field because it has rt_key
587 	 * but we want the actual address
588 	 */
589 	NEXTADDR(RTA_DST, sin_m);
590 	rtm->rtm_flags |= RTF_LLDATA;
591 	if (rtmsg(RTM_DELETE) == 0) {
592 		getnameinfo((struct sockaddr *)sin,
593 		    sin->sin6_len, host_buf,
594 		    sizeof(host_buf), NULL, 0,
595 		    (opts.nflag ? NI_NUMERICHOST : 0));
596 		xo_open_instance("neighbor-cache");
597 
598 		char *ifname = if_indextoname(sdl->sdl_index, ifix_buf);
599 		if (ifname == NULL) {
600 			strlcpy(ifix_buf, "?", sizeof(ifix_buf));
601 			ifname = ifix_buf;
602 		}
603 		char abuf[INET6_ADDRSTRLEN];
604 		inet_ntop(AF_INET6, &sin->sin6_addr, abuf, sizeof(abuf));
605 
606 		xo_emit("{:hostname/%s}{d:/ (%s) deleted\n}", host, host_buf);
607 		xo_emit("{e:address/%s}{e:interface/%s}", abuf, ifname);
608 		xo_close_instance("neighbor-cache");
609 	}
610 
611 	return 0;
612 }
613 
614 /*
615  * Dump the entire neighbor cache
616  */
617 static int
618 dump_rtsock(struct sockaddr_in6 *addr, int cflag)
619 {
620 	int mib[6];
621 	size_t needed;
622 	char *lim, *buf, *next;
623 	struct rt_msghdr *rtm;
624 	struct sockaddr_in6 *sin;
625 	struct sockaddr_dl *sdl;
626 	struct timeval now;
627 	time_t expire;
628 	int addrwidth;
629 	int llwidth;
630 	int ifwidth;
631 	char flgbuf[8];
632 	char *ifname;
633 
634 	/* Print header */
635 	if (!opts.tflag && !cflag) {
636 		char xobuf[200];
637 		snprintf(xobuf, sizeof(xobuf),
638 		    "{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:%%1s} {T:%%5s}\n",
639 		    W_ADDR, W_ADDR, W_LL, W_LL, W_IF, W_IF);
640 		xo_emit(xobuf, "Neighbor", "Linklayer Address", "Netif", "Expire", "S", "Flags");
641 	}
642 	xo_open_list("neighbor-cache");
643 again:;
644 	mib[0] = CTL_NET;
645 	mib[1] = PF_ROUTE;
646 	mib[2] = 0;
647 	mib[3] = AF_INET6;
648 	mib[4] = NET_RT_FLAGS;
649 #ifdef RTF_LLINFO
650 	mib[5] = RTF_LLINFO;
651 #else
652 	mib[5] = 0;
653 #endif
654 	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
655 		xo_err(1, "sysctl(PF_ROUTE estimate)");
656 	if (needed > 0) {
657 		if ((buf = malloc(needed)) == NULL)
658 			xo_err(1, "malloc");
659 		if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
660 			xo_err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)");
661 		lim = buf + needed;
662 	} else
663 		buf = lim = NULL;
664 
665 	int count = 0;
666 	for (next = buf; next && next < lim; next += rtm->rtm_msglen) {
667 		int isrouter = 0, prbs = 0;
668 
669 		rtm = (struct rt_msghdr *)next;
670 		sin = (struct sockaddr_in6 *)(rtm + 1);
671 		sdl = (struct sockaddr_dl *)((char *)sin +
672 		    ALIGN(sin->sin6_len));
673 
674 		/*
675 		 * Some OSes can produce a route that has the LINK flag but
676 		 * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD
677 		 * and BSD/OS, where xx is not the interface identifier on
678 		 * lo0).  Such routes entry would annoy getnbrinfo() below,
679 		 * so we skip them.
680 		 * XXX: such routes should have the GATEWAY flag, not the
681 		 * LINK flag.  However, there is rotten routing software
682 		 * that advertises all routes that have the GATEWAY flag.
683 		 * Thus, KAME kernel intentionally does not set the LINK flag.
684 		 * What is to be fixed is not ndp, but such routing software
685 		 * (and the kernel workaround)...
686 		 */
687 		if (sdl->sdl_family != AF_LINK)
688 			continue;
689 
690 		if (!(rtm->rtm_flags & RTF_HOST))
691 			continue;
692 
693 		if (addr) {
694 			if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr,
695 			    &sin->sin6_addr) == 0 ||
696 			    addr->sin6_scope_id != sin->sin6_scope_id)
697 				continue;
698 		} else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))
699 			continue;
700 		count++;
701 		if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) ||
702 		    IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) {
703 			/* XXX: should scope id be filled in the kernel? */
704 			if (sin->sin6_scope_id == 0)
705 				sin->sin6_scope_id = sdl->sdl_index;
706 		}
707 		getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
708 		    sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0));
709 		if (cflag) {
710 #ifdef RTF_WASCLONED
711 			if (rtm->rtm_flags & RTF_WASCLONED)
712 				delete(host_buf);
713 #elif defined(RTF_CLONED)
714 			if (rtm->rtm_flags & RTF_CLONED)
715 				delete(host_buf);
716 #else
717 			if (rtm->rtm_flags & RTF_PINNED)
718 				continue;
719 			delete(host_buf);
720 #endif
721 			continue;
722 		}
723 		gettimeofday(&now, 0);
724 		if (opts.tflag)
725 			ts_print(&now);
726 
727 		addrwidth = strlen(host_buf);
728 		if (addrwidth < W_ADDR)
729 			addrwidth = W_ADDR;
730 		llwidth = strlen(ether_str(sdl));
731 		if (W_ADDR + W_LL - addrwidth > llwidth)
732 			llwidth = W_ADDR + W_LL - addrwidth;
733 		ifname = if_indextoname(sdl->sdl_index, ifix_buf);
734 		if (ifname == NULL) {
735 			strlcpy(ifix_buf, "?", sizeof(ifix_buf));
736 			ifname = ifix_buf;
737 		}
738 		ifwidth = strlen(ifname);
739 		if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
740 			ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
741 
742 		xo_open_instance("neighbor-cache");
743 		/* Compose format string for libxo, as it doesn't support *.* */
744 		char xobuf[200];
745 		snprintf(xobuf, sizeof(xobuf),
746 		    "{:address/%%-%d.%ds/%%s} {:mac-address/%%-%d.%ds/%%s} {:interface/%%%d.%ds/%%s}",
747 		    addrwidth, addrwidth, llwidth, llwidth, ifwidth, ifwidth);
748 		xo_emit(xobuf, host_buf, ether_str(sdl), ifname);
749 
750 		/* Print neighbor discovery specific information */
751 		expire = rtm->rtm_rmx.rmx_expire;
752 		int expire_in = expire - now.tv_sec;
753 		if (expire > now.tv_sec)
754 			xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", sec2str(expire_in), expire_in);
755 		else if (expire == 0)
756 			xo_emit("{d:/ %-9.9s}{en:permanent/true}", "permanent");
757 		else
758 			xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", "expired", expire_in);
759 
760 		char *lle_state = "";
761 		switch (rtm->rtm_rmx.rmx_state) {
762 		case ND6_LLINFO_NOSTATE:
763 			lle_state = "N";
764 			break;
765 #ifdef ND6_LLINFO_WAITDELETE
766 		case ND6_LLINFO_WAITDELETE:
767 			lle_state = "W";
768 			break;
769 #endif
770 		case ND6_LLINFO_INCOMPLETE:
771 			lle_state = "I";
772 			break;
773 		case ND6_LLINFO_REACHABLE:
774 			lle_state = "R";
775 			break;
776 		case ND6_LLINFO_STALE:
777 			lle_state = "S";
778 			break;
779 		case ND6_LLINFO_DELAY:
780 			lle_state = "D";
781 			break;
782 		case ND6_LLINFO_PROBE:
783 			lle_state = "P";
784 			break;
785 		default:
786 			lle_state = "?";
787 			break;
788 		}
789 		xo_emit(" {:neighbor-state/%s}", lle_state);
790 
791 		isrouter = rtm->rtm_flags & RTF_GATEWAY;
792 		prbs = rtm->rtm_rmx.rmx_pksent;
793 
794 		/*
795 		 * other flags. R: router, P: proxy, W: ??
796 		 */
797 		if ((rtm->rtm_addrs & RTA_NETMASK) == 0) {
798 			snprintf(flgbuf, sizeof(flgbuf), "%s%s",
799 			    isrouter ? "R" : "",
800 			    (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
801 		} else {
802 #if 0			/* W and P are mystery even for us */
803 			sin = (struct sockaddr_in6 *)
804 			    (sdl->sdl_len + (char *)sdl);
805 			snprintf(flgbuf, sizeof(flgbuf), "%s%s%s%s",
806 			    isrouter ? "R" : "",
807 			    !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr) ? "P" : "",
808 			    (sin->sin6_len != sizeof(struct sockaddr_in6)) ? "W" : "",
809 			    (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
810 #else
811 			snprintf(flgbuf, sizeof(flgbuf), "%s%s",
812 			    isrouter ? "R" : "",
813 			    (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
814 #endif
815 		}
816 		xo_emit(" {:nd-flags/%s}", flgbuf);
817 
818 		if (prbs)
819 			xo_emit("{d:/ %d}", prbs);
820 
821 		xo_emit("\n");
822 		xo_close_instance("neighbor-cache");
823 	}
824 	if (buf != NULL)
825 		free(buf);
826 
827 	if (repeat) {
828 		xo_emit("\n");
829 		xo_flush();
830 		sleep(repeat);
831 		goto again;
832 	}
833 
834 	xo_close_list("neighbor-cache");
835 
836 	return (count);
837 }
838 #endif
839 
840 
841 static int
842 delete(char *host)
843 {
844 #ifndef WITHOUT_NETLINK
845 	return (delete_nl(0, host));
846 #else
847 	return (delete_rtsock(host));
848 #endif
849 }
850 
851 static int
852 dump(struct sockaddr_in6 *addr, int cflag)
853 {
854 #ifndef WITHOUT_NETLINK
855 	return (print_entries_nl(0, addr, cflag));
856 #else
857 	return (dump_rtsock(addr, cflag));
858 #endif
859 }
860 
861 static struct in6_nbrinfo *
862 getnbrinfo(struct in6_addr *addr, int ifindex, int warning)
863 {
864 	static struct in6_nbrinfo nbi;
865 	int sock;
866 
867 	if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
868 		xo_err(1, "socket");
869 
870 	bzero(&nbi, sizeof(nbi));
871 	if_indextoname(ifindex, nbi.ifname);
872 	nbi.addr = *addr;
873 	if (ioctl(sock, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) {
874 		if (warning)
875 			xo_warn("ioctl(SIOCGNBRINFO_IN6)");
876 		close(sock);
877 		return(NULL);
878 	}
879 
880 	close(sock);
881 	return(&nbi);
882 }
883 
884 char *
885 ether_str(struct sockaddr_dl *sdl)
886 {
887 	static char hbuf[NI_MAXHOST];
888 
889 	if (sdl->sdl_alen == ETHER_ADDR_LEN) {
890 		strlcpy(hbuf, ether_ntoa((struct ether_addr *)LLADDR(sdl)),
891 		    sizeof(hbuf));
892 	} else if (sdl->sdl_alen) {
893 		int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
894 		snprintf(hbuf, sizeof(hbuf), "%s", link_ntoa(sdl) + n);
895 	} else
896 		snprintf(hbuf, sizeof(hbuf), "(incomplete)");
897 
898 	return(hbuf);
899 }
900 
901 static int
902 ndp_ether_aton(char *a, u_char *n)
903 {
904 	int i, o[6];
905 
906 	i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
907 	    &o[3], &o[4], &o[5]);
908 	if (i != 6) {
909 		xo_warnx("invalid Ethernet address '%s'", a);
910 		return (1);
911 	}
912 	for (i = 0; i < 6; i++)
913 		n[i] = o[i];
914 	return (0);
915 }
916 
917 static void
918 usage(void)
919 {
920 	printf("usage: ndp [-nt] hostname\n");
921 	printf("       ndp [-nt] -a | -c | -p | -r | -H | -P | -R\n");
922 	printf("       ndp [-nt] -A wait\n");
923 	printf("       ndp [-nt] -d hostname\n");
924 	printf("       ndp [-nt] -f filename\n");
925 	printf("       ndp [-nt] -i interface [flags...]\n");
926 #ifdef SIOCSDEFIFACE_IN6
927 	printf("       ndp [-nt] -I [interface|delete]\n");
928 #endif
929 	printf("       ndp [-nt] -s nodename etheraddr [temp] [proxy]\n");
930 	exit(1);
931 }
932 
933 #ifdef WITHOUT_NETLINK
934 static int
935 rtmsg(int cmd)
936 {
937 	static int seq;
938 	int rlen;
939 	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
940 	register char *cp = m_rtmsg.m_space;
941 	register int l;
942 
943 	errno = 0;
944 	if (cmd == RTM_DELETE)
945 		goto doit;
946 	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
947 	rtm->rtm_flags = opts.flags;
948 	rtm->rtm_version = RTM_VERSION;
949 
950 	switch (cmd) {
951 	default:
952 		xo_errx(1, "internal wrong cmd");
953 	case RTM_ADD:
954 		rtm->rtm_addrs |= RTA_GATEWAY;
955 		if (opts.expire_time) {
956 			rtm->rtm_rmx.rmx_expire = opts.expire_time;
957 			rtm->rtm_inits = RTV_EXPIRE;
958 		}
959 		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
960 		/* FALLTHROUGH */
961 	case RTM_GET:
962 		rtm->rtm_addrs |= RTA_DST;
963 	}
964 
965 	NEXTADDR(RTA_DST, sin_m);
966 	NEXTADDR(RTA_GATEWAY, sdl_m);
967 
968 	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
969 doit:
970 	l = rtm->rtm_msglen;
971 	rtm->rtm_seq = ++seq;
972 	rtm->rtm_type = cmd;
973 	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
974 		if (errno != ESRCH || cmd != RTM_DELETE) {
975 			xo_err(1, "writing to routing socket");
976 			/* NOTREACHED */
977 		}
978 	}
979 	do {
980 		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
981 	} while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq ||
982 	    rtm->rtm_pid != pid));
983 	if (l < 0)
984 		xo_warn("read from routing socket");
985 	return (0);
986 }
987 #endif
988 
989 static void
990 ifinfo(char *ifname, int argc, char **argv)
991 {
992 	struct in6_ndireq nd;
993 	int i, sock;
994 	u_int32_t newflags;
995 #ifdef IPV6CTL_USETEMPADDR
996 	u_int8_t nullbuf[8];
997 #endif
998 
999 	if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
1000 		xo_err(1, "socket");
1001 		/* NOTREACHED */
1002 	}
1003 	bzero(&nd, sizeof(nd));
1004 	strlcpy(nd.ifname, ifname, sizeof(nd.ifname));
1005 	if (ioctl(sock, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
1006 		xo_err(1, "ioctl(SIOCGIFINFO_IN6)");
1007 		/* NOTREACHED */
1008 	}
1009 #define	ND nd.ndi
1010 	newflags = ND.flags;
1011 	for (i = 0; i < argc; i++) {
1012 		int clear = 0;
1013 		char *cp = argv[i];
1014 
1015 		if (*cp == '-') {
1016 			clear = 1;
1017 			cp++;
1018 		}
1019 
1020 #define	SETFLAG(s, f) do {			\
1021 	if (strcmp(cp, (s)) == 0) {		\
1022 		if (clear)			\
1023 			newflags &= ~(f);	\
1024 		else				\
1025 			newflags |= (f);	\
1026 	}					\
1027 } while (0)
1028 /*
1029  * XXX: this macro is not 100% correct, in that it matches "nud" against
1030  *      "nudbogus".  But we just let it go since this is minor.
1031  */
1032 #define	SETVALUE(f, v) do {						\
1033 	char *valptr;							\
1034 	unsigned long newval;						\
1035 	v = 0; /* unspecified */					\
1036 	if (strncmp(cp, f, strlen(f)) == 0) {				\
1037 		valptr = strchr(cp, '=');				\
1038 		if (valptr == NULL)					\
1039 			xo_err(1, "syntax error in %s field", (f));	\
1040 		errno = 0;						\
1041 		newval = strtoul(++valptr, NULL, 0);			\
1042 		if (errno)						\
1043 			xo_err(1, "syntax error in %s's value", (f));	\
1044 		v = newval;						\
1045 	}								\
1046 } while (0)
1047 
1048 		SETFLAG("disabled", ND6_IFF_IFDISABLED);
1049 		SETFLAG("nud", ND6_IFF_PERFORMNUD);
1050 #ifdef ND6_IFF_ACCEPT_RTADV
1051 		SETFLAG("accept_rtadv", ND6_IFF_ACCEPT_RTADV);
1052 #endif
1053 #ifdef ND6_IFF_AUTO_LINKLOCAL
1054 		SETFLAG("auto_linklocal", ND6_IFF_AUTO_LINKLOCAL);
1055 #endif
1056 #ifdef ND6_IFF_NO_PREFER_IFACE
1057 		SETFLAG("no_prefer_iface", ND6_IFF_NO_PREFER_IFACE);
1058 #endif
1059 		SETVALUE("basereachable", ND.basereachable);
1060 		SETVALUE("retrans", ND.retrans);
1061 		SETVALUE("curhlim", ND.chlim);
1062 
1063 		ND.flags = newflags;
1064 		if (ioctl(sock, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) {
1065 			xo_err(1, "ioctl(SIOCSIFINFO_IN6)");
1066 			/* NOTREACHED */
1067 		}
1068 #undef SETFLAG
1069 #undef SETVALUE
1070 	}
1071 
1072 	if (!ND.initialized) {
1073 		xo_errx(1, "%s: not initialized yet", ifname);
1074 		/* NOTREACHED */
1075 	}
1076 
1077 	if (ioctl(sock, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
1078 		xo_err(1, "ioctl(SIOCGIFINFO_IN6)");
1079 		/* NOTREACHED */
1080 	}
1081 	xo_open_container("ifinfo");
1082 
1083 	xo_emit("{e:interface/%s}", ifname);
1084 	xo_emit("linkmtu={:linkmtu/%d}", ND.linkmtu);
1085 	xo_emit(", maxmtu={:maxmtu/%d}", ND.maxmtu);
1086 	xo_emit(", curhlim={:curhlim/%d}", ND.chlim);
1087 	xo_emit("{d:/, basereachable=%ds%dms}{e:basereachable_ms/%u}",
1088 	    ND.basereachable / 1000, ND.basereachable % 1000, ND.basereachable);
1089 	xo_emit("{d:/, reachable=%ds}{e:reachable_ms/%u}", ND.reachable, ND.reachable * 1000);
1090 	xo_emit("{d:/, retrans=%ds%dms}{e:retrans_ms/%u}", ND.retrans / 1000, ND.retrans % 1000,
1091 	    ND.retrans);
1092 #ifdef IPV6CTL_USETEMPADDR
1093 	memset(nullbuf, 0, sizeof(nullbuf));
1094 	if (memcmp(nullbuf, ND.randomid, sizeof(nullbuf)) != 0) {
1095 		int j;
1096 		u_int8_t *rbuf;
1097 
1098 		for (i = 0; i < 3; i++) {
1099 			const char *txt, *field;
1100 			switch (i) {
1101 			case 0:
1102 				txt = "\nRandom seed(0): ";
1103 				field = "seed_0";
1104 				rbuf = ND.randomseed0;
1105 				break;
1106 			case 1:
1107 				txt = "\nRandom seed(1): ";
1108 				field = "seed_1";
1109 				rbuf = ND.randomseed1;
1110 				break;
1111 			case 2:
1112 				txt = "\nRandom ID:      ";
1113 				field = "random_id";
1114 				rbuf = ND.randomid;
1115 				break;
1116 			default:
1117 				xo_errx(1, "impossible case for tempaddr display");
1118 			}
1119 			char abuf[20], xobuf[200];
1120 			for (j = 0; j < 8; j++)
1121 				snprintf(&abuf[j * 2], sizeof(abuf), "%02X", rbuf[j]);
1122 			snprintf(xobuf, sizeof(xobuf), "%s{:%s/%%s}", txt, field);
1123 			xo_emit(xobuf, abuf);
1124 		}
1125 	}
1126 #endif /* IPV6CTL_USETEMPADDR */
1127 	if (ND.flags) {
1128 		xo_emit("\nFlags: {e:flags/%u}", ND.flags);
1129 		xo_open_list("flags_pretty");
1130 #ifdef ND6_IFF_IFDISABLED
1131 		if ((ND.flags & ND6_IFF_IFDISABLED))
1132 			xo_emit("{l:%s} ", "disabled");
1133 #endif
1134 		if ((ND.flags & ND6_IFF_PERFORMNUD))
1135 			xo_emit("{l:%s} ", "nud");
1136 #ifdef ND6_IFF_ACCEPT_RTADV
1137 		if ((ND.flags & ND6_IFF_ACCEPT_RTADV))
1138 			xo_emit("{l:%s} ", "accept_rtadv");
1139 #endif
1140 #ifdef ND6_IFF_AUTO_LINKLOCAL
1141 		if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL))
1142 			xo_emit("{l:%s} ", "auto_linklocal");
1143 #endif
1144 #ifdef ND6_IFF_NO_PREFER_IFACE
1145 		if ((ND.flags & ND6_IFF_NO_PREFER_IFACE))
1146 			xo_emit("{l:%s} ", "no_prefer_iface");
1147 #endif
1148 		xo_close_list("flags");
1149 	}
1150 	xo_emit("\n");
1151 #undef ND
1152 	xo_close_container("ifinfo");
1153 
1154 	close(sock);
1155 }
1156 
1157 #ifndef ND_RA_FLAG_RTPREF_MASK	/* XXX: just for compilation on *BSD release */
1158 #define ND_RA_FLAG_RTPREF_MASK	0x18 /* 00011000 */
1159 #endif
1160 
1161 static void
1162 rtrlist(void)
1163 {
1164 	int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_DRLIST };
1165 	char *buf;
1166 	struct in6_defrouter *p, *ep;
1167 	size_t l;
1168 	struct timeval now;
1169 
1170 	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
1171 		xo_err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
1172 		/*NOTREACHED*/
1173 	}
1174 	if (l == 0)
1175 		return;
1176 	buf = malloc(l);
1177 	if (!buf) {
1178 		xo_err(1, "malloc");
1179 		/*NOTREACHED*/
1180 	}
1181 	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
1182 		xo_err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
1183 		/*NOTREACHED*/
1184 	}
1185 
1186 	xo_open_list("router-list");
1187 
1188 	ep = (struct in6_defrouter *)(buf + l);
1189 	for (p = (struct in6_defrouter *)buf; p < ep; p++) {
1190 		int rtpref;
1191 		char abuf[INET6_ADDRSTRLEN], *paddr;
1192 
1193 		if (getnameinfo((struct sockaddr *)&p->rtaddr,
1194 		    p->rtaddr.sin6_len, host_buf, sizeof(host_buf), NULL, 0,
1195 		    (opts.nflag ? NI_NUMERICHOST : 0)) != 0)
1196 			strlcpy(host_buf, "?", sizeof(host_buf));
1197 		if (opts.nflag)
1198 			paddr = host_buf;
1199 		else {
1200 			inet_ntop(AF_INET6, &p->rtaddr.sin6_addr, abuf, sizeof(abuf));
1201 			paddr = abuf;
1202 		}
1203 
1204 		xo_open_instance("router-list");
1205 		xo_emit("{:hostname/%s}{e:address/%s} if={:interface/%s}",
1206 		    host_buf, paddr,
1207 		    if_indextoname(p->if_index, ifix_buf));
1208 		xo_open_list("flags_pretty");
1209 		char rflags[6] = {}, *pflags = rflags;
1210 		if (p->flags & ND_RA_FLAG_MANAGED) {
1211 			*pflags++ = 'M';
1212 			xo_emit("{el:%s}", "managed");
1213 		}
1214 		if (p->flags & ND_RA_FLAG_OTHER) {
1215 			*pflags++ = 'O';
1216 			xo_emit("{el:%s}", "other");
1217 		}
1218 #ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
1219 		if (p->flags & ND_RA_FLAG_IPV6_ONLY) {
1220 			*pflags++ = 'S';
1221 			xo_emit("{el:%s}", "ipv6only");
1222 		}
1223 #endif
1224 		xo_close_list("flags_pretty");
1225 		xo_emit(", flags={:flags/%s}", rflags);
1226 
1227 		rtpref = ((p->flags & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff;
1228 		xo_emit(", pref={:preference/%s}", rtpref_str[rtpref]);
1229 
1230 		gettimeofday(&now, 0);
1231 		if (p->expire == 0)
1232 			xo_emit(", expire=Never\n{en:permanent/true}");
1233 		else
1234 			xo_emit("{d:/, expire=%s\n}{e:expires_sec/%ld}",
1235 			    sec2str(p->expire - now.tv_sec),
1236 			    (long)p->expire - now.tv_sec);
1237 		xo_close_instance("router-list");
1238 	}
1239 	free(buf);
1240 	xo_close_list("router-list");
1241 }
1242 
1243 static void
1244 plist(void)
1245 {
1246 	int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_PRLIST };
1247 	char *buf;
1248 	struct in6_prefix *p, *ep, *n;
1249 	struct sockaddr_in6 *advrtr;
1250 	size_t l;
1251 	struct timeval now;
1252 	const int niflags = NI_NUMERICHOST;
1253 	int ninflags = opts.nflag ? NI_NUMERICHOST : 0;
1254 	char namebuf[NI_MAXHOST];
1255 
1256 	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
1257 		xo_err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
1258 		/*NOTREACHED*/
1259 	}
1260 	buf = malloc(l);
1261 	if (!buf) {
1262 		xo_err(1, "malloc");
1263 		/*NOTREACHED*/
1264 	}
1265 	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
1266 		xo_err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
1267 		/*NOTREACHED*/
1268 	}
1269 
1270 	xo_open_list("prefix-list");
1271 
1272 	ep = (struct in6_prefix *)(buf + l);
1273 	for (p = (struct in6_prefix *)buf; p < ep; p = n) {
1274 		advrtr = (struct sockaddr_in6 *)(p + 1);
1275 		n = (struct in6_prefix *)&advrtr[p->advrtrs];
1276 
1277 		xo_open_instance("prefix-list");
1278 		if (getnameinfo((struct sockaddr *)&p->prefix,
1279 		    p->prefix.sin6_len, namebuf, sizeof(namebuf),
1280 		    NULL, 0, niflags) != 0)
1281 			strlcpy(namebuf, "?", sizeof(namebuf));
1282 		xo_emit("{:prefix/%s%s%d} if={:interface/%s}\n", namebuf, "/",
1283 		    p->prefixlen, if_indextoname(p->if_index, ifix_buf));
1284 
1285 		gettimeofday(&now, 0);
1286 		/*
1287 		 * meaning of fields, especially flags, is very different
1288 		 * by origin.  notify the difference to the users.
1289 		 */
1290 		char flags[10] = {}, *pflags = flags;
1291 		xo_open_list("flags_pretty");
1292 		if (p->raflags.onlink) {
1293 			*pflags++ = 'L';
1294 			xo_emit("{el:%s}", "ra_onlink");
1295 		}
1296 		if (p->raflags.autonomous) {
1297 			*pflags++ = 'A';
1298 			xo_emit("{el:%s}", "ra_autonomous");
1299 		}
1300 		if (p->flags & NDPRF_ONLINK) {
1301 			*pflags++ = 'O';
1302 			xo_emit("{el:%s}", "is_onlink");
1303 		}
1304 		if (p->flags & NDPRF_DETACHED) {
1305 			*pflags++ = 'D';
1306 			xo_emit("{el:%s}", "is_detached");
1307 		}
1308 #ifdef NDPRF_HOME
1309 		if (p->flags & NDPRF_HOME) {
1310 			*pflags++ = 'H';
1311 			xo_emit("{el:%s}", "is_home");
1312 		}
1313 #endif
1314 		xo_close_list("flags_pretty");
1315 		xo_emit("flags={:flags/%s}", flags);
1316 		int expire_in = p->expire - now.tv_sec;
1317 
1318 		if (p->vltime == ND6_INFINITE_LIFETIME)
1319 			xo_emit(" vltime=infinity{e:valid-lifetime/%lu}",
1320 			    (unsigned long)p->vltime);
1321 		else
1322 			xo_emit(" vltime={:valid-lifetime/%lu}",
1323 			    (unsigned long)p->vltime);
1324 		if (p->pltime == ND6_INFINITE_LIFETIME)
1325 			xo_emit(", pltime=infinity{e:preferred-lifetime/%lu}",
1326 			    (unsigned long)p->pltime);
1327 		else
1328 			xo_emit(", pltime={:preferred-lifetime/%lu}",
1329 			    (unsigned long)p->pltime);
1330 		if (p->expire == 0)
1331 			xo_emit(", expire=Never{en:permanent/true}");
1332 		else if (p->expire >= now.tv_sec)
1333 			xo_emit(", expire=%s{e:expires_sec/%d}",
1334 			    sec2str(expire_in), expire_in);
1335 		else
1336 			xo_emit(", expired{e:expires_sec/%d}", expire_in);
1337 		xo_emit(", ref={:refcount/%d}", p->refcnt);
1338 		xo_emit("\n");
1339 		/*
1340 		 * "advertising router" list is meaningful only if the prefix
1341 		 * information is from RA.
1342 		 */
1343 		if (p->advrtrs) {
1344 			int j;
1345 			struct sockaddr_in6 *sin6;
1346 
1347 			sin6 = advrtr;
1348 			xo_emit("  advertised by\n");
1349 			xo_open_list("advertising-routers");
1350 			for (j = 0; j < p->advrtrs; j++) {
1351 				struct in6_nbrinfo *nbi;
1352 
1353 				xo_open_instance("advertising-routers");
1354 				if (getnameinfo((struct sockaddr *)sin6,
1355 				    sin6->sin6_len, namebuf, sizeof(namebuf),
1356 				    NULL, 0, ninflags) != 0)
1357 					strlcpy(namebuf, "?", sizeof(namebuf));
1358 				char abuf[INET6_ADDRSTRLEN];
1359 				inet_ntop(AF_INET6, &sin6->sin6_addr, abuf,
1360 				    sizeof(abuf));
1361 
1362 				xo_emit("    {:hostname/%s}{e:address/%s}",
1363 				    namebuf, abuf);
1364 
1365 				nbi = getnbrinfo(&sin6->sin6_addr,
1366 				    p->if_index, 0);
1367 				const char *state = "";
1368 				if (nbi) {
1369 					switch (nbi->state) {
1370 					case ND6_LLINFO_REACHABLE:
1371 					case ND6_LLINFO_STALE:
1372 					case ND6_LLINFO_DELAY:
1373 					case ND6_LLINFO_PROBE:
1374 						state = "reachable";
1375 						break;
1376 					default:
1377 						state = "unreachable";
1378 					}
1379 				} else
1380 					state = "no neighbor state";
1381 				xo_emit(" ({:state/%s})\n", state);
1382 				sin6++;
1383 				xo_close_instance("advertising-routers");
1384 			}
1385 			xo_close_list("advertising-routers");
1386 		} else
1387 			xo_emit("  No advertising router\n");
1388 		xo_close_instance("prefix-list");
1389 	}
1390 	free(buf);
1391 
1392 	xo_close_list("prefix-list");
1393 }
1394 
1395 static void
1396 pfx_flush(void)
1397 {
1398 	char dummyif[IFNAMSIZ+8];
1399 	int sock;
1400 
1401 	if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1402 		xo_err(1, "socket");
1403 	strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
1404 	if (ioctl(sock, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0)
1405 		xo_err(1, "ioctl(SIOCSPFXFLUSH_IN6)");
1406 
1407 	close(sock);
1408 }
1409 
1410 static void
1411 rtr_flush(void)
1412 {
1413 	char dummyif[IFNAMSIZ+8];
1414 	int sock;
1415 
1416 	if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1417 		xo_err(1, "socket");
1418 	strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
1419 	if (ioctl(sock, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0)
1420 		xo_err(1, "ioctl(SIOCSRTRFLUSH_IN6)");
1421 
1422 	close(sock);
1423 }
1424 
1425 static void
1426 harmonize_rtr(void)
1427 {
1428 	char dummyif[IFNAMSIZ+8];
1429 	int sock;
1430 
1431 	if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1432 		xo_err(1, "socket");
1433 	strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
1434 	if (ioctl(sock, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0)
1435 		xo_err(1, "ioctl(SIOCSNDFLUSH_IN6)");
1436 
1437 	close(sock);
1438 }
1439 
1440 #ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
1441 static void
1442 setdefif(char *ifname)
1443 {
1444 	struct in6_ndifreq ndifreq;
1445 	unsigned int ifindex;
1446 	int sock;
1447 
1448 	if (strcasecmp(ifname, "delete") == 0)
1449 		ifindex = 0;
1450 	else {
1451 		if ((ifindex = if_nametoindex(ifname)) == 0)
1452 			xo_err(1, "failed to resolve i/f index for %s", ifname);
1453 	}
1454 
1455 	if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1456 		xo_err(1, "socket");
1457 
1458 	strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */
1459 	ndifreq.ifindex = ifindex;
1460 
1461 	if (ioctl(sock, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
1462 		xo_err(1, "ioctl(SIOCSDEFIFACE_IN6)");
1463 
1464 	close(sock);
1465 }
1466 
1467 static void
1468 getdefif(void)
1469 {
1470 	struct in6_ndifreq ndifreq;
1471 	char ifname[IFNAMSIZ+8];
1472 	int sock;
1473 
1474 	if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1475 		xo_err(1, "socket");
1476 
1477 	memset(&ndifreq, 0, sizeof(ndifreq));
1478 	strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */
1479 
1480 	if (ioctl(sock, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
1481 		xo_err(1, "ioctl(SIOCGDEFIFACE_IN6)");
1482 
1483 	if (ndifreq.ifindex == 0)
1484 		xo_emit("No default interface.\n");
1485 	else {
1486 		if ((if_indextoname(ndifreq.ifindex, ifname)) == NULL)
1487 			xo_err(1, "failed to resolve ifname for index %lu",
1488 			    ndifreq.ifindex);
1489 		xo_emit("ND default interface = {:default-interface/%s}\n", ifname);
1490 	}
1491 
1492 	close(sock);
1493 }
1494 #endif /* SIOCSDEFIFACE_IN6 */
1495 
1496 char *
1497 sec2str(time_t total)
1498 {
1499 	static char result[256];
1500 	int days, hours, mins, secs;
1501 	int first = 1;
1502 	char *p = result;
1503 	char *ep = &result[sizeof(result)];
1504 	int n;
1505 
1506 	days = total / 3600 / 24;
1507 	hours = (total / 3600) % 24;
1508 	mins = (total / 60) % 60;
1509 	secs = total % 60;
1510 
1511 	if (days) {
1512 		first = 0;
1513 		n = snprintf(p, ep - p, "%dd", days);
1514 		if (n < 0 || n >= ep - p)
1515 			return "?";
1516 		p += n;
1517 	}
1518 	if (!first || hours) {
1519 		first = 0;
1520 		n = snprintf(p, ep - p, "%dh", hours);
1521 		if (n < 0 || n >= ep - p)
1522 			return "?";
1523 		p += n;
1524 	}
1525 	if (!first || mins) {
1526 		first = 0;
1527 		n = snprintf(p, ep - p, "%dm", mins);
1528 		if (n < 0 || n >= ep - p)
1529 			return "?";
1530 		p += n;
1531 	}
1532 	snprintf(p, ep - p, "%ds", secs);
1533 
1534 	return(result);
1535 }
1536 
1537 /*
1538  * Print the timestamp
1539  * from tcpdump/util.c
1540  */
1541 void
1542 ts_print(const struct timeval *tvp)
1543 {
1544 	int sec;
1545 
1546 	/* Default */
1547 	sec = (tvp->tv_sec + thiszone) % 86400;
1548 	xo_emit("{:tv_sec/%lld}{:tv_usec/%lld}%02d:%02d:%02d.%06u ",
1549 	    tvp->tv_sec, tvp->tv_usec,
1550 	    sec / 3600, (sec % 3600) / 60, sec % 60, (u_int32_t)tvp->tv_usec);
1551 }
1552 
1553 #undef NEXTADDR
1554