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