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