1224006Shrs/*-
2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3330449Seadler *
4224006Shrs * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org>
5224006Shrs * All rights reserved.
6224006Shrs *
7224006Shrs * Redistribution and use in source and binary forms, with or without
8224006Shrs * modification, are permitted provided that the following conditions
9224006Shrs * are met:
10224006Shrs * 1. Redistributions of source code must retain the above copyright
11224006Shrs *    notice, this list of conditions and the following disclaimer.
12224006Shrs * 2. Redistributions in binary form must reproduce the above copyright
13224006Shrs *    notice, this list of conditions and the following disclaimer in the
14224006Shrs *    documentation and/or other materials provided with the distribution.
15224006Shrs *
16224006Shrs * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
17224006Shrs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18224006Shrs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19224006Shrs * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS
20224006Shrs * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21224006Shrs * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22224006Shrs * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23224006Shrs * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24224006Shrs * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25224006Shrs * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26224006Shrs * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27224006Shrs *
28224006Shrs * $FreeBSD: stable/11/usr.sbin/rtadvctl/rtadvctl.c 330449 2018-03-05 07:26:05Z eadler $
29224006Shrs *
30224006Shrs */
31224006Shrs
32224006Shrs#include <sys/queue.h>
33224006Shrs#include <sys/types.h>
34224006Shrs#include <sys/socket.h>
35224006Shrs#include <sys/stat.h>
36224006Shrs#include <sys/un.h>
37224006Shrs#include <sys/uio.h>
38224006Shrs#include <net/if.h>
39224006Shrs#include <net/if_dl.h>
40224006Shrs#include <net/if_types.h>
41224006Shrs#include <net/ethernet.h>
42224006Shrs#include <netinet/in.h>
43224006Shrs#include <netinet/ip6.h>
44224006Shrs#include <netinet/icmp6.h>
45224006Shrs#include <netinet6/in6_var.h>
46224006Shrs#include <netinet6/nd6.h>
47224006Shrs#include <arpa/inet.h>
48224006Shrs#include <fcntl.h>
49224006Shrs#include <errno.h>
50224144Shrs#include <inttypes.h>
51224006Shrs#include <netdb.h>
52224006Shrs#include <unistd.h>
53224006Shrs#include <string.h>
54224006Shrs#include <stdarg.h>
55224006Shrs#include <stdio.h>
56224006Shrs#include <stdlib.h>
57224006Shrs#include <stdarg.h>
58224006Shrs#include <syslog.h>
59253970Shrs#include <time.h>
60224006Shrs#include <err.h>
61224006Shrs
62224006Shrs#include "pathnames.h"
63224006Shrs#include "rtadvd.h"
64224006Shrs#include "if.h"
65224006Shrs#include "timer_subr.h"
66224144Shrs#include "timer.h"
67224006Shrs#include "control.h"
68224006Shrs#include "control_client.h"
69224006Shrs
70224006Shrs#define RA_IFSTATUS_INACTIVE	0
71224006Shrs#define RA_IFSTATUS_RA_RECV	1
72224006Shrs#define RA_IFSTATUS_RA_SEND	2
73224006Shrs
74224006Shrsstatic int vflag = LOG_ERR;
75224006Shrs
76224006Shrsstatic void	usage(void);
77224006Shrs
78224006Shrsstatic int	action_propset(char *);
79224006Shrsstatic int	action_propget(char *, struct ctrl_msg_pl *);
80224006Shrsstatic int	action_plgeneric(int, char *, char *);
81224006Shrs
82224006Shrsstatic int	action_enable(int, char **);
83224006Shrsstatic int	action_disable(int, char **);
84224006Shrsstatic int	action_reload(int, char **);
85224006Shrsstatic int	action_echo(int, char **);
86224006Shrsstatic int	action_version(int, char **);
87224006Shrsstatic int	action_shutdown(int, char **);
88224006Shrs
89224006Shrsstatic int	action_show(int, char **);
90224006Shrsstatic int	action_show_prefix(struct prefix *);
91224006Shrsstatic int	action_show_rtinfo(struct rtinfo *);
92224006Shrsstatic int	action_show_rdnss(void *);
93224006Shrsstatic int	action_show_dnssl(void *);
94224006Shrs
95224006Shrsstatic int	csock_client_open(struct sockinfo *);
96224006Shrsstatic size_t	dname_labeldec(char *, size_t, const char *);
97224006Shrsstatic void	mysyslog(int, const char *, ...);
98224006Shrs
99224006Shrsstatic const char *rtpref_str[] = {
100224006Shrs	"medium",		/* 00 */
101224006Shrs	"high",			/* 01 */
102224006Shrs	"rsv",			/* 10 */
103224006Shrs	"low"			/* 11 */
104224006Shrs};
105224006Shrs
106224006Shrsstatic struct dispatch_table {
107224006Shrs	const char	*dt_comm;
108224006Shrs	int (*dt_act)(int, char **);
109224006Shrs} dtable[] = {
110224006Shrs	{ "show", action_show },
111224006Shrs	{ "reload", action_reload },
112224006Shrs	{ "shutdown", action_shutdown },
113224006Shrs	{ "enable", action_enable },
114224006Shrs	{ "disable", action_disable },
115224144Shrs	{ NULL, NULL },
116224006Shrs	{ "echo", action_echo },
117224006Shrs	{ "version", action_version },
118224006Shrs	{ NULL, NULL },
119224006Shrs};
120224006Shrs
121224144Shrsstatic char errmsgbuf[1024];
122224144Shrsstatic char *errmsg = NULL;
123224144Shrs
124224006Shrsstatic void
125224006Shrsmysyslog(int priority, const char * restrict fmt, ...)
126224006Shrs{
127224006Shrs	va_list ap;
128224006Shrs
129224006Shrs	if (vflag >= priority) {
130224006Shrs		va_start(ap, fmt);
131224006Shrs		vfprintf(stderr, fmt, ap);
132224006Shrs		fprintf(stderr, "\n");
133224006Shrs		va_end(ap);
134224006Shrs	}
135224006Shrs}
136224006Shrs
137224006Shrsstatic void
138224006Shrsusage(void)
139224006Shrs{
140224006Shrs	int i;
141224006Shrs
142224006Shrs	for (i = 0; (size_t)i < sizeof(dtable)/sizeof(dtable[0]); i++) {
143224006Shrs		if (dtable[i].dt_comm == NULL)
144224006Shrs			break;
145224006Shrs		printf("%s\n", dtable[i].dt_comm);
146224006Shrs	}
147224006Shrs
148224006Shrs	exit(1);
149224006Shrs}
150224006Shrs
151224006Shrsint
152224006Shrsmain(int argc, char *argv[])
153224006Shrs{
154224006Shrs	int i;
155224006Shrs	int ch;
156224006Shrs	int (*action)(int, char **) = NULL;
157224006Shrs	int error;
158224006Shrs
159224006Shrs	while ((ch = getopt(argc, argv, "Dv")) != -1) {
160224006Shrs		switch (ch) {
161224006Shrs		case 'D':
162224006Shrs			vflag = LOG_DEBUG;
163224006Shrs			break;
164224006Shrs		case 'v':
165224006Shrs			vflag++;
166224006Shrs			break;
167224006Shrs		default:
168224006Shrs			usage();
169224006Shrs		}
170224006Shrs	}
171224006Shrs	argc -= optind;
172224006Shrs	argv += optind;
173224006Shrs
174224006Shrs	if (argc == 0)
175224006Shrs		usage();
176224006Shrs
177224006Shrs	for (i = 0; (size_t)i < sizeof(dtable)/sizeof(dtable[0]); i++) {
178224006Shrs		if (dtable[i].dt_comm == NULL ||
179224006Shrs		    strcmp(dtable[i].dt_comm, argv[0]) == 0) {
180224006Shrs			action = dtable[i].dt_act;
181224006Shrs			break;
182224006Shrs		}
183224006Shrs	}
184224006Shrs
185224144Shrs	if (action == NULL)
186224006Shrs		usage();
187224006Shrs
188224144Shrs	error = (dtable[i].dt_act)(--argc, ++argv);
189224144Shrs	if (error) {
190224144Shrs		fprintf(stderr, "%s failed", dtable[i].dt_comm);
191224144Shrs		if (errmsg != NULL)
192224144Shrs			fprintf(stderr, ": %s", errmsg);
193224144Shrs		fprintf(stderr, ".\n");
194224144Shrs	}
195224144Shrs
196224006Shrs	return (error);
197224006Shrs}
198224006Shrs
199224006Shrsstatic int
200224006Shrscsock_client_open(struct sockinfo *s)
201224006Shrs{
202224006Shrs	struct sockaddr_un sun;
203224006Shrs
204224006Shrs	if ((s->si_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
205224006Shrs		err(1, "cannot open control socket.");
206224006Shrs
207224006Shrs	memset(&sun, 0, sizeof(sun));
208224006Shrs	sun.sun_family = AF_UNIX;
209224006Shrs	sun.sun_len = sizeof(sun);
210224006Shrs	strlcpy(sun.sun_path, s->si_name, sizeof(sun.sun_path));
211224006Shrs
212224006Shrs	if (connect(s->si_fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
213224006Shrs		err(1, "connect: %s", s->si_name);
214224006Shrs
215224006Shrs	mysyslog(LOG_DEBUG,
216224006Shrs	    "<%s> connected to %s", __func__, sun.sun_path);
217224006Shrs
218224006Shrs	return (0);
219224006Shrs}
220224006Shrs
221224006Shrsstatic int
222224006Shrsaction_plgeneric(int action, char *plstr, char *buf)
223224006Shrs{
224224006Shrs	struct ctrl_msg_hdr *cm;
225224006Shrs	struct ctrl_msg_pl cp;
226224006Shrs	struct sockinfo *s;
227224006Shrs	char *msg;
228224006Shrs	char *p;
229224006Shrs	char *q;
230224006Shrs
231224006Shrs	s = &ctrlsock;
232224006Shrs	csock_client_open(s);
233224006Shrs
234224006Shrs	cm = (struct ctrl_msg_hdr *)buf;
235224006Shrs	msg = (char *)buf + sizeof(*cm);
236224006Shrs
237224006Shrs	cm->cm_version = CM_VERSION;
238224006Shrs	cm->cm_type = action;
239224006Shrs	cm->cm_len = sizeof(*cm);
240224006Shrs
241224006Shrs	if (plstr != NULL) {
242224006Shrs		memset(&cp, 0, sizeof(cp));
243224006Shrs		p = strchr(plstr, ':');
244224006Shrs		q = strchr(plstr, '=');
245224006Shrs		if (p != NULL && q != NULL && p > q)
246224006Shrs			return (1);
247224006Shrs
248224006Shrs		if (p == NULL) {		/* No : */
249224006Shrs			cp.cp_ifname = NULL;
250224006Shrs			cp.cp_key = plstr;
251224006Shrs		} else if  (p == plstr) {	/* empty */
252224006Shrs			cp.cp_ifname = NULL;
253224006Shrs			cp.cp_key = plstr + 1;
254224006Shrs		} else {
255224006Shrs			*p++ = '\0';
256224006Shrs			cp.cp_ifname = plstr;
257224006Shrs			cp.cp_key = p;
258224006Shrs		}
259224006Shrs		if (q == NULL)
260224006Shrs			cp.cp_val = NULL;
261224006Shrs		else {
262224006Shrs			*q++ = '\0';
263224006Shrs			cp.cp_val = q;
264224006Shrs		}
265225519Shrs		cm->cm_len += cm_pl2bin(msg, &cp);
266224006Shrs
267224006Shrs		mysyslog(LOG_DEBUG, "<%s> key=%s, val_len=%d, ifname=%s",
268224006Shrs		    __func__,cp.cp_key, cp.cp_val_len, cp.cp_ifname);
269224006Shrs	}
270224006Shrs
271225519Shrs	return (cm_handler_client(s->si_fd, CM_STATE_MSG_DISPATCH, buf));
272224006Shrs}
273224006Shrs
274224006Shrsstatic int
275224006Shrsaction_propget(char *argv, struct ctrl_msg_pl *cp)
276224006Shrs{
277224006Shrs	int error;
278224006Shrs	struct ctrl_msg_hdr *cm;
279224006Shrs	char buf[CM_MSG_MAXLEN];
280224006Shrs	char *msg;
281224006Shrs
282224006Shrs	memset(cp, 0, sizeof(*cp));
283224006Shrs	cm = (struct ctrl_msg_hdr *)buf;
284224006Shrs	msg = (char *)buf + sizeof(*cm);
285224006Shrs
286224006Shrs	error = action_plgeneric(CM_TYPE_REQ_GET_PROP, argv, buf);
287224006Shrs	if (error || cm->cm_len <= sizeof(*cm))
288224006Shrs		return (1);
289224006Shrs
290225519Shrs	cm_bin2pl(msg, cp);
291224006Shrs	mysyslog(LOG_DEBUG, "<%s> type=%d, len=%d",
292224006Shrs	    __func__, cm->cm_type, cm->cm_len);
293224006Shrs	mysyslog(LOG_DEBUG, "<%s> key=%s, val_len=%d, ifname=%s",
294224006Shrs	    __func__,cp->cp_key, cp->cp_val_len, cp->cp_ifname);
295224006Shrs
296224006Shrs	return (0);
297224006Shrs}
298224006Shrs
299224006Shrsstatic int
300224006Shrsaction_propset(char *argv)
301224006Shrs{
302224006Shrs	char buf[CM_MSG_MAXLEN];
303224006Shrs
304224006Shrs	return (action_plgeneric(CM_TYPE_REQ_SET_PROP, argv, buf));
305224006Shrs}
306224006Shrs
307224006Shrsstatic int
308224144Shrsaction_disable(int argc, char **argv)
309224006Shrs{
310224144Shrs	char *action_argv;
311224144Shrs	char argv_disable[IFNAMSIZ + sizeof(":disable=")];
312224144Shrs	int i;
313224144Shrs	int error;
314224006Shrs
315224144Shrs	if (argc < 1)
316224144Shrs		return (1);
317224144Shrs
318224144Shrs	error = 0;
319224144Shrs	for (i = 0; i < argc; i++) {
320224144Shrs		sprintf(argv_disable, "%s:disable=", argv[i]);
321224144Shrs		action_argv = argv_disable;
322224144Shrs		error += action_propset(action_argv);
323224144Shrs	}
324224144Shrs
325224144Shrs	return (error);
326224006Shrs}
327224006Shrs
328224006Shrsstatic int
329224144Shrsaction_enable(int argc, char **argv)
330224006Shrs{
331224144Shrs	char *action_argv;
332224144Shrs	char argv_enable[IFNAMSIZ + sizeof(":enable=")];
333224144Shrs	int i;
334224144Shrs	int error;
335224006Shrs
336224144Shrs	if (argc < 1)
337224144Shrs		return (1);
338224144Shrs
339224144Shrs	error = 0;
340224144Shrs	for (i = 0; i < argc; i++) {
341224144Shrs		sprintf(argv_enable, "%s:enable=", argv[i]);
342224144Shrs		action_argv = argv_enable;
343224144Shrs		error += action_propset(action_argv);
344224144Shrs	}
345224144Shrs
346224144Shrs	return (error);
347224006Shrs}
348224006Shrs
349224006Shrsstatic int
350224144Shrsaction_reload(int argc, char **argv)
351224006Shrs{
352224006Shrs	char *action_argv;
353224144Shrs	char argv_reload[IFNAMSIZ + sizeof(":reload=")];
354224144Shrs	int i;
355224144Shrs	int error;
356224006Shrs
357224144Shrs	if (argc == 0) {
358224144Shrs		action_argv = strdup(":reload=");
359224144Shrs		return (action_propset(action_argv));
360224144Shrs	}
361224144Shrs
362224144Shrs	error = 0;
363224144Shrs	for (i = 0; i < argc; i++) {
364224144Shrs		sprintf(argv_reload, "%s:reload=", argv[i]);
365224144Shrs		action_argv = argv_reload;
366224144Shrs		error += action_propset(action_argv);
367224144Shrs	}
368224144Shrs
369224144Shrs	return (error);
370224006Shrs}
371224006Shrs
372224006Shrsstatic int
373224006Shrsaction_echo(int argc __unused, char **argv __unused)
374224006Shrs{
375224006Shrs	char *action_argv;
376224006Shrs
377224006Shrs	action_argv = strdup("echo");
378224144Shrs	return (action_propset(action_argv));
379224006Shrs}
380224006Shrs
381224006Shrsstatic int
382224006Shrsaction_shutdown(int argc __unused, char **argv __unused)
383224006Shrs{
384224006Shrs	char *action_argv;
385224006Shrs
386224006Shrs	action_argv = strdup("shutdown");
387224144Shrs	return (action_propset(action_argv));
388224006Shrs}
389224006Shrs
390224006Shrs/* XXX */
391224006Shrsstatic int
392224006Shrsaction_version(int argc __unused, char **argv __unused)
393224006Shrs{
394224006Shrs	char *action_argv;
395224006Shrs	struct ctrl_msg_pl cp;
396224006Shrs	int error;
397224006Shrs
398224006Shrs	action_argv = strdup(":version=");
399224006Shrs	error = action_propget(action_argv, &cp);
400224006Shrs	if (error)
401224006Shrs		return (error);
402224006Shrs
403224006Shrs	printf("version=%s\n", cp.cp_val);
404224006Shrs	return (0);
405224006Shrs}
406224006Shrs
407224006Shrsstatic int
408224006Shrsaction_show(int argc, char **argv)
409224006Shrs{
410224006Shrs	char *action_argv;
411224006Shrs	char argv_ifilist[sizeof(":ifilist=")] = ":ifilist=";
412224006Shrs	char argv_ifi[IFNAMSIZ + sizeof(":ifi=")];
413224006Shrs	char argv_rai[IFNAMSIZ + sizeof(":rai=")];
414224006Shrs	char argv_rti[IFNAMSIZ + sizeof(":rti=")];
415224006Shrs	char argv_pfx[IFNAMSIZ + sizeof(":pfx=")];
416224144Shrs	char argv_ifi_ra_timer[IFNAMSIZ + sizeof(":ifi_ra_timer=")];
417224006Shrs	char argv_rdnss[IFNAMSIZ + sizeof(":rdnss=")];
418224006Shrs	char argv_dnssl[IFNAMSIZ + sizeof(":dnssl=")];
419224006Shrs	char ssbuf[SSBUFLEN];
420224006Shrs
421253970Shrs	struct timespec now, ts0, ts;
422224006Shrs	struct ctrl_msg_pl cp;
423224006Shrs	struct ifinfo *ifi;
424224006Shrs	TAILQ_HEAD(, ifinfo) ifl = TAILQ_HEAD_INITIALIZER(ifl);
425224006Shrs	char *endp;
426224006Shrs	char *p;
427224006Shrs	int error;
428224006Shrs	int i;
429224006Shrs	int len;
430224006Shrs
431224006Shrs	if (argc == 0) {
432224006Shrs		action_argv = argv_ifilist;
433224006Shrs		error = action_propget(action_argv, &cp);
434224006Shrs		if (error)
435224006Shrs			return (error);
436224006Shrs
437224006Shrs		p = cp.cp_val;
438224006Shrs		endp = p + cp.cp_val_len;
439224006Shrs		while (p < endp) {
440224006Shrs			ifi = malloc(sizeof(*ifi));
441224006Shrs			if (ifi == NULL)
442224144Shrs				return (1);
443224006Shrs			memset(ifi, 0, sizeof(*ifi));
444224006Shrs
445224006Shrs			strcpy(ifi->ifi_ifname, p);
446224006Shrs			ifi->ifi_ifindex = if_nametoindex(ifi->ifi_ifname);
447224006Shrs			TAILQ_INSERT_TAIL(&ifl, ifi, ifi_next);
448224006Shrs			p += strlen(ifi->ifi_ifname) + 1;
449224006Shrs		}
450224006Shrs	} else {
451224006Shrs		for (i = 0; i < argc; i++) {
452224006Shrs			ifi = malloc(sizeof(*ifi));
453224006Shrs			if (ifi == NULL)
454224144Shrs				return (1);
455224006Shrs			memset(ifi, 0, sizeof(*ifi));
456224006Shrs
457224006Shrs			strcpy(ifi->ifi_ifname, argv[i]);
458224006Shrs			ifi->ifi_ifindex = if_nametoindex(ifi->ifi_ifname);
459224144Shrs			if (ifi->ifi_ifindex == 0) {
460224144Shrs				sprintf(errmsgbuf, "invalid interface %s",
461224144Shrs				    ifi->ifi_ifname);
462224144Shrs				errmsg = errmsgbuf;
463224144Shrs				return (1);
464224144Shrs			}
465224144Shrs
466224006Shrs			TAILQ_INSERT_TAIL(&ifl, ifi, ifi_next);
467224006Shrs		}
468224006Shrs	}
469224006Shrs
470253970Shrs	clock_gettime(CLOCK_REALTIME_FAST, &now);
471253970Shrs	clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
472253970Shrs	TS_SUB(&now, &ts, &ts0);
473253970Shrs
474224006Shrs	TAILQ_FOREACH(ifi, &ifl, ifi_next) {
475224006Shrs		struct ifinfo *ifi_s;
476224144Shrs		struct rtadvd_timer *rat;
477224006Shrs		struct rainfo *rai;
478224006Shrs		struct rtinfo *rti;
479224006Shrs		struct prefix *pfx;
480224006Shrs		int c;
481224006Shrs		int ra_ifstatus;
482224006Shrs
483224006Shrs		sprintf(argv_ifi, "%s:ifi=", ifi->ifi_ifname);
484224006Shrs		action_argv = argv_ifi;
485224006Shrs		error = action_propget(action_argv, &cp);
486224006Shrs		if (error)
487224006Shrs			return (error);
488224006Shrs		ifi_s = (struct ifinfo *)cp.cp_val;
489224006Shrs
490224006Shrs		if (!(ifi_s->ifi_persist) && vflag < LOG_NOTICE)
491224006Shrs			continue;
492224006Shrs
493224006Shrs		printf("%s: flags=<", ifi->ifi_ifname);
494224006Shrs
495224006Shrs		c = 0;
496224006Shrs		if (ifi_s->ifi_ifindex == 0)
497224006Shrs			c += printf("NONEXISTENT");
498224006Shrs		else
499224006Shrs			c += printf("%s", (ifi_s->ifi_flags & IFF_UP) ?
500224006Shrs			    "UP" : "DOWN");
501224144Shrs		switch (ifi_s->ifi_state) {
502224144Shrs		case IFI_STATE_CONFIGURED:
503224006Shrs			c += printf("%s%s", (c) ? "," : "", "CONFIGURED");
504224144Shrs			break;
505224144Shrs		case IFI_STATE_TRANSITIVE:
506224144Shrs			c += printf("%s%s", (c) ? "," : "", "TRANSITIVE");
507224144Shrs			break;
508224144Shrs		}
509224006Shrs		if (ifi_s->ifi_persist)
510224006Shrs			c += printf("%s%s", (c) ? "," : "", "PERSIST");
511224006Shrs		printf(">");
512224006Shrs
513224006Shrs		ra_ifstatus = RA_IFSTATUS_INACTIVE;
514224006Shrs		if ((ifi_s->ifi_flags & IFF_UP) &&
515224144Shrs		    ((ifi_s->ifi_state == IFI_STATE_CONFIGURED) ||
516224144Shrs			(ifi_s->ifi_state == IFI_STATE_TRANSITIVE))) {
517224144Shrs#if (__FreeBSD_version < 900000)
518224144Shrs			/*
519224144Shrs			 * RA_RECV: !ip6.forwarding && ip6.accept_rtadv
520224144Shrs			 * RA_SEND: ip6.forwarding
521224144Shrs			 */
522224144Shrs			if (getinet6sysctl(IPV6CTL_FORWARDING) == 0) {
523224144Shrs				if (getinet6sysctl(IPV6CTL_ACCEPT_RTADV))
524224144Shrs					ra_ifstatus = RA_IFSTATUS_RA_RECV;
525224144Shrs				else
526224144Shrs					ra_ifstatus = RA_IFSTATUS_INACTIVE;
527224144Shrs			} else
528224144Shrs				ra_ifstatus = RA_IFSTATUS_RA_SEND;
529224144Shrs#else
530224144Shrs			/*
531224144Shrs			 * RA_RECV: ND6_IFF_ACCEPT_RTADV
532224144Shrs			 * RA_SEND: ip6.forwarding
533224144Shrs			 */
534224006Shrs			if (ifi_s->ifi_nd_flags & ND6_IFF_ACCEPT_RTADV)
535224006Shrs				ra_ifstatus = RA_IFSTATUS_RA_RECV;
536224006Shrs			else if (getinet6sysctl(IPV6CTL_FORWARDING))
537224006Shrs				ra_ifstatus = RA_IFSTATUS_RA_SEND;
538224006Shrs			else
539224006Shrs				ra_ifstatus = RA_IFSTATUS_INACTIVE;
540224144Shrs#endif
541224006Shrs		}
542224006Shrs
543224006Shrs		c = 0;
544224006Shrs		printf(" status=<");
545224006Shrs		if (ra_ifstatus == RA_IFSTATUS_INACTIVE)
546224006Shrs			printf("%s%s", (c) ? "," : "", "INACTIVE");
547224006Shrs		else if (ra_ifstatus == RA_IFSTATUS_RA_RECV)
548224006Shrs			printf("%s%s", (c) ? "," : "", "RA_RECV");
549224006Shrs		else if (ra_ifstatus == RA_IFSTATUS_RA_SEND)
550224006Shrs			printf("%s%s", (c) ? "," : "", "RA_SEND");
551224006Shrs		printf("> ");
552224006Shrs
553224144Shrs		switch (ifi_s->ifi_state) {
554224144Shrs		case IFI_STATE_CONFIGURED:
555224144Shrs		case IFI_STATE_TRANSITIVE:
556224144Shrs			break;
557224144Shrs		default:
558224006Shrs			printf("\n");
559224006Shrs			continue;
560224006Shrs		}
561224006Shrs
562224006Shrs		printf("mtu %d\n", ifi_s->ifi_phymtu);
563224006Shrs
564224006Shrs		sprintf(argv_rai, "%s:rai=", ifi->ifi_ifname);
565224006Shrs		action_argv = argv_rai;
566224006Shrs
567224006Shrs		error = action_propget(action_argv, &cp);
568224006Shrs		if (error)
569224006Shrs			continue;
570224006Shrs
571224006Shrs		rai = (struct rainfo *)cp.cp_val;
572224006Shrs
573224006Shrs		printf("\tDefaultLifetime: %s",
574224006Shrs		    sec2str(rai->rai_lifetime, ssbuf));
575224006Shrs		if (ra_ifstatus != RA_IFSTATUS_RA_SEND &&
576224006Shrs		    rai->rai_lifetime == 0)
577224006Shrs			printf(" (RAs will be sent with zero lifetime)");
578224006Shrs
579224006Shrs		printf("\n");
580224006Shrs
581225519Shrs		printf("\tMinAdvInterval/MaxAdvInterval: ");
582225519Shrs		printf("%s/", sec2str(rai->rai_mininterval, ssbuf));
583225519Shrs		printf("%s\n", sec2str(rai->rai_maxinterval, ssbuf));
584224006Shrs		if (rai->rai_linkmtu)
585224006Shrs			printf("\tAdvLinkMTU: %d", rai->rai_linkmtu);
586224006Shrs		else
587224006Shrs			printf("\tAdvLinkMTU: <none>");
588224006Shrs
589224006Shrs		printf(", ");
590224006Shrs
591224006Shrs		printf("Flags: ");
592224006Shrs		if (rai->rai_managedflg || rai->rai_otherflg) {
593224006Shrs			printf("%s", rai->rai_managedflg ? "M" : "");
594224006Shrs			printf("%s", rai->rai_otherflg ? "O" : "");
595224006Shrs		} else
596224006Shrs			printf("<none>");
597224006Shrs
598224006Shrs		printf(", ");
599224006Shrs
600224006Shrs		printf("Preference: %s\n",
601224006Shrs		    rtpref_str[(rai->rai_rtpref >> 3) & 0xff]);
602224006Shrs
603225519Shrs		printf("\tReachableTime: %s, ",
604225519Shrs		    sec2str(rai->rai_reachabletime, ssbuf));
605225519Shrs		printf("RetransTimer: %s, "
606224006Shrs		    "CurHopLimit: %d\n",
607224006Shrs		    sec2str(rai->rai_retranstimer, ssbuf),
608224006Shrs		    rai->rai_hoplimit);
609224006Shrs		printf("\tAdvIfPrefixes: %s\n",
610224006Shrs		    rai->rai_advifprefix ? "yes" : "no");
611224144Shrs
612224144Shrs		/* RA timer */
613224144Shrs		rat = NULL;
614224144Shrs		if (ifi_s->ifi_ra_timer != NULL) {
615224144Shrs			sprintf(argv_ifi_ra_timer, "%s:ifi_ra_timer=",
616224144Shrs			    ifi->ifi_ifname);
617224144Shrs			action_argv = argv_ifi_ra_timer;
618224144Shrs
619224144Shrs			error = action_propget(action_argv, &cp);
620224144Shrs			if (error)
621224144Shrs				return (error);
622224144Shrs
623224144Shrs			rat = (struct rtadvd_timer *)cp.cp_val;
624224144Shrs		}
625253970Shrs		printf("\tNext RA send: ");
626253970Shrs		if (rat == NULL)
627253970Shrs			printf("never\n");
628253970Shrs		else {
629253970Shrs			ts.tv_sec = rat->rat_tm.tv_sec + ts0.tv_sec;
630253970Shrs			printf("%s", ctime(&ts.tv_sec));
631253970Shrs		}
632253970Shrs		printf("\tLast RA send: ");
633253970Shrs		if (ifi_s->ifi_ra_lastsent.tv_sec == 0)
634253970Shrs			printf("never\n");
635253970Shrs		else {
636253970Shrs			ts.tv_sec = ifi_s->ifi_ra_lastsent.tv_sec + ts0.tv_sec;
637253970Shrs			printf("%s", ctime(&ts.tv_sec));
638253970Shrs		}
639224006Shrs		if (rai->rai_clockskew)
640224144Shrs			printf("\tClock skew: %" PRIu16 "sec\n",
641224006Shrs			    rai->rai_clockskew);
642224006Shrs
643224006Shrs		if (vflag < LOG_WARNING)
644224006Shrs			continue;
645224006Shrs
646224006Shrs		/* route information */
647224006Shrs		sprintf(argv_rti, "%s:rti=", ifi->ifi_ifname);
648224006Shrs		action_argv = argv_rti;
649224006Shrs		error = action_propget(action_argv, &cp);
650224006Shrs		if (error)
651224006Shrs			return (error);
652224006Shrs
653224006Shrs		rti = (struct rtinfo *)cp.cp_val;
654224006Shrs		len = cp.cp_val_len / sizeof(*rti);
655224006Shrs		if (len > 0) {
656224006Shrs			printf("\tRoute Info:\n");
657224006Shrs
658224006Shrs			for (i = 0; i < len; i++)
659224006Shrs				action_show_rtinfo(&rti[i]);
660224006Shrs		}
661224144Shrs
662224006Shrs		/* prefix information */
663224006Shrs		sprintf(argv_pfx, "%s:pfx=", ifi->ifi_ifname);
664224006Shrs		action_argv = argv_pfx;
665224006Shrs
666224006Shrs		error = action_propget(action_argv, &cp);
667224006Shrs		if (error)
668224006Shrs			continue;
669224006Shrs
670224006Shrs		pfx = (struct prefix *)cp.cp_val;
671224006Shrs		len = cp.cp_val_len / sizeof(*pfx);
672224006Shrs
673224006Shrs		if (len > 0) {
674224006Shrs			printf("\tPrefixes (%d):\n", len);
675224006Shrs
676224006Shrs			for (i = 0; i < len; i++)
677224006Shrs				action_show_prefix(&pfx[i]);
678224006Shrs		}
679224006Shrs
680224006Shrs		/* RDNSS information */
681224006Shrs		sprintf(argv_rdnss, "%s:rdnss=", ifi->ifi_ifname);
682224006Shrs		action_argv = argv_rdnss;
683224006Shrs
684224006Shrs		error = action_propget(action_argv, &cp);
685224006Shrs		if (error)
686224006Shrs			continue;
687224006Shrs
688224144Shrs		len = *((uint16_t *)cp.cp_val);
689224006Shrs
690224006Shrs		if (len > 0) {
691224006Shrs			printf("\tRDNSS entries:\n");
692224006Shrs			action_show_rdnss(cp.cp_val);
693224006Shrs		}
694224006Shrs
695224006Shrs		/* DNSSL information */
696224006Shrs		sprintf(argv_dnssl, "%s:dnssl=", ifi->ifi_ifname);
697224006Shrs		action_argv = argv_dnssl;
698224006Shrs
699224006Shrs		error = action_propget(action_argv, &cp);
700224006Shrs		if (error)
701224006Shrs			continue;
702224006Shrs
703224144Shrs		len = *((uint16_t *)cp.cp_val);
704224006Shrs
705224006Shrs		if (len > 0) {
706224006Shrs			printf("\tDNSSL entries:\n");
707224006Shrs			action_show_dnssl(cp.cp_val);
708224006Shrs		}
709224006Shrs
710224006Shrs		if (vflag < LOG_NOTICE)
711224006Shrs			continue;
712224006Shrs
713224006Shrs		printf("\n");
714224006Shrs
715224144Shrs		printf("\tCounters\n"
716224144Shrs		    "\t RA burst counts: %" PRIu16 " (interval: %s)\n"
717224144Shrs		    "\t RS wait counts: %" PRIu16 "\n",
718224144Shrs		    ifi_s->ifi_burstcount,
719224144Shrs		    sec2str(ifi_s->ifi_burstinterval, ssbuf),
720224144Shrs		    ifi_s->ifi_rs_waitcount);
721224144Shrs
722224144Shrs		printf("\tOutputs\n"
723224144Shrs		    "\t RA: %" PRIu64 "\n", ifi_s->ifi_raoutput);
724224144Shrs
725224144Shrs		printf("\tInputs\n"
726224144Shrs		    "\t RA: %" PRIu64 " (normal)\n"
727224144Shrs		    "\t RA: %" PRIu64 " (inconsistent)\n"
728224144Shrs		    "\t RS: %" PRIu64 "\n",
729224006Shrs		    ifi_s->ifi_rainput,
730224144Shrs		    ifi_s->ifi_rainconsistent,
731224006Shrs		    ifi_s->ifi_rsinput);
732224006Shrs
733224006Shrs		printf("\n");
734224006Shrs
735224144Shrs#if 0	/* Not implemented yet */
736224006Shrs		printf("\tReceived RAs:\n");
737224144Shrs#endif
738224006Shrs	}
739224006Shrs
740224006Shrs	return (0);
741224006Shrs}
742224006Shrs
743224006Shrsstatic int
744224006Shrsaction_show_rtinfo(struct rtinfo *rti)
745224006Shrs{
746224006Shrs	char ntopbuf[INET6_ADDRSTRLEN];
747224006Shrs	char ssbuf[SSBUFLEN];
748224006Shrs
749224006Shrs	printf("\t  %s/%d (pref: %s, ltime: %s)\n",
750224006Shrs	    inet_ntop(AF_INET6, &rti->rti_prefix,
751224006Shrs		ntopbuf, sizeof(ntopbuf)),
752224006Shrs	    rti->rti_prefixlen,
753224006Shrs	    rtpref_str[0xff & (rti->rti_rtpref >> 3)],
754224006Shrs	    (rti->rti_ltime == ND6_INFINITE_LIFETIME) ?
755224006Shrs	    "infinity" : sec2str(rti->rti_ltime, ssbuf));
756224006Shrs
757224006Shrs	return (0);
758224006Shrs}
759224006Shrs
760224006Shrsstatic int
761224006Shrsaction_show_prefix(struct prefix *pfx)
762224006Shrs{
763224006Shrs	char ntopbuf[INET6_ADDRSTRLEN];
764224006Shrs	char ssbuf[SSBUFLEN];
765253970Shrs	struct timespec now;
766224006Shrs
767253970Shrs	clock_gettime(CLOCK_MONOTONIC_FAST, &now);
768224006Shrs	printf("\t  %s/%d", inet_ntop(AF_INET6, &pfx->pfx_prefix,
769224006Shrs		ntopbuf, sizeof(ntopbuf)), pfx->pfx_prefixlen);
770224006Shrs
771224006Shrs	printf(" (");
772224006Shrs	switch (pfx->pfx_origin) {
773224006Shrs	case PREFIX_FROM_KERNEL:
774224006Shrs		printf("KERNEL");
775224006Shrs		break;
776224006Shrs	case PREFIX_FROM_CONFIG:
777224006Shrs		printf("CONFIG");
778224006Shrs		break;
779224006Shrs	case PREFIX_FROM_DYNAMIC:
780224006Shrs		printf("DYNAMIC");
781224006Shrs		break;
782224006Shrs	}
783224006Shrs
784224006Shrs	printf(",");
785224006Shrs
786224006Shrs	printf(" vltime=%s",
787224006Shrs	    (pfx->pfx_validlifetime == ND6_INFINITE_LIFETIME) ?
788224006Shrs	    "infinity" : sec2str(pfx->pfx_validlifetime, ssbuf));
789224006Shrs
790224006Shrs	if (pfx->pfx_vltimeexpire > 0)
791224006Shrs		printf("(expire: %s)",
792224006Shrs		    ((long)pfx->pfx_vltimeexpire > now.tv_sec) ?
793224006Shrs		    sec2str(pfx->pfx_vltimeexpire - now.tv_sec, ssbuf) :
794224006Shrs		    "0");
795224006Shrs
796224006Shrs	printf(",");
797224006Shrs
798224006Shrs	printf(" pltime=%s",
799224006Shrs	    (pfx->pfx_preflifetime == ND6_INFINITE_LIFETIME) ?
800224006Shrs	    "infinity" : sec2str(pfx->pfx_preflifetime, ssbuf));
801224006Shrs
802224006Shrs	if (pfx->pfx_pltimeexpire > 0)
803224006Shrs		printf("(expire %s)",
804224006Shrs		    ((long)pfx->pfx_pltimeexpire > now.tv_sec) ?
805224006Shrs		    sec2str(pfx->pfx_pltimeexpire - now.tv_sec, ssbuf) :
806224006Shrs		    "0");
807224006Shrs
808224006Shrs	printf(",");
809224006Shrs
810224006Shrs	printf(" flags=");
811224006Shrs	if (pfx->pfx_onlinkflg || pfx->pfx_autoconfflg) {
812224006Shrs		printf("%s", pfx->pfx_onlinkflg ? "L" : "");
813224006Shrs		printf("%s", pfx->pfx_autoconfflg ? "A" : "");
814224006Shrs	} else
815224006Shrs		printf("<none>");
816224006Shrs
817224006Shrs	if (pfx->pfx_timer) {
818253970Shrs		struct timespec *rest;
819224006Shrs
820224006Shrs		rest = rtadvd_timer_rest(pfx->pfx_timer);
821224006Shrs		if (rest) { /* XXX: what if not? */
822224006Shrs			printf(" expire=%s", sec2str(rest->tv_sec, ssbuf));
823224006Shrs		}
824224006Shrs	}
825224006Shrs
826224006Shrs	printf(")\n");
827224006Shrs
828224006Shrs	return (0);
829224006Shrs}
830224006Shrs
831224006Shrsstatic int
832224006Shrsaction_show_rdnss(void *msg)
833224006Shrs{
834224006Shrs	struct rdnss *rdn;
835224006Shrs	struct rdnss_addr *rda;
836224144Shrs	uint16_t *rdn_cnt;
837224144Shrs	uint16_t *rda_cnt;
838224006Shrs	int i;
839224006Shrs	int j;
840224006Shrs	char *p;
841224144Shrs	uint32_t	ltime;
842224006Shrs	char ntopbuf[INET6_ADDRSTRLEN];
843224006Shrs	char ssbuf[SSBUFLEN];
844224006Shrs
845224006Shrs	p = msg;
846224144Shrs	rdn_cnt = (uint16_t *)p;
847224006Shrs	p += sizeof(*rdn_cnt);
848224006Shrs
849224006Shrs	if (*rdn_cnt > 0) {
850224006Shrs		for (i = 0; i < *rdn_cnt; i++) {
851224006Shrs			rdn = (struct rdnss *)p;
852224006Shrs			ltime = rdn->rd_ltime;
853224006Shrs			p += sizeof(*rdn);
854224006Shrs
855224144Shrs			rda_cnt = (uint16_t *)p;
856224006Shrs			p += sizeof(*rda_cnt);
857224006Shrs			if (*rda_cnt > 0)
858224006Shrs				for (j = 0; j < *rda_cnt; j++) {
859224006Shrs					rda = (struct rdnss_addr *)p;
860224006Shrs					printf("\t  %s (ltime=%s)\n",
861224006Shrs					    inet_ntop(AF_INET6,
862224006Shrs						&rda->ra_dns,
863224006Shrs						ntopbuf,
864224006Shrs						sizeof(ntopbuf)),
865224006Shrs					    sec2str(ltime, ssbuf));
866224006Shrs					p += sizeof(*rda);
867224006Shrs				}
868224006Shrs		}
869224006Shrs	}
870224006Shrs
871224006Shrs	return (0);
872224006Shrs}
873224006Shrs
874224006Shrsstatic int
875224006Shrsaction_show_dnssl(void *msg)
876224006Shrs{
877224006Shrs	struct dnssl *dns;
878224006Shrs	struct dnssl_addr *dna;
879224144Shrs	uint16_t *dns_cnt;
880224144Shrs	uint16_t *dna_cnt;
881224006Shrs	int i;
882224006Shrs	int j;
883224006Shrs	char *p;
884224144Shrs	uint32_t ltime;
885224006Shrs	char hbuf[NI_MAXHOST];
886224006Shrs	char ssbuf[SSBUFLEN];
887224006Shrs
888224006Shrs	p = msg;
889224144Shrs	dns_cnt = (uint16_t *)p;
890224006Shrs	p += sizeof(*dns_cnt);
891224006Shrs
892224006Shrs	if (*dns_cnt > 0) {
893224006Shrs		for (i = 0; i < *dns_cnt; i++) {
894224006Shrs			dns = (struct dnssl *)p;
895224006Shrs			ltime = dns->dn_ltime;
896224006Shrs			p += sizeof(*dns);
897224006Shrs
898224144Shrs			dna_cnt = (uint16_t *)p;
899224006Shrs			p += sizeof(*dna_cnt);
900224006Shrs			if (*dna_cnt > 0)
901224006Shrs				for (j = 0; j < *dna_cnt; j++) {
902224006Shrs					dna = (struct dnssl_addr *)p;
903224006Shrs					dname_labeldec(hbuf, sizeof(hbuf),
904224006Shrs					    dna->da_dom);
905224006Shrs					printf("\t  %s (ltime=%s)\n",
906224006Shrs					    hbuf, sec2str(ltime, ssbuf));
907224006Shrs					p += sizeof(*dna);
908224006Shrs				}
909224006Shrs		}
910224006Shrs	}
911224006Shrs
912224006Shrs	return (0);
913224006Shrs}
914224006Shrs
915224006Shrs/* Decode domain name label encoding in RFC 1035 Section 3.1 */
916224006Shrsstatic size_t
917224006Shrsdname_labeldec(char *dst, size_t dlen, const char *src)
918224006Shrs{
919224006Shrs	size_t len;
920224006Shrs	const char *src_origin;
921224006Shrs	const char *src_last;
922224006Shrs	const char *dst_origin;
923224006Shrs
924224006Shrs	src_origin = src;
925224006Shrs	src_last = strchr(src, '\0');
926224006Shrs	dst_origin = dst;
927224006Shrs	memset(dst, '\0', dlen);
928224006Shrs	while (src && (len = (uint8_t)(*src++) & 0x3f) &&
929224006Shrs	    (src + len) <= src_last) {
930224006Shrs		if (dst != dst_origin)
931224006Shrs			*dst++ = '.';
932224006Shrs		mysyslog(LOG_DEBUG, "<%s> labellen = %zd", __func__, len);
933224006Shrs		memcpy(dst, src, len);
934224006Shrs		src += len;
935224006Shrs		dst += len;
936224006Shrs	}
937224006Shrs	*dst = '\0';
938224006Shrs
939224006Shrs	return (src - src_origin);
940224006Shrs}
941