freebsd-dev/usr.sbin/rtadvctl/rtadvctl.c
Pedro F. Giffuni 1de7b4b805 various: general adoption of SPDX licensing ID tags.
Mainly focus on files that use BSD 2-Clause license, however the tool I
was using misidentified many licenses so this was mostly a manual - error
prone - task.

The Software Package Data Exchange (SPDX) group provides a specification
to make it easier for automated tools to detect and summarize well known
opensource licenses. We are gradually adopting the specification, noting
that the tags are considered only advisory and do not, in any way,
superceed or replace the license texts.

No functional change intended.
2017-11-27 15:37:16 +00:00

941 lines
21 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*
*/
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet6/in6_var.h>
#include <netinet6/nd6.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <inttypes.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <syslog.h>
#include <time.h>
#include <err.h>
#include "pathnames.h"
#include "rtadvd.h"
#include "if.h"
#include "timer_subr.h"
#include "timer.h"
#include "control.h"
#include "control_client.h"
#define RA_IFSTATUS_INACTIVE 0
#define RA_IFSTATUS_RA_RECV 1
#define RA_IFSTATUS_RA_SEND 2
static int vflag = LOG_ERR;
static void usage(void);
static int action_propset(char *);
static int action_propget(char *, struct ctrl_msg_pl *);
static int action_plgeneric(int, char *, char *);
static int action_enable(int, char **);
static int action_disable(int, char **);
static int action_reload(int, char **);
static int action_echo(int, char **);
static int action_version(int, char **);
static int action_shutdown(int, char **);
static int action_show(int, char **);
static int action_show_prefix(struct prefix *);
static int action_show_rtinfo(struct rtinfo *);
static int action_show_rdnss(void *);
static int action_show_dnssl(void *);
static int csock_client_open(struct sockinfo *);
static size_t dname_labeldec(char *, size_t, const char *);
static void mysyslog(int, const char *, ...);
static const char *rtpref_str[] = {
"medium", /* 00 */
"high", /* 01 */
"rsv", /* 10 */
"low" /* 11 */
};
static struct dispatch_table {
const char *dt_comm;
int (*dt_act)(int, char **);
} dtable[] = {
{ "show", action_show },
{ "reload", action_reload },
{ "shutdown", action_shutdown },
{ "enable", action_enable },
{ "disable", action_disable },
{ NULL, NULL },
{ "echo", action_echo },
{ "version", action_version },
{ NULL, NULL },
};
static char errmsgbuf[1024];
static char *errmsg = NULL;
static void
mysyslog(int priority, const char * restrict fmt, ...)
{
va_list ap;
if (vflag >= priority) {
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
}
}
static void
usage(void)
{
int i;
for (i = 0; (size_t)i < sizeof(dtable)/sizeof(dtable[0]); i++) {
if (dtable[i].dt_comm == NULL)
break;
printf("%s\n", dtable[i].dt_comm);
}
exit(1);
}
int
main(int argc, char *argv[])
{
int i;
int ch;
int (*action)(int, char **) = NULL;
int error;
while ((ch = getopt(argc, argv, "Dv")) != -1) {
switch (ch) {
case 'D':
vflag = LOG_DEBUG;
break;
case 'v':
vflag++;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc == 0)
usage();
for (i = 0; (size_t)i < sizeof(dtable)/sizeof(dtable[0]); i++) {
if (dtable[i].dt_comm == NULL ||
strcmp(dtable[i].dt_comm, argv[0]) == 0) {
action = dtable[i].dt_act;
break;
}
}
if (action == NULL)
usage();
error = (dtable[i].dt_act)(--argc, ++argv);
if (error) {
fprintf(stderr, "%s failed", dtable[i].dt_comm);
if (errmsg != NULL)
fprintf(stderr, ": %s", errmsg);
fprintf(stderr, ".\n");
}
return (error);
}
static int
csock_client_open(struct sockinfo *s)
{
struct sockaddr_un sun;
if ((s->si_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
err(1, "cannot open control socket.");
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
sun.sun_len = sizeof(sun);
strlcpy(sun.sun_path, s->si_name, sizeof(sun.sun_path));
if (connect(s->si_fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
err(1, "connect: %s", s->si_name);
mysyslog(LOG_DEBUG,
"<%s> connected to %s", __func__, sun.sun_path);
return (0);
}
static int
action_plgeneric(int action, char *plstr, char *buf)
{
struct ctrl_msg_hdr *cm;
struct ctrl_msg_pl cp;
struct sockinfo *s;
char *msg;
char *p;
char *q;
s = &ctrlsock;
csock_client_open(s);
cm = (struct ctrl_msg_hdr *)buf;
msg = (char *)buf + sizeof(*cm);
cm->cm_version = CM_VERSION;
cm->cm_type = action;
cm->cm_len = sizeof(*cm);
if (plstr != NULL) {
memset(&cp, 0, sizeof(cp));
p = strchr(plstr, ':');
q = strchr(plstr, '=');
if (p != NULL && q != NULL && p > q)
return (1);
if (p == NULL) { /* No : */
cp.cp_ifname = NULL;
cp.cp_key = plstr;
} else if (p == plstr) { /* empty */
cp.cp_ifname = NULL;
cp.cp_key = plstr + 1;
} else {
*p++ = '\0';
cp.cp_ifname = plstr;
cp.cp_key = p;
}
if (q == NULL)
cp.cp_val = NULL;
else {
*q++ = '\0';
cp.cp_val = q;
}
cm->cm_len += cm_pl2bin(msg, &cp);
mysyslog(LOG_DEBUG, "<%s> key=%s, val_len=%d, ifname=%s",
__func__,cp.cp_key, cp.cp_val_len, cp.cp_ifname);
}
return (cm_handler_client(s->si_fd, CM_STATE_MSG_DISPATCH, buf));
}
static int
action_propget(char *argv, struct ctrl_msg_pl *cp)
{
int error;
struct ctrl_msg_hdr *cm;
char buf[CM_MSG_MAXLEN];
char *msg;
memset(cp, 0, sizeof(*cp));
cm = (struct ctrl_msg_hdr *)buf;
msg = (char *)buf + sizeof(*cm);
error = action_plgeneric(CM_TYPE_REQ_GET_PROP, argv, buf);
if (error || cm->cm_len <= sizeof(*cm))
return (1);
cm_bin2pl(msg, cp);
mysyslog(LOG_DEBUG, "<%s> type=%d, len=%d",
__func__, cm->cm_type, cm->cm_len);
mysyslog(LOG_DEBUG, "<%s> key=%s, val_len=%d, ifname=%s",
__func__,cp->cp_key, cp->cp_val_len, cp->cp_ifname);
return (0);
}
static int
action_propset(char *argv)
{
char buf[CM_MSG_MAXLEN];
return (action_plgeneric(CM_TYPE_REQ_SET_PROP, argv, buf));
}
static int
action_disable(int argc, char **argv)
{
char *action_argv;
char argv_disable[IFNAMSIZ + sizeof(":disable=")];
int i;
int error;
if (argc < 1)
return (1);
error = 0;
for (i = 0; i < argc; i++) {
sprintf(argv_disable, "%s:disable=", argv[i]);
action_argv = argv_disable;
error += action_propset(action_argv);
}
return (error);
}
static int
action_enable(int argc, char **argv)
{
char *action_argv;
char argv_enable[IFNAMSIZ + sizeof(":enable=")];
int i;
int error;
if (argc < 1)
return (1);
error = 0;
for (i = 0; i < argc; i++) {
sprintf(argv_enable, "%s:enable=", argv[i]);
action_argv = argv_enable;
error += action_propset(action_argv);
}
return (error);
}
static int
action_reload(int argc, char **argv)
{
char *action_argv;
char argv_reload[IFNAMSIZ + sizeof(":reload=")];
int i;
int error;
if (argc == 0) {
action_argv = strdup(":reload=");
return (action_propset(action_argv));
}
error = 0;
for (i = 0; i < argc; i++) {
sprintf(argv_reload, "%s:reload=", argv[i]);
action_argv = argv_reload;
error += action_propset(action_argv);
}
return (error);
}
static int
action_echo(int argc __unused, char **argv __unused)
{
char *action_argv;
action_argv = strdup("echo");
return (action_propset(action_argv));
}
static int
action_shutdown(int argc __unused, char **argv __unused)
{
char *action_argv;
action_argv = strdup("shutdown");
return (action_propset(action_argv));
}
/* XXX */
static int
action_version(int argc __unused, char **argv __unused)
{
char *action_argv;
struct ctrl_msg_pl cp;
int error;
action_argv = strdup(":version=");
error = action_propget(action_argv, &cp);
if (error)
return (error);
printf("version=%s\n", cp.cp_val);
return (0);
}
static int
action_show(int argc, char **argv)
{
char *action_argv;
char argv_ifilist[sizeof(":ifilist=")] = ":ifilist=";
char argv_ifi[IFNAMSIZ + sizeof(":ifi=")];
char argv_rai[IFNAMSIZ + sizeof(":rai=")];
char argv_rti[IFNAMSIZ + sizeof(":rti=")];
char argv_pfx[IFNAMSIZ + sizeof(":pfx=")];
char argv_ifi_ra_timer[IFNAMSIZ + sizeof(":ifi_ra_timer=")];
char argv_rdnss[IFNAMSIZ + sizeof(":rdnss=")];
char argv_dnssl[IFNAMSIZ + sizeof(":dnssl=")];
char ssbuf[SSBUFLEN];
struct timespec now, ts0, ts;
struct ctrl_msg_pl cp;
struct ifinfo *ifi;
TAILQ_HEAD(, ifinfo) ifl = TAILQ_HEAD_INITIALIZER(ifl);
char *endp;
char *p;
int error;
int i;
int len;
if (argc == 0) {
action_argv = argv_ifilist;
error = action_propget(action_argv, &cp);
if (error)
return (error);
p = cp.cp_val;
endp = p + cp.cp_val_len;
while (p < endp) {
ifi = malloc(sizeof(*ifi));
if (ifi == NULL)
return (1);
memset(ifi, 0, sizeof(*ifi));
strcpy(ifi->ifi_ifname, p);
ifi->ifi_ifindex = if_nametoindex(ifi->ifi_ifname);
TAILQ_INSERT_TAIL(&ifl, ifi, ifi_next);
p += strlen(ifi->ifi_ifname) + 1;
}
} else {
for (i = 0; i < argc; i++) {
ifi = malloc(sizeof(*ifi));
if (ifi == NULL)
return (1);
memset(ifi, 0, sizeof(*ifi));
strcpy(ifi->ifi_ifname, argv[i]);
ifi->ifi_ifindex = if_nametoindex(ifi->ifi_ifname);
if (ifi->ifi_ifindex == 0) {
sprintf(errmsgbuf, "invalid interface %s",
ifi->ifi_ifname);
errmsg = errmsgbuf;
return (1);
}
TAILQ_INSERT_TAIL(&ifl, ifi, ifi_next);
}
}
clock_gettime(CLOCK_REALTIME_FAST, &now);
clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
TS_SUB(&now, &ts, &ts0);
TAILQ_FOREACH(ifi, &ifl, ifi_next) {
struct ifinfo *ifi_s;
struct rtadvd_timer *rat;
struct rainfo *rai;
struct rtinfo *rti;
struct prefix *pfx;
int c;
int ra_ifstatus;
sprintf(argv_ifi, "%s:ifi=", ifi->ifi_ifname);
action_argv = argv_ifi;
error = action_propget(action_argv, &cp);
if (error)
return (error);
ifi_s = (struct ifinfo *)cp.cp_val;
if (!(ifi_s->ifi_persist) && vflag < LOG_NOTICE)
continue;
printf("%s: flags=<", ifi->ifi_ifname);
c = 0;
if (ifi_s->ifi_ifindex == 0)
c += printf("NONEXISTENT");
else
c += printf("%s", (ifi_s->ifi_flags & IFF_UP) ?
"UP" : "DOWN");
switch (ifi_s->ifi_state) {
case IFI_STATE_CONFIGURED:
c += printf("%s%s", (c) ? "," : "", "CONFIGURED");
break;
case IFI_STATE_TRANSITIVE:
c += printf("%s%s", (c) ? "," : "", "TRANSITIVE");
break;
}
if (ifi_s->ifi_persist)
c += printf("%s%s", (c) ? "," : "", "PERSIST");
printf(">");
ra_ifstatus = RA_IFSTATUS_INACTIVE;
if ((ifi_s->ifi_flags & IFF_UP) &&
((ifi_s->ifi_state == IFI_STATE_CONFIGURED) ||
(ifi_s->ifi_state == IFI_STATE_TRANSITIVE))) {
#if (__FreeBSD_version < 900000)
/*
* RA_RECV: !ip6.forwarding && ip6.accept_rtadv
* RA_SEND: ip6.forwarding
*/
if (getinet6sysctl(IPV6CTL_FORWARDING) == 0) {
if (getinet6sysctl(IPV6CTL_ACCEPT_RTADV))
ra_ifstatus = RA_IFSTATUS_RA_RECV;
else
ra_ifstatus = RA_IFSTATUS_INACTIVE;
} else
ra_ifstatus = RA_IFSTATUS_RA_SEND;
#else
/*
* RA_RECV: ND6_IFF_ACCEPT_RTADV
* RA_SEND: ip6.forwarding
*/
if (ifi_s->ifi_nd_flags & ND6_IFF_ACCEPT_RTADV)
ra_ifstatus = RA_IFSTATUS_RA_RECV;
else if (getinet6sysctl(IPV6CTL_FORWARDING))
ra_ifstatus = RA_IFSTATUS_RA_SEND;
else
ra_ifstatus = RA_IFSTATUS_INACTIVE;
#endif
}
c = 0;
printf(" status=<");
if (ra_ifstatus == RA_IFSTATUS_INACTIVE)
printf("%s%s", (c) ? "," : "", "INACTIVE");
else if (ra_ifstatus == RA_IFSTATUS_RA_RECV)
printf("%s%s", (c) ? "," : "", "RA_RECV");
else if (ra_ifstatus == RA_IFSTATUS_RA_SEND)
printf("%s%s", (c) ? "," : "", "RA_SEND");
printf("> ");
switch (ifi_s->ifi_state) {
case IFI_STATE_CONFIGURED:
case IFI_STATE_TRANSITIVE:
break;
default:
printf("\n");
continue;
}
printf("mtu %d\n", ifi_s->ifi_phymtu);
sprintf(argv_rai, "%s:rai=", ifi->ifi_ifname);
action_argv = argv_rai;
error = action_propget(action_argv, &cp);
if (error)
continue;
rai = (struct rainfo *)cp.cp_val;
printf("\tDefaultLifetime: %s",
sec2str(rai->rai_lifetime, ssbuf));
if (ra_ifstatus != RA_IFSTATUS_RA_SEND &&
rai->rai_lifetime == 0)
printf(" (RAs will be sent with zero lifetime)");
printf("\n");
printf("\tMinAdvInterval/MaxAdvInterval: ");
printf("%s/", sec2str(rai->rai_mininterval, ssbuf));
printf("%s\n", sec2str(rai->rai_maxinterval, ssbuf));
if (rai->rai_linkmtu)
printf("\tAdvLinkMTU: %d", rai->rai_linkmtu);
else
printf("\tAdvLinkMTU: <none>");
printf(", ");
printf("Flags: ");
if (rai->rai_managedflg || rai->rai_otherflg) {
printf("%s", rai->rai_managedflg ? "M" : "");
printf("%s", rai->rai_otherflg ? "O" : "");
} else
printf("<none>");
printf(", ");
printf("Preference: %s\n",
rtpref_str[(rai->rai_rtpref >> 3) & 0xff]);
printf("\tReachableTime: %s, ",
sec2str(rai->rai_reachabletime, ssbuf));
printf("RetransTimer: %s, "
"CurHopLimit: %d\n",
sec2str(rai->rai_retranstimer, ssbuf),
rai->rai_hoplimit);
printf("\tAdvIfPrefixes: %s\n",
rai->rai_advifprefix ? "yes" : "no");
/* RA timer */
rat = NULL;
if (ifi_s->ifi_ra_timer != NULL) {
sprintf(argv_ifi_ra_timer, "%s:ifi_ra_timer=",
ifi->ifi_ifname);
action_argv = argv_ifi_ra_timer;
error = action_propget(action_argv, &cp);
if (error)
return (error);
rat = (struct rtadvd_timer *)cp.cp_val;
}
printf("\tNext RA send: ");
if (rat == NULL)
printf("never\n");
else {
ts.tv_sec = rat->rat_tm.tv_sec + ts0.tv_sec;
printf("%s", ctime(&ts.tv_sec));
}
printf("\tLast RA send: ");
if (ifi_s->ifi_ra_lastsent.tv_sec == 0)
printf("never\n");
else {
ts.tv_sec = ifi_s->ifi_ra_lastsent.tv_sec + ts0.tv_sec;
printf("%s", ctime(&ts.tv_sec));
}
if (rai->rai_clockskew)
printf("\tClock skew: %" PRIu16 "sec\n",
rai->rai_clockskew);
if (vflag < LOG_WARNING)
continue;
/* route information */
sprintf(argv_rti, "%s:rti=", ifi->ifi_ifname);
action_argv = argv_rti;
error = action_propget(action_argv, &cp);
if (error)
return (error);
rti = (struct rtinfo *)cp.cp_val;
len = cp.cp_val_len / sizeof(*rti);
if (len > 0) {
printf("\tRoute Info:\n");
for (i = 0; i < len; i++)
action_show_rtinfo(&rti[i]);
}
/* prefix information */
sprintf(argv_pfx, "%s:pfx=", ifi->ifi_ifname);
action_argv = argv_pfx;
error = action_propget(action_argv, &cp);
if (error)
continue;
pfx = (struct prefix *)cp.cp_val;
len = cp.cp_val_len / sizeof(*pfx);
if (len > 0) {
printf("\tPrefixes (%d):\n", len);
for (i = 0; i < len; i++)
action_show_prefix(&pfx[i]);
}
/* RDNSS information */
sprintf(argv_rdnss, "%s:rdnss=", ifi->ifi_ifname);
action_argv = argv_rdnss;
error = action_propget(action_argv, &cp);
if (error)
continue;
len = *((uint16_t *)cp.cp_val);
if (len > 0) {
printf("\tRDNSS entries:\n");
action_show_rdnss(cp.cp_val);
}
/* DNSSL information */
sprintf(argv_dnssl, "%s:dnssl=", ifi->ifi_ifname);
action_argv = argv_dnssl;
error = action_propget(action_argv, &cp);
if (error)
continue;
len = *((uint16_t *)cp.cp_val);
if (len > 0) {
printf("\tDNSSL entries:\n");
action_show_dnssl(cp.cp_val);
}
if (vflag < LOG_NOTICE)
continue;
printf("\n");
printf("\tCounters\n"
"\t RA burst counts: %" PRIu16 " (interval: %s)\n"
"\t RS wait counts: %" PRIu16 "\n",
ifi_s->ifi_burstcount,
sec2str(ifi_s->ifi_burstinterval, ssbuf),
ifi_s->ifi_rs_waitcount);
printf("\tOutputs\n"
"\t RA: %" PRIu64 "\n", ifi_s->ifi_raoutput);
printf("\tInputs\n"
"\t RA: %" PRIu64 " (normal)\n"
"\t RA: %" PRIu64 " (inconsistent)\n"
"\t RS: %" PRIu64 "\n",
ifi_s->ifi_rainput,
ifi_s->ifi_rainconsistent,
ifi_s->ifi_rsinput);
printf("\n");
#if 0 /* Not implemented yet */
printf("\tReceived RAs:\n");
#endif
}
return (0);
}
static int
action_show_rtinfo(struct rtinfo *rti)
{
char ntopbuf[INET6_ADDRSTRLEN];
char ssbuf[SSBUFLEN];
printf("\t %s/%d (pref: %s, ltime: %s)\n",
inet_ntop(AF_INET6, &rti->rti_prefix,
ntopbuf, sizeof(ntopbuf)),
rti->rti_prefixlen,
rtpref_str[0xff & (rti->rti_rtpref >> 3)],
(rti->rti_ltime == ND6_INFINITE_LIFETIME) ?
"infinity" : sec2str(rti->rti_ltime, ssbuf));
return (0);
}
static int
action_show_prefix(struct prefix *pfx)
{
char ntopbuf[INET6_ADDRSTRLEN];
char ssbuf[SSBUFLEN];
struct timespec now;
clock_gettime(CLOCK_MONOTONIC_FAST, &now);
printf("\t %s/%d", inet_ntop(AF_INET6, &pfx->pfx_prefix,
ntopbuf, sizeof(ntopbuf)), pfx->pfx_prefixlen);
printf(" (");
switch (pfx->pfx_origin) {
case PREFIX_FROM_KERNEL:
printf("KERNEL");
break;
case PREFIX_FROM_CONFIG:
printf("CONFIG");
break;
case PREFIX_FROM_DYNAMIC:
printf("DYNAMIC");
break;
}
printf(",");
printf(" vltime=%s",
(pfx->pfx_validlifetime == ND6_INFINITE_LIFETIME) ?
"infinity" : sec2str(pfx->pfx_validlifetime, ssbuf));
if (pfx->pfx_vltimeexpire > 0)
printf("(expire: %s)",
((long)pfx->pfx_vltimeexpire > now.tv_sec) ?
sec2str(pfx->pfx_vltimeexpire - now.tv_sec, ssbuf) :
"0");
printf(",");
printf(" pltime=%s",
(pfx->pfx_preflifetime == ND6_INFINITE_LIFETIME) ?
"infinity" : sec2str(pfx->pfx_preflifetime, ssbuf));
if (pfx->pfx_pltimeexpire > 0)
printf("(expire %s)",
((long)pfx->pfx_pltimeexpire > now.tv_sec) ?
sec2str(pfx->pfx_pltimeexpire - now.tv_sec, ssbuf) :
"0");
printf(",");
printf(" flags=");
if (pfx->pfx_onlinkflg || pfx->pfx_autoconfflg) {
printf("%s", pfx->pfx_onlinkflg ? "L" : "");
printf("%s", pfx->pfx_autoconfflg ? "A" : "");
} else
printf("<none>");
if (pfx->pfx_timer) {
struct timespec *rest;
rest = rtadvd_timer_rest(pfx->pfx_timer);
if (rest) { /* XXX: what if not? */
printf(" expire=%s", sec2str(rest->tv_sec, ssbuf));
}
}
printf(")\n");
return (0);
}
static int
action_show_rdnss(void *msg)
{
struct rdnss *rdn;
struct rdnss_addr *rda;
uint16_t *rdn_cnt;
uint16_t *rda_cnt;
int i;
int j;
char *p;
uint32_t ltime;
char ntopbuf[INET6_ADDRSTRLEN];
char ssbuf[SSBUFLEN];
p = msg;
rdn_cnt = (uint16_t *)p;
p += sizeof(*rdn_cnt);
if (*rdn_cnt > 0) {
for (i = 0; i < *rdn_cnt; i++) {
rdn = (struct rdnss *)p;
ltime = rdn->rd_ltime;
p += sizeof(*rdn);
rda_cnt = (uint16_t *)p;
p += sizeof(*rda_cnt);
if (*rda_cnt > 0)
for (j = 0; j < *rda_cnt; j++) {
rda = (struct rdnss_addr *)p;
printf("\t %s (ltime=%s)\n",
inet_ntop(AF_INET6,
&rda->ra_dns,
ntopbuf,
sizeof(ntopbuf)),
sec2str(ltime, ssbuf));
p += sizeof(*rda);
}
}
}
return (0);
}
static int
action_show_dnssl(void *msg)
{
struct dnssl *dns;
struct dnssl_addr *dna;
uint16_t *dns_cnt;
uint16_t *dna_cnt;
int i;
int j;
char *p;
uint32_t ltime;
char hbuf[NI_MAXHOST];
char ssbuf[SSBUFLEN];
p = msg;
dns_cnt = (uint16_t *)p;
p += sizeof(*dns_cnt);
if (*dns_cnt > 0) {
for (i = 0; i < *dns_cnt; i++) {
dns = (struct dnssl *)p;
ltime = dns->dn_ltime;
p += sizeof(*dns);
dna_cnt = (uint16_t *)p;
p += sizeof(*dna_cnt);
if (*dna_cnt > 0)
for (j = 0; j < *dna_cnt; j++) {
dna = (struct dnssl_addr *)p;
dname_labeldec(hbuf, sizeof(hbuf),
dna->da_dom);
printf("\t %s (ltime=%s)\n",
hbuf, sec2str(ltime, ssbuf));
p += sizeof(*dna);
}
}
}
return (0);
}
/* Decode domain name label encoding in RFC 1035 Section 3.1 */
static size_t
dname_labeldec(char *dst, size_t dlen, const char *src)
{
size_t len;
const char *src_origin;
const char *src_last;
const char *dst_origin;
src_origin = src;
src_last = strchr(src, '\0');
dst_origin = dst;
memset(dst, '\0', dlen);
while (src && (len = (uint8_t)(*src++) & 0x3f) &&
(src + len) <= src_last) {
if (dst != dst_origin)
*dst++ = '.';
mysyslog(LOG_DEBUG, "<%s> labellen = %zd", __func__, len);
memcpy(dst, src, len);
src += len;
dst += len;
}
*dst = '\0';
return (src - src_origin);
}