diff options
author | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 1999-12-11 10:23:30 +0000 |
---|---|---|
committer | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 1999-12-11 10:23:30 +0000 |
commit | 4c0cc50fe83672393efc377af381c8a603449440 (patch) | |
tree | 3f273bd063e11d5c7c9763d2c44ea1cb44680529 /usr.sbin | |
parent | cd988a82bb394117dcbba6cdf3dca12f3a5794e9 (diff) |
router advertisement daemon, from KAME
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/rtadvd/advcap.c | 455 | ||||
-rw-r--r-- | usr.sbin/rtadvd/advcap.h | 43 | ||||
-rw-r--r-- | usr.sbin/rtadvd/config.c | 642 | ||||
-rw-r--r-- | usr.sbin/rtadvd/config.h | 32 | ||||
-rw-r--r-- | usr.sbin/rtadvd/if.c | 592 | ||||
-rw-r--r-- | usr.sbin/rtadvd/if.h | 55 | ||||
-rw-r--r-- | usr.sbin/rtadvd/pathnames.h | 1 | ||||
-rw-r--r-- | usr.sbin/rtadvd/rrenum.c | 407 | ||||
-rw-r--r-- | usr.sbin/rtadvd/rrenum.h | 32 | ||||
-rw-r--r-- | usr.sbin/rtadvd/rtadvd.8 | 116 | ||||
-rw-r--r-- | usr.sbin/rtadvd/rtadvd.c | 1262 | ||||
-rw-r--r-- | usr.sbin/rtadvd/rtadvd.conf | 13 | ||||
-rw-r--r-- | usr.sbin/rtadvd/rtadvd.conf.5 | 249 | ||||
-rw-r--r-- | usr.sbin/rtadvd/rtadvd.h | 104 | ||||
-rw-r--r-- | usr.sbin/rtadvd/timer.c | 199 | ||||
-rw-r--r-- | usr.sbin/rtadvd/timer.h | 61 |
16 files changed, 4263 insertions, 0 deletions
diff --git a/usr.sbin/rtadvd/advcap.c b/usr.sbin/rtadvd/advcap.c new file mode 100644 index 00000000000..81724fc5fab --- /dev/null +++ b/usr.sbin/rtadvd/advcap.c @@ -0,0 +1,455 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)remcap.c 5.5 (Berkeley) 2/2/91"; +#endif /* not lint */ + +/* + * remcap - routines for dealing with the remote host data base + * + * derived from termcap + */ +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <syslog.h> +#include <errno.h> +#include <string.h> +#include "pathnames.h" + +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif +#define MAXHOP 32 /* max number of tc= indirections */ + +#define tgetent agetent +#define tnchktc anchktc +#define tnamatch anamatch +#define tgetnum agetnum +#define tgetflag agetflag +#define tgetstr agetstr + +#if 0 +#define V_TERMCAP "REMOTE" +#define V_TERM "HOST" +#endif + +char *RM; + +/* + * termcap - routines for dealing with the terminal capability data base + * + * BUG: Should use a "last" pointer in tbuf, so that searching + * for capabilities alphabetically would not be a n**2/2 + * process when large numbers of capabilities are given. + * Note: If we add a last pointer now we will screw up the + * tc capability. We really should compile termcap. + * + * Essentially all the work here is scanning and decoding escapes + * in string capabilities. We don't use stdio because the editor + * doesn't, and because living w/o it is not hard. + */ + +static char *tbuf; +static int hopcount; /* detect infinite loops in termcap, init 0 */ + +static char *remotefile; + +extern char *conffile; + +int tgetent __P((char *, char *)); +int getent __P((char *, char *, char *)); +int tnchktc __P((void)); +int tnamatch __P((char *)); +static char *tskip __P((char *)); +int tgetnum __P((char *)); +int tgetflag __P((char *)); +char *tgetstr __P((char *, char **)); +static char *tdecode __P((char *, char **)); + +/* + * Get an entry for terminal name in buffer bp, + * from the termcap file. Parse is very rudimentary; + * we just notice escaped newlines. + */ +int +tgetent(bp, name) + char *bp, *name; +{ + char *cp; + + remotefile = cp = conffile ? conffile : _PATH_RTADVDCONF; + return (getent(bp, name, cp)); +} + +int +getent(bp, name, cp) + char *bp, *name, *cp; +{ + register int c; + register int i = 0, cnt = 0; + char ibuf[BUFSIZ]; + int tf; + + tbuf = bp; + tf = 0; + /* + * TERMCAP can have one of two things in it. It can be the + * name of a file to use instead of /etc/termcap. In this + * case it better start with a "/". Or it can be an entry to + * use so we don't have to read the file. In this case it + * has to already have the newlines crunched out. + */ + if (cp && *cp) { + tf = open(RM = cp, O_RDONLY); + } + if (tf < 0) { + syslog(LOG_WARNING, + "<%s> open: %s", __FUNCTION__, strerror(errno)); + return (-2); + } + for (;;) { + cp = bp; + for (;;) { + if (i == cnt) { + cnt = read(tf, ibuf, BUFSIZ); + if (cnt <= 0) { + close(tf); + return (0); + } + i = 0; + } + c = ibuf[i++]; + if (c == '\n') { + if (cp > bp && cp[-1] == '\\') { + cp--; + continue; + } + break; + } + if (cp >= bp+BUFSIZ) { + write(2,"Remcap entry too long\n", 23); + break; + } else + *cp++ = c; + } + *cp = 0; + + /* + * The real work for the match. + */ + if (tnamatch(name)) { + close(tf); + return (tnchktc()); + } + } +} + +/* + * tnchktc: check the last entry, see if it's tc=xxx. If so, + * recursively find xxx and append that entry (minus the names) + * to take the place of the tc=xxx entry. This allows termcap + * entries to say "like an HP2621 but doesn't turn on the labels". + * Note that this works because of the left to right scan. + */ +int +tnchktc() +{ + register char *p, *q; + char tcname[16]; /* name of similar terminal */ + char tcbuf[BUFSIZ]; + char *holdtbuf = tbuf; + int l; + + p = tbuf + strlen(tbuf) - 2; /* before the last colon */ + while (*--p != ':') + if (p<tbuf) { + write(2, "Bad remcap entry\n", 18); + return (0); + } + p++; + /* p now points to beginning of last field */ + if (p[0] != 't' || p[1] != 'c') + return (1); + strcpy(tcname, p+3); + q = tcname; + while (*q && *q != ':') + q++; + *q = 0; + if (++hopcount > MAXHOP) { + write(2, "Infinite tc= loop\n", 18); + return (0); + } + if (getent(tcbuf, tcname, remotefile) != 1) { + return (0); + } + for (q = tcbuf; *q++ != ':'; ) + ; + l = p - holdtbuf + strlen(q); + if (l > BUFSIZ) { + write(2, "Remcap entry too long\n", 23); + q[BUFSIZ - (p-holdtbuf)] = 0; + } + strcpy(p, q); + tbuf = holdtbuf; + return (1); +} + +/* + * Tnamatch deals with name matching. The first field of the termcap + * entry is a sequence of names separated by |'s, so we compare + * against each such name. The normal : terminator after the last + * name (before the first field) stops us. + */ +int +tnamatch(np) + char *np; +{ + register char *Np, *Bp; + + Bp = tbuf; + if (*Bp == '#') + return (0); + for (;;) { + for (Np = np; *Np && *Bp == *Np; Bp++, Np++) + continue; + if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) + return (1); + while (*Bp && *Bp != ':' && *Bp != '|') + Bp++; + if (*Bp == 0 || *Bp == ':') + return (0); + Bp++; + } +} + +/* + * Skip to the next field. Notice that this is very dumb, not + * knowing about \: escapes or any such. If necessary, :'s can be put + * into the termcap file in octal. + */ +static char * +tskip(bp) + register char *bp; +{ + int dquote; + + dquote = 0; + while (*bp) { + switch (*bp) { + case ':': + if (!dquote) + goto breakbreak; + else + bp++; + break; + case '\\': + bp++; + if (isdigit(*bp)) { + while (isdigit(*bp++)) + ; + } else + bp++; + case '"': + dquote = (dquote ? 1 : 0); + bp++; + break; + default: + bp++; + break; + } + } +breakbreak: + if (*bp == ':') + bp++; + return (bp); +} + +/* + * Return the (numeric) option id. + * Numeric options look like + * li#80 + * i.e. the option string is separated from the numeric value by + * a # character. If the option is not found we return -1. + * Note that we handle octal numbers beginning with 0. + */ +int +tgetnum(id) + char *id; +{ + register long int i; + register int base; + register char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (*bp == 0) + return (-1); + if (strncmp(bp, id, strlen(id)) != 0) + continue; + bp += strlen(id); + if (*bp == '@') + return (-1); + if (*bp != '#') + continue; + bp++; + base = 10; + if (*bp == '0') + base = 8; + i = 0; + while (isdigit(*bp)) + i *= base, i += *bp++ - '0'; + return (i); + } +} + +/* + * Handle a flag option. + * Flag options are given "naked", i.e. followed by a : or the end + * of the buffer. Return 1 if we find the option, or 0 if it is + * not given. + */ +int +tgetflag(id) + char *id; +{ + register char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (!*bp) + return (0); + if (strncmp(bp, id, strlen(id)) == 0) { + bp += strlen(id); + if (!*bp || *bp == ':') + return (1); + else if (*bp == '@') + return (0); + } + } +} + +/* + * Get a string valued option. + * These are given as + * cl=^Z + * Much decoding is done on the strings, and the strings are + * placed in area, which is a ref parameter which is updated. + * No checking on area overflow. + */ +char * +tgetstr(id, area) + char *id, **area; +{ + register char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (!*bp) + return (0); + if (strncmp(bp, id, strlen(id)) != 0) + continue; + bp += strlen(id); + if (*bp == '@') + return (0); + if (*bp != '=') + continue; + bp++; + return (tdecode(bp, area)); + } +} + +/* + * Tdecode does the grung work to decode the + * string capability escapes. + */ +static char * +tdecode(str, area) + register char *str; + char **area; +{ + register char *cp; + register int c; + register char *dp; + int i; + char term; + + term = ':'; + cp = *area; +again: + if (*str == '"') { + term = '"'; + str++; + } + while ((c = *str++) && c != term) { + switch (c) { + + case '^': + c = *str++ & 037; + break; + + case '\\': + dp = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\""; + c = *str++; +nextc: + if (*dp++ == c) { + c = *dp++; + break; + } + dp++; + if (*dp) + goto nextc; + if (isdigit(c)) { + c -= '0', i = 2; + do + c <<= 3, c |= *str++ - '0'; + while (--i && isdigit(*str)); + } + break; + } + *cp++ = c; + } + if (c == term && term != ':') { + term = ':'; + goto again; + } + *cp++ = 0; + str = *area; + *area = cp; + return (str); +} diff --git a/usr.sbin/rtadvd/advcap.h b/usr.sbin/rtadvd/advcap.h new file mode 100644 index 00000000000..445a71a6b16 --- /dev/null +++ b/usr.sbin/rtadvd/advcap.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1994,1995 by Andrey A. Chernov, Moscow, Russia. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Based on Id: termcap.h,v 1.8 1996/09/10 12:42:10 peter Exp */ + +#ifndef _ADVCAP_H_ +#define _ADVCAP_H_ + +#include <sys/cdefs.h> + +__BEGIN_DECLS + +extern int agetent __P((char *, const char *)); +extern int agetflag __P((const char *)); +extern int agetnum __P((const char *)); +extern char *agetstr __P((const char *, char **)); + +__END_DECLS + +#endif /* _ADVCAP_H_ */ diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c new file mode 100644 index 00000000000..851fe5bb49e --- /dev/null +++ b/usr.sbin/rtadvd/config.c @@ -0,0 +1,642 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <net/if.h> +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include <net/if_var.h> +#endif /* __FreeBSD__ >= 3 */ +#include <net/route.h> +#include <net/if_dl.h> + +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet6/ip6.h> +#include <netinet6/ip6_var.h> +#include <netinet6/icmp6.h> + +#include <arpa/inet.h> + +#include <stdio.h> +#include <syslog.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#ifdef __NetBSD__ +#include <search.h> +#endif +#include <unistd.h> + +#include "rtadvd.h" +#include "advcap.h" +#include "timer.h" +#include "if.h" +#include "config.h" + +static void makeentry __P((char *, int, char *, int)); +static void make_packet __P((struct rainfo *)); +static void get_prefix __P((struct rainfo *)); + +extern struct rainfo *ralist; + +void +getconfig(intface) + char *intface; +{ + int stat, pfxs, i; + char tbuf[BUFSIZ]; + struct rainfo *tmp; + long val; + char buf[BUFSIZ]; + char *bp = buf; + char *addr; + +#define MUSTHAVE(var, cap) \ + { \ + int t; \ + if ((t = agetnum(cap)) < 0) { \ + fprintf(stderr, "rtadvd: need %s for interface %s\n", \ + cap, intface); \ + exit(1); \ + } \ + var = t; \ + } +#define MAYHAVE(var, cap, def) \ + { \ + if ((var = agetnum(cap)) < 0) \ + var = def; \ + } + + if ((stat = agetent(tbuf, intface)) <= 0) { + memset(tbuf, 0, sizeof(tbuf)); + syslog(LOG_INFO, + "<%s> %s isn't defined in the configuration file" + " or the configuration file doesn't exist." + " Treat it as default", + __FUNCTION__, intface); + } + + tmp = (struct rainfo *)malloc(sizeof(*ralist)); + memset(tmp, 0, sizeof(*tmp)); + tmp->prefix.next = tmp->prefix.prev = &tmp->prefix; + + /* get interface information */ + if (agetflag("nolladdr")) + tmp->advlinkopt = 0; + else + tmp->advlinkopt = 1; + if (tmp->advlinkopt) { + if ((tmp->sdl = if_nametosdl(intface)) == NULL) { + syslog(LOG_ERR, + "<%s> can't get information of %s", + __FUNCTION__, intface); + exit(1); + } + tmp->ifindex = tmp->sdl->sdl_index; + } else + tmp->ifindex = if_nametoindex(intface); + strncpy(tmp->ifname, intface, sizeof(tmp->ifname)); + if ((tmp->phymtu = if_getmtu(intface)) == 0) { + tmp->phymtu = IPV6_MMTU; + syslog(LOG_WARNING, + "<%s> can't get interface mtu of %s. Treat as %d", + __FUNCTION__, intface, IPV6_MMTU); + } + + /* + * set router configuration variables. + */ + MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL); + if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) { + syslog(LOG_ERR, + "<%s> maxinterval must be between %d and %d", + __FUNCTION__, MIN_MAXINTERVAL, MAX_MAXINTERVAL); + exit(1); + } + tmp->maxinterval = (u_int)val; + MAYHAVE(val, "mininterval", tmp->maxinterval/3); + if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) { + syslog(LOG_ERR, + "<%s> mininterval must be between %d and %d", + __FUNCTION__, + MIN_MININTERVAL, + (tmp->maxinterval * 3) / 4); + exit(1); + } + tmp->mininterval = (u_int)val; + + MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT); + tmp->hoplimit = val & 0xff; + + MAYHAVE(val, "raflags", 0); + tmp->managedflg= val & ND_RA_FLAG_MANAGED; + tmp->otherflg = val & ND_RA_FLAG_OTHER; + + MAYHAVE(val, "rltime", tmp->maxinterval * 3); + if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) { + syslog(LOG_ERR, + "<%s> router lifetime on %s must be 0 or" + " between %d and %d", + __FUNCTION__, intface, + tmp->maxinterval, MAXROUTERLIFETIME); + exit(1); + } + tmp->lifetime = val & 0xffff; + + MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME); + if (val > MAXREACHABLETIME) { + syslog(LOG_ERR, + "<%s> reachable time must be no greater than %d", + __FUNCTION__, MAXREACHABLETIME); + exit(1); + } + tmp->reachabletime = (u_int32_t)val; + + MAYHAVE(val, "retrans", DEF_ADVRETRANSTIMER); + if (val < 0 || val > 0xffffffff) { + syslog(LOG_ERR, + "<%s> retrans time out of range", __FUNCTION__); + exit(1); + } + tmp->retranstimer = (u_int32_t)val; + + /* prefix information */ + if ((pfxs = agetnum("addrs")) < 0) { + /* auto configure prefix information */ + if (agetstr("addr", &bp) || agetstr("addr1", &bp)) { + syslog(LOG_ERR, + "<%s> conflicting prefix configuration for %s: " + "automatic and manual config at the same time", + __FUNCTION__, intface); + exit(1); + } + get_prefix(tmp); + } + else { + tmp->pfxs = pfxs; + for (i = 0; i < pfxs; i++) { + struct prefix *pfx; + char entbuf[256]; + int added = (pfxs > 1) ? 1 : 0; + + /* allocate memory to store prefix information */ + if ((pfx = malloc(sizeof(struct prefix))) == NULL) { + syslog(LOG_ERR, + "<%s> can't allocate enough memory", + __FUNCTION__); + exit(1); + } + /* link into chain */ + insque(pfx, &tmp->prefix); + + makeentry(entbuf, i, "prefixlen", added); + MAYHAVE(val, entbuf, 64); + if (val < 0 || val > 128) { + syslog(LOG_ERR, + "<%s> prefixlen out of range", + __FUNCTION__); + exit(1); + } + pfx->prefixlen = (int)val; + + makeentry(entbuf, i, "pinfoflags", added); + MAYHAVE(val, entbuf, + (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO)); + pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK; + pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO; + + makeentry(entbuf, i, "vltime", added); + MAYHAVE(val, entbuf, DEF_ADVVALIDLIFETIME); + if (val < 0 || val > 0xffffffff) { + syslog(LOG_ERR, + "<%s> vltime out of range", + __FUNCTION__); + exit(1); + } + pfx->validlifetime = (u_int32_t)val; + + makeentry(entbuf, i, "pltime", added); + MAYHAVE(val, entbuf, DEF_ADVPREFERREDLIFETIME); + if (val < 0 || val > 0xffffffff) { + syslog(LOG_ERR, + "<%s> pltime out of range", + __FUNCTION__); + exit(1); + } + pfx->preflifetime = (u_int32_t)val; + + makeentry(entbuf, i, "addr", added); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) { + syslog(LOG_ERR, + "<%s> need %s as an prefix for " + "interface %s", + __FUNCTION__, entbuf, intface); + exit(1); + } + if (inet_pton(AF_INET6, addr, + &pfx->prefix) != 1) { + syslog(LOG_ERR, + "<%s> inet_pton failed for %s", + __FUNCTION__, addr); + exit(1); + } + if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) { + syslog(LOG_ERR, + "<%s> multicast prefix(%s) must " + "not be advertised (IF=%s)", + __FUNCTION__, addr, intface); + exit(1); + } + if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix)) + syslog(LOG_NOTICE, + "<%s> link-local prefix(%s) will be" + " advertised on %s", + __FUNCTION__, addr, intface); + } + } + + MAYHAVE(val, "mtu", 0); + if (val < 0 || val > 0xffffffff) { + syslog(LOG_ERR, + "<%s> mtu out of range", __FUNCTION__); + exit(1); + } + tmp->linkmtu = (u_int32_t)val; + if (tmp->linkmtu == 0) { + char *mtustr; + + if ((mtustr = (char *)agetstr("mtu", &bp)) && + strcmp(mtustr, "auto") == 0) + tmp->linkmtu = tmp->phymtu; + } + else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) { + syslog(LOG_ERR, + "<%s> advertised link mtu must be between" + " least MTU and physical link MTU", + __FUNCTION__); + exit(1); + } + + /* okey */ + tmp->next = ralist; + ralist = tmp; + + /* construct the sending packet */ + make_packet(tmp); + + /* set timer */ + tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update, + tmp, tmp); + ra_timer_update((void *)tmp, &tmp->timer->tm); + rtadvd_set_timer(&tmp->timer->tm, tmp->timer); +} + +static void +get_prefix(struct rainfo *rai) +{ + size_t len; + u_char *buf, *lim, *next; + u_char ntopbuf[INET6_ADDRSTRLEN]; + + if ((len = rtbuf_len()) < 0) { + syslog(LOG_ERR, + "<%s> can't get buffer length for routing info", + __FUNCTION__); + exit(1); + } + if ((buf = malloc(len)) == NULL) { + syslog(LOG_ERR, + "<%s> can't allocate buffer", __FUNCTION__); + exit(1); + } + if (get_rtinfo(buf, &len) < 0) { + syslog(LOG_ERR, + "<%s> can't get routing inforamtion", __FUNCTION__); + exit(1); + } + + lim = buf + len; + next = get_next_msg(buf, lim, rai->ifindex, &len, + RTADV_TYPE2BITMASK(RTM_GET)); + while (next < lim) { + struct prefix *pp; + struct in6_addr *a; + + /* allocate memory to store prefix info. */ + if ((pp = malloc(sizeof(*pp))) == NULL) { + syslog(LOG_ERR, + "<%s> can't get allocate buffer for prefix", + __FUNCTION__); + exit(1); + } + memset(pp, 0, sizeof(*pp)); + + /* set prefix and its length */ + a = get_addr(next); + memcpy(&pp->prefix, a, sizeof(*a)); + if ((pp->prefixlen = get_prefixlen(next)) < 0) { + syslog(LOG_ERR, + "<%s> failed to get prefixlen " + "or prefixl is invalid", + __FUNCTION__); + exit(1); + } + syslog(LOG_DEBUG, + "<%s> add %s/%d to prefix list on %s", + __FUNCTION__, + inet_ntop(AF_INET6, a, ntopbuf, INET6_ADDRSTRLEN), + pp->prefixlen, rai->ifname); + + /* set other fields with protocol defaults */ + pp->validlifetime = DEF_ADVVALIDLIFETIME; + pp->preflifetime = DEF_ADVPREFERREDLIFETIME; + pp->onlinkflg = 1; + pp->autoconfflg = 1; + + /* link into chain */ + insque(pp, &rai->prefix); + + /* counter increment */ + rai->pfxs++; + + /* forward pointer and get next prefix(if any) */ + next += len; + next = get_next_msg(next, lim, rai->ifindex, + &len, RTADV_TYPE2BITMASK(RTM_GET)); + } + + free(buf); +} + +static void +makeentry(buf, id, string, add) + char *buf, *string; + int id, add; +{ + strcpy(buf, string); + if (add) { + char *cp; + + cp = (char *)index(buf, '\0'); + cp += sprintf(cp, "%d", id); + *cp = '\0'; + } +} + +/* + * Add a prefix to the list of specified interface and reconstruct + * the outgoing packet. + * The prefix must not be in the list. + * XXX: other parameter of the prefix(e.g. lifetime) shoule be + * able to be specified. + */ +static void +add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr) +{ + struct prefix *prefix; + u_char ntopbuf[INET6_ADDRSTRLEN]; + + if ((prefix = malloc(sizeof(*prefix))) == NULL) { + syslog(LOG_ERR, "<%s> memory allocation failed", + __FUNCTION__); + return; /* XXX: error or exit? */ + } + prefix->prefix = ipr->ipr_prefix.sin6_addr; + prefix->prefixlen = ipr->ipr_plen; + prefix->validlifetime = ipr->ipr_vltime; + prefix->preflifetime = ipr->ipr_pltime; + prefix->onlinkflg = ipr->ipr_raf_onlink; + prefix->autoconfflg = ipr->ipr_raf_auto; + + insque(prefix, &rai->prefix); + + syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s", + __FUNCTION__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + ipr->ipr_plen, rai->ifname); + + /* free the previous packet */ + free(rai->ra_data); + rai->ra_data = 0; + + /* reconstruct the packet */ + rai->pfxs++; + make_packet(rai); + + /* + * reset the timer so that the new prefix will be advertised quickly. + */ + rai->initcounter = 0; + ra_timer_update((void *)rai, &rai->timer->tm); + rtadvd_set_timer(&rai->timer->tm, rai->timer); +} + +/* + * Delete a prefix to the list of specified interface and reconstruct + * the outgoing packet. + * The prefix must be in the list + */ +void +delete_prefix(struct rainfo *rai, struct prefix *prefix) +{ + u_char ntopbuf[INET6_ADDRSTRLEN]; + + remque(prefix); + syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s", + __FUNCTION__, inet_ntop(AF_INET6, &prefix->prefix, + ntopbuf, INET6_ADDRSTRLEN), + prefix->prefixlen, rai->ifname); + free(prefix); + rai->pfxs--; + make_packet(rai); +} + +/* + * Try to get an in6_prefixreq contents for a prefix which matches + * ipr->ipr_prefix and ipr->ipr_plen and belongs to + * the interface whose name is ipr->ipr_name[]. + */ +static int +init_prefix(struct in6_prefixreq *ipr) +{ + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__, + strerror(errno)); + exit(1); + } + + if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) { + syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __FUNCTION__, + strerror(errno)); + + ipr->ipr_vltime = DEF_ADVVALIDLIFETIME; + ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME; + ipr->ipr_raf_onlink = 1; + ipr->ipr_raf_auto = 1; + /* omit other field initialization */ + } + else if (ipr->ipr_origin < PR_ORIG_RR) { + u_char ntopbuf[INET6_ADDRSTRLEN]; + + syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is" + "lower than PR_ORIG_RR(router renumbering)." + "This should not happen if I am router", __FUNCTION__, + inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf, + sizeof(ntopbuf)), ipr->ipr_origin); + return 1; + } + + close(s); + return 0; +} + +void +make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen) +{ + struct in6_prefixreq ipr; + + memset(&ipr, 0, sizeof(ipr)); + if (if_indextoname(ifindex, ipr.ipr_name) == NULL) { + syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't" + "exist. This should not happen! %s", __FUNCTION__, + ifindex, strerror(errno)); + exit(1); + } + ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix); + ipr.ipr_prefix.sin6_family = AF_INET6; + ipr.ipr_prefix.sin6_addr = *addr; + ipr.ipr_plen = plen; + + if (init_prefix(&ipr)) + return; /* init failed by some error */ + add_prefix(rai, &ipr); +} + +static void +make_packet(struct rainfo *rainfo) +{ + size_t packlen, lladdroptlen = 0; + char *buf; + struct nd_router_advert *ra; + struct nd_opt_prefix_info *ndopt_pi; + struct nd_opt_mtu *ndopt_mtu; + struct prefix *pfx; + + /* calculate total length */ + packlen = sizeof(struct nd_router_advert); + if (rainfo->advlinkopt) { + if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) { + syslog(LOG_INFO, + "<%s> link-layer address option has" + " null length on %s." + " Treat as not included.", + __FUNCTION__, rainfo->ifname); + rainfo->advlinkopt = 0; + } + packlen += lladdroptlen; + } + if (rainfo->pfxs) + packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs; + if (rainfo->linkmtu) + packlen += sizeof(struct nd_opt_mtu); + + /* allocate memory for the packet */ + if ((buf = malloc(packlen)) == NULL) { + syslog(LOG_ERR, + "<%s> can't get enough memory for an RA packet", + __FUNCTION__); + exit(1); + } + rainfo->ra_data = buf; + /* XXX: what if packlen > 576? */ + rainfo->ra_datalen = packlen; + + /* + * construct the packet + */ + ra = (struct nd_router_advert *)buf; + ra->nd_ra_type = ND_ROUTER_ADVERT; + ra->nd_ra_code = 0; + ra->nd_ra_cksum = 0; + ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit); + ra->nd_ra_flags_reserved = 0; + ra->nd_ra_flags_reserved |= + rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0; + ra->nd_ra_flags_reserved |= + rainfo->otherflg ? ND_RA_FLAG_OTHER : 0; + ra->nd_ra_router_lifetime = htons(rainfo->lifetime); + ra->nd_ra_reachable = htonl(rainfo->reachabletime); + ra->nd_ra_retransmit = htonl(rainfo->retranstimer); + buf += sizeof(*ra); + + if (rainfo->advlinkopt) { + lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf); + buf += lladdroptlen; + } + + if (rainfo->linkmtu) { + ndopt_mtu = (struct nd_opt_mtu *)buf; + ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU; + ndopt_mtu->nd_opt_mtu_len = 1; + ndopt_mtu->nd_opt_mtu_reserved = 0; + ndopt_mtu->nd_opt_mtu_mtu = ntohl(rainfo->linkmtu); + buf += sizeof(struct nd_opt_mtu); + } + + for (pfx = rainfo->prefix.next; + pfx != &rainfo->prefix; pfx = pfx->next) { + ndopt_pi = (struct nd_opt_prefix_info *)buf; + ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; + ndopt_pi->nd_opt_pi_len = 4; + ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen; + ndopt_pi->nd_opt_pi_flags_reserved = 0; + if (pfx->onlinkflg) + ndopt_pi->nd_opt_pi_flags_reserved |= + ND_OPT_PI_FLAG_ONLINK; + if (pfx->autoconfflg) + ndopt_pi->nd_opt_pi_flags_reserved |= + ND_OPT_PI_FLAG_AUTO; + ndopt_pi->nd_opt_pi_valid_time = ntohl(pfx->validlifetime); + ndopt_pi->nd_opt_pi_preferred_time = + ntohl(pfx->preflifetime); + ndopt_pi->nd_opt_pi_reserved2 = 0; + ndopt_pi->nd_opt_pi_prefix = pfx->prefix; + + buf += sizeof(struct nd_opt_prefix_info); + } + + return; +} diff --git a/usr.sbin/rtadvd/config.h b/usr.sbin/rtadvd/config.h new file mode 100644 index 00000000000..823af1691ae --- /dev/null +++ b/usr.sbin/rtadvd/config.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern void getconfig __P((char *)); +extern void delete_prefix __P((struct rainfo *, struct prefix *)); +extern void make_prefix __P((struct rainfo *, int, struct in6_addr *, int)); diff --git a/usr.sbin/rtadvd/if.c b/usr.sbin/rtadvd/if.c new file mode 100644 index 00000000000..3488968f393 --- /dev/null +++ b/usr.sbin/rtadvd/if.c @@ -0,0 +1,592 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_types.h> +#ifdef __FreeBSD__ +# include <net/ethernet.h> +#endif +#ifdef __bsdi__ +# include <ifaddrs.h> +#endif +#ifdef __NetBSD__ +#include <net/if_ether.h> +#endif +#include <net/route.h> +#include <net/if_dl.h> +#include <netinet/in.h> +#include <netinet/icmp6.h> +#ifdef __bsdi__ +# include <netinet/if_ether.h> +#endif +#ifdef __OpenBSD__ +#include <netinet/if_ether.h> +#endif +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include "rtadvd.h" +#include "if.h" + +#define ROUNDUP(a, size) \ + (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) + +#define NEXT_SA(ap) (ap) = (struct sockaddr *) \ + ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\ + sizeof(u_long)) :\ + sizeof(u_long))) + +struct if_msghdr **iflist; +int iflist_init_ok; +size_t ifblock_size; +char *ifblock; + +static void get_iflist __P((char **buf, size_t *size)); +static void parse_iflist __P((struct if_msghdr ***ifmlist_p, char *buf, + size_t bufsize)); + +static void +get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) +{ + int i; + + for (i = 0; i < RTAX_MAX; i++) { + if (addrs & (1 << i)) { + rti_info[i] = sa; + NEXT_SA(sa); + } + else + rti_info[i] = NULL; + } +} + +struct sockaddr_dl * +if_nametosdl(char *name) +{ + int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; + char *buf, *next, *lim; + size_t len; + struct if_msghdr *ifm; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_dl *sdl = NULL, *ret_sdl; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return(NULL); + if ((buf = malloc(len)) == NULL) + return(NULL); + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + free(buf); + return(NULL); + } + + lim = buf + len; + for (next = buf; next < lim; next += ifm->ifm_msglen) { + ifm = (struct if_msghdr *)next; + if (ifm->ifm_type == RTM_IFINFO) { + sa = (struct sockaddr *)(ifm + 1); + get_rtaddrs(ifm->ifm_addrs, sa, rti_info); + if ((sa = rti_info[RTAX_IFP]) != NULL) { + if (sa->sa_family == AF_LINK) { + sdl = (struct sockaddr_dl *)sa; + if (strlen(name) != sdl->sdl_nlen) + continue; /* not same len */ + if (strncmp(&sdl->sdl_data[0], + name, + sdl->sdl_nlen) == 0) { + break; + } + } + } + } + } + if (next == lim) { + /* search failed */ + free(buf); + return(NULL); + } + + if ((ret_sdl = malloc(sdl->sdl_len)) == NULL) + return(NULL); + memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len); + return(ret_sdl); +} + +int +if_getmtu(char *name) +{ +#if defined(__FreeBSD__) || defined(__NetBSD__) + struct ifreq ifr; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + return(0); + + ifr.ifr_addr.sa_family = AF_INET6; + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0) { + close(s); + return(0); + } + + close(s); + + return(ifr.ifr_mtu); +#endif +#ifdef __bsdi__ + struct ifaddrs *ifa; + struct if_data *ifd; + + if (getifaddrs(&ifa) < 0) + return(0); + while (ifa) { + if (strcmp(ifa->ifa_name, name) == 0) { + ifd = ifa->ifa_data; + if (ifd) + return ifd->ifi_mtu; + else + return 0; + } + ifa = ifa->ifa_next; + } + return 0; +#endif + /* last resort */ + return 0; +} + +/* give interface index and its old flags, then new flags returned */ +int +if_getflags(int ifindex, int oifflags) +{ + struct ifreq ifr; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__, + strerror(errno)); + return (oifflags & ~IFF_UP); + } + + if_indextoname(ifindex, ifr.ifr_name); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { + syslog(LOG_ERR, "<%s> ioctl:SIOCGIFFLAGS: failed for %s", + __FUNCTION__, ifr.ifr_name); + close(s); + return (oifflags & ~IFF_UP); + } + return (ifr.ifr_flags); +} + +#define ROUNDUP8(a) (1 + (((a) - 1) | 7)) +int +lladdropt_length(struct sockaddr_dl *sdl) +{ + switch(sdl->sdl_type) { + case IFT_ETHER: + return(ROUNDUP8(ETHER_ADDR_LEN + 2)); + default: + return(0); + } +} + +void +lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt) +{ + char *addr; + + ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */ + + switch(sdl->sdl_type) { + case IFT_ETHER: + ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3; + addr = (char *)(ndopt + 1); + memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN); + break; + default: + syslog(LOG_ERR, + "<%s> unsupported link type(%d)", + __FUNCTION__, sdl->sdl_type); + exit(1); + } + + return; +} + +int +rtbuf_len() +{ + size_t len; + + int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0}; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return(-1); + + return(len); +} + +int +get_rtinfo(char *buf, size_t *len) +{ + int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0}; + + if (sysctl(mib, 6, buf, len, NULL, 0) < 0) + return(-1); + + return(0); +} + +#define FILTER_MATCH(type, filter) ((0x1 << type) & filter) +#define SIN6(s) ((struct sockaddr_in6 *)(s)) +#define SDL(s) ((struct sockaddr_dl *)(s)) +char * +get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter) +{ + struct rt_msghdr *rtm; + struct ifa_msghdr *ifam; + struct sockaddr *sa, *dst, *gw, *ifa, *rti_info[RTAX_MAX]; + + *lenp = 0; + for (rtm = (struct rt_msghdr *)buf; + rtm < (struct rt_msghdr *)lim; + rtm = (struct rt_msghdr *)(((char *)rtm) + rtm->rtm_msglen)) { + /* just for safety */ + if (!rtm->rtm_msglen) { + syslog(LOG_WARNING, "<%s> rtm_msglen is 0 " + "(buf=%p lim=%p rtm=%p)", __FUNCTION__, + buf, lim, rtm); + break; + } + if (FILTER_MATCH(rtm->rtm_type, filter) == 0) { + continue; + } + + switch (rtm->rtm_type) { + case RTM_GET: + case RTM_ADD: + case RTM_DELETE: + /* address related checks */ + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + if ((dst = rti_info[RTAX_DST]) == NULL || + dst->sa_family != AF_INET6) + continue; + + if (IN6_IS_ADDR_LINKLOCAL(&SIN6(dst)->sin6_addr) || + IN6_IS_ADDR_MULTICAST(&SIN6(dst)->sin6_addr)) + continue; + + if ((gw = rti_info[RTAX_GATEWAY]) == NULL || + gw->sa_family != AF_LINK) + continue; + if (ifindex && SDL(gw)->sdl_index != ifindex) + continue; + + if (rti_info[RTAX_NETMASK] == NULL) + continue; + + /* found */ + *lenp = rtm->rtm_msglen; + return (char *)rtm; + /* NOTREACHED */ + case RTM_NEWADDR: + case RTM_DELADDR: + ifam = (struct ifa_msghdr *)rtm; + + /* address related checks */ + sa = (struct sockaddr *)(ifam + 1); + get_rtaddrs(ifam->ifam_addrs, sa, rti_info); + if ((ifa = rti_info[RTAX_IFA]) == NULL || + (ifa->sa_family != AF_INET && + ifa->sa_family != AF_INET6)) + continue; + + if (ifa->sa_family == AF_INET6 && + (IN6_IS_ADDR_LINKLOCAL(&SIN6(ifa)->sin6_addr) || + IN6_IS_ADDR_MULTICAST(&SIN6(ifa)->sin6_addr))) + continue; + + if (ifindex && ifam->ifam_index != ifindex) + continue; + + /* found */ + *lenp = ifam->ifam_msglen; + return (char *)rtm; + /* NOTREACHED */ + case RTM_IFINFO: + /* found */ + *lenp = rtm->rtm_msglen; + return (char *)rtm; + /* NOTREACHED */ + } + } + + return (char *)rtm; +} +#undef FILTER_MATCH(type, filter) + +struct in6_addr * +get_addr(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + + return(&SIN6(rti_info[RTAX_DST])->sin6_addr); +} + +int +get_rtm_ifindex(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + + return(((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index); +} + +int +get_ifm_ifindex(char *buf) +{ + struct if_msghdr *ifm = (struct if_msghdr *)buf; + + return ((int)ifm->ifm_index); +} + +int +get_ifam_ifindex(char *buf) +{ + struct ifa_msghdr *ifam = (struct ifa_msghdr *)buf; + + return ((int)ifam->ifam_index); +} + +int +get_ifm_flags(char *buf) +{ + struct if_msghdr *ifm = (struct if_msghdr *)buf; + + return (ifm->ifm_flags); +} + +int +get_prefixlen(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + int masklen; + u_char *p, *lim; + + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + sa = rti_info[RTAX_NETMASK]; + + p = (u_char *)(&SIN6(sa)->sin6_addr); + lim = (u_char *)sa + sa->sa_len; + for (masklen = 0; p < lim; p++) { + switch (*p) { + case 0xff: + masklen += 8; + break; + case 0xfe: + masklen += 7; + break; + case 0xfc: + masklen += 6; + break; + case 0xf8: + masklen += 5; + break; + case 0xf0: + masklen += 4; + break; + case 0xe0: + masklen += 3; + break; + case 0xc0: + masklen += 2; + break; + case 0x80: + masklen += 1; + break; + case 0x00: + break; + default: + return(-1); + } + } + + return(masklen); +} + +int +rtmsg_type(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + + return(rtm->rtm_type); +} + +int +rtmsg_len(char *buf) +{ + struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + + return(rtm->rtm_msglen); +} + +int +ifmsg_len(char *buf) +{ + struct if_msghdr *ifm = (struct if_msghdr *)buf; + + return(ifm->ifm_msglen); +} + +/* + * alloc buffer and get if_msghdrs block from kernel, + * and put them into the buffer + */ +static void +get_iflist(char **buf, size_t *size) +{ + int mib[6]; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET6; + mib[4] = NET_RT_IFLIST; + mib[5] = 0; + + if (sysctl(mib, 6, NULL, size, NULL, 0) < 0) { + syslog(LOG_ERR, "<%s> sysctl: iflist size get failed", + __FUNCTION__); + exit(1); + } + if ((*buf = malloc(*size)) == NULL) { + syslog(LOG_ERR, "<%s> malloc failed", __FUNCTION__); + exit(1); + } + if (sysctl(mib, 6, *buf, size, NULL, 0) < 0) { + syslog(LOG_ERR, "<%s> sysctl: iflist get failed", + __FUNCTION__); + exit(1); + } + return; +} + +/* + * alloc buffer and parse if_msghdrs block passed as arg, + * and init the buffer as list of pointers ot each of the if_msghdr. + */ +static void +parse_iflist(struct if_msghdr ***ifmlist_p, char *buf, size_t bufsize) +{ + int iflentry_size, malloc_size; + struct if_msghdr *ifm; + struct ifa_msghdr *ifam; + char *lim; + + /* + * Estimate least size of an iflist entry, to be obtained from kernel. + * Should add sizeof(sockaddr) ?? + */ + iflentry_size = sizeof(struct if_msghdr); + /* roughly estimate max list size of pointers to each if_msghdr */ + malloc_size = (bufsize/iflentry_size) * sizeof(size_t); + if ((*ifmlist_p = (struct if_msghdr **)malloc(malloc_size)) == NULL) { + syslog(LOG_ERR, "<%s> malloc failed", __FUNCTION__); + exit(1); + } + + lim = buf + bufsize; + for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) { + if (ifm->ifm_msglen == 0) { + syslog(LOG_WARNING, "<%s> ifm_msglen is 0 " + "(buf=%p lim=%p ifm=%p)", __FUNCTION__, + buf, lim, ifm); + return; + } + + if (ifm->ifm_type == RTM_IFINFO) { + (*ifmlist_p)[ifm->ifm_index] = ifm; + } else { + syslog(LOG_ERR, "out of sync parsing NET_RT_IFLIST\n" + "expected %d, got %d\n msglen = %d\n" + "buf:%p, ifm:%p, lim:%p\n", + RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen, + buf, ifm, lim); + exit (1); + } + for (ifam = (struct ifa_msghdr *) + ((char *)ifm + ifm->ifm_msglen); + ifam < (struct ifa_msghdr *)lim; + ifam = (struct ifa_msghdr *) + ((char *)ifam + ifam->ifam_msglen)) { + /* just for safety */ + if (!ifam->ifam_msglen) { + syslog(LOG_WARNING, "<%s> ifa_msglen is 0 " + "(buf=%p lim=%p ifam=%p)", __FUNCTION__, + buf, lim, ifam); + return; + } + if (ifam->ifam_type != RTM_NEWADDR) + break; + } + ifm = (struct if_msghdr *)ifam; + } +} + +void +init_iflist() +{ + if (ifblock) { + free(ifblock); + ifblock_size = 0; + } + if (iflist) + free(iflist); + /* get iflist block from kernel */ + get_iflist(&ifblock, &ifblock_size); + + /* make list of pointers to each if_msghdr */ + parse_iflist(&iflist, ifblock, ifblock_size); + +} diff --git a/usr.sbin/rtadvd/if.h b/usr.sbin/rtadvd/if.h new file mode 100644 index 00000000000..0fc97a47196 --- /dev/null +++ b/usr.sbin/rtadvd/if.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define RTADV_TYPE2BITMASK(type) (0x1 << type) + +extern struct if_msghdr **iflist; +extern size_t ifblock_size; +extern char *ifblock; + +struct sockaddr_dl *if_nametosdl __P((char *name)); +int if_getmtu __P((char *name)); +int if_getflags __P((int ifindex, int oifflags)); +int lladdropt_length __P((struct sockaddr_dl *sdl)); +void lladdropt_fill __P((struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)); +int rtbuf_len __P((void)); +int get_rtinfo __P((char *buf, size_t *len)); +char *get_next_msg __P((char *buf, char *lim, int ifindex, size_t *lenp, + int filter)); +struct in6_addr *get_addr __P((char *buf)); +int get_rtm_ifindex __P((char *buf)); +int get_ifm_ifindex __P((char *buf)); +int get_ifam_ifindex __P((char *buf)); +int get_ifm_flags __P((char *buf)); +int get_prefixlen __P((char *buf)); +int rtmsg_type __P((char *buf)); +int ifmsg_type __P((char *buf)); +int rtmsg_len __P((char *buf)); +int ifmsg_len __P((char *buf)); +void init_iflist __P((void)); diff --git a/usr.sbin/rtadvd/pathnames.h b/usr.sbin/rtadvd/pathnames.h new file mode 100644 index 00000000000..a14ed50f772 --- /dev/null +++ b/usr.sbin/rtadvd/pathnames.h @@ -0,0 +1 @@ +#define _PATH_RTADVDCONF "/usr/local/v6/etc/rtadvd.conf" diff --git a/usr.sbin/rtadvd/rrenum.c b/usr.sbin/rtadvd/rrenum.c new file mode 100644 index 00000000000..4ed70202831 --- /dev/null +++ b/usr.sbin/rtadvd/rrenum.c @@ -0,0 +1,407 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include <net/if_var.h> +#endif /* __FreeBSD__ >= 3 */ +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/icmp6.h> + +#include <arpa/inet.h> + +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> +#include "rrenum.h" +#include "if.h" + +#define RR_ISSET_SEGNUM(segnum_bits, segnum) \ + ((((segnum_bits)[(segnum) >> 5]) & (1 << ((segnum) & 31))) != 0) +#define RR_SET_SEGNUM(segnum_bits, segnum) \ + (((segnum_bits)[(segnum) >> 5]) |= (1 << ((segnum) & 31))) + +struct rr_operation { + u_long rro_seqnum; + u_long rro_segnum_bits[8]; +}; + +static struct rr_operation rro; +static int rr_rcvifindex; +static int rrcmd2pco[RPM_PCO_MAX] = {0, + SIOCAIFPREFIX_IN6, + SIOCCIFPREFIX_IN6, + SIOCSGIFPREFIX_IN6 + }; +static int s; + +/* + * Check validity of a Prefix Control Operation(PCO). + * Return 0 on success, 1 on failure. + */ +static int +rr_pco_check(int len, struct rr_pco_match *rpm) +{ + struct rr_pco_use *rpu, *rpulim; + int checklen; + + /* rpm->rpm_len must be (4N * 3) as router-renum-05.txt */ + if ((rpm->rpm_len - 3) < 0 || /* must be at least 3 */ + (rpm->rpm_len - 3) & 0x3) { /* must be multiple of 4 */ + syslog(LOG_WARNING, "<%s> rpm_len %d is not 4N * 3", + __FUNCTION__, rpm->rpm_len); + return 1; + } + /* rpm->rpm_code must be valid value */ + switch(rpm->rpm_code) { + case RPM_PCO_ADD: + case RPM_PCO_CHANGE: + case RPM_PCO_SETGLOBAL: + break; + default: + syslog(LOG_WARNING, "<%s> unknown rpm_code %d", __FUNCTION__, + rpm->rpm_code); + return 1; + } + /* rpm->rpm_matchlen must be 0 to 128 inclusive */ + if (rpm->rpm_matchlen > 128) { + syslog(LOG_WARNING, "<%s> rpm_matchlen %d is over 128", + __FUNCTION__, rpm->rpm_matchlen); + return 1; + } + + /* + * rpu->rpu_uselen, rpu->rpu_keeplen, and sum of them must be + * between 0 and 128 inclusive + */ + for (rpu = (struct rr_pco_use *)(rpm + 1), + rpulim = (struct rr_pco_use *)((char *)rpm + len); + rpu < rpulim; + rpu += 1) { + checklen = rpu->rpu_uselen; + checklen += rpu->rpu_keeplen; + /* + * omit these check, because either of rpu_uselen + * and rpu_keeplen is unsigned char + * (128 > rpu_uselen > 0) + * (128 > rpu_keeplen > 0) + * (rpu_uselen + rpu_keeplen > 0) + */ + if (checklen > 128) { + syslog(LOG_WARNING, "<%s> sum of rpu_uselen %d and" + " rpu_keeplen %d is %d(over 128)", + __FUNCTION__, rpu->rpu_uselen, + rpu->rpu_keeplen, + rpu->rpu_uselen + rpu->rpu_keeplen); + return 1; + } + } + return 0; +} + +static void +do_use_prefix(int len, struct rr_pco_match *rpm, struct in6_rrenumreq *irr) { + struct rr_pco_use *rpu, *rpulim; + + rpu = (struct rr_pco_use *)(rpm + 1); + rpulim = (struct rr_pco_use *)((char *)rpm + len); + + if (rpu == rpulim) { + if (rpm->rpm_code == RPM_PCO_ADD) + return; + + irr->irr_u_uselen = 0; + irr->irr_u_keeplen = 0; + irr->irr_raf_mask_onlink = 0; + irr->irr_raf_mask_auto = 0; + irr->irr_vltime = 0; + irr->irr_pltime = 0; + memset(&irr->irr_flags, 0, sizeof(irr->irr_flags)); + irr->irr_useprefix.sin6_len = 0; /* let it mean, no addition */ + irr->irr_useprefix.sin6_family = 0; + irr->irr_useprefix.sin6_addr = in6addr_any; + if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 && + errno != EADDRNOTAVAIL) + syslog(LOG_ERR, "<%s> ioctl: %s", __FUNCTION__, + strerror(errno)); + return; + } + + for (rpu = (struct rr_pco_use *)(rpm + 1), + rpulim = (struct rr_pco_use *)((char *)rpm + len); + rpu < rpulim; + rpu += 1) { + /* init in6_rrenumreq fields */ + irr->irr_u_uselen = rpu->rpu_uselen; + irr->irr_u_keeplen = rpu->rpu_keeplen; + irr->irr_raf_mask_onlink = rpu->rpu_mask_onlink; + irr->irr_raf_mask_auto = rpu->rpu_mask_autonomous; + irr->irr_vltime = rpu->rpu_vltime; + irr->irr_pltime = rpu->rpu_pltime; + irr->irr_raf_onlink = rpu->rpu_onlink; + irr->irr_raf_auto = rpu->rpu_autonomous; + irr->irr_rrf_decrvalid = rpu->rpu_decr_vltime; + irr->irr_rrf_decrprefd = rpu->rpu_decr_pltime; + irr->irr_useprefix.sin6_len = sizeof(irr->irr_useprefix); + irr->irr_useprefix.sin6_family = AF_INET6; + irr->irr_useprefix.sin6_addr = rpu->rpu_prefix; + + if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 && + errno != EADDRNOTAVAIL) + syslog(LOG_ERR, "<%s> ioctl: %s", __FUNCTION__, + strerror(errno)); + } +} + +/* + * process a Prefix Control Operation(PCO). + * return 0 on success, 1 on failure + */ +static int +do_pco(struct icmp6_router_renum *rr, int len, struct rr_pco_match *rpm) +{ + int ifindex = 0; + struct in6_rrenumreq irr; + + if ((rr_pco_check(len, rpm) != NULL)) + return 1; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__, + strerror(errno)); + exit(1); + } + + memset(&irr, 0, sizeof(irr)); + irr.irr_origin = PR_ORIG_RR; + irr.irr_m_len = rpm->rpm_matchlen; + irr.irr_m_minlen = rpm->rpm_minlen; + irr.irr_m_maxlen = rpm->rpm_maxlen; + irr.irr_matchprefix.sin6_len = sizeof(irr.irr_matchprefix); + irr.irr_matchprefix.sin6_family = AF_INET6; + irr.irr_matchprefix.sin6_addr = rpm->rpm_prefix; + + while (if_indextoname(++ifindex, irr.irr_name)) { + /* + * if rr_forceapply(A flag) is 0 and IFF_UP is off, + * the interface is not applied + */ + if (!rr->rr_forceapply && + (iflist[ifindex]->ifm_flags & IFF_UP) == 0) + continue; + /* TODO: interface scope check */ + do_use_prefix(len, rpm, &irr); + } + if (errno == ENXIO) + return 0; + else if (errno) { + syslog(LOG_ERR, "<%s> if_indextoname: %s", __FUNCTION__, + strerror(errno)); + return 1; + } + return 0; +} + +/* + * call do_pco() for each Prefix Control Operations(PCOs) in a received + * Router Renumbering Command packet. + * return 0 on success, 1 on failure + */ +static int +do_rr(int len, struct icmp6_router_renum *rr) +{ + struct rr_pco_match *rpm; + char *cp, *lim; + + lim = (char *)rr + len; + cp = (char *)(rr + 1); + len -= sizeof(struct icmp6_router_renum); + + /* get iflist block from kernel again, to get up-to-date information */ + init_iflist(); + + while (cp < lim) { + int rpmlen; + + rpm = (struct rr_pco_match *)cp; + if (len < sizeof(struct rr_pco_match)) { + tooshort: + syslog(LOG_ERR, "<%s> pkt too short. left len = %d. " + "gabage at end of pkt?", __FUNCTION__, len); + return 1; + } + rpmlen = rpm->rpm_len << 3; + if (len < rpmlen) + goto tooshort; + + if (do_pco(rr, rpmlen, rpm)) { + syslog(LOG_WARNING, "<%s> invalid PCO", __FUNCTION__); + goto next; + } + + next: + cp += rpmlen; + len -= rpmlen; + } + + return 0; +} + +/* + * check validity of a router renumbering command packet + * return 0 on success, 1 on failure + */ +static int +rr_command_check(int len, struct icmp6_router_renum *rr, struct in6_addr *from, + struct in6_addr *dst) +{ + u_char ntopbuf[INET6_ADDRSTRLEN]; + + /* omit rr minimal length check. hope kernel have done it. */ + /* rr_command length check */ + if (len < (sizeof(struct icmp6_router_renum) + + sizeof(struct rr_pco_match))) { + syslog(LOG_ERR, "<%s> rr_command len %d is too short", + __FUNCTION__, len); + return 1; + } + + /* destination check. only for multicast. omit unicast check. */ + if (IN6_IS_ADDR_MULTICAST(dst) && !IN6_IS_ADDR_MC_LINKLOCAL(dst) && + !IN6_IS_ADDR_MC_SITELOCAL(dst)) { + syslog(LOG_ERR, "<%s> dst mcast addr %s is illegal", + __FUNCTION__, + inet_ntop(AF_INET6, dst, ntopbuf, INET6_ADDRSTRLEN)); + return 1; + } + + /* seqnum and segnum check */ + if (rro.rro_seqnum > rr->rr_seqnum) { + syslog(LOG_WARNING, + "<%s> rcvd old seqnum %d from %s", + __FUNCTION__, (u_int32_t)ntohl(rr->rr_seqnum), + inet_ntop(AF_INET6, from, ntopbuf, INET6_ADDRSTRLEN)); + return 1; + } + if (rro.rro_seqnum == rr->rr_seqnum && + rr->rr_test == 0 && + RR_ISSET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum)) { + if (rr->rr_reqresult) + syslog(LOG_WARNING, + "<%s> rcvd duped segnum %d from %s", + __FUNCTION__, rr->rr_segnum, + inet_ntop(AF_INET6, from, ntopbuf, + INET6_ADDRSTRLEN)); + return 0; + } + + /* update seqnum */ + if (rro.rro_seqnum != rr->rr_seqnum) { + /* then must be "<" */ + + /* init rro_segnum_bits */ + memset(rro.rro_segnum_bits, 0, + sizeof(rro.rro_segnum_bits)); + } + rro.rro_seqnum = rr->rr_seqnum; + + return 0; +} + +static void +rr_command_input(int len, struct icmp6_router_renum *rr, + struct in6_addr *from, struct in6_addr *dst) +{ + /* rr_command validity check */ + if (rr_command_check(len, rr, from, dst)) + goto failed; + if (rr->rr_test && !rr->rr_reqresult) + return; + + /* do router renumbering */ + if (do_rr(len, rr)) { + goto failed; + } + + /* update segnum */ + RR_SET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum); + + return; + + failed: + syslog(LOG_ERR, "<%s> received RR was invalid", __FUNCTION__); + return; +} + +void +rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi, + struct sockaddr_in6 *from, struct in6_addr *dst) +{ + u_char ntopbuf[2][INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + + syslog(LOG_DEBUG, + "<%s> RR received from %s to %s on %s", + __FUNCTION__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf[0], INET6_ADDRSTRLEN), + inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + + rr_rcvifindex = pi->ipi6_ifindex; + + /* TODO: some consistency check. */ + + switch (rr->rr_code) { + case ICMP6_ROUTER_RENUMBERING_COMMAND: + rr_command_input(len, rr, &from->sin6_addr, dst); + /* TODO: send reply msg */ + break; + case ICMP6_ROUTER_RENUMBERING_RESULT: + /* RESULT will be processed by rrenumd */ + break; + case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET: + /* TODO: sequence number reset */ + break; + default: + syslog(LOG_ERR, "<%s> received unknown code %d", + __FUNCTION__, rr->rr_code); + break; + + } + + return; +} diff --git a/usr.sbin/rtadvd/rrenum.h b/usr.sbin/rtadvd/rrenum.h new file mode 100644 index 00000000000..78bdf6f248c --- /dev/null +++ b/usr.sbin/rtadvd/rrenum.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +void rr_input __P((int len, struct icmp6_router_renum *rr, + struct in6_pktinfo *pi, struct sockaddr_in6 *from, + struct in6_addr *dst)); diff --git a/usr.sbin/rtadvd/rtadvd.8 b/usr.sbin/rtadvd/rtadvd.8 new file mode 100644 index 00000000000..f3d2912f253 --- /dev/null +++ b/usr.sbin/rtadvd/rtadvd.8 @@ -0,0 +1,116 @@ +.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" KAME Id: rtadvd.8,v 1.1.1.1 1999/08/08 23:31:42 itojun Exp +.\" +.Dd May 17, 1998 +.Dt RTADVD 8 +.Os KAME +.Sh NAME +.Nm rtadvd +.Nd router advertisement daemon +.Sh SYNOPSIS +.Nm +.Op Fl c Ar configfile +.Op Fl dDfs +.Ar interface ... +.Sh DESCRIPTION +.Nm Rtadvd +advertises router advertisement packet to the specified +.Ar interfaces . +.Pp +The program will daemonize itself on invocation. +Then, it will voluntarily send router advertisement packet periodically. +If a router solicitation packet from host has reached the program, +the program will respond by router advertisement packet. +.Pp +For each interface, which is called advertising interface, +content of router advertisement can be described in +.Xr rtadvd.conf 5 . +.Pp +If there is no description for the interface in the configuration file +or if the configuration file does not exist, +.Nm +sets all the parameters to their default values. +In particular, +.Nm +gets all the interface routes from the routing table and advertises +them as on-link prefixes. +.Pp +.Nm Rtadvd +watches the routing table. +By default, if an interface direct route is +added/deleted on an advertising interface, +.Nm +adds/deletes the corresponding prefix to/from its advertising list, +respectively. +If you do not want to enable this feature, you should specify the +.Ic Fl s +command line option when advocation. +.Bl -tag -width indent +.\" +.It Fl c +Specify an alternate location, +.Ar configfile , +for the configuration file. +By default, +.Pa /usr/local/v6/etc/rtadvd.conf +is used. +.It Fl d +Debug. +.It Fl D +More debug. +.It Fl f +Foreground mode. +Do not become daemon. +.It Fl s +Static prefix. +Do not watch the routing table. +.El +.Sh RETURN VALUES +The program exits with 0 on success, and non-zero on failures. +.Sh FILES +.Bl -tag -width /usr/local/v6/etc/rtadvd.conf -compact +.It Pa /usr/local/v6/etc/rtadvd.conf +The default configuration file. +.El +.Sh SEE ALSO +.Xr daemon 3 , +.Xr rtadvd.conf 5 , +.Xr rtsol 8 +.Sh HISTORY +The +.Nm +command first appeared in WIDE Hydrangea IPv6 protocol stack kit. +.Sh CAVEAT +Do not perform router advertisement toward upstream direction, +you should only advertise to downstream direction. +If you advertise toward upstream by mistake, +you will see icmp6 redirect storm on that subnet. +This is because of the specification, +which says that advertising router is assumed to become +the default outgoing router for end hosts in the subnet. diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c new file mode 100644 index 00000000000..23aa48a6297 --- /dev/null +++ b/usr.sbin/rtadvd/rtadvd.c @@ -0,0 +1,1262 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/time.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/if_dl.h> +#include <netinet/in.h> +#include <netinet/ip6.h> +#include <netinet6/ip6_var.h> +#include <netinet/icmp6.h> + +#include <arpa/inet.h> + +#include <time.h> +#include <unistd.h> +#include <stdio.h> +#include <err.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> +#include "rtadvd.h" +#include "rrenum.h" +#include "advcap.h" +#include "timer.h" +#include "if.h" +#include "config.h" + +struct msghdr rcvmhdr; +static u_char rcvcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int))]; +struct msghdr sndmhdr; +struct iovec rcviov[2]; +struct iovec sndiov[2]; +struct sockaddr_in6 from; +struct sockaddr_in6 sin6_allnodes = {sizeof(sin6_allnodes), AF_INET6}; +int sock, rtsock; +int accept_rr = 1; +int dflag = 0, sflag = 0; + +u_char *conffile = NULL; + +struct rainfo *ralist = NULL; +struct nd_optlist { + struct nd_optlist *next; + struct nd_opt_hdr *opt; +}; +union nd_opts { + struct nd_opt_hdr *nd_opt_array[7]; + struct { + struct nd_opt_hdr *zero; + struct nd_opt_hdr *src_lladdr; + struct nd_opt_hdr *tgt_lladdr; + struct nd_opt_prefix_info *pi; + struct nd_opt_rd_hdr *rh; + struct nd_opt_mtu *mtu; + struct nd_optlist *list; + } nd_opt_each; +}; +#define nd_opts_src_lladdr nd_opt_each.src_lladdr +#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr +#define nd_opts_pi nd_opt_each.pi +#define nd_opts_rh nd_opt_each.rh +#define nd_opts_mtu nd_opt_each.mtu +#define nd_opts_list nd_opt_each.list + +#define NDOPT_FLAG_SRCLINKADDR 0x1 +#define NDOPT_FLAG_TGTLINKADDR 0x2 +#define NDOPT_FLAG_PREFIXINFO 0x4 +#define NDOPT_FLAG_RDHDR 0x8 +#define NDOPT_FLAG_MTU 0x10 + +u_int32_t ndopt_flags[] = { + 0, NDOPT_FLAG_SRCLINKADDR, NDOPT_FLAG_TGTLINKADDR, + NDOPT_FLAG_PREFIXINFO, NDOPT_FLAG_RDHDR, NDOPT_FLAG_MTU +}; + +int main __P((int, char *[])); +static void sock_open __P((void)); +static void rtsock_open __P((void)); +static void rtadvd_input __P((void)); +static void rs_input __P((int, struct nd_router_solicit *, + struct in6_pktinfo *, struct sockaddr_in6 *)); +static void ra_input __P((int, struct nd_router_advert *, + struct in6_pktinfo *, struct sockaddr_in6 *)); +static void prefix_check __P((struct nd_opt_prefix_info *, struct rainfo *, + struct sockaddr_in6 *)); +static int nd6_options __P((struct nd_opt_hdr *, int, + union nd_opts *, u_int32_t)); +static void free_ndopts __P((union nd_opts *)); +static struct rainfo *if_indextorainfo __P((int)); +static void ra_output __P((struct rainfo *)); +static void rtmsg_input __P((void)); +struct prefix *find_prefix __P((struct rainfo *, struct in6_addr *, int)); + + +int +main(argc, argv) + int argc; + char *argv[]; +{ + fd_set fdset; + int maxfd = 0; + struct timeval *timeout; + int i, ch; + int fflag = 0; + + openlog(*argv, LOG_NDELAY|LOG_PID, LOG_DAEMON); + + /* get command line options and arguments */ + while ((ch = getopt(argc, argv, "c:dDfs")) != -1) { + switch(ch) { + case 'c': + conffile = optarg; + break; + case 'd': + dflag = 1; + break; + case 'D': + dflag = 2; + break; + case 'f': + fflag = 1; + break; + case 's': + sflag = 1; + } + } + argc -= optind; + argv += optind; + if (argc == 0) { + fprintf(stderr, + "usage: rtadvd [-c conffile] [-d|D] [-f] [-s]" + "interfaces...\n"); + exit(1); + } + + /* set log level */ + if (dflag == 0) + (void)setlogmask(LOG_UPTO(LOG_ERR)); + if (dflag == 1) + (void)setlogmask(LOG_UPTO(LOG_INFO)); + + /* timer initialization */ + rtadvd_timer_init(); + + /* random value initialization */ + srandom((u_long)time(NULL)); + + /* get iflist block from kernel */ + init_iflist(); + + while (argc--) + getconfig(*argv++); + + if (inet_pton(AF_INET6, ALLNODES, &sin6_allnodes.sin6_addr) != 1) { + fprintf(stderr, "fatal: inet_pton failed\n"); + exit(1); + } + sock_open(); + + if (!fflag) + daemon(1, 0); + + FD_ZERO(&fdset); + FD_SET(sock, &fdset); + maxfd = sock; + if (sflag == 0) { + rtsock_open(); + FD_SET(rtsock, &fdset); + if (rtsock > sock) + maxfd = rtsock; + } + + while (1) { + struct fd_set select_fd = fdset; /* reinitialize */ + + /* timer expiration check and reset the timer */ + timeout = rtadvd_check_timer(); + + syslog(LOG_DEBUG, + "<%s> set timer to %ld:%ld. waiting for inputs " + "or timeout", + __FUNCTION__, + timeout->tv_sec, timeout->tv_usec); + + if ((i = select(maxfd + 1, &select_fd, + NULL, NULL, timeout)) < 0){ + syslog(LOG_ERR, "<%s> select: %s", + __FUNCTION__, strerror(errno)); + continue; + } + if (i == 0) /* timeout */ + continue; + if (sflag == 0 && FD_ISSET(rtsock, &select_fd)) + rtmsg_input(); + if (FD_ISSET(sock, &select_fd)) + rtadvd_input(); + } + exit(0); /* NOTREACHED */ +} + +static void +rtmsg_input() +{ + int n, type, ifindex, plen; + size_t len; + char msg[2048], *next, *lim; + u_char ifname[16]; + struct prefix *prefix; + struct rainfo *rai; + struct in6_addr *addr; + char addrbuf[INET6_ADDRSTRLEN]; + + n = read(rtsock, msg, 2048); + if (dflag > 1) { + syslog(LOG_DEBUG, + "<%s> received a routing message " + "(type = %d, len = %d)", + __FUNCTION__, + rtmsg_type(msg), n); + } + if (n > rtmsg_len(msg)) { + /* + * This usually won't happen for messages received on + * an routing socket. + */ + if (dflag > 1) + syslog(LOG_DEBUG, + "<%s> received data length is larger than" + "1st routing message len. multiple messages?" + " read %d bytes, but 1st msg len = %d", + __FUNCTION__, n, rtmsg_len(msg)); +#if 0 + /* adjust length */ + n = rtmsg_len(msg); +#endif + } + + lim = msg + n; + for (next = msg; next < lim; next += len) { + next = get_next_msg(next, lim, 0, &len, + RTADV_TYPE2BITMASK(RTM_ADD) | + RTADV_TYPE2BITMASK(RTM_DELETE) | + RTADV_TYPE2BITMASK(RTM_NEWADDR) | + RTADV_TYPE2BITMASK(RTM_DELADDR) | + RTADV_TYPE2BITMASK(RTM_IFINFO)); + if (len == 0) + break; + type = rtmsg_type(next); + switch (type) { + case RTM_ADD: + case RTM_DELETE: + ifindex = get_rtm_ifindex(next); + break; + case RTM_NEWADDR: + case RTM_DELADDR: + ifindex = get_ifam_ifindex(next); + break; + case RTM_IFINFO: + ifindex = get_ifm_ifindex(next); + break; + default: + /* should not reach here */ + if (dflag > 1) { + syslog(LOG_DEBUG, + "<%s:%d> unknown rtmsg %d on %s", + __FUNCTION__, __LINE__, type, + if_indextoname(ifindex, ifname)); + } + return; + } + + if ((rai = if_indextorainfo(ifindex)) == NULL) { + if (dflag > 1) { + syslog(LOG_DEBUG, + "<%s> route changed on " + "non advertising interface(%s)", + __FUNCTION__, + if_indextoname(ifindex, ifname)); + } + return; + } + + switch(type) { + case RTM_ADD: + /* init iffalgs because it may have changed */ + iflist[ifindex]->ifm_flags = + if_getflags(ifindex, + iflist[ifindex]->ifm_flags); + + addr = get_addr(msg); + plen = get_prefixlen(msg); + /* sanity check for plen */ + if (plen < 4 /* as RFC2373, prefixlen is at least 4 */ + || plen > 127) { + syslog(LOG_INFO, "<%s> new interface route's" + "plen %d is invalid for a prefix", + __FUNCTION__, plen); + return; + } + prefix = find_prefix(rai, addr, plen); + if (prefix) { + if (dflag > 1) { + syslog(LOG_DEBUG, + "<%s> new prefix(%s/%d) " + "added on %s, " + "but it was already in list", + __FUNCTION__, + inet_ntop(AF_INET6, + addr, (char *)addrbuf, + INET6_ADDRSTRLEN), + plen, + rai->ifname); + } + return; + } + make_prefix(rai, ifindex, addr, plen); + break; + case RTM_DELETE: + /* init ifflags because it may have changed */ + iflist[ifindex]->ifm_flags = + if_getflags(ifindex, + iflist[ifindex]->ifm_flags); + + addr = get_addr(msg); + plen = get_prefixlen(msg); + /* sanity check for plen */ + if (plen < 4 /* as RFC2373, prefixlen is at least 4 */ + || plen > 127) { + syslog(LOG_INFO, "<%s> deleted interface" + "route's" + "plen %d is invalid for a prefix", + __FUNCTION__, plen); + return; + } + prefix = find_prefix(rai, addr, plen); + if (prefix == NULL) { + if (dflag > 1) { + syslog(LOG_DEBUG, + "<%s> prefix(%s/%d) was " + "deleted on %s, " + "but it was not in list", + __FUNCTION__, + inet_ntop(AF_INET6, + addr, (char *)addrbuf, + INET6_ADDRSTRLEN), + plen, + rai->ifname); + } + return; + } + delete_prefix(rai, prefix); + break; + case RTM_NEWADDR: + case RTM_DELADDR: + /* init ifflags because it may have changed */ + iflist[ifindex]->ifm_flags = + if_getflags(ifindex, + iflist[ifindex]->ifm_flags); + break; + case RTM_IFINFO: + iflist[ifindex]->ifm_flags = get_ifm_flags(next); + break; + default: + /* should not reach here */ + if (dflag > 1) { + syslog(LOG_DEBUG, + "<%s:%d> unknown rtmsg %d on %s", + __FUNCTION__, __LINE__, type, + if_indextoname(ifindex, ifname)); + } + return; + } + } + + return; +} + +void +rtadvd_input() +{ + int i; + int *hlimp = NULL; +#ifdef OLDRAWSOCKET + struct ip6_hdr *ip; +#endif + struct icmp6_hdr *icp; + int ifindex = 0; + struct cmsghdr *cm; + struct in6_pktinfo *pi = NULL; + u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + struct in6_addr dst = in6addr_any; + + /* + * Get message. We reset msg_controllen since the field could + * be modified if we had received a message before setting + * receive options. + */ + rcvmhdr.msg_controllen = sizeof(rcvcmsgbuf); + if ((i = recvmsg(sock, &rcvmhdr, 0)) < 0) + return; + + /* extract optional information via Advanced API */ + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); + cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_PKTINFO && + cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { + pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); + ifindex = pi->ipi6_ifindex; + dst = pi->ipi6_addr; + } + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_HOPLIMIT && + cm->cmsg_len == CMSG_LEN(sizeof(int))) + hlimp = (int *)CMSG_DATA(cm); + } + if (ifindex == 0) { + syslog(LOG_ERR, + "<%s> failed to get receiving interface", + __FUNCTION__); + return; + } + if (hlimp == NULL) { + syslog(LOG_ERR, + "<%s> failed to get receiving hop limit", + __FUNCTION__); + return; + } + +#ifdef OLDRAWSOCKET + if (i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) { + syslog(LOG_ERR, + "<%s> packet size(%d) is too short", + __FUNCTION__, i); + return; + } + + ip = (struct ip6_hdr *)rcvmhdr.msg_iov[0].iov_base; + icp = (struct icmp6_hdr *)(ip + 1); /* XXX: ext. hdr? */ +#else + if (i < sizeof(struct icmp6_hdr)) { + syslog(LOG_ERR, + "<%s> packet size(%d) is too short", + __FUNCTION__, i); + return; + } + + icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; +#endif + + switch(icp->icmp6_type) { + case ND_ROUTER_SOLICIT: + /* + * Message verification - RFC-2461 6.1.1 + * XXX: these checks must be done in the kernel as well, + * but we can't completely rely on them. + */ + if (*hlimp != 255) { + syslog(LOG_NOTICE, + "<%s> RS with invalid hop limit(%d) " + "received from %s on %s", + __FUNCTION__, *hlimp, + inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + if (icp->icmp6_code) { + syslog(LOG_NOTICE, + "<%s> RS with invalid ICMP6 code(%d) " + "received from %s on %s", + __FUNCTION__, icp->icmp6_code, + inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + if (i < sizeof(struct nd_router_solicit)) { + syslog(LOG_NOTICE, + "<%s> RS from %s on %s does not have enough " + "length (len = %d)", + __FUNCTION__, + inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf), i); + return; + } + rs_input(i, (struct nd_router_solicit *)icp, pi, &from); + break; + case ND_ROUTER_ADVERT: + /* + * Message verification - RFC-2461 6.1.2 + * XXX: there's a same dilemma as above... + */ + if (*hlimp != 255) { + syslog(LOG_NOTICE, + "<%s> RA with invalid hop limit(%d) " + "received from %s on %s", + __FUNCTION__, *hlimp, + inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + if (icp->icmp6_code) { + syslog(LOG_NOTICE, + "<%s> RA with invalid ICMP6 code(%d) " + "received from %s on %s", + __FUNCTION__, icp->icmp6_code, + inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + if (i < sizeof(struct nd_router_advert)) { + syslog(LOG_NOTICE, + "<%s> RA from %s on %s does not have enough " + "length (len = %d)", + __FUNCTION__, + inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf), i); + return; + } + ra_input(i, (struct nd_router_advert *)icp, pi, &from); + break; + case ICMP6_ROUTER_RENUMBERING: + if (accept_rr == 0) { + syslog(LOG_ERR, + "<%s> received a router renumbering " + "message, but not allowed to be accepted", + __FUNCTION__); + break; + } + rr_input(i, (struct icmp6_router_renum *)icp, pi, &from, + &dst); + break; + default: + /* + * Note that this case is POSSIBLE, especially just + * after invocation of the daemon. This is because we + * could receive message after opening the socket and + * before setting ICMP6 type filter(see sock_open()). + */ + syslog(LOG_ERR, + "<%s> invalid icmp type(%d)", + __FUNCTION__, icp->icmp6_type); + return; + } + + return; +} + +static void +rs_input(int len, struct nd_router_solicit *rs, + struct in6_pktinfo *pi, struct sockaddr_in6 *from) +{ + u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + union nd_opts ndopts; + struct rainfo *ra; + + syslog(LOG_DEBUG, + "<%s> RS received from %s on %s", + __FUNCTION__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + + /* ND option check */ + memset(&ndopts, 0, sizeof(ndopts)); + if (nd6_options((struct nd_opt_hdr *)(rs + 1), + len - sizeof(struct nd_router_solicit), + &ndopts, NDOPT_FLAG_SRCLINKADDR)) { + syslog(LOG_DEBUG, + "<%s> ND option check failed for an RS from %s on %s", + __FUNCTION__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + + /* + * If the IP source address is the unspecified address, there + * must be no source link-layer address option in the message. + * (RFC-2461 6.1.1) + */ + if (IN6_IS_ADDR_UNSPECIFIED(&from->sin6_addr) && + ndopts.nd_opts_src_lladdr) { + syslog(LOG_ERR, + "<%s> RS from unspecified src on %s has a link-layer" + " address option", + __FUNCTION__, + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + goto done; + } + + ra = ralist; + while (ra != NULL) { + if (pi->ipi6_ifindex == ra->ifindex) + break; + ra = ra->next; + } + if (ra == NULL) { + syslog(LOG_INFO, + "<%s> RS received on non advertising interface(%s)", + __FUNCTION__, + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + goto done; + } + + /* + * Decide whether to send RA according to the rate-limit + * consideration. + */ + { + long delay; /* must not be greater than 1000000 */ + struct timeval interval, now, min_delay, tm_tmp, *rest; + + /* + * If there is already a waiting RS packet, don't + * update the timer. + */ + if (ra->waiting++) + goto done; + + /* + * Compute a random delay. If the computed value + * corresponds to a time later than the time the next + * multicast RA is scheduled to be sent, ignore the random + * delay and send the advertisement at the + * already-scheduled time. RFC-2461 6.2.6 + */ + delay = random() % MAX_RA_DELAY_TIME; + interval.tv_sec = 0; + interval.tv_usec = delay; + rest = rtadvd_timer_rest(ra->timer); + if (TIMEVAL_LT(*rest, interval)) { + syslog(LOG_DEBUG, + "<%s> random delay is larger than " + "the rest of normal timer", + __FUNCTION__); + interval = *rest; + } + + /* + * If we sent a multicast Router Advertisement within + * the last MIN_DELAY_BETWEEN_RAS seconds, schedule + * the advertisement to be sent at a time corresponding to + * MIN_DELAY_BETWEEN_RAS plus the random value after the + * previous advertisement was sent. + */ + gettimeofday(&now, NULL); + TIMEVAL_SUB(&now, &ra->lastsent, &tm_tmp); + min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS; + min_delay.tv_usec = 0; + if (TIMEVAL_LT(tm_tmp, min_delay)) { + TIMEVAL_SUB(&min_delay, &tm_tmp, &min_delay); + TIMEVAL_ADD(&min_delay, &interval, &interval); + } + rtadvd_set_timer(&interval, ra->timer); + goto done; + } + + done: + free_ndopts(&ndopts); + return; +} + +static void +ra_input(int len, struct nd_router_advert *ra, + struct in6_pktinfo *pi, struct sockaddr_in6 *from) +{ + struct rainfo *rai; + u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + union nd_opts ndopts; + char *on_off[] = {"OFF", "ON"}; + u_int32_t reachabletime, retranstimer, mtu; + + syslog(LOG_DEBUG, + "<%s> RA received from %s on %s", + __FUNCTION__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + + /* ND option check */ + memset(&ndopts, 0, sizeof(ndopts)); + if (nd6_options((struct nd_opt_hdr *)(ra + 1), + len - sizeof(struct nd_router_advert), + &ndopts, + NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU)) { + syslog(LOG_ERR, + "<%s> ND option check failed for an RA from %s on %s", + __FUNCTION__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + + /* + * RA consistency check according to RFC-2461 6.2.7 + */ + if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == 0) { + syslog(LOG_INFO, + "<%s> received RA from %s on non-advertising" + " interface(%s)", + __FUNCTION__, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + goto done; + } + /* Cur Hop Limit value */ + if (ra->nd_ra_curhoplimit && rai->hoplimit && + ra->nd_ra_curhoplimit != rai->hoplimit) { + syslog(LOG_WARNING, + "<%s> CurHopLimit inconsistent on %s:" + " %d from %s, %d from us", + __FUNCTION__, + rai->ifname, + ra->nd_ra_curhoplimit, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->hoplimit); + } + /* M flag */ + if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != + rai->managedflg) { + syslog(LOG_WARNING, + "<%s> M flag inconsistent on %s:" + " %s from %s, %s from us", + __FUNCTION__, + rai->ifname, + on_off[!rai->managedflg], + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + on_off[rai->managedflg]); + } + /* O flag */ + if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != + rai->otherflg) { + syslog(LOG_WARNING, + "<%s> O flag inconsistent on %s:" + " %s from %s, %s from us", + __FUNCTION__, + rai->ifname, + on_off[!rai->otherflg], + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + on_off[rai->otherflg]); + } + /* Reachable Time */ + reachabletime = ntohl(ra->nd_ra_reachable); + if (reachabletime && rai->reachabletime && + reachabletime != rai->reachabletime) { + syslog(LOG_WARNING, + "<%s> ReachableTime inconsistent on %s:" + " %d from %s, %d from us", + __FUNCTION__, + rai->ifname, + reachabletime, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->reachabletime); + } + /* Retrans Timer */ + retranstimer = ntohl(ra->nd_ra_retransmit); + if (retranstimer && rai->retranstimer && + retranstimer != rai->retranstimer) { + syslog(LOG_WARNING, + "<%s> RetranceTimer inconsistent on %s:" + " %d from %s, %d from us", + __FUNCTION__, + rai->ifname, + retranstimer, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->retranstimer); + } + /* Values in the MTU options */ + if (ndopts.nd_opts_mtu) { + mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); + if (mtu && rai->linkmtu && mtu != rai->linkmtu) { + syslog(LOG_WARNING, + "<%s> MTU option value inconsistent on %s:" + " %d from %s, %d from us", + __FUNCTION__, + rai->ifname, mtu, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->linkmtu); + } + } + /* Preferred and Valid Lifetimes for prefixes */ + { + struct nd_optlist *optp = ndopts.nd_opts_list; + + if (ndopts.nd_opts_pi) + prefix_check(ndopts.nd_opts_pi, rai, from); + while (optp) { + prefix_check((struct nd_opt_prefix_info *)optp->opt, + rai, from); + optp = optp->next; + } + } + + done: + free_ndopts(&ndopts); + return; +} + +static void +prefix_check(struct nd_opt_prefix_info *pinfo, + struct rainfo *rai, struct sockaddr_in6 *from) +{ + u_int32_t preferred_time, valid_time; + struct prefix *pp; + u_char ntopbuf[INET6_ADDRSTRLEN], prefixbuf[INET6_ADDRSTRLEN]; + +#if 0 /* impossible */ + if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION) + return; +#endif + + /* + * log if the adveritsed prefix has link-local scope(sanity check?) + */ + if (IN6_IS_ADDR_LINKLOCAL(&pinfo->nd_opt_pi_prefix)) { + syslog(LOG_INFO, + "<%s> link-local prefix %s/%d is advertised " + "from %s on %s", + __FUNCTION__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->ifname); + } + + if ((pp = find_prefix(rai, &pinfo->nd_opt_pi_prefix, + pinfo->nd_opt_pi_prefix_len)) == NULL) { + syslog(LOG_INFO, + "<%s> prefix %s/%d from %s on %s is not in our list", + __FUNCTION__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + rai->ifname); + return; + } + + preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time); + if (preferred_time != pp->preflifetime) + syslog(LOG_WARNING, + "<%s> prefeerred lifetime for %s/%d" + " inconsistent on %s:" + " %d from %s, %d from us", + __FUNCTION__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + rai->ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + pp->preflifetime); + + valid_time = ntohl(pinfo->nd_opt_pi_valid_time); + if (valid_time != pp->validlifetime) + syslog(LOG_WARNING, + "<%s> valid lifetime for %s/%d" + " inconsistent on %s:" + " %d from %s, %d from us", + __FUNCTION__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + rai->ifname, valid_time, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + pp->validlifetime); +} + +struct prefix * +find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen) +{ + struct prefix *pp; + int bytelen, bitlen; + + for (pp = rai->prefix.next; pp != &rai->prefix; pp = pp->next) { + if (plen != pp->prefixlen) + continue; + bytelen = plen / 8; + bitlen = plen % 8; + if (memcmp((void *)prefix, (void *)&pp->prefix, bytelen)) + continue; + if (prefix->s6_addr[bytelen] >> (8 - bitlen) == + pp->prefix.s6_addr[bytelen] >> (8 - bitlen)) + return(pp); + } + + return(NULL); +} + +static int +nd6_options(struct nd_opt_hdr *hdr, int limit, + union nd_opts *ndopts, u_int32_t optflags) +{ + int optlen = 0; + + for (; limit > 0; limit -= optlen) { + hdr = (struct nd_opt_hdr *)((caddr_t)hdr + optlen); + optlen = hdr->nd_opt_len << 3; + if (hdr->nd_opt_len == 0) { + syslog(LOG_ERR, + "<%s> bad ND option length(0) (type = %d)", + __FUNCTION__, hdr->nd_opt_type); + goto bad; + } + + if (hdr->nd_opt_type > ND_OPT_MTU) { + syslog(LOG_INFO, + "<%s> unknown ND option(type %d)", + __FUNCTION__, + hdr->nd_opt_type); + continue; + } + + if ((ndopt_flags[hdr->nd_opt_type] & optflags) == 0) { + syslog(LOG_INFO, + "<%s> unexpected ND option(type %d)", + __FUNCTION__, + hdr->nd_opt_type); + continue; + } + + switch(hdr->nd_opt_type) { + case ND_OPT_SOURCE_LINKADDR: + case ND_OPT_TARGET_LINKADDR: + case ND_OPT_REDIRECTED_HEADER: + case ND_OPT_MTU: + if (ndopts->nd_opt_array[hdr->nd_opt_type]) { + syslog(LOG_INFO, + "<%s> duplicated ND option" + " (type = %d)", + __FUNCTION__, + hdr->nd_opt_type); + } + ndopts->nd_opt_array[hdr->nd_opt_type] = hdr; + break; + case ND_OPT_PREFIX_INFORMATION: + { + struct nd_optlist *pfxlist; + + if (ndopts->nd_opts_pi == 0) { + ndopts->nd_opts_pi = + (struct nd_opt_prefix_info *)hdr; + continue; + } + if ((pfxlist = malloc(sizeof(*pfxlist))) == NULL) { + syslog(LOG_ERR, + "<%s> can't allocate memory", + __FUNCTION__); + goto bad; + } + pfxlist->next = ndopts->nd_opts_list; + pfxlist->opt = hdr; + ndopts->nd_opts_list = pfxlist; + + break; + } + default: /* impossible */ + break; + } + } + + return(0); + + bad: + free_ndopts(ndopts); + + return(-1); +} + +static void +free_ndopts(union nd_opts *ndopts) +{ + struct nd_optlist *opt = ndopts->nd_opts_list, *next; + + while(opt) { + next = opt->next; + free(opt); + opt = next; + } +} + +void +sock_open() +{ + struct icmp6_filter filt; + struct ipv6_mreq mreq; + struct rainfo *ra = ralist; + int on; + /* XXX: should be max MTU attached to the node */ + static u_char answer[1500]; + static u_char sndcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int))]; + + if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { + syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__, + strerror(errno)); + exit(1); + } + + /* specify to tell receiving interface */ + on = 1; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on, + sizeof(on)) < 0) { + syslog(LOG_ERR, "<%s> IPV6_PKTINFO: %s", + __FUNCTION__, strerror(errno)); + exit(1); + } + + on = 1; + /* specify to tell value of hoplimit field of received IP6 hdr */ + if (setsockopt(sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, + sizeof(on)) < 0) { + syslog(LOG_ERR, "<%s> IPV6_HOPLIMIT: %s", + __FUNCTION__, strerror(errno)); + exit(1); + } + + ICMP6_FILTER_SETBLOCKALL(&filt); + ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt); + ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); + if (accept_rr) + ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt); + if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, + sizeof(filt)) < 0) { + syslog(LOG_ERR, "<%s> IICMP6_FILTER: %s", + __FUNCTION__, strerror(errno)); + exit(1); + } + + /* + * join all routers multicast address on each advertising interface. + */ + if (inet_pton(AF_INET6, ALLROUTERS, &mreq.ipv6mr_multiaddr.s6_addr) + != 1) { + syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)", + __FUNCTION__); + exit(1); + } + while(ra) { + mreq.ipv6mr_interface = ra->ifindex; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq, + sizeof(mreq)) < 0) { + syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP on %s: %s", + __FUNCTION__, ra->ifname, strerror(errno)); + exit(1); + } + ra = ra->next; + } + + /* initialize msghdr for receiving packets */ + rcviov[0].iov_base = (caddr_t)answer; + rcviov[0].iov_len = sizeof(answer); + rcvmhdr.msg_name = (caddr_t)&from; + rcvmhdr.msg_namelen = sizeof(from); + rcvmhdr.msg_iov = rcviov; + rcvmhdr.msg_iovlen = 1; + rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; + rcvmhdr.msg_controllen = sizeof(rcvcmsgbuf); + + /* initialize msghdr for sending packets */ + sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); + sndmhdr.msg_iov = sndiov; + sndmhdr.msg_iovlen = 1; + sndmhdr.msg_control = (caddr_t)sndcmsgbuf; + sndmhdr.msg_controllen = sizeof(sndcmsgbuf); + + return; +} + +/* open a routing socket to watch the routing table */ +static void +rtsock_open() +{ + if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { + syslog(LOG_ERR, + "<%s> socket: %s", __FUNCTION__, strerror(errno)); + exit(1); + } +} + +static struct rainfo * +if_indextorainfo(int index) +{ + struct rainfo *rai = ralist; + + for (rai = ralist; rai; rai = rai->next) { + if (rai->ifindex == index) + return(rai); + } + + return(NULL); /* search failed */ +} + +static void +ra_output(rainfo) +struct rainfo *rainfo; +{ + int i; + + struct cmsghdr *cm; + struct in6_pktinfo *pi; + + sndmhdr.msg_name = (caddr_t)&sin6_allnodes; + sndmhdr.msg_iov[0].iov_base = (caddr_t)rainfo->ra_data; + sndmhdr.msg_iov[0].iov_len = rainfo->ra_datalen; + + cm = CMSG_FIRSTHDR(&sndmhdr); + /* specify the outgoing interface */ + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_PKTINFO; + cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + pi = (struct in6_pktinfo *)CMSG_DATA(cm); + memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ + pi->ipi6_ifindex = rainfo->ifindex; + + /* specify the hop limit of the packet */ + { + int hoplimit = 255; + + cm = CMSG_NXTHDR(&sndmhdr, cm); + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_HOPLIMIT; + cm->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); + } + + syslog(LOG_DEBUG, + "<%s> send RA on %s, # of waitings = %d", + __FUNCTION__, rainfo->ifname, rainfo->waiting); + + i = sendmsg(sock, &sndmhdr, 0); + + if (i < 0 || i != rainfo->ra_datalen) { + if (i < 0) { + syslog(LOG_ERR, "<%s> sendmsg on %s: %s", + __FUNCTION__, rainfo->ifname, + strerror(errno)); + } + } + + /* update counter */ + if (rainfo->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS) + rainfo->initcounter++; + + /* update timestamp */ + gettimeofday(&rainfo->lastsent, NULL); + + /* reset waiting conter */ + rainfo->waiting = 0; +} + +/* process RA timer */ +void +ra_timeout(void *data) +{ + struct rainfo *rai = (struct rainfo *)data; + +#ifdef notyet + /* if necessary, reconstruct the packet. */ +#endif + + syslog(LOG_DEBUG, + "<%s> RA timer on %s is expired", + __FUNCTION__, rai->ifname); + + if (iflist[rai->ifindex]->ifm_flags & IFF_UP) + ra_output(rai); + else + syslog(LOG_DEBUG, "<%s> %s is not up, skip sending RA", + __FUNCTION__, rai->ifname); +} + +/* update RA timer */ +void +ra_timer_update(void *data, struct timeval *tm) +{ + struct rainfo *rai = (struct rainfo *)data; + long interval; + + /* + * Whenever a multicast advertisement is sent from an interface, + * the timer is reset to a uniformly-distributed random value + * between the interface's configured MinRtrAdvInterval and + * MaxRtrAdvInterval(discovery-v2-02 6.2.4). + */ + interval = rai->mininterval; + interval += random() % (rai->maxinterval - rai->mininterval); + + /* + * For the first few advertisements (up to + * MAX_INITIAL_RTR_ADVERTISEMENTS), if the randomly chosen interval + * is greater than MAX_INITIAL_RTR_ADVERT_INTERVAL, the timer + * SHOULD be set to MAX_INITIAL_RTR_ADVERT_INTERVAL instead. + * (RFC-2461 6.2.4) + */ + if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS && + interval > MAX_INITIAL_RTR_ADVERT_INTERVAL) + interval = MAX_INITIAL_RTR_ADVERT_INTERVAL; + + tm->tv_sec = interval; + tm->tv_usec = 0; + + syslog(LOG_DEBUG, + "<%s> RA timer on %s is set to %ld:%ld", + __FUNCTION__, rai->ifname, tm->tv_sec, tm->tv_usec); + + return; +} diff --git a/usr.sbin/rtadvd/rtadvd.conf b/usr.sbin/rtadvd/rtadvd.conf new file mode 100644 index 00000000000..6f563564a61 --- /dev/null +++ b/usr.sbin/rtadvd/rtadvd.conf @@ -0,0 +1,13 @@ +# +# common definitions. +# +default:\ + :chlim#64:raflags#0:rltime#1800:rtime#30000:retrans#1000:\ + :pinfoflags#192:vltime#3600000:pltime#3600000:mtu#1500: +ether:\ + :mtu#1500:tc=default: +# +# interfaces. +# +ef0:\ + :addrs#1:addr="3ffe:501:4819:1000::":prefixlen#64:tc=ether: diff --git a/usr.sbin/rtadvd/rtadvd.conf.5 b/usr.sbin/rtadvd/rtadvd.conf.5 new file mode 100644 index 00000000000..18446d18427 --- /dev/null +++ b/usr.sbin/rtadvd/rtadvd.conf.5 @@ -0,0 +1,249 @@ +.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" KAME Id: rtadvd.conf.5,v 1.1.1.1 1999/08/08 23:31:42 itojun Exp +.\" +.Dd May 17, 1998 +.Dt RTADVD.CONF 5 +.Os KAME +.Sh NAME +.Nm rtadvd.conf +.Nd config file for router advertisement daemon +.Sh DESCRIPTION +The file describes how the router advertisement packet must be constructed +for each of the interfaces. +.Pp +It obeys famous +.Xr termcap 5 +file format. +Each line in the file describes a network interface. +Fields are separated by a colon +.Po +.Dq \&: +.Pc , +and each field contains one capability description. +Lines may be concatenated by \e character. +The comment marker is `#' character. +.Pp +.Sh CAPABILITIES +Capabilities describe the value to be filled into ICMPv6 router +advertisement message and to control +.Xr rtadvd 8 +behavior. +Therefore, you are encouraged to read IETF neighbor discovery documents +if you would like to modify sample configuration file. +.Pp +Note that almost all items have default values. +If you omit an item, the default value of the item will be used. +.Pp +There are two items to control interval of sending router advertisements. +.Bl -tag -width indent +.It Cm \&maxinterval +(num) The maximum time allowed between sending unsolicited +multicast router advertisements +.Pq unit: seconds . +The default value is 600. Its value must be no less than 4 seconds +and no greater than 1800 seconds. +.It Cm \&mininterval +(num) The minimum time allowed between sending unsolicited multicast +router advertisements +.Pq unit: seconds . +The default value is the one third of value of +.Ic maxinterval. +Its value must be no less than 3 seconds and no greater than .75 * +the value of +.Ic maxinterval. +.El +.Pp +The following items are for ICMPv6 router advertisement message +header. +.Bl -tag -width indent +.It Cm \&chlim +(num) The value for Cur Hop Limit field. +The default value is 64. +.It Cm \&raflags +(num) Flags field in router advertisement message header. +Bit 7 +.Po +.Li 0x80 +.Pc +means Managed address configuration flag bit, +and Bit 6 +.Po +.Li 0x40 +.Pc +means Other stateful configuration flag bit. +The default value is 0. +.It Cm \&rltime +(num) Router lifetime field +.Pq unit: seconds . +Its value must be no greater than 3600000. +The default value is 1800. +.It Cm \&rtime +(num) Reachable time field +.Pq unit: milliseconds . +The default value is 0, which means unspecified by this router. +.It Cm \&retrans +(num) Retrans Timer field +.Pq unit: milliseconds . +The default value is 0, which means unspecified by this router. +.El +.Pp +The following items are for ICMPv6 prefix information option, +which will be attached to router advertisement header. +.Bl -tag -width indent +.It Cm \&addrs +(num) Number of prefixes. +Its default is 0, so it must explicitly be set to positve values +if you want to specify any prefix information option. +If its value is 0, +.Xr rtadvd 8 +looks up the system routing table and +advertise the prefixes corresponding to interface routes +on the interface. +If its value is more than 1, you must specify the index of the prefix +for each item below. +Indices vary from 0 to N-1, where N is the +value of +.Ic addrs. +Each index shall follows the name of each item, e.g. +.Dq prefixlen2 . +.It Cm \&prefixlen +(num) Prefix length field. +The default value is 64. +.It Cm \&pinfoflags +(num) Flags field in prefix information option. +Bit 7 +.Po +.Li 0x80 +.Pc +means On-link flag bit, +and Bit 6 +.Po +.Li 0x40 +.Pc +means Autonomous address-configuration flag bit. +The default value is 0xc0, i.e. both bits are set. +.It Cm \&addr +(str) The address filled into Prefix field. +Since +.Dq \&: +is used for +.Xr termcap 5 +file format as well as IPv6 numeric address, the field MUST be quoted by +doublequote character. +This field cannot be +omitted if the value of +.Ic addrs +is more than 0. +.It Cm \&vltime +(num) Valid lifetime field +.Pq unit: seconds . +The default value is 2592000(30 days). +.It Cm \&pltime +(num) Preferred lifetime field +.Pq unit: seconds . +The default value is 604800(7 days). +.El +.Pp +The following items are for ICMPv6 MTU option, +which will be attached to router advertisement header. +.Bl -tag -width indent +.It Cm \&mtu +(num or str) MTU (maximum transmission unit) field. +If 0 is specified, it means that the option will not be included. +The default value is 0. If the special string +.Dq auto +is specified for this item, MTU option will be included and its value +will be set to the interface MTU automatically. +.El +.Pp +The following item controls ICMPv6 source link-layer address option, +which will be attached to router advertisement header. +.Bl -tag -width indent +.It Cm \&nolladdr +(bool) By default +.Po +if +.Cm \&nolladdr +is not specified +.Pc , +.Xr rtadvd 8 +will try to get link-layer address for the interface from the kernel, +and attach that in source link-layer address option. +If this capability exists, +.Xr rtadvd 8 +will not attach source link-layer address option to +router advertisement packets. +.El +.Pp +You can also refer one line from another by using +.Cm tc +capability. +See +.Xr termcap 5 +for details on the capability. +.Sh EXAMPLE +.Bd -literal -offset +# +# common definitions. +# +default:\\ + :raflags#0:rltime#3600:\\ + :pinfoflags#64:vltime#360000:pltime#360000:mtu#1500: +ether:\\ + :mtu#1280:tc=default: + +# +# interfaces. +# +ef0:\\ + :addrs#1:\\ + :addr="3ffe:501:4819:1000::":tc=ether: +ef1:\\ + :addrs#2:addr0="3ffe:501:4819:2000::":\\ + :addr1="3ffe:501:4819:3000::":tc=ether: + +.Ed +.Sh SEE ALSO +.Xr termcap 5 , +.Xr rtadvd 8 , +.Xr rtsol 8 +.Pp +Thomas Narten, Erik Nordmark and W. A. Simpson, +.Do +Neighbor Discovery for IP version 6 (IPv6) +.Dc , +RFC 2461 +.Sh HISTORY +The +.Xr rtadvd 8 +and the configuration file +.Nm +first appeared in WIDE Hydrangea IPv6 protocol stack kit. +.\" .Sh BUGS +.\" (to be written) diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h new file mode 100644 index 00000000000..b1403b17c82 --- /dev/null +++ b/usr.sbin/rtadvd/rtadvd.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define ALLNODES "ff02::1" +#define ALLROUTERS "ff02::2" +#define ANY "::" +#define RTSOLLEN 8 + +/* protocol constants and default values */ +#define DEF_MAXRTRADVINTERVAL 600 +#define DEF_ADVLINKMTU 0 +#define DEF_ADVREACHABLETIME 0 +#define DEF_ADVRETRANSTIMER 0 +#define DEF_ADVCURHOPLIMIT 64 +#define DEF_ADVVALIDLIFETIME 2592000 +#define DEF_ADVPREFERREDLIFETIME 604800 + +#define MAXROUTERLIFETIME 9000 +#define MIN_MAXINTERVAL 4 +#define MAX_MAXINTERVAL 1800 +#define MIN_MININTERVAL 3 +#define MAXREACHABLETIME 3600000 + +#define MAX_INITIAL_RTR_ADVERT_INTERVAL 16 +#define MAX_INITIAL_RTR_ADVERTISEMENTS 3 +#define MAX_FINAL_RTR_ADVERTISEMENTS 3 +#define MIN_DELAY_BETWEEN_RAS 3 +#define MAX_RA_DELAY_TIME 500000 /* usec */ + +struct prefix { + struct prefix *next; /* forward link */ + struct prefix *prev; /* previous link */ + + u_int32_t validlifetime; /* AdvValidLifetime */ + u_int32_t preflifetime; /* AdvPreferredLifetime */ + u_int onlinkflg; /* bool: AdvOnLinkFlag */ + u_int autoconfflg; /* bool: AdvAutonomousFlag */ + int prefixlen; + struct in6_addr prefix; +}; + +struct rainfo { + /* pointer for list */ + struct rainfo *next; + + /* timer related parameters */ + struct rtadvd_timer *timer; + int initcounter; /* counter for the first few advertisements */ + struct timeval lastsent; /* timestamp when the lates RA was sent */ + int waiting; /* number of RS waiting for RA */ + + /* interface information */ + int ifindex; + int advlinkopt; /* bool: whether include link-layer addr opt */ + struct sockaddr_dl *sdl; + char ifname[16]; + int phymtu; /* mtu of the physical interface */ + + /* Router configuration variables */ + u_short lifetime; /* AdvDefaultLifetime */ + u_int maxinterval; /* MaxRtrAdvInterval */ + u_int mininterval; /* MinRtrAdvInterval */ + int managedflg; /* AdvManagedFlag */ + int otherflg; /* AdvOtherConfigFlag */ + u_int32_t linkmtu; /* AdvLinkMTU */ + u_int32_t reachabletime; /* AdvReachableTime */ + u_int32_t retranstimer; /* AdvRetransTimer */ + u_int hoplimit; /* AdvCurHopLimit */ + struct prefix prefix; /* AdvPrefixList(link head) */ + int pfxs; /* number of prefixes */ + + /* actual RA packet data and its length */ + size_t ra_datalen; + u_char *ra_data; +}; + +void ra_timeout __P((void *)); +void ra_timer_update __P((void *, struct timeval *)); diff --git a/usr.sbin/rtadvd/timer.c b/usr.sbin/rtadvd/timer.c new file mode 100644 index 00000000000..aa3d1645165 --- /dev/null +++ b/usr.sbin/rtadvd/timer.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/time.h> + +#include <unistd.h> +#include <syslog.h> +#include <stdlib.h> +#include <string.h> +#ifdef __NetBSD__ +#include <search.h> +#endif +#include "timer.h" + +static struct rtadvd_timer timer_head; + +#define MILLION 1000000 + +static struct timeval tm_max = {0x7fffffff, 0x7fffffff}; + +void +rtadvd_timer_init() +{ + memset(&timer_head, 0, sizeof(timer_head)); + + timer_head.next = timer_head.prev = &timer_head; + timer_head.tm = tm_max; +} + +struct rtadvd_timer * +rtadvd_add_timer(void (*timeout) __P((void *)), + void (*update) __P((void *, struct timeval *)), + void *timeodata, void *updatedata) +{ + struct rtadvd_timer *newtimer; + + if ((newtimer = malloc(sizeof(*newtimer))) == NULL) { + syslog(LOG_ERR, + "<%s> can't allocate memory", __FUNCTION__); + exit(1); + } + + memset(newtimer, 0, sizeof(*newtimer)); + + if (timeout == NULL) { + syslog(LOG_ERR, + "<%s> timeout function unspecfied", __FUNCTION__); + exit(1); + } + if (update == NULL) { + syslog(LOG_ERR, + "<%s> update function unspecfied", __FUNCTION__); + exit(1); + } + newtimer->expire = timeout; + newtimer->update = update; + newtimer->expire_data = timeodata; + newtimer->update_data = updatedata; + newtimer->tm = tm_max; + + /* link into chain */ + insque(newtimer, &timer_head); + + return(newtimer); +} + +void +rtadvd_set_timer(struct timeval *tm, struct rtadvd_timer *timer) +{ + struct timeval now; + + /* reset the timer */ + gettimeofday(&now, NULL); + + TIMEVAL_ADD(&now, tm, &timer->tm); + + /* update the next expiration time */ + if (TIMEVAL_LT(timer->tm, timer_head.tm)) + timer_head.tm = timer->tm; + + return; +} + +/* + * Check expiration for each timer. If a timer is expired, + * call the expire function for the timer and update the timer. + * Return the next interval for select() call. + */ +struct timeval * +rtadvd_check_timer() +{ + static struct timeval returnval; + struct timeval now; + struct rtadvd_timer *tm = timer_head.next; + + gettimeofday(&now, NULL); + + timer_head.tm = tm_max; + + while(tm != &timer_head) { + if (TIMEVAL_LEQ(tm->tm, now)) { + (*tm->expire)(tm->expire_data); + (*tm->update)(tm->update_data, &tm->tm); + TIMEVAL_ADD(&tm->tm, &now, &tm->tm); + } + + if (TIMEVAL_LT(tm->tm, timer_head.tm)) + timer_head.tm = tm->tm; + + tm = tm->next; + } + + if (TIMEVAL_LT(timer_head.tm, now)) { + /* this may occur when the interval is too small */ + returnval.tv_sec = returnval.tv_usec = 0; + } + else + TIMEVAL_SUB(&timer_head.tm, &now, &returnval); + return(&returnval); +} + +struct timeval * +rtadvd_timer_rest(struct rtadvd_timer *timer) +{ + static struct timeval returnval, now; + + gettimeofday(&now, NULL); + if (TIMEVAL_LEQ(timer->tm, now)) { + syslog(LOG_DEBUG, + "<%s> a timer must be expired, but not yet", + __FUNCTION__); + returnval.tv_sec = returnval.tv_usec = 0; + } + else + TIMEVAL_SUB(&timer->tm, &now, &returnval); + + return(&returnval); +} + +/* result = a + b */ +void +TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result) +{ + long l; + + if ((l = a->tv_usec + b->tv_usec) < MILLION) { + result->tv_usec = l; + result->tv_sec = a->tv_sec + b->tv_sec; + } + else { + result->tv_usec = l - MILLION; + result->tv_sec = a->tv_sec + b->tv_sec + 1; + } +} + +/* + * result = a - b + * XXX: this function assumes that a >= b. + */ +void +TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result) +{ + long l; + + if ((l = a->tv_usec - b->tv_usec) >= 0) { + result->tv_usec = l; + result->tv_sec = a->tv_sec - b->tv_sec; + } + else { + result->tv_usec = MILLION + l; + result->tv_sec = a->tv_sec - b->tv_sec - 1; + } +} diff --git a/usr.sbin/rtadvd/timer.h b/usr.sbin/rtadvd/timer.h new file mode 100644 index 00000000000..3a0c99b4033 --- /dev/null +++ b/usr.sbin/rtadvd/timer.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* a < b */ +#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\ + (((a).tv_sec == (b).tv_sec) && \ + ((a).tv_usec < (b).tv_usec))) + +/* a <= b */ +#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\ + (((a).tv_sec == (b).tv_sec) &&\ + ((a).tv_usec <= (b).tv_usec))) + +struct rtadvd_timer { + struct rtadvd_timer *next; + struct rtadvd_timer *prev; + struct rainfo *rai; + struct timeval tm; + + void (*expire) __P((void *)); /* expiration function */ + void *expire_data; + void (*update) __P((void *, struct timeval *)); /* update function */ + void *update_data; +}; + +void rtadvd_timer_init __P((void)); +struct rtadvd_timer *rtadvd_add_timer __P((void (*) __P((void *)), + void (*) __P((void *, struct timeval *)), void *, void *)); +void rtadvd_set_timer __P((struct timeval *, struct rtadvd_timer *)); +struct timeval * rtadvd_check_timer __P((void)); +struct timeval * rtadvd_timer_rest __P((struct rtadvd_timer *)); +void TIMEVAL_ADD __P((struct timeval *, struct timeval *, + struct timeval *)); +void TIMEVAL_SUB __P((struct timeval *, struct timeval *, + struct timeval *)); |