diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h index 2add6c1..c3ae980 100644 --- a/sys/netinet/icmp6.h +++ b/sys/netinet/icmp6.h @@ -309,6 +309,10 @@ struct nd_opt_hdr { /* Neighbor discovery option header */ #define ND_OPT_MTU 5 #define ND_OPT_ROUTE_INFO 200 /* draft-ietf-ipngwg-router-preference, not officially assigned yet */ +#define ND_OPT_RDNSS 25 /* RFC 6106 */ +#define ND_OPT_DNSSL 31 /* RFC 6106 */ +#define ND_OPT_MAX 31 + struct nd_opt_prefix_info { /* prefix information */ u_int8_t nd_opt_pi_type; @@ -348,6 +352,22 @@ struct nd_opt_route_info { /* route info */ /* prefix follows */ } __attribute__((__packed__)); +struct nd_opt_rdnss { /* RDNSS option (RFC 6106) */ + u_int8_t nd_opt_rdnss_type; + u_int8_t nd_opt_rdnss_len; + u_int16_t nd_opt_rdnss_reserved; + u_int32_t nd_opt_rdnss_lifetime; + /* followed by list of recursive DNS servers */ +} __attribute__((__packed__)); + +struct nd_opt_dnssl { /* DNSSL option (RFC 6106) */ + u_int8_t nd_opt_dnssl_type; + u_int8_t nd_opt_dnssl_len; + u_int16_t nd_opt_dnssl_reserved; + u_int32_t nd_opt_dnssl_lifetime; + /* followed by list of DNS search domains */ +} __attribute__((__packed__)); + /* * icmp6 namelookup */ diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index b37d8b0..d3b38a9 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -104,6 +104,7 @@ SUBDIR= 802_11 \ rpcbind \ rrenumd \ rtadvd \ + rtadvctl \ rtprio \ rtsold \ rwhod \ diff --git a/usr.sbin/rtadvctl/Makefile b/usr.sbin/rtadvctl/Makefile new file mode 100644 index 0000000..2001fed --- /dev/null +++ b/usr.sbin/rtadvctl/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD: stable/10/usr.sbin/rtadvctl/Makefile 224144 2011-07-17 19:24:54Z hrs $ +# +.PATH: ${.CURDIR}/../rtadvd + +PROG= rtadvctl +MAN= rtadvctl.8 + +SRCS= rtadvctl.c control.c control_client.c if.c timer_subr.c + +CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../rtadvd +WARNS?= 1 + +.include diff --git a/usr.sbin/rtadvctl/rtadvctl.8 b/usr.sbin/rtadvctl/rtadvctl.8 new file mode 100644 index 0000000..e344874 --- /dev/null +++ b/usr.sbin/rtadvctl/rtadvctl.8 @@ -0,0 +1,105 @@ +.\" 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. +.\" +.\" 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. +.\" +.\" $FreeBSD: stable/10/usr.sbin/rtadvctl/rtadvctl.8 235873 2012-05-24 02:24:03Z wblock $ +.\" +.Dd July 16, 2011 +.Dt RTADVCTL 8 +.Os +.Sh NAME +.Nm rtadvctl +.Nd control program for +.Xr rtadvd 8 +daemon +.Sh SYNOPSIS +.Nm +.Op Fl v +.Ar subcommand +.Op Ar interface ... +.Sh DESCRIPTION +.Nm +is a utility that communicates with +.Xr rtadvd 8 +daemon and displays information about Router Advertisement messages being +sent on each interface. +.Pp +This utility provides several options and subcommands. +The options are as follows: +.Bl -tag -width indent +.\" +.It Fl v +Increase verbosity level. +When specified once, the +.Nm +utility shows additional information about prefixes, RDNSS, and DNSSL +options. +When given twice, it additionally shows information about +inactive interfaces and some statistics. +.El +.Pp +The subcommands are as follows: +.Bl -tag -width indent +.\" +.It reload Op interfaces... +Specifies to reload the configuration file. If one or more +.Ar interface +is specified, configuration entries for the interfaces will be reloaded +selectively. +.It enable interfaces... +Specifies to mark the interface as enable and to try to reload the +configuration entry. +This subcommand is useful for dynamically-added interfaces. +.Pp +The +.Xr rtadvd 8 +daemon marks an interface as enable if the interface exists and the +configuration file has a valid entry for that when it is invoked. +.It disable interfaces... +Specifies to mark the interface as disable. +.It shutdown +Makes the +.Xr rtadvd 8 +daemon shut down. +Note that the +.Xr rtadvd 8 +daemon will send several RAs with zero lifetime to invalidate the old +information on each interface. +It will take at most nine seconds. +.It show Op interfaces... +Displays information on Router Advertisement messages being sent +on each interface. +.El +.Sh SEE ALSO +.Xr rtadvd 8 , +.Xr rtadvd.conf 5 +.Sh HISTORY +The +.Nm +command first appeared in +.Fx 9.0 . +.Sh AUTHORS +.Nm +was written by +.An "Hiroki Sato" Aq hrs@FreeBSD.org . diff --git a/usr.sbin/rtadvctl/rtadvctl.c b/usr.sbin/rtadvctl/rtadvctl.c new file mode 100644 index 0000000..463963d --- /dev/null +++ b/usr.sbin/rtadvctl/rtadvctl.c @@ -0,0 +1,939 @@ +/*- + * 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. + * + * 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. + * + * $FreeBSD: stable/10/usr.sbin/rtadvctl/rtadvctl.c 253970 2013-08-05 20:13:02Z hrs $ + * + */ + +#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 +#include +#include +#include +#include +#include + +#include "pathnames.h" +#include "rtadvd.h" +#include "if.h" +#include "timer_subr.h" +#include "timer.h" +#include "control.h" +#include "control_client.h" + +#define RA_IFSTATUS_INACTIVE 0 +#define RA_IFSTATUS_RA_RECV 1 +#define RA_IFSTATUS_RA_SEND 2 + +static int vflag = LOG_ERR; + +static void usage(void); + +static int action_propset(char *); +static int action_propget(char *, struct ctrl_msg_pl *); +static int action_plgeneric(int, char *, char *); + +static int action_enable(int, char **); +static int action_disable(int, char **); +static int action_reload(int, char **); +static int action_echo(int, char **); +static int action_version(int, char **); +static int action_shutdown(int, char **); + +static int action_show(int, char **); +static int action_show_prefix(struct prefix *); +static int action_show_rtinfo(struct rtinfo *); +static int action_show_rdnss(void *); +static int action_show_dnssl(void *); + +static int csock_client_open(struct sockinfo *); +static size_t dname_labeldec(char *, size_t, const char *); +static void mysyslog(int, const char *, ...); + +static const char *rtpref_str[] = { + "medium", /* 00 */ + "high", /* 01 */ + "rsv", /* 10 */ + "low" /* 11 */ +}; + +static struct dispatch_table { + const char *dt_comm; + int (*dt_act)(int, char **); +} dtable[] = { + { "show", action_show }, + { "reload", action_reload }, + { "shutdown", action_shutdown }, + { "enable", action_enable }, + { "disable", action_disable }, + { NULL, NULL }, + { "echo", action_echo }, + { "version", action_version }, + { NULL, NULL }, +}; + +static char errmsgbuf[1024]; +static char *errmsg = NULL; + +static void +mysyslog(int priority, const char * restrict fmt, ...) +{ + va_list ap; + + if (vflag >= priority) { + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + } +} + +static void +usage(void) +{ + int i; + + for (i = 0; (size_t)i < sizeof(dtable)/sizeof(dtable[0]); i++) { + if (dtable[i].dt_comm == NULL) + break; + printf("%s\n", dtable[i].dt_comm); + } + + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int i; + int ch; + int (*action)(int, char **) = NULL; + int error; + + while ((ch = getopt(argc, argv, "Dv")) != -1) { + switch (ch) { + case 'D': + vflag = LOG_DEBUG; + break; + case 'v': + vflag++; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + + for (i = 0; (size_t)i < sizeof(dtable)/sizeof(dtable[0]); i++) { + if (dtable[i].dt_comm == NULL || + strcmp(dtable[i].dt_comm, argv[0]) == 0) { + action = dtable[i].dt_act; + break; + } + } + + if (action == NULL) + usage(); + + error = (dtable[i].dt_act)(--argc, ++argv); + if (error) { + fprintf(stderr, "%s failed", dtable[i].dt_comm); + if (errmsg != NULL) + fprintf(stderr, ": %s", errmsg); + fprintf(stderr, ".\n"); + } + + return (error); +} + +static int +csock_client_open(struct sockinfo *s) +{ + struct sockaddr_un sun; + + if ((s->si_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) + err(1, "cannot open control socket."); + + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + sun.sun_len = sizeof(sun); + strlcpy(sun.sun_path, s->si_name, sizeof(sun.sun_path)); + + if (connect(s->si_fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) + err(1, "connect: %s", s->si_name); + + mysyslog(LOG_DEBUG, + "<%s> connected to %s", __func__, sun.sun_path); + + return (0); +} + +static int +action_plgeneric(int action, char *plstr, char *buf) +{ + struct ctrl_msg_hdr *cm; + struct ctrl_msg_pl cp; + struct sockinfo *s; + char *msg; + char *p; + char *q; + + s = &ctrlsock; + csock_client_open(s); + + cm = (struct ctrl_msg_hdr *)buf; + msg = (char *)buf + sizeof(*cm); + + cm->cm_version = CM_VERSION; + cm->cm_type = action; + cm->cm_len = sizeof(*cm); + + if (plstr != NULL) { + memset(&cp, 0, sizeof(cp)); + p = strchr(plstr, ':'); + q = strchr(plstr, '='); + if (p != NULL && q != NULL && p > q) + return (1); + + if (p == NULL) { /* No : */ + cp.cp_ifname = NULL; + cp.cp_key = plstr; + } else if (p == plstr) { /* empty */ + cp.cp_ifname = NULL; + cp.cp_key = plstr + 1; + } else { + *p++ = '\0'; + cp.cp_ifname = plstr; + cp.cp_key = p; + } + if (q == NULL) + cp.cp_val = NULL; + else { + *q++ = '\0'; + cp.cp_val = q; + } + cm->cm_len += cm_pl2bin(msg, &cp); + + mysyslog(LOG_DEBUG, "<%s> key=%s, val_len=%d, ifname=%s", + __func__,cp.cp_key, cp.cp_val_len, cp.cp_ifname); + } + + return (cm_handler_client(s->si_fd, CM_STATE_MSG_DISPATCH, buf)); +} + +static int +action_propget(char *argv, struct ctrl_msg_pl *cp) +{ + int error; + struct ctrl_msg_hdr *cm; + char buf[CM_MSG_MAXLEN]; + char *msg; + + memset(cp, 0, sizeof(*cp)); + cm = (struct ctrl_msg_hdr *)buf; + msg = (char *)buf + sizeof(*cm); + + error = action_plgeneric(CM_TYPE_REQ_GET_PROP, argv, buf); + if (error || cm->cm_len <= sizeof(*cm)) + return (1); + + cm_bin2pl(msg, cp); + mysyslog(LOG_DEBUG, "<%s> type=%d, len=%d", + __func__, cm->cm_type, cm->cm_len); + mysyslog(LOG_DEBUG, "<%s> key=%s, val_len=%d, ifname=%s", + __func__,cp->cp_key, cp->cp_val_len, cp->cp_ifname); + + return (0); +} + +static int +action_propset(char *argv) +{ + char buf[CM_MSG_MAXLEN]; + + return (action_plgeneric(CM_TYPE_REQ_SET_PROP, argv, buf)); +} + +static int +action_disable(int argc, char **argv) +{ + char *action_argv; + char argv_disable[IFNAMSIZ + sizeof(":disable=")]; + int i; + int error; + + if (argc < 1) + return (1); + + error = 0; + for (i = 0; i < argc; i++) { + sprintf(argv_disable, "%s:disable=", argv[i]); + action_argv = argv_disable; + error += action_propset(action_argv); + } + + return (error); +} + +static int +action_enable(int argc, char **argv) +{ + char *action_argv; + char argv_enable[IFNAMSIZ + sizeof(":enable=")]; + int i; + int error; + + if (argc < 1) + return (1); + + error = 0; + for (i = 0; i < argc; i++) { + sprintf(argv_enable, "%s:enable=", argv[i]); + action_argv = argv_enable; + error += action_propset(action_argv); + } + + return (error); +} + +static int +action_reload(int argc, char **argv) +{ + char *action_argv; + char argv_reload[IFNAMSIZ + sizeof(":reload=")]; + int i; + int error; + + if (argc == 0) { + action_argv = strdup(":reload="); + return (action_propset(action_argv)); + } + + error = 0; + for (i = 0; i < argc; i++) { + sprintf(argv_reload, "%s:reload=", argv[i]); + action_argv = argv_reload; + error += action_propset(action_argv); + } + + return (error); +} + +static int +action_echo(int argc __unused, char **argv __unused) +{ + char *action_argv; + + action_argv = strdup("echo"); + return (action_propset(action_argv)); +} + +static int +action_shutdown(int argc __unused, char **argv __unused) +{ + char *action_argv; + + action_argv = strdup("shutdown"); + return (action_propset(action_argv)); +} + +/* XXX */ +static int +action_version(int argc __unused, char **argv __unused) +{ + char *action_argv; + struct ctrl_msg_pl cp; + int error; + + action_argv = strdup(":version="); + error = action_propget(action_argv, &cp); + if (error) + return (error); + + printf("version=%s\n", cp.cp_val); + return (0); +} + +static int +action_show(int argc, char **argv) +{ + char *action_argv; + char argv_ifilist[sizeof(":ifilist=")] = ":ifilist="; + char argv_ifi[IFNAMSIZ + sizeof(":ifi=")]; + char argv_rai[IFNAMSIZ + sizeof(":rai=")]; + char argv_rti[IFNAMSIZ + sizeof(":rti=")]; + char argv_pfx[IFNAMSIZ + sizeof(":pfx=")]; + char argv_ifi_ra_timer[IFNAMSIZ + sizeof(":ifi_ra_timer=")]; + char argv_rdnss[IFNAMSIZ + sizeof(":rdnss=")]; + char argv_dnssl[IFNAMSIZ + sizeof(":dnssl=")]; + char ssbuf[SSBUFLEN]; + + struct timespec now, ts0, ts; + struct ctrl_msg_pl cp; + struct ifinfo *ifi; + TAILQ_HEAD(, ifinfo) ifl = TAILQ_HEAD_INITIALIZER(ifl); + char *endp; + char *p; + int error; + int i; + int len; + + if (argc == 0) { + action_argv = argv_ifilist; + error = action_propget(action_argv, &cp); + if (error) + return (error); + + p = cp.cp_val; + endp = p + cp.cp_val_len; + while (p < endp) { + ifi = malloc(sizeof(*ifi)); + if (ifi == NULL) + return (1); + memset(ifi, 0, sizeof(*ifi)); + + strcpy(ifi->ifi_ifname, p); + ifi->ifi_ifindex = if_nametoindex(ifi->ifi_ifname); + TAILQ_INSERT_TAIL(&ifl, ifi, ifi_next); + p += strlen(ifi->ifi_ifname) + 1; + } + } else { + for (i = 0; i < argc; i++) { + ifi = malloc(sizeof(*ifi)); + if (ifi == NULL) + return (1); + memset(ifi, 0, sizeof(*ifi)); + + strcpy(ifi->ifi_ifname, argv[i]); + ifi->ifi_ifindex = if_nametoindex(ifi->ifi_ifname); + if (ifi->ifi_ifindex == 0) { + sprintf(errmsgbuf, "invalid interface %s", + ifi->ifi_ifname); + errmsg = errmsgbuf; + return (1); + } + + TAILQ_INSERT_TAIL(&ifl, ifi, ifi_next); + } + } + + clock_gettime(CLOCK_REALTIME_FAST, &now); + clock_gettime(CLOCK_MONOTONIC_FAST, &ts); + TS_SUB(&now, &ts, &ts0); + + TAILQ_FOREACH(ifi, &ifl, ifi_next) { + struct ifinfo *ifi_s; + struct rtadvd_timer *rat; + struct rainfo *rai; + struct rtinfo *rti; + struct prefix *pfx; + int c; + int ra_ifstatus; + + sprintf(argv_ifi, "%s:ifi=", ifi->ifi_ifname); + action_argv = argv_ifi; + error = action_propget(action_argv, &cp); + if (error) + return (error); + ifi_s = (struct ifinfo *)cp.cp_val; + + if (!(ifi_s->ifi_persist) && vflag < LOG_NOTICE) + continue; + + printf("%s: flags=<", ifi->ifi_ifname); + + c = 0; + if (ifi_s->ifi_ifindex == 0) + c += printf("NONEXISTENT"); + else + c += printf("%s", (ifi_s->ifi_flags & IFF_UP) ? + "UP" : "DOWN"); + switch (ifi_s->ifi_state) { + case IFI_STATE_CONFIGURED: + c += printf("%s%s", (c) ? "," : "", "CONFIGURED"); + break; + case IFI_STATE_TRANSITIVE: + c += printf("%s%s", (c) ? "," : "", "TRANSITIVE"); + break; + } + if (ifi_s->ifi_persist) + c += printf("%s%s", (c) ? "," : "", "PERSIST"); + printf(">"); + + ra_ifstatus = RA_IFSTATUS_INACTIVE; + if ((ifi_s->ifi_flags & IFF_UP) && + ((ifi_s->ifi_state == IFI_STATE_CONFIGURED) || + (ifi_s->ifi_state == IFI_STATE_TRANSITIVE))) { +#if (__FreeBSD_version < 900000) + /* + * RA_RECV: !ip6.forwarding && ip6.accept_rtadv + * RA_SEND: ip6.forwarding + */ + if (getinet6sysctl(IPV6CTL_FORWARDING) == 0) { + if (getinet6sysctl(IPV6CTL_ACCEPT_RTADV)) + ra_ifstatus = RA_IFSTATUS_RA_RECV; + else + ra_ifstatus = RA_IFSTATUS_INACTIVE; + } else + ra_ifstatus = RA_IFSTATUS_RA_SEND; +#else + /* + * RA_RECV: ND6_IFF_ACCEPT_RTADV + * RA_SEND: ip6.forwarding + */ + if (ifi_s->ifi_nd_flags & ND6_IFF_ACCEPT_RTADV) + ra_ifstatus = RA_IFSTATUS_RA_RECV; + else if (getinet6sysctl(IPV6CTL_FORWARDING)) + ra_ifstatus = RA_IFSTATUS_RA_SEND; + else + ra_ifstatus = RA_IFSTATUS_INACTIVE; +#endif + } + + c = 0; + printf(" status=<"); + if (ra_ifstatus == RA_IFSTATUS_INACTIVE) + printf("%s%s", (c) ? "," : "", "INACTIVE"); + else if (ra_ifstatus == RA_IFSTATUS_RA_RECV) + printf("%s%s", (c) ? "," : "", "RA_RECV"); + else if (ra_ifstatus == RA_IFSTATUS_RA_SEND) + printf("%s%s", (c) ? "," : "", "RA_SEND"); + printf("> "); + + switch (ifi_s->ifi_state) { + case IFI_STATE_CONFIGURED: + case IFI_STATE_TRANSITIVE: + break; + default: + printf("\n"); + continue; + } + + printf("mtu %d\n", ifi_s->ifi_phymtu); + + sprintf(argv_rai, "%s:rai=", ifi->ifi_ifname); + action_argv = argv_rai; + + error = action_propget(action_argv, &cp); + if (error) + continue; + + rai = (struct rainfo *)cp.cp_val; + + printf("\tDefaultLifetime: %s", + sec2str(rai->rai_lifetime, ssbuf)); + if (ra_ifstatus != RA_IFSTATUS_RA_SEND && + rai->rai_lifetime == 0) + printf(" (RAs will be sent with zero lifetime)"); + + printf("\n"); + + printf("\tMinAdvInterval/MaxAdvInterval: "); + printf("%s/", sec2str(rai->rai_mininterval, ssbuf)); + printf("%s\n", sec2str(rai->rai_maxinterval, ssbuf)); + if (rai->rai_linkmtu) + printf("\tAdvLinkMTU: %d", rai->rai_linkmtu); + else + printf("\tAdvLinkMTU: "); + + printf(", "); + + printf("Flags: "); + if (rai->rai_managedflg || rai->rai_otherflg) { + printf("%s", rai->rai_managedflg ? "M" : ""); + printf("%s", rai->rai_otherflg ? "O" : ""); + } else + printf(""); + + printf(", "); + + printf("Preference: %s\n", + rtpref_str[(rai->rai_rtpref >> 3) & 0xff]); + + printf("\tReachableTime: %s, ", + sec2str(rai->rai_reachabletime, ssbuf)); + printf("RetransTimer: %s, " + "CurHopLimit: %d\n", + sec2str(rai->rai_retranstimer, ssbuf), + rai->rai_hoplimit); + printf("\tAdvIfPrefixes: %s\n", + rai->rai_advifprefix ? "yes" : "no"); + + /* RA timer */ + rat = NULL; + if (ifi_s->ifi_ra_timer != NULL) { + sprintf(argv_ifi_ra_timer, "%s:ifi_ra_timer=", + ifi->ifi_ifname); + action_argv = argv_ifi_ra_timer; + + error = action_propget(action_argv, &cp); + if (error) + return (error); + + rat = (struct rtadvd_timer *)cp.cp_val; + } + printf("\tNext RA send: "); + if (rat == NULL) + printf("never\n"); + else { + ts.tv_sec = rat->rat_tm.tv_sec + ts0.tv_sec; + printf("%s", ctime(&ts.tv_sec)); + } + printf("\tLast RA send: "); + if (ifi_s->ifi_ra_lastsent.tv_sec == 0) + printf("never\n"); + else { + ts.tv_sec = ifi_s->ifi_ra_lastsent.tv_sec + ts0.tv_sec; + printf("%s", ctime(&ts.tv_sec)); + } + if (rai->rai_clockskew) + printf("\tClock skew: %" PRIu16 "sec\n", + rai->rai_clockskew); + + if (vflag < LOG_WARNING) + continue; + + /* route information */ + sprintf(argv_rti, "%s:rti=", ifi->ifi_ifname); + action_argv = argv_rti; + error = action_propget(action_argv, &cp); + if (error) + return (error); + + rti = (struct rtinfo *)cp.cp_val; + len = cp.cp_val_len / sizeof(*rti); + if (len > 0) { + printf("\tRoute Info:\n"); + + for (i = 0; i < len; i++) + action_show_rtinfo(&rti[i]); + } + + /* prefix information */ + sprintf(argv_pfx, "%s:pfx=", ifi->ifi_ifname); + action_argv = argv_pfx; + + error = action_propget(action_argv, &cp); + if (error) + continue; + + pfx = (struct prefix *)cp.cp_val; + len = cp.cp_val_len / sizeof(*pfx); + + if (len > 0) { + printf("\tPrefixes (%d):\n", len); + + for (i = 0; i < len; i++) + action_show_prefix(&pfx[i]); + } + + /* RDNSS information */ + sprintf(argv_rdnss, "%s:rdnss=", ifi->ifi_ifname); + action_argv = argv_rdnss; + + error = action_propget(action_argv, &cp); + if (error) + continue; + + len = *((uint16_t *)cp.cp_val); + + if (len > 0) { + printf("\tRDNSS entries:\n"); + action_show_rdnss(cp.cp_val); + } + + /* DNSSL information */ + sprintf(argv_dnssl, "%s:dnssl=", ifi->ifi_ifname); + action_argv = argv_dnssl; + + error = action_propget(action_argv, &cp); + if (error) + continue; + + len = *((uint16_t *)cp.cp_val); + + if (len > 0) { + printf("\tDNSSL entries:\n"); + action_show_dnssl(cp.cp_val); + } + + if (vflag < LOG_NOTICE) + continue; + + printf("\n"); + + printf("\tCounters\n" + "\t RA burst counts: %" PRIu16 " (interval: %s)\n" + "\t RS wait counts: %" PRIu16 "\n", + ifi_s->ifi_burstcount, + sec2str(ifi_s->ifi_burstinterval, ssbuf), + ifi_s->ifi_rs_waitcount); + + printf("\tOutputs\n" + "\t RA: %" PRIu64 "\n", ifi_s->ifi_raoutput); + + printf("\tInputs\n" + "\t RA: %" PRIu64 " (normal)\n" + "\t RA: %" PRIu64 " (inconsistent)\n" + "\t RS: %" PRIu64 "\n", + ifi_s->ifi_rainput, + ifi_s->ifi_rainconsistent, + ifi_s->ifi_rsinput); + + printf("\n"); + +#if 0 /* Not implemented yet */ + printf("\tReceived RAs:\n"); +#endif + } + + return (0); +} + +static int +action_show_rtinfo(struct rtinfo *rti) +{ + char ntopbuf[INET6_ADDRSTRLEN]; + char ssbuf[SSBUFLEN]; + + printf("\t %s/%d (pref: %s, ltime: %s)\n", + inet_ntop(AF_INET6, &rti->rti_prefix, + ntopbuf, sizeof(ntopbuf)), + rti->rti_prefixlen, + rtpref_str[0xff & (rti->rti_rtpref >> 3)], + (rti->rti_ltime == ND6_INFINITE_LIFETIME) ? + "infinity" : sec2str(rti->rti_ltime, ssbuf)); + + return (0); +} + +static int +action_show_prefix(struct prefix *pfx) +{ + char ntopbuf[INET6_ADDRSTRLEN]; + char ssbuf[SSBUFLEN]; + struct timespec now; + + clock_gettime(CLOCK_MONOTONIC_FAST, &now); + printf("\t %s/%d", inet_ntop(AF_INET6, &pfx->pfx_prefix, + ntopbuf, sizeof(ntopbuf)), pfx->pfx_prefixlen); + + printf(" ("); + switch (pfx->pfx_origin) { + case PREFIX_FROM_KERNEL: + printf("KERNEL"); + break; + case PREFIX_FROM_CONFIG: + printf("CONFIG"); + break; + case PREFIX_FROM_DYNAMIC: + printf("DYNAMIC"); + break; + } + + printf(","); + + printf(" vltime=%s", + (pfx->pfx_validlifetime == ND6_INFINITE_LIFETIME) ? + "infinity" : sec2str(pfx->pfx_validlifetime, ssbuf)); + + if (pfx->pfx_vltimeexpire > 0) + printf("(expire: %s)", + ((long)pfx->pfx_vltimeexpire > now.tv_sec) ? + sec2str(pfx->pfx_vltimeexpire - now.tv_sec, ssbuf) : + "0"); + + printf(","); + + printf(" pltime=%s", + (pfx->pfx_preflifetime == ND6_INFINITE_LIFETIME) ? + "infinity" : sec2str(pfx->pfx_preflifetime, ssbuf)); + + if (pfx->pfx_pltimeexpire > 0) + printf("(expire %s)", + ((long)pfx->pfx_pltimeexpire > now.tv_sec) ? + sec2str(pfx->pfx_pltimeexpire - now.tv_sec, ssbuf) : + "0"); + + printf(","); + + printf(" flags="); + if (pfx->pfx_onlinkflg || pfx->pfx_autoconfflg) { + printf("%s", pfx->pfx_onlinkflg ? "L" : ""); + printf("%s", pfx->pfx_autoconfflg ? "A" : ""); + } else + printf(""); + + if (pfx->pfx_timer) { + struct timespec *rest; + + rest = rtadvd_timer_rest(pfx->pfx_timer); + if (rest) { /* XXX: what if not? */ + printf(" expire=%s", sec2str(rest->tv_sec, ssbuf)); + } + } + + printf(")\n"); + + return (0); +} + +static int +action_show_rdnss(void *msg) +{ + struct rdnss *rdn; + struct rdnss_addr *rda; + uint16_t *rdn_cnt; + uint16_t *rda_cnt; + int i; + int j; + char *p; + uint32_t ltime; + char ntopbuf[INET6_ADDRSTRLEN]; + char ssbuf[SSBUFLEN]; + + p = msg; + rdn_cnt = (uint16_t *)p; + p += sizeof(*rdn_cnt); + + if (*rdn_cnt > 0) { + for (i = 0; i < *rdn_cnt; i++) { + rdn = (struct rdnss *)p; + ltime = rdn->rd_ltime; + p += sizeof(*rdn); + + rda_cnt = (uint16_t *)p; + p += sizeof(*rda_cnt); + if (*rda_cnt > 0) + for (j = 0; j < *rda_cnt; j++) { + rda = (struct rdnss_addr *)p; + printf("\t %s (ltime=%s)\n", + inet_ntop(AF_INET6, + &rda->ra_dns, + ntopbuf, + sizeof(ntopbuf)), + sec2str(ltime, ssbuf)); + p += sizeof(*rda); + } + } + } + + return (0); +} + +static int +action_show_dnssl(void *msg) +{ + struct dnssl *dns; + struct dnssl_addr *dna; + uint16_t *dns_cnt; + uint16_t *dna_cnt; + int i; + int j; + char *p; + uint32_t ltime; + char hbuf[NI_MAXHOST]; + char ssbuf[SSBUFLEN]; + + p = msg; + dns_cnt = (uint16_t *)p; + p += sizeof(*dns_cnt); + + if (*dns_cnt > 0) { + for (i = 0; i < *dns_cnt; i++) { + dns = (struct dnssl *)p; + ltime = dns->dn_ltime; + p += sizeof(*dns); + + dna_cnt = (uint16_t *)p; + p += sizeof(*dna_cnt); + if (*dna_cnt > 0) + for (j = 0; j < *dna_cnt; j++) { + dna = (struct dnssl_addr *)p; + dname_labeldec(hbuf, sizeof(hbuf), + dna->da_dom); + printf("\t %s (ltime=%s)\n", + hbuf, sec2str(ltime, ssbuf)); + p += sizeof(*dna); + } + } + } + + return (0); +} + +/* Decode domain name label encoding in RFC 1035 Section 3.1 */ +static size_t +dname_labeldec(char *dst, size_t dlen, const char *src) +{ + size_t len; + const char *src_origin; + const char *src_last; + const char *dst_origin; + + src_origin = src; + src_last = strchr(src, '\0'); + dst_origin = dst; + memset(dst, '\0', dlen); + while (src && (len = (uint8_t)(*src++) & 0x3f) && + (src + len) <= src_last) { + if (dst != dst_origin) + *dst++ = '.'; + mysyslog(LOG_DEBUG, "<%s> labellen = %zd", __func__, len); + memcpy(dst, src, len); + src += len; + dst += len; + } + *dst = '\0'; + + return (src - src_origin); +} diff --git a/usr.sbin/rtadvd/Makefile b/usr.sbin/rtadvd/Makefile index 54eb3ae..4273d75 100644 --- a/usr.sbin/rtadvd/Makefile +++ b/usr.sbin/rtadvd/Makefile @@ -11,14 +11,19 @@ # AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT # LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE. -# $FreeBSD: src/usr.sbin/rtadvd/Makefile,v 1.1.2.2 2001/04/25 12:10:48 ru Exp $ +# +# $FreeBSD: stable/10/usr.sbin/rtadvd/Makefile 224144 2011-07-17 19:24:54Z hrs $ PROG= rtadvd -SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c dump.c -WARNS?= 2 +MAN= rtadvd.conf.5 rtadvd.8 +SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c timer_subr.c \ + control.c control_server.c -CFLAGS+=-DINET6 +DPADD= ${LIBUTIL} +LDADD= -lutil -MAN= rtadvd.conf.5 rtadvd.8 +CFLAGS+= -DHAVE_ARC4RANDOM + +WARNS?= 1 .include diff --git a/usr.sbin/rtadvd/advcap.c b/usr.sbin/rtadvd/advcap.c index 6332acd..bc08f33 100644 --- a/usr.sbin/rtadvd/advcap.c +++ b/usr.sbin/rtadvd/advcap.c @@ -1,4 +1,5 @@ -/* $KAME: advcap.c,v 1.5 2001/02/01 09:12:08 jinmei Exp $ */ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/advcap.c 222824 2011-06-07 15:40:17Z hrs $ */ +/* $KAME: advcap.c,v 1.11 2003/05/19 09:46:50 keiichi Exp $ */ /* * Copyright (c) 1983 The Regents of the University of California. @@ -12,7 +13,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors + * 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. * @@ -27,9 +28,6 @@ * 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. - * - * $FreeBSD: src/usr.sbin/rtadvd/advcap.c,v 1.1.2.2 2001/07/03 11:02:13 ume Exp $ - * $DragonFly: src/usr.sbin/rtadvd/advcap.c,v 1.5 2005/02/17 14:00:10 joerg Exp $ */ /* @@ -66,8 +64,6 @@ #define V_TERM "HOST" #endif -char *RM; - /* * termcap - routines for dealing with the terminal capability data base * @@ -85,16 +81,14 @@ char *RM; static char *tbuf; static int hopcount; /* detect infinite loops in termcap, init 0 */ -static char *remotefile; - -extern char *conffile; +extern const char *conffile; int tgetent(char *, char *); -int getent(char *, char *, char *); +int getent(char *, char *, const char *); int tnchktc(void); int tnamatch(char *); static char *tskip(char *); -long long tgetnum(char *); +int64_t tgetnum(char *); int tgetflag(char *); char *tgetstr(char *, char **); static char *tdecode(char *, char **); @@ -107,18 +101,16 @@ static char *tdecode(char *, char **); int tgetent(char *bp, char *name) { - char *cp; - - remotefile = cp = conffile ? conffile : _PATH_RTADVDCONF; - return (getent(bp, name, cp)); + return (getent(bp, name, conffile)); } int -getent(char *bp, char *name, char *cp) +getent(char *bp, char *name, const char *cfile) { int c; int i = 0, cnt = 0; char ibuf[BUFSIZ]; + char *cp; int tf; tbuf = bp; @@ -130,9 +122,9 @@ getent(char *bp, char *name, char *cp) * 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 (cfile && *cfile) + tf = open(cfile, O_RDONLY); + if (tf < 0) { syslog(LOG_INFO, "<%s> open: %s", __func__, strerror(errno)); @@ -157,8 +149,9 @@ getent(char *bp, char *name, char *cp) } break; } - if (cp >= bp+BUFSIZ) { - write(2,"Remcap entry too long\n", 23); + if (cp >= bp + BUFSIZ) { + write(STDERR_FILENO, "Remcap entry too long\n", + 23); break; } else *cp++ = c; @@ -193,31 +186,31 @@ tnchktc(void) p = tbuf + strlen(tbuf) - 2; /* before the last colon */ while (*--p != ':') - if (p MAXHOP) { - write(2, "Infinite tc= loop\n", 18); + write(STDERR_FILENO, "Infinite tc= loop\n", 18); return (0); } - if (getent(tcbuf, tcname, remotefile) != 1) { + if (getent(tcbuf, tcname, conffile) != 1) { return (0); } for (q = tcbuf; *q++ != ':'; ) ; l = p - holdtbuf + strlen(q); if (l > BUFSIZ) { - write(2, "Remcap entry too long\n", 23); + write(STDERR_FILENO, "Remcap entry too long\n", 23); q[BUFSIZ - (p-holdtbuf)] = 0; } strcpy(p, q); @@ -301,10 +294,10 @@ breakbreak: * a # character. If the option is not found we return -1. * Note that we handle octal numbers beginning with 0. */ -long long +int64_t tgetnum(char *id) { - long long i; + int64_t i; int base; char *bp = tbuf; @@ -385,7 +378,7 @@ tgetstr(char *id, char **area) } /* - * Tdecode does the grunt work to decode the + * Tdecode does the grung work to decode the * string capability escapes. */ static char * @@ -393,7 +386,7 @@ tdecode(char *str, char **area) { char *cp; int c; - char *dp; + const char *dp; int i; char term; diff --git a/usr.sbin/rtadvd/advcap.h b/usr.sbin/rtadvd/advcap.h index 82deed1..2447cf5 100644 --- a/usr.sbin/rtadvd/advcap.h +++ b/usr.sbin/rtadvd/advcap.h @@ -1,4 +1,5 @@ -/* $KAME: advcap.h,v 1.3 2001/02/01 09:12:08 jinmei Exp $ */ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/advcap.h 173412 2007-11-07 10:53:41Z kevlo $ */ +/* $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. @@ -24,9 +25,6 @@ * 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. - * - * $FreeBSD: src/usr.sbin/rtadvd/advcap.h,v 1.1.2.2 2001/07/03 11:02:14 ume Exp $ - * $DragonFly: src/usr.sbin/rtadvd/advcap.h,v 1.3 2003/11/03 19:31:42 eirikn Exp $ */ /* Based on Id: termcap.h,v 1.8 1996/09/10 12:42:10 peter Exp */ @@ -40,7 +38,7 @@ __BEGIN_DECLS extern int agetent(char *, const char *); extern int agetflag(const char *); -extern long long agetnum(const char *); +extern int64_t agetnum(const char *); extern char *agetstr(const char *, char **); __END_DECLS diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c index 09560cc..7b393c5 100644 --- a/usr.sbin/rtadvd/config.c +++ b/usr.sbin/rtadvd/config.c @@ -1,9 +1,11 @@ -/* $KAME: config.c,v 1.37 2001/05/25 07:34:00 itojun Exp $ */ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/config.c 254955 2013-08-27 11:50:33Z hrs $ */ +/* $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: @@ -15,7 +17,7 @@ * 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 @@ -27,20 +29,14 @@ * 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. - * - * $FreeBSD: src/usr.sbin/rtadvd/config.c,v 1.3.2.5 2003/04/22 09:40:57 suz Exp $ */ #include #include #include -#include -#include #include -#if defined(__DragonFly__) #include -#endif /* __DragonFly__ */ #include #include @@ -49,18 +45,19 @@ #include #include #include -#ifdef MIP6 -#include -#endif +#include #include #include #include #include +#include +#include #include -#include #include +#include +#include #include #include @@ -70,31 +67,57 @@ #include "if.h" #include "config.h" +/* label of tcapcode + number + domain name + zero octet */ +static char entbuf[10 + 3 + NI_MAXHOST + 1]; +static char oentbuf[10 + 3 + NI_MAXHOST + 1]; +static char abuf[DNAME_LABELENC_MAXLEN]; + 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 *, int); -static void get_prefix(struct rainfo *); -static int getinet6sysctl(int); +static void makeentry(char *, size_t, int, const char *); +static size_t dname_labelenc(char *, const char *); -void -getconfig(char *intface) +/* Encode domain name label encoding in RFC 1035 Section 3.1 */ +static size_t +dname_labelenc(char *dst, const char *src) { - int stat, pfxs, i; - char tbuf[BUFSIZ]; - struct rainfo *tmp; - long val; - long long val64; - char buf[BUFSIZ]; - char *bp = buf; - char *addr; - static int forwarding = -1; + char *dst_origin; + char *p; + size_t len; + + dst_origin = dst; + len = strlen(src); + + /* Length fields per 63 octets + '\0' (<= DNAME_LABELENC_MAXLEN) */ + memset(dst, 0, len + len / 64 + 1 + 1); + + syslog(LOG_DEBUG, "<%s> labelenc = %s", __func__, src); + while (src && (len = strlen(src)) != 0) { + /* Put a length field with 63 octet limitation first. */ + p = strchr(src, '.'); + if (p == NULL) + *dst++ = len = MIN(63, len); + else + *dst++ = len = MIN(63, p - src); + /* Copy 63 octets at most. */ + memcpy(dst, src, len); + dst += len; + if (p == NULL) /* the last label */ + break; + src = p + 1; + } + /* Always need a 0-length label at the tail. */ + *dst++ = '\0'; + + syslog(LOG_DEBUG, "<%s> labellen = %td", __func__, dst - dst_origin); + return (dst - dst_origin); +} -#define MUSTHAVE(var, cap) \ +#define MUSTHAVE(var, cap) \ do { \ - int t; \ + int64_t t; \ if ((t = agetnum(cap)) < 0) { \ fprintf(stderr, "rtadvd: need %s for interface %s\n", \ cap, intface); \ @@ -102,53 +125,264 @@ getconfig(char *intface) } \ var = t; \ } while (0) -#define MAYHAVE(var, cap, def) \ + +#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)); - syslog(LOG_INFO, - "<%s> %s isn't defined in the configuration file" - " or the configuration file doesn't exist." - " Treat it as default", - __func__, intface); +int +loadconfig_index(int idx) +{ + char ifname[IFNAMSIZ]; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (if_indextoname(idx, ifname) != NULL) + return (loadconfig_ifname(ifname)); + else + return (1); +} + +int +loadconfig_ifname(char *ifname) +{ + struct ifinfo *ifi; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + update_ifinfo(&ifilist, UPDATE_IFINFO_ALL); + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + /* NULL means all IFs will be processed. */ + if (ifname != NULL && + strcmp(ifi->ifi_ifname, ifname) != 0) + continue; + + if (!ifi->ifi_persist) { + syslog(LOG_INFO, + "<%s> %s is not a target interface. " + "Ignored at this moment.", __func__, + ifi->ifi_ifname); + continue; + + } + if (ifi->ifi_ifindex == 0) { + syslog(LOG_ERR, + "<%s> %s not found. " + "Ignored at this moment.", __func__, + ifi->ifi_ifname); + continue; + } + if (getconfig(ifi) == NULL) { + syslog(LOG_ERR, + "<%s> invalid configuration for %s. " + "Ignored at this moment.", __func__, + ifi->ifi_ifname); + continue; + } } + return (0); +} - tmp = (struct rainfo *)malloc(sizeof(*ralist)); - memset(tmp, 0, sizeof(*tmp)); - tmp->prefix.next = tmp->prefix.prev = &tmp->prefix; - tmp->route.next = tmp->route.prev = &tmp->route; +int +rm_ifinfo_index(int idx) +{ + struct ifinfo *ifi; - /* check if we are allowed to forward packets (if not determined) */ - if (forwarding < 0) { - if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0) - exit(1); + ifi = if_indextoifinfo(idx); + if (ifi == NULL) { + syslog(LOG_ERR, "<%s>: ifinfo not found (idx=%d)", + __func__, idx); + return (-1); + } + + return (rm_ifinfo(ifi)); +} + +int +rm_ifinfo(struct ifinfo *ifi) +{ + int error; + + syslog(LOG_DEBUG, "<%s> enter (%s).", __func__, ifi->ifi_ifname); + switch (ifi->ifi_state) { + case IFI_STATE_UNCONFIGURED: + return (0); + break; + default: + ifi->ifi_state = IFI_STATE_UNCONFIGURED; + syslog(LOG_DEBUG, + "<%s> ifname=%s marked as UNCONFIGURED.", + __func__, ifi->ifi_ifname); + + /* XXX: No MC leaving here because index is disappeared */ + + /* Inactivate timer */ + rtadvd_remove_timer(ifi->ifi_ra_timer); + ifi->ifi_ra_timer = NULL; + break; + } + + /* clean up ifi */ + if (!ifi->ifi_persist) { + TAILQ_REMOVE(&ifilist, ifi, ifi_next); + syslog(LOG_DEBUG, "<%s>: ifinfo (idx=%d) removed.", + __func__, ifi->ifi_ifindex); + free(ifi); + } else { + /* recreate an empty entry */ + update_persist_ifinfo(&ifilist, ifi->ifi_ifname); + syslog(LOG_DEBUG, "<%s>: ifname=%s is persistent.", + __func__, ifi->ifi_ifname); + } + + /* clean up rai if any */ + switch (ifi->ifi_state) { + case IFI_STATE_CONFIGURED: + if (ifi->ifi_rainfo != NULL) { + error = rm_rainfo(ifi->ifi_rainfo); + if (error) + return (error); + ifi->ifi_rainfo = NULL; + } + break; + case IFI_STATE_TRANSITIVE: + if (ifi->ifi_rainfo == ifi->ifi_rainfo_trans) { + if (ifi->ifi_rainfo != NULL) { + error = rm_rainfo(ifi->ifi_rainfo); + if (error) + return (error); + ifi->ifi_rainfo = NULL; + ifi->ifi_rainfo_trans = NULL; + } + } else { + if (ifi->ifi_rainfo != NULL) { + error = rm_rainfo(ifi->ifi_rainfo); + if (error) + return (error); + ifi->ifi_rainfo = NULL; + } + if (ifi->ifi_rainfo_trans != NULL) { + error = rm_rainfo(ifi->ifi_rainfo_trans); + if (error) + return (error); + ifi->ifi_rainfo_trans = NULL; + } + } + } + + syslog(LOG_DEBUG, "<%s> leave (%s).", __func__, ifi->ifi_ifname); + return (0); +} + +int +rm_rainfo(struct rainfo *rai) +{ + struct prefix *pfx; + struct soliciter *sol; + struct rdnss *rdn; + struct rdnss_addr *rdna; + struct dnssl *dns; + struct rtinfo *rti; + + syslog(LOG_DEBUG, "<%s>: enter", __func__); + + TAILQ_REMOVE(&railist, rai, rai_next); + if (rai->rai_ifinfo != NULL) + syslog(LOG_DEBUG, "<%s>: rainfo (idx=%d) removed.", + __func__, rai->rai_ifinfo->ifi_ifindex); + + if (rai->rai_ra_data != NULL) + free(rai->rai_ra_data); + + while ((pfx = TAILQ_FIRST(&rai->rai_prefix)) != NULL) + delete_prefix(pfx); + while ((sol = TAILQ_FIRST(&rai->rai_soliciter)) != NULL) { + TAILQ_REMOVE(&rai->rai_soliciter, sol, sol_next); + free(sol); + } + while ((rdn = TAILQ_FIRST(&rai->rai_rdnss)) != NULL) { + TAILQ_REMOVE(&rai->rai_rdnss, rdn, rd_next); + while ((rdna = TAILQ_FIRST(&rdn->rd_list)) != NULL) { + TAILQ_REMOVE(&rdn->rd_list, rdna, ra_next); + free(rdna); + } + free(rdn); + } + while ((dns = TAILQ_FIRST(&rai->rai_dnssl)) != NULL) { + TAILQ_REMOVE(&rai->rai_dnssl, dns, dn_next); + free(dns); + } + while ((rti = TAILQ_FIRST(&rai->rai_route)) != NULL) { + TAILQ_REMOVE(&rai->rai_route, rti, rti_next); + free(rti); + } + free(rai); + syslog(LOG_DEBUG, "<%s>: leave", __func__); + + return (0); +} + +struct ifinfo * +getconfig(struct ifinfo *ifi) +{ + int stat, i; + int error; + char tbuf[BUFSIZ]; + struct rainfo *rai; + struct rainfo *rai_old; + int32_t val; + int64_t val64; + char buf[BUFSIZ]; + char *bp = buf; + char *addr, *flagstr; + + if (ifi == NULL) /* if does not exist */ + return (NULL); + + if (ifi->ifi_state == IFI_STATE_TRANSITIVE && + ifi->ifi_rainfo == NULL) { + syslog(LOG_INFO, "<%s> %s is shutting down. Skipped.", + __func__, ifi->ifi_ifname); + return (NULL); } + if ((stat = agetent(tbuf, ifi->ifi_ifname)) <= 0) { + memset(tbuf, 0, sizeof(tbuf)); + syslog(LOG_INFO, + "<%s> %s isn't defined in the configuration file" + " or the configuration file doesn't exist." + " Treat it as default", + __func__, ifi->ifi_ifname); + } + + ELM_MALLOC(rai, exit(1)); + TAILQ_INIT(&rai->rai_prefix); + TAILQ_INIT(&rai->rai_route); + TAILQ_INIT(&rai->rai_rdnss); + TAILQ_INIT(&rai->rai_dnssl); + TAILQ_INIT(&rai->rai_soliciter); + rai->rai_ifinfo = ifi; + + /* gather on-link prefixes from the network interfaces. */ + if (agetflag("noifprefix")) + rai->rai_advifprefix = 0; + else + rai->rai_advifprefix = 1; + /* get interface information */ if (agetflag("nolladdr")) - tmp->advlinkopt = 0; + rai->rai_advlinkopt = 0; else - tmp->advlinkopt = 1; - if (tmp->advlinkopt) { - if ((tmp->sdl = if_nametosdl(intface)) == NULL) { + rai->rai_advlinkopt = 1; + if (rai->rai_advlinkopt) { + if (ifi->ifi_sdl.sdl_type == 0) { syslog(LOG_ERR, - "<%s> can't get information of %s", - __func__, intface); - exit(1); + "<%s> can't get information of %s", + __func__, ifi->ifi_ifname); + goto getconfig_free_rai; } - tmp->ifindex = tmp->sdl->sdl_index; - } else - tmp->ifindex = if_nametoindex(intface); - strncpy(tmp->ifname, intface, sizeof(tmp->ifname)); - if ((tmp->phymtu = if_getmtu(intface)) == 0) { - tmp->phymtu = IPV6_MMTU; - syslog(LOG_WARNING, - "<%s> can't get interface mtu of %s. Treat as %d", - __func__, intface, IPV6_MMTU); } /* @@ -157,418 +391,642 @@ getconfig(char *intface) MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL); if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) { syslog(LOG_ERR, - "<%s> maxinterval must be between %e and %u", - __func__, MIN_MAXINTERVAL, MAX_MAXINTERVAL); - exit(1); + "<%s> maxinterval (%" PRIu32 ") on %s is invalid " + "(must be between %u and %u)", __func__, val, + ifi->ifi_ifname, MIN_MAXINTERVAL, MAX_MAXINTERVAL); + goto getconfig_free_rai; } - tmp->maxinterval = (u_int)val; - MAYHAVE(val, "mininterval", tmp->maxinterval/3); - if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) { + rai->rai_maxinterval = (uint16_t)val; + + MAYHAVE(val, "mininterval", rai->rai_maxinterval/3); + if ((uint16_t)val < MIN_MININTERVAL || + (uint16_t)val > (rai->rai_maxinterval * 3) / 4) { syslog(LOG_ERR, - "<%s> mininterval must be between %e and %d", - __func__, - MIN_MININTERVAL, - (tmp->maxinterval * 3) / 4); - exit(1); + "<%s> mininterval (%" PRIu32 ") on %s is invalid " + "(must be between %d and %d)", + __func__, val, ifi->ifi_ifname, MIN_MININTERVAL, + (rai->rai_maxinterval * 3) / 4); + goto getconfig_free_rai; } - tmp->mininterval = (u_int)val; + rai->rai_mininterval = (uint16_t)val; MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT); - tmp->hoplimit = val & 0xff; - - MAYHAVE(val, "raflags", 0); - tmp->managedflg = val & ND_RA_FLAG_MANAGED; - tmp->otherflg = val & ND_RA_FLAG_OTHER; -#ifdef MIP6 - if (mobileip6) - tmp->haflg = val & ND_RA_FLAG_HA; -#endif + rai->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)) { + syslog(LOG_ERR, "<%s> the \'h\' and \'l\'" + " router flags are exclusive", __func__); + goto getconfig_free_rai; + } + val |= ND_RA_FLAG_RTPREF_LOW; + } + } else + MAYHAVE(val, "raflags", 0); + + rai->rai_managedflg = val & ND_RA_FLAG_MANAGED; + rai->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 - tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK; - if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) { - syslog(LOG_ERR, "<%s> invalid router preference on %s", - __func__, intface); - exit(1); + rai->rai_rtpref = val & ND_RA_FLAG_RTPREF_MASK; + if (rai->rai_rtpref == ND_RA_FLAG_RTPREF_RSV) { + syslog(LOG_ERR, "<%s> invalid router preference (%02x) on %s", + __func__, rai->rai_rtpref, ifi->ifi_ifname); + goto getconfig_free_rai; } - MAYHAVE(val, "rltime", tmp->maxinterval * 3); - if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) { + MAYHAVE(val, "rltime", rai->rai_maxinterval * 3); + if ((uint16_t)val && ((uint16_t)val < rai->rai_maxinterval || + (uint16_t)val > MAXROUTERLIFETIME)) { syslog(LOG_ERR, - "<%s> router lifetime on %s must be 0 or" - " between %d and %d", - __func__, intface, - tmp->maxinterval, MAXROUTERLIFETIME); - exit(1); + "<%s> router lifetime (%" PRIu32 ") on %s is invalid " + "(must be 0 or between %d and %d)", + __func__, val, ifi->ifi_ifname, rai->rai_maxinterval, + MAXROUTERLIFETIME); + goto getconfig_free_rai; } - /* - * Basically, hosts MUST NOT send Router Advertisement messages at any - * time (RFC 2461, Section 6.2.3). However, it would sometimes be - * useful to allow hosts to advertise some parameters such as prefix - * information and link MTU. Thus, we allow hosts to invoke rtadvd - * only when router lifetime (on every advertising interface) is - * explicitly set zero. (see also the above section) - */ - if (val && forwarding == 0) { - syslog(LOG_WARNING, - "<%s> non zero router lifetime is specified for %s, " - "which must not be allowed for hosts.", - __func__, intface); - exit(1); - } - tmp->lifetime = val & 0xffff; + rai->rai_lifetime = val & 0xffff; MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME); - if (val > MAXREACHABLETIME) { + if (val < 0 || val > MAXREACHABLETIME) { syslog(LOG_ERR, - "<%s> reachable time must be no greater than %d", - __func__, MAXREACHABLETIME); - exit(1); + "<%s> reachable time (%" PRIu32 ") on %s is invalid " + "(must be no greater than %d)", + __func__, val, ifi->ifi_ifname, MAXREACHABLETIME); + goto getconfig_free_rai; } - tmp->reachabletime = (u_int32_t)val; + rai->rai_reachabletime = (uint32_t)val; MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER); if (val64 < 0 || val64 > 0xffffffff) { - syslog(LOG_ERR, - "<%s> retrans time out of range", __func__); - exit(1); + syslog(LOG_ERR, "<%s> retrans time (%" PRIu64 ") on %s out of range", + __func__, val64, ifi->ifi_ifname); + goto getconfig_free_rai; } - tmp->retranstimer = (u_int32_t)val64; + rai->rai_retranstimer = (uint32_t)val64; -#ifndef MIP6 - if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) { + if (agetnum("hapref") != -1 || agetnum("hatime") != -1) { syslog(LOG_ERR, - "<%s> mobile-ip6 configuration not supported", - __func__); - exit(1); - } -#else - if (!mobileip6) { - if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) { - syslog(LOG_ERR, - "<%s> mobile-ip6 configuration without " - "proper command line option", - __func__); - exit(1); - } - } else { - tmp->hapref = 0; - if ((val = agetnum("hapref")) >= 0) - tmp->hapref = (int16_t)val; - if (tmp->hapref != 0) { - tmp->hatime = 0; - MUSTHAVE(val, "hatime"); - tmp->hatime = (u_int16_t)val; - if (tmp->hatime <= 0) { - syslog(LOG_ERR, - "<%s> home agent lifetime must be greater than 0", - __func__); - exit(1); - } - } + "<%s> mobile-ip6 configuration not supported", + __func__); + goto getconfig_free_rai; } -#endif - /* prefix information */ /* - * This is an implementation specific parameter to consinder + * This is an implementation specific parameter to consider * link propagation delays and poorly synchronized clocks when * checking consistency of advertised lifetimes. */ MAYHAVE(val, "clockskew", 0); - tmp->clockskew = val; + rai->rai_clockskew = val; + + rai->rai_pfxs = 0; + for (i = -1; i < MAXPREFIX; i++) { + struct prefix *pfx; - if ((pfxs = agetnum("addrs")) < 0) { - /* auto configure prefix information */ - if (agetstr("addr", &bp) || agetstr("addr1", &bp)) { + 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->pfx_rainfo = rai; + pfx->pfx_origin = PREFIX_FROM_CONFIG; + + if (inet_pton(AF_INET6, addr, &pfx->pfx_prefix) != 1) { syslog(LOG_ERR, - "<%s> conflicting prefix configuration for %s: " - "automatic and manual config at the same time", - __func__, intface); - exit(1); + "<%s> inet_pton failed for %s", + __func__, addr); + goto getconfig_free_pfx; } - get_prefix(tmp); - } - else { - tmp->pfxs = pfxs; - for (i = 0; i < pfxs; i++) { - struct prefix *pfx; - char entbuf[256]; - int added = (pfxs > 1) ? 1 : 0; - - /* allocate memory to store prefix information */ - if ((pfx = malloc(sizeof(struct prefix))) == NULL) { - syslog(LOG_ERR, - "<%s> can't allocate enough memory", - __func__); - exit(1); - } - memset(pfx, 0, sizeof(*pfx)); - - /* link into chain */ - insque(pfx, &tmp->prefix); - pfx->rainfo = tmp; - - pfx->origin = PREFIX_FROM_CONFIG; - - makeentry(entbuf, sizeof(entbuf), i, "prefixlen", - added); - MAYHAVE(val, entbuf, 64); - if (val < 0 || val > 128) { - syslog(LOG_ERR, - "<%s> prefixlen out of range", - __func__); - exit(1); - } - pfx->prefixlen = (int)val; - - makeentry(entbuf, sizeof(entbuf), i, "pinfoflags", - added); -#ifdef MIP6 - if (mobileip6) - { - MAYHAVE(val, entbuf, - (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO| - ND_OPT_PI_FLAG_ROUTER)); - } else -#endif - { - 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; -#ifdef MIP6 - pfx->routeraddr = val & ND_OPT_PI_FLAG_ROUTER; -#endif + if (IN6_IS_ADDR_MULTICAST(&pfx->pfx_prefix)) { + syslog(LOG_ERR, + "<%s> multicast prefix (%s) must " + "not be advertised on %s", + __func__, addr, ifi->ifi_ifname); + goto getconfig_free_pfx; + } + if (IN6_IS_ADDR_LINKLOCAL(&pfx->pfx_prefix)) + syslog(LOG_NOTICE, + "<%s> link-local prefix (%s) will be" + " advertised on %s", + __func__, addr, ifi->ifi_ifname); - makeentry(entbuf, sizeof(entbuf), i, "vltime", added); - MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); - if (val64 < 0 || val64 > 0xffffffff) { - syslog(LOG_ERR, - "<%s> vltime out of range", - __func__); - exit(1); - } - pfx->validlifetime = (u_int32_t)val64; - - makeentry(entbuf, sizeof(entbuf), i, "vltimedecr", - added); - if (agetflag(entbuf)) { - struct timeval now; - gettimeofday(&now, 0); - pfx->vltimeexpire = - now.tv_sec + pfx->validlifetime; - } + makeentry(entbuf, sizeof(entbuf), i, "prefixlen"); + MAYHAVE(val, entbuf, 64); + if (val < 0 || val > 128) { + syslog(LOG_ERR, "<%s> prefixlen (%" PRIu32 ") for %s " + "on %s out of range", + __func__, val, addr, ifi->ifi_ifname); + goto getconfig_free_pfx; + } + pfx->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->pfx_onlinkflg = val & ND_OPT_PI_FLAG_ONLINK; + pfx->pfx_autoconfflg = val & ND_OPT_PI_FLAG_AUTO; - makeentry(entbuf, sizeof(entbuf), i, "pltime", added); - MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME); - if (val64 < 0 || val64 > 0xffffffff) { - syslog(LOG_ERR, - "<%s> pltime out of range", - __func__); - exit(1); - } - pfx->preflifetime = (u_int32_t)val64; - - makeentry(entbuf, sizeof(entbuf), i, "pltimedecr", - added); - if (agetflag(entbuf)) { - struct timeval now; - gettimeofday(&now, 0); - pfx->pltimeexpire = - now.tv_sec + pfx->preflifetime; - } + makeentry(entbuf, sizeof(entbuf), i, "vltime"); + MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); + if (val64 < 0 || val64 > 0xffffffff) { + syslog(LOG_ERR, "<%s> vltime (%" PRIu64 ") for " + "%s/%d on %s is out of range", + __func__, val64, + addr, pfx->pfx_prefixlen, ifi->ifi_ifname); + goto getconfig_free_pfx; + } + pfx->pfx_validlifetime = (uint32_t)val64; - makeentry(entbuf, sizeof(entbuf), i, "addr", added); - addr = (char *)agetstr(entbuf, &bp); - if (addr == NULL) { - syslog(LOG_ERR, - "<%s> need %s as an prefix for " - "interface %s", - __func__, entbuf, intface); - exit(1); - } - if (inet_pton(AF_INET6, addr, - &pfx->prefix) != 1) { - syslog(LOG_ERR, - "<%s> inet_pton failed for %s", - __func__, addr); - exit(1); - } - if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) { - syslog(LOG_ERR, - "<%s> multicast prefix(%s) must " - "not be advertised (IF=%s)", - __func__, addr, intface); - exit(1); - } - if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix)) - syslog(LOG_NOTICE, - "<%s> link-local prefix(%s) will be" - " advertised on %s", - __func__, addr, intface); + makeentry(entbuf, sizeof(entbuf), i, "vltimedecr"); + if (agetflag(entbuf)) { + struct timespec now; + + clock_gettime(CLOCK_MONOTONIC_FAST, &now); + pfx->pfx_vltimeexpire = + now.tv_sec + pfx->pfx_validlifetime; + } + + makeentry(entbuf, sizeof(entbuf), i, "pltime"); + MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME); + if (val64 < 0 || val64 > 0xffffffff) { + syslog(LOG_ERR, + "<%s> pltime (%" PRIu64 ") for %s/%d on %s " + "is out of range", + __func__, val64, + addr, pfx->pfx_prefixlen, ifi->ifi_ifname); + goto getconfig_free_pfx; } + pfx->pfx_preflifetime = (uint32_t)val64; + + makeentry(entbuf, sizeof(entbuf), i, "pltimedecr"); + if (agetflag(entbuf)) { + struct timespec now; + + clock_gettime(CLOCK_MONOTONIC_FAST, &now); + pfx->pfx_pltimeexpire = + now.tv_sec + pfx->pfx_preflifetime; + } + /* link into chain */ + TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next); + rai->rai_pfxs++; + continue; +getconfig_free_pfx: + free(pfx); } + if (rai->rai_advifprefix && rai->rai_pfxs == 0) + get_prefix(rai); - MAYHAVE(val, "mtu", 0); - if (val < 0 || val > 0xffffffff) { + MAYHAVE(val64, "mtu", 0); + if (val < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, - "<%s> mtu out of range", __func__); - exit(1); + "<%s> mtu (%" PRIu64 ") on %s out of range", + __func__, val64, ifi->ifi_ifname); + goto getconfig_free_rai; } - tmp->linkmtu = (u_int32_t)val; - if (tmp->linkmtu == 0) { + rai->rai_linkmtu = (uint32_t)val64; + if (rai->rai_linkmtu == 0) { char *mtustr; if ((mtustr = (char *)agetstr("mtu", &bp)) && strcmp(mtustr, "auto") == 0) - tmp->linkmtu = tmp->phymtu; + rai->rai_linkmtu = ifi->ifi_phymtu; } - else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) { + else if (rai->rai_linkmtu < IPV6_MMTU || + rai->rai_linkmtu > ifi->ifi_phymtu) { syslog(LOG_ERR, - "<%s> advertised link mtu must be between" - " least MTU and physical link MTU", - __func__); - exit(1); + "<%s> advertised link mtu (%" PRIu32 ") on %s is invalid (must " + "be between least MTU (%d) and physical link MTU (%d)", + __func__, rai->rai_linkmtu, ifi->ifi_ifname, + IPV6_MMTU, ifi->ifi_phymtu); + goto getconfig_free_rai; } - /* route information */ +#ifdef SIOCSIFINFO_IN6 + { + struct in6_ndireq ndi; + int s; - MAYHAVE(val, "routes", 0); - if (val < 0 || val > 0xffffffff) { - syslog(LOG_ERR, - "<%s> number of route information improper", __func__); - exit(1); - } - tmp->routes = val; - for (i = 0; i < tmp->routes; i++) { - struct rtinfo *rti; - char entbuf[256]; - int added = (tmp->routes > 1) ? 1 : 0; - - /* allocate memory to store prefix information */ - if ((rti = malloc(sizeof(struct rtinfo))) == NULL) { - syslog(LOG_ERR, - "<%s> can't allocate enough memory", - __func__); + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "<%s> socket: %s", __func__, + strerror(errno)); exit(1); } - memset(rti, 0, sizeof(*rti)); - - /* link into chain */ - insque(rti, &tmp->route); + memset(&ndi, 0, sizeof(ndi)); + strncpy(ndi.ifname, ifi->ifi_ifname, sizeof(ndi.ifname)); + if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&ndi) < 0) + syslog(LOG_INFO, "<%s> ioctl:SIOCGIFINFO_IN6 at %s: %s", + __func__, ifi->ifi_ifname, strerror(errno)); + + /* reflect the RA info to the host variables in kernel */ + ndi.ndi.chlim = rai->rai_hoplimit; + ndi.ndi.retrans = rai->rai_retranstimer; + ndi.ndi.basereachable = rai->rai_reachabletime; + if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&ndi) < 0) + syslog(LOG_INFO, "<%s> ioctl:SIOCSIFINFO_IN6 at %s: %s", + __func__, ifi->ifi_ifname, strerror(errno)); - makeentry(entbuf, sizeof(entbuf), i, "rtrplen", added); - MAYHAVE(val, entbuf, 64); - if (val < 0 || val > 128) { - syslog(LOG_ERR, - "<%s> prefixlen out of range", - __func__); - exit(1); - } - rti->prefixlen = (int)val; - - makeentry(entbuf, sizeof(entbuf), i, "rtrflags", added); - MAYHAVE(val, entbuf, 0); - rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK; - if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) { - syslog(LOG_ERR, "<%s> invalid route preference", - __func__); - exit(1); - } + close(s); + } +#endif - makeentry(entbuf, sizeof(entbuf), i, "rtrltime", added); - /* - * XXX: since default value of route lifetime is not defined in - * draft-draves-route-selection-01.txt, I took the default - * value of valid lifetime of prefix as its default. - * It need be much considered. - */ - MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); - if (val64 < 0 || val64 > 0xffffffff) { - syslog(LOG_ERR, - "<%s> rtrltime out of range", - __func__); - exit(1); - } - rti->ltime = (u_int32_t)val64; + /* route information */ + rai->rai_routes = 0; + for (i = -1; i < MAXROUTE; i++) { + struct rtinfo *rti; - makeentry(entbuf, sizeof(entbuf), i, "rtrprefix", added); + makeentry(entbuf, sizeof(entbuf), i, "rtprefix"); addr = (char *)agetstr(entbuf, &bp); if (addr == NULL) { - syslog(LOG_ERR, - "<%s> need %s as an route for " - "interface %s", - __func__, entbuf, intface); - exit(1); + makeentry(oentbuf, sizeof(oentbuf), i, "rtrprefix"); + addr = (char *)agetstr(oentbuf, &bp); + if (addr) + fprintf(stderr, "%s was obsoleted. Use %s.\n", + oentbuf, entbuf); } - if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) { - syslog(LOG_ERR, - "<%s> inet_pton failed for %s", - __func__, addr); - exit(1); + if (addr == NULL) + continue; + + /* allocate memory to store prefix information */ + ELM_MALLOC(rti, exit(1)); + + if (inet_pton(AF_INET6, addr, &rti->rti_prefix) != 1) { + syslog(LOG_ERR, "<%s> inet_pton failed for %s", + __func__, addr); + goto getconfig_free_rti; } #if 0 /* * XXX: currently there's no restriction in route information - * prefix according to draft-draves-route-selection-01.txt, - * however I think the similar restriction be necessary. + * prefix according to + * draft-ietf-ipngwg-router-selection-00.txt. + * However, I think the similar restriction be necessary. */ MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) { syslog(LOG_ERR, - "<%s> multicast route (%s) must " - "not be advertised (IF=%s)", - __func__, addr, intface); - exit(1); + "<%s> multicast route (%s) must " + "not be advertised on %s", + __func__, addr, ifi->ifi_ifname); + goto getconfig_free_rti; } if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) { syslog(LOG_NOTICE, - "<%s> link-local route (%s) must " - "not be advertised on %s", - __func__, addr, intface); - exit(1); + "<%s> link-local route (%s) will " + "be advertised on %s", + __func__, addr, ifi->ifi_ifname); + goto getconfig_free_rti; } #endif + + makeentry(entbuf, sizeof(entbuf), i, "rtplen"); + /* XXX: 256 is a magic number for compatibility check. */ + MAYHAVE(val, entbuf, 256); + if (val == 256) { + makeentry(oentbuf, sizeof(oentbuf), i, "rtrplen"); + MAYHAVE(val, oentbuf, 256); + if (val != 256) + fprintf(stderr, "%s was obsoleted. Use %s.\n", + oentbuf, entbuf); + else + val = 64; + } + if (val < 0 || val > 128) { + syslog(LOG_ERR, "<%s> prefixlen (%" PRIu32 ") for %s on %s " + "out of range", + __func__, val, addr, ifi->ifi_ifname); + goto getconfig_free_rti; + } + rti->rti_prefixlen = (int)val; + + makeentry(entbuf, sizeof(entbuf), i, "rtflags"); + if ((flagstr = (char *)agetstr(entbuf, &bp))) { + val = 0; + if (strchr(flagstr, 'h')) + val |= ND_RA_FLAG_RTPREF_HIGH; + if (strchr(flagstr, 'l')) { + if ((val & ND_RA_FLAG_RTPREF_HIGH)) { + syslog(LOG_ERR, + "<%s> the \'h\' and \'l\' route" + " preferences are exclusive", + __func__); + goto getconfig_free_rti; + } + 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->rti_rtpref = val & ND_RA_FLAG_RTPREF_MASK; + if (rti->rti_rtpref == ND_RA_FLAG_RTPREF_RSV) { + syslog(LOG_ERR, "<%s> invalid route preference (%02x) " + "for %s/%d on %s", + __func__, rti->rti_rtpref, addr, + rti->rti_prefixlen, ifi->ifi_ifname); + goto getconfig_free_rti; + } + + /* + * 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, + ifi->ifi_ifname); + val64 = rai->rai_lifetime; + } + } + if (val64 < 0 || val64 > 0xffffffff) { + syslog(LOG_ERR, "<%s> route lifetime (%" PRIu64 ") for " + "%s/%d on %s out of range", __func__, + val64, addr, rti->rti_prefixlen, + ifi->ifi_ifname); + goto getconfig_free_rti; + } + rti->rti_ltime = (uint32_t)val64; + + /* link into chain */ + TAILQ_INSERT_TAIL(&rai->rai_route, rti, rti_next); + rai->rai_routes++; + continue; +getconfig_free_rti: + free(rti); + } + + /* DNS server and DNS search list information */ + for (i = -1; i < MAXRDNSSENT ; i++) { + struct rdnss *rdn; + struct rdnss_addr *rdna; + char *ap; + int c; + + makeentry(entbuf, sizeof(entbuf), i, "rdnss"); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) + break; + ELM_MALLOC(rdn, exit(1)); + + TAILQ_INIT(&rdn->rd_list); + + for (ap = addr; ap - addr < (ssize_t)strlen(addr); ap += c+1) { + c = strcspn(ap, ","); + strncpy(abuf, ap, c); + abuf[c] = '\0'; + ELM_MALLOC(rdna, goto getconfig_free_rdn); + if (inet_pton(AF_INET6, abuf, &rdna->ra_dns) != 1) { + syslog(LOG_ERR, "<%s> inet_pton failed for %s", + __func__, abuf); + free(rdna); + goto getconfig_free_rdn; + } + TAILQ_INSERT_TAIL(&rdn->rd_list, rdna, ra_next); + } + + makeentry(entbuf, sizeof(entbuf), i, "rdnssltime"); + MAYHAVE(val, entbuf, (rai->rai_maxinterval * 3 / 2)); + if ((uint16_t)val < rai->rai_maxinterval || + (uint16_t)val > rai->rai_maxinterval * 2) { + syslog(LOG_ERR, "%s (%" PRIu16 ") on %s is invalid " + "(must be between %d and %d)", + entbuf, val, ifi->ifi_ifname, rai->rai_maxinterval, + rai->rai_maxinterval * 2); + goto getconfig_free_rdn; + } + rdn->rd_ltime = val; + + /* link into chain */ + TAILQ_INSERT_TAIL(&rai->rai_rdnss, rdn, rd_next); + continue; +getconfig_free_rdn: + while ((rdna = TAILQ_FIRST(&rdn->rd_list)) != NULL) { + TAILQ_REMOVE(&rdn->rd_list, rdna, ra_next); + free(rdna); + } + free(rdn); } - /* okey */ - tmp->next = ralist; - ralist = tmp; + for (i = -1; i < MAXDNSSLENT ; i++) { + struct dnssl *dns; + struct dnssl_addr *dnsa; + char *ap; + int c; + + makeentry(entbuf, sizeof(entbuf), i, "dnssl"); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) + break; + + ELM_MALLOC(dns, exit(1)); + + TAILQ_INIT(&dns->dn_list); + + for (ap = addr; ap - addr < (ssize_t)strlen(addr); ap += c+1) { + c = strcspn(ap, ","); + strncpy(abuf, ap, c); + abuf[c] = '\0'; + ELM_MALLOC(dnsa, goto getconfig_free_dns); + dnsa->da_len = dname_labelenc(dnsa->da_dom, abuf); + syslog(LOG_DEBUG, "<%s>: dnsa->da_len = %d", __func__, + dnsa->da_len); + TAILQ_INSERT_TAIL(&dns->dn_list, dnsa, da_next); + } + + makeentry(entbuf, sizeof(entbuf), i, "dnsslltime"); + MAYHAVE(val, entbuf, (rai->rai_maxinterval * 3 / 2)); + if ((uint16_t)val < rai->rai_maxinterval || + (uint16_t)val > rai->rai_maxinterval * 2) { + syslog(LOG_ERR, "%s (%" PRIu16 ") on %s is invalid " + "(must be between %d and %d)", + entbuf, val, ifi->ifi_ifname, rai->rai_maxinterval, + rai->rai_maxinterval * 2); + goto getconfig_free_dns; + } + dns->dn_ltime = val; + /* link into chain */ + TAILQ_INSERT_TAIL(&rai->rai_dnssl, dns, dn_next); + continue; +getconfig_free_dns: + while ((dnsa = TAILQ_FIRST(&dns->dn_list)) != NULL) { + TAILQ_REMOVE(&dns->dn_list, dnsa, da_next); + free(dnsa); + } + free(dns); + } /* construct the sending packet */ - make_packet(tmp); + make_packet(rai); + + /* + * If an entry with the same ifindex exists, remove it first. + * Before the removal, RDNSS and DNSSL options with + * zero-lifetime will be sent. + */ + switch (ifi->ifi_state) { + case IFI_STATE_UNCONFIGURED: + /* UNCONFIGURED -> TRANSITIVE */ - /* set timer */ - tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update, - tmp, tmp); - ra_timer_update((void *)tmp, &tmp->timer->tm); - rtadvd_set_timer(&tmp->timer->tm, tmp->timer); + error = sock_mc_join(&sock, ifi->ifi_ifindex); + if (error) + exit(1); + + ifi->ifi_state = IFI_STATE_TRANSITIVE; + ifi->ifi_burstcount = MAX_INITIAL_RTR_ADVERTISEMENTS; + ifi->ifi_burstinterval = MAX_INITIAL_RTR_ADVERT_INTERVAL; + + /* The same two rai mean initial burst */ + ifi->ifi_rainfo = rai; + ifi->ifi_rainfo_trans = rai; + TAILQ_INSERT_TAIL(&railist, rai, rai_next); + + if (ifi->ifi_ra_timer == NULL) + ifi->ifi_ra_timer = rtadvd_add_timer(ra_timeout, + ra_timer_update, ifi, ifi); + ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm); + rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm, + ifi->ifi_ra_timer); + + syslog(LOG_DEBUG, + "<%s> ifname=%s marked as TRANSITIVE (initial burst).", + __func__, ifi->ifi_ifname); + break; + case IFI_STATE_CONFIGURED: + /* CONFIGURED -> TRANSITIVE */ + rai_old = ifi->ifi_rainfo; + if (rai_old == NULL) { + syslog(LOG_ERR, + "<%s> ifi_rainfo is NULL" + " in IFI_STATE_CONFIGURED.", __func__); + ifi = NULL; + break; + } else { + struct rdnss *rdn; + struct dnssl *dns; + + rai_old->rai_lifetime = 0; + TAILQ_FOREACH(rdn, &rai_old->rai_rdnss, rd_next) + rdn->rd_ltime = 0; + TAILQ_FOREACH(dns, &rai_old->rai_dnssl, dn_next) + dns->dn_ltime = 0; + + ifi->ifi_rainfo_trans = rai_old; + ifi->ifi_state = IFI_STATE_TRANSITIVE; + ifi->ifi_burstcount = MAX_FINAL_RTR_ADVERTISEMENTS; + ifi->ifi_burstinterval = MIN_DELAY_BETWEEN_RAS; + + ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm); + rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm, + ifi->ifi_ra_timer); + + syslog(LOG_DEBUG, + "<%s> ifname=%s marked as TRANSITIVE" + " (transitional burst)", + __func__, ifi->ifi_ifname); + } + ifi->ifi_rainfo = rai; + TAILQ_INSERT_TAIL(&railist, rai, rai_next); + break; + case IFI_STATE_TRANSITIVE: + if (ifi->ifi_rainfo != NULL) { + if (ifi->ifi_rainfo == ifi->ifi_rainfo_trans) { + /* Reinitialize initial burst */ + rm_rainfo(ifi->ifi_rainfo); + ifi->ifi_rainfo = rai; + ifi->ifi_rainfo_trans = rai; + ifi->ifi_burstcount = + MAX_INITIAL_RTR_ADVERTISEMENTS; + ifi->ifi_burstinterval = + MAX_INITIAL_RTR_ADVERT_INTERVAL; + } else { + /* Replace ifi_rainfo with the new one */ + rm_rainfo(ifi->ifi_rainfo); + ifi->ifi_rainfo = rai; + } + TAILQ_INSERT_TAIL(&railist, rai, rai_next); + + ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm); + rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm, + ifi->ifi_ra_timer); + } else { + /* XXX: NOTREACHED. Being shut down. */ + syslog(LOG_ERR, + "<%s> %s is shutting down. Skipped.", + __func__, ifi->ifi_ifname); + rm_rainfo(rai); + + return (NULL); + } + break; + } + + return (ifi); + +getconfig_free_rai: + free(rai); + return (NULL); } -static void +void get_prefix(struct rainfo *rai) { struct ifaddrs *ifap, *ifa; - struct prefix *pp; + struct prefix *pfx; struct in6_addr *a; - u_char *p, *ep, *m, *lim; - u_char ntopbuf[INET6_ADDRSTRLEN]; + struct ifinfo *ifi; + char *p, *ep, *m, *lim; + char ntopbuf[INET6_ADDRSTRLEN]; if (getifaddrs(&ifap) < 0) { syslog(LOG_ERR, - "<%s> can't get interface addresses", - __func__); + "<%s> can't get interface addresses", + __func__); exit(1); } + ifi = rai->rai_ifinfo; + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { int plen; - if (strcmp(ifa->ifa_name, rai->ifname) != 0) + if (strcmp(ifa->ifa_name, ifi->ifi_ifname) != 0) continue; if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -577,128 +1035,104 @@ get_prefix(struct rainfo *rai) 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; + m = (char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; + lim = (char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len; plen = prefixlen(m, lim); - if (plen < 0 || plen > 128) { + if (plen <= 0 || plen > 128) { syslog(LOG_ERR, "<%s> failed to get prefixlen " - "or prefix is invalid", - __func__); + "or prefix is invalid", + __func__); exit(1); } + if (plen == 128) /* XXX */ + continue; if (find_prefix(rai, a, plen)) { /* ignore a duplicated prefix. */ continue; } /* allocate memory to store prefix info. */ - if ((pp = malloc(sizeof(*pp))) == NULL) { - syslog(LOG_ERR, - "<%s> can't get allocate buffer for prefix", - __func__); - exit(1); - } - memset(pp, 0, sizeof(*pp)); + ELM_MALLOC(pfx, exit(1)); /* set prefix, sweep bits outside of prefixlen */ - pp->prefixlen = plen; - memcpy(&pp->prefix, a, sizeof(*a)); - p = (u_char *)&pp->prefix; - ep = (u_char *)(&pp->prefix + 1); - while (m < lim) + pfx->pfx_prefixlen = plen; + memcpy(&pfx->pfx_prefix, a, sizeof(*a)); + p = (char *)&pfx->pfx_prefix; + ep = (char *)(&pfx->pfx_prefix + 1); + while (m < lim && p < ep) *p++ &= *m++; while (p < ep) *p++ = 0x00; - - if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf, + if (!inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, sizeof(ntopbuf))) { syslog(LOG_ERR, "<%s> inet_ntop failed", __func__); exit(1); } syslog(LOG_DEBUG, - "<%s> add %s/%d to prefix list on %s", - __func__, ntopbuf, pp->prefixlen, rai->ifname); + "<%s> add %s/%d to prefix list on %s", + __func__, ntopbuf, pfx->pfx_prefixlen, ifi->ifi_ifname); /* set other fields with protocol defaults */ - pp->validlifetime = DEF_ADVVALIDLIFETIME; - pp->preflifetime = DEF_ADVPREFERREDLIFETIME; - pp->onlinkflg = 1; - pp->autoconfflg = 1; - pp->origin = PREFIX_FROM_KERNEL; + pfx->pfx_validlifetime = DEF_ADVVALIDLIFETIME; + pfx->pfx_preflifetime = DEF_ADVPREFERREDLIFETIME; + pfx->pfx_onlinkflg = 1; + pfx->pfx_autoconfflg = 1; + pfx->pfx_origin = PREFIX_FROM_KERNEL; + pfx->pfx_rainfo = rai; /* link into chain */ - insque(pp, &rai->prefix); + TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next); /* counter increment */ - rai->pfxs++; + rai->rai_pfxs++; } freeifaddrs(ifap); } static void -makeentry(char *buf, size_t len, int id, char *string, int add) +makeentry(char *buf, size_t len, int id, const char *string) { - char *ep = buf + len; - - strcpy(buf, string); - if (add) { - char *cp; - cp = (char *)index(buf, '\0'); - snprintf(cp, ep - cp, "%d", id); - } + 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 parameter of the prefix(e.g. lifetime) shoule be + * 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; - u_char ntopbuf[INET6_ADDRSTRLEN]; - - if ((prefix = malloc(sizeof(*prefix))) == NULL) { - syslog(LOG_ERR, "<%s> memory allocation failed", - __func__); - return; /* XXX: error or exit? */ - } - memset(prefix, 0, sizeof(*prefix)); - 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; - - insque(prefix, &rai->prefix); - prefix->rainfo = rai; + struct prefix *pfx; + struct ifinfo *ifi; + char ntopbuf[INET6_ADDRSTRLEN]; + + ifi = rai->rai_ifinfo; + ELM_MALLOC(pfx, return); + pfx->pfx_prefix = ipr->ipr_prefix.sin6_addr; + pfx->pfx_prefixlen = ipr->ipr_plen; + pfx->pfx_validlifetime = ipr->ipr_vltime; + pfx->pfx_preflifetime = ipr->ipr_pltime; + pfx->pfx_onlinkflg = ipr->ipr_raf_onlink; + pfx->pfx_autoconfflg = ipr->ipr_raf_auto; + pfx->pfx_origin = PREFIX_FROM_DYNAMIC; + pfx->pfx_rainfo = rai; + + TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next); syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s", - __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - ipr->ipr_plen, rai->ifname); - - /* free the previous packet */ - free(rai->ra_data); - rai->ra_data = NULL; - - /* reconstruct the packet */ - rai->pfxs++; - make_packet(rai); + __func__, + inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf, + sizeof(ntopbuf)), ipr->ipr_plen, ifi->ifi_ifname); - /* - * reset the timer so that the new prefix will be advertised quickly. - */ - rai->initcounter = 0; - ra_timer_update((void *)rai, &rai->timer->tm); - rtadvd_set_timer(&rai->timer->tm, rai->timer); + rai->rai_pfxs++; } /* @@ -707,30 +1141,37 @@ add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr) * The prefix must be in the list. */ void -delete_prefix(struct prefix *prefix) +delete_prefix(struct prefix *pfx) { - u_char ntopbuf[INET6_ADDRSTRLEN]; - struct rainfo *rai = prefix->rainfo; + struct rainfo *rai; + struct ifinfo *ifi; + char ntopbuf[INET6_ADDRSTRLEN]; - remque(prefix); + rai = pfx->pfx_rainfo; + ifi = rai->rai_ifinfo; + TAILQ_REMOVE(&rai->rai_prefix, pfx, pfx_next); syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s", - __func__, inet_ntop(AF_INET6, &prefix->prefix, - ntopbuf, INET6_ADDRSTRLEN), - prefix->prefixlen, rai->ifname); - if (prefix->timer) - rtadvd_remove_timer(&prefix->timer); - free(prefix); - rai->pfxs--; + __func__, + inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, + sizeof(ntopbuf)), pfx->pfx_prefixlen, ifi->ifi_ifname); + if (pfx->pfx_timer) + rtadvd_remove_timer(pfx->pfx_timer); + free(pfx); + + rai->rai_pfxs--; } void -invalidate_prefix(struct prefix *prefix) +invalidate_prefix(struct prefix *pfx) { - u_char ntopbuf[INET6_ADDRSTRLEN]; - struct timeval timo; - struct rainfo *rai = prefix->rainfo; - - if (prefix->timer) { /* sanity check */ + struct timespec timo; + struct rainfo *rai; + struct ifinfo *ifi; + char ntopbuf[INET6_ADDRSTRLEN]; + + rai = pfx->pfx_rainfo; + ifi = rai->rai_ifinfo; + if (pfx->pfx_timer) { /* sanity check */ syslog(LOG_ERR, "<%s> assumption failure: timer already exists", __func__); @@ -739,38 +1180,40 @@ invalidate_prefix(struct prefix *prefix) syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, " "will expire in %ld seconds", __func__, - inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, INET6_ADDRSTRLEN), - prefix->prefixlen, rai->ifname, (long)prefix_timo); + inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, sizeof(ntopbuf)), + pfx->pfx_prefixlen, ifi->ifi_ifname, (long)prefix_timo); /* set the expiration timer */ - prefix->timer = rtadvd_add_timer(prefix_timeout, NULL, prefix, NULL); - if (prefix->timer == NULL) { + pfx->pfx_timer = rtadvd_add_timer(prefix_timeout, NULL, pfx, NULL); + if (pfx->pfx_timer == NULL) { syslog(LOG_ERR, "<%s> failed to add a timer for a prefix. " "remove the prefix", __func__); - delete_prefix(prefix); + delete_prefix(pfx); } timo.tv_sec = prefix_timo; - timo.tv_usec = 0; - rtadvd_set_timer(&timo, prefix->timer); + timo.tv_nsec = 0; + rtadvd_set_timer(&timo, pfx->pfx_timer); } static struct rtadvd_timer * prefix_timeout(void *arg) { - struct prefix *prefix = (struct prefix *)arg; - - delete_prefix(prefix); - return(NULL); + delete_prefix((struct prefix *)arg); + + return (NULL); } void -update_prefix(struct prefix * prefix) +update_prefix(struct prefix *pfx) { - u_char ntopbuf[INET6_ADDRSTRLEN]; - struct rainfo *rai = prefix->rainfo; + struct rainfo *rai; + struct ifinfo *ifi; + char ntopbuf[INET6_ADDRSTRLEN]; - if (prefix->timer == NULL) { /* sanity check */ + rai = pfx->pfx_rainfo; + ifi = rai->rai_ifinfo; + if (pfx->pfx_timer == NULL) { /* sanity check */ syslog(LOG_ERR, "<%s> assumption failure: timer does not exist", __func__); @@ -778,11 +1221,12 @@ update_prefix(struct prefix * prefix) } syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s", - __func__, inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, - INET6_ADDRSTRLEN), prefix->prefixlen, rai->ifname); + __func__, inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, + sizeof(ntopbuf)), pfx->pfx_prefixlen, ifi->ifi_ifname); /* stop the expiration timer */ - rtadvd_remove_timer(&prefix->timer); + rtadvd_remove_timer(pfx->pfx_timer); + pfx->pfx_timer = NULL; } /* @@ -798,13 +1242,13 @@ init_prefix(struct in6_prefixreq *ipr) if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "<%s> socket: %s", __func__, - strerror(errno)); + strerror(errno)); exit(1); } if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) { syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __func__, - strerror(errno)); + strerror(errno)); ipr->ipr_vltime = DEF_ADVVALIDLIFETIME; ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME; @@ -813,25 +1257,25 @@ init_prefix(struct in6_prefixreq *ipr) /* omit other field initialization */ } else if (ipr->ipr_origin < PR_ORIG_RR) { - u_char ntopbuf[INET6_ADDRSTRLEN]; + char ntopbuf[INET6_ADDRSTRLEN]; syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is" - "lower than PR_ORIG_RR(router renumbering)." - "This should not happen if I am router", __func__, - inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf, - sizeof(ntopbuf)), ipr->ipr_origin); + "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; + return (1); } close(s); - return 0; + 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; + return (0); #endif } @@ -842,9 +1286,9 @@ make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen) memset(&ipr, 0, sizeof(ipr)); if (if_indextoname(ifindex, ipr.ipr_name) == NULL) { - syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't" - "exist. This should not happen! %s", __func__, - ifindex, strerror(errno)); + syslog(LOG_ERR, "<%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); @@ -858,65 +1302,79 @@ make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen) } void -make_packet(struct rainfo *rainfo) +make_packet(struct rainfo *rai) { size_t packlen, lladdroptlen = 0; char *buf; struct nd_router_advert *ra; struct nd_opt_prefix_info *ndopt_pi; struct nd_opt_mtu *ndopt_mtu; -#ifdef MIP6 - struct nd_opt_advinterval *ndopt_advint; - struct nd_opt_homeagent_info *ndopt_hai; -#endif struct nd_opt_route_info *ndopt_rti; - struct prefix *pfx; struct rtinfo *rti; + struct nd_opt_rdnss *ndopt_rdnss; + struct rdnss *rdn; + struct nd_opt_dnssl *ndopt_dnssl; + struct dnssl *dns; + size_t len; + struct prefix *pfx; + struct ifinfo *ifi; + ifi = rai->rai_ifinfo; /* calculate total length */ packlen = sizeof(struct nd_router_advert); - if (rainfo->advlinkopt) { - if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) { + if (rai->rai_advlinkopt) { + if ((lladdroptlen = lladdropt_length(&ifi->ifi_sdl)) == 0) { syslog(LOG_INFO, - "<%s> link-layer address option has" - " null length on %s." - " Treat as not included.", - __func__, rainfo->ifname); - rainfo->advlinkopt = 0; + "<%s> link-layer address option has" + " null length on %s. Treat as not included.", + __func__, ifi->ifi_ifname); + rai->rai_advlinkopt = 0; } packlen += lladdroptlen; } - if (rainfo->pfxs) - packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs; - if (rainfo->linkmtu) + if (rai->rai_pfxs) + packlen += sizeof(struct nd_opt_prefix_info) * rai->rai_pfxs; + if (rai->rai_linkmtu) packlen += sizeof(struct nd_opt_mtu); -#ifdef MIP6 - if (mobileip6 && rainfo->maxinterval) - packlen += sizeof(struct nd_opt_advinterval); - if (mobileip6 && rainfo->hatime) - packlen += sizeof(struct nd_opt_homeagent_info); -#endif -#ifdef ND_OPT_ROUTE_INFO - for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) - packlen += sizeof(struct nd_opt_route_info) + - ((rti->prefixlen + 0x3f) >> 6) * 8; -#endif + TAILQ_FOREACH(rti, &rai->rai_route, rti_next) + packlen += sizeof(struct nd_opt_route_info) + + ((rti->rti_prefixlen + 0x3f) >> 6) * 8; + + TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) { + struct rdnss_addr *rdna; + + packlen += sizeof(struct nd_opt_rdnss); + TAILQ_FOREACH(rdna, &rdn->rd_list, ra_next) + packlen += sizeof(rdna->ra_dns); + } + TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) { + struct dnssl_addr *dnsa; + + packlen += sizeof(struct nd_opt_dnssl); + len = 0; + TAILQ_FOREACH(dnsa, &dns->dn_list, da_next) + len += dnsa->da_len; + + /* A zero octet and 8 octet boundary */ + len++; + len += (len % 8) ? 8 - len % 8 : 0; + + packlen += len; + } /* allocate memory for the packet */ if ((buf = malloc(packlen)) == NULL) { syslog(LOG_ERR, - "<%s> can't get enough memory for an RA packet", - __func__); + "<%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; + memset(buf, 0, packlen); + if (rai->rai_ra_data) /* Free old data if any. */ + free(rai->rai_ra_data); + rai->rai_ra_data = buf; /* XXX: what if packlen > 576? */ - rainfo->ra_datalen = packlen; + rai->rai_ra_datalen = packlen; /* * construct the packet @@ -925,104 +1383,70 @@ make_packet(struct rainfo *rainfo) 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_curhoplimit = (uint8_t)(0xff & rai->rai_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 = 0xff & rai->rai_rtpref; ra->nd_ra_flags_reserved |= - rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0; + rai->rai_managedflg ? ND_RA_FLAG_MANAGED : 0; ra->nd_ra_flags_reserved |= - rainfo->otherflg ? ND_RA_FLAG_OTHER : 0; -#ifdef MIP6 - ra->nd_ra_flags_reserved |= - rainfo->haflg ? ND_RA_FLAG_HA : 0; -#endif - ra->nd_ra_router_lifetime = htons(rainfo->lifetime); - ra->nd_ra_reachable = htonl(rainfo->reachabletime); - ra->nd_ra_retransmit = htonl(rainfo->retranstimer); + rai->rai_otherflg ? ND_RA_FLAG_OTHER : 0; + ra->nd_ra_router_lifetime = htons(rai->rai_lifetime); + ra->nd_ra_reachable = htonl(rai->rai_reachabletime); + ra->nd_ra_retransmit = htonl(rai->rai_retranstimer); buf += sizeof(*ra); - if (rainfo->advlinkopt) { - lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf); + if (rai->rai_advlinkopt) { + lladdropt_fill(&ifi->ifi_sdl, (struct nd_opt_hdr *)buf); buf += lladdroptlen; } - if (rainfo->linkmtu) { + if (rai->rai_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); + ndopt_mtu->nd_opt_mtu_mtu = htonl(rai->rai_linkmtu); buf += sizeof(struct nd_opt_mtu); } -#ifdef MIP6 - if (mobileip6 && rainfo->maxinterval) { - ndopt_advint = (struct nd_opt_advinterval *)buf; - ndopt_advint->nd_opt_adv_type = ND_OPT_ADVINTERVAL; - ndopt_advint->nd_opt_adv_len = 1; - ndopt_advint->nd_opt_adv_reserved = 0; - ndopt_advint->nd_opt_adv_interval = htonl(rainfo->maxinterval * - 1000); - buf += sizeof(struct nd_opt_advinterval); - } -#endif - -#ifdef MIP6 - if (rainfo->hatime) { - ndopt_hai = (struct nd_opt_homeagent_info *)buf; - ndopt_hai->nd_opt_hai_type = ND_OPT_HOMEAGENT_INFO; - ndopt_hai->nd_opt_hai_len = 1; - ndopt_hai->nd_opt_hai_reserved = 0; - ndopt_hai->nd_opt_hai_preference = htons(rainfo->hapref); - ndopt_hai->nd_opt_hai_lifetime = htons(rainfo->hatime); - buf += sizeof(struct nd_opt_homeagent_info); - } -#endif - - for (pfx = rainfo->prefix.next; - pfx != &rainfo->prefix; pfx = pfx->next) { - u_int32_t vltime, pltime; - struct timeval now; + TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { + uint32_t vltime, pltime; + struct timespec 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_prefix_len = pfx->pfx_prefixlen; ndopt_pi->nd_opt_pi_flags_reserved = 0; - if (pfx->onlinkflg) + if (pfx->pfx_onlinkflg) ndopt_pi->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_ONLINK; - if (pfx->autoconfflg) + if (pfx->pfx_autoconfflg) ndopt_pi->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO; -#ifdef MIP6 - if (pfx->routeraddr) - ndopt_pi->nd_opt_pi_flags_reserved |= - ND_OPT_PI_FLAG_ROUTER; -#endif - if (pfx->timer) + if (pfx->pfx_timer) vltime = 0; else { - if (pfx->vltimeexpire || pfx->pltimeexpire) - gettimeofday(&now, NULL); - if (pfx->vltimeexpire == 0) - vltime = pfx->validlifetime; + if (pfx->pfx_vltimeexpire || pfx->pfx_pltimeexpire) + clock_gettime(CLOCK_MONOTONIC_FAST, &now); + if (pfx->pfx_vltimeexpire == 0) + vltime = pfx->pfx_validlifetime; else - vltime = (pfx->vltimeexpire > now.tv_sec) ? - pfx->vltimeexpire - now.tv_sec : 0; + vltime = ((time_t)pfx->pfx_vltimeexpire > now.tv_sec) ? + pfx->pfx_vltimeexpire - now.tv_sec : 0; } - if (pfx->timer) + if (pfx->pfx_timer) pltime = 0; else { - if (pfx->pltimeexpire == 0) - pltime = pfx->preflifetime; + if (pfx->pfx_pltimeexpire == 0) + pltime = pfx->pfx_preflifetime; else - pltime = (pfx->pltimeexpire > now.tv_sec) ? - pfx->pltimeexpire - now.tv_sec : 0; + pltime = ((time_t)pfx->pfx_pltimeexpire > now.tv_sec) ? + pfx->pfx_pltimeexpire - now.tv_sec : 0; } if (vltime < pltime) { /* @@ -1034,45 +1458,72 @@ make_packet(struct rainfo *rainfo) 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; + ndopt_pi->nd_opt_pi_prefix = pfx->pfx_prefix; buf += sizeof(struct nd_opt_prefix_info); } -#ifdef ND_OPT_ROUTE_INFO - for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) { - u_int8_t psize = (rti->prefixlen + 0x3f) >> 6; + TAILQ_FOREACH(rti, &rai->rai_route, rti_next) { + uint8_t psize = (rti->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); + ndopt_rti->nd_opt_rti_prefixlen = rti->rti_prefixlen; + ndopt_rti->nd_opt_rti_flags = 0xff & rti->rti_rtpref; + ndopt_rti->nd_opt_rti_lifetime = htonl(rti->rti_ltime); + memcpy(ndopt_rti + 1, &rti->rti_prefix, psize * 8); buf += sizeof(struct nd_opt_route_info) + psize * 8; } -#endif - return; -} + TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) { + struct rdnss_addr *rdna; -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) { - syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s", - __func__, code, - strerror(errno)); - return(-1); + ndopt_rdnss = (struct nd_opt_rdnss *)buf; + ndopt_rdnss->nd_opt_rdnss_type = ND_OPT_RDNSS; + ndopt_rdnss->nd_opt_rdnss_len = 0; + ndopt_rdnss->nd_opt_rdnss_reserved = 0; + ndopt_rdnss->nd_opt_rdnss_lifetime = htonl(rdn->rd_ltime); + buf += sizeof(struct nd_opt_rdnss); + + TAILQ_FOREACH(rdna, &rdn->rd_list, ra_next) { + memcpy(buf, &rdna->ra_dns, sizeof(rdna->ra_dns)); + buf += sizeof(rdna->ra_dns); + } + /* Length field should be in 8 octets */ + ndopt_rdnss->nd_opt_rdnss_len = (buf - (char *)ndopt_rdnss) / 8; + + syslog(LOG_DEBUG, "<%s>: nd_opt_dnss_len = %d", __func__, + ndopt_rdnss->nd_opt_rdnss_len); } - else - return(value); + + TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) { + struct dnssl_addr *dnsa; + + ndopt_dnssl = (struct nd_opt_dnssl *)buf; + ndopt_dnssl->nd_opt_dnssl_type = ND_OPT_DNSSL; + ndopt_dnssl->nd_opt_dnssl_len = 0; + ndopt_dnssl->nd_opt_dnssl_reserved = 0; + ndopt_dnssl->nd_opt_dnssl_lifetime = htonl(dns->dn_ltime); + buf += sizeof(*ndopt_dnssl); + + TAILQ_FOREACH(dnsa, &dns->dn_list, da_next) { + memcpy(buf, dnsa->da_dom, dnsa->da_len); + buf += dnsa->da_len; + } + + /* A zero octet after encoded DNS server list. */ + *buf++ = '\0'; + + /* Padding to next 8 octets boundary */ + len = buf - (char *)ndopt_dnssl; + len += (len % 8) ? 8 - len % 8 : 0; + + /* Length field must be in 8 octets */ + ndopt_dnssl->nd_opt_dnssl_len = len / 8; + + syslog(LOG_DEBUG, "<%s>: nd_opt_dnssl_len = %d", __func__, + ndopt_dnssl->nd_opt_dnssl_len); + } + return; } diff --git a/usr.sbin/rtadvd/config.h b/usr.sbin/rtadvd/config.h index 9d03c88..be09cd5 100644 --- a/usr.sbin/rtadvd/config.h +++ b/usr.sbin/rtadvd/config.h @@ -1,9 +1,10 @@ -/* $KAME: config.h,v 1.3 2000/05/16 13:34:13 itojun Exp $ */ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/config.h 224144 2011-07-17 19:24:54Z hrs $ */ +/* $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: @@ -15,7 +16,7 @@ * 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 @@ -27,14 +28,26 @@ * 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. - * - * $FreeBSD: src/usr.sbin/rtadvd/config.h,v 1.1.2.3 2002/06/29 18:59:53 ume Exp $ - * $DragonFly: src/usr.sbin/rtadvd/config.h,v 1.3 2003/11/03 19:31:42 eirikn Exp $ */ -extern void getconfig(char *); +extern struct ifinfo *getconfig(struct ifinfo *); +extern int rm_ifinfo(struct ifinfo *); +extern int rm_ifinfo_index(int); +extern int rm_rainfo(struct rainfo *); +extern int loadconfig_ifname(char *); +extern int loadconfig_index(int); extern void delete_prefix(struct prefix *); extern void invalidate_prefix(struct prefix *); extern void update_prefix(struct prefix *); extern void make_prefix(struct rainfo *, int, struct in6_addr *, int); extern void make_packet(struct rainfo *); +extern void get_prefix(struct rainfo *); + +/* + * it is highly unlikely to have 100 prefix information options, + * so it should be okay to limit it + */ +#define MAXPREFIX 100 +#define MAXROUTE 100 +#define MAXRDNSSENT 100 +#define MAXDNSSLENT 100 diff --git a/usr.sbin/rtadvd/control.c b/usr.sbin/rtadvd/control.c new file mode 100644 index 0000000..43600ad --- /dev/null +++ b/usr.sbin/rtadvd/control.c @@ -0,0 +1,492 @@ +/*- + * 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. + * + * 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. + * + * $FreeBSD: stable/10/usr.sbin/rtadvd/control.c 225519 2011-09-12 23:52:55Z hrs $ + * + */ + +#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 "if.h" +#include "pathnames.h" +#include "control.h" + +#define CM_RECV_TIMEOUT 30 + +int +cm_recv(int fd, char *buf) +{ + int n; + struct ctrl_msg_hdr *cm; + char *msg; + struct pollfd pfds[1]; + int i; + + syslog(LOG_DEBUG, "<%s> enter, fd=%d", __func__, fd); + + memset(buf, 0, CM_MSG_MAXLEN); + cm = (struct ctrl_msg_hdr *)buf; + msg = (char *)buf + sizeof(*cm); + + pfds[0].fd = fd; + pfds[0].events = POLLIN; + + for (;;) { + i = poll(pfds, sizeof(pfds)/sizeof(pfds[0]), + CM_RECV_TIMEOUT); + + if (i == 0) + continue; + + if (i < 0) { + syslog(LOG_ERR, "<%s> poll error: %s", + __func__, strerror(errno)); + continue; + } + + if (pfds[0].revents & POLLIN) { + n = read(fd, cm, sizeof(*cm)); + if (n < 0 && errno == EAGAIN) { + syslog(LOG_DEBUG, + "<%s> waiting...", __func__); + continue; + } + break; + } + } + + if (n != sizeof(*cm)) { + syslog(LOG_WARNING, + "<%s> received a too small message.", __func__); + goto cm_recv_err; + } + if (cm->cm_len > CM_MSG_MAXLEN) { + syslog(LOG_WARNING, + "<%s> received a too large message.", __func__); + goto cm_recv_err; + } + if (cm->cm_version != CM_VERSION) { + syslog(LOG_WARNING, + "<%s> version mismatch", __func__); + goto cm_recv_err; + } + if (cm->cm_type >= CM_TYPE_MAX) { + syslog(LOG_WARNING, + "<%s> invalid msg type.", __func__); + goto cm_recv_err; + } + + syslog(LOG_DEBUG, + "<%s> ctrl msg received: type=%d", __func__, + cm->cm_type); + + if (cm->cm_len > sizeof(cm)) { + int msglen = cm->cm_len - sizeof(*cm); + + syslog(LOG_DEBUG, + "<%s> ctrl msg has payload (len=%d)", __func__, + msglen); + + for (;;) { + i = poll(pfds, sizeof(pfds)/sizeof(pfds[0]), + CM_RECV_TIMEOUT); + + if (i == 0) + continue; + + if (i < 0) { + syslog(LOG_ERR, "<%s> poll error: %s", + __func__, strerror(errno)); + continue; + } + + if (pfds[0].revents & POLLIN) { + n = read(fd, msg, msglen); + if (n < 0 && errno == EAGAIN) { + syslog(LOG_DEBUG, + "<%s> waiting...", __func__); + continue; + } + } + break; + } + if (n != msglen) { + syslog(LOG_WARNING, + "<%s> payload size mismatch.", __func__); + goto cm_recv_err; + } + buf[CM_MSG_MAXLEN - 1] = '\0'; + } + + return (0); + +cm_recv_err: + close(fd); + return (-1); +} + +int +cm_send(int fd, char *buf) +{ + struct iovec iov[2]; + int iovcnt; + ssize_t len; + ssize_t iov_len_total; + struct ctrl_msg_hdr *cm; + char *msg; + + cm = (struct ctrl_msg_hdr *)buf; + msg = (char *)buf + sizeof(*cm); + + iovcnt = 1; + iov[0].iov_base = cm; + iov[0].iov_len = sizeof(*cm); + iov_len_total = iov[0].iov_len; + if (cm->cm_len > sizeof(*cm)) { + iovcnt++; + iov[1].iov_base = msg; + iov[1].iov_len = cm->cm_len - iov[0].iov_len; + iov_len_total += iov[1].iov_len; + } + + syslog(LOG_DEBUG, + "<%s> ctrl msg send: type=%d, count=%d, total_len=%zd", __func__, + cm->cm_type, iovcnt, iov_len_total); + + len = writev(fd, iov, iovcnt); + syslog(LOG_DEBUG, + "<%s> ctrl msg send: length=%zd", __func__, len); + + if (len == -1) { + syslog(LOG_DEBUG, + "<%s> write failed: (%d)%s", __func__, errno, + strerror(errno)); + close(fd); + return (-1); + } + + syslog(LOG_DEBUG, + "<%s> write length = %zd (actual)", __func__, len); + syslog(LOG_DEBUG, + "<%s> write length = %zd (expected)", __func__, iov_len_total); + + if (len != iov_len_total) { + close(fd); + return (-1); + } + + return (0); +} + +int +csock_accept(struct sockinfo *s) +{ + struct sockaddr_un sun; + int flags; + int fd; + + sun.sun_len = sizeof(sun); + if ((fd = accept(s->si_fd, (struct sockaddr *)&sun, + (socklen_t *)&sun.sun_len)) == -1) { + if (errno != EWOULDBLOCK && errno != EINTR) + syslog(LOG_WARNING, "<%s> accept ", __func__); + syslog(LOG_WARNING, "<%s> Xaccept: %s", __func__, strerror(errno)); + return (-1); + } + if ((flags = fcntl(fd, F_GETFL, 0)) == -1) { + syslog(LOG_WARNING, "<%s> fcntl F_GETFL", __func__); + close(s->si_fd); + return (-1); + } + if ((flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1) { + syslog(LOG_WARNING, "<%s> fcntl F_SETFL", __func__); + return (-1); + } + syslog(LOG_DEBUG, "<%s> accept connfd=%d, listenfd=%d", __func__, + fd, s->si_fd); + + return (fd); +} + +int +csock_close(struct sockinfo *s) +{ + close(s->si_fd); + unlink(s->si_name); + syslog(LOG_DEBUG, "<%s> remove %s", __func__, s->si_name); + return (0); +} + +int +csock_listen(struct sockinfo *s) +{ + if (s->si_fd == -1) { + syslog(LOG_ERR, "<%s> listen failed", __func__); + return (-1); + } + if (listen(s->si_fd, SOCK_BACKLOG) == -1) { + syslog(LOG_ERR, "<%s> listen failed", __func__); + return (-1); + } + + return (0); +} + +int +csock_open(struct sockinfo *s, mode_t mode) +{ + int flags; + struct sockaddr_un sun; + mode_t old_umask; + + if (s == NULL) { + syslog(LOG_ERR, "<%s> internal error.", __func__); + exit(1); + } + if (s->si_name == NULL) + s->si_name = _PATH_CTRL_SOCK; + + if ((s->si_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { + syslog(LOG_ERR, + "<%s> cannot open control socket", __func__); + return (-1); + } + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + sun.sun_len = sizeof(sun); + strlcpy(sun.sun_path, s->si_name, sizeof(sun.sun_path)); + + if (unlink(s->si_name) == -1) + if (errno != ENOENT) { + syslog(LOG_ERR, + "<%s> unlink %s", __func__, s->si_name); + close(s->si_fd); + return (-1); + } + old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH); + if (bind(s->si_fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { + syslog(LOG_ERR, + "<%s> bind failed: %s", __func__, s->si_name); + close(s->si_fd); + umask(old_umask); + return (-1); + } + umask(old_umask); + if (chmod(s->si_name, mode) == -1) { + syslog(LOG_ERR, + "<%s> chmod failed: %s", __func__, s->si_name); + goto csock_open_err; + } + if ((flags = fcntl(s->si_fd, F_GETFL, 0)) == -1) { + syslog(LOG_ERR, + "<%s> fcntl F_GETFL failed: %s", __func__, s->si_name); + goto csock_open_err; + } + if ((flags = fcntl(s->si_fd, F_SETFL, flags | O_NONBLOCK)) == -1) { + syslog(LOG_ERR, + "<%s> fcntl F_SETFL failed: %s", __func__, s->si_name); + goto csock_open_err; + } + + return (s->si_fd); + +csock_open_err: + close(s->si_fd); + unlink(s->si_name); + return (-1); +} + +struct ctrl_msg_pl * +cm_bin2pl(char *str, struct ctrl_msg_pl *cp) +{ + size_t len; + size_t *lenp; + char *p; + + memset(cp, 0, sizeof(*cp)); + + p = str; + + lenp = (size_t *)p; + len = *lenp++; + p = (char *)lenp; + syslog(LOG_DEBUG, "<%s> len(ifname) = %zu", __func__, len); + if (len > 0) { + cp->cp_ifname = malloc(len + 1); + if (cp->cp_ifname == NULL) { + syslog(LOG_ERR, "<%s> malloc", __func__); + exit(1); + } + memcpy(cp->cp_ifname, p, len); + cp->cp_ifname[len] = '\0'; + p += len; + } + + lenp = (size_t *)p; + len = *lenp++; + p = (char *)lenp; + syslog(LOG_DEBUG, "<%s> len(key) = %zu", __func__, len); + if (len > 0) { + cp->cp_key = malloc(len + 1); + if (cp->cp_key == NULL) { + syslog(LOG_ERR, "<%s> malloc", __func__); + exit(1); + } + memcpy(cp->cp_key, p, len); + cp->cp_key[len] = '\0'; + p += len; + } + + lenp = (size_t *)p; + len = *lenp++; + p = (char *)lenp; + syslog(LOG_DEBUG, "<%s> len(val) = %zu", __func__, len); + if (len > 0) { + cp->cp_val = malloc(len + 1); + if (cp->cp_val == NULL) { + syslog(LOG_ERR, "<%s> malloc", __func__); + exit(1); + } + memcpy(cp->cp_val, p, len); + cp->cp_val[len] = '\0'; + cp->cp_val_len = len; + } else + cp->cp_val_len = 0; + + return (cp); +} + +size_t +cm_pl2bin(char *str, struct ctrl_msg_pl *cp) +{ + size_t len; + size_t *lenp; + char *p; + struct ctrl_msg_hdr *cm; + + len = sizeof(size_t); + if (cp->cp_ifname != NULL) + len += strlen(cp->cp_ifname); + len += sizeof(size_t); + if (cp->cp_key != NULL) + len += strlen(cp->cp_key); + len += sizeof(size_t); + if (cp->cp_val != NULL && cp->cp_val_len > 0) + len += cp->cp_val_len; + + if (len > CM_MSG_MAXLEN - sizeof(*cm)) { + syslog(LOG_DEBUG, "<%s> msg too long (len=%zu)", + __func__, len); + return (0); + } + syslog(LOG_DEBUG, "<%s> msglen=%zu", __func__, len); + memset(str, 0, len); + p = str; + lenp = (size_t *)p; + + if (cp->cp_ifname != NULL) { + *lenp++ = strlen(cp->cp_ifname); + p = (char *)lenp; + memcpy(p, cp->cp_ifname, strlen(cp->cp_ifname)); + p += strlen(cp->cp_ifname); + } else { + *lenp++ = '\0'; + p = (char *)lenp; + } + + lenp = (size_t *)p; + if (cp->cp_key != NULL) { + *lenp++ = strlen(cp->cp_key); + p = (char *)lenp; + memcpy(p, cp->cp_key, strlen(cp->cp_key)); + p += strlen(cp->cp_key); + } else { + *lenp++ = '\0'; + p = (char *)lenp; + } + + lenp = (size_t *)p; + if (cp->cp_val != NULL && cp->cp_val_len > 0) { + *lenp++ = cp->cp_val_len; + p = (char *)lenp; + memcpy(p, cp->cp_val, cp->cp_val_len); + p += cp->cp_val_len; + } else { + *lenp++ = '\0'; + p = (char *)lenp; + } + + return (len); +} + +size_t +cm_str2bin(char *bin, void *str, size_t len) +{ + struct ctrl_msg_hdr *cm; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (len > CM_MSG_MAXLEN - sizeof(*cm)) { + syslog(LOG_DEBUG, "<%s> msg too long (len=%zu)", + __func__, len); + return (0); + } + syslog(LOG_DEBUG, "<%s> msglen=%zu", __func__, len); + memcpy(bin, (char *)str, len); + + return (len); +} + +void * +cm_bin2str(char *bin, void *str, size_t len) +{ + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + memcpy((char *)str, bin, len); + + return (str); +} diff --git a/usr.sbin/rtadvd/control.h b/usr.sbin/rtadvd/control.h new file mode 100644 index 0000000..63653c2 --- /dev/null +++ b/usr.sbin/rtadvd/control.h @@ -0,0 +1,74 @@ +/*- + * 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. + * + * 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. + * + * $FreeBSD: stable/10/usr.sbin/rtadvd/control.h 225519 2011-09-12 23:52:55Z hrs $ + * + */ + +#define SOCK_BACKLOG 5 + +#define CM_MSG_MAXLEN 8192 +#define CM_VERSION 1 +#define CM_VERSION_STR "1.0" + +#define CM_TYPE_EOM 0 +#define CM_TYPE_ACK 1 +#define CM_TYPE_ERR 2 +#define CM_TYPE_NUL 3 +#define CM_TYPE_REQ_SET_PROP 4 +#define CM_TYPE_REQ_GET_PROP 5 +#define CM_TYPE_MAX 6 + +#define CM_STATE_EOM 0 +#define CM_STATE_INIT 1 +#define CM_STATE_MSG_DISPATCH 2 +#define CM_STATE_MSG_RECV 3 +#define CM_STATE_ACK_WAIT 4 + +struct ctrl_msg_hdr { + int cm_version; + size_t cm_len; + int cm_type; +}; + +struct ctrl_msg_pl { + char *cp_ifname; + char *cp_key; + + size_t cp_val_len; + char *cp_val; +}; + +int csock_open(struct sockinfo *, mode_t); +int csock_close(struct sockinfo *); +int csock_listen(struct sockinfo *); +int csock_accept(struct sockinfo *); +int cm_send(int, char *); +int cm_recv(int, char *); + +size_t cm_pl2bin(char *, struct ctrl_msg_pl *); +struct ctrl_msg_pl *cm_bin2pl(char *, struct ctrl_msg_pl *); +size_t cm_str2bin(char *, void *, size_t); +void *cm_bin2str(char *, void *, size_t); diff --git a/usr.sbin/rtadvd/control_client.c b/usr.sbin/rtadvd/control_client.c new file mode 100644 index 0000000..d6e487b --- /dev/null +++ b/usr.sbin/rtadvd/control_client.c @@ -0,0 +1,131 @@ +/*- + * 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. + * + * 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. + * + * $FreeBSD: stable/10/usr.sbin/rtadvd/control_client.c 225519 2011-09-12 23:52:55Z hrs $ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" +#include "rtadvd.h" +#include "if.h" +#include "control.h" +#include "control_client.h" + +int +cm_handler_client(int fd, int state, char *buf_orig) +{ + char buf[CM_MSG_MAXLEN]; + struct ctrl_msg_hdr *cm; + struct ctrl_msg_hdr *cm_orig; + int error; + char *msg; + char *msg_orig; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + memset(buf, 0, sizeof(buf)); + cm = (struct ctrl_msg_hdr *)buf; + cm_orig = (struct ctrl_msg_hdr *)buf_orig; + msg = (char *)buf + sizeof(*cm); + msg_orig = (char *)buf_orig + sizeof(*cm_orig); + + if (cm_orig->cm_len > CM_MSG_MAXLEN) { + syslog(LOG_DEBUG, "<%s> msg too long", __func__); + close(fd); + return (-1); + } + cm->cm_type = cm_orig->cm_type; + if (cm_orig->cm_len > sizeof(*cm_orig)) { + memcpy(msg, msg_orig, cm_orig->cm_len - sizeof(*cm)); + cm->cm_len = cm_orig->cm_len; + } + while (state != CM_STATE_EOM) { + syslog(LOG_DEBUG, "<%s> state = %d", __func__, state); + + switch (state) { + case CM_STATE_INIT: + state = CM_STATE_EOM; + break; + case CM_STATE_MSG_DISPATCH: + cm->cm_version = CM_VERSION; + error = cm_send(fd, buf); + if (error) + syslog(LOG_WARNING, + "<%s> cm_send()", __func__); + state = CM_STATE_ACK_WAIT; + break; + case CM_STATE_ACK_WAIT: + error = cm_recv(fd, buf); + if (error) { + syslog(LOG_ERR, + "<%s> cm_recv()", __func__); + close(fd); + return (-1); + } + switch (cm->cm_type) { + case CM_TYPE_ACK: + syslog(LOG_DEBUG, + "<%s> CM_TYPE_ACK", __func__); + break; + case CM_TYPE_ERR: + syslog(LOG_DEBUG, + "<%s> CM_TYPE_ERR", __func__); + close(fd); + return (-1); + default: + syslog(LOG_DEBUG, + "<%s> unknown status", __func__); + close(fd); + return (-1); + } + memcpy(buf_orig, buf, cm->cm_len); + state = CM_STATE_EOM; + break; + } + } + close(fd); + return (0); +} diff --git a/usr.sbin/rtadvd/control_client.h b/usr.sbin/rtadvd/control_client.h new file mode 100644 index 0000000..22da031 --- /dev/null +++ b/usr.sbin/rtadvd/control_client.h @@ -0,0 +1,30 @@ +/* + * 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. + * + * 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. + * + * $FreeBSD: stable/10/usr.sbin/rtadvd/control_client.h 225519 2011-09-12 23:52:55Z hrs $ + * + */ + +int cm_handler_client(int, int, char *); diff --git a/usr.sbin/rtadvd/control_server.c b/usr.sbin/rtadvd/control_server.c new file mode 100644 index 0000000..361801e --- /dev/null +++ b/usr.sbin/rtadvd/control_server.c @@ -0,0 +1,752 @@ +/*- + * 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. + * + * 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. + * + * $FreeBSD: stable/10/usr.sbin/rtadvd/control_server.c 225519 2011-09-12 23:52:55Z hrs $ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" +#include "rtadvd.h" +#include "if.h" +#include "config.h" +#include "control.h" +#include "control_server.h" +#include "timer.h" + +static char *do_reload_ifname; +static int do_reload; +static int do_shutdown; + +void set_do_reload(int sig __unused) { do_reload = 1; } +void set_do_reload_ifname(char *ifname){ do_reload_ifname = ifname; } +void set_do_shutdown(int sig __unused) { do_shutdown = 1; } +void reset_do_reload(void) { do_reload = 0; do_reload_ifname = NULL; } +void reset_do_shutdown(void) { do_shutdown = 0; } +int is_do_reload(void) { return (do_reload); } +int is_do_shutdown(void) { return (do_shutdown); } +char *reload_ifname(void) { return (do_reload_ifname); } + +#define DEF_PL_HANDLER(key) { #key, cm_getprop_##key } + +static int cm_getprop_echo(struct ctrl_msg_pl *); +static int cm_getprop_version(struct ctrl_msg_pl *); +static int cm_getprop_ifilist(struct ctrl_msg_pl *); +static int cm_getprop_ifi(struct ctrl_msg_pl *); +static int cm_getprop_ifi_ra_timer(struct ctrl_msg_pl *); +static int cm_getprop_rai(struct ctrl_msg_pl *); +static int cm_getprop_pfx(struct ctrl_msg_pl *); +static int cm_getprop_rdnss(struct ctrl_msg_pl *); +static int cm_getprop_dnssl(struct ctrl_msg_pl *); +static int cm_getprop_rti(struct ctrl_msg_pl *); + +static int cm_setprop_reload(struct ctrl_msg_pl *); +static int cm_setprop_enable(struct ctrl_msg_pl *); +static int cm_setprop_disable(struct ctrl_msg_pl *); + +static struct dispatch_table { + const char *dt_comm; + int (*dt_act)(struct ctrl_msg_pl *cp); +} getprop_dtable[] = { + { "", cm_getprop_echo }, + DEF_PL_HANDLER(echo), + DEF_PL_HANDLER(version), + DEF_PL_HANDLER(ifilist), + DEF_PL_HANDLER(ifi), + DEF_PL_HANDLER(ifi_ra_timer), + DEF_PL_HANDLER(rai), + DEF_PL_HANDLER(rti), + DEF_PL_HANDLER(pfx), + DEF_PL_HANDLER(rdnss), + DEF_PL_HANDLER(dnssl), +}; + +static int +cm_getprop_echo(struct ctrl_msg_pl *cp) +{ + + syslog(LOG_DEBUG, "<%s> enter", __func__); + cp->cp_val = strdup(""); + cp->cp_val_len = strlen(cp->cp_val) + 1; + + return (0); +} + +static int +cm_getprop_version(struct ctrl_msg_pl *cp) +{ + + syslog(LOG_DEBUG, "<%s> enter", __func__); + cp->cp_val = strdup(CM_VERSION_STR); + cp->cp_val_len = strlen(cp->cp_val) + 1; + + return (0); +} + +static int +cm_getprop_ifilist(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + char *p; + size_t len; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + len = 0; + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + len += strlen(ifi->ifi_ifname) + 1; + } + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + p = malloc(len); + if (p == NULL) + exit(1); + memset(p, 0, len); + cp->cp_val = p; + + if (len > 0) + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + syslog(LOG_DEBUG, "<%s> add ifname=%s(%d)", + __func__, ifi->ifi_ifname, ifi->ifi_ifindex); + strcpy(p, ifi->ifi_ifname); + p += strlen(ifi->ifi_ifname) + 1; + } + cp->cp_val_len = p - cp->cp_val; + + return (0); +} + +static int +cm_getprop_ifi(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + char *p; + size_t len; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + + p = malloc(sizeof(*ifi)); + if (p == NULL) + exit(1); + len = cm_str2bin(p, ifi, sizeof(*ifi)); + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + if (len == 0) + return (1); + + cp->cp_val = p; + cp->cp_val_len = len; + + return (0); +} + +static int +cm_getprop_rai(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + struct rainfo *rai; + char *p; + size_t len; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + if ((rai = ifi->ifi_rainfo) == NULL) { + syslog(LOG_ERR, "<%s> %s has no rainfo", __func__, + cp->cp_ifname); + return (1); + } + + p = malloc(sizeof(*rai)); + if (p == NULL) + exit(1); + len = cm_str2bin(p, rai, sizeof(*rai)); + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + if (len == 0) + return (1); + + cp->cp_val = p; + cp->cp_val_len = len; + + return (0); +} + +static int +cm_getprop_ifi_ra_timer(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + struct rainfo *rai; + struct rtadvd_timer *rtimer; + char *p; + size_t len; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + if ((rai = ifi->ifi_rainfo) == NULL) { + syslog(LOG_ERR, "<%s> %s has no rainfo", __func__, + cp->cp_ifname); + return (1); + } + if ((rtimer = ifi->ifi_ra_timer) == NULL) { + syslog(LOG_ERR, "<%s> %s has no ifi_ra_timer", __func__, + cp->cp_ifname); + return (1); + } + p = malloc(sizeof(*rtimer)); + if (p == NULL) + exit(1); + len = cm_str2bin(p, rtimer, sizeof(*rtimer)); + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + if (len == 0) + return (1); + + cp->cp_val = p; + cp->cp_val_len = len; + + return (0); +} + +static int +cm_getprop_rti(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + struct rainfo *rai; + struct rtinfo *rti; + char *p; + size_t len; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + len = 0; + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + if (ifi->ifi_rainfo == NULL) { + syslog(LOG_ERR, "<%s> %s has no rainfo", __func__, + cp->cp_ifname); + return (1); + } + rai = ifi->ifi_rainfo; + TAILQ_FOREACH(rti, &rai->rai_route, rti_next) { + len += sizeof(*rti); + } + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + p = malloc(len); + if (p == NULL) + exit(1); + memset(p, 0, len); + cp->cp_val = p; + + if (len > 0) + TAILQ_FOREACH(rti, &rai->rai_route, rti_next) { + memcpy(p, rti, sizeof(*rti)); + p += sizeof(*rti); + } + cp->cp_val_len = p - cp->cp_val; + + return (0); +} + +static int +cm_getprop_pfx(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + struct rainfo *rai; + struct prefix *pfx; + char *p; + size_t len; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + len = 0; + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + if (ifi->ifi_rainfo == NULL) { + syslog(LOG_ERR, "<%s> %s has no rainfo", __func__, + cp->cp_ifname); + return (1); + } + rai = ifi->ifi_rainfo; + TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { + len += sizeof(*pfx); + } + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + p = malloc(len); + if (p == NULL) + exit(1); + memset(p, 0, len); + cp->cp_val = p; + + if (len > 0) + TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { + memcpy(p, pfx, sizeof(*pfx)); + p += sizeof(*pfx); + } + cp->cp_val_len = p - cp->cp_val; + + return (0); +} + +static int +cm_getprop_rdnss(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + struct rainfo *rai; + struct rdnss *rdn; + struct rdnss_addr *rda; + char *p; + size_t len; + uint16_t *rdn_cnt; + uint16_t *rda_cnt; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + len = 0; + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + if (ifi->ifi_rainfo == NULL) { + syslog(LOG_ERR, "<%s> %s has no rainfo", __func__, + cp->cp_ifname); + return (1); + } + rai = ifi->ifi_rainfo; + + len = sizeof(*rdn_cnt); + TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) { + len += sizeof(*rdn); + len += sizeof(*rda_cnt); + TAILQ_FOREACH(rda, &rdn->rd_list, ra_next) { + len += sizeof(*rda); + } + } + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + p = malloc(len); + if (p == NULL) + exit(1); + memset(p, 0, len); + cp->cp_val = p; + + rdn_cnt = (uint16_t *)p; + p += sizeof(*rdn_cnt); + TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) { + *rdn_cnt += 1; + memcpy(p, rdn, sizeof(*rdn)); + p += sizeof(*rdn); + + rda_cnt = (uint16_t *)p; + p += sizeof(*rda_cnt); + TAILQ_FOREACH(rda, &rdn->rd_list, ra_next) { + *rda_cnt += 1; + memcpy(p, rda, sizeof(*rda)); + p += sizeof(*rda); + } + } + syslog(LOG_DEBUG, "<%s> rdn_cnt = %d", __func__, *rdn_cnt); + cp->cp_val_len = p - cp->cp_val; + + return (0); +} + +static int +cm_getprop_dnssl(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + struct rainfo *rai; + struct dnssl *dns; + struct dnssl_addr *dna; + char *p; + size_t len; + uint16_t *dns_cnt; + uint16_t *dna_cnt; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + len = 0; + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + if (ifi->ifi_rainfo == NULL) { + syslog(LOG_ERR, "<%s> %s has no rainfo", __func__, + cp->cp_ifname); + return (1); + } + rai = ifi->ifi_rainfo; + + len = sizeof(*dns_cnt); + TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) { + len += sizeof(*dns); + len += sizeof(*dna_cnt); + TAILQ_FOREACH(dna, &dns->dn_list, da_next) { + len += sizeof(*dna); + } + } + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + p = malloc(len); + if (p == NULL) + exit(1); + memset(p, 0, len); + cp->cp_val = p; + + dns_cnt = (uint16_t *)cp->cp_val; + p += sizeof(*dns_cnt); + TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) { + (*dns_cnt)++; + memcpy(p, dns, sizeof(*dns)); + p += sizeof(*dns); + + dna_cnt = (uint16_t *)p; + p += sizeof(*dna_cnt); + TAILQ_FOREACH(dna, &dns->dn_list, da_next) { + (*dna_cnt)++; + memcpy(p, dna, sizeof(*dna)); + p += sizeof(*dna); + } + } + cp->cp_val_len = p - cp->cp_val; + + return (0); +} + +int +cm_getprop(struct ctrl_msg_pl *cp) +{ + size_t i; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (cp == NULL) + return (1); + + for (i = 0; + i < sizeof(getprop_dtable) / sizeof(getprop_dtable[0]); + i++) { + if (strcmp(cp->cp_key, getprop_dtable[i].dt_comm) == 0) + return (getprop_dtable[i].dt_act(cp)); + } + return (1); +} + +int +cm_setprop(struct ctrl_msg_pl *cp) +{ + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (cp == NULL || cp->cp_key == NULL) + return (1); + + if (strncmp(cp->cp_key, "reload", sizeof("reload")) == 0) + cm_setprop_reload(cp); + else if (strncmp(cp->cp_key, "shutdown", sizeof("shutdown")) == 0) + set_do_shutdown(0); + else if (strncmp(cp->cp_key, "enable", sizeof("enable")) == 0) + cm_setprop_enable(cp); + else if (strncmp(cp->cp_key, "disable", sizeof("disable")) == 0) + cm_setprop_disable(cp); + else if (strncmp(cp->cp_key, "echo", 8) == 0) + ; /* do nothing */ + else + return (1); + + return (0); +} + +static int +cm_setprop_reload(struct ctrl_msg_pl *cp) +{ + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + set_do_reload_ifname(cp->cp_ifname); + set_do_reload(1); + + return (0); +} + +static int +cm_setprop_enable(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + + ifi->ifi_persist = 1; + set_do_reload_ifname(ifi->ifi_ifname); + set_do_reload(0); + + return (0); +} + +static int +cm_setprop_disable(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + + if (ifi->ifi_persist == 1) { + ifi->ifi_persist = 0; + rm_ifinfo(ifi); + + /* MC leaving needed here */ + sock_mc_leave(&sock, ifi->ifi_ifindex); + + set_do_reload_ifname(ifi->ifi_ifname); + set_do_reload(0); + } + + return (0); +} + +int +cm_handler_server(int fd) +{ + int state; + char *msg; + struct ctrl_msg_hdr *cm; + struct ctrl_msg_pl cp; + char buf[CM_MSG_MAXLEN]; + char pbuf[CM_MSG_MAXLEN]; + int error; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + memset(buf, 0, sizeof(buf)); + memset(pbuf, 0, sizeof(pbuf)); + cm = (struct ctrl_msg_hdr *)buf; + msg = (char *)buf + sizeof(*cm); + + state = CM_STATE_INIT; + while (state != CM_STATE_EOM) { + syslog(LOG_DEBUG, "<%s> state = %d", __func__, state); + + switch (state) { + case CM_STATE_INIT: + state = CM_STATE_MSG_RECV; + break; + case CM_STATE_MSG_DISPATCH: + cm->cm_version = CM_VERSION; + error = cm_send(fd, buf); + if (error) + syslog(LOG_WARNING, + "<%s> cm_send()", __func__); + state = CM_STATE_EOM; + break; + case CM_STATE_ACK_WAIT: + error = cm_recv(fd, buf); + if (error) { + syslog(LOG_ERR, + "<%s> cm_recv()", __func__); + close(fd); + return (-1); + } + + switch (cm->cm_type) { + case CM_TYPE_ACK: + break; + case CM_TYPE_ERR: + syslog(LOG_DEBUG, + "<%s> CM_TYPE_ERR", __func__); + close(fd); + return (-1); + default: + syslog(LOG_DEBUG, + "<%s> unknown status", __func__); + close(fd); + return (-1); + } + state = CM_STATE_EOM; + break; + case CM_STATE_MSG_RECV: + error = cm_recv(fd, buf); + + if (error) { + syslog(LOG_ERR, + "<%s> cm_recv()", __func__); + close(fd); + return (-1); + } + memset(&cp, 0, sizeof(cp)); + + syslog(LOG_DEBUG, + "<%s> cm->cm_type = %d", __func__, cm->cm_type); + syslog(LOG_DEBUG, + "<%s> cm->cm_len = %zu", __func__, cm->cm_len); + + switch (cm->cm_type) { + case CM_TYPE_EOM: + state = CM_STATE_EOM; + case CM_TYPE_NUL: + cm->cm_type = CM_TYPE_ACK; + cm->cm_len = sizeof(*cm); + break; + case CM_TYPE_REQ_GET_PROP: + cm_bin2pl(msg, &cp); + error = cm_getprop(&cp); + if (error) { + cm->cm_type = CM_TYPE_ERR; + cm->cm_len = sizeof(*cm); + } else { + cm->cm_type = CM_TYPE_ACK; + cm->cm_len = sizeof(*cm); + cm->cm_len += cm_pl2bin(msg, &cp); + } + if (cp.cp_val != NULL) + free(cp.cp_val); + break; + case CM_TYPE_REQ_SET_PROP: + cm_bin2pl(msg, &cp); + error = cm_setprop(&cp); + if (error) { + cm->cm_type = CM_TYPE_ERR; + cm->cm_len = sizeof(*cm); + } else { + cm->cm_type = CM_TYPE_ACK; + cm->cm_len = sizeof(*cm); + } + break; + default: + cm->cm_type = CM_TYPE_ERR; + cm->cm_len = sizeof(*cm); + } + + switch (cm->cm_type) { + case CM_TYPE_ERR: + case CM_TYPE_ACK: + state = CM_STATE_MSG_DISPATCH; + break; + } + } + } + syslog(LOG_DEBUG, "<%s> leave", __func__); + + return (0); +} diff --git a/usr.sbin/rtadvd/control_server.h b/usr.sbin/rtadvd/control_server.h new file mode 100644 index 0000000..dfd49b2 --- /dev/null +++ b/usr.sbin/rtadvd/control_server.h @@ -0,0 +1,42 @@ +/*- + * 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. + * + * 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. + * + * $FreeBSD: stable/10/usr.sbin/rtadvd/control_server.h 225519 2011-09-12 23:52:55Z hrs $ + * + */ + +int cm_getprop(struct ctrl_msg_pl *); +int cm_setprop(struct ctrl_msg_pl *); + +int cm_handler_server(int); + +void set_do_reload(int); +void set_do_reload_ifname(char *); +void set_do_shutdown(int); +void reset_do_reload(void); +void reset_do_shutdown(void); +int is_do_reload(void); +char *reload_ifname(void); +int is_do_shutdown(void); diff --git a/usr.sbin/rtadvd/if.c b/usr.sbin/rtadvd/if.c index 32efd9b..36f30ab 100644 --- a/usr.sbin/rtadvd/if.c +++ b/usr.sbin/rtadvd/if.c @@ -1,9 +1,11 @@ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/if.c 255156 2013-09-02 20:44:19Z hrs $ */ /* $KAME: if.c,v 1.17 2001/01/21 15:27:30 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: @@ -15,7 +17,7 @@ * 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 @@ -27,9 +29,6 @@ * 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. - * - * $FreeBSD: src/usr.sbin/rtadvd/if.c,v 1.2.2.3 2001/07/03 11:02:14 ume Exp $ - * $DragonFly: src/usr.sbin/rtadvd/if.c,v 1.6 2005/12/05 00:56:37 swildner Exp $ */ #include @@ -37,183 +36,88 @@ #include #include #include +#include #include -#ifdef __DragonFly__ -# include -#endif -#include -#ifdef __NetBSD__ -#include -#endif +#include +#include #include -#include #include +#include +#include #include -#ifdef __bsdi__ -# include -#endif -#ifdef __OpenBSD__ -#include -#endif +#include #include #include +#include #include #include #include + +#include "pathnames.h" #include "rtadvd.h" #include "if.h" -struct if_msghdr **iflist; -int iflist_init_ok; -size_t ifblock_size; -char *ifblock; +#define ROUNDUP(a, size) \ + (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) + +#define NEXT_SA(ap) \ + (ap) = (struct sockaddr *)((caddr_t)(ap) + \ + ((ap)->sa_len ? ROUNDUP((ap)->sa_len, sizeof(u_long)) : \ + sizeof(u_long))) -static void get_iflist(char **buf, size_t *size); -static void parse_iflist(struct if_msghdr ***ifmlist_p, char *buf, - size_t bufsize); +struct sockaddr_in6 sin6_linklocal_allnodes = { + .sin6_len = sizeof(sin6_linklocal_allnodes), + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LINKLOCAL_ALLNODES_INIT, +}; + +struct sockaddr_in6 sin6_linklocal_allrouters = { + .sin6_len = sizeof(sin6_linklocal_allrouters), + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT, +}; + +struct sockaddr_in6 sin6_sitelocal_allrouters = { + .sin6_len = sizeof(sin6_sitelocal_allrouters), + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_SITELOCAL_ALLROUTERS_INIT, +}; + +struct sockinfo sock = { .si_fd = -1, .si_name = NULL }; +struct sockinfo rtsock = { .si_fd = -1, .si_name = NULL }; +struct sockinfo ctrlsock = { .si_fd = -1, .si_name = _PATH_CTRL_SOCK }; + +char *mcastif; + +static void get_rtaddrs(int, struct sockaddr *, + struct sockaddr **); +static struct if_msghdr *get_next_msghdr(struct if_msghdr *, + struct if_msghdr *); static void get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) { int i; - + for (i = 0; i < RTAX_MAX; i++) { if (addrs & (1 << i)) { rti_info[i] = sa; - RT_ADVANCE(sa, sa); + NEXT_SA(sa); } else rti_info[i] = NULL; } } -struct sockaddr_dl * -if_nametosdl(char *name) -{ - int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; - char *buf, *next, *lim; - size_t len; - struct if_msghdr *ifm; - struct sockaddr *sa, *rti_info[RTAX_MAX]; - struct sockaddr_dl *sdl = NULL, *ret_sdl; - - if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) - return(NULL); - if ((buf = malloc(len)) == NULL) - return(NULL); - if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { - free(buf); - return(NULL); - } - - lim = buf + len; - for (next = buf; next < lim; next += ifm->ifm_msglen) { - ifm = (struct if_msghdr *)next; - if (ifm->ifm_type == RTM_IFINFO) { - sa = (struct sockaddr *)(ifm + 1); - get_rtaddrs(ifm->ifm_addrs, sa, rti_info); - if ((sa = rti_info[RTAX_IFP]) != NULL) { - if (sa->sa_family == AF_LINK) { - sdl = (struct sockaddr_dl *)sa; - if (strlen(name) != sdl->sdl_nlen) - continue; /* not same len */ - if (strncmp(&sdl->sdl_data[0], - name, - sdl->sdl_nlen) == 0) { - break; - } - } - } - } - } - if (next == lim) { - /* search failed */ - free(buf); - return(NULL); - } - - if ((ret_sdl = malloc(sdl->sdl_len)) == NULL) - return(NULL); - memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len); - return(ret_sdl); -} - -int -if_getmtu(char *name) -{ - struct ifaddrs *ifap, *ifa; - struct if_data *ifd; - u_long 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; - strncpy(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) { - syslog(LOG_ERR, "<%s> socket: %s", __func__, - strerror(errno)); - return (oifflags & ~IFF_UP); - } - - if_indextoname(ifindex, ifr.ifr_name); - if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { - syslog(LOG_ERR, "<%s> ioctl:SIOCGIFFLAGS: failed for %s", - __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); + switch (sdl->sdl_type) { + case IFT_ETHER: + return (ROUNDUP8(ETHER_ADDR_LEN + 2)); + default: + return (0); } } @@ -224,17 +128,16 @@ lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt) ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */ - switch(sdl->sdl_type) { - case IFT_ETHER: - ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3; - addr = (char *)(ndopt + 1); - memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN); - break; - default: - syslog(LOG_ERR, - "<%s> unsupported link type(%d)", - __func__, sdl->sdl_type); - exit(1); + switch (sdl->sdl_type) { + case IFT_ETHER: + ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3; + addr = (char *)(ndopt + 1); + memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN); + break; + default: + syslog(LOG_ERR, "<%s> unsupported link type(%d)", + __func__, sdl->sdl_type); + exit(1); } return; @@ -244,13 +147,12 @@ int rtbuf_len(void) { 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 (-1); - return(len); + return (len); } #define FILTER_MATCH(type, filter) ((0x1 << type) & filter) @@ -270,14 +172,21 @@ get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter) /* just for safety */ if (!rtm->rtm_msglen) { syslog(LOG_WARNING, "<%s> rtm_msglen is 0 " - "(buf=%p lim=%p rtm=%p)", __func__, - buf, lim, rtm); + "(buf=%p lim=%p rtm=%p)", __func__, + buf, lim, rtm); break; } - if (FILTER_MATCH(rtm->rtm_type, filter) == 0) { + if (((struct rt_msghdr *)buf)->rtm_version != RTM_VERSION) { + syslog(LOG_WARNING, + "<%s> routing message version mismatch " + "(buf=%p lim=%p rtm=%p)", __func__, + buf, lim, rtm); continue; } + if (FILTER_MATCH(rtm->rtm_type, filter) == 0) + continue; + switch (rtm->rtm_type) { case RTM_GET: case RTM_ADD: @@ -331,6 +240,7 @@ get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter) return (char *)rtm; /* NOTREACHED */ case RTM_IFINFO: + case RTM_IFANNOUNCE: /* found */ *lenp = rtm->rtm_msglen; return (char *)rtm; @@ -338,7 +248,7 @@ get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter) } } - return (char *)rtm; + return ((char *)rtm); } #undef FILTER_MATCH @@ -351,7 +261,7 @@ get_addr(char *buf) sa = (struct sockaddr *)(rtm + 1); get_rtaddrs(rtm->rtm_addrs, sa, rti_info); - return(&SIN6(rti_info[RTAX_DST])->sin6_addr); + return (&SIN6(rti_info[RTAX_DST])->sin6_addr); } int @@ -363,31 +273,7 @@ get_rtm_ifindex(char *buf) 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); + return (((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index); } int @@ -395,19 +281,19 @@ get_prefixlen(char *buf) { struct rt_msghdr *rtm = (struct rt_msghdr *)buf; struct sockaddr *sa, *rti_info[RTAX_MAX]; - u_char *p, *lim; - + 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; + p = (char *)(&SIN6(sa)->sin6_addr); + lim = (char *)sa + sa->sa_len; return prefixlen(p, lim); } int -prefixlen(u_char *p, u_char *lim) +prefixlen(unsigned char *p, unsigned char *lim) { int masklen; @@ -440,144 +326,425 @@ prefixlen(u_char *p, u_char *lim) case 0x00: break; default: - return(-1); + return (-1); } } - return(masklen); + return (masklen); } -int -rtmsg_type(char *buf) +struct ifinfo * +update_persist_ifinfo(struct ifilist_head_t *ifi_head, const char *ifname) { - struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + struct ifinfo *ifi; + int ifindex; + + ifi = NULL; + ifindex = if_nametoindex(ifname); + TAILQ_FOREACH(ifi, ifi_head, ifi_next) { + if (ifindex != 0) { + if (ifindex == ifi->ifi_ifindex) + break; + } else { + if (strncmp(ifname, ifi->ifi_ifname, + sizeof(ifi->ifi_ifname)) == 0) + break; + } + } - return(rtm->rtm_type); -} + if (ifi == NULL) { + /* A new ifinfo element is needed. */ + syslog(LOG_DEBUG, "<%s> new entry: %s", __func__, + ifname); + + ELM_MALLOC(ifi, exit(1)); + ifi->ifi_ifindex = 0; + strncpy(ifi->ifi_ifname, ifname, sizeof(ifi->ifi_ifname)-1); + ifi->ifi_ifname[sizeof(ifi->ifi_ifname)-1] = '\0'; + ifi->ifi_rainfo = NULL; + ifi->ifi_state = IFI_STATE_UNCONFIGURED; + TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next); + } -int -rtmsg_len(char *buf) -{ - struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + ifi->ifi_persist = 1; - return(rtm->rtm_msglen); + syslog(LOG_DEBUG, "<%s> %s is marked PERSIST", __func__, + ifi->ifi_ifname); + syslog(LOG_DEBUG, "<%s> %s is state = %d", __func__, + ifi->ifi_ifname, ifi->ifi_state); + return (ifi); } int -ifmsg_len(char *buf) +update_ifinfo_nd_flags(struct ifinfo *ifi) { - struct if_msghdr *ifm = (struct if_msghdr *)buf; + struct in6_ndireq nd; + int s; + int error; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, + "<%s> socket() failed.", __func__); + return (1); + } + /* ND flags */ + memset(&nd, 0, sizeof(nd)); + strncpy(nd.ifname, ifi->ifi_ifname, + sizeof(nd.ifname)); + error = ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd); + if (error) { + close(s); + if (errno != EPFNOSUPPORT) + syslog(LOG_ERR, "<%s> ioctl() failed.", __func__); + return (1); + } + ifi->ifi_nd_flags = nd.ndi.flags; + close(s); - return(ifm->ifm_msglen); + return (0); } -/* - * alloc buffer and get if_msghdrs block from kernel, - * and put them into the buffer - */ -static void -get_iflist(char **buf, size_t *size) +struct ifinfo * +update_ifinfo(struct ifilist_head_t *ifi_head, int ifindex) { - int mib[6]; - - mib[0] = CTL_NET; - mib[1] = PF_ROUTE; - mib[2] = 0; - mib[3] = AF_INET6; - mib[4] = NET_RT_IFLIST; - mib[5] = 0; - - if (sysctl(mib, 6, NULL, size, NULL, 0) < 0) { - syslog(LOG_ERR, "<%s> sysctl: iflist size get failed", - __func__); + struct if_msghdr *ifm; + struct ifinfo *ifi = NULL; + struct sockaddr *sa; + struct sockaddr *rti_info[RTAX_MAX]; + char *msg; + size_t len; + char *lim; + int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET6, NET_RT_IFLIST, 0 }; + int error; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), NULL, &len, NULL, 0) < + 0) { + syslog(LOG_ERR, + "<%s> sysctl: NET_RT_IFLIST size get failed", __func__); exit(1); } - if ((*buf = malloc(*size)) == NULL) { + if ((msg = malloc(len)) == NULL) { syslog(LOG_ERR, "<%s> malloc failed", __func__); exit(1); } - if (sysctl(mib, 6, *buf, size, NULL, 0) < 0) { - syslog(LOG_ERR, "<%s> sysctl: iflist get failed", - __func__); + if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), msg, &len, NULL, 0) < + 0) { + syslog(LOG_ERR, + "<%s> sysctl: NET_RT_IFLIST get failed", __func__); exit(1); } - return; -} -/* - * alloc buffer and parse if_msghdrs block passed as arg, - * and init the buffer as list of pointers ot each of the if_msghdr. - */ -static void -parse_iflist(struct if_msghdr ***ifmlist_p, char *buf, size_t bufsize) -{ - int iflentry_size, malloc_size; - struct if_msghdr *ifm; - struct ifa_msghdr *ifam; - char *lim; + lim = msg + len; + for (ifm = (struct if_msghdr *)msg; + ifm != NULL && ifm < (struct if_msghdr *)lim; + ifm = get_next_msghdr(ifm,(struct if_msghdr *)lim)) { + int ifi_new; - /* - * Estimate least size of an iflist entry, to be obtained from kernel. - * Should add sizeof(sockaddr) ?? - */ - iflentry_size = sizeof(struct if_msghdr); - /* roughly estimate max list size of pointers to each if_msghdr */ - malloc_size = (bufsize/iflentry_size) * sizeof(size_t); - if ((*ifmlist_p = (struct if_msghdr **)malloc(malloc_size)) == NULL) { - syslog(LOG_ERR, "<%s> malloc failed", __func__); - exit(1); - } + syslog(LOG_DEBUG, "<%s> ifm = %p, lim = %p, diff = %zu", + __func__, ifm, lim, (char *)lim - (char *)ifm); - lim = buf + bufsize; - for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) { + if (ifm->ifm_version != RTM_VERSION) { + syslog(LOG_ERR, + "<%s> ifm_vesrion mismatch", __func__); + exit(1); + } if (ifm->ifm_msglen == 0) { - syslog(LOG_WARNING, "<%s> ifm_msglen is 0 " - "(buf=%p lim=%p ifm=%p)", __func__, - buf, lim, ifm); - return; + syslog(LOG_WARNING, + "<%s> ifm_msglen is 0", __func__); + free(msg); + return (NULL); } + ifi_new = 0; if (ifm->ifm_type == RTM_IFINFO) { - (*ifmlist_p)[ifm->ifm_index] = ifm; + struct ifreq ifr; + int s; + char ifname[IFNAMSIZ]; + + syslog(LOG_DEBUG, "<%s> RTM_IFINFO found. " + "ifm_index = %d, ifindex = %d", + __func__, ifm->ifm_index, ifindex); + + /* when ifindex is specified */ + if (ifindex != UPDATE_IFINFO_ALL && + ifindex != ifm->ifm_index) + continue; + + /* lookup an entry with the same ifindex */ + TAILQ_FOREACH(ifi, ifi_head, ifi_next) { + if (ifm->ifm_index == ifi->ifi_ifindex) + break; + if_indextoname(ifm->ifm_index, ifname); + if (strncmp(ifname, ifi->ifi_ifname, + sizeof(ifname)) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_DEBUG, + "<%s> new entry for idx=%d", + __func__, ifm->ifm_index); + ELM_MALLOC(ifi, exit(1)); + ifi->ifi_rainfo = NULL; + ifi->ifi_state = IFI_STATE_UNCONFIGURED; + ifi->ifi_persist = 0; + ifi_new = 1; + } + /* ifindex */ + ifi->ifi_ifindex = ifm->ifm_index; + + /* ifname */ + if_indextoname(ifm->ifm_index, ifi->ifi_ifname); + if (ifi->ifi_ifname == NULL) { + syslog(LOG_WARNING, + "<%s> ifname not found (idx=%d)", + __func__, ifm->ifm_index); + if (ifi_new) + free(ifi); + continue; + } + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, + "<%s> socket() failed.", __func__); + if (ifi_new) + free(ifi); + continue; + } + + /* MTU */ + ifi->ifi_phymtu = ifm->ifm_data.ifi_mtu; + if (ifi->ifi_phymtu == 0) { + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_addr.sa_family = AF_INET6; + strncpy(ifr.ifr_name, ifi->ifi_ifname, + sizeof(ifr.ifr_name)); + error = ioctl(s, SIOCGIFMTU, (caddr_t)&ifr); + if (error) { + close(s); + syslog(LOG_ERR, + "<%s> ioctl() failed.", + __func__); + if (ifi_new) + free(ifi); + continue; + } + ifi->ifi_phymtu = ifr.ifr_mtu; + if (ifi->ifi_phymtu == 0) { + syslog(LOG_WARNING, + "<%s> no interface mtu info" + " on %s. %d will be used.", + __func__, ifi->ifi_ifname, + IPV6_MMTU); + ifi->ifi_phymtu = IPV6_MMTU; + } + } + close(s); + + /* ND flags */ + error = update_ifinfo_nd_flags(ifi); + if (error) { + if (ifi_new) + free(ifi); + continue; + } + + /* SDL */ + 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) { + memcpy(&ifi->ifi_sdl, + (struct sockaddr_dl *)sa, + sizeof(ifi->ifi_sdl)); + } + } else + memset(&ifi->ifi_sdl, 0, + sizeof(ifi->ifi_sdl)); + + /* flags */ + ifi->ifi_flags = ifm->ifm_flags; + + /* type */ + ifi->ifi_type = ifm->ifm_type; } else { - syslog(LOG_ERR, "out of sync parsing NET_RT_IFLIST\n" - "expected %d, got %d\n msglen = %d\n" - "buf:%p, ifm:%p, lim:%p\n", - RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen, - buf, ifm, lim); - exit (1); + syslog(LOG_ERR, + "out of sync parsing NET_RT_IFLIST\n" + "expected %d, got %d\n msglen = %d\n", + RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen); + exit(1); } - for (ifam = (struct ifa_msghdr *) - ((char *)ifm + ifm->ifm_msglen); - ifam < (struct ifa_msghdr *)lim; - ifam = (struct ifa_msghdr *) - ((char *)ifam + ifam->ifam_msglen)) { - /* just for safety */ - if (!ifam->ifam_msglen) { - syslog(LOG_WARNING, "<%s> ifa_msglen is 0 " - "(buf=%p lim=%p ifam=%p)", __func__, - buf, lim, ifam); - return; - } - if (ifam->ifam_type != RTM_NEWADDR) - break; + + if (ifi_new) { + syslog(LOG_DEBUG, + "<%s> adding %s(idx=%d) to ifilist", + __func__, ifi->ifi_ifname, ifi->ifi_ifindex); + TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next); } - ifm = (struct if_msghdr *)ifam; } + free(msg); + + if (mcastif != NULL) { + error = sock_mc_rr_update(&sock, mcastif); + if (error) + exit(1); + } + + return (ifi); } -void -init_iflist(void) +static struct if_msghdr * +get_next_msghdr(struct if_msghdr *ifm, struct if_msghdr *lim) +{ + struct ifa_msghdr *ifam; + + for (ifam = (struct ifa_msghdr *)((char *)ifm + ifm->ifm_msglen); + ifam < (struct ifa_msghdr *)lim; + ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen)) { + if (!ifam->ifam_msglen) { + syslog(LOG_WARNING, + "<%s> ifa_msglen is 0", __func__); + return (NULL); + } + if (ifam->ifam_type != RTM_NEWADDR) + break; + } + + return ((struct if_msghdr *)ifam); +} + +int +getinet6sysctl(int code) { - if (ifblock) { - free(ifblock); - ifblock_size = 0; + 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) { + syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s", + __func__, code, + strerror(errno)); + return (-1); + } + else + return (value); +} + + +int +sock_mc_join(struct sockinfo *s, int ifindex) +{ + struct ipv6_mreq mreq; + char ifname[IFNAMSIZ]; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (ifindex == 0) + return (1); + + /* + * join all routers multicast address on each advertising + * interface. + */ + memset(&mreq, 0, sizeof(mreq)); + /* XXX */ + memcpy(&mreq.ipv6mr_multiaddr.s6_addr, + &sin6_linklocal_allrouters.sin6_addr, + sizeof(mreq.ipv6mr_multiaddr.s6_addr)); + + mreq.ipv6mr_interface = ifindex; + if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, + sizeof(mreq)) < 0) { + syslog(LOG_ERR, + "<%s> IPV6_JOIN_GROUP(link) on %s: %s", + __func__, if_indextoname(ifindex, ifname), + strerror(errno)); + return (1); } - if (iflist) - free(iflist); - /* get iflist block from kernel */ - get_iflist(&ifblock, &ifblock_size); + syslog(LOG_DEBUG, + "<%s> %s: join link-local all-routers MC group", + __func__, if_indextoname(ifindex, ifname)); + + return (0); +} + +int +sock_mc_leave(struct sockinfo *s, int ifindex) +{ + struct ipv6_mreq mreq; + char ifname[IFNAMSIZ]; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (ifindex == 0) + return (1); + + /* + * join all routers multicast address on each advertising + * interface. + */ + + memset(&mreq, 0, sizeof(mreq)); + /* XXX */ + memcpy(&mreq.ipv6mr_multiaddr.s6_addr, + &sin6_linklocal_allrouters.sin6_addr, + sizeof(mreq.ipv6mr_multiaddr.s6_addr)); + + mreq.ipv6mr_interface = ifindex; + if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, + sizeof(mreq)) < 0) { + syslog(LOG_ERR, + "<%s> IPV6_JOIN_LEAVE(link) on %s: %s", + __func__, if_indextoname(ifindex, ifname), + strerror(errno)); + return (1); + } + syslog(LOG_DEBUG, + "<%s> %s: leave link-local all-routers MC group", + __func__, if_indextoname(ifindex, ifname)); + + return (0); +} + +int +sock_mc_rr_update(struct sockinfo *s, char *mif) +{ + struct ipv6_mreq mreq; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (mif == NULL) + return (1); + /* + * When attending router renumbering, join all-routers site-local + * multicast group. + */ + /* XXX */ + memcpy(&mreq.ipv6mr_multiaddr.s6_addr, + &sin6_sitelocal_allrouters.sin6_addr, + sizeof(mreq.ipv6mr_multiaddr.s6_addr)); + if ((mreq.ipv6mr_interface = if_nametoindex(mif)) == 0) { + syslog(LOG_ERR, + "<%s> invalid interface: %s", + __func__, mif); + return (1); + } + + if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq, sizeof(mreq)) < 0) { + syslog(LOG_ERR, + "<%s> IPV6_JOIN_GROUP(site) on %s: %s", + __func__, mif, strerror(errno)); + return (1); + } + + syslog(LOG_DEBUG, + "<%s> %s: join site-local all-routers MC group", + __func__, mif); - /* make list of pointers to each if_msghdr */ - parse_iflist(&iflist, ifblock, ifblock_size); + return (0); } diff --git a/usr.sbin/rtadvd/if.h b/usr.sbin/rtadvd/if.h index bf282ec..0f17a41 100644 --- a/usr.sbin/rtadvd/if.h +++ b/usr.sbin/rtadvd/if.h @@ -1,9 +1,10 @@ -/* $KAME: if.h,v 1.6 2001/01/21 15:37:14 itojun Exp $ */ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/if.h 224144 2011-07-17 19:24:54Z hrs $ */ +/* $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: @@ -15,7 +16,7 @@ * 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 @@ -27,34 +28,34 @@ * 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. - * - * $FreeBSD: src/usr.sbin/rtadvd/if.h,v 1.1.2.2 2001/07/03 11:02:14 ume Exp $ - * $DragonFly: src/usr.sbin/rtadvd/if.h,v 1.3 2003/11/03 19:31:42 eirikn Exp $ */ -#define RTADV_TYPE2BITMASK(type) (0x1 << type) +#define UPDATE_IFINFO_ALL 0 -extern struct if_msghdr **iflist; -extern size_t ifblock_size; -extern char *ifblock; +struct sockinfo { + int si_fd; + const char *si_name; +}; + +extern struct sockinfo sock; +extern struct sockinfo rtsock; +extern struct sockinfo ctrlsock; -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); +int prefixlen(unsigned char *, unsigned char *); + +struct ifinfo *update_ifinfo(struct ifilist_head_t *, int); +int update_ifinfo_nd_flags(struct ifinfo *); +struct ifinfo *update_persist_ifinfo(struct ifilist_head_t *, + const char *); + +int sock_mc_join(struct sockinfo *, int); +int sock_mc_leave(struct sockinfo *, int); +int sock_mc_rr_update(struct sockinfo *, char *); +int getinet6sysctl(int); diff --git a/usr.sbin/rtadvd/pathnames.h b/usr.sbin/rtadvd/pathnames.h index 9c7ea03..d5c20d5 100644 --- a/usr.sbin/rtadvd/pathnames.h +++ b/usr.sbin/rtadvd/pathnames.h @@ -1,5 +1,6 @@ /* $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 $ */ -/* $DragonFly: src/usr.sbin/rtadvd/pathnames.h,v 1.2 2003/06/17 04:30:02 dillon Exp $ */ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/pathnames.h 224144 2011-07-17 19:24:54Z hrs $ */ -#define _PATH_RTADVDCONF "/etc/rtadvd.conf" +#define _PATH_RTADVDCONF "/etc/rtadvd.conf" +#define _PATH_RTADVDPID "/var/run/rtadvd.pid" +#define _PATH_CTRL_SOCK "/var/run/rtadvd.sock" diff --git a/usr.sbin/rtadvd/rrenum.c b/usr.sbin/rtadvd/rrenum.c index 82ab834..c57c805 100644 --- a/usr.sbin/rtadvd/rrenum.c +++ b/usr.sbin/rtadvd/rrenum.c @@ -1,4 +1,5 @@ -/* $KAME: rrenum.c,v 1.10 2001/01/21 15:32:16 itojun Exp $ */ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/rrenum.c 253970 2013-08-05 20:13:02Z hrs $ */ +/* $KAME: rrenum.c,v 1.12 2002/06/10 19:59:47 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -27,8 +28,6 @@ * 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. - * - * $FreeBSD: src/usr.sbin/rtadvd/rrenum.c,v 1.2.2.2 2001/07/03 11:02:14 ume Exp $ */ #include #include @@ -37,9 +36,8 @@ #include #include -#if defined(__DragonFly__) +#include #include -#endif /* __DragonFly__ */ #include #include #include @@ -48,8 +46,10 @@ #include #include +#include #include #include +#include #include #include "rtadvd.h" #include "rrenum.h" @@ -77,7 +77,7 @@ static int s = -1; /* * Check validity of a Prefix Control Operation(PCO). - * Return 0 on success, 1 on failure. + * return 0 on success, 1 on failure. */ static int rr_pco_check(int len, struct rr_pco_match *rpm) @@ -89,25 +89,25 @@ rr_pco_check(int len, struct rr_pco_match *rpm) if ((rpm->rpm_len - 3) < 0 || /* must be at least 3 */ (rpm->rpm_len - 3) & 0x3) { /* must be multiple of 4 */ syslog(LOG_WARNING, "<%s> rpm_len %d is not 4N * 3", - __func__, rpm->rpm_len); - return 1; + __func__, rpm->rpm_len); + return (1); } /* rpm->rpm_code must be valid value */ - switch(rpm->rpm_code) { + switch (rpm->rpm_code) { case RPM_PCO_ADD: case RPM_PCO_CHANGE: case RPM_PCO_SETGLOBAL: break; default: syslog(LOG_WARNING, "<%s> unknown rpm_code %d", __func__, - rpm->rpm_code); - return 1; + rpm->rpm_code); + return (1); } /* rpm->rpm_matchlen must be 0 to 128 inclusive */ if (rpm->rpm_matchlen > 128) { syslog(LOG_WARNING, "<%s> rpm_matchlen %d is over 128", - __func__, rpm->rpm_matchlen); - return 1; + __func__, rpm->rpm_matchlen); + return (1); } /* @@ -129,23 +129,23 @@ rr_pco_check(int len, struct rr_pco_match *rpm) */ if (checklen > 128) { syslog(LOG_WARNING, "<%s> sum of rpu_uselen %d and" - " rpu_keeplen %d is %d(over 128)", - __func__, rpu->rpu_uselen, - rpu->rpu_keeplen, - rpu->rpu_uselen + rpu->rpu_keeplen); - return 1; + " rpu_keeplen %d is %d(over 128)", + __func__, rpu->rpu_uselen, rpu->rpu_keeplen, + rpu->rpu_uselen + rpu->rpu_keeplen); + return (1); } } - return 0; + return (0); } static void do_use_prefix(int len, struct rr_pco_match *rpm, - struct in6_rrenumreq *irr, int ifindex) + struct in6_rrenumreq *irr, int ifindex) { struct rr_pco_use *rpu, *rpulim; struct rainfo *rai; - struct prefix *pp; + struct ifinfo *ifi; + struct prefix *pfx; rpu = (struct rr_pco_use *)(rpm + 1); rpulim = (struct rr_pco_use *)((char *)rpm + len); @@ -167,7 +167,7 @@ do_use_prefix(int len, struct rr_pco_match *rpm, if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 && errno != EADDRNOTAVAIL) syslog(LOG_ERR, "<%s> ioctl: %s", __func__, - strerror(errno)); + strerror(errno)); return; } @@ -179,19 +179,23 @@ do_use_prefix(int len, struct rr_pco_match *rpm, 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) == 0 ? 0 : 1; + !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK); irr->irr_raf_mask_auto = - (rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_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; + (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; + (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; + (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; + (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; @@ -199,38 +203,45 @@ do_use_prefix(int len, struct rr_pco_match *rpm, if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 && errno != EADDRNOTAVAIL) syslog(LOG_ERR, "<%s> ioctl: %s", __func__, - strerror(errno)); + 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) + ifi = if_indextoifinfo(ifindex); + if (ifi == NULL || ifi->ifi_rainfo == NULL) continue; /* non-advertising IF */ + rai = ifi->ifi_rainfo; - for (pp = rai->prefix.next; pp != &rai->prefix; - pp = pp->next) { - struct timeval now; + TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { + struct timespec now; - if (prefix_match(&pp->prefix, pp->prefixlen, - &rpm->rpm_prefix, - rpm->rpm_matchlen)) { + if (prefix_match(&pfx->pfx_prefix, + pfx->pfx_prefixlen, &rpm->rpm_prefix, + rpm->rpm_matchlen)) { /* change parameters */ - pp->validlifetime = ntohl(rpu->rpu_vltime); - pp->preflifetime = ntohl(rpu->rpu_pltime); + pfx->pfx_validlifetime = + ntohl(rpu->rpu_vltime); + pfx->pfx_preflifetime = + ntohl(rpu->rpu_pltime); if (irr->irr_rrf_decrvalid) { - gettimeofday(&now, 0); - pp->vltimeexpire = - now.tv_sec + pp->validlifetime; + clock_gettime(CLOCK_MONOTONIC_FAST, + &now); + pfx->pfx_vltimeexpire = + now.tv_sec + + pfx->pfx_validlifetime; } else - pp->vltimeexpire = 0; + pfx->pfx_vltimeexpire = 0; if (irr->irr_rrf_decrprefd) { - gettimeofday(&now, 0); - pp->pltimeexpire = - now.tv_sec + pp->preflifetime; + clock_gettime(CLOCK_MONOTONIC_FAST, + &now); + pfx->pfx_pltimeexpire = + now.tv_sec + + pfx->pfx_preflifetime; } else - pp->pltimeexpire = 0; + pfx->pfx_pltimeexpire = 0; } } } @@ -246,13 +257,14 @@ do_pco(struct icmp6_router_renum *rr, int len, struct rr_pco_match *rpm) { int ifindex = 0; struct in6_rrenumreq irr; - + struct ifinfo *ifi; + if ((rr_pco_check(len, rpm) != 0)) - return 1; + return (1); if (s == -1 && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "<%s> socket: %s", __func__, - strerror(errno)); + strerror(errno)); exit(1); } @@ -266,24 +278,30 @@ do_pco(struct icmp6_router_renum *rr, int len, struct rr_pco_match *rpm) irr.irr_matchprefix.sin6_addr = rpm->rpm_prefix; while (if_indextoname(++ifindex, irr.irr_name)) { + ifi = if_indextoifinfo(ifindex); + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> ifindex not found.", + __func__); + return (1); + } /* - * if ICMP6_RR_FLAGS_FORCEAPPLY(A flag) is 0 and IFF_UP is off, - * the interface is not applied + * 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 && - (iflist[ifindex]->ifm_flags & IFF_UP) == 0) + (ifi->ifi_flags & IFF_UP) == 0) continue; /* TODO: interface scope check */ do_use_prefix(len, rpm, &irr, ifindex); } if (errno == ENXIO) - return 0; + return (0); else if (errno) { syslog(LOG_ERR, "<%s> if_indextoname: %s", __func__, - strerror(errno)); - return 1; + strerror(errno)); + return (1); } - return 0; + return (0); } /* @@ -301,18 +319,17 @@ do_rr(int len, struct icmp6_router_renum *rr) cp = (char *)(rr + 1); len -= sizeof(struct icmp6_router_renum); - /* get iflist block from kernel again, to get up-to-date information */ - init_iflist(); + update_ifinfo(&ifilist, UPDATE_IFINFO_ALL); while (cp < lim) { int rpmlen; rpm = (struct rr_pco_match *)cp; - if (len < sizeof(struct rr_pco_match)) { + if ((size_t)len < sizeof(struct rr_pco_match)) { tooshort: syslog(LOG_ERR, "<%s> pkt too short. left len = %d. " - "garbage at end of pkt?", __func__, len); - return 1; + "garbage at end of pkt?", __func__, len); + return (1); } rpmlen = rpm->rpm_len << 3; if (len < rpmlen) @@ -328,7 +345,7 @@ do_rr(int len, struct icmp6_router_renum *rr) len -= rpmlen; } - return 0; + return (0); } /* @@ -337,46 +354,45 @@ do_rr(int len, struct icmp6_router_renum *rr) */ static int rr_command_check(int len, struct icmp6_router_renum *rr, struct in6_addr *from, - struct in6_addr *dst) + struct in6_addr *dst) { u_char ntopbuf[INET6_ADDRSTRLEN]; /* omit rr minimal length check. hope kernel have done it. */ /* rr_command length check */ - if (len < (sizeof(struct icmp6_router_renum) + - sizeof(struct rr_pco_match))) { + if ((size_t)len < (sizeof(struct icmp6_router_renum) + + sizeof(struct rr_pco_match))) { syslog(LOG_ERR, "<%s> rr_command len %d is too short", - __func__, len); - return 1; + __func__, len); + return (1); } /* destination check. only for multicast. omit unicast check. */ if (IN6_IS_ADDR_MULTICAST(dst) && !IN6_IS_ADDR_MC_LINKLOCAL(dst) && !IN6_IS_ADDR_MC_SITELOCAL(dst)) { syslog(LOG_ERR, "<%s> dst mcast addr %s is illegal", - __func__, - inet_ntop(AF_INET6, dst, ntopbuf, INET6_ADDRSTRLEN)); - return 1; + __func__, + inet_ntop(AF_INET6, dst, ntopbuf, sizeof(ntopbuf))); + return (1); } /* seqnum and segnum check */ if (rro.rro_seqnum > rr->rr_seqnum) { syslog(LOG_WARNING, - "<%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; + "<%s> rcvd old seqnum %d from %s", + __func__, (u_int32_t)ntohl(rr->rr_seqnum), + inet_ntop(AF_INET6, from, ntopbuf, sizeof(ntopbuf))); + return (1); } if (rro.rro_seqnum == rr->rr_seqnum && (rr->rr_flags & ICMP6_RR_FLAGS_TEST) == 0 && RR_ISSET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum)) { if ((rr->rr_flags & ICMP6_RR_FLAGS_REQRESULT) != 0) syslog(LOG_WARNING, - "<%s> rcvd duped segnum %d from %s", - __func__, rr->rr_segnum, - inet_ntop(AF_INET6, from, ntopbuf, - INET6_ADDRSTRLEN)); - return 0; + "<%s> rcvd duped segnum %d from %s", + __func__, rr->rr_segnum, inet_ntop(AF_INET6, from, + ntopbuf, sizeof(ntopbuf))); + return (0); } /* update seqnum */ @@ -385,16 +401,16 @@ rr_command_check(int len, struct icmp6_router_renum *rr, struct in6_addr *from, /* init rro_segnum_bits */ memset(rro.rro_segnum_bits, 0, - sizeof(rro.rro_segnum_bits)); + sizeof(rro.rro_segnum_bits)); } rro.rro_seqnum = rr->rr_seqnum; - return 0; + return (0); } static void rr_command_input(int len, struct icmp6_router_renum *rr, - struct in6_addr *from, struct in6_addr *dst) + struct in6_addr *from, struct in6_addr *dst) { /* rr_command validity check */ if (rr_command_check(len, rr, from, dst)) @@ -404,9 +420,8 @@ rr_command_input(int len, struct icmp6_router_renum *rr, return; /* do router renumbering */ - if (do_rr(len, rr)) { + if (do_rr(len, rr)) goto failed; - } /* update segnum */ RR_SET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum); @@ -420,27 +435,26 @@ rr_command_input(int len, struct icmp6_router_renum *rr, void rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi, - struct sockaddr_in6 *from, struct in6_addr *dst) + struct sockaddr_in6 *from, struct in6_addr *dst) { u_char ntopbuf[2][INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; syslog(LOG_DEBUG, - "<%s> RR received from %s to %s on %s", - __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)); + "<%s> RR received from %s to %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[0] ,sizeof(ntopbuf[0])), + inet_ntop(AF_INET6, &dst, ntopbuf[1], sizeof(ntopbuf[1])), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); /* packet validation based on Section 4.1 of RFC2894 */ - if (len < sizeof(struct icmp6_router_renum)) { + if ((size_t)len < sizeof(struct icmp6_router_renum)) { syslog(LOG_NOTICE, - "<%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)); + "<%s>: RR short message (size %d) from %s to %s on %s", + __func__, len, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[0], + sizeof(ntopbuf[0])), + inet_ntop(AF_INET6, &dst, ntopbuf[1], sizeof(ntopbuf[1])), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } @@ -452,16 +466,16 @@ rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi, * 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)) { + if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && !IN6_ARE_ADDR_EQUAL( + &sin6_sitelocal_allrouters.sin6_addr, &pi->ipi6_addr)) { syslog(LOG_NOTICE, - "<%s>: RR message with invalid destination (%s) " - "from %s on %s", - __func__, - inet_ntop(AF_INET6, &dst, ntopbuf[0], INET6_ADDRSTRLEN), - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf[1], INET6_ADDRSTRLEN), - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + "<%s>: RR message with invalid destination (%s) " + "from %s on %s", + __func__, + inet_ntop(AF_INET6, &dst, ntopbuf[0], sizeof(ntopbuf[0])), + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[1], + sizeof(ntopbuf[1])), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } @@ -480,7 +494,7 @@ rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi, break; default: syslog(LOG_ERR, "<%s> received unknown code %d", - __func__, rr->rr_code); + __func__, rr->rr_code); break; } diff --git a/usr.sbin/rtadvd/rrenum.h b/usr.sbin/rtadvd/rrenum.h index 39aebb4..033d19d 100644 --- a/usr.sbin/rtadvd/rrenum.h +++ b/usr.sbin/rtadvd/rrenum.h @@ -1,9 +1,10 @@ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/rrenum.h 222732 2011-06-06 03:06:43Z hrs $ */ /* $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: @@ -15,7 +16,7 @@ * 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 @@ -27,9 +28,6 @@ * 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. - * - * $FreeBSD: src/usr.sbin/rtadvd/rrenum.h,v 1.1.2.2 2001/07/03 11:02:14 ume Exp $ - * $DragonFly: src/usr.sbin/rtadvd/rrenum.h,v 1.3 2003/11/03 19:31:42 eirikn Exp $ */ void rr_input(int, struct icmp6_router_renum *, struct in6_pktinfo *, diff --git a/usr.sbin/rtadvd/rtadvd.8 b/usr.sbin/rtadvd/rtadvd.8 index ffdb361..6e36ad6 100644 --- a/usr.sbin/rtadvd/rtadvd.8 +++ b/usr.sbin/rtadvd/rtadvd.8 @@ -1,6 +1,4 @@ -.\" $FreeBSD: src/usr.sbin/rtadvd/rtadvd.8,v 1.3.2.8 2003/03/11 22:31:32 trhodes Exp $ -.\" $DragonFly: src/usr.sbin/rtadvd/rtadvd.8,v 1.4 2007/11/23 23:16:37 swildner Exp $ -.\" $KAME: rtadvd.8,v 1.17 2001/02/04 05:34:38 jinmei 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. @@ -29,7 +27,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 17, 1998 +.\" $FreeBSD: stable/10/usr.sbin/rtadvd/rtadvd.8 247270 2013-02-25 16:47:09Z des $ +.\" +.Dd February 25, 2013 .Dt RTADVD 8 .Os .Sh NAME @@ -37,14 +37,21 @@ .Nd router advertisement daemon .Sh SYNOPSIS .Nm -.Op Fl dDfMRs +.Op Fl dDfRs .Op Fl c Ar configfile -.Ar interface ... +.Op Fl C Ar ctlsock +.Op Fl M Ar ifname +.Op Fl p Ar pidfile +.Op Ar interface ... .Sh DESCRIPTION -The .Nm -utility sends router advertisement packets to the specified +sends router advertisement packets to the specified .Ar interfaces . +If no interfaces are specified, +.Nm +will still run, but will not advertise any routes until interfaces are +added using +.Xr rtadvctl 8 . .Pp The program will daemonize itself on invocation. It will then send router advertisement packets periodically, as well @@ -63,9 +70,8 @@ In particular, reads all the interface routes from the routing table and advertises them as on-link prefixes. .Pp -The .Nm -utility also watches the routing table. +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, @@ -103,7 +109,7 @@ 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). +time (RFC 4861, 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, @@ -121,31 +127,34 @@ for the configuration file. By default, .Pa /etc/rtadvd.conf is used. +.It Fl C +Specify an alternate location for the control socket used by +.Xr rtadvctl 8 . +The default is +.Pa /var/run/rtadvd.sock . .It Fl d Print debugging information. .It Fl D Even more debugging information is printed. .It Fl f Foreground mode (useful when debugging). +Log messages will be dumped to stderr when this option is specified. .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 appeared in the command +tries to join the first advertising interface appearing on the command line. This option has meaning only with the .Fl R option, which enables routing renumbering protocol support. -.\".It Fl m -.\"Enables mobile IPv6 support. -.\"This changes the content of router advertisement option, as well as -.\"permitted configuration directives. +.It Fl 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. -On KAME-based systems, -.Xr rrenumd 8 -generates router renumbering request packets. This option is currently disabled, and is ignored by .Nm with a warning message. @@ -154,11 +163,17 @@ 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 . +Use +.Dv SIGHUP +to reload the configuration file +.Pa /etc/rtadvd.conf . +If an invalid parameter is found in the configuration file upon the reload, +the entry will be ignored and the old configuration will be used. +When parameters in an existing entry are updated, +.Nm +will send Router Advertisement messages with the old configuration but +zero router lifetime to the interface first, and then start to send a new +message. .Pp Use .Dv SIGTERM @@ -169,30 +184,53 @@ In this case, .Nm will transmit router advertisement with router lifetime 0 to all the interfaces -(in accordance with RFC 2461 6.2.5). +.Pq in accordance with RFC 4861 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 -contains the pid of the currently running -.Nm . -.It Pa /var/run/rtadvd.dump -in which -.Nm -dumps its internal state. +The default process ID file. .El .Sh EXIT STATUS .Ex -std .Sh SEE ALSO .Xr rtadvd.conf 5 , -.Xr rrenumd 8 , +.Xr rtadvctl 8 , .Xr rtsol 8 +.Rs +.%A Thomas Narten +.%A Erik Nordmark +.%A W. A. Simpson +.%A Hesham Soliman +.%T Neighbor Discovery for IP version 6 (IPv6) +.%R RFC 4861 +.Re +.Rs +.%A Thomas Narten +.%A Erik Nordmark +.%A W. A. Simpson +.%T Neighbor Discovery for IP version 6 (IPv6) +.%R RFC 2461 (obsoleted by RFC 4861) +.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 Options for DNS Configuration +.%R RFC 6106 +.Re .Sh HISTORY The .Nm -utility first appeared in WIDE Hydrangea IPv6 protocol stack kit. -.Sh CAVEAT +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 diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c index 15a7243..7c50697 100644 --- a/usr.sbin/rtadvd/rtadvd.c +++ b/usr.sbin/rtadvd/rtadvd.c @@ -1,9 +1,11 @@ -/* $KAME: rtadvd.c,v 1.50 2001/02/04 06:15:15 itojun Exp $ */ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/rtadvd.c 275038 2014-11-25 13:12:45Z dim $ */ +/* $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: @@ -15,7 +17,7 @@ * 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 @@ -27,19 +29,21 @@ * 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. - * - * $FreeBSD: src/usr.sbin/rtadvd/rtadvd.c,v 1.3.2.4 2003/04/05 10:31:58 ume Exp $ */ #include +#include #include #include -#include #include +#include +#include #include -#include +#include +#include #include +#include #include #include #include @@ -47,55 +51,68 @@ #include +#include +#include +#include + #include #include #include -#include #include #include +#include +#include +#include +#include #include +#include #include +#include + +#include "pathnames.h" #include "rtadvd.h" +#include "if.h" #include "rrenum.h" #include "advcap.h" +#include "timer_subr.h" #include "timer.h" -#include "if.h" #include "config.h" -#include "dump.h" +#include "control.h" +#include "control_server.h" + +#define RTADV_TYPE2BITMASK(type) (0x1 << type) struct msghdr rcvmhdr; -static u_char *rcvcmsgbuf; +static char *rcvcmsgbuf; static size_t rcvcmsgbuflen; -static u_char *sndcmsgbuf = NULL; +static char *sndcmsgbuf = NULL; static size_t sndcmsgbuflen; -static int do_dump; -static int do_die; struct msghdr sndmhdr; struct iovec rcviov[2]; struct iovec sndiov[2]; -struct sockaddr_in6 from; -struct sockaddr_in6 sin6_allnodes = {sizeof(sin6_allnodes), AF_INET6}; -struct in6_addr in6a_site_allrouters; -static char *dumpfilename = "/var/run/rtadvd.dump"; /* XXX: should be configurable */ -static char *pidfilename = "/var/run/rtadvd.pid"; /* should be configurable */ -static char *mcastif; -int sock; -int rtsock = -1; -#ifdef MIP6 -int mobileip6 = 0; -#endif -int accept_rr = 0; -int dflag = 0, sflag = 0; - -u_char *conffile = NULL; +struct sockaddr_in6 rcvfrom; +static const char *pidfilename = _PATH_RTADVDPID; +const char *conffile = _PATH_RTADVDCONF; +static struct pidfh *pfh; +static int dflag, sflag; +static int wait_shutdown; + +#define PFD_RAWSOCK 0 +#define PFD_RTSOCK 1 +#define PFD_CSOCK 2 +#define PFD_MAX 3 + +struct railist_head_t railist = + TAILQ_HEAD_INITIALIZER(railist); +struct ifilist_head_t ifilist = + TAILQ_HEAD_INITIALIZER(ifilist); -struct rainfo *ralist = NULL; struct nd_optlist { - struct nd_optlist *next; - struct nd_opt_hdr *opt; + TAILQ_ENTRY(nd_optlist) nol_next; + struct nd_opt_hdr *nol_opt; }; -union nd_opts { - struct nd_opt_hdr *nd_opt_array[7]; +union nd_opt { + struct nd_opt_hdr *opt_array[9]; struct { struct nd_opt_hdr *zero; struct nd_opt_hdr *src_lladdr; @@ -103,75 +120,84 @@ union nd_opts { struct nd_opt_prefix_info *pi; struct nd_opt_rd_hdr *rh; struct nd_opt_mtu *mtu; - struct nd_optlist *list; + TAILQ_HEAD(, nd_optlist) opt_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 +#define opt_src_lladdr nd_opt_each.src_lladdr +#define opt_tgt_lladdr nd_opt_each.tgt_lladdr +#define opt_pi nd_opt_each.pi +#define opt_rh nd_opt_each.rh +#define opt_mtu nd_opt_each.mtu +#define opt_list nd_opt_each.opt_list + +#define NDOPT_FLAG_SRCLINKADDR (1 << 0) +#define NDOPT_FLAG_TGTLINKADDR (1 << 1) +#define NDOPT_FLAG_PREFIXINFO (1 << 2) +#define NDOPT_FLAG_RDHDR (1 << 3) +#define NDOPT_FLAG_MTU (1 << 4) +#define NDOPT_FLAG_RDNSS (1 << 5) +#define NDOPT_FLAG_DNSSL (1 << 6) + +static uint32_t ndopt_flags[] = { + [ND_OPT_SOURCE_LINKADDR] = NDOPT_FLAG_SRCLINKADDR, + [ND_OPT_TARGET_LINKADDR] = NDOPT_FLAG_TGTLINKADDR, + [ND_OPT_PREFIX_INFORMATION] = NDOPT_FLAG_PREFIXINFO, + [ND_OPT_REDIRECTED_HEADER] = NDOPT_FLAG_RDHDR, + [ND_OPT_MTU] = NDOPT_FLAG_MTU, + [ND_OPT_RDNSS] = NDOPT_FLAG_RDNSS, + [ND_OPT_DNSSL] = NDOPT_FLAG_DNSSL, }; -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(void); +static void rtadvd_shutdown(void); +static void sock_open(struct sockinfo *); +static void rtsock_open(struct sockinfo *); +static void rtadvd_input(struct sockinfo *); +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_opt *, uint32_t); +static void free_ndopts(union nd_opt *); +static void rtmsg_input(struct sockinfo *); +static void set_short_delay(struct ifinfo *); +static int check_accept_rtadv(int); + +static void +usage(void) +{ + + fprintf(stderr, "usage: rtadvd [-dDfRs] " + "[-c configfile] [-C ctlsock] [-M ifname] [-p pidfile]\n"); + exit(1); +} int main(int argc, char *argv[]) { - fd_set fdset; - int maxfd = 0; - struct timeval *timeout; + struct pollfd set[PFD_MAX]; + struct timespec *timeout; int i, ch; - int fflag = 0; - FILE *pidfp; - pid_t pid; - - openlog("rtadvd", LOG_NDELAY|LOG_PID, LOG_DAEMON); + int fflag = 0, logopt; + int error; + pid_t pid, otherpid; /* get command line options and arguments */ -#ifdef MIP6 -#define OPTIONS "c:dDfM:mRs" -#else -#define OPTIONS "c:dDfM:Rs" -#endif - while ((ch = getopt(argc, argv, OPTIONS)) != -1) { -#undef OPTIONS + while ((ch = getopt(argc, argv, "c:C:dDfhM:p:Rs")) != -1) { switch (ch) { case 'c': conffile = optarg; break; + case 'C': + ctrlsock.si_name = optarg; + break; case 'd': - dflag = 1; + dflag++; break; case 'D': - dflag = 2; + dflag += 3; break; case 'f': fflag = 1; @@ -179,11 +205,6 @@ main(int argc, char *argv[]) case 'M': mcastif = optarg; break; -#ifdef MIP6 - case 'm': - mobileip6 = 1; - break; -#endif case 'R': fprintf(stderr, "rtadvd: " "the -R option is currently ignored.\n"); @@ -193,85 +214,113 @@ main(int argc, char *argv[]) case 's': sflag = 1; break; + case 'p': + pidfilename = optarg; + break; + default: + usage(); } } argc -= optind; argv += optind; - if (argc == 0) { - fprintf(stderr, -#ifdef MIP6 - "usage: rtadvd [-dDfMmRs] [-c conffile] " -#else - "usage: rtadvd [-dDfMRs] [-c conffile] " -#endif - "interfaces...\n"); - exit(1); - } + + logopt = LOG_NDELAY | LOG_PID; + if (fflag) + logopt |= LOG_PERROR; + openlog("rtadvd", logopt, LOG_DAEMON); /* set log level */ - if (dflag == 0) - setlogmask(LOG_UPTO(LOG_ERR)); - if (dflag == 1) - setlogmask(LOG_UPTO(LOG_INFO)); + if (dflag > 2) + (void)setlogmask(LOG_UPTO(LOG_DEBUG)); + else if (dflag > 1) + (void)setlogmask(LOG_UPTO(LOG_INFO)); + else if (dflag > 0) + (void)setlogmask(LOG_UPTO(LOG_NOTICE)); + else + (void)setlogmask(LOG_UPTO(LOG_ERR)); /* timer initialization */ rtadvd_timer_init(); +#ifndef HAVE_ARC4RANDOM /* random value initialization */ - srandom((u_long)time(NULL)); +#ifdef __FreeBSD__ + srandomdev(); +#else + srandom((unsigned long)time(NULL)); +#endif +#endif + pfh = pidfile_open(pidfilename, 0600, &otherpid); + if (pfh == NULL) { + if (errno == EEXIST) + errx(1, "%s already running, pid: %d", + getprogname(), otherpid); + syslog(LOG_ERR, + "failed to open the pid file %s, run anyway.", + pidfilename); + } + if (!fflag) + daemon(1, 0); - /* get iflist block from kernel */ - init_iflist(); + sock_open(&sock); - while (argc--) - getconfig(*argv++); + update_ifinfo(&ifilist, UPDATE_IFINFO_ALL); + for (i = 0; i < argc; i++) + update_persist_ifinfo(&ifilist, argv[i]); - if (inet_pton(AF_INET6, ALLNODES, &sin6_allnodes.sin6_addr) != 1) { - fprintf(stderr, "fatal: inet_pton failed\n"); + csock_open(&ctrlsock, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (ctrlsock.si_fd == -1) { + syslog(LOG_ERR, "cannot open control socket: %s", + strerror(errno)); exit(1); } - sock_open(); - - if (!fflag) - daemon(1, 0); /* record the current PID */ pid = getpid(); - if ((pidfp = fopen(pidfilename, "w")) == NULL) { - syslog(LOG_ERR, - "<%s> failed to open a log file(%s), run anyway.", - __func__, pidfilename); - } else { - fprintf(pidfp, "%d\n", pid); - fclose(pidfp); - } + pidfile_write(pfh); - FD_ZERO(&fdset); - FD_SET(sock, &fdset); - maxfd = sock; + set[PFD_RAWSOCK].fd = sock.si_fd; + set[PFD_RAWSOCK].events = POLLIN; if (sflag == 0) { - rtsock_open(); - FD_SET(rtsock, &fdset); - if (rtsock > sock) - maxfd = rtsock; + rtsock_open(&rtsock); + set[PFD_RTSOCK].fd = rtsock.si_fd; + set[PFD_RTSOCK].events = POLLIN; } else - rtsock = -1; + set[PFD_RTSOCK].fd = -1; + set[PFD_CSOCK].fd = ctrlsock.si_fd; + set[PFD_CSOCK].events = POLLIN; + signal(SIGTERM, set_do_shutdown); + signal(SIGINT, set_do_shutdown); + signal(SIGHUP, set_do_reload); + + error = csock_listen(&ctrlsock); + if (error) { + syslog(LOG_ERR, "cannot listen control socket: %s", + strerror(errno)); + exit(1); + } - signal(SIGTERM, (void *)set_die); - signal(SIGUSR1, (void *)rtadvd_set_dump_file); + /* load configuration file */ + set_do_reload(0); while (1) { - struct fd_set select_fd = fdset; /* reinitialize */ + if (is_do_shutdown()) + rtadvd_shutdown(); - if (do_dump) { /* SIGUSR1 */ - do_dump = 0; - rtadvd_dump_file(dumpfilename); + if (is_do_reload()) { + loadconfig_ifname(reload_ifname()); + if (reload_ifname() == NULL) + syslog(LOG_INFO, + "configuration file reloaded."); + else + syslog(LOG_INFO, + "configuration file for %s reloaded.", + reload_ifname()); + reset_do_reload(); } - if (do_die) { - die(); - /*NOTREACHED*/ - } + /* timeout handler update for active interfaces */ + rtadvd_update_timeout_handler(); /* timer expiration check and reset the timer */ timeout = rtadvd_check_timer(); @@ -281,99 +330,176 @@ main(int argc, char *argv[]) "<%s> set timer to %ld:%ld. waiting for " "inputs or timeout", __func__, (long int)timeout->tv_sec, - (long int)timeout->tv_usec); + (long int)timeout->tv_nsec / 1000); } else { syslog(LOG_DEBUG, "<%s> there's no timer. waiting for inputs", __func__); } + if ((i = poll(set, sizeof(set)/sizeof(set[0]), + timeout ? (timeout->tv_sec * 1000 + + timeout->tv_nsec / 1000 / 1000) : INFTIM)) < 0) { - if ((i = select(maxfd + 1, &select_fd, - NULL, NULL, timeout)) < 0) { - /* EINTR would occur upon SIGUSR1 for status dump */ + /* EINTR would occur if a signal was delivered */ if (errno != EINTR) - syslog(LOG_ERR, "<%s> select: %s", - __func__, strerror(errno)); + syslog(LOG_ERR, "poll() failed: %s", + strerror(errno)); continue; } if (i == 0) /* timeout */ continue; - if (rtsock != -1 && FD_ISSET(rtsock, &select_fd)) - rtmsg_input(); - if (FD_ISSET(sock, &select_fd)) - rtadvd_input(); + if (rtsock.si_fd != -1 && set[PFD_RTSOCK].revents & POLLIN) + rtmsg_input(&rtsock); + + if (set[PFD_RAWSOCK].revents & POLLIN) + rtadvd_input(&sock); + + if (set[PFD_CSOCK].revents & POLLIN) { + int fd; + + fd = csock_accept(&ctrlsock); + if (fd == -1) + syslog(LOG_ERR, + "cannot accept() control socket: %s", + strerror(errno)); + else { + cm_handler_server(fd); + close(fd); + } + } } exit(0); /* NOTREACHED */ } static void -rtadvd_set_dump_file(void) +rtadvd_shutdown(void) { - do_dump = 1; -} - -static void -set_die(int sig) -{ - do_die = 1; -} + struct ifinfo *ifi; + struct rainfo *rai; + struct rdnss *rdn; + struct dnssl *dns; -static void -die(void) -{ - struct rainfo *ra; - int i; - const int retrans = MAX_FINAL_RTR_ADVERTISEMENTS; + if (wait_shutdown) { + syslog(LOG_INFO, + "waiting expiration of the all RA timers."); + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + /* + * Ignore !IFF_UP interfaces in waiting for shutdown. + */ + if (!(ifi->ifi_flags & IFF_UP) && + ifi->ifi_ra_timer != NULL) { + ifi->ifi_state = IFI_STATE_UNCONFIGURED; + rtadvd_remove_timer(ifi->ifi_ra_timer); + ifi->ifi_ra_timer = NULL; + syslog(LOG_DEBUG, "<%s> %s(idx=%d) is down. " + "Timer removed and marked as UNCONFIGURED.", + __func__, ifi->ifi_ifname, + ifi->ifi_ifindex); + } + } + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (ifi->ifi_ra_timer != NULL) + break; + } + if (ifi == NULL) { + syslog(LOG_NOTICE, "gracefully terminated."); + exit(0); + } - if (dflag > 1) { - syslog(LOG_DEBUG, "<%s> cease to be an advertising router\n", - __func__); + sleep(1); + return; } - for (ra = ralist; ra; ra = ra->next) { - ra->lifetime = 0; - make_packet(ra); + syslog(LOG_DEBUG, "<%s> cease to be an advertising router", + __func__); + + wait_shutdown = 1; + + TAILQ_FOREACH(rai, &railist, rai_next) { + rai->rai_lifetime = 0; + TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) + rdn->rd_ltime = 0; + TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) + dns->dn_ltime = 0; } - for (i = 0; i < retrans; i++) { - for (ra = ralist; ra; ra = ra->next) - ra_output(ra); - sleep(MIN_DELAY_BETWEEN_RAS); + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (!ifi->ifi_persist) + continue; + if (ifi->ifi_state == IFI_STATE_UNCONFIGURED) + continue; + if (ifi->ifi_ra_timer == NULL) + continue; + if (ifi->ifi_ra_lastsent.tv_sec == 0 && + ifi->ifi_ra_lastsent.tv_nsec == 0 && + ifi->ifi_ra_timer != NULL) { + /* + * When RA configured but never sent, + * ignore the IF immediately. + */ + rtadvd_remove_timer(ifi->ifi_ra_timer); + ifi->ifi_ra_timer = NULL; + ifi->ifi_state = IFI_STATE_UNCONFIGURED; + continue; + } + + ifi->ifi_state = IFI_STATE_TRANSITIVE; + + /* Mark as the shut-down state. */ + ifi->ifi_rainfo_trans = ifi->ifi_rainfo; + ifi->ifi_rainfo = NULL; + + ifi->ifi_burstcount = MAX_FINAL_RTR_ADVERTISEMENTS; + ifi->ifi_burstinterval = MIN_DELAY_BETWEEN_RAS; + + ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm); + rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm, + ifi->ifi_ra_timer); } - exit(0); - /*NOTREACHED*/ + syslog(LOG_NOTICE, "final RA transmission started."); + + pidfile_remove(pfh); + csock_close(&ctrlsock); } static void -rtmsg_input(void) +rtmsg_input(struct sockinfo *s) { int n, type, ifindex = 0, plen; size_t len; char msg[2048], *next, *lim; - u_char ifname[IF_NAMESIZE]; - struct prefix *prefix; + char ifname[IFNAMSIZ]; + struct if_announcemsghdr *ifan; + struct rt_msghdr *rtm; + struct prefix *pfx; struct rainfo *rai; struct in6_addr *addr; + struct ifinfo *ifi; char addrbuf[INET6_ADDRSTRLEN]; + int prefixchange = 0; - n = read(rtsock, msg, sizeof(msg)); - if (dflag > 1) { - syslog(LOG_DEBUG, "<%s> received a routing message " - "(type = %d, len = %d)", __func__, rtmsg_type(msg), n); + if (s == NULL) { + syslog(LOG_ERR, "<%s> internal error", __func__); + exit(1); } - if (n > rtmsg_len(msg)) { + n = read(s->si_fd, msg, sizeof(msg)); + rtm = (struct rt_msghdr *)msg; + syslog(LOG_DEBUG, "<%s> received a routing message " + "(type = %d, len = %d)", __func__, rtm->rtm_type, n); + + if (n > rtm->rtm_msglen) { /* - * This usually won't happen for messages received on + * This usually won't happen for messages received on * a routing socket. */ - if (dflag > 1) - syslog(LOG_DEBUG, - "<%s> received data length is larger than " - "1st routing message len. multiple messages? " - "read %d bytes, but 1st msg len = %d", - __func__, n, rtmsg_len(msg)); + syslog(LOG_DEBUG, + "<%s> received data length is larger than " + "1st routing message len. multiple messages? " + "read %d bytes, but 1st msg len = %d", + __func__, n, rtm->rtm_msglen); #if 0 /* adjust length */ - n = rtmsg_len(msg); + n = rtm->rtm_msglen; #endif } @@ -382,14 +508,15 @@ rtmsg_input(void) 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)); + RTADV_TYPE2BITMASK(RTM_ADD) | + RTADV_TYPE2BITMASK(RTM_DELETE) | + RTADV_TYPE2BITMASK(RTM_NEWADDR) | + RTADV_TYPE2BITMASK(RTM_DELADDR) | + RTADV_TYPE2BITMASK(RTM_IFINFO) | + RTADV_TYPE2BITMASK(RTM_IFANNOUNCE)); if (len == 0) break; - type = rtmsg_type(next); + type = ((struct rt_msghdr *)next)->rtm_type; switch (type) { case RTM_ADD: case RTM_DELETE: @@ -397,40 +524,82 @@ rtmsg_input(void) break; case RTM_NEWADDR: case RTM_DELADDR: - ifindex = get_ifam_ifindex(next); + ifindex = (int)((struct ifa_msghdr *)next)->ifam_index; break; case RTM_IFINFO: - ifindex = get_ifm_ifindex(next); + ifindex = (int)((struct if_msghdr *)next)->ifm_index; break; - default: - /* should not reach here */ - if (dflag > 1) { + case RTM_IFANNOUNCE: + ifan = (struct if_announcemsghdr *)next; + switch (ifan->ifan_what) { + case IFAN_ARRIVAL: + case IFAN_DEPARTURE: + break; + default: syslog(LOG_DEBUG, - "<%s:%d> unknown rtmsg %d on %s", - __func__, __LINE__, type, - if_indextoname(ifindex, ifname)); + "<%s:%d> unknown ifan msg (ifan_what=%d)", + __func__, __LINE__, ifan->ifan_what); + continue; } - continue; - } - if ((rai = if_indextorainfo(ifindex)) == NULL) { - if (dflag > 1) { - syslog(LOG_DEBUG, - "<%s> route changed on " - "non advertising interface(%s)", - __func__, - if_indextoname(ifindex, ifname)); + syslog(LOG_DEBUG, "<%s>: if_announcemsg (idx=%d:%d)", + __func__, ifan->ifan_index, ifan->ifan_what); + switch (ifan->ifan_what) { + case IFAN_ARRIVAL: + syslog(LOG_NOTICE, + "interface added (idx=%d)", + ifan->ifan_index); + update_ifinfo(&ifilist, ifan->ifan_index); + loadconfig_index(ifan->ifan_index); + break; + case IFAN_DEPARTURE: + syslog(LOG_NOTICE, + "interface removed (idx=%d)", + ifan->ifan_index); + rm_ifinfo_index(ifan->ifan_index); + + /* Clear ifi_ifindex */ + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (ifi->ifi_ifindex + == ifan->ifan_index) { + ifi->ifi_ifindex = 0; + break; + } + } + update_ifinfo(&ifilist, ifan->ifan_index); + break; } continue; + default: + /* should not reach here */ + syslog(LOG_DEBUG, + "<%s:%d> unknown rtmsg %d on %s", + __func__, __LINE__, type, + if_indextoname(ifindex, ifname)); + continue; + } + ifi = if_indextoifinfo(ifindex); + if (ifi == NULL) { + syslog(LOG_DEBUG, + "<%s> ifinfo not found for idx=%d. Why?", + __func__, ifindex); + continue; } - oldifflags = iflist[ifindex]->ifm_flags; + rai = ifi->ifi_rainfo; + if (rai == NULL) { + syslog(LOG_DEBUG, + "<%s> route changed on " + "non advertising interface(%s)", + __func__, ifi->ifi_ifname); + continue; + } + + oldifflags = ifi->ifi_flags; + /* init ifflags because it may have changed */ + update_ifinfo(&ifilist, ifindex); switch (type) { case RTM_ADD: - /* init ifflags because it may have changed */ - iflist[ifindex]->ifm_flags = - if_getflags(ifindex, iflist[ifindex]->ifm_flags); - if (sflag) break; /* we aren't interested in prefixes */ @@ -444,33 +613,31 @@ rtmsg_input(void) __func__, plen); break; } - prefix = find_prefix(rai, addr, plen); - if (prefix) { - if (prefix->timer) { + pfx = find_prefix(rai, addr, plen); + if (pfx) { + if (pfx->pfx_timer) { /* * If the prefix has been invalidated, * make it available again. */ - update_prefix(prefix); - } else if (dflag > 1) { + update_prefix(pfx); + prefixchange = 1; + } else syslog(LOG_DEBUG, "<%s> new prefix(%s/%d) " "added on %s, " "but it was already in list", __func__, inet_ntop(AF_INET6, addr, - (char *)addrbuf, INET6_ADDRSTRLEN), - plen, rai->ifname); - } + (char *)addrbuf, + sizeof(addrbuf)), + plen, ifi->ifi_ifname); break; } make_prefix(rai, ifindex, addr, plen); + prefixchange = 1; break; case RTM_DELETE: - /* init ifflags because it may have changed */ - iflist[ifindex]->ifm_flags = - if_getflags(ifindex, iflist[ifindex]->ifm_flags); - if (sflag) break; @@ -485,61 +652,64 @@ rtmsg_input(void) __func__, plen); break; } - prefix = find_prefix(rai, addr, plen); - if (prefix == NULL) { - if (dflag > 1) { - syslog(LOG_DEBUG, - "<%s> prefix(%s/%d) was " - "deleted on %s, " - "but it was not in list", - __func__, - inet_ntop(AF_INET6, addr, - (char *)addrbuf, INET6_ADDRSTRLEN), - plen, rai->ifname); - } + pfx = find_prefix(rai, addr, plen); + if (pfx == NULL) { + syslog(LOG_DEBUG, + "<%s> prefix(%s/%d) was deleted on %s, " + "but it was not in list", + __func__, inet_ntop(AF_INET6, addr, + (char *)addrbuf, sizeof(addrbuf)), + plen, ifi->ifi_ifname); break; } - invalidate_prefix(prefix); + invalidate_prefix(pfx); + prefixchange = 1; break; case RTM_NEWADDR: case RTM_DELADDR: - /* init ifflags because it may have changed */ - iflist[ifindex]->ifm_flags = - if_getflags(ifindex, iflist[ifindex]->ifm_flags); - break; case RTM_IFINFO: - iflist[ifindex]->ifm_flags = get_ifm_flags(next); break; default: /* should not reach here */ - if (dflag > 1) { - syslog(LOG_DEBUG, - "<%s:%d> unknown rtmsg %d on %s", - __func__, __LINE__, type, - if_indextoname(ifindex, ifname)); - } + syslog(LOG_DEBUG, + "<%s:%d> unknown rtmsg %d on %s", + __func__, __LINE__, type, + if_indextoname(ifindex, ifname)); return; } /* check if an interface flag is changed */ - if ((oldifflags & IFF_UP) != 0 && /* UP to DOWN */ - (iflist[ifindex]->ifm_flags & IFF_UP) == 0) { - syslog(LOG_INFO, - "<%s> interface %s becomes down. stop timer.", - __func__, rai->ifname); - rtadvd_remove_timer(&rai->timer); - } else if ((oldifflags & IFF_UP) == 0 && /* DOWN to UP */ - (iflist[ifindex]->ifm_flags & IFF_UP) != 0) { - syslog(LOG_INFO, - "<%s> interface %s becomes up. restart timer.", - __func__, rai->ifname); - - rai->initcounter = 0; /* reset the counter */ - rai->waiting = 0; /* XXX */ - rai->timer = rtadvd_add_timer(ra_timeout, - ra_timer_update, rai, rai); - ra_timer_update(rai, &rai->timer->tm); - rtadvd_set_timer(&rai->timer->tm, rai->timer); + if ((oldifflags & IFF_UP) && /* UP to DOWN */ + !(ifi->ifi_flags & IFF_UP)) { + syslog(LOG_NOTICE, + "ifi_ifname); + rtadvd_remove_timer(ifi->ifi_ra_timer); + ifi->ifi_ra_timer = NULL; + } else if (!(oldifflags & IFF_UP) && /* DOWN to UP */ + (ifi->ifi_flags & IFF_UP)) { + syslog(LOG_NOTICE, + "interface %s becomes up. restart timer.", + ifi->ifi_ifname); + + ifi->ifi_state = IFI_STATE_TRANSITIVE; + ifi->ifi_burstcount = + MAX_INITIAL_RTR_ADVERTISEMENTS; + ifi->ifi_burstinterval = + MAX_INITIAL_RTR_ADVERT_INTERVAL; + + ifi->ifi_ra_timer = rtadvd_add_timer(ra_timeout, + ra_timer_update, ifi, ifi); + ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm); + rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm, + ifi->ifi_ra_timer); + } else if (prefixchange && + (ifi->ifi_flags & IFF_UP)) { + /* + * An advertised prefix has been added or invalidated. + * Will notice the change in a short delay. + */ + set_short_delay(ifi); } } @@ -547,27 +717,34 @@ rtmsg_input(void) } void -rtadvd_input(void) +rtadvd_input(struct sockinfo *s) { - int i; + ssize_t i; int *hlimp = NULL; #ifdef OLDRAWSOCKET struct ip6_hdr *ip; -#endif +#endif struct icmp6_hdr *icp; int ifindex = 0; struct cmsghdr *cm; struct in6_pktinfo *pi = NULL; - u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; struct in6_addr dst = in6addr_any; + struct ifinfo *ifi; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + if (s == NULL) { + syslog(LOG_ERR, "<%s> internal error", __func__); + exit(1); + } /* * 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) + if ((i = recvmsg(s->si_fd, &rcvmhdr, 0)) < 0) return; /* extract optional information via Advanced API */ @@ -587,45 +764,39 @@ rtadvd_input(void) hlimp = (int *)CMSG_DATA(cm); } if (ifindex == 0) { - syslog(LOG_ERR, - "<%s> failed to get receiving interface", - __func__); + syslog(LOG_ERR, "failed to get receiving interface"); return; } if (hlimp == NULL) { - syslog(LOG_ERR, - "<%s> failed to get receiving hop limit", - __func__); + syslog(LOG_ERR, "failed to get receiving hop limit"); return; } /* - * If we happen to receive data on an interface which is now down, - * just discard the data. + * If we happen to receive data on an interface which is now gone + * or down, just discard the data. */ - if ((iflist[pi->ipi6_ifindex]->ifm_flags & IFF_UP) == 0) { + ifi = if_indextoifinfo(pi->ipi6_ifindex); + if (ifi == NULL || !(ifi->ifi_flags & IFF_UP)) { syslog(LOG_INFO, - "<%s> received data on a disabled interface (%s)", - __func__, - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + "<%s> received data on a disabled interface (%s)", + __func__, + (ifi == NULL) ? "[gone]" : ifi->ifi_ifname); return; } #ifdef OLDRAWSOCKET - if (i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) { + if ((size_t)i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) { syslog(LOG_ERR, - "<%s> packet size(%d) is too short", - __func__, i); + "packet size(%d) is too short", i); return; } ip = (struct ip6_hdr *)rcvmhdr.msg_iov[0].iov_base; icp = (struct icmp6_hdr *)(ip + 1); /* XXX: ext. hdr? */ #else - if (i < sizeof(struct icmp6_hdr)) { - syslog(LOG_ERR, - "<%s> packet size(%d) is too short", - __func__, i); + if ((size_t)i < sizeof(struct icmp6_hdr)) { + syslog(LOG_ERR, "packet size(%zd) is too short", i); return; } @@ -635,88 +806,94 @@ rtadvd_input(void) switch (icp->icmp6_type) { case ND_ROUTER_SOLICIT: /* - * Message verification - RFC-2461 6.1.1 + * Message verification - RFC 4861 6.1.1 * XXX: these checks must be done in the kernel as well, * but we can't completely rely on them. */ if (*hlimp != 255) { syslog(LOG_NOTICE, - "<%s> RS with invalid hop limit(%d) " + "RS with invalid hop limit(%d) " "received from %s on %s", - __func__, *hlimp, - inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, - INET6_ADDRSTRLEN), + *hlimp, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } if (icp->icmp6_code) { syslog(LOG_NOTICE, - "<%s> RS with invalid ICMP6 code(%d) " + "RS with invalid ICMP6 code(%d) " "received from %s on %s", - __func__, icp->icmp6_code, - inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, - INET6_ADDRSTRLEN), + icp->icmp6_code, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } - if (i < sizeof(struct nd_router_solicit)) { + if ((size_t)i < sizeof(struct nd_router_solicit)) { syslog(LOG_NOTICE, - "<%s> RS from %s on %s does not have enough " - "length (len = %d)", - __func__, - inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, - INET6_ADDRSTRLEN), + "RS from %s on %s does not have enough " + "length (len = %zd)", + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf), i); return; } - rs_input(i, (struct nd_router_solicit *)icp, pi, &from); + 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... + * Message verification - RFC 4861 6.1.2 + * XXX: there's the same dilemma as above... */ + if (!IN6_IS_ADDR_LINKLOCAL(&rcvfrom.sin6_addr)) { + syslog(LOG_NOTICE, + "RA with non-linklocal source address " + "received from %s on %s", + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, + ntopbuf, sizeof(ntopbuf)), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } if (*hlimp != 255) { syslog(LOG_NOTICE, - "<%s> RA with invalid hop limit(%d) " + "RA with invalid hop limit(%d) " "received from %s on %s", - __func__, *hlimp, - inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, - INET6_ADDRSTRLEN), + *hlimp, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } if (icp->icmp6_code) { syslog(LOG_NOTICE, - "<%s> RA with invalid ICMP6 code(%d) " + "RA with invalid ICMP6 code(%d) " "received from %s on %s", - __func__, icp->icmp6_code, - inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, - INET6_ADDRSTRLEN), + icp->icmp6_code, + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } - if (i < sizeof(struct nd_router_advert)) { + if ((size_t)i < sizeof(struct nd_router_advert)) { syslog(LOG_NOTICE, - "<%s> RA from %s on %s does not have enough " - "length (len = %d)", - __func__, - inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, - INET6_ADDRSTRLEN), + "RA from %s on %s does not have enough " + "length (len = %zd)", + inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf), i); return; } - ra_input(i, (struct nd_router_advert *)icp, pi, &from); + ra_input(i, (struct nd_router_advert *)icp, pi, &rcvfrom); break; case ICMP6_ROUTER_RENUMBERING: - if (accept_rr == 0) { - syslog(LOG_ERR, "<%s> received a router renumbering " - "message, but not allowed to be accepted", - __func__); + if (mcastif == NULL) { + syslog(LOG_ERR, "received a router renumbering " + "message, but not allowed to be accepted"); break; } - rr_input(i, (struct icmp6_router_renum *)icp, pi, &from, - &dst); + rr_input(i, (struct icmp6_router_renum *)icp, pi, &rcvfrom, + &dst); break; default: /* @@ -725,8 +902,7 @@ rtadvd_input(void) * could receive message after opening the socket and * before setting ICMP6 type filter(see sock_open()). */ - syslog(LOG_ERR, "<%s> invalid icmp type(%d)", - __func__, icp->icmp6_type); + syslog(LOG_ERR, "invalid icmp type(%d)", icp->icmp6_type); return; } @@ -737,53 +913,57 @@ static void rs_input(int len, struct nd_router_solicit *rs, struct in6_pktinfo *pi, struct sockaddr_in6 *from) { - u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; - union nd_opts ndopts; - struct rainfo *ra; + char ntopbuf[INET6_ADDRSTRLEN]; + char ifnamebuf[IFNAMSIZ]; + union nd_opt ndopts; + struct rainfo *rai; + struct ifinfo *ifi; + struct soliciter *sol; syslog(LOG_DEBUG, - "<%s> RS received from %s on %s", - __func__, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + "<%s> RS received from %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); /* ND option check */ memset(&ndopts, 0, sizeof(ndopts)); + TAILQ_INIT(&ndopts.opt_list); if (nd6_options((struct nd_opt_hdr *)(rs + 1), len - sizeof(struct nd_router_solicit), - &ndopts, NDOPT_FLAG_SRCLINKADDR)) { - syslog(LOG_DEBUG, - "<%s> ND option check failed for an RS from %s on %s", - __func__, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + &ndopts, NDOPT_FLAG_SRCLINKADDR)) { + syslog(LOG_INFO, + "<%s> ND option check failed for an RS from %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), + 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) + * (RFC 4861 6.1.1) */ if (IN6_IS_ADDR_UNSPECIFIED(&from->sin6_addr) && - ndopts.nd_opts_src_lladdr) { - syslog(LOG_ERR, - "<%s> RS from unspecified src on %s has a link-layer" - " address option", - __func__, - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + ndopts.opt_src_lladdr) { + syslog(LOG_INFO, + "<%s> RS from unspecified src on %s has a link-layer" + " address option", + __func__, if_indextoname(pi->ipi6_ifindex, ifnamebuf)); goto done; } - ra = ralist; - while (ra != NULL) { - if (pi->ipi6_ifindex == ra->ifindex) - break; - ra = ra->next; + ifi = if_indextoifinfo(pi->ipi6_ifindex); + if (ifi == NULL) { + syslog(LOG_INFO, + "<%s> if (idx=%d) not found. Why?", + __func__, pi->ipi6_ifindex); + goto done; } - if (ra == NULL) { + rai = ifi->ifi_rainfo; + if (rai == NULL) { syslog(LOG_INFO, "<%s> RS received on non advertising interface(%s)", __func__, @@ -791,73 +971,30 @@ rs_input(int len, struct nd_router_solicit *rs, goto done; } - ra->rsinput++; /* increment statistics */ + rai->rai_ifinfo->ifi_rsinput++; /* * Decide whether to send RA according to the rate-limit * consideration. */ - { - long delay; /* must not be greater than 1000000 */ - struct timeval interval, now, min_delay, tm_tmp, *rest; - struct soliciter *sol; - /* - * 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->next; - } - - /* - * If there is already a waiting RS packet, don't - * update the timer. - */ - if (ra->waiting++) - goto done; - - /* - * Compute a random delay. If the computed value - * corresponds to a time later than the time the next - * multicast RA is scheduled to be sent, ignore the random - * delay and send the advertisement at the - * already-scheduled time. RFC-2461 6.2.6 - */ - delay = random() % MAX_RA_DELAY_TIME; - interval.tv_sec = 0; - interval.tv_usec = delay; - rest = rtadvd_timer_rest(ra->timer); - if (TIMEVAL_LT(*rest, interval)) { - syslog(LOG_DEBUG, - "<%s> random delay is larger than " - "the rest of normal timer", - __func__); - interval = *rest; - } + /* record sockaddr waiting for RA, if possible */ + sol = (struct soliciter *)malloc(sizeof(*sol)); + if (sol) { + sol->sol_addr = *from; + /* XXX RFC 2553 need clarification on flowinfo */ + sol->sol_addr.sin6_flowinfo = 0; + TAILQ_INSERT_TAIL(&rai->rai_soliciter, sol, sol_next); + } - /* - * If we sent a multicast Router Advertisement within - * the last MIN_DELAY_BETWEEN_RAS seconds, schedule - * the advertisement to be sent at a time corresponding to - * MIN_DELAY_BETWEEN_RAS plus the random value after the - * previous advertisement was sent. - */ - gettimeofday(&now, NULL); - TIMEVAL_SUB(&now, &ra->lastsent, &tm_tmp); - min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS; - min_delay.tv_usec = 0; - if (TIMEVAL_LT(tm_tmp, min_delay)) { - TIMEVAL_SUB(&min_delay, &tm_tmp, &min_delay); - TIMEVAL_ADD(&min_delay, &interval, &interval); - } - rtadvd_set_timer(&interval, ra->timer); + /* + * If there is already a waiting RS packet, don't + * update the timer. + */ + if (ifi->ifi_rs_waitcount++) goto done; - } + + set_short_delay(ifi); done: free_ndopts(&ndopts); @@ -865,212 +1002,287 @@ rs_input(int len, struct nd_router_solicit *rs, } static void -ra_input(int len, struct nd_router_advert *ra, +set_short_delay(struct ifinfo *ifi) +{ + long delay; /* must not be greater than 1000000 */ + struct timespec interval, now, min_delay, tm_tmp, *rest; + + if (ifi->ifi_ra_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 4861 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_nsec = delay * 1000; + rest = rtadvd_timer_rest(ifi->ifi_ra_timer); + if (TS_CMP(rest, &interval, <)) { + syslog(LOG_DEBUG, "<%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. + */ + clock_gettime(CLOCK_MONOTONIC_FAST, &now); + TS_SUB(&now, &ifi->ifi_ra_lastsent, &tm_tmp); + min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS; + min_delay.tv_nsec = 0; + if (TS_CMP(&tm_tmp, &min_delay, <)) { + TS_SUB(&min_delay, &tm_tmp, &min_delay); + TS_ADD(&min_delay, &interval, &interval); + } + rtadvd_set_timer(&interval, ifi->ifi_ra_timer); +} + +static int +check_accept_rtadv(int idx) +{ + struct ifinfo *ifi; + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (ifi->ifi_ifindex == idx) + break; + } + if (ifi == NULL) { + syslog(LOG_DEBUG, + "<%s> if (idx=%d) not found. Why?", + __func__, idx); + return (0); + } +#if (__FreeBSD_version < 900000) + /* + * RA_RECV: !ip6.forwarding && ip6.accept_rtadv + * RA_SEND: ip6.forwarding + */ + return ((getinet6sysctl(IPV6CTL_FORWARDING) == 0) && + (getinet6sysctl(IPV6CTL_ACCEPT_RTADV) == 1)); +#else + /* + * RA_RECV: ND6_IFF_ACCEPT_RTADV + * RA_SEND: ip6.forwarding + */ + if (update_ifinfo_nd_flags(ifi) != 0) { + syslog(LOG_ERR, "cannot get nd6 flags (idx=%d)", idx); + return (0); + } + + return (ifi->ifi_nd_flags & ND6_IFF_ACCEPT_RTADV); +#endif +} + +static void +ra_input(int len, struct nd_router_advert *nra, struct in6_pktinfo *pi, struct sockaddr_in6 *from) { struct rainfo *rai; - u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; - union nd_opts ndopts; - char *on_off[] = {"OFF", "ON"}; - u_int32_t reachabletime, retranstimer, mtu; + struct ifinfo *ifi; + char ntopbuf[INET6_ADDRSTRLEN]; + char ifnamebuf[IFNAMSIZ]; + union nd_opt ndopts; + const char *on_off[] = {"OFF", "ON"}; + uint32_t reachabletime, retranstimer, mtu; int inconsistent = 0; + int error; + + syslog(LOG_DEBUG, "<%s> RA received from %s on %s", __func__, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); - syslog(LOG_DEBUG, - "<%s> RA received from %s on %s", - __func__, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); - /* ND option check */ memset(&ndopts, 0, sizeof(ndopts)); - 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)) { - syslog(LOG_ERR, - "<%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)); + TAILQ_INIT(&ndopts.opt_list); + error = nd6_options((struct nd_opt_hdr *)(nra + 1), + len - sizeof(struct nd_router_advert), &ndopts, + NDOPT_FLAG_SRCLINKADDR | NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU | + NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL); + if (error) { + syslog(LOG_INFO, + "<%s> ND option check failed for an RA from %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, + ifnamebuf)); return; } /* - * RA consistency check according to RFC-2461 6.2.7 + * RA consistency check according to RFC 4861 6.2.7 */ - if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == NULL) { + ifi = if_indextoifinfo(pi->ipi6_ifindex); + if (ifi->ifi_rainfo == NULL) { syslog(LOG_INFO, - "<%s> received RA from %s on non-advertising" - " interface(%s)", - __func__, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + "<%s> received RA from %s on non-advertising" + " interface(%s)", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, + ifnamebuf)); goto done; } - rai->rainput++; /* increment statistics */ - + rai = ifi->ifi_rainfo; + ifi->ifi_rainput++; + syslog(LOG_DEBUG, "<%s> ifi->ifi_rainput = %" PRIu64, __func__, + ifi->ifi_rainput); + /* Cur Hop Limit value */ - if (ra->nd_ra_curhoplimit && rai->hoplimit && - ra->nd_ra_curhoplimit != rai->hoplimit) { - syslog(LOG_INFO, - "<%s> CurHopLimit inconsistent on %s:" - " %d from %s, %d from us", - __func__, - rai->ifname, - ra->nd_ra_curhoplimit, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - rai->hoplimit); + if (nra->nd_ra_curhoplimit && rai->rai_hoplimit && + nra->nd_ra_curhoplimit != rai->rai_hoplimit) { + syslog(LOG_NOTICE, + "CurHopLimit inconsistent on %s:" + " %d from %s, %d from us", + ifi->ifi_ifname, nra->nd_ra_curhoplimit, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), rai->rai_hoplimit); inconsistent++; } /* M flag */ - if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != - rai->managedflg) { - syslog(LOG_INFO, - "<%s> M flag inconsistent on %s:" - " %s from %s, %s from us", - __func__, - rai->ifname, - on_off[!rai->managedflg], - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - on_off[rai->managedflg]); + if ((nra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != + rai->rai_managedflg) { + syslog(LOG_NOTICE, + "M flag inconsistent on %s:" + " %s from %s, %s from us", + ifi->ifi_ifname, on_off[!rai->rai_managedflg], + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), on_off[rai->rai_managedflg]); inconsistent++; } /* O flag */ - if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != - rai->otherflg) { - syslog(LOG_INFO, - "<%s> O flag inconsistent on %s:" - " %s from %s, %s from us", - __func__, - rai->ifname, - on_off[!rai->otherflg], - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - on_off[rai->otherflg]); + if ((nra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != + rai->rai_otherflg) { + syslog(LOG_NOTICE, + "O flag inconsistent on %s:" + " %s from %s, %s from us", + ifi->ifi_ifname, on_off[!rai->rai_otherflg], + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), on_off[rai->rai_otherflg]); inconsistent++; } /* Reachable Time */ - reachabletime = ntohl(ra->nd_ra_reachable); - if (reachabletime && rai->reachabletime && - reachabletime != rai->reachabletime) { - syslog(LOG_INFO, - "<%s> ReachableTime inconsistent on %s:" - " %d from %s, %d from us", - __func__, - rai->ifname, - reachabletime, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - rai->reachabletime); + reachabletime = ntohl(nra->nd_ra_reachable); + if (reachabletime && rai->rai_reachabletime && + reachabletime != rai->rai_reachabletime) { + syslog(LOG_NOTICE, + "ReachableTime inconsistent on %s:" + " %d from %s, %d from us", + ifi->ifi_ifname, reachabletime, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), rai->rai_reachabletime); inconsistent++; } /* Retrans Timer */ - retranstimer = ntohl(ra->nd_ra_retransmit); - if (retranstimer && rai->retranstimer && - retranstimer != rai->retranstimer) { - syslog(LOG_INFO, - "<%s> RetranceTimer inconsistent on %s:" - " %d from %s, %d from us", - __func__, - rai->ifname, - retranstimer, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - rai->retranstimer); + retranstimer = ntohl(nra->nd_ra_retransmit); + if (retranstimer && rai->rai_retranstimer && + retranstimer != rai->rai_retranstimer) { + syslog(LOG_NOTICE, + "RetranceTimer inconsistent on %s:" + " %d from %s, %d from us", + ifi->ifi_ifname, retranstimer, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), rai->rai_retranstimer); inconsistent++; } /* Values in the MTU options */ - if (ndopts.nd_opts_mtu) { - mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); - if (mtu && rai->linkmtu && mtu != rai->linkmtu) { - syslog(LOG_INFO, - "<%s> MTU option value inconsistent on %s:" - " %d from %s, %d from us", - __func__, - rai->ifname, mtu, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - rai->linkmtu); + if (ndopts.opt_mtu) { + mtu = ntohl(ndopts.opt_mtu->nd_opt_mtu_mtu); + if (mtu && rai->rai_linkmtu && mtu != rai->rai_linkmtu) { + syslog(LOG_NOTICE, + "MTU option value inconsistent on %s:" + " %d from %s, %d from us", + ifi->ifi_ifname, mtu, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), rai->rai_linkmtu); inconsistent++; } } /* Preferred and Valid Lifetimes for prefixes */ { - struct nd_optlist *optp = ndopts.nd_opts_list; + struct nd_optlist *nol; - if (ndopts.nd_opts_pi) { - if (prefix_check(ndopts.nd_opts_pi, rai, from)) + if (ndopts.opt_pi) + if (prefix_check(ndopts.opt_pi, rai, from)) inconsistent++; - } - while (optp) { - if (prefix_check((struct nd_opt_prefix_info *)optp->opt, - rai, from)) + + TAILQ_FOREACH(nol, &ndopts.opt_list, nol_next) + if (prefix_check((struct nd_opt_prefix_info *)nol->nol_opt, + rai, from)) inconsistent++; - optp = optp->next; - } } if (inconsistent) - rai->rainconsistent++; - -done: + ifi->ifi_rainconsistent++; + + done: free_ndopts(&ndopts); return; } +static uint32_t +udiff(uint32_t u, uint32_t v) +{ + return (u >= v ? u - v : v - u); +} + /* 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) + struct rainfo *rai, struct sockaddr_in6 *from) { - u_int32_t preferred_time, valid_time; - struct prefix *pp; + struct ifinfo *ifi; + uint32_t preferred_time, valid_time; + struct prefix *pfx; int inconsistent = 0; - u_char ntopbuf[INET6_ADDRSTRLEN], prefixbuf[INET6_ADDRSTRLEN]; - struct timeval now; + char ntopbuf[INET6_ADDRSTRLEN]; + char prefixbuf[INET6_ADDRSTRLEN]; + struct timespec now; #if 0 /* impossible */ if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION) - return(0); + return (0); #endif - + ifi = rai->rai_ifinfo; /* * log if the adveritsed prefix has link-local scope(sanity check?) */ - if (IN6_IS_ADDR_LINKLOCAL(&pinfo->nd_opt_pi_prefix)) { + if (IN6_IS_ADDR_LINKLOCAL(&pinfo->nd_opt_pi_prefix)) syslog(LOG_INFO, - "<%s> link-local prefix %s/%d is advertised " - "from %s on %s", - __func__, - inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, - prefixbuf, INET6_ADDRSTRLEN), - pinfo->nd_opt_pi_prefix_len, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - rai->ifname); - } - - if ((pp = find_prefix(rai, &pinfo->nd_opt_pi_prefix, - pinfo->nd_opt_pi_prefix_len)) == NULL) { + "<%s> link-local prefix %s/%d is advertised " + "from %s on %s", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, + sizeof(prefixbuf)), + pinfo->nd_opt_pi_prefix_len, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), ifi->ifi_ifname); + + if ((pfx = find_prefix(rai, &pinfo->nd_opt_pi_prefix, + pinfo->nd_opt_pi_prefix_len)) == NULL) { syslog(LOG_INFO, - "<%s> prefix %s/%d from %s on %s is not in our list", - __func__, - inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, - prefixbuf, INET6_ADDRSTRLEN), - pinfo->nd_opt_pi_prefix_len, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - rai->ifname); - return(0); + "<%s> prefix %s/%d from %s on %s is not in our list", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, + sizeof(prefixbuf)), + pinfo->nd_opt_pi_prefix_len, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), ifi->ifi_ifname); + return (0); } preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time); - if (pp->pltimeexpire) { + if (pfx->pfx_pltimeexpire) { /* * The lifetime is decremented in real time, so we should * compare the expiration time. @@ -1078,129 +1290,138 @@ prefix_check(struct nd_opt_prefix_info *pinfo, * XXX: can we really expect that all routers on the link * have synchronized clocks? */ - gettimeofday(&now, NULL); + clock_gettime(CLOCK_MONOTONIC_FAST, &now); preferred_time += now.tv_sec; - if (rai->clockskew && - abs(preferred_time - pp->pltimeexpire) > rai->clockskew) { + if (!pfx->pfx_timer && rai->rai_clockskew && + udiff(preferred_time, pfx->pfx_pltimeexpire) > rai->rai_clockskew) { syslog(LOG_INFO, - "<%s> prefeerred 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); + "<%s> preferred lifetime for %s/%d" + " (decr. in real time) inconsistent on %s:" + " %" PRIu32 " from %s, %" PRIu32 " from us", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, + sizeof(prefixbuf)), + pinfo->nd_opt_pi_prefix_len, + ifi->ifi_ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), pfx->pfx_pltimeexpire); inconsistent++; } - } else if (preferred_time != pp->preflifetime) { + } else if (!pfx->pfx_timer && preferred_time != pfx->pfx_preflifetime) syslog(LOG_INFO, - "<%s> prefeerred 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); - } + "<%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, + sizeof(prefixbuf)), + pinfo->nd_opt_pi_prefix_len, + ifi->ifi_ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), pfx->pfx_preflifetime); valid_time = ntohl(pinfo->nd_opt_pi_valid_time); - if (pp->vltimeexpire) { - gettimeofday(&now, NULL); + if (pfx->pfx_vltimeexpire) { + clock_gettime(CLOCK_MONOTONIC_FAST, &now); valid_time += now.tv_sec; - if (rai->clockskew && - abs(valid_time - pp->vltimeexpire) > rai->clockskew) { + if (!pfx->pfx_timer && rai->rai_clockskew && + udiff(valid_time, pfx->pfx_vltimeexpire) > rai->rai_clockskew) { syslog(LOG_INFO, - "<%s> valid lifetime for %s/%d" - " (decr. in real time) inconsistent on %s:" - " %d from %s, %ld from us", - __func__, - inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, - prefixbuf, INET6_ADDRSTRLEN), - pinfo->nd_opt_pi_prefix_len, - rai->ifname, preferred_time, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - pp->vltimeexpire); + "<%s> valid lifetime for %s/%d" + " (decr. in real time) inconsistent on %s:" + " %d from %s, %" PRIu32 " from us", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, + sizeof(prefixbuf)), + pinfo->nd_opt_pi_prefix_len, + ifi->ifi_ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), pfx->pfx_vltimeexpire); inconsistent++; } - } else if (valid_time != pp->validlifetime) { + } else if (!pfx->pfx_timer && valid_time != pfx->pfx_validlifetime) { syslog(LOG_INFO, - "<%s> valid lifetime for %s/%d" - " inconsistent on %s:" - " %d from %s, %d from us", - __func__, - inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, - prefixbuf, INET6_ADDRSTRLEN), - pinfo->nd_opt_pi_prefix_len, - rai->ifname, valid_time, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - pp->validlifetime); + "<%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, + sizeof(prefixbuf)), + pinfo->nd_opt_pi_prefix_len, + ifi->ifi_ifname, valid_time, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), pfx->pfx_validlifetime); inconsistent++; } - return(inconsistent); + return (inconsistent); } struct prefix * find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen) { - struct prefix *pp; + struct prefix *pfx; int bytelen, bitlen; + char bitmask; - for (pp = rai->prefix.next; pp != &rai->prefix; pp = pp->next) { - if (plen != pp->prefixlen) + TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { + if (plen != pfx->pfx_prefixlen) continue; + bytelen = plen / 8; bitlen = plen % 8; - if (memcmp((void *)prefix, (void *)&pp->prefix, bytelen)) + bitmask = 0xff << (8 - bitlen); + + if (memcmp((void *)prefix, (void *)&pfx->pfx_prefix, bytelen)) continue; - if (prefix->s6_addr[bytelen] >> (8 - bitlen) == - pp->prefix.s6_addr[bytelen] >> (8 - bitlen)) - return(pp); + + if (bitlen == 0 || + ((prefix->s6_addr[bytelen] & bitmask) == + (pfx->pfx_prefix.s6_addr[bytelen] & bitmask))) { + return (pfx); + } } - return(NULL); + 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) + struct in6_addr *p1, int plen1) { int bytelen, bitlen; + char bitmask; if (plen0 < plen1) - return(0); + return (0); + bytelen = plen1 / 8; bitlen = plen1 % 8; + bitmask = 0xff << (8 - bitlen); + if (memcmp((void *)p0, (void *)p1, bytelen)) - return(0); - if (p0->s6_addr[bytelen] >> (8 - bitlen) == - p1->s6_addr[bytelen] >> (8 - bitlen)) - return(1); + return (0); - 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) + union nd_opt *ndopts, uint32_t optflags) { int optlen = 0; for (; limit > 0; limit -= optlen) { - if (limit < sizeof(struct nd_opt_hdr)) { + if ((size_t)limit < sizeof(struct nd_opt_hdr)) { syslog(LOG_INFO, "<%s> short option header", __func__); goto bad; } @@ -1218,17 +1439,17 @@ nd6_options(struct nd_opt_hdr *hdr, int limit, goto bad; } - if (hdr->nd_opt_type > ND_OPT_MTU) { - syslog(LOG_INFO, - "<%s> unknown ND option(type %d)", + if (hdr->nd_opt_type > ND_OPT_MTU && + hdr->nd_opt_type != ND_OPT_RDNSS && + hdr->nd_opt_type != ND_OPT_DNSSL) { + syslog(LOG_INFO, "<%s> unknown ND option(type %d)", __func__, hdr->nd_opt_type); continue; } if ((ndopt_flags[hdr->nd_opt_type] & optflags) == 0) { - syslog(LOG_INFO, - "<%s> unexpected ND option(type %d)", - __func__, hdr->nd_opt_type); + syslog(LOG_INFO, "<%s> unexpected ND option(type %d)", + __func__, hdr->nd_opt_type); continue; } @@ -1236,45 +1457,62 @@ nd6_options(struct nd_opt_hdr *hdr, int limit, * 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)))) { + switch (hdr->nd_opt_type) { + case ND_OPT_MTU: + if (optlen == sizeof(struct nd_opt_mtu)) + break; + goto skip; + case ND_OPT_RDNSS: + if (optlen >= 24 && + (optlen - sizeof(struct nd_opt_rdnss)) % 16 == 0) + break; + goto skip; + case ND_OPT_DNSSL: + if (optlen >= 16 && + (optlen - sizeof(struct nd_opt_dnssl)) % 8 == 0) + break; + goto skip; + case ND_OPT_PREFIX_INFORMATION: + if (optlen == sizeof(struct nd_opt_prefix_info)) + break; +skip: syslog(LOG_INFO, "<%s> invalid option length", __func__); continue; } switch (hdr->nd_opt_type) { - case ND_OPT_SOURCE_LINKADDR: case ND_OPT_TARGET_LINKADDR: case ND_OPT_REDIRECTED_HEADER: + case ND_OPT_RDNSS: + case ND_OPT_DNSSL: break; /* we don't care about these options */ + case ND_OPT_SOURCE_LINKADDR: case ND_OPT_MTU: - if (ndopts->nd_opt_array[hdr->nd_opt_type]) { + if (ndopts->opt_array[hdr->nd_opt_type]) { syslog(LOG_INFO, "<%s> duplicated ND option (type = %d)", __func__, hdr->nd_opt_type); } - ndopts->nd_opt_array[hdr->nd_opt_type] = hdr; + ndopts->opt_array[hdr->nd_opt_type] = hdr; break; case ND_OPT_PREFIX_INFORMATION: { - struct nd_optlist *pfxlist; + struct nd_optlist *nol; - if (ndopts->nd_opts_pi == 0) { - ndopts->nd_opts_pi = + if (ndopts->opt_pi == 0) { + ndopts->opt_pi = (struct nd_opt_prefix_info *)hdr; continue; } - if ((pfxlist = malloc(sizeof(*pfxlist))) == NULL) { + nol = malloc(sizeof(*nol)); + if (nol == NULL) { syslog(LOG_ERR, "<%s> can't allocate memory", __func__); goto bad; } - pfxlist->next = ndopts->nd_opts_list; - pfxlist->opt = hdr; - ndopts->nd_opts_list = pfxlist; + nol->nol_opt = hdr; + TAILQ_INSERT_TAIL(&(ndopts->opt_list), nol, nol_next); break; } @@ -1283,165 +1521,93 @@ nd6_options(struct nd_opt_hdr *hdr, int limit, } } - return(0); + return (0); bad: free_ndopts(ndopts); - return(-1); + return (-1); } static void -free_ndopts(union nd_opts *ndopts) +free_ndopts(union nd_opt *ndopts) { - struct nd_optlist *opt = ndopts->nd_opts_list, *next; + struct nd_optlist *nol; - while (opt) { - next = opt->next; - free(opt); - opt = next; + while ((nol = TAILQ_FIRST(&ndopts->opt_list)) != NULL) { + TAILQ_REMOVE(&ndopts->opt_list, nol, nol_next); + free(nol); } } void -sock_open(void) +sock_open(struct sockinfo *s) { struct icmp6_filter filt; - struct ipv6_mreq mreq; - struct rainfo *ra = ralist; int on; /* XXX: should be max MTU attached to the node */ - static u_char answer[1500]; + static char answer[1500]; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + if (s == NULL) { + syslog(LOG_ERR, "<%s> internal error", __func__); + exit(1); + } rcvcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + - CMSG_SPACE(sizeof(int)); - rcvcmsgbuf = (u_char *)malloc(rcvcmsgbuflen); + CMSG_SPACE(sizeof(int)); + rcvcmsgbuf = (char *)malloc(rcvcmsgbuflen); if (rcvcmsgbuf == NULL) { syslog(LOG_ERR, "<%s> not enough core", __func__); exit(1); } - sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + - CMSG_SPACE(sizeof(int)); - sndcmsgbuf = (u_char *)malloc(sndcmsgbuflen); + sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int)); + sndcmsgbuf = (char *)malloc(sndcmsgbuflen); if (sndcmsgbuf == NULL) { syslog(LOG_ERR, "<%s> not enough core", __func__); exit(1); } - if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { - syslog(LOG_ERR, "<%s> socket: %s", __func__, - strerror(errno)); + if ((s->si_fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { + syslog(LOG_ERR, "<%s> socket: %s", __func__, strerror(errno)); exit(1); } - /* specify to tell receiving interface */ on = 1; -#ifdef IPV6_RECVPKTINFO - if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, - sizeof(on)) < 0) { - syslog(LOG_ERR, "<%s> IPV6_RECVPKTINFO: %s", - __func__, strerror(errno)); - exit(1); - } -#else /* old adv. API */ - if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on, - sizeof(on)) < 0) { - syslog(LOG_ERR, "<%s> IPV6_PKTINFO: %s", - __func__, strerror(errno)); + if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, + sizeof(on)) < 0) { + syslog(LOG_ERR, "<%s> IPV6_RECVPKTINFO: %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) { - syslog(LOG_ERR, "<%s> IPV6_RECVHOPLIMIT: %s", - __func__, strerror(errno)); - exit(1); - } -#else /* old adv. API */ - if (setsockopt(sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, - sizeof(on)) < 0) { - syslog(LOG_ERR, "<%s> IPV6_HOPLIMIT: %s", - __func__, strerror(errno)); + if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, + sizeof(on)) < 0) { + syslog(LOG_ERR, "<%s> IPV6_RECVHOPLIMIT: %s", __func__, + strerror(errno)); exit(1); } -#endif - ICMP6_FILTER_SETBLOCKALL(&filt); ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt); ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); - if (accept_rr) + if (mcastif != NULL) ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt); - if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, - sizeof(filt)) < 0) { - syslog(LOG_ERR, "<%s> IICMP6_FILTER: %s", - __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) { - syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)", - __func__); + if (setsockopt(s->si_fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, + sizeof(filt)) < 0) { + syslog(LOG_ERR, "<%s> IICMP6_FILTER: %s", + __func__, strerror(errno)); exit(1); } - while (ra) { - mreq.ipv6mr_interface = ra->ifindex; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, - sizeof(mreq)) < 0) { - syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP(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) { - syslog(LOG_ERR, "<%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) { - syslog(LOG_ERR, - "<%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) { - syslog(LOG_ERR, - "<%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)&from; - rcvmhdr.msg_namelen = sizeof(from); + 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; @@ -1453,53 +1619,130 @@ sock_open(void) 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(void) +rtsock_open(struct sockinfo *s) { - if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { + if (s == NULL) { + syslog(LOG_ERR, "<%s> internal error", __func__); + exit(1); + } + if ((s->si_fd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { syslog(LOG_ERR, - "<%s> socket: %s", __func__, strerror(errno)); + "<%s> socket: %s", __func__, strerror(errno)); exit(1); } } -struct rainfo * -if_indextorainfo(int index) +struct ifinfo * +if_indextoifinfo(int idx) { - struct rainfo *rai = ralist; + struct ifinfo *ifi; + char *name, name0[IFNAMSIZ]; + + /* Check if the interface has a valid name or not. */ + if (if_indextoname(idx, name0) == NULL) + return (NULL); - for (rai = ralist; rai; rai = rai->next) { - if (rai->ifindex == index) - return(rai); + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (ifi->ifi_ifindex == idx) + return (ifi); } - return(NULL); /* search failed */ + if (ifi != NULL) + syslog(LOG_DEBUG, "<%s> ifi found (idx=%d)", + __func__, idx); + else + syslog(LOG_DEBUG, "<%s> ifi not found (idx=%d)", + __func__, idx); + + return (NULL); /* search failed */ } -static void -ra_output(struct rainfo *rainfo) +void +ra_output(struct ifinfo *ifi) { int i; struct cmsghdr *cm; struct in6_pktinfo *pi; - struct soliciter *sol, *nextsol; + struct soliciter *sol; + struct rainfo *rai; - if ((iflist[rainfo->ifindex]->ifm_flags & IFF_UP) == 0) { - syslog(LOG_DEBUG, "<%s> %s is not up, skip sending RA", - __func__, rainfo->ifname); + switch (ifi->ifi_state) { + case IFI_STATE_CONFIGURED: + rai = ifi->ifi_rainfo; + break; + case IFI_STATE_TRANSITIVE: + rai = ifi->ifi_rainfo_trans; + break; + case IFI_STATE_UNCONFIGURED: + syslog(LOG_DEBUG, "<%s> %s is unconfigured. " + "Skip sending RAs.", + __func__, ifi->ifi_ifname); + return; + default: + rai = NULL; + } + if (rai == NULL) { + syslog(LOG_DEBUG, "<%s> rainfo is NULL on %s." + "Skip sending RAs.", + __func__, ifi->ifi_ifname); return; } + if (!(ifi->ifi_flags & IFF_UP)) { + syslog(LOG_DEBUG, "<%s> %s is not up. " + "Skip sending RAs.", + __func__, ifi->ifi_ifname); + return; + } + /* + * Check lifetime, ACCEPT_RTADV flag, and ip6.forwarding. + * + * (lifetime == 0) = output + * (lifetime != 0 && (check_accept_rtadv()) = no output + * + * Basically, hosts MUST NOT send Router Advertisement + * messages at any time (RFC 4861, 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) + */ + syslog(LOG_DEBUG, + "<%s> check lifetime=%d, ACCEPT_RTADV=%d, ip6.forwarding=%d " + "on %s", __func__, + rai->rai_lifetime, + check_accept_rtadv(ifi->ifi_ifindex), + getinet6sysctl(IPV6CTL_FORWARDING), + ifi->ifi_ifname); + + if (rai->rai_lifetime != 0) { + if (getinet6sysctl(IPV6CTL_FORWARDING) == 0) { + syslog(LOG_ERR, + "non-zero lifetime RA " + "but net.inet6.ip6.forwarding=0. " + "Ignored."); + return; + } + if (check_accept_rtadv(ifi->ifi_ifindex)) { + syslog(LOG_ERR, + "non-zero lifetime RA " + "on RA receiving interface %s." + " Ignored.", ifi->ifi_ifname); + return; + } + } - make_packet(rainfo); /* XXX: inefficient */ + make_packet(rai); /* 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; + sndmhdr.msg_name = (caddr_t)&sin6_linklocal_allnodes; + sndmhdr.msg_iov[0].iov_base = (caddr_t)rai->rai_ra_data; + sndmhdr.msg_iov[0].iov_len = rai->rai_ra_datalen; cm = CMSG_FIRSTHDR(&sndmhdr); /* specify the outgoing interface */ @@ -1508,7 +1751,7 @@ ra_output(struct rainfo *rainfo) 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; + pi->ipi6_ifindex = ifi->ifi_ifindex; /* specify the hop limit of the packet */ { @@ -1522,16 +1765,16 @@ ra_output(struct rainfo *rainfo) } syslog(LOG_DEBUG, - "<%s> send RA on %s, # of waitings = %d", - __func__, rainfo->ifname, rainfo->waiting); + "<%s> send RA on %s, # of RS waitings = %d", + __func__, ifi->ifi_ifname, ifi->ifi_rs_waitcount); - i = sendmsg(sock, &sndmhdr, 0); + i = sendmsg(sock.si_fd, &sndmhdr, 0); - if (i < 0 || i != rainfo->ra_datalen) { + if (i < 0 || (size_t)i != rai->rai_ra_datalen) { if (i < 0) { syslog(LOG_ERR, "<%s> sendmsg on %s: %s", - __func__, rainfo->ifname, - strerror(errno)); + __func__, ifi->ifi_ifname, + strerror(errno)); } } @@ -1540,92 +1783,150 @@ ra_output(struct rainfo *rainfo) * 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; - -#if 0 - sndmhdr.msg_name = (caddr_t)&sol->addr; - i = sendmsg(sock, &sndmhdr, 0); - if (i < 0 || i != rainfo->ra_datalen) { - if (i < 0) { - syslog(LOG_ERR, - "<%s> unicast sendmsg on %s: %s", - __func__, rainfo->ifname, - strerror(errno)); - } - } -#endif - - sol->next = NULL; + while ((sol = TAILQ_FIRST(&rai->rai_soliciter)) != NULL) { + TAILQ_REMOVE(&rai->rai_soliciter, sol, sol_next); free(sol); } - rainfo->soliciter = NULL; + + /* update timestamp */ + clock_gettime(CLOCK_MONOTONIC_FAST, &ifi->ifi_ra_lastsent); /* update counter */ - if (rainfo->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS) - rainfo->initcounter++; - rainfo->raoutput++; + ifi->ifi_rs_waitcount = 0; + ifi->ifi_raoutput++; - /* update timestamp */ - gettimeofday(&rainfo->lastsent, NULL); + switch (ifi->ifi_state) { + case IFI_STATE_CONFIGURED: + if (ifi->ifi_burstcount > 0) + ifi->ifi_burstcount--; + break; + case IFI_STATE_TRANSITIVE: + ifi->ifi_burstcount--; + if (ifi->ifi_burstcount == 0) { + if (ifi->ifi_rainfo == ifi->ifi_rainfo_trans) { + /* Initial burst finished. */ + if (ifi->ifi_rainfo_trans != NULL) + ifi->ifi_rainfo_trans = NULL; + } - /* reset waiting conter */ - rainfo->waiting = 0; + /* Remove burst RA information */ + if (ifi->ifi_rainfo_trans != NULL) { + rm_rainfo(ifi->ifi_rainfo_trans); + ifi->ifi_rainfo_trans = NULL; + } + + if (ifi->ifi_rainfo != NULL) { + /* + * TRANSITIVE -> CONFIGURED + * + * After initial burst or transition from + * one configuration to another, + * ifi_rainfo always points to the next RA + * information. + */ + ifi->ifi_state = IFI_STATE_CONFIGURED; + syslog(LOG_DEBUG, + "<%s> ifname=%s marked as " + "CONFIGURED.", __func__, + ifi->ifi_ifname); + } else { + /* + * TRANSITIVE -> UNCONFIGURED + * + * If ifi_rainfo points to NULL, this + * interface is shutting down. + * + */ + int error; + + ifi->ifi_state = IFI_STATE_UNCONFIGURED; + syslog(LOG_DEBUG, + "<%s> ifname=%s marked as " + "UNCONFIGURED.", __func__, + ifi->ifi_ifname); + error = sock_mc_leave(&sock, + ifi->ifi_ifindex); + if (error) + exit(1); + } + } + break; + } } /* process RA timer */ struct rtadvd_timer * -ra_timeout(void *data) +ra_timeout(void *arg) { - struct rainfo *rai = (struct rainfo *)data; + struct ifinfo *ifi; -#ifdef notyet - /* if necessary, reconstruct the packet. */ -#endif - - syslog(LOG_DEBUG, - "<%s> RA timer on %s is expired", - __func__, rai->ifname); + ifi = (struct ifinfo *)arg; + syslog(LOG_DEBUG, "<%s> RA timer on %s is expired", + __func__, ifi->ifi_ifname); - ra_output(rai); + ra_output(ifi); - return(rai->timer); + return (ifi->ifi_ra_timer); } /* update RA timer */ void -ra_timer_update(void *data, struct timeval *tm) +ra_timer_update(void *arg, struct timespec *tm) { - struct rainfo *rai = (struct rainfo *)data; - long interval; + uint16_t interval; + struct rainfo *rai; + struct ifinfo *ifi; - /* - * 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); + ifi = (struct ifinfo *)arg; + rai = ifi->ifi_rainfo; + interval = 0; - /* - * For the first few advertisements (up to - * MAX_INITIAL_RTR_ADVERTISEMENTS), if the randomly chosen interval - * is greater than MAX_INITIAL_RTR_ADVERT_INTERVAL, the timer - * SHOULD be set to MAX_INITIAL_RTR_ADVERT_INTERVAL instead. - * (RFC-2461 6.2.4) - */ - if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS && - interval > MAX_INITIAL_RTR_ADVERT_INTERVAL) - interval = MAX_INITIAL_RTR_ADVERT_INTERVAL; + switch (ifi->ifi_state) { + case IFI_STATE_UNCONFIGURED: + return; + break; + case IFI_STATE_CONFIGURED: + /* + * 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 (RFC4861 6.2.4). + */ + interval = rai->rai_mininterval; +#ifdef HAVE_ARC4RANDOM + interval += arc4random_uniform(rai->rai_maxinterval - + rai->rai_mininterval); +#else + interval += random() % (rai->rai_maxinterval - + rai->rai_mininterval); +#endif + break; + case IFI_STATE_TRANSITIVE: + /* + * For the first few advertisements (up to + * MAX_INITIAL_RTR_ADVERTISEMENTS), if the randomly chosen + * interval is greater than + * MAX_INITIAL_RTR_ADVERT_INTERVAL, the timer SHOULD be + * set to MAX_INITIAL_RTR_ADVERT_INTERVAL instead. (RFC + * 4861 6.2.4) + * + * In such cases, the router SHOULD transmit one or more + * (but not more than MAX_FINAL_RTR_ADVERTISEMENTS) final + * multicast Router Advertisements on the interface with a + * Router Lifetime field of zero. (RFC 4861 6.2.5) + */ + interval = ifi->ifi_burstinterval; + break; + } tm->tv_sec = interval; - tm->tv_usec = 0; + tm->tv_nsec = 0; syslog(LOG_DEBUG, - "<%s> RA timer on %s is set to %ld:%ld", - __func__, rai->ifname, - (long int)tm->tv_sec, (long int)tm->tv_usec); + "<%s> RA timer on %s is set to %ld:%ld", + __func__, ifi->ifi_ifname, + (long int)tm->tv_sec, (long int)tm->tv_nsec / 1000); return; } diff --git a/usr.sbin/rtadvd/rtadvd.conf b/usr.sbin/rtadvd/rtadvd.conf index ee1a99e..3b1efa6 100644 --- a/usr.sbin/rtadvd/rtadvd.conf +++ b/usr.sbin/rtadvd/rtadvd.conf @@ -1,6 +1,5 @@ -# $FreeBSD: src/usr.sbin/rtadvd/rtadvd.conf,v 1.1.2.2 2001/02/22 07:51:05 ume Exp $ -# $DragonFly: src/usr.sbin/rtadvd/rtadvd.conf,v 1.3 2008/08/10 21:29:16 hasso Exp $ -# $KAME: rtadvd.conf,v 1.12 2001/01/21 14:56:38 itojun Exp $ +# $FreeBSD: stable/10/usr.sbin/rtadvd/rtadvd.conf 222732 2011-06-06 03:06:43Z hrs $ +# $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 @@ -19,4 +18,5 @@ # this part by hand, and then invoke rtadvd with the -s option. #ef0:\ -# :addrs#1:addr="2001:db8:ffff:1000::":prefixlen#64: +# :addr="2001:db8:ffff:1000::":prefixlen#64:\ +# :rdnss="2001:db8:ffff:1000::1":dnssl="example.com": diff --git a/usr.sbin/rtadvd/rtadvd.conf.5 b/usr.sbin/rtadvd/rtadvd.conf.5 index 706b7c3..24c1037 100644 --- a/usr.sbin/rtadvd/rtadvd.conf.5 +++ b/usr.sbin/rtadvd/rtadvd.conf.5 @@ -1,5 +1,4 @@ -.\" $FreeBSD: src/usr.sbin/rtadvd/rtadvd.conf.5,v 1.1.2.9 2002/12/29 16:35:43 schweikh Exp $ -.\" $KAME: rtadvd.conf.5,v 1.35 2001/05/25 07:40:22 jinmei Exp $ +.\" $KAME: rtadvd.conf.5,v 1.50 2005/01/14 05:30:59 jinmei Exp $ .\" .\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. .\" All rights reserved. @@ -28,7 +27,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 17, 1998 +.\" $FreeBSD: stable/10/usr.sbin/rtadvd/rtadvd.conf.5 233522 2012-03-26 21:22:53Z joel $ +.\" +.Dd June 4, 2011 .Dt RTADVD.CONF 5 .Os .Sh NAME @@ -80,14 +81,14 @@ will use the default values. .It Cm \&maxinterval (num) The maximum time allowed between sending unsolicited multicast router advertisements -(unit: seconds). +.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 -(unit: seconds). +.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 * @@ -105,24 +106,55 @@ will use the default values. (num) The value for Cur Hop Limit field. The default value is 64. .It Cm \&raflags -(num) Flags field in router advertisement message header. +(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 -.Pq Li 0x80 +.Po +.Li 'm' or 0x80 +.Pc means Managed address configuration flag bit, and Bit 6 -.Pq Li 0x40 +.Po +.Li 'o' or 0x40 +.Pc means Other stateful configuration flag bit. Bit 4 -.Pq Li 0x10 +.Po +.Li 0x10 +.Pc and Bit 3 -.Pq Li 0x08 +.Po +.Li 0x08 +.Pc are used to encode router preference. -0x01 means high, 0x00 means medium, and 0x11 means low. -The default value is 0. +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 -(unit: seconds). -Its value must be no greater than 3600000. +.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 @@ -131,11 +163,11 @@ advertising interfaces as described in The default value is 1800. .It Cm \&rtime (num) Reachable time field -(unit: milliseconds). +.Pq unit: milliseconds . The default value is 0, which means unspecified by this router. .It Cm \&retrans (num) Retrans Timer field -(unit: milliseconds). +.Pq unit: milliseconds . The default value is 0, which means unspecified by this router. .El .Pp @@ -145,11 +177,31 @@ 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 +and +.Cm noifprefix +can be augmented with a number, like +.Dq Li prefix2 , +to specify multiple prefixes. .Bl -tag -width indent +.It Cm \&noifprefix +(bool) Specifies no prefix on the network interfaces will be advertised. +By default +.Nm rtadvd +automatically gathers on-link prefixes from all of the network interfaces +and advertise them. +The +.Cm noifprefix +disables that behavior. +If this is specified and no +.Cm addr +keyword is specified, no prefix information option will be included in the +message. .It Cm \&clockskew (num) Time skew to adjust link propagation delays and clock skews between routers on the link -(unit: seconds). +.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 @@ -157,34 +209,27 @@ 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 \&addrs -(num) Number of prefixes. -Its default is 0, so it must explicitly be set to positive values -if you want to specify any prefix information option. -If its value is 0, -.Xr rtadvd 8 -looks up the system routing table and -advertise the prefixes corresponding to interface routes -on the interface. -If its value is more than 1, you must specify the index of the prefix -for each item below. -Indices vary from 0 to N-1, where N is the -value of -.Cm addrs . -Each index shall follow the name of each item, e.g., -.Dq prefixlen2 . .It Cm \&prefixlen (num) Prefix length field. The default value is 64. .It Cm \&pinfoflags -(num) Flags field in prefix information option. +(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 -.Pq Li 0x80 +.Po +.Li 'l' or 0x80 +.Pc means On-link flag bit, and Bit 6 -.Pq Li 0x40 +.Po +.Li 'a' or 0x40 +.Pc means Autonomous address-configuration flag bit. -The default value is 0xc0, i.e., both bits are set. +The default value is "la" or 0xc0, i.e., both bits are set. .It Cm \&addr (str) The address filled into Prefix field. Since @@ -193,23 +238,19 @@ 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 addrs -is more than 0. .It Cm \&vltime (num) Valid lifetime field -(unit: seconds). +.Pq unit: seconds . The default value is 2592000 (30 days). .It Cm \&vltimedecr -(bool) This item means the advertised valid lifetime will decrements +(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 -(unit: seconds). +.Pq unit: seconds . The default value is 604800 (7 days). .It Cm \&pltimedecr -(bool) This item means the advertised preferred lifetime will decrements +(bool) This item means the advertised preferred lifetime will decrement in real time, which is disabled by default. .El .Pp @@ -237,9 +278,11 @@ will use the default value. .Bl -tag -width indent .It Cm \&nolladdr (bool) By default -(if +.Po +if .Cm \&nolladdr -is not specified), +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. @@ -249,7 +292,7 @@ will not attach source link-layer address option to router advertisement packets. .El .Pp -The following item controls ICMPV6 home agent information option, +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 @@ -272,31 +315,11 @@ explicitly. 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 \&routes -(num) Number of routes. -Its default is 0, so it must explicitly be set to positive values -if you want to specify any route information option. -If its value is 0, no route information is sent. -If its value is more than 1, you must specify the index of the routes -for each item below. -Indices vary from 0 to N-1, where N is the -value of -.Cm routes . -Each index shall follow the name of each item, e.g., -.Dq rtrplen2 . -.It Cm \&rtrplen -(num) Prefix length field in route information option. -The default value is 64. -.It Cm \&rtrflags -(num) Flags field in route information option. -Bit 4 -.Pq Li 0x10 -and Bit 3 -.Pq Li 0x08 -are used to encode router preference for the route. -The default value is 0x00, i.e. medium router preference. -.It Cm \&rtrprefix +.It Cm \&rtprefix (str) The prefix filled into the Prefix field of route information option. Since .Dq \&: @@ -304,15 +327,109 @@ 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 addrs -is more than 0. -.It Cm \&rtrltime +.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. -(unit: seconds). -The default value is 2592000 (30 days). (not specified in draft-draves-router-selection-01.txt now) +.Pq unit: seconds . +Since the specification does not define the default value of this +item, the value for this item should be specified by hand. +However, +.Nm rtadvd +allows this item to be unspecified, and uses the router lifetime +as the default value in such a case, just for compatibility with an +old version of the program. +.El +.Pp +In the above list, each keyword beginning with +.Dq Li rt +could be replaced with the one beginning with +.Dq Li rtr +for backward compatibility reason. +For example, +.Cm rtrplen +is accepted instead of +.Cm rtplen . +However, keywords that start with +.Dq Li rtr +have basically been obsoleted, and should not be used any more. +.Pp +The following items are for ICMPv6 Recursive DNS Server Option and +DNS Search List Option +.Pq RFC 6106 , +which will be attached to router advertisement header. +These items are optional. +.Bl -tag -width indent +.It Cm \&rdnss +(str) The IPv6 address of one or more recursive DNS servers. +The argument must be inside double quotes. +Multiple DNS servers can be specified in a comma-separated string. +If different lifetimes are needed for different servers, +separate entries can be given by using +.Cm rdnss , +.Cm rdnss0 , +.Cm rdnss1 , +.Cm rdnss2 ... +options with corresponding +.Cm rdnssltime , +.Cm rdnssltime0 , +.Cm rdnssltime1 , +.Cm rdnssltime2 ... +entries. +Note that the maximum number of servers depends on the receiver side. +See also +.Xr resolver 5 +manual page for resolver implementation in +.Fx . +.It Cm \&rdnssltime +The lifetime of the +.Cm rdnss +DNS server entries. +The default value is 3/2 of the interval time. +.It Cm \&dnssl +(str) One or more domain names in a comma-separated string. +These domain names will be used when making DNS queries on a +non-fully-qualified domain name. +If different lifetimes are needed for different domains, separate entries +can be given by using +.Cm dnssl , +.Cm dnssl0 , +.Cm dnssl1 , +.Cm dnssl2 ... +options with corresponding +.Cm dnsslltime , +.Cm dnsslltime0 , +.Cm dnsslltime1 , +.Cm dnsslltime2 ... +entries. +Note that the maximum number of names depends on the receiver side. +See also +.Xr resolver 5 +manual page for resolver implementation in +.Fx . +.It Cm \&dnsslltime +The lifetime of the +.Cm dnssl +DNS search list entries. +The default value is 3/2 of the interval time. .El +.Pp You can also refer one line from another by using .Cm tc capability. @@ -332,7 +449,7 @@ With the following configuration, overrides the router lifetime parameter for the .Li ne0 interface. -.Bd -literal +.Bd -literal -offset indent ne0:\\ :rltime#0: .Ed @@ -344,37 +461,65 @@ The configuration must be used with the .Fl s option to .Xr rtadvd 8 . -.Bd -literal +.Bd -literal -offset indent ef0:\\ - :addrs#1:addr="2001:db8:ffff:1000::":prefixlen#64: + :addr="2001:db8:ffff:1000::":prefixlen#64: +.Ed +.Pp +The following example configures the +.Li wlan0 +interface and adds two DNS servers and a DNS domain search options +using the default option lifetime values. +.Bd -literal -offset indent +wlan0:\\ + :addr="2001:db8:ffff:1000::":prefixlen#64:\\ + :rdnss="2001:db8:ffff::10,2001:db8:ffff::2:43":\\ + :dnssl="example.com": .Ed .Pp The following example presents the default values in an explicit manner. The configuration is provided just for reference purposes; YOU DO NOT NEED TO HAVE IT AT ALL. -.Bd -literal +.Bd -literal -offset indent default:\\ :chlim#64:raflags#0:rltime#1800:rtime#0:retrans#0:\\ - :pinfoflags#192:vltime#2592000:pltime#604800:mtu#0: + :pinfoflags="la":vltime#2592000:pltime#604800:mtu#0: ef0:\\ - :addrs#1:addr="2001:db8:ffff:1000::":prefixlen#64:tc=default: + :addr="2001:db8:ffff:1000::":prefixlen#64:tc=default: .Ed .Sh SEE ALSO .Xr termcap 5 , +.Xr resolver 5 , .Xr rtadvd 8 , .Xr rtsol 8 -.Pp -Thomas Narten, Erik Nordmark and W. A. Simpson, -.Do -Neighbor Discovery for IP version 6 (IPv6) -.Dc , -RFC 2461 -.Pp -Richard Draves, -.Do -Default Router Preferences and More-Specific Routes -.Dc , -draft-ietf-ipngwg-router-selection-01.txt +.Rs +.%A Thomas Narten +.%A Erik Nordmark +.%A W. A. Simpson +.%A Hesham Soliman +.%T Neighbor Discovery for IP version 6 (IPv6) +.%R RFC 4861 +.Re +.Rs +.%A Thomas Narten +.%A Erik Nordmark +.%A W. A. Simpson +.%T Neighbor Discovery for IP version 6 (IPv6) +.%R RFC 2461 (obsoleted by RFC 4861) +.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 Options for DNS Configuration +.%R RFC 6106 +.Re .Sh HISTORY The .Xr rtadvd 8 diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h index 64c56b6..cb42b47 100644 --- a/usr.sbin/rtadvd/rtadvd.h +++ b/usr.sbin/rtadvd/rtadvd.h @@ -1,9 +1,11 @@ -/* $KAME: rtadvd.h,v 1.16 2001/04/10 15:08:31 suz Exp $ */ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/rtadvd.h 253970 2013-08-05 20:13:02Z hrs $ */ +/* $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: @@ -15,7 +17,7 @@ * 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 @@ -27,16 +29,54 @@ * 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. - * - * $FreeBSD: src/usr.sbin/rtadvd/rtadvd.h,v 1.1.2.3 2002/06/29 18:59:53 ume Exp $ - * $DragonFly: src/usr.sbin/rtadvd/rtadvd.h,v 1.3 2003/11/03 19:31:42 eirikn Exp $ */ -#define ALLNODES "ff02::1" -#define ALLROUTERS_LINK "ff02::2" -#define ALLROUTERS_SITE "ff05::2" -#define ANY "::" -#define RTSOLLEN 8 +#define ELM_MALLOC(p,error_action) \ + do { \ + p = malloc(sizeof(*p)); \ + if (p == NULL) { \ + syslog(LOG_ERR, "<%s> malloc failed: %s", \ + __func__, strerror(errno)); \ + error_action; \ + } \ + memset(p, 0, sizeof(*p)); \ + } while(0) + +#define IN6ADDR_LINKLOCAL_ALLNODES_INIT \ + {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} + +#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \ + {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}} + +#define IN6ADDR_SITELOCAL_ALLROUTERS_INIT \ + {{{ 0xff, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}} + +extern struct sockaddr_in6 sin6_linklocal_allnodes; +extern struct sockaddr_in6 sin6_linklocal_allrouters; +extern struct sockaddr_in6 sin6_sitelocal_allrouters; + +/* + * RFC 3542 API deprecates IPV6_PKTINFO in favor of + * IPV6_RECVPKTINFO + */ +#ifndef IPV6_RECVPKTINFO +#ifdef IPV6_PKTINFO +#define IPV6_RECVPKTINFO IPV6_PKTINFO +#endif +#endif + +/* + * RFC 3542 API deprecates IPV6_HOPLIMIT in favor of + * IPV6_RECVHOPLIMIT + */ +#ifndef IPV6_RECVHOPLIMIT +#ifdef IPV6_HOPLIMIT +#define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT +#endif +#endif /* protocol constants and default values */ #define DEF_MAXRTRADVINTERVAL 600 @@ -47,21 +87,12 @@ #define DEF_ADVVALIDLIFETIME 2592000 #define DEF_ADVPREFERREDLIFETIME 604800 -/*XXX int-to-double comparison for INTERVAL items */ -#ifndef MIP6 -#define mobileip6 0 -#endif - #define MAXROUTERLIFETIME 9000 -#define MIN_MAXINTERVAL (mobileip6 ? 1.5 : 4.0) +#define MIN_MAXINTERVAL 4 #define MAX_MAXINTERVAL 1800 -#define MIN_MININTERVAL (mobileip6 ? 0.5 : 3) +#define MIN_MININTERVAL 3 #define MAXREACHABLETIME 3600000 -#ifndef MIP6 -#undef miobileip6 -#endif - #define MAX_INITIAL_RTR_ADVERT_INTERVAL 16 #define MAX_INITIAL_RTR_ADVERTISEMENTS 3 #define MAX_FINAL_RTR_ADVERTISEMENTS 3 @@ -73,108 +104,195 @@ #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 */ -#ifdef MIP6 - u_int routeraddr; /* bool: RouterAddress */ -#endif - int prefixlen; - int origin; /* from kernel or cofig */ - struct in6_addr prefix; + TAILQ_ENTRY(prefix) pfx_next; + + struct rainfo *pfx_rainfo; /* back pointer to the interface */ + /* + * Expiration timer. This is used when a prefix derived from + * the kernel is deleted. + */ + struct rtadvd_timer *pfx_timer; + + uint32_t pfx_validlifetime; /* AdvValidLifetime */ + uint32_t pfx_vltimeexpire; /* Expiration of vltime */ + uint32_t pfx_preflifetime; /* AdvPreferredLifetime */ + uint32_t pfx_pltimeexpire; /* Expiration of pltime */ + int pfx_onlinkflg; /* bool: AdvOnLinkFlag */ + int pfx_autoconfflg; /* bool: AdvAutonomousFlag */ + int pfx_prefixlen; + int pfx_origin; /* From kernel or config */ + + struct in6_addr pfx_prefix; }; struct rtinfo { - struct rtinfo *prev; /* previous link */ - struct rtinfo *next; /* forward link */ + TAILQ_ENTRY(rtinfo) rti_next; + + uint32_t rti_ltime; /* route lifetime */ + int rti_rtpref; /* route preference */ + int rti_prefixlen; + struct in6_addr rti_prefix; +}; + +struct rdnss_addr { + TAILQ_ENTRY(rdnss_addr) ra_next; + + struct in6_addr ra_dns; /* DNS server entry */ +}; + +struct rdnss { + TAILQ_ENTRY(rdnss) rd_next; + + TAILQ_HEAD(, rdnss_addr) rd_list; /* list of DNS servers */ + uint32_t rd_ltime; /* number of seconds valid */ +}; - u_int32_t ltime; /* route lifetime */ - u_int rtpref; /* router preference */ - int prefixlen; - struct in6_addr prefix; +/* + * The maximum length of a domain name in a DNS search list is calculated + * by a domain name + length fields per 63 octets + a zero octet at + * the tail and adding 8 octet boundary padding. + */ +#define _DNAME_LABELENC_MAXLEN \ + (NI_MAXHOST + (NI_MAXHOST / 64 + 1) + 1) + +#define DNAME_LABELENC_MAXLEN \ + (_DNAME_LABELENC_MAXLEN + 8 - _DNAME_LABELENC_MAXLEN % 8) + +struct dnssl_addr { + TAILQ_ENTRY(dnssl_addr) da_next; + + int da_len; /* length of entry */ + char da_dom[DNAME_LABELENC_MAXLEN]; /* search domain name entry */ +}; + +struct dnssl { + TAILQ_ENTRY(dnssl) dn_next; + + TAILQ_HEAD(, dnssl_addr) dn_list; /* list of search domains */ + uint32_t dn_ltime; /* number of seconds valid */ }; struct soliciter { - struct soliciter *next; - struct sockaddr_in6 addr; + TAILQ_ENTRY(soliciter) sol_next; + + struct sockaddr_in6 sol_addr; }; 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 */ + TAILQ_ENTRY(rainfo) rai_next; /* 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 */ + struct ifinfo *rai_ifinfo; + + int rai_advlinkopt; /* bool: whether include link-layer addr opt */ + int rai_advifprefix; /* bool: gather IF prefixes? */ /* Router configuration variables */ - u_short lifetime; /* AdvDefaultLifetime */ - u_int maxinterval; /* MaxRtrAdvInterval */ - u_int mininterval; /* MinRtrAdvInterval */ - int managedflg; /* AdvManagedFlag */ - int otherflg; /* AdvOtherConfigFlag */ -#ifdef MIP6 - int haflg; /* HAFlag */ -#endif - 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 MIP6 - u_short hapref; /* Home Agent Preference */ - u_short hatime; /* Home Agent Lifetime */ -#endif - struct rtinfo route; /* route information option (link head) */ - int routes; /* number of route information options */ + uint16_t rai_lifetime; /* AdvDefaultLifetime */ + uint16_t rai_maxinterval; /* MaxRtrAdvInterval */ + uint16_t rai_mininterval; /* MinRtrAdvInterval */ + int rai_managedflg; /* AdvManagedFlag */ + int rai_otherflg; /* AdvOtherConfigFlag */ - /* actual RA packet data and its length */ - size_t ra_datalen; - u_char *ra_data; + int rai_rtpref; /* router preference */ + uint32_t rai_linkmtu; /* AdvLinkMTU */ + uint32_t rai_reachabletime; /* AdvReachableTime */ + uint32_t rai_retranstimer; /* AdvRetransTimer */ + uint8_t rai_hoplimit; /* AdvCurHopLimit */ - /* 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 */ + TAILQ_HEAD(, prefix) rai_prefix;/* AdvPrefixList(link head) */ + int rai_pfxs; /* number of prefixes */ + + uint16_t rai_clockskew; /* used for consisitency check of lifetimes */ + + TAILQ_HEAD(, rdnss) rai_rdnss; /* DNS server list */ + TAILQ_HEAD(, dnssl) rai_dnssl; /* search domain list */ + TAILQ_HEAD(, rtinfo) rai_route; /* route information option (link head) */ + int rai_routes; /* number of route information options */ + /* actual RA packet data and its length */ + size_t rai_ra_datalen; + char *rai_ra_data; /* info about soliciter */ - struct soliciter *soliciter; /* recent solication source */ + TAILQ_HEAD(, soliciter) rai_soliciter; /* recent solication source */ }; -struct rtadvd_timer *ra_timeout(void *); -void ra_timer_update(void *, struct timeval *); +/* RA information list */ +extern TAILQ_HEAD(railist_head_t, rainfo) railist; + +/* + * ifi_state: + * + * (INIT) + * | + * | update_ifinfo() + * | update_persist_ifinfo() + * v + * UNCONFIGURED + * | ^ + * loadconfig()| |rm_ifinfo(), ra_output() + * (MC join)| |(MC leave) + * | | + * | | + * v | + * TRANSITIVE + * | ^ + * ra_output()| |getconfig() + * | | + * | | + * | | + * v | + * CONFIGURED + * + * + */ +#define IFI_STATE_UNCONFIGURED 0 +#define IFI_STATE_CONFIGURED 1 +#define IFI_STATE_TRANSITIVE 2 -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); +struct ifinfo { + TAILQ_ENTRY(ifinfo) ifi_next; -extern struct in6_addr in6a_site_allrouters; -#ifdef MIP6 -extern int mobileip6; -#endif + uint16_t ifi_state; + uint16_t ifi_persist; + uint16_t ifi_ifindex; + char ifi_ifname[IFNAMSIZ]; + uint8_t ifi_type; + uint16_t ifi_flags; + uint32_t ifi_nd_flags; + uint32_t ifi_phymtu; + struct sockaddr_dl ifi_sdl; + + struct rainfo *ifi_rainfo; + struct rainfo *ifi_rainfo_trans; + uint16_t ifi_burstcount; + uint32_t ifi_burstinterval; + struct rtadvd_timer *ifi_ra_timer; + /* timestamp when the latest RA was sent */ + struct timespec ifi_ra_lastsent; + uint16_t ifi_rs_waitcount; + + /* statistics */ + uint64_t ifi_raoutput; /* # of RAs sent */ + uint64_t ifi_rainput; /* # of RAs received */ + uint64_t ifi_rainconsistent; /* # of inconsistent recv'd RAs */ + uint64_t ifi_rsinput; /* # of RSs received */ +}; + +/* Interface list */ +extern TAILQ_HEAD(ifilist_head_t, ifinfo) ifilist; + +extern char *mcastif; + +struct rtadvd_timer *ra_timeout(void *); +void ra_timer_update(void *, struct timespec *); +void ra_output(struct ifinfo *); + +int prefix_match(struct in6_addr *, int, + struct in6_addr *, int); +struct ifinfo *if_indextoifinfo(int); +struct prefix *find_prefix(struct rainfo *, + struct in6_addr *, int); +void rtadvd_set_reload(int); +void rtadvd_set_shutdown(int); diff --git a/usr.sbin/rtadvd/timer.c b/usr.sbin/rtadvd/timer.c index 63a6df3..f205112 100644 --- a/usr.sbin/rtadvd/timer.c +++ b/usr.sbin/rtadvd/timer.c @@ -1,9 +1,11 @@ -/* $KAME: timer.c,v 1.4 2000/05/27 11:30:43 jinmei Exp $ */ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/timer.c 253995 2013-08-06 15:49:18Z hrs $ */ +/* $KAME: timer.c,v 1.9 2002/06/10 19:59:47 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: @@ -15,7 +17,7 @@ * 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 @@ -27,91 +29,121 @@ * 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. - * - * $FreeBSD: src/usr.sbin/rtadvd/timer.c,v 1.1.2.3 2002/06/29 18:59:53 ume Exp $ */ -#include +#include +#include + +#include +#include +#include #include #include #include #include #include -#include "timer.h" - -static struct rtadvd_timer timer_head; +#include +#include -#define MILLION 1000000 -#define TIMEVAL_EQUAL(t1,t2) ((t1)->tv_sec == (t2)->tv_sec &&\ - (t1)->tv_usec == (t2)->tv_usec) +#include "rtadvd.h" +#include "timer_subr.h" +#include "timer.h" -static struct timeval tm_max = {0x7fffffff, 0x7fffffff}; +struct rtadvd_timer_head_t ra_timer = + TAILQ_HEAD_INITIALIZER(ra_timer); +static struct timespec tm_limit; +static struct timespec tm_max; void rtadvd_timer_init(void) { - memset(&timer_head, 0, sizeof(timer_head)); + /* Generate maximum time in timespec. */ + tm_limit.tv_sec = (-1) & ~((time_t)1 << ((sizeof(tm_max.tv_sec) * 8) - 1)); + tm_limit.tv_nsec = (-1) & ~((long)1 << ((sizeof(tm_max.tv_nsec) * 8) - 1)); + tm_max = tm_limit; + TAILQ_INIT(&ra_timer); +} + +void +rtadvd_update_timeout_handler(void) +{ + struct ifinfo *ifi; + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + switch (ifi->ifi_state) { + case IFI_STATE_CONFIGURED: + case IFI_STATE_TRANSITIVE: + if (ifi->ifi_ra_timer != NULL) + continue; + + syslog(LOG_DEBUG, "<%s> add timer for %s (idx=%d)", + __func__, ifi->ifi_ifname, ifi->ifi_ifindex); + ifi->ifi_ra_timer = rtadvd_add_timer(ra_timeout, + ra_timer_update, ifi, ifi); + ra_timer_update((void *)ifi, + &ifi->ifi_ra_timer->rat_tm); + rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm, + ifi->ifi_ra_timer); + break; + case IFI_STATE_UNCONFIGURED: + if (ifi->ifi_ra_timer == NULL) + continue; + + syslog(LOG_DEBUG, + "<%s> remove timer for %s (idx=%d)", __func__, + ifi->ifi_ifname, ifi->ifi_ifindex); + rtadvd_remove_timer(ifi->ifi_ra_timer); + ifi->ifi_ra_timer = NULL; + break; + } + } - timer_head.next = timer_head.prev = &timer_head; - timer_head.tm = tm_max; + return; } struct rtadvd_timer * rtadvd_add_timer(struct rtadvd_timer *(*timeout)(void *), - void (*update)(void *, struct timeval *), + void (*update)(void *, struct timespec *), void *timeodata, void *updatedata) { - struct rtadvd_timer *newtimer; + struct rtadvd_timer *rat; - if ((newtimer = malloc(sizeof(*newtimer))) == NULL) { + if (timeout == NULL) { syslog(LOG_ERR, - "<%s> can't allocate memory", __func__); + "<%s> timeout function unspecified", __func__); exit(1); } - memset(newtimer, 0, sizeof(*newtimer)); - - if (timeout == NULL) { + rat = malloc(sizeof(*rat)); + if (rat == NULL) { syslog(LOG_ERR, - "<%s> timeout function unspecfied", __func__); + "<%s> can't allocate memory", __func__); exit(1); } - newtimer->expire = timeout; - newtimer->update = update; - newtimer->expire_data = timeodata; - newtimer->update_data = updatedata; - newtimer->tm = tm_max; + memset(rat, 0, sizeof(*rat)); + + rat->rat_expire = timeout; + rat->rat_update = update; + rat->rat_expire_data = timeodata; + rat->rat_update_data = updatedata; + rat->rat_tm = tm_max; /* link into chain */ - insque(newtimer, &timer_head); + TAILQ_INSERT_TAIL(&ra_timer, rat, rat_next); - return(newtimer); + return (rat); } void -rtadvd_remove_timer(struct rtadvd_timer **timer) +rtadvd_remove_timer(struct rtadvd_timer *rat) { - 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; + if (rat == NULL) + return; - return; + TAILQ_REMOVE(&ra_timer, rat, rat_next); + free(rat); } /* @@ -119,94 +151,49 @@ rtadvd_set_timer(struct timeval *tm, struct rtadvd_timer *timer) * call the expire function for the timer and update the timer. * Return the next interval for select() call. */ -struct timeval * +struct timespec * rtadvd_check_timer(void) { - 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)) + static struct timespec returnval; + struct timespec now; + struct rtadvd_timer *rat; + + clock_gettime(CLOCK_MONOTONIC_FAST, &now); + tm_max = tm_limit; + TAILQ_FOREACH(rat, &ra_timer, rat_next) { + if (TS_CMP(&rat->rat_tm, &now, <=)) { + if (((*rat->rat_expire)(rat->rat_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 (rat->rat_update) + (*rat->rat_update)(rat->rat_update_data, &rat->rat_tm); + TS_ADD(&rat->rat_tm, &now, &rat->rat_tm); } - - if (TIMEVAL_LT(tm->tm, timer_head.tm)) - timer_head.tm = tm->tm; + if (TS_CMP(&rat->rat_tm, &tm_max, <)) + tm_max = rat->rat_tm; } - - if (TIMEVAL_EQUAL(&tm_max, &timer_head.tm)) { + if (TS_CMP(&tm_max, &tm_limit, ==)) { /* no need to timeout */ - return(NULL); - } - else if (TIMEVAL_LT(timer_head.tm, now)) { + return (NULL); + } else if (TS_CMP(&tm_max, &now, <)) { /* this may occur when the interval is too small */ - returnval.tv_sec = returnval.tv_usec = 0; - } - else - TIMEVAL_SUB(&timer_head.tm, &now, &returnval); - return(&returnval); -} - -struct timeval * -rtadvd_timer_rest(struct rtadvd_timer *timer) -{ - static struct timeval returnval, now; - - gettimeofday(&now, NULL); - if (TIMEVAL_LEQ(timer->tm, now)) { - syslog(LOG_DEBUG, - "<%s> a timer must be expired, but not yet", - __func__); - returnval.tv_sec = returnval.tv_usec = 0; - } - else - TIMEVAL_SUB(&timer->tm, &now, &returnval); - - return(&returnval); + returnval.tv_sec = returnval.tv_nsec = 0; + } else + TS_SUB(&tm_max, &now, &returnval); + return (&returnval); } -/* result = a + b */ void -TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result) +rtadvd_set_timer(struct timespec *tm, struct rtadvd_timer *rat) { - long l; + struct timespec now; - 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; - } -} + /* reset the timer */ + clock_gettime(CLOCK_MONOTONIC_FAST, &now); + TS_ADD(&now, tm, &rat->rat_tm); -/* - * result = a - b - * XXX: this function assumes that a >= b. - */ -void -TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result) -{ - long l; + /* update the next expiration time */ + if (TS_CMP(&rat->rat_tm, &tm_max, <)) + tm_max = rat->rat_tm; - 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; - } + return; } diff --git a/usr.sbin/rtadvd/timer.h b/usr.sbin/rtadvd/timer.h index 6543e46..98ec538 100644 --- a/usr.sbin/rtadvd/timer.h +++ b/usr.sbin/rtadvd/timer.h @@ -1,9 +1,10 @@ -/* $KAME: timer.h,v 1.3 2000/05/27 11:30:43 jinmei Exp $ */ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/timer.h 253970 2013-08-05 20:13:02Z hrs $ */ +/* $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: @@ -15,7 +16,7 @@ * 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 @@ -27,41 +28,25 @@ * 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. - * - * $FreeBSD: src/usr.sbin/rtadvd/timer.h,v 1.1.2.3 2002/06/29 18:59:53 ume Exp $ - * $DragonFly: src/usr.sbin/rtadvd/timer.h,v 1.3 2003/11/03 19:31:42 eirikn Exp $ */ -/* 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))) - +extern TAILQ_HEAD(rtadvd_timer_head_t, rtadvd_timer) ra_timer; struct rtadvd_timer { - struct rtadvd_timer *next; - struct rtadvd_timer *prev; - struct rainfo *rai; - struct timeval tm; + TAILQ_ENTRY(rtadvd_timer) rat_next; - struct rtadvd_timer *(*expire)(void *); /* expiration function */ - void *expire_data; - void (*update)(void *, struct timeval *); /* update function */ - void *update_data; + struct rainfo *rat_rai; + struct timespec rat_tm; + struct rtadvd_timer *(*rat_expire)(void *); + void *rat_expire_data; + void (*rat_update)(void *, struct timespec *); + void *rat_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 *); +void rtadvd_timer_init(void); +void rtadvd_update_timeout_handler(void); +struct rtadvd_timer *rtadvd_add_timer(struct rtadvd_timer *(*)(void *), + void (*)(void *, struct timespec *), void *, void *); +void rtadvd_set_timer(struct timespec *, + struct rtadvd_timer *); +void rtadvd_remove_timer(struct rtadvd_timer *); +struct timespec *rtadvd_check_timer(void); diff --git a/usr.sbin/rtadvd/timer_subr.c b/usr.sbin/rtadvd/timer_subr.c new file mode 100644 index 0000000..fe77324 --- /dev/null +++ b/usr.sbin/rtadvd/timer_subr.c @@ -0,0 +1,91 @@ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/timer_subr.c 253970 2013-08-05 20:13:02Z hrs $ */ +/* $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 + +#include "timer.h" +#include "timer_subr.h" + +struct timespec * +rtadvd_timer_rest(struct rtadvd_timer *rat) +{ + static struct timespec returnval, now; + + clock_gettime(CLOCK_MONOTONIC_FAST, &now); + if (TS_CMP(&rat->rat_tm, &now, <=)) { + syslog(LOG_DEBUG, + "<%s> a timer must be expired, but not yet", + __func__); + returnval.tv_sec = returnval.tv_nsec = 0; + } + else + TS_SUB(&rat->rat_tm, &now, &returnval); + + return (&returnval); +} + +char * +sec2str(uint32_t s, char *buf) +{ + uint32_t day; + uint32_t hour; + uint32_t min; + uint32_t sec; + char *p; + + min = s / 60; + sec = s % 60; + + hour = min / 60; + min = min % 60; + + day = hour / 24; + hour = hour % 24; + + p = buf; + if (day > 0) + p += sprintf(p, "%" PRIu32 "d", day); + if (hour > 0) + p += sprintf(p, "%" PRIu32 "h", hour); + if (min > 0) + p += sprintf(p, "%" PRIu32 "m", min); + + if ((p == buf) || (sec > 0 && p > buf)) + sprintf(p, "%" PRIu32 "s", sec); + + return (buf); +} diff --git a/usr.sbin/rtadvd/timer_subr.h b/usr.sbin/rtadvd/timer_subr.h new file mode 100644 index 0000000..e6565b3 --- /dev/null +++ b/usr.sbin/rtadvd/timer_subr.h @@ -0,0 +1,59 @@ +/* $FreeBSD: stable/10/usr.sbin/rtadvd/timer_subr.h 253970 2013-08-05 20:13:02Z hrs $ */ +/* $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. + */ + +#define SSBUFLEN 1024 + +#define TS_CMP(tsp, usp, cmp) \ + (((tsp)->tv_sec == (usp)->tv_sec) ? \ + ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ + ((tsp)->tv_sec cmp (usp)->tv_sec)) +#define TS_ADD(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ + if ((vsp)->tv_nsec >= 1000000000L) { \ + (vsp)->tv_sec++; \ + (vsp)->tv_nsec -= 1000000000L; \ + } \ + } while (0) +#define TS_SUB(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ + if ((vsp)->tv_nsec < 0) { \ + (vsp)->tv_sec--; \ + (vsp)->tv_nsec += 1000000000L; \ + } \ + } while (0) + +struct timespec *rtadvd_timer_rest(struct rtadvd_timer *); +char *sec2str(uint32_t, char *buf);