rtadvd - Import newer rtadvd, add rtadvctl from FreeBSD-10
authorMatthew Dillon <dillon@apollo.backplane.com>
Mon, 21 Sep 2015 00:59:44 +0000 (17:59 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Mon, 21 Sep 2015 01:02:36 +0000 (18:02 -0700)
* This patch updates the rtvdvd daemon and adds the rtadvctl
  utility to the base system. Both of these are from
  FreeBSD-10.2-RELEASE.  The intent is to add RFC 6106 functionality
  (DNS information distributed via router advertisements) to the
  system

Submitted-by: Charles Musser <cmusser@sonic.net>
30 files changed:
sys/netinet/icmp6.h
usr.sbin/Makefile
usr.sbin/rtadvctl/Makefile [new file with mode: 0644]
usr.sbin/rtadvctl/rtadvctl.8 [new file with mode: 0644]
usr.sbin/rtadvctl/rtadvctl.c [new file with mode: 0644]
usr.sbin/rtadvd/Makefile
usr.sbin/rtadvd/advcap.c
usr.sbin/rtadvd/advcap.h
usr.sbin/rtadvd/config.c
usr.sbin/rtadvd/config.h
usr.sbin/rtadvd/control.c [new file with mode: 0644]
usr.sbin/rtadvd/control.h [copied from usr.sbin/rtadvd/rrenum.h with 53% similarity]
usr.sbin/rtadvd/control_client.c [new file with mode: 0644]
usr.sbin/rtadvd/control_client.h [new file with mode: 0644]
usr.sbin/rtadvd/control_server.c [new file with mode: 0644]
usr.sbin/rtadvd/control_server.h [new file with mode: 0644]
usr.sbin/rtadvd/if.c
usr.sbin/rtadvd/if.h
usr.sbin/rtadvd/pathnames.h
usr.sbin/rtadvd/rrenum.c
usr.sbin/rtadvd/rrenum.h
usr.sbin/rtadvd/rtadvd.8
usr.sbin/rtadvd/rtadvd.c
usr.sbin/rtadvd/rtadvd.conf
usr.sbin/rtadvd/rtadvd.conf.5
usr.sbin/rtadvd/rtadvd.h
usr.sbin/rtadvd/timer.c
usr.sbin/rtadvd/timer.h
usr.sbin/rtadvd/timer_subr.c [copied from usr.sbin/rtadvd/rrenum.h with 56% similarity]
usr.sbin/rtadvd/timer_subr.h [copied from usr.sbin/rtadvd/rrenum.h with 60% similarity]

index 2add6c1..c3ae980 100644 (file)
@@ -309,6 +309,10 @@ struct nd_opt_hdr {                /* Neighbor discovery option header */
 #define ND_OPT_MTU                     5
 
 #define ND_OPT_ROUTE_INFO              200     /* draft-ietf-ipngwg-router-preference, not officially assigned yet */
+#define ND_OPT_RDNSS                   25      /* RFC 6106 */
+#define ND_OPT_DNSSL                   31      /* RFC 6106 */
+#define ND_OPT_MAX                     31
+
 
 struct nd_opt_prefix_info {    /* prefix information */
        u_int8_t        nd_opt_pi_type;
@@ -348,6 +352,22 @@ struct nd_opt_route_info { /* route info */
        /* prefix follows */
 } __attribute__((__packed__));
 
+struct nd_opt_rdnss {          /* RDNSS option (RFC 6106) */
+       u_int8_t        nd_opt_rdnss_type;
+       u_int8_t        nd_opt_rdnss_len;
+       u_int16_t       nd_opt_rdnss_reserved;
+       u_int32_t       nd_opt_rdnss_lifetime;
+       /* followed by list of recursive DNS servers */
+} __attribute__((__packed__));
+
+struct nd_opt_dnssl {          /* DNSSL option (RFC 6106) */
+       u_int8_t        nd_opt_dnssl_type;
+       u_int8_t        nd_opt_dnssl_len;
+       u_int16_t       nd_opt_dnssl_reserved;
+       u_int32_t       nd_opt_dnssl_lifetime;
+       /* followed by list of DNS search domains */
+} __attribute__((__packed__));
+
 /*
  * icmp6 namelookup
  */
index 71b3fdc..f991df6 100644 (file)
@@ -104,6 +104,7 @@ SUBDIR= 802_11 \
        rpcbind \
        rrenumd \
        rtadvd \
+       rtadvctl \
        rtprio \
        rtsold \
        rwhod \
diff --git a/usr.sbin/rtadvctl/Makefile b/usr.sbin/rtadvctl/Makefile
new file mode 100644 (file)
index 0000000..2001fed
--- /dev/null
@@ -0,0 +1,13 @@
+# $FreeBSD: stable/10/usr.sbin/rtadvctl/Makefile 224144 2011-07-17 19:24:54Z hrs $
+#
+.PATH: ${.CURDIR}/../rtadvd
+
+PROG=  rtadvctl
+MAN=   rtadvctl.8
+
+SRCS=  rtadvctl.c control.c control_client.c if.c timer_subr.c
+
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../rtadvd
+WARNS?=        1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rtadvctl/rtadvctl.8 b/usr.sbin/rtadvctl/rtadvctl.8
new file mode 100644 (file)
index 0000000..e344874
--- /dev/null
@@ -0,0 +1,105 @@
+.\" 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: stable/10/usr.sbin/rtadvctl/rtadvctl.8 235873 2012-05-24 02:24:03Z wblock $
+.\"
+.Dd July 16, 2011
+.Dt RTADVCTL 8
+.Os
+.Sh NAME
+.Nm rtadvctl
+.Nd control program for
+.Xr rtadvd 8
+daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Ar subcommand
+.Op Ar interface ...
+.Sh DESCRIPTION
+.Nm
+is a utility that communicates with
+.Xr rtadvd 8
+daemon and displays information about Router Advertisement messages being
+sent on each interface.
+.Pp
+This utility provides several options and subcommands.
+The options are as follows:
+.Bl -tag -width indent
+.\"
+.It Fl v
+Increase verbosity level.
+When specified once, the
+.Nm
+utility shows additional information about prefixes, RDNSS, and DNSSL
+options.
+When given twice, it additionally shows information about
+inactive interfaces and some statistics.
+.El
+.Pp
+The subcommands are as follows:
+.Bl -tag -width indent
+.\"
+.It reload Op interfaces...
+Specifies to reload the configuration file.  If one or more
+.Ar interface
+is specified, configuration entries for the interfaces will be reloaded
+selectively.
+.It enable interfaces...
+Specifies to mark the interface as enable and to try to reload the
+configuration entry.
+This subcommand is useful for dynamically-added interfaces.
+.Pp
+The
+.Xr rtadvd 8
+daemon marks an interface as enable if the interface exists and the
+configuration file has a valid entry for that when it is invoked.
+.It disable interfaces...
+Specifies to mark the interface as disable.
+.It shutdown
+Makes the
+.Xr rtadvd 8
+daemon shut down.
+Note that the
+.Xr rtadvd 8
+daemon will send several RAs with zero lifetime to invalidate the old
+information on each interface.
+It will take at most nine seconds.
+.It show Op interfaces...
+Displays information on Router Advertisement messages being sent
+on each interface.
+.El
+.Sh SEE ALSO
+.Xr rtadvd 8 ,
+.Xr rtadvd.conf 5
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 9.0 .
+.Sh AUTHORS
+.Nm
+was written by
+.An "Hiroki Sato" Aq hrs@FreeBSD.org .
diff --git a/usr.sbin/rtadvctl/rtadvctl.c b/usr.sbin/rtadvctl/rtadvctl.c
new file mode 100644 (file)
index 0000000..463963d
--- /dev/null
@@ -0,0 +1,939 @@
+/*-
+ * 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: stable/10/usr.sbin/rtadvctl/rtadvctl.c 253970 2013-08-05 20:13:02Z hrs $
+ *
+ */
+
+#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/if_var.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);
+}
index 54eb3ae..4273d75 100644 (file)
 # AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
 # LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 # A PARTICULAR PURPOSE.
-# $FreeBSD: src/usr.sbin/rtadvd/Makefile,v 1.1.2.2 2001/04/25 12:10:48 ru Exp $
+#
+# $FreeBSD: stable/10/usr.sbin/rtadvd/Makefile 224144 2011-07-17 19:24:54Z hrs $
 
 PROG=  rtadvd
-SRCS=  rtadvd.c rrenum.c advcap.c if.c config.c timer.c dump.c
-WARNS?=        2
+MAN=   rtadvd.conf.5 rtadvd.8
+SRCS=  rtadvd.c rrenum.c advcap.c if.c config.c timer.c timer_subr.c   \
+       control.c control_server.c
 
-CFLAGS+=-DINET6
+DPADD= ${LIBUTIL}
+LDADD= -lutil
 
-MAN=   rtadvd.conf.5 rtadvd.8
+CFLAGS+= -DHAVE_ARC4RANDOM
+
+WARNS?=        1
 
 .include <bsd.prog.mk>
index 6332acd..bc08f33 100644 (file)
@@ -1,4 +1,5 @@
-/*     $KAME: advcap.c,v 1.5 2001/02/01 09:12:08 jinmei Exp $  */
+/*     $FreeBSD: stable/10/usr.sbin/rtadvd/advcap.c 222824 2011-06-07 15:40:17Z hrs $  */
+/*     $KAME: advcap.c,v 1.11 2003/05/19 09:46:50 keiichi Exp $        */
 
 /*
  * Copyright (c) 1983 The Regents of the University of California.
@@ -12,7 +13,7 @@
  * 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.
- * 3. Neither the name of the University nor the names of its contributors
+ * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
@@ -27,9 +28,6 @@
  * 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: src/usr.sbin/rtadvd/advcap.c,v 1.1.2.2 2001/07/03 11:02:13 ume Exp $
- * $DragonFly: src/usr.sbin/rtadvd/advcap.c,v 1.5 2005/02/17 14:00:10 joerg Exp $
  */
 
 /*
@@ -66,8 +64,6 @@
 #define V_TERM         "HOST"
 #endif
 
-char   *RM;
-
 /*
  * termcap - routines for dealing with the terminal capability data base
  *
@@ -85,16 +81,14 @@ char        *RM;
 static char *tbuf;
 static int hopcount;   /* detect infinite loops in termcap, init 0 */
 
-static char *remotefile;
-
-extern char *conffile;
+extern const char *conffile;
 
 int tgetent(char *, char *);
-int getent(char *, char *, char *);
+int getent(char *, char *, const char *);
 int tnchktc(void);
 int tnamatch(char *);
 static char *tskip(char *);
-long long tgetnum(char *);
+int64_t tgetnum(char *);
 int tgetflag(char *);
 char *tgetstr(char *, char **);
 static char *tdecode(char *, char **);
@@ -107,18 +101,16 @@ static char *tdecode(char *, char **);
 int
 tgetent(char *bp, char *name)
 {
-       char *cp;
-
-       remotefile = cp = conffile ? conffile : _PATH_RTADVDCONF;
-       return (getent(bp, name, cp));
+       return (getent(bp, name, conffile));
 }
 
 int
-getent(char *bp, char *name, char *cp)
+getent(char *bp, char *name, const char *cfile)
 {
        int c;
        int i = 0, cnt = 0;
        char ibuf[BUFSIZ];
+       char *cp;
        int tf;
 
        tbuf = bp;
@@ -130,9 +122,9 @@ getent(char *bp, char *name, char *cp)
         * use so we don't have to read the file. In this case it
         * has to already have the newlines crunched out.
         */
-       if (cp && *cp) {
-               tf = open(RM = cp, O_RDONLY);
-       }
+       if (cfile && *cfile)
+               tf = open(cfile, O_RDONLY);
+
        if (tf < 0) {
                syslog(LOG_INFO,
                       "<%s> open: %s", __func__, strerror(errno));
@@ -157,8 +149,9 @@ getent(char *bp, char *name, char *cp)
                                }
                                break;
                        }
-                       if (cp >= bp+BUFSIZ) {
-                               write(2,"Remcap entry too long\n", 23);
+                       if (cp >= bp + BUFSIZ) {
+                               write(STDERR_FILENO, "Remcap entry too long\n",
+                                     23);
                                break;
                        } else
                                *cp++ = c;
@@ -193,31 +186,31 @@ tnchktc(void)
 
        p = tbuf + strlen(tbuf) - 2;    /* before the last colon */
        while (*--p != ':')
-               if (p<tbuf) {
-                       write(2, "Bad remcap entry\n", 18);
+               if (p < tbuf) {
+                       write(STDERR_FILENO, "Bad remcap entry\n", 18);
                        return (0);
                }
        p++;
        /* p now points to beginning of last field */
        if (p[0] != 't' || p[1] != 'c')
                return (1);
-       strcpy(tcname, p+3);
+       strlcpy(tcname, p + 3, sizeof tcname);
        q = tcname;
        while (*q && *q != ':')
                q++;
        *q = 0;
        if (++hopcount > MAXHOP) {
-               write(2, "Infinite tc= loop\n", 18);
+               write(STDERR_FILENO, "Infinite tc= loop\n", 18);
                return (0);
        }
-       if (getent(tcbuf, tcname, remotefile) != 1) {
+       if (getent(tcbuf, tcname, conffile) != 1) {
                return (0);
        }
        for (q = tcbuf; *q++ != ':'; )
                ;
        l = p - holdtbuf + strlen(q);
        if (l > BUFSIZ) {
-               write(2, "Remcap entry too long\n", 23);
+               write(STDERR_FILENO, "Remcap entry too long\n", 23);
                q[BUFSIZ - (p-holdtbuf)] = 0;
        }
        strcpy(p, q);
@@ -301,10 +294,10 @@ breakbreak:
  * a # character.  If the option is not found we return -1.
  * Note that we handle octal numbers beginning with 0.
  */
-long long
+int64_t
 tgetnum(char *id)
 {
-       long long i;
+       int64_t i;
        int base;
        char *bp = tbuf;
 
@@ -385,7 +378,7 @@ tgetstr(char *id, char **area)
 }
 
 /*
- * Tdecode does the grunt work to decode the
+ * Tdecode does the grung work to decode the
  * string capability escapes.
  */
 static char *
@@ -393,7 +386,7 @@ tdecode(char *str, char **area)
 {
        char *cp;
        int c;
-       char *dp;
+       const char *dp;
        int i;
        char term;
 
index 82deed1..2447cf5 100644 (file)
@@ -1,4 +1,5 @@
-/*     $KAME: advcap.h,v 1.3 2001/02/01 09:12:08 jinmei Exp $  */
+/*     $FreeBSD: stable/10/usr.sbin/rtadvd/advcap.h 173412 2007-11-07 10:53:41Z kevlo $        */
+/*     $KAME: advcap.h,v 1.5 2003/06/09 05:40:54 t-momose Exp $        */
 
 /*
  * Copyright (C) 1994,1995 by Andrey A. Chernov, Moscow, Russia.
@@ -24,9 +25,6 @@
  * 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: src/usr.sbin/rtadvd/advcap.h,v 1.1.2.2 2001/07/03 11:02:14 ume Exp $
- * $DragonFly: src/usr.sbin/rtadvd/advcap.h,v 1.3 2003/11/03 19:31:42 eirikn Exp $
  */
 
 /* Based on Id: termcap.h,v 1.8 1996/09/10 12:42:10 peter Exp */
@@ -40,7 +38,7 @@ __BEGIN_DECLS
 
 extern int agetent(char *, const char *);
 extern int agetflag(const char *);
-extern long long agetnum(const char *);
+extern int64_t agetnum(const char *);
 extern char *agetstr(const char *, char **);
 
 __END_DECLS
index 09560cc..7b393c5 100644 (file)
@@ -1,9 +1,11 @@
-/*     $KAME: config.c,v 1.37 2001/05/25 07:34:00 itojun Exp $ */
+/*     $FreeBSD: stable/10/usr.sbin/rtadvd/config.c 254955 2013-08-27 11:50:33Z hrs $  */
+/*     $KAME: config.c,v 1.84 2003/08/05 12:34:23 itojun Exp $ */
 
 /*
  * Copyright (C) 1998 WIDE Project.
+ * 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:
@@ -15,7 +17,7 @@
  * 3. Neither the name of the project nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
- * 
+ *
  * 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
  * 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: src/usr.sbin/rtadvd/config.c,v 1.3.2.5 2003/04/22 09:40:57 suz Exp $
  */
 
 #include <sys/param.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/sysctl.h>
 
 #include <net/if.h>
-#if defined(__DragonFly__) 
 #include <net/if_var.h>
-#endif /* __DragonFly__  */
 #include <net/route.h>
 #include <net/if_dl.h>
 
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
 #include <netinet/icmp6.h>
-#ifdef MIP6
-#include <netinet6/mip6.h>
-#endif
+#include <netinet6/nd6.h>
 
 #include <arpa/inet.h>
 
 #include <stdio.h>
 #include <syslog.h>
 #include <errno.h>
+#include <inttypes.h>
+#include <netdb.h>
 #include <string.h>
-#include <stdlib.h>
 #include <search.h>
+#include <stdlib.h>
+#include <time.h>
 #include <unistd.h>
 #include <ifaddrs.h>
 
 #include "if.h"
 #include "config.h"
 
+/* label of tcapcode + number + domain name + zero octet */
+static char entbuf[10 + 3 + NI_MAXHOST + 1];
+static char oentbuf[10 + 3 + NI_MAXHOST + 1];
+static char abuf[DNAME_LABELENC_MAXLEN];
+
 static time_t prefix_timo = (60 * 120);        /* 2 hours.
                                         * XXX: should be configurable. */
-extern struct rainfo *ralist;
 
 static struct rtadvd_timer *prefix_timeout(void *);
-static void makeentry(char *, size_t, int, char *, int);
-static void get_prefix(struct rainfo *);
-static int getinet6sysctl(int);
+static void makeentry(char *, size_t, int, const char *);
+static size_t dname_labelenc(char *, const char *);
 
-void
-getconfig(char *intface)
+/* Encode domain name label encoding in RFC 1035 Section 3.1 */
+static size_t
+dname_labelenc(char *dst, const char *src)
 {
-       int stat, pfxs, i;
-       char tbuf[BUFSIZ];
-       struct rainfo *tmp;
-       long val;
-       long long val64;
-       char buf[BUFSIZ];
-       char *bp = buf;
-       char *addr;
-       static int forwarding = -1;
+       char *dst_origin;
+       char *p;
+       size_t len;
+
+       dst_origin = dst;
+       len = strlen(src);
+
+       /* Length fields per 63 octets + '\0' (<= DNAME_LABELENC_MAXLEN) */
+       memset(dst, 0, len + len / 64 + 1 + 1);
+
+       syslog(LOG_DEBUG, "<%s> labelenc = %s", __func__, src);
+       while (src && (len = strlen(src)) != 0) {
+               /* Put a length field with 63 octet limitation first. */
+               p = strchr(src, '.');
+               if (p == NULL)
+                       *dst++ = len = MIN(63, len);
+               else
+                       *dst++ = len = MIN(63, p - src);
+               /* Copy 63 octets at most. */
+               memcpy(dst, src, len);
+               dst += len;
+               if (p == NULL) /* the last label */
+                       break;
+               src = p + 1;
+       }
+       /* Always need a 0-length label at the tail. */
+       *dst++ = '\0';
+
+       syslog(LOG_DEBUG, "<%s> labellen = %td", __func__, dst - dst_origin);
+       return (dst - dst_origin);
+}
 
-#define MUSTHAVE(var, cap)     \
+#define        MUSTHAVE(var, cap)                                              \
     do {                                                               \
-       int t;                                                          \
+       int64_t t;                                                      \
        if ((t = agetnum(cap)) < 0) {                                   \
                fprintf(stderr, "rtadvd: need %s for interface %s\n",   \
                        cap, intface);                                  \
@@ -102,53 +125,264 @@ getconfig(char *intface)
        }                                                               \
        var = t;                                                        \
      } while (0)
-#define MAYHAVE(var, cap, def) \
+
+#define        MAYHAVE(var, cap, def)                                          \
      do {                                                              \
        if ((var = agetnum(cap)) < 0)                                   \
                var = def;                                              \
      } while (0)
 
-       if ((stat = agetent(tbuf, intface)) <= 0) {
-               memset(tbuf, 0, sizeof(tbuf));
-               syslog(LOG_INFO,
-                      "<%s> %s isn't defined in the configuration file"
-                      " or the configuration file doesn't exist."
-                      " Treat it as default",
-                       __func__, intface);
+int
+loadconfig_index(int idx)
+{
+       char ifname[IFNAMSIZ];
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       if (if_indextoname(idx, ifname) != NULL)
+               return (loadconfig_ifname(ifname));
+       else
+               return (1);
+}
+
+int
+loadconfig_ifname(char *ifname)
+{
+       struct ifinfo *ifi;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       update_ifinfo(&ifilist, UPDATE_IFINFO_ALL);
+       TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+               /* NULL means all IFs will be processed. */
+               if (ifname != NULL &&
+                   strcmp(ifi->ifi_ifname, ifname) != 0)
+                       continue;
+
+               if (!ifi->ifi_persist) {
+                       syslog(LOG_INFO,
+                           "<%s> %s is not a target interface.  "
+                           "Ignored at this moment.", __func__,
+                           ifi->ifi_ifname);
+                       continue;
+
+               }
+               if (ifi->ifi_ifindex == 0) {
+                       syslog(LOG_ERR,
+                           "<%s> %s not found.  "
+                           "Ignored at this moment.", __func__,
+                           ifi->ifi_ifname);
+                       continue;
+               }
+               if (getconfig(ifi) == NULL) {
+                       syslog(LOG_ERR,
+                           "<%s> invalid configuration for %s.  "
+                           "Ignored at this moment.", __func__,
+                           ifi->ifi_ifname);
+                       continue;
+               }
        }
+       return (0);
+}
 
-       tmp = (struct rainfo *)malloc(sizeof(*ralist));
-       memset(tmp, 0, sizeof(*tmp));
-       tmp->prefix.next = tmp->prefix.prev = &tmp->prefix;
-       tmp->route.next = tmp->route.prev = &tmp->route;
+int
+rm_ifinfo_index(int idx)
+{
+       struct ifinfo *ifi;
 
-       /* check if we are allowed to forward packets (if not determined) */
-       if (forwarding < 0) {
-               if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0)
-                       exit(1);
+       ifi = if_indextoifinfo(idx);
+       if (ifi == NULL) {
+               syslog(LOG_ERR, "<%s>: ifinfo not found (idx=%d)",
+                   __func__, idx);
+               return (-1);
+       }
+
+       return (rm_ifinfo(ifi));
+}
+
+int
+rm_ifinfo(struct ifinfo *ifi)
+{
+       int error;
+
+       syslog(LOG_DEBUG, "<%s> enter (%s).", __func__, ifi->ifi_ifname);
+       switch (ifi->ifi_state) {
+       case IFI_STATE_UNCONFIGURED:
+               return (0);
+               break;
+       default:
+               ifi->ifi_state = IFI_STATE_UNCONFIGURED;
+               syslog(LOG_DEBUG,
+                   "<%s> ifname=%s marked as UNCONFIGURED.",
+                   __func__, ifi->ifi_ifname);
+
+               /* XXX: No MC leaving here because index is disappeared */
+
+               /* Inactivate timer */
+               rtadvd_remove_timer(ifi->ifi_ra_timer);
+               ifi->ifi_ra_timer = NULL;
+               break;
+       }
+
+       /* clean up ifi */
+       if (!ifi->ifi_persist) {
+               TAILQ_REMOVE(&ifilist, ifi, ifi_next);
+               syslog(LOG_DEBUG, "<%s>: ifinfo (idx=%d) removed.",
+                   __func__, ifi->ifi_ifindex);
+               free(ifi);
+       } else {
+               /* recreate an empty entry */
+               update_persist_ifinfo(&ifilist, ifi->ifi_ifname);
+               syslog(LOG_DEBUG, "<%s>: ifname=%s is persistent.",
+                   __func__, ifi->ifi_ifname);
+       }
+
+       /* clean up rai if any */
+       switch (ifi->ifi_state) {
+       case IFI_STATE_CONFIGURED:
+               if (ifi->ifi_rainfo != NULL) {
+                       error = rm_rainfo(ifi->ifi_rainfo);
+                       if (error)
+                               return (error);
+                       ifi->ifi_rainfo = NULL;
+               }
+               break;
+       case IFI_STATE_TRANSITIVE:
+               if (ifi->ifi_rainfo == ifi->ifi_rainfo_trans) {
+                       if (ifi->ifi_rainfo != NULL) {
+                               error = rm_rainfo(ifi->ifi_rainfo);
+                               if (error)
+                                       return (error);
+                               ifi->ifi_rainfo = NULL;
+                               ifi->ifi_rainfo_trans = NULL;
+                       }
+               } else {
+                       if (ifi->ifi_rainfo != NULL) {
+                               error = rm_rainfo(ifi->ifi_rainfo);
+                               if (error)
+                                       return (error);
+                               ifi->ifi_rainfo = NULL;
+                       }
+                       if (ifi->ifi_rainfo_trans != NULL) {
+                               error = rm_rainfo(ifi->ifi_rainfo_trans);
+                               if (error)
+                                       return (error);
+                               ifi->ifi_rainfo_trans = NULL;
+                       }
+               }
+       }
+
+       syslog(LOG_DEBUG, "<%s> leave (%s).", __func__, ifi->ifi_ifname);
+       return (0);
+}
+
+int
+rm_rainfo(struct rainfo *rai)
+{
+       struct prefix *pfx;
+       struct soliciter *sol;
+       struct rdnss *rdn;
+       struct rdnss_addr *rdna;
+       struct dnssl *dns;
+       struct rtinfo *rti;
+
+       syslog(LOG_DEBUG, "<%s>: enter",  __func__);
+
+       TAILQ_REMOVE(&railist, rai, rai_next);
+       if (rai->rai_ifinfo != NULL)
+               syslog(LOG_DEBUG, "<%s>: rainfo (idx=%d) removed.",
+                   __func__, rai->rai_ifinfo->ifi_ifindex);
+
+       if (rai->rai_ra_data != NULL)
+               free(rai->rai_ra_data);
+
+       while ((pfx = TAILQ_FIRST(&rai->rai_prefix)) != NULL)
+               delete_prefix(pfx);
+       while ((sol = TAILQ_FIRST(&rai->rai_soliciter)) != NULL) {
+               TAILQ_REMOVE(&rai->rai_soliciter, sol, sol_next);
+               free(sol);
+       }
+       while ((rdn = TAILQ_FIRST(&rai->rai_rdnss)) != NULL) {
+               TAILQ_REMOVE(&rai->rai_rdnss, rdn, rd_next);
+               while ((rdna = TAILQ_FIRST(&rdn->rd_list)) != NULL) {
+                       TAILQ_REMOVE(&rdn->rd_list, rdna, ra_next);
+                       free(rdna);
+               }
+               free(rdn);
+       }
+       while ((dns = TAILQ_FIRST(&rai->rai_dnssl)) != NULL) {
+               TAILQ_REMOVE(&rai->rai_dnssl, dns, dn_next);
+               free(dns);
+       }
+       while ((rti = TAILQ_FIRST(&rai->rai_route)) != NULL) {
+               TAILQ_REMOVE(&rai->rai_route, rti, rti_next);
+               free(rti);
+       }
+       free(rai);
+       syslog(LOG_DEBUG, "<%s>: leave",  __func__);
+
+       return (0);
+}
+
+struct ifinfo *
+getconfig(struct ifinfo *ifi)
+{
+       int stat, i;
+       int error;
+       char tbuf[BUFSIZ];
+       struct rainfo *rai;
+       struct rainfo *rai_old;
+       int32_t val;
+       int64_t val64;
+       char buf[BUFSIZ];
+       char *bp = buf;
+       char *addr, *flagstr;
+
+       if (ifi == NULL)        /* if does not exist */
+               return (NULL);
+
+       if (ifi->ifi_state == IFI_STATE_TRANSITIVE &&
+           ifi->ifi_rainfo == NULL) {
+               syslog(LOG_INFO, "<%s> %s is shutting down.  Skipped.",
+                   __func__, ifi->ifi_ifname);
+               return (NULL);
        }
 
+       if ((stat = agetent(tbuf, ifi->ifi_ifname)) <= 0) {
+               memset(tbuf, 0, sizeof(tbuf));
+               syslog(LOG_INFO,
+                   "<%s> %s isn't defined in the configuration file"
+                   " or the configuration file doesn't exist."
+                   " Treat it as default",
+                    __func__, ifi->ifi_ifname);
+       }
+
+       ELM_MALLOC(rai, exit(1));
+       TAILQ_INIT(&rai->rai_prefix);
+       TAILQ_INIT(&rai->rai_route);
+       TAILQ_INIT(&rai->rai_rdnss);
+       TAILQ_INIT(&rai->rai_dnssl);
+       TAILQ_INIT(&rai->rai_soliciter);
+       rai->rai_ifinfo = ifi;
+
+       /* gather on-link prefixes from the network interfaces. */
+       if (agetflag("noifprefix"))
+               rai->rai_advifprefix = 0;
+       else
+               rai->rai_advifprefix = 1;
+
        /* get interface information */
        if (agetflag("nolladdr"))
-               tmp->advlinkopt = 0;
+               rai->rai_advlinkopt = 0;
        else
-               tmp->advlinkopt = 1;
-       if (tmp->advlinkopt) {
-               if ((tmp->sdl = if_nametosdl(intface)) == NULL) {
+               rai->rai_advlinkopt = 1;
+       if (rai->rai_advlinkopt) {
+               if (ifi->ifi_sdl.sdl_type == 0) {
                        syslog(LOG_ERR,
-                              "<%s> can't get information of %s",
-                              __func__, intface);
-                       exit(1);
+                           "<%s> can't get information of %s",
+                           __func__, ifi->ifi_ifname);
+                       goto getconfig_free_rai;
                }
-               tmp->ifindex = tmp->sdl->sdl_index;
-       } else
-               tmp->ifindex = if_nametoindex(intface);
-       strncpy(tmp->ifname, intface, sizeof(tmp->ifname));
-       if ((tmp->phymtu = if_getmtu(intface)) == 0) {
-               tmp->phymtu = IPV6_MMTU;
-               syslog(LOG_WARNING,
-                      "<%s> can't get interface mtu of %s. Treat as %d",
-                      __func__, intface, IPV6_MMTU);
        }
 
        /*
@@ -157,418 +391,642 @@ getconfig(char *intface)
        MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
        if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
                syslog(LOG_ERR,
-                      "<%s> maxinterval must be between %e and %u",
-                      __func__, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
-               exit(1);
+                   "<%s> maxinterval (%" PRIu32 ") on %s is invalid "
+                   "(must be between %u and %u)", __func__, val,
+                   ifi->ifi_ifname, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
+               goto getconfig_free_rai;
        }
-       tmp->maxinterval = (u_int)val;
-       MAYHAVE(val, "mininterval", tmp->maxinterval/3);
-       if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) {
+       rai->rai_maxinterval = (uint16_t)val;
+
+       MAYHAVE(val, "mininterval", rai->rai_maxinterval/3);
+       if ((uint16_t)val < MIN_MININTERVAL ||
+           (uint16_t)val > (rai->rai_maxinterval * 3) / 4) {
                syslog(LOG_ERR,
-                      "<%s> mininterval must be between %e and %d",
-                      __func__,
-                      MIN_MININTERVAL,
-                      (tmp->maxinterval * 3) / 4);
-               exit(1);
+                   "<%s> mininterval (%" PRIu32 ") on %s is invalid "
+                   "(must be between %d and %d)",
+                   __func__, val, ifi->ifi_ifname, MIN_MININTERVAL,
+                   (rai->rai_maxinterval * 3) / 4);
+               goto getconfig_free_rai;
        }
-       tmp->mininterval = (u_int)val;
+       rai->rai_mininterval = (uint16_t)val;
 
        MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
-       tmp->hoplimit = val & 0xff;
-
-       MAYHAVE(val, "raflags", 0);
-       tmp->managedflg = val & ND_RA_FLAG_MANAGED;
-       tmp->otherflg = val & ND_RA_FLAG_OTHER;
-#ifdef MIP6
-       if (mobileip6)
-               tmp->haflg = val & ND_RA_FLAG_HA;
-#endif
+       rai->rai_hoplimit = val & 0xff;
+
+       if ((flagstr = (char *)agetstr("raflags", &bp))) {
+               val = 0;
+               if (strchr(flagstr, 'm'))
+                       val |= ND_RA_FLAG_MANAGED;
+               if (strchr(flagstr, 'o'))
+                       val |= ND_RA_FLAG_OTHER;
+               if (strchr(flagstr, 'h'))
+                       val |= ND_RA_FLAG_RTPREF_HIGH;
+               if (strchr(flagstr, 'l')) {
+                       if ((val & ND_RA_FLAG_RTPREF_HIGH)) {
+                               syslog(LOG_ERR, "<%s> the \'h\' and \'l\'"
+                                   " router flags are exclusive", __func__);
+                               goto getconfig_free_rai;
+                       }
+                       val |= ND_RA_FLAG_RTPREF_LOW;
+               }
+       } else
+               MAYHAVE(val, "raflags", 0);
+
+       rai->rai_managedflg = val & ND_RA_FLAG_MANAGED;
+       rai->rai_otherflg = val & ND_RA_FLAG_OTHER;
 #ifndef ND_RA_FLAG_RTPREF_MASK
 #define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */
 #define ND_RA_FLAG_RTPREF_RSV  0x10 /* 00010000 */
 #endif
-       tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
-       if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) {
-               syslog(LOG_ERR, "<%s> invalid router preference on %s",
-                      __func__, intface);
-               exit(1);
+       rai->rai_rtpref = val & ND_RA_FLAG_RTPREF_MASK;
+       if (rai->rai_rtpref == ND_RA_FLAG_RTPREF_RSV) {
+               syslog(LOG_ERR, "<%s> invalid router preference (%02x) on %s",
+                   __func__, rai->rai_rtpref, ifi->ifi_ifname);
+               goto getconfig_free_rai;
        }
 
-       MAYHAVE(val, "rltime", tmp->maxinterval * 3);
-       if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) {
+       MAYHAVE(val, "rltime", rai->rai_maxinterval * 3);
+       if ((uint16_t)val && ((uint16_t)val < rai->rai_maxinterval ||
+           (uint16_t)val > MAXROUTERLIFETIME)) {
                syslog(LOG_ERR,
-                      "<%s> router lifetime on %s must be 0 or"
-                      " between %d and %d",
-                      __func__, intface,
-                      tmp->maxinterval, MAXROUTERLIFETIME);
-               exit(1);
+                   "<%s> router lifetime (%" PRIu32 ") on %s is invalid "
+                   "(must be 0 or between %d and %d)",
+                   __func__, val, ifi->ifi_ifname, rai->rai_maxinterval,
+                   MAXROUTERLIFETIME);
+               goto getconfig_free_rai;
        }
-       /*
-        * Basically, hosts MUST NOT send Router Advertisement messages at any
-        * time (RFC 2461, Section 6.2.3). However, it would sometimes be
-        * useful to allow hosts to advertise some parameters such as prefix
-        * information and link MTU. Thus, we allow hosts to invoke rtadvd
-        * only when router lifetime (on every advertising interface) is
-        * explicitly set zero. (see also the above section)
-        */
-       if (val && forwarding == 0) {
-               syslog(LOG_WARNING,
-                      "<%s> non zero router lifetime is specified for %s, "
-                      "which must not be allowed for hosts.",
-                      __func__, intface);
-               exit(1);
-       }
-       tmp->lifetime = val & 0xffff;
+       rai->rai_lifetime = val & 0xffff;
 
        MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
-       if (val > MAXREACHABLETIME) {
+       if (val < 0 || val > MAXREACHABLETIME) {
                syslog(LOG_ERR,
-                      "<%s> reachable time must be no greater than %d",
-                      __func__, MAXREACHABLETIME);
-               exit(1);
+                   "<%s> reachable time (%" PRIu32 ") on %s is invalid "
+                   "(must be no greater than %d)",
+                   __func__, val, ifi->ifi_ifname, MAXREACHABLETIME);
+               goto getconfig_free_rai;
        }
-       tmp->reachabletime = (u_int32_t)val;
+       rai->rai_reachabletime = (uint32_t)val;
 
        MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER);
        if (val64 < 0 || val64 > 0xffffffff) {
-               syslog(LOG_ERR,
-                      "<%s> retrans time out of range", __func__);
-               exit(1);
+               syslog(LOG_ERR, "<%s> retrans time (%" PRIu64 ") on %s out of range",
+                   __func__, val64, ifi->ifi_ifname);
+               goto getconfig_free_rai;
        }
-       tmp->retranstimer = (u_int32_t)val64;
+       rai->rai_retranstimer = (uint32_t)val64;
 
-#ifndef MIP6
-       if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) {
+       if (agetnum("hapref") != -1 || agetnum("hatime") != -1) {
                syslog(LOG_ERR,
-                      "<%s> mobile-ip6 configuration not supported",
-                      __func__);
-               exit(1);
-       }
-#else
-       if (!mobileip6) {
-               if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) {
-                       syslog(LOG_ERR,
-                              "<%s> mobile-ip6 configuration without "
-                              "proper command line option",
-                              __func__);
-                       exit(1);
-               }
-       } else {
-               tmp->hapref = 0;
-               if ((val = agetnum("hapref")) >= 0)
-                       tmp->hapref = (int16_t)val;
-               if (tmp->hapref != 0) {
-                       tmp->hatime = 0;
-                       MUSTHAVE(val, "hatime");
-                       tmp->hatime = (u_int16_t)val;
-                       if (tmp->hatime <= 0) {
-                               syslog(LOG_ERR,
-                                      "<%s> home agent lifetime must be greater than 0",
-                                      __func__);
-                               exit(1);
-                       }
-               }
+                   "<%s> mobile-ip6 configuration not supported",
+                   __func__);
+               goto getconfig_free_rai;
        }
-#endif
-
        /* prefix information */
 
        /*
-        * This is an implementation specific parameter to consinder
+        * This is an implementation specific parameter to consider
         * link propagation delays and poorly synchronized clocks when
         * checking consistency of advertised lifetimes.
         */
        MAYHAVE(val, "clockskew", 0);
-       tmp->clockskew = val;
+       rai->rai_clockskew = val;
+
+       rai->rai_pfxs = 0;
+       for (i = -1; i < MAXPREFIX; i++) {
+               struct prefix *pfx;
 
-       if ((pfxs = agetnum("addrs")) < 0) {
-               /* auto configure prefix information */
-               if (agetstr("addr", &bp) || agetstr("addr1", &bp)) {
+               makeentry(entbuf, sizeof(entbuf), i, "addr");
+               addr = (char *)agetstr(entbuf, &bp);
+               if (addr == NULL)
+                       continue;
+
+               /* allocate memory to store prefix information */
+               ELM_MALLOC(pfx, exit(1));
+               pfx->pfx_rainfo = rai;
+               pfx->pfx_origin = PREFIX_FROM_CONFIG;
+
+               if (inet_pton(AF_INET6, addr, &pfx->pfx_prefix) != 1) {
                        syslog(LOG_ERR,
-                              "<%s> conflicting prefix configuration for %s: "
-                              "automatic and manual config at the same time",
-                              __func__, intface);
-                       exit(1);
+                           "<%s> inet_pton failed for %s",
+                           __func__, addr);
+                       goto getconfig_free_pfx;
                }
-               get_prefix(tmp);
-       }
-       else {
-               tmp->pfxs = pfxs;
-               for (i = 0; i < pfxs; i++) {
-                       struct prefix *pfx;
-                       char entbuf[256];
-                       int added = (pfxs > 1) ? 1 : 0;
-
-                       /* allocate memory to store prefix information */
-                       if ((pfx = malloc(sizeof(struct prefix))) == NULL) {
-                               syslog(LOG_ERR,
-                                      "<%s> can't allocate enough memory",
-                                      __func__);
-                               exit(1);
-                       }
-                       memset(pfx, 0, sizeof(*pfx));
-
-                       /* link into chain */
-                       insque(pfx, &tmp->prefix);
-                       pfx->rainfo = tmp;
-
-                       pfx->origin = PREFIX_FROM_CONFIG;
-
-                       makeentry(entbuf, sizeof(entbuf), i, "prefixlen",
-                           added);
-                       MAYHAVE(val, entbuf, 64);
-                       if (val < 0 || val > 128) {
-                               syslog(LOG_ERR,
-                                      "<%s> prefixlen out of range",
-                                      __func__);
-                               exit(1);
-                       }
-                       pfx->prefixlen = (int)val;
-
-                       makeentry(entbuf, sizeof(entbuf), i, "pinfoflags",
-                           added);
-#ifdef MIP6
-                       if (mobileip6)
-                       {
-                               MAYHAVE(val, entbuf,
-                                   (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO|
-                                        ND_OPT_PI_FLAG_ROUTER));
-                       } else
-#endif
-                       {
-                               MAYHAVE(val, entbuf,
-                                   (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
-                       }
-                       pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
-                       pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
-#ifdef MIP6
-                       pfx->routeraddr = val & ND_OPT_PI_FLAG_ROUTER;
-#endif
+               if (IN6_IS_ADDR_MULTICAST(&pfx->pfx_prefix)) {
+                       syslog(LOG_ERR,
+                           "<%s> multicast prefix (%s) must "
+                           "not be advertised on %s",
+                           __func__, addr, ifi->ifi_ifname);
+                       goto getconfig_free_pfx;
+               }
+               if (IN6_IS_ADDR_LINKLOCAL(&pfx->pfx_prefix))
+                       syslog(LOG_NOTICE,
+                           "<%s> link-local prefix (%s) will be"
+                           " advertised on %s",
+                           __func__, addr, ifi->ifi_ifname);
 
-                       makeentry(entbuf, sizeof(entbuf), i, "vltime", added);
-                       MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
-                       if (val64 < 0 || val64 > 0xffffffff) {
-                               syslog(LOG_ERR,
-                                      "<%s> vltime out of range",
-                                      __func__);
-                               exit(1);
-                       }
-                       pfx->validlifetime = (u_int32_t)val64;
-
-                       makeentry(entbuf, sizeof(entbuf), i, "vltimedecr",
-                           added);
-                       if (agetflag(entbuf)) {
-                               struct timeval now;
-                               gettimeofday(&now, 0);
-                               pfx->vltimeexpire =
-                                       now.tv_sec + pfx->validlifetime;
-                       }
+               makeentry(entbuf, sizeof(entbuf), i, "prefixlen");
+               MAYHAVE(val, entbuf, 64);
+               if (val < 0 || val > 128) {
+                       syslog(LOG_ERR, "<%s> prefixlen (%" PRIu32 ") for %s "
+                           "on %s out of range",
+                           __func__, val, addr, ifi->ifi_ifname);
+                       goto getconfig_free_pfx;
+               }
+               pfx->pfx_prefixlen = (int)val;
+
+               makeentry(entbuf, sizeof(entbuf), i, "pinfoflags");
+               if ((flagstr = (char *)agetstr(entbuf, &bp))) {
+                       val = 0;
+                       if (strchr(flagstr, 'l'))
+                               val |= ND_OPT_PI_FLAG_ONLINK;
+                       if (strchr(flagstr, 'a'))
+                               val |= ND_OPT_PI_FLAG_AUTO;
+               } else {
+                       MAYHAVE(val, entbuf,
+                           (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
+               }
+               pfx->pfx_onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
+               pfx->pfx_autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
 
-                       makeentry(entbuf, sizeof(entbuf), i, "pltime", added);
-                       MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
-                       if (val64 < 0 || val64 > 0xffffffff) {
-                               syslog(LOG_ERR,
-                                      "<%s> pltime out of range",
-                                      __func__);
-                               exit(1);
-                       }
-                       pfx->preflifetime = (u_int32_t)val64;
-
-                       makeentry(entbuf, sizeof(entbuf), i, "pltimedecr",
-                           added);
-                       if (agetflag(entbuf)) {
-                               struct timeval now;
-                               gettimeofday(&now, 0);
-                               pfx->pltimeexpire =
-                                       now.tv_sec + pfx->preflifetime;
-                       }
+               makeentry(entbuf, sizeof(entbuf), i, "vltime");
+               MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
+               if (val64 < 0 || val64 > 0xffffffff) {
+                       syslog(LOG_ERR, "<%s> vltime (%" PRIu64 ") for "
+                           "%s/%d on %s is out of range",
+                           __func__, val64,
+                           addr, pfx->pfx_prefixlen, ifi->ifi_ifname);
+                       goto getconfig_free_pfx;
+               }
+               pfx->pfx_validlifetime = (uint32_t)val64;
 
-                       makeentry(entbuf, sizeof(entbuf), i, "addr", added);
-                       addr = (char *)agetstr(entbuf, &bp);
-                       if (addr == NULL) {
-                               syslog(LOG_ERR,
-                                      "<%s> need %s as an prefix for "
-                                      "interface %s",
-                                      __func__, entbuf, intface);
-                               exit(1);
-                       }
-                       if (inet_pton(AF_INET6, addr,
-                                     &pfx->prefix) != 1) {
-                               syslog(LOG_ERR,
-                                      "<%s> inet_pton failed for %s",
-                                      __func__, addr);
-                               exit(1);
-                       }
-                       if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
-                               syslog(LOG_ERR,
-                                      "<%s> multicast prefix(%s) must "
-                                      "not be advertised (IF=%s)",
-                                      __func__, addr, intface);
-                               exit(1);
-                       }
-                       if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
-                               syslog(LOG_NOTICE,
-                                      "<%s> link-local prefix(%s) will be"
-                                      " advertised on %s",
-                                      __func__, addr, intface);
+               makeentry(entbuf, sizeof(entbuf), i, "vltimedecr");
+               if (agetflag(entbuf)) {
+                       struct timespec now;
+
+                       clock_gettime(CLOCK_MONOTONIC_FAST, &now);
+                       pfx->pfx_vltimeexpire =
+                               now.tv_sec + pfx->pfx_validlifetime;
+               }
+
+               makeentry(entbuf, sizeof(entbuf), i, "pltime");
+               MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
+               if (val64 < 0 || val64 > 0xffffffff) {
+                       syslog(LOG_ERR,
+                           "<%s> pltime (%" PRIu64 ") for %s/%d on %s "
+                           "is out of range",
+                           __func__, val64,
+                           addr, pfx->pfx_prefixlen, ifi->ifi_ifname);
+                       goto getconfig_free_pfx;
                }
+               pfx->pfx_preflifetime = (uint32_t)val64;
+
+               makeentry(entbuf, sizeof(entbuf), i, "pltimedecr");
+               if (agetflag(entbuf)) {
+                       struct timespec now;
+
+                       clock_gettime(CLOCK_MONOTONIC_FAST, &now);
+                       pfx->pfx_pltimeexpire =
+                           now.tv_sec + pfx->pfx_preflifetime;
+               }
+               /* link into chain */
+               TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next);
+               rai->rai_pfxs++;
+               continue;
+getconfig_free_pfx:
+               free(pfx);
        }
+       if (rai->rai_advifprefix && rai->rai_pfxs == 0)
+               get_prefix(rai);
 
-       MAYHAVE(val, "mtu", 0);
-       if (val < 0 || val > 0xffffffff) {
+       MAYHAVE(val64, "mtu", 0);
+       if (val < 0 || val64 > 0xffffffff) {
                syslog(LOG_ERR,
-                      "<%s> mtu out of range", __func__);
-               exit(1);
+                   "<%s> mtu (%" PRIu64 ") on %s out of range",
+                   __func__, val64, ifi->ifi_ifname);
+               goto getconfig_free_rai;
        }
-       tmp->linkmtu = (u_int32_t)val;
-       if (tmp->linkmtu == 0) {
+       rai->rai_linkmtu = (uint32_t)val64;
+       if (rai->rai_linkmtu == 0) {
                char *mtustr;
 
                if ((mtustr = (char *)agetstr("mtu", &bp)) &&
                    strcmp(mtustr, "auto") == 0)
-                       tmp->linkmtu = tmp->phymtu;
+                       rai->rai_linkmtu = ifi->ifi_phymtu;
        }
-       else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) {
+       else if (rai->rai_linkmtu < IPV6_MMTU ||
+           rai->rai_linkmtu > ifi->ifi_phymtu) {
                syslog(LOG_ERR,
-                      "<%s> advertised link mtu must be between"
-                      " least MTU and physical link MTU",
-                      __func__);
-               exit(1);
+                   "<%s> advertised link mtu (%" PRIu32 ") on %s is invalid (must "
+                   "be between least MTU (%d) and physical link MTU (%d)",
+                   __func__, rai->rai_linkmtu, ifi->ifi_ifname,
+                   IPV6_MMTU, ifi->ifi_phymtu);
+               goto getconfig_free_rai;
        }
 
-       /* route information */
+#ifdef SIOCSIFINFO_IN6
+       {
+               struct in6_ndireq ndi;
+               int s;
 
-       MAYHAVE(val, "routes", 0);
-       if (val < 0 || val > 0xffffffff) {
-               syslog(LOG_ERR,
-                      "<%s> number of route information improper", __func__);
-               exit(1);
-       }
-       tmp->routes = val;
-       for (i = 0; i < tmp->routes; i++) {
-               struct rtinfo *rti;
-               char entbuf[256];
-               int added = (tmp->routes > 1) ? 1 : 0;
-
-               /* allocate memory to store prefix information */
-               if ((rti = malloc(sizeof(struct rtinfo))) == NULL) {
-                       syslog(LOG_ERR,
-                              "<%s> can't allocate enough memory",
-                              __func__);
+               if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+                       syslog(LOG_ERR, "<%s> socket: %s", __func__,
+                           strerror(errno));
                        exit(1);
                }
-               memset(rti, 0, sizeof(*rti));
-
-               /* link into chain */
-               insque(rti, &tmp->route);
+               memset(&ndi, 0, sizeof(ndi));
+               strncpy(ndi.ifname, ifi->ifi_ifname, sizeof(ndi.ifname));
+               if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&ndi) < 0)
+                       syslog(LOG_INFO, "<%s> ioctl:SIOCGIFINFO_IN6 at %s: %s",
+                           __func__, ifi->ifi_ifname, strerror(errno));
+
+               /* reflect the RA info to the host variables in kernel */
+               ndi.ndi.chlim = rai->rai_hoplimit;
+               ndi.ndi.retrans = rai->rai_retranstimer;
+               ndi.ndi.basereachable = rai->rai_reachabletime;
+               if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&ndi) < 0)
+                       syslog(LOG_INFO, "<%s> ioctl:SIOCSIFINFO_IN6 at %s: %s",
+                           __func__, ifi->ifi_ifname, strerror(errno));
 
-               makeentry(entbuf, sizeof(entbuf), i, "rtrplen", added);
-               MAYHAVE(val, entbuf, 64);
-               if (val < 0 || val > 128) {
-                       syslog(LOG_ERR,
-                              "<%s> prefixlen out of range",
-                              __func__);
-                       exit(1);
-               }
-               rti->prefixlen = (int)val;
-
-               makeentry(entbuf, sizeof(entbuf), i, "rtrflags", added);
-               MAYHAVE(val, entbuf, 0);
-               rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
-               if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) {
-                       syslog(LOG_ERR, "<%s> invalid route preference",
-                              __func__);
-                       exit(1);
-               }
+               close(s);
+       }
+#endif
 
-               makeentry(entbuf, sizeof(entbuf), i, "rtrltime", added);
-               /*
-                * XXX: since default value of route lifetime is not defined in
-                * draft-draves-route-selection-01.txt, I took the default 
-                * value of valid lifetime of prefix as its default.
-                * It need be much considered.
-                */
-               MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
-               if (val64 < 0 || val64 > 0xffffffff) {
-                       syslog(LOG_ERR,
-                              "<%s> rtrltime out of range",
-                              __func__);
-                       exit(1);
-               }
-               rti->ltime = (u_int32_t)val64;
+       /* route information */
+       rai->rai_routes = 0;
+       for (i = -1; i < MAXROUTE; i++) {
+               struct rtinfo *rti;
 
-               makeentry(entbuf, sizeof(entbuf), i, "rtrprefix", added);
+               makeentry(entbuf, sizeof(entbuf), i, "rtprefix");
                addr = (char *)agetstr(entbuf, &bp);
                if (addr == NULL) {
-                       syslog(LOG_ERR,
-                              "<%s> need %s as an route for "
-                              "interface %s",
-                              __func__, entbuf, intface);
-                       exit(1);
+                       makeentry(oentbuf, sizeof(oentbuf), i, "rtrprefix");
+                       addr = (char *)agetstr(oentbuf, &bp);
+                       if (addr)
+                               fprintf(stderr, "%s was obsoleted.  Use %s.\n",
+                                   oentbuf, entbuf);
                }
-               if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) {
-                       syslog(LOG_ERR,
-                              "<%s> inet_pton failed for %s",
-                              __func__, addr);
-                       exit(1);
+               if (addr == NULL)
+                       continue;
+
+               /* allocate memory to store prefix information */
+               ELM_MALLOC(rti, exit(1));
+
+               if (inet_pton(AF_INET6, addr, &rti->rti_prefix) != 1) {
+                       syslog(LOG_ERR, "<%s> inet_pton failed for %s",
+                           __func__, addr);
+                       goto getconfig_free_rti;
                }
 #if 0
                /*
                 * XXX: currently there's no restriction in route information
-                * prefix according to draft-draves-route-selection-01.txt,
-                * however I think the similar restriction be necessary.
+                * prefix according to
+                * draft-ietf-ipngwg-router-selection-00.txt.
+                * However, I think the similar restriction be necessary.
                 */
                MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
                if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) {
                        syslog(LOG_ERR,
-                              "<%s> multicast route (%s) must "
-                              "not be advertised (IF=%s)",
-                              __func__, addr, intface);
-                       exit(1);
+                           "<%s> multicast route (%s) must "
+                           "not be advertised on %s",
+                           __func__, addr, ifi->ifi_ifname);
+                       goto getconfig_free_rti;
                }
                if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) {
                        syslog(LOG_NOTICE,
-                              "<%s> link-local route (%s) must "
-                              "not be advertised on %s",
-                              __func__, addr, intface);
-                       exit(1);
+                           "<%s> link-local route (%s) will "
+                           "be advertised on %s",
+                           __func__, addr, ifi->ifi_ifname);
+                       goto getconfig_free_rti;
                }
 #endif
+
+               makeentry(entbuf, sizeof(entbuf), i, "rtplen");
+               /* XXX: 256 is a magic number for compatibility check. */
+               MAYHAVE(val, entbuf, 256);
+               if (val == 256) {
+                       makeentry(oentbuf, sizeof(oentbuf), i, "rtrplen");
+                       MAYHAVE(val, oentbuf, 256);
+                       if (val != 256)
+                               fprintf(stderr, "%s was obsoleted.  Use %s.\n",
+                                   oentbuf, entbuf);
+                       else
+                               val = 64;
+               }
+               if (val < 0 || val > 128) {
+                       syslog(LOG_ERR, "<%s> prefixlen (%" PRIu32 ") for %s on %s "
+                           "out of range",
+                           __func__, val, addr, ifi->ifi_ifname);
+                       goto getconfig_free_rti;
+               }
+               rti->rti_prefixlen = (int)val;
+
+               makeentry(entbuf, sizeof(entbuf), i, "rtflags");
+               if ((flagstr = (char *)agetstr(entbuf, &bp))) {
+                       val = 0;
+                       if (strchr(flagstr, 'h'))
+                               val |= ND_RA_FLAG_RTPREF_HIGH;
+                       if (strchr(flagstr, 'l')) {
+                               if ((val & ND_RA_FLAG_RTPREF_HIGH)) {
+                                       syslog(LOG_ERR,
+                                           "<%s> the \'h\' and \'l\' route"
+                                           " preferences are exclusive",
+                                           __func__);
+                                       goto getconfig_free_rti;
+                               }
+                               val |= ND_RA_FLAG_RTPREF_LOW;
+                       }
+               } else
+                       MAYHAVE(val, entbuf, 256); /* XXX */
+               if (val == 256) {
+                       makeentry(oentbuf, sizeof(oentbuf), i, "rtrflags");
+                       MAYHAVE(val, oentbuf, 256);
+                       if (val != 256) {
+                               fprintf(stderr, "%s was obsoleted.  Use %s.\n",
+                                   oentbuf, entbuf);
+                       } else
+                               val = 0;
+               }
+               rti->rti_rtpref = val & ND_RA_FLAG_RTPREF_MASK;
+               if (rti->rti_rtpref == ND_RA_FLAG_RTPREF_RSV) {
+                       syslog(LOG_ERR, "<%s> invalid route preference (%02x) "
+                           "for %s/%d on %s",
+                           __func__, rti->rti_rtpref, addr,
+                           rti->rti_prefixlen, ifi->ifi_ifname);
+                       goto getconfig_free_rti;
+               }
+
+               /*
+                * Since the spec does not a default value, we should make
+                * this entry mandatory.  However, FreeBSD 4.4 has shipped
+                * with this field being optional, we use the router lifetime
+                * as an ad-hoc default value with a warning message.
+                */
+               makeentry(entbuf, sizeof(entbuf), i, "rtltime");
+               MAYHAVE(val64, entbuf, -1);
+               if (val64 == -1) {
+                       makeentry(oentbuf, sizeof(oentbuf), i, "rtrltime");
+                       MAYHAVE(val64, oentbuf, -1);
+                       if (val64 != -1)
+                               fprintf(stderr, "%s was obsoleted.  Use %s.\n",
+                                   oentbuf, entbuf);
+                       else {
+                               fprintf(stderr, "%s should be specified "
+                                   "for interface %s.\n", entbuf,
+                                   ifi->ifi_ifname);
+                               val64 = rai->rai_lifetime;
+                       }
+               }
+               if (val64 < 0 || val64 > 0xffffffff) {
+                       syslog(LOG_ERR, "<%s> route lifetime (%" PRIu64 ") for "
+                           "%s/%d on %s out of range", __func__,
+                           val64, addr, rti->rti_prefixlen,
+                           ifi->ifi_ifname);
+                       goto getconfig_free_rti;
+               }
+               rti->rti_ltime = (uint32_t)val64;
+
+               /* link into chain */
+               TAILQ_INSERT_TAIL(&rai->rai_route, rti, rti_next);
+               rai->rai_routes++;
+               continue;
+getconfig_free_rti:
+               free(rti);
+       }
+
+       /* DNS server and DNS search list information */
+       for (i = -1; i < MAXRDNSSENT ; i++) {
+               struct rdnss *rdn;
+               struct rdnss_addr *rdna;
+               char *ap;
+               int c;
+
+               makeentry(entbuf, sizeof(entbuf), i, "rdnss");
+               addr = (char *)agetstr(entbuf, &bp);
+               if (addr == NULL)
+                       break;
+               ELM_MALLOC(rdn, exit(1));
+
+               TAILQ_INIT(&rdn->rd_list);
+
+               for (ap = addr; ap - addr < (ssize_t)strlen(addr); ap += c+1) {
+                       c = strcspn(ap, ",");
+                       strncpy(abuf, ap, c);
+                       abuf[c] = '\0';
+                       ELM_MALLOC(rdna, goto getconfig_free_rdn);
+                       if (inet_pton(AF_INET6, abuf, &rdna->ra_dns) != 1) {
+                               syslog(LOG_ERR, "<%s> inet_pton failed for %s",
+                                   __func__, abuf);
+                               free(rdna);
+                               goto getconfig_free_rdn;
+                       }
+                       TAILQ_INSERT_TAIL(&rdn->rd_list, rdna, ra_next);
+               }
+
+               makeentry(entbuf, sizeof(entbuf), i, "rdnssltime");
+               MAYHAVE(val, entbuf, (rai->rai_maxinterval * 3 / 2));
+               if ((uint16_t)val < rai->rai_maxinterval ||
+                   (uint16_t)val > rai->rai_maxinterval * 2) {
+                       syslog(LOG_ERR, "%s (%" PRIu16 ") on %s is invalid "
+                           "(must be between %d and %d)",
+                           entbuf, val, ifi->ifi_ifname, rai->rai_maxinterval,
+                           rai->rai_maxinterval * 2);
+                       goto getconfig_free_rdn;
+               }
+               rdn->rd_ltime = val;
+
+               /* link into chain */
+               TAILQ_INSERT_TAIL(&rai->rai_rdnss, rdn, rd_next);
+               continue;
+getconfig_free_rdn:
+               while ((rdna = TAILQ_FIRST(&rdn->rd_list)) != NULL) {
+                       TAILQ_REMOVE(&rdn->rd_list, rdna, ra_next);
+                       free(rdna);
+               }
+               free(rdn);
        }
 
