From: David van Moolenbroek Date: Sun, 19 Feb 2017 14:27:23 +0000 (+0000) Subject: Import NetBSD rtadvd(8) X-Git-Url: http://zhaoyanbai.com/repos/?a=commitdiff_plain;h=8f957290eb976b379733d0b3811fcb43f8fbaf1a;p=minix.git Import NetBSD rtadvd(8) Change-Id: I22626843d85c78f0fadefd58d61d7a85d285b2b8 --- diff --git a/distrib/sets/lists/minix-base/mi b/distrib/sets/lists/minix-base/mi index 65054a0e0..377a5be8b 100644 --- a/distrib/sets/lists/minix-base/mi +++ b/distrib/sets/lists/minix-base/mi @@ -160,6 +160,7 @@ ./etc/rc.d/npf minix-base ./etc/rc.d/pwcheck minix-base ./etc/rc.d/root minix-base +./etc/rc.d/rtadvd minix-base ./etc/rc.d/sysctl minix-base ./etc/rc.d/sysdb minix-base ./etc/rc.d/syslogd minix-base @@ -1133,6 +1134,7 @@ ./usr/sbin/rdate minix-base ./usr/sbin/rndc-confgen minix-base ./usr/sbin/rndc minix-base +./usr/sbin/rtadvd minix-base use_inet6 ./usr/sbin/service minix-base ./usr/sbin/services_mkdb minix-base ./usr/sbin/syslogd minix-base @@ -1258,6 +1260,8 @@ ./usr/share/examples/openssl/CA.pl minix-base crypto ./usr/share/examples/openssl/CA.sh minix-base crypto ./usr/share/examples/openssl/openssl.cnf minix-base crypto +./usr/share/examples/rtadvd minix-base +./usr/share/examples/rtadvd/rtadvd.conf minix-base use_inet6 ./usr/share/examples/tmux minix-base ./usr/share/examples/tmux/screen-keys.conf minix-base ./usr/share/i18n minix-base nls @@ -3938,6 +3942,10 @@ ./var/chroot/named/var/run/lwresd minix-base ./var/chroot/named/var/run/named minix-base ./var/chroot/named/var/tmp minix-base +./var/chroot/rtadvd minix-base +./var/chroot/rtadvd/etc minix-base +./var/chroot/rtadvd/var minix-base +./var/chroot/rtadvd/var/run minix-base ./var/chroot/tcpdump minix-base ./var/db minix-base ./var/db/obsolete minix-base diff --git a/distrib/sets/lists/minix-debug/mi b/distrib/sets/lists/minix-debug/mi index 9a7b780f7..c038f3805 100644 --- a/distrib/sets/lists/minix-debug/mi +++ b/distrib/sets/lists/minix-debug/mi @@ -660,6 +660,7 @@ ./usr/libdata/debug/usr/sbin/rdate.debug minix-debug debug ./usr/libdata/debug/usr/sbin/rndc-confgen.debug minix-debug debug ./usr/libdata/debug/usr/sbin/rndc.debug minix-debug debug +./usr/libdata/debug/usr/sbin/rtadvd.debug minix-debug use_inet6,debug ./usr/libdata/debug/usr/sbin/services_mkdb.debug minix-debug debug ./usr/libdata/debug/usr/sbin/syslogd.debug minix-debug debug ./usr/libdata/debug/usr/sbin/tcpdump.debug minix-debug debug diff --git a/distrib/sets/lists/minix-man/mi b/distrib/sets/lists/minix-man/mi index b84810212..8ff7ee84c 100644 --- a/distrib/sets/lists/minix-man/mi +++ b/distrib/sets/lists/minix-man/mi @@ -3390,6 +3390,7 @@ ./usr/man/man5/resolver.5 minix-man ./usr/man/man5/rhosts.5 minix-man obsolete ./usr/man/man5/rndc.conf.5 minix-man +./usr/man/man5/rtadvd.conf.5 minix-man use_inet6 ./usr/man/man5/serv.access.5 minix-man obsolete ./usr/man/man5/statvfs.5 minix-man ./usr/man/man5/syslog.conf.5 minix-man @@ -3564,6 +3565,7 @@ ./usr/man/man8/rotate.8 minix-man ./usr/man/man8/route.8 minix-man ./usr/man/man8/rshd.8 minix-man +./usr/man/man8/rtadvd.8 minix-man use_inet6 ./usr/man/man8/screendump.8 minix-man ./usr/man/man8/serial-ip.8 minix-man obsolete ./usr/man/man8/service.8 minix-man diff --git a/etc/mtree/NetBSD.dist.base b/etc/mtree/NetBSD.dist.base index 96f36e31c..66a52f826 100644 --- a/etc/mtree/NetBSD.dist.base +++ b/etc/mtree/NetBSD.dist.base @@ -205,6 +205,7 @@ ./usr/share/examples/lua ./usr/share/examples/lutok ./usr/share/examples/openssl +./usr/share/examples/rtadvd ./usr/share/examples/tmux ./usr/share/games ./usr/share/games/fortune @@ -714,6 +715,10 @@ ./var/chroot/named/var/run/lwresd mode=0775 gname=named ./var/chroot/named/var/run/named mode=0775 gname=named ./var/chroot/named/var/tmp mode=01775 gname=named +./var/chroot/rtadvd type=dir mode=0755 +./var/chroot/rtadvd/etc type=dir mode=0755 +./var/chroot/rtadvd/var type=dir mode=0755 +./var/chroot/rtadvd/var/run type=dir mode=0775 gname=_rtadvd ./var/chroot/tcpdump mode=0755 ./var/db ./var/db/obsolete diff --git a/etc/rc.d/Makefile b/etc/rc.d/Makefile index b9021d6e8..08562a212 100755 --- a/etc/rc.d/Makefile +++ b/etc/rc.d/Makefile @@ -45,7 +45,7 @@ CONFIGFILES=\ pwcheck \ \ \ - root \ + root rtadvd \ \ \ sysctl sysdb syslogd \ diff --git a/etc/rc.d/rtadvd b/etc/rc.d/rtadvd new file mode 100755 index 000000000..5fe67720f --- /dev/null +++ b/etc/rc.d/rtadvd @@ -0,0 +1,60 @@ +#!/bin/sh +# +# $NetBSD: rtadvd,v 1.8 2013/07/09 09:34:58 roy Exp $ +# + +# PROVIDE: rtadvd +# REQUIRE: DAEMON +# BEFORE: LOGIN + +$_rc_subr_loaded . /etc/rc.subr + +name=rtadvd +rcvar=$name +command="/usr/sbin/$name" +pidfile="/var/run/$name.pid" +extra_commands=reload +start_precmd=rtadvd_prestart +reload_precmd=rtadvd_prereload + +rtadvd_prereload() +{ + local chdir="$(getent passwd _rtadvd | cut -d: -f6)" + local conf=/etc/rtadvd.conf myflags o confdir + + [ -z "$chdir" -o "$chdir" = / ] && return 0 + + if [ -n "$flags" ]; then + myflags=$flags + else + eval myflags=\$${name}_flags + fi + set -- ${myflags} + while getopts c:dDfM:Rs o; do + case "$1" in + -c) conf="$OPTARG";; + esac + shift + done + confdir=$(dirname "$conf") + + echo "$name: copying $conf to $chdir$conf" + cp "$conf" "$chdir$conf" + + # Provide a link to the chrooted dump file + ln -snf "$chdir/var/run/$name.dump" /var/run +} + +rtadvd_prestart() +{ + if [ "$ip6mode" != router ]; then + warn \ + "${name} cannot be used on IPv6 host, only on an IPv6 router." + return 1 + fi + + rtadvd_prereload +} + +load_rc_config $name +run_rc_command "$1" diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 94e557a2f..5d9e52167 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -55,7 +55,7 @@ SUBDIR+= mdsetimage SUBDIR+= ndp .endif .if (${USE_INET6} != "no") -SUBDIR+= traceroute6 +SUBDIR+= rtadvd traceroute6 .endif .if !defined(__MINIX) diff --git a/usr.sbin/rtadvd/Makefile b/usr.sbin/rtadvd/Makefile new file mode 100644 index 000000000..4ef82581a --- /dev/null +++ b/usr.sbin/rtadvd/Makefile @@ -0,0 +1,26 @@ +# $NetBSD: Makefile,v 1.17 2012/08/10 12:10:30 joerg Exp $ + +WARNS?= 4 + +.include + +USE_FORT?= yes # network server + +PROG= rtadvd +SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c dump.c + +CPPFLAGS+=-DINET6 +MAN= rtadvd.8 rtadvd.conf.5 +LDADD+= -lutil +DPADD+= ${LIBUTIL} + +.if ${MKSHARE} != "no" +FILESDIR= /usr/share/examples/rtadvd +FILES= rtadvd.conf +.endif + +.include + +.if defined(HAVE_GCC) || defined(HAVE_LLVM) +COPTS.dump.c=-fno-strict-aliasing +.endif diff --git a/usr.sbin/rtadvd/advcap.c b/usr.sbin/rtadvd/advcap.c new file mode 100644 index 000000000..bd6c7f605 --- /dev/null +++ b/usr.sbin/rtadvd/advcap.c @@ -0,0 +1,444 @@ +/* $NetBSD: advcap.c,v 1.15 2015/06/05 15:41:59 roy Exp $ */ +/* $KAME: advcap.c,v 1.11 2003/05/19 09:46:50 keiichi Exp $ */ + +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * remcap - routines for dealing with the remote host data base + * + * derived from termcap + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +#ifndef __UNCONST +#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) +#endif + +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif +#define MAXHOP 32 /* max number of tc= indirections */ + +#define tgetent agetent +#define tnchktc anchktc +#define tnamatch anamatch +#define tgetnum agetnum +#define tgetflag agetflag +#define tgetstr agetstr + +#if 0 +#define V_TERMCAP "REMOTE" +#define V_TERM "HOST" +#endif + +char *RM; + +/* + * termcap - routines for dealing with the terminal capability data base + * + * BUG: Should use a "last" pointer in tbuf, so that searching + * for capabilities alphabetically would not be a n**2/2 + * process when large numbers of capabilities are given. + * Note: If we add a last pointer now we will screw up the + * tc capability. We really should compile termcap. + * + * Essentially all the work here is scanning and decoding escapes + * in string capabilities. We don't use stdio because the editor + * doesn't, and because living w/o it is not hard. + */ + +static char *tbuf; +static int hopcount; /* detect infinite loops in termcap, init 0 */ + +static char *remotefile; + +extern char *conffile; + +int tgetent(char *, char *); +int getent(char *, char *, char *); +int tnchktc(void); +int tnamatch(char *); +static char *tskip(char *); +int64_t tgetnum(char *); +int tgetflag(char *); +char *tgetstr(char *, char **); +static char *tdecode(char *, char **); + +/* + * Get an entry for terminal name in buffer bp, + * from the termcap file. Parse is very rudimentary; + * we just notice escaped newlines. + */ +int +tgetent(char *bp, char *name) +{ + char *cp; + + remotefile = cp = conffile ? conffile : __UNCONST(_PATH_RTADVDCONF); + return (getent(bp, name, cp)); +} + +int +getent(char *bp, char *name, char *cp) +{ + int c; + int i = 0, cnt = 0; + char ibuf[BUFSIZ]; + int tf; + + tbuf = bp; + tf = 0; + /* + * TERMCAP can have one of two things in it. It can be the + * name of a file to use instead of /etc/termcap. In this + * case it better start with a "/". Or it can be an entry to + * use so we don't have to read the file. In this case it + * has to already have the newlines crunched out. + */ + if (cp && *cp) { + tf = open(RM = cp, O_RDONLY); + } + if (tf < 0) { + syslog(LOG_INFO, "<%s> open: %m", __func__); + return (-2); + } + for (;;) { + cp = bp; + for (;;) { + if (i == cnt) { + cnt = read(tf, ibuf, BUFSIZ); + if (cnt <= 0) { + close(tf); + return (0); + } + i = 0; + } + c = ibuf[i++]; + if (c == '\n') { + if (cp > bp && cp[-1] == '\\') { + cp--; + continue; + } + break; + } + if (cp >= bp + BUFSIZ) { + write(2,"Remcap entry too long\n", 23); + break; + } else + *cp++ = c; + } + *cp = 0; + + /* + * The real work for the match. + */ + if (tnamatch(name)) { + close(tf); + return (tnchktc()); + } + } +} + +/* + * tnchktc: check the last entry, see if it's tc=xxx. If so, + * recursively find xxx and append that entry (minus the names) + * to take the place of the tc=xxx entry. This allows termcap + * entries to say "like an HP2621 but doesn't turn on the labels". + * Note that this works because of the left to right scan. + */ +int +tnchktc(void) +{ + char *p, *q; + char tcname[16]; /* name of similar terminal */ + char tcbuf[BUFSIZ]; + char *holdtbuf = tbuf; + int l; + + p = tbuf + strlen(tbuf) - 2; /* before the last colon */ + while (*--p != ':') + if (p < tbuf) { + write(2, "Bad remcap entry\n", 18); + return (0); + } + p++; + /* p now points to beginning of last field */ + if (p[0] != 't' || p[1] != 'c') + return (1); + strlcpy(tcname, p + 3, sizeof tcname); + q = tcname; + while (*q && *q != ':') + q++; + *q = 0; + if (++hopcount > MAXHOP) { + write(2, "Infinite tc= loop\n", 18); + return (0); + } + if (getent(tcbuf, tcname, remotefile) != 1) { + return (0); + } + for (q = tcbuf; *q++ != ':'; ) + ; + l = p - holdtbuf + strlen(q); + if (l > BUFSIZ) { + write(2, "Remcap entry too long\n", 23); + q[BUFSIZ - (p-holdtbuf)] = 0; + } + strcpy(p, q); + tbuf = holdtbuf; + return (1); +} + +/* + * Tnamatch deals with name matching. The first field of the termcap + * entry is a sequence of names separated by |'s, so we compare + * against each such name. The normal : terminator after the last + * name (before the first field) stops us. + */ +int +tnamatch(char *np) +{ + char *Np, *Bp; + + Bp = tbuf; + if (*Bp == '#') + return (0); + for (;;) { + for (Np = np; *Np && *Bp == *Np; Bp++, Np++) + continue; + if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) + return (1); + while (*Bp && *Bp != ':' && *Bp != '|') + Bp++; + if (*Bp == 0 || *Bp == ':') + return (0); + Bp++; + } +} + +/* + * Skip to the next field. Notice that this is very dumb, not + * knowing about \: escapes or any such. If necessary, :'s can be put + * into the termcap file in octal. + */ +static char * +tskip(char *bp) +{ + int dquote; + + dquote = 0; + while (*bp) { + switch (*bp) { + case ':': + if (!dquote) + goto breakbreak; + else + bp++; + break; + case '\\': + bp++; + if (isdigit((unsigned char)*bp)) { + while (isdigit((unsigned char)*bp++)) + ; + } else + bp++; + case '"': + dquote = (dquote ? 0 : 1); + bp++; + break; + default: + bp++; + break; + } + } +breakbreak: + if (*bp == ':') + bp++; + return (bp); +} + +/* + * Return the (numeric) option id. + * Numeric options look like + * li#80 + * i.e. the option string is separated from the numeric value by + * a # character. If the option is not found we return -1. + * Note that we handle octal numbers beginning with 0. + */ +int64_t +tgetnum(char *id) +{ + int64_t i; + int base; + char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (*bp == 0) + return (-1); + if (strncmp(bp, id, strlen(id)) != 0) + continue; + bp += strlen(id); + if (*bp == '@') + return (-1); + if (*bp != '#') + continue; + bp++; + base = 10; + if (*bp == '0') + base = 8; + i = 0; + while (isdigit((unsigned char)*bp)) + i *= base, i += *bp++ - '0'; + return (i); + } +} + +/* + * Handle a flag option. + * Flag options are given "naked", i.e. followed by a : or the end + * of the buffer. Return 1 if we find the option, or 0 if it is + * not given. + */ +int +tgetflag(char *id) +{ + char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (!*bp) + return (0); + if (strncmp(bp, id, strlen(id)) == 0) { + bp += strlen(id); + if (!*bp || *bp == ':') + return (1); + else if (*bp == '@') + return (0); + } + } +} + +/* + * Get a string valued option. + * These are given as + * cl=^Z + * Much decoding is done on the strings, and the strings are + * placed in area, which is a ref parameter which is updated. + * No checking on area overflow. + */ +char * +tgetstr(char *id, char **area) +{ + char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (!*bp) + return (0); + if (strncmp(bp, id, strlen(id)) != 0) + continue; + bp += strlen(id); + if (*bp == '@') + return (0); + if (*bp != '=') + continue; + bp++; + return (tdecode(bp, area)); + } +} + +/* + * Tdecode does the grung work to decode the + * string capability escapes. + */ +static char * +tdecode(char *str, char **area) +{ + char *cp; + int c; + const char *dps = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\"", *dp; + int i; + char term; + + term = ':'; + cp = *area; +again: + if (*str == '"') { + term = '"'; + str++; + } + while ((c = *str++) && c != term) { + switch (c) { + + case '^': + c = *str++ & 037; + break; + + case '\\': + dp = dps; + c = *str++; +nextc: + if (*dp++ == c) { + c = *dp++; + break; + } + dp++; + if (*dp) + goto nextc; + if (isdigit((unsigned char)c)) { + c -= '0', i = 2; + do + c <<= 3, c |= *str++ - '0'; + while (--i && isdigit((unsigned char)*str)); + } + break; + } + *cp++ = c; + } + if (c == term && term != ':') { + term = ':'; + goto again; + } + *cp++ = 0; + str = *area; + *area = cp; + return (str); +} diff --git a/usr.sbin/rtadvd/advcap.h b/usr.sbin/rtadvd/advcap.h new file mode 100644 index 000000000..3c8303f03 --- /dev/null +++ b/usr.sbin/rtadvd/advcap.h @@ -0,0 +1,46 @@ +/* $NetBSD: advcap.h,v 1.7 2011/12/10 19:14:29 roy Exp $ */ +/* $KAME: advcap.h,v 1.5 2003/06/09 05:40:54 t-momose Exp $ */ + +/* + * Copyright (C) 1994,1995 by Andrey A. Chernov, Moscow, Russia. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Based on Id: termcap.h,v 1.8 1996/09/10 12:42:10 peter Exp */ + +#ifndef _ADVCAP_H_ +#define _ADVCAP_H_ + +#include + +__BEGIN_DECLS + +extern int agetent(char *, const char *); +extern int agetflag(const char *); +extern int64_t agetnum(const char *); +extern char *agetstr(const char *, char **); + +__END_DECLS + +#endif /* _ADVCAP_H_ */ diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c new file mode 100644 index 000000000..a7ec65794 --- /dev/null +++ b/usr.sbin/rtadvd/config.c @@ -0,0 +1,1324 @@ +/* $NetBSD: config.c,v 1.34 2015/06/05 14:09:20 roy Exp $ */ +/* $KAME: config.c,v 1.93 2005/10/17 14:40:02 suz Exp $ */ + +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef __FreeBSD__ +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rtadvd.h" +#include "advcap.h" +#include "timer.h" +#include "if.h" +#include "config.h" + +#ifndef __arraycount +#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) +#endif + +static time_t prefix_timo = (60 * 120); /* 2 hours. + * XXX: should be configurable. */ +static struct rtadvd_timer *prefix_timeout(void *); +static void makeentry(char *, size_t, int, const char *); +static int getinet6sysctl(int); + +static size_t +encode_domain(char *dst, const char *src) +{ + ssize_t len; + char *odst, *p; + + odst = dst; + while (src && (len = strlen(src)) != 0) { + p = strchr(src, '.'); + *dst++ = len = MIN(63, p == NULL ? len : p - src); + memcpy(dst, src, len); + dst += len; + if (p == NULL) + break; + src = p + 1; + } + *dst++ = '\0'; + + return dst - odst; +} + +void +free_rainfo(struct rainfo *rai) +{ + struct prefix *pfx; + struct rtinfo *rti; + struct rdnss *rdnss; + struct rdnss_addr *rdnsa; + struct dnssl *dnssl; + struct dnssl_domain *dnsd; + + rtadvd_remove_timer(&rai->timer); + + while ((pfx = TAILQ_FIRST(&rai->prefix))) { + TAILQ_REMOVE(&rai->prefix, pfx, next); + free(pfx); + } + + while ((rti = TAILQ_FIRST(&rai->route))) { + TAILQ_REMOVE(&rai->route, rti, next); + free(rti); + } + + while ((rdnss = TAILQ_FIRST(&rai->rdnss))) { + TAILQ_REMOVE(&rai->rdnss, rdnss, next); + while ((rdnsa = TAILQ_FIRST(&rdnss->list))) { + TAILQ_REMOVE(&rdnss->list, rdnsa, next); + free(rdnsa); + } + free(rdnss); + } + + while ((dnssl = TAILQ_FIRST(&rai->dnssl))) { + TAILQ_REMOVE(&rai->dnssl, dnssl, next); + while ((dnsd = TAILQ_FIRST(&dnssl->list))) { + TAILQ_REMOVE(&dnssl->list, dnsd, next); + free(dnsd); + } + free(dnssl); + } + + free(rai->sdl); + free(rai->ra_data); + free(rai); +} + +void +getconfig(const char *intface, int exithard) +{ + int stat, c, i; + char tbuf[BUFSIZ]; + struct rainfo *tmp, *rai; + int32_t val; + int64_t val64; + char buf[BUFSIZ]; + char *bp = buf; + char *addr, *flagstr, *ap; + static int forwarding = -1; + char entbuf[256], abuf[256]; + struct rdnss *rdnss; + struct dnssl *dnssl; + +#define MUSTHAVE(var, cap) \ + do { \ + int64_t t; \ + if ((t = agetnum(cap)) < 0) { \ + fprintf(stderr, "rtadvd: need %s for interface %s\n", \ + cap, intface); \ + goto errexit; \ + } \ + var = t; \ + } while (0) +#define MAYHAVE(var, cap, def) \ + do { \ + if ((var = agetnum(cap)) < 0) \ + var = def; \ + } while (0) +#define ELM_MALLOC(p) \ + do { \ + p = calloc(1, sizeof(*p)); \ + if (p == NULL) { \ + syslog(LOG_ERR, "<%s> calloc failed: %m", \ + __func__); \ + goto errexit; \ + } \ + } while(/*CONSTCOND*/0) + + if (if_nametoindex(intface) == 0) { + syslog(LOG_INFO, "<%s> interface %s not found, ignoring", + __func__, intface); + return; + } + + syslog(LOG_DEBUG, "<%s> loading configuration for interface %s", + __func__, intface); + + if ((stat = agetent(tbuf, intface)) <= 0) { + memset(tbuf, 0, sizeof(tbuf)); + syslog(LOG_INFO, + "<%s> %s isn't defined in the configuration file" + " or the configuration file doesn't exist." + " Treat it as default", + __func__, intface); + } + + ELM_MALLOC(tmp); + TAILQ_INIT(&tmp->prefix); + TAILQ_INIT(&tmp->route); + TAILQ_INIT(&tmp->rdnss); + TAILQ_INIT(&tmp->dnssl); + + /* check if we are allowed to forward packets (if not determined) */ + if (forwarding < 0) { + if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0) + exit(1); + } + + /* get interface information */ + if (agetflag("nolladdr")) + tmp->advlinkopt = 0; + else + tmp->advlinkopt = 1; + if (tmp->advlinkopt) { + if ((tmp->sdl = if_nametosdl(intface)) == NULL) { + syslog(LOG_ERR, + "<%s> can't get information of %s", + __func__, intface); + goto errexit; + } + tmp->ifindex = tmp->sdl->sdl_index; + } else { + tmp->ifindex = if_nametoindex(intface); + if (tmp->ifindex == 0) { + syslog(LOG_ERR, + "<%s> can't get information of %s", + __func__, intface); + goto errexit; + } + } + tmp->ifflags = if_getflags(tmp->ifindex, 0); + strlcpy(tmp->ifname, intface, sizeof(tmp->ifname)); + if ((tmp->phymtu = if_getmtu(intface)) == 0) { + tmp->phymtu = IPV6_MMTU; + syslog(LOG_WARNING, + "<%s> can't get interface mtu of %s. Treat as %d", + __func__, intface, IPV6_MMTU); + } + + /* + * set router configuration variables. + */ + MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL); + if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) { + syslog(LOG_ERR, + "<%s> maxinterval (%d) on %s is invalid " + "(must be between %u and %u)", __func__, val, + intface, MIN_MAXINTERVAL, MAX_MAXINTERVAL); + goto errexit; + } + tmp->maxinterval = val; + MAYHAVE(val, "mininterval", tmp->maxinterval/3); + if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) { + syslog(LOG_ERR, + "<%s> mininterval (%d) on %s is invalid " + "(must be between %u and %d)", + __func__, val, intface, MIN_MININTERVAL, + (tmp->maxinterval * 3) / 4); + goto errexit; + } + tmp->mininterval = val; + + MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT); + tmp->hoplimit = val & 0xff; + + if ((flagstr = (char *)agetstr("raflags", &bp))) { + val = 0; + if (strchr(flagstr, 'm')) + val |= ND_RA_FLAG_MANAGED; + if (strchr(flagstr, 'o')) + val |= ND_RA_FLAG_OTHER; + if (strchr(flagstr, 'h')) + val |= ND_RA_FLAG_RTPREF_HIGH; + if (strchr(flagstr, 'l')) { + if ((val & ND_RA_FLAG_RTPREF_HIGH)) { + syslog(LOG_ERR, "<%s> the \'h\' and \'l\'" + " router flags are exclusive", __func__); + goto errexit; + } + val |= ND_RA_FLAG_RTPREF_LOW; + } + } else { + MAYHAVE(val, "raflags", 0); + } + tmp->managedflg = val & ND_RA_FLAG_MANAGED; + tmp->otherflg = val & ND_RA_FLAG_OTHER; +#ifndef ND_RA_FLAG_RTPREF_MASK +#define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ +#define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */ +#endif + tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK; + if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) { + syslog(LOG_ERR, "<%s> invalid router preference (%02x) on %s", + __func__, tmp->rtpref, intface); + goto errexit; + } + + MAYHAVE(val, "rltime", tmp->maxinterval * 3); + if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) { + syslog(LOG_ERR, + "<%s> router lifetime (%d) on %s is invalid " + "(must be 0 or between %d and %d)", + __func__, val, intface, + tmp->maxinterval, MAXROUTERLIFETIME); + goto errexit; + } + /* + * Basically, hosts MUST NOT send Router Advertisement messages at any + * time (RFC 2461, Section 6.2.3). However, it would sometimes be + * useful to allow hosts to advertise some parameters such as prefix + * information and link MTU. Thus, we allow hosts to invoke rtadvd + * only when router lifetime (on every advertising interface) is + * explicitly set zero. (see also the above section) + */ + if (val && forwarding == 0) { + syslog(LOG_ERR, + "<%s> non zero router lifetime is specified for %s, " + "which must not be allowed for hosts. you must " + "change router lifetime or enable IPv6 forwarding.", + __func__, intface); + goto errexit; + } + tmp->lifetime = val & 0xffff; + + MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME); + if (val < 0 || val > MAXREACHABLETIME) { + syslog(LOG_ERR, + "<%s> reachable time (%d) on %s is invalid " + "(must be no greater than %d)", + __func__, val, intface, MAXREACHABLETIME); + goto errexit; + } + tmp->reachabletime = (uint32_t)val; + + MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER); + if (val64 < 0 || val64 > 0xffffffff) { + syslog(LOG_ERR, "<%s> retrans time (%lld) on %s out of range", + __func__, (long long)val64, intface); + goto errexit; + } + tmp->retranstimer = (uint32_t)val64; + + if (agetnum("hapref") != -1 || agetnum("hatime") != -1) { + syslog(LOG_ERR, + "<%s> mobile-ip6 configuration not supported", + __func__); + goto errexit; + } + /* prefix information */ + + /* + * This is an implementation specific parameter to consider + * link propagation delays and poorly synchronized clocks when + * checking consistency of advertised lifetimes. + */ + MAYHAVE(val, "clockskew", 0); + tmp->clockskew = val; + + tmp->pfxs = 0; + for (i = -1; i < MAXPREFIX; i++) { + struct prefix *pfx; + + makeentry(entbuf, sizeof(entbuf), i, "addr"); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) + continue; + + /* allocate memory to store prefix information */ + if ((pfx = calloc(1, sizeof(*pfx))) == NULL) { + syslog(LOG_ERR, + "<%s> can't allocate memory: %m", + __func__); + goto errexit; + } + + TAILQ_INSERT_TAIL(&tmp->prefix, pfx, next); + tmp->pfxs++; + pfx->rainfo = tmp; + + pfx->origin = PREFIX_FROM_CONFIG; + + if (inet_pton(AF_INET6, addr, &pfx->prefix) != 1) { + syslog(LOG_ERR, + "<%s> inet_pton failed for %s", + __func__, addr); + goto errexit; + } + if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) { + syslog(LOG_ERR, + "<%s> multicast prefix (%s) must " + "not be advertised on %s", + __func__, addr, intface); + goto errexit; + } + if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix)) + syslog(LOG_NOTICE, + "<%s> link-local prefix (%s) will be" + " advertised on %s", + __func__, addr, intface); + + makeentry(entbuf, sizeof(entbuf), i, "prefixlen"); + MAYHAVE(val, entbuf, 64); + if (val < 0 || val > 128) { + syslog(LOG_ERR, "<%s> prefixlen (%d) for %s " + "on %s out of range", + __func__, val, addr, intface); + goto errexit; + } + pfx->prefixlen = (int)val; + + makeentry(entbuf, sizeof(entbuf), i, "pinfoflags"); + if ((flagstr = (char *)agetstr(entbuf, &bp))) { + val = 0; + if (strchr(flagstr, 'l')) + val |= ND_OPT_PI_FLAG_ONLINK; + if (strchr(flagstr, 'a')) + val |= ND_OPT_PI_FLAG_AUTO; + } else { + MAYHAVE(val, entbuf, + (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO)); + } + pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK; + pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO; + + makeentry(entbuf, sizeof(entbuf), i, "vltime"); + MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); + if (val64 < 0 || val64 > 0xffffffff) { + syslog(LOG_ERR, "<%s> vltime (%lld) for " + "%s/%d on %s is out of range", + __func__, (long long)val64, + addr, pfx->prefixlen, intface); + goto errexit; + } + pfx->validlifetime = (uint32_t)val64; + + makeentry(entbuf, sizeof(entbuf), i, "vltimedecr"); + if (agetflag(entbuf)) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + pfx->vltimeexpire = + now.tv_sec + pfx->validlifetime; + } + + makeentry(entbuf, sizeof(entbuf), i, "pltime"); + MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME); + if (val64 < 0 || val64 > 0xffffffff) { + syslog(LOG_ERR, + "<%s> pltime (%lld) for %s/%d on %s " + "is out of range", + __func__, (long long)val64, + addr, pfx->prefixlen, intface); + goto errexit; + } + pfx->preflifetime = (uint32_t)val64; + + makeentry(entbuf, sizeof(entbuf), i, "pltimedecr"); + if (agetflag(entbuf)) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + pfx->pltimeexpire = + now.tv_sec + pfx->preflifetime; + } + } + if (TAILQ_FIRST(&tmp->prefix) == NULL && !agetflag("noifprefix")) + get_prefix(tmp); + + MAYHAVE(val64, "mtu", 0); + if (val64 < 0 || val64 > 0xffffffff) { + syslog(LOG_ERR, + "<%s> mtu (%" PRIi64 ") on %s out of range", + __func__, val64, intface); + goto errexit; + } + tmp->linkmtu = (uint32_t)val64; + if (tmp->linkmtu == 0) { + char *mtustr; + + if ((mtustr = (char *)agetstr("mtu", &bp)) && + strcmp(mtustr, "auto") == 0) + tmp->linkmtu = tmp->phymtu; + } + else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) { + syslog(LOG_ERR, + "<%s> advertised link mtu (%d) on %s is invalid (must " + "be between least MTU (%d) and physical link MTU (%d)", + __func__, tmp->linkmtu, intface, + IPV6_MMTU, tmp->phymtu); + goto errexit; + } + +#ifdef SIOCSIFINFO_IN6 + { + struct in6_ndireq ndi; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "<%s> socket: %m", __func__); + goto errexit; + } + memset(&ndi, 0, sizeof(ndi)); + strncpy(ndi.ifname, intface, IFNAMSIZ); + if (ioctl(s, SIOCGIFINFO_IN6, &ndi) < 0) { + syslog(LOG_INFO, "<%s> ioctl:SIOCGIFINFO_IN6 at %s: %m", + __func__, intface); + } + + /* reflect the RA info to the host variables in kernel */ + ndi.ndi.chlim = tmp->hoplimit; + ndi.ndi.retrans = tmp->retranstimer; + ndi.ndi.basereachable = tmp->reachabletime; + if (ioctl(s, SIOCSIFINFO_IN6, &ndi) < 0) { + syslog(LOG_INFO, "<%s> ioctl:SIOCSIFINFO_IN6 at %s: %m", + __func__, intface); + } + close(s); + } +#endif + + /* route information */ + for (i = -1; i < MAXROUTE; i++) { + struct rtinfo *rti; + char oentbuf[256]; + + makeentry(entbuf, sizeof(entbuf), i, "rtprefix"); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) { + makeentry(oentbuf, sizeof(oentbuf), i, "rtrprefix"); + addr = (char *)agetstr(oentbuf, &bp); + if (addr) { + fprintf(stderr, "%s was obsoleted. Use %s.\n", + oentbuf, entbuf); + } + } + if (addr == NULL) + continue; + + ELM_MALLOC(rti); + memset(rti, 0, sizeof(*rti)); + + /* link into chain */ + TAILQ_INSERT_TAIL(&tmp->route, rti, next); + + if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) { + syslog(LOG_ERR, "<%s> inet_pton failed for %s", + __func__, addr); + goto errexit; + } +#if 0 + /* + * XXX: currently there's no restriction in route information + * prefix according to + * draft-ietf-ipngwg-router-selection-00.txt. + * However, I think the similar restriction be necessary. + */ + MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); + if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) { + syslog(LOG_ERR, + "<%s> multicast route (%s) must " + "not be advertised on %s", + __func__, addr, intface); + goto errexit; + } + if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) { + syslog(LOG_NOTICE, + "<%s> link-local route (%s) will " + "be advertised on %s", + __func__, addr, intface); + goto errexit; + } +#endif + + makeentry(entbuf, sizeof(entbuf), i, "rtplen"); + /* XXX: 256 is a magic number for compatibility check. */ + MAYHAVE(val, entbuf, 256); + if (val == 256) { + makeentry(oentbuf, sizeof(oentbuf), i, "rtrplen"); + MAYHAVE(val, oentbuf, 256); + if (val != 256) { + fprintf(stderr, "%s was obsoleted. Use %s.\n", + oentbuf, entbuf); + } else + val = 64; + } + if (val < 0 || val > 128) { + syslog(LOG_ERR, "<%s> prefixlen (%d) for %s on %s " + "out of range", + __func__, val, addr, intface); + goto errexit; + } + rti->prefixlen = (int)val; + + makeentry(entbuf, sizeof(entbuf), i, "rtflags"); + if ((flagstr = (char *)agetstr(entbuf, &bp))) { + val = 0; + if (strchr(flagstr, 'h')) + val |= ND_RA_FLAG_RTPREF_HIGH; + if (strchr(flagstr, 'l')) { + if ((val & ND_RA_FLAG_RTPREF_HIGH)) { + syslog(LOG_ERR, + "<%s> the \'h\' and \'l\' route" + " preferences are exclusive", + __func__); + goto errexit; + } + val |= ND_RA_FLAG_RTPREF_LOW; + } + } else + MAYHAVE(val, entbuf, 256); /* XXX */ + if (val == 256) { + makeentry(oentbuf, sizeof(oentbuf), i, "rtrflags"); + MAYHAVE(val, oentbuf, 256); + if (val != 256) { + fprintf(stderr, "%s was obsoleted. Use %s.\n", + oentbuf, entbuf); + } else + val = 0; + } + rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK; + if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) { + syslog(LOG_ERR, "<%s> invalid route preference (%02x) " + "for %s/%d on %s", + __func__, rti->rtpref, addr, + rti->prefixlen, intface); + goto errexit; + } + + /* + * Since the spec does not a default value, we should make + * this entry mandatory. However, FreeBSD 4.4 has shipped + * with this field being optional, we use the router lifetime + * as an ad-hoc default value with a warning message. + */ + makeentry(entbuf, sizeof(entbuf), i, "rtltime"); + MAYHAVE(val64, entbuf, -1); + if (val64 == -1) { + makeentry(oentbuf, sizeof(oentbuf), i, "rtrltime"); + MAYHAVE(val64, oentbuf, -1); + if (val64 != -1) { + fprintf(stderr, "%s was obsoleted. Use %s.\n", + oentbuf, entbuf); + } else { + fprintf(stderr, "%s should be specified " + "for interface %s.\n", + entbuf, intface); + val64 = tmp->lifetime; + } + } + if (val64 < 0 || val64 > 0xffffffff) { + syslog(LOG_ERR, "<%s> route lifetime (%lld) for " + "%s/%d on %s out of range", __func__, + (long long)val64, addr, rti->prefixlen, intface); + goto errexit; + } + rti->ltime = (uint32_t)val64; + } + + /* RDNSS */ + for (i = -1; i < MAXRDNSS; i++) { + struct rdnss_addr *rdnsa; + + makeentry(entbuf, sizeof(entbuf), i, "rdnss"); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) + continue; + + ELM_MALLOC(rdnss); + TAILQ_INSERT_TAIL(&tmp->rdnss, rdnss, next); + TAILQ_INIT(&rdnss->list); + + for (ap = addr; ap - addr < (ssize_t)strlen(addr); ap += c+1) { + c = strcspn(ap, ","); + strncpy(abuf, ap, c); + abuf[c] = '\0'; + ELM_MALLOC(rdnsa); + TAILQ_INSERT_TAIL(&rdnss->list, rdnsa, next); + if (inet_pton(AF_INET6, abuf, &rdnsa->addr) != 1) { + syslog(LOG_ERR, "<%s> inet_pton failed for %s", + __func__, addr); + goto errexit; + } + } + + makeentry(entbuf, sizeof(entbuf), i, "rdnssltime"); + MAYHAVE(val64, entbuf, tmp->maxinterval * 3 / 2); + if (val64 < tmp->maxinterval || + val64 > tmp->maxinterval * 2) + { + syslog(LOG_ERR, "<%s> %s (%lld) on %s is invalid", + __func__, entbuf, (long long)val64, intface); + goto errexit; + } + rdnss->lifetime = (uint32_t)val64; + + } + + /* DNSSL */ + TAILQ_INIT(&tmp->dnssl); + for (i = -1; i < MAXDNSSL; i++) { + struct dnssl_domain *dnsd; + + makeentry(entbuf, sizeof(entbuf), i, "dnssl"); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) + continue; + + ELM_MALLOC(dnssl); + TAILQ_INSERT_TAIL(&tmp->dnssl, dnssl, next); + TAILQ_INIT(&dnssl->list); + + for (ap = addr; ap - addr < (ssize_t)strlen(addr); ap += c+1) { + c = strcspn(ap, ","); + strncpy(abuf, ap, c); + abuf[c] = '\0'; + ELM_MALLOC(dnsd); + TAILQ_INSERT_TAIL(&dnssl->list, dnsd, next); + dnsd->len = encode_domain(dnsd->domain, abuf); + } + + makeentry(entbuf, sizeof(entbuf), i, "dnsslltime"); + MAYHAVE(val64, entbuf, tmp->maxinterval * 3 / 2); + if (val64 < tmp->maxinterval || + val64 > tmp->maxinterval * 2) + { + syslog(LOG_ERR, "<%s> %s (%lld) on %s is invalid", + __func__, entbuf, (long long)val64, intface); + goto errexit; + } + dnssl->lifetime = (uint32_t)val64; + + } + + TAILQ_FOREACH(rai, &ralist, next) { + if (rai->ifindex == tmp->ifindex) { + TAILQ_REMOVE(&ralist, rai, next); + /* If we already have a leaving RA use that + * as this config hasn't been advertised */ + if (rai->leaving) { + tmp->leaving = rai->leaving; + free_rainfo(rai); + rai = tmp->leaving; + rai->leaving_for = tmp; + break; + } + rai->lifetime = 0; + TAILQ_FOREACH(rdnss, &rai->rdnss, next) + rdnss->lifetime = 0; + TAILQ_FOREACH(dnssl, &rai->dnssl, next) + dnssl->lifetime = 0; + rai->leaving_for = tmp; + tmp->leaving = rai; + rai->initcounter = MAX_INITIAL_RTR_ADVERTISEMENTS; + rai->mininterval = MIN_DELAY_BETWEEN_RAS; + rai->maxinterval = MIN_DELAY_BETWEEN_RAS; + rai->leaving_adv = MAX_FINAL_RTR_ADVERTISEMENTS; + if (rai->timer == NULL) + rai->timer = rtadvd_add_timer(ra_timeout, + ra_timer_update, + rai, rai); + ra_timer_update((void *)rai, &rai->timer->tm); + rtadvd_set_timer(&rai->timer->tm, rai->timer); + break; + } + } + + /* okey */ + TAILQ_INSERT_TAIL(&ralist, tmp, next); + + /* construct the sending packet */ + make_packet(tmp); + + /* set timer */ + if (rai) + return; + tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update, + tmp, tmp); + ra_timer_set_short_delay(tmp); + + return; + +errexit: + if (exithard) + exit(1); + free_rainfo(tmp); +} + +void +get_prefix(struct rainfo *rai) +{ + struct ifaddrs *ifap, *ifa; + struct prefix *pp; + struct in6_addr *a; + unsigned char *p, *ep, *m, *lim; + char ntopbuf[INET6_ADDRSTRLEN]; + + if (getifaddrs(&ifap) < 0) { + syslog(LOG_ERR, + "<%s> can't get interface addresses", + __func__); + exit(1); + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + int plen; + + if (strcmp(ifa->ifa_name, rai->ifname) != 0) + continue; + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + if (IN6_IS_ADDR_LINKLOCAL(a)) + continue; + /* get prefix length */ + m = (unsigned char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; + lim = (unsigned char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len; + plen = prefixlen(m, lim); + if (plen <= 0 || plen > 128) { + syslog(LOG_ERR, "<%s> failed to get prefixlen " + "or prefix is invalid", + __func__); + exit(1); + } + if (plen == 128) /* XXX */ + continue; + if (find_prefix(rai, a, plen)) { + /* ignore a duplicated prefix. */ + continue; + } + + /* allocate memory to store prefix info. */ + if ((pp = calloc(1, sizeof(*pp))) == NULL) { + syslog(LOG_ERR, + "<%s> can't get allocate buffer for prefix", + __func__); + exit(1); + } + + /* set prefix, sweep bits outside of prefixlen */ + pp->prefixlen = plen; + memcpy(&pp->prefix, a, sizeof(*a)); + if (1) + { + p = (unsigned char *)&pp->prefix; + ep = (unsigned char *)(&pp->prefix + 1); + while (m < lim && p < ep) + *p++ &= *m++; + while (p < ep) + *p++ = 0x00; + } + if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf, + sizeof(ntopbuf))) { + syslog(LOG_ERR, "<%s> inet_ntop failed", __func__); + exit(1); + } + syslog(LOG_DEBUG, + "<%s> add %s/%d to prefix list on %s", + __func__, ntopbuf, pp->prefixlen, rai->ifname); + + /* set other fields with protocol defaults */ + pp->validlifetime = DEF_ADVVALIDLIFETIME; + pp->preflifetime = DEF_ADVPREFERREDLIFETIME; + pp->onlinkflg = 1; + pp->autoconfflg = 1; + pp->origin = PREFIX_FROM_KERNEL; + pp->rainfo = rai; + + /* link into chain */ + TAILQ_INSERT_TAIL(&rai->prefix, pp, next); + rai->pfxs++; + } + + freeifaddrs(ifap); +} + +static void +makeentry(char *buf, size_t len, int id, const char *string) +{ + + if (id < 0) + strlcpy(buf, string, len); + else + snprintf(buf, len, "%s%d", string, id); +} + +/* + * Add a prefix to the list of specified interface and reconstruct + * the outgoing packet. + * The prefix must not be in the list. + * XXX: other parameters of the prefix(e.g. lifetime) should be + * able to be specified. + */ +static void +add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr) +{ + struct prefix *prefix; + char ntopbuf[INET6_ADDRSTRLEN]; + + if ((prefix = calloc(1, sizeof(*prefix))) == NULL) { + syslog(LOG_ERR, "<%s> memory allocation failed", + __func__); + return; /* XXX: error or exit? */ + } + prefix->prefix = ipr->ipr_prefix.sin6_addr; + prefix->prefixlen = ipr->ipr_plen; + prefix->validlifetime = ipr->ipr_vltime; + prefix->preflifetime = ipr->ipr_pltime; + prefix->onlinkflg = ipr->ipr_raf_onlink; + prefix->autoconfflg = ipr->ipr_raf_auto; + prefix->origin = PREFIX_FROM_DYNAMIC; + + prefix->rainfo = rai; + TAILQ_INSERT_TAIL(&rai->prefix, prefix, next); + rai->pfxs++; + + syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s", + __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + ipr->ipr_plen, rai->ifname); + + /* free the previous packet */ + free(rai->ra_data); + rai->ra_data = NULL; + + /* reconstruct the packet */ + make_packet(rai); +} + +/* + * Delete a prefix to the list of specified interface and reconstruct + * the outgoing packet. + * The prefix must be in the list. + */ +void +delete_prefix(struct prefix *prefix) +{ + char ntopbuf[INET6_ADDRSTRLEN]; + struct rainfo *rai = prefix->rainfo; + + TAILQ_REMOVE(&rai->prefix, prefix, next); + rai->pfxs--; + syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s", + __func__, inet_ntop(AF_INET6, &prefix->prefix, + ntopbuf, INET6_ADDRSTRLEN), + prefix->prefixlen, rai->ifname); + rtadvd_remove_timer(&prefix->timer); + free(prefix); +} + +void +invalidate_prefix(struct prefix *prefix) +{ + char ntopbuf[INET6_ADDRSTRLEN]; + struct timespec timo; + struct rainfo *rai = prefix->rainfo; + + if (prefix->timer) { /* sanity check */ + syslog(LOG_ERR, + "<%s> assumption failure: timer already exists", + __func__); + exit(1); + } + + syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, " + "will expire in %ld seconds", __func__, + inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, INET6_ADDRSTRLEN), + prefix->prefixlen, rai->ifname, (long)prefix_timo); + + /* set the expiration timer */ + prefix->timer = rtadvd_add_timer(prefix_timeout, NULL, prefix, NULL); + if (prefix->timer == NULL) { + syslog(LOG_ERR, "<%s> failed to add a timer for a prefix. " + "remove the prefix", __func__); + delete_prefix(prefix); + } + timo.tv_sec = prefix_timo; + timo.tv_nsec = 0; + rtadvd_set_timer(&timo, prefix->timer); +} + +static struct rtadvd_timer * +prefix_timeout(void *arg) +{ + struct prefix *prefix = (struct prefix *)arg; + + delete_prefix(prefix); + + return(NULL); +} + +void +update_prefix(struct prefix * prefix) +{ + char ntopbuf[INET6_ADDRSTRLEN]; + struct rainfo *rai = prefix->rainfo; + + if (prefix->timer == NULL) { /* sanity check */ + syslog(LOG_ERR, + "<%s> assumption failure: timer does not exist", + __func__); + exit(1); + } + + syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s", + __func__, inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, + INET6_ADDRSTRLEN), prefix->prefixlen, rai->ifname); + + /* stop the expiration timer */ + rtadvd_remove_timer(&prefix->timer); +} + +/* + * Try to get an in6_prefixreq contents for a prefix which matches + * ipr->ipr_prefix and ipr->ipr_plen and belongs to + * the interface whose name is ipr->ipr_name[]. + */ +static int +init_prefix(struct in6_prefixreq *ipr) +{ +#if 0 + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "<%s> socket: %m", __func__); + exit(1); + } + + if (ioctl(s, SIOCGIFPREFIX_IN6, ipr) < 0) { + syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX: %m", __func__); + + ipr->ipr_vltime = DEF_ADVVALIDLIFETIME; + ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME; + ipr->ipr_raf_onlink = 1; + ipr->ipr_raf_auto = 1; + /* omit other field initialization */ + } + else if (ipr->ipr_origin < PR_ORIG_RR) { + char ntopbuf[INET6_ADDRSTRLEN]; + + syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is" + "lower than PR_ORIG_RR(router renumbering)." + "This should not happen if I am router", __func__, + inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf, + sizeof(ntopbuf)), ipr->ipr_origin); + close(s); + return 1; + } + + close(s); + return 0; +#else + ipr->ipr_vltime = DEF_ADVVALIDLIFETIME; + ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME; + ipr->ipr_raf_onlink = 1; + ipr->ipr_raf_auto = 1; + return 0; +#endif +} + +void +make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen) +{ + struct in6_prefixreq ipr; + + memset(&ipr, 0, sizeof(ipr)); + if (if_indextoname(ifindex, ipr.ipr_name) == NULL) { + syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't" + "exist. This should not happen: %m", __func__, + ifindex); + exit(1); + } + ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix); + ipr.ipr_prefix.sin6_family = AF_INET6; + ipr.ipr_prefix.sin6_addr = *addr; + ipr.ipr_plen = plen; + + if (init_prefix(&ipr)) + return; /* init failed by some error */ + add_prefix(rai, &ipr); +} + +void +make_packet(struct rainfo *rainfo) +{ + size_t packlen, lladdroptlen = 0; + char *buf; + struct nd_router_advert *ra; + struct nd_opt_prefix_info *ndopt_pi; + struct nd_opt_mtu *ndopt_mtu; + struct prefix *pfx; + struct nd_opt_route_info *ndopt_rti; + struct rtinfo *rti; + struct nd_opt_rdnss *ndopt_rdnss; + struct rdnss *rdns; + struct rdnss_addr *rdnsa; + struct nd_opt_dnssl *ndopt_dnssl; + struct dnssl *dnsl; + struct dnssl_domain *dnsd; + size_t len, plen; + + /* calculate total length */ + packlen = sizeof(struct nd_router_advert); + if (rainfo->advlinkopt) { + if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) { + syslog(LOG_INFO, + "<%s> link-layer address option has" + " null length on %s. Treat as not included.", + __func__, rainfo->ifname); + rainfo->advlinkopt = 0; + } + packlen += lladdroptlen; + } + if (TAILQ_FIRST(&rainfo->prefix) != NULL) + packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs; + if (rainfo->linkmtu) + packlen += sizeof(struct nd_opt_mtu); + TAILQ_FOREACH(rti, &rainfo->route, next) + packlen += sizeof(struct nd_opt_route_info) + + ((rti->prefixlen + 0x3f) >> 6) * 8; + + TAILQ_FOREACH(rdns, &rainfo->rdnss, next) { + packlen += sizeof(struct nd_opt_rdnss); + TAILQ_FOREACH(rdnsa, &rdns->list, next) + packlen += sizeof(rdnsa->addr); + } + TAILQ_FOREACH(dnsl, &rainfo->dnssl, next) { + packlen += sizeof(struct nd_opt_dnssl); + len = 0; + TAILQ_FOREACH(dnsd, &dnsl->list, next) + len += dnsd->len; + len += len % 8 ? 8 - len % 8 : 0; + packlen += len; + } + + /* allocate memory for the packet */ + if ((buf = realloc(rainfo->ra_data, packlen)) == NULL) { + syslog(LOG_ERR, + "<%s> can't get enough memory for an RA packet %m", + __func__); + exit(1); + } + rainfo->ra_data = buf; + /* XXX: what if packlen > 576? */ + rainfo->ra_datalen = packlen; +#define CHECKLEN(size) \ + do { \ + if (buf + size > rainfo->ra_data + packlen) { \ + syslog(LOG_ERR, \ + "<%s, %d> RA packet does not fit in %zu",\ + __func__, __LINE__, packlen); \ + exit(1); \ + } \ + } while (/*CONSTCOND*/0) + /* + * construct the packet + */ + CHECKLEN(sizeof(*ra)); + ra = (struct nd_router_advert *)buf; + ra->nd_ra_type = ND_ROUTER_ADVERT; + ra->nd_ra_code = 0; + ra->nd_ra_cksum = 0; + ra->nd_ra_curhoplimit = (uint8_t)(0xff & rainfo->hoplimit); + ra->nd_ra_flags_reserved = 0; /* just in case */ + /* + * XXX: the router preference field, which is a 2-bit field, should be + * initialized before other fields. + */ + ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref; + ra->nd_ra_flags_reserved |= + rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0; + ra->nd_ra_flags_reserved |= + rainfo->otherflg ? ND_RA_FLAG_OTHER : 0; + ra->nd_ra_router_lifetime = htons(rainfo->lifetime); + ra->nd_ra_reachable = htonl(rainfo->reachabletime); + ra->nd_ra_retransmit = htonl(rainfo->retranstimer); + buf += sizeof(*ra); + + if (rainfo->advlinkopt) { + CHECKLEN(sizeof(struct nd_opt_hdr)); + lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf); + buf += lladdroptlen; + } + + if (rainfo->linkmtu) { + CHECKLEN(sizeof(*ndopt_mtu)); + ndopt_mtu = (struct nd_opt_mtu *)buf; + ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU; + ndopt_mtu->nd_opt_mtu_len = 1; + ndopt_mtu->nd_opt_mtu_reserved = 0; + ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu); + buf += sizeof(struct nd_opt_mtu); + } + + TAILQ_FOREACH(pfx, &rainfo->prefix, next) { + uint32_t vltime, pltime; + struct timespec now; + + CHECKLEN(sizeof(*ndopt_pi)); + ndopt_pi = (struct nd_opt_prefix_info *)buf; + ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; + ndopt_pi->nd_opt_pi_len = 4; + ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen; + ndopt_pi->nd_opt_pi_flags_reserved = 0; + if (pfx->onlinkflg) + ndopt_pi->nd_opt_pi_flags_reserved |= + ND_OPT_PI_FLAG_ONLINK; + if (pfx->autoconfflg) + ndopt_pi->nd_opt_pi_flags_reserved |= + ND_OPT_PI_FLAG_AUTO; + if (pfx->timer) + vltime = 0; + else { + if (pfx->vltimeexpire || pfx->pltimeexpire) + clock_gettime(CLOCK_MONOTONIC, &now); + if (pfx->vltimeexpire == 0) + vltime = pfx->validlifetime; + else + vltime = (pfx->vltimeexpire > now.tv_sec) ? + pfx->vltimeexpire - now.tv_sec : 0; + } + if (pfx->timer) + pltime = 0; + else { + if (pfx->pltimeexpire == 0) + pltime = pfx->preflifetime; + else + pltime = (pfx->pltimeexpire > now.tv_sec) ? + pfx->pltimeexpire - now.tv_sec : 0; + } + if (vltime < pltime) { + /* + * this can happen if vltime is decrement but pltime + * is not. + */ + pltime = vltime; + } + ndopt_pi->nd_opt_pi_valid_time = htonl(vltime); + ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime); + ndopt_pi->nd_opt_pi_reserved2 = 0; + ndopt_pi->nd_opt_pi_prefix = pfx->prefix; + + buf += sizeof(struct nd_opt_prefix_info); + } + + TAILQ_FOREACH(rti, &rainfo->route, next) { + uint8_t psize = (rti->prefixlen + 0x3f) >> 6; + + CHECKLEN(sizeof(*ndopt_rti)); + ndopt_rti = (struct nd_opt_route_info *)buf; + ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO; + ndopt_rti->nd_opt_rti_len = 1 + psize; + ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen; + ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref; + ndopt_rti->nd_opt_rti_lifetime = htonl(rti->ltime); + memcpy(ndopt_rti + 1, &rti->prefix, psize * 8); + buf += sizeof(struct nd_opt_route_info) + psize * 8; + } + + TAILQ_FOREACH(rdns, &rainfo->rdnss, next) { + CHECKLEN(sizeof(*ndopt_rdnss)); + ndopt_rdnss = (struct nd_opt_rdnss *)buf; + ndopt_rdnss->nd_opt_rdnss_type = ND_OPT_RDNSS; + ndopt_rdnss->nd_opt_rdnss_len = 1; + ndopt_rdnss->nd_opt_rdnss_reserved = 0; + ndopt_rdnss->nd_opt_rdnss_lifetime = htonl(rdns->lifetime); + buf += sizeof(*ndopt_rdnss); + + TAILQ_FOREACH(rdnsa, &rdns->list, next) { + CHECKLEN(sizeof(rdnsa->addr)); + memcpy(buf, &rdnsa->addr, sizeof(rdnsa->addr)); + ndopt_rdnss->nd_opt_rdnss_len += 2; + buf += sizeof(rdnsa->addr); + } + } + + TAILQ_FOREACH(dnsl, &rainfo->dnssl, next) { + CHECKLEN(sizeof(*ndopt_dnssl)); + ndopt_dnssl = (struct nd_opt_dnssl *)buf; + ndopt_dnssl->nd_opt_dnssl_type = ND_OPT_DNSSL; + ndopt_dnssl->nd_opt_dnssl_len = 0; + ndopt_dnssl->nd_opt_dnssl_reserved = 0; + ndopt_dnssl->nd_opt_dnssl_lifetime = htonl(dnsl->lifetime); + buf += sizeof(*ndopt_dnssl); + + TAILQ_FOREACH(dnsd, &dnsl->list, next) { + CHECKLEN(dnsd->len); + memcpy(buf, dnsd->domain, dnsd->len); + buf += dnsd->len; + } + /* Ensure our length is padded correctly */ + len = buf - (char *)ndopt_dnssl; + plen = len % 8 ? 8 - len % 8 : 0; + CHECKLEN(plen); + memset(buf, 0, plen); + buf += plen; + ndopt_dnssl->nd_opt_dnssl_len = (len + plen) / 8; + } + memset(buf, 0, packlen - (buf - rainfo->ra_data)); +} + +static int +getinet6sysctl(int code) +{ + const int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, code }; + int value; + size_t size; + + size = sizeof(value); + if (sysctl(mib, __arraycount(mib), &value, &size, NULL, 0) + < 0) { + syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %m", + __func__, code); + return -1; + } + else + return value; +} diff --git a/usr.sbin/rtadvd/config.h b/usr.sbin/rtadvd/config.h new file mode 100644 index 000000000..08153d773 --- /dev/null +++ b/usr.sbin/rtadvd/config.h @@ -0,0 +1,49 @@ +/* $NetBSD: config.h,v 1.9 2012/12/13 15:36:36 roy Exp $ */ +/* $KAME: config.h,v 1.9 2003/08/06 04:19:40 ono Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern void free_rainfo(struct rainfo *); +extern void getconfig(const char *, int); +extern void delete_prefix(struct prefix *); +extern void invalidate_prefix(struct prefix *); +extern void update_prefix(struct prefix *); +extern void make_prefix(struct rainfo *, int, struct in6_addr *, int); +extern void make_packet(struct rainfo *); +extern void get_prefix(struct rainfo *); + +/* + * it is highly unlikely to have 100 information options, + * so it should be okay to limit it + */ +#define MAXPREFIX 100 +#define MAXROUTE 100 +#define MAXRDNSS 100 +#define MAXDNSSL 100 diff --git a/usr.sbin/rtadvd/dump.c b/usr.sbin/rtadvd/dump.c new file mode 100644 index 000000000..8fe297915 --- /dev/null +++ b/usr.sbin/rtadvd/dump.c @@ -0,0 +1,280 @@ +/* $NetBSD: dump.c,v 1.12 2015/06/05 14:09:20 roy Exp $ */ +/* $KAME: dump.c,v 1.34 2004/06/14 05:35:59 itojun Exp $ */ + +/* + * Copyright (C) 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include + +#include +#include +#ifdef __FreeBSD__ +#include +#endif + +#include + +/* XXX: the following two are non-standard include files */ +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "rtadvd.h" +#include "timer.h" +#include "if.h" +#include "dump.h" + +static FILE *fp; + +static char *ether_str(struct sockaddr_dl *); +static void if_dump(void); + +static const char *rtpref_str[] = { + "medium", /* 00 */ + "high", /* 01 */ + "rsv", /* 10 */ + "low" /* 11 */ +}; + +static char * +ether_str(struct sockaddr_dl *sdl) +{ + static char hbuf[NI_MAXHOST]; + + if (sdl->sdl_alen) { + if (getnameinfo((struct sockaddr *)sdl, sdl->sdl_len, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) + snprintf(hbuf, sizeof(hbuf), ""); + } else + snprintf(hbuf, sizeof(hbuf), "NONE"); + + return(hbuf); +} + +static void +if_dump(void) +{ + struct rainfo *rai; + struct prefix *pfx; + struct rtinfo *rti; + struct rdnss *rdns; + struct rdnss_addr *rdnsa; + struct dnssl *dnsl; + struct dnssl_domain *dnsd; + char *p, len; + char prefixbuf[INET6_ADDRSTRLEN]; + struct timespec now; + + clock_gettime(CLOCK_MONOTONIC, &now); /* XXX: unused in most cases */ + TAILQ_FOREACH(rai, &ralist, next) { + fprintf(fp, "%s:\n", rai->ifname); + + fprintf(fp, " Status: %s\n", + (rai->ifflags & IFF_UP) ? "UP" : "DOWN"); + + /* control information */ + if (rai->lastsent.tv_sec) { + /* note that ctime() appends CR by itself */ + fprintf(fp, " Last RA sent: %s", + ctime((time_t *)&rai->lastsent.tv_sec)); + } + if (rai->timer) { + fprintf(fp, " Next RA will be sent: %s", + ctime((time_t *)&rai->timer->tm.tv_sec)); + } + else + fprintf(fp, " RA timer is stopped"); + fprintf(fp, " waits: %d, initcount: %d\n", + rai->waiting, rai->initcounter); + + /* statistics */ + fprintf(fp, " statistics: RA(out/in/inconsistent): " + "%llu/%llu/%llu, ", + (unsigned long long)rai->raoutput, + (unsigned long long)rai->rainput, + (unsigned long long)rai->rainconsistent); + fprintf(fp, "RS(input): %llu\n", + (unsigned long long)rai->rsinput); + + /* interface information */ + if (rai->advlinkopt) + fprintf(fp, " Link-layer address: %s\n", + ether_str(rai->sdl)); + fprintf(fp, " MTU: %d\n", rai->phymtu); + + /* Router configuration variables */ + fprintf(fp, " DefaultLifetime: %d, MaxAdvInterval: %d, " + "MinAdvInterval: %d\n", rai->lifetime, rai->maxinterval, + rai->mininterval); + fprintf(fp, " Flags: %s%s%s, ", + rai->managedflg ? "M" : "", rai->otherflg ? "O" : "", + ""); + fprintf(fp, "Preference: %s, ", + rtpref_str[(rai->rtpref >> 3) & 0xff]); + fprintf(fp, "MTU: %d\n", rai->linkmtu); + fprintf(fp, " ReachableTime: %d, RetransTimer: %d, " + "CurHopLimit: %d\n", rai->reachabletime, + rai->retranstimer, rai->hoplimit); + if (rai->clockskew) + fprintf(fp, " Clock skew: %dsec\n", + rai->clockskew); + TAILQ_FOREACH(pfx, &rai->prefix, next) { + if (pfx == TAILQ_FIRST(&rai->prefix)) + fprintf(fp, " Prefixes:\n"); + fprintf(fp, " %s/%d(", + inet_ntop(AF_INET6, &pfx->prefix, prefixbuf, + sizeof(prefixbuf)), pfx->prefixlen); + switch (pfx->origin) { + case PREFIX_FROM_KERNEL: + fprintf(fp, "KERNEL, "); + break; + case PREFIX_FROM_CONFIG: + fprintf(fp, "CONFIG, "); + break; + case PREFIX_FROM_DYNAMIC: + fprintf(fp, "DYNAMIC, "); + break; + } + if (pfx->validlifetime == ND6_INFINITE_LIFETIME) + fprintf(fp, "vltime: infinity"); + else + fprintf(fp, "vltime: %ld", + (long)pfx->validlifetime); + if (pfx->vltimeexpire != 0) + fprintf(fp, "(decr,expire %lld), ", (long long) + (pfx->vltimeexpire > now.tv_sec ? + pfx->vltimeexpire - now.tv_sec : 0)); + else + fprintf(fp, ", "); + if (pfx->preflifetime == ND6_INFINITE_LIFETIME) + fprintf(fp, "pltime: infinity"); + else + fprintf(fp, "pltime: %ld", + (long)pfx->preflifetime); + if (pfx->pltimeexpire != 0) + fprintf(fp, "(decr,expire %lld), ", (long long) + (pfx->pltimeexpire > now.tv_sec ? + pfx->pltimeexpire - now.tv_sec : 0)); + else + fprintf(fp, ", "); + fprintf(fp, "flags: %s%s%s", + pfx->onlinkflg ? "L" : "", + pfx->autoconfflg ? "A" : "", + ""); + if (pfx->timer) { + struct timespec *rest; + + rest = rtadvd_timer_rest(pfx->timer); + if (rest) { /* XXX: what if not? */ + fprintf(fp, ", expire in: %ld", + (long)rest->tv_sec); + } + } + fprintf(fp, ")\n"); + } + + TAILQ_FOREACH(rti, &rai->route, next) { + if (rti == TAILQ_FIRST(&rai->route)) + fprintf(fp, " Route Information:\n"); + fprintf(fp, " %s/%d (", + inet_ntop(AF_INET6, &rti->prefix, + prefixbuf, sizeof(prefixbuf)), + rti->prefixlen); + fprintf(fp, "preference: %s, ", + rtpref_str[0xff & (rti->rtpref >> 3)]); + if (rti->ltime == ND6_INFINITE_LIFETIME) + fprintf(fp, "lifetime: infinity"); + else + fprintf(fp, "lifetime: %ld", (long)rti->ltime); + fprintf(fp, ")\n"); + } + + TAILQ_FOREACH(rdns, &rai->rdnss, next) { + fprintf(fp, " Recursive DNS Servers:\n"); + if (rdns->lifetime == ND6_INFINITE_LIFETIME) + fprintf(fp, " lifetime: infinity\n"); + else + fprintf(fp, " lifetime: %ld\n", + (long)rdns->lifetime); + TAILQ_FOREACH(rdnsa, &rdns->list, next) + fprintf(fp, " %s\n", + inet_ntop(AF_INET6, &rdnsa->addr, + prefixbuf, sizeof(prefixbuf))); + } + + TAILQ_FOREACH(dnsl, &rai->dnssl, next) { + fprintf(fp, " DNS Search List:\n"); + if (dnsl->lifetime == ND6_INFINITE_LIFETIME) + fprintf(fp, " lifetime: infinity\n"); + else + fprintf(fp, " lifetime: %ld\n", + (long)dnsl->lifetime); + TAILQ_FOREACH(dnsd, &dnsl->list, next) { + fprintf(fp, " "); + for (p = dnsd->domain, len = *p++; + len != 0; + len = *p++) + { + if (p != dnsd->domain) + fputc('.', fp); + while(len-- != 0) + fputc(*p++, fp); + } + fputc('\n', fp); + } + } + } +} + +void +rtadvd_dump_file(const char *dumpfile) +{ + syslog(LOG_DEBUG, "<%s> dump current status to %s", __func__, + dumpfile); + + if ((fp = fopen(dumpfile, "w")) == NULL) { + syslog(LOG_WARNING, "<%s> open a dump file(%s): %m", + __func__, dumpfile); + return; + } + + if_dump(); + + fclose(fp); +} diff --git a/usr.sbin/rtadvd/dump.h b/usr.sbin/rtadvd/dump.h new file mode 100644 index 000000000..ba518bbf2 --- /dev/null +++ b/usr.sbin/rtadvd/dump.h @@ -0,0 +1,33 @@ +/* $NetBSD: dump.h,v 1.2 2011/12/10 19:14:29 roy Exp $ */ +/* $KAME: dump.h,v 1.1 2000/05/23 11:31:26 itojun Exp $ */ + +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern void rtadvd_dump_file(const char *); diff --git a/usr.sbin/rtadvd/if.c b/usr.sbin/rtadvd/if.c new file mode 100644 index 000000000..37d045c52 --- /dev/null +++ b/usr.sbin/rtadvd/if.c @@ -0,0 +1,418 @@ +/* $NetBSD: if.c,v 1.23 2015/06/05 15:41:59 roy Exp $ */ +/* $KAME: if.c,v 1.36 2004/11/30 22:32:01 suz Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __FreeBSD__ +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rtadvd.h" +#include "if.h" + +#ifndef RT_ROUNDUP +#define RT_ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len)) +#endif + +static void +get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) +{ + int i; + + for (i = 0; i < RTAX_MAX; i++) { + if (addrs & (1 << i)) { + rti_info[i] = sa; + RT_ADVANCE(sa, sa); + } + else + rti_info[i] = NULL; + } +} + +struct sockaddr_dl * +if_nametosdl(const char *name) +{ + struct ifaddrs *ifap, *ifa; + struct sockaddr_dl *sdl; + + if (getifaddrs(&ifap) != 0) + return (NULL); + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (strcmp(ifa->ifa_name, name) != 0) + continue; + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + + sdl = malloc(ifa->ifa_addr->sa_len); + if (!sdl) + continue; /*XXX*/ + + memcpy(sdl, ifa->ifa_addr, ifa->ifa_addr->sa_len); + freeifaddrs(ifap); + return (sdl); + } + + freeifaddrs(ifap); + return (NULL); +} + +int +if_getmtu(const char *name) +{ + struct ifreq ifr; + int s, mtu; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + return 0; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_addr.sa_family = AF_INET6; + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCGIFMTU, &ifr) != -1) + mtu = ifr.ifr_mtu; + else + mtu = 0; + close(s); + + return mtu; +} + +/* give interface index and its old flags, then new flags returned */ +int +if_getflags(int ifindex, int oifflags) +{ + struct ifreq ifr; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "<%s> socket: %m", __func__); + return (oifflags & ~IFF_UP); + } + + memset(&ifr, 0, sizeof(ifr)); + if_indextoname(ifindex, ifr.ifr_name); + if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) { + syslog(LOG_ERR, "<%s> ioctl:SIOCGIFFLAGS: failed for %s", + __func__, ifr.ifr_name); + close(s); + return (oifflags & ~IFF_UP); + } + close(s); + return (ifr.ifr_flags); +} + +#define ROUNDUP8(a) (1 + (((a) - 1) | 7)) +int +lladdropt_length(struct sockaddr_dl *sdl) +{ + switch (sdl->sdl_type) { + case IFT_ETHER: + case IFT_FDDI: + return(ROUNDUP8(ETHER_ADDR_LEN + 2)); + default: + return(0); + } +} + +void +lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt) +{ + char *addr; + + ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */ + + switch (sdl->sdl_type) { + case IFT_ETHER: + case IFT_FDDI: + ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3; + addr = (char *)(ndopt + 1); + memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN); + break; + default: + syslog(LOG_ERR, "<%s> unsupported link type(%d)", + __func__, sdl->sdl_type); + exit(1); + } + + return; +} + +#define FILTER_MATCH(type, filter) ((0x1 << type) & filter) +#define SIN6(s) ((struct sockaddr_in6 *)(s)) +#define SDL(s) ((struct sockaddr_dl *)(s)) +char * +get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter) +{ + struct rt_msghdr *rtm; + struct ifa_msghdr *ifam; + struct sockaddr *sa, *dst, *gw, *ifa, *rti_info[RTAX_MAX]; + + *lenp = 0; + for (rtm = (struct rt_msghdr *)buf; + rtm < (struct rt_msghdr *)lim; + rtm = (struct rt_msghdr *)(((char *)rtm) + rtm->rtm_msglen)) { + /* just for safety */ + if (!rtm->rtm_msglen) { + syslog(LOG_WARNING, "<%s> rtm_msglen is 0 " + "(buf=%p lim=%p rtm=%p)", __func__, + buf, lim, rtm); + break; + } + if (FILTER_MATCH(rtm->rtm_type, filter) == 0) { + continue; + } + + switch (rtm->rtm_type) { + case RTM_GET: + case RTM_ADD: + case RTM_DELETE: + /* address related checks */ + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + if ((dst = rti_info[RTAX_DST]) == NULL || + dst->sa_family != AF_INET6) + continue; + + if (IN6_IS_ADDR_LINKLOCAL(&SIN6(dst)->sin6_addr) || + IN6_IS_ADDR_MULTICAST(&SIN6(dst)->sin6_addr)) + continue; + + if ((gw = rti_info[RTAX_GATEWAY]) == NULL || + gw->sa_family != AF_LINK) + continue; + if (ifindex && SDL(gw)->sdl_index != ifindex) + continue; + + if (rti_info[RTAX_NETMASK] == NULL) + continue; + + /* found */ + *lenp = rtm->rtm_msglen; + return (char *)rtm; + /* NOTREACHED */ + case RTM_NEWADDR: + case RTM_DELADDR: + ifam = (struct ifa_msghdr *)rtm; + + /* address related checks */ + sa = (struct sockaddr *)(ifam + 1); + get_rtaddrs(ifam->ifam_addrs, sa, rti_info); + if ((ifa = rti_info[RTAX_IFA]) == NULL || + (ifa->sa_family != AF_INET && + ifa->sa_family != AF_INET6)) + continue; + + if (ifa->sa_family == AF_INET6 && + (IN6_IS_ADDR_LINKLOCAL(&SIN6(ifa)->sin6_addr) || + IN6_IS_ADDR_MULTICAST(&SIN6(ifa)->sin6_addr))) + continue; + + if (ifindex && ifam->ifam_index != ifindex) + continue; + + /* found */ + *lenp = ifam->ifam_msglen; + return (char *)rtm; + /* NOTREACHED */ +#ifdef RTM_IFANNOUNCE + case RTM_IFANNOUNCE: +#endif + case RTM_IFINFO: + /* found */ + *lenp = rtm->rtm_msglen; + return (char *)rtm; + /* NOTREACHED */ + } + } + + return (char *)rtm; +} +#undef FILTER_MATCH + +struct in6_addr * +get_addr(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + + return(&SIN6(rti_info[RTAX_DST])->sin6_addr); +} + +int +get_rtm_ifindex(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + + return(((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index); +} + +int +get_ifm_ifindex(char *buf) +{ + struct if_msghdr *ifm = (struct if_msghdr *)buf; + + return ((int)ifm->ifm_index); +} + +int +get_ifam_ifindex(char *buf) +{ + struct ifa_msghdr *ifam = (struct ifa_msghdr *)buf; + + return ((int)ifam->ifam_index); +} + +int +get_ifm_flags(char *buf) +{ + struct if_msghdr *ifm = (struct if_msghdr *)buf; + + return (ifm->ifm_flags); +} + +#ifdef RTM_IFANNOUNCE +int +get_ifan_ifindex(char *buf) +{ + struct if_announcemsghdr *ifan = (struct if_announcemsghdr *)buf; + + return ((int)ifan->ifan_index); +} + +int +get_ifan_what(char *buf) +{ + struct if_announcemsghdr *ifan = (struct if_announcemsghdr *)buf; + + return ((int)ifan->ifan_what); +} +#endif + +int +get_prefixlen(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + unsigned char *p, *lim; + + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + sa = rti_info[RTAX_NETMASK]; + + p = (unsigned char *)(&SIN6(sa)->sin6_addr); + lim = (unsigned char *)sa + sa->sa_len; + return prefixlen(p, lim); +} + +int +prefixlen(const unsigned char *p, const unsigned char *lim) +{ + int masklen; + + for (masklen = 0; p < lim; p++) { + switch (*p) { + case 0xff: + masklen += 8; + break; + case 0xfe: + masklen += 7; + break; + case 0xfc: + masklen += 6; + break; + case 0xf8: + masklen += 5; + break; + case 0xf0: + masklen += 4; + break; + case 0xe0: + masklen += 3; + break; + case 0xc0: + masklen += 2; + break; + case 0x80: + masklen += 1; + break; + case 0x00: + break; + default: + return(-1); + } + } + + return(masklen); +} + +int +rtmsg_type(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + + return(rtm->rtm_type); +} + +int +rtmsg_len(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + + return(rtm->rtm_msglen); +} diff --git a/usr.sbin/rtadvd/if.h b/usr.sbin/rtadvd/if.h new file mode 100644 index 000000000..7eeb7dc51 --- /dev/null +++ b/usr.sbin/rtadvd/if.h @@ -0,0 +1,54 @@ +/* $NetBSD: if.h,v 1.10 2012/12/13 15:36:36 roy Exp $ */ +/* $KAME: if.h,v 1.12 2003/09/21 07:17:03 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define RTADV_TYPE2BITMASK(type) (0x1 << type) + +struct nd_opt_hdr; +struct sockaddr_dl *if_nametosdl(const char *); +int if_getmtu(const char *); +int if_getflags(int, int); +int lladdropt_length(struct sockaddr_dl *); +void lladdropt_fill(struct sockaddr_dl *, struct nd_opt_hdr *); +char *get_next_msg(char *, char *, int, size_t *, int); +struct in6_addr *get_addr(char *); +int get_rtm_ifindex(char *); +int get_ifm_ifindex(char *); +int get_ifam_ifindex(char *); +int get_ifm_flags(char *); +#ifdef RTM_IFANNOUNCE +int get_ifan_ifindex(char *); +int get_ifan_what(char *); +#endif +int get_prefixlen(char *); +int prefixlen(const unsigned char *, const unsigned char *); +int rtmsg_type(char *); +int rtmsg_len(char *); diff --git a/usr.sbin/rtadvd/pathnames.h b/usr.sbin/rtadvd/pathnames.h new file mode 100644 index 000000000..279f1952c --- /dev/null +++ b/usr.sbin/rtadvd/pathnames.h @@ -0,0 +1,4 @@ +/* $NetBSD: pathnames.h,v 1.3 2000/05/23 11:37:59 itojun Exp $ */ +/* $KAME: pathnames.h,v 1.2 2000/05/16 13:34:13 itojun Exp $ */ + +#define _PATH_RTADVDCONF "/etc/rtadvd.conf" diff --git a/usr.sbin/rtadvd/rrenum.c b/usr.sbin/rtadvd/rrenum.c new file mode 100644 index 000000000..4ed24b19d --- /dev/null +++ b/usr.sbin/rtadvd/rrenum.c @@ -0,0 +1,488 @@ +/* $NetBSD: rrenum.c,v 1.18 2015/06/05 15:41:59 roy Exp $ */ +/* $KAME: rrenum.c,v 1.14 2004/06/14 05:36:00 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include + +#include +#ifdef __FreeBSD__ +#include +#endif +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include "rtadvd.h" +#include "rrenum.h" +#include "if.h" + +#define RR_ISSET_SEGNUM(segnum_bits, segnum) \ + ((((segnum_bits)[(segnum) >> 5]) & (1 << ((segnum) & 31))) != 0) +#define RR_SET_SEGNUM(segnum_bits, segnum) \ + (((segnum_bits)[(segnum) >> 5]) |= (1 << ((segnum) & 31))) + +struct rr_operation { + u_long rro_seqnum; + u_long rro_segnum_bits[8]; +}; + +static struct rr_operation rro; +static int rr_rcvifindex; +static int rrcmd2pco[RPM_PCO_MAX] = { + 0, + SIOCAIFPREFIX_IN6, + SIOCCIFPREFIX_IN6, + SIOCSGIFPREFIX_IN6 +}; +static int s = -1; + +/* + * Check validity of a Prefix Control Operation(PCO). + * Return 0 on success, 1 on failure. + */ +static int +rr_pco_check(int len, struct rr_pco_match *rpm) +{ + struct rr_pco_use *rpu, *rpulim; + int checklen; + + /* rpm->rpm_len must be (4N * 3) as router-renum-05.txt */ + if ((rpm->rpm_len - 3) < 0 || /* must be at least 3 */ + (rpm->rpm_len - 3) & 0x3) { /* must be multiple of 4 */ + syslog(LOG_WARNING, "<%s> rpm_len %d is not 4N * 3", + __func__, rpm->rpm_len); + return 1; + } + /* rpm->rpm_code must be valid value */ + switch (rpm->rpm_code) { + case RPM_PCO_ADD: + case RPM_PCO_CHANGE: + case RPM_PCO_SETGLOBAL: + break; + default: + syslog(LOG_WARNING, "<%s> unknown rpm_code %d", __func__, + rpm->rpm_code); + return 1; + } + /* rpm->rpm_matchlen must be 0 to 128 inclusive */ + if (rpm->rpm_matchlen > 128) { + syslog(LOG_WARNING, "<%s> rpm_matchlen %d is over 128", + __func__, rpm->rpm_matchlen); + return 1; + } + + /* + * rpu->rpu_uselen, rpu->rpu_keeplen, and sum of them must be + * between 0 and 128 inclusive + */ + for (rpu = (struct rr_pco_use *)(rpm + 1), + rpulim = (struct rr_pco_use *)((char *)rpm + len); + rpu < rpulim; + rpu += 1) { + checklen = rpu->rpu_uselen; + checklen += rpu->rpu_keeplen; + /* + * omit these check, because either of rpu_uselen + * and rpu_keeplen is unsigned char + * (128 > rpu_uselen > 0) + * (128 > rpu_keeplen > 0) + * (rpu_uselen + rpu_keeplen > 0) + */ + if (checklen > 128) { + syslog(LOG_WARNING, "<%s> sum of rpu_uselen %d and" + " rpu_keeplen %d is %d(over 128)", + __func__, rpu->rpu_uselen, + rpu->rpu_keeplen, + rpu->rpu_uselen + rpu->rpu_keeplen); + return 1; + } + } + return 0; +} + +static void +do_use_prefix(int len, struct rr_pco_match *rpm, + struct in6_rrenumreq *irr, int ifindex) +{ + struct rr_pco_use *rpu, *rpulim; + struct rainfo *rai; + struct prefix *pp; + + rpu = (struct rr_pco_use *)(rpm + 1); + rpulim = (struct rr_pco_use *)((char *)rpm + len); + + if (rpu == rpulim) { /* no use prefix */ + if (rpm->rpm_code == RPM_PCO_ADD) + return; + + irr->irr_u_uselen = 0; + irr->irr_u_keeplen = 0; + irr->irr_raf_mask_onlink = 0; + irr->irr_raf_mask_auto = 0; + irr->irr_vltime = 0; + irr->irr_pltime = 0; + memset(&irr->irr_flags, 0, sizeof(irr->irr_flags)); + irr->irr_useprefix.sin6_len = 0; /* let it mean, no addition */ + irr->irr_useprefix.sin6_family = 0; + irr->irr_useprefix.sin6_addr = in6addr_any; + if (ioctl(s, rrcmd2pco[rpm->rpm_code], irr) < 0 && + errno != EADDRNOTAVAIL) + syslog(LOG_ERR, "<%s> ioctl: %m", __func__); + return; + } + + for (rpu = (struct rr_pco_use *)(rpm + 1), + rpulim = (struct rr_pco_use *)((char *)rpm + len); + rpu < rpulim; + rpu += 1) { + /* init in6_rrenumreq fields */ + irr->irr_u_uselen = rpu->rpu_uselen; + irr->irr_u_keeplen = rpu->rpu_keeplen; + irr->irr_raf_mask_onlink = + !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK); + irr->irr_raf_mask_auto = + !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_AUTO); + irr->irr_vltime = ntohl(rpu->rpu_vltime); + irr->irr_pltime = ntohl(rpu->rpu_pltime); + irr->irr_raf_onlink = + (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK) == 0 ? 0 : 1; + irr->irr_raf_auto = + (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_AUTO) == 0 ? 0 : 1; + irr->irr_rrf_decrvalid = + (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME) == 0 ? 0 : 1; + irr->irr_rrf_decrprefd = + (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME) == 0 ? 0 : 1; + irr->irr_useprefix.sin6_len = sizeof(irr->irr_useprefix); + irr->irr_useprefix.sin6_family = AF_INET6; + irr->irr_useprefix.sin6_addr = rpu->rpu_prefix; + + if (ioctl(s, rrcmd2pco[rpm->rpm_code], irr) < 0 && + errno != EADDRNOTAVAIL) + syslog(LOG_ERR, "<%s> ioctl: %m", __func__); + + /* very adhoc: should be rewritten */ + if (rpm->rpm_code == RPM_PCO_CHANGE && + IN6_ARE_ADDR_EQUAL(&rpm->rpm_prefix, &rpu->rpu_prefix) && + rpm->rpm_matchlen == rpu->rpu_uselen && + rpu->rpu_uselen == rpu->rpu_keeplen) { + if ((rai = if_indextorainfo(ifindex)) == NULL) + continue; /* non-advertising IF */ + + TAILQ_FOREACH(pp, &rai->prefix, next) { + struct timespec now; + + if (prefix_match(&pp->prefix, pp->prefixlen, + &rpm->rpm_prefix, + rpm->rpm_matchlen)) { + /* change parameters */ + pp->validlifetime = ntohl(rpu->rpu_vltime); + pp->preflifetime = ntohl(rpu->rpu_pltime); + if (irr->irr_rrf_decrvalid) { + clock_gettime(CLOCK_MONOTONIC, + &now); + pp->vltimeexpire = + now.tv_sec + pp->validlifetime; + } else + pp->vltimeexpire = 0; + if (irr->irr_rrf_decrprefd) { + clock_gettime(CLOCK_MONOTONIC, + &now); + pp->pltimeexpire = + now.tv_sec + pp->preflifetime; + } else + pp->pltimeexpire = 0; + } + } + } + } +} + +/* + * process a Prefix Control Operation(PCO). + * return 0 on success, 1 on failure + */ +static int +do_pco(struct icmp6_router_renum *rr, int len, struct rr_pco_match *rpm) +{ + int ifindex = 0; + struct in6_rrenumreq irr; + struct rainfo *rai; + + if ((rr_pco_check(len, rpm) != 0)) + return 1; + + if (s == -1 && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "<%s> socket: %m", __func__); + exit(1); + } + + memset(&irr, 0, sizeof(irr)); + irr.irr_origin = PR_ORIG_RR; + irr.irr_m_len = rpm->rpm_matchlen; + irr.irr_m_minlen = rpm->rpm_minlen; + irr.irr_m_maxlen = rpm->rpm_maxlen; + irr.irr_matchprefix.sin6_len = sizeof(irr.irr_matchprefix); + irr.irr_matchprefix.sin6_family = AF_INET6; + irr.irr_matchprefix.sin6_addr = rpm->rpm_prefix; + + /* + * if ICMP6_RR_FLAGS_FORCEAPPLY(A flag) is 0 and IFF_UP is off, + * the interface is not applied + */ + + if (rr->rr_flags & ICMP6_RR_FLAGS_FORCEAPPLY) { + while (if_indextoname(++ifindex, irr.irr_name)) { + rai = if_indextorainfo(ifindex); + if (rai && (rai->ifflags & IFF_UP)) { + /* TODO: interface scope check */ + do_use_prefix(len, rpm, &irr, ifindex); + } + } + } + if (errno == ENXIO) + return 0; + else if (errno) { + syslog(LOG_ERR, "<%s> if_indextoname: %m", __func__); + return 1; + } + return 0; +} + +/* + * call do_pco() for each Prefix Control Operations(PCOs) in a received + * Router Renumbering Command packet. + * return 0 on success, 1 on failure + */ +static int +do_rr(size_t len, struct icmp6_router_renum *rr) +{ + struct rr_pco_match *rpm; + char *cp, *lim; + + lim = (char *)rr + len; + cp = (char *)(rr + 1); + len -= sizeof(struct icmp6_router_renum); + + while (cp < lim) { + size_t rpmlen; + + rpm = (struct rr_pco_match *)cp; + if (len < sizeof(struct rr_pco_match)) { + tooshort: + syslog(LOG_ERR, "<%s> pkt too short. left len = %zd. " + "garbage at end of pkt?", __func__, len); + return 1; + } + rpmlen = rpm->rpm_len << 3; + if (len < rpmlen) + goto tooshort; + + if (do_pco(rr, rpmlen, rpm)) { + syslog(LOG_WARNING, "<%s> invalid PCO", __func__); + goto next; + } + + next: + cp += rpmlen; + len -= rpmlen; + } + + return 0; +} + +/* + * check validity of a router renumbering command packet + * return 0 on success, 1 on failure + */ +static int +rr_command_check(size_t len, struct icmp6_router_renum *rr, + struct in6_addr *from, struct in6_addr *dst) +{ + char ntopbuf[INET6_ADDRSTRLEN]; + + /* omit rr minimal length check. hope kernel have done it. */ + /* rr_command length check */ + if (len < (sizeof(struct icmp6_router_renum) + + sizeof(struct rr_pco_match))) { + syslog(LOG_ERR, "<%s> rr_command len %zd is too short", + __func__, len); + return 1; + } + + /* destination check. only for multicast. omit unicast check. */ + if (IN6_IS_ADDR_MULTICAST(dst) && !IN6_IS_ADDR_MC_LINKLOCAL(dst) && + !IN6_IS_ADDR_MC_SITELOCAL(dst)) { + syslog(LOG_ERR, "<%s> dst mcast addr %s is illegal", + __func__, + inet_ntop(AF_INET6, dst, ntopbuf, INET6_ADDRSTRLEN)); + return 1; + } + + /* seqnum and segnum check */ + if (rro.rro_seqnum > rr->rr_seqnum) { + syslog(LOG_WARNING, + "<%s> rcvd old seqnum %d from %s", + __func__, (uint32_t)ntohl(rr->rr_seqnum), + inet_ntop(AF_INET6, from, ntopbuf, INET6_ADDRSTRLEN)); + return 1; + } + if (rro.rro_seqnum == rr->rr_seqnum && + (rr->rr_flags & ICMP6_RR_FLAGS_TEST) == 0 && + RR_ISSET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum)) { + if ((rr->rr_flags & ICMP6_RR_FLAGS_REQRESULT) != 0) + syslog(LOG_WARNING, + "<%s> rcvd duped segnum %d from %s", + __func__, rr->rr_segnum, + inet_ntop(AF_INET6, from, ntopbuf, + INET6_ADDRSTRLEN)); + return 0; + } + + /* update seqnum */ + if (rro.rro_seqnum != rr->rr_seqnum) { + /* then must be "<" */ + + /* init rro_segnum_bits */ + memset(rro.rro_segnum_bits, 0, + sizeof(rro.rro_segnum_bits)); + } + rro.rro_seqnum = rr->rr_seqnum; + + return 0; +} + +static void +rr_command_input(int len, struct icmp6_router_renum *rr, + struct in6_addr *from, struct in6_addr *dst) +{ + /* rr_command validity check */ + if (rr_command_check(len, rr, from, dst)) + goto failed; + if ((rr->rr_flags & (ICMP6_RR_FLAGS_TEST|ICMP6_RR_FLAGS_REQRESULT)) == + ICMP6_RR_FLAGS_TEST) + return; + + /* do router renumbering */ + if (do_rr(len, rr)) { + goto failed; + } + + /* update segnum */ + RR_SET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum); + + return; + + failed: + syslog(LOG_ERR, "<%s> received RR was invalid", __func__); + return; +} + +void +rr_input(size_t len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi, + struct sockaddr_in6 *from, struct in6_addr *dst) +{ + char ntopbuf[2][INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + + syslog(LOG_DEBUG, + "<%s> RR received from %s to %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf[0], INET6_ADDRSTRLEN), + inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + + /* packet validation based on Section 4.1 of RFC2894 */ + if (len < sizeof(struct icmp6_router_renum)) { + syslog(LOG_NOTICE, + "<%s>: RR short message (size %zd) from %s to %s on %s", + __func__, len, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf[0], INET6_ADDRSTRLEN), + inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + + /* + * If the IPv6 destination address is neither an All Routers multicast + * address [AARCH] nor one of the receiving router's unicast addresses, + * the message MUST be discarded and SHOULD be logged to network + * management. + * We rely on the kernel input routine for unicast addresses, and thus + * check multicast destinations only. + */ + if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && + !IN6_ARE_ADDR_EQUAL(&sin6_sitelocal_allrouters.sin6_addr, + &pi->ipi6_addr)) + { + syslog(LOG_NOTICE, + "<%s>: RR message with invalid destination (%s) " + "from %s on %s", + __func__, + inet_ntop(AF_INET6, &dst, ntopbuf[0], INET6_ADDRSTRLEN), + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf[1], INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + + rr_rcvifindex = pi->ipi6_ifindex; + + switch (rr->rr_code) { + case ICMP6_ROUTER_RENUMBERING_COMMAND: + rr_command_input(len, rr, &from->sin6_addr, dst); + /* TODO: send reply msg */ + break; + case ICMP6_ROUTER_RENUMBERING_RESULT: + /* RESULT will be processed by rrenumd */ + break; + case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET: + /* TODO: sequence number reset */ + break; + default: + syslog(LOG_ERR, "<%s> received unknown code %d", + __func__, rr->rr_code); + break; + + } + + return; +} diff --git a/usr.sbin/rtadvd/rrenum.h b/usr.sbin/rtadvd/rrenum.h new file mode 100644 index 000000000..482445c57 --- /dev/null +++ b/usr.sbin/rtadvd/rrenum.h @@ -0,0 +1,34 @@ +/* $NetBSD: rrenum.h,v 1.5 2011/12/10 19:14:29 roy Exp $ */ +/* $KAME: rrenum.h,v 1.3 2001/01/21 15:37:14 itojun Exp $ */ + +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +void rr_input(size_t, struct icmp6_router_renum *, struct in6_pktinfo *, + struct sockaddr_in6 *, struct in6_addr *); diff --git a/usr.sbin/rtadvd/rtadvd.8 b/usr.sbin/rtadvd/rtadvd.8 new file mode 100644 index 000000000..860858d98 --- /dev/null +++ b/usr.sbin/rtadvd/rtadvd.8 @@ -0,0 +1,216 @@ +.\" $NetBSD: rtadvd.8,v 1.24 2012/12/13 21:49:38 wiz Exp $ +.\" $KAME: rtadvd.8,v 1.24 2002/05/31 16:16:08 jinmei Exp $ +.\" +.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd December 13, 2006 +.Dt RTADVD 8 +.Os +.Sh NAME +.Nm rtadvd +.Nd router advertisement daemon +.Sh SYNOPSIS +.Nm +.Op Fl DdfRs +.Op Fl c Ar configfile +.Op Fl M Ar ifname +.Ar interface ... +.Sh DESCRIPTION +.Nm +sends router advertisement packets to the specified interfaces. +.Pp +The program will daemonize itself on invocation. +It will then send router advertisement packets periodically, as well +as in response to router solicitation messages sent by end hosts. +.Pp +Router advertisements can be configured on a per-interface basis, as +described in +.Xr rtadvd.conf 5 . +.Pp +If there is no configuration file entry for an interface, +or if the configuration file does not exist at all, +.Nm +sets all the parameters to their default values. +In particular, +.Nm +reads all the interface routes from the routing table and advertises +them as on-link prefixes. +.Pp +.Nm +also watches the routing table. +If an interface direct route is +added on an advertising interface and no static prefixes are +specified by the configuration file, +.Nm +adds the corresponding prefix to its advertising list. +.Pp +Similarly, when an interface direct route is deleted, +.Nm +will start advertising the prefixes with zero valid and preferred +lifetimes to help the receiving hosts switch to a new prefix when +renumbering. +Note, however, that the zero valid lifetime cannot invalidate the +autoconfigured addresses at a receiving host immediately. +According to the specification, the host will retain the address +for a certain period, which will typically be two hours. +The zero lifetimes rather intend to make the address deprecated, +indicating that a new non-deprecated address should be used as the +source address of a new connection. +This behavior will last for two hours. +Then +.Nm +will completely remove the prefix from the advertising list, +and succeeding advertisements will not contain the prefix information. +.Pp +Moreover, if the status of an advertising interface changes, +.Nm +will start or stop sending router advertisements according +to the latest status. +.Pp +The +.Fl s +option may be used to disable this behavior; +.Nm +will not watch the routing table and the whole functionality described +above will be suppressed. +.Pp +Basically, hosts MUST NOT send Router Advertisement messages at any +time (RFC 2461, Section 6.2.3). +However, it would sometimes be useful to allow hosts to advertise some +parameters such as prefix information and link MTU. +Thus, +.Nm +can be invoked if router lifetime is explicitly set to zero on every +advertising interface. +.Pp +The command line options are: +.Bl -tag -width indent +.\" +.It Fl c Ar configfile +Specify an alternate location, +.Ar configfile , +for the configuration file. +By default, +.Pa /etc/rtadvd.conf +is used. +.It Fl D +Even more debugging information than that offered by the +.Fl d +option is printed. +.It Fl d +Print debugging information. +.It Fl f +Foreground mode (useful when debugging). +Log messages will be dumped to stderr when this option is specified. +.It Fl M Ar ifname +Specify an interface to join the all-routers site-local multicast group. +By default, +.Nm +tries to join the first advertising interface appearing on the command +line. +This option has meaning only with the +.Fl R +option, which enables routing renumbering protocol support. +.\".It Fl m +.\"Enables mobile IPv6 support. +.\"This changes the content of router advertisement option, as well as +.\"permitted configuration directives. +.It Fl R +Accept router renumbering requests. +If you enable it, an +.Xr ipsec 4 +setup is suggested for security reasons. +.\"On KAME-based systems, +.\".Xr rrenumd 8 +.\"generates router renumbering request packets. +This option is currently disabled, and is ignored by +.Nm +with a warning message. +.It Fl s +Do not add or delete prefixes dynamically. +Only statically configured prefixes, if any, will be advertised. +.El +.Pp +Use +.Dv SIGHUP +to reload the configuration file +.Pa /etc/rtadvd.conf . +If an invalid parameter is found in the configuration file upon the reload, the +entry will be ignored and the old configuration will be used. +When parameters in an existing entry are updated, +.Nm +will send Router Advertisement messages with the old configuration but zero +router lifetime to the interface first, and then start to send a new message. +.Pp +Upon receipt of signal +.Dv SIGUSR1 , +.Nm +will dump the current internal state into +.Pa /var/run/rtadvd.dump . +.Pp +Use +.Dv SIGTERM +to kill +.Nm +gracefully. +In this case, +.Nm +will transmit router advertisement with router lifetime 0 +to all the interfaces +.Pq in accordance with RFC 2461 6.2.5 . +.Sh FILES +.Bl -tag -width /var/run/rtadvd.dumpXX -compact +.It Pa /etc/rtadvd.conf +The default configuration file. +.It Pa /var/run/rtadvd.pid +Contains the PID of the currently running +.Nm . +.It Pa /var/run/rtadvd.dump +The file in which +.Nm +dumps its internal state. +.El +.Sh EXIT STATUS +.Ex -std rtadvd +.Sh SEE ALSO +.Xr rtadvd.conf 5 , +.Xr rtsol 8 +.Sh HISTORY +The +.Nm +command first appeared in the WIDE Hydrangea IPv6 protocol stack kit. +.Sh BUGS +There used to be some text that recommended users not to let +.Nm +advertise Router Advertisement messages on an upstream link to avoid +undesirable +.Xr icmp6 4 +redirect messages. +However, based on later discussion in the IETF IPng working group, +all routers should rather advertise the messages regardless of +the network topology, in order to ensure reachability. diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c new file mode 100644 index 000000000..eb2c51c50 --- /dev/null +++ b/usr.sbin/rtadvd/rtadvd.c @@ -0,0 +1,1820 @@ +/* $NetBSD: rtadvd.c,v 1.50 2015/06/15 04:15:33 ozaki-r Exp $ */ +/* $KAME: rtadvd.c,v 1.92 2005/10/17 14:40:02 suz Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__NetBSD__) || defined(__minix) +#include +#endif +#include +#include + +#include "rtadvd.h" +#include "rrenum.h" +#include "advcap.h" +#include "timer.h" +#include "if.h" +#include "config.h" +#include "dump.h" + +struct msghdr rcvmhdr; +static unsigned char *rcvcmsgbuf; +static size_t rcvcmsgbuflen; +static unsigned char *sndcmsgbuf; +static size_t sndcmsgbuflen; +volatile sig_atomic_t do_dump; +volatile sig_atomic_t do_reconf; +volatile sig_atomic_t do_die; +struct msghdr sndmhdr; +struct iovec rcviov[2]; +struct iovec sndiov[2]; +struct sockaddr_in6 rcvfrom; +static const char *dumpfilename = "/var/run/rtadvd.dump"; /* XXX configurable */ +static char *mcastif; +int sock; +int rtsock = -1; +int accept_rr = 0; +int dflag = 0, sflag = 0; + +static char **if_argv; +static int if_argc; + +char *conffile = NULL; + +struct ralist_head_t ralist = TAILQ_HEAD_INITIALIZER(ralist); + +struct nd_optlist { + TAILQ_ENTRY(nd_optlist) next; + struct nd_opt_hdr *opt; +}; +union nd_opts { + struct nd_opt_hdr *nd_opt_array[9]; + struct { + struct nd_opt_hdr *zero; + struct nd_opt_hdr *src_lladdr; + struct nd_opt_hdr *tgt_lladdr; + struct nd_opt_prefix_info *pi; + struct nd_opt_rd_hdr *rh; + struct nd_opt_mtu *mtu; + TAILQ_HEAD(, nd_optlist) list; + } nd_opt_each; +}; +#define nd_opts_src_lladdr nd_opt_each.src_lladdr +#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr +#define nd_opts_pi nd_opt_each.pi +#define nd_opts_rh nd_opt_each.rh +#define nd_opts_mtu nd_opt_each.mtu +#define nd_opts_list nd_opt_each.list + +#define NDOPT_FLAG_SRCLINKADDR (1 << 0) +#define NDOPT_FLAG_TGTLINKADDR (1 << 1) +#define NDOPT_FLAG_PREFIXINFO (1 << 2) +#define NDOPT_FLAG_RDHDR (1 << 3) +#define NDOPT_FLAG_MTU (1 << 4) +#define NDOPT_FLAG_RDNSS (1 << 5) +#define NDOPT_FLAG_DNSSL (1 << 6) + +uint32_t ndopt_flags[] = { + [ND_OPT_SOURCE_LINKADDR] = NDOPT_FLAG_SRCLINKADDR, + [ND_OPT_TARGET_LINKADDR] = NDOPT_FLAG_TGTLINKADDR, + [ND_OPT_PREFIX_INFORMATION] = NDOPT_FLAG_PREFIXINFO, + [ND_OPT_REDIRECTED_HEADER] = NDOPT_FLAG_RDHDR, + [ND_OPT_MTU] = NDOPT_FLAG_MTU, + [ND_OPT_RDNSS] = NDOPT_FLAG_RDNSS, + [ND_OPT_DNSSL] = NDOPT_FLAG_DNSSL, +}; + +struct sockaddr_in6 sin6_linklocal_allnodes = { + .sin6_len = sizeof(sin6_linklocal_allnodes), + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LINKLOCAL_ALLNODES_INIT, +}; +#ifdef notdef +struct sockaddr_in6 sin6_linklocal_allrouters = { + .sin6_len = sizeof(sin6_linklocal_allrouters), + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT, +}; +#endif +struct sockaddr_in6 sin6_sitelocal_allrouters = { + .sin6_len = sizeof(sin6_sitelocal_allrouters), + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_SITELOCAL_ALLROUTERS_INIT, +}; + +static void set_die(int); +static void die(void); +static void set_reconf(int); +static void sock_open(void); +static void rtsock_open(void); +static void rtadvd_input(void); +static void rs_input(int, struct nd_router_solicit *, + struct in6_pktinfo *, struct sockaddr_in6 *); +static void ra_input(int, struct nd_router_advert *, + struct in6_pktinfo *, struct sockaddr_in6 *); +static struct rainfo *ra_output(struct rainfo *); +static int prefix_check(struct nd_opt_prefix_info *, struct rainfo *, + struct sockaddr_in6 *); +static int nd6_options(struct nd_opt_hdr *, int, union nd_opts *, uint32_t); +static void free_ndopts(union nd_opts *); +static void rtmsg_input(void); +static void rtadvd_set_dump_file(int); + +int +main(int argc, char *argv[]) +{ + struct pollfd set[2]; + struct timespec *timeout; + int i, ch; + int fflag = 0, logopt; + struct passwd *pw; + + /* get command line options and arguments */ +#define OPTIONS "c:dDfM:Rs" + while ((ch = getopt(argc, argv, OPTIONS)) != -1) { +#undef OPTIONS + switch (ch) { + case 'c': + conffile = optarg; + break; + case 'd': + dflag = 1; + break; + case 'D': + dflag = 2; + break; + case 'f': + fflag = 1; + break; + case 'M': + mcastif = optarg; + break; + case 'R': + fprintf(stderr, "rtadvd: " + "the -R option is currently ignored.\n"); + /* accept_rr = 1; */ + /* run anyway... */ + break; + case 's': + sflag = 1; + break; + } + } + argc -= optind; + argv += optind; + if (argc == 0) { + fprintf(stderr, + "usage: rtadvd [-DdfRs] [-c conffile]" + " [-M ifname] interface ...\n"); + exit(1); + } + + logopt = LOG_NDELAY | LOG_PID; + if (fflag) + logopt |= LOG_PERROR; + openlog("rtadvd", logopt, LOG_DAEMON); + + /* set log level */ + if (dflag == 0) + (void)setlogmask(LOG_UPTO(LOG_ERR)); + if (dflag == 1) + (void)setlogmask(LOG_UPTO(LOG_INFO)); + + errno = 0; /* Ensure errno is 0 so we know if getpwnam errors or not */ + if ((pw = getpwnam(RTADVD_USER)) == NULL) { + if (errno == 0) + syslog(LOG_ERR, + "user %s does not exist, aborting", + RTADVD_USER); + else + syslog(LOG_ERR, "getpwnam: %s: %m", RTADVD_USER); + exit(1); + } + + /* timer initialization */ + rtadvd_timer_init(); + + if_argc = argc; + if_argv = argv; + while (argc--) + getconfig(*argv++, 1); + + if (!fflag) + daemon(1, 0); + + sock_open(); + +#if defined(__NetBSD__) || defined(__minix) + /* record the current PID */ + if (pidfile(NULL) < 0) { + syslog(LOG_ERR, + "<%s> failed to open the pid log file, run anyway.", + __func__); + } +#endif + + set[0].fd = sock; + set[0].events = POLLIN; + if (sflag == 0) { + rtsock_open(); + set[1].fd = rtsock; + set[1].events = POLLIN; + } else + set[1].fd = -1; + + syslog(LOG_INFO, "dropping privileges to %s", RTADVD_USER); + if (chroot(pw->pw_dir) == -1) { + syslog(LOG_ERR, "chroot: %s: %m", pw->pw_dir); + exit(1); + } + if (chdir("/") == -1) { + syslog(LOG_ERR, "chdir: /: %m"); + exit(1); + } + if (setgroups(1, &pw->pw_gid) == -1 || + setgid(pw->pw_gid) == -1 || + setuid(pw->pw_uid) == -1) + { + syslog(LOG_ERR, "failed to drop privileges: %m"); + exit(1); + } + + signal(SIGINT, set_die); + signal(SIGTERM, set_die); + signal(SIGHUP, set_reconf); + signal(SIGUSR1, rtadvd_set_dump_file); + + for (;;) { + if (do_dump) { /* SIGUSR1 */ + do_dump = 0; + rtadvd_dump_file(dumpfilename); + } + + if (do_reconf) { /* SIGHUP */ + do_reconf = 0; + syslog(LOG_INFO, "<%s> reloading config on SIGHUP", + __func__); + argc = if_argc; + argv = if_argv; + while (argc--) + getconfig(*argv++, 0); + } + + /* timer expiration check and reset the timer */ + timeout = rtadvd_check_timer(); + + if (do_die) { + die(); + /*NOTREACHED*/ + } + + if (timeout != NULL) { + syslog(LOG_DEBUG, + "<%s> set timer to %ld:%ld. waiting for " + "inputs or timeout", __func__, + (long int)timeout->tv_sec, + (long int)timeout->tv_nsec); + } else { + syslog(LOG_DEBUG, + "<%s> there's no timer. waiting for inputs", + __func__); + } + + if ((i = poll(set, 2, timeout ? (timeout->tv_sec * 1000 + + (timeout->tv_nsec + 999999) / 1000000) : INFTIM)) < 0) + { + /* EINTR would occur upon SIGUSR1 for status dump */ + if (errno != EINTR) + syslog(LOG_ERR, "<%s> poll: %m", __func__); + continue; + } + if (i == 0) /* timeout */ + continue; + if (rtsock != -1 && set[1].revents & POLLIN) + rtmsg_input(); + if (set[0].revents & POLLIN) + rtadvd_input(); + } + exit(0); /* NOTREACHED */ +} + +static void +rtadvd_set_dump_file(__unused int sig) +{ + + do_dump = 1; +} + +static void +set_reconf(__unused int sig) +{ + + do_reconf = 1; +} + +static void +set_die(__unused int sig) +{ + + do_die = 1; +} + +static void +die(void) +{ + static int waiting; + struct rainfo *rai, *ran; + struct rdnss *rdnss; + struct dnssl *dnssl; + + if (waiting) { + if (TAILQ_FIRST(&ralist)) { + syslog(LOG_INFO, + "<%s> waiting for expiration of all RA timers", + __func__); + return; + } + syslog(LOG_NOTICE, "<%s> gracefully terminated", __func__); + free(rcvcmsgbuf); + free(sndcmsgbuf); + exit(0); + /* NOT REACHED */ + } + + if (TAILQ_FIRST(&ralist) == NULL) { + syslog(LOG_NOTICE, "<%s> gracefully terminated", __func__); + exit(0); + /* NOT REACHED */ + } + + waiting = 1; + syslog(LOG_NOTICE, "<%s> final RA transmission started", __func__); + + TAILQ_FOREACH_SAFE(rai, &ralist, next, ran) { + if (rai->leaving) { + TAILQ_REMOVE(&ralist, rai, next); + TAILQ_INSERT_HEAD(&ralist, rai->leaving, next); + rai->leaving->leaving = rai->leaving; + rai->leaving->leaving_for = rai->leaving; + free_rainfo(rai); + continue; + } + rai->lifetime = 0; + TAILQ_FOREACH(rdnss, &rai->rdnss, next) + rdnss->lifetime = 0; + TAILQ_FOREACH(dnssl, &rai->dnssl, next) + dnssl->lifetime = 0; + make_packet(rai); + rai->leaving = rai; + rai->leaving_for = rai; + rai->initcounter = MAX_INITIAL_RTR_ADVERTISEMENTS; + rai->mininterval = MIN_DELAY_BETWEEN_RAS; + rai->maxinterval = MIN_DELAY_BETWEEN_RAS; + rai->leaving_adv = MAX_FINAL_RTR_ADVERTISEMENTS; + ra_output(rai); + ra_timer_update((void *)rai, &rai->timer->tm); + rtadvd_set_timer(&rai->timer->tm, rai->timer); + } +} + +static void +rtmsg_input(void) +{ + int n, type, ifindex = 0, plen; + size_t len; + union rt_msghdr_buf { + struct rt_msghdr rt_msghdr; + char data[2048]; + } buffer; + char *msg, *next, *lim, **argv; + char ifname[IF_NAMESIZE]; + struct prefix *prefix; + struct rainfo *rai; + struct in6_addr *addr; + char addrbuf[INET6_ADDRSTRLEN]; + int prefixchange = 0, argc; + + memset(&buffer, 0, sizeof(buffer)); + n = read(rtsock, &buffer, sizeof(buffer)); + + /* We read the buffer first to clear the FD */ + if (do_die) + return; + + msg = buffer.data; + if (dflag > 1) { + syslog(LOG_DEBUG, "<%s> received a routing message " + "(type = %d, len = %d)", __func__, rtmsg_type(msg), + rtmsg_len(msg)); + } + if (n > rtmsg_len(msg)) { + /* + * This usually won't happen for messages received on + * a routing socket. + */ + if (dflag > 1) + syslog(LOG_DEBUG, + "<%s> received data length is larger than " + "1st routing message len. multiple messages? " + "read %d bytes, but 1st msg len = %d", + __func__, n, rtmsg_len(msg)); +#if 0 + /* adjust length */ + n = rtmsg_len(msg); +#endif + } + + lim = msg + n; + for (next = msg; next < lim; next += len) { + int oldifflags; + + next = get_next_msg(next, lim, 0, &len, + RTADV_TYPE2BITMASK(RTM_ADD) | + RTADV_TYPE2BITMASK(RTM_DELETE) | + RTADV_TYPE2BITMASK(RTM_NEWADDR) | + RTADV_TYPE2BITMASK(RTM_DELADDR) | +#ifdef RTM_IFANNOUNCE + RTADV_TYPE2BITMASK(RTM_IFANNOUNCE) | +#endif + RTADV_TYPE2BITMASK(RTM_IFINFO)); + if (len == 0) + break; + type = rtmsg_type(next); + switch (type) { + case RTM_ADD: + case RTM_DELETE: + ifindex = get_rtm_ifindex(next); + break; + case RTM_NEWADDR: + case RTM_DELADDR: + ifindex = get_ifam_ifindex(next); + break; +#ifdef RTM_IFANNOUNCE + case RTM_IFANNOUNCE: + ifindex = get_ifan_ifindex(next); + if (get_ifan_what(next) == IFAN_ARRIVAL) { + syslog(LOG_DEBUG, + "<%s> interface %s arrived", + __func__, + if_indextoname(ifindex, ifname)); + if (if_argc == 0) { + getconfig(ifname, 0); + continue; + } + argc = if_argc; + argv = if_argv; + while (argc--) { + if (strcmp(ifname, *argv++) == 0) { + getconfig(ifname, 0); + break; + } + } + continue; + } + break; +#endif + case RTM_IFINFO: + ifindex = get_ifm_ifindex(next); + break; + default: + /* should not reach here */ + if (dflag > 1) { + syslog(LOG_DEBUG, + "<%s:%d> unknown rtmsg %d on %s", + __func__, __LINE__, type, + if_indextoname(ifindex, ifname)); + } + continue; + } + + if ((rai = if_indextorainfo(ifindex)) == NULL) { + if (dflag > 1) { + syslog(LOG_DEBUG, + "<%s> route changed on " + "non advertising interface %s (%d)", + __func__, + if_indextoname(ifindex, ifname), + ifindex); + } + continue; + } + oldifflags = rai->ifflags; + + switch (type) { + case RTM_ADD: + /* init ifflags because it may have changed */ + rai->ifflags = if_getflags(ifindex, rai->ifflags); + + if (sflag) + break; /* we aren't interested in prefixes */ + + addr = get_addr(msg); + plen = get_prefixlen(msg); + /* sanity check for plen */ + /* as RFC2373, prefixlen is at least 4 */ + if (plen < 4 || plen > 127) { + syslog(LOG_INFO, "<%s> new interface route's" + "plen %d is invalid for a prefix", + __func__, plen); + break; + } + prefix = find_prefix(rai, addr, plen); + if (prefix) { + if (prefix->timer) { + /* + * If the prefix has been invalidated, + * make it available again. + */ + update_prefix(prefix); + prefixchange = 1; + } else if (dflag > 1) { + syslog(LOG_DEBUG, + "<%s> new prefix(%s/%d) " + "added on %s, " + "but it was already in list", + __func__, + inet_ntop(AF_INET6, addr, + (char *)addrbuf, INET6_ADDRSTRLEN), + plen, rai->ifname); + } + break; + } + make_prefix(rai, ifindex, addr, plen); + prefixchange = 1; + break; + case RTM_DELETE: + /* init ifflags because it may have changed */ + rai->ifflags = if_getflags(ifindex, rai->ifflags); + + if (sflag) + break; + + addr = get_addr(msg); + plen = get_prefixlen(msg); + /* sanity check for plen */ + /* as RFC2373, prefixlen is at least 4 */ + if (plen < 4 || plen > 127) { + syslog(LOG_INFO, + "<%s> deleted interface route's " + "plen %d is invalid for a prefix", + __func__, plen); + break; + } + prefix = find_prefix(rai, addr, plen); + if (prefix == NULL) { + if (dflag > 1) { + syslog(LOG_DEBUG, + "<%s> prefix(%s/%d) was " + "deleted on %s, " + "but it was not in list", + __func__, + inet_ntop(AF_INET6, addr, + (char *)addrbuf, INET6_ADDRSTRLEN), + plen, rai->ifname); + } + break; + } + invalidate_prefix(prefix); + prefixchange = 1; + break; + case RTM_NEWADDR: + case RTM_DELADDR: + /* init ifflags because it may have changed */ + rai->ifflags = if_getflags(ifindex, rai->ifflags); + break; + case RTM_IFINFO: + rai->ifflags = get_ifm_flags(next); + break; +#ifdef RTM_IFANNOUNCE + case RTM_IFANNOUNCE: + if (get_ifan_what(next) == IFAN_DEPARTURE) { + syslog(LOG_DEBUG, + "<%s> interface %s departed", + __func__, rai->ifname); + TAILQ_REMOVE(&ralist, rai, next); + if (rai->leaving) + free_rainfo(rai->leaving); + free_rainfo(rai); + continue; + } + break; +#endif + default: + /* should not reach here */ + if (dflag > 1) { + syslog(LOG_DEBUG, + "<%s:%d> unknown rtmsg %d on %s", + __func__, __LINE__, type, + if_indextoname(ifindex, ifname)); + } + return; + } + + /* check if an interface flag is changed */ + if ((oldifflags & IFF_UP) != 0 && /* UP to DOWN */ + (rai->ifflags & IFF_UP) == 0) { + syslog(LOG_INFO, + "<%s> interface %s becomes down. stop timer.", + __func__, rai->ifname); + rtadvd_remove_timer(&rai->timer); + } else if ((oldifflags & IFF_UP) == 0 && /* DOWN to UP */ + (rai->ifflags & IFF_UP) != 0) { + syslog(LOG_INFO, + "<%s> interface %s becomes up. restart timer.", + __func__, rai->ifname); + + rai->initcounter = 0; /* reset the counter */ + rai->waiting = 0; /* XXX */ + rtadvd_remove_timer(&rai->timer); + rai->timer = rtadvd_add_timer(ra_timeout, + ra_timer_update, rai, rai); + ra_timer_update((void *)rai, &rai->timer->tm); + rtadvd_set_timer(&rai->timer->tm, rai->timer); + } else if (prefixchange && rai->ifflags & IFF_UP) { + /* + * An advertised prefix has been added or invalidated. + * Will notice the change in a short delay. + */ + rai->initcounter = 0; + ra_timer_set_short_delay(rai); + } + } + + return; +} + +void +rtadvd_input(void) +{ + ssize_t i; + int *hlimp = NULL; +#ifdef OLDRAWSOCKET + struct ip6_hdr *ip; +#endif + struct icmp6_hdr *icp; + int ifindex = 0; + struct cmsghdr *cm; + struct in6_pktinfo *pi = NULL; + char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + struct in6_addr dst = in6addr_any; + struct rainfo *rai; + + /* + * Get message. We reset msg_controllen since the field could + * be modified if we had received a message before setting + * receive options. + */ + rcvmhdr.msg_controllen = rcvcmsgbuflen; + if ((i = recvmsg(sock, &rcvmhdr, 0)) < 0) + return; + + /* We read the buffer first to clear the FD */ + if (do_die) + return; + + /* extract optional information via Advanced API */ + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); + cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_PKTINFO && + cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { + pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); + ifindex = pi->ipi6_ifindex; + dst = pi->ipi6_addr; + } + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_HOPLIMIT && + cm->cmsg_len == CMSG_LEN(sizeof(int))) + hlimp = (int *)CMSG_DATA(cm); + } + if (ifindex == 0) { + syslog(LOG_ERR, + "<%s> failed to get receiving interface", + __func__); + return; + } + if (hlimp == NULL) { + syslog(LOG_ERR, + "<%s> failed to get receiving hop limit", + __func__); + return; + } + + if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == NULL) { + if (dflag > 1) { + syslog(LOG_DEBUG, + "<%s> received data for non advertising " + "interface (%s)", + __func__, + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + } + return; + } + /* + * If we happen to receive data on an interface which is now down, + * just discard the data. + */ + if ((rai->ifflags & IFF_UP) == 0) { + syslog(LOG_INFO, + "<%s> received data on a disabled interface (%s)", + __func__, + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + +#ifdef OLDRAWSOCKET + if ((size_t)i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) { + syslog(LOG_ERR, + "<%s> packet size(%d) is too short", + __func__, i); + return; + } + + ip = (struct ip6_hdr *)rcvmhdr.msg_iov[0].iov_base; + icp = (struct icmp6_hdr *)(ip + 1); /* XXX: ext. hdr? */ +#else + if ((size_t)i < sizeof(struct icmp6_hdr)) { + syslog(LOG_ERR, + "<%s> packet size(%zd) is too short", + __func__, i); + return; + } + + icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; +#endif + + switch (icp->icmp6_type) { + case ND_ROUTER_SOLICIT: + /* + * Message verification - RFC-2461 6.1.1 + * XXX: these checks must be done in the kernel as well, + * but we can't completely rely on them. + */ + if (*hlimp != 255) { + syslog(LOG_NOTICE, + "<%s> RS with invalid hop limit(%d) " + "received from %s on %s", + __func__, *hlimp, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + if (icp->icmp6_code) { + syslog(LOG_NOTICE, + "<%s> RS with invalid ICMP6 code(%d) " + "received from %s on %s", + __func__, icp->icmp6_code, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + if ((size_t)i < sizeof(struct nd_router_solicit)) { + syslog(LOG_NOTICE, + "<%s> RS from %s on %s does not have enough " + "length (len = %zd)", + __func__, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf), i); + return; + } + rs_input(i, (struct nd_router_solicit *)icp, pi, &rcvfrom); + break; + case ND_ROUTER_ADVERT: + /* + * Message verification - RFC-2461 6.1.2 + * XXX: there's a same dilemma as above... + */ + if (*hlimp != 255) { + syslog(LOG_NOTICE, + "<%s> RA with invalid hop limit(%d) " + "received from %s on %s", + __func__, *hlimp, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + if (icp->icmp6_code) { + syslog(LOG_NOTICE, + "<%s> RA with invalid ICMP6 code(%d) " + "received from %s on %s", + __func__, icp->icmp6_code, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + if ((size_t)i < sizeof(struct nd_router_advert)) { + syslog(LOG_NOTICE, + "<%s> RA from %s on %s does not have enough " + "length (len = %zd)", + __func__, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf), i); + return; + } + ra_input(i, (struct nd_router_advert *)icp, pi, &rcvfrom); + break; + case ICMP6_ROUTER_RENUMBERING: + if (accept_rr == 0) { + syslog(LOG_ERR, "<%s> received a router renumbering " + "message, but not allowed to be accepted", + __func__); + break; + } + rr_input(i, (struct icmp6_router_renum *)icp, pi, &rcvfrom, + &dst); + break; + default: + /* + * Note that this case is POSSIBLE, especially just + * after invocation of the daemon. This is because we + * could receive message after opening the socket and + * before setting ICMP6 type filter(see sock_open()). + */ + syslog(LOG_ERR, "<%s> invalid icmp type(%d)", + __func__, icp->icmp6_type); + return; + } + + return; +} + +static void +rs_input(int len, struct nd_router_solicit *rs, + struct in6_pktinfo *pi, struct sockaddr_in6 *from) +{ + char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + union nd_opts ndopts; + struct rainfo *rai; + struct soliciter *sol; + + syslog(LOG_DEBUG, + "<%s> RS received from %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + + /* ND option check */ + memset(&ndopts, 0, sizeof(ndopts)); + TAILQ_INIT(&ndopts.nd_opts_list); + if (nd6_options((struct nd_opt_hdr *)(rs + 1), + len - sizeof(struct nd_router_solicit), + &ndopts, NDOPT_FLAG_SRCLINKADDR)) { + syslog(LOG_INFO, + "<%s> ND option check failed for an RS from %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + + /* + * If the IP source address is the unspecified address, there + * must be no source link-layer address option in the message. + * (RFC-2461 6.1.1) + */ + if (IN6_IS_ADDR_UNSPECIFIED(&from->sin6_addr) && + ndopts.nd_opts_src_lladdr) { + syslog(LOG_INFO, + "<%s> RS from unspecified src on %s has a link-layer" + " address option", + __func__, + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + goto done; + } + + if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == NULL) { + syslog(LOG_INFO, + "<%s> RS received on non advertising interface(%s)", + __func__, + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + goto done; + } + + if (rai->leaving) { + syslog(LOG_INFO, + "<%s> RS received on reconfiguring advertising interface(%s)", + __func__, rai->ifname); + goto done; + } + + rai->rsinput++; /* increment statistics */ + + /* + * Decide whether to send RA according to the rate-limit + * consideration. + */ + + /* record sockaddr waiting for RA, if possible */ + sol = malloc(sizeof(*sol)); + if (sol) { + sol->addr = *from; + /* XXX RFC2553 need clarification on flowinfo */ + sol->addr.sin6_flowinfo = 0; + TAILQ_INSERT_HEAD(&rai->soliciter, sol, next); + } + + /* + * If there is already a waiting RS packet, don't + * update the timer. + */ + if (rai->waiting++) + goto done; + + ra_timer_set_short_delay(rai); + +done: + free_ndopts(&ndopts); + return; +} + +void +ra_timer_set_short_delay(struct rainfo *rai) +{ + long delay; /* must not be greater than 1000000 */ + struct timespec interval, now, min_delay, tm_tmp, *rest; + + /* + * Compute a random delay. If the computed value + * corresponds to a time later than the time the next + * multicast RA is scheduled to be sent, ignore the random + * delay and send the advertisement at the + * already-scheduled time. RFC2461 6.2.6 + */ + delay = arc4random() % MAX_RA_DELAY_TIME; + interval.tv_sec = 0; + interval.tv_nsec = delay; + rest = rtadvd_timer_rest(rai->timer); + if (timespeccmp(rest, &interval, <)) { + syslog(LOG_DEBUG, "<%s> random delay is larger than " + "the rest of current timer", __func__); + interval = *rest; + } + + /* + * If we sent a multicast Router Advertisement within + * the last MIN_DELAY_BETWEEN_RAS seconds, schedule + * the advertisement to be sent at a time corresponding to + * MIN_DELAY_BETWEEN_RAS plus the random value after the + * previous advertisement was sent. + */ + clock_gettime(CLOCK_MONOTONIC, &now); + timespecsub(&now, &rai->lastsent, &tm_tmp); + min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS; + min_delay.tv_nsec = 0; + if (timespeccmp(&tm_tmp, &min_delay, <)) { + timespecsub(&min_delay, &tm_tmp, &min_delay); + timespecadd(&min_delay, &interval, &interval); + } + rtadvd_set_timer(&interval, rai->timer); +} + +static void +ra_input(int len, struct nd_router_advert *ra, + struct in6_pktinfo *pi, struct sockaddr_in6 *from) +{ + struct rainfo *rai; + char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + union nd_opts ndopts; + const char *on_off[] = {"OFF", "ON"}; + uint32_t reachabletime, retranstimer, mtu; + struct nd_optlist *optp; + int inconsistent = 0; + + syslog(LOG_DEBUG, + "<%s> RA received from %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + + /* ND option check */ + memset(&ndopts, 0, sizeof(ndopts)); + TAILQ_INIT(&ndopts.nd_opts_list); + if (nd6_options((struct nd_opt_hdr *)(ra + 1), + len - sizeof(struct nd_router_advert), + &ndopts, NDOPT_FLAG_SRCLINKADDR | + NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU | + NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL)) + { + syslog(LOG_INFO, + "<%s> ND option check failed for an RA from %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + + /* + * RA consistency check according to RFC-2461 6.2.7 + */ + if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == 0) { + syslog(LOG_INFO, + "<%s> received RA from %s on non-advertising" + " interface(%s)", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + goto done; + } + if (rai->leaving) { + syslog(LOG_DEBUG, + "<%s> received RA on re-configuring interface (%s)", + __func__, rai->ifname); + goto done; + } + rai->rainput++; /* increment statistics */ + + /* Cur Hop Limit value */ + if (ra->nd_ra_curhoplimit && rai->hoplimit && + ra->nd_ra_curhoplimit != rai->hoplimit) { + syslog(LOG_INFO, + "<%s> CurHopLimit inconsistent on %s:" + " %d from %s, %d from us", + __func__, + rai->ifname, + ra->nd_ra_curhoplimit, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->hoplimit); + inconsistent++; + } + /* M flag */ + if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != + rai->managedflg) { + syslog(LOG_INFO, + "<%s> M flag inconsistent on %s:" + " %s from %s, %s from us", + __func__, + rai->ifname, + on_off[!rai->managedflg], + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + on_off[rai->managedflg]); + inconsistent++; + } + /* O flag */ + if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != + rai->otherflg) { + syslog(LOG_INFO, + "<%s> O flag inconsistent on %s:" + " %s from %s, %s from us", + __func__, + rai->ifname, + on_off[!rai->otherflg], + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + on_off[rai->otherflg]); + inconsistent++; + } + /* Reachable Time */ + reachabletime = ntohl(ra->nd_ra_reachable); + if (reachabletime && rai->reachabletime && + reachabletime != rai->reachabletime) { + syslog(LOG_INFO, + "<%s> ReachableTime inconsistent on %s:" + " %d from %s, %d from us", + __func__, + rai->ifname, + reachabletime, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->reachabletime); + inconsistent++; + } + /* Retrans Timer */ + retranstimer = ntohl(ra->nd_ra_retransmit); + if (retranstimer && rai->retranstimer && + retranstimer != rai->retranstimer) { + syslog(LOG_INFO, + "<%s> RetranceTimer inconsistent on %s:" + " %d from %s, %d from us", + __func__, + rai->ifname, + retranstimer, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->retranstimer); + inconsistent++; + } + /* Values in the MTU options */ + if (ndopts.nd_opts_mtu) { + mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); + if (mtu && rai->linkmtu && mtu != rai->linkmtu) { + syslog(LOG_INFO, + "<%s> MTU option value inconsistent on %s:" + " %d from %s, %d from us", + __func__, + rai->ifname, mtu, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->linkmtu); + inconsistent++; + } + } + /* Preferred and Valid Lifetimes for prefixes */ + if (ndopts.nd_opts_pi) + if (prefix_check(ndopts.nd_opts_pi, rai, from)) + inconsistent++; + TAILQ_FOREACH(optp, &ndopts.nd_opts_list, next) + if (prefix_check((struct nd_opt_prefix_info *)optp->opt, + rai, from)) + inconsistent++; + + if (inconsistent) + rai->rainconsistent++; + +done: + free_ndopts(&ndopts); + return; +} + +/* return a non-zero value if the received prefix is inconsitent with ours */ +static int +prefix_check(struct nd_opt_prefix_info *pinfo, + struct rainfo *rai, struct sockaddr_in6 *from) +{ + uint32_t preferred_time, valid_time; + struct prefix *pp; + int inconsistent = 0; + char ntopbuf[INET6_ADDRSTRLEN], prefixbuf[INET6_ADDRSTRLEN]; + struct timespec now; + +#if 0 /* impossible */ + if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION) + return(0); +#endif + + /* + * log if the adveritsed prefix has link-local scope(sanity check?) + */ + if (IN6_IS_ADDR_LINKLOCAL(&pinfo->nd_opt_pi_prefix)) { + syslog(LOG_INFO, + "<%s> link-local prefix %s/%d is advertised " + "from %s on %s", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->ifname); + } + + if ((pp = find_prefix(rai, &pinfo->nd_opt_pi_prefix, + pinfo->nd_opt_pi_prefix_len)) == NULL) { + syslog(LOG_INFO, + "<%s> prefix %s/%d from %s on %s is not in our list", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->ifname); + return(0); + } + + preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time); + if (pp->pltimeexpire) { + /* + * The lifetime is decremented in real time, so we should + * compare the expiration time. + * (RFC 2461 Section 6.2.7.) + * XXX: can we really expect that all routers on the link + * have synchronized clocks? + */ + clock_gettime(CLOCK_MONOTONIC, &now); + preferred_time += now.tv_sec; + + if (!pp->timer && rai->clockskew && + llabs((long long)preferred_time - pp->pltimeexpire) > rai->clockskew) { + syslog(LOG_INFO, + "<%s> preferred lifetime for %s/%d" + " (decr. in real time) inconsistent on %s:" + " %d from %s, %ld from us", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + rai->ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + pp->pltimeexpire); + inconsistent++; + } + } else if (!pp->timer && preferred_time != pp->preflifetime) { + syslog(LOG_INFO, + "<%s> preferred lifetime for %s/%d" + " inconsistent on %s:" + " %d from %s, %d from us", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + rai->ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + pp->preflifetime); + } + + valid_time = ntohl(pinfo->nd_opt_pi_valid_time); + if (pp->vltimeexpire) { + clock_gettime(CLOCK_MONOTONIC, &now); + valid_time += now.tv_sec; + + if (!pp->timer && rai->clockskew && + llabs((long long)valid_time - pp->vltimeexpire) > rai->clockskew) { + syslog(LOG_INFO, + "<%s> valid lifetime for %s/%d" + " (decr. in real time) inconsistent on %s:" + " %d from %s, %ld from us", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + rai->ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + pp->vltimeexpire); + inconsistent++; + } + } else if (!pp->timer && valid_time != pp->validlifetime) { + syslog(LOG_INFO, + "<%s> valid lifetime for %s/%d" + " inconsistent on %s:" + " %d from %s, %d from us", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + rai->ifname, valid_time, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + pp->validlifetime); + inconsistent++; + } + + return(inconsistent); +} + +struct prefix * +find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen) +{ + struct prefix *pp; + int bytelen, bitlen; + unsigned char bitmask; + + TAILQ_FOREACH(pp, &rai->prefix, next) { + if (plen != pp->prefixlen) + continue; + bytelen = plen / 8; + bitlen = plen % 8; + bitmask = 0xff << (8 - bitlen); + if (memcmp((void *)prefix, (void *)&pp->prefix, bytelen)) + continue; + if (bitlen == 0 || + ((prefix->s6_addr[bytelen] & bitmask) == + (pp->prefix.s6_addr[bytelen] & bitmask))) { + return(pp); + } + } + + return(NULL); +} + +/* check if p0/plen0 matches p1/plen1; return 1 if matches, otherwise 0. */ +int +prefix_match(struct in6_addr *p0, int plen0, + struct in6_addr *p1, int plen1) +{ + int bytelen, bitlen; + unsigned char bitmask; + + if (plen0 < plen1) + return(0); + bytelen = plen1 / 8; + bitlen = plen1 % 8; + bitmask = 0xff << (8 - bitlen); + if (memcmp((void *)p0, (void *)p1, bytelen)) + return(0); + if (bitlen == 0 || + ((p0->s6_addr[bytelen] & bitmask) == + (p1->s6_addr[bytelen] & bitmask))) { + return(1); + } + + return(0); +} + +static int +nd6_options(struct nd_opt_hdr *hdr, int limit, + union nd_opts *ndopts, uint32_t optflags) +{ + int optlen = 0; + + for (; limit > 0; limit -= optlen) { + if ((size_t)limit < sizeof(struct nd_opt_hdr)) { + syslog(LOG_INFO, "<%s> short option header", __func__); + goto bad; + } + + hdr = (struct nd_opt_hdr *)((char *)hdr + optlen); + if (hdr->nd_opt_len == 0) { + syslog(LOG_INFO, + "<%s> bad ND option length(0) (type = %d)", + __func__, hdr->nd_opt_type); + goto bad; + } + optlen = hdr->nd_opt_len << 3; + if (optlen > limit) { + syslog(LOG_INFO, "<%s> short option", __func__); + goto bad; + } + + if (hdr->nd_opt_type > ND_OPT_MTU && + hdr->nd_opt_type != ND_OPT_RDNSS && + hdr->nd_opt_type != ND_OPT_DNSSL) + { + syslog(LOG_INFO, "<%s> unknown ND option(type %d)", + __func__, hdr->nd_opt_type); + continue; + } + + if ((ndopt_flags[hdr->nd_opt_type] & optflags) == 0) { + syslog(LOG_INFO, "<%s> unexpected ND option(type %d)", + __func__, hdr->nd_opt_type); + continue; + } + + /* + * Option length check. Do it here for all fixed-length + * options. + */ + if ((hdr->nd_opt_type == ND_OPT_MTU && + (optlen != sizeof(struct nd_opt_mtu))) || + ((hdr->nd_opt_type == ND_OPT_PREFIX_INFORMATION && + optlen != sizeof(struct nd_opt_prefix_info))) || + (hdr->nd_opt_type == ND_OPT_RDNSS && + ((optlen < (int)sizeof(struct nd_opt_rdnss) || + (optlen - sizeof(struct nd_opt_rdnss)) % 16 != 0))) || + (hdr->nd_opt_type == ND_OPT_DNSSL && + optlen < (int)sizeof(struct nd_opt_dnssl))) + { + syslog(LOG_INFO, "<%s> invalid option length", + __func__); + continue; + } + + switch (hdr->nd_opt_type) { + case ND_OPT_TARGET_LINKADDR: + case ND_OPT_REDIRECTED_HEADER: + case ND_OPT_RDNSS: + case ND_OPT_DNSSL: + break; /* we don't care about these options */ + case ND_OPT_SOURCE_LINKADDR: + case ND_OPT_MTU: + if (ndopts->nd_opt_array[hdr->nd_opt_type]) { + syslog(LOG_INFO, + "<%s> duplicated ND option (type = %d)", + __func__, hdr->nd_opt_type); + } + ndopts->nd_opt_array[hdr->nd_opt_type] = hdr; + break; + case ND_OPT_PREFIX_INFORMATION: + { + struct nd_optlist *pfxlist; + + if (ndopts->nd_opts_pi == 0) { + ndopts->nd_opts_pi = + (struct nd_opt_prefix_info *)hdr; + continue; + } + if ((pfxlist = malloc(sizeof(*pfxlist))) == NULL) { + syslog(LOG_ERR, "<%s> can't allocate memory", + __func__); + goto bad; + } + pfxlist->opt = hdr; + TAILQ_INSERT_TAIL(&ndopts->nd_opts_list, pfxlist, next); + + break; + } + default: /* impossible */ + break; + } + } + + return(0); + + bad: + free_ndopts(ndopts); + + return(-1); +} + +static void +free_ndopts(union nd_opts *ndopts) +{ + struct nd_optlist *opt; + + while ((opt = TAILQ_FIRST(&ndopts->nd_opts_list)) != NULL) { + TAILQ_REMOVE(&ndopts->nd_opts_list, opt, next); + free(opt); + } +} + +void +sock_open(void) +{ + struct icmp6_filter filt; + struct ipv6_mreq mreq; + struct rainfo *ra; + int on; + /* XXX: should be max MTU attached to the node */ + static unsigned char answer[1500]; + + rcvcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int)); + rcvcmsgbuf = malloc(rcvcmsgbuflen); + if (rcvcmsgbuf == NULL) { + syslog(LOG_ERR, "<%s> malloc: %m", __func__); + exit(1); + } + + sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)); + sndcmsgbuf = malloc(sndcmsgbuflen); + if (sndcmsgbuf == NULL) { + syslog(LOG_ERR, "<%s> malloc: %m", __func__); + exit(1); + } + + if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { + syslog(LOG_ERR, "<%s> socket: %m", __func__); + exit(1); + } + + /* RFC 4861 Section 4.2 */ + on = 255; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &on, + sizeof(on)) == -1) { + syslog(LOG_ERR, "<%s> IPV6_MULTICAST_HOPS: %m", __func__); + exit(1); + } + + /* specify to tell receiving interface */ + on = 1; +#ifdef IPV6_RECVPKTINFO + if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, + sizeof(on)) < 0) { + syslog(LOG_ERR, "<%s> IPV6_RECVPKTINFO: %m", __func__); + exit(1); + } +#else /* old adv. API */ + if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on, + sizeof(on)) < 0) { + syslog(LOG_ERR, "<%s> IPV6_PKTINFO: %m", __func__); + exit(1); + } +#endif + + on = 1; + /* specify to tell value of hoplimit field of received IP6 hdr */ +#ifdef IPV6_RECVHOPLIMIT + if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, + sizeof(on)) < 0) { + syslog(LOG_ERR, "<%s> IPV6_RECVHOPLIMIT: %m", __func__); + exit(1); + } +#else /* old adv. API */ + if (setsockopt(sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, + sizeof(on)) < 0) { + syslog(LOG_ERR, "<%s> IPV6_HOPLIMIT: %m", __func__); + exit(1); + } +#endif + + ICMP6_FILTER_SETBLOCKALL(&filt); + ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt); + ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); + if (accept_rr) + ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt); + if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, + sizeof(filt)) < 0) { + syslog(LOG_ERR, "<%s> IICMP6_FILTER: %m", __func__); + exit(1); + } + + /* + * join all routers multicast address on each advertising interface. + */ + if (inet_pton(AF_INET6, ALLROUTERS_LINK, + mreq.ipv6mr_multiaddr.s6_addr) != 1) + { + syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)", + __func__); + exit(1); + } + TAILQ_FOREACH(ra, &ralist, next) { + mreq.ipv6mr_interface = ra->ifindex; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, + sizeof(mreq)) < 0) { + syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP(link) on %s: %m", + __func__, ra->ifname); + exit(1); + } + } + + /* + * When attending router renumbering, join all-routers site-local + * multicast group. + */ + if (accept_rr) { + if (inet_pton(AF_INET6, ALLROUTERS_SITE, + mreq.ipv6mr_multiaddr.s6_addr) != 1) + { + syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)", + __func__); + exit(1); + } + ra = TAILQ_FIRST(&ralist); + if (mcastif) { + if ((mreq.ipv6mr_interface = if_nametoindex(mcastif)) + == 0) { + syslog(LOG_ERR, + "<%s> invalid interface: %s", + __func__, mcastif); + exit(1); + } + } else + mreq.ipv6mr_interface = ra->ifindex; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq, sizeof(mreq)) < 0) { + syslog(LOG_ERR, + "<%s> IPV6_JOIN_GROUP(site) on %s: %m", + __func__, + mcastif ? mcastif : ra->ifname); + exit(1); + } + } + + /* initialize msghdr for receiving packets */ + rcviov[0].iov_base = answer; + rcviov[0].iov_len = sizeof(answer); + rcvmhdr.msg_name = &rcvfrom; + rcvmhdr.msg_namelen = sizeof(rcvfrom); + rcvmhdr.msg_iov = rcviov; + rcvmhdr.msg_iovlen = 1; + rcvmhdr.msg_control = rcvcmsgbuf; + rcvmhdr.msg_controllen = rcvcmsgbuflen; + + /* initialize msghdr for sending packets */ + sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); + sndmhdr.msg_iov = sndiov; + sndmhdr.msg_iovlen = 1; + sndmhdr.msg_control = (void *)sndcmsgbuf; + sndmhdr.msg_controllen = sndcmsgbuflen; + + return; +} + +/* open a routing socket to watch the routing table */ +static void +rtsock_open(void) +{ + if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { + syslog(LOG_ERR, "<%s> socket: %m", __func__); + exit(1); + } +} + +struct rainfo * +if_indextorainfo(unsigned int idx) +{ + struct rainfo *rai; + + TAILQ_FOREACH(rai, &ralist, next) { + if (rai->ifindex == idx) + return(rai); + } + + return(NULL); /* search failed */ +} + +struct rainfo * +ra_output(struct rainfo *rai) +{ + int i; + struct cmsghdr *cm; + struct in6_pktinfo *pi; + struct soliciter *sol; + + if ((rai->ifflags & IFF_UP) == 0) { + syslog(LOG_DEBUG, "<%s> %s is not up, skip sending RA", + __func__, rai->ifname); + return NULL; + } + + make_packet(rai); /* XXX: inefficient */ + + sndmhdr.msg_name = (void *)&sin6_linklocal_allnodes; + sndmhdr.msg_iov[0].iov_base = (void *)rai->ra_data; + sndmhdr.msg_iov[0].iov_len = rai->ra_datalen; + + cm = CMSG_FIRSTHDR(&sndmhdr); + /* specify the outgoing interface */ + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_PKTINFO; + cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + pi = (struct in6_pktinfo *)CMSG_DATA(cm); + memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ + pi->ipi6_ifindex = rai->ifindex; + + syslog(LOG_DEBUG, + "<%s> send RA on %s, # of waitings = %d", + __func__, rai->ifname, rai->waiting); + + i = sendmsg(sock, &sndmhdr, 0); + + if (i < 0 || (size_t)i != rai->ra_datalen) { + if (i < 0) { + syslog(LOG_ERR, "<%s> sendmsg on %s: %m", + __func__, rai->ifname); + } + } + + /* + * unicast advertisements + * XXX commented out. reason: though spec does not forbit it, unicast + * advert does not really help + */ + while ((sol = TAILQ_FIRST(&rai->soliciter)) != NULL) { +#if 0 + sndmhdr.msg_name = (void *)&sol->addr; + i = sendmsg(sock, &sndmhdr, 0); + if (i < 0 || i != rai->ra_datalen) { + if (i < 0) { + syslog(LOG_ERR, + "<%s> unicast sendmsg on %s: %m", + __func__, rai->ifname); + } + } +#endif + TAILQ_REMOVE(&rai->soliciter, sol, next); + free(sol); + } + + if (rai->leaving_adv > 0) { + if (--(rai->leaving_adv) == 0) { + /* leaving for ourself means we're shutting down */ + if (rai->leaving_for == rai) { + TAILQ_REMOVE(&ralist, rai, next); + free_rainfo(rai); + return NULL; + } + syslog(LOG_DEBUG, + "<%s> expired RA," + " new config active for interface (%s)", + __func__, rai->ifname); + rai->leaving_for->timer = rtadvd_add_timer(ra_timeout, + ra_timer_update, + rai->leaving_for, rai->leaving_for); + ra_timer_set_short_delay(rai->leaving_for); + rai->leaving_for->leaving = NULL; + free_rainfo(rai); + return NULL; + } + } + + /* update counter */ + if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS) + rai->initcounter++; + rai->raoutput++; + + /* update timestamp */ + clock_gettime(CLOCK_MONOTONIC, &rai->lastsent); + + /* reset waiting conter */ + rai->waiting = 0; + + return rai; +} + +/* process RA timer */ +struct rtadvd_timer * +ra_timeout(void *data) +{ + struct rainfo *rai = (struct rainfo *)data; + +#ifdef notyet + /* if necessary, reconstruct the packet. */ +#endif + + syslog(LOG_DEBUG, + "<%s> RA timer on %s is expired", + __func__, rai->ifname); + + if (ra_output(rai)) + return(rai->timer); + return NULL; +} + +/* update RA timer */ +void +ra_timer_update(void *data, struct timespec *tm) +{ + struct rainfo *rai = (struct rainfo *)data; + long interval; + + /* + * Whenever a multicast advertisement is sent from an interface, + * the timer is reset to a uniformly-distributed random value + * between the interface's configured MinRtrAdvInterval and + * MaxRtrAdvInterval (RFC2461 6.2.4). + */ + interval = rai->mininterval; + if (rai->mininterval != rai->maxinterval) + interval += arc4random() % (rai->maxinterval-rai->mininterval); + + /* + * For the first few advertisements (up to + * MAX_INITIAL_RTR_ADVERTISEMENTS), if the randomly chosen interval + * is greater than MAX_INITIAL_RTR_ADVERT_INTERVAL, the timer + * SHOULD be set to MAX_INITIAL_RTR_ADVERT_INTERVAL instead. + * (RFC-2461 6.2.4) + */ + if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS && + interval > MAX_INITIAL_RTR_ADVERT_INTERVAL) + interval = MAX_INITIAL_RTR_ADVERT_INTERVAL; + + tm->tv_sec = interval; + tm->tv_nsec = 0; + + syslog(LOG_DEBUG, + "<%s> RA timer on %s is set to %ld:%ld", + __func__, rai->ifname, + (long int)tm->tv_sec, (long int)tm->tv_nsec); + + return; +} diff --git a/usr.sbin/rtadvd/rtadvd.conf b/usr.sbin/rtadvd/rtadvd.conf new file mode 100644 index 000000000..2806556b2 --- /dev/null +++ b/usr.sbin/rtadvd/rtadvd.conf @@ -0,0 +1,21 @@ +# $NetBSD: rtadvd.conf,v 1.4 2009/11/01 15:17:59 jakllsch Exp $ +# $KAME: rtadvd.conf,v 1.13 2003/06/25 03:45:21 itojun Exp $ +# +# Note: All of the following parameters have default values defined +# in specifications, and hence you usually do not have to set them +# by hand unless you need special non-default values. +# +# You even do not need to create the configuration file. rtadvd +# would usually work well without a configuration file. +# See also: rtadvd(8) + +# per-interface definitions. +# Mainly IPv6 prefixes are configured in this part. However, rtadvd +# automatically learns appropriate prefixes from the kernel's routing +# table, and advertises the prefixes, so you don't have to configure +# this part, either. +# If you don't want the automatic advertisement, (uncomment and) configure +# this part by hand, and then invoke rtadvd with the -s option. + +#ef0:\ +# :addr="2001:db8:ffff:1000::":prefixlen#64: diff --git a/usr.sbin/rtadvd/rtadvd.conf.5 b/usr.sbin/rtadvd/rtadvd.conf.5 new file mode 100644 index 000000000..e458379ed --- /dev/null +++ b/usr.sbin/rtadvd/rtadvd.conf.5 @@ -0,0 +1,497 @@ +.\" $NetBSD: rtadvd.conf.5,v 1.18 2012/12/11 16:37:23 roy Exp $ +.\" $KAME: rtadvd.conf.5,v 1.50 2005/01/14 05:30:59 jinmei Exp $ +.\" +.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd December 11, 2012 +.Dt RTADVD.CONF 5 +.Os +.Sh NAME +.Nm rtadvd.conf +.Nd config file for router advertisement daemon +.Sh DESCRIPTION +This file describes how the router advertisement packets must be constructed +for each of the interfaces. +.Pp +As described in +.Xr rtadvd 8 , +you do not have to set this configuration file up at all, +unless you need some special configurations. +You may even omit the file as a whole. +In such cases, the +.Nm rtadvd +daemon will automatically configure itself using default values +specified in the specification. +.Pp +It obeys the famous +.Xr capfile 5 +file format. +Each line in the file describes a network interface. +Fields are separated by a colon +.Pq Sq \&: , +and each field contains one capability description. +Lines may be concatenated by the +.Sq \e +character. +The comment marker is the +.Sq \&# +character. +.Sh CAPABILITIES +Capabilities describe the value to be filled into ICMPv6 router +advertisement messages and to control +.Xr rtadvd 8 +behavior. +Therefore, you are encouraged to read IETF neighbor discovery documents +if you would like to modify the sample configuration file. +.Pp +Note that almost all items have default values. +If you omit an item, the default value of the item will be used. +.Pp +There are two items which control the interval of sending router advertisements. +These items can be omitted, then +.Nm rtadvd +will use the default values. +.Bl -tag -width indent +.It Cm \&maxinterval +(num) The maximum time allowed between sending unsolicited +multicast router advertisements +.Pq unit: seconds . +The default value is 600. +Its value must be no less than 4 seconds +and no greater than 1800 seconds. +.It Cm \&mininterval +(num) The minimum time allowed between sending unsolicited multicast +router advertisements +.Pq unit: seconds . +The default value is the one third of value of +.Cm maxinterval . +Its value must be no less than 3 seconds and no greater than .75 * +the value of +.Cm maxinterval . +.El +.Pp +The following items are for ICMPv6 router advertisement message +header. +These items can be omitted, then +.Nm rtadvd +will use the default values. +.Bl -tag -width indent +.It Cm \&chlim +(num) The value for Cur Hop Limit field. +The default value is 64. +.It Cm \&raflags +(str or num) A 8-bit flags field in router advertisement message header. +This field can be specified either as a case-sensitive string or as an +integer. +A sting consists of characters each of which corresponds to a +particular flag bit(s). +An integer should be the logical OR of all enabled bits. +Bit 7 +.Po +.Li 'm' or 0x80 +.Pc +means Managed address configuration flag bit, +and Bit 6 +.Po +.Li 'o' or 0x40 +.Pc +means Other stateful configuration flag bit. +Bit 4 +.Po +.Li 0x10 +.Pc +and Bit 3 +.Po +.Li 0x08 +.Pc +are used to encode router preference. +Bits 01 +.Po +or 'h' +.Pc +means high, 00 means medium, and 11 +.Po +or 'l' +.Pc +means low. +Bits 10 is reserved, and must not be specified. +There is no character to specify the medium preference explicitly. +The default value of the entire flag is 0 +.Po +or a null string, +.Pc +which means no additional +configuration methods, and the medium router preference. +.It Cm \&rltime +(num) Router lifetime field +.Pq unit: seconds . +The value must be either zero or between +the value of +.Cm maxinterval +and 9000. +When +.Nm rtadvd +runs on a host, this value must explicitly set 0 on all the +advertising interfaces as described in +.Xr rtadvd 8 . +The default value is 1800. +.It Cm \&rtime +(num) Reachable time field +.Pq unit: milliseconds . +The default value is 0, which means unspecified by this router. +.It Cm \&retrans +(num) Retrans Timer field +.Pq unit: milliseconds . +The default value is 0, which means unspecified by this router. +.El +.Pp +The following items are for ICMPv6 prefix information option, +which will be attached to router advertisement header. +These items can be omitted, then +.Nm rtadvd +will automatically get appropriate prefixes from the kernel's routing table, +and advertise the prefixes with the default parameters, unless the +.Cm noifprefix +flag is specified. +Keywords other than +.Cm clockskew +and +.Cm noifprefix +can be augmented with a number, like +.Dq Li prefix2 , +to specify multiple prefixes. +.Bl -tag -width indent +.It Cm \&noifprefix +(bool) Specified whether +.Nm rtadvd +should gather prefix information from the interface if no +.Cm addr +is specified. +If no +.Cm addr +is given, and +.Cm noifprefix +is set, +.Nm rtadvd +will send RA packets with no prefix information. +.It Cm \&clockskew +(num) Time skew to adjust link propagation delays and clock skews +between routers on the link +.Pq unit: seconds . +This value is used in consistency check for locally-configured and +advertised prefix lifetimes, and has its meaning when the local router +configures a prefix on the link with a lifetime that decrements in +real time. +If the value is 0, it means the consistency check will be skipped +for such prefixes. +The default value is 0. +.It Cm \&prefixlen +(num) Prefix length field. +The default value is 64. +.It Cm \&pinfoflags +(str or num) A 8-bit flags field in prefix information option. +This field can be specified either as a case-sensitive string or as an +integer. +A sting consists of characters each of which corresponds to a +particular flag bit(s). +An integer should be the logical OR of all enabled bits. +Bit 7 +.Po +.Li 'l' or 0x80 +.Pc +means On-link flag bit, +and Bit 6 +.Po +.Li 'a' or 0x40 +.Pc +means Autonomous address-configuration flag bit. +The default value is "la" or 0xc0, i.e., both bits are set. +.It Cm \&addr +(str) The address filled into Prefix field. +Since +.Dq \&: +is used for +.Xr capfile 5 +file format as well as IPv6 numeric address, the field MUST be quoted by +doublequote character. +.It Cm \&vltime +(num) Valid lifetime field +.Pq unit: seconds . +The default value is 2592000 (30 days). +.It Cm \&vltimedecr +(bool) This item means the advertised valid lifetime will decrement +in real time, which is disabled by default. +.It Cm \&pltime +(num) Preferred lifetime field +.Pq unit: seconds . +The default value is 604800 (7 days). +.It Cm \&pltimedecr +(bool) This item means the advertised preferred lifetime will decrement +in real time, which is disabled by default. +.El +.Pp +The following item is for ICMPv6 MTU option, +which will be attached to router advertisement header. +This item can be omitted, then +.Nm rtadvd +will use the default value. +.Bl -tag -width indent +.It Cm \&mtu +(num or str) MTU (maximum transmission unit) field. +If 0 is specified, it means that the option will not be included. +The default value is 0. +If the special string +.Dq auto +is specified for this item, MTU option will be included and its value +will be set to the interface MTU automatically. +.El +.Pp +The following item controls ICMPv6 source link-layer address option, +which will be attached to router advertisement header. +As noted above, you can just omit the item, then +.Nm rtadvd +will use the default value. +.Bl -tag -width indent +.It Cm \&nolladdr +(bool) By default +.Po +if +.Cm \&nolladdr +is not specified +.Pc , +.Xr rtadvd 8 +will try to get link-layer address for the interface from the kernel, +and attach that in source link-layer address option. +If this capability exists, +.Xr rtadvd 8 +will not attach source link-layer address option to +router advertisement packets. +.El +.Pp +The following items are for ICMPv6 route information option, +which will be attached to router advertisement header. +These items are optional. +Each items can be augmented with number, like +.Dq Li rtplen2 , +to specify multiple routes. +.Bl -tag -width indent +.It Cm \&rtprefix +(str) The prefix filled into the Prefix field of route information option. +Since +.Dq \&: +is used for +.Xr capfile 5 +file format as well as IPv6 numeric address, the field MUST be quoted by +doublequote character. +.It Cm \&rtplen +(num) Prefix length field in route information option. +The default value is 64. +.It Cm \&rtflags +(str or num) A 8-bit flags field in route information option. +Currently only the preference values are defined. +The notation is same as that of the raflags field. +Bit 4 +.Po +.Li 0x10 +.Pc +and Bit 3 +.Po +.Li 0x08 +.Pc +are used to encode the route preference for the route. +The default value is 0x00, i.e. medium preference. +.It Cm \&rtltime +(num) route lifetime field in route information option. +.Pq unit: seconds . +Since the specification does not define the default value of this +item, the value for this item should be specified by hand. +However, +.Nm rtadvd +allows this item to be unspecified, and uses the router lifetime +as the default value in such a case, just for compatibility with an +old version of the program. +.El +.Pp +In the above list, each keyword beginning with +.Dq Li rt +could be replaced with the one beginning with +.Dq Li rtr +for backward compatibility reason. +For example, +.Cm rtrplen +is accepted instead of +.Cm rtplen . +However, keywords that start with +.Dq Li rtr +have basically been obsoleted, and should not be used any more. +.Pp +The following items are for ICMPv6 Recursive DNS Server Option and +DNS Search List Option +.Pq RFC 6106 , +which will be attached to router advertisement header. +These items are optional. +.Bl -tag -width indent +.It Cm \&rdnss +(str) The IPv6 address of one or more recursive DNS servers. +The argument must be inside double quotes. +Multiple DNS servers can be specified in a comma-separated string. +If different lifetimes are needed for different servers, +separate entries can be given by using +.Cm rdnss , +.Cm rdnss0 , +.Cm rdnss1 , +.Cm rdnss2 ... +options with corresponding +.Cm rdnssltime , +.Cm rdnssltime0 , +.Cm rdnssltime1 , +.Cm rdnssltime2 ... +entries. +Note that the maximum number of servers depends on the receiver side. +See also the +.Xr resolv.conf 5 +manual page for the resolver implementation. +.It Cm \&rdnssltime +The lifetime of the +.Cm rdnss +DNS server entries. +The default value is 3/2 of the interval time. +.It Cm \&dnssl +(str) One or more domain names in a comma-separated string. +These domain names will be used when making DNS queries on a +non-fully-qualified domain name. +If different lifetimes are needed for different domains, separate entries +can be given by using +.Cm dnssl , +.Cm dnssl0 , +.Cm dnssl1 , +.Cm dnssl2 ... +options with corresponding +.Cm dnsslltime , +.Cm dnsslltime0 , +.Cm dnsslltime1 , +.Cm dnsslltime2 ... +entries. +Note that the maximum number of names depends on the receiver side. +See also the +.Xr resolv.conf 5 +manual page for the resolver implementation. +.It Cm \&dnsslltime +The lifetime of the +.Cm dnssl +DNS search list entries. +The default value is 3/2 of the interval time. +.El +.Pp +You can also refer one line from another by using +.Cm tc +capability. +See +.Xr capfile 5 +for details on the capability. +.Sh EXAMPLES +As presented above, all of the advertised parameters have default values +defined in specifications, and hence you usually do not have to set them +by hand, unless you need special non-default values. +It can cause interoperability problem if you use an ill-configured +parameter. +.Pp +To override a configuration parameter, you can specify the parameter alone. +With the following configuration, +.Xr rtadvd 8 +overrides the router lifetime parameter for the +.Li ne0 +interface. +.Bd -literal +ne0:\\ + :rltime#0: +.Ed +.Pp +The following example manually configures prefixes advertised from the +.Li ef0 +interface. +The configuration must be used with the +.Fl s +option to +.Xr rtadvd 8 . +.Bd -literal +ef0:\\ + :addr="2001:db8:ffff:1000::":prefixlen#64: +.Ed +.Pp +The following example configures the +.Li wlan0 +interface and adds two DNS servers and a DNS domain search options +using the default option lifetime values. +.Bd -literal -offset +wlan0:\\ + :addr="2001:db8:ffff:1000::":prefixlen#64:\\ + :rdnss="2001:db8:ffff::10,2001:db8:ffff::2:43":\\ + :dnssl="example.com": +.Ed +.Pp +The following example presents the default values in an explicit manner. +The configuration is provided just for reference purposes; +YOU DO NOT NEED TO HAVE IT AT ALL. +.Bd -literal +default:\\ + :chlim#64:raflags#0:rltime#1800:rtime#0:retrans#0:\\ + :pinfoflags="la":vltime#2592000:pltime#604800:mtu#0: +ef0:\\ + :addr="2001:db8:ffff:1000::":prefixlen#64:tc=default: +.Ed +.Sh SEE ALSO +.Xr capfile 5 , +.Xr rtadvd 8 , +.Xr rtsol 8 +.Pp +Thomas Narten, Erik Nordmark and W. A. Simpson, +.Do +Neighbor Discovery for IP version 6 (IPv6) +.Dc , +RFC 2461 +.Pp +Richard Draves, +.Do +Default Router Preferences and More-Specific Routes +.Dc , +RFC 4191 +.Pp +J. Jeong, S. Park, L. Beloeil, S. Madanapalli +.Do +IPv6 Router Advertisement Options for DNS Configuration +.Dc , +RFC 6106 +.Sh HISTORY +The +.Xr rtadvd 8 +and the configuration file +.Nm +first appeared in WIDE Hydrangea IPv6 protocol stack kit. +.\" .Sh BUGS +.\" (to be written) diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h new file mode 100644 index 000000000..ad6fe8694 --- /dev/null +++ b/usr.sbin/rtadvd/rtadvd.h @@ -0,0 +1,196 @@ +/* $NetBSD: rtadvd.h,v 1.14 2015/06/05 14:09:20 roy Exp $ */ +/* $KAME: rtadvd.h,v 1.30 2005/10/17 14:40:02 suz Exp $ */ + +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define RTADVD_USER "_rtadvd" + +#define ALLNODES "ff02::1" +#define ALLROUTERS_LINK "ff02::2" +#define ALLROUTERS_SITE "ff05::2" + +#define IN6ADDR_SITELOCAL_ALLROUTERS_INIT \ + {{{ 0xff, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}} + +//extern struct sockaddr_in6 sin6_linklocal_allnodes; +//extern struct sockaddr_in6 sin6_linklocal_allrouters; +extern struct sockaddr_in6 sin6_sitelocal_allrouters; + +/* protocol constants and default values */ +#define DEF_MAXRTRADVINTERVAL 600 +#define DEF_ADVLINKMTU 0 +#define DEF_ADVREACHABLETIME 0 +#define DEF_ADVRETRANSTIMER 0 +#define DEF_ADVCURHOPLIMIT 64 +#define DEF_ADVVALIDLIFETIME 2592000 +#define DEF_ADVPREFERREDLIFETIME 604800 + +#define MAXROUTERLIFETIME 9000 +#define MIN_MAXINTERVAL 4 +#define MAX_MAXINTERVAL 1800 +#define MIN_MININTERVAL 3 +#define MAXREACHABLETIME 3600000 + +#define MAX_INITIAL_RTR_ADVERT_INTERVAL 16 +#define MAX_INITIAL_RTR_ADVERTISEMENTS 3 +#define MAX_FINAL_RTR_ADVERTISEMENTS 3 +#define MIN_DELAY_BETWEEN_RAS 3 +#define MAX_RA_DELAY_TIME 500000000 /* nsec */ + +#define PREFIX_FROM_KERNEL 1 +#define PREFIX_FROM_CONFIG 2 +#define PREFIX_FROM_DYNAMIC 3 + +struct prefix { + TAILQ_ENTRY(prefix) next; + + struct rainfo *rainfo; /* back pointer to the interface */ + + struct rtadvd_timer *timer; /* expiration timer. used when a prefix + * derived from the kernel is deleted. + */ + + uint32_t validlifetime; /* AdvValidLifetime */ + long vltimeexpire; /* expiration of vltime; decrement case only */ + uint32_t preflifetime; /* AdvPreferredLifetime */ + long pltimeexpire; /* expiration of pltime; decrement case only */ + uint16_t onlinkflg; /* bool: AdvOnLinkFlag */ + uint16_t autoconfflg; /* bool: AdvAutonomousFlag */ + int prefixlen; + int origin; /* from kernel or config */ + struct in6_addr prefix; +}; + +struct rtinfo { + TAILQ_ENTRY(rtinfo) next; + + uint32_t ltime; /* route lifetime */ + uint16_t rtpref; /* route preference */ + int prefixlen; + struct in6_addr prefix; +}; + +struct rdnss_addr { + TAILQ_ENTRY(rdnss_addr) next; + + struct in6_addr addr; +}; + +struct rdnss { + TAILQ_ENTRY(rdnss) next; + + TAILQ_HEAD(, rdnss_addr) list; + uint32_t lifetime; +}; + +struct dnssl_domain { + TAILQ_ENTRY(dnssl_domain) next; + + int len; + char domain[256]; +}; + +struct dnssl { + TAILQ_ENTRY(dnssl) next; + + TAILQ_HEAD(, dnssl_domain) list; + uint32_t lifetime; +}; + +struct soliciter { + TAILQ_ENTRY(soliciter) next; + + struct sockaddr_in6 addr; +}; + +struct rainfo { + TAILQ_ENTRY(rainfo) next; + + /* timer related parameters */ + struct rtadvd_timer *timer; + int initcounter; /* counter for the first few advertisements */ + struct timespec lastsent; /* timestamp when the latest RA was sent */ + int waiting; /* number of RS waiting for RA */ + struct rainfo *leaving; /* the config which is leaving */ + struct rainfo *leaving_for; /* the new config to activate */ + int leaving_adv; /* number of RA left to send */ + + /* interface information */ + uint16_t ifindex; + int ifflags; + int advlinkopt; /* bool: whether include link-layer addr opt */ + struct sockaddr_dl *sdl; + char ifname[16]; + uint32_t phymtu; /* mtu of the physical interface */ + + /* Router configuration variables */ + uint16_t lifetime; /* AdvDefaultLifetime */ + uint16_t maxinterval; /* MaxRtrAdvInterval */ + uint16_t mininterval; /* MinRtrAdvInterval */ + int managedflg; /* AdvManagedFlag */ + int otherflg; /* AdvOtherConfigFlag */ + + int rtpref; /* router preference */ + uint32_t linkmtu; /* AdvLinkMTU */ + uint32_t reachabletime; /* AdvReachableTime */ + uint32_t retranstimer; /* AdvRetransTimer */ + uint16_t hoplimit; /* AdvCurHopLimit */ + TAILQ_HEAD(, prefix) prefix; /* AdvPrefixList(link head) */ + int pfxs; + uint16_t clockskew;/* used for consisitency check of lifetimes */ + + TAILQ_HEAD(, rtinfo) route; + TAILQ_HEAD(, rdnss) rdnss; /* RDNSS list */ + TAILQ_HEAD(, dnssl) dnssl; /* DNS Search List */ + + /* actual RA packet data and its length */ + size_t ra_datalen; + char *ra_data; + + /* statistics */ + uint64_t raoutput; /* number of RAs sent */ + uint64_t rainput; /* number of RAs received */ + uint64_t rainconsistent; /* number of RAs inconsistent with ours */ + uint64_t rsinput; /* number of RSs received */ + + /* info about soliciter */ + TAILQ_HEAD(, soliciter) soliciter; /* recent solication source */ +}; + +extern TAILQ_HEAD(ralist_head_t, rainfo) ralist; + +struct rtadvd_timer *ra_timeout(void *); +void ra_timer_update(void *, struct timespec *); +void ra_timer_set_short_delay(struct rainfo *); + +int prefix_match(struct in6_addr *, int, struct in6_addr *, int); +struct rainfo *if_indextorainfo(unsigned int); +struct prefix *find_prefix(struct rainfo *, struct in6_addr *, int); diff --git a/usr.sbin/rtadvd/timer.c b/usr.sbin/rtadvd/timer.c new file mode 100644 index 000000000..14766c60f --- /dev/null +++ b/usr.sbin/rtadvd/timer.c @@ -0,0 +1,168 @@ +/* $NetBSD: timer.c,v 1.12 2015/06/05 14:09:20 roy Exp $ */ +/* $KAME: timer.c,v 1.11 2005/04/14 06:22:35 suz Exp $ */ + +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include "timer.h" + +struct rtadvd_timer_head_t ra_timer = TAILQ_HEAD_INITIALIZER(ra_timer); +static struct timespec tm_limit = { LONG_MAX, 1000000000L - 1 }; +static struct timespec tm_max; + +void +rtadvd_timer_init(void) +{ + + TAILQ_INIT(&ra_timer); + tm_max = tm_limit; +} + +struct rtadvd_timer * +rtadvd_add_timer(struct rtadvd_timer *(*timeout) (void *), + void (*update) (void *, struct timespec *), + void *timeodata, void *updatedata) +{ + struct rtadvd_timer *newtimer; + + if ((newtimer = malloc(sizeof(*newtimer))) == NULL) { + syslog(LOG_ERR, + "<%s> can't allocate memory", __func__); + exit(1); + } + + memset(newtimer, 0, sizeof(*newtimer)); + + if (timeout == NULL) { + syslog(LOG_ERR, + "<%s> timeout function unspecified", __func__); + exit(1); + } + newtimer->expire = timeout; + newtimer->update = update; + newtimer->expire_data = timeodata; + newtimer->update_data = updatedata; + newtimer->tm = tm_max; + + /* link into chain */ + TAILQ_INSERT_TAIL(&ra_timer, newtimer, next); + + return(newtimer); +} + +void +rtadvd_remove_timer(struct rtadvd_timer **timer) +{ + + if (*timer) { + TAILQ_REMOVE(&ra_timer, *timer, next); + free(*timer); + *timer = NULL; + } +} + +void +rtadvd_set_timer(struct timespec *tm, struct rtadvd_timer *timer) +{ + struct timespec now; + + /* reset the timer */ + clock_gettime(CLOCK_MONOTONIC, &now); + timespecadd(&now, tm, &timer->tm); + + /* upate the next expiration time */ + if (timespeccmp(&timer->tm, &tm_max, <)) + tm_max = timer->tm; +} + +/* + * Check expiration for each timer. If a timer expires, + * call the expire function for the timer and update the timer. + * Return the next interval for select() call. + */ +struct timespec * +rtadvd_check_timer(void) +{ + static struct timespec returnval; + struct timespec now; + struct rtadvd_timer *tm, *tmn; + + clock_gettime(CLOCK_MONOTONIC, &now); + tm_max = tm_limit; + + TAILQ_FOREACH_SAFE(tm, &ra_timer, next, tmn) { + if (timespeccmp(&tm->tm, &now, <=)) { + if ((*tm->expire)(tm->expire_data) == NULL) + continue; /* the timer was removed */ + if (tm->update) + (*tm->update)(tm->update_data, &tm->tm); + timespecadd(&tm->tm, &now, &tm->tm); + } + if (timespeccmp(&tm->tm, &tm_max, <)) + tm_max = tm->tm; + } + + if (timespeccmp(&tm_max, &tm_limit, ==)) + return(NULL); + if (timespeccmp(&tm_max, &now, <)) { + /* this may occur when the interval is too small */ + timespecclear(&returnval); + } else + timespecsub(&tm_max, &now, &returnval); + return(&returnval); +} + +struct timespec * +rtadvd_timer_rest(struct rtadvd_timer *timer) +{ + static struct timespec returnval; + struct timespec now; + + clock_gettime(CLOCK_MONOTONIC, &now); + if (timespeccmp(&timer->tm, &now, <=)) { + syslog(LOG_DEBUG, + "<%s> a timer must be expired, but not yet", + __func__); + returnval.tv_sec = 0; + returnval.tv_nsec = 0; + } + else + timespecsub(&timer->tm, &now, &returnval); + + return(&returnval); +} diff --git a/usr.sbin/rtadvd/timer.h b/usr.sbin/rtadvd/timer.h new file mode 100644 index 000000000..3feefa946 --- /dev/null +++ b/usr.sbin/rtadvd/timer.h @@ -0,0 +1,51 @@ +/* $NetBSD: timer.h,v 1.8 2015/06/05 14:09:20 roy Exp $ */ +/* $KAME: timer.h,v 1.5 2002/05/31 13:30:38 jinmei Exp $ */ + +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern TAILQ_HEAD(rtadvd_timer_head_t, rtadvd_timer) ra_timer; +struct rtadvd_timer { + TAILQ_ENTRY(rtadvd_timer) next; + struct rainfo *rai; + struct timespec tm; + + struct rtadvd_timer *(*expire) (void *); /* expiration function */ + void *expire_data; + void (*update)(void *, struct timespec *); /* update function */ + void *update_data; +}; + +void rtadvd_timer_init(void); +struct rtadvd_timer *rtadvd_add_timer(struct rtadvd_timer *(*) (void *), + void (*) (void *, struct timespec *), void *, void *); +void rtadvd_set_timer(struct timespec *, struct rtadvd_timer *); +void rtadvd_remove_timer(struct rtadvd_timer **); +struct timespec * rtadvd_check_timer(void); +struct timespec * rtadvd_timer_rest(struct rtadvd_timer *);