rtadvctl.c revision 281143
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: head/usr.sbin/rtadvctl/rtadvctl.c 281143 2015-04-06 09:42:23Z glebius $
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
72static int vflag = LOG_ERR;
73
74static void	usage(void);
75
76static int	action_propset(char *);
77static int	action_propget(char *, struct ctrl_msg_pl *);
78static int	action_plgeneric(int, char *, char *);
79
80static int	action_enable(int, char **);
81static int	action_disable(int, char **);
82static int	action_reload(int, char **);
83static int	action_echo(int, char **);
84static int	action_version(int, char **);
85static int	action_shutdown(int, char **);
86
87static int	action_show(int, char **);
88static int	action_show_prefix(struct prefix *);
89static int	action_show_rtinfo(struct rtinfo *);
90static int	action_show_rdnss(void *);
91static int	action_show_dnssl(void *);
92
93static int	csock_client_open(struct sockinfo *);
94static size_t	dname_labeldec(char *, size_t, const char *);
95static void	mysyslog(int, const char *, ...);
96
97static const char *rtpref_str[] = {
98	"medium",		/* 00 */
99	"high",			/* 01 */
100	"rsv",			/* 10 */
101	"low"			/* 11 */
102};
103
104static 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
119static char errmsgbuf[1024];
120static char *errmsg = NULL;
121
122static void
123mysyslog(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
135static void
136usage(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
149int
150main(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
197static int
198csock_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
219static int
220action_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
272static int
273action_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
297static int
298action_propset(char *argv)
299{
300	char buf[CM_MSG_MAXLEN];
301
302	return (action_plgeneric(CM_TYPE_REQ_SET_PROP, argv, buf));
303}
304
305static int
306action_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
326static int
327action_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
347static int
348action_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
370static int
371action_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
379static int
380action_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 */
389static int
390action_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
405static int
406action_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
741static int
742action_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
758static int
759action_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
829static int
830action_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
872static int
873action_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 */
914static size_t
915dname_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