summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/rtadvd/advcap.c455
-rw-r--r--usr.sbin/rtadvd/advcap.h43
-rw-r--r--usr.sbin/rtadvd/config.c642
-rw-r--r--usr.sbin/rtadvd/config.h32
-rw-r--r--usr.sbin/rtadvd/if.c592
-rw-r--r--usr.sbin/rtadvd/if.h55
-rw-r--r--usr.sbin/rtadvd/pathnames.h1
-rw-r--r--usr.sbin/rtadvd/rrenum.c407
-rw-r--r--usr.sbin/rtadvd/rrenum.h32
-rw-r--r--usr.sbin/rtadvd/rtadvd.8116
-rw-r--r--usr.sbin/rtadvd/rtadvd.c1262
-rw-r--r--usr.sbin/rtadvd/rtadvd.conf13
-rw-r--r--usr.sbin/rtadvd/rtadvd.conf.5249
-rw-r--r--usr.sbin/rtadvd/rtadvd.h104
-rw-r--r--usr.sbin/rtadvd/timer.c199
-rw-r--r--usr.sbin/rtadvd/timer.h61
16 files changed, 4263 insertions, 0 deletions
diff --git a/usr.sbin/rtadvd/advcap.c b/usr.sbin/rtadvd/advcap.c
new file mode 100644
index 00000000000..81724fc5fab
--- /dev/null
+++ b/usr.sbin/rtadvd/advcap.c
@@ -0,0 +1,455 @@
+/*
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)remcap.c 5.5 (Berkeley) 2/2/91";
+#endif /* not lint */
+
+/*
+ * remcap - routines for dealing with the remote host data base
+ *
+ * derived from termcap
+ */
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include "pathnames.h"
+
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+#define MAXHOP 32 /* max number of tc= indirections */
+
+#define tgetent agetent
+#define tnchktc anchktc
+#define tnamatch anamatch
+#define tgetnum agetnum
+#define tgetflag agetflag
+#define tgetstr agetstr
+
+#if 0
+#define V_TERMCAP "REMOTE"
+#define V_TERM "HOST"
+#endif
+
+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 __P((char *, char *));
+int getent __P((char *, char *, char *));
+int tnchktc __P((void));
+int tnamatch __P((char *));
+static char *tskip __P((char *));
+int tgetnum __P((char *));
+int tgetflag __P((char *));
+char *tgetstr __P((char *, char **));
+static char *tdecode __P((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(bp, name)
+ char *bp, *name;
+{
+ char *cp;
+
+ remotefile = cp = conffile ? conffile : _PATH_RTADVDCONF;
+ return (getent(bp, name, cp));
+}
+
+int
+getent(bp, name, cp)
+ char *bp, *name, *cp;
+{
+ register int c;
+ register 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_WARNING,
+ "<%s> open: %s", __FUNCTION__, strerror(errno));
+ return (-2);
+ }
+ for (;;) {
+ cp = bp;
+ for (;;) {
+ if (i == cnt) {
+ cnt = read(tf, ibuf, BUFSIZ);
+ if (cnt <= 0) {
+ close(tf);
+ return (0);
+ }
+ i = 0;
+ }
+ c = ibuf[i++];
+ if (c == '\n') {
+ if (cp > bp && cp[-1] == '\\') {
+ cp--;
+ continue;
+ }
+ break;
+ }
+ if (cp >= bp+BUFSIZ) {
+ 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()
+{
+ register 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);
+ strcpy(tcname, p+3);
+ 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(np)
+ char *np;
+{
+ register 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(bp)
+ register char *bp;
+{
+ int dquote;
+
+ dquote = 0;
+ while (*bp) {
+ switch (*bp) {
+ case ':':
+ if (!dquote)
+ goto breakbreak;
+ else
+ bp++;
+ break;
+ case '\\':
+ bp++;
+ if (isdigit(*bp)) {
+ while (isdigit(*bp++))
+ ;
+ } else
+ bp++;
+ case '"':
+ dquote = (dquote ? 1 : 0);
+ bp++;
+ break;
+ default:
+ bp++;
+ break;
+ }
+ }
+breakbreak:
+ if (*bp == ':')
+ bp++;
+ return (bp);
+}
+
+/*
+ * Return the (numeric) option id.
+ * Numeric options look like
+ * li#80
+ * i.e. the option string is separated from the numeric value by
+ * a # character. If the option is not found we return -1.
+ * Note that we handle octal numbers beginning with 0.
+ */
+int
+tgetnum(id)
+ char *id;
+{
+ register long int i;
+ register int base;
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (*bp == 0)
+ return (-1);
+ if (strncmp(bp, id, strlen(id)) != 0)
+ continue;
+ bp += strlen(id);
+ if (*bp == '@')
+ return (-1);
+ if (*bp != '#')
+ continue;
+ bp++;
+ base = 10;
+ if (*bp == '0')
+ base = 8;
+ i = 0;
+ while (isdigit(*bp))
+ i *= base, i += *bp++ - '0';
+ return (i);
+ }
+}
+
+/*
+ * Handle a flag option.
+ * Flag options are given "naked", i.e. followed by a : or the end
+ * of the buffer. Return 1 if we find the option, or 0 if it is
+ * not given.
+ */
+int
+tgetflag(id)
+ char *id;
+{
+ register 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(id, area)
+ char *id, **area;
+{
+ register 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(str, area)
+ register char *str;
+ char **area;
+{
+ register char *cp;
+ register int c;
+ register char *dp;
+ int i;
+ char term;
+
+ term = ':';
+ cp = *area;
+again:
+ if (*str == '"') {
+ term = '"';
+ str++;
+ }
+ while ((c = *str++) && c != term) {
+ switch (c) {
+
+ case '^':
+ c = *str++ & 037;
+ break;
+
+ case '\\':
+ dp = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\"";
+ c = *str++;
+nextc:
+ if (*dp++ == c) {
+ c = *dp++;
+ break;
+ }
+ dp++;
+ if (*dp)
+ goto nextc;
+ if (isdigit(c)) {
+ c -= '0', i = 2;
+ do
+ c <<= 3, c |= *str++ - '0';
+ while (--i && isdigit(*str));
+ }
+ break;
+ }
+ *cp++ = c;
+ }
+ if (c == term && term != ':') {
+ term = ':';
+ goto again;
+ }
+ *cp++ = 0;
+ str = *area;
+ *area = cp;
+ return (str);
+}
diff --git a/usr.sbin/rtadvd/advcap.h b/usr.sbin/rtadvd/advcap.h
new file mode 100644
index 00000000000..445a71a6b16
--- /dev/null
+++ b/usr.sbin/rtadvd/advcap.h
@@ -0,0 +1,43 @@
+/*
+ * 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 __P((char *, const char *));
+extern int agetflag __P((const char *));
+extern int agetnum __P((const char *));
+extern char *agetstr __P((const char *, char **));
+
+__END_DECLS
+
+#endif /* _ADVCAP_H_ */
diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c
new file mode 100644
index 00000000000..851fe5bb49e
--- /dev/null
+++ b/usr.sbin/rtadvd/config.c
@@ -0,0 +1,642 @@
+/*
+ * 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 <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef __NetBSD__
+#include <search.h>
+#endif
+#include <unistd.h>
+
+#include "rtadvd.h"
+#include "advcap.h"
+#include "timer.h"
+#include "if.h"
+#include "config.h"
+
+static void makeentry __P((char *, int, char *, int));
+static void make_packet __P((struct rainfo *));
+static void get_prefix __P((struct rainfo *));
+
+extern struct rainfo *ralist;
+
+void
+getconfig(intface)
+ char *intface;
+{
+ int stat, pfxs, i;
+ char tbuf[BUFSIZ];
+ struct rainfo *tmp;
+ long val;
+ char buf[BUFSIZ];
+ char *bp = buf;
+ char *addr;
+
+#define MUSTHAVE(var, cap) \
+ { \
+ int t; \
+ if ((t = agetnum(cap)) < 0) { \
+ fprintf(stderr, "rtadvd: need %s for interface %s\n", \
+ cap, intface); \
+ exit(1); \
+ } \
+ var = t; \
+ }
+#define MAYHAVE(var, cap, def) \
+ { \
+ if ((var = agetnum(cap)) < 0) \
+ var = def; \
+ }
+
+ 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",
+ __FUNCTION__, intface);
+ }
+
+ tmp = (struct rainfo *)malloc(sizeof(*ralist));
+ memset(tmp, 0, sizeof(*tmp));
+ tmp->prefix.next = tmp->prefix.prev = &tmp->prefix;
+
+ /* 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",
+ __FUNCTION__, intface);
+ exit(1);
+ }
+ tmp->ifindex = tmp->sdl->sdl_index;
+ } else
+ tmp->ifindex = if_nametoindex(intface);
+ strncpy(tmp->ifname, intface, sizeof(tmp->ifname));
+ if ((tmp->phymtu = if_getmtu(intface)) == 0) {
+ tmp->phymtu = IPV6_MMTU;
+ syslog(LOG_WARNING,
+ "<%s> can't get interface mtu of %s. Treat as %d",
+ __FUNCTION__, intface, IPV6_MMTU);
+ }
+
+ /*
+ * set router configuration variables.
+ */
+ MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
+ if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
+ syslog(LOG_ERR,
+ "<%s> maxinterval must be between %d and %d",
+ __FUNCTION__, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
+ exit(1);
+ }
+ tmp->maxinterval = (u_int)val;
+ MAYHAVE(val, "mininterval", tmp->maxinterval/3);
+ if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) {
+ syslog(LOG_ERR,
+ "<%s> mininterval must be between %d and %d",
+ __FUNCTION__,
+ MIN_MININTERVAL,
+ (tmp->maxinterval * 3) / 4);
+ exit(1);
+ }
+ tmp->mininterval = (u_int)val;
+
+ MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
+ tmp->hoplimit = val & 0xff;
+
+ MAYHAVE(val, "raflags", 0);
+ tmp->managedflg= val & ND_RA_FLAG_MANAGED;
+ tmp->otherflg = val & ND_RA_FLAG_OTHER;
+
+ MAYHAVE(val, "rltime", tmp->maxinterval * 3);
+ if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) {
+ syslog(LOG_ERR,
+ "<%s> router lifetime on %s must be 0 or"
+ " between %d and %d",
+ __FUNCTION__, intface,
+ tmp->maxinterval, MAXROUTERLIFETIME);
+ exit(1);
+ }
+ tmp->lifetime = val & 0xffff;
+
+ MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
+ if (val > MAXREACHABLETIME) {
+ syslog(LOG_ERR,
+ "<%s> reachable time must be no greater than %d",
+ __FUNCTION__, MAXREACHABLETIME);
+ exit(1);
+ }
+ tmp->reachabletime = (u_int32_t)val;
+
+ MAYHAVE(val, "retrans", DEF_ADVRETRANSTIMER);
+ if (val < 0 || val > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> retrans time out of range", __FUNCTION__);
+ exit(1);
+ }
+ tmp->retranstimer = (u_int32_t)val;
+
+ /* prefix information */
+ if ((pfxs = agetnum("addrs")) < 0) {
+ /* auto configure prefix information */
+ if (agetstr("addr", &bp) || agetstr("addr1", &bp)) {
+ syslog(LOG_ERR,
+ "<%s> conflicting prefix configuration for %s: "
+ "automatic and manual config at the same time",
+ __FUNCTION__, intface);
+ exit(1);
+ }
+ get_prefix(tmp);
+ }
+ else {
+ tmp->pfxs = pfxs;
+ for (i = 0; i < pfxs; i++) {
+ struct prefix *pfx;
+ char entbuf[256];
+ int added = (pfxs > 1) ? 1 : 0;
+
+ /* allocate memory to store prefix information */
+ if ((pfx = malloc(sizeof(struct prefix))) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't allocate enough memory",
+ __FUNCTION__);
+ exit(1);
+ }
+ /* link into chain */
+ insque(pfx, &tmp->prefix);
+
+ makeentry(entbuf, i, "prefixlen", added);
+ MAYHAVE(val, entbuf, 64);
+ if (val < 0 || val > 128) {
+ syslog(LOG_ERR,
+ "<%s> prefixlen out of range",
+ __FUNCTION__);
+ exit(1);
+ }
+ pfx->prefixlen = (int)val;
+
+ makeentry(entbuf, i, "pinfoflags", added);
+ 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, i, "vltime", added);
+ MAYHAVE(val, entbuf, DEF_ADVVALIDLIFETIME);
+ if (val < 0 || val > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> vltime out of range",
+ __FUNCTION__);
+ exit(1);
+ }
+ pfx->validlifetime = (u_int32_t)val;
+
+ makeentry(entbuf, i, "pltime", added);
+ MAYHAVE(val, entbuf, DEF_ADVPREFERREDLIFETIME);
+ if (val < 0 || val > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> pltime out of range",
+ __FUNCTION__);
+ exit(1);
+ }
+ pfx->preflifetime = (u_int32_t)val;
+
+ makeentry(entbuf, i, "addr", added);
+ addr = (char *)agetstr(entbuf, &bp);
+ if (addr == NULL) {
+ syslog(LOG_ERR,
+ "<%s> need %s as an prefix for "
+ "interface %s",
+ __FUNCTION__, entbuf, intface);
+ exit(1);
+ }
+ if (inet_pton(AF_INET6, addr,
+ &pfx->prefix) != 1) {
+ syslog(LOG_ERR,
+ "<%s> inet_pton failed for %s",
+ __FUNCTION__, addr);
+ exit(1);
+ }
+ if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
+ syslog(LOG_ERR,
+ "<%s> multicast prefix(%s) must "
+ "not be advertised (IF=%s)",
+ __FUNCTION__, addr, intface);
+ exit(1);
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
+ syslog(LOG_NOTICE,
+ "<%s> link-local prefix(%s) will be"
+ " advertised on %s",
+ __FUNCTION__, addr, intface);
+ }
+ }
+
+ MAYHAVE(val, "mtu", 0);
+ if (val < 0 || val > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> mtu out of range", __FUNCTION__);
+ exit(1);
+ }
+ tmp->linkmtu = (u_int32_t)val;
+ 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 must be between"
+ " least MTU and physical link MTU",
+ __FUNCTION__);
+ exit(1);
+ }
+
+ /* okey */
+ tmp->next = ralist;
+ ralist = tmp;
+
+ /* construct the sending packet */
+ make_packet(tmp);
+
+ /* set timer */
+ tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
+ tmp, tmp);
+ ra_timer_update((void *)tmp, &tmp->timer->tm);
+ rtadvd_set_timer(&tmp->timer->tm, tmp->timer);
+}
+
+static void
+get_prefix(struct rainfo *rai)
+{
+ size_t len;
+ u_char *buf, *lim, *next;
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ if ((len = rtbuf_len()) < 0) {
+ syslog(LOG_ERR,
+ "<%s> can't get buffer length for routing info",
+ __FUNCTION__);
+ exit(1);
+ }
+ if ((buf = malloc(len)) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't allocate buffer", __FUNCTION__);
+ exit(1);
+ }
+ if (get_rtinfo(buf, &len) < 0) {
+ syslog(LOG_ERR,
+ "<%s> can't get routing inforamtion", __FUNCTION__);
+ exit(1);
+ }
+
+ lim = buf + len;
+ next = get_next_msg(buf, lim, rai->ifindex, &len,
+ RTADV_TYPE2BITMASK(RTM_GET));
+ while (next < lim) {
+ struct prefix *pp;
+ struct in6_addr *a;
+
+ /* allocate memory to store prefix info. */
+ if ((pp = malloc(sizeof(*pp))) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't get allocate buffer for prefix",
+ __FUNCTION__);
+ exit(1);
+ }
+ memset(pp, 0, sizeof(*pp));
+
+ /* set prefix and its length */
+ a = get_addr(next);
+ memcpy(&pp->prefix, a, sizeof(*a));
+ if ((pp->prefixlen = get_prefixlen(next)) < 0) {
+ syslog(LOG_ERR,
+ "<%s> failed to get prefixlen "
+ "or prefixl is invalid",
+ __FUNCTION__);
+ exit(1);
+ }
+ syslog(LOG_DEBUG,
+ "<%s> add %s/%d to prefix list on %s",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, a, ntopbuf, INET6_ADDRSTRLEN),
+ pp->prefixlen, rai->ifname);
+
+ /* set other fields with protocol defaults */
+ pp->validlifetime = DEF_ADVVALIDLIFETIME;
+ pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
+ pp->onlinkflg = 1;
+ pp->autoconfflg = 1;
+
+ /* link into chain */
+ insque(pp, &rai->prefix);
+
+ /* counter increment */
+ rai->pfxs++;
+
+ /* forward pointer and get next prefix(if any) */
+ next += len;
+ next = get_next_msg(next, lim, rai->ifindex,
+ &len, RTADV_TYPE2BITMASK(RTM_GET));
+ }
+
+ free(buf);
+}
+
+static void
+makeentry(buf, id, string, add)
+ char *buf, *string;
+ int id, add;
+{
+ strcpy(buf, string);
+ if (add) {
+ char *cp;
+
+ cp = (char *)index(buf, '\0');
+ cp += sprintf(cp, "%d", id);
+ *cp = '\0';
+ }
+}
+
+/*
+ * Add a prefix to the list of specified interface and reconstruct
+ * the outgoing packet.
+ * The prefix must not be in the list.
+ * XXX: other parameter of the prefix(e.g. lifetime) shoule be
+ * able to be specified.
+ */
+static void
+add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
+{
+ struct prefix *prefix;
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ if ((prefix = malloc(sizeof(*prefix))) == NULL) {
+ syslog(LOG_ERR, "<%s> memory allocation failed",
+ __FUNCTION__);
+ 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;
+
+ insque(prefix, &rai->prefix);
+
+ syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
+ __FUNCTION__, 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 = 0;
+
+ /* reconstruct the packet */
+ rai->pfxs++;
+ make_packet(rai);
+
+ /*
+ * reset the timer so that the new prefix will be advertised quickly.
+ */
+ rai->initcounter = 0;
+ ra_timer_update((void *)rai, &rai->timer->tm);
+ rtadvd_set_timer(&rai->timer->tm, rai->timer);
+}
+
+/*
+ * 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 rainfo *rai, struct prefix *prefix)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ remque(prefix);
+ syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
+ __FUNCTION__, inet_ntop(AF_INET6, &prefix->prefix,
+ ntopbuf, INET6_ADDRSTRLEN),
+ prefix->prefixlen, rai->ifname);
+ free(prefix);
+ rai->pfxs--;
+ make_packet(rai);
+}
+
+/*
+ * 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)
+{
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
+ strerror(errno));
+ exit(1);
+ }
+
+ if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
+ syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __FUNCTION__,
+ strerror(errno));
+
+ ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
+ ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
+ ipr->ipr_raf_onlink = 1;
+ ipr->ipr_raf_auto = 1;
+ /* omit other field initialization */
+ }
+ else if (ipr->ipr_origin < PR_ORIG_RR) {
+ u_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", __FUNCTION__,
+ inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), ipr->ipr_origin);
+ return 1;
+ }
+
+ close(s);
+ return 0;
+}
+
+void
+make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
+{
+ struct in6_prefixreq ipr;
+
+ memset(&ipr, 0, sizeof(ipr));
+ if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
+ syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
+ "exist. This should not happen! %s", __FUNCTION__,
+ ifindex, strerror(errno));
+ exit(1);
+ }
+ ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
+ ipr.ipr_prefix.sin6_family = AF_INET6;
+ ipr.ipr_prefix.sin6_addr = *addr;
+ ipr.ipr_plen = plen;
+
+ if (init_prefix(&ipr))
+ return; /* init failed by some error */
+ add_prefix(rai, &ipr);
+}
+
+static 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;
+
+ /* 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.",
+ __FUNCTION__, rainfo->ifname);
+ rainfo->advlinkopt = 0;
+ }
+ packlen += lladdroptlen;
+ }
+ if (rainfo->pfxs)
+ packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
+ if (rainfo->linkmtu)
+ packlen += sizeof(struct nd_opt_mtu);
+
+ /* allocate memory for the packet */
+ if ((buf = malloc(packlen)) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't get enough memory for an RA packet",
+ __FUNCTION__);
+ exit(1);
+ }
+ rainfo->ra_data = buf;
+ /* XXX: what if packlen > 576? */
+ rainfo->ra_datalen = packlen;
+
+ /*
+ * construct the packet
+ */
+ ra = (struct nd_router_advert *)buf;
+ ra->nd_ra_type = ND_ROUTER_ADVERT;
+ ra->nd_ra_code = 0;
+ ra->nd_ra_cksum = 0;
+ ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit);
+ ra->nd_ra_flags_reserved = 0;
+ 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) {
+ lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
+ buf += lladdroptlen;
+ }
+
+ if (rainfo->linkmtu) {
+ ndopt_mtu = (struct nd_opt_mtu *)buf;
+ ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
+ ndopt_mtu->nd_opt_mtu_len = 1;
+ ndopt_mtu->nd_opt_mtu_reserved = 0;
+ ndopt_mtu->nd_opt_mtu_mtu = ntohl(rainfo->linkmtu);
+ buf += sizeof(struct nd_opt_mtu);
+ }
+
+ for (pfx = rainfo->prefix.next;
+ pfx != &rainfo->prefix; pfx = pfx->next) {
+ 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;
+ ndopt_pi->nd_opt_pi_valid_time = ntohl(pfx->validlifetime);
+ ndopt_pi->nd_opt_pi_preferred_time =
+ ntohl(pfx->preflifetime);
+ ndopt_pi->nd_opt_pi_reserved2 = 0;
+ ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
+
+ buf += sizeof(struct nd_opt_prefix_info);
+ }
+
+ return;
+}
diff --git a/usr.sbin/rtadvd/config.h b/usr.sbin/rtadvd/config.h
new file mode 100644
index 00000000000..823af1691ae
--- /dev/null
+++ b/usr.sbin/rtadvd/config.h
@@ -0,0 +1,32 @@
+/*
+ * 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 getconfig __P((char *));
+extern void delete_prefix __P((struct rainfo *, struct prefix *));
+extern void make_prefix __P((struct rainfo *, int, struct in6_addr *, int));
diff --git a/usr.sbin/rtadvd/if.c b/usr.sbin/rtadvd/if.c
new file mode 100644
index 00000000000..3488968f393
--- /dev/null
+++ b/usr.sbin/rtadvd/if.c
@@ -0,0 +1,592 @@
+/*
+ * 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/sysctl.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#ifdef __FreeBSD__
+# include <net/ethernet.h>
+#endif
+#ifdef __bsdi__
+# include <ifaddrs.h>
+#endif
+#ifdef __NetBSD__
+#include <net/if_ether.h>
+#endif
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+#ifdef __bsdi__
+# include <netinet/if_ether.h>
+#endif
+#ifdef __OpenBSD__
+#include <netinet/if_ether.h>
+#endif
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include "rtadvd.h"
+#include "if.h"
+
+#define ROUNDUP(a, size) \
+ (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
+
+#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
+ ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
+ sizeof(u_long)) :\
+ sizeof(u_long)))
+
+struct if_msghdr **iflist;
+int iflist_init_ok;
+size_t ifblock_size;
+char *ifblock;
+
+static void get_iflist __P((char **buf, size_t *size));
+static void parse_iflist __P((struct if_msghdr ***ifmlist_p, char *buf,
+ size_t bufsize));
+
+static void
+get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
+{
+ int i;
+
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (addrs & (1 << i)) {
+ rti_info[i] = sa;
+ NEXT_SA(sa);
+ }
+ else
+ rti_info[i] = NULL;
+ }
+}
+
+struct sockaddr_dl *
+if_nametosdl(char *name)
+{
+ int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
+ char *buf, *next, *lim;
+ size_t len;
+ struct if_msghdr *ifm;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_dl *sdl = NULL, *ret_sdl;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+ return(NULL);
+ if ((buf = malloc(len)) == NULL)
+ return(NULL);
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+ free(buf);
+ return(NULL);
+ }
+
+ lim = buf + len;
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sa = (struct sockaddr *)(ifm + 1);
+ get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
+ if ((sa = rti_info[RTAX_IFP]) != NULL) {
+ if (sa->sa_family == AF_LINK) {
+ sdl = (struct sockaddr_dl *)sa;
+ if (strlen(name) != sdl->sdl_nlen)
+ continue; /* not same len */
+ if (strncmp(&sdl->sdl_data[0],
+ name,
+ sdl->sdl_nlen) == 0) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (next == lim) {
+ /* search failed */
+ free(buf);
+ return(NULL);
+ }
+
+ if ((ret_sdl = malloc(sdl->sdl_len)) == NULL)
+ return(NULL);
+ memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
+ return(ret_sdl);
+}
+
+int
+if_getmtu(char *name)
+{
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+ struct ifreq ifr;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ return(0);
+
+ ifr.ifr_addr.sa_family = AF_INET6;
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0) {
+ close(s);
+ return(0);
+ }
+
+ close(s);
+
+ return(ifr.ifr_mtu);
+#endif
+#ifdef __bsdi__
+ struct ifaddrs *ifa;
+ struct if_data *ifd;
+
+ if (getifaddrs(&ifa) < 0)
+ return(0);
+ while (ifa) {
+ if (strcmp(ifa->ifa_name, name) == 0) {
+ ifd = ifa->ifa_data;
+ if (ifd)
+ return ifd->ifi_mtu;
+ else
+ return 0;
+ }
+ ifa = ifa->ifa_next;
+ }
+ return 0;
+#endif
+ /* last resort */
+ return 0;
+}
+
+/* give interface index and its old flags, then new flags returned */
+int
+if_getflags(int ifindex, int oifflags)
+{
+ struct ifreq ifr;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
+ strerror(errno));
+ return (oifflags & ~IFF_UP);
+ }
+
+ if_indextoname(ifindex, ifr.ifr_name);
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "<%s> ioctl:SIOCGIFFLAGS: failed for %s",
+ __FUNCTION__, ifr.ifr_name);
+ close(s);
+ return (oifflags & ~IFF_UP);
+ }
+ return (ifr.ifr_flags);
+}
+
+#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
+int
+lladdropt_length(struct sockaddr_dl *sdl)
+{
+ switch(sdl->sdl_type) {
+ case IFT_ETHER:
+ return(ROUNDUP8(ETHER_ADDR_LEN + 2));
+ default:
+ return(0);
+ }
+}
+
+void
+lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
+{
+ char *addr;
+
+ ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
+
+ switch(sdl->sdl_type) {
+ case IFT_ETHER:
+ ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
+ addr = (char *)(ndopt + 1);
+ memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
+ break;
+ default:
+ syslog(LOG_ERR,
+ "<%s> unsupported link type(%d)",
+ __FUNCTION__, sdl->sdl_type);
+ exit(1);
+ }
+
+ return;
+}
+
+int
+rtbuf_len()
+{
+ size_t len;
+
+ int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0};
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+ return(-1);
+
+ return(len);
+}
+
+int
+get_rtinfo(char *buf, size_t *len)
+{
+ int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0};
+
+ if (sysctl(mib, 6, buf, len, NULL, 0) < 0)
+ return(-1);
+
+ return(0);
+}
+
+#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)", __FUNCTION__,
+ 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 */
+ case RTM_IFINFO:
+ /* found */
+ *lenp = rtm->rtm_msglen;
+ return (char *)rtm;
+ /* NOTREACHED */
+ }
+ }
+
+ return (char *)rtm;
+}
+#undef FILTER_MATCH(type, filter)
+
+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);
+}
+
+int
+get_prefixlen(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ int masklen;
+ u_char *p, *lim;
+
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+ sa = rti_info[RTAX_NETMASK];
+
+ p = (u_char *)(&SIN6(sa)->sin6_addr);
+ lim = (u_char *)sa + sa->sa_len;
+ 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);
+}
+
+int
+ifmsg_len(char *buf)
+{
+ struct if_msghdr *ifm = (struct if_msghdr *)buf;
+
+ return(ifm->ifm_msglen);
+}
+
+/*
+ * alloc buffer and get if_msghdrs block from kernel,
+ * and put them into the buffer
+ */
+static void
+get_iflist(char **buf, size_t *size)
+{
+ int mib[6];
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET6;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ if (sysctl(mib, 6, NULL, size, NULL, 0) < 0) {
+ syslog(LOG_ERR, "<%s> sysctl: iflist size get failed",
+ __FUNCTION__);
+ exit(1);
+ }
+ if ((*buf = malloc(*size)) == NULL) {
+ syslog(LOG_ERR, "<%s> malloc failed", __FUNCTION__);
+ exit(1);
+ }
+ if (sysctl(mib, 6, *buf, size, NULL, 0) < 0) {
+ syslog(LOG_ERR, "<%s> sysctl: iflist get failed",
+ __FUNCTION__);
+ exit(1);
+ }
+ return;
+}
+
+/*
+ * alloc buffer and parse if_msghdrs block passed as arg,
+ * and init the buffer as list of pointers ot each of the if_msghdr.
+ */
+static void
+parse_iflist(struct if_msghdr ***ifmlist_p, char *buf, size_t bufsize)
+{
+ int iflentry_size, malloc_size;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ char *lim;
+
+ /*
+ * Estimate least size of an iflist entry, to be obtained from kernel.
+ * Should add sizeof(sockaddr) ??
+ */
+ iflentry_size = sizeof(struct if_msghdr);
+ /* roughly estimate max list size of pointers to each if_msghdr */
+ malloc_size = (bufsize/iflentry_size) * sizeof(size_t);
+ if ((*ifmlist_p = (struct if_msghdr **)malloc(malloc_size)) == NULL) {
+ syslog(LOG_ERR, "<%s> malloc failed", __FUNCTION__);
+ exit(1);
+ }
+
+ lim = buf + bufsize;
+ for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) {
+ if (ifm->ifm_msglen == 0) {
+ syslog(LOG_WARNING, "<%s> ifm_msglen is 0 "
+ "(buf=%p lim=%p ifm=%p)", __FUNCTION__,
+ buf, lim, ifm);
+ return;
+ }
+
+ if (ifm->ifm_type == RTM_IFINFO) {
+ (*ifmlist_p)[ifm->ifm_index] = ifm;
+ } else {
+ syslog(LOG_ERR, "out of sync parsing NET_RT_IFLIST\n"
+ "expected %d, got %d\n msglen = %d\n"
+ "buf:%p, ifm:%p, lim:%p\n",
+ RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen,
+ buf, ifm, lim);
+ exit (1);
+ }
+ for (ifam = (struct ifa_msghdr *)
+ ((char *)ifm + ifm->ifm_msglen);
+ ifam < (struct ifa_msghdr *)lim;
+ ifam = (struct ifa_msghdr *)
+ ((char *)ifam + ifam->ifam_msglen)) {
+ /* just for safety */
+ if (!ifam->ifam_msglen) {
+ syslog(LOG_WARNING, "<%s> ifa_msglen is 0 "
+ "(buf=%p lim=%p ifam=%p)", __FUNCTION__,
+ buf, lim, ifam);
+ return;
+ }
+ if (ifam->ifam_type != RTM_NEWADDR)
+ break;
+ }
+ ifm = (struct if_msghdr *)ifam;
+ }
+}
+
+void
+init_iflist()
+{
+ if (ifblock) {
+ free(ifblock);
+ ifblock_size = 0;
+ }
+ if (iflist)
+ free(iflist);
+ /* get iflist block from kernel */
+ get_iflist(&ifblock, &ifblock_size);
+
+ /* make list of pointers to each if_msghdr */
+ parse_iflist(&iflist, ifblock, ifblock_size);
+
+}
diff --git a/usr.sbin/rtadvd/if.h b/usr.sbin/rtadvd/if.h
new file mode 100644
index 00000000000..0fc97a47196
--- /dev/null
+++ b/usr.sbin/rtadvd/if.h
@@ -0,0 +1,55 @@
+/*
+ * 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)
+
+extern struct if_msghdr **iflist;
+extern size_t ifblock_size;
+extern char *ifblock;
+
+struct sockaddr_dl *if_nametosdl __P((char *name));
+int if_getmtu __P((char *name));
+int if_getflags __P((int ifindex, int oifflags));
+int lladdropt_length __P((struct sockaddr_dl *sdl));
+void lladdropt_fill __P((struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt));
+int rtbuf_len __P((void));
+int get_rtinfo __P((char *buf, size_t *len));
+char *get_next_msg __P((char *buf, char *lim, int ifindex, size_t *lenp,
+ int filter));
+struct in6_addr *get_addr __P((char *buf));
+int get_rtm_ifindex __P((char *buf));
+int get_ifm_ifindex __P((char *buf));
+int get_ifam_ifindex __P((char *buf));
+int get_ifm_flags __P((char *buf));
+int get_prefixlen __P((char *buf));
+int rtmsg_type __P((char *buf));
+int ifmsg_type __P((char *buf));
+int rtmsg_len __P((char *buf));
+int ifmsg_len __P((char *buf));
+void init_iflist __P((void));
diff --git a/usr.sbin/rtadvd/pathnames.h b/usr.sbin/rtadvd/pathnames.h
new file mode 100644
index 00000000000..a14ed50f772
--- /dev/null
+++ b/usr.sbin/rtadvd/pathnames.h
@@ -0,0 +1 @@
+#define _PATH_RTADVDCONF "/usr/local/v6/etc/rtadvd.conf"
diff --git a/usr.sbin/rtadvd/rrenum.c b/usr.sbin/rtadvd/rrenum.c
new file mode 100644
index 00000000000..4ed70202831
--- /dev/null
+++ b/usr.sbin/rtadvd/rrenum.c
@@ -0,0 +1,407 @@
+/*
+ * 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/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#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 "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;
+
+/*
+ * 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",
+ __FUNCTION__, 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", __FUNCTION__,
+ 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",
+ __FUNCTION__, 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)",
+ __FUNCTION__, 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) {
+ struct rr_pco_use *rpu, *rpulim;
+
+ rpu = (struct rr_pco_use *)(rpm + 1);
+ rpulim = (struct rr_pco_use *)((char *)rpm + len);
+
+ if (rpu == rpulim) {
+ if (rpm->rpm_code == RPM_PCO_ADD)
+ return;
+
+ irr->irr_u_uselen = 0;
+ irr->irr_u_keeplen = 0;
+ irr->irr_raf_mask_onlink = 0;
+ irr->irr_raf_mask_auto = 0;
+ irr->irr_vltime = 0;
+ irr->irr_pltime = 0;
+ memset(&irr->irr_flags, 0, sizeof(irr->irr_flags));
+ irr->irr_useprefix.sin6_len = 0; /* let it mean, no addition */
+ irr->irr_useprefix.sin6_family = 0;
+ irr->irr_useprefix.sin6_addr = in6addr_any;
+ if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 &&
+ errno != EADDRNOTAVAIL)
+ syslog(LOG_ERR, "<%s> ioctl: %s", __FUNCTION__,
+ strerror(errno));
+ return;
+ }
+
+ for (rpu = (struct rr_pco_use *)(rpm + 1),
+ rpulim = (struct rr_pco_use *)((char *)rpm + len);
+ rpu < rpulim;
+ rpu += 1) {
+ /* init in6_rrenumreq fields */
+ irr->irr_u_uselen = rpu->rpu_uselen;
+ irr->irr_u_keeplen = rpu->rpu_keeplen;
+ irr->irr_raf_mask_onlink = rpu->rpu_mask_onlink;
+ irr->irr_raf_mask_auto = rpu->rpu_mask_autonomous;
+ irr->irr_vltime = rpu->rpu_vltime;
+ irr->irr_pltime = rpu->rpu_pltime;
+ irr->irr_raf_onlink = rpu->rpu_onlink;
+ irr->irr_raf_auto = rpu->rpu_autonomous;
+ irr->irr_rrf_decrvalid = rpu->rpu_decr_vltime;
+ irr->irr_rrf_decrprefd = rpu->rpu_decr_pltime;
+ irr->irr_useprefix.sin6_len = sizeof(irr->irr_useprefix);
+ irr->irr_useprefix.sin6_family = AF_INET6;
+ irr->irr_useprefix.sin6_addr = rpu->rpu_prefix;
+
+ if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 &&
+ errno != EADDRNOTAVAIL)
+ syslog(LOG_ERR, "<%s> ioctl: %s", __FUNCTION__,
+ strerror(errno));
+ }
+}
+
+/*
+ * 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;
+
+ if ((rr_pco_check(len, rpm) != NULL))
+ return 1;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
+ strerror(errno));
+ exit(1);
+ }
+
+ memset(&irr, 0, sizeof(irr));
+ irr.irr_origin = PR_ORIG_RR;
+ irr.irr_m_len = rpm->rpm_matchlen;
+ irr.irr_m_minlen = rpm->rpm_minlen;
+ irr.irr_m_maxlen = rpm->rpm_maxlen;
+ irr.irr_matchprefix.sin6_len = sizeof(irr.irr_matchprefix);
+ irr.irr_matchprefix.sin6_family = AF_INET6;
+ irr.irr_matchprefix.sin6_addr = rpm->rpm_prefix;
+
+ while (if_indextoname(++ifindex, irr.irr_name)) {
+ /*
+ * if rr_forceapply(A flag) is 0 and IFF_UP is off,
+ * the interface is not applied
+ */
+ if (!rr->rr_forceapply &&
+ (iflist[ifindex]->ifm_flags & IFF_UP) == 0)
+ continue;
+ /* TODO: interface scope check */
+ do_use_prefix(len, rpm, &irr);
+ }
+ if (errno == ENXIO)
+ return 0;
+ else if (errno) {
+ syslog(LOG_ERR, "<%s> if_indextoname: %s", __FUNCTION__,
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * call do_pco() for each Prefix Control Operations(PCOs) in a received
+ * Router Renumbering Command packet.
+ * return 0 on success, 1 on failure
+ */
+static int
+do_rr(int len, struct icmp6_router_renum *rr)
+{
+ struct rr_pco_match *rpm;
+ char *cp, *lim;
+
+ lim = (char *)rr + len;
+ cp = (char *)(rr + 1);
+ len -= sizeof(struct icmp6_router_renum);
+
+ /* get iflist block from kernel again, to get up-to-date information */
+ init_iflist();
+
+ while (cp < lim) {
+ int rpmlen;
+
+ rpm = (struct rr_pco_match *)cp;
+ if (len < sizeof(struct rr_pco_match)) {
+ tooshort:
+ syslog(LOG_ERR, "<%s> pkt too short. left len = %d. "
+ "gabage at end of pkt?", __FUNCTION__, 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", __FUNCTION__);
+ goto next;
+ }
+
+ next:
+ cp += rpmlen;
+ len -= rpmlen;
+ }
+
+ return 0;
+}
+
+/*
+ * check validity of a router renumbering command packet
+ * return 0 on success, 1 on failure
+ */
+static int
+rr_command_check(int len, struct icmp6_router_renum *rr, struct in6_addr *from,
+ struct in6_addr *dst)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ /* omit rr minimal length check. hope kernel have done it. */
+ /* rr_command length check */
+ if (len < (sizeof(struct icmp6_router_renum) +
+ sizeof(struct rr_pco_match))) {
+ syslog(LOG_ERR, "<%s> rr_command len %d is too short",
+ __FUNCTION__, 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",
+ __FUNCTION__,
+ 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",
+ __FUNCTION__, (u_int32_t)ntohl(rr->rr_seqnum),
+ inet_ntop(AF_INET6, from, ntopbuf, INET6_ADDRSTRLEN));
+ return 1;
+ }
+ if (rro.rro_seqnum == rr->rr_seqnum &&
+ rr->rr_test == 0 &&
+ RR_ISSET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum)) {
+ if (rr->rr_reqresult)
+ syslog(LOG_WARNING,
+ "<%s> rcvd duped segnum %d from %s",
+ __FUNCTION__, 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_test && !rr->rr_reqresult)
+ 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", __FUNCTION__);
+ return;
+}
+
+void
+rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi,
+ struct sockaddr_in6 *from, struct in6_addr *dst)
+{
+ u_char ntopbuf[2][INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+
+ syslog(LOG_DEBUG,
+ "<%s> RR received from %s to %s on %s",
+ __FUNCTION__,
+ 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));
+
+ rr_rcvifindex = pi->ipi6_ifindex;
+
+ /* TODO: some consistency check. */
+
+ 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",
+ __FUNCTION__, rr->rr_code);
+ break;
+
+ }
+
+ return;
+}
diff --git a/usr.sbin/rtadvd/rrenum.h b/usr.sbin/rtadvd/rrenum.h
new file mode 100644
index 00000000000..78bdf6f248c
--- /dev/null
+++ b/usr.sbin/rtadvd/rrenum.h
@@ -0,0 +1,32 @@
+/*
+ * 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 __P((int len, struct icmp6_router_renum *rr,
+ struct in6_pktinfo *pi, struct sockaddr_in6 *from,
+ struct in6_addr *dst));
diff --git a/usr.sbin/rtadvd/rtadvd.8 b/usr.sbin/rtadvd/rtadvd.8
new file mode 100644
index 00000000000..f3d2912f253
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.8
@@ -0,0 +1,116 @@
+.\" 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.
+.\"
+.\" KAME Id: rtadvd.8,v 1.1.1.1 1999/08/08 23:31:42 itojun Exp
+.\"
+.Dd May 17, 1998
+.Dt RTADVD 8
+.Os KAME
+.Sh NAME
+.Nm rtadvd
+.Nd router advertisement daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar configfile
+.Op Fl dDfs
+.Ar interface ...
+.Sh DESCRIPTION
+.Nm Rtadvd
+advertises router advertisement packet to the specified
+.Ar interfaces .
+.Pp
+The program will daemonize itself on invocation.
+Then, it will voluntarily send router advertisement packet periodically.
+If a router solicitation packet from host has reached the program,
+the program will respond by router advertisement packet.
+.Pp
+For each interface, which is called advertising interface,
+content of router advertisement can be described in
+.Xr rtadvd.conf 5 .
+.Pp
+If there is no description for the interface in the configuration file
+or if the configuration file does not exist,
+.Nm
+sets all the parameters to their default values.
+In particular,
+.Nm
+gets all the interface routes from the routing table and advertises
+them as on-link prefixes.
+.Pp
+.Nm Rtadvd
+watches the routing table.
+By default, if an interface direct route is
+added/deleted on an advertising interface,
+.Nm
+adds/deletes the corresponding prefix to/from its advertising list,
+respectively.
+If you do not want to enable this feature, you should specify the
+.Ic Fl s
+command line option when advocation.
+.Bl -tag -width indent
+.\"
+.It Fl c
+Specify an alternate location,
+.Ar configfile ,
+for the configuration file.
+By default,
+.Pa /usr/local/v6/etc/rtadvd.conf
+is used.
+.It Fl d
+Debug.
+.It Fl D
+More debug.
+.It Fl f
+Foreground mode.
+Do not become daemon.
+.It Fl s
+Static prefix.
+Do not watch the routing table.
+.El
+.Sh RETURN VALUES
+The program exits with 0 on success, and non-zero on failures.
+.Sh FILES
+.Bl -tag -width /usr/local/v6/etc/rtadvd.conf -compact
+.It Pa /usr/local/v6/etc/rtadvd.conf
+The default configuration file.
+.El
+.Sh SEE ALSO
+.Xr daemon 3 ,
+.Xr rtadvd.conf 5 ,
+.Xr rtsol 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+.Sh CAVEAT
+Do not perform router advertisement toward upstream direction,
+you should only advertise to downstream direction.
+If you advertise toward upstream by mistake,
+you will see icmp6 redirect storm on that subnet.
+This is because of the specification,
+which says that advertising router is assumed to become
+the default outgoing router for end hosts in the subnet.
diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c
new file mode 100644
index 00000000000..23aa48a6297
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.c
@@ -0,0 +1,1262 @@
+/*
+ * 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 <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>
+#include "rtadvd.h"
+#include "rrenum.h"
+#include "advcap.h"
+#include "timer.h"
+#include "if.h"
+#include "config.h"
+
+struct msghdr rcvmhdr;
+static u_char rcvcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int))];
+struct msghdr sndmhdr;
+struct iovec rcviov[2];
+struct iovec sndiov[2];
+struct sockaddr_in6 from;
+struct sockaddr_in6 sin6_allnodes = {sizeof(sin6_allnodes), AF_INET6};
+int sock, rtsock;
+int accept_rr = 1;
+int dflag = 0, sflag = 0;
+
+u_char *conffile = NULL;
+
+struct rainfo *ralist = NULL;
+struct nd_optlist {
+ struct nd_optlist *next;
+ struct nd_opt_hdr *opt;
+};
+union nd_opts {
+ struct nd_opt_hdr *nd_opt_array[7];
+ 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;
+ struct 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 0x1
+#define NDOPT_FLAG_TGTLINKADDR 0x2
+#define NDOPT_FLAG_PREFIXINFO 0x4
+#define NDOPT_FLAG_RDHDR 0x8
+#define NDOPT_FLAG_MTU 0x10
+
+u_int32_t ndopt_flags[] = {
+ 0, NDOPT_FLAG_SRCLINKADDR, NDOPT_FLAG_TGTLINKADDR,
+ NDOPT_FLAG_PREFIXINFO, NDOPT_FLAG_RDHDR, NDOPT_FLAG_MTU
+};
+
+int main __P((int, char *[]));
+static void sock_open __P((void));
+static void rtsock_open __P((void));
+static void rtadvd_input __P((void));
+static void rs_input __P((int, struct nd_router_solicit *,
+ struct in6_pktinfo *, struct sockaddr_in6 *));
+static void ra_input __P((int, struct nd_router_advert *,
+ struct in6_pktinfo *, struct sockaddr_in6 *));
+static void prefix_check __P((struct nd_opt_prefix_info *, struct rainfo *,
+ struct sockaddr_in6 *));
+static int nd6_options __P((struct nd_opt_hdr *, int,
+ union nd_opts *, u_int32_t));
+static void free_ndopts __P((union nd_opts *));
+static struct rainfo *if_indextorainfo __P((int));
+static void ra_output __P((struct rainfo *));
+static void rtmsg_input __P((void));
+struct prefix *find_prefix __P((struct rainfo *, struct in6_addr *, int));
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ fd_set fdset;
+ int maxfd = 0;
+ struct timeval *timeout;
+ int i, ch;
+ int fflag = 0;
+
+ openlog(*argv, LOG_NDELAY|LOG_PID, LOG_DAEMON);
+
+ /* get command line options and arguments */
+ while ((ch = getopt(argc, argv, "c:dDfs")) != -1) {
+ switch(ch) {
+ case 'c':
+ conffile = optarg;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'D':
+ dflag = 2;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc == 0) {
+ fprintf(stderr,
+ "usage: rtadvd [-c conffile] [-d|D] [-f] [-s]"
+ "interfaces...\n");
+ exit(1);
+ }
+
+ /* set log level */
+ if (dflag == 0)
+ (void)setlogmask(LOG_UPTO(LOG_ERR));
+ if (dflag == 1)
+ (void)setlogmask(LOG_UPTO(LOG_INFO));
+
+ /* timer initialization */
+ rtadvd_timer_init();
+
+ /* random value initialization */
+ srandom((u_long)time(NULL));
+
+ /* get iflist block from kernel */
+ init_iflist();
+
+ while (argc--)
+ getconfig(*argv++);
+
+ if (inet_pton(AF_INET6, ALLNODES, &sin6_allnodes.sin6_addr) != 1) {
+ fprintf(stderr, "fatal: inet_pton failed\n");
+ exit(1);
+ }
+ sock_open();
+
+ if (!fflag)
+ daemon(1, 0);
+
+ FD_ZERO(&fdset);
+ FD_SET(sock, &fdset);
+ maxfd = sock;
+ if (sflag == 0) {
+ rtsock_open();
+ FD_SET(rtsock, &fdset);
+ if (rtsock > sock)
+ maxfd = rtsock;
+ }
+
+ while (1) {
+ struct fd_set select_fd = fdset; /* reinitialize */
+
+ /* timer expiration check and reset the timer */
+ timeout = rtadvd_check_timer();
+
+ syslog(LOG_DEBUG,
+ "<%s> set timer to %ld:%ld. waiting for inputs "
+ "or timeout",
+ __FUNCTION__,
+ timeout->tv_sec, timeout->tv_usec);
+
+ if ((i = select(maxfd + 1, &select_fd,
+ NULL, NULL, timeout)) < 0){
+ syslog(LOG_ERR, "<%s> select: %s",
+ __FUNCTION__, strerror(errno));
+ continue;
+ }
+ if (i == 0) /* timeout */
+ continue;
+ if (sflag == 0 && FD_ISSET(rtsock, &select_fd))
+ rtmsg_input();
+ if (FD_ISSET(sock, &select_fd))
+ rtadvd_input();
+ }
+ exit(0); /* NOTREACHED */
+}
+
+static void
+rtmsg_input()
+{
+ int n, type, ifindex, plen;
+ size_t len;
+ char msg[2048], *next, *lim;
+ u_char ifname[16];
+ struct prefix *prefix;
+ struct rainfo *rai;
+ struct in6_addr *addr;
+ char addrbuf[INET6_ADDRSTRLEN];
+
+ n = read(rtsock, msg, 2048);
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s> received a routing message "
+ "(type = %d, len = %d)",
+ __FUNCTION__,
+ rtmsg_type(msg), n);
+ }
+ if (n > rtmsg_len(msg)) {
+ /*
+ * This usually won't happen for messages received on
+ * an 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",
+ __FUNCTION__, n, rtmsg_len(msg));
+#if 0
+ /* adjust length */
+ n = rtmsg_len(msg);
+#endif
+ }
+
+ lim = msg + n;
+ for (next = msg; next < lim; next += len) {
+ next = get_next_msg(next, lim, 0, &len,
+ RTADV_TYPE2BITMASK(RTM_ADD) |
+ RTADV_TYPE2BITMASK(RTM_DELETE) |
+ RTADV_TYPE2BITMASK(RTM_NEWADDR) |
+ RTADV_TYPE2BITMASK(RTM_DELADDR) |
+ RTADV_TYPE2BITMASK(RTM_IFINFO));
+ 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;
+ 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",
+ __FUNCTION__, __LINE__, type,
+ if_indextoname(ifindex, ifname));
+ }
+ return;
+ }
+
+ if ((rai = if_indextorainfo(ifindex)) == NULL) {
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s> route changed on "
+ "non advertising interface(%s)",
+ __FUNCTION__,
+ if_indextoname(ifindex, ifname));
+ }
+ return;
+ }
+
+ switch(type) {
+ case RTM_ADD:
+ /* init iffalgs because it may have changed */
+ iflist[ifindex]->ifm_flags =
+ if_getflags(ifindex,
+ iflist[ifindex]->ifm_flags);
+
+ addr = get_addr(msg);
+ plen = get_prefixlen(msg);
+ /* sanity check for plen */
+ if (plen < 4 /* as RFC2373, prefixlen is at least 4 */
+ || plen > 127) {
+ syslog(LOG_INFO, "<%s> new interface route's"
+ "plen %d is invalid for a prefix",
+ __FUNCTION__, plen);
+ return;
+ }
+ prefix = find_prefix(rai, addr, plen);
+ if (prefix) {
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s> new prefix(%s/%d) "
+ "added on %s, "
+ "but it was already in list",
+ __FUNCTION__,
+ inet_ntop(AF_INET6,
+ addr, (char *)addrbuf,
+ INET6_ADDRSTRLEN),
+ plen,
+ rai->ifname);
+ }
+ return;
+ }
+ make_prefix(rai, ifindex, addr, plen);
+ break;
+ case RTM_DELETE:
+ /* init ifflags because it may have changed */
+ iflist[ifindex]->ifm_flags =
+ if_getflags(ifindex,
+ iflist[ifindex]->ifm_flags);
+
+ addr = get_addr(msg);
+ plen = get_prefixlen(msg);
+ /* sanity check for plen */
+ if (plen < 4 /* as RFC2373, prefixlen is at least 4 */
+ || plen > 127) {
+ syslog(LOG_INFO, "<%s> deleted interface"
+ "route's"
+ "plen %d is invalid for a prefix",
+ __FUNCTION__, plen);
+ return;
+ }
+ 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",
+ __FUNCTION__,
+ inet_ntop(AF_INET6,
+ addr, (char *)addrbuf,
+ INET6_ADDRSTRLEN),
+ plen,
+ rai->ifname);
+ }
+ return;
+ }
+ delete_prefix(rai, prefix);
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ /* init ifflags because it may have changed */
+ iflist[ifindex]->ifm_flags =
+ if_getflags(ifindex,
+ iflist[ifindex]->ifm_flags);
+ break;
+ case RTM_IFINFO:
+ iflist[ifindex]->ifm_flags = get_ifm_flags(next);
+ break;
+ default:
+ /* should not reach here */
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s:%d> unknown rtmsg %d on %s",
+ __FUNCTION__, __LINE__, type,
+ if_indextoname(ifindex, ifname));
+ }
+ return;
+ }
+ }
+
+ return;
+}
+
+void
+rtadvd_input()
+{
+ int 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;
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ struct in6_addr dst = in6addr_any;
+
+ /*
+ * 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 = sizeof(rcvcmsgbuf);
+ if ((i = recvmsg(sock, &rcvmhdr, 0)) < 0)
+ return;
+
+ /* extract optional information via Advanced API */
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr);
+ cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO &&
+ cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
+ pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ ifindex = pi->ipi6_ifindex;
+ dst = pi->ipi6_addr;
+ }
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ hlimp = (int *)CMSG_DATA(cm);
+ }
+ if (ifindex == 0) {
+ syslog(LOG_ERR,
+ "<%s> failed to get receiving interface",
+ __FUNCTION__);
+ return;
+ }
+ if (hlimp == NULL) {
+ syslog(LOG_ERR,
+ "<%s> failed to get receiving hop limit",
+ __FUNCTION__);
+ return;
+ }
+
+#ifdef OLDRAWSOCKET
+ if (i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
+ syslog(LOG_ERR,
+ "<%s> packet size(%d) is too short",
+ __FUNCTION__, i);
+ return;
+ }
+
+ ip = (struct ip6_hdr *)rcvmhdr.msg_iov[0].iov_base;
+ icp = (struct icmp6_hdr *)(ip + 1); /* XXX: ext. hdr? */
+#else
+ if (i < sizeof(struct icmp6_hdr)) {
+ syslog(LOG_ERR,
+ "<%s> packet size(%d) is too short",
+ __FUNCTION__, 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",
+ __FUNCTION__, *hlimp,
+ inet_ntop(AF_INET6, &from.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",
+ __FUNCTION__, icp->icmp6_code,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (i < sizeof(struct nd_router_solicit)) {
+ syslog(LOG_NOTICE,
+ "<%s> RS from %s on %s does not have enough "
+ "length (len = %d)",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
+ return;
+ }
+ rs_input(i, (struct nd_router_solicit *)icp, pi, &from);
+ 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",
+ __FUNCTION__, *hlimp,
+ inet_ntop(AF_INET6, &from.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",
+ __FUNCTION__, icp->icmp6_code,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (i < sizeof(struct nd_router_advert)) {
+ syslog(LOG_NOTICE,
+ "<%s> RA from %s on %s does not have enough "
+ "length (len = %d)",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
+ return;
+ }
+ ra_input(i, (struct nd_router_advert *)icp, pi, &from);
+ break;
+ case ICMP6_ROUTER_RENUMBERING:
+ if (accept_rr == 0) {
+ syslog(LOG_ERR,
+ "<%s> received a router renumbering "
+ "message, but not allowed to be accepted",
+ __FUNCTION__);
+ break;
+ }
+ rr_input(i, (struct icmp6_router_renum *)icp, pi, &from,
+ &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)",
+ __FUNCTION__, icp->icmp6_type);
+ return;
+ }
+
+ return;
+}
+
+static void
+rs_input(int len, struct nd_router_solicit *rs,
+ struct in6_pktinfo *pi, struct sockaddr_in6 *from)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ union nd_opts ndopts;
+ struct rainfo *ra;
+
+ syslog(LOG_DEBUG,
+ "<%s> RS received from %s on %s",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* ND option check */
+ memset(&ndopts, 0, sizeof(ndopts));
+ if (nd6_options((struct nd_opt_hdr *)(rs + 1),
+ len - sizeof(struct nd_router_solicit),
+ &ndopts, NDOPT_FLAG_SRCLINKADDR)) {
+ syslog(LOG_DEBUG,
+ "<%s> ND option check failed for an RS from %s on %s",
+ __FUNCTION__,
+ 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_ERR,
+ "<%s> RS from unspecified src on %s has a link-layer"
+ " address option",
+ __FUNCTION__,
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+
+ ra = ralist;
+ while (ra != NULL) {
+ if (pi->ipi6_ifindex == ra->ifindex)
+ break;
+ ra = ra->next;
+ }
+ if (ra == NULL) {
+ syslog(LOG_INFO,
+ "<%s> RS received on non advertising interface(%s)",
+ __FUNCTION__,
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+
+ /*
+ * Decide whether to send RA according to the rate-limit
+ * consideration.
+ */
+ {
+ long delay; /* must not be greater than 1000000 */
+ struct timeval interval, now, min_delay, tm_tmp, *rest;
+
+ /*
+ * If there is already a waiting RS packet, don't
+ * update the timer.
+ */
+ if (ra->waiting++)
+ goto done;
+
+ /*
+ * Compute a random delay. If the computed value
+ * corresponds to a time later than the time the next
+ * multicast RA is scheduled to be sent, ignore the random
+ * delay and send the advertisement at the
+ * already-scheduled time. RFC-2461 6.2.6
+ */
+ delay = random() % MAX_RA_DELAY_TIME;
+ interval.tv_sec = 0;
+ interval.tv_usec = delay;
+ rest = rtadvd_timer_rest(ra->timer);
+ if (TIMEVAL_LT(*rest, interval)) {
+ syslog(LOG_DEBUG,
+ "<%s> random delay is larger than "
+ "the rest of normal timer",
+ __FUNCTION__);
+ 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.
+ */
+ gettimeofday(&now, NULL);
+ TIMEVAL_SUB(&now, &ra->lastsent, &tm_tmp);
+ min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS;
+ min_delay.tv_usec = 0;
+ if (TIMEVAL_LT(tm_tmp, min_delay)) {
+ TIMEVAL_SUB(&min_delay, &tm_tmp, &min_delay);
+ TIMEVAL_ADD(&min_delay, &interval, &interval);
+ }
+ rtadvd_set_timer(&interval, ra->timer);
+ goto done;
+ }
+
+ done:
+ free_ndopts(&ndopts);
+ return;
+}
+
+static void
+ra_input(int len, struct nd_router_advert *ra,
+ struct in6_pktinfo *pi, struct sockaddr_in6 *from)
+{
+ struct rainfo *rai;
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ union nd_opts ndopts;
+ char *on_off[] = {"OFF", "ON"};
+ u_int32_t reachabletime, retranstimer, mtu;
+
+ syslog(LOG_DEBUG,
+ "<%s> RA received from %s on %s",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* ND option check */
+ memset(&ndopts, 0, sizeof(ndopts));
+ if (nd6_options((struct nd_opt_hdr *)(ra + 1),
+ len - sizeof(struct nd_router_advert),
+ &ndopts,
+ NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU)) {
+ syslog(LOG_ERR,
+ "<%s> ND option check failed for an RA from %s on %s",
+ __FUNCTION__,
+ 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)",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+ /* Cur Hop Limit value */
+ if (ra->nd_ra_curhoplimit && rai->hoplimit &&
+ ra->nd_ra_curhoplimit != rai->hoplimit) {
+ syslog(LOG_WARNING,
+ "<%s> CurHopLimit inconsistent on %s:"
+ " %d from %s, %d from us",
+ __FUNCTION__,
+ rai->ifname,
+ ra->nd_ra_curhoplimit,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->hoplimit);
+ }
+ /* M flag */
+ if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) !=
+ rai->managedflg) {
+ syslog(LOG_WARNING,
+ "<%s> M flag inconsistent on %s:"
+ " %s from %s, %s from us",
+ __FUNCTION__,
+ rai->ifname,
+ on_off[!rai->managedflg],
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ on_off[rai->managedflg]);
+ }
+ /* O flag */
+ if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) !=
+ rai->otherflg) {
+ syslog(LOG_WARNING,
+ "<%s> O flag inconsistent on %s:"
+ " %s from %s, %s from us",
+ __FUNCTION__,
+ rai->ifname,
+ on_off[!rai->otherflg],
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ on_off[rai->otherflg]);
+ }
+ /* Reachable Time */
+ reachabletime = ntohl(ra->nd_ra_reachable);
+ if (reachabletime && rai->reachabletime &&
+ reachabletime != rai->reachabletime) {
+ syslog(LOG_WARNING,
+ "<%s> ReachableTime inconsistent on %s:"
+ " %d from %s, %d from us",
+ __FUNCTION__,
+ rai->ifname,
+ reachabletime,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->reachabletime);
+ }
+ /* Retrans Timer */
+ retranstimer = ntohl(ra->nd_ra_retransmit);
+ if (retranstimer && rai->retranstimer &&
+ retranstimer != rai->retranstimer) {
+ syslog(LOG_WARNING,
+ "<%s> RetranceTimer inconsistent on %s:"
+ " %d from %s, %d from us",
+ __FUNCTION__,
+ rai->ifname,
+ retranstimer,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->retranstimer);
+ }
+ /* 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_WARNING,
+ "<%s> MTU option value inconsistent on %s:"
+ " %d from %s, %d from us",
+ __FUNCTION__,
+ rai->ifname, mtu,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->linkmtu);
+ }
+ }
+ /* Preferred and Valid Lifetimes for prefixes */
+ {
+ struct nd_optlist *optp = ndopts.nd_opts_list;
+
+ if (ndopts.nd_opts_pi)
+ prefix_check(ndopts.nd_opts_pi, rai, from);
+ while (optp) {
+ prefix_check((struct nd_opt_prefix_info *)optp->opt,
+ rai, from);
+ optp = optp->next;
+ }
+ }
+
+ done:
+ free_ndopts(&ndopts);
+ return;
+}
+
+static void
+prefix_check(struct nd_opt_prefix_info *pinfo,
+ struct rainfo *rai, struct sockaddr_in6 *from)
+{
+ u_int32_t preferred_time, valid_time;
+ struct prefix *pp;
+ u_char ntopbuf[INET6_ADDRSTRLEN], prefixbuf[INET6_ADDRSTRLEN];
+
+#if 0 /* impossible */
+ if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION)
+ return;
+#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",
+ __FUNCTION__,
+ 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",
+ __FUNCTION__,
+ 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;
+ }
+
+ preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time);
+ if (preferred_time != pp->preflifetime)
+ syslog(LOG_WARNING,
+ "<%s> prefeerred lifetime for %s/%d"
+ " inconsistent on %s:"
+ " %d from %s, %d from us",
+ __FUNCTION__,
+ 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 (valid_time != pp->validlifetime)
+ syslog(LOG_WARNING,
+ "<%s> valid lifetime for %s/%d"
+ " inconsistent on %s:"
+ " %d from %s, %d from us",
+ __FUNCTION__,
+ 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);
+}
+
+struct prefix *
+find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen)
+{
+ struct prefix *pp;
+ int bytelen, bitlen;
+
+ for (pp = rai->prefix.next; pp != &rai->prefix; pp = pp->next) {
+ if (plen != pp->prefixlen)
+ continue;
+ bytelen = plen / 8;
+ bitlen = plen % 8;
+ if (memcmp((void *)prefix, (void *)&pp->prefix, bytelen))
+ continue;
+ if (prefix->s6_addr[bytelen] >> (8 - bitlen) ==
+ pp->prefix.s6_addr[bytelen] >> (8 - bitlen))
+ return(pp);
+ }
+
+ return(NULL);
+}
+
+static int
+nd6_options(struct nd_opt_hdr *hdr, int limit,
+ union nd_opts *ndopts, u_int32_t optflags)
+{
+ int optlen = 0;
+
+ for (; limit > 0; limit -= optlen) {
+ hdr = (struct nd_opt_hdr *)((caddr_t)hdr + optlen);
+ optlen = hdr->nd_opt_len << 3;
+ if (hdr->nd_opt_len == 0) {
+ syslog(LOG_ERR,
+ "<%s> bad ND option length(0) (type = %d)",
+ __FUNCTION__, hdr->nd_opt_type);
+ goto bad;
+ }
+
+ if (hdr->nd_opt_type > ND_OPT_MTU) {
+ syslog(LOG_INFO,
+ "<%s> unknown ND option(type %d)",
+ __FUNCTION__,
+ hdr->nd_opt_type);
+ continue;
+ }
+
+ if ((ndopt_flags[hdr->nd_opt_type] & optflags) == 0) {
+ syslog(LOG_INFO,
+ "<%s> unexpected ND option(type %d)",
+ __FUNCTION__,
+ hdr->nd_opt_type);
+ continue;
+ }
+
+ switch(hdr->nd_opt_type) {
+ case ND_OPT_SOURCE_LINKADDR:
+ case ND_OPT_TARGET_LINKADDR:
+ case ND_OPT_REDIRECTED_HEADER:
+ case ND_OPT_MTU:
+ if (ndopts->nd_opt_array[hdr->nd_opt_type]) {
+ syslog(LOG_INFO,
+ "<%s> duplicated ND option"
+ " (type = %d)",
+ __FUNCTION__,
+ 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",
+ __FUNCTION__);
+ goto bad;
+ }
+ pfxlist->next = ndopts->nd_opts_list;
+ pfxlist->opt = hdr;
+ ndopts->nd_opts_list = pfxlist;
+
+ break;
+ }
+ default: /* impossible */
+ break;
+ }
+ }
+
+ return(0);
+
+ bad:
+ free_ndopts(ndopts);
+
+ return(-1);
+}
+
+static void
+free_ndopts(union nd_opts *ndopts)
+{
+ struct nd_optlist *opt = ndopts->nd_opts_list, *next;
+
+ while(opt) {
+ next = opt->next;
+ free(opt);
+ opt = next;
+ }
+}
+
+void
+sock_open()
+{
+ struct icmp6_filter filt;
+ struct ipv6_mreq mreq;
+ struct rainfo *ra = ralist;
+ int on;
+ /* XXX: should be max MTU attached to the node */
+ static u_char answer[1500];
+ static u_char sndcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int))];
+
+ if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
+ strerror(errno));
+ exit(1);
+ }
+
+ /* specify to tell receiving interface */
+ on = 1;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_PKTINFO: %s",
+ __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+
+ on = 1;
+ /* specify to tell value of hoplimit field of received IP6 hdr */
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_HOPLIMIT: %s",
+ __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+
+ 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: %s",
+ __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * join all routers multicast address on each advertising interface.
+ */
+ if (inet_pton(AF_INET6, ALLROUTERS, &mreq.ipv6mr_multiaddr.s6_addr)
+ != 1) {
+ syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)",
+ __FUNCTION__);
+ exit(1);
+ }
+ while(ra) {
+ mreq.ipv6mr_interface = ra->ifindex;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq,
+ sizeof(mreq)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP on %s: %s",
+ __FUNCTION__, ra->ifname, strerror(errno));
+ exit(1);
+ }
+ ra = ra->next;
+ }
+
+ /* initialize msghdr for receiving packets */
+ rcviov[0].iov_base = (caddr_t)answer;
+ rcviov[0].iov_len = sizeof(answer);
+ rcvmhdr.msg_name = (caddr_t)&from;
+ rcvmhdr.msg_namelen = sizeof(from);
+ rcvmhdr.msg_iov = rcviov;
+ rcvmhdr.msg_iovlen = 1;
+ rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
+ rcvmhdr.msg_controllen = sizeof(rcvcmsgbuf);
+
+ /* initialize msghdr for sending packets */
+ sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmhdr.msg_iov = sndiov;
+ sndmhdr.msg_iovlen = 1;
+ sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
+ sndmhdr.msg_controllen = sizeof(sndcmsgbuf);
+
+ return;
+}
+
+/* open a routing socket to watch the routing table */
+static void
+rtsock_open()
+{
+ if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
+ syslog(LOG_ERR,
+ "<%s> socket: %s", __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+}
+
+static struct rainfo *
+if_indextorainfo(int index)
+{
+ struct rainfo *rai = ralist;
+
+ for (rai = ralist; rai; rai = rai->next) {
+ if (rai->ifindex == index)
+ return(rai);
+ }
+
+ return(NULL); /* search failed */
+}
+
+static void
+ra_output(rainfo)
+struct rainfo *rainfo;
+{
+ int i;
+
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi;
+
+ sndmhdr.msg_name = (caddr_t)&sin6_allnodes;
+ sndmhdr.msg_iov[0].iov_base = (caddr_t)rainfo->ra_data;
+ sndmhdr.msg_iov[0].iov_len = rainfo->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 = rainfo->ifindex;
+
+ /* specify the hop limit of the packet */
+ {
+ int hoplimit = 255;
+
+ cm = CMSG_NXTHDR(&sndmhdr, cm);
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_HOPLIMIT;
+ cm->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
+ }
+
+ syslog(LOG_DEBUG,
+ "<%s> send RA on %s, # of waitings = %d",
+ __FUNCTION__, rainfo->ifname, rainfo->waiting);
+
+ i = sendmsg(sock, &sndmhdr, 0);
+
+ if (i < 0 || i != rainfo->ra_datalen) {
+ if (i < 0) {
+ syslog(LOG_ERR, "<%s> sendmsg on %s: %s",
+ __FUNCTION__, rainfo->ifname,
+ strerror(errno));
+ }
+ }
+
+ /* update counter */
+ if (rainfo->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS)
+ rainfo->initcounter++;
+
+ /* update timestamp */
+ gettimeofday(&rainfo->lastsent, NULL);
+
+ /* reset waiting conter */
+ rainfo->waiting = 0;
+}
+
+/* process RA timer */
+void
+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",
+ __FUNCTION__, rai->ifname);
+
+ if (iflist[rai->ifindex]->ifm_flags & IFF_UP)
+ ra_output(rai);
+ else
+ syslog(LOG_DEBUG, "<%s> %s is not up, skip sending RA",
+ __FUNCTION__, rai->ifname);
+}
+
+/* update RA timer */
+void
+ra_timer_update(void *data, struct timeval *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(discovery-v2-02 6.2.4).
+ */
+ interval = rai->mininterval;
+ interval += random() % (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_usec = 0;
+
+ syslog(LOG_DEBUG,
+ "<%s> RA timer on %s is set to %ld:%ld",
+ __FUNCTION__, rai->ifname, tm->tv_sec, tm->tv_usec);
+
+ return;
+}
diff --git a/usr.sbin/rtadvd/rtadvd.conf b/usr.sbin/rtadvd/rtadvd.conf
new file mode 100644
index 00000000000..6f563564a61
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.conf
@@ -0,0 +1,13 @@
+#
+# common definitions.
+#
+default:\
+ :chlim#64:raflags#0:rltime#1800:rtime#30000:retrans#1000:\
+ :pinfoflags#192:vltime#3600000:pltime#3600000:mtu#1500:
+ether:\
+ :mtu#1500:tc=default:
+#
+# interfaces.
+#
+ef0:\
+ :addrs#1:addr="3ffe:501:4819:1000::":prefixlen#64:tc=ether:
diff --git a/usr.sbin/rtadvd/rtadvd.conf.5 b/usr.sbin/rtadvd/rtadvd.conf.5
new file mode 100644
index 00000000000..18446d18427
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.conf.5
@@ -0,0 +1,249 @@
+.\" 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.
+.\"
+.\" KAME Id: rtadvd.conf.5,v 1.1.1.1 1999/08/08 23:31:42 itojun Exp
+.\"
+.Dd May 17, 1998
+.Dt RTADVD.CONF 5
+.Os KAME
+.Sh NAME
+.Nm rtadvd.conf
+.Nd config file for router advertisement daemon
+.Sh DESCRIPTION
+The file describes how the router advertisement packet must be constructed
+for each of the interfaces.
+.Pp
+It obeys famous
+.Xr termcap 5
+file format.
+Each line in the file describes a network interface.
+Fields are separated by a colon
+.Po
+.Dq \&:
+.Pc ,
+and each field contains one capability description.
+Lines may be concatenated by \e character.
+The comment marker is `#' character.
+.Pp
+.Sh CAPABILITIES
+Capabilities describe the value to be filled into ICMPv6 router
+advertisement message and to control
+.Xr rtadvd 8
+behavior.
+Therefore, you are encouraged to read IETF neighbor discovery documents
+if you would like to modify 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 to control interval of sending router advertisements.
+.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
+.Ic maxinterval.
+Its value must be no less than 3 seconds and no greater than .75 *
+the value of
+.Ic maxinterval.
+.El
+.Pp
+The following items are for ICMPv6 router advertisement message
+header.
+.Bl -tag -width indent
+.It Cm \&chlim
+(num) The value for Cur Hop Limit field.
+The default value is 64.
+.It Cm \&raflags
+(num) Flags field in router advertisement message header.
+Bit 7
+.Po
+.Li 0x80
+.Pc
+means Managed address configuration flag bit,
+and Bit 6
+.Po
+.Li 0x40
+.Pc
+means Other stateful configuration flag bit.
+The default value is 0.
+.It Cm \&rltime
+(num) Router lifetime field
+.Pq unit: seconds .
+Its value must be no greater than 3600000.
+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.
+.Bl -tag -width indent
+.It Cm \&addrs
+(num) Number of prefixes.
+Its default is 0, so it must explicitly be set to positve values
+if you want to specify any prefix information option.
+If its value is 0,
+.Xr rtadvd 8
+looks up the system routing table and
+advertise the prefixes corresponding to interface routes
+on the interface.
+If its value is more than 1, you must specify the index of the prefix
+for each item below.
+Indices vary from 0 to N-1, where N is the
+value of
+.Ic addrs.
+Each index shall follows the name of each item, e.g.
+.Dq prefixlen2 .
+.It Cm \&prefixlen
+(num) Prefix length field.
+The default value is 64.
+.It Cm \&pinfoflags
+(num) Flags field in prefix information option.
+Bit 7
+.Po
+.Li 0x80
+.Pc
+means On-link flag bit,
+and Bit 6
+.Po
+.Li 0x40
+.Pc
+means Autonomous address-configuration flag bit.
+The default value is 0xc0, i.e. both bits are set.
+.It Cm \&addr
+(str) The address filled into Prefix field.
+Since
+.Dq \&:
+is used for
+.Xr termcap 5
+file format as well as IPv6 numeric address, the field MUST be quoted by
+doublequote character.
+This field cannot be
+omitted if the value of
+.Ic addrs
+is more than 0.
+.It Cm \&vltime
+(num) Valid lifetime field
+.Pq unit: seconds .
+The default value is 2592000(30 days).
+.It Cm \&pltime
+(num) Preferred lifetime field
+.Pq unit: seconds .
+The default value is 604800(7 days).
+.El
+.Pp
+The following items are for ICMPv6 MTU option,
+which will be attached to router advertisement header.
+.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.
+.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
+You can also refer one line from another by using
+.Cm tc
+capability.
+See
+.Xr termcap 5
+for details on the capability.
+.Sh EXAMPLE
+.Bd -literal -offset
+#
+# common definitions.
+#
+default:\\
+ :raflags#0:rltime#3600:\\
+ :pinfoflags#64:vltime#360000:pltime#360000:mtu#1500:
+ether:\\
+ :mtu#1280:tc=default:
+
+#
+# interfaces.
+#
+ef0:\\
+ :addrs#1:\\
+ :addr="3ffe:501:4819:1000::":tc=ether:
+ef1:\\
+ :addrs#2:addr0="3ffe:501:4819:2000::":\\
+ :addr1="3ffe:501:4819:3000::":tc=ether:
+
+.Ed
+.Sh SEE ALSO
+.Xr termcap 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
+.Sh HISTORY
+The
+.Xr rtadvd 8
+and the configuration file
+.Nm
+first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h
new file mode 100644
index 00000000000..b1403b17c82
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.h
@@ -0,0 +1,104 @@
+/*
+ * 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 ALLNODES "ff02::1"
+#define ALLROUTERS "ff02::2"
+#define ANY "::"
+#define RTSOLLEN 8
+
+/* protocol constants and default values */
+#define DEF_MAXRTRADVINTERVAL 600
+#define DEF_ADVLINKMTU 0
+#define DEF_ADVREACHABLETIME 0
+#define DEF_ADVRETRANSTIMER 0
+#define DEF_ADVCURHOPLIMIT 64
+#define DEF_ADVVALIDLIFETIME 2592000
+#define DEF_ADVPREFERREDLIFETIME 604800
+
+#define MAXROUTERLIFETIME 9000
+#define MIN_MAXINTERVAL 4
+#define MAX_MAXINTERVAL 1800
+#define MIN_MININTERVAL 3
+#define MAXREACHABLETIME 3600000
+
+#define MAX_INITIAL_RTR_ADVERT_INTERVAL 16
+#define MAX_INITIAL_RTR_ADVERTISEMENTS 3
+#define MAX_FINAL_RTR_ADVERTISEMENTS 3
+#define MIN_DELAY_BETWEEN_RAS 3
+#define MAX_RA_DELAY_TIME 500000 /* usec */
+
+struct prefix {
+ struct prefix *next; /* forward link */
+ struct prefix *prev; /* previous link */
+
+ u_int32_t validlifetime; /* AdvValidLifetime */
+ u_int32_t preflifetime; /* AdvPreferredLifetime */
+ u_int onlinkflg; /* bool: AdvOnLinkFlag */
+ u_int autoconfflg; /* bool: AdvAutonomousFlag */
+ int prefixlen;
+ struct in6_addr prefix;
+};
+
+struct rainfo {
+ /* pointer for list */
+ struct rainfo *next;
+
+ /* timer related parameters */
+ struct rtadvd_timer *timer;
+ int initcounter; /* counter for the first few advertisements */
+ struct timeval lastsent; /* timestamp when the lates RA was sent */
+ int waiting; /* number of RS waiting for RA */
+
+ /* interface information */
+ int ifindex;
+ int advlinkopt; /* bool: whether include link-layer addr opt */
+ struct sockaddr_dl *sdl;
+ char ifname[16];
+ int phymtu; /* mtu of the physical interface */
+
+ /* Router configuration variables */
+ u_short lifetime; /* AdvDefaultLifetime */
+ u_int maxinterval; /* MaxRtrAdvInterval */
+ u_int mininterval; /* MinRtrAdvInterval */
+ int managedflg; /* AdvManagedFlag */
+ int otherflg; /* AdvOtherConfigFlag */
+ u_int32_t linkmtu; /* AdvLinkMTU */
+ u_int32_t reachabletime; /* AdvReachableTime */
+ u_int32_t retranstimer; /* AdvRetransTimer */
+ u_int hoplimit; /* AdvCurHopLimit */
+ struct prefix prefix; /* AdvPrefixList(link head) */
+ int pfxs; /* number of prefixes */
+
+ /* actual RA packet data and its length */
+ size_t ra_datalen;
+ u_char *ra_data;
+};
+
+void ra_timeout __P((void *));
+void ra_timer_update __P((void *, struct timeval *));
diff --git a/usr.sbin/rtadvd/timer.c b/usr.sbin/rtadvd/timer.c
new file mode 100644
index 00000000000..aa3d1645165
--- /dev/null
+++ b/usr.sbin/rtadvd/timer.c
@@ -0,0 +1,199 @@
+/*
+ * 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/time.h>
+
+#include <unistd.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef __NetBSD__
+#include <search.h>
+#endif
+#include "timer.h"
+
+static struct rtadvd_timer timer_head;
+
+#define MILLION 1000000
+
+static struct timeval tm_max = {0x7fffffff, 0x7fffffff};
+
+void
+rtadvd_timer_init()
+{
+ memset(&timer_head, 0, sizeof(timer_head));
+
+ timer_head.next = timer_head.prev = &timer_head;
+ timer_head.tm = tm_max;
+}
+
+struct rtadvd_timer *
+rtadvd_add_timer(void (*timeout) __P((void *)),
+ void (*update) __P((void *, struct timeval *)),
+ void *timeodata, void *updatedata)
+{
+ struct rtadvd_timer *newtimer;
+
+ if ((newtimer = malloc(sizeof(*newtimer))) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't allocate memory", __FUNCTION__);
+ exit(1);
+ }
+
+ memset(newtimer, 0, sizeof(*newtimer));
+
+ if (timeout == NULL) {
+ syslog(LOG_ERR,
+ "<%s> timeout function unspecfied", __FUNCTION__);
+ exit(1);
+ }
+ if (update == NULL) {
+ syslog(LOG_ERR,
+ "<%s> update function unspecfied", __FUNCTION__);
+ exit(1);
+ }
+ newtimer->expire = timeout;
+ newtimer->update = update;
+ newtimer->expire_data = timeodata;
+ newtimer->update_data = updatedata;
+ newtimer->tm = tm_max;
+
+ /* link into chain */
+ insque(newtimer, &timer_head);
+
+ return(newtimer);
+}
+
+void
+rtadvd_set_timer(struct timeval *tm, struct rtadvd_timer *timer)
+{
+ struct timeval now;
+
+ /* reset the timer */
+ gettimeofday(&now, NULL);
+
+ TIMEVAL_ADD(&now, tm, &timer->tm);
+
+ /* update the next expiration time */
+ if (TIMEVAL_LT(timer->tm, timer_head.tm))
+ timer_head.tm = timer->tm;
+
+ return;
+}
+
+/*
+ * Check expiration for each timer. If a timer is expired,
+ * call the expire function for the timer and update the timer.
+ * Return the next interval for select() call.
+ */
+struct timeval *
+rtadvd_check_timer()
+{
+ static struct timeval returnval;
+ struct timeval now;
+ struct rtadvd_timer *tm = timer_head.next;
+
+ gettimeofday(&now, NULL);
+
+ timer_head.tm = tm_max;
+
+ while(tm != &timer_head) {
+ if (TIMEVAL_LEQ(tm->tm, now)) {
+ (*tm->expire)(tm->expire_data);
+ (*tm->update)(tm->update_data, &tm->tm);
+ TIMEVAL_ADD(&tm->tm, &now, &tm->tm);
+ }
+
+ if (TIMEVAL_LT(tm->tm, timer_head.tm))
+ timer_head.tm = tm->tm;
+
+ tm = tm->next;
+ }
+
+ if (TIMEVAL_LT(timer_head.tm, now)) {
+ /* this may occur when the interval is too small */
+ returnval.tv_sec = returnval.tv_usec = 0;
+ }
+ else
+ TIMEVAL_SUB(&timer_head.tm, &now, &returnval);
+ return(&returnval);
+}
+
+struct timeval *
+rtadvd_timer_rest(struct rtadvd_timer *timer)
+{
+ static struct timeval returnval, now;
+
+ gettimeofday(&now, NULL);
+ if (TIMEVAL_LEQ(timer->tm, now)) {
+ syslog(LOG_DEBUG,
+ "<%s> a timer must be expired, but not yet",
+ __FUNCTION__);
+ returnval.tv_sec = returnval.tv_usec = 0;
+ }
+ else
+ TIMEVAL_SUB(&timer->tm, &now, &returnval);
+
+ return(&returnval);
+}
+
+/* result = a + b */
+void
+TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result)
+{
+ long l;
+
+ if ((l = a->tv_usec + b->tv_usec) < MILLION) {
+ result->tv_usec = l;
+ result->tv_sec = a->tv_sec + b->tv_sec;
+ }
+ else {
+ result->tv_usec = l - MILLION;
+ result->tv_sec = a->tv_sec + b->tv_sec + 1;
+ }
+}
+
+/*
+ * result = a - b
+ * XXX: this function assumes that a >= b.
+ */
+void
+TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result)
+{
+ long l;
+
+ if ((l = a->tv_usec - b->tv_usec) >= 0) {
+ result->tv_usec = l;
+ result->tv_sec = a->tv_sec - b->tv_sec;
+ }
+ else {
+ result->tv_usec = MILLION + l;
+ result->tv_sec = a->tv_sec - b->tv_sec - 1;
+ }
+}
diff --git a/usr.sbin/rtadvd/timer.h b/usr.sbin/rtadvd/timer.h
new file mode 100644
index 00000000000..3a0c99b4033
--- /dev/null
+++ b/usr.sbin/rtadvd/timer.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+/* a < b */
+#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\
+ (((a).tv_sec == (b).tv_sec) && \
+ ((a).tv_usec < (b).tv_usec)))
+
+/* a <= b */
+#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\
+ (((a).tv_sec == (b).tv_sec) &&\
+ ((a).tv_usec <= (b).tv_usec)))
+
+struct rtadvd_timer {
+ struct rtadvd_timer *next;
+ struct rtadvd_timer *prev;
+ struct rainfo *rai;
+ struct timeval tm;
+
+ void (*expire) __P((void *)); /* expiration function */
+ void *expire_data;
+ void (*update) __P((void *, struct timeval *)); /* update function */
+ void *update_data;
+};
+
+void rtadvd_timer_init __P((void));
+struct rtadvd_timer *rtadvd_add_timer __P((void (*) __P((void *)),
+ void (*) __P((void *, struct timeval *)), void *, void *));
+void rtadvd_set_timer __P((struct timeval *, struct rtadvd_timer *));
+struct timeval * rtadvd_check_timer __P((void));
+struct timeval * rtadvd_timer_rest __P((struct rtadvd_timer *));
+void TIMEVAL_ADD __P((struct timeval *, struct timeval *,
+ struct timeval *));
+void TIMEVAL_SUB __P((struct timeval *, struct timeval *,
+ struct timeval *));