]> Zhao Yanbai Git Server - minix.git/commitdiff
Import NetBSD rtadvd(8) 72/3472/1
authorDavid van Moolenbroek <david@minix3.org>
Sun, 19 Feb 2017 14:27:23 +0000 (14:27 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Tue, 21 Mar 2017 22:00:28 +0000 (22:00 +0000)
Change-Id: I22626843d85c78f0fadefd58d61d7a85d285b2b8

26 files changed:
distrib/sets/lists/minix-base/mi
distrib/sets/lists/minix-debug/mi
distrib/sets/lists/minix-man/mi
etc/mtree/NetBSD.dist.base
etc/rc.d/Makefile
etc/rc.d/rtadvd [new file with mode: 0755]
usr.sbin/Makefile
usr.sbin/rtadvd/Makefile [new file with mode: 0644]
usr.sbin/rtadvd/advcap.c [new file with mode: 0644]
usr.sbin/rtadvd/advcap.h [new file with mode: 0644]
usr.sbin/rtadvd/config.c [new file with mode: 0644]
usr.sbin/rtadvd/config.h [new file with mode: 0644]
usr.sbin/rtadvd/dump.c [new file with mode: 0644]
usr.sbin/rtadvd/dump.h [new file with mode: 0644]
usr.sbin/rtadvd/if.c [new file with mode: 0644]
usr.sbin/rtadvd/if.h [new file with mode: 0644]
usr.sbin/rtadvd/pathnames.h [new file with mode: 0644]
usr.sbin/rtadvd/rrenum.c [new file with mode: 0644]
usr.sbin/rtadvd/rrenum.h [new file with mode: 0644]
usr.sbin/rtadvd/rtadvd.8 [new file with mode: 0644]
usr.sbin/rtadvd/rtadvd.c [new file with mode: 0644]
usr.sbin/rtadvd/rtadvd.conf [new file with mode: 0644]
usr.sbin/rtadvd/rtadvd.conf.5 [new file with mode: 0644]
usr.sbin/rtadvd/rtadvd.h [new file with mode: 0644]
usr.sbin/rtadvd/timer.c [new file with mode: 0644]
usr.sbin/rtadvd/timer.h [new file with mode: 0644]

index 65054a0e0a851ec44e002f39f277503ae64754bd..377a5be8b867ce3a50939f98087e120e44f62525 100644 (file)
 ./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
index 9a7b780f7f3af2ac3af392de3ef67cb7ba205348..c038f3805fcf9c5bd61c6b19b70e69dc002810a1 100644 (file)
 ./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
index b8481021232c9eed0fdf1af4918897970714395e..8ff7ee84cb968997f5358701879aa4508a770110 100644 (file)
 ./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
index 96f36e31cb065372ab17ff134ea3a70a00cc08e9..66a52f826aa75a9c37062e048066467e5c08da2d 100644 (file)
 ./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
index b9021d6e8c15e61cc82e4dad7ffae9da82c70137..08562a2126fc417da631ff291b0946ff735d7f76 100755 (executable)
@@ -45,7 +45,7 @@ CONFIGFILES=\
                pwcheck \
                \
                \
-               root \
+               root rtadvd \
                \
                \
                sysctl sysdb syslogd \
diff --git a/etc/rc.d/rtadvd b/etc/rc.d/rtadvd
new file mode 100755 (executable)
index 0000000..5fe6772
--- /dev/null
@@ -0,0 +1,60 @@
+#!/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"
index 94e557a2f0d96a90f76f29961ae4b9f89abe5809..5d9e521676b42eb542a86a91076b58c78d1a664d 100644 (file)
@@ -55,7 +55,7 @@ SUBDIR+= mdsetimage
 SUBDIR+= ndp
 .endif
 .if (${USE_INET6} != "no")
-SUBDIR+= traceroute6
+SUBDIR+= rtadvd traceroute6
 .endif
 
 .if !defined(__MINIX)
diff --git a/usr.sbin/rtadvd/Makefile b/usr.sbin/rtadvd/Makefile
new file mode 100644 (file)
index 0000000..4ef8258
--- /dev/null
@@ -0,0 +1,26 @@
+# $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
diff --git a/usr.sbin/rtadvd/advcap.c b/usr.sbin/rtadvd/advcap.c
new file mode 100644 (file)
index 0000000..bd6c7f6
--- /dev/null
@@ -0,0 +1,444 @@
+/*     $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);
+}
diff --git a/usr.sbin/rtadvd/advcap.h b/usr.sbin/rtadvd/advcap.h
new file mode 100644 (file)
index 0000000..3c8303f
--- /dev/null
@@ -0,0 +1,46 @@
+/*     $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_ */
diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c
new file mode 100644 (file)
index 0000000..a7ec657
--- /dev/null
@@ -0,0 +1,1324 @@
+/*     $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;
+}
diff --git a/usr.sbin/rtadvd/config.h b/usr.sbin/rtadvd/config.h
new file mode 100644 (file)
index 0000000..08153d7
--- /dev/null
@@ -0,0 +1,49 @@
+/*     $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
diff --git a/usr.sbin/rtadvd/dump.c b/usr.sbin/rtadvd/dump.c
new file mode 100644 (file)
index 0000000..8fe2979
--- /dev/null
@@ -0,0 +1,280 @@
+/*     $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);
+}
diff --git a/usr.sbin/rtadvd/dump.h b/usr.sbin/rtadvd/dump.h
new file mode 100644 (file)
index 0000000..ba518bb
--- /dev/null
@@ -0,0 +1,33 @@
+/*     $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 *);
diff --git a/usr.sbin/rtadvd/if.c b/usr.sbin/rtadvd/if.c
new file mode 100644 (file)
index 0000000..37d045c
--- /dev/null
@@ -0,0 +1,418 @@
+/*     $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);
+}
diff --git a/usr.sbin/rtadvd/if.h b/usr.sbin/rtadvd/if.h
new file mode 100644 (file)
index 0000000..7eeb7dc
--- /dev/null
@@ -0,0 +1,54 @@
+/*     $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 *);
diff --git a/usr.sbin/rtadvd/pathnames.h b/usr.sbin/rtadvd/pathnames.h
new file mode 100644 (file)
index 0000000..279f195
--- /dev/null
@@ -0,0 +1,4 @@
+/*     $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"
diff --git a/usr.sbin/rtadvd/rrenum.c b/usr.sbin/rtadvd/rrenum.c
new file mode 100644 (file)
index 0000000..4ed24b1
--- /dev/null
@@ -0,0 +1,488 @@
+/*     $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;
+}
diff --git a/usr.sbin/rtadvd/rrenum.h b/usr.sbin/rtadvd/rrenum.h
new file mode 100644 (file)
index 0000000..482445c
--- /dev/null
@@ -0,0 +1,34 @@
+/*     $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 *);
diff --git a/usr.sbin/rtadvd/rtadvd.8 b/usr.sbin/rtadvd/rtadvd.8
new file mode 100644 (file)
index 0000000..860858d
--- /dev/null
@@ -0,0 +1,216 @@
+.\"    $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.
diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c
new file mode 100644 (file)
index 0000000..eb2c51c
--- /dev/null
@@ -0,0 +1,1820 @@
+/*     $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;
+}
diff --git a/usr.sbin/rtadvd/rtadvd.conf b/usr.sbin/rtadvd/rtadvd.conf
new file mode 100644 (file)
index 0000000..2806556
--- /dev/null
@@ -0,0 +1,21 @@
+#      $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:
diff --git a/usr.sbin/rtadvd/rtadvd.conf.5 b/usr.sbin/rtadvd/rtadvd.conf.5
new file mode 100644 (file)
index 0000000..e458379
--- /dev/null
@@ -0,0 +1,497 @@
+.\"    $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)
diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h
new file mode 100644 (file)
index 0000000..ad6fe86
--- /dev/null
@@ -0,0 +1,196 @@
+/*     $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);
diff --git a/usr.sbin/rtadvd/timer.c b/usr.sbin/rtadvd/timer.c
new file mode 100644 (file)
index 0000000..14766c6
--- /dev/null
@@ -0,0 +1,168 @@
+/*     $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);
+}
diff --git a/usr.sbin/rtadvd/timer.h b/usr.sbin/rtadvd/timer.h
new file mode 100644 (file)
index 0000000..3feefa9
--- /dev/null
@@ -0,0 +1,51 @@
+/*     $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 *);