-       /* okey */
-       tmp->next = ralist;
-       ralist = tmp;
+       for (i = -1; i < MAXDNSSLENT ; i++) {
+               struct dnssl *dns;
+               struct dnssl_addr *dnsa;
+               char *ap;
+               int c;
+
+               makeentry(entbuf, sizeof(entbuf), i, "dnssl");
+               addr = (char *)agetstr(entbuf, &bp);
+               if (addr == NULL)
+                       break;
+
+               ELM_MALLOC(dns, exit(1));
+
+               TAILQ_INIT(&dns->dn_list);
+
+               for (ap = addr; ap - addr < (ssize_t)strlen(addr); ap += c+1) {
+                       c = strcspn(ap, ",");
+                       strncpy(abuf, ap, c);
+                       abuf[c] = '\0';
+                       ELM_MALLOC(dnsa, goto getconfig_free_dns);
+                       dnsa->da_len = dname_labelenc(dnsa->da_dom, abuf);
+                       syslog(LOG_DEBUG, "<%s>: dnsa->da_len = %d", __func__,
+                           dnsa->da_len);
+                       TAILQ_INSERT_TAIL(&dns->dn_list, dnsa, da_next);
+               }
+
+               makeentry(entbuf, sizeof(entbuf), i, "dnsslltime");
+               MAYHAVE(val, entbuf, (rai->rai_maxinterval * 3 / 2));
+               if ((uint16_t)val < rai->rai_maxinterval ||
+                   (uint16_t)val > rai->rai_maxinterval * 2) {
+                       syslog(LOG_ERR, "%s (%" PRIu16 ") on %s is invalid "
+                           "(must be between %d and %d)",
+                           entbuf, val, ifi->ifi_ifname, rai->rai_maxinterval,
+                           rai->rai_maxinterval * 2);
+                       goto getconfig_free_dns;
+               }
+               dns->dn_ltime = val;
 
+               /* link into chain */
+               TAILQ_INSERT_TAIL(&rai->rai_dnssl, dns, dn_next);
+               continue;
+getconfig_free_dns:
+               while ((dnsa = TAILQ_FIRST(&dns->dn_list)) != NULL) {
+                       TAILQ_REMOVE(&dns->dn_list, dnsa, da_next);
+                       free(dnsa);
+               }
+               free(dns);
+       }
        /* construct the sending packet */
