]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/rtadvctl/rtadvctl.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / rtadvctl / rtadvctl.c
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 <inttypes.h>
50 #include <netdb.h>
51 #include <unistd.h>
52 #include <string.h>
53 #include <stdarg.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <stdarg.h>
57 #include <syslog.h>
58 #include <time.h>
59 #include <err.h>
60
61 #include "pathnames.h"
62 #include "rtadvd.h"
63 #include "if.h"
64 #include "timer_subr.h"
65 #include "timer.h"
66 #include "control.h"
67 #include "control_client.h"
68
69 #define RA_IFSTATUS_INACTIVE    0
70 #define RA_IFSTATUS_RA_RECV     1
71 #define RA_IFSTATUS_RA_SEND     2
72
73 static int vflag = LOG_ERR;
74
75 static void     usage(void);
76
77 static int      action_propset(char *);
78 static int      action_propget(char *, struct ctrl_msg_pl *);
79 static int      action_plgeneric(int, char *, char *);
80
81 static int      action_enable(int, char **);
82 static int      action_disable(int, char **);
83 static int      action_reload(int, char **);
84 static int      action_echo(int, char **);
85 static int      action_version(int, char **);
86 static int      action_shutdown(int, char **);
87
88 static int      action_show(int, char **);
89 static int      action_show_prefix(struct prefix *);
90 static int      action_show_rtinfo(struct rtinfo *);
91 static int      action_show_rdnss(void *);
92 static int      action_show_dnssl(void *);
93
94 static int      csock_client_open(struct sockinfo *);
95 static size_t   dname_labeldec(char *, size_t, const char *);
96 static void     mysyslog(int, const char *, ...);
97
98 static const char *rtpref_str[] = {
99         "medium",               /* 00 */
100         "high",                 /* 01 */
101         "rsv",                  /* 10 */
102         "low"                   /* 11 */
103 };
104
105 static struct dispatch_table {
106         const char      *dt_comm;
107         int (*dt_act)(int, char **);
108 } dtable[] = {
109         { "show", action_show },
110         { "reload", action_reload },
111         { "shutdown", action_shutdown },
112         { "enable", action_enable },
113         { "disable", action_disable },
114         { NULL, NULL },
115         { "echo", action_echo },
116         { "version", action_version },
117         { NULL, NULL },
118 };
119
120 static char errmsgbuf[1024];
121 static char *errmsg = NULL;
122
123 static void
124 mysyslog(int priority, const char * restrict fmt, ...)
125 {
126         va_list ap;
127
128         if (vflag >= priority) {
129                 va_start(ap, fmt);
130                 vfprintf(stderr, fmt, ap);
131                 fprintf(stderr, "\n");
132                 va_end(ap);
133         }
134 }
135
136 static void
137 usage(void)
138 {
139         int i;
140
141         for (i = 0; (size_t)i < sizeof(dtable)/sizeof(dtable[0]); i++) {
142                 if (dtable[i].dt_comm == NULL)
143                         break;
144                 printf("%s\n", dtable[i].dt_comm);
145         }
146
147         exit(1);
148 }
149
150 int
151 main(int argc, char *argv[])
152 {
153         int i;
154         int ch;
155         int (*action)(int, char **) = NULL;
156         int error;
157
158         while ((ch = getopt(argc, argv, "Dv")) != -1) {
159                 switch (ch) {
160                 case 'D':
161                         vflag = LOG_DEBUG;
162                         break;
163                 case 'v':
164                         vflag++;
165                         break;
166                 default:
167                         usage();
168                 }
169         }
170         argc -= optind;
171         argv += optind;
172
173         if (argc == 0)
174                 usage();
175
176         for (i = 0; (size_t)i < sizeof(dtable)/sizeof(dtable[0]); i++) {
177                 if (dtable[i].dt_comm == NULL ||
178                     strcmp(dtable[i].dt_comm, argv[0]) == 0) {
179                         action = dtable[i].dt_act;
180                         break;
181                 }
182         }
183
184         if (action == NULL)
185                 usage();
186
187         error = (dtable[i].dt_act)(--argc, ++argv);
188         if (error) {
189                 fprintf(stderr, "%s failed", dtable[i].dt_comm);
190                 if (errmsg != NULL)
191                         fprintf(stderr, ": %s", errmsg);
192                 fprintf(stderr, ".\n");
193         }
194
195         return (error);
196 }
197
198 static int
199 csock_client_open(struct sockinfo *s)
200 {
201         struct sockaddr_un sun;
202
203         if ((s->si_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
204                 err(1, "cannot open control socket.");
205
206         memset(&sun, 0, sizeof(sun));
207         sun.sun_family = AF_UNIX;
208         sun.sun_len = sizeof(sun);
209         strlcpy(sun.sun_path, s->si_name, sizeof(sun.sun_path));
210
211         if (connect(s->si_fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
212                 err(1, "connect: %s", s->si_name);
213
214         mysyslog(LOG_DEBUG,
215             "<%s> connected to %s", __func__, sun.sun_path);
216
217         return (0);
218 }
219
220 static int
221 action_plgeneric(int action, char *plstr, char *buf)
222 {
223         struct ctrl_msg_hdr *cm;
224         struct ctrl_msg_pl cp;
225         struct sockinfo *s;
226         char *msg;
227         char *p;
228         char *q;
229
230         s = &ctrlsock;
231         csock_client_open(s);
232
233         cm = (struct ctrl_msg_hdr *)buf;
234         msg = (char *)buf + sizeof(*cm);
235
236         cm->cm_version = CM_VERSION;
237         cm->cm_type = action;
238         cm->cm_len = sizeof(*cm);
239
240         if (plstr != NULL) {
241                 memset(&cp, 0, sizeof(cp));
242                 p = strchr(plstr, ':');
243                 q = strchr(plstr, '=');
244                 if (p != NULL && q != NULL && p > q)
245                         return (1);
246
247                 if (p == NULL) {                /* No : */
248                         cp.cp_ifname = NULL;
249                         cp.cp_key = plstr;
250                 } else if  (p == plstr) {       /* empty */
251                         cp.cp_ifname = NULL;
252                         cp.cp_key = plstr + 1;
253                 } else {
254                         *p++ = '\0';
255                         cp.cp_ifname = plstr;
256                         cp.cp_key = p;
257                 }
258                 if (q == NULL)
259                         cp.cp_val = NULL;
260                 else {
261                         *q++ = '\0';
262                         cp.cp_val = q;
263                 }
264                 cm->cm_len += cm_pl2bin(msg, &cp);
265
266                 mysyslog(LOG_DEBUG, "<%s> key=%s, val_len=%d, ifname=%s",
267                     __func__,cp.cp_key, cp.cp_val_len, cp.cp_ifname);
268         }
269
270         return (cm_handler_client(s->si_fd, CM_STATE_MSG_DISPATCH, buf));
271 }
272
273 static int
274 action_propget(char *argv, struct ctrl_msg_pl *cp)
275 {
276         int error;
277         struct ctrl_msg_hdr *cm;
278         char buf[CM_MSG_MAXLEN];
279         char *msg;
280
281         memset(cp, 0, sizeof(*cp));
282         cm = (struct ctrl_msg_hdr *)buf;
283         msg = (char *)buf + sizeof(*cm);
284
285         error = action_plgeneric(CM_TYPE_REQ_GET_PROP, argv, buf);
286         if (error || cm->cm_len <= sizeof(*cm))
287                 return (1);
288
289         cm_bin2pl(msg, cp);
290         mysyslog(LOG_DEBUG, "<%s> type=%d, len=%d",
291             __func__, cm->cm_type, cm->cm_len);
292         mysyslog(LOG_DEBUG, "<%s> key=%s, val_len=%d, ifname=%s",
293             __func__,cp->cp_key, cp->cp_val_len, cp->cp_ifname);
294
295         return (0);
296 }
297
298 static int
299 action_propset(char *argv)
300 {
301         char buf[CM_MSG_MAXLEN];
302
303         return (action_plgeneric(CM_TYPE_REQ_SET_PROP, argv, buf));
304 }
305
306 static int
307 action_disable(int argc, char **argv)
308 {
309         char *action_argv;
310         char argv_disable[IFNAMSIZ + sizeof(":disable=")];
311         int i;
312         int error;
313
314         if (argc < 1)
315                 return (1);
316
317         error = 0;
318         for (i = 0; i < argc; i++) {
319                 sprintf(argv_disable, "%s:disable=", argv[i]);
320                 action_argv = argv_disable;
321                 error += action_propset(action_argv);
322         }
323
324         return (error);
325 }
326
327 static int
328 action_enable(int argc, char **argv)
329 {
330         char *action_argv;
331         char argv_enable[IFNAMSIZ + sizeof(":enable=")];
332         int i;
333         int error;
334
335         if (argc < 1)
336                 return (1);
337
338         error = 0;
339         for (i = 0; i < argc; i++) {
340                 sprintf(argv_enable, "%s:enable=", argv[i]);
341                 action_argv = argv_enable;
342                 error += action_propset(action_argv);
343         }
344
345         return (error);
346 }
347
348 static int
349 action_reload(int argc, char **argv)
350 {
351         char *action_argv;
352         char argv_reload[IFNAMSIZ + sizeof(":reload=")];
353         int i;
354         int error;
355
356         if (argc == 0) {
357                 action_argv = strdup(":reload=");
358                 return (action_propset(action_argv));
359         }
360
361         error = 0;
362         for (i = 0; i < argc; i++) {
363                 sprintf(argv_reload, "%s:reload=", argv[i]);
364                 action_argv = argv_reload;
365                 error += action_propset(action_argv);
366         }
367
368         return (error);
369 }
370
371 static int
372 action_echo(int argc __unused, char **argv __unused)
373 {
374         char *action_argv;
375
376         action_argv = strdup("echo");
377         return (action_propset(action_argv));
378 }
379
380 static int
381 action_shutdown(int argc __unused, char **argv __unused)
382 {
383         char *action_argv;
384
385         action_argv = strdup("shutdown");
386         return (action_propset(action_argv));
387 }
388
389 /* XXX */
390 static int
391 action_version(int argc __unused, char **argv __unused)
392 {
393         char *action_argv;
394         struct ctrl_msg_pl cp;
395         int error;
396
397         action_argv = strdup(":version=");
398         error = action_propget(action_argv, &cp);
399         if (error)
400                 return (error);
401
402         printf("version=%s\n", cp.cp_val);
403         return (0);
404 }
405
406 static int
407 action_show(int argc, char **argv)
408 {
409         char *action_argv;
410         char argv_ifilist[sizeof(":ifilist=")] = ":ifilist=";
411         char argv_ifi[IFNAMSIZ + sizeof(":ifi=")];
412         char argv_rai[IFNAMSIZ + sizeof(":rai=")];
413         char argv_rti[IFNAMSIZ + sizeof(":rti=")];
414         char argv_pfx[IFNAMSIZ + sizeof(":pfx=")];
415         char argv_ifi_ra_timer[IFNAMSIZ + sizeof(":ifi_ra_timer=")];
416         char argv_rdnss[IFNAMSIZ + sizeof(":rdnss=")];
417         char argv_dnssl[IFNAMSIZ + sizeof(":dnssl=")];
418         char ssbuf[SSBUFLEN];
419
420         struct timespec now, ts0, ts;
421         struct ctrl_msg_pl cp;
422         struct ifinfo *ifi;
423         TAILQ_HEAD(, ifinfo) ifl = TAILQ_HEAD_INITIALIZER(ifl);
424         char *endp;
425         char *p;
426         int error;
427         int i;
428         int len;
429
430         if (argc == 0) {
431                 action_argv = argv_ifilist;
432                 error = action_propget(action_argv, &cp);
433                 if (error)
434                         return (error);
435
436                 p = cp.cp_val;
437                 endp = p + cp.cp_val_len;
438                 while (p < endp) {
439                         ifi = malloc(sizeof(*ifi));
440                         if (ifi == NULL)
441                                 return (1);
442                         memset(ifi, 0, sizeof(*ifi));
443
444                         strcpy(ifi->ifi_ifname, p);
445                         ifi->ifi_ifindex = if_nametoindex(ifi->ifi_ifname);
446                         TAILQ_INSERT_TAIL(&ifl, ifi, ifi_next);
447                         p += strlen(ifi->ifi_ifname) + 1;
448                 }
449         } else {
450                 for (i = 0; i < argc; i++) {
451                         ifi = malloc(sizeof(*ifi));
452                         if (ifi == NULL)
453                                 return (1);
454                         memset(ifi, 0, sizeof(*ifi));
455
456                         strcpy(ifi->ifi_ifname, argv[i]);
457                         ifi->ifi_ifindex = if_nametoindex(ifi->ifi_ifname);
458                         if (ifi->ifi_ifindex == 0) {
459                                 sprintf(errmsgbuf, "invalid interface %s",
460                                     ifi->ifi_ifname);
461                                 errmsg = errmsgbuf;
462                                 return (1);
463                         }
464
465                         TAILQ_INSERT_TAIL(&ifl, ifi, ifi_next);
466                 }
467         }
468
469         clock_gettime(CLOCK_REALTIME_FAST, &now);
470         clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
471         TS_SUB(&now, &ts, &ts0);
472
473         TAILQ_FOREACH(ifi, &ifl, ifi_next) {
474                 struct ifinfo *ifi_s;
475                 struct rtadvd_timer *rat;
476                 struct rainfo *rai;
477                 struct rtinfo *rti;
478                 struct prefix *pfx;
479                 int c;
480                 int ra_ifstatus;
481
482                 sprintf(argv_ifi, "%s:ifi=", ifi->ifi_ifname);
483                 action_argv = argv_ifi;
484                 error = action_propget(action_argv, &cp);
485                 if (error)
486                         return (error);
487                 ifi_s = (struct ifinfo *)cp.cp_val;
488
489                 if (!(ifi_s->ifi_persist) && vflag < LOG_NOTICE)
490                         continue;
491
492                 printf("%s: flags=<", ifi->ifi_ifname);
493
494                 c = 0;
495                 if (ifi_s->ifi_ifindex == 0)
496                         c += printf("NONEXISTENT");
497                 else
498                         c += printf("%s", (ifi_s->ifi_flags & IFF_UP) ?
499                             "UP" : "DOWN");
500                 switch (ifi_s->ifi_state) {
501                 case IFI_STATE_CONFIGURED:
502                         c += printf("%s%s", (c) ? "," : "", "CONFIGURED");
503                         break;
504                 case IFI_STATE_TRANSITIVE:
505                         c += printf("%s%s", (c) ? "," : "", "TRANSITIVE");
506                         break;
507                 }
508                 if (ifi_s->ifi_persist)
509                         c += printf("%s%s", (c) ? "," : "", "PERSIST");
510                 printf(">");
511
512                 ra_ifstatus = RA_IFSTATUS_INACTIVE;
513                 if ((ifi_s->ifi_flags & IFF_UP) &&
514                     ((ifi_s->ifi_state == IFI_STATE_CONFIGURED) ||
515                         (ifi_s->ifi_state == IFI_STATE_TRANSITIVE))) {
516 #if (__FreeBSD_version < 900000)
517                         /*
518                          * RA_RECV: !ip6.forwarding && ip6.accept_rtadv
519                          * RA_SEND: ip6.forwarding
520                          */
521                         if (getinet6sysctl(IPV6CTL_FORWARDING) == 0) {
522                                 if (getinet6sysctl(IPV6CTL_ACCEPT_RTADV))
523                                         ra_ifstatus = RA_IFSTATUS_RA_RECV;
524                                 else
525                                         ra_ifstatus = RA_IFSTATUS_INACTIVE;
526                         } else
527                                 ra_ifstatus = RA_IFSTATUS_RA_SEND;
528 #else
529                         /*
530                          * RA_RECV: ND6_IFF_ACCEPT_RTADV
531                          * RA_SEND: ip6.forwarding
532                          */
533                         if (ifi_s->ifi_nd_flags & ND6_IFF_ACCEPT_RTADV)
534                                 ra_ifstatus = RA_IFSTATUS_RA_RECV;
535                         else if (getinet6sysctl(IPV6CTL_FORWARDING))
536                                 ra_ifstatus = RA_IFSTATUS_RA_SEND;
537                         else
538                                 ra_ifstatus = RA_IFSTATUS_INACTIVE;
539 #endif
540                 }
541
542                 c = 0;
543                 printf(" status=<");
544                 if (ra_ifstatus == RA_IFSTATUS_INACTIVE)
545                         printf("%s%s", (c) ? "," : "", "INACTIVE");
546                 else if (ra_ifstatus == RA_IFSTATUS_RA_RECV)
547                         printf("%s%s", (c) ? "," : "", "RA_RECV");
548                 else if (ra_ifstatus == RA_IFSTATUS_RA_SEND)
549                         printf("%s%s", (c) ? "," : "", "RA_SEND");
550                 printf("> ");
551
552                 switch (ifi_s->ifi_state) {
553                 case IFI_STATE_CONFIGURED:
554                 case IFI_STATE_TRANSITIVE:
555                         break;
556                 default:
557                         printf("\n");
558                         continue;
559                 }
560
561                 printf("mtu %d\n", ifi_s->ifi_phymtu);
562
563                 sprintf(argv_rai, "%s:rai=", ifi->ifi_ifname);
564                 action_argv = argv_rai;
565
566                 error = action_propget(action_argv, &cp);
567                 if (error)
568                         continue;
569
570                 rai = (struct rainfo *)cp.cp_val;
571
572                 printf("\tDefaultLifetime: %s",
573                     sec2str(rai->rai_lifetime, ssbuf));
574                 if (ra_ifstatus != RA_IFSTATUS_RA_SEND &&
575                     rai->rai_lifetime == 0)
576                         printf(" (RAs will be sent with zero lifetime)");
577
578                 printf("\n");
579
580                 printf("\tMinAdvInterval/MaxAdvInterval: ");
581                 printf("%s/", sec2str(rai->rai_mininterval, ssbuf));
582                 printf("%s\n", sec2str(rai->rai_maxinterval, ssbuf));
583                 if (rai->rai_linkmtu)
584                         printf("\tAdvLinkMTU: %d", rai->rai_linkmtu);
585                 else
586                         printf("\tAdvLinkMTU: <none>");
587
588                 printf(", ");
589
590                 printf("Flags: ");
591                 if (rai->rai_managedflg || rai->rai_otherflg) {
592                         printf("%s", rai->rai_managedflg ? "M" : "");
593                         printf("%s", rai->rai_otherflg ? "O" : "");
594                 } else
595                         printf("<none>");
596
597                 printf(", ");
598
599                 printf("Preference: %s\n",
600                     rtpref_str[(rai->rai_rtpref >> 3) & 0xff]);
601
602                 printf("\tReachableTime: %s, ",
603                     sec2str(rai->rai_reachabletime, ssbuf));
604                 printf("RetransTimer: %s, "
605                     "CurHopLimit: %d\n",
606                     sec2str(rai->rai_retranstimer, ssbuf),
607                     rai->rai_hoplimit);
608                 printf("\tAdvIfPrefixes: %s\n",
609                     rai->rai_advifprefix ? "yes" : "no");
610
611                 /* RA timer */
612                 rat = NULL;
613                 if (ifi_s->ifi_ra_timer != NULL) {
614                         sprintf(argv_ifi_ra_timer, "%s:ifi_ra_timer=",
615                             ifi->ifi_ifname);
616                         action_argv = argv_ifi_ra_timer;
617
618                         error = action_propget(action_argv, &cp);
619                         if (error)
620                                 return (error);
621
622                         rat = (struct rtadvd_timer *)cp.cp_val;
623                 }
624                 printf("\tNext RA send: ");
625                 if (rat == NULL)
626                         printf("never\n");
627                 else {
628                         ts.tv_sec = rat->rat_tm.tv_sec + ts0.tv_sec;
629                         printf("%s", ctime(&ts.tv_sec));
630                 }
631                 printf("\tLast RA send: ");
632                 if (ifi_s->ifi_ra_lastsent.tv_sec == 0)
633                         printf("never\n");
634                 else {
635                         ts.tv_sec = ifi_s->ifi_ra_lastsent.tv_sec + ts0.tv_sec;
636                         printf("%s", ctime(&ts.tv_sec));
637                 }
638                 if (rai->rai_clockskew)
639                         printf("\tClock skew: %" PRIu16 "sec\n",
640                             rai->rai_clockskew);
641
642                 if (vflag < LOG_WARNING)
643                         continue;
644
645                 /* route information */
646                 sprintf(argv_rti, "%s:rti=", ifi->ifi_ifname);
647                 action_argv = argv_rti;
648                 error = action_propget(action_argv, &cp);
649                 if (error)
650                         return (error);
651
652                 rti = (struct rtinfo *)cp.cp_val;
653                 len = cp.cp_val_len / sizeof(*rti);
654                 if (len > 0) {
655                         printf("\tRoute Info:\n");
656
657                         for (i = 0; i < len; i++)
658                                 action_show_rtinfo(&rti[i]);
659                 }
660
661                 /* prefix information */
662                 sprintf(argv_pfx, "%s:pfx=", ifi->ifi_ifname);
663                 action_argv = argv_pfx;
664
665                 error = action_propget(action_argv, &cp);
666                 if (error)
667                         continue;
668
669                 pfx = (struct prefix *)cp.cp_val;
670                 len = cp.cp_val_len / sizeof(*pfx);
671
672                 if (len > 0) {
673                         printf("\tPrefixes (%d):\n", len);
674
675                         for (i = 0; i < len; i++)
676                                 action_show_prefix(&pfx[i]);
677                 }
678
679                 /* RDNSS information */
680                 sprintf(argv_rdnss, "%s:rdnss=", ifi->ifi_ifname);
681                 action_argv = argv_rdnss;
682
683                 error = action_propget(action_argv, &cp);
684                 if (error)
685                         continue;
686
687                 len = *((uint16_t *)cp.cp_val);
688
689                 if (len > 0) {
690                         printf("\tRDNSS entries:\n");
691                         action_show_rdnss(cp.cp_val);
692                 }
693
694                 /* DNSSL information */
695                 sprintf(argv_dnssl, "%s:dnssl=", ifi->ifi_ifname);
696                 action_argv = argv_dnssl;
697
698                 error = action_propget(action_argv, &cp);
699                 if (error)
700                         continue;
701
702                 len = *((uint16_t *)cp.cp_val);
703
704                 if (len > 0) {
705                         printf("\tDNSSL entries:\n");
706                         action_show_dnssl(cp.cp_val);
707                 }
708
709                 if (vflag < LOG_NOTICE)
710                         continue;
711
712                 printf("\n");
713
714                 printf("\tCounters\n"
715                     "\t RA burst counts: %" PRIu16 " (interval: %s)\n"
716                     "\t RS wait counts: %" PRIu16 "\n",
717                     ifi_s->ifi_burstcount,
718                     sec2str(ifi_s->ifi_burstinterval, ssbuf),
719                     ifi_s->ifi_rs_waitcount);
720
721                 printf("\tOutputs\n"
722                     "\t RA: %" PRIu64 "\n", ifi_s->ifi_raoutput);
723
724                 printf("\tInputs\n"
725                     "\t RA: %" PRIu64 " (normal)\n"
726                     "\t RA: %" PRIu64 " (inconsistent)\n"
727                     "\t RS: %" PRIu64 "\n",
728                     ifi_s->ifi_rainput,
729                     ifi_s->ifi_rainconsistent,
730                     ifi_s->ifi_rsinput);
731
732                 printf("\n");
733
734 #if 0   /* Not implemented yet */
735                 printf("\tReceived RAs:\n");
736 #endif
737         }
738
739         return (0);
740 }
741
742 static int
743 action_show_rtinfo(struct rtinfo *rti)
744 {
745         char ntopbuf[INET6_ADDRSTRLEN];
746         char ssbuf[SSBUFLEN];
747
748         printf("\t  %s/%d (pref: %s, ltime: %s)\n",
749             inet_ntop(AF_INET6, &rti->rti_prefix,
750                 ntopbuf, sizeof(ntopbuf)),
751             rti->rti_prefixlen,
752             rtpref_str[0xff & (rti->rti_rtpref >> 3)],
753             (rti->rti_ltime == ND6_INFINITE_LIFETIME) ?
754             "infinity" : sec2str(rti->rti_ltime, ssbuf));
755
756         return (0);
757 }
758
759 static int
760 action_show_prefix(struct prefix *pfx)
761 {
762         char ntopbuf[INET6_ADDRSTRLEN];
763         char ssbuf[SSBUFLEN];
764         struct timespec now;
765
766         clock_gettime(CLOCK_MONOTONIC_FAST, &now);
767         printf("\t  %s/%d", inet_ntop(AF_INET6, &pfx->pfx_prefix,
768                 ntopbuf, sizeof(ntopbuf)), pfx->pfx_prefixlen);
769
770         printf(" (");
771         switch (pfx->pfx_origin) {
772         case PREFIX_FROM_KERNEL:
773                 printf("KERNEL");
774                 break;
775         case PREFIX_FROM_CONFIG:
776                 printf("CONFIG");
777                 break;
778         case PREFIX_FROM_DYNAMIC:
779                 printf("DYNAMIC");
780                 break;
781         }
782
783         printf(",");
784
785         printf(" vltime=%s",
786             (pfx->pfx_validlifetime == ND6_INFINITE_LIFETIME) ?
787             "infinity" : sec2str(pfx->pfx_validlifetime, ssbuf));
788
789         if (pfx->pfx_vltimeexpire > 0)
790                 printf("(expire: %s)",
791                     ((long)pfx->pfx_vltimeexpire > now.tv_sec) ?
792                     sec2str(pfx->pfx_vltimeexpire - now.tv_sec, ssbuf) :
793                     "0");
794
795         printf(",");
796
797         printf(" pltime=%s",
798             (pfx->pfx_preflifetime == ND6_INFINITE_LIFETIME) ?
799             "infinity" : sec2str(pfx->pfx_preflifetime, ssbuf));
800
801         if (pfx->pfx_pltimeexpire > 0)
802                 printf("(expire %s)",
803                     ((long)pfx->pfx_pltimeexpire > now.tv_sec) ?
804                     sec2str(pfx->pfx_pltimeexpire - now.tv_sec, ssbuf) :
805                     "0");
806
807         printf(",");
808
809         printf(" flags=");
810         if (pfx->pfx_onlinkflg || pfx->pfx_autoconfflg) {
811                 printf("%s", pfx->pfx_onlinkflg ? "L" : "");
812                 printf("%s", pfx->pfx_autoconfflg ? "A" : "");
813         } else
814                 printf("<none>");
815
816         if (pfx->pfx_timer) {
817                 struct timespec *rest;
818
819                 rest = rtadvd_timer_rest(pfx->pfx_timer);
820                 if (rest) { /* XXX: what if not? */
821                         printf(" expire=%s", sec2str(rest->tv_sec, ssbuf));
822                 }
823         }
824
825         printf(")\n");
826
827         return (0);
828 }
829
830 static int
831 action_show_rdnss(void *msg)
832 {
833         struct rdnss *rdn;
834         struct rdnss_addr *rda;
835         uint16_t *rdn_cnt;
836         uint16_t *rda_cnt;
837         int i;
838         int j;
839         char *p;
840         uint32_t        ltime;
841         char ntopbuf[INET6_ADDRSTRLEN];
842         char ssbuf[SSBUFLEN];
843
844         p = msg;
845         rdn_cnt = (uint16_t *)p;
846         p += sizeof(*rdn_cnt);
847
848         if (*rdn_cnt > 0) {
849                 for (i = 0; i < *rdn_cnt; i++) {
850                         rdn = (struct rdnss *)p;
851                         ltime = rdn->rd_ltime;
852                         p += sizeof(*rdn);
853
854                         rda_cnt = (uint16_t *)p;
855                         p += sizeof(*rda_cnt);
856                         if (*rda_cnt > 0)
857                                 for (j = 0; j < *rda_cnt; j++) {
858                                         rda = (struct rdnss_addr *)p;
859                                         printf("\t  %s (ltime=%s)\n",
860                                             inet_ntop(AF_INET6,
861                                                 &rda->ra_dns,
862                                                 ntopbuf,
863                                                 sizeof(ntopbuf)),
864                                             sec2str(ltime, ssbuf));
865                                         p += sizeof(*rda);
866                                 }
867                 }
868         }
869
870         return (0);
871 }
872
873 static int
874 action_show_dnssl(void *msg)
875 {
876         struct dnssl *dns;
877         struct dnssl_addr *dna;
878         uint16_t *dns_cnt;
879         uint16_t *dna_cnt;
880         int i;
881         int j;
882         char *p;
883         uint32_t ltime;
884         char hbuf[NI_MAXHOST];
885         char ssbuf[SSBUFLEN];
886
887         p = msg;
888         dns_cnt = (uint16_t *)p;
889         p += sizeof(*dns_cnt);
890
891         if (*dns_cnt > 0) {
892                 for (i = 0; i < *dns_cnt; i++) {
893                         dns = (struct dnssl *)p;
894                         ltime = dns->dn_ltime;
895                         p += sizeof(*dns);
896
897                         dna_cnt = (uint16_t *)p;
898                         p += sizeof(*dna_cnt);
899                         if (*dna_cnt > 0)
900                                 for (j = 0; j < *dna_cnt; j++) {
901                                         dna = (struct dnssl_addr *)p;
902                                         dname_labeldec(hbuf, sizeof(hbuf),
903                                             dna->da_dom);
904                                         printf("\t  %s (ltime=%s)\n",
905                                             hbuf, sec2str(ltime, ssbuf));
906                                         p += sizeof(*dna);
907                                 }
908                 }
909         }
910
911         return (0);
912 }
913
914 /* Decode domain name label encoding in RFC 1035 Section 3.1 */
915 static size_t
916 dname_labeldec(char *dst, size_t dlen, const char *src)
917 {
918         size_t len;
919         const char *src_origin;
920         const char *src_last;
921         const char *dst_origin;
922
923         src_origin = src;
924         src_last = strchr(src, '\0');
925         dst_origin = dst;
926         memset(dst, '\0', dlen);
927         while (src && (len = (uint8_t)(*src++) & 0x3f) &&
928             (src + len) <= src_last) {
929                 if (dst != dst_origin)
930                         *dst++ = '.';
931                 mysyslog(LOG_DEBUG, "<%s> labellen = %zd", __func__, len);
932                 memcpy(dst, src, len);
933                 src += len;
934                 dst += len;
935         }
936         *dst = '\0';
937
938         return (src - src_origin);
939 }