summaryrefslogtreecommitdiffstats
path: root/usr.sbin/rtadvd
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/rtadvd')
-rw-r--r--usr.sbin/rtadvd/Makefile26
-rw-r--r--usr.sbin/rtadvd/Makefile.depend20
-rw-r--r--usr.sbin/rtadvd/advcap.c436
-rw-r--r--usr.sbin/rtadvd/advcap.h46
-rw-r--r--usr.sbin/rtadvd/config.c1540
-rw-r--r--usr.sbin/rtadvd/config.h53
-rw-r--r--usr.sbin/rtadvd/control.c492
-rw-r--r--usr.sbin/rtadvd/control.h74
-rw-r--r--usr.sbin/rtadvd/control_client.c131
-rw-r--r--usr.sbin/rtadvd/control_client.h30
-rw-r--r--usr.sbin/rtadvd/control_server.c752
-rw-r--r--usr.sbin/rtadvd/control_server.h42
-rw-r--r--usr.sbin/rtadvd/if.c748
-rw-r--r--usr.sbin/rtadvd/if.h61
-rw-r--r--usr.sbin/rtadvd/pathnames.h6
-rw-r--r--usr.sbin/rtadvd/rrenum.c502
-rw-r--r--usr.sbin/rtadvd/rrenum.h34
-rw-r--r--usr.sbin/rtadvd/rtadvd.8242
-rw-r--r--usr.sbin/rtadvd/rtadvd.c1914
-rw-r--r--usr.sbin/rtadvd/rtadvd.conf22
-rw-r--r--usr.sbin/rtadvd/rtadvd.conf.5530
-rw-r--r--usr.sbin/rtadvd/rtadvd.h298
-rw-r--r--usr.sbin/rtadvd/timer.c199
-rw-r--r--usr.sbin/rtadvd/timer.h52
-rw-r--r--usr.sbin/rtadvd/timer_subr.c91
-rw-r--r--usr.sbin/rtadvd/timer_subr.h59
26 files changed, 8400 insertions, 0 deletions
diff --git a/usr.sbin/rtadvd/Makefile b/usr.sbin/rtadvd/Makefile
new file mode 100644
index 0000000..5d627e5
--- /dev/null
+++ b/usr.sbin/rtadvd/Makefile
@@ -0,0 +1,26 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+#
+# $FreeBSD$
+
+PROG= rtadvd
+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
+
+LIBADD= util
+
+WARNS?= 1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rtadvd/Makefile.depend b/usr.sbin/rtadvd/Makefile.depend
new file mode 100644
index 0000000..7de116d
--- /dev/null
+++ b/usr.sbin/rtadvd/Makefile.depend
@@ -0,0 +1,20 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ gnu/lib/libgcc \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.sbin/rtadvd/advcap.c b/usr.sbin/rtadvd/advcap.c
new file mode 100644
index 0000000..542e066
--- /dev/null
+++ b/usr.sbin/rtadvd/advcap.c
@@ -0,0 +1,436 @@
+/* $FreeBSD$ */
+/* $KAME: advcap.c,v 1.11 2003/05/19 09:46:50 keiichi Exp $ */
+
+/*
+ * Copyright (c) 1983 The Regents of the University of California.
+ * 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.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ */
+
+/*
+ * remcap - routines for dealing with the remote host data base
+ *
+ * derived from termcap
+ */
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include "pathnames.h"
+
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+#define MAXHOP 32 /* max number of tc= indirections */
+
+#define tgetent agetent
+#define tnchktc anchktc
+#define tnamatch anamatch
+#define tgetnum agetnum
+#define tgetflag agetflag
+#define tgetstr agetstr
+
+#if 0
+#define V_TERMCAP "REMOTE"
+#define V_TERM "HOST"
+#endif
+
+/*
+ * termcap - routines for dealing with the terminal capability data base
+ *
+ * BUG: Should use a "last" pointer in tbuf, so that searching
+ * for capabilities alphabetically would not be a n**2/2
+ * process when large numbers of capabilities are given.
+ * Note: If we add a last pointer now we will screw up the
+ * tc capability. We really should compile termcap.
+ *
+ * Essentially all the work here is scanning and decoding escapes
+ * in string capabilities. We don't use stdio because the editor
+ * doesn't, and because living w/o it is not hard.
+ */
+
+static char *tbuf;
+static int hopcount; /* detect infinite loops in termcap, init 0 */
+
+extern const char *conffile;
+
+int tgetent(char *, char *);
+int getent(char *, char *, const char *);
+int tnchktc(void);
+int tnamatch(char *);
+static char *tskip(char *);
+int64_t tgetnum(char *);
+int tgetflag(char *);
+char *tgetstr(char *, char **);
+static char *tdecode(char *, char **);
+
+/*
+ * Get an entry for terminal name in buffer bp,
+ * from the termcap file. Parse is very rudimentary;
+ * we just notice escaped newlines.
+ */
+int
+tgetent(char *bp, char *name)
+{
+ return (getent(bp, name, conffile));
+}
+
+int
+getent(char *bp, char *name, const char *cfile)
+{
+ int c;
+ int i = 0, cnt = 0;
+ char ibuf[BUFSIZ];
+ char *cp;
+ int tf;
+
+ tbuf = bp;
+ tf = 0;
+ /*
+ * TERMCAP can have one of two things in it. It can be the
+ * name of a file to use instead of /etc/termcap. In this
+ * case it better start with a "/". Or it can be an entry to
+ * use so we don't have to read the file. In this case it
+ * has to already have the newlines crunched out.
+ */
+ if (cfile && *cfile)
+ tf = open(cfile, O_RDONLY);
+
+ if (tf < 0) {
+ syslog(LOG_INFO,
+ "<%s> open: %s", __func__, strerror(errno));
+ return (-2);
+ }
+ for (;;) {
+ cp = bp;
+ for (;;) {
+ if (i == cnt) {
+ cnt = read(tf, ibuf, BUFSIZ);
+ if (cnt <= 0) {
+ close(tf);
+ return (0);
+ }
+ i = 0;
+ }
+ c = ibuf[i++];
+ if (c == '\n') {
+ if (cp > bp && cp[-1] == '\\') {
+ cp--;
+ continue;
+ }
+ break;
+ }
+ if (cp >= bp + BUFSIZ - 1) {
+ write(STDERR_FILENO, "Remcap entry too long\n",
+ 22);
+ break;
+ } else
+ *cp++ = c;
+ }
+ *cp = 0;
+
+ /*
+ * The real work for the match.
+ */
+ if (tnamatch(name)) {
+ close(tf);
+ return (tnchktc());
+ }
+ }
+}
+
+/*
+ * tnchktc: check the last entry, see if it's tc=xxx. If so,
+ * recursively find xxx and append that entry (minus the names)
+ * to take the place of the tc=xxx entry. This allows termcap
+ * entries to say "like an HP2621 but doesn't turn on the labels".
+ * Note that this works because of the left to right scan.
+ */
+int
+tnchktc(void)
+{
+ char *p, *q;
+ char tcname[16]; /* name of similar terminal */
+ char tcbuf[BUFSIZ];
+ char *holdtbuf = tbuf;
+ int l;
+
+ p = tbuf + strlen(tbuf) - 2; /* before the last colon */
+ while (*--p != ':')
+ 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);
+ strlcpy(tcname, p + 3, sizeof tcname);
+ q = tcname;
+ while (*q && *q != ':')
+ q++;
+ *q = 0;
+ if (++hopcount > MAXHOP) {
+ write(STDERR_FILENO, "Infinite tc= loop\n", 18);
+ return (0);
+ }
+ if (getent(tcbuf, tcname, conffile) != 1) {
+ return (0);
+ }
+ for (q = tcbuf; *q++ != ':'; )
+ ;
+ l = p - holdtbuf + strlen(q);
+ if (l > BUFSIZ) {
+ write(STDERR_FILENO, "Remcap entry too long\n", 23);
+ q[BUFSIZ - (p-holdtbuf)] = 0;
+ }
+ strcpy(p, q);
+ tbuf = holdtbuf;
+ return (1);
+}
+
+/*
+ * Tnamatch deals with name matching. The first field of the termcap
+ * entry is a sequence of names separated by |'s, so we compare
+ * against each such name. The normal : terminator after the last
+ * name (before the first field) stops us.
+ */
+int
+tnamatch(char *np)
+{
+ char *Np, *Bp;
+
+ Bp = tbuf;
+ if (*Bp == '#')
+ return (0);
+ for (;;) {
+ for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
+ continue;
+ if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
+ return (1);
+ while (*Bp && *Bp != ':' && *Bp != '|')
+ Bp++;
+ if (*Bp == 0 || *Bp == ':')
+ return (0);
+ Bp++;
+ }
+}
+
+/*
+ * Skip to the next field. Notice that this is very dumb, not
+ * knowing about \: escapes or any such. If necessary, :'s can be put
+ * into the termcap file in octal.
+ */
+static char *
+tskip(char *bp)
+{
+ int dquote;
+
+ dquote = 0;
+ while (*bp) {
+ switch (*bp) {
+ case ':':
+ if (!dquote)
+ goto breakbreak;
+ else
+ bp++;
+ break;
+ case '\\':
+ bp++;
+ if (isdigit(*bp)) {
+ while (isdigit(*bp++))
+ ;
+ } else
+ bp++;
+ case '"':
+ dquote = (dquote ? 1 : 0);
+ bp++;
+ break;
+ default:
+ bp++;
+ break;
+ }
+ }
+breakbreak:
+ if (*bp == ':')
+ bp++;
+ return (bp);
+}
+
+/*
+ * Return the (numeric) option id.
+ * Numeric options look like
+ * li#80
+ * i.e. the option string is separated from the numeric value by
+ * a # character. If the option is not found we return -1.
+ * Note that we handle octal numbers beginning with 0.
+ */
+int64_t
+tgetnum(char *id)
+{
+ int64_t i;
+ int base;
+ char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (*bp == 0)
+ return (-1);
+ if (strncmp(bp, id, strlen(id)) != 0)
+ continue;
+ bp += strlen(id);
+ if (*bp == '@')
+ return (-1);
+ if (*bp != '#')
+ continue;
+ bp++;
+ base = 10;
+ if (*bp == '0')
+ base = 8;
+ i = 0;
+ while (isdigit(*bp))
+ i *= base, i += *bp++ - '0';
+ return (i);
+ }
+}
+
+/*
+ * Handle a flag option.
+ * Flag options are given "naked", i.e. followed by a : or the end
+ * of the buffer. Return 1 if we find the option, or 0 if it is
+ * not given.
+ */
+int
+tgetflag(char *id)
+{
+ char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (strncmp(bp, id, strlen(id)) == 0) {
+ bp += strlen(id);
+ if (!*bp || *bp == ':')
+ return (1);
+ else if (*bp == '@')
+ return (0);
+ }
+ }
+}
+
+/*
+ * Get a string valued option.
+ * These are given as
+ * cl=^Z
+ * Much decoding is done on the strings, and the strings are
+ * placed in area, which is a ref parameter which is updated.
+ * No checking on area overflow.
+ */
+char *
+tgetstr(char *id, char **area)
+{
+ char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (strncmp(bp, id, strlen(id)) != 0)
+ continue;
+ bp += strlen(id);
+ if (*bp == '@')
+ return (0);
+ if (*bp != '=')
+ continue;
+ bp++;
+ return (tdecode(bp, area));
+ }
+}
+
+/*
+ * Tdecode does the grung work to decode the
+ * string capability escapes.
+ */
+static char *
+tdecode(char *str, char **area)
+{
+ char *cp;
+ int c;
+ const char *dp;
+ int i;
+ char term;
+
+ term = ':';
+ cp = *area;
+again:
+ if (*str == '"') {
+ term = '"';
+ str++;
+ }
+ while ((c = *str++) && c != term) {
+ switch (c) {
+
+ case '^':
+ c = *str++ & 037;
+ break;
+
+ case '\\':
+ dp = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\"";
+ c = *str++;
+nextc:
+ if (*dp++ == c) {
+ c = *dp++;
+ break;
+ }
+ dp++;
+ if (*dp)
+ goto nextc;
+ if (isdigit(c)) {
+ c -= '0', i = 2;
+ do
+ c <<= 3, c |= *str++ - '0';
+ while (--i && isdigit(*str));
+ }
+ break;
+ }
+ *cp++ = c;
+ }
+ if (c == term && term != ':') {
+ term = ':';
+ goto again;
+ }
+ *cp++ = 0;
+ str = *area;
+ *area = cp;
+ return (str);
+}
diff --git a/usr.sbin/rtadvd/advcap.h b/usr.sbin/rtadvd/advcap.h
new file mode 100644
index 0000000..3cc124a
--- /dev/null
+++ b/usr.sbin/rtadvd/advcap.h
@@ -0,0 +1,46 @@
+/* $FreeBSD$ */
+/* $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.
+ * 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 AUTHOR ``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 AUTHOR 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.
+ */
+
+/* Based on Id: termcap.h,v 1.8 1996/09/10 12:42:10 peter Exp */
+
+#ifndef _ADVCAP_H_
+#define _ADVCAP_H_
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+extern int agetent(char *, const char *);
+extern int agetflag(const char *);
+extern int64_t agetnum(const char *);
+extern char *agetstr(const char *, char **);
+
+__END_DECLS
+
+#endif /* _ADVCAP_H_ */
diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c
new file mode 100644
index 0000000..d63af5a
--- /dev/null
+++ b/usr.sbin/rtadvd/config.c
@@ -0,0 +1,1540 @@
+/* $FreeBSD$ */
+/* $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:
+ * 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.
+ * 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
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#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 <search.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+
+#include "rtadvd.h"
+#include "advcap.h"
+#include "timer.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. */
+
+static struct rtadvd_timer *prefix_timeout(void *);
+static void makeentry(char *, size_t, int, const char *);
+static ssize_t dname_labelenc(char *, const char *);
+
+/* Encode domain name label encoding in RFC 1035 Section 3.1 */
+static ssize_t
+dname_labelenc(char *dst, const char *src)
+{
+ char *dst_origin;
+ char *p;
+ size_t len;
+
+ dst_origin = dst;
+ len = strlen(src);
+
+ if (len + len / 64 + 1 + 1 > DNAME_LABELENC_MAXLEN)
+ return (-1);
+ /* 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);
+ if (dst + 1 + len < dst_origin + DNAME_LABELENC_MAXLEN)
+ dst++;
+ else
+ return (-1);
+ /* 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) \
+ do { \
+ int64_t t; \
+ if ((t = agetnum(cap)) < 0) { \
+ fprintf(stderr, "rtadvd: need %s for interface %s\n", \
+ cap, intface); \
+ exit(1); \
+ } \
+ var = t; \
+ } while (0)
+
+#define MAYHAVE(var, cap, def) \
+ do { \
+ if ((var = agetnum(cap)) < 0) \
+ var = def; \
+ } while (0)
+
+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);
+}
+
+int
+rm_ifinfo_index(int idx)
+{
+ struct ifinfo *ifi;
+
+ 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"))
+ rai->rai_advlinkopt = 0;
+ else
+ 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__, ifi->ifi_ifname);
+ goto getconfig_free_rai;
+ }
+ }
+
+ /*
+ * set router configuration variables.
+ */
+ MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
+ if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
+ syslog(LOG_ERR,
+ "<%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;
+ }
+ 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 (%" 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;
+ }
+ rai->rai_mininterval = (uint16_t)val;
+
+ MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
+ 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
+ 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", rai->rai_maxinterval * 3);
+ if ((uint16_t)val && ((uint16_t)val < rai->rai_maxinterval ||
+ (uint16_t)val > MAXROUTERLIFETIME)) {
+ syslog(LOG_ERR,
+ "<%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;
+ }
+ rai->rai_lifetime = val & 0xffff;
+
+ MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
+ if (val < 0 || val > MAXREACHABLETIME) {
+ syslog(LOG_ERR,
+ "<%s> reachable time (%" PRIu32 ") on %s is invalid "
+ "(must be no greater than %d)",
+ __func__, val, ifi->ifi_ifname, MAXREACHABLETIME);
+ goto getconfig_free_rai;
+ }
+ rai->rai_reachabletime = (uint32_t)val;
+
+ MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER);
+ if (val64 < 0 || val64 > 0xffffffff) {
+ syslog(LOG_ERR, "<%s> retrans time (%" PRIu64 ") on %s out of range",
+ __func__, val64, ifi->ifi_ifname);
+ goto getconfig_free_rai;
+ }
+ rai->rai_retranstimer = (uint32_t)val64;
+
+ if (agetnum("hapref") != -1 || agetnum("hatime") != -1) {
+ syslog(LOG_ERR,
+ "<%s> mobile-ip6 configuration not supported",
+ __func__);
+ goto getconfig_free_rai;
+ }
+ /* prefix information */
+
+ /*
+ * 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);
+ rai->rai_clockskew = val;
+
+ rai->rai_pfxs = 0;
+ for (i = -1; i < MAXPREFIX; i++) {
+ struct prefix *pfx;
+
+ 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> inet_pton failed for %s",
+ __func__, addr);
+ goto getconfig_free_pfx;
+ }
+ 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, "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, "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, "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(val64, "mtu", 0);
+ if (val < 0 || val64 > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> mtu (%" PRIu64 ") on %s out of range",
+ __func__, val64, ifi->ifi_ifname);
+ goto getconfig_free_rai;
+ }
+ rai->rai_linkmtu = (uint32_t)val64;
+ if (rai->rai_linkmtu == 0) {
+ char *mtustr;
+
+ if ((mtustr = (char *)agetstr("mtu", &bp)) &&
+ strcmp(mtustr, "auto") == 0)
+ rai->rai_linkmtu = ifi->ifi_phymtu;
+ }
+ else if (rai->rai_linkmtu < IPV6_MMTU ||
+ rai->rai_linkmtu > ifi->ifi_phymtu) {
+ syslog(LOG_ERR,
+ "<%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;
+ }
+
+#ifdef SIOCSIFINFO_IN6
+ {
+ struct in6_ndireq ndi;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+ 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));
+
+ close(s);
+ }
+#endif
+
+ /* route information */
+ rai->rai_routes = 0;
+ for (i = -1; i < MAXROUTE; i++) {
+ struct rtinfo *rti;
+
+ makeentry(entbuf, sizeof(entbuf), i, "rtprefix");
+ addr = (char *)agetstr(entbuf, &bp);
+ if (addr == NULL) {
+ makeentry(oentbuf, sizeof(oentbuf), i, "rtrprefix");
+ addr = (char *)agetstr(oentbuf, &bp);
+ if (addr)
+ fprintf(stderr, "%s was obsoleted. Use %s.\n",
+ oentbuf, entbuf);
+ }
+ 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-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 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) 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)
+ continue;
+ 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);
+ }
+
+ 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)
+ continue;
+
+ 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);
+ if (dnsa->da_len < 0) {
+ syslog(LOG_ERR, "Invalid dnssl entry: %s",
+ abuf);
+ goto getconfig_free_dns;
+ }
+ 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(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 */
+
+ 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);
+}
+
+void
+get_prefix(struct rainfo *rai)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct prefix *pfx;
+ struct in6_addr *a;
+ 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__);
+ exit(1);
+ }
+ ifi = rai->rai_ifinfo;
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ int plen;
+
+ if (strcmp(ifa->ifa_name, ifi->ifi_ifname) != 0)
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
+ if (IN6_IS_ADDR_LINKLOCAL(a))
+ continue;
+
+ /* get prefix length */
+ 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) {
+ syslog(LOG_ERR, "<%s> failed to get prefixlen "
+ "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. */
+ ELM_MALLOC(pfx, exit(1));
+
+ /* set prefix, sweep bits outside of prefixlen */
+ 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, &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, pfx->pfx_prefixlen, ifi->ifi_ifname);
+
+ /* set other fields with protocol defaults */
+ 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 */
+ TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next);
+
+ /* counter increment */
+ rai->rai_pfxs++;
+ }
+
+ freeifaddrs(ifap);
+}
+
+static void
+makeentry(char *buf, size_t len, int id, const char *string)
+{
+
+ 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 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 *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,
+ sizeof(ntopbuf)), ipr->ipr_plen, ifi->ifi_ifname);
+
+ rai->rai_pfxs++;
+}
+
+/*
+ * Delete a prefix to the list of specified interface and reconstruct
+ * the outgoing packet.
+ * The prefix must be in the list.
+ */
+void
+delete_prefix(struct prefix *pfx)
+{
+ struct rainfo *rai;
+ struct ifinfo *ifi;
+ char ntopbuf[INET6_ADDRSTRLEN];
+
+ 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, &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 *pfx)
+{
+ 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__);
+ exit(1);
+ }
+
+ syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, "
+ "will expire in %ld seconds", __func__,
+ inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, sizeof(ntopbuf)),
+ pfx->pfx_prefixlen, ifi->ifi_ifname, (long)prefix_timo);
+
+ /* set the expiration timer */
+ 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(pfx);
+ }
+ timo.tv_sec = prefix_timo;
+ timo.tv_nsec = 0;
+ rtadvd_set_timer(&timo, pfx->pfx_timer);
+}
+
+static struct rtadvd_timer *
+prefix_timeout(void *arg)
+{
+
+ delete_prefix((struct prefix *)arg);
+
+ return (NULL);
+}
+
+void
+update_prefix(struct prefix *pfx)
+{
+ struct rainfo *rai;
+ struct ifinfo *ifi;
+ char ntopbuf[INET6_ADDRSTRLEN];
+
+ 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__);
+ exit(1);
+ }
+
+ syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s",
+ __func__, inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf,
+ sizeof(ntopbuf)), pfx->pfx_prefixlen, ifi->ifi_ifname);
+
+ /* stop the expiration timer */
+ rtadvd_remove_timer(pfx->pfx_timer);
+ pfx->pfx_timer = NULL;
+}
+
+/*
+ * Try to get an in6_prefixreq contents for a prefix which matches
+ * ipr->ipr_prefix and ipr->ipr_plen and belongs to
+ * the interface whose name is ipr->ipr_name[].
+ */
+static int
+init_prefix(struct in6_prefixreq *ipr)
+{
+#if 0
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+
+ if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
+ syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __func__,
+ strerror(errno));
+
+ ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
+ ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
+ ipr->ipr_raf_onlink = 1;
+ ipr->ipr_raf_auto = 1;
+ /* omit other field initialization */
+ }
+ else if (ipr->ipr_origin < PR_ORIG_RR) {
+ 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);
+ close(s);
+ return (1);
+ }
+
+ close(s);
+ 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);
+#endif
+}
+
+void
+make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
+{
+ struct in6_prefixreq ipr;
+
+ 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));
+ exit(1);
+ }
+ ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
+ ipr.ipr_prefix.sin6_family = AF_INET6;
+ ipr.ipr_prefix.sin6_addr = *addr;
+ ipr.ipr_plen = plen;
+
+ if (init_prefix(&ipr))
+ return; /* init failed by some error */
+ add_prefix(rai, &ipr);
+}
+
+void
+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;
+ struct nd_opt_route_info *ndopt_rti;
+ 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 (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__, ifi->ifi_ifname);
+ rai->rai_advlinkopt = 0;
+ }
+ packlen += lladdroptlen;
+ }
+ if (rai->rai_pfxs)
+ packlen += sizeof(struct nd_opt_prefix_info) * rai->rai_pfxs;
+ if (rai->rai_linkmtu)
+ packlen += sizeof(struct nd_opt_mtu);
+
+ 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__);
+ exit(1);
+ }
+ 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? */
+ rai->rai_ra_datalen = packlen;
+
+ /*
+ * construct the packet
+ */
+ ra = (struct nd_router_advert *)buf;
+ ra->nd_ra_type = ND_ROUTER_ADVERT;
+ ra->nd_ra_code = 0;
+ ra->nd_ra_cksum = 0;
+ 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 & rai->rai_rtpref;
+ ra->nd_ra_flags_reserved |=
+ rai->rai_managedflg ? ND_RA_FLAG_MANAGED : 0;
+ ra->nd_ra_flags_reserved |=
+ 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 (rai->rai_advlinkopt) {
+ lladdropt_fill(&ifi->ifi_sdl, (struct nd_opt_hdr *)buf);
+ buf += lladdroptlen;
+ }
+
+ 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(rai->rai_linkmtu);
+ buf += sizeof(struct nd_opt_mtu);
+ }
+
+ 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->pfx_prefixlen;
+ ndopt_pi->nd_opt_pi_flags_reserved = 0;
+ if (pfx->pfx_onlinkflg)
+ ndopt_pi->nd_opt_pi_flags_reserved |=
+ ND_OPT_PI_FLAG_ONLINK;
+ if (pfx->pfx_autoconfflg)
+ ndopt_pi->nd_opt_pi_flags_reserved |=
+ ND_OPT_PI_FLAG_AUTO;
+ if (pfx->pfx_timer)
+ vltime = 0;
+ else {
+ if (pfx->pfx_vltimeexpire || pfx->pfx_pltimeexpire)
+ clock_gettime(CLOCK_MONOTONIC_FAST, &now);
+ if (pfx->pfx_vltimeexpire == 0)
+ vltime = pfx->pfx_validlifetime;
+ else
+ vltime = ((time_t)pfx->pfx_vltimeexpire > now.tv_sec) ?
+ pfx->pfx_vltimeexpire - now.tv_sec : 0;
+ }
+ if (pfx->pfx_timer)
+ pltime = 0;
+ else {
+ if (pfx->pfx_pltimeexpire == 0)
+ pltime = pfx->pfx_preflifetime;
+ else
+ pltime = ((time_t)pfx->pfx_pltimeexpire > now.tv_sec) ?
+ pfx->pfx_pltimeexpire - now.tv_sec : 0;
+ }
+ if (vltime < pltime) {
+ /*
+ * this can happen if vltime is decrement but pltime
+ * is not.
+ */
+ pltime = vltime;
+ }
+ 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->pfx_prefix;
+
+ buf += sizeof(struct nd_opt_prefix_info);
+ }
+
+ 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->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;
+ }
+
+ TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) {
+ struct rdnss_addr *rdna;
+
+ 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);
+ }
+
+ 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;
+ buf = (char *)ndopt_dnssl + len;
+
+ /* 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;
+}
diff --git a/usr.sbin/rtadvd/config.h b/usr.sbin/rtadvd/config.h
new file mode 100644
index 0000000..219390b
--- /dev/null
+++ b/usr.sbin/rtadvd/config.h
@@ -0,0 +1,53 @@
+/* $FreeBSD$ */
+/* $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:
+ * 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.
+ * 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
+ * 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.
+ */
+
+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
index 0000000..5e4a68b
--- /dev/null
+++ b/usr.sbin/rtadvd/control.c
@@ -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$
+ *
+ */
+
+#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);
+}
diff --git a/usr.sbin/rtadvd/control.h b/usr.sbin/rtadvd/control.h
new file mode 100644
index 0000000..2168302
--- /dev/null
+++ b/usr.sbin/rtadvd/control.h
@@ -0,0 +1,74 @@
+/*-
+ * 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$
+ *
+ */
+
+#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
index 0000000..33efe37
--- /dev/null
+++ b/usr.sbin/rtadvd/control_client.c
@@ -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$
+ *
+ */
+
+#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
index 0000000..2f50f17
--- /dev/null
+++ b/usr.sbin/rtadvd/control_client.h
@@ -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$
+ *
+ */
+
+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
index 0000000..76ca541
--- /dev/null
+++ b/usr.sbin/rtadvd/control_server.c
@@ -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$
+ *
+ */
+
+#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
index 0000000..76fe9cd
--- /dev/null
+++ b/usr.sbin/rtadvd/control_server.h
@@ -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$
+ *
+ */
+
+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);
diff --git a/usr.sbin/rtadvd/if.c b/usr.sbin/rtadvd/if.c
new file mode 100644
index 0000000..b9af28d
--- /dev/null
+++ b/usr.sbin/rtadvd/if.c
@@ -0,0 +1,748 @@
+/* $FreeBSD$ */
+/* $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:
+ * 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.
+ * 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
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/ethernet.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#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"
+
+#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)))
+
+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;
+ NEXT_SA(sa);
+ }
+ else
+ rti_info[i] = NULL;
+ }
+}
+
+#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);
+ }
+}
+
+void
+lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
+{
+ char *addr;
+
+ 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);
+ }
+
+ return;
+}
+
+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 (len);
+}
+
+#define FILTER_MATCH(type, filter) ((0x1 << type) & filter)
+#define SIN6(s) ((struct sockaddr_in6 *)(s))
+#define SDL(s) ((struct sockaddr_dl *)(s))
+char *
+get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter)
+{
+ struct rt_msghdr *rtm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr *sa, *dst, *gw, *ifa, *rti_info[RTAX_MAX];
+
+ *lenp = 0;
+ for (rtm = (struct rt_msghdr *)buf;
+ rtm < (struct rt_msghdr *)lim;
+ rtm = (struct rt_msghdr *)(((char *)rtm) + rtm->rtm_msglen)) {
+ /* 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);
+ break;
+ }
+ 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:
+ case RTM_DELETE:
+ /* address related checks */
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+ if ((dst = rti_info[RTAX_DST]) == NULL ||
+ dst->sa_family != AF_INET6)
+ continue;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&SIN6(dst)->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&SIN6(dst)->sin6_addr))
+ continue;
+
+ if ((gw = rti_info[RTAX_GATEWAY]) == NULL ||
+ gw->sa_family != AF_LINK)
+ continue;
+ if (ifindex && SDL(gw)->sdl_index != ifindex)
+ continue;
+
+ if (rti_info[RTAX_NETMASK] == NULL)
+ continue;
+
+ /* found */
+ *lenp = rtm->rtm_msglen;
+ return (char *)rtm;
+ /* NOTREACHED */
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+
+ /* address related checks */
+ sa = (struct sockaddr *)(ifam + 1);
+ get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
+ if ((ifa = rti_info[RTAX_IFA]) == NULL ||
+ (ifa->sa_family != AF_INET &&
+ ifa->sa_family != AF_INET6))
+ continue;
+
+ if (ifa->sa_family == AF_INET6 &&
+ (IN6_IS_ADDR_LINKLOCAL(&SIN6(ifa)->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&SIN6(ifa)->sin6_addr)))
+ continue;
+
+ if (ifindex && ifam->ifam_index != ifindex)
+ continue;
+
+ /* found */
+ *lenp = ifam->ifam_msglen;
+ return (char *)rtm;
+ /* NOTREACHED */
+ case RTM_IFINFO:
+ case RTM_IFANNOUNCE:
+ /* found */
+ *lenp = rtm->rtm_msglen;
+ return (char *)rtm;
+ /* NOTREACHED */
+ }
+ }
+
+ return ((char *)rtm);
+}
+#undef FILTER_MATCH
+
+struct in6_addr *
+get_addr(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+ return (&SIN6(rti_info[RTAX_DST])->sin6_addr);
+}
+
+int
+get_rtm_ifindex(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+
+ 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_prefixlen(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ char *p, *lim;
+
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+ sa = rti_info[RTAX_NETMASK];
+
+ p = (char *)(&SIN6(sa)->sin6_addr);
+ lim = (char *)sa + sa->sa_len;
+ return prefixlen(p, lim);
+}
+
+int
+prefixlen(unsigned char *p, unsigned char *lim)
+{
+ int masklen;
+
+ for (masklen = 0; p < lim; p++) {
+ switch (*p) {
+ case 0xff:
+ masklen += 8;
+ break;
+ case 0xfe:
+ masklen += 7;
+ break;
+ case 0xfc:
+ masklen += 6;
+ break;
+ case 0xf8:
+ masklen += 5;
+ break;
+ case 0xf0:
+ masklen += 4;
+ break;
+ case 0xe0:
+ masklen += 3;
+ break;
+ case 0xc0:
+ masklen += 2;
+ break;
+ case 0x80:
+ masklen += 1;
+ break;
+ case 0x00:
+ break;
+ default:
+ return (-1);
+ }
+ }
+
+ return (masklen);
+}
+
+struct ifinfo *
+update_persist_ifinfo(struct ifilist_head_t *ifi_head, const char *ifname)
+{
+ 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;
+ }
+ }
+
+ 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;
+ strlcpy(ifi->ifi_ifname, ifname, sizeof(ifi->ifi_ifname));
+ ifi->ifi_rainfo = NULL;
+ ifi->ifi_state = IFI_STATE_UNCONFIGURED;
+ TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next);
+ }
+
+ ifi->ifi_persist = 1;
+
+ 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
+update_ifinfo_nd_flags(struct ifinfo *ifi)
+{
+ 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 (0);
+}
+
+struct ifinfo *
+update_ifinfo(struct ifilist_head_t *ifi_head, int ifindex)
+{
+ 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 ((msg = malloc(len)) == NULL) {
+ syslog(LOG_ERR, "<%s> malloc failed", __func__);
+ exit(1);
+ }
+ 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);
+ }
+
+ 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;
+
+ syslog(LOG_DEBUG, "<%s> ifm = %p, lim = %p, diff = %zu",
+ __func__, ifm, lim, (char *)lim - (char *)ifm);
+
+ 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", __func__);
+ free(msg);
+ return (NULL);
+ }
+
+ ifi_new = 0;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ 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",
+ RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen);
+ exit(1);
+ }
+
+ 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);
+ }
+ }
+ free(msg);
+
+ if (mcastif != NULL) {
+ error = sock_mc_rr_update(&sock, mcastif);
+ if (error)
+ exit(1);
+ }
+
+ return (ifi);
+}
+
+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)
+{
+ 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);
+ }
+ 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);
+
+ return (0);
+}
diff --git a/usr.sbin/rtadvd/if.h b/usr.sbin/rtadvd/if.h
new file mode 100644
index 0000000..6efdd56
--- /dev/null
+++ b/usr.sbin/rtadvd/if.h
@@ -0,0 +1,61 @@
+/* $FreeBSD$ */
+/* $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:
+ * 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.
+ * 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
+ * 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.
+ */
+
+#define UPDATE_IFINFO_ALL 0
+
+struct sockinfo {
+ int si_fd;
+ const char *si_name;
+};
+
+extern struct sockinfo sock;
+extern struct sockinfo rtsock;
+extern struct sockinfo ctrlsock;
+
+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_prefixlen(char *);
+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);
diff --git a/usr.sbin/rtadvd/pathnames.h b/usr.sbin/rtadvd/pathnames.h
new file mode 100644
index 0000000..248ee19
--- /dev/null
+++ b/usr.sbin/rtadvd/pathnames.h
@@ -0,0 +1,6 @@
+/* $KAME: pathnames.h,v 1.2 2000/05/16 13:34:13 itojun Exp $ */
+/* $FreeBSD$ */
+
+#define _PATH_RTADVDCONF "/etc/rtadvd.conf"
+#define _PATH_RTADVDPID "/var/run/rtadvd.pid"
+#define _PATH_CTRL_SOCK "/var/run/rtadvd.sock"
diff --git a/usr.sbin/rtadvd/rrenum.c b/usr.sbin/rtadvd/rrenum.c
new file mode 100644
index 0000000..eede4b6
--- /dev/null
+++ b/usr.sbin/rtadvd/rrenum.c
@@ -0,0 +1,502 @@
+/* $FreeBSD$ */
+/* $KAME: rrenum.c,v 1.12 2002/06/10 19:59:47 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 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:
+ * 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.
+ * 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
+ * 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.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/icmp6.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"
+#include "if.h"
+
+#define RR_ISSET_SEGNUM(segnum_bits, segnum) \
+ ((((segnum_bits)[(segnum) >> 5]) & (1 << ((segnum) & 31))) != 0)
+#define RR_SET_SEGNUM(segnum_bits, segnum) \
+ (((segnum_bits)[(segnum) >> 5]) |= (1 << ((segnum) & 31)))
+
+struct rr_operation {
+ u_long rro_seqnum;
+ u_long rro_segnum_bits[8];
+};
+
+static struct rr_operation rro;
+static int rr_rcvifindex;
+static int rrcmd2pco[RPM_PCO_MAX] = {
+ 0,
+ SIOCAIFPREFIX_IN6,
+ SIOCCIFPREFIX_IN6,
+ SIOCSGIFPREFIX_IN6
+};
+static int s = -1;
+
+/*
+ * Check validity of a Prefix Control Operation(PCO).
+ * return 0 on success, 1 on failure.
+ */
+static int
+rr_pco_check(int len, struct rr_pco_match *rpm)
+{
+ struct rr_pco_use *rpu, *rpulim;
+ int checklen;
+
+ /* rpm->rpm_len must be (4N * 3) as router-renum-05.txt */
+ 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);
+ }
+ /* rpm->rpm_code must be valid value */
+ 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_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);
+ }
+
+ /*
+ * rpu->rpu_uselen, rpu->rpu_keeplen, and sum of them must be
+ * between 0 and 128 inclusive
+ */
+ for (rpu = (struct rr_pco_use *)(rpm + 1),
+ rpulim = (struct rr_pco_use *)((char *)rpm + len);
+ rpu < rpulim;
+ rpu += 1) {
+ checklen = rpu->rpu_uselen;
+ checklen += rpu->rpu_keeplen;
+ /*
+ * omit these check, because either of rpu_uselen
+ * and rpu_keeplen is unsigned char
+ * (128 > rpu_uselen > 0)
+ * (128 > rpu_keeplen > 0)
+ * (rpu_uselen + rpu_keeplen > 0)
+ */
+ 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);
+ }
+ }
+ return (0);
+}
+
+static void
+do_use_prefix(int len, struct rr_pco_match *rpm,
+ struct in6_rrenumreq *irr, int ifindex)
+{
+ struct rr_pco_use *rpu, *rpulim;
+ struct rainfo *rai;
+ struct ifinfo *ifi;
+ struct prefix *pfx;
+
+ rpu = (struct rr_pco_use *)(rpm + 1);
+ rpulim = (struct rr_pco_use *)((char *)rpm + len);
+
+ if (rpu == rpulim) { /* no use prefix */
+ if (rpm->rpm_code == RPM_PCO_ADD)
+ return;
+
+ irr->irr_u_uselen = 0;
+ irr->irr_u_keeplen = 0;
+ irr->irr_raf_mask_onlink = 0;
+ irr->irr_raf_mask_auto = 0;
+ irr->irr_vltime = 0;
+ irr->irr_pltime = 0;
+ memset(&irr->irr_flags, 0, sizeof(irr->irr_flags));
+ irr->irr_useprefix.sin6_len = 0; /* let it mean, no addition */
+ irr->irr_useprefix.sin6_family = 0;
+ irr->irr_useprefix.sin6_addr = in6addr_any;
+ if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 &&
+ errno != EADDRNOTAVAIL)
+ syslog(LOG_ERR, "<%s> ioctl: %s", __func__,
+ strerror(errno));
+ return;
+ }
+
+ for (rpu = (struct rr_pco_use *)(rpm + 1),
+ rpulim = (struct rr_pco_use *)((char *)rpm + len);
+ rpu < rpulim;
+ rpu += 1) {
+ /* init in6_rrenumreq fields */
+ 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);
+ irr->irr_raf_mask_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;
+ irr->irr_raf_auto =
+ (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;
+ irr->irr_rrf_decrprefd =
+ (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;
+
+ if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 &&
+ errno != EADDRNOTAVAIL)
+ syslog(LOG_ERR, "<%s> ioctl: %s", __func__,
+ 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) {
+ ifi = if_indextoifinfo(ifindex);
+ if (ifi == NULL || ifi->ifi_rainfo == NULL)
+ continue; /* non-advertising IF */
+ rai = ifi->ifi_rainfo;
+
+ TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) {
+ struct timespec now;
+
+ if (prefix_match(&pfx->pfx_prefix,
+ pfx->pfx_prefixlen, &rpm->rpm_prefix,
+ rpm->rpm_matchlen)) {
+ /* change parameters */
+ pfx->pfx_validlifetime =
+ ntohl(rpu->rpu_vltime);
+ pfx->pfx_preflifetime =
+ ntohl(rpu->rpu_pltime);
+ if (irr->irr_rrf_decrvalid) {
+ clock_gettime(CLOCK_MONOTONIC_FAST,
+ &now);
+ pfx->pfx_vltimeexpire =
+ now.tv_sec +
+ pfx->pfx_validlifetime;
+ } else
+ pfx->pfx_vltimeexpire = 0;
+ if (irr->irr_rrf_decrprefd) {
+ clock_gettime(CLOCK_MONOTONIC_FAST,
+ &now);
+ pfx->pfx_pltimeexpire =
+ now.tv_sec +
+ pfx->pfx_preflifetime;
+ } else
+ pfx->pfx_pltimeexpire = 0;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * process a Prefix Control Operation(PCO).
+ * return 0 on success, 1 on failure
+ */
+static int
+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);
+
+ if (s == -1 && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+
+ memset(&irr, 0, sizeof(irr));
+ irr.irr_origin = PR_ORIG_RR;
+ irr.irr_m_len = rpm->rpm_matchlen;
+ irr.irr_m_minlen = rpm->rpm_minlen;
+ irr.irr_m_maxlen = rpm->rpm_maxlen;
+ irr.irr_matchprefix.sin6_len = sizeof(irr.irr_matchprefix);
+ irr.irr_matchprefix.sin6_family = AF_INET6;
+ 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 ((rr->rr_flags & ICMP6_RR_FLAGS_FORCEAPPLY) == 0 &&
+ (ifi->ifi_flags & IFF_UP) == 0)
+ continue;
+ /* TODO: interface scope check */
+ do_use_prefix(len, rpm, &irr, ifindex);
+ }
+ if (errno == ENXIO)
+ return (0);
+ else if (errno) {
+ syslog(LOG_ERR, "<%s> if_indextoname: %s", __func__,
+ strerror(errno));
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * call do_pco() for each Prefix Control Operations(PCOs) in a received
+ * Router Renumbering Command packet.
+ * return 0 on success, 1 on failure
+ */
+static int
+do_rr(int len, struct icmp6_router_renum *rr)
+{
+ struct rr_pco_match *rpm;
+ char *cp, *lim;
+
+ lim = (char *)rr + len;
+ cp = (char *)(rr + 1);
+ len -= sizeof(struct icmp6_router_renum);
+
+ update_ifinfo(&ifilist, UPDATE_IFINFO_ALL);
+
+ while (cp < lim) {
+ int rpmlen;
+
+ rpm = (struct rr_pco_match *)cp;
+ 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);
+ }
+ rpmlen = rpm->rpm_len << 3;
+ if (len < rpmlen)
+ goto tooshort;
+
+ if (do_pco(rr, rpmlen, rpm)) {
+ syslog(LOG_WARNING, "<%s> invalid PCO", __func__);
+ goto next;
+ }
+
+ next:
+ cp += rpmlen;
+ len -= rpmlen;
+ }
+
+ return (0);
+}
+
+/*
+ * check validity of a router renumbering command packet
+ * return 0 on success, 1 on failure
+ */
+static int
+rr_command_check(int len, struct icmp6_router_renum *rr, struct in6_addr *from,
+ struct in6_addr *dst)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ /* omit rr minimal length check. hope kernel have done it. */
+ /* rr_command length check */
+ 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);
+ }
+
+ /* 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, 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, 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, sizeof(ntopbuf)));
+ return (0);
+ }
+
+ /* update seqnum */
+ if (rro.rro_seqnum != rr->rr_seqnum) {
+ /* then must be "<" */
+
+ /* init rro_segnum_bits */
+ memset(rro.rro_segnum_bits, 0,
+ sizeof(rro.rro_segnum_bits));
+ }
+ rro.rro_seqnum = rr->rr_seqnum;
+
+ return (0);
+}
+
+static void
+rr_command_input(int len, struct icmp6_router_renum *rr,
+ struct in6_addr *from, struct in6_addr *dst)
+{
+ /* rr_command validity check */
+ if (rr_command_check(len, rr, from, dst))
+ goto failed;
+ if ((rr->rr_flags & (ICMP6_RR_FLAGS_TEST|ICMP6_RR_FLAGS_REQRESULT)) ==
+ ICMP6_RR_FLAGS_TEST)
+ return;
+
+ /* do router renumbering */
+ if (do_rr(len, rr))
+ goto failed;
+
+ /* update segnum */
+ RR_SET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum);
+
+ return;
+
+ failed:
+ syslog(LOG_ERR, "<%s> received RR was invalid", __func__);
+ return;
+}
+
+void
+rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi,
+ 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] ,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 ((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],
+ sizeof(ntopbuf[0])),
+ inet_ntop(AF_INET6, &dst, ntopbuf[1], sizeof(ntopbuf[1])),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /*
+ * If the IPv6 destination address is neither an All Routers multicast
+ * address [AARCH] nor one of the receiving router's unicast addresses,
+ * the message MUST be discarded and SHOULD be logged to network
+ * management.
+ * 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(
+ &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], sizeof(ntopbuf[0])),
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[1],
+ sizeof(ntopbuf[1])),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ rr_rcvifindex = pi->ipi6_ifindex;
+
+ switch (rr->rr_code) {
+ case ICMP6_ROUTER_RENUMBERING_COMMAND:
+ rr_command_input(len, rr, &from->sin6_addr, dst);
+ /* TODO: send reply msg */
+ break;
+ case ICMP6_ROUTER_RENUMBERING_RESULT:
+ /* RESULT will be processed by rrenumd */
+ break;
+ case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET:
+ /* TODO: sequence number reset */
+ break;
+ default:
+ syslog(LOG_ERR, "<%s> received unknown code %d",
+ __func__, rr->rr_code);
+ break;
+
+ }
+
+ return;
+}
diff --git a/usr.sbin/rtadvd/rrenum.h b/usr.sbin/rtadvd/rrenum.h
new file mode 100644
index 0000000..2b20d59
--- /dev/null
+++ b/usr.sbin/rtadvd/rrenum.h
@@ -0,0 +1,34 @@
+/* $FreeBSD$ */
+/* $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:
+ * 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.
+ * 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
+ * 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.
+ */
+
+void rr_input(int, struct icmp6_router_renum *, struct in6_pktinfo *,
+ struct sockaddr_in6 *, struct in6_addr *);
diff --git a/usr.sbin/rtadvd/rtadvd.8 b/usr.sbin/rtadvd/rtadvd.8
new file mode 100644
index 0000000..fcb46fe
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.8
@@ -0,0 +1,242 @@
+.\" $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.
+.\"
+.\" 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.
+.\" 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
+.\" 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$
+.\"
+.Dd February 25, 2013
+.Dt RTADVD 8
+.Os
+.Sh NAME
+.Nm rtadvd
+.Nd router advertisement daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dDfRs
+.Op Fl c Ar configfile
+.Op Fl C Ar ctlsock
+.Op Fl M Ar ifname
+.Op Fl p Ar pidfile
+.Op Ar interface ...
+.Sh DESCRIPTION
+.Nm
+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
+as in response to router solicitation messages sent by end hosts.
+.Pp
+Router advertisements can be configured on a per-interface basis, as
+described in
+.Xr rtadvd.conf 5 .
+.Pp
+If there is no configuration file entry for an interface,
+or if the configuration file does not exist altogether,
+.Nm
+sets all the parameters to their default values.
+In particular,
+.Nm
+reads all the interface routes from the routing table and advertises
+them as on-link prefixes.
+.Pp
+.Nm
+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,
+.Nm
+adds the corresponding prefix to its advertising list.
+.Pp
+Similarly, when an interface direct route is deleted,
+.Nm
+will start advertising the prefixes with zero valid and preferred
+lifetimes to help the receiving hosts switch to a new prefix when
+renumbering.
+Note, however, that the zero valid lifetime cannot invalidate the
+autoconfigured addresses at a receiving host immediately.
+According to the specification, the host will retain the address
+for a certain period, which will typically be two hours.
+The zero lifetimes rather intend to make the address deprecated,
+indicating that a new non-deprecated address should be used as the
+source address of a new connection.
+This behavior will last for two hours.
+Then
+.Nm
+will completely remove the prefix from the advertising list,
+and succeeding advertisements will not contain the prefix information.
+.Pp
+Moreover, if the status of an advertising interface changes,
+.Nm
+will start or stop sending router advertisements according
+to the latest status.
+.Pp
+The
+.Fl s
+option may be used to disable this behavior;
+.Nm
+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 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,
+.Nm
+can be invoked if router lifetime is explicitly set zero on every
+advertising interface.
+.Pp
+The command line options are:
+.Bl -tag -width indent
+.\"
+.It Fl c
+Specify an alternate location,
+.Ar configfile ,
+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 appearing on the command
+line.
+This option has meaning only with the
+.Fl R
+option, which enables routing renumbering protocol support.
+.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.
+This option is currently disabled, and is ignored by
+.Nm
+with a warning message.
+.It Fl s
+Do not add or delete prefixes dynamically.
+Only statically configured prefixes, if any, will be advertised.
+.El
+.Pp
+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
+to kill
+.Nm
+gracefully.
+In this case,
+.Nm
+will transmit router advertisement with router lifetime 0
+to all the interfaces
+.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
+The default process ID file.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr rtadvd.conf 5 ,
+.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
+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
+undesirable
+.Xr icmp6 4
+redirect messages.
+However, based on the later discussion in the IETF ipng working group,
+all routers should rather advertise the messages regardless of
+the network topology, in order to ensure reachability.
diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c
new file mode 100644
index 0000000..7694811
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.c
@@ -0,0 +1,1914 @@
+/* $FreeBSD$ */
+/* $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:
+ * 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.
+ * 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
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#include <net/if.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 <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <netinet/in_var.h>
+#include <netinet6/nd6.h>
+
+#include <time.h>
+#include <unistd.h>
+#include <stdio.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 "config.h"
+#include "control.h"
+#include "control_server.h"
+
+#define RTADV_TYPE2BITMASK(type) (0x1 << type)
+
+struct msghdr rcvmhdr;
+static char *rcvcmsgbuf;
+static size_t rcvcmsgbuflen;
+static char *sndcmsgbuf = NULL;
+static size_t sndcmsgbuflen;
+struct msghdr sndmhdr;
+struct iovec rcviov[2];
+struct iovec sndiov[2];
+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 nd_optlist {
+ TAILQ_ENTRY(nd_optlist) nol_next;
+ struct nd_opt_hdr *nol_opt;
+};
+union nd_opt {
+ struct nd_opt_hdr *opt_array[9];
+ struct {
+ struct nd_opt_hdr *zero;
+ struct nd_opt_hdr *src_lladdr;
+ struct nd_opt_hdr *tgt_lladdr;
+ struct nd_opt_prefix_info *pi;
+ struct nd_opt_rd_hdr *rh;
+ struct nd_opt_mtu *mtu;
+ TAILQ_HEAD(, nd_optlist) opt_list;
+ } nd_opt_each;
+};
+#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 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[])
+{
+ struct pollfd set[PFD_MAX];
+ struct timespec *timeout;
+ int i, ch;
+ int fflag = 0, logopt;
+ int error;
+ pid_t pid, otherpid;
+
+ /* get command line options and arguments */
+ 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++;
+ break;
+ case 'D':
+ dflag += 3;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'M':
+ mcastif = optarg;
+ break;
+ case 'R':
+ fprintf(stderr, "rtadvd: "
+ "the -R option is currently ignored.\n");
+ /* accept_rr = 1; */
+ /* run anyway... */
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'p':
+ pidfilename = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ logopt = LOG_NDELAY | LOG_PID;
+ if (fflag)
+ logopt |= LOG_PERROR;
+ openlog("rtadvd", logopt, LOG_DAEMON);
+
+ /* set log level */
+ 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();
+
+ 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);
+
+ sock_open(&sock);
+
+ update_ifinfo(&ifilist, UPDATE_IFINFO_ALL);
+ for (i = 0; i < argc; i++)
+ update_persist_ifinfo(&ifilist, argv[i]);
+
+ 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);
+ }
+
+ /* record the current PID */
+ pid = getpid();
+ pidfile_write(pfh);
+
+ set[PFD_RAWSOCK].fd = sock.si_fd;
+ set[PFD_RAWSOCK].events = POLLIN;
+ if (sflag == 0) {
+ rtsock_open(&rtsock);
+ set[PFD_RTSOCK].fd = rtsock.si_fd;
+ set[PFD_RTSOCK].events = POLLIN;
+ } else
+ 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);
+ }
+
+ /* load configuration file */
+ set_do_reload(0);
+
+ while (1) {
+ if (is_do_shutdown())
+ rtadvd_shutdown();
+
+ 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();
+ }
+
+ /* timeout handler update for active interfaces */
+ rtadvd_update_timeout_handler();
+
+ /* timer expiration check and reset the timer */
+ timeout = rtadvd_check_timer();
+
+ if (timeout != NULL) {
+ syslog(LOG_DEBUG,
+ "<%s> set timer to %ld:%ld. waiting for "
+ "inputs or timeout", __func__,
+ (long int)timeout->tv_sec,
+ (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) {
+
+ /* EINTR would occur if a signal was delivered */
+ if (errno != EINTR)
+ syslog(LOG_ERR, "poll() failed: %s",
+ strerror(errno));
+ continue;
+ }
+ if (i == 0) /* timeout */
+ continue;
+ 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_shutdown(void)
+{
+ struct ifinfo *ifi;
+ struct rainfo *rai;
+ struct rdnss *rdn;
+ struct dnssl *dns;
+
+ 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);
+ }
+
+ sleep(1);
+ return;
+ }
+
+ 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;
+ }
+ 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;
+
+ 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_NOTICE, "final RA transmission started.");
+
+ pidfile_remove(pfh);
+ csock_close(&ctrlsock);
+}
+
+static void
+rtmsg_input(struct sockinfo *s)
+{
+ int n, type, ifindex = 0, plen;
+ size_t len;
+ char msg[2048], *next, *lim;
+ char ifname[IFNAMSIZ];
+ struct if_announcemsghdr *ifan;
+ struct rt_msghdr *rtm;
+ struct prefix *pfx;
+ struct rainfo *rai;
+ struct in6_addr *addr;
+ struct ifinfo *ifi;
+ char addrbuf[INET6_ADDRSTRLEN];
+ int prefixchange = 0;
+
+ if (s == NULL) {
+ syslog(LOG_ERR, "<%s> internal error", __func__);
+ exit(1);
+ }
+ n = read(s->si_fd, msg, sizeof(msg));
+ rtm = (struct rt_msghdr *)msg;
+ syslog(LOG_DEBUG, "<%s> received a routing message "
+ "(type = %d, len = %d)", __func__, rtm->rtm_type, n);
+
+ if (n > rtm->rtm_msglen) {
+ /*
+ * This usually won't happen for messages received on
+ * a routing socket.
+ */
+ syslog(LOG_DEBUG,
+ "<%s> received data length is larger than "
+ "1st routing message len. multiple messages? "
+ "read %d bytes, but 1st msg len = %d",
+ __func__, n, rtm->rtm_msglen);
+#if 0
+ /* adjust length */
+ n = rtm->rtm_msglen;
+#endif
+ }
+
+ lim = msg + n;
+ for (next = msg; next < lim; next += len) {
+ int oldifflags;
+
+ next = get_next_msg(next, lim, 0, &len,
+ RTADV_TYPE2BITMASK(RTM_ADD) |
+ RTADV_TYPE2BITMASK(RTM_DELETE) |
+ RTADV_TYPE2BITMASK(RTM_NEWADDR) |
+ RTADV_TYPE2BITMASK(RTM_DELADDR) |
+ RTADV_TYPE2BITMASK(RTM_IFINFO) |
+ RTADV_TYPE2BITMASK(RTM_IFANNOUNCE));
+ if (len == 0)
+ break;
+ type = ((struct rt_msghdr *)next)->rtm_type;
+ switch (type) {
+ case RTM_ADD:
+ case RTM_DELETE:
+ ifindex = get_rtm_ifindex(next);
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifindex = (int)((struct ifa_msghdr *)next)->ifam_index;
+ break;
+ case RTM_IFINFO:
+ ifindex = (int)((struct if_msghdr *)next)->ifm_index;
+ break;
+ case RTM_IFANNOUNCE:
+ ifan = (struct if_announcemsghdr *)next;
+ switch (ifan->ifan_what) {
+ case IFAN_ARRIVAL:
+ case IFAN_DEPARTURE:
+ break;
+ default:
+ syslog(LOG_DEBUG,
+ "<%s:%d> unknown ifan msg (ifan_what=%d)",
+ __func__, __LINE__, ifan->ifan_what);
+ continue;
+ }
+
+ syslog(LOG_DEBUG, "<%s>: if_announcemsg (idx=%d:%d)",
+ __func__, ifan->ifan_index, ifan->ifan_what);
+ switch (ifan->ifan_what) {
+ case IFAN_ARRIVAL:
+ syslog(LOG_NOTICE,
+ "interface added (idx=%d)",
+ ifan->ifan_index);
+ update_ifinfo(&ifilist, ifan->ifan_index);
+ loadconfig_index(ifan->ifan_index);
+ break;
+ case IFAN_DEPARTURE:
+ syslog(LOG_NOTICE,
+ "interface removed (idx=%d)",
+ ifan->ifan_index);
+ rm_ifinfo_index(ifan->ifan_index);
+
+ /* Clear ifi_ifindex */
+ TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+ if (ifi->ifi_ifindex
+ == ifan->ifan_index) {
+ ifi->ifi_ifindex = 0;
+ break;
+ }
+ }
+ update_ifinfo(&ifilist, ifan->ifan_index);
+ break;
+ }
+ continue;
+ default:
+ /* should not reach here */
+ syslog(LOG_DEBUG,
+ "<%s:%d> unknown rtmsg %d on %s",
+ __func__, __LINE__, type,
+ if_indextoname(ifindex, ifname));
+ continue;
+ }
+ ifi = if_indextoifinfo(ifindex);
+ if (ifi == NULL) {
+ syslog(LOG_DEBUG,
+ "<%s> ifinfo not found for idx=%d. Why?",
+ __func__, ifindex);
+ continue;
+ }
+ rai = ifi->ifi_rainfo;
+ if (rai == NULL) {
+ syslog(LOG_DEBUG,
+ "<%s> route changed on "
+ "non advertising interface(%s)",
+ __func__, ifi->ifi_ifname);
+ continue;
+ }
+
+ oldifflags = ifi->ifi_flags;
+ /* init ifflags because it may have changed */
+ update_ifinfo(&ifilist, ifindex);
+
+ switch (type) {
+ case RTM_ADD:
+ if (sflag)
+ break; /* we aren't interested in prefixes */
+
+ addr = get_addr(msg);
+ plen = get_prefixlen(msg);
+ /* sanity check for plen */
+ /* as RFC2373, prefixlen is at least 4 */
+ if (plen < 4 || plen > 127) {
+ syslog(LOG_INFO, "<%s> new interface route's"
+ "plen %d is invalid for a prefix",
+ __func__, plen);
+ break;
+ }
+ pfx = find_prefix(rai, addr, plen);
+ if (pfx) {
+ if (pfx->pfx_timer) {
+ /*
+ * If the prefix has been invalidated,
+ * make it available again.
+ */
+ update_prefix(pfx);
+ prefixchange = 1;
+ } else
+ syslog(LOG_DEBUG,
+ "<%s> new prefix(%s/%d) "
+ "added on %s, "
+ "but it was already in list",
+ __func__,
+ inet_ntop(AF_INET6, addr,
+ (char *)addrbuf,
+ sizeof(addrbuf)),
+ plen, ifi->ifi_ifname);
+ break;
+ }
+ make_prefix(rai, ifindex, addr, plen);
+ prefixchange = 1;
+ break;
+ case RTM_DELETE:
+ if (sflag)
+ break;
+
+ addr = get_addr(msg);
+ plen = get_prefixlen(msg);
+ /* sanity check for plen */
+ /* as RFC2373, prefixlen is at least 4 */
+ if (plen < 4 || plen > 127) {
+ syslog(LOG_INFO,
+ "<%s> deleted interface route's "
+ "plen %d is invalid for a prefix",
+ __func__, plen);
+ break;
+ }
+ pfx = find_prefix(rai, addr, plen);
+ if (pfx == NULL) {
+ syslog(LOG_DEBUG,
+ "<%s> prefix(%s/%d) was deleted on %s, "
+ "but it was not in list",
+ __func__, inet_ntop(AF_INET6, addr,
+ (char *)addrbuf, sizeof(addrbuf)),
+ plen, ifi->ifi_ifname);
+ break;
+ }
+ invalidate_prefix(pfx);
+ prefixchange = 1;
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ case RTM_IFINFO:
+ break;
+ default:
+ /* should not reach here */
+ syslog(LOG_DEBUG,
+ "<%s:%d> unknown rtmsg %d on %s",
+ __func__, __LINE__, type,
+ if_indextoname(ifindex, ifname));
+ return;
+ }
+
+ /* check if an interface flag is changed */
+ if ((oldifflags & IFF_UP) && /* UP to DOWN */
+ !(ifi->ifi_flags & IFF_UP)) {
+ syslog(LOG_NOTICE,
+ "<interface %s becomes down. stop timer.",
+ ifi->ifi_ifname);
+ rtadvd_remove_timer(ifi->ifi_ra_timer);
+ ifi->ifi_ra_timer = NULL;
+ } else if (!(oldifflags & IFF_UP) && /* DOWN to UP */
+ (ifi->ifi_flags & IFF_UP)) {
+ syslog(LOG_NOTICE,
+ "interface %s becomes up. restart timer.",
+ ifi->ifi_ifname);
+
+ ifi->ifi_state = IFI_STATE_TRANSITIVE;
+ ifi->ifi_burstcount =
+ MAX_INITIAL_RTR_ADVERTISEMENTS;
+ ifi->ifi_burstinterval =
+ MAX_INITIAL_RTR_ADVERT_INTERVAL;
+
+ 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);
+ } else if (prefixchange &&
+ (ifi->ifi_flags & IFF_UP)) {
+ /*
+ * An advertised prefix has been added or invalidated.
+ * Will notice the change in a short delay.
+ */
+ set_short_delay(ifi);
+ }
+ }
+
+ return;
+}
+
+void
+rtadvd_input(struct sockinfo *s)
+{
+ ssize_t i;
+ int *hlimp = NULL;
+#ifdef OLDRAWSOCKET
+ struct ip6_hdr *ip;
+#endif
+ struct icmp6_hdr *icp;
+ int ifindex = 0;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi = NULL;
+ char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ struct in6_addr dst = in6addr_any;
+ struct ifinfo *ifi;
+
+ syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+ if (s == NULL) {
+ syslog(LOG_ERR, "<%s> internal error", __func__);
+ exit(1);
+ }
+ /*
+ * Get message. We reset msg_controllen since the field could
+ * be modified if we had received a message before setting
+ * receive options.
+ */
+ rcvmhdr.msg_controllen = rcvcmsgbuflen;
+ if ((i = recvmsg(s->si_fd, &rcvmhdr, 0)) < 0)
+ return;
+
+ /* extract optional information via Advanced API */
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr);
+ cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO &&
+ cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
+ pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ ifindex = pi->ipi6_ifindex;
+ dst = pi->ipi6_addr;
+ }
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ hlimp = (int *)CMSG_DATA(cm);
+ }
+ if (ifindex == 0) {
+ syslog(LOG_ERR, "failed to get receiving interface");
+ return;
+ }
+ if (hlimp == NULL) {
+ syslog(LOG_ERR, "failed to get receiving hop limit");
+ return;
+ }
+
+ /*
+ * If we happen to receive data on an interface which is now gone
+ * or down, just discard the data.
+ */
+ ifi = if_indextoifinfo(pi->ipi6_ifindex);
+ if (ifi == NULL || !(ifi->ifi_flags & IFF_UP)) {
+ syslog(LOG_INFO,
+ "<%s> received data on a disabled interface (%s)",
+ __func__,
+ (ifi == NULL) ? "[gone]" : ifi->ifi_ifname);
+ return;
+ }
+
+#ifdef OLDRAWSOCKET
+ if ((size_t)i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
+ syslog(LOG_ERR,
+ "packet size(%d) is too short", i);
+ return;
+ }
+
+ ip = (struct ip6_hdr *)rcvmhdr.msg_iov[0].iov_base;
+ icp = (struct icmp6_hdr *)(ip + 1); /* XXX: ext. hdr? */
+#else
+ if ((size_t)i < sizeof(struct icmp6_hdr)) {
+ syslog(LOG_ERR, "packet size(%zd) is too short", i);
+ return;
+ }
+
+ icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base;
+#endif
+
+ switch (icp->icmp6_type) {
+ case ND_ROUTER_SOLICIT:
+ /*
+ * Message verification - RFC 4861 6.1.1
+ * XXX: these checks must be done in the kernel as well,
+ * but we can't completely rely on them.
+ */
+ if (*hlimp != 255) {
+ syslog(LOG_NOTICE,
+ "RS with invalid hop limit(%d) "
+ "received from %s on %s",
+ *hlimp,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ sizeof(ntopbuf)),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (icp->icmp6_code) {
+ syslog(LOG_NOTICE,
+ "RS with invalid ICMP6 code(%d) "
+ "received from %s on %s",
+ icp->icmp6_code,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ sizeof(ntopbuf)),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if ((size_t)i < sizeof(struct nd_router_solicit)) {
+ syslog(LOG_NOTICE,
+ "RS from %s on %s does not have enough "
+ "length (len = %zd)",
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ sizeof(ntopbuf)),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
+ return;
+ }
+ rs_input(i, (struct nd_router_solicit *)icp, pi, &rcvfrom);
+ break;
+ case ND_ROUTER_ADVERT:
+ /*
+ * Message verification - RFC 4861 6.1.2
+ * XXX: there's the same dilemma as above...
+ */
+ if (!IN6_IS_ADDR_LINKLOCAL(&rcvfrom.sin6_addr)) {
+ syslog(LOG_NOTICE,
+ "RA with non-linklocal source address "
+ "received from %s on %s",
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr,
+ ntopbuf, sizeof(ntopbuf)),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (*hlimp != 255) {
+ syslog(LOG_NOTICE,
+ "RA with invalid hop limit(%d) "
+ "received from %s on %s",
+ *hlimp,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ sizeof(ntopbuf)),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (icp->icmp6_code) {
+ syslog(LOG_NOTICE,
+ "RA with invalid ICMP6 code(%d) "
+ "received from %s on %s",
+ icp->icmp6_code,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ sizeof(ntopbuf)),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if ((size_t)i < sizeof(struct nd_router_advert)) {
+ syslog(LOG_NOTICE,
+ "RA from %s on %s does not have enough "
+ "length (len = %zd)",
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ sizeof(ntopbuf)),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
+ return;
+ }
+ ra_input(i, (struct nd_router_advert *)icp, pi, &rcvfrom);
+ break;
+ case ICMP6_ROUTER_RENUMBERING:
+ if (mcastif == NULL) {
+ syslog(LOG_ERR, "received a router renumbering "
+ "message, but not allowed to be accepted");
+ break;
+ }
+ rr_input(i, (struct icmp6_router_renum *)icp, pi, &rcvfrom,
+ &dst);
+ break;
+ default:
+ /*
+ * Note that this case is POSSIBLE, especially just
+ * after invocation of the daemon. This is because we
+ * could receive message after opening the socket and
+ * before setting ICMP6 type filter(see sock_open()).
+ */
+ syslog(LOG_ERR, "invalid icmp type(%d)", icp->icmp6_type);
+ return;
+ }
+
+ return;
+}
+
+static void
+rs_input(int len, struct nd_router_solicit *rs,
+ struct in6_pktinfo *pi, struct sockaddr_in6 *from)
+{
+ char ntopbuf[INET6_ADDRSTRLEN];
+ char ifnamebuf[IFNAMSIZ];
+ union nd_opt ndopts;
+ struct rainfo *rai;
+ struct ifinfo *ifi;
+ struct soliciter *sol;
+
+ syslog(LOG_DEBUG,
+ "<%s> RS received from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* ND option check */
+ memset(&ndopts, 0, sizeof(ndopts));
+ TAILQ_INIT(&ndopts.opt_list);
+ if (nd6_options((struct nd_opt_hdr *)(rs + 1),
+ len - sizeof(struct nd_router_solicit),
+ &ndopts, NDOPT_FLAG_SRCLINKADDR)) {
+ syslog(LOG_INFO,
+ "<%s> ND option check failed for an RS from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /*
+ * If the IP source address is the unspecified address, there
+ * must be no source link-layer address option in the message.
+ * (RFC 4861 6.1.1)
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&from->sin6_addr) &&
+ ndopts.opt_src_lladdr) {
+ syslog(LOG_INFO,
+ "<%s> RS from unspecified src on %s has a link-layer"
+ " address option",
+ __func__, if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+
+ ifi = if_indextoifinfo(pi->ipi6_ifindex);
+ if (ifi == NULL) {
+ syslog(LOG_INFO,
+ "<%s> if (idx=%d) not found. Why?",
+ __func__, pi->ipi6_ifindex);
+ goto done;
+ }
+ rai = ifi->ifi_rainfo;
+ if (rai == NULL) {
+ syslog(LOG_INFO,
+ "<%s> RS received on non advertising interface(%s)",
+ __func__,
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+
+ rai->rai_ifinfo->ifi_rsinput++;
+
+ /*
+ * Decide whether to send RA according to the rate-limit
+ * consideration.
+ */
+
+ /* record sockaddr waiting for RA, if possible */
+ sol = (struct soliciter *)malloc(sizeof(*sol));
+ if (sol) {
+ sol->sol_addr = *from;
+ /* XXX RFC 2553 need clarification on flowinfo */
+ sol->sol_addr.sin6_flowinfo = 0;
+ TAILQ_INSERT_TAIL(&rai->rai_soliciter, sol, sol_next);
+ }
+
+ /*
+ * If there is already a waiting RS packet, don't
+ * update the timer.
+ */
+ if (ifi->ifi_rs_waitcount++)
+ goto done;
+
+ set_short_delay(ifi);
+
+ done:
+ free_ndopts(&ndopts);
+ return;
+}
+
+static void
+set_short_delay(struct ifinfo *ifi)
+{
+ long delay; /* must not be greater than 1000000 */
+ struct timespec interval, now, min_delay, tm_tmp, *rest;
+
+ if (ifi->ifi_ra_timer == NULL)
+ return;
+ /*
+ * Compute a random delay. If the computed value
+ * corresponds to a time later than the time the next
+ * multicast RA is scheduled to be sent, ignore the random
+ * delay and send the advertisement at the
+ * already-scheduled time. RFC 4861 6.2.6
+ */
+ delay = arc4random_uniform(MAX_RA_DELAY_TIME);
+ interval.tv_sec = 0;
+ interval.tv_nsec = delay * 1000;
+ rest = rtadvd_timer_rest(ifi->ifi_ra_timer);
+ if (TS_CMP(rest, &interval, <)) {
+ syslog(LOG_DEBUG, "<%s> random delay is larger than "
+ "the rest of the current timer", __func__);
+ interval = *rest;
+ }
+
+ /*
+ * If we sent a multicast Router Advertisement within
+ * the last MIN_DELAY_BETWEEN_RAS seconds, schedule
+ * the advertisement to be sent at a time corresponding to
+ * MIN_DELAY_BETWEEN_RAS plus the random value after the
+ * previous advertisement was sent.
+ */
+ clock_gettime(CLOCK_MONOTONIC_FAST, &now);
+ TS_SUB(&now, &ifi->ifi_ra_lastsent, &tm_tmp);
+ min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS;
+ min_delay.tv_nsec = 0;
+ if (TS_CMP(&tm_tmp, &min_delay, <)) {
+ TS_SUB(&min_delay, &tm_tmp, &min_delay);
+ TS_ADD(&min_delay, &interval, &interval);
+ }
+ rtadvd_set_timer(&interval, ifi->ifi_ra_timer);
+}
+
+static int
+check_accept_rtadv(int idx)
+{
+ struct ifinfo *ifi;
+
+ TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+ if (ifi->ifi_ifindex == idx)
+ break;
+ }
+ if (ifi == NULL) {
+ syslog(LOG_DEBUG,
+ "<%s> if (idx=%d) not found. Why?",
+ __func__, idx);
+ return (0);
+ }
+#if (__FreeBSD_version < 900000)
+ /*
+ * RA_RECV: !ip6.forwarding && ip6.accept_rtadv
+ * RA_SEND: ip6.forwarding
+ */
+ return ((getinet6sysctl(IPV6CTL_FORWARDING) == 0) &&
+ (getinet6sysctl(IPV6CTL_ACCEPT_RTADV) == 1));
+#else
+ /*
+ * RA_RECV: ND6_IFF_ACCEPT_RTADV
+ * RA_SEND: ip6.forwarding
+ */
+ if (update_ifinfo_nd_flags(ifi) != 0) {
+ syslog(LOG_ERR, "cannot get nd6 flags (idx=%d)", idx);
+ return (0);
+ }
+
+ return (ifi->ifi_nd_flags & ND6_IFF_ACCEPT_RTADV);
+#endif
+}
+
+static void
+ra_input(int len, struct nd_router_advert *nra,
+ struct in6_pktinfo *pi, struct sockaddr_in6 *from)
+{
+ struct rainfo *rai;
+ struct ifinfo *ifi;
+ char ntopbuf[INET6_ADDRSTRLEN];
+ char ifnamebuf[IFNAMSIZ];
+ union nd_opt ndopts;
+ const char *on_off[] = {"OFF", "ON"};
+ uint32_t reachabletime, retranstimer, mtu;
+ int inconsistent = 0;
+ int error;
+
+ syslog(LOG_DEBUG, "<%s> RA received from %s on %s", __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* ND option check */
+ memset(&ndopts, 0, sizeof(ndopts));
+ TAILQ_INIT(&ndopts.opt_list);
+ error = nd6_options((struct nd_opt_hdr *)(nra + 1),
+ len - sizeof(struct nd_router_advert), &ndopts,
+ NDOPT_FLAG_SRCLINKADDR | NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU |
+ NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL);
+ if (error) {
+ syslog(LOG_INFO,
+ "<%s> ND option check failed for an RA from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex,
+ ifnamebuf));
+ return;
+ }
+
+ /*
+ * RA consistency check according to RFC 4861 6.2.7
+ */
+ ifi = if_indextoifinfo(pi->ipi6_ifindex);
+ if (ifi->ifi_rainfo == NULL) {
+ syslog(LOG_INFO,
+ "<%s> received RA from %s on non-advertising"
+ " interface(%s)",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex,
+ ifnamebuf));
+ goto done;
+ }
+ rai = ifi->ifi_rainfo;
+ ifi->ifi_rainput++;
+ syslog(LOG_DEBUG, "<%s> ifi->ifi_rainput = %" PRIu64, __func__,
+ ifi->ifi_rainput);
+
+ /* Cur Hop Limit value */
+ if (nra->nd_ra_curhoplimit && rai->rai_hoplimit &&
+ nra->nd_ra_curhoplimit != rai->rai_hoplimit) {
+ syslog(LOG_NOTICE,
+ "CurHopLimit inconsistent on %s:"
+ " %d from %s, %d from us",
+ ifi->ifi_ifname, nra->nd_ra_curhoplimit,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), rai->rai_hoplimit);
+ inconsistent++;
+ }
+ /* M flag */
+ if ((nra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) !=
+ rai->rai_managedflg) {
+ syslog(LOG_NOTICE,
+ "M flag inconsistent on %s:"
+ " %s from %s, %s from us",
+ ifi->ifi_ifname, on_off[!rai->rai_managedflg],
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), on_off[rai->rai_managedflg]);
+ inconsistent++;
+ }
+ /* O flag */
+ if ((nra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) !=
+ rai->rai_otherflg) {
+ syslog(LOG_NOTICE,
+ "O flag inconsistent on %s:"
+ " %s from %s, %s from us",
+ ifi->ifi_ifname, on_off[!rai->rai_otherflg],
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), on_off[rai->rai_otherflg]);
+ inconsistent++;
+ }
+ /* Reachable Time */
+ reachabletime = ntohl(nra->nd_ra_reachable);
+ if (reachabletime && rai->rai_reachabletime &&
+ reachabletime != rai->rai_reachabletime) {
+ syslog(LOG_NOTICE,
+ "ReachableTime inconsistent on %s:"
+ " %d from %s, %d from us",
+ ifi->ifi_ifname, reachabletime,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), rai->rai_reachabletime);
+ inconsistent++;
+ }
+ /* Retrans Timer */
+ retranstimer = ntohl(nra->nd_ra_retransmit);
+ if (retranstimer && rai->rai_retranstimer &&
+ retranstimer != rai->rai_retranstimer) {
+ syslog(LOG_NOTICE,
+ "RetranceTimer inconsistent on %s:"
+ " %d from %s, %d from us",
+ ifi->ifi_ifname, retranstimer,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), rai->rai_retranstimer);
+ inconsistent++;
+ }
+ /* Values in the MTU options */
+ if (ndopts.opt_mtu) {
+ mtu = ntohl(ndopts.opt_mtu->nd_opt_mtu_mtu);
+ if (mtu && rai->rai_linkmtu && mtu != rai->rai_linkmtu) {
+ syslog(LOG_NOTICE,
+ "MTU option value inconsistent on %s:"
+ " %d from %s, %d from us",
+ ifi->ifi_ifname, mtu,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), rai->rai_linkmtu);
+ inconsistent++;
+ }
+ }
+ /* Preferred and Valid Lifetimes for prefixes */
+ {
+ struct nd_optlist *nol;
+
+ if (ndopts.opt_pi)
+ if (prefix_check(ndopts.opt_pi, rai, from))
+ inconsistent++;
+
+ TAILQ_FOREACH(nol, &ndopts.opt_list, nol_next)
+ if (prefix_check((struct nd_opt_prefix_info *)nol->nol_opt,
+ rai, from))
+ inconsistent++;
+ }
+
+ if (inconsistent)
+ ifi->ifi_rainconsistent++;
+
+ done:
+ free_ndopts(&ndopts);
+ return;
+}
+
+static uint32_t
+udiff(uint32_t u, uint32_t v)
+{
+ return (u >= v ? u - v : v - u);
+}
+
+/* return a non-zero value if the received prefix is inconsitent with ours */
+static int
+prefix_check(struct nd_opt_prefix_info *pinfo,
+ struct rainfo *rai, struct sockaddr_in6 *from)
+{
+ struct ifinfo *ifi;
+ uint32_t preferred_time, valid_time;
+ struct prefix *pfx;
+ int inconsistent = 0;
+ char ntopbuf[INET6_ADDRSTRLEN];
+ char prefixbuf[INET6_ADDRSTRLEN];
+ struct timespec now;
+
+#if 0 /* impossible */
+ if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION)
+ return (0);
+#endif
+ ifi = rai->rai_ifinfo;
+ /*
+ * log if the adveritsed prefix has link-local scope(sanity check?)
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&pinfo->nd_opt_pi_prefix))
+ syslog(LOG_INFO,
+ "<%s> link-local prefix %s/%d is advertised "
+ "from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf,
+ sizeof(prefixbuf)),
+ pinfo->nd_opt_pi_prefix_len,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), ifi->ifi_ifname);
+
+ if ((pfx = find_prefix(rai, &pinfo->nd_opt_pi_prefix,
+ pinfo->nd_opt_pi_prefix_len)) == NULL) {
+ syslog(LOG_INFO,
+ "<%s> prefix %s/%d from %s on %s is not in our list",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf,
+ sizeof(prefixbuf)),
+ pinfo->nd_opt_pi_prefix_len,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), ifi->ifi_ifname);
+ return (0);
+ }
+
+ preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time);
+ if (pfx->pfx_pltimeexpire) {
+ /*
+ * The lifetime is decremented in real time, so we should
+ * compare the expiration time.
+ * (RFC 2461 Section 6.2.7.)
+ * XXX: can we really expect that all routers on the link
+ * have synchronized clocks?
+ */
+ clock_gettime(CLOCK_MONOTONIC_FAST, &now);
+ preferred_time += now.tv_sec;
+
+ if (!pfx->pfx_timer && rai->rai_clockskew &&
+ udiff(preferred_time, pfx->pfx_pltimeexpire) > rai->rai_clockskew) {
+ syslog(LOG_INFO,
+ "<%s> preferred lifetime for %s/%d"
+ " (decr. in real time) inconsistent on %s:"
+ " %" PRIu32 " from %s, %" PRIu32 " from us",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf,
+ sizeof(prefixbuf)),
+ pinfo->nd_opt_pi_prefix_len,
+ ifi->ifi_ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), pfx->pfx_pltimeexpire);
+ inconsistent++;
+ }
+ } else if (!pfx->pfx_timer && preferred_time != pfx->pfx_preflifetime)
+ syslog(LOG_INFO,
+ "<%s> preferred lifetime for %s/%d"
+ " inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf,
+ sizeof(prefixbuf)),
+ pinfo->nd_opt_pi_prefix_len,
+ ifi->ifi_ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), pfx->pfx_preflifetime);
+
+ valid_time = ntohl(pinfo->nd_opt_pi_valid_time);
+ if (pfx->pfx_vltimeexpire) {
+ clock_gettime(CLOCK_MONOTONIC_FAST, &now);
+ valid_time += now.tv_sec;
+
+ if (!pfx->pfx_timer && rai->rai_clockskew &&
+ udiff(valid_time, pfx->pfx_vltimeexpire) > rai->rai_clockskew) {
+ syslog(LOG_INFO,
+ "<%s> valid lifetime for %s/%d"
+ " (decr. in real time) inconsistent on %s:"
+ " %d from %s, %" PRIu32 " from us",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf,
+ sizeof(prefixbuf)),
+ pinfo->nd_opt_pi_prefix_len,
+ ifi->ifi_ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), pfx->pfx_vltimeexpire);
+ inconsistent++;
+ }
+ } else if (!pfx->pfx_timer && valid_time != pfx->pfx_validlifetime) {
+ syslog(LOG_INFO,
+ "<%s> valid lifetime for %s/%d"
+ " inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf,
+ sizeof(prefixbuf)),
+ pinfo->nd_opt_pi_prefix_len,
+ ifi->ifi_ifname, valid_time,
+ inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), pfx->pfx_validlifetime);
+ inconsistent++;
+ }
+
+ return (inconsistent);
+}
+
+struct prefix *
+find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen)
+{
+ struct prefix *pfx;
+ int bytelen, bitlen;
+ char bitmask;
+
+ TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) {
+ if (plen != pfx->pfx_prefixlen)
+ continue;
+
+ bytelen = plen / 8;
+ bitlen = plen % 8;
+ bitmask = 0xff << (8 - bitlen);
+
+ if (memcmp((void *)prefix, (void *)&pfx->pfx_prefix, bytelen))
+ continue;
+
+ if (bitlen == 0 ||
+ ((prefix->s6_addr[bytelen] & bitmask) ==
+ (pfx->pfx_prefix.s6_addr[bytelen] & bitmask))) {
+ return (pfx);
+ }
+ }
+
+ return (NULL);
+}
+
+/* check if p0/plen0 matches p1/plen1; return 1 if matches, otherwise 0. */
+int
+prefix_match(struct in6_addr *p0, int plen0,
+ struct in6_addr *p1, int plen1)
+{
+ int bytelen, bitlen;
+ char bitmask;
+
+ if (plen0 < plen1)
+ return (0);
+
+ bytelen = plen1 / 8;
+ bitlen = plen1 % 8;
+ bitmask = 0xff << (8 - bitlen);
+
+ if (memcmp((void *)p0, (void *)p1, bytelen))
+ return (0);
+
+ if (bitlen == 0 ||
+ ((p0->s6_addr[bytelen] & bitmask) ==
+ (p1->s6_addr[bytelen] & bitmask))) {
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+nd6_options(struct nd_opt_hdr *hdr, int limit,
+ union nd_opt *ndopts, uint32_t optflags)
+{
+ int optlen = 0;
+
+ for (; limit > 0; limit -= optlen) {
+ if ((size_t)limit < sizeof(struct nd_opt_hdr)) {
+ syslog(LOG_INFO, "<%s> short option header", __func__);
+ goto bad;
+ }
+
+ hdr = (struct nd_opt_hdr *)((caddr_t)hdr + optlen);
+ if (hdr->nd_opt_len == 0) {
+ syslog(LOG_INFO,
+ "<%s> bad ND option length(0) (type = %d)",
+ __func__, hdr->nd_opt_type);
+ goto bad;
+ }
+ optlen = hdr->nd_opt_len << 3;
+ if (optlen > limit) {
+ syslog(LOG_INFO, "<%s> short option", __func__);
+ goto bad;
+ }
+
+ if (hdr->nd_opt_type > ND_OPT_MTU &&
+ hdr->nd_opt_type != ND_OPT_RDNSS &&
+ hdr->nd_opt_type != ND_OPT_DNSSL) {
+ syslog(LOG_INFO, "<%s> unknown ND option(type %d)",
+ __func__, hdr->nd_opt_type);
+ continue;
+ }
+
+ if ((ndopt_flags[hdr->nd_opt_type] & optflags) == 0) {
+ syslog(LOG_INFO, "<%s> unexpected ND option(type %d)",
+ __func__, hdr->nd_opt_type);
+ continue;
+ }
+
+ /*
+ * Option length check. Do it here for all fixed-length
+ * options.
+ */
+ switch (hdr->nd_opt_type) {
+ case ND_OPT_MTU:
+ if (optlen == sizeof(struct nd_opt_mtu))
+ break;
+ goto skip;
+ case ND_OPT_RDNSS:
+ if (optlen >= 24 &&
+ (optlen - sizeof(struct nd_opt_rdnss)) % 16 == 0)
+ break;
+ goto skip;
+ case ND_OPT_DNSSL:
+ if (optlen >= 16 &&
+ (optlen - sizeof(struct nd_opt_dnssl)) % 8 == 0)
+ break;
+ goto skip;
+ case ND_OPT_PREFIX_INFORMATION:
+ if (optlen == sizeof(struct nd_opt_prefix_info))
+ break;
+skip:
+ syslog(LOG_INFO, "<%s> invalid option length",
+ __func__);
+ continue;
+ }
+
+ switch (hdr->nd_opt_type) {
+ case ND_OPT_TARGET_LINKADDR:
+ case ND_OPT_REDIRECTED_HEADER:
+ case ND_OPT_RDNSS:
+ case ND_OPT_DNSSL:
+ break; /* we don't care about these options */
+ case ND_OPT_SOURCE_LINKADDR:
+ case ND_OPT_MTU:
+ if (ndopts->opt_array[hdr->nd_opt_type]) {
+ syslog(LOG_INFO,
+ "<%s> duplicated ND option (type = %d)",
+ __func__, hdr->nd_opt_type);
+ }
+ ndopts->opt_array[hdr->nd_opt_type] = hdr;
+ break;
+ case ND_OPT_PREFIX_INFORMATION:
+ {
+ struct nd_optlist *nol;
+
+ if (ndopts->opt_pi == 0) {
+ ndopts->opt_pi =
+ (struct nd_opt_prefix_info *)hdr;
+ continue;
+ }
+ nol = malloc(sizeof(*nol));
+ if (nol == NULL) {
+ syslog(LOG_ERR, "<%s> can't allocate memory",
+ __func__);
+ goto bad;
+ }
+ nol->nol_opt = hdr;
+ TAILQ_INSERT_TAIL(&(ndopts->opt_list), nol, nol_next);
+
+ break;
+ }
+ default: /* impossible */
+ break;
+ }
+ }
+
+ return (0);
+
+ bad:
+ free_ndopts(ndopts);
+
+ return (-1);
+}
+
+static void
+free_ndopts(union nd_opt *ndopts)
+{
+ struct nd_optlist *nol;
+
+ while ((nol = TAILQ_FIRST(&ndopts->opt_list)) != NULL) {
+ TAILQ_REMOVE(&ndopts->opt_list, nol, nol_next);
+ free(nol);
+ }
+}
+
+void
+sock_open(struct sockinfo *s)
+{
+ struct icmp6_filter filt;
+ int on;
+ /* XXX: should be max MTU attached to the node */
+ static char answer[1500];
+
+ syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+ if (s == NULL) {
+ syslog(LOG_ERR, "<%s> internal error", __func__);
+ exit(1);
+ }
+ rcvcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ rcvcmsgbuf = (char *)malloc(rcvcmsgbuflen);
+ if (rcvcmsgbuf == NULL) {
+ syslog(LOG_ERR, "<%s> not enough core", __func__);
+ exit(1);
+ }
+
+ sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ sndcmsgbuf = (char *)malloc(sndcmsgbuflen);
+ if (sndcmsgbuf == NULL) {
+ syslog(LOG_ERR, "<%s> not enough core", __func__);
+ exit(1);
+ }
+
+ if ((s->si_fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __func__, strerror(errno));
+ exit(1);
+ }
+ /* specify to tell receiving interface */
+ on = 1;
+ if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_RECVPKTINFO: %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+ on = 1;
+ /* specify to tell value of hoplimit field of received IP6 hdr */
+ if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_RECVHOPLIMIT: %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
+ if (mcastif != NULL)
+ ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt);
+
+ if (setsockopt(s->si_fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0) {
+ syslog(LOG_ERR, "<%s> IICMP6_FILTER: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+
+ /* initialize msghdr for receiving packets */
+ rcviov[0].iov_base = (caddr_t)answer;
+ rcviov[0].iov_len = sizeof(answer);
+ rcvmhdr.msg_name = (caddr_t)&rcvfrom;
+ rcvmhdr.msg_namelen = sizeof(rcvfrom);
+ rcvmhdr.msg_iov = rcviov;
+ rcvmhdr.msg_iovlen = 1;
+ rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
+ rcvmhdr.msg_controllen = rcvcmsgbuflen;
+
+ /* initialize msghdr for sending packets */
+ sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmhdr.msg_iov = sndiov;
+ sndmhdr.msg_iovlen = 1;
+ sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
+ sndmhdr.msg_controllen = sndcmsgbuflen;
+
+ return;
+}
+
+/* open a routing socket to watch the routing table */
+static void
+rtsock_open(struct sockinfo *s)
+{
+ if (s == NULL) {
+ syslog(LOG_ERR, "<%s> internal error", __func__);
+ exit(1);
+ }
+ if ((s->si_fd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
+ syslog(LOG_ERR,
+ "<%s> socket: %s", __func__, strerror(errno));
+ exit(1);
+ }
+}
+
+struct ifinfo *
+if_indextoifinfo(int idx)
+{
+ struct ifinfo *ifi;
+ char *name, name0[IFNAMSIZ];
+
+ /* Check if the interface has a valid name or not. */
+ if (if_indextoname(idx, name0) == NULL)
+ return (NULL);
+
+ TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+ if (ifi->ifi_ifindex == idx)
+ return (ifi);
+ }
+
+ if (ifi != NULL)
+ syslog(LOG_DEBUG, "<%s> ifi found (idx=%d)",
+ __func__, idx);
+ else
+ syslog(LOG_DEBUG, "<%s> ifi not found (idx=%d)",
+ __func__, idx);
+
+ return (NULL); /* search failed */
+}
+
+void
+ra_output(struct ifinfo *ifi)
+{
+ int i;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi;
+ struct soliciter *sol;
+ struct rainfo *rai;
+
+ switch (ifi->ifi_state) {
+ case IFI_STATE_CONFIGURED:
+ rai = ifi->ifi_rainfo;
+ break;
+ case IFI_STATE_TRANSITIVE:
+ rai = ifi->ifi_rainfo_trans;
+ break;
+ case IFI_STATE_UNCONFIGURED:
+ syslog(LOG_DEBUG, "<%s> %s is unconfigured. "
+ "Skip sending RAs.",
+ __func__, ifi->ifi_ifname);
+ return;
+ default:
+ rai = NULL;
+ }
+ if (rai == NULL) {
+ syslog(LOG_DEBUG, "<%s> rainfo is NULL on %s."
+ "Skip sending RAs.",
+ __func__, ifi->ifi_ifname);
+ return;
+ }
+ if (!(ifi->ifi_flags & IFF_UP)) {
+ syslog(LOG_DEBUG, "<%s> %s is not up. "
+ "Skip sending RAs.",
+ __func__, ifi->ifi_ifname);
+ return;
+ }
+ /*
+ * Check lifetime, ACCEPT_RTADV flag, and ip6.forwarding.
+ *
+ * (lifetime == 0) = output
+ * (lifetime != 0 && (check_accept_rtadv()) = no output
+ *
+ * Basically, hosts MUST NOT send Router Advertisement
+ * messages at any 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,
+ * we allow hosts to invoke rtadvd only when router lifetime
+ * (on every advertising interface) is explicitly set
+ * zero. (see also the above section)
+ */
+ syslog(LOG_DEBUG,
+ "<%s> check lifetime=%d, ACCEPT_RTADV=%d, ip6.forwarding=%d "
+ "on %s", __func__,
+ rai->rai_lifetime,
+ check_accept_rtadv(ifi->ifi_ifindex),
+ getinet6sysctl(IPV6CTL_FORWARDING),
+ ifi->ifi_ifname);
+
+ if (rai->rai_lifetime != 0) {
+ if (getinet6sysctl(IPV6CTL_FORWARDING) == 0) {
+ syslog(LOG_ERR,
+ "non-zero lifetime RA "
+ "but net.inet6.ip6.forwarding=0. "
+ "Ignored.");
+ return;
+ }
+ if (check_accept_rtadv(ifi->ifi_ifindex)) {
+ syslog(LOG_ERR,
+ "non-zero lifetime RA "
+ "on RA receiving interface %s."
+ " Ignored.", ifi->ifi_ifname);
+ return;
+ }
+ }
+
+ make_packet(rai); /* XXX: inefficient */
+
+ sndmhdr.msg_name = (caddr_t)&sin6_linklocal_allnodes;
+ sndmhdr.msg_iov[0].iov_base = (caddr_t)rai->rai_ra_data;
+ sndmhdr.msg_iov[0].iov_len = rai->rai_ra_datalen;
+
+ cm = CMSG_FIRSTHDR(&sndmhdr);
+ /* specify the outgoing interface */
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/
+ pi->ipi6_ifindex = ifi->ifi_ifindex;
+
+ /* specify the hop limit of the packet */
+ {
+ int hoplimit = 255;
+
+ cm = CMSG_NXTHDR(&sndmhdr, cm);
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_HOPLIMIT;
+ cm->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
+ }
+
+ syslog(LOG_DEBUG,
+ "<%s> send RA on %s, # of RS waitings = %d",
+ __func__, ifi->ifi_ifname, ifi->ifi_rs_waitcount);
+
+ i = sendmsg(sock.si_fd, &sndmhdr, 0);
+
+ if (i < 0 || (size_t)i != rai->rai_ra_datalen) {
+ if (i < 0) {
+ syslog(LOG_ERR, "<%s> sendmsg on %s: %s",
+ __func__, ifi->ifi_ifname,
+ strerror(errno));
+ }
+ }
+
+ /*
+ * unicast advertisements
+ * XXX commented out. reason: though spec does not forbit it, unicast
+ * advert does not really help
+ */
+ while ((sol = TAILQ_FIRST(&rai->rai_soliciter)) != NULL) {
+ TAILQ_REMOVE(&rai->rai_soliciter, sol, sol_next);
+ free(sol);
+ }
+
+ /* update timestamp */
+ clock_gettime(CLOCK_MONOTONIC_FAST, &ifi->ifi_ra_lastsent);
+
+ /* update counter */
+ ifi->ifi_rs_waitcount = 0;
+ ifi->ifi_raoutput++;
+
+ switch (ifi->ifi_state) {
+ case IFI_STATE_CONFIGURED:
+ if (ifi->ifi_burstcount > 0)
+ ifi->ifi_burstcount--;
+ break;
+ case IFI_STATE_TRANSITIVE:
+ ifi->ifi_burstcount--;
+ if (ifi->ifi_burstcount == 0) {
+ if (ifi->ifi_rainfo == ifi->ifi_rainfo_trans) {
+ /* Initial burst finished. */
+ if (ifi->ifi_rainfo_trans != NULL)
+ ifi->ifi_rainfo_trans = NULL;
+ }
+
+ /* Remove burst RA information */
+ if (ifi->ifi_rainfo_trans != NULL) {
+ rm_rainfo(ifi->ifi_rainfo_trans);
+ ifi->ifi_rainfo_trans = NULL;
+ }
+
+ if (ifi->ifi_rainfo != NULL) {
+ /*
+ * TRANSITIVE -> CONFIGURED
+ *
+ * After initial burst or transition from
+ * one configuration to another,
+ * ifi_rainfo always points to the next RA
+ * information.
+ */
+ ifi->ifi_state = IFI_STATE_CONFIGURED;
+ syslog(LOG_DEBUG,
+ "<%s> ifname=%s marked as "
+ "CONFIGURED.", __func__,
+ ifi->ifi_ifname);
+ } else {
+ /*
+ * TRANSITIVE -> UNCONFIGURED
+ *
+ * If ifi_rainfo points to NULL, this
+ * interface is shutting down.
+ *
+ */
+ int error;
+
+ ifi->ifi_state = IFI_STATE_UNCONFIGURED;
+ syslog(LOG_DEBUG,
+ "<%s> ifname=%s marked as "
+ "UNCONFIGURED.", __func__,
+ ifi->ifi_ifname);
+ error = sock_mc_leave(&sock,
+ ifi->ifi_ifindex);
+ if (error)
+ exit(1);
+ }
+ }
+ break;
+ }
+}
+
+/* process RA timer */
+struct rtadvd_timer *
+ra_timeout(void *arg)
+{
+ struct ifinfo *ifi;
+
+ ifi = (struct ifinfo *)arg;
+ syslog(LOG_DEBUG, "<%s> RA timer on %s is expired",
+ __func__, ifi->ifi_ifname);
+
+ ra_output(ifi);
+
+ return (ifi->ifi_ra_timer);
+}
+
+/* update RA timer */
+void
+ra_timer_update(void *arg, struct timespec *tm)
+{
+ uint16_t interval;
+ struct rainfo *rai;
+ struct ifinfo *ifi;
+
+ ifi = (struct ifinfo *)arg;
+ rai = ifi->ifi_rainfo;
+ interval = 0;
+
+ switch (ifi->ifi_state) {
+ case IFI_STATE_UNCONFIGURED:
+ return;
+ break;
+ case IFI_STATE_CONFIGURED:
+ /*
+ * Whenever a multicast advertisement is sent from
+ * an interface, the timer is reset to a
+ * uniformly-distributed random value between the
+ * interface's configured MinRtrAdvInterval and
+ * MaxRtrAdvInterval (RFC4861 6.2.4).
+ */
+ interval = rai->rai_mininterval;
+ interval += arc4random_uniform(rai->rai_maxinterval -
+ rai->rai_mininterval);
+ break;
+ case IFI_STATE_TRANSITIVE:
+ /*
+ * For the first few advertisements (up to
+ * MAX_INITIAL_RTR_ADVERTISEMENTS), if the randomly chosen
+ * interval is greater than
+ * MAX_INITIAL_RTR_ADVERT_INTERVAL, the timer SHOULD be
+ * set to MAX_INITIAL_RTR_ADVERT_INTERVAL instead. (RFC
+ * 4861 6.2.4)
+ *
+ * In such cases, the router SHOULD transmit one or more
+ * (but not more than MAX_FINAL_RTR_ADVERTISEMENTS) final
+ * multicast Router Advertisements on the interface with a
+ * Router Lifetime field of zero. (RFC 4861 6.2.5)
+ */
+ interval = ifi->ifi_burstinterval;
+ break;
+ }
+
+ tm->tv_sec = interval;
+ tm->tv_nsec = 0;
+
+ syslog(LOG_DEBUG,
+ "<%s> RA timer on %s is set to %ld:%ld",
+ __func__, ifi->ifi_ifname,
+ (long int)tm->tv_sec, (long int)tm->tv_nsec / 1000);
+
+ return;
+}
diff --git a/usr.sbin/rtadvd/rtadvd.conf b/usr.sbin/rtadvd/rtadvd.conf
new file mode 100644
index 0000000..1e42c75
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.conf
@@ -0,0 +1,22 @@
+# $FreeBSD$
+# $KAME: rtadvd.conf,v 1.13 2003/06/25 03:45:21 itojun Exp $
+#
+# Note: All of the following parameters have default values defined
+# in specifications, and hence you usually do not have to set them
+# by hand unless you need special non-default values.
+#
+# You even do not need to create the configuration file. rtadvd
+# would usually work well without a configuration file.
+# See also: rtadvd(8)
+
+# per-interface definitions.
+# Mainly IPv6 prefixes are configured in this part. However, rtadvd
+# automatically learns appropriate prefixes from the kernel's routing
+# table, and advertises the prefixes, so you don't have to configure
+# this part, either.
+# If you don't want the automatic advertisement, (uncomment and) configure
+# this part by hand, and then invoke rtadvd with the -s option.
+
+#ef0:\
+# :addr="2001:db8:ffff:1000::":prefixlen#64:\
+# :rdnss="2001:db8:ffff:1000::1":dnssl="example.com":
diff --git a/usr.sbin/rtadvd/rtadvd.conf.5 b/usr.sbin/rtadvd/rtadvd.conf.5
new file mode 100644
index 0000000..d4a0c02
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.conf.5
@@ -0,0 +1,530 @@
+.\" $KAME: rtadvd.conf.5,v 1.50 2005/01/14 05:30:59 jinmei Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 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:
+.\" 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.
+.\" 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
+.\" 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$
+.\"
+.Dd June 4, 2011
+.Dt RTADVD.CONF 5
+.Os
+.Sh NAME
+.Nm rtadvd.conf
+.Nd config file for router advertisement daemon
+.Sh DESCRIPTION
+This file describes how the router advertisement packets must be constructed
+for each of the interfaces.
+.Pp
+As described in
+.Xr rtadvd 8 ,
+you do not have to set this configuration file up at all,
+unless you need some special configurations.
+You may even omit the file as a whole.
+In such cases, the
+.Nm rtadvd
+daemon will automatically configure itself using default values
+specified in the specification.
+.Pp
+It obeys the famous
+.Xr termcap 5
+file format.
+Each line in the file describes a network interface.
+Fields are separated by a colon
+.Pq Sq \&: ,
+and each field contains one capability description.
+Lines may be concatenated by the
+.Sq \e
+character.
+The comment marker is the
+.Sq \&#
+character.
+.Sh CAPABILITIES
+Capabilities describe the value to be filled into ICMPv6 router
+advertisement messages and to control
+.Xr rtadvd 8
+behavior.
+Therefore, you are encouraged to read IETF neighbor discovery documents
+if you would like to modify the sample configuration file.
+.Pp
+Note that almost all items have default values.
+If you omit an item, the default value of the item will be used.
+.Pp
+There are two items which control the interval of sending router advertisements.
+These items can be omitted, then
+.Nm rtadvd
+will use the default values.
+.Bl -tag -width indent
+.It Cm \&maxinterval
+(num) The maximum time allowed between sending unsolicited
+multicast router advertisements
+.Pq unit: seconds .
+The default value is 600.
+Its value must be no less than 4 seconds
+and no greater than 1800 seconds.
+.It Cm \&mininterval
+(num) The minimum time allowed between sending unsolicited multicast
+router advertisements
+.Pq unit: seconds .
+The default value is the one third of value of
+.Cm maxinterval .
+Its value must be no less than 3 seconds and no greater than .75 *
+the value of
+.Cm maxinterval .
+.El
+.Pp
+The following items are for ICMPv6 router advertisement message
+header.
+These items can be omitted, then
+.Nm rtadvd
+will use the default values.
+.Bl -tag -width indent
+.It Cm \&chlim
+(num) The value for Cur Hop Limit field.
+The default value is 64.
+.It Cm \&raflags
+(str or num) A 8-bit flags field in router advertisement message header.
+This field can be specified either as a case-sensitive string or as an
+integer.
+A string consists of characters each of which corresponds to a
+particular flag bit(s).
+An integer should be the logical OR of all enabled bits.
+Bit 7
+.Po
+.Li 'm' or 0x80
+.Pc
+means Managed address configuration flag bit,
+and Bit 6
+.Po
+.Li 'o' or 0x40
+.Pc
+means Other stateful configuration flag bit.
+Bit 4
+.Po
+.Li 0x10
+.Pc
+and Bit 3
+.Po
+.Li 0x08
+.Pc
+are used to encode router preference.
+Bits 01
+.Po
+or 'h'
+.Pc
+means high, 00 means medium, and 11
+.Po
+or 'l'
+.Pc
+means low.
+Bits 10 is reserved, and must not be specified.
+There is no character to specify the medium preference explicitly.
+The default value of the entire flag is 0
+.Po
+or a null string,
+.Pc
+which means no additional
+configuration methods, and the medium router preference.
+.It Cm \&rltime
+(num) Router lifetime field
+.Pq unit: seconds .
+The value must be either zero or between
+the value of
+.Cm maxinterval
+and 9000.
+When
+.Nm rtadvd
+runs on a host, this value must explicitly set 0 on all the
+advertising interfaces as described in
+.Xr rtadvd 8 .
+The default value is 1800.
+.It Cm \&rtime
+(num) Reachable time field
+.Pq unit: milliseconds .
+The default value is 0, which means unspecified by this router.
+.It Cm \&retrans
+(num) Retrans Timer field
+.Pq unit: milliseconds .
+The default value is 0, which means unspecified by this router.
+.El
+.Pp
+The following items are for ICMPv6 prefix information option,
+which will be attached to router advertisement header.
+These items can be omitted, then
+.Nm rtadvd
+will automatically get appropriate prefixes from the kernel's routing table,
+and advertise the prefixes with the default parameters.
+Keywords other than
+.Cm clockskew
+and
+.Cm noifprefix
+can be augmented with a number, like
+.Dq Li prefix2 ,
+to specify multiple prefixes.
+.Bl -tag -width indent
+.It Cm \&noifprefix
+(bool) Specifies no prefix on the network interfaces will be advertised.
+By default
+.Nm rtadvd
+automatically gathers on-link prefixes from all of the network interfaces
+and advertise them.
+The
+.Cm noifprefix
+disables that behavior.
+If this is specified and no
+.Cm addr
+keyword is specified, no prefix information option will be included in the
+message.
+.It Cm \&clockskew
+(num) Time skew to adjust link propagation delays and clock skews
+between routers on the link
+.Pq unit: seconds .
+This value is used in consistency check for locally-configured and
+advertised prefix lifetimes, and has its meaning when the local router
+configures a prefix on the link with a lifetime that decrements in
+real time.
+If the value is 0, it means the consistency check will be skipped
+for such prefixes.
+The default value is 0.
+.It Cm \&prefixlen
+(num) Prefix length field.
+The default value is 64.
+.It Cm \&pinfoflags
+(str or num) A 8-bit flags field in prefix information option.
+This field can be specified either as a case-sensitive string or as an
+integer.
+A string consists of characters each of which corresponds to a
+particular flag bit(s).
+An integer should be the logical OR of all enabled bits.
+Bit 7
+.Po
+.Li 'l' or 0x80
+.Pc
+means On-link flag bit,
+and Bit 6
+.Po
+.Li 'a' or 0x40
+.Pc
+means Autonomous address-configuration flag bit.
+The default value is "la" or 0xc0, i.e., both bits are set.
+.It Cm \&addr
+(str) The address filled into Prefix field.
+Since
+.Dq \&:
+is used for
+.Xr termcap 5
+file format as well as IPv6 numeric address, the field MUST be quoted by
+doublequote character.
+.It Cm \&vltime
+(num) Valid lifetime field
+.Pq unit: seconds .
+The default value is 2592000 (30 days).
+.It Cm \&vltimedecr
+(bool) This item means the advertised valid lifetime will decrement
+in real time, which is disabled by default.
+.It Cm \&pltime
+(num) Preferred lifetime field
+.Pq unit: seconds .
+The default value is 604800 (7 days).
+.It Cm \&pltimedecr
+(bool) This item means the advertised preferred lifetime will decrement
+in real time, which is disabled by default.
+.El
+.Pp
+The following item is for ICMPv6 MTU option,
+which will be attached to router advertisement header.
+This item can be omitted, then
+.Nm rtadvd
+will use the default value.
+.Bl -tag -width indent
+.It Cm \&mtu
+(num or str) MTU (maximum transmission unit) field.
+If 0 is specified, it means that the option will not be included.
+The default value is 0.
+If the special string
+.Dq auto
+is specified for this item, MTU option will be included and its value
+will be set to the interface MTU automatically.
+.El
+.Pp
+The following item controls ICMPv6 source link-layer address option,
+which will be attached to router advertisement header.
+As noted above, you can just omit the item, then
+.Nm rtadvd
+will use the default value.
+.Bl -tag -width indent
+.It Cm \&nolladdr
+(bool) By default
+.Po
+if
+.Cm \&nolladdr
+is not specified
+.Pc ,
+.Xr rtadvd 8
+will try to get link-layer address for the interface from the kernel,
+and attach that in source link-layer address option.
+If this capability exists,
+.Xr rtadvd 8
+will not attach source link-layer address option to
+router advertisement packets.
+.El
+.Pp
+The following item controls ICMPv6 home agent information option,
+which was defined with mobile IPv6 support.
+It will be attached to router advertisement header just like other options do.
+.Bl -tag -width indent
+.It Cm \&hapref
+(num) Specifies home agent preference.
+If set to non-zero,
+.Cm \&hatime
+must be present as well.
+.It Cm \&hatime
+(num) Specifies home agent lifetime.
+.El
+.Pp
+When mobile IPv6 support is turned on for
+.Xr rtadvd 8 ,
+advertisement interval option will be attached to router advertisement
+packet, by configuring
+.Cm \&maxinterval
+explicitly.
+.Pp
+The following items are for ICMPv6 route information option,
+which will be attached to router advertisement header.
+These items are optional.
+Each items can be augmented with number, like
+.Dq Li rtplen2 ,
+to specify multiple routes.
+.Bl -tag -width indent
+.It Cm \&rtprefix
+(str) The prefix filled into the Prefix field of route information option.
+Since
+.Dq \&:
+is used for
+.Xr termcap 5
+file format as well as IPv6 numeric address, the field MUST be quoted by
+doublequote character.
+.It Cm \&rtplen
+(num) Prefix length field in route information option.
+The default value is 64.
+.It Cm \&rtflags
+(str or num) A 8-bit flags field in route information option.
+Currently only the preference values are defined.
+The notation is same as that of the raflags field.
+Bit 4
+.Po
+.Li 0x10
+.Pc
+and
+Bit 3
+.Po
+.Li 0x08
+.Pc
+are used to encode the route preference for the route.
+The default value is 0x00, i.e., medium preference.
+.It Cm \&rtltime
+(num) route lifetime field in route information option.
+.Pq unit: seconds .
+Since the specification does not define the default value of this
+item, the value for this item should be specified by hand.
+However,
+.Nm rtadvd
+allows this item to be unspecified, and uses the router lifetime
+as the default value in such a case, just for compatibility with an
+old version of the program.
+.El
+.Pp
+In the above list, each keyword beginning with
+.Dq Li rt
+could be replaced with the one beginning with
+.Dq Li rtr
+for backward compatibility reason.
+For example,
+.Cm rtrplen
+is accepted instead of
+.Cm rtplen .
+However, keywords that start with
+.Dq Li rtr
+have basically been obsoleted, and should not be used any more.
+.Pp
+The following items are for ICMPv6 Recursive DNS Server Option and
+DNS Search List Option
+.Pq RFC 6106 ,
+which will be attached to router advertisement header.
+These items are optional.
+.Bl -tag -width indent
+.It Cm \&rdnss
+(str) The IPv6 address of one or more recursive DNS servers.
+The argument must be inside double quotes.
+Multiple DNS servers can be specified in a comma-separated string.
+If different lifetimes are needed for different servers,
+separate entries can be given by using
+.Cm rdnss ,
+.Cm rdnss0 ,
+.Cm rdnss1 ,
+.Cm rdnss2 ...
+options with corresponding
+.Cm rdnssltime ,
+.Cm rdnssltime0 ,
+.Cm rdnssltime1 ,
+.Cm rdnssltime2 ...
+entries.
+Note that the maximum number of servers depends on the receiver side.
+See also
+.Xr resolver 5
+manual page for resolver implementation in
+.Fx .
+.It Cm \&rdnssltime
+The lifetime of the
+.Cm rdnss
+DNS server entries.
+The default value is 3/2 of the interval time.
+.It Cm \&dnssl
+(str) One or more domain names in a comma-separated string.
+These domain names will be used when making DNS queries on a
+non-fully-qualified domain name.
+If different lifetimes are needed for different domains, separate entries
+can be given by using
+.Cm dnssl ,
+.Cm dnssl0 ,
+.Cm dnssl1 ,
+.Cm dnssl2 ...
+options with corresponding
+.Cm dnsslltime ,
+.Cm dnsslltime0 ,
+.Cm dnsslltime1 ,
+.Cm dnsslltime2 ...
+entries.
+Note that the maximum number of names depends on the receiver side.
+See also
+.Xr resolver 5
+manual page for resolver implementation in
+.Fx .
+.It Cm \&dnsslltime
+The lifetime of the
+.Cm dnssl
+DNS search list entries.
+The default value is 3/2 of the interval time.
+.El
+.Pp
+You can also refer one line from another by using
+.Cm tc
+capability.
+See
+.Xr termcap 5
+for details on the capability.
+.Sh EXAMPLES
+As presented above, all of the advertised parameters have default values
+defined in specifications, and hence you usually do not have to set them
+by hand, unless you need special non-default values.
+It can cause interoperability problem if you use an ill-configured
+parameter.
+.Pp
+To override a configuration parameter, you can specify the parameter alone.
+With the following configuration,
+.Xr rtadvd 8
+overrides the router lifetime parameter for the
+.Li ne0
+interface.
+.Bd -literal -offset indent
+ne0:\\
+ :rltime#0:
+.Ed
+.Pp
+The following example manually configures prefixes advertised from the
+.Li ef0
+interface.
+The configuration must be used with the
+.Fl s
+option to
+.Xr rtadvd 8 .
+.Bd -literal -offset indent
+ef0:\\
+ :addr="2001:db8:ffff:1000::":prefixlen#64:
+.Ed
+.Pp
+The following example configures the
+.Li wlan0
+interface and adds two DNS servers and a DNS domain search options
+using the default option lifetime values.
+.Bd -literal -offset indent
+wlan0:\\
+ :addr="2001:db8:ffff:1000::":prefixlen#64:\\
+ :rdnss="2001:db8:ffff::10,2001:db8:ffff::2:43":\\
+ :dnssl="example.com":
+.Ed
+.Pp
+The following example presents the default values in an explicit manner.
+The configuration is provided just for reference purposes;
+YOU DO NOT NEED TO HAVE IT AT ALL.
+.Bd -literal -offset indent
+default:\\
+ :chlim#64:raflags#0:rltime#1800:rtime#0:retrans#0:\\
+ :pinfoflags="la":vltime#2592000:pltime#604800:mtu#0:
+ef0:\\
+ :addr="2001:db8:ffff:1000::":prefixlen#64:tc=default:
+.Ed
+.Sh SEE ALSO
+.Xr resolver 5 ,
+.Xr termcap 5 ,
+.Xr rtadvd 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
+.Xr rtadvd 8
+and the configuration file
+.Nm
+first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h
new file mode 100644
index 0000000..e7ed87f
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.h
@@ -0,0 +1,298 @@
+/* $FreeBSD$ */
+/* $KAME: rtadvd.h,v 1.26 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:
+ * 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.
+ * 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
+ * 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.
+ */
+
+#define ELM_MALLOC(p,error_action) \
+ do { \
+ p = malloc(sizeof(*p)); \
+ if (p == NULL) { \
+ syslog(LOG_ERR, "<%s> malloc failed: %s", \
+ __func__, strerror(errno)); \
+ error_action; \
+ } \
+ memset(p, 0, sizeof(*p)); \
+ } while(0)
+
+#define IN6ADDR_LINKLOCAL_ALLNODES_INIT \
+ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}}
+
+#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \
+ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}}
+
+#define IN6ADDR_SITELOCAL_ALLROUTERS_INIT \
+ {{{ 0xff, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}}
+
+extern struct sockaddr_in6 sin6_linklocal_allnodes;
+extern struct sockaddr_in6 sin6_linklocal_allrouters;
+extern struct sockaddr_in6 sin6_sitelocal_allrouters;
+
+/*
+ * RFC 3542 API deprecates IPV6_PKTINFO in favor of
+ * IPV6_RECVPKTINFO
+ */
+#ifndef IPV6_RECVPKTINFO
+#ifdef IPV6_PKTINFO
+#define IPV6_RECVPKTINFO IPV6_PKTINFO
+#endif
+#endif
+
+/*
+ * RFC 3542 API deprecates IPV6_HOPLIMIT in favor of
+ * IPV6_RECVHOPLIMIT
+ */
+#ifndef IPV6_RECVHOPLIMIT
+#ifdef IPV6_HOPLIMIT
+#define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT
+#endif
+#endif
+
+/* protocol constants and default values */
+#define DEF_MAXRTRADVINTERVAL 600
+#define DEF_ADVLINKMTU 0
+#define DEF_ADVREACHABLETIME 0
+#define DEF_ADVRETRANSTIMER 0
+#define DEF_ADVCURHOPLIMIT 64
+#define DEF_ADVVALIDLIFETIME 2592000
+#define DEF_ADVPREFERREDLIFETIME 604800
+
+#define MAXROUTERLIFETIME 9000
+#define MIN_MAXINTERVAL 4
+#define MAX_MAXINTERVAL 1800
+#define MIN_MININTERVAL 3
+#define MAXREACHABLETIME 3600000
+
+#define MAX_INITIAL_RTR_ADVERT_INTERVAL 16
+#define MAX_INITIAL_RTR_ADVERTISEMENTS 3
+#define MAX_FINAL_RTR_ADVERTISEMENTS 3
+#define MIN_DELAY_BETWEEN_RAS 3
+#define MAX_RA_DELAY_TIME 500000 /* usec */
+
+#define PREFIX_FROM_KERNEL 1
+#define PREFIX_FROM_CONFIG 2
+#define PREFIX_FROM_DYNAMIC 3
+
+struct prefix {
+ TAILQ_ENTRY(prefix) pfx_next;
+
+ struct rainfo *pfx_rainfo; /* back pointer to the interface */
+ /*
+ * Expiration timer. This is used when a prefix derived from
+ * the kernel is deleted.
+ */
+ struct rtadvd_timer *pfx_timer;
+
+ uint32_t pfx_validlifetime; /* AdvValidLifetime */
+ uint32_t pfx_vltimeexpire; /* Expiration of vltime */
+ uint32_t pfx_preflifetime; /* AdvPreferredLifetime */
+ uint32_t pfx_pltimeexpire; /* Expiration of pltime */
+ int pfx_onlinkflg; /* bool: AdvOnLinkFlag */
+ int pfx_autoconfflg; /* bool: AdvAutonomousFlag */
+ int pfx_prefixlen;
+ int pfx_origin; /* From kernel or config */
+
+ struct in6_addr pfx_prefix;
+};
+
+struct rtinfo {
+ TAILQ_ENTRY(rtinfo) rti_next;
+
+ uint32_t rti_ltime; /* route lifetime */
+ int rti_rtpref; /* route preference */
+ int rti_prefixlen;
+ struct in6_addr rti_prefix;
+};
+
+struct rdnss_addr {
+ TAILQ_ENTRY(rdnss_addr) ra_next;
+
+ struct in6_addr ra_dns; /* DNS server entry */
+};
+
+struct rdnss {
+ TAILQ_ENTRY(rdnss) rd_next;
+
+ TAILQ_HEAD(, rdnss_addr) rd_list; /* list of DNS servers */
+ uint32_t rd_ltime; /* number of seconds valid */
+};
+
+/*
+ * The maximum length of a domain name in a DNS search list is calculated
+ * by a domain name + length fields per 63 octets + a zero octet at
+ * the tail and adding 8 octet boundary padding.
+ */
+#define _DNAME_LABELENC_MAXLEN \
+ (NI_MAXHOST + (NI_MAXHOST / 64 + 1) + 1)
+
+#define DNAME_LABELENC_MAXLEN \
+ (_DNAME_LABELENC_MAXLEN + 8 - _DNAME_LABELENC_MAXLEN % 8)
+
+struct dnssl_addr {
+ TAILQ_ENTRY(dnssl_addr) da_next;
+
+ int da_len; /* length of entry */
+ char da_dom[DNAME_LABELENC_MAXLEN]; /* search domain name entry */
+};
+
+struct dnssl {
+ TAILQ_ENTRY(dnssl) dn_next;
+
+ TAILQ_HEAD(, dnssl_addr) dn_list; /* list of search domains */
+ uint32_t dn_ltime; /* number of seconds valid */
+};
+
+struct soliciter {
+ TAILQ_ENTRY(soliciter) sol_next;
+
+ struct sockaddr_in6 sol_addr;
+};
+
+struct rainfo {
+ /* pointer for list */
+ TAILQ_ENTRY(rainfo) rai_next;
+
+ /* interface information */
+ struct ifinfo *rai_ifinfo;
+
+ int rai_advlinkopt; /* bool: whether include link-layer addr opt */
+ int rai_advifprefix; /* bool: gather IF prefixes? */
+
+ /* Router configuration variables */
+ uint16_t rai_lifetime; /* AdvDefaultLifetime */
+ uint16_t rai_maxinterval; /* MaxRtrAdvInterval */
+ uint16_t rai_mininterval; /* MinRtrAdvInterval */
+ int rai_managedflg; /* AdvManagedFlag */
+ int rai_otherflg; /* AdvOtherConfigFlag */
+
+ int rai_rtpref; /* router preference */
+ uint32_t rai_linkmtu; /* AdvLinkMTU */
+ uint32_t rai_reachabletime; /* AdvReachableTime */
+ uint32_t rai_retranstimer; /* AdvRetransTimer */
+ uint8_t rai_hoplimit; /* AdvCurHopLimit */
+
+ TAILQ_HEAD(, prefix) rai_prefix;/* AdvPrefixList(link head) */
+ int rai_pfxs; /* number of prefixes */
+
+ uint16_t rai_clockskew; /* used for consisitency check of lifetimes */
+
+ TAILQ_HEAD(, rdnss) rai_rdnss; /* DNS server list */
+ TAILQ_HEAD(, dnssl) rai_dnssl; /* search domain list */
+ TAILQ_HEAD(, rtinfo) rai_route; /* route information option (link head) */
+ int rai_routes; /* number of route information options */
+ /* actual RA packet data and its length */
+ size_t rai_ra_datalen;
+ char *rai_ra_data;
+
+ /* info about soliciter */
+ TAILQ_HEAD(, soliciter) rai_soliciter; /* recent solication source */
+};
+
+/* RA information list */
+extern TAILQ_HEAD(railist_head_t, rainfo) railist;
+
+/*
+ * ifi_state:
+ *
+ * (INIT)
+ * |
+ * | update_ifinfo()
+ * | update_persist_ifinfo()
+ * v
+ * UNCONFIGURED
+ * | ^
+ * loadconfig()| |rm_ifinfo(), ra_output()
+ * (MC join)| |(MC leave)
+ * | |
+ * | |
+ * v |
+ * TRANSITIVE
+ * | ^
+ * ra_output()| |getconfig()
+ * | |
+ * | |
+ * | |
+ * v |
+ * CONFIGURED
+ *
+ *
+ */
+#define IFI_STATE_UNCONFIGURED 0
+#define IFI_STATE_CONFIGURED 1
+#define IFI_STATE_TRANSITIVE 2
+
+struct ifinfo {
+ TAILQ_ENTRY(ifinfo) ifi_next;
+
+ uint16_t ifi_state;
+ uint16_t ifi_persist;
+ uint16_t ifi_ifindex;
+ char ifi_ifname[IFNAMSIZ];
+ uint8_t ifi_type;
+ uint16_t ifi_flags;
+ uint32_t ifi_nd_flags;
+ uint32_t ifi_phymtu;
+ struct sockaddr_dl ifi_sdl;
+
+ struct rainfo *ifi_rainfo;
+ struct rainfo *ifi_rainfo_trans;
+ uint16_t ifi_burstcount;
+ uint32_t ifi_burstinterval;
+ struct rtadvd_timer *ifi_ra_timer;
+ /* timestamp when the latest RA was sent */
+ struct timespec ifi_ra_lastsent;
+ uint16_t ifi_rs_waitcount;
+
+ /* statistics */
+ uint64_t ifi_raoutput; /* # of RAs sent */
+ uint64_t ifi_rainput; /* # of RAs received */
+ uint64_t ifi_rainconsistent; /* # of inconsistent recv'd RAs */
+ uint64_t ifi_rsinput; /* # of RSs received */
+};
+
+/* Interface list */
+extern TAILQ_HEAD(ifilist_head_t, ifinfo) ifilist;
+
+extern char *mcastif;
+
+struct rtadvd_timer *ra_timeout(void *);
+void ra_timer_update(void *, struct timespec *);
+void ra_output(struct ifinfo *);
+
+int prefix_match(struct in6_addr *, int,
+ struct in6_addr *, int);
+struct ifinfo *if_indextoifinfo(int);
+struct prefix *find_prefix(struct rainfo *,
+ struct in6_addr *, int);
+void rtadvd_set_reload(int);
+void rtadvd_set_shutdown(int);
diff --git a/usr.sbin/rtadvd/timer.c b/usr.sbin/rtadvd/timer.c
new file mode 100644
index 0000000..452add4
--- /dev/null
+++ b/usr.sbin/rtadvd/timer.c
@@ -0,0 +1,199 @@
+/* $FreeBSD$ */
+/* $KAME: timer.c,v 1.9 2002/06/10 19:59:47 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:
+ * 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.
+ * 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
+ * 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.
+ */
+
+#include <sys/queue.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+
+#include <unistd.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <search.h>
+#include <time.h>
+#include <netdb.h>
+
+#include "rtadvd.h"
+#include "timer_subr.h"
+#include "timer.h"
+
+struct rtadvd_timer_head_t ra_timer =
+ TAILQ_HEAD_INITIALIZER(ra_timer);
+static struct timespec tm_limit;
+static struct timespec tm_max;
+
+void
+rtadvd_timer_init(void)
+{
+ /* Generate maximum time in timespec. */
+ tm_limit.tv_sec = (-1) & ~((time_t)1 << ((sizeof(tm_max.tv_sec) * 8) - 1));
+ tm_limit.tv_nsec = (-1) & ~((long)1 << ((sizeof(tm_max.tv_nsec) * 8) - 1));
+ tm_max = tm_limit;
+ TAILQ_INIT(&ra_timer);
+}
+
+void
+rtadvd_update_timeout_handler(void)
+{
+ struct ifinfo *ifi;
+
+ TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+ switch (ifi->ifi_state) {
+ case IFI_STATE_CONFIGURED:
+ case IFI_STATE_TRANSITIVE:
+ if (ifi->ifi_ra_timer != NULL)
+ continue;
+
+ syslog(LOG_DEBUG, "<%s> add timer for %s (idx=%d)",
+ __func__, ifi->ifi_ifname, ifi->ifi_ifindex);
+ ifi->ifi_ra_timer = rtadvd_add_timer(ra_timeout,
+ ra_timer_update, ifi, ifi);
+ ra_timer_update((void *)ifi,
+ &ifi->ifi_ra_timer->rat_tm);
+ rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm,
+ ifi->ifi_ra_timer);
+ break;
+ case IFI_STATE_UNCONFIGURED:
+ if (ifi->ifi_ra_timer == NULL)
+ continue;
+
+ syslog(LOG_DEBUG,
+ "<%s> remove timer for %s (idx=%d)", __func__,
+ ifi->ifi_ifname, ifi->ifi_ifindex);
+ rtadvd_remove_timer(ifi->ifi_ra_timer);
+ ifi->ifi_ra_timer = NULL;
+ break;
+ }
+ }
+
+ return;
+}
+
+struct rtadvd_timer *
+rtadvd_add_timer(struct rtadvd_timer *(*timeout)(void *),
+ void (*update)(void *, struct timespec *),
+ void *timeodata, void *updatedata)
+{
+ struct rtadvd_timer *rat;
+
+ if (timeout == NULL) {
+ syslog(LOG_ERR,
+ "<%s> timeout function unspecified", __func__);
+ exit(1);
+ }
+
+ rat = malloc(sizeof(*rat));
+ if (rat == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't allocate memory", __func__);
+ exit(1);
+ }
+ memset(rat, 0, sizeof(*rat));
+
+ rat->rat_expire = timeout;
+ rat->rat_update = update;
+ rat->rat_expire_data = timeodata;
+ rat->rat_update_data = updatedata;
+ rat->rat_tm = tm_max;
+
+ /* link into chain */
+ TAILQ_INSERT_TAIL(&ra_timer, rat, rat_next);
+
+ return (rat);
+}
+
+void
+rtadvd_remove_timer(struct rtadvd_timer *rat)
+{
+
+ if (rat == NULL)
+ return;
+
+ TAILQ_REMOVE(&ra_timer, rat, rat_next);
+ free(rat);
+}
+
+/*
+ * Check expiration for each timer. If a timer expires,
+ * call the expire function for the timer and update the timer.
+ * Return the next interval for select() call.
+ */
+struct timespec *
+rtadvd_check_timer(void)
+{
+ static struct timespec returnval;
+ struct timespec now;
+ struct rtadvd_timer *rat;
+
+ clock_gettime(CLOCK_MONOTONIC_FAST, &now);
+ tm_max = tm_limit;
+ TAILQ_FOREACH(rat, &ra_timer, rat_next) {
+ if (TS_CMP(&rat->rat_tm, &now, <=)) {
+ if (((*rat->rat_expire)(rat->rat_expire_data) == NULL))
+ continue; /* the timer was removed */
+ if (rat->rat_update)
+ (*rat->rat_update)(rat->rat_update_data, &rat->rat_tm);
+ TS_ADD(&rat->rat_tm, &now, &rat->rat_tm);
+ }
+ if (TS_CMP(&rat->rat_tm, &tm_max, <))
+ tm_max = rat->rat_tm;
+ }
+ if (TS_CMP(&tm_max, &tm_limit, ==)) {
+ /* no need to timeout */
+ return (NULL);
+ } else if (TS_CMP(&tm_max, &now, <)) {
+ /* this may occur when the interval is too small */
+ returnval.tv_sec = returnval.tv_nsec = 0;
+ } else
+ TS_SUB(&tm_max, &now, &returnval);
+ return (&returnval);
+}
+
+void
+rtadvd_set_timer(struct timespec *tm, struct rtadvd_timer *rat)
+{
+ struct timespec now;
+
+ /* reset the timer */
+ clock_gettime(CLOCK_MONOTONIC_FAST, &now);
+ TS_ADD(&now, tm, &rat->rat_tm);
+
+ /* update the next expiration time */
+ if (TS_CMP(&rat->rat_tm, &tm_max, <))
+ tm_max = rat->rat_tm;
+
+ return;
+}
diff --git a/usr.sbin/rtadvd/timer.h b/usr.sbin/rtadvd/timer.h
new file mode 100644
index 0000000..4498aaf
--- /dev/null
+++ b/usr.sbin/rtadvd/timer.h
@@ -0,0 +1,52 @@
+/* $FreeBSD$ */
+/* $KAME: timer.h,v 1.5 2002/05/31 13:30:38 jinmei 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:
+ * 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.
+ * 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
+ * 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.
+ */
+
+extern TAILQ_HEAD(rtadvd_timer_head_t, rtadvd_timer) ra_timer;
+struct rtadvd_timer {
+ TAILQ_ENTRY(rtadvd_timer) rat_next;
+
+ struct rainfo *rat_rai;
+ struct timespec rat_tm;
+ struct rtadvd_timer *(*rat_expire)(void *);
+ void *rat_expire_data;
+ void (*rat_update)(void *, struct timespec *);
+ void *rat_update_data;
+};
+
+void rtadvd_timer_init(void);
+void rtadvd_update_timeout_handler(void);
+struct rtadvd_timer *rtadvd_add_timer(struct rtadvd_timer *(*)(void *),
+ void (*)(void *, struct timespec *), void *, void *);
+void rtadvd_set_timer(struct timespec *,
+ struct rtadvd_timer *);
+void rtadvd_remove_timer(struct rtadvd_timer *);
+struct timespec *rtadvd_check_timer(void);
diff --git a/usr.sbin/rtadvd/timer_subr.c b/usr.sbin/rtadvd/timer_subr.c
new file mode 100644
index 0000000..0ddf0a4
--- /dev/null
+++ b/usr.sbin/rtadvd/timer_subr.c
@@ -0,0 +1,91 @@
+/* $FreeBSD$ */
+/* $KAME: timer.c,v 1.9 2002/06/10 19:59:47 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:
+ * 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.
+ * 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
+ * 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.
+ */
+
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <time.h>
+
+#include "timer.h"
+#include "timer_subr.h"
+
+struct timespec *
+rtadvd_timer_rest(struct rtadvd_timer *rat)
+{
+ static struct timespec returnval, now;
+
+ clock_gettime(CLOCK_MONOTONIC_FAST, &now);
+ if (TS_CMP(&rat->rat_tm, &now, <=)) {
+ syslog(LOG_DEBUG,
+ "<%s> a timer must be expired, but not yet",
+ __func__);
+ returnval.tv_sec = returnval.tv_nsec = 0;
+ }
+ else
+ TS_SUB(&rat->rat_tm, &now, &returnval);
+
+ return (&returnval);
+}
+
+char *
+sec2str(uint32_t s, char *buf)
+{
+ uint32_t day;
+ uint32_t hour;
+ uint32_t min;
+ uint32_t sec;
+ char *p;
+
+ min = s / 60;
+ sec = s % 60;
+
+ hour = min / 60;
+ min = min % 60;
+
+ day = hour / 24;
+ hour = hour % 24;
+
+ p = buf;
+ if (day > 0)
+ p += sprintf(p, "%" PRIu32 "d", day);
+ if (hour > 0)
+ p += sprintf(p, "%" PRIu32 "h", hour);
+ if (min > 0)
+ p += sprintf(p, "%" PRIu32 "m", min);
+
+ if ((p == buf) || (sec > 0 && p > buf))
+ sprintf(p, "%" PRIu32 "s", sec);
+
+ return (buf);
+}
diff --git a/usr.sbin/rtadvd/timer_subr.h b/usr.sbin/rtadvd/timer_subr.h
new file mode 100644
index 0000000..32e1bb1
--- /dev/null
+++ b/usr.sbin/rtadvd/timer_subr.h
@@ -0,0 +1,59 @@
+/* $FreeBSD$ */
+/* $KAME: timer.h,v 1.5 2002/05/31 13:30:38 jinmei 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:
+ * 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.
+ * 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
+ * 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.
+ */
+
+#define SSBUFLEN 1024
+
+#define TS_CMP(tsp, usp, cmp) \
+ (((tsp)->tv_sec == (usp)->tv_sec) ? \
+ ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \
+ ((tsp)->tv_sec cmp (usp)->tv_sec))
+#define TS_ADD(tsp, usp, vsp) \
+ do { \
+ (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \
+ (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \
+ if ((vsp)->tv_nsec >= 1000000000L) { \
+ (vsp)->tv_sec++; \
+ (vsp)->tv_nsec -= 1000000000L; \
+ } \
+ } while (0)
+#define TS_SUB(tsp, usp, vsp) \
+ do { \
+ (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
+ (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
+ if ((vsp)->tv_nsec < 0) { \
+ (vsp)->tv_sec--; \
+ (vsp)->tv_nsec += 1000000000L; \
+ } \
+ } while (0)
+
+struct timespec *rtadvd_timer_rest(struct rtadvd_timer *);
+char *sec2str(uint32_t, char *buf);
OpenPOWER on IntegriCloud