-       make_packet(tmp);
+       make_packet(rai);
+
+       /*
+        * If an entry with the same ifindex exists, remove it first.
+        * Before the removal, RDNSS and DNSSL options with
+        * zero-lifetime will be sent.
+        */
+       switch (ifi->ifi_state) {
+       case IFI_STATE_UNCONFIGURED:
+               /* UNCONFIGURED -> TRANSITIVE */
 
-       /* set timer */
-       tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
-                                     tmp, tmp);
-       ra_timer_update((void *)tmp, &tmp->timer->tm);
-       rtadvd_set_timer(&tmp->timer->tm, tmp->timer);
+               error = sock_mc_join(&sock, ifi->ifi_ifindex);
+               if (error)
+                       exit(1);
+
+               ifi->ifi_state = IFI_STATE_TRANSITIVE;
+               ifi->ifi_burstcount = MAX_INITIAL_RTR_ADVERTISEMENTS;
+               ifi->ifi_burstinterval = MAX_INITIAL_RTR_ADVERT_INTERVAL;
+
+               /* The same two rai mean initial burst */
+               ifi->ifi_rainfo = rai;
+               ifi->ifi_rainfo_trans = rai;
+               TAILQ_INSERT_TAIL(&railist, rai, rai_next);
+
+               if (ifi->ifi_ra_timer == NULL)
+                       ifi->ifi_ra_timer = rtadvd_add_timer(ra_timeout,
+                           ra_timer_update, ifi, ifi);
+               ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm);
+               rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm,
+                   ifi->ifi_ra_timer);
+
+               syslog(LOG_DEBUG,
+                   "<%s> ifname=%s marked as TRANSITIVE (initial burst).",
+                   __func__, ifi->ifi_ifname);
+               break;
+       case IFI_STATE_CONFIGURED:
+               /* CONFIGURED -> TRANSITIVE */
+               rai_old = ifi->ifi_rainfo;
+               if (rai_old == NULL) {
+                       syslog(LOG_ERR,
+                           "<%s> ifi_rainfo is NULL"
+                           " in IFI_STATE_CONFIGURED.", __func__);
+                       ifi = NULL;
+                       break;
+               } else {
+                       struct rdnss *rdn;
+                       struct dnssl *dns;
+
+                       rai_old->rai_lifetime = 0;
+                       TAILQ_FOREACH(rdn, &rai_old->rai_rdnss, rd_next)
+                           rdn->rd_ltime = 0;
+                       TAILQ_FOREACH(dns, &rai_old->rai_dnssl, dn_next)
+                           dns->dn_ltime = 0;
+
+                       ifi->ifi_rainfo_trans = rai_old;
+                       ifi->ifi_state = IFI_STATE_TRANSITIVE;
+                       ifi->ifi_burstcount = MAX_FINAL_RTR_ADVERTISEMENTS;
+                       ifi->ifi_burstinterval = MIN_DELAY_BETWEEN_RAS;
+
+                       ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm);
+                       rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm,
+                           ifi->ifi_ra_timer);
+
+                       syslog(LOG_DEBUG,
+                           "<%s> ifname=%s marked as TRANSITIVE"
+                           " (transitional burst)",
+                           __func__, ifi->ifi_ifname);
+               }
+               ifi->ifi_rainfo = rai;
+               TAILQ_INSERT_TAIL(&railist, rai, rai_next);
+               break;
+       case IFI_STATE_TRANSITIVE:
+               if (ifi->ifi_rainfo != NULL) {
+                       if (ifi->ifi_rainfo == ifi->ifi_rainfo_trans) {
+                               /* Reinitialize initial burst */
+                               rm_rainfo(ifi->ifi_rainfo);
+                               ifi->ifi_rainfo = rai;
+                               ifi->ifi_rainfo_trans = rai;
+                               ifi->ifi_burstcount =
+                                   MAX_INITIAL_RTR_ADVERTISEMENTS;
+                               ifi->ifi_burstinterval =
+                                   MAX_INITIAL_RTR_ADVERT_INTERVAL;
+                       } else {
+                               /* Replace ifi_rainfo with the new one */
+                               rm_rainfo(ifi->ifi_rainfo);
+                               ifi->ifi_rainfo = rai;
+                       }
+                       TAILQ_INSERT_TAIL(&railist, rai, rai_next);
+
+                       ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm);
+                       rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm,
+                           ifi->ifi_ra_timer);
+               } else {
+                       /* XXX: NOTREACHED.  Being shut down. */
+                       syslog(LOG_ERR,
+                           "<%s> %s is shutting down.  Skipped.",
+                           __func__, ifi->ifi_ifname);
+                       rm_rainfo(rai);
+
+                       return (NULL);
+               }
+               break;
+       }
+
+       return (ifi);
+
+getconfig_free_rai:
+       free(rai);
+       return (NULL);
 }
 
-static void
+void
 get_prefix(struct rainfo *rai)
 {
        struct ifaddrs *ifap, *ifa;
-       struct prefix *pp;
+       struct prefix *pfx;
        struct in6_addr *a;
-       u_char *p, *ep, *m, *lim;
-       u_char ntopbuf[INET6_ADDRSTRLEN];
+       struct ifinfo *ifi;
+       char *p, *ep, *m, *lim;
+       char ntopbuf[INET6_ADDRSTRLEN];
 
        if (getifaddrs(&ifap) < 0) {
                syslog(LOG_ERR,
-                      "<%s> can't get interface addresses",
-                      __func__);
+                   "<%s> can't get interface addresses",
+                   __func__);
                exit(1);
        }
+       ifi = rai->rai_ifinfo;
+
        for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
                int plen;
 
-               if (strcmp(ifa->ifa_name, rai->ifname) != 0)
+               if (strcmp(ifa->ifa_name, ifi->ifi_ifname) != 0)
                        continue;
                if (ifa->ifa_addr->sa_family != AF_INET6)
                        continue;
@@ -577,128 +1035,104 @@ get_prefix(struct rainfo *rai)
                        continue;
 
                /* get prefix length */
-               m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
-               lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len;
+               m = (char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
+               lim = (char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len;
                plen = prefixlen(m, lim);
-               if (plen < 0 || plen > 128) {
+               if (plen <= 0 || plen > 128) {
                        syslog(LOG_ERR, "<%s> failed to get prefixlen "
-                              "or prefix is invalid",
-                              __func__);
+                           "or prefix is invalid",
+                           __func__);
                        exit(1);
                }
+               if (plen == 128)        /* XXX */
+                       continue;
                if (find_prefix(rai, a, plen)) {
                        /* ignore a duplicated prefix. */
                        continue;
                }
 
                /* allocate memory to store prefix info. */
-               if ((pp = malloc(sizeof(*pp))) == NULL) {
-                       syslog(LOG_ERR,
-                              "<%s> can't get allocate buffer for prefix",
-                              __func__);
-                       exit(1);
-               }
-               memset(pp, 0, sizeof(*pp));
+               ELM_MALLOC(pfx, exit(1));
 
                /* set prefix, sweep bits outside of prefixlen */
-               pp->prefixlen = plen;
-               memcpy(&pp->prefix, a, sizeof(*a));
-               p = (u_char *)&pp->prefix;
-               ep = (u_char *)(&pp->prefix + 1);
-               while (m < lim)
+               pfx->pfx_prefixlen = plen;
+               memcpy(&pfx->pfx_prefix, a, sizeof(*a));
+               p = (char *)&pfx->pfx_prefix;
+               ep = (char *)(&pfx->pfx_prefix + 1);
+               while (m < lim && p < ep)
                        *p++ &= *m++;
                while (p < ep)
                        *p++ = 0x00;
-
-               if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf,
+               if (!inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf,
                    sizeof(ntopbuf))) {
                        syslog(LOG_ERR, "<%s> inet_ntop failed", __func__);
                        exit(1);
                }
                syslog(LOG_DEBUG,
-                      "<%s> add %s/%d to prefix list on %s",
-                      __func__, ntopbuf, pp->prefixlen, rai->ifname);
+                   "<%s> add %s/%d to prefix list on %s",
+                   __func__, ntopbuf, pfx->pfx_prefixlen, ifi->ifi_ifname);
 
                /* set other fields with protocol defaults */
-               pp->validlifetime = DEF_ADVVALIDLIFETIME;
-               pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
-               pp->onlinkflg = 1;
-               pp->autoconfflg = 1;
-               pp->origin = PREFIX_FROM_KERNEL;
+               pfx->pfx_validlifetime = DEF_ADVVALIDLIFETIME;
+               pfx->pfx_preflifetime = DEF_ADVPREFERREDLIFETIME;
+               pfx->pfx_onlinkflg = 1;
+               pfx->pfx_autoconfflg = 1;
+               pfx->pfx_origin = PREFIX_FROM_KERNEL;
+               pfx->pfx_rainfo = rai;
 
                /* link into chain */
-               insque(pp, &rai->prefix);
+               TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next);
 
                /* counter increment */
-               rai->pfxs++;
+               rai->rai_pfxs++;
        }
 
        freeifaddrs(ifap);
 }
 
 static void
-makeentry(char *buf, size_t len, int id, char *string, int add)
+makeentry(char *buf, size_t len, int id, const char *string)
 {
-       char *ep = buf + len;
-
-       strcpy(buf, string);
-       if (add) {
-               char *cp;
 
-               cp = (char *)index(buf, '\0');
-               snprintf(cp, ep - cp, "%d", id);
-       }
+       if (id < 0)
+               strlcpy(buf, string, len);
+       else
+               snprintf(buf, len, "%s%d", string, id);
 }
 
 /*
  * Add a prefix to the list of specified interface and reconstruct
  * the outgoing packet.
  * The prefix must not be in the list.
- * XXX: other parameter of the prefix(e.g. lifetime) shoule be
+ * XXX: other parameters of the prefix (e.g. lifetime) should be
  * able to be specified.
  */
 static void
 add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
 {
-       struct prefix *prefix;
-       u_char ntopbuf[INET6_ADDRSTRLEN];
-
-       if ((prefix = malloc(sizeof(*prefix))) == NULL) {
-               syslog(LOG_ERR, "<%s> memory allocation failed",
-                      __func__);
-               return;         /* XXX: error or exit? */
-       }
-       memset(prefix, 0, sizeof(*prefix));
-       prefix->prefix = ipr->ipr_prefix.sin6_addr;
-       prefix->prefixlen = ipr->ipr_plen;
-       prefix->validlifetime = ipr->ipr_vltime;
-       prefix->preflifetime = ipr->ipr_pltime;
-       prefix->onlinkflg = ipr->ipr_raf_onlink;
-       prefix->autoconfflg = ipr->ipr_raf_auto;
-       prefix->origin = PREFIX_FROM_DYNAMIC;
-
-       insque(prefix, &rai->prefix);
-       prefix->rainfo = rai;
+       struct prefix *pfx;
+       struct ifinfo *ifi;
+       char ntopbuf[INET6_ADDRSTRLEN];
+
+       ifi = rai->rai_ifinfo;
+       ELM_MALLOC(pfx, return);
+       pfx->pfx_prefix = ipr->ipr_prefix.sin6_addr;
+       pfx->pfx_prefixlen = ipr->ipr_plen;
+       pfx->pfx_validlifetime = ipr->ipr_vltime;
+       pfx->pfx_preflifetime = ipr->ipr_pltime;
+       pfx->pfx_onlinkflg = ipr->ipr_raf_onlink;
+       pfx->pfx_autoconfflg = ipr->ipr_raf_auto;
+       pfx->pfx_origin = PREFIX_FROM_DYNAMIC;
+       pfx->pfx_rainfo = rai;
+
+       TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next);
 
        syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
-              __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
-                                      ntopbuf, INET6_ADDRSTRLEN),
-              ipr->ipr_plen, rai->ifname);
-
-       /* free the previous packet */
-       free(rai->ra_data);
-       rai->ra_data = NULL;
-
-       /* reconstruct the packet */
-       rai->pfxs++;
-       make_packet(rai);
+           __func__,
+           inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
+               sizeof(ntopbuf)), ipr->ipr_plen, ifi->ifi_ifname);
 
-       /*
-        * reset the timer so that the new prefix will be advertised quickly.
-        */
-       rai->initcounter = 0;
-       ra_timer_update((void *)rai, &rai->timer->tm);
-       rtadvd_set_timer(&rai->timer->tm, rai->timer);
+       rai->rai_pfxs++;
 }
 
 /*
@@ -707,30 +1141,37 @@ add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
  * The prefix must be in the list.
  */
 void
-delete_prefix(struct prefix *prefix)
+delete_prefix(struct prefix *pfx)
 {
-       u_char ntopbuf[INET6_ADDRSTRLEN];
-       struct rainfo *rai = prefix->rainfo;
+       struct rainfo *rai;
+       struct ifinfo *ifi;
+       char ntopbuf[INET6_ADDRSTRLEN];
 
-       remque(prefix);
+       rai = pfx->pfx_rainfo;
+       ifi = rai->rai_ifinfo;
+       TAILQ_REMOVE(&rai->rai_prefix, pfx, pfx_next);
        syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
-              __func__, inet_ntop(AF_INET6, &prefix->prefix,
-                                      ntopbuf, INET6_ADDRSTRLEN),
-              prefix->prefixlen, rai->ifname);
-       if (prefix->timer)
-               rtadvd_remove_timer(&prefix->timer);
-       free(prefix);
-       rai->pfxs--;
+           __func__,
+           inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf,
+               sizeof(ntopbuf)), pfx->pfx_prefixlen, ifi->ifi_ifname);
+       if (pfx->pfx_timer)
+               rtadvd_remove_timer(pfx->pfx_timer);
+       free(pfx);
+
+       rai->rai_pfxs--;
 }
 
 void
-invalidate_prefix(struct prefix *prefix)
+invalidate_prefix(struct prefix *pfx)
 {
-       u_char ntopbuf[INET6_ADDRSTRLEN];
-       struct timeval timo;
-       struct rainfo *rai = prefix->rainfo;
-
-       if (prefix->timer) {    /* sanity check */
+       struct timespec timo;
+       struct rainfo *rai;
+       struct ifinfo *ifi;
+       char ntopbuf[INET6_ADDRSTRLEN];
+
+       rai = pfx->pfx_rainfo;
+       ifi = rai->rai_ifinfo;
+       if (pfx->pfx_timer) {   /* sanity check */
                syslog(LOG_ERR,
                    "<%s> assumption failure: timer already exists",
                    __func__);
@@ -739,38 +1180,40 @@ invalidate_prefix(struct prefix *prefix)
 
        syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, "
            "will expire in %ld seconds", __func__,
-           inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, INET6_ADDRSTRLEN),
-           prefix->prefixlen, rai->ifname, (long)prefix_timo);
+           inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, sizeof(ntopbuf)),
+           pfx->pfx_prefixlen, ifi->ifi_ifname, (long)prefix_timo);
 
        /* set the expiration timer */
-       prefix->timer = rtadvd_add_timer(prefix_timeout, NULL, prefix, NULL);
-       if (prefix->timer == NULL) {
+       pfx->pfx_timer = rtadvd_add_timer(prefix_timeout, NULL, pfx, NULL);
+       if (pfx->pfx_timer == NULL) {
                syslog(LOG_ERR, "<%s> failed to add a timer for a prefix. "
                    "remove the prefix", __func__);
-               delete_prefix(prefix);
+               delete_prefix(pfx);
        }
        timo.tv_sec = prefix_timo;
-       timo.tv_usec = 0;
-       rtadvd_set_timer(&timo, prefix->timer);
+       timo.tv_nsec = 0;
+       rtadvd_set_timer(&timo, pfx->pfx_timer);
 }
 
 static struct rtadvd_timer *
 prefix_timeout(void *arg)
 {
-       struct prefix *prefix = (struct prefix *)arg;
-       
-       delete_prefix(prefix);
 
-       return(NULL);
+       delete_prefix((struct prefix *)arg);
+
+       return (NULL);
 }
 
 void
-update_prefix(struct prefix * prefix)
+update_prefix(struct prefix *pfx)
 {
-       u_char ntopbuf[INET6_ADDRSTRLEN];
-       struct rainfo *rai = prefix->rainfo;
+       struct rainfo *rai;
+       struct ifinfo *ifi;
+       char ntopbuf[INET6_ADDRSTRLEN];
 
-       if (prefix->timer == NULL) { /* sanity check */
+       rai = pfx->pfx_rainfo;
+       ifi = rai->rai_ifinfo;
+       if (pfx->pfx_timer == NULL) { /* sanity check */
                syslog(LOG_ERR,
                    "<%s> assumption failure: timer does not exist",
                    __func__);
@@ -778,11 +1221,12 @@ update_prefix(struct prefix * prefix)
        }
 
        syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s",
-           __func__, inet_ntop(AF_INET6, &prefix->prefix, ntopbuf,
-           INET6_ADDRSTRLEN), prefix->prefixlen, rai->ifname);
+           __func__, inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf,
+               sizeof(ntopbuf)), pfx->pfx_prefixlen, ifi->ifi_ifname);
 
        /* stop the expiration timer */
-       rtadvd_remove_timer(&prefix->timer);
+       rtadvd_remove_timer(pfx->pfx_timer);
+       pfx->pfx_timer = NULL;
 }
 
 /*
@@ -798,13 +1242,13 @@ init_prefix(struct in6_prefixreq *ipr)
 
        if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
                syslog(LOG_ERR, "<%s> socket: %s", __func__,
-                      strerror(errno));
+                   strerror(errno));
                exit(1);
        }
 
        if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
                syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __func__,
-                      strerror(errno));
+                   strerror(errno));
 
                ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
                ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
@@ -813,25 +1257,25 @@ init_prefix(struct in6_prefixreq *ipr)
                /* omit other field initialization */
        }
        else if (ipr->ipr_origin < PR_ORIG_RR) {
-               u_char ntopbuf[INET6_ADDRSTRLEN];
+               char ntopbuf[INET6_ADDRSTRLEN];
 
                syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is"
-                      "lower than PR_ORIG_RR(router renumbering)."
-                      "This should not happen if I am router", __func__,
-                      inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
-                                sizeof(ntopbuf)), ipr->ipr_origin);
+                   "lower than PR_ORIG_RR(router renumbering)."
+                   "This should not happen if I am router", __func__,
+                   inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
+                       sizeof(ntopbuf)), ipr->ipr_origin);
                close(s);
-               return 1;
+               return (1);
        }
 
        close(s);
-       return 0;
+       return (0);
 #else
        ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
        ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
        ipr->ipr_raf_onlink = 1;
        ipr->ipr_raf_auto = 1;
-        return 0;
+       return (0);
 #endif
 }
 
@@ -842,9 +1286,9 @@ make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
 
        memset(&ipr, 0, sizeof(ipr));
        if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
-               syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
-                      "exist. This should not happen! %s", __func__,
-                      ifindex, strerror(errno));
+               syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't "
+                   "exist. This should not happen! %s", __func__,
+                   ifindex, strerror(errno));
                exit(1);
        }
        ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
@@ -858,65 +1302,79 @@ make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
 }
 
 void
