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
74static int vflag = LOG_ERR;
75
76static void	usage(void);
77
78static int	action_propset(char *);
79static int	action_propget(char *, struct ctrl_msg_pl *);
80static int	action_plgeneric(int, char *, char *);
81
82static int	action_enable(int, char **);
83static int	action_disable(int, char **);
84static int	action_reload(int, char **);
85static int	action_echo(int, char **);
86static int	action_version(int, char **);
87static int	action_shutdown(int, char **);
88
89static int	action_show(int, char **);
90static int	action_show_prefix(struct prefix *);
91static int	action_show_rtinfo(struct rtinfo *);
92static int	action_show_rdnss(void *);
93static int	action_show_dnssl(void *);
94
95static int	csock_client_open(struct sockinfo *);
96static size_t	dname_labeldec(char *, size_t, const char *);
97static void	mysyslog(int, const char *, ...);
98
99static const char *rtpref_str[] = {
100	"medium",		/* 00 */
101	"high",			/* 01 */
102	"rsv",			/* 10 */
103	"low"			/* 11 */
104};
105
106static 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
121static char errmsgbuf[1024];
122static char *errmsg = NULL;
123
124static void
125mysyslog(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
137static void
138usage(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
151int
152main(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
199static int
200csock_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
221static int
222action_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
274static int
275action_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
299static int
300action_propset(char *argv)
301{
302	char buf[CM_MSG_MAXLEN];
303
304	return (action_plgeneric(CM_TYPE_REQ_SET_PROP, argv, buf));
305}
306
307static int
308action_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
328static int
329action_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
349static int
350action_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
372static int
373action_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
381static int
382action_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 */
391static int
392action_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
407static int
408action_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			/*
518			 * RA_RECV: ND6_IFF_ACCEPT_RTADV
519			 * RA_SEND: ip6.forwarding
520			 */
521			if (ifi_s->ifi_nd_flags & ND6_IFF_ACCEPT_RTADV)
522				ra_ifstatus = RA_IFSTATUS_RA_RECV;
523			else if (getinet6sysctl(IPV6CTL_FORWARDING))
524				ra_ifstatus = RA_IFSTATUS_RA_SEND;
525			else
526				ra_ifstatus = RA_IFSTATUS_INACTIVE;
527		}
528
529		c = 0;
530		printf(" status=<");
531		if (ra_ifstatus == RA_IFSTATUS_INACTIVE)
532			printf("%s%s", (c) ? "," : "", "INACTIVE");
533		else if (ra_ifstatus == RA_IFSTATUS_RA_RECV)
534			printf("%s%s", (c) ? "," : "", "RA_RECV");
535		else if (ra_ifstatus == RA_IFSTATUS_RA_SEND)
536			printf("%s%s", (c) ? "," : "", "RA_SEND");
537		printf("> ");
538
539		switch (ifi_s->ifi_state) {
540		case IFI_STATE_CONFIGURED:
541		case IFI_STATE_TRANSITIVE:
542			break;
543		default:
544			printf("\n");
545			continue;
546		}
547
548		printf("mtu %d\n", ifi_s->ifi_phymtu);
549
550		sprintf(argv_rai, "%s:rai=", ifi->ifi_ifname);
551		action_argv = argv_rai;
552
553		error = action_propget(action_argv, &cp);
554		if (error)
555			continue;
556
557		rai = (struct rainfo *)cp.cp_val;
558
559		printf("\tDefaultLifetime: %s",
560		    sec2str(rai->rai_lifetime, ssbuf));
561		if (ra_ifstatus != RA_IFSTATUS_RA_SEND &&
562		    rai->rai_lifetime == 0)
563			printf(" (RAs will be sent with zero lifetime)");
564
565		printf("\n");
566
567		printf("\tMinAdvInterval/MaxAdvInterval: ");
568		printf("%s/", sec2str(rai->rai_mininterval, ssbuf));
569		printf("%s\n", sec2str(rai->rai_maxinterval, ssbuf));
570		if (rai->rai_linkmtu)
571			printf("\tAdvLinkMTU: %d", rai->rai_linkmtu);
572		else
573			printf("\tAdvLinkMTU: <none>");
574
575		printf(", ");
576
577		printf("Flags: ");
578		if (rai->rai_managedflg || rai->rai_otherflg) {
579			printf("%s", rai->rai_managedflg ? "M" : "");
580			printf("%s", rai->rai_otherflg ? "O" : "");
581		} else
582			printf("<none>");
583
584		printf(", ");
585
586		printf("Preference: %s\n",
587		    rtpref_str[(rai->rai_rtpref >> 3) & 0xff]);
588
589		printf("\tReachableTime: %s, ",
590		    sec2str(rai->rai_reachabletime, ssbuf));
591		printf("RetransTimer: %s, "
592		    "CurHopLimit: %d\n",
593		    sec2str(rai->rai_retranstimer, ssbuf),
594		    rai->rai_hoplimit);
595		printf("\tAdvIfPrefixes: %s\n",
596		    rai->rai_advifprefix ? "yes" : "no");
597
598		/* RA timer */
599		rat = NULL;
600		if (ifi_s->ifi_ra_timer != NULL) {
601			sprintf(argv_ifi_ra_timer, "%s:ifi_ra_timer=",
602			    ifi->ifi_ifname);
603			action_argv = argv_ifi_ra_timer;
604
605			error = action_propget(action_argv, &cp);
606			if (error)
607				return (error);
608
609			rat = (struct rtadvd_timer *)cp.cp_val;
610		}
611		printf("\tNext RA send: ");
612		if (rat == NULL)
613			printf("never\n");
614		else {
615			ts.tv_sec = rat->rat_tm.tv_sec + ts0.tv_sec;
616			printf("%s", ctime(&ts.tv_sec));
617		}
618		printf("\tLast RA send: ");
619		if (ifi_s->ifi_ra_lastsent.tv_sec == 0)
620			printf("never\n");
621		else {
622			ts.tv_sec = ifi_s->ifi_ra_lastsent.tv_sec + ts0.tv_sec;
623			printf("%s", ctime(&ts.tv_sec));
624		}
625		if (rai->rai_clockskew)
626			printf("\tClock skew: %" PRIu16 "sec\n",
627			    rai->rai_clockskew);
628
629		if (vflag < LOG_WARNING)
630			continue;
631
632		/* route information */
633		sprintf(argv_rti, "%s:rti=", ifi->ifi_ifname);
634		action_argv = argv_rti;
635		error = action_propget(action_argv, &cp);
636		if (error)
637			return (error);
638
639		rti = (struct rtinfo *)cp.cp_val;
640		len = cp.cp_val_len / sizeof(*rti);
641		if (len > 0) {
642			printf("\tRoute Info:\n");
643
644			for (i = 0; i < len; i++)
645				action_show_rtinfo(&rti[i]);
646		}
647
648		/* prefix information */
649		sprintf(argv_pfx, "%s:pfx=", ifi->ifi_ifname);
650		action_argv = argv_pfx;
651
652		error = action_propget(action_argv, &cp);
653		if (error)
654			continue;
655
656		pfx = (struct prefix *)cp.cp_val;
657		len = cp.cp_val_len / sizeof(*pfx);
658
659		if (len > 0) {
660			printf("\tPrefixes (%d):\n", len);
661
662			for (i = 0; i < len; i++)
663				action_show_prefix(&pfx[i]);
664		}
665
666		/* RDNSS information */
667		sprintf(argv_rdnss, "%s:rdnss=", ifi->ifi_ifname);
668		action_argv = argv_rdnss;
669
670		error = action_propget(action_argv, &cp);
671		if (error)
672			continue;
673
674		len = *((uint16_t *)cp.cp_val);
675
676		if (len > 0) {
677			printf("\tRDNSS entries:\n");
678			action_show_rdnss(cp.cp_val);
679		}
680
681		/* DNSSL information */
682		sprintf(argv_dnssl, "%s:dnssl=", ifi->ifi_ifname);
683		action_argv = argv_dnssl;
684
685		error = action_propget(action_argv, &cp);
686		if (error)
687			continue;
688
689		len = *((uint16_t *)cp.cp_val);
690
691		if (len > 0) {
692			printf("\tDNSSL entries:\n");
693			action_show_dnssl(cp.cp_val);
694		}
695
696		if (vflag < LOG_NOTICE)
697			continue;
698
699		printf("\n");
700
701		printf("\tCounters\n"
702		    "\t RA burst counts: %" PRIu16 " (interval: %s)\n"
703		    "\t RS wait counts: %" PRIu16 "\n",
704		    ifi_s->ifi_burstcount,
705		    sec2str(ifi_s->ifi_burstinterval, ssbuf),
706		    ifi_s->ifi_rs_waitcount);
707
708		printf("\tOutputs\n"
709		    "\t RA: %" PRIu64 "\n", ifi_s->ifi_raoutput);
710
711		printf("\tInputs\n"
712		    "\t RA: %" PRIu64 " (normal)\n"
713		    "\t RA: %" PRIu64 " (inconsistent)\n"
714		    "\t RS: %" PRIu64 "\n",
715		    ifi_s->ifi_rainput,
716		    ifi_s->ifi_rainconsistent,
717		    ifi_s->ifi_rsinput);
718
719		printf("\n");
720
721#if 0	/* Not implemented yet */
722		printf("\tReceived RAs:\n");
723#endif
724	}
725
726	return (0);
727}
728
729static int
730action_show_rtinfo(struct rtinfo *rti)
731{
732	char ntopbuf[INET6_ADDRSTRLEN];
733	char ssbuf[SSBUFLEN];
734
735	printf("\t  %s/%d (pref: %s, ltime: %s)\n",
736	    inet_ntop(AF_INET6, &rti->rti_prefix,
737		ntopbuf, sizeof(ntopbuf)),
738	    rti->rti_prefixlen,
739	    rtpref_str[0xff & (rti->rti_rtpref >> 3)],
740	    (rti->rti_ltime == ND6_INFINITE_LIFETIME) ?
741	    "infinity" : sec2str(rti->rti_ltime, ssbuf));
742
743	return (0);
744}
745
746static int
747action_show_prefix(struct prefix *pfx)
748{
749	char ntopbuf[INET6_ADDRSTRLEN];
750	char ssbuf[SSBUFLEN];
751	struct timespec now;
752
753	clock_gettime(CLOCK_MONOTONIC_FAST, &now);
754	printf("\t  %s/%d", inet_ntop(AF_INET6, &pfx->pfx_prefix,
755		ntopbuf, sizeof(ntopbuf)), pfx->pfx_prefixlen);
756
757	printf(" (");
758	switch (pfx->pfx_origin) {
759	case PREFIX_FROM_KERNEL:
760		printf("KERNEL");
761		break;
762	case PREFIX_FROM_CONFIG:
763		printf("CONFIG");
764		break;
765	case PREFIX_FROM_DYNAMIC:
766		printf("DYNAMIC");
767		break;
768	}
769
770	printf(",");
771
772	printf(" vltime=%s",
773	    (pfx->pfx_validlifetime == ND6_INFINITE_LIFETIME) ?
774	    "infinity" : sec2str(pfx->pfx_validlifetime, ssbuf));
775
776	if (pfx->pfx_vltimeexpire > 0)
777		printf("(expire: %s)",
778		    ((long)pfx->pfx_vltimeexpire > now.tv_sec) ?
779		    sec2str(pfx->pfx_vltimeexpire - now.tv_sec, ssbuf) :
780		    "0");
781
782	printf(",");
783
784	printf(" pltime=%s",
785	    (pfx->pfx_preflifetime == ND6_INFINITE_LIFETIME) ?
786	    "infinity" : sec2str(pfx->pfx_preflifetime, ssbuf));
787
788	if (pfx->pfx_pltimeexpire > 0)
789		printf("(expire %s)",
790		    ((long)pfx->pfx_pltimeexpire > now.tv_sec) ?
791		    sec2str(pfx->pfx_pltimeexpire - now.tv_sec, ssbuf) :
792		    "0");
793
794	printf(",");
795
796	printf(" flags=");
797	if (pfx->pfx_onlinkflg || pfx->pfx_autoconfflg) {
798		printf("%s", pfx->pfx_onlinkflg ? "L" : "");
799		printf("%s", pfx->pfx_autoconfflg ? "A" : "");
800	} else
801		printf("<none>");
802
803	if (pfx->pfx_timer) {
804		struct timespec *rest;
805
806		rest = rtadvd_timer_rest(pfx->pfx_timer);
807		if (rest) { /* XXX: what if not? */
808			printf(" expire=%s", sec2str(rest->tv_sec, ssbuf));
809		}
810	}
811
812	printf(")\n");
813
814	return (0);
815}
816
817static int
818action_show_rdnss(void *msg)
819{
820	struct rdnss *rdn;
821	struct rdnss_addr *rda;
822	uint16_t *rdn_cnt;
823	uint16_t *rda_cnt;
824	int i;
825	int j;
826	char *p;
827	uint32_t	ltime;
828	char ntopbuf[INET6_ADDRSTRLEN];
829	char ssbuf[SSBUFLEN];
830
831	p = msg;
832	rdn_cnt = (uint16_t *)p;
833	p += sizeof(*rdn_cnt);
834
835	if (*rdn_cnt > 0) {
836		for (i = 0; i < *rdn_cnt; i++) {
837			rdn = (struct rdnss *)p;
838			ltime = rdn->rd_ltime;
839			p += sizeof(*rdn);
840
841			rda_cnt = (uint16_t *)p;
842			p += sizeof(*rda_cnt);
843			if (*rda_cnt > 0)
844				for (j = 0; j < *rda_cnt; j++) {
845					rda = (struct rdnss_addr *)p;
846					printf("\t  %s (ltime=%s)\n",
847					    inet_ntop(AF_INET6,
848						&rda->ra_dns,
849						ntopbuf,
850						sizeof(ntopbuf)),
851					    sec2str(ltime, ssbuf));
852					p += sizeof(*rda);
853				}
854		}
855	}
856
857	return (0);
858}
859
860static int
861action_show_dnssl(void *msg)
862{
863	struct dnssl *dns;
864	struct dnssl_addr *dna;
865	uint16_t *dns_cnt;
866	uint16_t *dna_cnt;
867	int i;
868	int j;
869	char *p;
870	uint32_t ltime;
871	char hbuf[NI_MAXHOST];
872	char ssbuf[SSBUFLEN];
873
874	p = msg;
875	dns_cnt = (uint16_t *)p;
876	p += sizeof(*dns_cnt);
877
878	if (*dns_cnt > 0) {
879		for (i = 0; i < *dns_cnt; i++) {
880			dns = (struct dnssl *)p;
881			ltime = dns->dn_ltime;
882			p += sizeof(*dns);
883
884			dna_cnt = (uint16_t *)p;
885			p += sizeof(*dna_cnt);
886			if (*dna_cnt > 0)
887				for (j = 0; j < *dna_cnt; j++) {
888					dna = (struct dnssl_addr *)p;
889					dname_labeldec(hbuf, sizeof(hbuf),
890					    dna->da_dom);
891					printf("\t  %s (ltime=%s)\n",
892					    hbuf, sec2str(ltime, ssbuf));
893					p += sizeof(*dna);
894				}
895		}
896	}
897
898	return (0);
899}
900
901/* Decode domain name label encoding in RFC 1035 Section 3.1 */
902static size_t
903dname_labeldec(char *dst, size_t dlen, const char *src)
904{
905	size_t len;
906	const char *src_origin;
907	const char *src_last;
908	const char *dst_origin;
909
910	src_origin = src;
911	src_last = strchr(src, '\0');
912	dst_origin = dst;
913	memset(dst, '\0', dlen);
914	while (src && (len = (uint8_t)(*src++) & 0x3f) &&
915	    (src + len) <= src_last) {
916		if (dst != dst_origin)
917			*dst++ = '.';
918		mysyslog(LOG_DEBUG, "<%s> labellen = %zd", __func__, len);
919		memcpy(dst, src, len);
920		src += len;
921		dst += len;
922	}
923	*dst = '\0';
924
925	return (src - src_origin);
926}
927