summaryrefslogtreecommitdiffstats
path: root/network_cmds/rtadvd.tproj
diff options
context:
space:
mode:
Diffstat (limited to 'network_cmds/rtadvd.tproj')
-rw-r--r--network_cmds/rtadvd.tproj/advcap.c483
-rw-r--r--network_cmds/rtadvd.tproj/advcap.h45
-rw-r--r--network_cmds/rtadvd.tproj/config.c1313
-rw-r--r--network_cmds/rtadvd.tproj/config.h46
-rw-r--r--network_cmds/rtadvd.tproj/dump.c283
-rw-r--r--network_cmds/rtadvd.tproj/dump.h32
-rw-r--r--network_cmds/rtadvd.tproj/if.c604
-rw-r--r--network_cmds/rtadvd.tproj/if.h64
-rw-r--r--network_cmds/rtadvd.tproj/pathnames.h4
-rw-r--r--network_cmds/rtadvd.tproj/rrenum.c484
-rw-r--r--network_cmds/rtadvd.tproj/rrenum.h33
-rw-r--r--network_cmds/rtadvd.tproj/rtadvd.8207
-rw-r--r--network_cmds/rtadvd.tproj/rtadvd.c1650
-rw-r--r--network_cmds/rtadvd.tproj/rtadvd.conf20
-rw-r--r--network_cmds/rtadvd.tproj/rtadvd.conf.5509
-rw-r--r--network_cmds/rtadvd.tproj/rtadvd.h205
-rw-r--r--network_cmds/rtadvd.tproj/rtadvd_logging.c45
-rw-r--r--network_cmds/rtadvd.tproj/rtadvd_logging.h24
-rw-r--r--network_cmds/rtadvd.tproj/timer.c205
-rw-r--r--network_cmds/rtadvd.tproj/timer.h64
20 files changed, 6320 insertions, 0 deletions
diff --git a/network_cmds/rtadvd.tproj/advcap.c b/network_cmds/rtadvd.tproj/advcap.c
new file mode 100644
index 0000000..33b6fae
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/advcap.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+/* $KAME: advcap.c,v 1.11 2003/05/19 09:46:50 keiichi Exp $ */
+
+/*
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ */
+
+/*
+ * 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 <errno.h>
+#include <string.h>
+#include "pathnames.h"
+#include "rtadvd_logging.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(char *, char *);
+int getent(char *, char *, char *);
+int tnchktc(void);
+int tnamatch(char *);
+static char *tskip(char *);
+int64_t tgetnum(char *);
+int tgetflag(char *);
+char *tgetstr(char *, char **);
+static char *tdecode(char *, char **);
+
+/*
+ * Get an entry for terminal name in buffer bp,
+ * from the termcap file. Parse is very rudimentary;
+ * we just notice escaped newlines.
+ */
+int
+tgetent(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;
+{
+ int c;
+ int i = 0, cnt = 0;
+ char ibuf[BUFSIZ];
+ int tf;
+
+ tbuf = bp;
+ tf = 0;
+ /*
+ * TERMCAP can have one of two things in it. It can be the
+ * name of a file to use instead of /etc/termcap. In this
+ * case it better start with a "/". Or it can be an entry to
+ * use so we don't have to read the file. In this case it
+ * has to already have the newlines crunched out.
+ */
+ if (cp && *cp) {
+ tf = open(RM = cp, O_RDONLY);
+ }
+ if (tf < 0) {
+ infolog("<%s> open: %s", __func__, strerror(errno));
+ return (-2);
+ }
+ for (;;) {
+ cp = bp;
+ for (;;) {
+ if (i == cnt) {
+ cnt = read(tf, ibuf, BUFSIZ);
+ if (cnt <= 0) {
+ close(tf);
+ return (0);
+ }
+ i = 0;
+ }
+ c = ibuf[i++];
+ if (c == '\n') {
+ if (cp > bp && cp[-1] == '\\') {
+ cp--;
+ continue;
+ }
+ break;
+ }
+ if (cp >= bp + BUFSIZ - 1) {
+ write(STDERR_FILENO, "Remcap entry too long\n",
+ 22);
+ break;
+ } else
+ *cp++ = c;
+ }
+ *cp = 0;
+
+ /*
+ * The real work for the match.
+ */
+ if (tnamatch(name)) {
+ close(tf);
+ return (tnchktc());
+ }
+ }
+}
+
+/*
+ * tnchktc: check the last entry, see if it's tc=xxx. If so,
+ * recursively find xxx and append that entry (minus the names)
+ * to take the place of the tc=xxx entry. This allows termcap
+ * entries to say "like an HP2621 but doesn't turn on the labels".
+ * Note that this works because of the left to right scan.
+ */
+int
+tnchktc()
+{
+ char *p, *q;
+ char tcname[16]; /* name of similar terminal */
+ char tcbuf[BUFSIZ];
+ char *holdtbuf = tbuf;
+ int l;
+
+ p = tbuf + strlen(tbuf) - 2; /* before the last colon */
+ while (*--p != ':')
+ if (p < tbuf) {
+ write(STDERR_FILENO, "Bad remcap entry\n", 18);
+ return (0);
+ }
+ p++;
+ /* p now points to beginning of last field */
+ if (p[0] != 't' || p[1] != 'c')
+ return (1);
+ strlcpy(tcname, p + 3, sizeof tcname);
+ q = tcname;
+ while (*q && *q != ':')
+ q++;
+ *q = 0;
+ if (++hopcount > MAXHOP) {
+ write(STDERR_FILENO, "Infinite tc= loop\n", 18);
+ return (0);
+ }
+ if (getent(tcbuf, tcname, remotefile) != 1) {
+ return (0);
+ }
+ for (q = tcbuf; *q++ != ':'; )
+ ;
+ l = p - holdtbuf + strlen(q);
+
+ /* check length before copying string below */
+ if (l > BUFSIZ) {
+ write(STDERR_FILENO, "Remcap entry too long\n", 23);
+ q[BUFSIZ - (p-holdtbuf)] = 0;
+ }
+ strlcpy(p, q, p-tbuf);
+ 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;
+{
+ 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)
+ char *bp;
+{
+ int dquote;
+
+ dquote = 0;
+ while (*bp) {
+ switch (*bp) {
+ case ':':
+ if (!dquote)
+ goto breakbreak;
+ else
+ bp++;
+ break;
+ case '\\':
+ bp++;
+ if (isdigit(*bp)) {
+ while (isdigit(*bp++))
+ ;
+ } else
+ bp++;
+ case '"':
+ dquote = (dquote ? 1 : 0);
+ bp++;
+ break;
+ default:
+ bp++;
+ break;
+ }
+ }
+breakbreak:
+ if (*bp == ':')
+ bp++;
+ return (bp);
+}
+
+/*
+ * Return the (numeric) option id.
+ * Numeric options look like
+ * li#80
+ * i.e. the option string is separated from the numeric value by
+ * a # character. If the option is not found we return -1.
+ * Note that we handle octal numbers beginning with 0.
+ */
+int64_t
+tgetnum(id)
+ char *id;
+{
+ int64_t i;
+ int base;
+ char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (*bp == 0)
+ return (-1);
+ if (strncmp(bp, id, strlen(id)) != 0)
+ continue;
+ bp += strlen(id);
+ if (*bp == '@')
+ return (-1);
+ if (*bp != '#')
+ continue;
+ bp++;
+ base = 10;
+ if (*bp == '0')
+ base = 8;
+ i = 0;
+ while (isdigit(*bp))
+ i *= base, i += *bp++ - '0';
+ return (i);
+ }
+}
+
+/*
+ * Handle a flag option.
+ * Flag options are given "naked", i.e. followed by a : or the end
+ * of the buffer. Return 1 if we find the option, or 0 if it is
+ * not given.
+ */
+int
+tgetflag(id)
+ char *id;
+{
+ char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (strncmp(bp, id, strlen(id)) == 0) {
+ bp += strlen(id);
+ if (!*bp || *bp == ':')
+ return (1);
+ else if (*bp == '@')
+ return (0);
+ }
+ }
+}
+
+/*
+ * Get a string valued option.
+ * These are given as
+ * cl=^Z
+ * Much decoding is done on the strings, and the strings are
+ * placed in area, which is a ref parameter which is updated.
+ * No checking on area overflow.
+ */
+char *
+tgetstr(id, area)
+ char *id, **area;
+{
+ char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (strncmp(bp, id, strlen(id)) != 0)
+ continue;
+ bp += strlen(id);
+ if (*bp == '@')
+ return (0);
+ if (*bp != '=')
+ continue;
+ bp++;
+ return (tdecode(bp, area));
+ }
+}
+
+/*
+ * Tdecode does the grung work to decode the
+ * string capability escapes.
+ */
+static char *
+tdecode(str, area)
+ char *str;
+ char **area;
+{
+ char *cp;
+ int c;
+ 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/network_cmds/rtadvd.tproj/advcap.h b/network_cmds/rtadvd.tproj/advcap.h
new file mode 100644
index 0000000..1b42b40
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/advcap.h
@@ -0,0 +1,45 @@
+/* $KAME: advcap.h,v 1.5 2003/06/09 05:40:54 t-momose Exp $ */
+
+/*
+ * Copyright (C) 1994,1995 by Andrey A. Chernov, Moscow, Russia.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Based on Id: termcap.h,v 1.8 1996/09/10 12:42:10 peter Exp */
+
+#ifndef _ADVCAP_H_
+#define _ADVCAP_H_
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+extern int agetent(char *, const char *);
+extern int agetflag(const char *);
+extern int64_t agetnum(const char *);
+extern char *agetstr(const char *, char **);
+
+__END_DECLS
+
+#endif /* _ADVCAP_H_ */
diff --git a/network_cmds/rtadvd.tproj/config.c b/network_cmds/rtadvd.tproj/config.c
new file mode 100644
index 0000000..3d6388d
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/config.c
@@ -0,0 +1,1313 @@
+/*
+ * Copyright (c) 2009-2020 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+/* $KAME: config.c,v 1.84 2003/08/05 12:34:23 itojun Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#include <netinet6/nd6.h>
+
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <search.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+#include <stddef.h>
+
+#include "rtadvd.h"
+#include "advcap.h"
+#include "timer.h"
+#include "if.h"
+#include "config.h"
+
+static time_t prefix_timo = (60 * 120); /* 2 hours.
+ * XXX: should be configurable. */
+extern struct rainfo *ralist;
+
+static struct rtadvd_timer *prefix_timeout(void *);
+static void makeentry(char *, size_t, int, char *);
+static int getinet6sysctl(int);
+static int encode_domain(char *, u_char *);
+
+void
+getconfig(intface)
+ char *intface;
+{
+ int stat, i;
+ int rdnss_length;
+ int dnssl_length;
+ char tbuf[BUFSIZ];
+ struct rainfo *rai;
+ long val;
+ int64_t val64;
+ char buf[BUFSIZ];
+ char *bp = buf;
+ char *addr, *flagstr;
+ char *capport;
+ static int forwarding = -1;
+
+#define MUSTHAVE(var, cap) \
+ do { \
+ int64_t t; \
+ if ((t = agetnum(cap)) < 0) { \
+ fprintf(stderr, "rtadvd: need %s for interface %s\n", \
+ cap, intface); \
+ exit(1); \
+ } \
+ var = t; \
+ } while (0)
+#define MAYHAVE(var, cap, def) \
+ do { \
+ if ((var = agetnum(cap)) < 0) \
+ var = def; \
+ } while (0)
+
+ if ((stat = agetent(tbuf, intface)) <= 0) {
+ memset(tbuf, 0, sizeof(tbuf));
+ errorlog("<%s> %s isn't defined in the configuration file"
+ " or the configuration file doesn't exist."
+ " Treat it as default",
+ __func__, intface);
+ }
+
+ ELM_MALLOC(rai, exit(1));
+ rai->prefix.next = rai->prefix.prev = &rai->prefix;
+#ifdef ROUTEINFO
+ rai->route.next = rai->route.prev = &rai->route;
+#endif
+ rai->rdnss_list.next = rai->rdnss_list.prev = &rai->rdnss_list;
+ rai->dnssl_list.next = rai->dnssl_list.prev = &rai->dnssl_list;
+
+ /* check if we are allowed to forward packets (if not determined) */
+ if (forwarding < 0) {
+ if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0)
+ exit(1);
+ }
+
+ /* get interface information */
+ if (agetflag("nolladdr"))
+ rai->advlinkopt = 0;
+ else
+ rai->advlinkopt = 1;
+ if (rai->advlinkopt) {
+ if ((rai->sdl = if_nametosdl(intface)) == NULL) {
+ errorlog("<%s> can't get information of %s",
+ __func__, intface);
+ exit(1);
+ }
+ rai->ifindex = rai->sdl->sdl_index;
+ } else
+ rai->ifindex = if_nametoindex(intface);
+ strlcpy(rai->ifname, intface, sizeof(rai->ifname));
+ if ((rai->phymtu = if_getmtu(intface)) == 0) {
+ rai->phymtu = IPV6_MMTU;
+ errorlog("<%s> can't get interface mtu of %s. Treat as %d",
+ __func__, intface, IPV6_MMTU);
+ }
+
+ /*
+ * set router configuration variables.
+ */
+ MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
+ if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
+ errorlog("<%s> maxinterval (%ld) on %s is invalid "
+ "(must be between %u and %u)", __func__, val,
+ intface, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
+ exit(1);
+ }
+ rai->maxinterval = (u_int)val;
+ MAYHAVE(val, "mininterval", rai->maxinterval/3);
+ if (val < MIN_MININTERVAL || val > (rai->maxinterval * 3) / 4) {
+ errorlog("<%s> mininterval (%ld) on %s is invalid "
+ "(must be between %d and %d)",
+ __func__, val, intface, MIN_MININTERVAL,
+ (rai->maxinterval * 3) / 4);
+ exit(1);
+ }
+ rai->mininterval = (u_int)val;
+
+ MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
+ rai->hoplimit = val & 0xff;
+
+ if ((flagstr = (char *)agetstr("raflags", &bp))) {
+ val = 0;
+ if (strchr(flagstr, 'm'))
+ val |= ND_RA_FLAG_MANAGED;
+ if (strchr(flagstr, 'o'))
+ val |= ND_RA_FLAG_OTHER;
+ if (strchr(flagstr, 'h'))
+ val |= ND_RA_FLAG_RTPREF_HIGH;
+ if (strchr(flagstr, 'l')) {
+ if ((val & ND_RA_FLAG_RTPREF_HIGH)) {
+ errorlog("<%s> the \'h\' and \'l\'"
+ " router flags are exclusive", __func__);
+ exit(1);
+ }
+ val |= ND_RA_FLAG_RTPREF_LOW;
+ }
+ } else {
+ MAYHAVE(val, "raflags", 0);
+ }
+ rai->managedflg = val & ND_RA_FLAG_MANAGED;
+ rai->otherflg = val & ND_RA_FLAG_OTHER;
+#ifndef ND_RA_FLAG_RTPREF_MASK
+#define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */
+#define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */
+#endif
+ rai->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
+ if (rai->rtpref == ND_RA_FLAG_RTPREF_RSV) {
+ errorlog("<%s> invalid router preference (%02x) on %s",
+ __func__, rai->rtpref, intface);
+ exit(1);
+ }
+
+ MAYHAVE(val, "rltime", rai->maxinterval * 3);
+ if (val && (val < rai->maxinterval || val > MAXROUTERLIFETIME)) {
+ errorlog("<%s> router lifetime (%ld) on %s is invalid "
+ "(must be 0 or between %d and %d)",
+ __func__, val, intface,
+ rai->maxinterval,
+ MAXROUTERLIFETIME);
+ exit(1);
+ }
+ /*
+ * Basically, hosts MUST NOT send Router Advertisement messages at any
+ * time (RFC 2461, Section 6.2.3). However, it would sometimes be
+ * useful to allow hosts to advertise some parameters such as prefix
+ * information and link MTU. Thus, we allow hosts to invoke rtadvd
+ * only when router lifetime (on every advertising interface) is
+ * explicitly set zero. (see also the above section)
+ */
+ if (val && forwarding == 0) {
+ errorlog("<%s> non zero router lifetime is specified for %s, "
+ "which must not be allowed for hosts. you must "
+ "change router lifetime or enable IPv6 forwarding.",
+ __func__, intface);
+ exit(1);
+ }
+ rai->lifetime = val & 0xffff;
+
+ MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
+ if (val < 0 || val > MAXREACHABLETIME) {
+ errorlog("<%s> reachable time (%ld) on %s is invalid "
+ "(must be no greater than %d)",
+ __func__, val, intface, MAXREACHABLETIME);
+ exit(1);
+ }
+ rai->reachabletime = (u_int32_t)val;
+
+ MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER);
+ if (val64 < 0 || val64 > 0xffffffff) {
+ errorlog("<%s> retrans time (%lld) on %s out of range",
+ __func__, (long long)val64, intface);
+ exit(1);
+ }
+ rai->retranstimer = (u_int32_t)val64;
+
+ if (agetnum("hapref") != -1 || agetnum("hatime") != -1) {
+ errorlog("<%s> mobile-ip6 configuration not supported",
+ __func__);
+ exit(1);
+ }
+ /* prefix information */
+
+ /*
+ * This is an implementation specific parameter to consider
+ * link propagation delays and poorly synchronized clocks when
+ * checking consistency of advertised lifetimes.
+ */
+ MAYHAVE(val, "clockskew", 0);
+ rai->clockskew = val;
+
+ rai->pfxs = 0;
+ for (i = -1; i < MAXPREFIX; i++) {
+ struct prefix *pfx;
+ char entbuf[256];
+
+ makeentry(entbuf, sizeof(entbuf), i, "addr");
+ addr = (char *)agetstr(entbuf, &bp);
+ if (addr == NULL)
+ continue;
+
+ /* allocate memory to store prefix information */
+ ELM_MALLOC(pfx, exit(1));
+
+ pfx->rainfo = rai;
+ pfx->origin = PREFIX_FROM_CONFIG;
+
+ if (inet_pton(AF_INET6, addr, &pfx->prefix) != 1) {
+ errorlog("<%s> inet_pton failed for %s",
+ __func__, addr);
+ exit(1);
+ }
+ if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
+ errorlog("<%s> multicast prefix (%s) must "
+ "not be advertised on %s",
+ __func__, addr, intface);
+ exit(1);
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
+ noticelog("<%s> link-local prefix (%s) will be"
+ " advertised on %s",
+ __func__, addr, intface);
+
+ makeentry(entbuf, sizeof(entbuf), i, "prefixlen");
+ MAYHAVE(val, entbuf, 64);
+ if (val < 0 || val > 128) {
+ errorlog("<%s> prefixlen (%ld) for %s "
+ "on %s out of range",
+ __func__, val, addr, intface);
+ exit(1);
+ }
+ pfx->prefixlen = (int)val;
+
+ makeentry(entbuf, sizeof(entbuf), i, "pinfoflags");
+ if ((flagstr = (char *)agetstr(entbuf, &bp))) {
+ val = 0;
+ if (strchr(flagstr, 'l'))
+ val |= ND_OPT_PI_FLAG_ONLINK;
+ if (strchr(flagstr, 'a'))
+ val |= ND_OPT_PI_FLAG_AUTO;
+ } else {
+ MAYHAVE(val, entbuf,
+ (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
+ }
+ pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
+ pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
+
+ makeentry(entbuf, sizeof(entbuf), i, "vltime");
+ MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
+ if (val64 < 0 || val64 > 0xffffffff) {
+ errorlog("<%s> vltime (%lld) for "
+ "%s/%d on %s is out of range",
+ __func__, (long long)val64,
+ addr, pfx->prefixlen, intface);
+ exit(1);
+ }
+ pfx->validlifetime = (u_int32_t)val64;
+
+ makeentry(entbuf, sizeof(entbuf), i, "vltimedecr");
+ if (agetflag(entbuf)) {
+ struct timeval now;
+ gettimeofday(&now, 0);
+ pfx->vltimeexpire =
+ now.tv_sec + pfx->validlifetime;
+ }
+
+ makeentry(entbuf, sizeof(entbuf), i, "pltime");
+ MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
+ if (val64 < 0 || val64 > 0xffffffff) {
+ errorlog("<%s> pltime (%lld) for %s/%d on %s "
+ "is out of range",
+ __func__, (long long)val64,
+ addr, pfx->prefixlen, intface);
+ exit(1);
+ }
+ pfx->preflifetime = (u_int32_t)val64;
+
+ makeentry(entbuf, sizeof(entbuf), i, "pltimedecr");
+ if (agetflag(entbuf)) {
+ struct timeval now;
+ gettimeofday(&now, 0);
+ pfx->pltimeexpire =
+ now.tv_sec + pfx->preflifetime;
+ }
+ /* link into chain */
+ insque(pfx, &rai->prefix);
+ rai->pfxs++;
+ }
+ if (rai->pfxs == 0)
+ get_prefix(rai);
+
+ MAYHAVE(val, "mtu", 0);
+ if (val < 0 || val > 0xffffffff) {
+ errorlog("<%s> mtu (%ld) on %s out of range",
+ __func__, val, intface);
+ exit(1);
+ }
+ rai->linkmtu = (u_int32_t)val;
+ if (rai->linkmtu == 0) {
+ char *mtustr;
+
+ if ((mtustr = (char *)agetstr("mtu", &bp)) &&
+ strcmp(mtustr, "auto") == 0)
+ rai->linkmtu = rai->phymtu;
+ }
+ else if (rai->linkmtu < IPV6_MMTU || rai->linkmtu > rai->phymtu) {
+ errorlog("<%s> advertised link mtu (%lu) on %s is invalid (must "
+ "be between least MTU (%d) and physical link MTU (%d)",
+ __func__, (unsigned long)rai->linkmtu, intface,
+ IPV6_MMTU, rai->phymtu);
+ exit(1);
+ }
+
+#ifdef SIOCSIFINFO_IN6
+ {
+ struct in6_ndireq ndi;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ errorlog("<%s> socket: %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+ memset(&ndi, 0, sizeof(ndi));
+ strlcpy(ndi.ifname, intface, sizeof(ndi.ifname));
+ if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&ndi) < 0) {
+ infolog("<%s> ioctl:SIOCGIFINFO_IN6 at %s: %s",
+ __func__, intface, strerror(errno));
+ }
+
+ /* reflect the RA info to the host variables in kernel */
+ ndi.ndi.chlim = rai->hoplimit;
+ ndi.ndi.retrans = rai->retranstimer;
+ ndi.ndi.basereachable = rai->reachabletime;
+ if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&ndi) < 0) {
+ infolog("<%s> ioctl:SIOCSIFINFO_IN6 at %s: %s",
+ __func__, intface, strerror(errno));
+ }
+ close(s);
+ }
+#endif
+
+ /* route information */
+#ifdef ROUTEINFO
+ rai->routes = 0;
+ for (i = -1; i < MAXROUTE; i++) {
+ struct rtinfo *rti;
+ char entbuf[256], oentbuf[256];
+
+ makeentry(entbuf, sizeof(entbuf), i, "rtprefix");
+ addr = (char *)agetstr(entbuf, &bp);
+ if (addr == NULL) {
+ makeentry(oentbuf, sizeof(oentbuf), i, "rtrprefix");
+ addr = (char *)agetstr(oentbuf, &bp);
+ if (addr) {
+ fprintf(stderr, "%s was obsoleted. Use %s.\n",
+ oentbuf, entbuf);
+ }
+ }
+ if (addr == NULL)
+ continue;
+
+ /* allocate memory to store prefix information */
+ ELM_MALLOC(rti, exit(1));
+
+ /* link into chain */
+ insque(rti, &rai->route);
+ rai->routes++;
+
+ if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) {
+ errorlog( "<%s> inet_pton failed for %s",
+ __func__, addr);
+ exit(1);
+ }
+#if 0
+ /*
+ * XXX: currently there's no restriction in route information
+ * prefix according to
+ * draft-ietf-ipngwg-router-selection-00.txt.
+ * However, I think the similar restriction be necessary.
+ */
+ MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
+ if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) {
+ errorlog("<%s> multicast route (%s) must "
+ "not be advertised on %s",
+ __func__, addr, intface);
+ exit(1);
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) {
+ noticelog("<%s> link-local route (%s) will "
+ "be advertised on %s",
+ __func__, addr, intface);
+ exit(1);
+ }
+#endif
+
+ makeentry(entbuf, sizeof(entbuf), i, "rtplen");
+ /* XXX: 256 is a magic number for compatibility check. */
+ MAYHAVE(val, entbuf, 256);
+ if (val == 256) {
+ makeentry(oentbuf, sizeof(oentbuf), i, "rtrplen");
+ MAYHAVE(val, oentbuf, 256);
+ if (val != 256) {
+ fprintf(stderr, "%s was obsoleted. Use %s.\n",
+ oentbuf, entbuf);
+ } else
+ val = 64;
+ }
+ if (val < 0 || val > 128) {
+ errorlog("<%s> prefixlen (%ld) for %s on %s "
+ "out of range",
+ __func__, val, addr, intface);
+ exit(1);
+ }
+ rti->prefixlen = (int)val;
+
+ makeentry(entbuf, sizeof(entbuf), i, "rtflags");
+ if ((flagstr = (char *)agetstr(entbuf, &bp))) {
+ val = 0;
+ if (strchr(flagstr, 'h'))
+ val |= ND_RA_FLAG_RTPREF_HIGH;
+ if (strchr(flagstr, 'l')) {
+ if ((val & ND_RA_FLAG_RTPREF_HIGH)) {
+ errorlog(
+ "<%s> the \'h\' and \'l\' route"
+ " preferences are exclusive",
+ __func__);
+ exit(1);
+ }
+ val |= ND_RA_FLAG_RTPREF_LOW;
+ }
+ } else
+ MAYHAVE(val, entbuf, 256); /* XXX */
+ if (val == 256) {
+ makeentry(oentbuf, sizeof(oentbuf), i, "rtrflags");
+ MAYHAVE(val, oentbuf, 256);
+ if (val != 256) {
+ fprintf(stderr, "%s was obsoleted. Use %s.\n",
+ oentbuf, entbuf);
+ } else
+ val = 0;
+ }
+ rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
+ if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) {
+ errorlog("<%s> invalid route preference (%02x) "
+ "for %s/%d on %s",
+ __func__, rti->rtpref, addr,
+ rti->prefixlen, intface);
+ exit(1);
+ }
+
+ /*
+ * Since the spec does not a default value, we should make
+ * this entry mandatory. However, FreeBSD 4.4 has shipped
+ * with this field being optional, we use the router lifetime
+ * as an ad-hoc default value with a warning message.
+ */
+ makeentry(entbuf, sizeof(entbuf), i, "rtltime");
+ MAYHAVE(val64, entbuf, -1);
+ if (val64 == -1) {
+ makeentry(oentbuf, sizeof(oentbuf), i, "rtrltime");
+ MAYHAVE(val64, oentbuf, -1);
+ if (val64 != -1) {
+ fprintf(stderr, "%s was obsoleted. Use %s.\n",
+ oentbuf, entbuf);
+ } else {
+ fprintf(stderr, "%s should be specified "
+ "for interface %s.\n",
+ entbuf, intface);
+ val64 = rai->lifetime;
+ }
+ }
+ if (val64 < 0 || val64 > 0xffffffff) {
+ errorlog( "<%s> route lifetime (%lld) for "
+ "%s/%d on %s out of range", __func__,
+ (long long)val64, addr, rti->prefixlen, intface);
+ exit(1);
+ }
+ rti->ltime = (u_int32_t)val64;
+ }
+#endif
+
+ /* RDNSS option (RFC5006) */
+ MAYHAVE(val, "rdnsslifetime", 2 * rai->maxinterval);
+ if (val < rai->maxinterval || val > (2 * rai->maxinterval)) {
+ noticelog("<%s> rdnsslifetime (%lu) on %s SHOULD "
+ "be between %u and %u", __func__, val,
+ intface, rai->maxinterval, 2 * rai->maxinterval);
+ }
+ rai->rdnss_lifetime = val;
+ if ((rdnss_length = agetnum("rdnssaddrs")) < 0) {
+ rai->rdnss_length = 0;
+ }
+ else {
+ rai->rdnss_length = rdnss_length;
+
+ /* traverse in reverse order so that the queue has correct order */
+ for (i = (rdnss_length - 1); i >= 0; i--) {
+ struct rdnss *rdnss;
+ char entbuf[256];
+
+ /* allocate memory to store server address information */
+ ELM_MALLOC(rdnss, exit(1));
+ /* link into chain */
+ insque(rdnss, &rai->rdnss_list);
+
+ makeentry(entbuf, sizeof(entbuf), i, "rdnssaddr");
+ addr = (char *)agetstr(entbuf, &bp);
+
+ if (addr == NULL && rdnss_length == 1) {
+ makeentry(entbuf, sizeof(entbuf), -1, "rdnssaddr");
+ addr = agetstr(entbuf, &bp);
+ }
+
+ if (addr == NULL) {
+ errorlog("<%s> need %s as a DNS server address for "
+ "interface %s",
+ __func__, entbuf, intface);
+ exit(1);
+ }
+
+ if (inet_pton(AF_INET6, addr, &rdnss->addr) != 1) {
+ errorlog("<%s> inet_pton failed for %s",
+ __func__, addr);
+ exit(1);
+ }
+ if (IN6_IS_ADDR_MULTICAST(&rdnss->addr)) {
+ errorlog("<%s> multicast address (%s) must "
+ "not be advertised as recursive DNS server",
+ __func__, addr);
+ exit(1);
+ }
+ }
+ }
+
+ /* DNSSL option (RFC6106) */
+
+ /* Parse the DNSSL lifetime from the config */
+ MAYHAVE(val, "dnssllifetime", 2 * rai->maxinterval);
+ if (val < rai->maxinterval || val > (2 * rai->maxinterval)) {
+ noticelog("<%s> dnssllifetime (%lu) on %s SHOULD "
+ "be between %u and %u", __func__, val,
+ intface, rai->maxinterval, 2 * rai->maxinterval);
+ }
+ rai->dnssl_lifetime = val;
+ rai->dnssl_option_length = 8; /* 8 bytes for the option header */
+
+ /* Parse the DNSSL domain list from the config */
+ if ((dnssl_length = agetnum("dnssldomains")) < 0) {
+ rai->dnssl_length = 0;
+ } else {
+ rai->dnssl_length = dnssl_length;
+
+ for (i = (rai->dnssl_length - 1); i >= 0; i--) {
+ unsigned char *dnssl_buf;
+ struct dnssl *dnssl;
+ int dnssl_len;
+ char entbuf[sizeof("dnssldomain") + 20];
+ char *domain;
+ int domain_len;
+
+ makeentry(entbuf, sizeof(entbuf), i, "dnssldomain");
+ domain = agetstr(entbuf, &bp);
+
+ if (domain == NULL && rai->dnssl_length == 1) {
+ makeentry(entbuf, sizeof(entbuf), -1, "dnssldomain");
+ domain = agetstr(entbuf, &bp);
+ }
+
+ if (domain == NULL) {
+ errorlog("<%s> need %s as a DNS search domain for "
+ "interface %s",
+ __func__, entbuf, intface);
+ exit(1);
+ }
+
+ domain_len = strlen(domain);
+
+ /* Trim off leading dots */
+ while (domain_len > 0 && domain[0] == '.') {
+ domain++;
+ domain_len--;
+ }
+
+ /* Trim off trailing dots */
+ while (domain_len > 0 && domain[domain_len-1] == '.') {
+ domain_len--;
+ }
+
+ if (domain_len > 0) {
+ dnssl_len = sizeof(struct dnssl) + domain_len + 1;
+ dnssl_buf = (unsigned char *)malloc(dnssl_len);
+
+ memset(dnssl_buf, 0, dnssl_len);
+
+ dnssl = (struct dnssl *)dnssl_buf;
+ insque(dnssl, &rai->dnssl_list);
+
+ /* Copy the domain name in at the end of the dnssl struct */
+ memcpy(dnssl_buf + offsetof(struct dnssl, domain), domain,
+ domain_len);
+
+ /* Add 2 for leading length byte and the trailing 0 byte */
+ rai->dnssl_option_length += domain_len + 2;
+ }
+ }
+
+ /* Round up to the next multiple of 8 */
+ rai->dnssl_option_length += (8 - (rai->dnssl_option_length & 0x7));
+ }
+
+ /* captive portal */
+ capport = agetstr("capport", &bp);
+ if (capport != NULL) {
+ rai->capport = strdup(capport);
+ rai->capport_length = strlen(capport);
+ rai->capport_option_length
+ = sizeof(struct nd_opt_hdr) + rai->capport_length;
+ rai->capport_option_length
+ += (8 - (rai->capport_option_length & 0x7));
+ }
+
+ /* okey */
+ rai->next = ralist;
+ ralist = rai;
+
+ /* construct the sending packet */
+ make_packet(rai);
+
+ /* set timer */
+ rai->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
+ rai, rai);
+ ra_timer_update((void *)rai, &rai->timer->tm);
+ rtadvd_set_timer(&rai->timer->tm, rai->timer);
+}
+
+void
+get_prefix(struct rainfo *rai)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct prefix *pfx;
+ struct in6_addr *a;
+ u_char *p, *ep, *m, *lim;
+ char ntopbuf[INET6_ADDRSTRLEN];
+
+ if (getifaddrs(&ifap) < 0) {
+ errorlog(
+ "<%s> can't get interface addresses",
+ __func__);
+ exit(1);
+ }
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ int plen;
+
+ if (strcmp(ifa->ifa_name, rai->ifname) != 0)
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
+ if (IN6_IS_ADDR_LINKLOCAL(a))
+ continue;
+ /* get prefix length */
+ m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
+ lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len;
+ plen = prefixlen(m, lim);
+ if (plen <= 0 || plen > 128) {
+ errorlog( "<%s> failed to get prefixlen "
+ "or prefix is invalid",
+ __func__);
+ exit(1);
+ }
+ if (plen == 128) /* XXX */
+ continue;
+ if (find_prefix(rai, a, plen)) {
+ /* ignore a duplicated prefix. */
+ continue;
+ }
+
+ /* allocate memory to store prefix info. */
+ ELM_MALLOC(pfx, exit(1));
+ /* set prefix, sweep bits outside of prefixlen */
+ pfx->prefixlen = plen;
+ memcpy(&pfx->prefix, a, sizeof(*a));
+ p = (u_char *)&pfx->prefix;
+ ep = (u_char *)(&pfx->prefix + 1);
+ while (m < lim && p < ep)
+ *p++ &= *m++;
+ while (p < ep)
+ *p++ = 0x00;
+ if (!inet_ntop(AF_INET6, &pfx->prefix, ntopbuf,
+ sizeof(ntopbuf))) {
+ errorlog("<%s> inet_ntop failed", __func__);
+ exit(1);
+ }
+ debuglog("<%s> add %s/%d to prefix list on %s",
+ __func__, ntopbuf, pfx->prefixlen, rai->ifname);
+
+ /* set other fields with protocol defaults */
+ pfx->validlifetime = DEF_ADVVALIDLIFETIME;
+ pfx->preflifetime = DEF_ADVPREFERREDLIFETIME;
+ pfx->onlinkflg = 1;
+ pfx->autoconfflg = 1;
+ pfx->origin = PREFIX_FROM_KERNEL;
+ pfx->rainfo = rai;
+
+ /* link into chain */
+ insque(pfx, &rai->prefix);
+
+ /* counter increment */
+ rai->pfxs++;
+ }
+
+ freeifaddrs(ifap);
+}
+
+static void
+makeentry(buf, len, id, string)
+ char *buf;
+ size_t len;
+ int id;
+ char *string;
+{
+
+ if (id < 0)
+ strlcpy(buf, string, len);
+ else
+ snprintf(buf, len, "%s%d", string, id);
+}
+
+/*
+ * Add a prefix to the list of specified interface and reconstruct
+ * the outgoing packet.
+ * The prefix must not be in the list.
+ * XXX: other parameters of the prefix (e.g. lifetime) should be
+ * able to be specified.
+ */
+static void
+add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
+{
+ struct prefix *prefix;
+ char ntopbuf[INET6_ADDRSTRLEN];
+
+ ELM_MALLOC(prefix, exit(1));
+ prefix->prefix = ipr->ipr_prefix.sin6_addr;
+ prefix->prefixlen = ipr->ipr_plen;
+ prefix->validlifetime = ipr->ipr_vltime;
+ prefix->preflifetime = ipr->ipr_pltime;
+ prefix->onlinkflg = ipr->ipr_raf_onlink;
+ prefix->autoconfflg = ipr->ipr_raf_auto;
+ prefix->origin = PREFIX_FROM_DYNAMIC;
+ prefix->rainfo = rai;
+
+ insque(prefix, &rai->prefix);
+
+ debuglog("<%s> new prefix %s/%d was added on %s",
+ __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ ipr->ipr_plen, rai->ifname);
+
+ /* free the previous packet */
+ free(rai->ra_data);
+ rai->ra_data = NULL;
+
+ /* reconstruct the packet */
+ rai->pfxs++;
+ make_packet(rai);
+}
+
+/*
+ * Delete a prefix to the list of specified interface and reconstruct
+ * the outgoing packet.
+ * The prefix must be in the list.
+ */
+void
+delete_prefix(struct prefix *prefix)
+{
+ char ntopbuf[INET6_ADDRSTRLEN];
+ struct rainfo *rai = prefix->rainfo;
+
+ remque(prefix);
+ debuglog("<%s> prefix %s/%d was deleted on %s",
+ __func__, inet_ntop(AF_INET6, &prefix->prefix,
+ ntopbuf, INET6_ADDRSTRLEN),
+ prefix->prefixlen, rai->ifname);
+ if (prefix->timer)
+ rtadvd_remove_timer(&prefix->timer);
+ free(prefix);
+ rai->pfxs--;
+}
+
+void
+invalidate_prefix(struct prefix *prefix)
+{
+ char ntopbuf[INET6_ADDRSTRLEN];
+ struct timeval timo;
+ struct rainfo *rai = prefix->rainfo;
+
+ if (prefix->timer) { /* sanity check */
+ errorlog("<%s> assumption failure: timer already exists",
+ __func__);
+ exit(1);
+ }
+
+ debuglog("<%s> prefix %s/%d was invalidated on %s, "
+ "will expire in %ld seconds", __func__,
+ inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, INET6_ADDRSTRLEN),
+ prefix->prefixlen, rai->ifname, (long)prefix_timo);
+
+ /* set the expiration timer */
+ prefix->timer = rtadvd_add_timer(prefix_timeout, NULL, prefix, NULL);
+ if (prefix->timer == NULL) {
+ errorlog("<%s> failed to add a timer for a prefix. "
+ "remove the prefix", __func__);
+ delete_prefix(prefix);
+ return;
+ }
+ timo.tv_sec = prefix_timo;
+ timo.tv_usec = 0;
+ rtadvd_set_timer(&timo, prefix->timer);
+}
+
+static struct rtadvd_timer *
+prefix_timeout(void *arg)
+{
+ struct prefix *prefix = (struct prefix *)arg;
+
+ delete_prefix(prefix);
+
+ return(NULL);
+}
+
+void
+update_prefix(struct prefix * prefix)
+{
+ char ntopbuf[INET6_ADDRSTRLEN];
+ struct rainfo *rai = prefix->rainfo;
+
+ if (prefix->timer == NULL) { /* sanity check */
+ errorlog("<%s> assumption failure: timer does not exist",
+ __func__);
+ exit(1);
+ }
+
+ debuglog("<%s> prefix %s/%d was re-enabled on %s",
+ __func__, inet_ntop(AF_INET6, &prefix->prefix, ntopbuf,
+ INET6_ADDRSTRLEN), prefix->prefixlen, rai->ifname);
+
+ /* stop the expiration timer */
+ rtadvd_remove_timer(&prefix->timer);
+}
+
+/*
+ * Try to get an in6_prefixreq contents for a prefix which matches
+ * ipr->ipr_prefix and ipr->ipr_plen and belongs to
+ * the interface whose name is ipr->ipr_name[].
+ */
+static int
+init_prefix(struct in6_prefixreq *ipr)
+{
+#if 0
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ errorlog("<%s> socket: %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+
+ if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
+ infolog("<%s> ioctl:SIOCGIFPREFIX %s", __func__,
+ strerror(errno));
+
+ ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
+ ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
+ ipr->ipr_raf_onlink = 1;
+ ipr->ipr_raf_auto = 1;
+ /* omit other field initialization */
+ }
+ else if (ipr->ipr_origin < PR_ORIG_RR) {
+ char ntopbuf[INET6_ADDRSTRLEN];
+
+ noticelog("<%s> Added prefix(%s)'s origin %d is"
+ "lower than PR_ORIG_RR(router renumbering)."
+ "This should not happen if I am router", __func__,
+ inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), ipr->ipr_origin);
+ close(s);
+ return 1;
+ }
+
+ close(s);
+ return 0;
+#else
+ ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
+ ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
+ ipr->ipr_raf_onlink = 1;
+ ipr->ipr_raf_auto = 1;
+ return 0;
+#endif
+}
+
+void
+make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
+{
+ struct in6_prefixreq ipr;
+
+ memset(&ipr, 0, sizeof(ipr));
+ if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
+ errorlog("<%s> Prefix added interface No.%d doesn't"
+ "exist. This should not happen! %s", __func__,
+ ifindex, strerror(errno));
+ exit(1);
+ }
+ ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
+ ipr.ipr_prefix.sin6_family = AF_INET6;
+ ipr.ipr_prefix.sin6_addr = *addr;
+ ipr.ipr_plen = plen;
+
+ if (init_prefix(&ipr))
+ return; /* init failed by some error */
+ add_prefix(rai, &ipr);
+}
+
+void
+make_packet(struct rainfo *rainfo)
+{
+ size_t packlen, lladdroptlen = 0;
+ u_char *buf;
+ struct nd_router_advert *ra;
+ struct nd_opt_prefix_info *ndopt_pi;
+ struct nd_opt_mtu *ndopt_mtu;
+#ifdef ROUTEINFO
+ struct nd_opt_route_info *ndopt_rti;
+ struct rtinfo *rti;
+#endif
+ struct prefix *pfx;
+
+ /* calculate total length */
+ packlen = sizeof(struct nd_router_advert);
+ if (rainfo->advlinkopt) {
+ if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
+ infolog("<%s> link-layer address option has"
+ " null length on %s. Treat as not included.",
+ __func__, 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);
+#ifdef ROUTEINFO
+ for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next)
+ packlen += sizeof(struct nd_opt_route_info) +
+ ((rti->prefixlen + 0x3f) >> 6) * 8;
+#endif
+ if (rainfo->rdnss_length > 0)
+ packlen += 8 + sizeof(struct in6_addr) * rainfo->rdnss_length;
+
+ if (rainfo->dnssl_length > 0) {
+ packlen += rainfo->dnssl_option_length;
+ }
+ if (rainfo->capport_option_length != 0) {
+ packlen += rainfo->capport_option_length;
+ }
+
+ /* allocate memory for the packet */
+ if ((buf = malloc(packlen)) == NULL) {
+ errorlog("<%s> can't get enough memory for an RA packet",
+ __func__);
+ exit(1);
+ }
+ if (rainfo->ra_data) {
+ /* free the previous packet */
+ free(rainfo->ra_data);
+ rainfo->ra_data = NULL;
+ }
+ rainfo->ra_data = buf;
+ /* 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; /* just in case */
+ /*
+ * XXX: the router preference field, which is a 2-bit field, should be
+ * initialized before other fields.
+ */
+ ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref;
+ ra->nd_ra_flags_reserved |=
+ rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
+ ra->nd_ra_flags_reserved |=
+ rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
+ ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
+ ra->nd_ra_reachable = htonl(rainfo->reachabletime);
+ ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
+ buf += sizeof(*ra);
+
+ if (rainfo->advlinkopt) {
+ 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 = htonl(rainfo->linkmtu);
+ buf += sizeof(struct nd_opt_mtu);
+ }
+
+ for (pfx = rainfo->prefix.next;
+ pfx != &rainfo->prefix; pfx = pfx->next) {
+ u_int32_t vltime, pltime;
+ struct timeval now;
+
+ ndopt_pi = (struct nd_opt_prefix_info *)buf;
+ ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
+ ndopt_pi->nd_opt_pi_len = 4;
+ ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
+ ndopt_pi->nd_opt_pi_flags_reserved = 0;
+ if (pfx->onlinkflg)
+ ndopt_pi->nd_opt_pi_flags_reserved |=
+ ND_OPT_PI_FLAG_ONLINK;
+ if (pfx->autoconfflg)
+ ndopt_pi->nd_opt_pi_flags_reserved |=
+ ND_OPT_PI_FLAG_AUTO;
+ if (pfx->timer)
+ vltime = 0;
+ else {
+ if (pfx->vltimeexpire || pfx->pltimeexpire)
+ gettimeofday(&now, NULL);
+ if (pfx->vltimeexpire == 0)
+ vltime = pfx->validlifetime;
+ else
+ vltime = (pfx->vltimeexpire > now.tv_sec) ?
+ pfx->vltimeexpire - now.tv_sec : 0;
+ }
+ if (pfx->timer)
+ pltime = 0;
+ else {
+ if (pfx->pltimeexpire == 0)
+ pltime = pfx->preflifetime;
+ else
+ pltime = (pfx->pltimeexpire > now.tv_sec) ?
+ pfx->pltimeexpire - now.tv_sec : 0;
+ }
+ if (vltime < pltime) {
+ /*
+ * this can happen if vltime is decrement but pltime
+ * is not.
+ */
+ pltime = vltime;
+ }
+ ndopt_pi->nd_opt_pi_valid_time = htonl(vltime);
+ ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime);
+ ndopt_pi->nd_opt_pi_reserved2 = 0;
+ ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
+
+ buf += sizeof(struct nd_opt_prefix_info);
+ }
+
+#ifdef ROUTEINFO
+ for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) {
+ u_int8_t psize = (rti->prefixlen + 0x3f) >> 6;
+
+ ndopt_rti = (struct nd_opt_route_info *)buf;
+ ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO;
+ ndopt_rti->nd_opt_rti_len = 1 + psize;
+ ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen;
+ ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref;
+ ndopt_rti->nd_opt_rti_lifetime = htonl(rti->ltime);
+ memcpy(ndopt_rti + 1, &rti->prefix, psize * 8);
+ buf += sizeof(struct nd_opt_route_info) + psize * 8;
+ }
+#endif
+
+ if (rainfo->rdnss_length > 0) {
+ struct nd_opt_rdnss * ndopt_rdnss;
+ struct rdnss * rdnss;
+
+ ndopt_rdnss = (struct nd_opt_rdnss*) buf;
+ ndopt_rdnss->nd_opt_rdnss_type = ND_OPT_RDNSS;
+ ndopt_rdnss->nd_opt_rdnss_len = 1 + (rainfo->rdnss_length * 2);
+ ndopt_rdnss->nd_opt_rdnss_reserved = 0;
+ ndopt_rdnss->nd_opt_rdnss_lifetime = htonl(rainfo->rdnss_lifetime);
+ buf += 8;
+
+ for (rdnss = rainfo->rdnss_list.next;
+ rdnss != &rainfo->rdnss_list;
+ rdnss = rdnss->next)
+ {
+ struct in6_addr* addr6 = (struct in6_addr*) buf;
+ *addr6 = rdnss->addr;
+ buf += sizeof *addr6;
+ }
+ }
+
+ if (rainfo->dnssl_length > 0) {
+ struct nd_opt_dnssl * dnssl_opt;
+ struct dnssl * dnssl;
+ int domains_length = 0;
+ u_char * cursor = buf;
+
+ memset(cursor, 0, rainfo->dnssl_option_length);
+
+ dnssl_opt = (struct nd_opt_dnssl *)cursor;
+ dnssl_opt->nd_opt_dnssl_type = ND_OPT_DNSSL;
+ /*
+ * Length is in units of 8 octets. Divide total byte length
+ * of the option by 8.
+ */
+ dnssl_opt->nd_opt_dnssl_len = rainfo->dnssl_option_length >> 3;
+ dnssl_opt->nd_opt_dnssl_reserved = 0;
+ dnssl_opt->nd_opt_dnssl_lifetime =
+ htonl(rainfo->dnssl_lifetime);
+
+ cursor += offsetof(struct nd_opt_dnssl, nd_opt_dnssl_domains);
+
+ for (dnssl = rainfo->dnssl_list.next;
+ dnssl != &rainfo->dnssl_list;
+ dnssl = dnssl->next)
+ {
+ int encodeLen = encode_domain(dnssl->domain, cursor);
+ cursor += encodeLen;
+ domains_length += encodeLen;
+ }
+
+ buf += rainfo->dnssl_option_length;
+ }
+ if (rainfo->capport != NULL) {
+ struct nd_opt_hdr * capport_opt;
+ u_int32_t zero_space;
+
+ capport_opt = (struct nd_opt_hdr *)buf;
+#ifndef ND_OPT_CAPTIVE_PORTAL
+#define ND_OPT_CAPTIVE_PORTAL 37 /* RFC 7710 */
+#endif /* ND_OPT_CAPTIVE_PORTAL */
+ capport_opt->nd_opt_type = ND_OPT_CAPTIVE_PORTAL;
+ capport_opt->nd_opt_len = rainfo->capport_option_length >> 3;
+ buf += sizeof(*capport_opt);
+ bcopy(rainfo->capport, buf, rainfo->capport_length);
+ buf += rainfo->capport_length;
+ zero_space = rainfo->capport_option_length
+ - rainfo->capport_length;
+ if (zero_space > 0) {
+ bzero(buf, zero_space);
+ buf += zero_space;
+ }
+ }
+ return;
+}
+
+static int
+getinet6sysctl(int code)
+{
+ int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
+ int value;
+ size_t size;
+
+ mib[3] = code;
+ size = sizeof(value);
+ if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
+ < 0) {
+ errorlog( "<%s>: failed to get ip6 sysctl(%d): %s",
+ __func__, code,
+ strerror(errno));
+ return(-1);
+ }
+ else
+ return(value);
+}
+
+/*
+ * Encode a domain name into a buffer according to the rules in RFC 1035 section
+ * 3.1. Do not use the compression techniques outlined in section 4.1.4.
+ */
+int
+encode_domain(char *domain, u_char *dst)
+{
+ char *domainCopy = strdup(domain);
+ char *input = domainCopy;
+ char *label;
+ u_char *cursor = dst;
+
+ while ((label = strsep(&input, ".")) != NULL) {
+ int label_len = strlen(label) & 0x3f; /* Max length is 63 */
+ if (label_len > 0) {
+ *cursor = (u_char)label_len;
+ cursor++;
+ memcpy(cursor, label, label_len);
+ cursor += label_len;
+ }
+ }
+ *cursor = 0;
+ cursor++;
+
+ free(domainCopy);
+
+ return (cursor - dst);
+}
diff --git a/network_cmds/rtadvd.tproj/config.h b/network_cmds/rtadvd.tproj/config.h
new file mode 100644
index 0000000..f1f0387
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/config.h
@@ -0,0 +1,46 @@
+/* $KAME: config.h,v 1.8 2003/06/17 08:26:22 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern void getconfig(char *);
+extern void delete_prefix(struct prefix *);
+extern void invalidate_prefix(struct prefix *);
+extern void update_prefix(struct prefix *);
+extern void make_prefix(struct rainfo *, int, struct in6_addr *, int);
+extern void make_packet(struct rainfo *);
+extern void get_prefix(struct rainfo *);
+
+
+/*
+ * it is highly unlikely to have 100 prefix information options,
+ * so it should be okay to limit it
+ */
+#define MAXPREFIX 100
+#define MAXROUTE 100
diff --git a/network_cmds/rtadvd.tproj/dump.c b/network_cmds/rtadvd.tproj/dump.c
new file mode 100644
index 0000000..ea5dcd9
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/dump.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+/* $KAME: dump.c,v 1.32 2003/05/19 09:46:50 keiichi Exp $ */
+
+/*
+ * Copyright (C) 2000 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+
+/* XXX: the following two are non-standard include files */
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+
+#include <arpa/inet.h>
+
+#include <time.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#include "rtadvd.h"
+#include "timer.h"
+#include "if.h"
+#include "dump.h"
+
+static FILE *fp;
+
+extern struct rainfo *ralist;
+
+static char *ether_str(struct sockaddr_dl *);
+static void if_dump(void);
+
+static char *rtpref_str[] = {
+ "medium", /* 00 */
+ "high", /* 01 */
+ "rsv", /* 10 */
+ "low" /* 11 */
+};
+
+static char *
+ether_str(sdl)
+ struct sockaddr_dl *sdl;
+{
+ static char hbuf[32];
+ u_char *cp;
+
+ if (sdl->sdl_alen && sdl->sdl_alen > 5) {
+ cp = (u_char *)LLADDR(sdl);
+ snprintf(hbuf, sizeof(hbuf), "%x:%x:%x:%x:%x:%x",
+ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
+ } else
+ snprintf(hbuf, sizeof(hbuf), "NONE");
+
+ return(hbuf);
+}
+
+static void
+if_dump()
+{
+ struct rainfo *rai;
+ struct prefix *pfx;
+#ifdef ROUTEINFO
+ struct rtinfo *rti;
+#endif
+ char prefixbuf[INET6_ADDRSTRLEN];
+ int first;
+ struct timeval now;
+
+ gettimeofday(&now, NULL); /* XXX: unused in most cases */
+ for (rai = ralist; rai; rai = rai->next) {
+ fprintf(fp, "%s:\n", rai->ifname);
+ struct if_msghdr *ifm = get_interface_entry(rai->ifindex);
+ if (ifm == NULL) {
+ debuglog("Skippingg RA entry for interface %s"
+ "as we couldn't find interface entry for it.", rai->ifname);
+ continue;
+ }
+ fprintf(fp, " Status: %s\n",
+ (ifm->ifm_flags & IFF_UP) ? "UP" :
+ "DOWN");
+
+ /* control information */
+ if (rai->lastsent.tv_sec) {
+ /* note that ctime() appends CR by itself */
+ fprintf(fp, " Last RA sent: %s",
+ ctime((time_t *)&rai->lastsent.tv_sec));
+ }
+ if (rai->timer) {
+ fprintf(fp, " Next RA will be sent: %s",
+ ctime((time_t *)&rai->timer->tm.tv_sec));
+ }
+ else
+ fprintf(fp, " RA timer is stopped");
+ fprintf(fp, " waits: %d, initcount: %d\n",
+ rai->waiting, rai->initcounter);
+
+ /* statistics */
+ fprintf(fp, " statistics: RA(out/in/inconsistent): "
+ "%llu/%llu/%llu, ",
+ (unsigned long long)rai->raoutput,
+ (unsigned long long)rai->rainput,
+ (unsigned long long)rai->rainconsistent);
+ fprintf(fp, "RS(input): %llu\n",
+ (unsigned long long)rai->rsinput);
+
+ /* interface information */
+ if (rai->advlinkopt)
+ fprintf(fp, " Link-layer address: %s\n",
+ ether_str(rai->sdl));
+ fprintf(fp, " MTU: %d\n", rai->phymtu);
+
+ /* Router configuration variables */
+ fprintf(fp, " DefaultLifetime: %d, MaxAdvInterval: %d, "
+ "MinAdvInterval: %d\n", rai->lifetime, rai->maxinterval,
+ rai->mininterval);
+ fprintf(fp, " Flags: %s%s%s, ",
+ rai->managedflg ? "M" : "", rai->otherflg ? "O" : "", "");
+ fprintf(fp, "Preference: %s, ",
+ rtpref_str[(rai->rtpref >> 3) & 0xff]);
+ fprintf(fp, "MTU: %d\n", rai->linkmtu);
+ fprintf(fp, " ReachableTime: %d, RetransTimer: %d, "
+ "CurHopLimit: %d\n", rai->reachabletime,
+ rai->retranstimer, rai->hoplimit);
+ if (rai->clockskew)
+ fprintf(fp, " Clock skew: %ldsec\n",
+ rai->clockskew);
+ for (first = 1, pfx = rai->prefix.next; pfx != &rai->prefix;
+ pfx = pfx->next) {
+ if (first) {
+ fprintf(fp, " Prefixes:\n");
+ first = 0;
+ }
+ fprintf(fp, " %s/%d(",
+ inet_ntop(AF_INET6, &pfx->prefix, prefixbuf,
+ sizeof(prefixbuf)), pfx->prefixlen);
+ switch (pfx->origin) {
+ case PREFIX_FROM_KERNEL:
+ fprintf(fp, "KERNEL, ");
+ break;
+ case PREFIX_FROM_CONFIG:
+ fprintf(fp, "CONFIG, ");
+ break;
+ case PREFIX_FROM_DYNAMIC:
+ fprintf(fp, "DYNAMIC, ");
+ break;
+ }
+ if (pfx->validlifetime == ND6_INFINITE_LIFETIME)
+ fprintf(fp, "vltime: infinity");
+ else
+ fprintf(fp, "vltime: %ld",
+ (long)pfx->validlifetime);
+ if (pfx->vltimeexpire != 0)
+ fprintf(fp, "(decr,expire %ld), ", (long)
+ pfx->vltimeexpire > now.tv_sec ?
+ pfx->vltimeexpire - now.tv_sec : 0);
+ else
+ fprintf(fp, ", ");
+ if (pfx->preflifetime == ND6_INFINITE_LIFETIME)
+ fprintf(fp, "pltime: infinity");
+ else
+ fprintf(fp, "pltime: %ld",
+ (long)pfx->preflifetime);
+ if (pfx->pltimeexpire != 0)
+ fprintf(fp, "(decr,expire %ld), ", (long)
+ pfx->pltimeexpire > now.tv_sec ?
+ pfx->pltimeexpire - now.tv_sec : 0);
+ else
+ fprintf(fp, ", ");
+ fprintf(fp, "flags: %s%s%s",
+ pfx->onlinkflg ? "L" : "",
+ pfx->autoconfflg ? "A" : "",
+ "");
+ if (pfx->timer) {
+ struct timeval *rest;
+
+ rest = rtadvd_timer_rest(pfx->timer);
+ if (rest) { /* XXX: what if not? */
+ fprintf(fp, ", expire in: %ld",
+ (long)rest->tv_sec);
+ }
+ }
+ fprintf(fp, ")\n");
+ }
+#ifdef ROUTEINFO
+ for (first = 1, rti = rai->route.next; rti != &rai->route;
+ rti = rti->next) {
+ if (first) {
+ fprintf(fp, " Route Information:\n");
+ first = 0;
+ }
+ fprintf(fp, " %s/%d (",
+ inet_ntop(AF_INET6, &rti->prefix,
+ prefixbuf, sizeof(prefixbuf)),
+ rti->prefixlen);
+ fprintf(fp, "preference: %s, ",
+ rtpref_str[0xff & (rti->rtpref >> 3)]);
+ if (rti->ltime == ND6_INFINITE_LIFETIME)
+ fprintf(fp, "lifetime: infinity");
+ else
+ fprintf(fp, "lifetime: %ld", (long)rti->ltime);
+ fprintf(fp, ")\n");
+ }
+#endif
+ }
+}
+
+void
+rtadvd_dump_file(dumpfile)
+ char *dumpfile;
+{
+ debuglog("<%s> dump current status to %s", __func__,
+ dumpfile);
+
+ if ((fp = fopen(dumpfile, "w")) == NULL) {
+ errorlog("<%s> open a dump file(%s)",
+ __func__, dumpfile);
+ return;
+ }
+
+ if_dump();
+
+ fclose(fp);
+}
diff --git a/network_cmds/rtadvd.tproj/dump.h b/network_cmds/rtadvd.tproj/dump.h
new file mode 100644
index 0000000..4e10b4a
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/dump.h
@@ -0,0 +1,32 @@
+/* $KAME: dump.h,v 1.1 2000/05/23 11:31:26 itojun Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern void rtadvd_dump_file(char *);
diff --git a/network_cmds/rtadvd.tproj/if.c b/network_cmds/rtadvd.tproj/if.c
new file mode 100644
index 0000000..9862af2
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/if.c
@@ -0,0 +1,604 @@
+/* $KAME: if.c,v 1.17 2001/01/21 15:27:30 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/ethernet.h>
+#include <ifaddrs.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "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(uint32_t)) :\
+ sizeof(uint32_t)))
+
+/* Interface list */
+TAILQ_HEAD(ifilist_head_t, ifinfo);
+struct ifilist_head_t ifilist = TAILQ_HEAD_INITIALIZER(ifilist);
+size_t ifblock_size;
+char *ifblock;
+
+static void get_iflist(char **buf, size_t *size);
+static void parse_iflist(char *buf, size_t bufsize);
+static void purge_iflist();
+
+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 = NULL;
+
+ 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 (sdl == NULL || (ret_sdl = malloc(sdl->sdl_len)) == NULL)
+ goto end;
+ memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
+
+end:
+ free(buf);
+ return(ret_sdl);
+}
+
+int
+if_getmtu(char *name)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct if_data *ifd;
+ int mtu = 0;
+
+ if (getifaddrs(&ifap) < 0)
+ return(0);
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (strcmp(ifa->ifa_name, name) == 0) {
+ ifd = ifa->ifa_data;
+ if (ifd)
+ mtu = ifd->ifi_mtu;
+ break;
+ }
+ }
+ freeifaddrs(ifap);
+
+#ifdef SIOCGIFMTU /* XXX: this ifdef may not be necessary */
+ if (mtu == 0) {
+ struct ifreq ifr;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ return(0);
+
+ ifr.ifr_addr.sa_family = AF_INET6;
+ strlcpy(ifr.ifr_name, name,
+ sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0) {
+ close(s);
+ return(0);
+ }
+ close(s);
+
+ mtu = ifr.ifr_mtu;
+ }
+#endif
+
+ return(mtu);
+}
+
+/* give interface index and its old flags, then new flags returned */
+int
+if_getflags(int ifindex, int oifflags)
+{
+ struct ifreq ifr;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ errorlog("<%s> socket: %s", __func__,
+ strerror(errno));
+ return (oifflags & ~IFF_UP);
+ }
+
+ if_indextoname(ifindex, ifr.ifr_name);
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ errorlog("<%s> ioctl:SIOCGIFFLAGS: failed for %s",
+ __func__, ifr.ifr_name);
+ close(s);
+ return (oifflags & ~IFF_UP);
+ }
+ close(s);
+ return (ifr.ifr_flags);
+}
+
+#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
+int
+lladdropt_length(struct sockaddr_dl *sdl)
+{
+ switch (sdl->sdl_type) {
+ case IFT_ETHER:
+ return(ROUNDUP8(ETHER_ADDR_LEN + 2));
+ default:
+ return(0);
+ }
+}
+
+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:
+ errorlog("<%s> unsupported link type(%d)",
+ __func__, 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);
+}
+
+#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) {
+ errorlog("<%s> rtm_msglen is 0 "
+ "(buf=%p lim=%p rtm=%p)", __func__,
+ buf, lim, rtm);
+ break;
+ }
+ if (FILTER_MATCH(rtm->rtm_type, filter) == 0) {
+ continue;
+ }
+
+ switch (rtm->rtm_type) {
+ case RTM_GET:
+ case RTM_ADD:
+ case RTM_DELETE:
+ /* address related checks */
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+ if ((dst = rti_info[RTAX_DST]) == NULL ||
+ dst->sa_family != AF_INET6)
+ continue;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&SIN6(dst)->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&SIN6(dst)->sin6_addr))
+ continue;
+
+ if ((gw = rti_info[RTAX_GATEWAY]) == NULL ||
+ gw->sa_family != AF_LINK)
+ continue;
+ if (ifindex && SDL(gw)->sdl_index != ifindex)
+ continue;
+
+ if (rti_info[RTAX_NETMASK] == NULL)
+ continue;
+
+ /* found */
+ *lenp = rtm->rtm_msglen;
+ return (char *)rtm;
+ /* NOTREACHED */
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+
+ /* address related checks */
+ sa = (struct sockaddr *)(ifam + 1);
+ get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
+ if ((ifa = rti_info[RTAX_IFA]) == NULL ||
+ (ifa->sa_family != AF_INET &&
+ ifa->sa_family != AF_INET6))
+ continue;
+
+ if (ifa->sa_family == AF_INET6 &&
+ (IN6_IS_ADDR_LINKLOCAL(&SIN6(ifa)->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&SIN6(ifa)->sin6_addr)))
+ continue;
+
+ if (ifindex && ifam->ifam_index != ifindex)
+ continue;
+
+ /* found */
+ *lenp = ifam->ifam_msglen;
+ return (char *)rtm;
+ /* NOTREACHED */
+ case RTM_IFINFO:
+ /* found */
+ *lenp = rtm->rtm_msglen;
+ return (char *)rtm;
+ /* NOTREACHED */
+ }
+ }
+
+ return (char *)rtm;
+}
+#undef FILTER_MATCH
+
+struct in6_addr *
+get_addr(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+ return(&SIN6(rti_info[RTAX_DST])->sin6_addr);
+}
+
+int
+get_rtm_ifindex(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+ return(((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index);
+}
+
+int
+get_ifm_ifindex(char *buf)
+{
+ struct if_msghdr *ifm = (struct if_msghdr *)buf;
+
+ return ((int)ifm->ifm_index);
+}
+
+int
+get_ifam_ifindex(char *buf)
+{
+ struct ifa_msghdr *ifam = (struct ifa_msghdr *)buf;
+
+ return ((int)ifam->ifam_index);
+}
+
+int
+get_ifm_flags(char *buf)
+{
+ struct if_msghdr *ifm = (struct if_msghdr *)buf;
+
+ return (ifm->ifm_flags);
+}
+
+int
+get_prefixlen(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ 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;
+ return prefixlen(p, lim);
+}
+
+int
+prefixlen(u_char *p, u_char *lim)
+{
+ int masklen;
+
+ for (masklen = 0; p < lim; p++) {
+ switch (*p) {
+ case 0xff:
+ masklen += 8;
+ break;
+ case 0xfe:
+ masklen += 7;
+ break;
+ case 0xfc:
+ masklen += 6;
+ break;
+ case 0xf8:
+ masklen += 5;
+ break;
+ case 0xf0:
+ masklen += 4;
+ break;
+ case 0xe0:
+ masklen += 3;
+ break;
+ case 0xc0:
+ masklen += 2;
+ break;
+ case 0x80:
+ masklen += 1;
+ break;
+ case 0x00:
+ break;
+ default:
+ return(-1);
+ }
+ }
+
+ return(masklen);
+}
+
+int
+rtmsg_type(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+
+ return(rtm->rtm_type);
+}
+
+int
+rtmsg_len(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+
+ return(rtm->rtm_msglen);
+}
+
+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) {
+ errorlog("<%s> sysctl: iflist size get failed",
+ __func__);
+ exit(1);
+ }
+ if ((*buf = malloc(*size)) == NULL) {
+ errorlog("<%s> malloc failed", __func__);
+ exit(1);
+ }
+ if (sysctl(mib, 6, *buf, size, NULL, 0) < 0) {
+ errorlog("<%s> sysctl: iflist get failed",
+ __func__);
+ exit(1);
+ }
+ return;
+}
+
+struct if_msghdr *
+get_interface_entry(int if_index)
+{
+ struct ifinfo *ifi = NULL;
+ struct if_msghdr *ifm = NULL;
+
+ TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+ if (if_index == ifi->ifm->ifm_index) {
+ ifm = ifi->ifm;
+ break;
+ }
+ }
+ return ifm;
+}
+
+/*
+ * 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(char *buf, size_t bufsize)
+{
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ char *lim;
+
+ lim = buf + bufsize;
+ for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) {
+ if (ifm->ifm_msglen == 0) {
+ errorlog("<%s> ifm_msglen is 0 "
+ "(buf=%p lim=%p ifm=%p)", __func__,
+ buf, lim, ifm);
+ return;
+ }
+
+ if (ifm->ifm_type == RTM_IFINFO) {
+ struct ifinfo *ifi = NULL;
+
+ if (get_interface_entry(ifm->ifm_index) != NULL) {
+ debuglog("Interface entry is already present for "
+ "interface index: %d. Skipping.", ifm->ifm_index);
+ continue;
+ }
+
+ ELM_MALLOC(ifi, exit(1));
+ ifi->ifm = ifm;
+ TAILQ_INSERT_TAIL(&ifilist, ifi, ifi_next);
+ } else {
+ errorlog("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) {
+ errorlog("<%s> ifa_msglen is 0 "
+ "(buf=%p lim=%p ifam=%p)", __func__,
+ buf, lim, ifam);
+ return;
+ }
+ if (ifam->ifam_type != RTM_NEWADDR)
+ break;
+ }
+ ifm = (struct if_msghdr *)ifam;
+ }
+}
+
+static void
+purge_iflist()
+{
+ struct ifinfo *ifi = NULL;
+ if (!TAILQ_EMPTY(&ifilist)) {
+ while ((ifi = TAILQ_FIRST(&ifilist)) != NULL) {
+ TAILQ_REMOVE(&ifilist, ifi, ifi_next);
+ free(ifi);
+ }
+ }
+}
+
+void
+init_iflist()
+{
+ purge_iflist();
+
+ if (ifblock) {
+ free(ifblock);
+ ifblock_size = 0;
+ }
+
+ /* get iflist block from kernel */
+ get_iflist(&ifblock, &ifblock_size);
+
+ /* make list of pointers to each if_msghdr */
+ parse_iflist(ifblock, ifblock_size);
+}
diff --git a/network_cmds/rtadvd.tproj/if.h b/network_cmds/rtadvd.tproj/if.h
new file mode 100644
index 0000000..e733d63
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/if.h
@@ -0,0 +1,64 @@
+/* $KAME: if.h,v 1.10 2003/02/24 11:29:10 ono Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define RTADV_TYPE2BITMASK(type) (0x1 << type)
+
+extern size_t ifblock_size;
+extern char *ifblock;
+
+struct nd_opt_hdr;
+struct sockaddr_dl *if_nametosdl(char *);
+int if_getmtu(char *);
+int if_getflags(int, int);
+int lladdropt_length(struct sockaddr_dl *);
+void lladdropt_fill(struct sockaddr_dl *, struct nd_opt_hdr *);
+int rtbuf_len(void);
+char *get_next_msg(char *, char *, int, size_t *, int);
+struct in6_addr *get_addr(char *);
+int get_rtm_ifindex(char *);
+int get_ifm_ifindex(char *);
+int get_ifam_ifindex(char *);
+int get_ifm_flags(char *);
+int get_prefixlen(char *);
+int prefixlen(u_char *, u_char *);
+int rtmsg_type(char *);
+int ifmsg_type(char *);
+int rtmsg_len(char *);
+int ifmsg_len(char *);
+void init_iflist(void);
+
+struct ifinfo {
+ TAILQ_ENTRY(ifinfo) ifi_next;
+ struct if_msghdr * ifm;
+};
+
+struct if_msghdr *get_interface_entry(int if_index);
+
diff --git a/network_cmds/rtadvd.tproj/pathnames.h b/network_cmds/rtadvd.tproj/pathnames.h
new file mode 100644
index 0000000..d288ab9
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/pathnames.h
@@ -0,0 +1,4 @@
+/* $KAME: pathnames.h,v 1.2 2000/05/16 13:34:13 itojun Exp $ */
+/* $FreeBSD: src/usr.sbin/rtadvd/pathnames.h,v 1.2.2.2 2001/07/03 11:02:14 ume Exp $ */
+
+#define _PATH_RTADVDCONF "/etc/rtadvd.conf"
diff --git a/network_cmds/rtadvd.tproj/rrenum.c b/network_cmds/rtadvd.tproj/rrenum.c
new file mode 100644
index 0000000..ae04a55
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/rrenum.c
@@ -0,0 +1,484 @@
+/* $KAME: rrenum.c,v 1.12 2002/06/10 19:59:47 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "rtadvd.h"
+#include "rrenum.h"
+#include "if.h"
+
+#define RR_ISSET_SEGNUM(segnum_bits, segnum) \
+ ((((segnum_bits)[(segnum) >> 5]) & (1 << ((segnum) & 31))) != 0)
+#define RR_SET_SEGNUM(segnum_bits, segnum) \
+ (((segnum_bits)[(segnum) >> 5]) |= (1 << ((segnum) & 31)))
+
+struct rr_operation {
+ u_long rro_seqnum;
+ u_long rro_segnum_bits[8];
+};
+
+static struct rr_operation rro;
+static int rr_rcvifindex;
+static int rrcmd2pco[RPM_PCO_MAX] = {
+ 0,
+ SIOCAIFPREFIX_IN6,
+ SIOCCIFPREFIX_IN6,
+ SIOCSGIFPREFIX_IN6
+};
+static int s = -1;
+
+/*
+ * Check validity of a Prefix Control Operation(PCO).
+ * Return 0 on success, 1 on failure.
+ */
+static int
+rr_pco_check(int len, struct rr_pco_match *rpm)
+{
+ struct rr_pco_use *rpu, *rpulim;
+ int checklen;
+
+ /* rpm->rpm_len must be (4N * 3) as router-renum-05.txt */
+ if ((rpm->rpm_len - 3) < 0 || /* must be at least 3 */
+ (rpm->rpm_len - 3) & 0x3) { /* must be multiple of 4 */
+ errorlog("<%s> rpm_len %d is not 4N * 3",
+ __func__, rpm->rpm_len);
+ return 1;
+ }
+ /* rpm->rpm_code must be valid value */
+ switch (rpm->rpm_code) {
+ case RPM_PCO_ADD:
+ case RPM_PCO_CHANGE:
+ case RPM_PCO_SETGLOBAL:
+ break;
+ default:
+ errorlog("<%s> unknown rpm_code %d", __func__,
+ rpm->rpm_code);
+ return 1;
+ }
+ /* rpm->rpm_matchlen must be 0 to 128 inclusive */
+ if (rpm->rpm_matchlen > 128) {
+ errorlog("<%s> rpm_matchlen %d is over 128",
+ __func__, rpm->rpm_matchlen);
+ return 1;
+ }
+
+ /*
+ * rpu->rpu_uselen, rpu->rpu_keeplen, and sum of them must be
+ * between 0 and 128 inclusive
+ */
+ for (rpu = (struct rr_pco_use *)(rpm + 1),
+ rpulim = (struct rr_pco_use *)((char *)rpm + len);
+ rpu < rpulim;
+ rpu += 1) {
+ checklen = rpu->rpu_uselen;
+ checklen += rpu->rpu_keeplen;
+ /*
+ * omit these check, because either of rpu_uselen
+ * and rpu_keeplen is unsigned char
+ * (128 > rpu_uselen > 0)
+ * (128 > rpu_keeplen > 0)
+ * (rpu_uselen + rpu_keeplen > 0)
+ */
+ if (checklen > 128) {
+ errorlog("<%s> sum of rpu_uselen %d and"
+ " rpu_keeplen %d is %d(over 128)",
+ __func__, rpu->rpu_uselen,
+ rpu->rpu_keeplen,
+ rpu->rpu_uselen + rpu->rpu_keeplen);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+do_use_prefix(int len, struct rr_pco_match *rpm,
+ struct in6_rrenumreq *irr, int ifindex)
+{
+ struct rr_pco_use *rpu, *rpulim;
+ struct rainfo *rai;
+ struct prefix *pp;
+
+ rpu = (struct rr_pco_use *)(rpm + 1);
+ rpulim = (struct rr_pco_use *)((char *)rpm + len);
+
+ if (rpu == rpulim) { /* no use prefix */
+ if (rpm->rpm_code == RPM_PCO_ADD)
+ return;
+
+ irr->irr_u_uselen = 0;
+ irr->irr_u_keeplen = 0;
+ irr->irr_raf_mask_onlink = 0;
+ irr->irr_raf_mask_auto = 0;
+ irr->irr_vltime = 0;
+ irr->irr_pltime = 0;
+ memset(&irr->irr_flags, 0, sizeof(irr->irr_flags));
+ irr->irr_useprefix.sin6_len = 0; /* let it mean, no addition */
+ irr->irr_useprefix.sin6_family = 0;
+ irr->irr_useprefix.sin6_addr = in6addr_any;
+ if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 &&
+ errno != EADDRNOTAVAIL)
+ errorlog("<%s> ioctl: %s", __func__,
+ strerror(errno));
+ return;
+ }
+
+ for (rpu = (struct rr_pco_use *)(rpm + 1),
+ rpulim = (struct rr_pco_use *)((char *)rpm + len);
+ rpu < rpulim;
+ rpu += 1) {
+ /* init in6_rrenumreq fields */
+ irr->irr_u_uselen = rpu->rpu_uselen;
+ irr->irr_u_keeplen = rpu->rpu_keeplen;
+ irr->irr_raf_mask_onlink =
+ !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK);
+ irr->irr_raf_mask_auto =
+ !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_AUTO);
+ irr->irr_vltime = ntohl(rpu->rpu_vltime);
+ irr->irr_pltime = ntohl(rpu->rpu_pltime);
+ irr->irr_raf_onlink =
+ (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK) == 0 ? 0 : 1;
+ irr->irr_raf_auto =
+ (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_AUTO) == 0 ? 0 : 1;
+ irr->irr_rrf_decrvalid =
+ (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME) == 0 ? 0 : 1;
+ irr->irr_rrf_decrprefd =
+ (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME) == 0 ? 0 : 1;
+ irr->irr_useprefix.sin6_len = sizeof(irr->irr_useprefix);
+ irr->irr_useprefix.sin6_family = AF_INET6;
+ irr->irr_useprefix.sin6_addr = rpu->rpu_prefix;
+
+ if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 &&
+ errno != EADDRNOTAVAIL)
+ errorlog("<%s> ioctl: %s", __func__,
+ strerror(errno));
+
+ /* very adhoc: should be rewritten */
+ if (rpm->rpm_code == RPM_PCO_CHANGE &&
+ IN6_ARE_ADDR_EQUAL(&rpm->rpm_prefix, &rpu->rpu_prefix) &&
+ rpm->rpm_matchlen == rpu->rpu_uselen &&
+ rpu->rpu_uselen == rpu->rpu_keeplen) {
+ if ((rai = if_indextorainfo(ifindex)) == NULL)
+ continue; /* non-advertising IF */
+
+ for (pp = rai->prefix.next; pp != &rai->prefix;
+ pp = pp->next) {
+ struct timeval now;
+
+ if (prefix_match(&pp->prefix, pp->prefixlen,
+ &rpm->rpm_prefix,
+ rpm->rpm_matchlen)) {
+ /* change parameters */
+ pp->validlifetime = ntohl(rpu->rpu_vltime);
+ pp->preflifetime = ntohl(rpu->rpu_pltime);
+ if (irr->irr_rrf_decrvalid) {
+ gettimeofday(&now, 0);
+ pp->vltimeexpire =
+ now.tv_sec + pp->validlifetime;
+ } else
+ pp->vltimeexpire = 0;
+ if (irr->irr_rrf_decrprefd) {
+ gettimeofday(&now, 0);
+ pp->pltimeexpire =
+ now.tv_sec + pp->preflifetime;
+ } else
+ pp->pltimeexpire = 0;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * process a Prefix Control Operation(PCO).
+ * return 0 on success, 1 on failure
+ */
+static int
+do_pco(struct icmp6_router_renum *rr, int len, struct rr_pco_match *rpm)
+{
+ int ifindex = 0;
+ struct in6_rrenumreq irr;
+
+ if ((rr_pco_check(len, rpm) != 0))
+ return 1;
+
+ if (s == -1 && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ errorlog("<%s> socket: %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+
+ memset(&irr, 0, sizeof(irr));
+ irr.irr_origin = PR_ORIG_RR;
+ irr.irr_m_len = rpm->rpm_matchlen;
+ irr.irr_m_minlen = rpm->rpm_minlen;
+ irr.irr_m_maxlen = rpm->rpm_maxlen;
+ irr.irr_matchprefix.sin6_len = sizeof(irr.irr_matchprefix);
+ irr.irr_matchprefix.sin6_family = AF_INET6;
+ irr.irr_matchprefix.sin6_addr = rpm->rpm_prefix;
+
+ while (if_indextoname(++ifindex, irr.irr_name)) {
+ struct if_msghdr * ifm = get_interface_entry(ifindex);
+ if (ifm == NULL) {
+ debuglog("Couldn't find interface entry for %d. Skipping.", ifindex);
+ continue;
+ }
+ /*
+ * if ICMP6_RR_FLAGS_FORCEAPPLY(A flag) is 0 and IFF_UP is off,
+ * the interface is not applied
+ */
+ if ((rr->rr_flags & ICMP6_RR_FLAGS_FORCEAPPLY) == 0 &&
+ (ifm->ifm_flags & IFF_UP) == 0)
+ continue;
+ /* TODO: interface scope check */
+ do_use_prefix(len, rpm, &irr, ifindex);
+ }
+ if (errno == ENXIO)
+ return 0;
+ else if (errno) {
+ errorlog("<%s> if_indextoname: %s", __func__,
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * call do_pco() for each Prefix Control Operations(PCOs) in a received
+ * Router Renumbering Command packet.
+ * return 0 on success, 1 on failure
+ */
+static int
+do_rr(int len, struct icmp6_router_renum *rr)
+{
+ struct rr_pco_match *rpm;
+ char *cp, *lim;
+
+ lim = (char *)rr + len;
+ cp = (char *)(rr + 1);
+ len -= sizeof(struct icmp6_router_renum);
+
+ /* 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:
+ errorlog("<%s> pkt too short. left len = %d. "
+ "gabage at end of pkt?", __func__, len);
+ return 1;
+ }
+ rpmlen = rpm->rpm_len << 3;
+ if (len < rpmlen)
+ goto tooshort;
+
+ if (do_pco(rr, rpmlen, rpm)) {
+ errorlog("<%s> invalid PCO", __func__);
+ goto next;
+ }
+
+ next:
+ cp += rpmlen;
+ len -= rpmlen;
+ }
+
+ return 0;
+}
+
+/*
+ * check validity of a router renumbering command packet
+ * return 0 on success, 1 on failure
+ */
+static int
+rr_command_check(int len, struct icmp6_router_renum *rr, struct in6_addr *from,
+ struct in6_addr *dst)
+{
+ 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))) {
+ errorlog("<%s> rr_command len %d is too short",
+ __func__, len);
+ return 1;
+ }
+
+ /* destination check. only for multicast. omit unicast check. */
+ if (IN6_IS_ADDR_MULTICAST(dst) && !IN6_IS_ADDR_MC_LINKLOCAL(dst) &&
+ !IN6_IS_ADDR_MC_SITELOCAL(dst)) {
+ errorlog("<%s> dst mcast addr %s is illegal",
+ __func__,
+ inet_ntop(AF_INET6, dst, ntopbuf, INET6_ADDRSTRLEN));
+ return 1;
+ }
+
+ /* seqnum and segnum check */
+ if (rro.rro_seqnum > rr->rr_seqnum) {
+ errorlog("<%s> rcvd old seqnum %d from %s",
+ __func__, (u_int32_t)ntohl(rr->rr_seqnum),
+ inet_ntop(AF_INET6, from, ntopbuf, INET6_ADDRSTRLEN));
+ return 1;
+ }
+ if (rro.rro_seqnum == rr->rr_seqnum &&
+ (rr->rr_flags & ICMP6_RR_FLAGS_TEST) == 0 &&
+ RR_ISSET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum)) {
+ if ((rr->rr_flags & ICMP6_RR_FLAGS_REQRESULT) != 0)
+ errorlog("<%s> rcvd duped segnum %d from %s",
+ __func__, rr->rr_segnum,
+ inet_ntop(AF_INET6, from, ntopbuf,
+ INET6_ADDRSTRLEN));
+ return 0;
+ }
+
+ /* update seqnum */
+ if (rro.rro_seqnum != rr->rr_seqnum) {
+ /* then must be "<" */
+
+ /* init rro_segnum_bits */
+ memset(rro.rro_segnum_bits, 0,
+ sizeof(rro.rro_segnum_bits));
+ }
+ rro.rro_seqnum = rr->rr_seqnum;
+
+ return 0;
+}
+
+static void
+rr_command_input(int len, struct icmp6_router_renum *rr,
+ struct in6_addr *from, struct in6_addr *dst)
+{
+ /* rr_command validity check */
+ if (rr_command_check(len, rr, from, dst))
+ goto failed;
+ if ((rr->rr_flags & (ICMP6_RR_FLAGS_TEST|ICMP6_RR_FLAGS_REQRESULT)) ==
+ ICMP6_RR_FLAGS_TEST)
+ return;
+
+ /* do router renumbering */
+ if (do_rr(len, rr)) {
+ goto failed;
+ }
+
+ /* update segnum */
+ RR_SET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum);
+
+ return;
+
+ failed:
+ errorlog("<%s> received RR was invalid", __func__);
+ return;
+}
+
+void
+rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi,
+ struct sockaddr_in6 *from, struct in6_addr *dst)
+{
+ char ntopbuf[2][INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+
+ debuglog("<%s> RR received from %s to %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf[0], INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* packet validation based on Section 4.1 of RFC2894 */
+ if (len < sizeof(struct icmp6_router_renum)) {
+ noticelog("<%s>: RR short message (size %d) from %s to %s on %s",
+ __func__, len,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf[0], INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /*
+ * If the IPv6 destination address is neither an All Routers multicast
+ * address [AARCH] nor one of the receiving router's unicast addresses,
+ * the message MUST be discarded and SHOULD be logged to network
+ * management.
+ * We rely on the kernel input routine for unicast addresses, and thus
+ * check multicast destinations only.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) &&
+ !IN6_ARE_ADDR_EQUAL(&in6a_site_allrouters, &pi->ipi6_addr)) {
+ noticelog("<%s>: RR message with invalid destination (%s) "
+ "from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &dst, ntopbuf[0], INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf[1], INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ rr_rcvifindex = pi->ipi6_ifindex;
+
+ switch (rr->rr_code) {
+ case ICMP6_ROUTER_RENUMBERING_COMMAND:
+ rr_command_input(len, rr, &from->sin6_addr, dst);
+ /* TODO: send reply msg */
+ break;
+ case ICMP6_ROUTER_RENUMBERING_RESULT:
+ /* RESULT will be processed by rrenumd */
+ break;
+ case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET:
+ /* TODO: sequence number reset */
+ break;
+ default:
+ errorlog("<%s> received unknown code %d",
+ __func__, rr->rr_code);
+ break;
+
+ }
+
+ return;
+}
diff --git a/network_cmds/rtadvd.tproj/rrenum.h b/network_cmds/rtadvd.tproj/rrenum.h
new file mode 100644
index 0000000..b6ed486
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/rrenum.h
@@ -0,0 +1,33 @@
+/* $KAME: rrenum.h,v 1.3 2001/01/21 15:37:14 itojun Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+void rr_input(int, struct icmp6_router_renum *, struct in6_pktinfo *,
+ struct sockaddr_in6 *, struct in6_addr *);
diff --git a/network_cmds/rtadvd.tproj/rtadvd.8 b/network_cmds/rtadvd.tproj/rtadvd.8
new file mode 100644
index 0000000..27429de
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/rtadvd.8
@@ -0,0 +1,207 @@
+.\" $FreeBSD: src/usr.sbin/rtadvd/rtadvd.8,v 1.3.2.6 2001/08/16 15:56:30 ru Exp $
+.\" $KAME: rtadvd.8,v 1.24 2002/05/31 16:16:08 jinmei Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd August 27, 2011
+.Dt RTADVD 8
+.Os
+.Sh NAME
+.Nm rtadvd
+.Nd router advertisement daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dDfMRs
+.Op Fl c Ar configfile
+.Op Fl F Ar dumpfile
+.Op Fl p Ar pidfile
+.Ar interface ...
+.Sh DESCRIPTION
+.Nm
+sends router advertisement packets to the specified
+.Ar interfaces .
+.Pp
+The program will daemonize itself on invocation.
+It will then send router advertisement packets periodically, as well
+as in response to router solicitation messages sent by end hosts.
+.Pp
+Router advertisements can be configured on a per-interface basis, as
+described in
+.Xr rtadvd.conf 5 .
+.Pp
+If there is no configuration file entry for an interface,
+or if the configuration file does not exist altogether,
+.Nm
+sets all the parameters to their default values.
+In particular,
+.Nm
+reads all the interface routes from the routing table and advertises
+them as on-link prefixes.
+.Pp
+.Nm
+also watches the routing table.
+If an interface direct route is
+added on an advertising interface and no static prefixes are
+specified by the configuration file,
+.Nm
+adds the corresponding prefix to its advertising list.
+.Pp
+Similarly, when an interface direct route is deleted,
+.Nm
+will start advertising the prefixes with zero valid and preferred
+lifetimes to help the receiving hosts switch to a new prefix when
+renumbering.
+Note, however, that the zero valid lifetime cannot invalidate the
+autoconfigured addresses at a receiving host immediately.
+According to the specification, the host will retain the address
+for a certain period, which will typically be two hours.
+The zero lifetimes rather intend to make the address deprecated,
+indicating that a new non-deprecated address should be used as the
+source address of a new connection.
+This behavior will last for two hours.
+Then
+.Nm
+will completely remove the prefix from the advertising list,
+and succeeding advertisements will not contain the prefix information.
+.Pp
+Moreover, if the status of an advertising interface changes,
+.Nm
+will start or stop sending router advertisements according
+to the latest status.
+.Pp
+The
+.Fl s
+option may be used to disable this behavior;
+.Nm
+will not watch the routing table and the whole functionality described
+above will be suppressed.
+.Pp
+Basically, hosts MUST NOT send Router Advertisement messages at any
+time (RFC 2461, Section 6.2.3).
+However, it would sometimes be useful to allow hosts to advertise some
+parameters such as prefix information and link MTU.
+Thus,
+.Nm
+can be invoked if router lifetime is explicitly set zero on every
+advertising interface.
+.Pp
+The command line options are:
+.Bl -tag -width indent
+.\"
+.It Fl c
+Specify an alternate location,
+.Ar configfile ,
+for the configuration file.
+By default,
+.Pa /etc/rtadvd.conf
+is used.
+.It Fl d
+Print debugging information.
+.It Fl D
+Even more debugging information is printed.
+.It Fl f
+This option is now deprecated and ignored.
+.It Fl F
+Specify an alternative file in which to dump internal states when
+.Nm
+receives signal
+.Dv SIGUSR1 .
+The default is
+.Pa /var/run/rtadvd.dump .
+.It Fl M
+Specify an interface to join the all-routers site-local multicast group.
+By default,
+.Nm
+tries to join the first advertising interface appearing on the command
+line.
+This option has meaning only with the
+.Fl R
+option, which enables routing renumbering protocol support.
+.It Fl p
+Specify an alternative file in which to store the process ID.
+The default is
+.Pa /var/run/rtadvd.pid.
+.It Fl R
+Accept router renumbering requests.
+If you enable it, certain IPsec setup is suggested for security reasons.
+This option is currently disabled, and is ignored by
+.Nm
+with a warning message.
+.It Fl s
+Do not add or delete prefixes dynamically.
+Only statically configured prefixes, if any, will be advertised.
+.El
+.Pp
+Upon receipt of signal
+.Dv SIGUSR1 ,
+.Nm
+will dump the current internal state into
+.Pa /var/run/rtadvd.dump
+or the file specified with option
+.Fl F .
+.Pp
+Use
+.Dv SIGTERM
+to kill
+.Nm
+gracefully.
+In this case,
+.Nm
+will transmit router advertisement with router lifetime 0
+to all the interfaces
+.Pq in accordance with RFC2461 6.2.5 .
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /etc/rtadvd.conf
+The default configuration file.
+.It Pa /var/run/rtadvd.pid
+The default process ID file.
+.It Pa /var/run/rtadvd.dump
+The default file in which
+.Nm
+dumps its internal state.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr rtadvd.conf 5 ,
+.Xr rtsol 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in the WIDE Hydrangea IPv6 protocol stack kit.
+.Sh BUGS
+There used to be some text that recommended users not to let
+.Nm
+advertise Router Advertisement messages on an upstream link to avoid
+undesirable
+.Xr icmp6 4
+redirect messages.
+However, based on the later discussion in the IETF ipng working group,
+all routers should rather advertise the messages regardless of
+the network topology, in order to ensure reachability.
diff --git a/network_cmds/rtadvd.tproj/rtadvd.c b/network_cmds/rtadvd.tproj/rtadvd.c
new file mode 100644
index 0000000..be5c0f8
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/rtadvd.c
@@ -0,0 +1,1650 @@
+/*
+ * Copyright (c) 2020 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+/* $KAME: rtadvd.c,v 1.82 2003/08/05 12:34:23 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <errno.h>
+#include <libutil.h>
+#include <string.h>
+#include <stdlib.h>
+#include "rtadvd.h"
+#include "rrenum.h"
+#include "advcap.h"
+#include "timer.h"
+#include "if.h"
+#include "config.h"
+#include "dump.h"
+
+struct msghdr rcvmhdr;
+static u_char *rcvcmsgbuf;
+static size_t rcvcmsgbuflen;
+static u_char *sndcmsgbuf = NULL;
+static size_t sndcmsgbuflen;
+volatile sig_atomic_t do_dump;
+volatile sig_atomic_t do_die;
+struct msghdr sndmhdr;
+struct iovec rcviov[2];
+struct iovec sndiov[2];
+struct sockaddr_in6 rcvfrom;
+struct sockaddr_in6 sin6_allnodes = {sizeof(sin6_allnodes), AF_INET6};
+struct in6_addr in6a_site_allrouters;
+static char *dumpfilename = "/var/run/rtadvd.dump";
+static char *pidfilename = "/var/run/rtadvd.pid";
+static struct pidfh *pfh;
+static char *mcastif;
+int sock;
+int rtsock = -1;
+int accept_rr = 0;
+int dflag = 0, sflag = 0;
+int so_traffic_class = SO_TC_CTL; /* use control class, by default */
+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[9];
+ struct {
+ struct nd_opt_hdr *zero;
+ struct nd_opt_hdr *src_lladdr;
+ struct nd_opt_hdr *tgt_lladdr;
+ struct nd_opt_prefix_info *pi;
+ struct nd_opt_rd_hdr *rh;
+ struct nd_opt_mtu *mtu;
+ 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(int, char *[]);
+static void set_die(int);
+static void die(void);
+static void sock_open(void);
+static void rtsock_open(void);
+static void rtadvd_input(void);
+static void rs_input(int, struct nd_router_solicit *,
+ struct in6_pktinfo *, struct sockaddr_in6 *);
+static void ra_input(int, struct nd_router_advert *,
+ struct in6_pktinfo *, struct sockaddr_in6 *);
+static int prefix_check(struct nd_opt_prefix_info *, struct rainfo *,
+ struct sockaddr_in6 *);
+static int nd6_options(struct nd_opt_hdr *, int,
+ union nd_opts *, u_int32_t);
+static void free_ndopts(union nd_opts *);
+static void ra_output(struct rainfo *);
+static void rtmsg_input(void);
+static void rtadvd_set_dump_file(int);
+static void set_short_delay(struct rainfo *);
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ fd_set *fdsetp, *selectfdp;
+ int fdmasks;
+ int maxfd = 0;
+ struct timeval *timeout;
+ int i, ch;
+ int fflag = 0;
+ pid_t pid, otherpid;
+
+ /* get command line options and arguments */
+ while ((ch = getopt(argc, argv, "c:dDF:fMp:Rs")) != -1) {
+ switch (ch) {
+ case 'c':
+ conffile = optarg;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'D':
+ dflag = 2;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'M':
+ mcastif = optarg;
+ break;
+ case 'R':
+ fprintf(stderr, "rtadvd: "
+ "the -R option is currently ignored.\n");
+ /* accept_rr = 1; */
+ /* run anyway... */
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'p':
+ pidfilename = optarg;
+ break;
+ case 'F':
+ dumpfilename = optarg;
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc == 0) {
+ fprintf(stderr,
+ "usage: rtadvd [-dDfMRs] [-c conffile] "
+ "[-F dumpfile] [-p pidfile] interfaces...\n");
+ exit(1);
+ }
+
+ /* 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);
+ }
+
+ pfh = pidfile_open(pidfilename, 0600, &otherpid);
+ if (pfh == NULL) {
+ if (errno == EEXIST)
+ errx(1, "%s already running, pid: %d",
+ getprogname(), otherpid);
+ errorlog("<%s> failed to open the pid log file, run anyway.",
+ __func__);
+ }
+
+ if (!fflag)
+ daemon(1, 0);
+
+ sock_open();
+
+ /* record the current PID */
+ pid = getpid();
+ pidfile_write(pfh);
+
+ maxfd = sock;
+ if (sflag == 0) {
+ rtsock_open();
+ if (rtsock > sock)
+ maxfd = rtsock;
+ } else
+ rtsock = -1;
+
+ fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask);
+ if ((fdsetp = malloc(fdmasks)) == NULL) {
+ err(1, "malloc");
+ /*NOTREACHED*/
+ }
+ if ((selectfdp = malloc(fdmasks)) == NULL) {
+ err(1, "malloc");
+ /*NOTREACHED*/
+ }
+ memset(fdsetp, 0, fdmasks);
+ FD_SET(sock, fdsetp);
+ if (rtsock >= 0)
+ FD_SET(rtsock, fdsetp);
+
+ signal(SIGTERM, set_die);
+ signal(SIGUSR1, rtadvd_set_dump_file);
+
+ while (1) {
+ memcpy(selectfdp, fdsetp, fdmasks); /* reinitialize */
+
+ if (do_dump) { /* SIGUSR1 */
+ do_dump = 0;
+ rtadvd_dump_file(dumpfilename);
+ }
+
+ if (do_die) {
+ die();
+ /*NOTREACHED*/
+ }
+
+ /* timer expiration check and reset the timer */
+ timeout = rtadvd_check_timer();
+
+ if (timeout != NULL) {
+ debuglog("<%s> set timer to %ld:%ld. waiting for "
+ "inputs or timeout", __func__,
+ (long int)timeout->tv_sec,
+ (long int)timeout->tv_usec);
+ } else {
+ debuglog("<%s> there's no timer. waiting for inputs",
+ __func__);
+ }
+
+ if ((i = select(maxfd + 1, selectfdp, NULL, NULL,
+ timeout)) < 0) {
+ /* EINTR would occur upon SIGUSR1 for status dump */
+ if (errno != EINTR)
+ errorlog( "<%s> select: %s",
+ __func__, strerror(errno));
+ continue;
+ }
+ if (i == 0) /* timeout */
+ continue;
+ if (rtsock != -1 && FD_ISSET(rtsock, selectfdp))
+ rtmsg_input();
+ if (FD_ISSET(sock, selectfdp))
+ rtadvd_input();
+ }
+ exit(0); /* NOTREACHED */
+}
+
+static void
+rtadvd_set_dump_file(sig)
+ int sig;
+{
+ do_dump = 1;
+}
+
+static void
+set_die(sig)
+ int sig;
+{
+ do_die = 1;
+}
+
+static void
+die()
+{
+ struct rainfo *ra;
+ int i;
+ const int retrans = MAX_FINAL_RTR_ADVERTISEMENTS;
+
+ if (dflag > 1) {
+ debuglog("<%s> cease to be an advertising router\n",
+ __func__);
+ }
+
+ for (ra = ralist; ra; ra = ra->next) {
+ ra->lifetime = 0;
+ make_packet(ra);
+ }
+ for (i = 0; i < retrans; i++) {
+ for (ra = ralist; ra; ra = ra->next)
+ ra_output(ra);
+
+ if (retrans != 1)
+ sleep(MIN_DELAY_BETWEEN_RAS);
+ }
+ pidfile_remove(pfh);
+ exit(0);
+ /*NOTREACHED*/
+}
+
+static void
+rtmsg_input()
+{
+ int n, type, ifindex = 0, plen;
+ size_t len;
+ char msg[2048], *next, *lim;
+ char ifname[IF_NAMESIZE];
+ struct prefix *prefix;
+ struct rainfo *rai;
+ struct in6_addr *addr;
+ char addrbuf[INET6_ADDRSTRLEN];
+ int prefixchange = 0;
+
+ n = read(rtsock, msg, sizeof(msg));
+ if (dflag > 1) {
+ debuglog( "<%s> received a routing message "
+ "(type = %d, len = %d)", __func__, rtmsg_type(msg), n);
+ }
+ if (n > rtmsg_len(msg)) {
+ /*
+ * This usually won't happen for messages received on
+ * a routing socket.
+ */
+ if (dflag > 1)
+ debuglog("<%s> received data length is larger than "
+ "1st routing message len. multiple messages? "
+ "read %d bytes, but 1st msg len = %d",
+ __func__, n, rtmsg_len(msg));
+#if 0
+ /* adjust length */
+ n = rtmsg_len(msg);
+#endif
+ }
+
+ lim = msg + n;
+ for (next = msg; next < lim; next += len) {
+ struct if_msghdr * ifm = NULL;
+ int oldifflags;
+
+ next = get_next_msg(next, lim, 0, &len,
+ RTADV_TYPE2BITMASK(RTM_ADD) |
+ RTADV_TYPE2BITMASK(RTM_DELETE) |
+ RTADV_TYPE2BITMASK(RTM_NEWADDR) |
+ RTADV_TYPE2BITMASK(RTM_DELADDR) |
+ RTADV_TYPE2BITMASK(RTM_IFINFO));
+ 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) {
+ debuglog("<%s:%d> unknown rtmsg %d on %s",
+ __func__, __LINE__, type,
+ if_indextoname(ifindex, ifname));
+ }
+ continue;
+ }
+
+ if ((rai = if_indextorainfo(ifindex)) == NULL) {
+ if (dflag > 1) {
+ debuglog("<%s> route changed on "
+ "non advertising interface(%s)",
+ __func__,
+ if_indextoname(ifindex, ifname));
+ }
+ continue;
+ }
+ ifm = get_interface_entry(ifindex);
+ if (ifm == NULL) {
+ debuglog("Couldn't find interface entry for %d. Skipping.", ifindex);
+ continue;
+ }
+ oldifflags = ifm->ifm_flags;
+
+ switch (type) {
+ case RTM_ADD:
+ /* init ifflags because it may have changed */
+ ifm->ifm_flags =
+ if_getflags(ifindex, ifm->ifm_flags);
+
+ if (sflag)
+ break; /* we aren't interested in prefixes */
+
+ addr = get_addr(msg);
+ plen = get_prefixlen(msg);
+ /* sanity check for plen */
+ /* as RFC2373, prefixlen is at least 4 */
+ if (plen < 4 || plen > 127) {
+ infolog("<%s> new interface route's"
+ "plen %d is invalid for a prefix",
+ __func__, plen);
+ break;
+ }
+ prefix = find_prefix(rai, addr, plen);
+ if (prefix) {
+ if (prefix->timer) {
+ /*
+ * If the prefix has been invalidated,
+ * make it available again.
+ */
+ update_prefix(prefix);
+ prefixchange = 1;
+ } else if (dflag > 1) {
+ debuglog("<%s> new prefix(%s/%d) "
+ "added on %s, "
+ "but it was already in list",
+ __func__,
+ inet_ntop(AF_INET6, addr,
+ (char *)addrbuf, INET6_ADDRSTRLEN),
+ plen, rai->ifname);
+ }
+ break;
+ }
+ make_prefix(rai, ifindex, addr, plen);
+ prefixchange = 1;
+ break;
+ case RTM_DELETE:
+ /* init ifflags because it may have changed */
+ ifm->ifm_flags = if_getflags(ifindex, ifm->ifm_flags);
+
+ if (sflag)
+ break;
+
+ addr = get_addr(msg);
+ plen = get_prefixlen(msg);
+ /* sanity check for plen */
+ /* as RFC2373, prefixlen is at least 4 */
+ if (plen < 4 || plen > 127) {
+ infolog("<%s> deleted interface route's "
+ "plen %d is invalid for a prefix",
+ __func__, plen);
+ break;
+ }
+ prefix = find_prefix(rai, addr, plen);
+ if (prefix == NULL) {
+ if (dflag > 1) {
+ debuglog("<%s> prefix(%s/%d) was "
+ "deleted on %s, "
+ "but it was not in list",
+ __func__,
+ inet_ntop(AF_INET6, addr,
+ (char *)addrbuf, INET6_ADDRSTRLEN),
+ plen, rai->ifname);
+ }
+ break;
+ }
+ invalidate_prefix(prefix);
+ prefixchange = 1;
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ /* init ifflags because it may have changed */
+ ifm->ifm_flags = if_getflags(ifindex, ifm->ifm_flags);
+ break;
+ case RTM_IFINFO:
+ ifm->ifm_flags = get_ifm_flags(next);
+ break;
+ default:
+ /* should not reach here */
+ if (dflag > 1) {
+ debuglog("<%s:%d> unknown rtmsg %d on %s",
+ __func__, __LINE__, type,
+ if_indextoname(ifindex, ifname));
+ }
+ return;
+ }
+
+ /* check if an interface flag is changed */
+ if ((oldifflags & IFF_UP) && /* UP to DOWN */
+ !(ifm->ifm_flags & IFF_UP)) {
+ infolog("<%s> interface %s becomes down. stop timer.",
+ __func__, rai->ifname);
+ rtadvd_remove_timer(&rai->timer);
+ } else if (!(oldifflags & IFF_UP) && /* DOWN to UP */
+ (ifm->ifm_flags & IFF_UP)) {
+ infolog("<%s> interface %s becomes up. restart timer.",
+ __func__, rai->ifname);
+
+ rai->initcounter = 0; /* reset the counter */
+ rai->waiting = 0; /* XXX */
+ rai->timer = rtadvd_add_timer(ra_timeout,
+ ra_timer_update, rai, rai);
+ ra_timer_update((void *)rai, &rai->timer->tm);
+ rtadvd_set_timer(&rai->timer->tm, rai->timer);
+ } else if (prefixchange &&
+ (ifm->ifm_flags & IFF_UP)) {
+ /*
+ * An advertised prefix has been added or invalidated.
+ * Will notice the change in a short delay.
+ */
+ rai->initcounter = 0;
+ set_short_delay(rai);
+ }
+ }
+
+ 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;
+ char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ struct in6_addr dst = in6addr_any;
+ struct if_msghdr *ifm = NULL;
+
+ /*
+ * Get message. We reset msg_controllen since the field could
+ * be modified if we had received a message before setting
+ * receive options.
+ */
+ rcvmhdr.msg_controllen = rcvcmsgbuflen;
+ if ((i = recvmsg(sock, &rcvmhdr, 0)) < 0)
+ return;
+
+ /* 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) {
+ errorlog("<%s> failed to get receiving interface",
+ __func__);
+ return;
+ }
+ if (hlimp == NULL) {
+ errorlog("<%s> failed to get receiving hop limit",
+ __func__);
+ return;
+ }
+
+ ifm = get_interface_entry(pi->ipi6_ifindex);
+ /*
+ * If we happen to receive data on an interface which is now gone
+ * or down, just discard the data.
+ */
+ if (ifm == NULL ||
+ (ifm->ifm_flags & IFF_UP) == 0) {
+ infolog("<%s> received data on a disabled interface (%s)",
+ __func__,
+ (ifm == NULL) ? "[gone]" :
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+#ifdef OLDRAWSOCKET
+ if (i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
+ errorlog("<%s> packet size(%d) is too short",
+ __func__, i);
+ return;
+ }
+
+ ip = (struct ip6_hdr *)rcvmhdr.msg_iov[0].iov_base;
+ icp = (struct icmp6_hdr *)(ip + 1); /* XXX: ext. hdr? */
+#else
+ if (i < sizeof(struct icmp6_hdr)) {
+ errorlog("<%s> packet size(%d) is too short",
+ __func__, i);
+ return;
+ }
+
+ icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base;
+#endif
+
+ switch (icp->icmp6_type) {
+ case ND_ROUTER_SOLICIT:
+ /*
+ * Message verification - RFC-2461 6.1.1
+ * XXX: these checks must be done in the kernel as well,
+ * but we can't completely rely on them.
+ */
+ if (*hlimp != 255) {
+ noticelog("<%s> RS with invalid hop limit(%d) "
+ "received from %s on %s",
+ __func__, *hlimp,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (icp->icmp6_code) {
+ noticelog("<%s> RS with invalid ICMP6 code(%d) "
+ "received from %s on %s",
+ __func__, icp->icmp6_code,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (i < sizeof(struct nd_router_solicit)) {
+ noticelog("<%s> RS from %s on %s does not have enough "
+ "length (len = %d)",
+ __func__,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
+ return;
+ }
+ rs_input(i, (struct nd_router_solicit *)icp, pi, &rcvfrom);
+ break;
+ case ND_ROUTER_ADVERT:
+ /*
+ * Message verification - RFC-2461 6.1.2
+ * XXX: there's a same dilemma as above...
+ */
+ if (*hlimp != 255) {
+ noticelog("<%s> RA with invalid hop limit(%d) "
+ "received from %s on %s",
+ __func__, *hlimp,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (icp->icmp6_code) {
+ noticelog("<%s> RA with invalid ICMP6 code(%d) "
+ "received from %s on %s",
+ __func__, icp->icmp6_code,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (i < sizeof(struct nd_router_advert)) {
+ noticelog("<%s> RA from %s on %s does not have enough "
+ "length (len = %d)",
+ __func__,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
+ return;
+ }
+ ra_input(i, (struct nd_router_advert *)icp, pi, &rcvfrom);
+ break;
+ case ICMP6_ROUTER_RENUMBERING:
+ if (accept_rr == 0) {
+ errorlog("<%s> received a router renumbering "
+ "message, but not allowed to be accepted",
+ __func__);
+ break;
+ }
+ rr_input(i, (struct icmp6_router_renum *)icp, pi, &rcvfrom,
+ &dst);
+ break;
+ default:
+ /*
+ * Note that this case is POSSIBLE, especially just
+ * after invocation of the daemon. This is because we
+ * could receive message after opening the socket and
+ * before setting ICMP6 type filter(see sock_open()).
+ */
+ errorlog("<%s> invalid icmp type(%d)",
+ __func__, icp->icmp6_type);
+ return;
+ }
+
+ return;
+}
+
+static void
+rs_input(int len, struct nd_router_solicit *rs,
+ struct in6_pktinfo *pi, struct sockaddr_in6 *from)
+{
+ char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ union nd_opts ndopts;
+ struct rainfo *ra;
+ struct soliciter *sol;
+
+ debuglog(
+ "<%s> RS received from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* ND option check */
+ memset(&ndopts, 0, sizeof(ndopts));
+ if (nd6_options((struct nd_opt_hdr *)(rs + 1),
+ len - sizeof(struct nd_router_solicit),
+ &ndopts, NDOPT_FLAG_SRCLINKADDR)) {
+ infolog("<%s> ND option check failed for an RS from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /*
+ * If the IP source address is the unspecified address, there
+ * must be no source link-layer address option in the message.
+ * (RFC-2461 6.1.1)
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&from->sin6_addr) &&
+ ndopts.nd_opts_src_lladdr) {
+ infolog("<%s> RS from unspecified src on %s has a link-layer"
+ " address option",
+ __func__,
+ 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) {
+ infolog("<%s> RS received on non advertising interface(%s)",
+ __func__,
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+
+ ra->rsinput++; /* increment statistics */
+
+ /*
+ * Decide whether to send RA according to the rate-limit
+ * consideration.
+ */
+
+ /* record sockaddr waiting for RA, if possible */
+ sol = (struct soliciter *)malloc(sizeof(*sol));
+ if (sol) {
+ sol->addr = *from;
+ /* XXX RFC2553 need clarification on flowinfo */
+ sol->addr.sin6_flowinfo = 0;
+ sol->next = ra->soliciter;
+ ra->soliciter = sol;
+ }
+
+ /*
+ * If there is already a waiting RS packet, don't
+ * update the timer.
+ */
+ if (ra->waiting++)
+ goto done;
+
+ set_short_delay(ra);
+
+ done:
+ free_ndopts(&ndopts);
+ return;
+}
+
+static void
+set_short_delay(rai)
+ struct rainfo *rai;
+{
+ long delay; /* must not be greater than 1000000 */
+ struct timeval interval, now, min_delay, tm_tmp, *rest;
+
+ if (rai->timer == NULL)
+ return;
+ /*
+ * Compute a random delay. If the computed value
+ * corresponds to a time later than the time the next
+ * multicast RA is scheduled to be sent, ignore the random
+ * delay and send the advertisement at the
+ * already-scheduled time. RFC-2461 6.2.6
+ */
+#ifdef HAVE_ARC4RANDOM
+ delay = arc4random_uniform(MAX_RA_DELAY_TIME);
+#else
+ delay = random() % MAX_RA_DELAY_TIME;
+#endif
+ interval.tv_sec = 0;
+ interval.tv_usec = delay;
+ rest = rtadvd_timer_rest(rai->timer);
+ if (TIMEVAL_LT(*rest, interval)) {
+ debuglog("<%s> random delay is larger than "
+ "the rest of the current timer", __func__);
+ interval = *rest;
+ }
+
+ /*
+ * If we sent a multicast Router Advertisement within
+ * the last MIN_DELAY_BETWEEN_RAS seconds, schedule
+ * the advertisement to be sent at a time corresponding to
+ * MIN_DELAY_BETWEEN_RAS plus the random value after the
+ * previous advertisement was sent.
+ */
+ gettimeofday(&now, NULL);
+ TIMEVAL_SUB(&now, &rai->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, rai->timer);
+}
+
+static void
+ra_input(int len, struct nd_router_advert *ra,
+ struct in6_pktinfo *pi, struct sockaddr_in6 *from)
+{
+ struct rainfo *rai;
+ char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ union nd_opts ndopts;
+ char *on_off[] = {"OFF", "ON"};
+ u_int32_t reachabletime, retranstimer, mtu;
+ int inconsistent = 0;
+
+ debuglog("<%s> RA received from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* ND option check */
+ memset(&ndopts, 0, sizeof(ndopts));
+ if (nd6_options((struct nd_opt_hdr *)(ra + 1),
+ len - sizeof(struct nd_router_advert),
+ &ndopts, NDOPT_FLAG_SRCLINKADDR |
+ NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU)) {
+ infolog("<%s> ND option check failed for an RA from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /*
+ * RA consistency check according to RFC-2461 6.2.7
+ */
+ if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == 0) {
+ infolog("<%s> received RA from %s on non-advertising"
+ " interface(%s)",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+ rai->rainput++; /* increment statistics */
+
+ /* Cur Hop Limit value */
+ if (ra->nd_ra_curhoplimit && rai->hoplimit &&
+ ra->nd_ra_curhoplimit != rai->hoplimit) {
+ infolog("<%s> CurHopLimit inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ rai->ifname,
+ ra->nd_ra_curhoplimit,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->hoplimit);
+ inconsistent++;
+ }
+ /* M flag */
+ if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) !=
+ rai->managedflg) {
+ infolog("<%s> M flag inconsistent on %s:"
+ " %s from %s, %s from us",
+ __func__,
+ rai->ifname,
+ on_off[!rai->managedflg],
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ on_off[rai->managedflg]);
+ inconsistent++;
+ }
+ /* O flag */
+ if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) !=
+ rai->otherflg) {
+ infolog("<%s> O flag inconsistent on %s:"
+ " %s from %s, %s from us",
+ __func__,
+ rai->ifname,
+ on_off[!rai->otherflg],
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ on_off[rai->otherflg]);
+ inconsistent++;
+ }
+ /* Reachable Time */
+ reachabletime = ntohl(ra->nd_ra_reachable);
+ if (reachabletime && rai->reachabletime &&
+ reachabletime != rai->reachabletime) {
+ infolog("<%s> ReachableTime inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ rai->ifname,
+ reachabletime,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->reachabletime);
+ inconsistent++;
+ }
+ /* Retrans Timer */
+ retranstimer = ntohl(ra->nd_ra_retransmit);
+ if (retranstimer && rai->retranstimer &&
+ retranstimer != rai->retranstimer) {
+ infolog("<%s> RetranceTimer inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ rai->ifname,
+ retranstimer,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->retranstimer);
+ inconsistent++;
+ }
+ /* Values in the MTU options */
+ if (ndopts.nd_opts_mtu) {
+ mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
+ if (mtu && rai->linkmtu && mtu != rai->linkmtu) {
+ infolog("<%s> MTU option value inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ rai->ifname, mtu,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->linkmtu);
+ inconsistent++;
+ }
+ }
+ /* Preferred and Valid Lifetimes for prefixes */
+ {
+ struct nd_optlist *optp = ndopts.nd_opts_list;
+
+ if (ndopts.nd_opts_pi) {
+ if (prefix_check(ndopts.nd_opts_pi, rai, from))
+ inconsistent++;
+ }
+ while (optp) {
+ if (prefix_check((struct nd_opt_prefix_info *)optp->opt,
+ rai, from))
+ inconsistent++;
+ optp = optp->next;
+ }
+ }
+
+ if (inconsistent)
+ rai->rainconsistent++;
+
+ done:
+ free_ndopts(&ndopts);
+ return;
+}
+
+/* return a non-zero value if the received prefix is inconsitent with ours */
+static int
+prefix_check(struct nd_opt_prefix_info *pinfo,
+ struct rainfo *rai, struct sockaddr_in6 *from)
+{
+ u_int32_t preferred_time, valid_time;
+ struct prefix *pp;
+ int inconsistent = 0;
+ char ntopbuf[INET6_ADDRSTRLEN], prefixbuf[INET6_ADDRSTRLEN];
+ struct timeval now;
+
+#if 0 /* impossible */
+ if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION)
+ return(0);
+#endif
+
+ /*
+ * log if the adveritsed prefix has link-local scope(sanity check?)
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&pinfo->nd_opt_pi_prefix)) {
+ infolog("<%s> link-local prefix %s/%d is advertised "
+ "from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->ifname);
+ }
+
+ if ((pp = find_prefix(rai, &pinfo->nd_opt_pi_prefix,
+ pinfo->nd_opt_pi_prefix_len)) == NULL) {
+ infolog("<%s> prefix %s/%d from %s on %s is not in our list",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->ifname);
+ return(0);
+ }
+
+ preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time);
+ if (pp->pltimeexpire) {
+ /*
+ * The lifetime is decremented in real time, so we should
+ * compare the expiration time.
+ * (RFC 2461 Section 6.2.7.)
+ * XXX: can we really expect that all routers on the link
+ * have synchronized clocks?
+ */
+ gettimeofday(&now, NULL);
+ preferred_time += now.tv_sec;
+
+ if (!pp->timer && rai->clockskew &&
+ preferred_time - pp->pltimeexpire > rai->clockskew) {
+ infolog("<%s> preferred lifetime for %s/%d"
+ " (decr. in real time) inconsistent on %s:"
+ " %d from %s, %ld from us",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->pltimeexpire);
+ inconsistent++;
+ }
+ } else if (!pp->timer && preferred_time != pp->preflifetime) {
+ infolog("<%s> preferred lifetime for %s/%d"
+ " inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->preflifetime);
+ }
+
+ valid_time = ntohl(pinfo->nd_opt_pi_valid_time);
+ if (pp->vltimeexpire) {
+ gettimeofday(&now, NULL);
+ valid_time += now.tv_sec;
+
+ if (!pp->timer && rai->clockskew &&
+ valid_time - pp->vltimeexpire > rai->clockskew) {
+ infolog("<%s> valid lifetime for %s/%d"
+ " (decr. in real time) inconsistent on %s:"
+ " %d from %s, %ld from us",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->vltimeexpire);
+ inconsistent++;
+ }
+ } else if (!pp->timer && valid_time != pp->validlifetime) {
+ infolog("<%s> valid lifetime for %s/%d"
+ " inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, valid_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->validlifetime);
+ inconsistent++;
+ }
+
+ return(inconsistent);
+}
+
+struct prefix *
+find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen)
+{
+ struct prefix *pp;
+ int bytelen, bitlen;
+ u_char bitmask;
+
+ for (pp = rai->prefix.next; pp != &rai->prefix; pp = pp->next) {
+ if (plen != pp->prefixlen)
+ continue;
+ bytelen = plen / 8;
+ bitlen = plen % 8;
+ bitmask = 0xff << (8 - bitlen);
+ if (memcmp((void *)prefix, (void *)&pp->prefix, bytelen))
+ continue;
+ if (bitlen == 0 ||
+ ((prefix->s6_addr[bytelen] & bitmask) ==
+ (pp->prefix.s6_addr[bytelen] & bitmask))) {
+ return(pp);
+ }
+ }
+
+ return(NULL);
+}
+
+/* check if p0/plen0 matches p1/plen1; return 1 if matches, otherwise 0. */
+int
+prefix_match(struct in6_addr *p0, int plen0,
+ struct in6_addr *p1, int plen1)
+{
+ int bytelen, bitlen;
+ u_char bitmask;
+
+ if (plen0 < plen1)
+ return(0);
+ bytelen = plen1 / 8;
+ bitlen = plen1 % 8;
+ bitmask = 0xff << (8 - bitlen);
+ if (memcmp((void *)p0, (void *)p1, bytelen))
+ return(0);
+ if (bitlen == 0 ||
+ ((p0->s6_addr[bytelen] & bitmask) ==
+ (p1->s6_addr[bytelen] & bitmask))) {
+ return(1);
+ }
+
+ return(0);
+}
+
+static int
+nd6_options(struct nd_opt_hdr *hdr, int limit,
+ union nd_opts *ndopts, u_int32_t optflags)
+{
+ int optlen = 0;
+
+ for (; limit > 0; limit -= optlen) {
+ if (limit < sizeof(struct nd_opt_hdr)) {
+ infolog("<%s> short option header", __func__);
+ goto bad;
+ }
+
+ hdr = (struct nd_opt_hdr *)((caddr_t)hdr + optlen);
+ if (hdr->nd_opt_len == 0) {
+ infolog("<%s> bad ND option length(0) (type = %d)",
+ __func__, hdr->nd_opt_type);
+ goto bad;
+ }
+ optlen = hdr->nd_opt_len << 3;
+ if (optlen > limit) {
+ infolog("<%s> short option", __func__);
+ goto bad;
+ }
+
+ if (hdr->nd_opt_type > ND_OPT_MTU) {
+ infolog("<%s> unknown ND option(type %d)",
+ __func__, hdr->nd_opt_type);
+ continue;
+ }
+
+ if ((ndopt_flags[hdr->nd_opt_type] & optflags) == 0) {
+ infolog("<%s> unexpected ND option(type %d)",
+ __func__, hdr->nd_opt_type);
+ continue;
+ }
+
+ /*
+ * Option length check. Do it here for all fixed-length
+ * options.
+ */
+ if ((hdr->nd_opt_type == ND_OPT_MTU &&
+ (optlen != sizeof(struct nd_opt_mtu))) ||
+ ((hdr->nd_opt_type == ND_OPT_PREFIX_INFORMATION &&
+ optlen != sizeof(struct nd_opt_prefix_info)))) {
+ infolog("<%s> invalid option length",
+ __func__);
+ continue;
+ }
+
+ switch (hdr->nd_opt_type) {
+ case ND_OPT_TARGET_LINKADDR:
+ case ND_OPT_REDIRECTED_HEADER:
+ break; /* we don't care about these options */
+ case ND_OPT_SOURCE_LINKADDR:
+ case ND_OPT_MTU:
+ if (ndopts->nd_opt_array[hdr->nd_opt_type]) {
+ infolog("<%s> duplicated ND option (type = %d)",
+ __func__, hdr->nd_opt_type);
+ }
+ ndopts->nd_opt_array[hdr->nd_opt_type] = hdr;
+ break;
+ case ND_OPT_PREFIX_INFORMATION:
+ {
+ struct nd_optlist *pfxlist;
+
+ if (ndopts->nd_opts_pi == 0) {
+ ndopts->nd_opts_pi =
+ (struct nd_opt_prefix_info *)hdr;
+ continue;
+ }
+ if ((pfxlist = malloc(sizeof(*pfxlist))) == NULL) {
+ errorlog("<%s> can't allocate memory",
+ __func__);
+ 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];
+
+ rcvcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ rcvcmsgbuf = (u_char *)malloc(rcvcmsgbuflen);
+ if (rcvcmsgbuf == NULL) {
+ errorlog("<%s> not enough core", __func__);
+ exit(1);
+ }
+
+ sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ sndcmsgbuf = (u_char *)malloc(sndcmsgbuflen);
+ if (sndcmsgbuf == NULL) {
+ errorlog("<%s> not enough core", __func__);
+ exit(1);
+ }
+
+ if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ errorlog("<%s> socket: %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+
+ (void) setsockopt(sock, SOL_SOCKET, SO_TRAFFIC_CLASS,
+ (void *)&so_traffic_class, sizeof (so_traffic_class));
+
+ /* specify to tell receiving interface */
+ on = 1;
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) < 0) {
+ errorlog("<%s> IPV6_RECVPKTINFO: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+#else /* old adv. API */
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0) {
+ errorlog("<%s> IPV6_PKTINFO: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+#endif
+
+ on = 1;
+ /* specify to tell value of hoplimit field of received IP6 hdr */
+#ifdef IPV6_RECVHOPLIMIT
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ errorlog( "<%s> IPV6_RECVHOPLIMIT: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+#else /* old adv. API */
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ errorlog("<%s> IPV6_HOPLIMIT: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+#endif
+
+ on = 1;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_DONTFRAG, &on,
+ sizeof(on)) < 0) {
+ errorlog("<%s> IPV6_DONTFRAG: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
+ if (accept_rr)
+ ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt);
+ if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0) {
+ errorlog("<%s> IICMP6_FILTER: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * join all routers multicast address on each advertising interface.
+ */
+ if (inet_pton(AF_INET6, ALLROUTERS_LINK,
+ &mreq.ipv6mr_multiaddr.s6_addr)
+ != 1) {
+ errorlog("<%s> inet_pton failed(library bug?)",
+ __func__);
+ exit(1);
+ }
+ while (ra) {
+ mreq.ipv6mr_interface = ra->ifindex;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
+ sizeof(mreq)) < 0) {
+ errorlog("<%s> IPV6_JOIN_GROUP(link) on %s: %s",
+ __func__, ra->ifname, strerror(errno));
+ exit(1);
+ }
+ ra = ra->next;
+ }
+
+ /*
+ * When attending router renumbering, join all-routers site-local
+ * multicast group.
+ */
+ if (accept_rr) {
+ if (inet_pton(AF_INET6, ALLROUTERS_SITE,
+ &in6a_site_allrouters) != 1) {
+ errorlog("<%s> inet_pton failed(library bug?)",
+ __func__);
+ exit(1);
+ }
+ mreq.ipv6mr_multiaddr = in6a_site_allrouters;
+ if (mcastif) {
+ if ((mreq.ipv6mr_interface = if_nametoindex(mcastif))
+ == 0) {
+ errorlog("<%s> invalid interface: %s",
+ __func__, mcastif);
+ exit(1);
+ }
+ } else
+ mreq.ipv6mr_interface = ralist->ifindex;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq, sizeof(mreq)) < 0) {
+ errorlog("<%s> IPV6_JOIN_GROUP(site) on %s: %s",
+ __func__,
+ mcastif ? mcastif : ralist->ifname,
+ strerror(errno));
+ exit(1);
+ }
+ }
+
+ /* initialize msghdr for receiving packets */
+ rcviov[0].iov_base = (caddr_t)answer;
+ rcviov[0].iov_len = sizeof(answer);
+ rcvmhdr.msg_name = (caddr_t)&rcvfrom;
+ rcvmhdr.msg_namelen = sizeof(rcvfrom);
+ rcvmhdr.msg_iov = rcviov;
+ rcvmhdr.msg_iovlen = 1;
+ rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
+ rcvmhdr.msg_controllen = rcvcmsgbuflen;
+
+ /* initialize msghdr for sending packets */
+ sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmhdr.msg_iov = sndiov;
+ sndmhdr.msg_iovlen = 1;
+ sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
+ sndmhdr.msg_controllen = sndcmsgbuflen;
+
+ return;
+}
+
+/* open a routing socket to watch the routing table */
+static void
+rtsock_open()
+{
+ if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
+ errorlog("<%s> socket: %s", __func__, strerror(errno));
+ exit(1);
+ }
+}
+
+struct rainfo *
+if_indextorainfo(int idx)
+{
+ struct rainfo *rai = ralist;
+
+ for (rai = ralist; rai; rai = rai->next) {
+ if (rai->ifindex == idx)
+ return(rai);
+ }
+
+ return(NULL); /* search failed */
+}
+
+static void
+ra_output(rainfo)
+struct rainfo *rainfo;
+{
+ int i;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi;
+ struct soliciter *sol, *nextsol;
+ struct if_msghdr *ifm = get_interface_entry(rainfo->ifindex);
+
+ if (ifm == NULL ||
+ (ifm->ifm_flags & IFF_UP) == 0) {
+ debuglog("<%s> %s is not up, skip sending RA",
+ __func__, rainfo->ifname);
+ return;
+ }
+
+ make_packet(rainfo); /* XXX: inefficient */
+
+ 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));
+ }
+
+ debuglog("<%s> send RA on %s, # of waitings = %d",
+ __func__, rainfo->ifname, rainfo->waiting);
+
+ i = sendmsg(sock, &sndmhdr, 0);
+
+ if (i < 0 || i != rainfo->ra_datalen) {
+ if (i < 0) {
+ errorlog("<%s> sendmsg on %s: %s",
+ __func__, rainfo->ifname,
+ strerror(errno));
+ }
+ }
+ /* update counter */
+ if (rainfo->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS)
+ rainfo->initcounter++;
+ rainfo->raoutput++;
+
+ /*
+ * unicast advertisements
+ * XXX commented out. reason: though spec does not forbit it, unicast
+ * advert does not really help
+ */
+ for (sol = rainfo->soliciter; sol; sol = nextsol) {
+ nextsol = sol->next;
+
+ sol->next = NULL;
+ free(sol);
+ }
+ rainfo->soliciter = NULL;
+
+ /* update timestamp */
+ gettimeofday(&rainfo->lastsent, NULL);
+
+ /* reset waiting conter */
+ rainfo->waiting = 0;
+}
+
+/* process RA timer */
+struct rtadvd_timer *
+ra_timeout(void *data)
+{
+ struct rainfo *rai = (struct rainfo *)data;
+
+#ifdef notyet
+ /* if necessary, reconstruct the packet. */
+#endif
+
+ debuglog("<%s> RA timer on %s is expired",
+ __func__, rai->ifname);
+
+ ra_output(rai);
+
+ return(rai->timer);
+}
+
+/* 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 (RFC2461 6.2.4).
+ */
+ interval = rai->mininterval;
+ interval += random() % (rai->maxinterval - rai->mininterval);
+
+ /*
+ * The first advertisement is sent as soon as rtadvd starts up
+ * and for the next 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 = rai->initcounter == 0 ? 0 : interval;
+ tm->tv_usec = 0;
+
+ debuglog("<%s> RA timer on %s is set to %ld:%ld",
+ __func__, rai->ifname,
+ (long int)tm->tv_sec, (long int)tm->tv_usec);
+
+ return;
+}
diff --git a/network_cmds/rtadvd.tproj/rtadvd.conf b/network_cmds/rtadvd.tproj/rtadvd.conf
new file mode 100644
index 0000000..6d109b2
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/rtadvd.conf
@@ -0,0 +1,20 @@
+# $KAME: rtadvd.conf,v 1.13 2003/06/25 03:45:21 itojun Exp $
+#
+# Note: All of the following parameters have default values defined
+# in specifications, and hence you usually do not have to set them
+# by hand unless you need special non-default values.
+#
+# You even do not need to create the configuration file. rtadvd
+# would usually work well without a configuration file.
+# See also: rtadvd(8)
+
+# per-interface definitions.
+# Mainly IPv6 prefixes are configured in this part. However, rtadvd
+# automatically learns appropriate prefixes from the kernel's routing
+# table, and advertises the prefixes, so you don't have to configure
+# this part, either.
+# If you don't want the automatic advertisement, (uncomment and) configure
+# this part by hand, and then invoke rtadvd with the -s option.
+
+#ef0:\
+# :addr="3ffe:501:ffff:1000::":prefixlen#64:
diff --git a/network_cmds/rtadvd.tproj/rtadvd.conf.5 b/network_cmds/rtadvd.tproj/rtadvd.conf.5
new file mode 100644
index 0000000..87978fc
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/rtadvd.conf.5
@@ -0,0 +1,509 @@
+.\" $KAME: rtadvd.conf.5,v 1.50 2005/01/14 05:30:59 jinmei Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd February 24, 2012
+.Dt RTADVD.CONF 5
+.Os
+.Sh NAME
+.Nm rtadvd.conf
+.Nd config file for router advertisement daemon
+.Sh DESCRIPTION
+This file describes how the router advertisement packets must be constructed
+for each of the interfaces.
+.Pp
+As described in
+.Xr rtadvd 8 ,
+you do not have to set this configuration file up at all,
+unless you need some special configurations.
+You may even omit the file as a whole.
+In such cases, the
+.Nm rtadvd
+daemon will automatically configure itself using default values
+specified in the specification.
+.Pp
+It obeys the famous
+.Xr termcap 5
+file format.
+Each line in the file describes a network interface.
+Fields are separated by a colon
+.Pq Sq \&: ,
+and each field contains one capability description.
+Lines may be concatenated by the
+.Sq \e
+character.
+The comment marker is the
+.Sq \&#
+character.
+.Sh CAPABILITIES
+Capabilities describe the value to be filled into ICMPv6 router
+advertisement messages and to control
+.Xr rtadvd 8
+behavior.
+Therefore, you are encouraged to read IETF neighbor discovery documents
+if you would like to modify the sample configuration file.
+.Pp
+Note that almost all items have default values.
+If you omit an item, the default value of the item will be used.
+.Pp
+There are two items which control the interval of sending router advertisements.
+These items can be omitted, then
+.Nm rtadvd
+will use the default values.
+.Bl -tag -width indent
+.It Cm \&maxinterval
+(num) The maximum time allowed between sending unsolicited
+multicast router advertisements
+.Pq unit: seconds .
+The default value is 600.
+Its value must be no less than 4 seconds
+and no greater than 1800 seconds.
+.It Cm \&mininterval
+(num) The minimum time allowed between sending unsolicited multicast
+router advertisements
+.Pq unit: seconds .
+The default value is the one third of value of
+.Cm maxinterval .
+Its value must be no less than 3 seconds and no greater than .75 *
+the value of
+.Cm maxinterval .
+.El
+.Pp
+The following items are for ICMPv6 router advertisement message
+header.
+These items can be omitted, then
+.Nm rtadvd
+will use the default values.
+.Bl -tag -width indent
+.It Cm \&chlim
+(num) The value for Cur Hop Limit field.
+The default value is 64.
+.It Cm \&raflags
+(str or num) A 8-bit flags field in router advertisement message header.
+This field can be specified either as a case-sensitive string or as an
+integer.
+A string consists of characters each of which corresponds to a
+particular flag bit(s).
+An integer should be the logical OR of all enabled bits.
+Bit 7
+.Po
+.Li 'm' or 0x80
+.Pc
+means Managed address configuration flag bit,
+and Bit 6
+.Po
+.Li 'o' or 0x40
+.Pc
+means Other stateful configuration flag bit.
+Bit 4
+.Po
+.Li 0x10
+.Pc
+and Bit 3
+.Po
+.Li 0x08
+.Pc
+are used to encode router preference.
+Bits 01
+.Po
+or 'h'
+.Pc
+means high, 00 means medium, and 11
+.Po
+or 'l'
+.Pc
+means low.
+Bits 10 is reserved, and must not be specified.
+There is no character to specify the medium preference explicitly.
+The default value of the entire flag is 0
+.Po
+or a null string,
+.Pc
+which means no additional
+configuration methods, and the medium router preference.
+.It Cm \&rltime
+(num) Router lifetime field
+.Pq unit: seconds .
+The value must be either zero or between
+the value of
+.Cm maxinterval
+and 9000.
+When
+.Nm rtadvd
+runs on a host, this value must explicitly set 0 on all the
+advertising interfaces as described in
+.Xr rtadvd 8 .
+The default value is 1800.
+.It Cm \&rtime
+(num) Reachable time field
+.Pq unit: milliseconds .
+The default value is 0, which means unspecified by this router.
+.It Cm \&retrans
+(num) Retrans Timer field
+.Pq unit: milliseconds .
+The default value is 0, which means unspecified by this router.
+.El
+.Pp
+The following items are for ICMPv6 prefix information option,
+which will be attached to router advertisement header.
+These items can be omitted, then
+.Nm rtadvd
+will automatically get appropriate prefixes from the kernel's routing table,
+and advertise the prefixes with the default parameters.
+Keywords other than
+.Cm clockskew
+can be augmented with a number, like
+.Dq Li prefix2 ,
+to specify multiple prefixes.
+.Bl -tag -width indent
+.It Cm \&clockskew
+(num) Time skew to adjust link propagation delays and clock skews
+between routers on the link
+.Pq unit: seconds .
+This value is used in consistency check for locally-configured and
+advertised prefix lifetimes, and has its meaning when the local router
+configures a prefix on the link with a lifetime that decrements in
+real time.
+If the value is 0, it means the consistency check will be skipped
+for such prefixes.
+The default value is 0.
+.It Cm \&prefixlen
+(num) Prefix length field.
+The default value is 64.
+.It Cm \&pinfoflags
+(str or num) A 8-bit flags field in prefix information option.
+This field can be specified either as a case-sensitive string or as an
+integer.
+A string consists of characters each of which corresponds to a
+particular flag bit(s).
+An integer should be the logical OR of all enabled bits.
+Bit 7
+.Po
+.Li 'l' or 0x80
+.Pc
+means On-link flag bit,
+and Bit 6
+.Po
+.Li 'a' or 0x40
+.Pc
+means Autonomous address-configuration flag bit.
+The default value is "la" or 0xc0, i.e., both bits are set.
+.It Cm \&addr
+(str) The address filled into Prefix field.
+Since
+.Dq \&:
+is used for
+.Xr termcap 5
+file format as well as IPv6 numeric address, the field MUST be quoted by
+doublequote character.
+.It Cm \&vltime
+(num) Valid lifetime field
+.Pq unit: seconds .
+The default value is 2592000 (30 days).
+.It Cm \&vltimedecr
+(bool) This item means the advertised valid lifetime will decrement
+in real time, which is disabled by default.
+.It Cm \&pltime
+(num) Preferred lifetime field
+.Pq unit: seconds .
+The default value is 604800 (7 days).
+.It Cm \&pltimedecr
+(bool) This item means the advertised preferred lifetime will decrement
+in real time, which is disabled by default.
+.El
+.Pp
+The following item is for ICMPv6 MTU option,
+which will be attached to router advertisement header.
+This item can be omitted, then
+.Nm rtadvd
+will use the default value.
+.Bl -tag -width indent
+.It Cm \&mtu
+(num or str) MTU (maximum transmission unit) field.
+If 0 is specified, it means that the option will not be included.
+The default value is 0.
+If the special string
+.Dq auto
+is specified for this item, MTU option will be included and its value
+will be set to the interface MTU automatically.
+.El
+.Pp
+The following item controls ICMPv6 source link-layer address option,
+which will be attached to router advertisement header.
+As noted above, you can just omit the item, then
+.Nm rtadvd
+will use the default value.
+.Bl -tag -width indent
+.It Cm \&nolladdr
+(bool) By default
+.Po
+if
+.Cm \&nolladdr
+is not specified
+.Pc ,
+.Xr rtadvd 8
+will try to get link-layer address for the interface from the kernel,
+and attach that in source link-layer address option.
+If this capability exists,
+.Xr rtadvd 8
+will not attach source link-layer address option to
+router advertisement packets.
+.El
+.Pp
+The following item controls ICMPv6 home agent information option,
+which was defined with mobile IPv6 support.
+It will be attached to router advertisement header just like other options do.
+.Bl -tag -width indent
+.It Cm \&hapref
+(num) Specifies home agent preference.
+If set to non-zero,
+.Cm \&hatime
+must be present as well.
+.It Cm \&hatime
+(num) Specifies home agent lifetime.
+.El
+.Pp
+When mobile IPv6 support is turned on for
+.Xr rtadvd 8 ,
+advertisement interval option will be attached to router advertisement
+packet, by configuring
+.Cm \&maxinterval
+explicitly.
+.Pp
+The following items are for ICMPv6 route information option,
+which will be attached to router advertisement header.
+These items are optional.
+Each items can be augmented with number, like
+.Dq Li rtplen2 ,
+to specify multiple routes.
+.Bl -tag -width indent
+.It Cm \&rtprefix
+(str) The prefix filled into the Prefix field of route information option.
+Since
+.Dq \&:
+is used for
+.Xr termcap 5
+file format as well as IPv6 numeric address, the field MUST be quoted by
+doublequote character.
+.It Cm \&rtplen
+(num) Prefix length field in route information option.
+The default value is 64.
+.It Cm \&rtflags
+(str or num) A 8-bit flags field in route information option.
+Currently only the preference values are defined.
+The notation is same as that of the raflags field.
+Bit 4
+.Po
+.Li 0x10
+.Pc
+and
+Bit 3
+.Po
+.Li 0x08
+.Pc
+are used to encode the route preference for the route.
+The default value is 0x00, i.e., medium preference.
+.It Cm \&rtltime
+(num) route lifetime field in route information option.
+.Pq unit: seconds .
+Since the specification does not define the default value of this
+item, the value for this item should be specified by hand.
+However,
+.Nm rtadvd
+allows this item to be unspecified, and uses the router lifetime
+as the default value in such a case, just for compatibility with an
+old version of the program.
+.El
+.Pp
+In the above list, each keyword beginning with
+.Dq Li rt
+could be replaced with the one beginning with
+.Dq Li rtr
+for backward compatibility reason.
+For example,
+.Cm rtrplen
+is accepted instead of
+.Cm rtplen .
+However, keywords that start with
+.Dq Li rtr
+have basically been obsoleted, and should not be used any more.
+.Pp
+You can also refer one line from another by using
+.Cm tc
+capability.
+See
+.Xr termcap 5
+for details on the capability.
+.Pp
+The following items are for the ICMPv6 recursive DNS server (RDNSS) option,
+which will be attached to the router advertisement header.
+.Bl -tag -width indent
+.It Cm \&rdnssaddrs
+(num) Number of recursive DNS server addresses.
+Its default is 0, so it must explicitly be set to positive values
+if you want to specify any DNS server address.
+If its value is 0, no DNS server information is sent.
+If its value is more than 1, you must specify the index of the address
+for the
+.Cm rdnssaddr
+item below.
+Indices vary from 0 to N-1, where N is the
+value of
+.Cm rdnssaddrs .
+Each index shall follow the name of
+.Cm rdnssaddr ,
+e.g.,
+.Dq rdnssaddr0 .
+.It Cm \&rdnssaddr
+(str) The IPv6 address of the recursive DNS server.
+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
+.Cm rdnssaddrs
+is more than 0.
+.It Cm \&rdnsslifetime
+(num) The lifetime field in RDNSS option.
+(unit: seconds).
+The default value is 2 * the value of
+.Cm \&maxinterval ,
+which is also the maximum value that should be set. The minimum value is
+.Cm \&maxinterval .
+If you specify a value outside of this range, a message is logged.
+.El
+.Pp
+The following items are for the ICMPv6 DNS search list (DNSSL) option,
+which will be attached to the router advertisement header.
+.Bl -tag -width indent
+.It Cm \&dnssldomains
+(num) Number of DNS search domains.
+Its default is 0, so it must explicitly be set to positive values
+if you want to specify any DNS search domains.
+If its value is 0, no DNS search domain information is sent.
+If its value is more than 1, you must specify the index of the search domain
+for the
+.Cm dnssldomain
+item below.
+Indices vary from 0 to N-1, where N is the
+value of
+.Cm dnssldomains .
+Each index shall follow the name of
+.Cm dnssldomain ,
+e.g.,
+.Dq dnssldomain0 .
+.It Cm \&dnssldomain
+(str) The DNS search domain.
+This field cannot be
+omitted if the value of
+.Cm dnssldomains
+is more than 0.
+.It Cm \&dnssllifetime
+(num) The lifetime field in the DNSSL option.
+(unit: seconds).
+The default value is 2 * the value of
+.Cm \&maxinterval ,
+which is also the maximum value that should be set. The minimum value is
+.Cm \&maxinterval .
+If you specify a value outside of this range, a message is logged.
+.El
+.Sh EXAMPLES
+As presented above, all of the advertised parameters have default values
+defined in specifications, and hence you usually do not have to set them
+by hand, unless you need special non-default values.
+It can cause interoperability problem if you use an ill-configured
+parameter.
+.Pp
+To override a configuration parameter, you can specify the parameter alone.
+With the following configuration,
+.Xr rtadvd 8
+overrides the router lifetime parameter for the
+.Li ne0
+interface.
+.Bd -literal -offset
+ne0:\\
+ :rltime#0:
+.Ed
+.Pp
+The following example manually configures prefixes advertised from the
+.Li ef0
+interface.
+The configuration must be used with the
+.Fl s
+option to
+.Xr rtadvd 8 .
+.Bd -literal -offset
+ef0:\\
+ :addr="3ffe:501:ffff:1000::":prefixlen#64:
+.Ed
+.Pp
+The following example presents the default values in an explicit manner.
+The configuration is provided just for reference purposes;
+YOU DO NOT NEED TO HAVE IT AT ALL.
+.Bd -literal -offset
+default:\\
+ :chlim#64:raflags#0:rltime#1800:rtime#0:retrans#0:\\
+ :pinfoflags="la":vltime#2592000:pltime#604800:mtu#0:
+ef0:\\
+ :addr="3ffe:501:ffff:1000::":prefixlen#64:tc=default:
+.Ed
+.Sh SEE ALSO
+.Xr termcap 5 ,
+.Xr rtadvd 8 ,
+.Xr rtsol 8
+.Rs
+.%A Thomas Narten
+.%A Erik Nordmark
+.%A W. A. Simpson
+.%T Neighbor Discovery for IP version 6 (IPv6)
+.%R RFC 2461
+.Re
+.Rs
+.%A Richard Draves
+.%T Default Router Preferences and More-Specific Routes
+.%R draft-ietf-ipngwg-router-selection-xx.txt
+.Re
+.Rs
+.%A J. Jeong
+.%A S. Park
+.%A L. Beloeil
+.%A S. Madanapalli
+.%T IPv6 Router Advertisement Option for DNS Configuration
+.%R RFC 5006
+.Re
+.Sh HISTORY
+The
+.Xr rtadvd 8
+and the configuration file
+.Nm
+first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/network_cmds/rtadvd.tproj/rtadvd.h b/network_cmds/rtadvd.tproj/rtadvd.h
new file mode 100644
index 0000000..504f398
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/rtadvd.h
@@ -0,0 +1,205 @@
+/* $KAME: rtadvd.h,v 1.26 2003/08/05 12:34:23 itojun Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "rtadvd_logging.h"
+
+#define ELM_MALLOC(p, error_action) \
+ do { \
+ p = malloc(sizeof(*p)); \
+ if (p == NULL) { \
+ errorlog("<%s> malloc failed: %s", \
+ __func__, strerror(errno)); \
+ error_action; \
+ } \
+ memset(p, 0, sizeof(*p)); \
+ } while(0)
+
+#define ROUTEINFO 1
+
+#define ALLNODES "ff02::1"
+#define ALLROUTERS_LINK "ff02::2"
+#define ALLROUTERS_SITE "ff05::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 1
+#define MIN_DELAY_BETWEEN_RAS 3
+#define MAX_RA_DELAY_TIME 500000 /* usec */
+
+#define PREFIX_FROM_KERNEL 1
+#define PREFIX_FROM_CONFIG 2
+#define PREFIX_FROM_DYNAMIC 3
+
+struct prefix {
+ struct prefix *next; /* forward link */
+ struct prefix *prev; /* previous link */
+
+ struct rainfo *rainfo; /* back pointer to the interface */
+
+ struct rtadvd_timer *timer; /* expiration timer. used when a prefix
+ * derived from the kernel is deleted.
+ */
+
+ u_int32_t validlifetime; /* AdvValidLifetime */
+ long vltimeexpire; /* expiration of vltime; decrement case only */
+ u_int32_t preflifetime; /* AdvPreferredLifetime */
+ long pltimeexpire; /* expiration of pltime; decrement case only */
+ u_int onlinkflg; /* bool: AdvOnLinkFlag */
+ u_int autoconfflg; /* bool: AdvAutonomousFlag */
+ int prefixlen;
+ int origin; /* from kernel or config */
+ struct in6_addr prefix;
+};
+
+#ifdef ROUTEINFO
+struct rtinfo {
+ struct rtinfo *prev; /* previous link */
+ struct rtinfo *next; /* forward link */
+
+ u_int32_t ltime; /* route lifetime */
+ u_int rtpref; /* route preference */
+ int prefixlen;
+ struct in6_addr prefix;
+};
+#endif
+
+struct soliciter {
+ struct soliciter *next;
+ struct sockaddr_in6 addr;
+};
+
+struct rdnss {
+ struct rdnss *next; /* forward link */
+ struct rdnss *prev; /* previous link */
+
+ struct in6_addr addr;
+};
+
+struct dnssl {
+ struct dnssl *next;
+ struct dnssl *prev;
+
+ char domain[1];
+};
+
+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 latest 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 */
+
+ int rtpref; /* router preference */
+ 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 */
+ long clockskew; /* used for consisitency check of lifetimes */
+
+#ifdef ROUTEINFO
+ struct rtinfo route; /* route information option (link head) */
+ int routes; /* number of route information options */
+#endif
+
+ /* Recursive DNS Servers RFC5006 */
+ struct rdnss rdnss_list;
+ int rdnss_length;
+ u_int32_t rdnss_lifetime;
+
+ /* DNS Search List RFC6106 */
+ struct dnssl dnssl_list;
+ int dnssl_length;
+ u_int32_t dnssl_lifetime;
+ u_int32_t dnssl_option_length;
+
+ /* Captive Portal RFC 7710 */
+ char * capport;
+ u_int32_t capport_length;
+ u_int32_t capport_option_length;
+
+ /* actual RA packet data and its length */
+ size_t ra_datalen;
+ u_char *ra_data;
+
+ /* statistics */
+ u_quad_t raoutput; /* number of RAs sent */
+ u_quad_t rainput; /* number of RAs received */
+ u_quad_t rainconsistent; /* number of RAs inconsistent with ours */
+ u_quad_t rsinput; /* number of RSs received */
+
+ /* info about soliciter */
+ struct soliciter *soliciter; /* recent solication source */
+};
+
+struct rtadvd_timer *ra_timeout(void *);
+void ra_timer_update(void *, struct timeval *);
+
+int prefix_match(struct in6_addr *, int, struct in6_addr *, int);
+struct rainfo *if_indextorainfo(int);
+struct prefix *find_prefix(struct rainfo *, struct in6_addr *, int);
+
+extern struct in6_addr in6a_site_allrouters;
diff --git a/network_cmds/rtadvd.tproj/rtadvd_logging.c b/network_cmds/rtadvd.tproj/rtadvd_logging.c
new file mode 100644
index 0000000..90dec08
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/rtadvd_logging.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019 Apple Inc. All rights reserved.
+ *
+ * This document is the property of Apple Inc.
+ * It is considered confidential and proprietary.
+ *
+ * This document may not be reproduced or transmitted in any form,
+ * in whole or in part, without the express written permission of
+ * Apple Inc.
+ */
+#include <assert.h>
+#include <os/log_private.h>
+
+#define kRtadvdLoggerID "com.apple.rtadvd"
+static os_log_t rtadvdLogger = NULL; /* Handle for Logger */
+
+static boolean_t rtadvd_logger_create(void);
+
+static boolean_t
+rtadvd_logger_create(void)
+{
+ assert(rtadvdLogger == NULL);
+ rtadvdLogger = os_log_create(kRtadvdLoggerID, "daemon");
+
+ if (rtadvdLogger == NULL) {
+ os_log_error(OS_LOG_DEFAULT, "Couldn't create os log object");
+ }
+
+ return (rtadvdLogger != NULL);
+}
+
+void
+rtadvdLog(int level, const char *format, ...)
+{
+ va_list args;
+
+ if (rtadvdLogger == NULL && !rtadvd_logger_create()) {
+ return;
+ }
+
+ va_start(args, format);
+ os_log_with_args(rtadvdLogger, level, format, args, __builtin_return_address(0));
+ va_end(args);
+ return;
+}
diff --git a/network_cmds/rtadvd.tproj/rtadvd_logging.h b/network_cmds/rtadvd.tproj/rtadvd_logging.h
new file mode 100644
index 0000000..11273e6
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/rtadvd_logging.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019 Apple Inc. All rights reserved.
+ *
+ * This document is the property of Apple Inc.
+ * It is considered confidential and proprietary.
+ *
+ * This document may not be reproduced or transmitted in any form,
+ * in whole or in part, without the express written permission of
+ * Apple Inc.
+ */
+#include <os/log.h>
+extern void rtadvdLog(int level, const char *format, ...);
+
+#define errorlog(__format, ...) \
+rtadvdLog(OS_LOG_TYPE_DEFAULT, __format, ## __VA_ARGS__)
+
+#define noticelog(__format, ...) \
+rtadvdLog(OS_LOG_TYPE_DEFAULT, __format, ## __VA_ARGS__)
+
+#define infolog(__format, ...) \
+rtadvdLog(OS_LOG_TYPE_INFO, __format, ## __VA_ARGS__)
+
+#define debuglog(__format, ...) \
+rtadvdLog(OS_LOG_TYPE_DEBUG, __format, ## __VA_ARGS__)
diff --git a/network_cmds/rtadvd.tproj/timer.c b/network_cmds/rtadvd.tproj/timer.c
new file mode 100644
index 0000000..3134fc0
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/timer.c
@@ -0,0 +1,205 @@
+/* $KAME: timer.c,v 1.9 2002/06/10 19:59:47 itojun Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/time.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <search.h>
+#include "timer.h"
+#include "rtadvd_logging.h"
+
+static struct rtadvd_timer timer_head;
+
+#define MILLION 1000000
+#define TIMEVAL_EQUAL(t1,t2) ((t1)->tv_sec == (t2)->tv_sec &&\
+ (t1)->tv_usec == (t2)->tv_usec)
+
+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(struct rtadvd_timer *(*timeout)(void *),
+ void (*update)(void *, struct timeval *),
+ void *timeodata, void *updatedata)
+{
+ struct rtadvd_timer *newtimer;
+
+ if ((newtimer = malloc(sizeof(*newtimer))) == NULL) {
+ errorlog("<%s> can't allocate memory", __func__);
+ exit(1);
+ }
+
+ memset(newtimer, 0, sizeof(*newtimer));
+
+ if (timeout == NULL) {
+ errorlog("<%s> timeout function unspecified", __func__);
+ exit(1);
+ }
+ newtimer->expire = timeout;
+ newtimer->update = update;
+ newtimer->expire_data = timeodata;
+ newtimer->update_data = updatedata;
+ newtimer->tm = tm_max;
+
+ /* link into chain */
+ insque(newtimer, &timer_head);
+
+ return(newtimer);
+}
+
+void
+rtadvd_remove_timer(struct rtadvd_timer **timer)
+{
+ remque(*timer);
+ free(*timer);
+ *timer = NULL;
+}
+
+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 expires,
+ * 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, *tm_next;
+
+ gettimeofday(&now, NULL);
+
+ timer_head.tm = tm_max;
+
+ for (tm = timer_head.next; tm != &timer_head; tm = tm_next) {
+ tm_next = tm->next;
+
+ if (TIMEVAL_LEQ(tm->tm, now)) {
+ if (((*tm->expire)(tm->expire_data) == NULL))
+ continue; /* the timer was removed */
+ if (tm->update)
+ (*tm->update)(tm->update_data, &tm->tm);
+ TIMEVAL_ADD(&tm->tm, &now, &tm->tm);
+ }
+
+ if (TIMEVAL_LT(tm->tm, timer_head.tm))
+ timer_head.tm = tm->tm;
+ }
+
+ if (TIMEVAL_EQUAL(&tm_max, &timer_head.tm)) {
+ /* no need to timeout */
+ return(NULL);
+ } else 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)) {
+ debuglog("<%s> a timer must be expired, but not yet",
+ __func__);
+ 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/network_cmds/rtadvd.tproj/timer.h b/network_cmds/rtadvd.tproj/timer.h
new file mode 100644
index 0000000..78314b7
--- /dev/null
+++ b/network_cmds/rtadvd.tproj/timer.h
@@ -0,0 +1,64 @@
+/* $KAME: timer.h,v 1.5 2002/05/31 13:30:38 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* 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;
+
+ struct rtadvd_timer *(*expire)(void *); /* expiration function */
+ void *expire_data;
+ void (*update)(void *, struct timeval *); /* update function */
+ void *update_data;
+};
+
+void rtadvd_timer_init(void);
+struct rtadvd_timer *rtadvd_add_timer(struct rtadvd_timer *(*)(void *),
+ void (*)(void *, struct timeval *), void *, void *);
+void rtadvd_set_timer(struct timeval *, struct rtadvd_timer *);
+void rtadvd_remove_timer(struct rtadvd_timer **);
+struct timeval * rtadvd_check_timer(void);
+struct timeval * rtadvd_timer_rest(struct rtadvd_timer *);
+void TIMEVAL_ADD(struct timeval *, struct timeval *,
+ struct timeval *);
+void TIMEVAL_SUB(struct timeval *, struct timeval *,
+ struct timeval *);