-make_packet(struct rainfo *rainfo)
+make_packet(struct rainfo *rai)
 {
        size_t packlen, lladdroptlen = 0;
        char *buf;
        struct nd_router_advert *ra;
        struct nd_opt_prefix_info *ndopt_pi;
        struct nd_opt_mtu *ndopt_mtu;
-#ifdef MIP6
-       struct nd_opt_advinterval *ndopt_advint;
-       struct nd_opt_homeagent_info *ndopt_hai;
-#endif
        struct nd_opt_route_info *ndopt_rti;
-       struct prefix *pfx;
        struct rtinfo *rti;
+       struct nd_opt_rdnss *ndopt_rdnss;
+       struct rdnss *rdn;
+       struct nd_opt_dnssl *ndopt_dnssl;
+       struct dnssl *dns;
+       size_t len;
+       struct prefix *pfx;
+       struct ifinfo *ifi;
 
+       ifi = rai->rai_ifinfo;
        /* calculate total length */
        packlen = sizeof(struct nd_router_advert);
-       if (rainfo->advlinkopt) {
-               if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
+       if (rai->rai_advlinkopt) {
+               if ((lladdroptlen = lladdropt_length(&ifi->ifi_sdl)) == 0) {
                        syslog(LOG_INFO,
-                              "<%s> link-layer address option has"
-                              " null length on %s."
-                              " Treat as not included.",
-                              __func__, rainfo->ifname);
-                       rainfo->advlinkopt = 0;
+                           "<%s> link-layer address option has"
+                           " null length on %s.  Treat as not included.",
+                           __func__, ifi->ifi_ifname);
+                       rai->rai_advlinkopt = 0;
                }
                packlen += lladdroptlen;
        }
-       if (rainfo->pfxs)
-               packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
-       if (rainfo->linkmtu)
+       if (rai->rai_pfxs)
+               packlen += sizeof(struct nd_opt_prefix_info) * rai->rai_pfxs;
+       if (rai->rai_linkmtu)
                packlen += sizeof(struct nd_opt_mtu);
-#ifdef MIP6
-       if (mobileip6 && rainfo->maxinterval)
-               packlen += sizeof(struct nd_opt_advinterval);
-       if (mobileip6 && rainfo->hatime)
-               packlen += sizeof(struct nd_opt_homeagent_info);
-#endif
-#ifdef ND_OPT_ROUTE_INFO
-       for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next)
-               packlen += sizeof(struct nd_opt_route_info) + 
-                          ((rti->prefixlen + 0x3f) >> 6) * 8;
-#endif
 
+       TAILQ_FOREACH(rti, &rai->rai_route, rti_next)
+               packlen += sizeof(struct nd_opt_route_info) +
+                          ((rti->rti_prefixlen + 0x3f) >> 6) * 8;
+
+       TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) {
+               struct rdnss_addr *rdna;
+
+               packlen += sizeof(struct nd_opt_rdnss);
+               TAILQ_FOREACH(rdna, &rdn->rd_list, ra_next)
+                       packlen += sizeof(rdna->ra_dns);
+       }
+       TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) {
+               struct dnssl_addr *dnsa;
+
+               packlen += sizeof(struct nd_opt_dnssl);
+               len = 0;
+               TAILQ_FOREACH(dnsa, &dns->dn_list, da_next)
+                       len += dnsa->da_len;
+
+               /* A zero octet and 8 octet boundary */
+               len++;
+               len += (len % 8) ? 8 - len % 8 : 0;
+
+               packlen += len;
+       }
        /* allocate memory for the packet */
        if ((buf = malloc(packlen)) == NULL) {
                syslog(LOG_ERR,
-                      "<%s> can't get enough memory for an RA packet",
-                      __func__);
+                   "<%s> can't get enough memory for an RA packet",
+                   __func__);
                exit(1);
        }
-       if (rainfo->ra_data) {
-               /* free the previous packet */
-               free(rainfo->ra_data);
-               rainfo->ra_data = NULL;
-       }
-       rainfo->ra_data = buf;
+       memset(buf, 0, packlen);
+       if (rai->rai_ra_data)   /* Free old data if any. */
+               free(rai->rai_ra_data);
+       rai->rai_ra_data = buf;
        /* XXX: what if packlen > 576? */
-       rainfo->ra_datalen = packlen;
+       rai->rai_ra_datalen = packlen;
 
        /*
         * construct the packet
@@ -925,104 +1383,70 @@ make_packet(struct rainfo *rainfo)
        ra->nd_ra_type = ND_ROUTER_ADVERT;
        ra->nd_ra_code = 0;
        ra->nd_ra_cksum = 0;
-       ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit);
+       ra->nd_ra_curhoplimit = (uint8_t)(0xff & rai->rai_hoplimit);
        ra->nd_ra_flags_reserved = 0; /* just in case */
        /*
         * XXX: the router preference field, which is a 2-bit field, should be
         * initialized before other fields.
         */
-       ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref;
+       ra->nd_ra_flags_reserved = 0xff & rai->rai_rtpref;
        ra->nd_ra_flags_reserved |=
-               rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
+               rai->rai_managedflg ? ND_RA_FLAG_MANAGED : 0;
        ra->nd_ra_flags_reserved |=
-               rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
-#ifdef MIP6
-       ra->nd_ra_flags_reserved |=
-               rainfo->haflg ? ND_RA_FLAG_HA : 0;
-#endif
-       ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
-       ra->nd_ra_reachable = htonl(rainfo->reachabletime);
-       ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
+               rai->rai_otherflg ? ND_RA_FLAG_OTHER : 0;
+       ra->nd_ra_router_lifetime = htons(rai->rai_lifetime);
+       ra->nd_ra_reachable = htonl(rai->rai_reachabletime);
+       ra->nd_ra_retransmit = htonl(rai->rai_retranstimer);
        buf += sizeof(*ra);
 
-       if (rainfo->advlinkopt) {
-               lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
+       if (rai->rai_advlinkopt) {
+               lladdropt_fill(&ifi->ifi_sdl, (struct nd_opt_hdr *)buf);
                buf += lladdroptlen;
        }
 
-       if (rainfo->linkmtu) {
+       if (rai->rai_linkmtu) {
                ndopt_mtu = (struct nd_opt_mtu *)buf;
                ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
                ndopt_mtu->nd_opt_mtu_len = 1;
                ndopt_mtu->nd_opt_mtu_reserved = 0;
-               ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu);
+               ndopt_mtu->nd_opt_mtu_mtu = htonl(rai->rai_linkmtu);
                buf += sizeof(struct nd_opt_mtu);
        }
 
-#ifdef MIP6
-       if (mobileip6 && rainfo->maxinterval) {
-               ndopt_advint = (struct nd_opt_advinterval *)buf;
-               ndopt_advint->nd_opt_adv_type = ND_OPT_ADVINTERVAL;
-               ndopt_advint->nd_opt_adv_len = 1;
-               ndopt_advint->nd_opt_adv_reserved = 0;
-               ndopt_advint->nd_opt_adv_interval = htonl(rainfo->maxinterval *
-                                                         1000);
-               buf += sizeof(struct nd_opt_advinterval);
-       }
-#endif
-       
-#ifdef MIP6
-       if (rainfo->hatime) {
-               ndopt_hai = (struct nd_opt_homeagent_info *)buf;
-               ndopt_hai->nd_opt_hai_type = ND_OPT_HOMEAGENT_INFO;
-               ndopt_hai->nd_opt_hai_len = 1;
-               ndopt_hai->nd_opt_hai_reserved = 0;
-               ndopt_hai->nd_opt_hai_preference = htons(rainfo->hapref);
-               ndopt_hai->nd_opt_hai_lifetime = htons(rainfo->hatime);
-               buf += sizeof(struct nd_opt_homeagent_info);
-       }
-#endif
-       
-       for (pfx = rainfo->prefix.next;
-            pfx != &rainfo->prefix; pfx = pfx->next) {
-               u_int32_t vltime, pltime;
-               struct timeval now;
+       TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) {
+               uint32_t vltime, pltime;
+               struct timespec now;
 
                ndopt_pi = (struct nd_opt_prefix_info *)buf;
                ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
                ndopt_pi->nd_opt_pi_len = 4;
-               ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
+               ndopt_pi->nd_opt_pi_prefix_len = pfx->pfx_prefixlen;
                ndopt_pi->nd_opt_pi_flags_reserved = 0;
-               if (pfx->onlinkflg)
+               if (pfx->pfx_onlinkflg)
                        ndopt_pi->nd_opt_pi_flags_reserved |=
                                ND_OPT_PI_FLAG_ONLINK;
-               if (pfx->autoconfflg)
+               if (pfx->pfx_autoconfflg)
                        ndopt_pi->nd_opt_pi_flags_reserved |=
                                ND_OPT_PI_FLAG_AUTO;
-#ifdef MIP6
-               if (pfx->routeraddr)
-                       ndopt_pi->nd_opt_pi_flags_reserved |=
-                               ND_OPT_PI_FLAG_ROUTER;
-#endif
-               if (pfx->timer)
+               if (pfx->pfx_timer)
                        vltime = 0;
                else {
-                       if (pfx->vltimeexpire || pfx->pltimeexpire)
-                               gettimeofday(&now, NULL);
-                       if (pfx->vltimeexpire == 0)
-                               vltime = pfx->validlifetime;
+                       if (pfx->pfx_vltimeexpire || pfx->pfx_pltimeexpire)
+                               clock_gettime(CLOCK_MONOTONIC_FAST, &now);
+                       if (pfx->pfx_vltimeexpire == 0)
+                               vltime = pfx->pfx_validlifetime;
                        else
-                               vltime = (pfx->vltimeexpire > now.tv_sec) ?
-                                   pfx->vltimeexpire - now.tv_sec : 0;
+                               vltime = ((time_t)pfx->pfx_vltimeexpire > now.tv_sec) ?
+                                   pfx->pfx_vltimeexpire - now.tv_sec : 0;
                }
-               if (pfx->timer)
+               if (pfx->pfx_timer)
                        pltime = 0;
                else {
-                       if (pfx->pltimeexpire == 0)
-                               pltime = pfx->preflifetime;
+                       if (pfx->pfx_pltimeexpire == 0)
+                               pltime = pfx->pfx_preflifetime;
                        else
-                               pltime = (pfx->pltimeexpire > now.tv_sec) ? 
-                                   pfx->pltimeexpire - now.tv_sec : 0;
+                               pltime = ((time_t)pfx->pfx_pltimeexpire > now.tv_sec) ?
+                                   pfx->pfx_pltimeexpire - now.tv_sec : 0;
                }
                if (vltime < pltime) {
                        /*
@@ -1034,45 +1458,72 @@ make_packet(struct rainfo *rainfo)
                ndopt_pi->nd_opt_pi_valid_time = htonl(vltime);
                ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime);
                ndopt_pi->nd_opt_pi_reserved2 = 0;
-               ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
+               ndopt_pi->nd_opt_pi_prefix = pfx->pfx_prefix;
 
                buf += sizeof(struct nd_opt_prefix_info);
        }
 
-#ifdef ND_OPT_ROUTE_INFO
-       for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) {
-               u_int8_t psize = (rti->prefixlen + 0x3f) >> 6;
+       TAILQ_FOREACH(rti, &rai->rai_route, rti_next) {
+               uint8_t psize = (rti->rti_prefixlen + 0x3f) >> 6;
 
                ndopt_rti = (struct nd_opt_route_info *)buf;
                ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO;
                ndopt_rti->nd_opt_rti_len = 1 + psize;
-               ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen;
-               ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref;
-               ndopt_rti->nd_opt_rti_lifetime = htonl(rti->ltime);
-               memcpy(ndopt_rti + 1, &rti->prefix, psize * 8);
+               ndopt_rti->nd_opt_rti_prefixlen = rti->rti_prefixlen;
+               ndopt_rti->nd_opt_rti_flags = 0xff & rti->rti_rtpref;
+               ndopt_rti->nd_opt_rti_lifetime = htonl(rti->rti_ltime);
+               memcpy(ndopt_rti + 1, &rti->rti_prefix, psize * 8);
                buf += sizeof(struct nd_opt_route_info) + psize * 8;
        }
-#endif
 
-       return;
-}
+       TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) {
+               struct rdnss_addr *rdna;
 
-static int
-getinet6sysctl(int code)
-{
-       int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
-       int value;
-       size_t size;
-
-       mib[3] = code;
-       size = sizeof(value);
-       if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
-           < 0) {
-               syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
-                      __func__, code,
-                      strerror(errno));
-               return(-1);
+               ndopt_rdnss = (struct nd_opt_rdnss *)buf;
+               ndopt_rdnss->nd_opt_rdnss_type = ND_OPT_RDNSS;
+               ndopt_rdnss->nd_opt_rdnss_len = 0;
+               ndopt_rdnss->nd_opt_rdnss_reserved = 0;
+               ndopt_rdnss->nd_opt_rdnss_lifetime = htonl(rdn->rd_ltime);
+               buf += sizeof(struct nd_opt_rdnss);
+
+               TAILQ_FOREACH(rdna, &rdn->rd_list, ra_next) {
+                       memcpy(buf, &rdna->ra_dns, sizeof(rdna->ra_dns));
+                       buf += sizeof(rdna->ra_dns);
+               }
+               /* Length field should be in 8 octets */
+               ndopt_rdnss->nd_opt_rdnss_len = (buf - (char *)ndopt_rdnss) / 8;
+
+               syslog(LOG_DEBUG, "<%s>: nd_opt_dnss_len = %d", __func__,
+                   ndopt_rdnss->nd_opt_rdnss_len);
        }
-       else
-               return(value);
+
+       TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) {
+               struct dnssl_addr *dnsa;
+
+               ndopt_dnssl = (struct nd_opt_dnssl *)buf;
+               ndopt_dnssl->nd_opt_dnssl_type = ND_OPT_DNSSL;
+               ndopt_dnssl->nd_opt_dnssl_len = 0;
+               ndopt_dnssl->nd_opt_dnssl_reserved = 0;
+               ndopt_dnssl->nd_opt_dnssl_lifetime = htonl(dns->dn_ltime);
+               buf += sizeof(*ndopt_dnssl);
+
+               TAILQ_FOREACH(dnsa, &dns->dn_list, da_next) {
+                       memcpy(buf, dnsa->da_dom, dnsa->da_len);
+                       buf += dnsa->da_len;
+               }
+
+               /* A zero octet after encoded DNS server list. */
+               *buf++ = '\0';
+
+               /* Padding to next 8 octets boundary */
+               len = buf - (char *)ndopt_dnssl;
+               len += (len % 8) ? 8 - len % 8 : 0;
+
+               /* Length field must be in 8 octets */
+               ndopt_dnssl->nd_opt_dnssl_len = len / 8;
+
+               syslog(LOG_DEBUG, "<%s>: nd_opt_dnssl_len = %d", __func__,
+                   ndopt_dnssl->nd_opt_dnssl_len);
+       }
+       return;
 }
index 9d03c88..be09cd5 100644 (file)
@@ -1,9 +1,10 @@
-/*     $KAME: config.h,v 1.3 2000/05/16 13:34:13 itojun Exp $  */
+/*     $FreeBSD: stable/10/usr.sbin/rtadvd/config.h 224144 2011-07-17 19:24:54Z hrs $  */
+/*     $KAME: config.h,v 1.8 2003/06/17 08:26:22 itojun Exp $  */
 
 /*
  * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
  * All rights reserved.
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -15,7 +16,7 @@
  * 3. Neither the name of the project nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
- * 
+ *
  * 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
  * 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: src/usr.sbin/rtadvd/config.h,v 1.1.2.3 2002/06/29 18:59:53 ume Exp $
- * $DragonFly: src/usr.sbin/rtadvd/config.h,v 1.3 2003/11/03 19:31:42 eirikn Exp $
  */
 
-extern void getconfig(char *);
+extern struct ifinfo *getconfig(struct ifinfo *);
+extern int rm_ifinfo(struct ifinfo *);
+extern int rm_ifinfo_index(int);
+extern int rm_rainfo(struct rainfo *);
+extern int loadconfig_ifname(char *);
+extern int loadconfig_index(int);
 extern void delete_prefix(struct prefix *);
 extern void invalidate_prefix(struct prefix *);
 extern void update_prefix(struct prefix *);
 extern void make_prefix(struct rainfo *, int, struct in6_addr *, int);
 extern void make_packet(struct rainfo *);
