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