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