+extern void get_prefix(struct rainfo *);
+
+/*
+ * it is highly unlikely to have 100 prefix information options,
+ * so it should be okay to limit it
+ */
+#define MAXPREFIX      100
+#define MAXROUTE       100
+#define MAXRDNSSENT    100
+#define MAXDNSSLENT    100
diff --git a/usr.sbin/rtadvd/control.c b/usr.sbin/rtadvd/control.c
new file mode 100644 (file)
index 0000000..50fde2e
--- /dev/null
@@ -0,0 +1,492 @@
+/*-
+ * 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: stable/10/usr.sbin/rtadvd/control.c 225519 2011-09-12 23:52:55Z hrs $
+ *
+ */
+
+#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 <netinet/in.h>
+#include <netinet/icmp6.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <poll.h>
+#include <signal.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include "rtadvd.h"
+#include "if.h"
+#include "pathnames.h"
+#include "control.h"
+
+#define        CM_RECV_TIMEOUT 30
+
+int
+cm_recv(int fd, char *buf)
+{
+       int n;
+       struct ctrl_msg_hdr     *cm;
+       char *msg;
+       struct pollfd pfds[1];
+       int i;
+
+       syslog(LOG_DEBUG, "<%s> enter, fd=%d", __func__, fd);
+
+       memset(buf, 0, CM_MSG_MAXLEN);
+       cm = (struct ctrl_msg_hdr *)buf;
+       msg = (char *)buf + sizeof(*cm);
+
+       pfds[0].fd = fd;
+       pfds[0].events = POLLIN;
+
+       for (;;) {
+               i = poll(pfds, sizeof(pfds)/sizeof(pfds[0]),
+                   CM_RECV_TIMEOUT);
+
+               if (i == 0)
+                       continue;
+
+               if (i < 0) {
+                       syslog(LOG_ERR, "<%s> poll error: %s",
+                           __func__, strerror(errno));
+                       continue;
+               }
+
+               if (pfds[0].revents & POLLIN) {
+                       n = read(fd, cm, sizeof(*cm));
+                       if (n < 0 && errno == EAGAIN) {
+                               syslog(LOG_DEBUG,
+                                   "<%s> waiting...", __func__);
+                               continue;
+                       }
+                       break;
+               }
+       }
+
+       if (n != sizeof(*cm)) {
+               syslog(LOG_WARNING,
+                   "<%s> received a too small message.", __func__);
+               goto cm_recv_err;
+       }
+       if (cm->cm_len > CM_MSG_MAXLEN) {
+               syslog(LOG_WARNING,
+                   "<%s> received a too large message.", __func__);
+               goto cm_recv_err;
+       }
+       if (cm->cm_version != CM_VERSION) {
+               syslog(LOG_WARNING,
+                   "<%s> version mismatch", __func__);
+               goto cm_recv_err;
+       }
+       if (cm->cm_type >= CM_TYPE_MAX) {
+               syslog(LOG_WARNING,
+                   "<%s> invalid msg type.", __func__);
+               goto cm_recv_err;
+       }
+
+       syslog(LOG_DEBUG,
+           "<%s> ctrl msg received: type=%d", __func__,
+           cm->cm_type);
+
+       if (cm->cm_len > sizeof(cm)) {
+               int msglen = cm->cm_len - sizeof(*cm);
+
+               syslog(LOG_DEBUG,
+                   "<%s> ctrl msg has payload (len=%d)", __func__,
+                   msglen);
+
+               for (;;) {
+                       i = poll(pfds, sizeof(pfds)/sizeof(pfds[0]),
+                           CM_RECV_TIMEOUT);
+
+                       if (i == 0)
+                               continue;
+
+                       if (i < 0) {
+                               syslog(LOG_ERR, "<%s> poll error: %s",
+                                   __func__, strerror(errno));
+                               continue;
+                       }
+
+                       if (pfds[0].revents & POLLIN) {
+                               n = read(fd, msg, msglen);
+                               if (n < 0 && errno == EAGAIN) {
+                                       syslog(LOG_DEBUG,
+                                           "<%s> waiting...", __func__);
+                                       continue;
+                               }
+                       }
+                       break;
+               }
+               if (n != msglen) {
+                       syslog(LOG_WARNING,
+                           "<%s> payload size mismatch.", __func__);
+                       goto cm_recv_err;
+               }
+               buf[CM_MSG_MAXLEN - 1] = '\0';
+       }
+
+       return (0);
+
+cm_recv_err:
+       close(fd);
+       return (-1);
+}
+
+int
+cm_send(int fd, char *buf)
+{
+       struct iovec iov[2];
+       int iovcnt;
+       ssize_t len;
+       ssize_t iov_len_total;
+       struct ctrl_msg_hdr *cm;
+       char *msg;
+
+       cm = (struct ctrl_msg_hdr *)buf;
+       msg = (char *)buf + sizeof(*cm);
+
+       iovcnt = 1;
+       iov[0].iov_base = cm;
+       iov[0].iov_len = sizeof(*cm);
+       iov_len_total = iov[0].iov_len;
+       if (cm->cm_len > sizeof(*cm)) {
+               iovcnt++;
+               iov[1].iov_base = msg;
+               iov[1].iov_len = cm->cm_len - iov[0].iov_len;
+               iov_len_total += iov[1].iov_len;
+       }
+
+       syslog(LOG_DEBUG,
+           "<%s> ctrl msg send: type=%d, count=%d, total_len=%zd", __func__,
+           cm->cm_type, iovcnt, iov_len_total);
+
+       len = writev(fd, iov, iovcnt);
+       syslog(LOG_DEBUG,
+           "<%s> ctrl msg send: length=%zd", __func__, len);
+
+       if (len == -1) {
+               syslog(LOG_DEBUG,
+                   "<%s> write failed: (%d)%s", __func__, errno,
+                   strerror(errno));
+               close(fd);
+               return (-1);
+       }
+
+       syslog(LOG_DEBUG,
+           "<%s> write length = %zd (actual)", __func__, len);
+       syslog(LOG_DEBUG,
+           "<%s> write length = %zd (expected)", __func__, iov_len_total);
+
+       if (len != iov_len_total) {
+               close(fd);
+               return (-1);
+       }
+
+       return (0);
+}
+
+int
+csock_accept(struct sockinfo *s)
+{
+       struct sockaddr_un      sun;
+       int     flags;
+       int     fd;
+
+       sun.sun_len = sizeof(sun);
+       if ((fd = accept(s->si_fd, (struct sockaddr *)&sun,
+                   (socklen_t *)&sun.sun_len)) == -1) {
+               if (errno != EWOULDBLOCK && errno != EINTR)
+                       syslog(LOG_WARNING, "<%s> accept ", __func__);
+               syslog(LOG_WARNING, "<%s> Xaccept: %s", __func__, strerror(errno));
+               return (-1);
+       }
+       if ((flags = fcntl(fd, F_GETFL, 0)) == -1) {
+               syslog(LOG_WARNING, "<%s> fcntl F_GETFL", __func__);
+               close(s->si_fd);
+               return (-1);
+       }
+       if ((flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1) {
+               syslog(LOG_WARNING, "<%s> fcntl F_SETFL", __func__);
+               return (-1);
+       }
+       syslog(LOG_DEBUG, "<%s> accept connfd=%d, listenfd=%d", __func__,
+           fd, s->si_fd);
+
+       return (fd);
+}
+
+int
+csock_close(struct sockinfo *s)
+{
+       close(s->si_fd);
+       unlink(s->si_name);
+       syslog(LOG_DEBUG, "<%s> remove %s", __func__, s->si_name);
+       return (0);
+}
+
+int
+csock_listen(struct sockinfo *s)
+{
+       if (s->si_fd == -1) {
+               syslog(LOG_ERR, "<%s> listen failed", __func__);
+               return (-1);
+       }
+       if (listen(s->si_fd, SOCK_BACKLOG) == -1) {
+               syslog(LOG_ERR, "<%s> listen failed", __func__);
+               return (-1);
+       }
+
+       return (0);
+}
+
+int
+csock_open(struct sockinfo *s, mode_t mode)
+{
+       int flags;
+       struct sockaddr_un      sun;
+       mode_t  old_umask;
+
+       if (s == NULL) {
+               syslog(LOG_ERR, "<%s> internal error.", __func__);
+               exit(1);
+       }
+       if (s->si_name == NULL)
+               s->si_name = _PATH_CTRL_SOCK;
+
+       if ((s->si_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
+               syslog(LOG_ERR,
+                   "<%s> cannot open control socket", __func__);
+               return (-1);
+       }
+       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 (unlink(s->si_name) == -1)
+               if (errno != ENOENT) {
+                       syslog(LOG_ERR,
+                           "<%s> unlink %s", __func__, s->si_name);
+                       close(s->si_fd);
+                       return (-1);
+               }
+       old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
+       if (bind(s->si_fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+               syslog(LOG_ERR,
+                   "<%s> bind failed: %s", __func__, s->si_name);
+               close(s->si_fd);
+               umask(old_umask);
+               return (-1);
+       }
+       umask(old_umask);
+       if (chmod(s->si_name, mode) == -1) {
+               syslog(LOG_ERR,
+                   "<%s> chmod failed: %s", __func__, s->si_name);
+               goto csock_open_err;
+       }
+       if ((flags = fcntl(s->si_fd, F_GETFL, 0)) == -1) {
+               syslog(LOG_ERR,
+                   "<%s> fcntl F_GETFL failed: %s", __func__, s->si_name);
+               goto csock_open_err;
+       }
+       if ((flags = fcntl(s->si_fd, F_SETFL, flags | O_NONBLOCK)) == -1) {
+               syslog(LOG_ERR,
+                   "<%s> fcntl F_SETFL failed: %s", __func__, s->si_name);
+               goto csock_open_err;
+       }
+
+       return (s->si_fd);
+
+csock_open_err:
+       close(s->si_fd);
+       unlink(s->si_name);
+       return (-1);
+}
+
+struct ctrl_msg_pl *
+cm_bin2pl(char *str, struct ctrl_msg_pl *cp)
+{
+       size_t len;
+       size_t *lenp;
+       char *p;
+
+       memset(cp, 0, sizeof(*cp));
+
+       p = str;
+
+       lenp = (size_t *)p;
+       len = *lenp++;
+       p = (char *)lenp;
+       syslog(LOG_DEBUG, "<%s> len(ifname) = %zu", __func__, len);
+       if (len > 0) {
+               cp->cp_ifname = malloc(len + 1);
+               if (cp->cp_ifname == NULL) {
+                       syslog(LOG_ERR, "<%s> malloc", __func__);
+                       exit(1);
+               }
+               memcpy(cp->cp_ifname, p, len);
+               cp->cp_ifname[len] = '\0';
+               p += len;
+       }
+
+       lenp = (size_t *)p;
+       len = *lenp++;
+       p = (char *)lenp;
+       syslog(LOG_DEBUG, "<%s> len(key) = %zu", __func__, len);
+       if (len > 0) {
+               cp->cp_key = malloc(len + 1);
+               if (cp->cp_key == NULL) {
+                       syslog(LOG_ERR, "<%s> malloc", __func__);
+                       exit(1);
+               }
+               memcpy(cp->cp_key, p, len);
+               cp->cp_key[len] = '\0';
+               p += len;
+       }
+
+       lenp = (size_t *)p;
+       len = *lenp++;
+       p = (char *)lenp;
+       syslog(LOG_DEBUG, "<%s> len(val) = %zu", __func__, len);
+       if (len > 0) {
+               cp->cp_val = malloc(len + 1);
+               if (cp->cp_val == NULL) {
+                       syslog(LOG_ERR, "<%s> malloc", __func__);
+                       exit(1);
+               }
+               memcpy(cp->cp_val, p, len);
+               cp->cp_val[len] = '\0';
+               cp->cp_val_len = len;
+       } else
+               cp->cp_val_len = 0;
+
+       return (cp);
+}
+
+size_t
+cm_pl2bin(char *str, struct ctrl_msg_pl *cp)
+{
+       size_t len;
+       size_t *lenp;
+       char *p;
+       struct ctrl_msg_hdr *cm;
+
+       len = sizeof(size_t);
+       if (cp->cp_ifname != NULL)
+               len += strlen(cp->cp_ifname);
+       len += sizeof(size_t);
+       if (cp->cp_key != NULL)
+               len += strlen(cp->cp_key);
+       len += sizeof(size_t);
+       if (cp->cp_val != NULL && cp->cp_val_len > 0)
+               len += cp->cp_val_len;
+
+       if (len > CM_MSG_MAXLEN - sizeof(*cm)) {
+               syslog(LOG_DEBUG, "<%s> msg too long (len=%zu)",
+                   __func__, len);
+               return (0);
+       }
+       syslog(LOG_DEBUG, "<%s> msglen=%zu", __func__, len);
+       memset(str, 0, len);
+       p = str;
+       lenp = (size_t *)p;
+
+       if (cp->cp_ifname != NULL) {
+               *lenp++ = strlen(cp->cp_ifname);
+               p = (char *)lenp;
+               memcpy(p, cp->cp_ifname, strlen(cp->cp_ifname));
+               p += strlen(cp->cp_ifname);
+       } else {
+               *lenp++ = '\0';
+               p = (char *)lenp;
+       }
+
+       lenp = (size_t *)p;
+       if (cp->cp_key != NULL) {
+               *lenp++ = strlen(cp->cp_key);
+               p = (char *)lenp;
+               memcpy(p, cp->cp_key, strlen(cp->cp_key));
+               p += strlen(cp->cp_key);
+       } else {
+               *lenp++ = '\0';
+               p = (char *)lenp;
+       }
+
+       lenp = (size_t *)p;
+       if (cp->cp_val != NULL && cp->cp_val_len > 0) {
+               *lenp++ = cp->cp_val_len;
+               p = (char *)lenp;
+               memcpy(p, cp->cp_val, cp->cp_val_len);
+               p += cp->cp_val_len;
+       } else {
+               *lenp++ = '\0';
+               p = (char *)lenp;
+       }
+
+       return (len);
+}
+
+size_t
+cm_str2bin(char *bin, void *str, size_t len)
+{
+       struct ctrl_msg_hdr *cm;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       if (len > CM_MSG_MAXLEN - sizeof(*cm)) {
+               syslog(LOG_DEBUG, "<%s> msg too long (len=%zu)",
+                   __func__, len);
+               return (0);
+       }
+       syslog(LOG_DEBUG, "<%s> msglen=%zu", __func__, len);
+       memcpy(bin, (char *)str, len);
+
+       return (len);
+}
+
+void *
+cm_bin2str(char *bin, void *str, size_t len)
+{
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       memcpy((char *)str, bin, len);
+
+       return (str);
+}
similarity index 53%
copy from usr.sbin/rtadvd/rrenum.h
copy to usr.sbin/rtadvd/control.h
index 39aebb4..63653c2 100644 (file)
@@ -1,9 +1,7 @@
-/*     $KAME: rrenum.h,v 1.3 2001/01/21 15:37:14 itojun Exp $  */
-
-/*
- * Copyright (C) 1998 WIDE Project.
+/*-
+ * 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:
  * 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.
- * 3. Neither the name of the project nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- * 
+ *
  * 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
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/usr.sbin/rtadvd/rrenum.h,v 1.1.2.2 2001/07/03 11:02:14 ume Exp $
- * $DragonFly: src/usr.sbin/rtadvd/rrenum.h,v 1.3 2003/11/03 19:31:42 eirikn Exp $
+ * $FreeBSD: stable/10/usr.sbin/rtadvd/control.h 225519 2011-09-12 23:52:55Z hrs $
+ *
  */
 
-void rr_input(int, struct icmp6_router_renum *, struct in6_pktinfo *,
-       struct sockaddr_in6 *, struct in6_addr *);
+#define        SOCK_BACKLOG            5
+
+#define        CM_MSG_MAXLEN           8192
+#define        CM_VERSION              1
+#define        CM_VERSION_STR          "1.0"
+
+#define        CM_TYPE_EOM             0
+#define        CM_TYPE_ACK             1
+#define        CM_TYPE_ERR             2
+#define        CM_TYPE_NUL             3
+#define        CM_TYPE_REQ_SET_PROP    4
+#define        CM_TYPE_REQ_GET_PROP    5
+#define        CM_TYPE_MAX             6
+
+#define        CM_STATE_EOM            0
+#define        CM_STATE_INIT           1
+#define        CM_STATE_MSG_DISPATCH   2
+#define        CM_STATE_MSG_RECV       3
+#define        CM_STATE_ACK_WAIT       4
+
+struct ctrl_msg_hdr {
+       int     cm_version;
+       size_t  cm_len;
+       int     cm_type;
+};
+
+struct ctrl_msg_pl {
+       char    *cp_ifname;
+       char    *cp_key;
+
+       size_t  cp_val_len;
+       char    *cp_val;
+};
+
+int    csock_open(struct sockinfo *, mode_t);
+int    csock_close(struct sockinfo *);
+int    csock_listen(struct sockinfo *);
+int    csock_accept(struct sockinfo *);
+int    cm_send(int, char *);
+int    cm_recv(int, char *);
+
+size_t                 cm_pl2bin(char *, struct ctrl_msg_pl *);
+struct ctrl_msg_pl     *cm_bin2pl(char *, struct ctrl_msg_pl *);
+size_t                 cm_str2bin(char *, void *, size_t);
+void                   *cm_bin2str(char *, void *, size_t);
diff --git a/usr.sbin/rtadvd/control_client.c b/usr.sbin/rtadvd/control_client.c
new file mode 100644 (file)
index 0000000..d6e487b
--- /dev/null
@@ -0,0 +1,131 @@
+/*-
+ * 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: stable/10/usr.sbin/rtadvd/control_client.c 225519 2011-09-12 23:52:55Z hrs $
+ *
+ */
+
+#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 <netinet/in.h>
+#include <netinet/icmp6.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include "pathnames.h"
+#include "rtadvd.h"
+#include "if.h"
+#include "control.h"
+#include "control_client.h"
+
+int
+cm_handler_client(int fd, int state, char *buf_orig)
+{
+       char buf[CM_MSG_MAXLEN];
+       struct ctrl_msg_hdr *cm;
+       struct ctrl_msg_hdr *cm_orig;
+       int error;
+       char *msg;
+       char *msg_orig;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       memset(buf, 0, sizeof(buf));
+       cm = (struct ctrl_msg_hdr *)buf;
+       cm_orig = (struct ctrl_msg_hdr *)buf_orig;
+       msg = (char *)buf + sizeof(*cm);
+       msg_orig = (char *)buf_orig + sizeof(*cm_orig);
+
+       if (cm_orig->cm_len > CM_MSG_MAXLEN) {
+               syslog(LOG_DEBUG, "<%s> msg too long", __func__);
+               close(fd);
+               return (-1);
+       }
+       cm->cm_type = cm_orig->cm_type;
+       if (cm_orig->cm_len > sizeof(*cm_orig)) {
+               memcpy(msg, msg_orig, cm_orig->cm_len - sizeof(*cm));
+               cm->cm_len = cm_orig->cm_len;
+       }
+       while (state != CM_STATE_EOM) {
+               syslog(LOG_DEBUG, "<%s> state = %d", __func__, state);
+
+               switch (state) {
+               case CM_STATE_INIT:
+                       state = CM_STATE_EOM;
+                       break;
+               case CM_STATE_MSG_DISPATCH:
+                       cm->cm_version = CM_VERSION;
+                       error = cm_send(fd, buf);
+                       if (error)
+                               syslog(LOG_WARNING,
+                                   "<%s> cm_send()", __func__);
+                       state = CM_STATE_ACK_WAIT;
+                       break;
+               case CM_STATE_ACK_WAIT:
+                       error = cm_recv(fd, buf);
+                       if (error) {
+                               syslog(LOG_ERR,
+                                   "<%s> cm_recv()", __func__);
+                               close(fd);
+                               return (-1);
+                       }
+                       switch (cm->cm_type) {
+                       case CM_TYPE_ACK:
+                               syslog(LOG_DEBUG,
+                                   "<%s> CM_TYPE_ACK", __func__);
+                               break;
+                       case CM_TYPE_ERR:
+                               syslog(LOG_DEBUG,
+                                   "<%s> CM_TYPE_ERR", __func__);
+                               close(fd);
+                               return (-1);
+                       default:
+                               syslog(LOG_DEBUG,
+                                   "<%s> unknown status", __func__);
+                               close(fd);
+                               return (-1);
+                       }
+                       memcpy(buf_orig, buf, cm->cm_len);
+                       state = CM_STATE_EOM;
+                       break;
+               }
+       }
+       close(fd);
+       return (0);
+}
diff --git a/usr.sbin/rtadvd/control_client.h b/usr.sbin/rtadvd/control_client.h
new file mode 100644 (file)
index 0000000..22da031
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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: stable/10/usr.sbin/rtadvd/control_client.h 225519 2011-09-12 23:52:55Z hrs $
+ *
+ */
+
+int    cm_handler_client(int, int, char *);
diff --git a/usr.sbin/rtadvd/control_server.c b/usr.sbin/rtadvd/control_server.c
new file mode 100644 (file)
index 0000000..361801e
--- /dev/null
@@ -0,0 +1,752 @@
+/*-
+ * 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: stable/10/usr.sbin/rtadvd/control_server.c 225519 2011-09-12 23:52:55Z hrs $
+ *
+ */
+
+#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 <netinet/in.h>
+#include <netinet/icmp6.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include "pathnames.h"
+#include "rtadvd.h"
+#include "if.h"
+#include "config.h"
+#include "control.h"
+#include "control_server.h"
+#include "timer.h"
+
+static char *do_reload_ifname;
+static int do_reload;
+static int do_shutdown;
+
+void set_do_reload(int sig __unused)   { do_reload = 1; }
+void set_do_reload_ifname(char *ifname){ do_reload_ifname = ifname; }
+void set_do_shutdown(int sig __unused) { do_shutdown = 1; }
+void reset_do_reload(void)     { do_reload = 0; do_reload_ifname = NULL; }
+void reset_do_shutdown(void)   { do_shutdown = 0; }
+int is_do_reload(void)         { return (do_reload); }
+int is_do_shutdown(void)       { return (do_shutdown); }
+char *reload_ifname(void)      { return (do_reload_ifname); }
+
+#define        DEF_PL_HANDLER(key)     { #key, cm_getprop_##key }
+
+static int cm_getprop_echo(struct ctrl_msg_pl *);
+static int cm_getprop_version(struct ctrl_msg_pl *);
+static int cm_getprop_ifilist(struct ctrl_msg_pl *);
+static int cm_getprop_ifi(struct ctrl_msg_pl *);
+static int cm_getprop_ifi_ra_timer(struct ctrl_msg_pl *);
+static int cm_getprop_rai(struct ctrl_msg_pl *);
+static int cm_getprop_pfx(struct ctrl_msg_pl *);
+static int cm_getprop_rdnss(struct ctrl_msg_pl *);
+static int cm_getprop_dnssl(struct ctrl_msg_pl *);
+static int cm_getprop_rti(struct ctrl_msg_pl *);
+
+static int cm_setprop_reload(struct ctrl_msg_pl *);
+static int cm_setprop_enable(struct ctrl_msg_pl *);
+static int cm_setprop_disable(struct ctrl_msg_pl *);
+
+static struct dispatch_table {
+       const char      *dt_comm;
+       int             (*dt_act)(struct ctrl_msg_pl *cp);
+} getprop_dtable[] = {
+       { "",   cm_getprop_echo },
+       DEF_PL_HANDLER(echo),
+       DEF_PL_HANDLER(version),
+       DEF_PL_HANDLER(ifilist),
+       DEF_PL_HANDLER(ifi),
+       DEF_PL_HANDLER(ifi_ra_timer),
+       DEF_PL_HANDLER(rai),
+       DEF_PL_HANDLER(rti),
+       DEF_PL_HANDLER(pfx),
+       DEF_PL_HANDLER(rdnss),
+       DEF_PL_HANDLER(dnssl),
+};
+
+static int
+cm_getprop_echo(struct ctrl_msg_pl *cp)
+{
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+       cp->cp_val = strdup("");
+       cp->cp_val_len = strlen(cp->cp_val) + 1;
+
+       return (0);
+}
+
+static int
+cm_getprop_version(struct ctrl_msg_pl *cp)
+{
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+       cp->cp_val = strdup(CM_VERSION_STR);
+       cp->cp_val_len = strlen(cp->cp_val) + 1;
+
+       return (0);
+}
+
+static int
+cm_getprop_ifilist(struct ctrl_msg_pl *cp)
+{
+       struct ifinfo *ifi;
+       char *p;
+       size_t len;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       len = 0;
+       TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+               len += strlen(ifi->ifi_ifname) + 1;
+       }
+
+       syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len);
+
+       p = malloc(len);
+       if (p == NULL)
+               exit(1);
+       memset(p, 0, len);
+       cp->cp_val = p;
+
+       if (len > 0)
+               TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+                       syslog(LOG_DEBUG, "<%s> add ifname=%s(%d)",
+                           __func__, ifi->ifi_ifname, ifi->ifi_ifindex);
+                       strcpy(p, ifi->ifi_ifname);
+                       p += strlen(ifi->ifi_ifname) + 1;
+               }
+       cp->cp_val_len = p - cp->cp_val;
+
+       return (0);
+}
+
+static int
+cm_getprop_ifi(struct ctrl_msg_pl *cp)
+{
+       struct ifinfo *ifi;
+       char *p;
+       size_t len;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+               if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0)
+                       break;
+       }
+       if (ifi == NULL) {
+               syslog(LOG_ERR, "<%s> %s not found", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+
+       p = malloc(sizeof(*ifi));
+       if (p == NULL)
+               exit(1);
+       len = cm_str2bin(p, ifi, sizeof(*ifi));
+
+       syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len);
+
+       if (len == 0)
+               return (1);
+
+       cp->cp_val = p;
+       cp->cp_val_len = len;
+
+       return (0);
+}
+
+static int
+cm_getprop_rai(struct ctrl_msg_pl *cp)
+{
+       struct ifinfo *ifi;
+       struct rainfo *rai;
+       char *p;
+       size_t len;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+               if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0)
+                       break;
+       }
+       if (ifi == NULL) {
+               syslog(LOG_ERR, "<%s> %s not found", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+       if ((rai = ifi->ifi_rainfo) == NULL) {
+               syslog(LOG_ERR, "<%s> %s has no rainfo", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+
+       p = malloc(sizeof(*rai));
+       if (p == NULL)
+               exit(1);
+       len = cm_str2bin(p, rai, sizeof(*rai));
+
+       syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len);
+
+       if (len == 0)
+               return (1);
+
+       cp->cp_val = p;
+       cp->cp_val_len = len;
+
+       return (0);
+}
+
+static int
+cm_getprop_ifi_ra_timer(struct ctrl_msg_pl *cp)
+{
+       struct ifinfo *ifi;
+       struct rainfo *rai;
+       struct rtadvd_timer     *rtimer;
+       char *p;
+       size_t len;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+               if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0)
+                       break;
+       }
+       if (ifi == NULL) {
+               syslog(LOG_ERR, "<%s> %s not found", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+       if ((rai = ifi->ifi_rainfo) == NULL) {
+               syslog(LOG_ERR, "<%s> %s has no rainfo", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+       if ((rtimer = ifi->ifi_ra_timer) == NULL) {
+               syslog(LOG_ERR, "<%s> %s has no ifi_ra_timer", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+       p = malloc(sizeof(*rtimer));
+       if (p == NULL)
+               exit(1);
+       len = cm_str2bin(p, rtimer, sizeof(*rtimer));
+
+       syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len);
+
+       if (len == 0)
+               return (1);
+
+       cp->cp_val = p;
+       cp->cp_val_len = len;
+
+       return (0);
+}
+
+static int
+cm_getprop_rti(struct ctrl_msg_pl *cp)
+{
+       struct ifinfo *ifi;
+       struct rainfo *rai;
+       struct rtinfo *rti;
+       char *p;
+       size_t len;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       len = 0;
+       TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+               if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0)
+                       break;
+       }
+       if (ifi == NULL) {
+               syslog(LOG_ERR, "<%s> %s not found", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+       if (ifi->ifi_rainfo == NULL) {
+               syslog(LOG_ERR, "<%s> %s has no rainfo", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+       rai = ifi->ifi_rainfo;
+       TAILQ_FOREACH(rti, &rai->rai_route, rti_next) {
+               len += sizeof(*rti);
+       }
+
+       syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len);
+
+       p = malloc(len);
+       if (p == NULL)
+               exit(1);
+       memset(p, 0, len);
+       cp->cp_val = p;
+
+       if (len > 0)
+               TAILQ_FOREACH(rti, &rai->rai_route, rti_next) {
+                       memcpy(p, rti, sizeof(*rti));
+                       p += sizeof(*rti);
+               }
+       cp->cp_val_len = p - cp->cp_val;
+
+       return (0);
+}
+
+static int
+cm_getprop_pfx(struct ctrl_msg_pl *cp)
+{
+       struct ifinfo *ifi;
+       struct rainfo *rai;
+       struct prefix *pfx;
+       char *p;
+       size_t len;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       len = 0;
+       TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+               if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0)
+                       break;
+       }
+       if (ifi == NULL) {
+               syslog(LOG_ERR, "<%s> %s not found", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+       if (ifi->ifi_rainfo == NULL) {
+               syslog(LOG_ERR, "<%s> %s has no rainfo", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+       rai = ifi->ifi_rainfo;
+       TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) {
+               len += sizeof(*pfx);
+       }
+
+       syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len);
+
+       p = malloc(len);
+       if (p == NULL)
+               exit(1);
+       memset(p, 0, len);
+       cp->cp_val = p;
+
+       if (len > 0)
+               TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) {
+                       memcpy(p, pfx, sizeof(*pfx));
+                       p += sizeof(*pfx);
+               }
+       cp->cp_val_len = p - cp->cp_val;
+
+       return (0);
+}
+
+static int
+cm_getprop_rdnss(struct ctrl_msg_pl *cp)
+{
+       struct ifinfo *ifi;
+       struct rainfo *rai;
+       struct rdnss *rdn;
+       struct rdnss_addr *rda;
+       char *p;
+       size_t len;
+       uint16_t *rdn_cnt;
+       uint16_t *rda_cnt;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       len = 0;
+       TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+               if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0)
+                       break;
+       }
+       if (ifi == NULL) {
+               syslog(LOG_ERR, "<%s> %s not found", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+       if (ifi->ifi_rainfo == NULL) {
+               syslog(LOG_ERR, "<%s> %s has no rainfo", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+       rai = ifi->ifi_rainfo;
+
+       len = sizeof(*rdn_cnt);
+       TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) {
+               len += sizeof(*rdn);
+               len += sizeof(*rda_cnt);
+               TAILQ_FOREACH(rda, &rdn->rd_list, ra_next) {
+                       len += sizeof(*rda);
+               }
+       }
+
+       syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len);
+
+       p = malloc(len);
+       if (p == NULL)
+               exit(1);
+       memset(p, 0, len);
+       cp->cp_val = p;
+
+       rdn_cnt = (uint16_t *)p;
+       p += sizeof(*rdn_cnt);
+       TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) {
+               *rdn_cnt += 1;
+               memcpy(p, rdn, sizeof(*rdn));
+               p += sizeof(*rdn);
+
+               rda_cnt = (uint16_t *)p;
+               p += sizeof(*rda_cnt);
+               TAILQ_FOREACH(rda, &rdn->rd_list, ra_next) {
+                       *rda_cnt += 1;
+                       memcpy(p, rda, sizeof(*rda));
+                       p += sizeof(*rda);
+               }
+       }
+       syslog(LOG_DEBUG, "<%s> rdn_cnt = %d", __func__, *rdn_cnt);
+       cp->cp_val_len = p - cp->cp_val;
+
+       return (0);
+}
+
+static int
+cm_getprop_dnssl(struct ctrl_msg_pl *cp)
+{
+       struct ifinfo *ifi;
+       struct rainfo *rai;
+       struct dnssl *dns;
+       struct dnssl_addr *dna;
+       char *p;
+       size_t len;
+       uint16_t *dns_cnt;
+       uint16_t *dna_cnt;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       len = 0;
+       TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+               if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0)
+                       break;
+       }
+       if (ifi == NULL) {
+               syslog(LOG_ERR, "<%s> %s not found", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+       if (ifi->ifi_rainfo == NULL) {
+               syslog(LOG_ERR, "<%s> %s has no rainfo", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+       rai = ifi->ifi_rainfo;
+
+       len = sizeof(*dns_cnt);
+       TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) {
+               len += sizeof(*dns);
+               len += sizeof(*dna_cnt);
+               TAILQ_FOREACH(dna, &dns->dn_list, da_next) {
+                       len += sizeof(*dna);
+               }
+       }
+
+       syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len);
+
+       p = malloc(len);
+       if (p == NULL)
+               exit(1);
+       memset(p, 0, len);
+       cp->cp_val = p;
+
+       dns_cnt = (uint16_t *)cp->cp_val;
+       p += sizeof(*dns_cnt);
+       TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) {
+               (*dns_cnt)++;
+               memcpy(p, dns, sizeof(*dns));
+               p += sizeof(*dns);
+
+               dna_cnt = (uint16_t *)p;
+               p += sizeof(*dna_cnt);
+               TAILQ_FOREACH(dna, &dns->dn_list, da_next) {
+                       (*dna_cnt)++;
+                       memcpy(p, dna, sizeof(*dna));
+                       p += sizeof(*dna);
+               }
+       }
+       cp->cp_val_len = p - cp->cp_val;
+
+       return (0);
+}
+
+int
+cm_getprop(struct ctrl_msg_pl *cp)
+{
+       size_t i;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       if (cp == NULL)
+               return (1);
+
+       for (i = 0;
+            i < sizeof(getprop_dtable) / sizeof(getprop_dtable[0]);
+            i++) {
+               if (strcmp(cp->cp_key, getprop_dtable[i].dt_comm) == 0)
+                       return (getprop_dtable[i].dt_act(cp));
+       }
+       return (1);
+}
+
+int
+cm_setprop(struct ctrl_msg_pl *cp)
+{
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       if (cp == NULL || cp->cp_key == NULL)
+               return (1);
+
+       if (strncmp(cp->cp_key, "reload", sizeof("reload")) == 0)
+               cm_setprop_reload(cp);
+       else if (strncmp(cp->cp_key, "shutdown", sizeof("shutdown")) == 0)
+               set_do_shutdown(0);
+       else if (strncmp(cp->cp_key, "enable", sizeof("enable")) == 0)
+               cm_setprop_enable(cp);
+       else if (strncmp(cp->cp_key, "disable", sizeof("disable")) == 0)
+               cm_setprop_disable(cp);
+       else if (strncmp(cp->cp_key, "echo", 8) == 0)
+               ;               /* do nothing */
+       else
+               return (1);
+
+       return (0);
+}
+
+static int
+cm_setprop_reload(struct ctrl_msg_pl *cp)
+{
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       set_do_reload_ifname(cp->cp_ifname);
+       set_do_reload(1);
+
+       return (0);
+}
+
+static int
+cm_setprop_enable(struct ctrl_msg_pl *cp)
+{
+       struct ifinfo *ifi;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+               if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0)
+                       break;
+       }
+       if (ifi == NULL) {
+               syslog(LOG_ERR, "<%s> %s not found", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+
+       ifi->ifi_persist = 1;
+       set_do_reload_ifname(ifi->ifi_ifname);
+       set_do_reload(0);
+
+       return (0);
+}
+
+static int
+cm_setprop_disable(struct ctrl_msg_pl *cp)
+{
+       struct ifinfo *ifi;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+               if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0)
+                       break;
+       }
+       if (ifi == NULL) {
+               syslog(LOG_ERR, "<%s> %s not found", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+
+       if (ifi->ifi_persist == 1) {
+               ifi->ifi_persist = 0;
+               rm_ifinfo(ifi);
+
+               /* MC leaving needed here */
+               sock_mc_leave(&sock, ifi->ifi_ifindex);
+
+               set_do_reload_ifname(ifi->ifi_ifname);
+               set_do_reload(0);
+       }
+
+       return (0);
+}
+
+int
+cm_handler_server(int fd)
+{
+       int state;
+       char *msg;
+       struct ctrl_msg_hdr *cm;
+       struct ctrl_msg_pl cp;
+       char buf[CM_MSG_MAXLEN];
+       char pbuf[CM_MSG_MAXLEN];
+       int error;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       memset(buf, 0, sizeof(buf));
+       memset(pbuf, 0, sizeof(pbuf));
+       cm = (struct ctrl_msg_hdr *)buf;
+       msg = (char *)buf + sizeof(*cm);
+
+       state = CM_STATE_INIT;
+       while (state != CM_STATE_EOM) {
+               syslog(LOG_DEBUG, "<%s> state = %d", __func__, state);
+
+               switch (state) {
+               case CM_STATE_INIT:
+                       state = CM_STATE_MSG_RECV;
+                       break;
+               case CM_STATE_MSG_DISPATCH:
+                       cm->cm_version = CM_VERSION;
+                       error = cm_send(fd, buf);
+                       if (error)
+                               syslog(LOG_WARNING,
+                                   "<%s> cm_send()", __func__);
+                       state = CM_STATE_EOM;
+                       break;
+               case CM_STATE_ACK_WAIT:
+                       error = cm_recv(fd, buf);
+                       if (error) {
+                               syslog(LOG_ERR,
+                                   "<%s> cm_recv()", __func__);
+                               close(fd);
+                               return (-1);
+                       }
+
+                       switch (cm->cm_type) {
+                       case CM_TYPE_ACK:
+                               break;
+                       case CM_TYPE_ERR:
+                               syslog(LOG_DEBUG,
+                                   "<%s> CM_TYPE_ERR", __func__);
+                               close(fd);
+                               return (-1);
+                       default:
+                               syslog(LOG_DEBUG,
+                                   "<%s> unknown status", __func__);
+                               close(fd);
+                               return (-1);
+                       }
+                       state = CM_STATE_EOM;
+                       break;
+               case CM_STATE_MSG_RECV:
+                       error = cm_recv(fd, buf);
+
+                       if (error) {
+                               syslog(LOG_ERR,
+                                   "<%s> cm_recv()", __func__);
+                               close(fd);
+                               return (-1);
+                       }
+                       memset(&cp, 0, sizeof(cp));
+
+                       syslog(LOG_DEBUG,
+                           "<%s> cm->cm_type = %d", __func__, cm->cm_type);
+                       syslog(LOG_DEBUG,
+                           "<%s> cm->cm_len = %zu", __func__, cm->cm_len);
+
+                       switch (cm->cm_type) {
+                       case CM_TYPE_EOM:
+                               state = CM_STATE_EOM;
+                       case CM_TYPE_NUL:
+                               cm->cm_type = CM_TYPE_ACK;
+                               cm->cm_len = sizeof(*cm);
+                               break;
+                       case CM_TYPE_REQ_GET_PROP:
+                               cm_bin2pl(msg, &cp);
+                               error = cm_getprop(&cp);
+                               if (error) {
+                                       cm->cm_type = CM_TYPE_ERR;
+                                       cm->cm_len = sizeof(*cm);
+                               } else {
+                                       cm->cm_type = CM_TYPE_ACK;
+                                       cm->cm_len = sizeof(*cm);
+                                       cm->cm_len += cm_pl2bin(msg, &cp);
+                               }
+                               if (cp.cp_val != NULL)
+                                       free(cp.cp_val);
+                               break;
+                       case CM_TYPE_REQ_SET_PROP:
+                               cm_bin2pl(msg, &cp);
+                               error = cm_setprop(&cp);
+                               if (error) {
+                                       cm->cm_type = CM_TYPE_ERR;
+                                       cm->cm_len = sizeof(*cm);
+                               } else {
+                                       cm->cm_type = CM_TYPE_ACK;
+                                       cm->cm_len = sizeof(*cm);
+                               }
+                               break;
+                       default:
+                               cm->cm_type = CM_TYPE_ERR;
+                               cm->cm_len = sizeof(*cm);
+                       }
+
+                       switch (cm->cm_type) {
+                       case CM_TYPE_ERR:
+                       case CM_TYPE_ACK:
+                               state = CM_STATE_MSG_DISPATCH;
+                               break;
+                       }
+               }
+       }
+       syslog(LOG_DEBUG, "<%s> leave", __func__);
+
+       return (0);
+}
diff --git a/usr.sbin/rtadvd/control_server.h b/usr.sbin/rtadvd/control_server.h
new file mode 100644 (file)
index 0000000..dfd49b2
--- /dev/null
@@ -0,0 +1,42 @@
+/*-
+ * 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: stable/10/usr.sbin/rtadvd/control_server.h 225519 2011-09-12 23:52:55Z hrs $
+ *
+ */
+
+int    cm_getprop(struct ctrl_msg_pl *);
+int    cm_setprop(struct ctrl_msg_pl *);
+
+int    cm_handler_server(int);
+
+void   set_do_reload(int);
+void   set_do_reload_ifname(char *);
+void   set_do_shutdown(int);
+void   reset_do_reload(void);
+void   reset_do_shutdown(void);
+int    is_do_reload(void);
+char   *reload_ifname(void);
+int    is_do_shutdown(void);
index 32efd9b..36f30ab 100644 (file)
@@ -1,9 +1,11 @@
+/*     $FreeBSD: stable/10/usr.sbin/rtadvd/if.c 255156 2013-09-02 20:44:19Z hrs $      */
 /*     $KAME: if.c,v 1.17 2001/01/21 15:27:30 itojun Exp $     */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * 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:
@@ -15,7 +17,7 @@
  * 3. Neither the name of the project nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
- * 
+ *
  * 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
@@ -27,9 +29,6 @@
  * 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: src/usr.sbin/rtadvd/if.c,v 1.2.2.3 2001/07/03 11:02:14 ume Exp $
- * $DragonFly: src/usr.sbin/rtadvd/if.c,v 1.6 2005/12/05 00:56:37 swildner Exp $
  */
 
 #include <sys/param.h>
 #include <sys/sysctl.h>
 #include <sys/ioctl.h>
 #include <net/if.h>
+#include <net/if_dl.h>
 #include <net/if_types.h>
-#ifdef __DragonFly__
-# include <net/ethernet.h>
-#endif
-#include <ifaddrs.h>
-#ifdef __NetBSD__
-#include <net/if_ether.h>
-#endif
+#include <net/if_var.h>
+#include <net/ethernet.h>
 #include <net/route.h>
-#include <net/if_dl.h>
 #include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
 #include <netinet/icmp6.h>
-#ifdef __bsdi__
-# include <netinet/if_ether.h>
-#endif
-#ifdef __OpenBSD__
-#include <netinet/if_ether.h>
-#endif
+#include <netinet6/nd6.h>
 #include <unistd.h>
 #include <errno.h>
+#include <netdb.h>
 #include <stdlib.h>
 #include <string.h>
 #include <syslog.h>
+
+#include "pathnames.h"
 #include "rtadvd.h"
 #include "if.h"
 
-struct if_msghdr **iflist;
-int iflist_init_ok;
-size_t ifblock_size;
-char *ifblock;
+#define ROUNDUP(a, size)                                       \
+       (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
+
+#define        NEXT_SA(ap)                                                     \
+       (ap) = (struct sockaddr *)((caddr_t)(ap) +                      \
+           ((ap)->sa_len ? ROUNDUP((ap)->sa_len, sizeof(u_long)) :     \
+           sizeof(u_long)))
 
-static void get_iflist(char **buf, size_t *size);
-static void parse_iflist(struct if_msghdr ***ifmlist_p, char *buf,
-                      size_t bufsize);
+struct sockaddr_in6 sin6_linklocal_allnodes = {
+        .sin6_len =     sizeof(sin6_linklocal_allnodes),
+        .sin6_family =  AF_INET6,
+        .sin6_addr =    IN6ADDR_LINKLOCAL_ALLNODES_INIT,
+};
+
+struct sockaddr_in6 sin6_linklocal_allrouters = {
+        .sin6_len =     sizeof(sin6_linklocal_allrouters),
+        .sin6_family =  AF_INET6,
+        .sin6_addr =    IN6ADDR_LINKLOCAL_ALLROUTERS_INIT,
+};
+
+struct sockaddr_in6 sin6_sitelocal_allrouters = {
+        .sin6_len =     sizeof(sin6_sitelocal_allrouters),
+        .sin6_family =  AF_INET6,
+        .sin6_addr =    IN6ADDR_SITELOCAL_ALLROUTERS_INIT,
+};
+
+struct sockinfo sock = { .si_fd = -1, .si_name = NULL };
+struct sockinfo rtsock = { .si_fd = -1, .si_name = NULL };
+struct sockinfo ctrlsock = { .si_fd = -1, .si_name = _PATH_CTRL_SOCK };
+
+char *mcastif;
+
+static void            get_rtaddrs(int, struct sockaddr *,
+                           struct sockaddr **);
+static struct if_msghdr        *get_next_msghdr(struct if_msghdr *,
+                           struct if_msghdr *);
 
 static void
 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
 {
        int i;
-       
+
        for (i = 0; i < RTAX_MAX; i++) {
                if (addrs & (1 << i)) {
                        rti_info[i] = sa;
-                       RT_ADVANCE(sa, sa);
+                       NEXT_SA(sa);
                }
                else
                        rti_info[i] = NULL;
        }
 }
 
-struct sockaddr_dl *
-if_nametosdl(char *name)
-{
-       int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
-       char *buf, *next, *lim;
-       size_t len;
-       struct if_msghdr *ifm;
-       struct sockaddr *sa, *rti_info[RTAX_MAX];
-       struct sockaddr_dl *sdl = NULL, *ret_sdl;
-
-       if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
-               return(NULL);
-       if ((buf = malloc(len)) == NULL)
-               return(NULL);
-       if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
-               free(buf);
-               return(NULL);
-       }
-
-       lim = buf + len;
-       for (next = buf; next < lim; next += ifm->ifm_msglen) {
-               ifm = (struct if_msghdr *)next;
-               if (ifm->ifm_type == RTM_IFINFO) {
-                       sa = (struct sockaddr *)(ifm + 1);
-                       get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
-                       if ((sa = rti_info[RTAX_IFP]) != NULL) {
-                               if (sa->sa_family == AF_LINK) {
-                                       sdl = (struct sockaddr_dl *)sa;
-                                       if (strlen(name) != sdl->sdl_nlen)
-                                               continue; /* not same len */
-                                       if (strncmp(&sdl->sdl_data[0],
-                                                   name,
-                                                   sdl->sdl_nlen) == 0) {
-                                               break;
-                                       }
-                               }
-                       }
-               }
-       }
-       if (next == lim) {
-               /* search failed */
-               free(buf);
-               return(NULL);
-       }
-
-       if ((ret_sdl = malloc(sdl->sdl_len)) == NULL)
-               return(NULL);
-       memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
-       return(ret_sdl);
-}
-
-int
-if_getmtu(char *name)
-{
-       struct ifaddrs *ifap, *ifa;
-       struct if_data *ifd;
-       u_long mtu = 0;
-
-       if (getifaddrs(&ifap) < 0)
-               return(0);
-       for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
-               if (strcmp(ifa->ifa_name, name) == 0) {
-                       ifd = ifa->ifa_data;
-                       if (ifd)
-                               mtu = ifd->ifi_mtu;
-                       break;
-               }
-       }
-       freeifaddrs(ifap);
-
-#ifdef SIOCGIFMTU              /* XXX: this ifdef may not be necessary */
-       if (mtu == 0) {
-               struct ifreq ifr;
-               int s;
-
-               if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
-                       return(0);
-
-               ifr.ifr_addr.sa_family = AF_INET6;
-               strncpy(ifr.ifr_name, name,
-                       sizeof(ifr.ifr_name));
-               if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0) {
-                       close(s);
-                       return(0);
-               }
-               close(s);
-
-               mtu = ifr.ifr_mtu;
-       }
-#endif
-
-       return(mtu);
-}
-
-/* give interface index and its old flags, then new flags returned */
-int
-if_getflags(int ifindex, int oifflags)
-{
-       struct ifreq ifr;
-       int s;
-
-       if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
-               syslog(LOG_ERR, "<%s> socket: %s", __func__,
-                      strerror(errno));
-               return (oifflags & ~IFF_UP);
-       }
-
-       if_indextoname(ifindex, ifr.ifr_name);
-       if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
-               syslog(LOG_ERR, "<%s> ioctl:SIOCGIFFLAGS: failed for %s",
-                      __func__, ifr.ifr_name);
-               close(s);
-               return (oifflags & ~IFF_UP);
-       }
-       close(s);
-       return (ifr.ifr_flags);
-}
-
 #define ROUNDUP8(a) (1 + (((a) - 1) | 7))
 int
 lladdropt_length(struct sockaddr_dl *sdl)
 {
-       switch(sdl->sdl_type) {
-        case IFT_ETHER:
-                return(ROUNDUP8(ETHER_ADDR_LEN + 2));
-        default:
-                return(0);
+       switch (sdl->sdl_type) {
+       case IFT_ETHER:
+               return (ROUNDUP8(ETHER_ADDR_LEN + 2));
+       default:
+               return (0);
        }
 }
 
@@ -224,17 +128,16 @@ lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
 
        ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
 
-       switch(sdl->sdl_type) {
-        case IFT_ETHER:
-                ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
-                addr = (char *)(ndopt + 1);
-                memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
-                break;
-        default:
-                syslog(LOG_ERR,
-                       "<%s> unsupported link type(%d)",
-                       __func__, sdl->sdl_type);
-                exit(1);
+       switch (sdl->sdl_type) {
+       case IFT_ETHER:
+               ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
+               addr = (char *)(ndopt + 1);
+               memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
+               break;
+       default:
+               syslog(LOG_ERR, "<%s> unsupported link type(%d)",
+                   __func__, sdl->sdl_type);
+               exit(1);
        }
 
        return;
@@ -244,13 +147,12 @@ int
 rtbuf_len(void)
 {
        size_t len;
-
        int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0};
 
        if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
-               return(-1);
+               return (-1);
 
-       return(len);
+       return (len);
 }
 
 #define FILTER_MATCH(type, filter) ((0x1 << type) & filter)
@@ -270,14 +172,21 @@ get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter)
                /* just for safety */
                if (!rtm->rtm_msglen) {
                        syslog(LOG_WARNING, "<%s> rtm_msglen is 0 "
-                               "(buf=%p lim=%p rtm=%p)", __func__,
-                               buf, lim, rtm);
+                           "(buf=%p lim=%p rtm=%p)", __func__,
+                           buf, lim, rtm);
                        break;
                }
