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