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