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