-               if (FILTER_MATCH(rtm->rtm_type, filter) == 0) {
+               if (((struct rt_msghdr *)buf)->rtm_version != RTM_VERSION) {
+                       syslog(LOG_WARNING,
+                           "<%s> routing message version mismatch "
+                           "(buf=%p lim=%p rtm=%p)", __func__,
+                           buf, lim, rtm);
                        continue;
                }
 
+               if (FILTER_MATCH(rtm->rtm_type, filter) == 0)
+                       continue;
+
                switch (rtm->rtm_type) {
                case RTM_GET:
                case RTM_ADD:
@@ -331,6 +240,7 @@ get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter)
                        return (char *)rtm;
                        /* NOTREACHED */
                case RTM_IFINFO:
+               case RTM_IFANNOUNCE:
                        /* found */
                        *lenp = rtm->rtm_msglen;
                        return (char *)rtm;
@@ -338,7 +248,7 @@ get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter)
                }
        }
 
-       return (char *)rtm;
+       return ((char *)rtm);
 }
 #undef FILTER_MATCH
 
@@ -351,7 +261,7 @@ get_addr(char *buf)
        sa = (struct sockaddr *)(rtm + 1);
        get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
 
-       return(&SIN6(rti_info[RTAX_DST])->sin6_addr);
+       return (&SIN6(rti_info[RTAX_DST])->sin6_addr);
 }
 
 int
@@ -363,31 +273,7 @@ get_rtm_ifindex(char *buf)
        sa = (struct sockaddr *)(rtm + 1);
        get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
 
-       return(((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index);
-}
-
-int
-get_ifm_ifindex(char *buf)
-{
-       struct if_msghdr *ifm = (struct if_msghdr *)buf;
-
-       return ((int)ifm->ifm_index);
-}
-
-int
-get_ifam_ifindex(char *buf)
-{
-       struct ifa_msghdr *ifam = (struct ifa_msghdr *)buf;
-
-       return ((int)ifam->ifam_index);
-}
-
-int
-get_ifm_flags(char *buf)
-{
-       struct if_msghdr *ifm = (struct if_msghdr *)buf;
-
-       return (ifm->ifm_flags);
+       return (((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index);
 }
 
 int
@@ -395,19 +281,19 @@ get_prefixlen(char *buf)
 {
        struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
        struct sockaddr *sa, *rti_info[RTAX_MAX];
-       u_char *p, *lim;
-       
+       char *p, *lim;
+
        sa = (struct sockaddr *)(rtm + 1);
        get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
        sa = rti_info[RTAX_NETMASK];
 
-       p = (u_char *)(&SIN6(sa)->sin6_addr);
-       lim = (u_char *)sa + sa->sa_len;
+       p = (char *)(&SIN6(sa)->sin6_addr);
+       lim = (char *)sa + sa->sa_len;
        return prefixlen(p, lim);
 }
 
 int
-prefixlen(u_char *p, u_char *lim)
+prefixlen(unsigned char *p, unsigned char *lim)
 {
        int masklen;
 
@@ -440,144 +326,425 @@ prefixlen(u_char *p, u_char *lim)
                case 0x00:
                        break;
                default:
-                       return(-1);
+                       return (-1);
                }
        }
 
-       return(masklen);
+       return (masklen);
 }
 
-int
-rtmsg_type(char *buf)
+struct ifinfo *
+update_persist_ifinfo(struct ifilist_head_t *ifi_head, const char *ifname)
 {
-       struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+       struct ifinfo *ifi;
+       int ifindex;
+
+       ifi = NULL;
+       ifindex = if_nametoindex(ifname);
+       TAILQ_FOREACH(ifi, ifi_head, ifi_next) {
+               if (ifindex != 0) {
+                       if (ifindex == ifi->ifi_ifindex)
+                               break;
+               } else {
+                       if (strncmp(ifname, ifi->ifi_ifname,
+                               sizeof(ifi->ifi_ifname)) == 0)
+                               break;
+               }
+       }
 
-       return(rtm->rtm_type);
-}
+       if (ifi == NULL) {
+               /* A new ifinfo element is needed. */
+               syslog(LOG_DEBUG, "<%s> new entry: %s", __func__,
+                   ifname);
+
+               ELM_MALLOC(ifi, exit(1));
+               ifi->ifi_ifindex = 0;
+               strncpy(ifi->ifi_ifname, ifname, sizeof(ifi->ifi_ifname)-1);
+               ifi->ifi_ifname[sizeof(ifi->ifi_ifname)-1] = '\0';
+               ifi->ifi_rainfo = NULL;
+               ifi->ifi_state = IFI_STATE_UNCONFIGURED;
+               TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next);
+       }
 
-int
-rtmsg_len(char *buf)
-{
-       struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+       ifi->ifi_persist = 1;
 
-       return(rtm->rtm_msglen);
+       syslog(LOG_DEBUG, "<%s> %s is marked PERSIST", __func__,
+           ifi->ifi_ifname);
+       syslog(LOG_DEBUG, "<%s> %s is state = %d", __func__,
+           ifi->ifi_ifname, ifi->ifi_state);
+       return (ifi);
 }
 
 int
-ifmsg_len(char *buf)
+update_ifinfo_nd_flags(struct ifinfo *ifi)
 {
-       struct if_msghdr *ifm = (struct if_msghdr *)buf;
+       struct in6_ndireq nd;
+       int s;
+       int error;
+
+       if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+               syslog(LOG_ERR,
+                   "<%s> socket() failed.", __func__);
+               return (1);
+       }
+       /* ND flags */
+       memset(&nd, 0, sizeof(nd));
+       strncpy(nd.ifname, ifi->ifi_ifname,
+           sizeof(nd.ifname));
+       error = ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd);
+       if (error) {
+               close(s);
+               if (errno != EPFNOSUPPORT)
+                       syslog(LOG_ERR, "<%s> ioctl() failed.", __func__);
+               return (1);
+       }
+       ifi->ifi_nd_flags = nd.ndi.flags;
+       close(s);
 
-       return(ifm->ifm_msglen);
+       return (0);
 }
 
-/*
- * alloc buffer and get if_msghdrs block from kernel,
- * and put them into the buffer
- */
-static void
-get_iflist(char **buf, size_t *size)
+struct ifinfo *
+update_ifinfo(struct ifilist_head_t *ifi_head, int ifindex)
 {
-       int mib[6];
-
-       mib[0] = CTL_NET;
-       mib[1] = PF_ROUTE;
-       mib[2] = 0;
-       mib[3] = AF_INET6;
-       mib[4] = NET_RT_IFLIST;
-       mib[5] = 0;
-
-       if (sysctl(mib, 6, NULL, size, NULL, 0) < 0) {
-               syslog(LOG_ERR, "<%s> sysctl: iflist size get failed",
-                      __func__);
+       struct if_msghdr *ifm;
+       struct ifinfo *ifi = NULL;
+       struct sockaddr *sa;
+       struct sockaddr *rti_info[RTAX_MAX];
+       char *msg;
+       size_t len;
+       char *lim;
+       int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET6, NET_RT_IFLIST, 0 };
+       int error;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), NULL, &len, NULL, 0) <
+           0) {
+               syslog(LOG_ERR,
+                   "<%s> sysctl: NET_RT_IFLIST size get failed", __func__);
                exit(1);
        }
-       if ((*buf = malloc(*size)) == NULL) {
+       if ((msg = malloc(len)) == NULL) {
                syslog(LOG_ERR, "<%s> malloc failed", __func__);
                exit(1);
        }
-       if (sysctl(mib, 6, *buf, size, NULL, 0) < 0) {
-               syslog(LOG_ERR, "<%s> sysctl: iflist get failed",
-                      __func__);
+       if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), msg, &len, NULL, 0) <
+           0) {
+               syslog(LOG_ERR,
+                   "<%s> sysctl: NET_RT_IFLIST get failed", __func__);
                exit(1);
        }
-       return;
-}
 
-/*
- * alloc buffer and parse if_msghdrs block passed as arg,
- * and init the buffer as list of pointers ot each of the if_msghdr.
- */
-static void
-parse_iflist(struct if_msghdr ***ifmlist_p, char *buf, size_t bufsize)
-{
-       int iflentry_size, malloc_size;
-       struct if_msghdr *ifm;
-       struct ifa_msghdr *ifam;
-       char *lim;
+       lim = msg + len;
+       for (ifm = (struct if_msghdr *)msg;
+            ifm != NULL && ifm < (struct if_msghdr *)lim;
+            ifm = get_next_msghdr(ifm,(struct if_msghdr *)lim)) {
+               int ifi_new;
 
-       /*
-        * Estimate least size of an iflist entry, to be obtained from kernel.
-        * Should add sizeof(sockaddr) ??
-        */
-       iflentry_size = sizeof(struct if_msghdr);
-       /* roughly estimate max list size of pointers to each if_msghdr */
-       malloc_size = (bufsize/iflentry_size) * sizeof(size_t);
-       if ((*ifmlist_p = (struct if_msghdr **)malloc(malloc_size)) == NULL) {
-               syslog(LOG_ERR, "<%s> malloc failed", __func__);
-               exit(1);
-       }
+               syslog(LOG_DEBUG, "<%s> ifm = %p, lim = %p, diff = %zu",
+                   __func__, ifm, lim, (char *)lim - (char *)ifm);
 
-       lim = buf + bufsize;
-       for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) {
+               if (ifm->ifm_version != RTM_VERSION) {
+                       syslog(LOG_ERR,
+                           "<%s> ifm_vesrion mismatch", __func__);
+                       exit(1);
+               }
                if (ifm->ifm_msglen == 0) {
-                       syslog(LOG_WARNING, "<%s> ifm_msglen is 0 "
-                              "(buf=%p lim=%p ifm=%p)", __func__,
-                              buf, lim, ifm);
-                       return;
+                       syslog(LOG_WARNING,
+                           "<%s> ifm_msglen is 0", __func__);
+                       free(msg);
+                       return (NULL);
                }
 
+               ifi_new = 0;
                if (ifm->ifm_type == RTM_IFINFO) {
-                       (*ifmlist_p)[ifm->ifm_index] = ifm;
+                       struct ifreq ifr;
+                       int s;
+                       char ifname[IFNAMSIZ];
+
+                       syslog(LOG_DEBUG, "<%s> RTM_IFINFO found. "
+                           "ifm_index = %d, ifindex = %d",
+                           __func__, ifm->ifm_index, ifindex);
+
+                       /* when ifindex is specified */
+                       if (ifindex != UPDATE_IFINFO_ALL &&
+                           ifindex != ifm->ifm_index)
+                               continue;
+
+                       /* lookup an entry with the same ifindex */
+                       TAILQ_FOREACH(ifi, ifi_head, ifi_next) {
+                               if (ifm->ifm_index == ifi->ifi_ifindex)
+                                       break;
+                               if_indextoname(ifm->ifm_index, ifname);
+                               if (strncmp(ifname, ifi->ifi_ifname,
+                                       sizeof(ifname)) == 0)
+                                       break;
+                       }
+                       if (ifi == NULL) {
+                               syslog(LOG_DEBUG,
+                                   "<%s> new entry for idx=%d",
+                                   __func__, ifm->ifm_index);
+                               ELM_MALLOC(ifi, exit(1));
+                               ifi->ifi_rainfo = NULL;
+                               ifi->ifi_state = IFI_STATE_UNCONFIGURED;
+                               ifi->ifi_persist = 0;
+                               ifi_new = 1;
+                       }
+                       /* ifindex */
+                       ifi->ifi_ifindex = ifm->ifm_index;
+
+                       /* ifname */
+                       if_indextoname(ifm->ifm_index, ifi->ifi_ifname);
+                       if (ifi->ifi_ifname == NULL) {
+                               syslog(LOG_WARNING,
+                                   "<%s> ifname not found (idx=%d)",
+                                   __func__, ifm->ifm_index);
+                               if (ifi_new)
+                                       free(ifi);
+                               continue;
+                       }
+
+                       if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+                               syslog(LOG_ERR,
+                                   "<%s> socket() failed.", __func__);
+                               if (ifi_new)
+                                       free(ifi);
+                               continue;
+                       }
+
+                       /* MTU  */
+                       ifi->ifi_phymtu = ifm->ifm_data.ifi_mtu;
+                       if (ifi->ifi_phymtu == 0) {
+                               memset(&ifr, 0, sizeof(ifr));
+                               ifr.ifr_addr.sa_family = AF_INET6;
+                               strncpy(ifr.ifr_name, ifi->ifi_ifname,
+                                   sizeof(ifr.ifr_name));
+                               error = ioctl(s, SIOCGIFMTU, (caddr_t)&ifr);
+                               if (error) {
+                                       close(s);
+                                       syslog(LOG_ERR,
+                                           "<%s> ioctl() failed.",
+                                           __func__);
+                                       if (ifi_new)
+                                               free(ifi);
+                                       continue;
+                               }
+                               ifi->ifi_phymtu = ifr.ifr_mtu;
+                               if (ifi->ifi_phymtu == 0) {
+                                       syslog(LOG_WARNING,
+                                           "<%s> no interface mtu info"
+                                           " on %s.  %d will be used.",
+                                           __func__, ifi->ifi_ifname,
+                                           IPV6_MMTU);
+                                       ifi->ifi_phymtu = IPV6_MMTU;
+                               }
+                       }
+                       close(s);
+
+                       /* ND flags */
+                       error = update_ifinfo_nd_flags(ifi);
+                       if (error) {
+                               if (ifi_new)
+                                       free(ifi);
+                               continue;
+                       }
+
+                       /* SDL */
+                       sa = (struct sockaddr *)(ifm + 1);
+                       get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
+                       if ((sa = rti_info[RTAX_IFP]) != NULL) {
+                               if (sa->sa_family == AF_LINK) {
+                                       memcpy(&ifi->ifi_sdl,
+                                           (struct sockaddr_dl *)sa,
+                                           sizeof(ifi->ifi_sdl));
+                               }
+                       } else
+                               memset(&ifi->ifi_sdl, 0,
+                                   sizeof(ifi->ifi_sdl));
+
+                       /* flags */
+                       ifi->ifi_flags = ifm->ifm_flags;
+
+                       /* type */
+                       ifi->ifi_type = ifm->ifm_type;
                } else {
-                       syslog(LOG_ERR, "out of sync parsing NET_RT_IFLIST\n"
-                              "expected %d, got %d\n msglen = %d\n"
-                              "buf:%p, ifm:%p, lim:%p\n",
-                              RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen,
-                              buf, ifm, lim);
-                       exit (1);
+                       syslog(LOG_ERR,
+                           "out of sync parsing NET_RT_IFLIST\n"
+                           "expected %d, got %d\n msglen = %d\n",
+                           RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen);
+                       exit(1);
                }
-               for (ifam = (struct ifa_msghdr *)
-                       ((char *)ifm + ifm->ifm_msglen);
-                    ifam < (struct ifa_msghdr *)lim;
-                    ifam = (struct ifa_msghdr *)
-                       ((char *)ifam + ifam->ifam_msglen)) {
-                       /* just for safety */
-                       if (!ifam->ifam_msglen) {
-                               syslog(LOG_WARNING, "<%s> ifa_msglen is 0 "
-                                      "(buf=%p lim=%p ifam=%p)", __func__,
-                                      buf, lim, ifam);
-                               return;
-                       }
-                       if (ifam->ifam_type != RTM_NEWADDR)
-                               break;
+
+               if (ifi_new) {
+                       syslog(LOG_DEBUG,
+                           "<%s> adding %s(idx=%d) to ifilist",
+                           __func__, ifi->ifi_ifname, ifi->ifi_ifindex);
+                       TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next);
                }
-               ifm = (struct if_msghdr *)ifam;
        }
+       free(msg);
+
+       if (mcastif != NULL) {
+               error = sock_mc_rr_update(&sock, mcastif);
+               if (error)
+                       exit(1);
+       }
+
+       return (ifi);
 }
 
-void
-init_iflist(void)
+static struct if_msghdr *
+get_next_msghdr(struct if_msghdr *ifm, struct if_msghdr *lim)
+{
+       struct ifa_msghdr *ifam;
+
+       for (ifam = (struct ifa_msghdr *)((char *)ifm + ifm->ifm_msglen);
+            ifam < (struct ifa_msghdr *)lim;
+            ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen)) {
+               if (!ifam->ifam_msglen) {
+                       syslog(LOG_WARNING,
+                           "<%s> ifa_msglen is 0", __func__);
+                       return (NULL);
+               }
+               if (ifam->ifam_type != RTM_NEWADDR)
+                       break;
+       }
+
+       return ((struct if_msghdr *)ifam);
+}
+
+int
+getinet6sysctl(int code)
 {
-       if (ifblock) {
-               free(ifblock);
-               ifblock_size = 0;
+       int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
+       int value;
+       size_t size;
+
+       mib[3] = code;
+       size = sizeof(value);
+       if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
+           < 0) {
+               syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
+                   __func__, code,
+                   strerror(errno));
+               return (-1);
+       }
+       else
+               return (value);
+}
+
+
+int
+sock_mc_join(struct sockinfo *s, int ifindex)
+{
+       struct ipv6_mreq mreq;
+       char ifname[IFNAMSIZ];
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       if (ifindex == 0)
+               return (1);
+
+       /*
+        * join all routers multicast address on each advertising
+        * interface.
+        */
+       memset(&mreq, 0, sizeof(mreq));
+       /* XXX */
+       memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
+           &sin6_linklocal_allrouters.sin6_addr,
+           sizeof(mreq.ipv6mr_multiaddr.s6_addr));
+
+       mreq.ipv6mr_interface = ifindex;
+       if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
+               sizeof(mreq)) < 0) {
+               syslog(LOG_ERR,
+                   "<%s> IPV6_JOIN_GROUP(link) on %s: %s",
+                   __func__, if_indextoname(ifindex, ifname),
+                   strerror(errno));
+               return (1);
        }
