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