xref: /freebsd/usr.sbin/rtadvctl/rtadvctl.c (revision 39ee7a7a6bdd1557b1c3532abf60d139798ac88b)
1 /*-
2  * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
21  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  *
28  */
29 
30 #include <sys/queue.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/stat.h>
34 #include <sys/un.h>
35 #include <sys/uio.h>
36 #include <net/if.h>
37 #include <net/if_dl.h>
38 #include <net/if_types.h>
39 #include <net/ethernet.h>
40 #include <netinet/in.h>
41 #include <netinet/ip6.h>
42 #include <netinet/icmp6.h>
43 #include <netinet6/in6_var.h>
44 #include <netinet6/nd6.h>
45 #include <arpa/inet.h>
46 #include <fcntl.h>
47 #include <errno.h>
48 #include <inttypes.h>
49 #include <netdb.h>
50 #include <unistd.h>
51 #include <string.h>
52 #include <stdarg.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <stdarg.h>
56 #include <syslog.h>
57 #include <time.h>
58 #include <err.h>
59 
60 #include "pathnames.h"
61 #include "rtadvd.h"
62 #include "if.h"
63 #include "timer_subr.h"
64 #include "timer.h"
65 #include "control.h"
66 #include "control_client.h"
67 
68 #define RA_IFSTATUS_INACTIVE	0
69 #define RA_IFSTATUS_RA_RECV	1
70 #define RA_IFSTATUS_RA_SEND	2
71 
72 static int vflag = LOG_ERR;
73 
74 static void	usage(void);
75 
76 static int	action_propset(char *);
77 static int	action_propget(char *, struct ctrl_msg_pl *);
78 static int	action_plgeneric(int, char *, char *);
79 
80 static int	action_enable(int, char **);
81 static int	action_disable(int, char **);
82 static int	action_reload(int, char **);
83 static int	action_echo(int, char **);
84 static int	action_version(int, char **);
85 static int	action_shutdown(int, char **);
86 
87 static int	action_show(int, char **);
88 static int	action_show_prefix(struct prefix *);
89 static int	action_show_rtinfo(struct rtinfo *);
90 static int	action_show_rdnss(void *);
91 static int	action_show_dnssl(void *);
92 
93 static int	csock_client_open(struct sockinfo *);
94 static size_t	dname_labeldec(char *, size_t, const char *);
95 static void	mysyslog(int, const char *, ...);
96 
97 static const char *rtpref_str[] = {
98 	"medium",		/* 00 */
99 	"high",			/* 01 */
100 	"rsv",			/* 10 */
101 	"low"			/* 11 */
102 };
103 
104 static struct dispatch_table {
105 	const char	*dt_comm;
106 	int (*dt_act)(int, char **);
107 } dtable[] = {
108 	{ "show", action_show },
109 	{ "reload", action_reload },
110 	{ "shutdown", action_shutdown },
111 	{ "enable", action_enable },
112 	{ "disable", action_disable },
113 	{ NULL, NULL },
114 	{ "echo", action_echo },
115 	{ "version", action_version },
116 	{ NULL, NULL },
117 };
118 
119 static char errmsgbuf[1024];
120 static char *errmsg = NULL;
121 
122 static void
123 mysyslog(int priority, const char * restrict fmt, ...)
124 {
125 	va_list ap;
126 
127 	if (vflag >= priority) {
128 		va_start(ap, fmt);
129 		vfprintf(stderr, fmt, ap);
130 		fprintf(stderr, "\n");
131 		va_end(ap);
132 	}
133 }
134 
135 static void
136 usage(void)
137 {
138 	int i;
139 
140 	for (i = 0; (size_t)i < sizeof(dtable)/sizeof(dtable[0]); i++) {
141 		if (dtable[i].dt_comm == NULL)
142 			break;
143 		printf("%s\n", dtable[i].dt_comm);
144 	}
145 
146 	exit(1);
147 }
148 
149 int
150 main(int argc, char *argv[])
151 {
152 	int i;
153 	int ch;
154 	int (*action)(int, char **) = NULL;
155 	int error;
156 
157 	while ((ch = getopt(argc, argv, "Dv")) != -1) {
158 		switch (ch) {
159 		case 'D':
160 			vflag = LOG_DEBUG;
161 			break;
162 		case 'v':
163 			vflag++;
164 			break;
165 		default:
166 			usage();
167 		}
168 	}
169 	argc -= optind;
170 	argv += optind;
171 
172 	if (argc == 0)
173 		usage();
174 
175 	for (i = 0; (size_t)i < sizeof(dtable)/sizeof(dtable[0]); i++) {
176 		if (dtable[i].dt_comm == NULL ||
177 		    strcmp(dtable[i].dt_comm, argv[0]) == 0) {
178 			action = dtable[i].dt_act;
179 			break;
180 		}
181 	}
182 
183 	if (action == NULL)
184 		usage();
185 
186 	error = (dtable[i].dt_act)(--argc, ++argv);
187 	if (error) {
188 		fprintf(stderr, "%s failed", dtable[i].dt_comm);
189 		if (errmsg != NULL)
190 			fprintf(stderr, ": %s", errmsg);
191 		fprintf(stderr, ".\n");
192 	}
193 
194 	return (error);
195 }
196 
197 static int
198 csock_client_open(struct sockinfo *s)
199 {
200 	struct sockaddr_un sun;
201 
202 	if ((s->si_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
203 		err(1, "cannot open control socket.");
204 
205 	memset(&sun, 0, sizeof(sun));
206 	sun.sun_family = AF_UNIX;
207 	sun.sun_len = sizeof(sun);
208 	strlcpy(sun.sun_path, s->si_name, sizeof(sun.sun_path));
209 
210 	if (connect(s->si_fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
211 		err(1, "connect: %s", s->si_name);
212 
213 	mysyslog(LOG_DEBUG,
214 	    "<%s> connected to %s", __func__, sun.sun_path);
215 
216 	return (0);
217 }
218 
219 static int
220 action_plgeneric(int action, char *plstr, char *buf)
221 {
222 	struct ctrl_msg_hdr *cm;
223 	struct ctrl_msg_pl cp;
224 	struct sockinfo *s;
225 	char *msg;
226 	char *p;
227 	char *q;
228 
229 	s = &ctrlsock;
230 	csock_client_open(s);
231 
232 	cm = (struct ctrl_msg_hdr *)buf;
233 	msg = (char *)buf + sizeof(*cm);
234 
235 	cm->cm_version = CM_VERSION;
236 	cm->cm_type = action;
237 	cm->cm_len = sizeof(*cm);
238 
239 	if (plstr != NULL) {
240 		memset(&cp, 0, sizeof(cp));
241 		p = strchr(plstr, ':');
242 		q = strchr(plstr, '=');
243 		if (p != NULL && q != NULL && p > q)
244 			return (1);
245 
246 		if (p == NULL) {		/* No : */
247 			cp.cp_ifname = NULL;
248 			cp.cp_key = plstr;
249 		} else if  (p == plstr) {	/* empty */
250 			cp.cp_ifname = NULL;
251 			cp.cp_key = plstr + 1;
252 		} else {
253 			*p++ = '\0';
254 			cp.cp_ifname = plstr;
255 			cp.cp_key = p;
256 		}
257 		if (q == NULL)
258 			cp.cp_val = NULL;
259 		else {
260 			*q++ = '\0';
261 			cp.cp_val = q;
262 		}
263 		cm->cm_len += cm_pl2bin(msg, &cp);
264 
265 		mysyslog(LOG_DEBUG, "<%s> key=%s, val_len=%d, ifname=%s",
266 		    __func__,cp.cp_key, cp.cp_val_len, cp.cp_ifname);
267 	}
268 
269 	return (cm_handler_client(s->si_fd, CM_STATE_MSG_DISPATCH, buf));
270 }
271 
272 static int
273 action_propget(char *argv, struct ctrl_msg_pl *cp)
274 {
275 	int error;
276 	struct ctrl_msg_hdr *cm;
277 	char buf[CM_MSG_MAXLEN];
278 	char *msg;
279 
280 	memset(cp, 0, sizeof(*cp));
281 	cm = (struct ctrl_msg_hdr *)buf;
282 	msg = (char *)buf + sizeof(*cm);
283 
284 	error = action_plgeneric(CM_TYPE_REQ_GET_PROP, argv, buf);
285 	if (error || cm->cm_len <= sizeof(*cm))
286 		return (1);
287 
288 	cm_bin2pl(msg, cp);
289 	mysyslog(LOG_DEBUG, "<%s> type=%d, len=%d",
290 	    __func__, cm->cm_type, cm->cm_len);
291 	mysyslog(LOG_DEBUG, "<%s> key=%s, val_len=%d, ifname=%s",
292 	    __func__,cp->cp_key, cp->cp_val_len, cp->cp_ifname);
293 
294 	return (0);
295 }
296 
297 static int
298 action_propset(char *argv)
299 {
300 	char buf[CM_MSG_MAXLEN];
301 
302 	return (action_plgeneric(CM_TYPE_REQ_SET_PROP, argv, buf));
303 }
304 
305 static int
306 action_disable(int argc, char **argv)
307 {
308 	char *action_argv;
309 	char argv_disable[IFNAMSIZ + sizeof(":disable=")];
310 	int i;
311 	int error;
312 
313 	if (argc < 1)
314 		return (1);
315 
316 	error = 0;
317 	for (i = 0; i < argc; i++) {
318 		sprintf(argv_disable, "%s:disable=", argv[i]);
319 		action_argv = argv_disable;
320 		error += action_propset(action_argv);
321 	}
322 
323 	return (error);
324 }
325 
326 static int
327 action_enable(int argc, char **argv)
328 {
329 	char *action_argv;
330 	char argv_enable[IFNAMSIZ + sizeof(":enable=")];
331 	int i;
332 	int error;
333 
334 	if (argc < 1)
335 		return (1);
336 
337 	error = 0;
338 	for (i = 0; i < argc; i++) {
339 		sprintf(argv_enable, "%s:enable=", argv[i]);
340 		action_argv = argv_enable;
341 		error += action_propset(action_argv);
342 	}
343 
344 	return (error);
345 }
346 
347 static int
348 action_reload(int argc, char **argv)
349 {
350 	char *action_argv;
351 	char argv_reload[IFNAMSIZ + sizeof(":reload=")];
352 	int i;
353 	int error;
354 
355 	if (argc == 0) {
356 		action_argv = strdup(":reload=");
357 		return (action_propset(action_argv));
358 	}
359 
360 	error = 0;
361 	for (i = 0; i < argc; i++) {
362 		sprintf(argv_reload, "%s:reload=", argv[i]);
363 		action_argv = argv_reload;
364 		error += action_propset(action_argv);
365 	}
366 
367 	return (error);
368 }
369 
370 static int
371 action_echo(int argc __unused, char **argv __unused)
372 {
373 	char *action_argv;
374 
375 	action_argv = strdup("echo");
376 	return (action_propset(action_argv));
377 }
378 
379 static int
380 action_shutdown(int argc __unused, char **argv __unused)
381 {
382 	char *action_argv;
383 
384 	action_argv = strdup("shutdown");
385 	return (action_propset(action_argv));
386 }
387 
388 /* XXX */
389 static int
390 action_version(int argc __unused, char **argv __unused)
391 {
392 	char *action_argv;
393 	struct ctrl_msg_pl cp;
394 	int error;
395 
396 	action_argv = strdup(":version=");
397 	error = action_propget(action_argv, &cp);
398 	if (error)
399 		return (error);
400 
401 	printf("version=%s\n", cp.cp_val);
402 	return (0);
403 }
404 
405 static int
406 action_show(int argc, char **argv)
407 {
408 	char *action_argv;
409 	char argv_ifilist[sizeof(":ifilist=")] = ":ifilist=";
410 	char argv_ifi[IFNAMSIZ + sizeof(":ifi=")];
411 	char argv_rai[IFNAMSIZ + sizeof(":rai=")];
412 	char argv_rti[IFNAMSIZ + sizeof(":rti=")];
413 	char argv_pfx[IFNAMSIZ + sizeof(":pfx=")];
414 	char argv_ifi_ra_timer[IFNAMSIZ + sizeof(":ifi_ra_timer=")];
415 	char argv_rdnss[IFNAMSIZ + sizeof(":rdnss=")];
416 	char argv_dnssl[IFNAMSIZ + sizeof(":dnssl=")];
417 	char ssbuf[SSBUFLEN];
418 
419 	struct timespec now, ts0, ts;
420 	struct ctrl_msg_pl cp;
421 	struct ifinfo *ifi;
422 	TAILQ_HEAD(, ifinfo) ifl = TAILQ_HEAD_INITIALIZER(ifl);
423 	char *endp;
424 	char *p;
425 	int error;
426 	int i;
427 	int len;
428 
429 	if (argc == 0) {
430 		action_argv = argv_ifilist;
431 		error = action_propget(action_argv, &cp);
432 		if (error)
433 			return (error);
434 
435 		p = cp.cp_val;
436 		endp = p + cp.cp_val_len;
437 		while (p < endp) {
438 			ifi = malloc(sizeof(*ifi));
439 			if (ifi == NULL)
440 				return (1);
441 			memset(ifi, 0, sizeof(*ifi));
442 
443 			strcpy(ifi->ifi_ifname, p);
444 			ifi->ifi_ifindex = if_nametoindex(ifi->ifi_ifname);
445 			TAILQ_INSERT_TAIL(&ifl, ifi, ifi_next);
446 			p += strlen(ifi->ifi_ifname) + 1;
447 		}
448 	} else {
449 		for (i = 0; i < argc; i++) {
450 			ifi = malloc(sizeof(*ifi));
451 			if (ifi == NULL)
452 				return (1);
453 			memset(ifi, 0, sizeof(*ifi));
454 
455 			strcpy(ifi->ifi_ifname, argv[i]);
456 			ifi->ifi_ifindex = if_nametoindex(ifi->ifi_ifname);
457 			if (ifi->ifi_ifindex == 0) {
458 				sprintf(errmsgbuf, "invalid interface %s",
459 				    ifi->ifi_ifname);
460 				errmsg = errmsgbuf;
461 				return (1);
462 			}
463 
464 			TAILQ_INSERT_TAIL(&ifl, ifi, ifi_next);
465 		}
466 	}
467 
468 	clock_gettime(CLOCK_REALTIME_FAST, &now);
469 	clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
470 	TS_SUB(&now, &ts, &ts0);
471 
472 	TAILQ_FOREACH(ifi, &ifl, ifi_next) {
473 		struct ifinfo *ifi_s;
474 		struct rtadvd_timer *rat;
475 		struct rainfo *rai;
476 		struct rtinfo *rti;
477 		struct prefix *pfx;
478 		int c;
479 		int ra_ifstatus;
480 
481 		sprintf(argv_ifi, "%s:ifi=", ifi->ifi_ifname);
482 		action_argv = argv_ifi;
483 		error = action_propget(action_argv, &cp);
484 		if (error)
485 			return (error);
486 		ifi_s = (struct ifinfo *)cp.cp_val;
487 
488 		if (!(ifi_s->ifi_persist) && vflag < LOG_NOTICE)
489 			continue;
490 
491 		printf("%s: flags=<", ifi->ifi_ifname);
492 
493 		c = 0;
494 		if (ifi_s->ifi_ifindex == 0)
495 			c += printf("NONEXISTENT");
496 		else
497 			c += printf("%s", (ifi_s->ifi_flags & IFF_UP) ?
498 			    "UP" : "DOWN");
499 		switch (ifi_s->ifi_state) {
500 		case IFI_STATE_CONFIGURED:
501 			c += printf("%s%s", (c) ? "," : "", "CONFIGURED");
502 			break;
503 		case IFI_STATE_TRANSITIVE:
504 			c += printf("%s%s", (c) ? "," : "", "TRANSITIVE");
505 			break;
506 		}
507 		if (ifi_s->ifi_persist)
508 			c += printf("%s%s", (c) ? "," : "", "PERSIST");
509 		printf(">");
510 
511 		ra_ifstatus = RA_IFSTATUS_INACTIVE;
512 		if ((ifi_s->ifi_flags & IFF_UP) &&
513 		    ((ifi_s->ifi_state == IFI_STATE_CONFIGURED) ||
514 			(ifi_s->ifi_state == IFI_STATE_TRANSITIVE))) {
515 #if (__FreeBSD_version < 900000)
516 			/*
517 			 * RA_RECV: !ip6.forwarding && ip6.accept_rtadv
518 			 * RA_SEND: ip6.forwarding
519 			 */
520 			if (getinet6sysctl(IPV6CTL_FORWARDING) == 0) {
521 				if (getinet6sysctl(IPV6CTL_ACCEPT_RTADV))
522 					ra_ifstatus = RA_IFSTATUS_RA_RECV;
523 				else
524 					ra_ifstatus = RA_IFSTATUS_INACTIVE;
525 			} else
526 				ra_ifstatus = RA_IFSTATUS_RA_SEND;
527 #else
528 			/*
529 			 * RA_RECV: ND6_IFF_ACCEPT_RTADV
530 			 * RA_SEND: ip6.forwarding
531 			 */
532 			if (ifi_s->ifi_nd_flags & ND6_IFF_ACCEPT_RTADV)
533 				ra_ifstatus = RA_IFSTATUS_RA_RECV;
534 			else if (getinet6sysctl(IPV6CTL_FORWARDING))
535 				ra_ifstatus = RA_IFSTATUS_RA_SEND;
536 			else
537 				ra_ifstatus = RA_IFSTATUS_INACTIVE;
538 #endif
539 		}
540 
541 		c = 0;
542 		printf(" status=<");
543 		if (ra_ifstatus == RA_IFSTATUS_INACTIVE)
544 			printf("%s%s", (c) ? "," : "", "INACTIVE");
545 		else if (ra_ifstatus == RA_IFSTATUS_RA_RECV)
546 			printf("%s%s", (c) ? "," : "", "RA_RECV");
547 		else if (ra_ifstatus == RA_IFSTATUS_RA_SEND)
548 			printf("%s%s", (c) ? "," : "", "RA_SEND");
549 		printf("> ");
550 
551 		switch (ifi_s->ifi_state) {
552 		case IFI_STATE_CONFIGURED:
553 		case IFI_STATE_TRANSITIVE:
554 			break;
555 		default:
556 			printf("\n");
557 			continue;
558 		}
559 
560 		printf("mtu %d\n", ifi_s->ifi_phymtu);
561 
562 		sprintf(argv_rai, "%s:rai=", ifi->ifi_ifname);
563 		action_argv = argv_rai;
564 
565 		error = action_propget(action_argv, &cp);
566 		if (error)
567 			continue;
568 
569 		rai = (struct rainfo *)cp.cp_val;
570 
571 		printf("\tDefaultLifetime: %s",
572 		    sec2str(rai->rai_lifetime, ssbuf));
573 		if (ra_ifstatus != RA_IFSTATUS_RA_SEND &&
574 		    rai->rai_lifetime == 0)
575 			printf(" (RAs will be sent with zero lifetime)");
576 
577 		printf("\n");
578 
579 		printf("\tMinAdvInterval/MaxAdvInterval: ");
580 		printf("%s/", sec2str(rai->rai_mininterval, ssbuf));
581 		printf("%s\n", sec2str(rai->rai_maxinterval, ssbuf));
582 		if (rai->rai_linkmtu)
583 			printf("\tAdvLinkMTU: %d", rai->rai_linkmtu);
584 		else
585 			printf("\tAdvLinkMTU: <none>");
586 
587 		printf(", ");
588 
589 		printf("Flags: ");
590 		if (rai->rai_managedflg || rai->rai_otherflg) {
591 			printf("%s", rai->rai_managedflg ? "M" : "");
592 			printf("%s", rai->rai_otherflg ? "O" : "");
593 		} else
594 			printf("<none>");
595 
596 		printf(", ");
597 
598 		printf("Preference: %s\n",
599 		    rtpref_str[(rai->rai_rtpref >> 3) & 0xff]);
600 
601 		printf("\tReachableTime: %s, ",
602 		    sec2str(rai->rai_reachabletime, ssbuf));
603 		printf("RetransTimer: %s, "
604 		    "CurHopLimit: %d\n",
605 		    sec2str(rai->rai_retranstimer, ssbuf),
606 		    rai->rai_hoplimit);
607 		printf("\tAdvIfPrefixes: %s\n",
608 		    rai->rai_advifprefix ? "yes" : "no");
609 
610 		/* RA timer */
611 		rat = NULL;
612 		if (ifi_s->ifi_ra_timer != NULL) {
613 			sprintf(argv_ifi_ra_timer, "%s:ifi_ra_timer=",
614 			    ifi->ifi_ifname);
615 			action_argv = argv_ifi_ra_timer;
616 
617 			error = action_propget(action_argv, &cp);
618 			if (error)
619 				return (error);
620 
621 			rat = (struct rtadvd_timer *)cp.cp_val;
622 		}
623 		printf("\tNext RA send: ");
624 		if (rat == NULL)
625 			printf("never\n");
626 		else {
627 			ts.tv_sec = rat->rat_tm.tv_sec + ts0.tv_sec;
628 			printf("%s", ctime(&ts.tv_sec));
629 		}
630 		printf("\tLast RA send: ");
631 		if (ifi_s->ifi_ra_lastsent.tv_sec == 0)
632 			printf("never\n");
633 		else {
634 			ts.tv_sec = ifi_s->ifi_ra_lastsent.tv_sec + ts0.tv_sec;
635 			printf("%s", ctime(&ts.tv_sec));
636 		}
637 		if (rai->rai_clockskew)
638 			printf("\tClock skew: %" PRIu16 "sec\n",
639 			    rai->rai_clockskew);
640 
641 		if (vflag < LOG_WARNING)
642 			continue;
643 
644 		/* route information */
645 		sprintf(argv_rti, "%s:rti=", ifi->ifi_ifname);
646 		action_argv = argv_rti;
647 		error = action_propget(action_argv, &cp);
648 		if (error)
649 			return (error);
650 
651 		rti = (struct rtinfo *)cp.cp_val;
652 		len = cp.cp_val_len / sizeof(*rti);
653 		if (len > 0) {
654 			printf("\tRoute Info:\n");
655 
656 			for (i = 0; i < len; i++)
657 				action_show_rtinfo(&rti[i]);
658 		}
659 
660 		/* prefix information */
661 		sprintf(argv_pfx, "%s:pfx=", ifi->ifi_ifname);
662 		action_argv = argv_pfx;
663 
664 		error = action_propget(action_argv, &cp);
665 		if (error)
666 			continue;
667 
668 		pfx = (struct prefix *)cp.cp_val;
669 		len = cp.cp_val_len / sizeof(*pfx);
670 
671 		if (len > 0) {
672 			printf("\tPrefixes (%d):\n", len);
673 
674 			for (i = 0; i < len; i++)
675 				action_show_prefix(&pfx[i]);
676 		}
677 
678 		/* RDNSS information */
679 		sprintf(argv_rdnss, "%s:rdnss=", ifi->ifi_ifname);
680 		action_argv = argv_rdnss;
681 
682 		error = action_propget(action_argv, &cp);
683 		if (error)
684 			continue;
685 
686 		len = *((uint16_t *)cp.cp_val);
687 
688 		if (len > 0) {
689 			printf("\tRDNSS entries:\n");
690 			action_show_rdnss(cp.cp_val);
691 		}
692 
693 		/* DNSSL information */
694 		sprintf(argv_dnssl, "%s:dnssl=", ifi->ifi_ifname);
695 		action_argv = argv_dnssl;
696 
697 		error = action_propget(action_argv, &cp);
698 		if (error)
699 			continue;
700 
701 		len = *((uint16_t *)cp.cp_val);
702 
703 		if (len > 0) {
704 			printf("\tDNSSL entries:\n");
705 			action_show_dnssl(cp.cp_val);
706 		}
707 
708 		if (vflag < LOG_NOTICE)
709 			continue;
710 
711 		printf("\n");
712 
713 		printf("\tCounters\n"
714 		    "\t RA burst counts: %" PRIu16 " (interval: %s)\n"
715 		    "\t RS wait counts: %" PRIu16 "\n",
716 		    ifi_s->ifi_burstcount,
717 		    sec2str(ifi_s->ifi_burstinterval, ssbuf),
718 		    ifi_s->ifi_rs_waitcount);
719 
720 		printf("\tOutputs\n"
721 		    "\t RA: %" PRIu64 "\n", ifi_s->ifi_raoutput);
722 
723 		printf("\tInputs\n"
724 		    "\t RA: %" PRIu64 " (normal)\n"
725 		    "\t RA: %" PRIu64 " (inconsistent)\n"
726 		    "\t RS: %" PRIu64 "\n",
727 		    ifi_s->ifi_rainput,
728 		    ifi_s->ifi_rainconsistent,
729 		    ifi_s->ifi_rsinput);
730 
731 		printf("\n");
732 
733 #if 0	/* Not implemented yet */
734 		printf("\tReceived RAs:\n");
735 #endif
736 	}
737 
738 	return (0);
739 }
740 
741 static int
742 action_show_rtinfo(struct rtinfo *rti)
743 {
744 	char ntopbuf[INET6_ADDRSTRLEN];
745 	char ssbuf[SSBUFLEN];
746 
747 	printf("\t  %s/%d (pref: %s, ltime: %s)\n",
748 	    inet_ntop(AF_INET6, &rti->rti_prefix,
749 		ntopbuf, sizeof(ntopbuf)),
750 	    rti->rti_prefixlen,
751 	    rtpref_str[0xff & (rti->rti_rtpref >> 3)],
752 	    (rti->rti_ltime == ND6_INFINITE_LIFETIME) ?
753 	    "infinity" : sec2str(rti->rti_ltime, ssbuf));
754 
755 	return (0);
756 }
757 
758 static int
759 action_show_prefix(struct prefix *pfx)
760 {
761 	char ntopbuf[INET6_ADDRSTRLEN];
762 	char ssbuf[SSBUFLEN];
763 	struct timespec now;
764 
765 	clock_gettime(CLOCK_MONOTONIC_FAST, &now);
766 	printf("\t  %s/%d", inet_ntop(AF_INET6, &pfx->pfx_prefix,
767 		ntopbuf, sizeof(ntopbuf)), pfx->pfx_prefixlen);
768 
769 	printf(" (");
770 	switch (pfx->pfx_origin) {
771 	case PREFIX_FROM_KERNEL:
772 		printf("KERNEL");
773 		break;
774 	case PREFIX_FROM_CONFIG:
775 		printf("CONFIG");
776 		break;
777 	case PREFIX_FROM_DYNAMIC:
778 		printf("DYNAMIC");
779 		break;
780 	}
781 
782 	printf(",");
783 
784 	printf(" vltime=%s",
785 	    (pfx->pfx_validlifetime == ND6_INFINITE_LIFETIME) ?
786 	    "infinity" : sec2str(pfx->pfx_validlifetime, ssbuf));
787 
788 	if (pfx->pfx_vltimeexpire > 0)
789 		printf("(expire: %s)",
790 		    ((long)pfx->pfx_vltimeexpire > now.tv_sec) ?
791 		    sec2str(pfx->pfx_vltimeexpire - now.tv_sec, ssbuf) :
792 		    "0");
793 
794 	printf(",");
795 
796 	printf(" pltime=%s",
797 	    (pfx->pfx_preflifetime == ND6_INFINITE_LIFETIME) ?
798 	    "infinity" : sec2str(pfx->pfx_preflifetime, ssbuf));
799 
800 	if (pfx->pfx_pltimeexpire > 0)
801 		printf("(expire %s)",
802 		    ((long)pfx->pfx_pltimeexpire > now.tv_sec) ?
803 		    sec2str(pfx->pfx_pltimeexpire - now.tv_sec, ssbuf) :
804 		    "0");
805 
806 	printf(",");
807 
808 	printf(" flags=");
809 	if (pfx->pfx_onlinkflg || pfx->pfx_autoconfflg) {
810 		printf("%s", pfx->pfx_onlinkflg ? "L" : "");
811 		printf("%s", pfx->pfx_autoconfflg ? "A" : "");
812 	} else
813 		printf("<none>");
814 
815 	if (pfx->pfx_timer) {
816 		struct timespec *rest;
817 
818 		rest = rtadvd_timer_rest(pfx->pfx_timer);
819 		if (rest) { /* XXX: what if not? */
820 			printf(" expire=%s", sec2str(rest->tv_sec, ssbuf));
821 		}
822 	}
823 
824 	printf(")\n");
825 
826 	return (0);
827 }
828 
829 static int
830 action_show_rdnss(void *msg)
831 {
832 	struct rdnss *rdn;
833 	struct rdnss_addr *rda;
834 	uint16_t *rdn_cnt;
835 	uint16_t *rda_cnt;
836 	int i;
837 	int j;
838 	char *p;
839 	uint32_t	ltime;
840 	char ntopbuf[INET6_ADDRSTRLEN];
841 	char ssbuf[SSBUFLEN];
842 
843 	p = msg;
844 	rdn_cnt = (uint16_t *)p;
845 	p += sizeof(*rdn_cnt);
846 
847 	if (*rdn_cnt > 0) {
848 		for (i = 0; i < *rdn_cnt; i++) {
849 			rdn = (struct rdnss *)p;
850 			ltime = rdn->rd_ltime;
851 			p += sizeof(*rdn);
852 
853 			rda_cnt = (uint16_t *)p;
854 			p += sizeof(*rda_cnt);
855 			if (*rda_cnt > 0)
856 				for (j = 0; j < *rda_cnt; j++) {
857 					rda = (struct rdnss_addr *)p;
858 					printf("\t  %s (ltime=%s)\n",
859 					    inet_ntop(AF_INET6,
860 						&rda->ra_dns,
861 						ntopbuf,
862 						sizeof(ntopbuf)),
863 					    sec2str(ltime, ssbuf));
864 					p += sizeof(*rda);
865 				}
866 		}
867 	}
868 
869 	return (0);
870 }
871 
872 static int
873 action_show_dnssl(void *msg)
874 {
875 	struct dnssl *dns;
876 	struct dnssl_addr *dna;
877 	uint16_t *dns_cnt;
878 	uint16_t *dna_cnt;
879 	int i;
880 	int j;
881 	char *p;
882 	uint32_t ltime;
883 	char hbuf[NI_MAXHOST];
884 	char ssbuf[SSBUFLEN];
885 
886 	p = msg;
887 	dns_cnt = (uint16_t *)p;
888 	p += sizeof(*dns_cnt);
889 
890 	if (*dns_cnt > 0) {
891 		for (i = 0; i < *dns_cnt; i++) {
892 			dns = (struct dnssl *)p;
893 			ltime = dns->dn_ltime;
894 			p += sizeof(*dns);
895 
896 			dna_cnt = (uint16_t *)p;
897 			p += sizeof(*dna_cnt);
898 			if (*dna_cnt > 0)
899 				for (j = 0; j < *dna_cnt; j++) {
900 					dna = (struct dnssl_addr *)p;
901 					dname_labeldec(hbuf, sizeof(hbuf),
902 					    dna->da_dom);
903 					printf("\t  %s (ltime=%s)\n",
904 					    hbuf, sec2str(ltime, ssbuf));
905 					p += sizeof(*dna);
906 				}
907 		}
908 	}
909 
910 	return (0);
911 }
912 
913 /* Decode domain name label encoding in RFC 1035 Section 3.1 */
914 static size_t
915 dname_labeldec(char *dst, size_t dlen, const char *src)
916 {
917 	size_t len;
918 	const char *src_origin;
919 	const char *src_last;
920 	const char *dst_origin;
921 
922 	src_origin = src;
923 	src_last = strchr(src, '\0');
924 	dst_origin = dst;
925 	memset(dst, '\0', dlen);
926 	while (src && (len = (uint8_t)(*src++) & 0x3f) &&
927 	    (src + len) <= src_last) {
928 		if (dst != dst_origin)
929 			*dst++ = '.';
930 		mysyslog(LOG_DEBUG, "<%s> labellen = %zd", __func__, len);
931 		memcpy(dst, src, len);
932 		src += len;
933 		dst += len;
934 	}
935 	*dst = '\0';
936 
937 	return (src - src_origin);
938 }
939