./etc/rc.d/npf minix-base
./etc/rc.d/pwcheck minix-base
./etc/rc.d/root minix-base
+./etc/rc.d/rtadvd minix-base
./etc/rc.d/sysctl minix-base
./etc/rc.d/sysdb minix-base
./etc/rc.d/syslogd minix-base
./usr/sbin/rdate minix-base
./usr/sbin/rndc-confgen minix-base
./usr/sbin/rndc minix-base
+./usr/sbin/rtadvd minix-base use_inet6
./usr/sbin/service minix-base
./usr/sbin/services_mkdb minix-base
./usr/sbin/syslogd minix-base
./usr/share/examples/openssl/CA.pl minix-base crypto
./usr/share/examples/openssl/CA.sh minix-base crypto
./usr/share/examples/openssl/openssl.cnf minix-base crypto
+./usr/share/examples/rtadvd minix-base
+./usr/share/examples/rtadvd/rtadvd.conf minix-base use_inet6
./usr/share/examples/tmux minix-base
./usr/share/examples/tmux/screen-keys.conf minix-base
./usr/share/i18n minix-base nls
./var/chroot/named/var/run/lwresd minix-base
./var/chroot/named/var/run/named minix-base
./var/chroot/named/var/tmp minix-base
+./var/chroot/rtadvd minix-base
+./var/chroot/rtadvd/etc minix-base
+./var/chroot/rtadvd/var minix-base
+./var/chroot/rtadvd/var/run minix-base
./var/chroot/tcpdump minix-base
./var/db minix-base
./var/db/obsolete minix-base
./usr/libdata/debug/usr/sbin/rdate.debug minix-debug debug
./usr/libdata/debug/usr/sbin/rndc-confgen.debug minix-debug debug
./usr/libdata/debug/usr/sbin/rndc.debug minix-debug debug
+./usr/libdata/debug/usr/sbin/rtadvd.debug minix-debug use_inet6,debug
./usr/libdata/debug/usr/sbin/services_mkdb.debug minix-debug debug
./usr/libdata/debug/usr/sbin/syslogd.debug minix-debug debug
./usr/libdata/debug/usr/sbin/tcpdump.debug minix-debug debug
./usr/man/man5/resolver.5 minix-man
./usr/man/man5/rhosts.5 minix-man obsolete
./usr/man/man5/rndc.conf.5 minix-man
+./usr/man/man5/rtadvd.conf.5 minix-man use_inet6
./usr/man/man5/serv.access.5 minix-man obsolete
./usr/man/man5/statvfs.5 minix-man
./usr/man/man5/syslog.conf.5 minix-man
./usr/man/man8/rotate.8 minix-man
./usr/man/man8/route.8 minix-man
./usr/man/man8/rshd.8 minix-man
+./usr/man/man8/rtadvd.8 minix-man use_inet6
./usr/man/man8/screendump.8 minix-man
./usr/man/man8/serial-ip.8 minix-man obsolete
./usr/man/man8/service.8 minix-man
./usr/share/examples/lua
./usr/share/examples/lutok
./usr/share/examples/openssl
+./usr/share/examples/rtadvd
./usr/share/examples/tmux
./usr/share/games
./usr/share/games/fortune
./var/chroot/named/var/run/lwresd mode=0775 gname=named
./var/chroot/named/var/run/named mode=0775 gname=named
./var/chroot/named/var/tmp mode=01775 gname=named
+./var/chroot/rtadvd type=dir mode=0755
+./var/chroot/rtadvd/etc type=dir mode=0755
+./var/chroot/rtadvd/var type=dir mode=0755
+./var/chroot/rtadvd/var/run type=dir mode=0775 gname=_rtadvd
./var/chroot/tcpdump mode=0755
./var/db
./var/db/obsolete
pwcheck \
\
\
- root \
+ root rtadvd \
\
\
sysctl sysdb syslogd \
--- /dev/null
+#!/bin/sh
+#
+# $NetBSD: rtadvd,v 1.8 2013/07/09 09:34:58 roy Exp $
+#
+
+# PROVIDE: rtadvd
+# REQUIRE: DAEMON
+# BEFORE: LOGIN
+
+$_rc_subr_loaded . /etc/rc.subr
+
+name=rtadvd
+rcvar=$name
+command="/usr/sbin/$name"
+pidfile="/var/run/$name.pid"
+extra_commands=reload
+start_precmd=rtadvd_prestart
+reload_precmd=rtadvd_prereload
+
+rtadvd_prereload()
+{
+ local chdir="$(getent passwd _rtadvd | cut -d: -f6)"
+ local conf=/etc/rtadvd.conf myflags o confdir
+
+ [ -z "$chdir" -o "$chdir" = / ] && return 0
+
+ if [ -n "$flags" ]; then
+ myflags=$flags
+ else
+ eval myflags=\$${name}_flags
+ fi
+ set -- ${myflags}
+ while getopts c:dDfM:Rs o; do
+ case "$1" in
+ -c) conf="$OPTARG";;
+ esac
+ shift
+ done
+ confdir=$(dirname "$conf")
+
+ echo "$name: copying $conf to $chdir$conf"
+ cp "$conf" "$chdir$conf"
+
+ # Provide a link to the chrooted dump file
+ ln -snf "$chdir/var/run/$name.dump" /var/run
+}
+
+rtadvd_prestart()
+{
+ if [ "$ip6mode" != router ]; then
+ warn \
+ "${name} cannot be used on IPv6 host, only on an IPv6 router."
+ return 1
+ fi
+
+ rtadvd_prereload
+}
+
+load_rc_config $name
+run_rc_command "$1"
SUBDIR+= ndp
.endif
.if (${USE_INET6} != "no")
-SUBDIR+= traceroute6
+SUBDIR+= rtadvd traceroute6
.endif
.if !defined(__MINIX)
--- /dev/null
+# $NetBSD: Makefile,v 1.17 2012/08/10 12:10:30 joerg Exp $
+
+WARNS?= 4
+
+.include <bsd.own.mk>
+
+USE_FORT?= yes # network server
+
+PROG= rtadvd
+SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c dump.c
+
+CPPFLAGS+=-DINET6
+MAN= rtadvd.8 rtadvd.conf.5
+LDADD+= -lutil
+DPADD+= ${LIBUTIL}
+
+.if ${MKSHARE} != "no"
+FILESDIR= /usr/share/examples/rtadvd
+FILES= rtadvd.conf
+.endif
+
+.include <bsd.prog.mk>
+
+.if defined(HAVE_GCC) || defined(HAVE_LLVM)
+COPTS.dump.c=-fno-strict-aliasing
+.endif
--- /dev/null
+/* $NetBSD: advcap.c,v 1.15 2015/06/05 15:41:59 roy Exp $ */
+/* $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.
+ * 3. 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 __UNCONST
+#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#endif
+
+#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
+
+char *RM;
+
+/*
+ * 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 */
+
+static char *remotefile;
+
+extern char *conffile;
+
+int tgetent(char *, char *);
+int getent(char *, char *, 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)
+{
+ char *cp;
+
+ remotefile = cp = conffile ? conffile : __UNCONST(_PATH_RTADVDCONF);
+ return (getent(bp, name, cp));
+}
+
+int
+getent(char *bp, char *name, char *cp)
+{
+ int c;
+ int i = 0, cnt = 0;
+ char ibuf[BUFSIZ];
+ 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 (cp && *cp) {
+ tf = open(RM = cp, O_RDONLY);
+ }
+ if (tf < 0) {
+ syslog(LOG_INFO, "<%s> open: %m", __func__);
+ 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) {
+ write(2,"Remcap entry too long\n", 23);
+ 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(2, "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(2, "Infinite tc= loop\n", 18);
+ return (0);
+ }
+ if (getent(tcbuf, tcname, remotefile) != 1) {
+ return (0);
+ }
+ for (q = tcbuf; *q++ != ':'; )
+ ;
+ l = p - holdtbuf + strlen(q);
+ if (l > BUFSIZ) {
+ write(2, "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((unsigned char)*bp)) {
+ while (isdigit((unsigned char)*bp++))
+ ;
+ } else
+ bp++;
+ case '"':
+ dquote = (dquote ? 0 : 1);
+ 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((unsigned char)*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 *dps = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\"", *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 = dps;
+ c = *str++;
+nextc:
+ if (*dp++ == c) {
+ c = *dp++;
+ break;
+ }
+ dp++;
+ if (*dp)
+ goto nextc;
+ if (isdigit((unsigned char)c)) {
+ c -= '0', i = 2;
+ do
+ c <<= 3, c |= *str++ - '0';
+ while (--i && isdigit((unsigned char)*str));
+ }
+ break;
+ }
+ *cp++ = c;
+ }
+ if (c == term && term != ':') {
+ term = ':';
+ goto again;
+ }
+ *cp++ = 0;
+ str = *area;
+ *area = cp;
+ return (str);
+}
--- /dev/null
+/* $NetBSD: advcap.h,v 1.7 2011/12/10 19:14:29 roy Exp $ */
+/* $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_ */
--- /dev/null
+/* $NetBSD: config.c,v 1.34 2015/06/05 14:09:20 roy Exp $ */
+/* $KAME: config.c,v 1.93 2005/10/17 14:40:02 suz 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/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#ifdef __FreeBSD__
+#include <net/if_var.h>
+#endif
+
+#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 <string.h>
+#include <stdlib.h>
+#include <search.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+#include <inttypes.h>
+
+#include "rtadvd.h"
+#include "advcap.h"
+#include "timer.h"
+#include "if.h"
+#include "config.h"
+
+#ifndef __arraycount
+#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
+#endif
+
+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 int getinet6sysctl(int);
+
+static size_t
+encode_domain(char *dst, const char *src)
+{
+ ssize_t len;
+ char *odst, *p;
+
+ odst = dst;
+ while (src && (len = strlen(src)) != 0) {
+ p = strchr(src, '.');
+ *dst++ = len = MIN(63, p == NULL ? len : p - src);
+ memcpy(dst, src, len);
+ dst += len;
+ if (p == NULL)
+ break;
+ src = p + 1;
+ }
+ *dst++ = '\0';
+
+ return dst - odst;
+}
+
+void
+free_rainfo(struct rainfo *rai)
+{
+ struct prefix *pfx;
+ struct rtinfo *rti;
+ struct rdnss *rdnss;
+ struct rdnss_addr *rdnsa;
+ struct dnssl *dnssl;
+ struct dnssl_domain *dnsd;
+
+ rtadvd_remove_timer(&rai->timer);
+
+ while ((pfx = TAILQ_FIRST(&rai->prefix))) {
+ TAILQ_REMOVE(&rai->prefix, pfx, next);
+ free(pfx);
+ }
+
+ while ((rti = TAILQ_FIRST(&rai->route))) {
+ TAILQ_REMOVE(&rai->route, rti, next);
+ free(rti);
+ }
+
+ while ((rdnss = TAILQ_FIRST(&rai->rdnss))) {
+ TAILQ_REMOVE(&rai->rdnss, rdnss, next);
+ while ((rdnsa = TAILQ_FIRST(&rdnss->list))) {
+ TAILQ_REMOVE(&rdnss->list, rdnsa, next);
+ free(rdnsa);
+ }
+ free(rdnss);
+ }
+
+ while ((dnssl = TAILQ_FIRST(&rai->dnssl))) {
+ TAILQ_REMOVE(&rai->dnssl, dnssl, next);
+ while ((dnsd = TAILQ_FIRST(&dnssl->list))) {
+ TAILQ_REMOVE(&dnssl->list, dnsd, next);
+ free(dnsd);
+ }
+ free(dnssl);
+ }
+
+ free(rai->sdl);
+ free(rai->ra_data);
+ free(rai);
+}
+
+void
+getconfig(const char *intface, int exithard)
+{
+ int stat, c, i;
+ char tbuf[BUFSIZ];
+ struct rainfo *tmp, *rai;
+ int32_t val;
+ int64_t val64;
+ char buf[BUFSIZ];
+ char *bp = buf;
+ char *addr, *flagstr, *ap;
+ static int forwarding = -1;
+ char entbuf[256], abuf[256];
+ struct rdnss *rdnss;
+ struct dnssl *dnssl;
+
+#define MUSTHAVE(var, cap) \
+ do { \
+ int64_t t; \
+ if ((t = agetnum(cap)) < 0) { \
+ fprintf(stderr, "rtadvd: need %s for interface %s\n", \
+ cap, intface); \
+ goto errexit; \
+ } \
+ var = t; \
+ } while (0)
+#define MAYHAVE(var, cap, def) \
+ do { \
+ if ((var = agetnum(cap)) < 0) \
+ var = def; \
+ } while (0)
+#define ELM_MALLOC(p) \
+ do { \
+ p = calloc(1, sizeof(*p)); \
+ if (p == NULL) { \
+ syslog(LOG_ERR, "<%s> calloc failed: %m", \
+ __func__); \
+ goto errexit; \
+ } \
+ } while(/*CONSTCOND*/0)
+
+ if (if_nametoindex(intface) == 0) {
+ syslog(LOG_INFO, "<%s> interface %s not found, ignoring",
+ __func__, intface);
+ return;
+ }
+
+ syslog(LOG_DEBUG, "<%s> loading configuration for interface %s",
+ __func__, intface);
+
+ if ((stat = agetent(tbuf, intface)) <= 0) {
+ memset(tbuf, 0, sizeof(tbuf));
+ syslog(LOG_INFO,
+ "<%s> %s isn't defined in the configuration file"
+ " or the configuration file doesn't exist."
+ " Treat it as default",
+ __func__, intface);
+ }
+
+ ELM_MALLOC(tmp);
+ TAILQ_INIT(&tmp->prefix);
+ TAILQ_INIT(&tmp->route);
+ TAILQ_INIT(&tmp->rdnss);
+ TAILQ_INIT(&tmp->dnssl);
+
+ /* check if we are allowed to forward packets (if not determined) */
+ if (forwarding < 0) {
+ if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0)
+ exit(1);
+ }
+
+ /* get interface information */
+ if (agetflag("nolladdr"))
+ tmp->advlinkopt = 0;
+ else
+ tmp->advlinkopt = 1;
+ if (tmp->advlinkopt) {
+ if ((tmp->sdl = if_nametosdl(intface)) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't get information of %s",
+ __func__, intface);
+ goto errexit;
+ }
+ tmp->ifindex = tmp->sdl->sdl_index;
+ } else {
+ tmp->ifindex = if_nametoindex(intface);
+ if (tmp->ifindex == 0) {
+ syslog(LOG_ERR,
+ "<%s> can't get information of %s",
+ __func__, intface);
+ goto errexit;
+ }
+ }
+ tmp->ifflags = if_getflags(tmp->ifindex, 0);
+ strlcpy(tmp->ifname, intface, sizeof(tmp->ifname));
+ if ((tmp->phymtu = if_getmtu(intface)) == 0) {
+ tmp->phymtu = IPV6_MMTU;
+ syslog(LOG_WARNING,
+ "<%s> can't get interface mtu of %s. Treat as %d",
+ __func__, intface, IPV6_MMTU);
+ }
+
+ /*
+ * set router configuration variables.
+ */
+ MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
+ if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
+ syslog(LOG_ERR,
+ "<%s> maxinterval (%d) on %s is invalid "
+ "(must be between %u and %u)", __func__, val,
+ intface, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
+ goto errexit;
+ }
+ tmp->maxinterval = val;
+ MAYHAVE(val, "mininterval", tmp->maxinterval/3);
+ if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) {
+ syslog(LOG_ERR,
+ "<%s> mininterval (%d) on %s is invalid "
+ "(must be between %u and %d)",
+ __func__, val, intface, MIN_MININTERVAL,
+ (tmp->maxinterval * 3) / 4);
+ goto errexit;
+ }
+ tmp->mininterval = val;
+
+ MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
+ tmp->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 errexit;
+ }
+ val |= ND_RA_FLAG_RTPREF_LOW;
+ }
+ } else {
+ MAYHAVE(val, "raflags", 0);
+ }
+ tmp->managedflg = val & ND_RA_FLAG_MANAGED;
+ tmp->otherflg = val & ND_RA_FLAG_OTHER;
+#ifndef ND_RA_FLAG_RTPREF_MASK
+#define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */
+#define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */
+#endif
+ tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
+ if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) {
+ syslog(LOG_ERR, "<%s> invalid router preference (%02x) on %s",
+ __func__, tmp->rtpref, intface);
+ goto errexit;
+ }
+
+ MAYHAVE(val, "rltime", tmp->maxinterval * 3);
+ if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) {
+ syslog(LOG_ERR,
+ "<%s> router lifetime (%d) on %s is invalid "
+ "(must be 0 or between %d and %d)",
+ __func__, val, intface,
+ tmp->maxinterval, MAXROUTERLIFETIME);
+ goto errexit;
+ }
+ /*
+ * Basically, hosts MUST NOT send Router Advertisement messages at any
+ * time (RFC 2461, Section 6.2.3). However, it would sometimes be
+ * useful to allow hosts to advertise some parameters such as prefix
+ * information and link MTU. Thus, we allow hosts to invoke rtadvd
+ * only when router lifetime (on every advertising interface) is
+ * explicitly set zero. (see also the above section)
+ */
+ if (val && forwarding == 0) {
+ syslog(LOG_ERR,
+ "<%s> non zero router lifetime is specified for %s, "
+ "which must not be allowed for hosts. you must "
+ "change router lifetime or enable IPv6 forwarding.",
+ __func__, intface);
+ goto errexit;
+ }
+ tmp->lifetime = val & 0xffff;
+
+ MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
+ if (val < 0 || val > MAXREACHABLETIME) {
+ syslog(LOG_ERR,
+ "<%s> reachable time (%d) on %s is invalid "
+ "(must be no greater than %d)",
+ __func__, val, intface, MAXREACHABLETIME);
+ goto errexit;
+ }
+ tmp->reachabletime = (uint32_t)val;
+
+ MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER);
+ if (val64 < 0 || val64 > 0xffffffff) {
+ syslog(LOG_ERR, "<%s> retrans time (%lld) on %s out of range",
+ __func__, (long long)val64, intface);
+ goto errexit;
+ }
+ tmp->retranstimer = (uint32_t)val64;
+
+ if (agetnum("hapref") != -1 || agetnum("hatime") != -1) {
+ syslog(LOG_ERR,
+ "<%s> mobile-ip6 configuration not supported",
+ __func__);
+ goto errexit;
+ }
+ /* 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);
+ tmp->clockskew = val;
+
+ tmp->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 */
+ if ((pfx = calloc(1, sizeof(*pfx))) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't allocate memory: %m",
+ __func__);
+ goto errexit;
+ }
+
+ TAILQ_INSERT_TAIL(&tmp->prefix, pfx, next);
+ tmp->pfxs++;
+ pfx->rainfo = tmp;
+
+ pfx->origin = PREFIX_FROM_CONFIG;
+
+ if (inet_pton(AF_INET6, addr, &pfx->prefix) != 1) {
+ syslog(LOG_ERR,
+ "<%s> inet_pton failed for %s",
+ __func__, addr);
+ goto errexit;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
+ syslog(LOG_ERR,
+ "<%s> multicast prefix (%s) must "
+ "not be advertised on %s",
+ __func__, addr, intface);
+ goto errexit;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
+ syslog(LOG_NOTICE,
+ "<%s> link-local prefix (%s) will be"
+ " advertised on %s",
+ __func__, addr, intface);
+
+ makeentry(entbuf, sizeof(entbuf), i, "prefixlen");
+ MAYHAVE(val, entbuf, 64);
+ if (val < 0 || val > 128) {
+ syslog(LOG_ERR, "<%s> prefixlen (%d) for %s "
+ "on %s out of range",
+ __func__, val, addr, intface);
+ goto errexit;
+ }
+ 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->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
+ 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 (%lld) for "
+ "%s/%d on %s is out of range",
+ __func__, (long long)val64,
+ addr, pfx->prefixlen, intface);
+ goto errexit;
+ }
+ pfx->validlifetime = (uint32_t)val64;
+
+ makeentry(entbuf, sizeof(entbuf), i, "vltimedecr");
+ if (agetflag(entbuf)) {
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ pfx->vltimeexpire =
+ now.tv_sec + pfx->validlifetime;
+ }
+
+ makeentry(entbuf, sizeof(entbuf), i, "pltime");
+ MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
+ if (val64 < 0 || val64 > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> pltime (%lld) for %s/%d on %s "
+ "is out of range",
+ __func__, (long long)val64,
+ addr, pfx->prefixlen, intface);
+ goto errexit;
+ }
+ pfx->preflifetime = (uint32_t)val64;
+
+ makeentry(entbuf, sizeof(entbuf), i, "pltimedecr");
+ if (agetflag(entbuf)) {
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ pfx->pltimeexpire =
+ now.tv_sec + pfx->preflifetime;
+ }
+ }
+ if (TAILQ_FIRST(&tmp->prefix) == NULL && !agetflag("noifprefix"))
+ get_prefix(tmp);
+
+ MAYHAVE(val64, "mtu", 0);
+ if (val64 < 0 || val64 > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> mtu (%" PRIi64 ") on %s out of range",
+ __func__, val64, intface);
+ goto errexit;
+ }
+ tmp->linkmtu = (uint32_t)val64;
+ if (tmp->linkmtu == 0) {
+ char *mtustr;
+
+ if ((mtustr = (char *)agetstr("mtu", &bp)) &&
+ strcmp(mtustr, "auto") == 0)
+ tmp->linkmtu = tmp->phymtu;
+ }
+ else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) {
+ syslog(LOG_ERR,
+ "<%s> advertised link mtu (%d) on %s is invalid (must "
+ "be between least MTU (%d) and physical link MTU (%d)",
+ __func__, tmp->linkmtu, intface,
+ IPV6_MMTU, tmp->phymtu);
+ goto errexit;
+ }
+
+#ifdef SIOCSIFINFO_IN6
+ {
+ struct in6_ndireq ndi;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %m", __func__);
+ goto errexit;
+ }
+ memset(&ndi, 0, sizeof(ndi));
+ strncpy(ndi.ifname, intface, IFNAMSIZ);
+ if (ioctl(s, SIOCGIFINFO_IN6, &ndi) < 0) {
+ syslog(LOG_INFO, "<%s> ioctl:SIOCGIFINFO_IN6 at %s: %m",
+ __func__, intface);
+ }
+
+ /* reflect the RA info to the host variables in kernel */
+ ndi.ndi.chlim = tmp->hoplimit;
+ ndi.ndi.retrans = tmp->retranstimer;
+ ndi.ndi.basereachable = tmp->reachabletime;
+ if (ioctl(s, SIOCSIFINFO_IN6, &ndi) < 0) {
+ syslog(LOG_INFO, "<%s> ioctl:SIOCSIFINFO_IN6 at %s: %m",
+ __func__, intface);
+ }
+ close(s);
+ }
+#endif
+
+ /* route information */
+ for (i = -1; i < MAXROUTE; i++) {
+ struct rtinfo *rti;
+ char oentbuf[256];
+
+ 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;
+
+ ELM_MALLOC(rti);
+ memset(rti, 0, sizeof(*rti));
+
+ /* link into chain */
+ TAILQ_INSERT_TAIL(&tmp->route, rti, next);
+
+ if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) {
+ syslog(LOG_ERR, "<%s> inet_pton failed for %s",
+ __func__, addr);
+ goto errexit;
+ }
+#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, intface);
+ goto errexit;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) {
+ syslog(LOG_NOTICE,
+ "<%s> link-local route (%s) will "
+ "be advertised on %s",
+ __func__, addr, intface);
+ goto errexit;
+ }
+#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 (%d) for %s on %s "
+ "out of range",
+ __func__, val, addr, intface);
+ goto errexit;
+ }
+ 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 errexit;
+ }
+ 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->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
+ if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) {
+ syslog(LOG_ERR, "<%s> invalid route preference (%02x) "
+ "for %s/%d on %s",
+ __func__, rti->rtpref, addr,
+ rti->prefixlen, intface);
+ goto errexit;
+ }
+
+ /*
+ * 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, intface);
+ val64 = tmp->lifetime;
+ }
+ }
+ if (val64 < 0 || val64 > 0xffffffff) {
+ syslog(LOG_ERR, "<%s> route lifetime (%lld) for "
+ "%s/%d on %s out of range", __func__,
+ (long long)val64, addr, rti->prefixlen, intface);
+ goto errexit;
+ }
+ rti->ltime = (uint32_t)val64;
+ }
+
+ /* RDNSS */
+ for (i = -1; i < MAXRDNSS; i++) {
+ struct rdnss_addr *rdnsa;
+
+ makeentry(entbuf, sizeof(entbuf), i, "rdnss");
+ addr = (char *)agetstr(entbuf, &bp);
+ if (addr == NULL)
+ continue;
+
+ ELM_MALLOC(rdnss);
+ TAILQ_INSERT_TAIL(&tmp->rdnss, rdnss, next);
+ TAILQ_INIT(&rdnss->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(rdnsa);
+ TAILQ_INSERT_TAIL(&rdnss->list, rdnsa, next);
+ if (inet_pton(AF_INET6, abuf, &rdnsa->addr) != 1) {
+ syslog(LOG_ERR, "<%s> inet_pton failed for %s",
+ __func__, addr);
+ goto errexit;
+ }
+ }
+
+ makeentry(entbuf, sizeof(entbuf), i, "rdnssltime");
+ MAYHAVE(val64, entbuf, tmp->maxinterval * 3 / 2);
+ if (val64 < tmp->maxinterval ||
+ val64 > tmp->maxinterval * 2)
+ {
+ syslog(LOG_ERR, "<%s> %s (%lld) on %s is invalid",
+ __func__, entbuf, (long long)val64, intface);
+ goto errexit;
+ }
+ rdnss->lifetime = (uint32_t)val64;
+
+ }
+
+ /* DNSSL */
+ TAILQ_INIT(&tmp->dnssl);
+ for (i = -1; i < MAXDNSSL; i++) {
+ struct dnssl_domain *dnsd;
+
+ makeentry(entbuf, sizeof(entbuf), i, "dnssl");
+ addr = (char *)agetstr(entbuf, &bp);
+ if (addr == NULL)
+ continue;
+
+ ELM_MALLOC(dnssl);
+ TAILQ_INSERT_TAIL(&tmp->dnssl, dnssl, next);
+ TAILQ_INIT(&dnssl->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(dnsd);
+ TAILQ_INSERT_TAIL(&dnssl->list, dnsd, next);
+ dnsd->len = encode_domain(dnsd->domain, abuf);
+ }
+
+ makeentry(entbuf, sizeof(entbuf), i, "dnsslltime");
+ MAYHAVE(val64, entbuf, tmp->maxinterval * 3 / 2);
+ if (val64 < tmp->maxinterval ||
+ val64 > tmp->maxinterval * 2)
+ {
+ syslog(LOG_ERR, "<%s> %s (%lld) on %s is invalid",
+ __func__, entbuf, (long long)val64, intface);
+ goto errexit;
+ }
+ dnssl->lifetime = (uint32_t)val64;
+
+ }
+
+ TAILQ_FOREACH(rai, &ralist, next) {
+ if (rai->ifindex == tmp->ifindex) {
+ TAILQ_REMOVE(&ralist, rai, next);
+ /* If we already have a leaving RA use that
+ * as this config hasn't been advertised */
+ if (rai->leaving) {
+ tmp->leaving = rai->leaving;
+ free_rainfo(rai);
+ rai = tmp->leaving;
+ rai->leaving_for = tmp;
+ break;
+ }
+ rai->lifetime = 0;
+ TAILQ_FOREACH(rdnss, &rai->rdnss, next)
+ rdnss->lifetime = 0;
+ TAILQ_FOREACH(dnssl, &rai->dnssl, next)
+ dnssl->lifetime = 0;
+ rai->leaving_for = tmp;
+ tmp->leaving = rai;
+ rai->initcounter = MAX_INITIAL_RTR_ADVERTISEMENTS;
+ rai->mininterval = MIN_DELAY_BETWEEN_RAS;
+ rai->maxinterval = MIN_DELAY_BETWEEN_RAS;
+ rai->leaving_adv = MAX_FINAL_RTR_ADVERTISEMENTS;
+ if (rai->timer == NULL)
+ rai->timer = rtadvd_add_timer(ra_timeout,
+ ra_timer_update,
+ rai, rai);
+ ra_timer_update((void *)rai, &rai->timer->tm);
+ rtadvd_set_timer(&rai->timer->tm, rai->timer);
+ break;
+ }
+ }
+
+ /* okey */
+ TAILQ_INSERT_TAIL(&ralist, tmp, next);
+
+ /* construct the sending packet */
+ make_packet(tmp);
+
+ /* set timer */
+ if (rai)
+ return;
+ tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
+ tmp, tmp);
+ ra_timer_set_short_delay(tmp);
+
+ return;
+
+errexit:
+ if (exithard)
+ exit(1);
+ free_rainfo(tmp);
+}
+
+void
+get_prefix(struct rainfo *rai)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct prefix *pp;
+ struct in6_addr *a;
+ unsigned 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);
+ }
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ int plen;
+
+ if (strcmp(ifa->ifa_name, rai->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 = (unsigned char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
+ lim = (unsigned 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. */
+ if ((pp = calloc(1, sizeof(*pp))) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't get allocate buffer for prefix",
+ __func__);
+ exit(1);
+ }
+
+ /* set prefix, sweep bits outside of prefixlen */
+ pp->prefixlen = plen;
+ memcpy(&pp->prefix, a, sizeof(*a));
+ if (1)
+ {
+ p = (unsigned char *)&pp->prefix;
+ ep = (unsigned char *)(&pp->prefix + 1);
+ while (m < lim && p < ep)
+ *p++ &= *m++;
+ while (p < ep)
+ *p++ = 0x00;
+ }
+ if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf,
+ sizeof(ntopbuf))) {
+ syslog(LOG_ERR, "<%s> inet_ntop failed", __func__);
+ exit(1);
+ }
+ syslog(LOG_DEBUG,
+ "<%s> add %s/%d to prefix list on %s",
+ __func__, ntopbuf, pp->prefixlen, rai->ifname);
+
+ /* set other fields with protocol defaults */
+ pp->validlifetime = DEF_ADVVALIDLIFETIME;
+ pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
+ pp->onlinkflg = 1;
+ pp->autoconfflg = 1;
+ pp->origin = PREFIX_FROM_KERNEL;
+ pp->rainfo = rai;
+
+ /* link into chain */
+ TAILQ_INSERT_TAIL(&rai->prefix, pp, next);
+ 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 *prefix;
+ char ntopbuf[INET6_ADDRSTRLEN];
+
+ if ((prefix = calloc(1, sizeof(*prefix))) == NULL) {
+ syslog(LOG_ERR, "<%s> memory allocation failed",
+ __func__);
+ return; /* XXX: error or exit? */
+ }
+ prefix->prefix = ipr->ipr_prefix.sin6_addr;
+ prefix->prefixlen = ipr->ipr_plen;
+ prefix->validlifetime = ipr->ipr_vltime;
+ prefix->preflifetime = ipr->ipr_pltime;
+ prefix->onlinkflg = ipr->ipr_raf_onlink;
+ prefix->autoconfflg = ipr->ipr_raf_auto;
+ prefix->origin = PREFIX_FROM_DYNAMIC;
+
+ prefix->rainfo = rai;
+ TAILQ_INSERT_TAIL(&rai->prefix, prefix, next);
+ rai->pfxs++;
+
+ syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
+ __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ ipr->ipr_plen, rai->ifname);
+
+ /* free the previous packet */
+ free(rai->ra_data);
+ rai->ra_data = NULL;
+
+ /* reconstruct the packet */
+ make_packet(rai);
+}
+
+/*
+ * 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 *prefix)
+{
+ char ntopbuf[INET6_ADDRSTRLEN];
+ struct rainfo *rai = prefix->rainfo;
+
+ TAILQ_REMOVE(&rai->prefix, prefix, next);
+ rai->pfxs--;
+ syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
+ __func__, inet_ntop(AF_INET6, &prefix->prefix,
+ ntopbuf, INET6_ADDRSTRLEN),
+ prefix->prefixlen, rai->ifname);
+ rtadvd_remove_timer(&prefix->timer);
+ free(prefix);
+}
+
+void
+invalidate_prefix(struct prefix *prefix)
+{
+ char ntopbuf[INET6_ADDRSTRLEN];
+ struct timespec timo;
+ struct rainfo *rai = prefix->rainfo;
+
+ if (prefix->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, &prefix->prefix, ntopbuf, INET6_ADDRSTRLEN),
+ prefix->prefixlen, rai->ifname, (long)prefix_timo);
+
+ /* set the expiration timer */
+ prefix->timer = rtadvd_add_timer(prefix_timeout, NULL, prefix, NULL);
+ if (prefix->timer == NULL) {
+ syslog(LOG_ERR, "<%s> failed to add a timer for a prefix. "
+ "remove the prefix", __func__);
+ delete_prefix(prefix);
+ }
+ timo.tv_sec = prefix_timo;
+ timo.tv_nsec = 0;
+ rtadvd_set_timer(&timo, prefix->timer);
+}
+
+static struct rtadvd_timer *
+prefix_timeout(void *arg)
+{
+ struct prefix *prefix = (struct prefix *)arg;
+
+ delete_prefix(prefix);
+
+ return(NULL);
+}
+
+void
+update_prefix(struct prefix * prefix)
+{
+ char ntopbuf[INET6_ADDRSTRLEN];
+ struct rainfo *rai = prefix->rainfo;
+
+ if (prefix->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, &prefix->prefix, ntopbuf,
+ INET6_ADDRSTRLEN), prefix->prefixlen, rai->ifname);
+
+ /* stop the expiration timer */
+ rtadvd_remove_timer(&prefix->timer);
+}
+
+/*
+ * 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: %m", __func__);
+ exit(1);
+ }
+
+ if (ioctl(s, SIOCGIFPREFIX_IN6, ipr) < 0) {
+ syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX: %m", __func__);
+
+ 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: %m", __func__,
+ ifindex);
+ 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 *rainfo)
+{
+ 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 prefix *pfx;
+ struct nd_opt_route_info *ndopt_rti;
+ struct rtinfo *rti;
+ struct nd_opt_rdnss *ndopt_rdnss;
+ struct rdnss *rdns;
+ struct rdnss_addr *rdnsa;
+ struct nd_opt_dnssl *ndopt_dnssl;
+ struct dnssl *dnsl;
+ struct dnssl_domain *dnsd;
+ size_t len, plen;
+
+ /* calculate total length */
+ packlen = sizeof(struct nd_router_advert);
+ if (rainfo->advlinkopt) {
+ if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
+ syslog(LOG_INFO,
+ "<%s> link-layer address option has"
+ " null length on %s. Treat as not included.",
+ __func__, rainfo->ifname);
+ rainfo->advlinkopt = 0;
+ }
+ packlen += lladdroptlen;
+ }
+ if (TAILQ_FIRST(&rainfo->prefix) != NULL)
+ packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
+ if (rainfo->linkmtu)
+ packlen += sizeof(struct nd_opt_mtu);
+ TAILQ_FOREACH(rti, &rainfo->route, next)
+ packlen += sizeof(struct nd_opt_route_info) +
+ ((rti->prefixlen + 0x3f) >> 6) * 8;
+
+ TAILQ_FOREACH(rdns, &rainfo->rdnss, next) {
+ packlen += sizeof(struct nd_opt_rdnss);
+ TAILQ_FOREACH(rdnsa, &rdns->list, next)
+ packlen += sizeof(rdnsa->addr);
+ }
+ TAILQ_FOREACH(dnsl, &rainfo->dnssl, next) {
+ packlen += sizeof(struct nd_opt_dnssl);
+ len = 0;
+ TAILQ_FOREACH(dnsd, &dnsl->list, next)
+ len += dnsd->len;
+ len += len % 8 ? 8 - len % 8 : 0;
+ packlen += len;
+ }
+
+ /* allocate memory for the packet */
+ if ((buf = realloc(rainfo->ra_data, packlen)) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't get enough memory for an RA packet %m",
+ __func__);
+ exit(1);
+ }
+ rainfo->ra_data = buf;
+ /* XXX: what if packlen > 576? */
+ rainfo->ra_datalen = packlen;
+#define CHECKLEN(size) \
+ do { \
+ if (buf + size > rainfo->ra_data + packlen) { \
+ syslog(LOG_ERR, \
+ "<%s, %d> RA packet does not fit in %zu",\
+ __func__, __LINE__, packlen); \
+ exit(1); \
+ } \
+ } while (/*CONSTCOND*/0)
+ /*
+ * construct the packet
+ */
+ CHECKLEN(sizeof(*ra));
+ 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 & rainfo->hoplimit);
+ ra->nd_ra_flags_reserved = 0; /* just in case */
+ /*
+ * XXX: the router preference field, which is a 2-bit field, should be
+ * initialized before other fields.
+ */
+ ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref;
+ ra->nd_ra_flags_reserved |=
+ rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
+ ra->nd_ra_flags_reserved |=
+ rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
+ ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
+ ra->nd_ra_reachable = htonl(rainfo->reachabletime);
+ ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
+ buf += sizeof(*ra);
+
+ if (rainfo->advlinkopt) {
+ CHECKLEN(sizeof(struct nd_opt_hdr));
+ lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
+ buf += lladdroptlen;
+ }
+
+ if (rainfo->linkmtu) {
+ CHECKLEN(sizeof(*ndopt_mtu));
+ ndopt_mtu = (struct nd_opt_mtu *)buf;
+ ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
+ ndopt_mtu->nd_opt_mtu_len = 1;
+ ndopt_mtu->nd_opt_mtu_reserved = 0;
+ ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu);
+ buf += sizeof(struct nd_opt_mtu);
+ }
+
+ TAILQ_FOREACH(pfx, &rainfo->prefix, next) {
+ uint32_t vltime, pltime;
+ struct timespec now;
+
+ CHECKLEN(sizeof(*ndopt_pi));
+ ndopt_pi = (struct nd_opt_prefix_info *)buf;
+ ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
+ ndopt_pi->nd_opt_pi_len = 4;
+ ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
+ ndopt_pi->nd_opt_pi_flags_reserved = 0;
+ if (pfx->onlinkflg)
+ ndopt_pi->nd_opt_pi_flags_reserved |=
+ ND_OPT_PI_FLAG_ONLINK;
+ if (pfx->autoconfflg)
+ ndopt_pi->nd_opt_pi_flags_reserved |=
+ ND_OPT_PI_FLAG_AUTO;
+ if (pfx->timer)
+ vltime = 0;
+ else {
+ if (pfx->vltimeexpire || pfx->pltimeexpire)
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ if (pfx->vltimeexpire == 0)
+ vltime = pfx->validlifetime;
+ else
+ vltime = (pfx->vltimeexpire > now.tv_sec) ?
+ pfx->vltimeexpire - now.tv_sec : 0;
+ }
+ if (pfx->timer)
+ pltime = 0;
+ else {
+ if (pfx->pltimeexpire == 0)
+ pltime = pfx->preflifetime;
+ else
+ pltime = (pfx->pltimeexpire > now.tv_sec) ?
+ 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->prefix;
+
+ buf += sizeof(struct nd_opt_prefix_info);
+ }
+
+ TAILQ_FOREACH(rti, &rainfo->route, next) {
+ uint8_t psize = (rti->prefixlen + 0x3f) >> 6;
+
+ CHECKLEN(sizeof(*ndopt_rti));
+ ndopt_rti = (struct nd_opt_route_info *)buf;
+ ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO;
+ ndopt_rti->nd_opt_rti_len = 1 + psize;
+ ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen;
+ ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref;
+ ndopt_rti->nd_opt_rti_lifetime = htonl(rti->ltime);
+ memcpy(ndopt_rti + 1, &rti->prefix, psize * 8);
+ buf += sizeof(struct nd_opt_route_info) + psize * 8;
+ }
+
+ TAILQ_FOREACH(rdns, &rainfo->rdnss, next) {
+ CHECKLEN(sizeof(*ndopt_rdnss));
+ ndopt_rdnss = (struct nd_opt_rdnss *)buf;
+ ndopt_rdnss->nd_opt_rdnss_type = ND_OPT_RDNSS;
+ ndopt_rdnss->nd_opt_rdnss_len = 1;
+ ndopt_rdnss->nd_opt_rdnss_reserved = 0;
+ ndopt_rdnss->nd_opt_rdnss_lifetime = htonl(rdns->lifetime);
+ buf += sizeof(*ndopt_rdnss);
+
+ TAILQ_FOREACH(rdnsa, &rdns->list, next) {
+ CHECKLEN(sizeof(rdnsa->addr));
+ memcpy(buf, &rdnsa->addr, sizeof(rdnsa->addr));
+ ndopt_rdnss->nd_opt_rdnss_len += 2;
+ buf += sizeof(rdnsa->addr);
+ }
+ }
+
+ TAILQ_FOREACH(dnsl, &rainfo->dnssl, next) {
+ CHECKLEN(sizeof(*ndopt_dnssl));
+ 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(dnsl->lifetime);
+ buf += sizeof(*ndopt_dnssl);
+
+ TAILQ_FOREACH(dnsd, &dnsl->list, next) {
+ CHECKLEN(dnsd->len);
+ memcpy(buf, dnsd->domain, dnsd->len);
+ buf += dnsd->len;
+ }
+ /* Ensure our length is padded correctly */
+ len = buf - (char *)ndopt_dnssl;
+ plen = len % 8 ? 8 - len % 8 : 0;
+ CHECKLEN(plen);
+ memset(buf, 0, plen);
+ buf += plen;
+ ndopt_dnssl->nd_opt_dnssl_len = (len + plen) / 8;
+ }
+ memset(buf, 0, packlen - (buf - rainfo->ra_data));
+}
+
+static int
+getinet6sysctl(int code)
+{
+ const int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, code };
+ int value;
+ size_t size;
+
+ size = sizeof(value);
+ if (sysctl(mib, __arraycount(mib), &value, &size, NULL, 0)
+ < 0) {
+ syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %m",
+ __func__, code);
+ return -1;
+ }
+ else
+ return value;
+}
--- /dev/null
+/* $NetBSD: config.h,v 1.9 2012/12/13 15:36:36 roy Exp $ */
+/* $KAME: config.h,v 1.9 2003/08/06 04:19:40 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.
+ */
+
+extern void free_rainfo(struct rainfo *);
+extern void getconfig(const char *, 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 information options,
+ * so it should be okay to limit it
+ */
+#define MAXPREFIX 100
+#define MAXROUTE 100
+#define MAXRDNSS 100
+#define MAXDNSSL 100
--- /dev/null
+/* $NetBSD: dump.c,v 1.12 2015/06/05 14:09:20 roy Exp $ */
+/* $KAME: dump.c,v 1.34 2004/06/14 05:35:59 itojun Exp $ */
+
+/*
+ * Copyright (C) 2000 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/socket.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#ifdef __FreeBSD__
+#include <net/if_var.h>
+#endif
+
+#include <netinet/in.h>
+
+/* XXX: the following two are non-standard include files */
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+
+#include <arpa/inet.h>
+
+#include <time.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include "rtadvd.h"
+#include "timer.h"
+#include "if.h"
+#include "dump.h"
+
+static FILE *fp;
+
+static char *ether_str(struct sockaddr_dl *);
+static void if_dump(void);
+
+static const char *rtpref_str[] = {
+ "medium", /* 00 */
+ "high", /* 01 */
+ "rsv", /* 10 */
+ "low" /* 11 */
+};
+
+static char *
+ether_str(struct sockaddr_dl *sdl)
+{
+ static char hbuf[NI_MAXHOST];
+
+ if (sdl->sdl_alen) {
+ if (getnameinfo((struct sockaddr *)sdl, sdl->sdl_len,
+ hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
+ snprintf(hbuf, sizeof(hbuf), "<invalid>");
+ } else
+ snprintf(hbuf, sizeof(hbuf), "NONE");
+
+ return(hbuf);
+}
+
+static void
+if_dump(void)
+{
+ struct rainfo *rai;
+ struct prefix *pfx;
+ struct rtinfo *rti;
+ struct rdnss *rdns;
+ struct rdnss_addr *rdnsa;
+ struct dnssl *dnsl;
+ struct dnssl_domain *dnsd;
+ char *p, len;
+ char prefixbuf[INET6_ADDRSTRLEN];
+ struct timespec now;
+
+ clock_gettime(CLOCK_MONOTONIC, &now); /* XXX: unused in most cases */
+ TAILQ_FOREACH(rai, &ralist, next) {
+ fprintf(fp, "%s:\n", rai->ifname);
+
+ fprintf(fp, " Status: %s\n",
+ (rai->ifflags & IFF_UP) ? "UP" : "DOWN");
+
+ /* control information */
+ if (rai->lastsent.tv_sec) {
+ /* note that ctime() appends CR by itself */
+ fprintf(fp, " Last RA sent: %s",
+ ctime((time_t *)&rai->lastsent.tv_sec));
+ }
+ if (rai->timer) {
+ fprintf(fp, " Next RA will be sent: %s",
+ ctime((time_t *)&rai->timer->tm.tv_sec));
+ }
+ else
+ fprintf(fp, " RA timer is stopped");
+ fprintf(fp, " waits: %d, initcount: %d\n",
+ rai->waiting, rai->initcounter);
+
+ /* statistics */
+ fprintf(fp, " statistics: RA(out/in/inconsistent): "
+ "%llu/%llu/%llu, ",
+ (unsigned long long)rai->raoutput,
+ (unsigned long long)rai->rainput,
+ (unsigned long long)rai->rainconsistent);
+ fprintf(fp, "RS(input): %llu\n",
+ (unsigned long long)rai->rsinput);
+
+ /* interface information */
+ if (rai->advlinkopt)
+ fprintf(fp, " Link-layer address: %s\n",
+ ether_str(rai->sdl));
+ fprintf(fp, " MTU: %d\n", rai->phymtu);
+
+ /* Router configuration variables */
+ fprintf(fp, " DefaultLifetime: %d, MaxAdvInterval: %d, "
+ "MinAdvInterval: %d\n", rai->lifetime, rai->maxinterval,
+ rai->mininterval);
+ fprintf(fp, " Flags: %s%s%s, ",
+ rai->managedflg ? "M" : "", rai->otherflg ? "O" : "",
+ "");
+ fprintf(fp, "Preference: %s, ",
+ rtpref_str[(rai->rtpref >> 3) & 0xff]);
+ fprintf(fp, "MTU: %d\n", rai->linkmtu);
+ fprintf(fp, " ReachableTime: %d, RetransTimer: %d, "
+ "CurHopLimit: %d\n", rai->reachabletime,
+ rai->retranstimer, rai->hoplimit);
+ if (rai->clockskew)
+ fprintf(fp, " Clock skew: %dsec\n",
+ rai->clockskew);
+ TAILQ_FOREACH(pfx, &rai->prefix, next) {
+ if (pfx == TAILQ_FIRST(&rai->prefix))
+ fprintf(fp, " Prefixes:\n");
+ fprintf(fp, " %s/%d(",
+ inet_ntop(AF_INET6, &pfx->prefix, prefixbuf,
+ sizeof(prefixbuf)), pfx->prefixlen);
+ switch (pfx->origin) {
+ case PREFIX_FROM_KERNEL:
+ fprintf(fp, "KERNEL, ");
+ break;
+ case PREFIX_FROM_CONFIG:
+ fprintf(fp, "CONFIG, ");
+ break;
+ case PREFIX_FROM_DYNAMIC:
+ fprintf(fp, "DYNAMIC, ");
+ break;
+ }
+ if (pfx->validlifetime == ND6_INFINITE_LIFETIME)
+ fprintf(fp, "vltime: infinity");
+ else
+ fprintf(fp, "vltime: %ld",
+ (long)pfx->validlifetime);
+ if (pfx->vltimeexpire != 0)
+ fprintf(fp, "(decr,expire %lld), ", (long long)
+ (pfx->vltimeexpire > now.tv_sec ?
+ pfx->vltimeexpire - now.tv_sec : 0));
+ else
+ fprintf(fp, ", ");
+ if (pfx->preflifetime == ND6_INFINITE_LIFETIME)
+ fprintf(fp, "pltime: infinity");
+ else
+ fprintf(fp, "pltime: %ld",
+ (long)pfx->preflifetime);
+ if (pfx->pltimeexpire != 0)
+ fprintf(fp, "(decr,expire %lld), ", (long long)
+ (pfx->pltimeexpire > now.tv_sec ?
+ pfx->pltimeexpire - now.tv_sec : 0));
+ else
+ fprintf(fp, ", ");
+ fprintf(fp, "flags: %s%s%s",
+ pfx->onlinkflg ? "L" : "",
+ pfx->autoconfflg ? "A" : "",
+ "");
+ if (pfx->timer) {
+ struct timespec *rest;
+
+ rest = rtadvd_timer_rest(pfx->timer);
+ if (rest) { /* XXX: what if not? */
+ fprintf(fp, ", expire in: %ld",
+ (long)rest->tv_sec);
+ }
+ }
+ fprintf(fp, ")\n");
+ }
+
+ TAILQ_FOREACH(rti, &rai->route, next) {
+ if (rti == TAILQ_FIRST(&rai->route))
+ fprintf(fp, " Route Information:\n");
+ fprintf(fp, " %s/%d (",
+ inet_ntop(AF_INET6, &rti->prefix,
+ prefixbuf, sizeof(prefixbuf)),
+ rti->prefixlen);
+ fprintf(fp, "preference: %s, ",
+ rtpref_str[0xff & (rti->rtpref >> 3)]);
+ if (rti->ltime == ND6_INFINITE_LIFETIME)
+ fprintf(fp, "lifetime: infinity");
+ else
+ fprintf(fp, "lifetime: %ld", (long)rti->ltime);
+ fprintf(fp, ")\n");
+ }
+
+ TAILQ_FOREACH(rdns, &rai->rdnss, next) {
+ fprintf(fp, " Recursive DNS Servers:\n");
+ if (rdns->lifetime == ND6_INFINITE_LIFETIME)
+ fprintf(fp, " lifetime: infinity\n");
+ else
+ fprintf(fp, " lifetime: %ld\n",
+ (long)rdns->lifetime);
+ TAILQ_FOREACH(rdnsa, &rdns->list, next)
+ fprintf(fp, " %s\n",
+ inet_ntop(AF_INET6, &rdnsa->addr,
+ prefixbuf, sizeof(prefixbuf)));
+ }
+
+ TAILQ_FOREACH(dnsl, &rai->dnssl, next) {
+ fprintf(fp, " DNS Search List:\n");
+ if (dnsl->lifetime == ND6_INFINITE_LIFETIME)
+ fprintf(fp, " lifetime: infinity\n");
+ else
+ fprintf(fp, " lifetime: %ld\n",
+ (long)dnsl->lifetime);
+ TAILQ_FOREACH(dnsd, &dnsl->list, next) {
+ fprintf(fp, " ");
+ for (p = dnsd->domain, len = *p++;
+ len != 0;
+ len = *p++)
+ {
+ if (p != dnsd->domain)
+ fputc('.', fp);
+ while(len-- != 0)
+ fputc(*p++, fp);
+ }
+ fputc('\n', fp);
+ }
+ }
+ }
+}
+
+void
+rtadvd_dump_file(const char *dumpfile)
+{
+ syslog(LOG_DEBUG, "<%s> dump current status to %s", __func__,
+ dumpfile);
+
+ if ((fp = fopen(dumpfile, "w")) == NULL) {
+ syslog(LOG_WARNING, "<%s> open a dump file(%s): %m",
+ __func__, dumpfile);
+ return;
+ }
+
+ if_dump();
+
+ fclose(fp);
+}
--- /dev/null
+/* $NetBSD: dump.h,v 1.2 2011/12/10 19:14:29 roy Exp $ */
+/* $KAME: dump.h,v 1.1 2000/05/23 11:31:26 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.
+ */
+
+extern void rtadvd_dump_file(const char *);
--- /dev/null
+/* $NetBSD: if.c,v 1.23 2015/06/05 15:41:59 roy Exp $ */
+/* $KAME: if.c,v 1.36 2004/11/30 22:32:01 suz 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/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <ifaddrs.h>
+#ifdef __FreeBSD__
+#include <net/ethernet.h>
+#else
+#include <net/if_ether.h>
+#endif
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "rtadvd.h"
+#include "if.h"
+
+#ifndef RT_ROUNDUP
+#define RT_ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len))
+#endif
+
+static void
+get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
+{
+ int i;
+
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (addrs & (1 << i)) {
+ rti_info[i] = sa;
+ RT_ADVANCE(sa, sa);
+ }
+ else
+ rti_info[i] = NULL;
+ }
+}
+
+struct sockaddr_dl *
+if_nametosdl(const char *name)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct sockaddr_dl *sdl;
+
+ if (getifaddrs(&ifap) != 0)
+ return (NULL);
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (strcmp(ifa->ifa_name, name) != 0)
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+
+ sdl = malloc(ifa->ifa_addr->sa_len);
+ if (!sdl)
+ continue; /*XXX*/
+
+ memcpy(sdl, ifa->ifa_addr, ifa->ifa_addr->sa_len);
+ freeifaddrs(ifap);
+ return (sdl);
+ }
+
+ freeifaddrs(ifap);
+ return (NULL);
+}
+
+int
+if_getmtu(const char *name)
+{
+ struct ifreq ifr;
+ int s, mtu;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ return 0;
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_addr.sa_family = AF_INET6;
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFMTU, &ifr) != -1)
+ mtu = ifr.ifr_mtu;
+ else
+ mtu = 0;
+ close(s);
+
+ return mtu;
+}
+
+/* give interface index and its old flags, then new flags returned */
+int
+if_getflags(int ifindex, int oifflags)
+{
+ struct ifreq ifr;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %m", __func__);
+ return (oifflags & ~IFF_UP);
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ if_indextoname(ifindex, ifr.ifr_name);
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
+ syslog(LOG_ERR, "<%s> ioctl:SIOCGIFFLAGS: failed for %s",
+ __func__, ifr.ifr_name);
+ close(s);
+ return (oifflags & ~IFF_UP);
+ }
+ close(s);
+ return (ifr.ifr_flags);
+}
+
+#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
+int
+lladdropt_length(struct sockaddr_dl *sdl)
+{
+ switch (sdl->sdl_type) {
+ case IFT_ETHER:
+ case IFT_FDDI:
+ 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:
+ case IFT_FDDI:
+ 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;
+}
+
+#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 (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 */
+#ifdef RTM_IFANNOUNCE
+ case RTM_IFANNOUNCE:
+#endif
+ case RTM_IFINFO:
+ /* 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_ifm_ifindex(char *buf)
+{
+ struct if_msghdr *ifm = (struct if_msghdr *)buf;
+
+ return ((int)ifm->ifm_index);
+}
+
+int
+get_ifam_ifindex(char *buf)
+{
+ struct ifa_msghdr *ifam = (struct ifa_msghdr *)buf;
+
+ return ((int)ifam->ifam_index);
+}
+
+int
+get_ifm_flags(char *buf)
+{
+ struct if_msghdr *ifm = (struct if_msghdr *)buf;
+
+ return (ifm->ifm_flags);
+}
+
+#ifdef RTM_IFANNOUNCE
+int
+get_ifan_ifindex(char *buf)
+{
+ struct if_announcemsghdr *ifan = (struct if_announcemsghdr *)buf;
+
+ return ((int)ifan->ifan_index);
+}
+
+int
+get_ifan_what(char *buf)
+{
+ struct if_announcemsghdr *ifan = (struct if_announcemsghdr *)buf;
+
+ return ((int)ifan->ifan_what);
+}
+#endif
+
+int
+get_prefixlen(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ unsigned char *p, *lim;
+
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+ sa = rti_info[RTAX_NETMASK];
+
+ p = (unsigned char *)(&SIN6(sa)->sin6_addr);
+ lim = (unsigned char *)sa + sa->sa_len;
+ return prefixlen(p, lim);
+}
+
+int
+prefixlen(const unsigned char *p, const 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);
+}
+
+int
+rtmsg_type(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+
+ return(rtm->rtm_type);
+}
+
+int
+rtmsg_len(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+
+ return(rtm->rtm_msglen);
+}
--- /dev/null
+/* $NetBSD: if.h,v 1.10 2012/12/13 15:36:36 roy Exp $ */
+/* $KAME: if.h,v 1.12 2003/09/21 07:17:03 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.
+ */
+
+#define RTADV_TYPE2BITMASK(type) (0x1 << type)
+
+struct nd_opt_hdr;
+struct sockaddr_dl *if_nametosdl(const char *);
+int if_getmtu(const char *);
+int if_getflags(int, int);
+int lladdropt_length(struct sockaddr_dl *);
+void lladdropt_fill(struct sockaddr_dl *, struct nd_opt_hdr *);
+char *get_next_msg(char *, char *, int, size_t *, int);
+struct in6_addr *get_addr(char *);
+int get_rtm_ifindex(char *);
+int get_ifm_ifindex(char *);
+int get_ifam_ifindex(char *);
+int get_ifm_flags(char *);
+#ifdef RTM_IFANNOUNCE
+int get_ifan_ifindex(char *);
+int get_ifan_what(char *);
+#endif
+int get_prefixlen(char *);
+int prefixlen(const unsigned char *, const unsigned char *);
+int rtmsg_type(char *);
+int rtmsg_len(char *);
--- /dev/null
+/* $NetBSD: pathnames.h,v 1.3 2000/05/23 11:37:59 itojun Exp $ */
+/* $KAME: pathnames.h,v 1.2 2000/05/16 13:34:13 itojun Exp $ */
+
+#define _PATH_RTADVDCONF "/etc/rtadvd.conf"
--- /dev/null
+/* $NetBSD: rrenum.c,v 1.18 2015/06/05 15:41:59 roy Exp $ */
+/* $KAME: rrenum.c,v 1.14 2004/06/14 05:36:00 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>
+#ifdef __FreeBSD__
+#include <net/if_var.h>
+#endif
+#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 <string.h>
+#include <stdlib.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 prefix *pp;
+
+ 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], irr) < 0 &&
+ errno != EADDRNOTAVAIL)
+ syslog(LOG_ERR, "<%s> ioctl: %m", __func__);
+ 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], irr) < 0 &&
+ errno != EADDRNOTAVAIL)
+ syslog(LOG_ERR, "<%s> ioctl: %m", __func__);
+
+ /* very adhoc: should be rewritten */
+ if (rpm->rpm_code == RPM_PCO_CHANGE &&
+ IN6_ARE_ADDR_EQUAL(&rpm->rpm_prefix, &rpu->rpu_prefix) &&
+ rpm->rpm_matchlen == rpu->rpu_uselen &&
+ rpu->rpu_uselen == rpu->rpu_keeplen) {
+ if ((rai = if_indextorainfo(ifindex)) == NULL)
+ continue; /* non-advertising IF */
+
+ TAILQ_FOREACH(pp, &rai->prefix, next) {
+ struct timespec now;
+
+ if (prefix_match(&pp->prefix, pp->prefixlen,
+ &rpm->rpm_prefix,
+ rpm->rpm_matchlen)) {
+ /* change parameters */
+ pp->validlifetime = ntohl(rpu->rpu_vltime);
+ pp->preflifetime = ntohl(rpu->rpu_pltime);
+ if (irr->irr_rrf_decrvalid) {
+ clock_gettime(CLOCK_MONOTONIC,
+ &now);
+ pp->vltimeexpire =
+ now.tv_sec + pp->validlifetime;
+ } else
+ pp->vltimeexpire = 0;
+ if (irr->irr_rrf_decrprefd) {
+ clock_gettime(CLOCK_MONOTONIC,
+ &now);
+ pp->pltimeexpire =
+ now.tv_sec + pp->preflifetime;
+ } else
+ pp->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 rainfo *rai;
+
+ 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: %m", __func__);
+ 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;
+
+ /*
+ * 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) {
+ while (if_indextoname(++ifindex, irr.irr_name)) {
+ rai = if_indextorainfo(ifindex);
+ if (rai && (rai->ifflags & IFF_UP)) {
+ /* 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: %m", __func__);
+ 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(size_t 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);
+
+ while (cp < lim) {
+ size_t rpmlen;
+
+ rpm = (struct rr_pco_match *)cp;
+ if (len < sizeof(struct rr_pco_match)) {
+ tooshort:
+ syslog(LOG_ERR, "<%s> pkt too short. left len = %zd. "
+ "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(size_t len, struct icmp6_router_renum *rr,
+ struct in6_addr *from, struct in6_addr *dst)
+{
+ char ntopbuf[INET6_ADDRSTRLEN];
+
+ /* omit rr minimal length check. hope kernel have done it. */
+ /* rr_command length check */
+ if (len < (sizeof(struct icmp6_router_renum) +
+ sizeof(struct rr_pco_match))) {
+ syslog(LOG_ERR, "<%s> rr_command len %zd 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, INET6_ADDRSTRLEN));
+ return 1;
+ }
+
+ /* seqnum and segnum check */
+ if (rro.rro_seqnum > rr->rr_seqnum) {
+ syslog(LOG_WARNING,
+ "<%s> rcvd old seqnum %d from %s",
+ __func__, (uint32_t)ntohl(rr->rr_seqnum),
+ inet_ntop(AF_INET6, from, ntopbuf, INET6_ADDRSTRLEN));
+ return 1;
+ }
+ if (rro.rro_seqnum == rr->rr_seqnum &&
+ (rr->rr_flags & ICMP6_RR_FLAGS_TEST) == 0 &&
+ RR_ISSET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum)) {
+ if ((rr->rr_flags & ICMP6_RR_FLAGS_REQRESULT) != 0)
+ syslog(LOG_WARNING,
+ "<%s> rcvd duped segnum %d from %s",
+ __func__, rr->rr_segnum,
+ inet_ntop(AF_INET6, from, ntopbuf,
+ INET6_ADDRSTRLEN));
+ return 0;
+ }
+
+ /* 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(size_t len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi,
+ struct sockaddr_in6 *from, struct in6_addr *dst)
+{
+ char ntopbuf[2][INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+
+ syslog(LOG_DEBUG,
+ "<%s> RR received from %s to %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf[0], INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* packet validation based on Section 4.1 of RFC2894 */
+ if (len < sizeof(struct icmp6_router_renum)) {
+ syslog(LOG_NOTICE,
+ "<%s>: RR short message (size %zd) from %s to %s on %s",
+ __func__, len,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf[0], INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ 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], INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf[1], INET6_ADDRSTRLEN),
+ 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;
+}
--- /dev/null
+/* $NetBSD: rrenum.h,v 1.5 2011/12/10 19:14:29 roy Exp $ */
+/* $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(size_t, struct icmp6_router_renum *, struct in6_pktinfo *,
+ struct sockaddr_in6 *, struct in6_addr *);
--- /dev/null
+.\" $NetBSD: rtadvd.8,v 1.24 2012/12/13 21:49:38 wiz Exp $
+.\" $KAME: rtadvd.8,v 1.24 2002/05/31 16:16:08 jinmei Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" 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.
+.\"
+.Dd December 13, 2006
+.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 M Ar ifname
+.Ar interface ...
+.Sh DESCRIPTION
+.Nm
+sends router advertisement packets to the specified interfaces.
+.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 at all,
+.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 2461, Section 6.2.3).
+However, it would sometimes be useful to allow hosts to advertise some
+parameters such as prefix information and link MTU.
+Thus,
+.Nm
+can be invoked if router lifetime is explicitly set to zero on every
+advertising interface.
+.Pp
+The command line options are:
+.Bl -tag -width indent
+.\"
+.It Fl c Ar configfile
+Specify an alternate location,
+.Ar configfile ,
+for the configuration file.
+By default,
+.Pa /etc/rtadvd.conf
+is used.
+.It Fl D
+Even more debugging information than that offered by the
+.Fl d
+option is printed.
+.It Fl d
+Print debugging information.
+.It Fl f
+Foreground mode (useful when debugging).
+Log messages will be dumped to stderr when this option is specified.
+.It Fl M Ar ifname
+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 m
+.\"Enables mobile IPv6 support.
+.\"This changes the content of router advertisement option, as well as
+.\"permitted configuration directives.
+.It Fl R
+Accept router renumbering requests.
+If you enable it, an
+.Xr ipsec 4
+setup is suggested for security reasons.
+.\"On KAME-based systems,
+.\".Xr rrenumd 8
+.\"generates router renumbering request packets.
+This option is currently disabled, and is ignored by
+.Nm
+with a warning message.
+.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
+Upon receipt of signal
+.Dv SIGUSR1 ,
+.Nm
+will dump the current internal state into
+.Pa /var/run/rtadvd.dump .
+.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 2461 6.2.5 .
+.Sh FILES
+.Bl -tag -width /var/run/rtadvd.dumpXX -compact
+.It Pa /etc/rtadvd.conf
+The default configuration file.
+.It Pa /var/run/rtadvd.pid
+Contains the PID of the currently running
+.Nm .
+.It Pa /var/run/rtadvd.dump
+The file in which
+.Nm
+dumps its internal state.
+.El
+.Sh EXIT STATUS
+.Ex -std rtadvd
+.Sh SEE ALSO
+.Xr rtadvd.conf 5 ,
+.Xr rtsol 8
+.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 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.
--- /dev/null
+/* $NetBSD: rtadvd.c,v 1.50 2015/06/15 04:15:33 ozaki-r Exp $ */
+/* $KAME: rtadvd.c,v 1.92 2005/10/17 14:40:02 suz 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/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#if defined(__NetBSD__) || defined(__minix)
+#include <util.h>
+#endif
+#include <poll.h>
+#include <pwd.h>
+
+#include "rtadvd.h"
+#include "rrenum.h"
+#include "advcap.h"
+#include "timer.h"
+#include "if.h"
+#include "config.h"
+#include "dump.h"
+
+struct msghdr rcvmhdr;
+static unsigned char *rcvcmsgbuf;
+static size_t rcvcmsgbuflen;
+static unsigned char *sndcmsgbuf;
+static size_t sndcmsgbuflen;
+volatile sig_atomic_t do_dump;
+volatile sig_atomic_t do_reconf;
+volatile sig_atomic_t do_die;
+struct msghdr sndmhdr;
+struct iovec rcviov[2];
+struct iovec sndiov[2];
+struct sockaddr_in6 rcvfrom;
+static const char *dumpfilename = "/var/run/rtadvd.dump"; /* XXX configurable */
+static char *mcastif;
+int sock;
+int rtsock = -1;
+int accept_rr = 0;
+int dflag = 0, sflag = 0;
+
+static char **if_argv;
+static int if_argc;
+
+char *conffile = NULL;
+
+struct ralist_head_t ralist = TAILQ_HEAD_INITIALIZER(ralist);
+
+struct nd_optlist {
+ TAILQ_ENTRY(nd_optlist) next;
+ struct nd_opt_hdr *opt;
+};
+union nd_opts {
+ struct nd_opt_hdr *nd_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) list;
+ } nd_opt_each;
+};
+#define nd_opts_src_lladdr nd_opt_each.src_lladdr
+#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr
+#define nd_opts_pi nd_opt_each.pi
+#define nd_opts_rh nd_opt_each.rh
+#define nd_opts_mtu nd_opt_each.mtu
+#define nd_opts_list nd_opt_each.list
+
+#define NDOPT_FLAG_SRCLINKADDR (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)
+
+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,
+};
+
+struct sockaddr_in6 sin6_linklocal_allnodes = {
+ .sin6_len = sizeof(sin6_linklocal_allnodes),
+ .sin6_family = AF_INET6,
+ .sin6_addr = IN6ADDR_LINKLOCAL_ALLNODES_INIT,
+};
+#ifdef notdef
+struct sockaddr_in6 sin6_linklocal_allrouters = {
+ .sin6_len = sizeof(sin6_linklocal_allrouters),
+ .sin6_family = AF_INET6,
+ .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT,
+};
+#endif
+struct sockaddr_in6 sin6_sitelocal_allrouters = {
+ .sin6_len = sizeof(sin6_sitelocal_allrouters),
+ .sin6_family = AF_INET6,
+ .sin6_addr = IN6ADDR_SITELOCAL_ALLROUTERS_INIT,
+};
+
+static void set_die(int);
+static void die(void);
+static void set_reconf(int);
+static void sock_open(void);
+static void rtsock_open(void);
+static void rtadvd_input(void);
+static void rs_input(int, struct nd_router_solicit *,
+ struct in6_pktinfo *, struct sockaddr_in6 *);
+static void ra_input(int, struct nd_router_advert *,
+ struct in6_pktinfo *, struct sockaddr_in6 *);
+static struct rainfo *ra_output(struct rainfo *);
+static int prefix_check(struct nd_opt_prefix_info *, struct rainfo *,
+ struct sockaddr_in6 *);
+static int nd6_options(struct nd_opt_hdr *, int, union nd_opts *, uint32_t);
+static void free_ndopts(union nd_opts *);
+static void rtmsg_input(void);
+static void rtadvd_set_dump_file(int);
+
+int
+main(int argc, char *argv[])
+{
+ struct pollfd set[2];
+ struct timespec *timeout;
+ int i, ch;
+ int fflag = 0, logopt;
+ struct passwd *pw;
+
+ /* get command line options and arguments */
+#define OPTIONS "c:dDfM:Rs"
+ while ((ch = getopt(argc, argv, OPTIONS)) != -1) {
+#undef OPTIONS
+ switch (ch) {
+ case 'c':
+ conffile = optarg;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'D':
+ dflag = 2;
+ 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;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc == 0) {
+ fprintf(stderr,
+ "usage: rtadvd [-DdfRs] [-c conffile]"
+ " [-M ifname] interface ...\n");
+ exit(1);
+ }
+
+ logopt = LOG_NDELAY | LOG_PID;
+ if (fflag)
+ logopt |= LOG_PERROR;
+ openlog("rtadvd", logopt, LOG_DAEMON);
+
+ /* set log level */
+ if (dflag == 0)
+ (void)setlogmask(LOG_UPTO(LOG_ERR));
+ if (dflag == 1)
+ (void)setlogmask(LOG_UPTO(LOG_INFO));
+
+ errno = 0; /* Ensure errno is 0 so we know if getpwnam errors or not */
+ if ((pw = getpwnam(RTADVD_USER)) == NULL) {
+ if (errno == 0)
+ syslog(LOG_ERR,
+ "user %s does not exist, aborting",
+ RTADVD_USER);
+ else
+ syslog(LOG_ERR, "getpwnam: %s: %m", RTADVD_USER);
+ exit(1);
+ }
+
+ /* timer initialization */
+ rtadvd_timer_init();
+
+ if_argc = argc;
+ if_argv = argv;
+ while (argc--)
+ getconfig(*argv++, 1);
+
+ if (!fflag)
+ daemon(1, 0);
+
+ sock_open();
+
+#if defined(__NetBSD__) || defined(__minix)
+ /* record the current PID */
+ if (pidfile(NULL) < 0) {
+ syslog(LOG_ERR,
+ "<%s> failed to open the pid log file, run anyway.",
+ __func__);
+ }
+#endif
+
+ set[0].fd = sock;
+ set[0].events = POLLIN;
+ if (sflag == 0) {
+ rtsock_open();
+ set[1].fd = rtsock;
+ set[1].events = POLLIN;
+ } else
+ set[1].fd = -1;
+
+ syslog(LOG_INFO, "dropping privileges to %s", RTADVD_USER);
+ if (chroot(pw->pw_dir) == -1) {
+ syslog(LOG_ERR, "chroot: %s: %m", pw->pw_dir);
+ exit(1);
+ }
+ if (chdir("/") == -1) {
+ syslog(LOG_ERR, "chdir: /: %m");
+ exit(1);
+ }
+ if (setgroups(1, &pw->pw_gid) == -1 ||
+ setgid(pw->pw_gid) == -1 ||
+ setuid(pw->pw_uid) == -1)
+ {
+ syslog(LOG_ERR, "failed to drop privileges: %m");
+ exit(1);
+ }
+
+ signal(SIGINT, set_die);
+ signal(SIGTERM, set_die);
+ signal(SIGHUP, set_reconf);
+ signal(SIGUSR1, rtadvd_set_dump_file);
+
+ for (;;) {
+ if (do_dump) { /* SIGUSR1 */
+ do_dump = 0;
+ rtadvd_dump_file(dumpfilename);
+ }
+
+ if (do_reconf) { /* SIGHUP */
+ do_reconf = 0;
+ syslog(LOG_INFO, "<%s> reloading config on SIGHUP",
+ __func__);
+ argc = if_argc;
+ argv = if_argv;
+ while (argc--)
+ getconfig(*argv++, 0);
+ }
+
+ /* timer expiration check and reset the timer */
+ timeout = rtadvd_check_timer();
+
+ if (do_die) {
+ die();
+ /*NOTREACHED*/
+ }
+
+ 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);
+ } else {
+ syslog(LOG_DEBUG,
+ "<%s> there's no timer. waiting for inputs",
+ __func__);
+ }
+
+ if ((i = poll(set, 2, timeout ? (timeout->tv_sec * 1000 +
+ (timeout->tv_nsec + 999999) / 1000000) : INFTIM)) < 0)
+ {
+ /* EINTR would occur upon SIGUSR1 for status dump */
+ if (errno != EINTR)
+ syslog(LOG_ERR, "<%s> poll: %m", __func__);
+ continue;
+ }
+ if (i == 0) /* timeout */
+ continue;
+ if (rtsock != -1 && set[1].revents & POLLIN)
+ rtmsg_input();
+ if (set[0].revents & POLLIN)
+ rtadvd_input();
+ }
+ exit(0); /* NOTREACHED */
+}
+
+static void
+rtadvd_set_dump_file(__unused int sig)
+{
+
+ do_dump = 1;
+}
+
+static void
+set_reconf(__unused int sig)
+{
+
+ do_reconf = 1;
+}
+
+static void
+set_die(__unused int sig)
+{
+
+ do_die = 1;
+}
+
+static void
+die(void)
+{
+ static int waiting;
+ struct rainfo *rai, *ran;
+ struct rdnss *rdnss;
+ struct dnssl *dnssl;
+
+ if (waiting) {
+ if (TAILQ_FIRST(&ralist)) {
+ syslog(LOG_INFO,
+ "<%s> waiting for expiration of all RA timers",
+ __func__);
+ return;
+ }
+ syslog(LOG_NOTICE, "<%s> gracefully terminated", __func__);
+ free(rcvcmsgbuf);
+ free(sndcmsgbuf);
+ exit(0);
+ /* NOT REACHED */
+ }
+
+ if (TAILQ_FIRST(&ralist) == NULL) {
+ syslog(LOG_NOTICE, "<%s> gracefully terminated", __func__);
+ exit(0);
+ /* NOT REACHED */
+ }
+
+ waiting = 1;
+ syslog(LOG_NOTICE, "<%s> final RA transmission started", __func__);
+
+ TAILQ_FOREACH_SAFE(rai, &ralist, next, ran) {
+ if (rai->leaving) {
+ TAILQ_REMOVE(&ralist, rai, next);
+ TAILQ_INSERT_HEAD(&ralist, rai->leaving, next);
+ rai->leaving->leaving = rai->leaving;
+ rai->leaving->leaving_for = rai->leaving;
+ free_rainfo(rai);
+ continue;
+ }
+ rai->lifetime = 0;
+ TAILQ_FOREACH(rdnss, &rai->rdnss, next)
+ rdnss->lifetime = 0;
+ TAILQ_FOREACH(dnssl, &rai->dnssl, next)
+ dnssl->lifetime = 0;
+ make_packet(rai);
+ rai->leaving = rai;
+ rai->leaving_for = rai;
+ rai->initcounter = MAX_INITIAL_RTR_ADVERTISEMENTS;
+ rai->mininterval = MIN_DELAY_BETWEEN_RAS;
+ rai->maxinterval = MIN_DELAY_BETWEEN_RAS;
+ rai->leaving_adv = MAX_FINAL_RTR_ADVERTISEMENTS;
+ ra_output(rai);
+ ra_timer_update((void *)rai, &rai->timer->tm);
+ rtadvd_set_timer(&rai->timer->tm, rai->timer);
+ }
+}
+
+static void
+rtmsg_input(void)
+{
+ int n, type, ifindex = 0, plen;
+ size_t len;
+ union rt_msghdr_buf {
+ struct rt_msghdr rt_msghdr;
+ char data[2048];
+ } buffer;
+ char *msg, *next, *lim, **argv;
+ char ifname[IF_NAMESIZE];
+ struct prefix *prefix;
+ struct rainfo *rai;
+ struct in6_addr *addr;
+ char addrbuf[INET6_ADDRSTRLEN];
+ int prefixchange = 0, argc;
+
+ memset(&buffer, 0, sizeof(buffer));
+ n = read(rtsock, &buffer, sizeof(buffer));
+
+ /* We read the buffer first to clear the FD */
+ if (do_die)
+ return;
+
+ msg = buffer.data;
+ if (dflag > 1) {
+ syslog(LOG_DEBUG, "<%s> received a routing message "
+ "(type = %d, len = %d)", __func__, rtmsg_type(msg),
+ rtmsg_len(msg));
+ }
+ if (n > rtmsg_len(msg)) {
+ /*
+ * This usually won't happen for messages received on
+ * a routing socket.
+ */
+ if (dflag > 1)
+ 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, rtmsg_len(msg));
+#if 0
+ /* adjust length */
+ n = rtmsg_len(msg);
+#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) |
+#ifdef RTM_IFANNOUNCE
+ RTADV_TYPE2BITMASK(RTM_IFANNOUNCE) |
+#endif
+ RTADV_TYPE2BITMASK(RTM_IFINFO));
+ if (len == 0)
+ break;
+ type = rtmsg_type(next);
+ switch (type) {
+ case RTM_ADD:
+ case RTM_DELETE:
+ ifindex = get_rtm_ifindex(next);
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifindex = get_ifam_ifindex(next);
+ break;
+#ifdef RTM_IFANNOUNCE
+ case RTM_IFANNOUNCE:
+ ifindex = get_ifan_ifindex(next);
+ if (get_ifan_what(next) == IFAN_ARRIVAL) {
+ syslog(LOG_DEBUG,
+ "<%s> interface %s arrived",
+ __func__,
+ if_indextoname(ifindex, ifname));
+ if (if_argc == 0) {
+ getconfig(ifname, 0);
+ continue;
+ }
+ argc = if_argc;
+ argv = if_argv;
+ while (argc--) {
+ if (strcmp(ifname, *argv++) == 0) {
+ getconfig(ifname, 0);
+ break;
+ }
+ }
+ continue;
+ }
+ break;
+#endif
+ case RTM_IFINFO:
+ ifindex = get_ifm_ifindex(next);
+ break;
+ default:
+ /* should not reach here */
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s:%d> unknown rtmsg %d on %s",
+ __func__, __LINE__, type,
+ if_indextoname(ifindex, ifname));
+ }
+ continue;
+ }
+
+ if ((rai = if_indextorainfo(ifindex)) == NULL) {
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s> route changed on "
+ "non advertising interface %s (%d)",
+ __func__,
+ if_indextoname(ifindex, ifname),
+ ifindex);
+ }
+ continue;
+ }
+ oldifflags = rai->ifflags;
+
+ switch (type) {
+ case RTM_ADD:
+ /* init ifflags because it may have changed */
+ rai->ifflags = if_getflags(ifindex, rai->ifflags);
+
+ 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;
+ }
+ prefix = find_prefix(rai, addr, plen);
+ if (prefix) {
+ if (prefix->timer) {
+ /*
+ * If the prefix has been invalidated,
+ * make it available again.
+ */
+ update_prefix(prefix);
+ prefixchange = 1;
+ } else if (dflag > 1) {
+ 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, INET6_ADDRSTRLEN),
+ plen, rai->ifname);
+ }
+ break;
+ }
+ make_prefix(rai, ifindex, addr, plen);
+ prefixchange = 1;
+ break;
+ case RTM_DELETE:
+ /* init ifflags because it may have changed */
+ rai->ifflags = if_getflags(ifindex, rai->ifflags);
+
+ 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;
+ }
+ prefix = find_prefix(rai, addr, plen);
+ if (prefix == NULL) {
+ if (dflag > 1) {
+ 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, INET6_ADDRSTRLEN),
+ plen, rai->ifname);
+ }
+ break;
+ }
+ invalidate_prefix(prefix);
+ prefixchange = 1;
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ /* init ifflags because it may have changed */
+ rai->ifflags = if_getflags(ifindex, rai->ifflags);
+ break;
+ case RTM_IFINFO:
+ rai->ifflags = get_ifm_flags(next);
+ break;
+#ifdef RTM_IFANNOUNCE
+ case RTM_IFANNOUNCE:
+ if (get_ifan_what(next) == IFAN_DEPARTURE) {
+ syslog(LOG_DEBUG,
+ "<%s> interface %s departed",
+ __func__, rai->ifname);
+ TAILQ_REMOVE(&ralist, rai, next);
+ if (rai->leaving)
+ free_rainfo(rai->leaving);
+ free_rainfo(rai);
+ continue;
+ }
+ break;
+#endif
+ default:
+ /* should not reach here */
+ if (dflag > 1) {
+ 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) != 0 && /* UP to DOWN */
+ (rai->ifflags & IFF_UP) == 0) {
+ syslog(LOG_INFO,
+ "<%s> interface %s becomes down. stop timer.",
+ __func__, rai->ifname);
+ rtadvd_remove_timer(&rai->timer);
+ } else if ((oldifflags & IFF_UP) == 0 && /* DOWN to UP */
+ (rai->ifflags & IFF_UP) != 0) {
+ syslog(LOG_INFO,
+ "<%s> interface %s becomes up. restart timer.",
+ __func__, rai->ifname);
+
+ rai->initcounter = 0; /* reset the counter */
+ rai->waiting = 0; /* XXX */
+ rtadvd_remove_timer(&rai->timer);
+ rai->timer = rtadvd_add_timer(ra_timeout,
+ ra_timer_update, rai, rai);
+ ra_timer_update((void *)rai, &rai->timer->tm);
+ rtadvd_set_timer(&rai->timer->tm, rai->timer);
+ } else if (prefixchange && rai->ifflags & IFF_UP) {
+ /*
+ * An advertised prefix has been added or invalidated.
+ * Will notice the change in a short delay.
+ */
+ rai->initcounter = 0;
+ ra_timer_set_short_delay(rai);
+ }
+ }
+
+ return;
+}
+
+void
+rtadvd_input(void)
+{
+ 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 rainfo *rai;
+
+ /*
+ * 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(sock, &rcvmhdr, 0)) < 0)
+ return;
+
+ /* We read the buffer first to clear the FD */
+ if (do_die)
+ 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,
+ "<%s> failed to get receiving interface",
+ __func__);
+ return;
+ }
+ if (hlimp == NULL) {
+ syslog(LOG_ERR,
+ "<%s> failed to get receiving hop limit",
+ __func__);
+ return;
+ }
+
+ if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == NULL) {
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s> received data for non advertising "
+ "interface (%s)",
+ __func__,
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ }
+ return;
+ }
+ /*
+ * If we happen to receive data on an interface which is now down,
+ * just discard the data.
+ */
+ if ((rai->ifflags & IFF_UP) == 0) {
+ syslog(LOG_INFO,
+ "<%s> received data on a disabled interface (%s)",
+ __func__,
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+#ifdef OLDRAWSOCKET
+ if ((size_t)i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
+ syslog(LOG_ERR,
+ "<%s> packet size(%d) is too short",
+ __func__, 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,
+ "<%s> packet size(%zd) is too short",
+ __func__, i);
+ return;
+ }
+
+ icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base;
+#endif
+
+ switch (icp->icmp6_type) {
+ case ND_ROUTER_SOLICIT:
+ /*
+ * Message verification - RFC-2461 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,
+ "<%s> RS with invalid hop limit(%d) "
+ "received from %s on %s",
+ __func__, *hlimp,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (icp->icmp6_code) {
+ syslog(LOG_NOTICE,
+ "<%s> RS with invalid ICMP6 code(%d) "
+ "received from %s on %s",
+ __func__, icp->icmp6_code,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if ((size_t)i < sizeof(struct nd_router_solicit)) {
+ syslog(LOG_NOTICE,
+ "<%s> RS from %s on %s does not have enough "
+ "length (len = %zd)",
+ __func__,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ 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-2461 6.1.2
+ * XXX: there's a same dilemma as above...
+ */
+ if (*hlimp != 255) {
+ syslog(LOG_NOTICE,
+ "<%s> RA with invalid hop limit(%d) "
+ "received from %s on %s",
+ __func__, *hlimp,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (icp->icmp6_code) {
+ syslog(LOG_NOTICE,
+ "<%s> RA with invalid ICMP6 code(%d) "
+ "received from %s on %s",
+ __func__, icp->icmp6_code,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if ((size_t)i < sizeof(struct nd_router_advert)) {
+ syslog(LOG_NOTICE,
+ "<%s> RA from %s on %s does not have enough "
+ "length (len = %zd)",
+ __func__,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
+ return;
+ }
+ ra_input(i, (struct nd_router_advert *)icp, pi, &rcvfrom);
+ break;
+ case ICMP6_ROUTER_RENUMBERING:
+ if (accept_rr == 0) {
+ syslog(LOG_ERR, "<%s> received a router renumbering "
+ "message, but not allowed to be accepted",
+ __func__);
+ 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, "<%s> invalid icmp type(%d)",
+ __func__, 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], ifnamebuf[IFNAMSIZ];
+ union nd_opts ndopts;
+ struct rainfo *rai;
+ struct soliciter *sol;
+
+ syslog(LOG_DEBUG,
+ "<%s> RS received from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* ND option check */
+ memset(&ndopts, 0, sizeof(ndopts));
+ TAILQ_INIT(&ndopts.nd_opts_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, INET6_ADDRSTRLEN),
+ 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-2461 6.1.1)
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&from->sin6_addr) &&
+ ndopts.nd_opts_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;
+ }
+
+ if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == NULL) {
+ syslog(LOG_INFO,
+ "<%s> RS received on non advertising interface(%s)",
+ __func__,
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+
+ if (rai->leaving) {
+ syslog(LOG_INFO,
+ "<%s> RS received on reconfiguring advertising interface(%s)",
+ __func__, rai->ifname);
+ goto done;
+ }
+
+ rai->rsinput++; /* increment statistics */
+
+ /*
+ * Decide whether to send RA according to the rate-limit
+ * consideration.
+ */
+
+ /* record sockaddr waiting for RA, if possible */
+ sol = malloc(sizeof(*sol));
+ if (sol) {
+ sol->addr = *from;
+ /* XXX RFC2553 need clarification on flowinfo */
+ sol->addr.sin6_flowinfo = 0;
+ TAILQ_INSERT_HEAD(&rai->soliciter, sol, next);
+ }
+
+ /*
+ * If there is already a waiting RS packet, don't
+ * update the timer.
+ */
+ if (rai->waiting++)
+ goto done;
+
+ ra_timer_set_short_delay(rai);
+
+done:
+ free_ndopts(&ndopts);
+ return;
+}
+
+void
+ra_timer_set_short_delay(struct rainfo *rai)
+{
+ long delay; /* must not be greater than 1000000 */
+ struct timespec interval, now, min_delay, tm_tmp, *rest;
+
+ /*
+ * 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. RFC2461 6.2.6
+ */
+ delay = arc4random() % MAX_RA_DELAY_TIME;
+ interval.tv_sec = 0;
+ interval.tv_nsec = delay;
+ rest = rtadvd_timer_rest(rai->timer);
+ if (timespeccmp(rest, &interval, <)) {
+ syslog(LOG_DEBUG, "<%s> random delay is larger than "
+ "the rest of 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, &now);
+ timespecsub(&now, &rai->lastsent, &tm_tmp);
+ min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS;
+ min_delay.tv_nsec = 0;
+ if (timespeccmp(&tm_tmp, &min_delay, <)) {
+ timespecsub(&min_delay, &tm_tmp, &min_delay);
+ timespecadd(&min_delay, &interval, &interval);
+ }
+ rtadvd_set_timer(&interval, rai->timer);
+}
+
+static void
+ra_input(int len, struct nd_router_advert *ra,
+ struct in6_pktinfo *pi, struct sockaddr_in6 *from)
+{
+ struct rainfo *rai;
+ char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ union nd_opts ndopts;
+ const char *on_off[] = {"OFF", "ON"};
+ uint32_t reachabletime, retranstimer, mtu;
+ struct nd_optlist *optp;
+ int inconsistent = 0;
+
+ syslog(LOG_DEBUG,
+ "<%s> RA received from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* ND option check */
+ memset(&ndopts, 0, sizeof(ndopts));
+ TAILQ_INIT(&ndopts.nd_opts_list);
+ if (nd6_options((struct nd_opt_hdr *)(ra + 1),
+ len - sizeof(struct nd_router_advert),
+ &ndopts, NDOPT_FLAG_SRCLINKADDR |
+ NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU |
+ NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL))
+ {
+ syslog(LOG_INFO,
+ "<%s> ND option check failed for an RA from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /*
+ * RA consistency check according to RFC-2461 6.2.7
+ */
+ if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == 0) {
+ syslog(LOG_INFO,
+ "<%s> received RA from %s on non-advertising"
+ " interface(%s)",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+ if (rai->leaving) {
+ syslog(LOG_DEBUG,
+ "<%s> received RA on re-configuring interface (%s)",
+ __func__, rai->ifname);
+ goto done;
+ }
+ rai->rainput++; /* increment statistics */
+
+ /* Cur Hop Limit value */
+ if (ra->nd_ra_curhoplimit && rai->hoplimit &&
+ ra->nd_ra_curhoplimit != rai->hoplimit) {
+ syslog(LOG_INFO,
+ "<%s> CurHopLimit inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ rai->ifname,
+ ra->nd_ra_curhoplimit,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->hoplimit);
+ inconsistent++;
+ }
+ /* M flag */
+ if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) !=
+ rai->managedflg) {
+ syslog(LOG_INFO,
+ "<%s> M flag inconsistent on %s:"
+ " %s from %s, %s from us",
+ __func__,
+ rai->ifname,
+ on_off[!rai->managedflg],
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ on_off[rai->managedflg]);
+ inconsistent++;
+ }
+ /* O flag */
+ if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) !=
+ rai->otherflg) {
+ syslog(LOG_INFO,
+ "<%s> O flag inconsistent on %s:"
+ " %s from %s, %s from us",
+ __func__,
+ rai->ifname,
+ on_off[!rai->otherflg],
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ on_off[rai->otherflg]);
+ inconsistent++;
+ }
+ /* Reachable Time */
+ reachabletime = ntohl(ra->nd_ra_reachable);
+ if (reachabletime && rai->reachabletime &&
+ reachabletime != rai->reachabletime) {
+ syslog(LOG_INFO,
+ "<%s> ReachableTime inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ rai->ifname,
+ reachabletime,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->reachabletime);
+ inconsistent++;
+ }
+ /* Retrans Timer */
+ retranstimer = ntohl(ra->nd_ra_retransmit);
+ if (retranstimer && rai->retranstimer &&
+ retranstimer != rai->retranstimer) {
+ syslog(LOG_INFO,
+ "<%s> RetranceTimer inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ rai->ifname,
+ retranstimer,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->retranstimer);
+ inconsistent++;
+ }
+ /* Values in the MTU options */
+ if (ndopts.nd_opts_mtu) {
+ mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
+ if (mtu && rai->linkmtu && mtu != rai->linkmtu) {
+ syslog(LOG_INFO,
+ "<%s> MTU option value inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ rai->ifname, mtu,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->linkmtu);
+ inconsistent++;
+ }
+ }
+ /* Preferred and Valid Lifetimes for prefixes */
+ if (ndopts.nd_opts_pi)
+ if (prefix_check(ndopts.nd_opts_pi, rai, from))
+ inconsistent++;
+ TAILQ_FOREACH(optp, &ndopts.nd_opts_list, next)
+ if (prefix_check((struct nd_opt_prefix_info *)optp->opt,
+ rai, from))
+ inconsistent++;
+
+ if (inconsistent)
+ rai->rainconsistent++;
+
+done:
+ free_ndopts(&ndopts);
+ return;
+}
+
+/* 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)
+{
+ uint32_t preferred_time, valid_time;
+ struct prefix *pp;
+ int inconsistent = 0;
+ char ntopbuf[INET6_ADDRSTRLEN], prefixbuf[INET6_ADDRSTRLEN];
+ struct timespec now;
+
+#if 0 /* impossible */
+ if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION)
+ return(0);
+#endif
+
+ /*
+ * 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, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->ifname);
+ }
+
+ if ((pp = 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, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->ifname);
+ return(0);
+ }
+
+ preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time);
+ if (pp->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, &now);
+ preferred_time += now.tv_sec;
+
+ if (!pp->timer && rai->clockskew &&
+ llabs((long long)preferred_time - pp->pltimeexpire) > rai->clockskew) {
+ syslog(LOG_INFO,
+ "<%s> preferred lifetime for %s/%d"
+ " (decr. in real time) inconsistent on %s:"
+ " %d from %s, %ld from us",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->pltimeexpire);
+ inconsistent++;
+ }
+ } else if (!pp->timer && preferred_time != pp->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, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->preflifetime);
+ }
+
+ valid_time = ntohl(pinfo->nd_opt_pi_valid_time);
+ if (pp->vltimeexpire) {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ valid_time += now.tv_sec;
+
+ if (!pp->timer && rai->clockskew &&
+ llabs((long long)valid_time - pp->vltimeexpire) > rai->clockskew) {
+ syslog(LOG_INFO,
+ "<%s> valid lifetime for %s/%d"
+ " (decr. in real time) inconsistent on %s:"
+ " %d from %s, %ld from us",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->vltimeexpire);
+ inconsistent++;
+ }
+ } else if (!pp->timer && valid_time != pp->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, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, valid_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->validlifetime);
+ inconsistent++;
+ }
+
+ return(inconsistent);
+}
+
+struct prefix *
+find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen)
+{
+ struct prefix *pp;
+ int bytelen, bitlen;
+ unsigned char bitmask;
+
+ TAILQ_FOREACH(pp, &rai->prefix, next) {
+ if (plen != pp->prefixlen)
+ continue;
+ bytelen = plen / 8;
+ bitlen = plen % 8;
+ bitmask = 0xff << (8 - bitlen);
+ if (memcmp((void *)prefix, (void *)&pp->prefix, bytelen))
+ continue;
+ if (bitlen == 0 ||
+ ((prefix->s6_addr[bytelen] & bitmask) ==
+ (pp->prefix.s6_addr[bytelen] & bitmask))) {
+ return(pp);
+ }
+ }
+
+ 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;
+ unsigned 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_opts *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 *)((char *)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.
+ */
+ if ((hdr->nd_opt_type == ND_OPT_MTU &&
+ (optlen != sizeof(struct nd_opt_mtu))) ||
+ ((hdr->nd_opt_type == ND_OPT_PREFIX_INFORMATION &&
+ optlen != sizeof(struct nd_opt_prefix_info))) ||
+ (hdr->nd_opt_type == ND_OPT_RDNSS &&
+ ((optlen < (int)sizeof(struct nd_opt_rdnss) ||
+ (optlen - sizeof(struct nd_opt_rdnss)) % 16 != 0))) ||
+ (hdr->nd_opt_type == ND_OPT_DNSSL &&
+ optlen < (int)sizeof(struct nd_opt_dnssl)))
+ {
+ 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->nd_opt_array[hdr->nd_opt_type]) {
+ syslog(LOG_INFO,
+ "<%s> duplicated ND option (type = %d)",
+ __func__, hdr->nd_opt_type);
+ }
+ ndopts->nd_opt_array[hdr->nd_opt_type] = hdr;
+ break;
+ case ND_OPT_PREFIX_INFORMATION:
+ {
+ struct nd_optlist *pfxlist;
+
+ if (ndopts->nd_opts_pi == 0) {
+ ndopts->nd_opts_pi =
+ (struct nd_opt_prefix_info *)hdr;
+ continue;
+ }
+ if ((pfxlist = malloc(sizeof(*pfxlist))) == NULL) {
+ syslog(LOG_ERR, "<%s> can't allocate memory",
+ __func__);
+ goto bad;
+ }
+ pfxlist->opt = hdr;
+ TAILQ_INSERT_TAIL(&ndopts->nd_opts_list, pfxlist, next);
+
+ break;
+ }
+ default: /* impossible */
+ break;
+ }
+ }
+
+ return(0);
+
+ bad:
+ free_ndopts(ndopts);
+
+ return(-1);
+}
+
+static void
+free_ndopts(union nd_opts *ndopts)
+{
+ struct nd_optlist *opt;
+
+ while ((opt = TAILQ_FIRST(&ndopts->nd_opts_list)) != NULL) {
+ TAILQ_REMOVE(&ndopts->nd_opts_list, opt, next);
+ free(opt);
+ }
+}
+
+void
+sock_open(void)
+{
+ struct icmp6_filter filt;
+ struct ipv6_mreq mreq;
+ struct rainfo *ra;
+ int on;
+ /* XXX: should be max MTU attached to the node */
+ static unsigned char answer[1500];
+
+ rcvcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ rcvcmsgbuf = malloc(rcvcmsgbuflen);
+ if (rcvcmsgbuf == NULL) {
+ syslog(LOG_ERR, "<%s> malloc: %m", __func__);
+ exit(1);
+ }
+
+ sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo));
+ sndcmsgbuf = malloc(sndcmsgbuflen);
+ if (sndcmsgbuf == NULL) {
+ syslog(LOG_ERR, "<%s> malloc: %m", __func__);
+ exit(1);
+ }
+
+ if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %m", __func__);
+ exit(1);
+ }
+
+ /* RFC 4861 Section 4.2 */
+ on = 255;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &on,
+ sizeof(on)) == -1) {
+ syslog(LOG_ERR, "<%s> IPV6_MULTICAST_HOPS: %m", __func__);
+ exit(1);
+ }
+
+ /* specify to tell receiving interface */
+ on = 1;
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_RECVPKTINFO: %m", __func__);
+ exit(1);
+ }
+#else /* old adv. API */
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_PKTINFO: %m", __func__);
+ exit(1);
+ }
+#endif
+
+ on = 1;
+ /* specify to tell value of hoplimit field of received IP6 hdr */
+#ifdef IPV6_RECVHOPLIMIT
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_RECVHOPLIMIT: %m", __func__);
+ exit(1);
+ }
+#else /* old adv. API */
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_HOPLIMIT: %m", __func__);
+ exit(1);
+ }
+#endif
+
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
+ if (accept_rr)
+ ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt);
+ if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0) {
+ syslog(LOG_ERR, "<%s> IICMP6_FILTER: %m", __func__);
+ exit(1);
+ }
+
+ /*
+ * join all routers multicast address on each advertising interface.
+ */
+ if (inet_pton(AF_INET6, ALLROUTERS_LINK,
+ mreq.ipv6mr_multiaddr.s6_addr) != 1)
+ {
+ syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)",
+ __func__);
+ exit(1);
+ }
+ TAILQ_FOREACH(ra, &ralist, next) {
+ mreq.ipv6mr_interface = ra->ifindex;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
+ sizeof(mreq)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP(link) on %s: %m",
+ __func__, ra->ifname);
+ exit(1);
+ }
+ }
+
+ /*
+ * When attending router renumbering, join all-routers site-local
+ * multicast group.
+ */
+ if (accept_rr) {
+ if (inet_pton(AF_INET6, ALLROUTERS_SITE,
+ mreq.ipv6mr_multiaddr.s6_addr) != 1)
+ {
+ syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)",
+ __func__);
+ exit(1);
+ }
+ ra = TAILQ_FIRST(&ralist);
+ if (mcastif) {
+ if ((mreq.ipv6mr_interface = if_nametoindex(mcastif))
+ == 0) {
+ syslog(LOG_ERR,
+ "<%s> invalid interface: %s",
+ __func__, mcastif);
+ exit(1);
+ }
+ } else
+ mreq.ipv6mr_interface = ra->ifindex;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq, sizeof(mreq)) < 0) {
+ syslog(LOG_ERR,
+ "<%s> IPV6_JOIN_GROUP(site) on %s: %m",
+ __func__,
+ mcastif ? mcastif : ra->ifname);
+ exit(1);
+ }
+ }
+
+ /* initialize msghdr for receiving packets */
+ rcviov[0].iov_base = answer;
+ rcviov[0].iov_len = sizeof(answer);
+ rcvmhdr.msg_name = &rcvfrom;
+ rcvmhdr.msg_namelen = sizeof(rcvfrom);
+ rcvmhdr.msg_iov = rcviov;
+ rcvmhdr.msg_iovlen = 1;
+ rcvmhdr.msg_control = 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 = (void *)sndcmsgbuf;
+ sndmhdr.msg_controllen = sndcmsgbuflen;
+
+ return;
+}
+
+/* open a routing socket to watch the routing table */
+static void
+rtsock_open(void)
+{
+ if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %m", __func__);
+ exit(1);
+ }
+}
+
+struct rainfo *
+if_indextorainfo(unsigned int idx)
+{
+ struct rainfo *rai;
+
+ TAILQ_FOREACH(rai, &ralist, next) {
+ if (rai->ifindex == idx)
+ return(rai);
+ }
+
+ return(NULL); /* search failed */
+}
+
+struct rainfo *
+ra_output(struct rainfo *rai)
+{
+ int i;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi;
+ struct soliciter *sol;
+
+ if ((rai->ifflags & IFF_UP) == 0) {
+ syslog(LOG_DEBUG, "<%s> %s is not up, skip sending RA",
+ __func__, rai->ifname);
+ return NULL;
+ }
+
+ make_packet(rai); /* XXX: inefficient */
+
+ sndmhdr.msg_name = (void *)&sin6_linklocal_allnodes;
+ sndmhdr.msg_iov[0].iov_base = (void *)rai->ra_data;
+ sndmhdr.msg_iov[0].iov_len = 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 = rai->ifindex;
+
+ syslog(LOG_DEBUG,
+ "<%s> send RA on %s, # of waitings = %d",
+ __func__, rai->ifname, rai->waiting);
+
+ i = sendmsg(sock, &sndmhdr, 0);
+
+ if (i < 0 || (size_t)i != rai->ra_datalen) {
+ if (i < 0) {
+ syslog(LOG_ERR, "<%s> sendmsg on %s: %m",
+ __func__, rai->ifname);
+ }
+ }
+
+ /*
+ * unicast advertisements
+ * XXX commented out. reason: though spec does not forbit it, unicast
+ * advert does not really help
+ */
+ while ((sol = TAILQ_FIRST(&rai->soliciter)) != NULL) {
+#if 0
+ sndmhdr.msg_name = (void *)&sol->addr;
+ i = sendmsg(sock, &sndmhdr, 0);
+ if (i < 0 || i != rai->ra_datalen) {
+ if (i < 0) {
+ syslog(LOG_ERR,
+ "<%s> unicast sendmsg on %s: %m",
+ __func__, rai->ifname);
+ }
+ }
+#endif
+ TAILQ_REMOVE(&rai->soliciter, sol, next);
+ free(sol);
+ }
+
+ if (rai->leaving_adv > 0) {
+ if (--(rai->leaving_adv) == 0) {
+ /* leaving for ourself means we're shutting down */
+ if (rai->leaving_for == rai) {
+ TAILQ_REMOVE(&ralist, rai, next);
+ free_rainfo(rai);
+ return NULL;
+ }
+ syslog(LOG_DEBUG,
+ "<%s> expired RA,"
+ " new config active for interface (%s)",
+ __func__, rai->ifname);
+ rai->leaving_for->timer = rtadvd_add_timer(ra_timeout,
+ ra_timer_update,
+ rai->leaving_for, rai->leaving_for);
+ ra_timer_set_short_delay(rai->leaving_for);
+ rai->leaving_for->leaving = NULL;
+ free_rainfo(rai);
+ return NULL;
+ }
+ }
+
+ /* update counter */
+ if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS)
+ rai->initcounter++;
+ rai->raoutput++;
+
+ /* update timestamp */
+ clock_gettime(CLOCK_MONOTONIC, &rai->lastsent);
+
+ /* reset waiting conter */
+ rai->waiting = 0;
+
+ return rai;
+}
+
+/* process RA timer */
+struct rtadvd_timer *
+ra_timeout(void *data)
+{
+ struct rainfo *rai = (struct rainfo *)data;
+
+#ifdef notyet
+ /* if necessary, reconstruct the packet. */
+#endif
+
+ syslog(LOG_DEBUG,
+ "<%s> RA timer on %s is expired",
+ __func__, rai->ifname);
+
+ if (ra_output(rai))
+ return(rai->timer);
+ return NULL;
+}
+
+/* update RA timer */
+void
+ra_timer_update(void *data, struct timespec *tm)
+{
+ struct rainfo *rai = (struct rainfo *)data;
+ long interval;
+
+ /*
+ * 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 (RFC2461 6.2.4).
+ */
+ interval = rai->mininterval;
+ if (rai->mininterval != rai->maxinterval)
+ interval += arc4random() % (rai->maxinterval-rai->mininterval);
+
+ /*
+ * 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-2461 6.2.4)
+ */
+ if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS &&
+ interval > MAX_INITIAL_RTR_ADVERT_INTERVAL)
+ interval = MAX_INITIAL_RTR_ADVERT_INTERVAL;
+
+ tm->tv_sec = interval;
+ tm->tv_nsec = 0;
+
+ syslog(LOG_DEBUG,
+ "<%s> RA timer on %s is set to %ld:%ld",
+ __func__, rai->ifname,
+ (long int)tm->tv_sec, (long int)tm->tv_nsec);
+
+ return;
+}
--- /dev/null
+# $NetBSD: rtadvd.conf,v 1.4 2009/11/01 15:17:59 jakllsch Exp $
+# $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:
--- /dev/null
+.\" $NetBSD: rtadvd.conf.5,v 1.18 2012/12/11 16:37:23 roy Exp $
+.\" $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.
+.\"
+.Dd December 11, 2012
+.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 capfile 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 sting 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, unless the
+.Cm noifprefix
+flag is specified.
+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) Specified whether
+.Nm rtadvd
+should gather prefix information from the interface if no
+.Cm addr
+is specified.
+If no
+.Cm addr
+is given, and
+.Cm noifprefix
+is set,
+.Nm rtadvd
+will send RA packets with no prefix information.
+.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 sting 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 capfile 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 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 capfile 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 the
+.Xr resolv.conf 5
+manual page for the resolver implementation.
+.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 the
+.Xr resolv.conf 5
+manual page for the resolver implementation.
+.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 capfile 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
+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
+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
+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
+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 capfile 5 ,
+.Xr rtadvd 8 ,
+.Xr rtsol 8
+.Pp
+Thomas Narten, Erik Nordmark and W. A. Simpson,
+.Do
+Neighbor Discovery for IP version 6 (IPv6)
+.Dc ,
+RFC 2461
+.Pp
+Richard Draves,
+.Do
+Default Router Preferences and More-Specific Routes
+.Dc ,
+RFC 4191
+.Pp
+J. Jeong, S. Park, L. Beloeil, S. Madanapalli
+.Do
+IPv6 Router Advertisement Options for DNS Configuration
+.Dc ,
+RFC 6106
+.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)
--- /dev/null
+/* $NetBSD: rtadvd.h,v 1.14 2015/06/05 14:09:20 roy Exp $ */
+/* $KAME: rtadvd.h,v 1.30 2005/10/17 14:40:02 suz 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 RTADVD_USER "_rtadvd"
+
+#define ALLNODES "ff02::1"
+#define ALLROUTERS_LINK "ff02::2"
+#define ALLROUTERS_SITE "ff05::2"
+
+#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;
+
+/* 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 500000000 /* nsec */
+
+#define PREFIX_FROM_KERNEL 1
+#define PREFIX_FROM_CONFIG 2
+#define PREFIX_FROM_DYNAMIC 3
+
+struct prefix {
+ TAILQ_ENTRY(prefix) next;
+
+ struct rainfo *rainfo; /* back pointer to the interface */
+
+ struct rtadvd_timer *timer; /* expiration timer. used when a prefix
+ * derived from the kernel is deleted.
+ */
+
+ uint32_t validlifetime; /* AdvValidLifetime */
+ long vltimeexpire; /* expiration of vltime; decrement case only */
+ uint32_t preflifetime; /* AdvPreferredLifetime */
+ long pltimeexpire; /* expiration of pltime; decrement case only */
+ uint16_t onlinkflg; /* bool: AdvOnLinkFlag */
+ uint16_t autoconfflg; /* bool: AdvAutonomousFlag */
+ int prefixlen;
+ int origin; /* from kernel or config */
+ struct in6_addr prefix;
+};
+
+struct rtinfo {
+ TAILQ_ENTRY(rtinfo) next;
+
+ uint32_t ltime; /* route lifetime */
+ uint16_t rtpref; /* route preference */
+ int prefixlen;
+ struct in6_addr prefix;
+};
+
+struct rdnss_addr {
+ TAILQ_ENTRY(rdnss_addr) next;
+
+ struct in6_addr addr;
+};
+
+struct rdnss {
+ TAILQ_ENTRY(rdnss) next;
+
+ TAILQ_HEAD(, rdnss_addr) list;
+ uint32_t lifetime;
+};
+
+struct dnssl_domain {
+ TAILQ_ENTRY(dnssl_domain) next;
+
+ int len;
+ char domain[256];
+};
+
+struct dnssl {
+ TAILQ_ENTRY(dnssl) next;
+
+ TAILQ_HEAD(, dnssl_domain) list;
+ uint32_t lifetime;
+};
+
+struct soliciter {
+ TAILQ_ENTRY(soliciter) next;
+
+ struct sockaddr_in6 addr;
+};
+
+struct rainfo {
+ TAILQ_ENTRY(rainfo) next;
+
+ /* timer related parameters */
+ struct rtadvd_timer *timer;
+ int initcounter; /* counter for the first few advertisements */
+ struct timespec lastsent; /* timestamp when the latest RA was sent */
+ int waiting; /* number of RS waiting for RA */
+ struct rainfo *leaving; /* the config which is leaving */
+ struct rainfo *leaving_for; /* the new config to activate */
+ int leaving_adv; /* number of RA left to send */
+
+ /* interface information */
+ uint16_t ifindex;
+ int ifflags;
+ int advlinkopt; /* bool: whether include link-layer addr opt */
+ struct sockaddr_dl *sdl;
+ char ifname[16];
+ uint32_t phymtu; /* mtu of the physical interface */
+
+ /* Router configuration variables */
+ uint16_t lifetime; /* AdvDefaultLifetime */
+ uint16_t maxinterval; /* MaxRtrAdvInterval */
+ uint16_t mininterval; /* MinRtrAdvInterval */
+ int managedflg; /* AdvManagedFlag */
+ int otherflg; /* AdvOtherConfigFlag */
+
+ int rtpref; /* router preference */
+ uint32_t linkmtu; /* AdvLinkMTU */
+ uint32_t reachabletime; /* AdvReachableTime */
+ uint32_t retranstimer; /* AdvRetransTimer */
+ uint16_t hoplimit; /* AdvCurHopLimit */
+ TAILQ_HEAD(, prefix) prefix; /* AdvPrefixList(link head) */
+ int pfxs;
+ uint16_t clockskew;/* used for consisitency check of lifetimes */
+
+ TAILQ_HEAD(, rtinfo) route;
+ TAILQ_HEAD(, rdnss) rdnss; /* RDNSS list */
+ TAILQ_HEAD(, dnssl) dnssl; /* DNS Search List */
+
+ /* actual RA packet data and its length */
+ size_t ra_datalen;
+ char *ra_data;
+
+ /* statistics */
+ uint64_t raoutput; /* number of RAs sent */
+ uint64_t rainput; /* number of RAs received */
+ uint64_t rainconsistent; /* number of RAs inconsistent with ours */
+ uint64_t rsinput; /* number of RSs received */
+
+ /* info about soliciter */
+ TAILQ_HEAD(, soliciter) soliciter; /* recent solication source */
+};
+
+extern TAILQ_HEAD(ralist_head_t, rainfo) ralist;
+
+struct rtadvd_timer *ra_timeout(void *);
+void ra_timer_update(void *, struct timespec *);
+void ra_timer_set_short_delay(struct rainfo *);
+
+int prefix_match(struct in6_addr *, int, struct in6_addr *, int);
+struct rainfo *if_indextorainfo(unsigned int);
+struct prefix *find_prefix(struct rainfo *, struct in6_addr *, int);
--- /dev/null
+/* $NetBSD: timer.c,v 1.12 2015/06/05 14:09:20 roy Exp $ */
+/* $KAME: timer.c,v 1.11 2005/04/14 06:22:35 suz 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/time.h>
+
+#include <limits.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <search.h>
+#include "timer.h"
+
+struct rtadvd_timer_head_t ra_timer = TAILQ_HEAD_INITIALIZER(ra_timer);
+static struct timespec tm_limit = { LONG_MAX, 1000000000L - 1 };
+static struct timespec tm_max;
+
+void
+rtadvd_timer_init(void)
+{
+
+ TAILQ_INIT(&ra_timer);
+ tm_max = tm_limit;
+}
+
+struct rtadvd_timer *
+rtadvd_add_timer(struct rtadvd_timer *(*timeout) (void *),
+ void (*update) (void *, struct timespec *),
+ void *timeodata, void *updatedata)
+{
+ struct rtadvd_timer *newtimer;
+
+ if ((newtimer = malloc(sizeof(*newtimer))) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't allocate memory", __func__);
+ exit(1);
+ }
+
+ memset(newtimer, 0, sizeof(*newtimer));
+
+ if (timeout == NULL) {
+ syslog(LOG_ERR,
+ "<%s> timeout function unspecified", __func__);
+ exit(1);
+ }
+ newtimer->expire = timeout;
+ newtimer->update = update;
+ newtimer->expire_data = timeodata;
+ newtimer->update_data = updatedata;
+ newtimer->tm = tm_max;
+
+ /* link into chain */
+ TAILQ_INSERT_TAIL(&ra_timer, newtimer, next);
+
+ return(newtimer);
+}
+
+void
+rtadvd_remove_timer(struct rtadvd_timer **timer)
+{
+
+ if (*timer) {
+ TAILQ_REMOVE(&ra_timer, *timer, next);
+ free(*timer);
+ *timer = NULL;
+ }
+}
+
+void
+rtadvd_set_timer(struct timespec *tm, struct rtadvd_timer *timer)
+{
+ struct timespec now;
+
+ /* reset the timer */
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ timespecadd(&now, tm, &timer->tm);
+
+ /* upate the next expiration time */
+ if (timespeccmp(&timer->tm, &tm_max, <))
+ tm_max = timer->tm;
+}
+
+/*
+ * 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 *tm, *tmn;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ tm_max = tm_limit;
+
+ TAILQ_FOREACH_SAFE(tm, &ra_timer, next, tmn) {
+ if (timespeccmp(&tm->tm, &now, <=)) {
+ if ((*tm->expire)(tm->expire_data) == NULL)
+ continue; /* the timer was removed */
+ if (tm->update)
+ (*tm->update)(tm->update_data, &tm->tm);
+ timespecadd(&tm->tm, &now, &tm->tm);
+ }
+ if (timespeccmp(&tm->tm, &tm_max, <))
+ tm_max = tm->tm;
+ }
+
+ if (timespeccmp(&tm_max, &tm_limit, ==))
+ return(NULL);
+ if (timespeccmp(&tm_max, &now, <)) {
+ /* this may occur when the interval is too small */
+ timespecclear(&returnval);
+ } else
+ timespecsub(&tm_max, &now, &returnval);
+ return(&returnval);
+}
+
+struct timespec *
+rtadvd_timer_rest(struct rtadvd_timer *timer)
+{
+ static struct timespec returnval;
+ struct timespec now;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ if (timespeccmp(&timer->tm, &now, <=)) {
+ syslog(LOG_DEBUG,
+ "<%s> a timer must be expired, but not yet",
+ __func__);
+ returnval.tv_sec = 0;
+ returnval.tv_nsec = 0;
+ }
+ else
+ timespecsub(&timer->tm, &now, &returnval);
+
+ return(&returnval);
+}
--- /dev/null
+/* $NetBSD: timer.h,v 1.8 2015/06/05 14:09:20 roy Exp $ */
+/* $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) next;
+ struct rainfo *rai;
+ struct timespec tm;
+
+ struct rtadvd_timer *(*expire) (void *); /* expiration function */
+ void *expire_data;
+ void (*update)(void *, struct timespec *); /* update function */
+ void *update_data;
+};
+
+void rtadvd_timer_init(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);
+struct timespec * rtadvd_timer_rest(struct rtadvd_timer *);