-       if (iflist)
-               free(iflist);
-       /* get iflist block from kernel */
-       get_iflist(&ifblock, &ifblock_size);
+       syslog(LOG_DEBUG,
+           "<%s> %s: join link-local all-routers MC group",
+           __func__, if_indextoname(ifindex, ifname));
+
+       return (0);
+}
+
+int
+sock_mc_leave(struct sockinfo *s, int ifindex)
+{
+       struct ipv6_mreq mreq;
+       char ifname[IFNAMSIZ];
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       if (ifindex == 0)
+               return (1);
+
+       /*
+        * join all routers multicast address on each advertising
+        * interface.
+        */
+
+       memset(&mreq, 0, sizeof(mreq));
+       /* XXX */
+       memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
+           &sin6_linklocal_allrouters.sin6_addr,
+           sizeof(mreq.ipv6mr_multiaddr.s6_addr));
+
+       mreq.ipv6mr_interface = ifindex;
+       if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq,
+               sizeof(mreq)) < 0) {
+               syslog(LOG_ERR,
+                   "<%s> IPV6_JOIN_LEAVE(link) on %s: %s",
+                   __func__, if_indextoname(ifindex, ifname),
+                   strerror(errno));
+               return (1);
+       }
+       syslog(LOG_DEBUG,
+           "<%s> %s: leave link-local all-routers MC group",
+           __func__, if_indextoname(ifindex, ifname));
+
+       return (0);
+}
+
+int
+sock_mc_rr_update(struct sockinfo *s, char *mif)
+{
+       struct ipv6_mreq mreq;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       if (mif == NULL)
+               return (1);
+       /*
+        * When attending router renumbering, join all-routers site-local
+        * multicast group.
+        */
+       /* XXX */
+       memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
+           &sin6_sitelocal_allrouters.sin6_addr,
+           sizeof(mreq.ipv6mr_multiaddr.s6_addr));
+       if ((mreq.ipv6mr_interface = if_nametoindex(mif)) == 0) {
+               syslog(LOG_ERR,
+                   "<%s> invalid interface: %s",
+                   __func__, mif);
+               return (1);
+       }
+
+       if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+               &mreq, sizeof(mreq)) < 0) {
+               syslog(LOG_ERR,
+                   "<%s> IPV6_JOIN_GROUP(site) on %s: %s",
+                   __func__, mif, strerror(errno));
+               return (1);
+       }
+
+       syslog(LOG_DEBUG,
+           "<%s> %s: join site-local all-routers MC group",
+           __func__, mif);
 
-       /* make list of pointers to each if_msghdr */
-       parse_iflist(&iflist, ifblock, ifblock_size);
+       return (0);
 }
index bf282ec..0f17a41 100644 (file)
@@ -1,9 +1,10 @@
-/*     $KAME: if.h,v 1.6 2001/01/21 15:37:14 itojun Exp $      */
+/*     $FreeBSD: stable/10/usr.sbin/rtadvd/if.h 224144 2011-07-17 19:24:54Z hrs $      */
+/*     $KAME: if.h,v 1.10 2003/02/24 11:29:10 ono Exp $        */
 
 /*
  * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
  * All rights reserved.
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -15,7 +16,7 @@
  * 3. Neither the name of the project nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
- * 
+ *
  * 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
  * 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: src/usr.sbin/rtadvd/if.h,v 1.1.2.2 2001/07/03 11:02:14 ume Exp $
- * $DragonFly: src/usr.sbin/rtadvd/if.h,v 1.3 2003/11/03 19:31:42 eirikn Exp $
  */
 
-#define RTADV_TYPE2BITMASK(type) (0x1 << type)
+#define        UPDATE_IFINFO_ALL       0
 
-extern struct if_msghdr **iflist;
-extern size_t ifblock_size;
-extern char *ifblock;
+struct sockinfo {
+       int             si_fd;
+       const char      *si_name;
+};
+
+extern struct sockinfo sock;
+extern struct sockinfo rtsock;
+extern struct sockinfo ctrlsock;
 
-struct nd_opt_hdr;
-struct sockaddr_dl *if_nametosdl(char *);
-int if_getmtu(char *);
-int if_getflags(int, int);
 int lladdropt_length(struct sockaddr_dl *);
 void lladdropt_fill(struct sockaddr_dl *, struct nd_opt_hdr *);
 int rtbuf_len(void);
 char *get_next_msg(char *, char *, int, size_t *, int);
 struct in6_addr *get_addr(char *);
 int get_rtm_ifindex(char *);
-int get_ifm_ifindex(char *);
-int get_ifam_ifindex(char *);
-int get_ifm_flags(char *);
 int get_prefixlen(char *);
-int prefixlen(u_char *, u_char *);
-int rtmsg_type(char *);
-int ifmsg_type(char *);
-int rtmsg_len(char *);
-int ifmsg_len(char *);
-void init_iflist(void);
+int prefixlen(unsigned char *, unsigned char *);
+
+struct ifinfo  *update_ifinfo(struct ifilist_head_t *, int);
+int            update_ifinfo_nd_flags(struct ifinfo *);
+struct ifinfo  *update_persist_ifinfo(struct ifilist_head_t *,
+                       const char *);
+
+int            sock_mc_join(struct sockinfo *, int);
+int            sock_mc_leave(struct sockinfo *, int);
+int            sock_mc_rr_update(struct sockinfo *, char *);
+int            getinet6sysctl(int);
index 9c7ea03..d5c20d5 100644 (file)
@@ -1,5 +1,6 @@
 /*     $KAME: pathnames.h,v 1.2 2000/05/16 13:34:13 itojun Exp $       */
-/*     $FreeBSD: src/usr.sbin/rtadvd/pathnames.h,v 1.2.2.2 2001/07/03 11:02:14 ume Exp $ */
-/*     $DragonFly: src/usr.sbin/rtadvd/pathnames.h,v 1.2 2003/06/17 04:30:02 dillon Exp $ */
+/*     $FreeBSD: stable/10/usr.sbin/rtadvd/pathnames.h 224144 2011-07-17 19:24:54Z hrs $       */
 
-#define _PATH_RTADVDCONF "/etc/rtadvd.conf"
+#define        _PATH_RTADVDCONF "/etc/rtadvd.conf"
+#define        _PATH_RTADVDPID "/var/run/rtadvd.pid"
+#define        _PATH_CTRL_SOCK "/var/run/rtadvd.sock"
index 82ab834..a52bae4 100644 (file)
@@ -1,4 +1,5 @@
-/*     $KAME: rrenum.c,v 1.10 2001/01/21 15:32:16 itojun Exp $ */
+/*     $FreeBSD: stable/10/usr.sbin/rtadvd/rrenum.c 253970 2013-08-05 20:13:02Z hrs $  */
+/*     $KAME: rrenum.c,v 1.12 2002/06/10 19:59:47 itojun Exp $ */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -27,8 +28,6 @@
  * 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: src/usr.sbin/rtadvd/rrenum.c,v 1.2.2.2 2001/07/03 11:02:14 ume Exp $
  */
 #include <sys/types.h>
 #include <sys/param.h>
@@ -37,9 +36,8 @@
 #include <sys/sysctl.h>
 
 #include <net/if.h>
-#if defined(__DragonFly__)
+#include <net/if_dl.h>
 #include <net/if_var.h>
-#endif /* __DragonFly__ */
 #include <net/route.h>
 #include <netinet/in.h>
 #include <netinet/in_var.h>
 #include <arpa/inet.h>
 
 #include <errno.h>
+#include <netdb.h>
 #include <string.h>
 #include <stdlib.h>
+#include <time.h>
 #include <syslog.h>
 #include "rtadvd.h"
 #include "rrenum.h"
@@ -77,7 +77,7 @@ static int s = -1;
 
 /*
  * Check validity of a Prefix Control Operation(PCO).
- * Return 0 on success, 1 on failure.
+ * return 0 on success, 1 on failure.
  */
 static int
 rr_pco_check(int len, struct rr_pco_match *rpm)
@@ -89,25 +89,25 @@ rr_pco_check(int len, struct rr_pco_match *rpm)
        if ((rpm->rpm_len - 3) < 0 || /* must be at least 3 */
            (rpm->rpm_len - 3) & 0x3) { /* must be multiple of 4 */
                syslog(LOG_WARNING, "<%s> rpm_len %d is not 4N * 3",
-                      __func__, rpm->rpm_len);
-               return 1;
+                   __func__, rpm->rpm_len);
+               return (1);
        }
        /* rpm->rpm_code must be valid value */
-       switch(rpm->rpm_code) {
+       switch (rpm->rpm_code) {
        case RPM_PCO_ADD:
        case RPM_PCO_CHANGE:
        case RPM_PCO_SETGLOBAL:
                break;
        default:
                syslog(LOG_WARNING, "<%s> unknown rpm_code %d", __func__,
-                      rpm->rpm_code);
-               return 1;
+                   rpm->rpm_code);
+               return (1);
        }
        /* rpm->rpm_matchlen must be 0 to 128 inclusive */
        if (rpm->rpm_matchlen > 128) {
                syslog(LOG_WARNING, "<%s> rpm_matchlen %d is over 128",
-                      __func__, rpm->rpm_matchlen);
-               return 1;
+                   __func__, rpm->rpm_matchlen);
+               return (1);
        }
 
        /*
@@ -129,23 +129,23 @@ rr_pco_check(int len, struct rr_pco_match *rpm)
                 */
                if (checklen > 128) {
                        syslog(LOG_WARNING, "<%s> sum of rpu_uselen %d and"
-                              " rpu_keeplen %d is %d(over 128)",
-                              __func__, rpu->rpu_uselen,
-                              rpu->rpu_keeplen,
-                              rpu->rpu_uselen + rpu->rpu_keeplen);
-                       return 1;
+                           " rpu_keeplen %d is %d(over 128)",
+                           __func__, rpu->rpu_uselen, rpu->rpu_keeplen,
+                           rpu->rpu_uselen + rpu->rpu_keeplen);
+                       return (1);
                }
        }
-       return 0;
+       return (0);
 }
 
 static void
 do_use_prefix(int len, struct rr_pco_match *rpm,
-             struct in6_rrenumreq *irr, int ifindex)
+       struct in6_rrenumreq *irr, int ifindex)
 {
        struct rr_pco_use *rpu, *rpulim;
        struct rainfo *rai;
-       struct prefix *pp;
+       struct ifinfo *ifi;
+       struct prefix *pfx;
 
        rpu = (struct rr_pco_use *)(rpm + 1);
        rpulim = (struct rr_pco_use *)((char *)rpm + len);
@@ -167,7 +167,7 @@ do_use_prefix(int len, struct rr_pco_match *rpm,
                if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 &&
                    errno != EADDRNOTAVAIL)
                        syslog(LOG_ERR, "<%s> ioctl: %s", __func__,
-                              strerror(errno));
+                           strerror(errno));
                return;
        }
 
@@ -179,19 +179,23 @@ do_use_prefix(int len, struct rr_pco_match *rpm,
                irr->irr_u_uselen = rpu->rpu_uselen;
                irr->irr_u_keeplen = rpu->rpu_keeplen;
                irr->irr_raf_mask_onlink =
-                       (rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK) == 0 ? 0 : 1;
+                   !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK);
                irr->irr_raf_mask_auto =
-                       (rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_AUTO);
+                   !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_AUTO);
                irr->irr_vltime = ntohl(rpu->rpu_vltime);
                irr->irr_pltime = ntohl(rpu->rpu_pltime);
                irr->irr_raf_onlink =
-                       (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK) == 0 ? 0 : 1;
+                   (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK) == 0 ?
+                   0 : 1;
                irr->irr_raf_auto =
-                       (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_AUTO) == 0 ? 0 : 1;
+                   (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_AUTO) == 0 ?
+                   0 : 1;
                irr->irr_rrf_decrvalid =
-                       (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME) == 0 ? 0 : 1;
+                   (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME) == 0 ?
+                   0 : 1;
                irr->irr_rrf_decrprefd =
-                       (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME) == 0 ? 0 : 1;
+                   (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME) == 0 ?
+                   0 : 1;
                irr->irr_useprefix.sin6_len = sizeof(irr->irr_useprefix);
                irr->irr_useprefix.sin6_family = AF_INET6;
                irr->irr_useprefix.sin6_addr = rpu->rpu_prefix;
@@ -199,38 +203,45 @@ do_use_prefix(int len, struct rr_pco_match *rpm,
                if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 &&
                    errno != EADDRNOTAVAIL)
                        syslog(LOG_ERR, "<%s> ioctl: %s", __func__,
-                              strerror(errno));
+                           strerror(errno));
 
                /* very adhoc: should be rewritten */
                if (rpm->rpm_code == RPM_PCO_CHANGE &&
                    IN6_ARE_ADDR_EQUAL(&rpm->rpm_prefix, &rpu->rpu_prefix) &&
                    rpm->rpm_matchlen == rpu->rpu_uselen &&
                    rpu->rpu_uselen == rpu->rpu_keeplen) {
-                       if ((rai = if_indextorainfo(ifindex)) == NULL)
+                       ifi = if_indextoifinfo(ifindex);
+                       if (ifi == NULL || ifi->ifi_rainfo == NULL)
                                continue; /* non-advertising IF */
+                       rai = ifi->ifi_rainfo;
 
-                       for (pp = rai->prefix.next; pp != &rai->prefix;
-                            pp = pp->next) {
-                               struct timeval now;
+                       TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) {
+                               struct timespec now;
 
-                               if (prefix_match(&pp->prefix, pp->prefixlen,
-                                                &rpm->rpm_prefix,
-                                                rpm->rpm_matchlen)) {
+                               if (prefix_match(&pfx->pfx_prefix,
+                                   pfx->pfx_prefixlen, &rpm->rpm_prefix,
+                                   rpm->rpm_matchlen)) {
                                        /* change parameters */
-                                       pp->validlifetime = ntohl(rpu->rpu_vltime);
-                                       pp->preflifetime = ntohl(rpu->rpu_pltime);
+                                       pfx->pfx_validlifetime =
+                                           ntohl(rpu->rpu_vltime);
+                                       pfx->pfx_preflifetime =
+                                           ntohl(rpu->rpu_pltime);
                                        if (irr->irr_rrf_decrvalid) {
-                                               gettimeofday(&now, 0);
-                                               pp->vltimeexpire =
-                                                       now.tv_sec + pp->validlifetime;
+                                               clock_gettime(CLOCK_MONOTONIC_FAST,
+                                                   &now);
+                                               pfx->pfx_vltimeexpire =
+                                                   now.tv_sec +
+                                                   pfx->pfx_validlifetime;
                                        } else
-                                               pp->vltimeexpire = 0;
+                                               pfx->pfx_vltimeexpire = 0;
                                        if (irr->irr_rrf_decrprefd) {
-                                               gettimeofday(&now, 0);
-                                               pp->pltimeexpire =
-                                                       now.tv_sec + pp->preflifetime;
+                                               clock_gettime(CLOCK_MONOTONIC_FAST,
+                                                   &now);
+                                               pfx->pfx_pltimeexpire =
+                                                   now.tv_sec +
+                                                   pfx->pfx_preflifetime;
                                        } else
-                                               pp->pltimeexpire = 0;
+                                               pfx->pfx_pltimeexpire = 0;
                                }
                        }
                }
@@ -246,13 +257,14 @@ do_pco(struct icmp6_router_renum *rr, int len, struct rr_pco_match *rpm)
 {
        int ifindex = 0;
        struct in6_rrenumreq irr;
+       struct ifinfo *ifi;
 
        if ((rr_pco_check(len, rpm) != 0))
-               return 1;
+               return (1);
 
        if (s == -1 && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
                syslog(LOG_ERR, "<%s> socket: %s", __func__,
-                      strerror(errno));
+                   strerror(errno));
                exit(1);
        }
 
@@ -266,24 +278,30 @@ do_pco(struct icmp6_router_renum *rr, int len, struct rr_pco_match *rpm)
        irr.irr_matchprefix.sin6_addr = rpm->rpm_prefix;
 
        while (if_indextoname(++ifindex, irr.irr_name)) {
+               ifi = if_indextoifinfo(ifindex);
+               if (ifi == NULL) {
+                       syslog(LOG_ERR, "<%s> ifindex not found.",
+                           __func__);
+                       return (1);
+               }
                /*
-                * if ICMP6_RR_FLAGS_FORCEAPPLY(A flag) is 0 and IFF_UP is off,
-                * the interface is not applied
+                * if ICMP6_RR_FLAGS_FORCEAPPLY(A flag) is 0 and
+                * IFF_UP is off, the interface is not applied
                 */
                if ((rr->rr_flags & ICMP6_RR_FLAGS_FORCEAPPLY) == 0 &&
-                   (iflist[ifindex]->ifm_flags & IFF_UP) == 0)
+                   (ifi->ifi_flags & IFF_UP) == 0)
                        continue;
                /* TODO: interface scope check */
                do_use_prefix(len, rpm, &irr, ifindex);
        }
        if (errno == ENXIO)
-               return 0;
+               return (0);
        else if (errno) {
                syslog(LOG_ERR, "<%s> if_indextoname: %s", __func__,
-                      strerror(errno));
-               return 1;
+                   strerror(errno));
+               return (1);
        }
-       return 0;
+       return (0);
 }
 
 /*
@@ -301,18 +319,17 @@ do_rr(int len, struct icmp6_router_renum *rr)
        cp = (char *)(rr + 1);
        len -= sizeof(struct icmp6_router_renum);
 
-       /* get iflist block from kernel again, to get up-to-date information */
-       init_iflist();
+       update_ifinfo(&ifilist, UPDATE_IFINFO_ALL);
 
        while (cp < lim) {
                int rpmlen;
 
                rpm = (struct rr_pco_match *)cp;
-               if (len < sizeof(struct rr_pco_match)) {
+               if ((size_t)len < sizeof(struct rr_pco_match)) {
                    tooshort:
                        syslog(LOG_ERR, "<%s> pkt too short. left len = %d. "
-                              "garbage at end of pkt?", __func__, len);
-                       return 1;
+                           "garbage at end of pkt?", __func__, len);
+                       return (1);
                }
                rpmlen = rpm->rpm_len << 3;
                if (len < rpmlen)
@@ -328,7 +345,7 @@ do_rr(int len, struct icmp6_router_renum *rr)
                len -= rpmlen;
        }
 
-       return 0;
+       return (0);
 }
 
 /*
@@ -337,46 +354,45 @@ do_rr(int len, struct icmp6_router_renum *rr)
  */
 static int
 rr_command_check(int len, struct icmp6_router_renum *rr, struct in6_addr *from,
-                struct in6_addr *dst)
+       struct in6_addr *dst)
 {
        u_char ntopbuf[INET6_ADDRSTRLEN];
 
        /* omit rr minimal length check. hope kernel have done it. */
        /* rr_command length check */
-       if (len < (sizeof(struct icmp6_router_renum) +
-                  sizeof(struct rr_pco_match))) {
+       if ((size_t)len < (sizeof(struct icmp6_router_renum) +
+           sizeof(struct rr_pco_match))) {
                syslog(LOG_ERR, "<%s> rr_command len %d is too short",
-                      __func__, len);
-               return 1;
+                   __func__, len);
+               return (1);
        }
 
        /* destination check. only for multicast. omit unicast check. */
        if (IN6_IS_ADDR_MULTICAST(dst) && !IN6_IS_ADDR_MC_LINKLOCAL(dst) &&
            !IN6_IS_ADDR_MC_SITELOCAL(dst)) {
                syslog(LOG_ERR, "<%s> dst mcast addr %s is illegal",
-                      __func__,
-                      inet_ntop(AF_INET6, dst, ntopbuf, INET6_ADDRSTRLEN));
-               return 1;
+                   __func__,
+                   inet_ntop(AF_INET6, dst, ntopbuf, sizeof(ntopbuf)));
+               return (1);
        }
 
        /* seqnum and segnum check */
        if (rro.rro_seqnum > rr->rr_seqnum) {
                syslog(LOG_WARNING,
-                      "<%s> rcvd old seqnum %d from %s",
-                      __func__, (u_int32_t)ntohl(rr->rr_seqnum),
-                      inet_ntop(AF_INET6, from, ntopbuf, INET6_ADDRSTRLEN));
-               return 1;
+                   "<%s> rcvd old seqnum %d from %s",
+                   __func__, (u_int32_t)ntohl(rr->rr_seqnum),
+                  inet_ntop(AF_INET6, from, ntopbuf, sizeof(ntopbuf)));
+               return (1);
        }
        if (rro.rro_seqnum == rr->rr_seqnum &&
            (rr->rr_flags & ICMP6_RR_FLAGS_TEST) == 0 &&
            RR_ISSET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum)) {
                if ((rr->rr_flags & ICMP6_RR_FLAGS_REQRESULT) != 0)
                        syslog(LOG_WARNING,
-                              "<%s> rcvd duped segnum %d from %s",
-                              __func__, rr->rr_segnum,
-                              inet_ntop(AF_INET6, from, ntopbuf,
-                                        INET6_ADDRSTRLEN));
-               return 0;
+                           "<%s> rcvd duped segnum %d from %s",
+                           __func__, rr->rr_segnum, inet_ntop(AF_INET6, from,
+                               ntopbuf, sizeof(ntopbuf)));
+               return (0);
        }
 
        /* update seqnum */
@@ -385,16 +401,16 @@ rr_command_check(int len, struct icmp6_router_renum *rr, struct in6_addr *from,
 
                /* init rro_segnum_bits */
                memset(rro.rro_segnum_bits, 0,
-                      sizeof(rro.rro_segnum_bits));
+                   sizeof(rro.rro_segnum_bits));
        }
        rro.rro_seqnum = rr->rr_seqnum;
 
-       return 0;
+       return (0);
 }
 
 static void
 rr_command_input(int len, struct icmp6_router_renum *rr,
-                struct in6_addr *from, struct in6_addr *dst)
+       struct in6_addr *from, struct in6_addr *dst)
 {
        /* rr_command validity check */
        if (rr_command_check(len, rr, from, dst))
@@ -404,9 +420,8 @@ rr_command_input(int len, struct icmp6_router_renum *rr,
                return;
 
        /* do router renumbering */
-       if (do_rr(len, rr)) {
+       if (do_rr(len, rr))
                goto failed;
-       }
 
        /* update segnum */
        RR_SET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum);
@@ -420,27 +435,26 @@ rr_command_input(int len, struct icmp6_router_renum *rr,
 
 void
 rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi,
-        struct sockaddr_in6 *from, struct in6_addr *dst)
+       struct sockaddr_in6 *from, struct in6_addr *dst)
 {
        u_char ntopbuf[2][INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
 
        syslog(LOG_DEBUG,
-              "<%s> RR received from %s to %s on %s",
-              __func__,
-              inet_ntop(AF_INET6, &from->sin6_addr,
-                        ntopbuf[0], INET6_ADDRSTRLEN),
-              inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN),
-              if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+           "<%s> RR received from %s to %s on %s",
+           __func__,
+           inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[0] ,sizeof(ntopbuf[0])),
+           inet_ntop(AF_INET6, &dst, ntopbuf[1], sizeof(ntopbuf[1])),
+           if_indextoname(pi->ipi6_ifindex, ifnamebuf));
 
        /* packet validation based on Section 4.1 of RFC2894 */
-       if (len < sizeof(struct icmp6_router_renum)) {
+       if ((size_t)len < sizeof(struct icmp6_router_renum)) {
                syslog(LOG_NOTICE,
-                      "<%s>: RR short message (size %d) from %s to %s on %s",
-                      __func__, len,
-                      inet_ntop(AF_INET6, &from->sin6_addr,
-                                ntopbuf[0], INET6_ADDRSTRLEN),
-                      inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN),
-                      if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+                   "<%s>: RR short message (size %d) from %s to %s on %s",
+                   __func__, len,
+                   inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[0],
+                       sizeof(ntopbuf[0])),
+                   inet_ntop(AF_INET6, &dst, ntopbuf[1], sizeof(ntopbuf[1])),
+                   if_indextoname(pi->ipi6_ifindex, ifnamebuf));
                return;
        }
 
@@ -452,16 +466,16 @@ rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi,
         * We rely on the kernel input routine for unicast addresses, and thus
         * check multicast destinations only.
         */
-       if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) &&
-           !IN6_ARE_ADDR_EQUAL(&in6a_site_allrouters, &pi->ipi6_addr)) {
+       if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && !IN6_ARE_ADDR_EQUAL(
+           &sin6_sitelocal_allrouters.sin6_addr, &pi->ipi6_addr)) {
                syslog(LOG_NOTICE,
-                      "<%s>: RR message with invalid destination (%s) "
-                      "from %s on %s",
-                      __func__,
-                      inet_ntop(AF_INET6, &dst, ntopbuf[0], INET6_ADDRSTRLEN),
-                      inet_ntop(AF_INET6, &from->sin6_addr,
-                                ntopbuf[1], INET6_ADDRSTRLEN),
-                      if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+                   "<%s>: RR message with invalid destination (%s) "
+                   "from %s on %s",
+                   __func__,
+                   inet_ntop(AF_INET6, &dst, ntopbuf[0], sizeof(ntopbuf[0])),
+                   inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[1],
+                             sizeof(ntopbuf[1])),
+                   if_indextoname(pi->ipi6_ifindex, ifnamebuf));
                return;
        }
 
@@ -480,7 +494,7 @@ rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi,
                break;
        default:
                syslog(LOG_ERR, "<%s> received unknown code %d",
-                      __func__, rr->rr_code);
+                   __func__, rr->rr_code);
                break;
 
        }
index 39aebb4..033d19d 100644 (file)
@@ -1,9 +1,10 @@
+/*     $FreeBSD: stable/10/usr.sbin/rtadvd/rrenum.h 222732 2011-06-06 03:06:43Z hrs $  */
 /*     $KAME: rrenum.h,v 1.3 2001/01/21 15:37:14 itojun Exp $  */
 
 /*
  * Copyright (C) 1998 WIDE Project.
  * All rights reserved.
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -15,7 +16,7 @@
  * 3. Neither the name of the project nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
- * 
+ *
  * 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
@@ -27,9 +28,6 @@
  * 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: src/usr.sbin/rtadvd/rrenum.h,v 1.1.2.2 2001/07/03 11:02:14 ume Exp $
- * $DragonFly: src/usr.sbin/rtadvd/rrenum.h,v 1.3 2003/11/03 19:31:42 eirikn Exp $
  */
 
 void rr_input(int, struct icmp6_router_renum *, struct in6_pktinfo *,
index ffdb361..6e36ad6 100644 (file)
@@ -1,6 +1,4 @@
-.\"    $FreeBSD: src/usr.sbin/rtadvd/rtadvd.8,v 1.3.2.8 2003/03/11 22:31:32 trhodes Exp $
-.\"    $DragonFly: src/usr.sbin/rtadvd/rtadvd.8,v 1.4 2007/11/23 23:16:37 swildner Exp $
-.\"    $KAME: rtadvd.8,v 1.17 2001/02/04 05:34:38 jinmei Exp $
+.\"    $KAME: rtadvd.8,v 1.24 2002/05/31 16:16:08 jinmei Exp $
 .\"
 .\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 .\" All rights reserved.
@@ -29,7 +27,9 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd May 17, 1998
+.\" $FreeBSD: stable/10/usr.sbin/rtadvd/rtadvd.8 247270 2013-02-25 16:47:09Z des $
+.\"
+.Dd February 25, 2013
 .Dt RTADVD 8
 .Os
 .Sh NAME
 .Nd router advertisement daemon
 .Sh SYNOPSIS
 .Nm
-.Op Fl dDfMRs
+.Op Fl dDfRs
 .Op Fl c Ar configfile
-.Ar interface ...
+.Op Fl C Ar ctlsock
+.Op Fl M Ar ifname
+.Op Fl p Ar pidfile
+.Op Ar interface ...
 .Sh DESCRIPTION
-The
 .Nm
-utility sends router advertisement packets to the specified
+sends router advertisement packets to the specified
 .Ar interfaces .
+If no interfaces are specified,
+.Nm
+will still run, but will not advertise any routes until interfaces are
+added using
+.Xr rtadvctl 8 .
 .Pp
 The program will daemonize itself on invocation.
 It will then send router advertisement packets periodically, as well
@@ -63,9 +70,8 @@ In particular,
 reads all the interface routes from the routing table and advertises
 them as on-link prefixes.
 .Pp
-The
 .Nm
-utility also watches the routing table.
+also watches the routing table.
 If an interface direct route is
 added on an advertising interface and no static prefixes are
 specified by the configuration file,
@@ -103,7 +109,7 @@ will not watch the routing table and the whole functionality described
 above will be suppressed.
 .Pp
 Basically, hosts MUST NOT send Router Advertisement messages at any
-time (RFC 2461, Section 6.2.3).
+time (RFC 4861, Section 6.2.3).
 However, it would sometimes be useful to allow hosts to advertise some
 parameters such as prefix information and link MTU.
 Thus,
@@ -121,31 +127,34 @@ for the configuration file.
 By default,
 .Pa /etc/rtadvd.conf
 is used.
+.It Fl C
+Specify an alternate location for the control socket used by
+.Xr rtadvctl 8 .
+The default is
+.Pa /var/run/rtadvd.sock .
 .It Fl d
 Print debugging information.
 .It Fl D
 Even more debugging information is printed.
 .It Fl f
 Foreground mode (useful when debugging).
+Log messages will be dumped to stderr when this option is specified.
 .It Fl M
 Specify an interface to join the all-routers site-local multicast group.
 By default,
 .Nm
-tries to join the first advertising interface appeared in the command
+tries to join the first advertising interface appearing on the command
 line.
 This option has meaning only with the
 .Fl R
 option, which enables routing renumbering protocol support.
-.\".It Fl m
-.\"Enables mobile IPv6 support.
-.\"This changes the content of router advertisement option, as well as
-.\"permitted configuration directives.
+.It Fl p
+Specify an alternative file in which to store the process ID.
+The default is
+.Pa /var/run/rtadvd.pid .
 .It Fl R
 Accept router renumbering requests.
 If you enable it, certain IPsec setup is suggested for security reasons.
-On KAME-based systems,
-.Xr rrenumd 8
-generates router renumbering request packets.
 This option is currently disabled, and is ignored by
 .Nm
 with a warning message.
@@ -154,11 +163,17 @@ Do not add or delete prefixes dynamically.
 Only statically configured prefixes, if any, will be advertised.
 .El
 .Pp
-Upon receipt of signal
-.Dv SIGUSR1 ,
-.Nm
-will dump the current internal state into
-.Pa /var/run/rtadvd.dump .
+Use
+.Dv SIGHUP
+to reload the configuration file
+.Pa /etc/rtadvd.conf .
+If an invalid parameter is found in the configuration file upon the reload,
+the entry will be ignored and the old configuration will be used.
+When parameters in an existing entry are updated,
+.Nm
+will send Router Advertisement messages with the old configuration but
+zero router lifetime to the interface first, and then start to send a new
+message.
 .Pp
 Use
 .Dv SIGTERM
@@ -169,30 +184,53 @@ In this case,
 .Nm
 will transmit router advertisement with router lifetime 0
 to all the interfaces
-(in accordance with RFC 2461 6.2.5).
+.Pq in accordance with RFC 4861 6.2.5 .
 .Sh FILES
 .Bl -tag -width Pa -compact
 .It Pa /etc/rtadvd.conf
 The default configuration file.
 .It Pa /var/run/rtadvd.pid
-contains the pid of the currently running
-.Nm .
-.It Pa /var/run/rtadvd.dump
-in which
-.Nm
-dumps its internal state.
+The default process ID file.
 .El
 .Sh EXIT STATUS
 .Ex -std
 .Sh SEE ALSO
 .Xr rtadvd.conf 5 ,
-.Xr rrenumd 8 ,
+.Xr rtadvctl 8 ,
 .Xr rtsol 8
+.Rs
+.%A Thomas Narten
+.%A Erik Nordmark
+.%A W. A. Simpson
+.%A Hesham Soliman
+.%T Neighbor Discovery for IP version 6 (IPv6)
+.%R RFC 4861
+.Re
+.Rs
+.%A Thomas Narten
+.%A Erik Nordmark
+.%A W. A. Simpson
+.%T Neighbor Discovery for IP version 6 (IPv6)
+.%R RFC 2461 (obsoleted by RFC 4861)
+.Re
+.Rs
+.%A Richard Draves
+.%T Default Router Preferences and More-Specific Routes
+.%R draft-ietf-ipngwg-router-selection-xx.txt
+.Re
+.Rs
+.%A J. Jeong
+.%A S. Park
+.%A L. Beloeil
+.%A S. Madanapalli
+.%T IPv6 Router Advertisement Options for DNS Configuration
+.%R RFC 6106
+.Re
 .Sh HISTORY
 The
 .Nm
-utility first appeared in WIDE Hydrangea IPv6 protocol stack kit.
-.Sh CAVEAT
+command first appeared in the WIDE Hydrangea IPv6 protocol stack kit.
+.Sh BUGS
 There used to be some text that recommended users not to let
 .Nm
 advertise Router Advertisement messages on an upstream link to avoid
index 15a7243..7c50697 100644 (file)
@@ -1,9 +1,11 @@
-/*     $KAME: rtadvd.c,v 1.50 2001/02/04 06:15:15 itojun Exp $ */
+/*     $FreeBSD: stable/10/usr.sbin/rtadvd/rtadvd.c 275038 2014-11-25 13:12:45Z dim $  */
+/*     $KAME: rtadvd.c,v 1.82 2003/08/05 12:34:23 itojun Exp $ */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * 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:
@@ -15,7 +17,7 @@
  * 3. Neither the name of the project nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
- * 
+ *
  * 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
  * 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: src/usr.sbin/rtadvd/rtadvd.c,v 1.3.2.4 2003/04/05 10:31:58 ume Exp $
  */
 
 #include <sys/param.h>
+#include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/uio.h>
-#include <sys/time.h>
 #include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
 
 #include <net/if.h>
-#include <net/route.h>
+#include <net/if_types.h>
+#include <net/if_media.h>
 #include <net/if_dl.h>
+#include <net/route.h>
 #include <netinet/in.h>
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
 
 #include <arpa/inet.h>
 
+#include <net/if_var.h>
+#include <netinet/in_var.h>
+#include <netinet6/nd6.h>
+
 #include <time.h>
 #include <unistd.h>
 #include <stdio.h>
-#include <stdlib.h>
 #include <err.h>
 #include <errno.h>
+#include <inttypes.h>
+#include <libutil.h>
+#include <netdb.h>
+#include <signal.h>
 #include <string.h>
+#include <stdlib.h>
 #include <syslog.h>
+#include <poll.h>
+
+#include "pathnames.h"
 #include "rtadvd.h"
+#include "if.h"
 #include "rrenum.h"
 #include "advcap.h"
+#include "timer_subr.h"
 #include "timer.h"
-#include "if.h"
 #include "config.h"
-#include "dump.h"
+#include "control.h"
+#include "control_server.h"
+
+#define RTADV_TYPE2BITMASK(type) (0x1 << type)
 
 struct msghdr rcvmhdr;
-static u_char *rcvcmsgbuf;
+static char *rcvcmsgbuf;
 static size_t rcvcmsgbuflen;
-static u_char *sndcmsgbuf = NULL;
+static char *sndcmsgbuf = NULL;
 static size_t sndcmsgbuflen;
-static int do_dump;
-static int do_die;
 struct msghdr sndmhdr;
 struct iovec rcviov[2];
 struct iovec sndiov[2];
-struct sockaddr_in6 from;
-struct sockaddr_in6 sin6_allnodes = {sizeof(sin6_allnodes), AF_INET6};
-struct in6_addr in6a_site_allrouters;
-static char *dumpfilename = "/var/run/rtadvd.dump"; /* XXX: should be configurable */
-static char *pidfilename = "/var/run/rtadvd.pid"; /* should be configurable */
-static char *mcastif;
-int sock;
-int rtsock = -1;
-#ifdef MIP6
-int mobileip6 = 0;
-#endif
-int accept_rr = 0;
-int dflag = 0, sflag = 0;
-
-u_char *conffile = NULL;
+struct sockaddr_in6 rcvfrom;
+static const char *pidfilename = _PATH_RTADVDPID;
+const char *conffile = _PATH_RTADVDCONF;
+static struct pidfh *pfh;
+static int dflag, sflag;
+static int wait_shutdown;
+
+#define        PFD_RAWSOCK     0
+#define        PFD_RTSOCK      1
+#define        PFD_CSOCK       2
+#define        PFD_MAX         3
+
+struct railist_head_t railist =
+    TAILQ_HEAD_INITIALIZER(railist);
+struct ifilist_head_t ifilist =
+    TAILQ_HEAD_INITIALIZER(ifilist);
 
-struct rainfo *ralist = NULL;
 struct nd_optlist {
-       struct nd_optlist *next;
-       struct nd_opt_hdr *opt;
+       TAILQ_ENTRY(nd_optlist) nol_next;
+       struct nd_opt_hdr *nol_opt;
 };
-union nd_opts {
-       struct nd_opt_hdr *nd_opt_array[7];
+union nd_opt {
+       struct nd_opt_hdr *opt_array[9];
        struct {
                struct nd_opt_hdr *zero;
                struct nd_opt_hdr *src_lladdr;
@@ -103,75 +120,84 @@ union nd_opts {
                struct nd_opt_prefix_info *pi;
                struct nd_opt_rd_hdr *rh;
                struct nd_opt_mtu *mtu;
-               struct nd_optlist *list;
+               TAILQ_HEAD(, nd_optlist) opt_list;
        } nd_opt_each;
 };
-#define nd_opts_src_lladdr     nd_opt_each.src_lladdr
-#define nd_opts_tgt_lladdr     nd_opt_each.tgt_lladdr
-#define nd_opts_pi             nd_opt_each.pi
-#define nd_opts_rh             nd_opt_each.rh
-#define nd_opts_mtu            nd_opt_each.mtu
-#define nd_opts_list           nd_opt_each.list
-
-#define NDOPT_FLAG_SRCLINKADDR 0x1
-#define NDOPT_FLAG_TGTLINKADDR 0x2
-#define NDOPT_FLAG_PREFIXINFO 0x4
-#define NDOPT_FLAG_RDHDR 0x8
-#define NDOPT_FLAG_MTU 0x10
-
-u_int32_t ndopt_flags[] = {
-       0, NDOPT_FLAG_SRCLINKADDR, NDOPT_FLAG_TGTLINKADDR,
-       NDOPT_FLAG_PREFIXINFO, NDOPT_FLAG_RDHDR, NDOPT_FLAG_MTU
+#define opt_src_lladdr nd_opt_each.src_lladdr
+#define opt_tgt_lladdr nd_opt_each.tgt_lladdr
+#define opt_pi         nd_opt_each.pi
+#define opt_rh         nd_opt_each.rh
+#define opt_mtu                nd_opt_each.mtu
+#define opt_list       nd_opt_each.opt_list
+
+#define NDOPT_FLAG_SRCLINKADDR (1 << 0)
+#define NDOPT_FLAG_TGTLINKADDR (1 << 1)
+#define NDOPT_FLAG_PREFIXINFO  (1 << 2)
+#define NDOPT_FLAG_RDHDR       (1 << 3)
+#define NDOPT_FLAG_MTU         (1 << 4)
+#define NDOPT_FLAG_RDNSS       (1 << 5)
+#define NDOPT_FLAG_DNSSL       (1 << 6)
+
+static uint32_t ndopt_flags[] = {
+       [ND_OPT_SOURCE_LINKADDR]        = NDOPT_FLAG_SRCLINKADDR,
+       [ND_OPT_TARGET_LINKADDR]        = NDOPT_FLAG_TGTLINKADDR,
+       [ND_OPT_PREFIX_INFORMATION]     = NDOPT_FLAG_PREFIXINFO,
+       [ND_OPT_REDIRECTED_HEADER]      = NDOPT_FLAG_RDHDR,
+       [ND_OPT_MTU]                    = NDOPT_FLAG_MTU,
+       [ND_OPT_RDNSS]                  = NDOPT_FLAG_RDNSS,
+       [ND_OPT_DNSSL]                  = NDOPT_FLAG_DNSSL,
 };
 
-static void set_die(int);
-static void die(void);
-static void sock_open(void);
-static void rtsock_open(void);
-static void rtadvd_input(void);
-static void rs_input(int, struct nd_router_solicit *,
-                         struct in6_pktinfo *, struct sockaddr_in6 *);
-static void ra_input(int, struct nd_router_advert *,
-                         struct in6_pktinfo *, struct sockaddr_in6 *);
-static int prefix_check(struct nd_opt_prefix_info *, struct rainfo *,
-                            struct sockaddr_in6 *);
-static int nd6_options(struct nd_opt_hdr *, int,
-                           union nd_opts *, u_int32_t);
-static void free_ndopts(union nd_opts *);
-static void ra_output(struct rainfo *);
-static void rtmsg_input(void);
-static void rtadvd_set_dump_file(void);
+static void    rtadvd_shutdown(void);
+static void    sock_open(struct sockinfo *);
+static void    rtsock_open(struct sockinfo *);
+static void    rtadvd_input(struct sockinfo *);
+static void    rs_input(int, struct nd_router_solicit *,
+                   struct in6_pktinfo *, struct sockaddr_in6 *);
+static void    ra_input(int, struct nd_router_advert *,
+                   struct in6_pktinfo *, struct sockaddr_in6 *);
+static int     prefix_check(struct nd_opt_prefix_info *, struct rainfo *,
+                   struct sockaddr_in6 *);
+static int     nd6_options(struct nd_opt_hdr *, int,
+                   union nd_opt *, uint32_t);
+static void    free_ndopts(union nd_opt *);
+static void    rtmsg_input(struct sockinfo *);
+static void    set_short_delay(struct ifinfo *);
+static int     check_accept_rtadv(int);
+
+static void
+usage(void)
+{
+
+       fprintf(stderr, "usage: rtadvd [-dDfRs] "
+           "[-c configfile] [-C ctlsock] [-M ifname] [-p pidfile]\n");
+       exit(1);
+}
 
 int
 main(int argc, char *argv[])
 {
-       fd_set fdset;
-       int maxfd = 0;
-       struct timeval *timeout;
+       struct pollfd set[PFD_MAX];
+       struct timespec *timeout;
        int i, ch;
-       int fflag = 0;
-       FILE *pidfp;
-       pid_t pid;
-
-       openlog("rtadvd", LOG_NDELAY|LOG_PID, LOG_DAEMON);
+       int fflag = 0, logopt;
+       int error;
+       pid_t pid, otherpid;
 
        /* get command line options and arguments */
-#ifdef MIP6
-#define OPTIONS "c:dDfM:mRs"
-#else
-#define OPTIONS "c:dDfM:Rs"
-#endif
-       while ((ch = getopt(argc, argv, OPTIONS)) != -1) {
-#undef OPTIONS
+       while ((ch = getopt(argc, argv, "c:C:dDfhM:p:Rs")) != -1) {
                switch (ch) {
                case 'c':
                        conffile = optarg;
                        break;
+               case 'C':
+                       ctrlsock.si_name = optarg;
+                       break;
                case 'd':
-                       dflag = 1;
+                       dflag++;
                        break;
                case 'D':
-                       dflag = 2;
+                       dflag += 3;
                        break;
                case 'f':
                        fflag = 1;
@@ -179,11 +205,6 @@ main(int argc, char *argv[])
                case 'M':
                        mcastif = optarg;
                        break;
-#ifdef MIP6
-               case 'm':
-                       mobileip6 = 1;
-                       break;
-#endif
                case 'R':
                        fprintf(stderr, "rtadvd: "
                                "the -R option is currently ignored.\n");
@@ -193,85 +214,113 @@ main(int argc, char *argv[])
                case 's':
                        sflag = 1;
                        break;
+               case 'p':
+                       pidfilename = optarg;
+                       break;
+               default:
+                       usage();
                }
        }
        argc -= optind;
        argv += optind;
-       if (argc == 0) {
-               fprintf(stderr,
-#ifdef MIP6
-                       "usage: rtadvd [-dDfMmRs] [-c conffile] "
-#else
-                       "usage: rtadvd [-dDfMRs] [-c conffile] "
-#endif
-                       "interfaces...\n");
-               exit(1);
-       }
+
+       logopt = LOG_NDELAY | LOG_PID;
+       if (fflag)
+               logopt |= LOG_PERROR;
+       openlog("rtadvd", logopt, LOG_DAEMON);
 
        /* set log level */
-       if (dflag == 0)
-               setlogmask(LOG_UPTO(LOG_ERR));
-       if (dflag == 1)
-               setlogmask(LOG_UPTO(LOG_INFO));
+       if (dflag > 2)
+               (void)setlogmask(LOG_UPTO(LOG_DEBUG));
+       else if (dflag > 1)
+               (void)setlogmask(LOG_UPTO(LOG_INFO));
+       else if (dflag > 0)
+               (void)setlogmask(LOG_UPTO(LOG_NOTICE));
+       else
+               (void)setlogmask(LOG_UPTO(LOG_ERR));
 
        /* timer initialization */
        rtadvd_timer_init();
 
+#ifndef HAVE_ARC4RANDOM
        /* random value initialization */
-       srandom((u_long)time(NULL));
+#ifdef __FreeBSD__
+       srandomdev();
+#else
+       srandom((unsigned long)time(NULL));
+#endif
+#endif
+       pfh = pidfile_open(pidfilename, 0600, &otherpid);
+       if (pfh == NULL) {
+               if (errno == EEXIST)
+                       errx(1, "%s already running, pid: %d",
+                           getprogname(), otherpid);
+               syslog(LOG_ERR,
+                   "failed to open the pid file %s, run anyway.",
+                   pidfilename);
+       }
+       if (!fflag)
+               daemon(1, 0);
 
-       /* get iflist block from kernel */
-       init_iflist();
+       sock_open(&sock);
 
-       while (argc--)
-               getconfig(*argv++);
+       update_ifinfo(&ifilist, UPDATE_IFINFO_ALL);
+       for (i = 0; i < argc; i++)
+               update_persist_ifinfo(&ifilist, argv[i]);
 
-       if (inet_pton(AF_INET6, ALLNODES, &sin6_allnodes.sin6_addr) != 1) {
-               fprintf(stderr, "fatal: inet_pton failed\n");
+       csock_open(&ctrlsock, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+       if (ctrlsock.si_fd == -1) {
+               syslog(LOG_ERR, "cannot open control socket: %s",
+                   strerror(errno));
                exit(1);
        }
-       sock_open();
-
-       if (!fflag)
-               daemon(1, 0);
 
        /* record the current PID */
        pid = getpid();
-       if ((pidfp = fopen(pidfilename, "w")) == NULL) {
-               syslog(LOG_ERR,
-                   "<%s> failed to open a log file(%s), run anyway.",
-                   __func__, pidfilename);
-       } else {
-               fprintf(pidfp, "%d\n", pid);
-               fclose(pidfp);
-       }
+       pidfile_write(pfh);
 
-       FD_ZERO(&fdset);
-       FD_SET(sock, &fdset);
-       maxfd = sock;
+       set[PFD_RAWSOCK].fd = sock.si_fd;
+       set[PFD_RAWSOCK].events = POLLIN;
        if (sflag == 0) {
-               rtsock_open();
-               FD_SET(rtsock, &fdset);
-               if (rtsock > sock)
-                       maxfd = rtsock;
+               rtsock_open(&rtsock);
+               set[PFD_RTSOCK].fd = rtsock.si_fd;
+               set[PFD_RTSOCK].events = POLLIN;
        } else
-               rtsock = -1;
+               set[PFD_RTSOCK].fd = -1;
+       set[PFD_CSOCK].fd = ctrlsock.si_fd;
+       set[PFD_CSOCK].events = POLLIN;
+       signal(SIGTERM, set_do_shutdown);
+       signal(SIGINT, set_do_shutdown);
+       signal(SIGHUP, set_do_reload);
+
+       error = csock_listen(&ctrlsock);
+       if (error) {
+               syslog(LOG_ERR, "cannot listen control socket: %s",
+                   strerror(errno));
+               exit(1);
+       }
 
-       signal(SIGTERM, (void *)set_die);
-       signal(SIGUSR1, (void *)rtadvd_set_dump_file);
+       /* load configuration file */
+       set_do_reload(0);
 
        while (1) {
-               struct fd_set select_fd = fdset; /* reinitialize */
+               if (is_do_shutdown())
+                       rtadvd_shutdown();
 
-               if (do_dump) {  /* SIGUSR1 */
-                       do_dump = 0;
-                       rtadvd_dump_file(dumpfilename);
+               if (is_do_reload()) {
+                       loadconfig_ifname(reload_ifname());
+                       if (reload_ifname() == NULL)
+                               syslog(LOG_INFO,
+                                   "configuration file reloaded.");
+                       else
+                               syslog(LOG_INFO,
+                                   "configuration file for %s reloaded.",
+                                   reload_ifname());
+                       reset_do_reload();
                }
 
-               if (do_die) {
-                       die();
-                       /*NOTREACHED*/
-               }
+               /* timeout handler update for active interfaces */
+               rtadvd_update_timeout_handler();
 
                /* timer expiration check and reset the timer */
                timeout = rtadvd_check_timer();
@@ -281,99 +330,176 @@ main(int argc, char *argv[])
                            "<%s> set timer to %ld:%ld. waiting for "
                            "inputs or timeout", __func__,
                            (long int)timeout->tv_sec,
-                           (long int)timeout->tv_usec);
+                           (long int)timeout->tv_nsec / 1000);
                } else {
                        syslog(LOG_DEBUG,
                            "<%s> there's no timer. waiting for inputs",
                            __func__);
                }
+               if ((i = poll(set, sizeof(set)/sizeof(set[0]),
+                           timeout ? (timeout->tv_sec * 1000 +
+                               timeout->tv_nsec / 1000 / 1000) : INFTIM)) < 0) {
 
-               if ((i = select(maxfd + 1, &select_fd,
-                               NULL, NULL, timeout)) < 0) {
-                       /* EINTR would occur upon SIGUSR1 for status dump */
+                       /* EINTR would occur if a signal was delivered */
                        if (errno != EINTR)
-                               syslog(LOG_ERR, "<%s> select: %s",
-                                   __func__, strerror(errno));
+                               syslog(LOG_ERR, "poll() failed: %s",
+                                   strerror(errno));
                        continue;
                }
                if (i == 0)     /* timeout */
                        continue;
-               if (rtsock != -1 && FD_ISSET(rtsock, &select_fd))
-                       rtmsg_input();
-               if (FD_ISSET(sock, &select_fd))
-                       rtadvd_input();
+               if (rtsock.si_fd != -1 && set[PFD_RTSOCK].revents & POLLIN)
+                       rtmsg_input(&rtsock);
+
+               if (set[PFD_RAWSOCK].revents & POLLIN)
+                       rtadvd_input(&sock);
+
+               if (set[PFD_CSOCK].revents & POLLIN) {
+                       int fd;
+
+                       fd = csock_accept(&ctrlsock);
+                       if (fd == -1)
+                               syslog(LOG_ERR,
+                                   "cannot accept() control socket: %s",
+                                   strerror(errno));
+                       else {
+                               cm_handler_server(fd);
+                               close(fd);
+                       }
+               }
        }
        exit(0);                /* NOTREACHED */
 }
 
 static void
-rtadvd_set_dump_file(void)
+rtadvd_shutdown(void)
 {
-       do_dump = 1;
-}
-
-static void
-set_die(int sig)
-{
-       do_die = 1;
-}
+       struct ifinfo *ifi;
+       struct rainfo *rai;
+       struct rdnss *rdn;
+       struct dnssl *dns;
 
-static void
-die(void)
-{
-       struct rainfo *ra;
-       int i;
-       const int retrans = MAX_FINAL_RTR_ADVERTISEMENTS;
+       if (wait_shutdown) {
+               syslog(LOG_INFO,
+                   "waiting expiration of the all RA timers.");
+
+               TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+                       /*
+                        * Ignore !IFF_UP interfaces in waiting for shutdown.
+                        */
+                       if (!(ifi->ifi_flags & IFF_UP) &&
+                           ifi->ifi_ra_timer != NULL) {
+                               ifi->ifi_state = IFI_STATE_UNCONFIGURED;
+                               rtadvd_remove_timer(ifi->ifi_ra_timer);
+                               ifi->ifi_ra_timer = NULL;
+                               syslog(LOG_DEBUG, "<%s> %s(idx=%d) is down. "
+                                   "Timer removed and marked as UNCONFIGURED.",
+                                    __func__, ifi->ifi_ifname,
+                                   ifi->ifi_ifindex);
+                       }
+               }
+               TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+                       if (ifi->ifi_ra_timer != NULL)
+                               break;
+               }
+               if (ifi == NULL) {
+                       syslog(LOG_NOTICE, "gracefully terminated.");
+                       exit(0);
+               }
 
-       if (dflag > 1) {
-               syslog(LOG_DEBUG, "<%s> cease to be an advertising router\n",
-                   __func__);
+               sleep(1);
+               return;
        }
 
-       for (ra = ralist; ra; ra = ra->next) {
-               ra->lifetime = 0;
-               make_packet(ra);
+       syslog(LOG_DEBUG, "<%s> cease to be an advertising router",
+           __func__);
+
+       wait_shutdown = 1;
+
+       TAILQ_FOREACH(rai, &railist, rai_next) {
+               rai->rai_lifetime = 0;
+               TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next)
+                       rdn->rd_ltime = 0;
+               TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next)
+                       dns->dn_ltime = 0;
        }
-       for (i = 0; i < retrans; i++) {
-               for (ra = ralist; ra; ra = ra->next)
-                       ra_output(ra);
-               sleep(MIN_DELAY_BETWEEN_RAS);
+       TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+               if (!ifi->ifi_persist)
+                       continue;
+               if (ifi->ifi_state == IFI_STATE_UNCONFIGURED)
+                       continue;
+               if (ifi->ifi_ra_timer == NULL)
+                       continue;
+               if (ifi->ifi_ra_lastsent.tv_sec == 0 &&
+                   ifi->ifi_ra_lastsent.tv_nsec == 0 &&
+                   ifi->ifi_ra_timer != NULL) {
+                       /*
+                        * When RA configured but never sent,
+                        * ignore the IF immediately.
+                        */
+                       rtadvd_remove_timer(ifi->ifi_ra_timer);
+                       ifi->ifi_ra_timer = NULL;
+                       ifi->ifi_state = IFI_STATE_UNCONFIGURED;
+                       continue;
+               }
+
+               ifi->ifi_state = IFI_STATE_TRANSITIVE;
+
+               /* Mark as the shut-down state. */
+               ifi->ifi_rainfo_trans = ifi->ifi_rainfo;
+               ifi->ifi_rainfo = NULL;
+