diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index ec69f608c0a1..75a06d7f3b31 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -178,6 +178,7 @@ SUBDIR+= ndp SUBDIR+= rip6query SUBDIR+= route6d SUBDIR+= rrenumd +SUBDIR+= rtadvctl SUBDIR+= rtadvd SUBDIR+= rtsold SUBDIR+= traceroute6 diff --git a/usr.sbin/rtadvctl/Makefile b/usr.sbin/rtadvctl/Makefile new file mode 100644 index 000000000000..a66db84c4278 --- /dev/null +++ b/usr.sbin/rtadvctl/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD$ +# +.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 000000000000..b9c0f2e35a75 --- /dev/null +++ b/usr.sbin/rtadvctl/rtadvctl.8 @@ -0,0 +1,103 @@ +.\" 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$ +.\" +.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. +.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 000000000000..26bf11dc86fe --- /dev/null +++ b/usr.sbin/rtadvctl/rtadvctl.c @@ -0,0 +1,926 @@ +/*- + * 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$ + * + */ + +#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 += cmsg_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 (cmsg_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); + + cmsg_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 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); + } + } + + 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: %s/%s\n", + sec2str(rai->rai_mininterval, ssbuf), + 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("\t" + "ReachableTime: %s, " + "RetransTimer: %s, " + "CurHopLimit: %d\n", + sec2str(rai->rai_reachabletime, ssbuf), + 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: %s", + (rat == NULL) ? "never\n" : + ctime((time_t *)&rat->rat_tm.tv_sec)); + printf("\tLast RA sent: %s", + (ifi_s->ifi_ra_lastsent.tv_sec == 0) ? "never\n" : + ctime((time_t *)&ifi_s->ifi_ra_lastsent.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 timeval now; + + gettimeofday(&now, NULL); + 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 timeval *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 9dbfc997fabc..d48832d021cf 100644 --- a/usr.sbin/rtadvd/Makefile +++ b/usr.sbin/rtadvd/Makefile @@ -16,12 +16,13 @@ PROG= rtadvd MAN= rtadvd.conf.5 rtadvd.8 -SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c dump.c +SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c timer_subr.c \ + control.c control_server.c DPADD= ${LIBUTIL} LDADD= -lutil -CFLAGS+= -DHAVE_ARC4RANDOM -DHAVE_POLL_H -DROUTEINFO +CFLAGS+= -DHAVE_ARC4RANDOM WARNS?= 1 diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c index c0e442bd70e9..681611f49514 100644 --- a/usr.sbin/rtadvd/config.c +++ b/usr.sbin/rtadvd/config.c @@ -3,6 +3,7 @@ /* * Copyright (C) 1998 WIDE Project. + * Copyright (C) 2011 Hiroki Sato * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -52,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -130,48 +132,153 @@ dname_labelenc(char *dst, const char *src) var = def; \ } while (0) -#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) +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(char *ifl_names[], const int ifl_len) +loadconfig_ifname(char *ifname) { - int i; - int idx; - int error; + struct ifinfo *ifi; - for (i = 0; i < ifl_len; i++) { - idx = if_nametoindex(ifl_names[i]); - if (idx == 0) { + 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> interface %s not found. " - "Ignored at this moment.", __func__, ifl_names[i]); + "<%s> %s not found. " + "Ignored at this moment.", __func__, + ifi->ifi_ifname); continue; } - syslog(LOG_INFO, - "<%s> loading config for %s.", __func__, ifl_names[i]); - error = getconfig(idx); - if (error) + if (getconfig(ifi) == NULL) { syslog(LOG_ERR, "<%s> invalid configuration for %s. " - "Ignored at this moment.", __func__, ifl_names[i]); + "Ignored at this moment.", __func__, + ifi->ifi_ifname); + continue; + } } - return (0); } int -rmconfig(int idx) +rm_ifinfo_index(int idx) +{ + struct ifinfo *ifi; + + 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 becasue 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 rainfo *rai; struct prefix *pfx; struct soliciter *sol; struct rdnss *rdn; @@ -179,26 +286,16 @@ rmconfig(int idx) struct dnssl *dns; struct rtinfo *rti; - rai = if_indextorainfo(idx); - if (rai == NULL) { - syslog(LOG_ERR, "<%s>: rainfo not found (idx=%d)", - __func__, idx); - return (-1); - } + syslog(LOG_DEBUG, "<%s>: enter", __func__); TAILQ_REMOVE(&railist, rai, rai_next); - syslog(LOG_DEBUG, "<%s>: rainfo (idx=%d) removed.", - __func__, idx); - - /* Free all of allocated memories for this entry. */ - rtadvd_remove_timer(rai->rai_timer); + 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); - if (rai->rai_sdl != NULL) - free(rai->rai_sdl); - while ((pfx = TAILQ_FIRST(&rai->rai_prefix)) != NULL) { TAILQ_REMOVE(&rai->rai_prefix, pfx, pfx_next); free(pfx); @@ -224,51 +321,51 @@ rmconfig(int idx) free(rti); } free(rai); - + syslog(LOG_DEBUG, "<%s>: leave", __func__); + return (0); } -int -getconfig(int idx) +struct ifinfo * +getconfig(struct ifinfo *ifi) { int stat, i; + int error; char tbuf[BUFSIZ]; struct rainfo *rai; struct rainfo *rai_old; - long val; + int32_t val; int64_t val64; char buf[BUFSIZ]; char *bp = buf; char *addr, *flagstr; - char intface[IFNAMSIZ]; - if (if_indextoname(idx, intface) == NULL) { - syslog(LOG_ERR, "<%s> invalid index number (%d)", - __func__, idx); - return (-1); + 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); } - TAILQ_FOREACH(rai_old, &railist, rai_next) - if (idx == rai_old->rai_ifindex) - break; - - if ((stat = agetent(tbuf, intface)) <= 0) { + 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__, intface); + __func__, ifi->ifi_ifname); } ELM_MALLOC(rai, exit(1)); TAILQ_INIT(&rai->rai_prefix); -#ifdef ROUTEINFO TAILQ_INIT(&rai->rai_route); -#endif 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")) @@ -282,25 +379,12 @@ getconfig(int idx) else rai->rai_advlinkopt = 1; if (rai->rai_advlinkopt) { - if ((rai->rai_sdl = if_nametosdl(intface)) == NULL) { + if (ifi->ifi_sdl.sdl_type == 0) { syslog(LOG_ERR, "<%s> can't get information of %s", - __func__, intface); + __func__, ifi->ifi_ifname); goto getconfig_free_rai; } - rai->rai_ifindex = rai->rai_sdl->sdl_index; - } else - rai->rai_ifindex = if_nametoindex(intface); - strncpy(rai->rai_ifname, intface, sizeof(rai->rai_ifname)); - syslog(LOG_DEBUG, - "<%s> ifindex = %d on %s", __func__, rai->rai_ifindex, - rai->rai_ifname); - - if ((rai->rai_phymtu = if_getmtu(intface)) == 0) { - rai->rai_phymtu = IPV6_MMTU; - syslog(LOG_WARNING, - "<%s> can't get interface mtu of %s. Treat as %d", - __func__, intface, IPV6_MMTU); } /* @@ -309,24 +393,24 @@ getconfig(int idx) MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL); if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) { syslog(LOG_ERR, - "<%s> maxinterval (%ld) on %s is invalid " + "<%s> maxinterval (%" PRIu32 ") on %s is invalid " "(must be between %u and %u)", __func__, val, - intface, MIN_MAXINTERVAL, MAX_MAXINTERVAL); + ifi->ifi_ifname, MIN_MAXINTERVAL, MAX_MAXINTERVAL); goto getconfig_free_rai; } - rai->rai_maxinterval = (u_int)val; + rai->rai_maxinterval = (uint16_t)val; MAYHAVE(val, "mininterval", rai->rai_maxinterval/3); - if ((u_int)val < MIN_MININTERVAL || - (u_int)val > (rai->rai_maxinterval * 3) / 4) { + if ((uint16_t)val < MIN_MININTERVAL || + (uint16_t)val > (rai->rai_maxinterval * 3) / 4) { syslog(LOG_ERR, - "<%s> mininterval (%ld) on %s is invalid " + "<%s> mininterval (%" PRIu32 ") on %s is invalid " "(must be between %d and %d)", - __func__, val, intface, MIN_MININTERVAL, + __func__, val, ifi->ifi_ifname, MIN_MININTERVAL, (rai->rai_maxinterval * 3) / 4); goto getconfig_free_rai; } - rai->rai_mininterval = (u_int)val; + rai->rai_mininterval = (uint16_t)val; MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT); rai->rai_hoplimit = val & 0xff; @@ -359,17 +443,17 @@ getconfig(int idx) 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, intface); + __func__, rai->rai_rtpref, ifi->ifi_ifname); goto getconfig_free_rai; } MAYHAVE(val, "rltime", rai->rai_maxinterval * 3); - if ((u_int)val && ((u_int)val < rai->rai_maxinterval || - (u_int)val > MAXROUTERLIFETIME)) { + if ((uint16_t)val && ((uint16_t)val < rai->rai_maxinterval || + (uint16_t)val > MAXROUTERLIFETIME)) { syslog(LOG_ERR, - "<%s> router lifetime (%ld) on %s is invalid " + "<%s> router lifetime (%" PRIu32 ") on %s is invalid " "(must be 0 or between %d and %d)", - __func__, val, intface, rai->rai_maxinterval, + __func__, val, ifi->ifi_ifname, rai->rai_maxinterval, MAXROUTERLIFETIME); goto getconfig_free_rai; } @@ -378,20 +462,20 @@ getconfig(int idx) MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME); if (val < 0 || val > MAXREACHABLETIME) { syslog(LOG_ERR, - "<%s> reachable time (%ld) on %s is invalid " + "<%s> reachable time (%" PRIu32 ") on %s is invalid " "(must be no greater than %d)", - __func__, val, intface, MAXREACHABLETIME); + __func__, val, ifi->ifi_ifname, MAXREACHABLETIME); goto getconfig_free_rai; } - rai->rai_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 (%lld) on %s out of range", - __func__, (long long)val64, intface); + syslog(LOG_ERR, "<%s> retrans time (%" PRIu64 ") on %s out of range", + __func__, val64, ifi->ifi_ifname); goto getconfig_free_rai; } - rai->rai_retranstimer = (u_int32_t)val64; + rai->rai_retranstimer = (uint32_t)val64; if (agetnum("hapref") != -1 || agetnum("hatime") != -1) { syslog(LOG_ERR, @@ -433,21 +517,21 @@ getconfig(int idx) syslog(LOG_ERR, "<%s> multicast prefix (%s) must " "not be advertised on %s", - __func__, addr, intface); + __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, intface); + __func__, addr, ifi->ifi_ifname); makeentry(entbuf, sizeof(entbuf), i, "prefixlen"); MAYHAVE(val, entbuf, 64); if (val < 0 || val > 128) { - syslog(LOG_ERR, "<%s> prefixlen (%ld) for %s " + syslog(LOG_ERR, "<%s> prefixlen (%" PRIu32 ") for %s " "on %s out of range", - __func__, val, addr, intface); + __func__, val, addr, ifi->ifi_ifname); goto getconfig_free_pfx; } pfx->pfx_prefixlen = (int)val; @@ -469,13 +553,13 @@ getconfig(int idx) makeentry(entbuf, sizeof(entbuf), i, "vltime"); MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); if (val64 < 0 || val64 > 0xffffffff) { - syslog(LOG_ERR, "<%s> vltime (%lld) for " + syslog(LOG_ERR, "<%s> vltime (%" PRIu64 ") for " "%s/%d on %s is out of range", - __func__, (long long)val64, - addr, pfx->pfx_prefixlen, intface); + __func__, val64, + addr, pfx->pfx_prefixlen, ifi->ifi_ifname); goto getconfig_free_pfx; } - pfx->pfx_validlifetime = (u_int32_t)val64; + pfx->pfx_validlifetime = (uint32_t)val64; makeentry(entbuf, sizeof(entbuf), i, "vltimedecr"); if (agetflag(entbuf)) { @@ -489,13 +573,13 @@ getconfig(int idx) MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME); if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, - "<%s> pltime (%lld) for %s/%d on %s " + "<%s> pltime (%" PRIu64 ") for %s/%d on %s " "is out of range", - __func__, (long long)val64, - addr, pfx->pfx_prefixlen, intface); + __func__, val64, + addr, pfx->pfx_prefixlen, ifi->ifi_ifname); goto getconfig_free_pfx; } - pfx->pfx_preflifetime = (u_int32_t)val64; + pfx->pfx_preflifetime = (uint32_t)val64; makeentry(entbuf, sizeof(entbuf), i, "pltimedecr"); if (agetflag(entbuf)) { @@ -514,28 +598,28 @@ getconfig(int idx) if (rai->rai_advifprefix && rai->rai_pfxs == 0) get_prefix(rai); - MAYHAVE(val, "mtu", 0); - if (val < 0 || (u_int)val > 0xffffffff) { + MAYHAVE(val64, "mtu", 0); + if (val < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, - "<%s> mtu (%ld) on %s out of range", - __func__, val, intface); + "<%s> mtu (%" PRIu64 ") on %s out of range", + __func__, val64, ifi->ifi_ifname); goto getconfig_free_rai; } - rai->rai_linkmtu = (u_int32_t)val; + rai->rai_linkmtu = (uint32_t)val64; if (rai->rai_linkmtu == 0) { char *mtustr; if ((mtustr = (char *)agetstr("mtu", &bp)) && strcmp(mtustr, "auto") == 0) - rai->rai_linkmtu = rai->rai_phymtu; + rai->rai_linkmtu = ifi->ifi_phymtu; } else if (rai->rai_linkmtu < IPV6_MMTU || - rai->rai_linkmtu > rai->rai_phymtu) { + rai->rai_linkmtu > ifi->ifi_phymtu) { syslog(LOG_ERR, - "<%s> advertised link mtu (%lu) on %s is invalid (must " + "<%s> advertised link mtu (%" PRIu32 ") on %s is invalid (must " "be between least MTU (%d) and physical link MTU (%d)", - __func__, (unsigned long)rai->rai_linkmtu, intface, - IPV6_MMTU, rai->rai_phymtu); + __func__, rai->rai_linkmtu, ifi->ifi_ifname, + IPV6_MMTU, ifi->ifi_phymtu); goto getconfig_free_rai; } @@ -550,10 +634,10 @@ getconfig(int idx) exit(1); } memset(&ndi, 0, sizeof(ndi)); - strncpy(ndi.ifname, intface, IFNAMSIZ); + 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__, intface, strerror(errno)); + __func__, ifi->ifi_ifname, strerror(errno)); /* reflect the RA info to the host variables in kernel */ ndi.ndi.chlim = rai->rai_hoplimit; @@ -561,14 +645,13 @@ getconfig(int idx) 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__, intface, strerror(errno)); + __func__, ifi->ifi_ifname, strerror(errno)); close(s); } #endif /* route information */ -#ifdef ROUTEINFO rai->rai_routes = 0; for (i = -1; i < MAXROUTE; i++) { struct rtinfo *rti; @@ -605,14 +688,14 @@ getconfig(int idx) syslog(LOG_ERR, "<%s> multicast route (%s) must " "not be advertised on %s", - __func__, addr, intface); + __func__, addr, ifi->ifi_ifname); goto getconfig_free_rti; } if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) { syslog(LOG_NOTICE, "<%s> link-local route (%s) will " "be advertised on %s", - __func__, addr, intface); + __func__, addr, ifi->ifi_ifname); goto getconfig_free_rti; } #endif @@ -630,9 +713,9 @@ getconfig(int idx) val = 64; } if (val < 0 || val > 128) { - syslog(LOG_ERR, "<%s> prefixlen (%ld) for %s on %s " + syslog(LOG_ERR, "<%s> prefixlen (%" PRIu32 ") for %s on %s " "out of range", - __func__, val, addr, intface); + __func__, val, addr, ifi->ifi_ifname); goto getconfig_free_rti; } rti->rti_prefixlen = (int)val; @@ -668,7 +751,7 @@ getconfig(int idx) syslog(LOG_ERR, "<%s> invalid route preference (%02x) " "for %s/%d on %s", __func__, rti->rti_rtpref, addr, - rti->rti_prefixlen, intface); + rti->rti_prefixlen, ifi->ifi_ifname); goto getconfig_free_rti; } @@ -688,17 +771,19 @@ getconfig(int idx) oentbuf, entbuf); else { fprintf(stderr, "%s should be specified " - "for interface %s.\n", entbuf, intface); + "for interface %s.\n", entbuf, + ifi->ifi_ifname); val64 = rai->rai_lifetime; } } if (val64 < 0 || val64 > 0xffffffff) { - syslog(LOG_ERR, "<%s> route lifetime (%lld) for " + syslog(LOG_ERR, "<%s> route lifetime (%" PRIu64 ") for " "%s/%d on %s out of range", __func__, - (long long)val64, addr, rti->rti_prefixlen, intface); + val64, addr, rti->rti_prefixlen, + ifi->ifi_ifname); goto getconfig_free_rti; } - rti->rti_ltime = (u_int32_t)val64; + rti->rti_ltime = (uint32_t)val64; /* link into chain */ TAILQ_INSERT_TAIL(&rai->rai_route, rti, rti_next); @@ -707,7 +792,7 @@ getconfig(int idx) getconfig_free_rti: free(rti); } -#endif + /* DNS server and DNS search list information */ for (i = -1; i < MAXRDNSSENT ; i++) { struct rdnss *rdn; @@ -739,11 +824,11 @@ getconfig(int idx) makeentry(entbuf, sizeof(entbuf), i, "rdnssltime"); MAYHAVE(val, entbuf, (rai->rai_maxinterval * 3 / 2)); - if ((u_int)val < rai->rai_maxinterval || - (u_int)val > rai->rai_maxinterval * 2) { - syslog(LOG_ERR, "%s (%ld) on %s is invalid " + 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, intface, rai->rai_maxinterval, + entbuf, val, ifi->ifi_ifname, rai->rai_maxinterval, rai->rai_maxinterval * 2); goto getconfig_free_rdn; } @@ -788,11 +873,11 @@ getconfig(int idx) makeentry(entbuf, sizeof(entbuf), i, "dnsslltime"); MAYHAVE(val, entbuf, (rai->rai_maxinterval * 3 / 2)); - if ((u_int)val < rai->rai_maxinterval || - (u_int)val > rai->rai_maxinterval * 2) { - syslog(LOG_ERR, "%s (%ld) on %s is invalid " + 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, intface, rai->rai_maxinterval, + entbuf, val, ifi->ifi_ifname, rai->rai_maxinterval, rai->rai_maxinterval * 2); goto getconfig_free_dns; } @@ -816,36 +901,108 @@ getconfig(int idx) * Before the removal, RDNSS and DNSSL options with * zero-lifetime will be sent. */ - if (rai_old != NULL) { - const int retrans = MAX_FINAL_RTR_ADVERTISEMENTS; - struct rdnss *rdn; - struct dnssl *dns; + switch (ifi->ifi_state) { + case IFI_STATE_UNCONFIGURED: + /* UNCONFIGURED -> TRANSITIVE */ - 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; + error = sock_mc_join(&sock, ifi->ifi_ifindex); + if (error) + exit(1); - make_packet(rai_old); - for (i = 0; i < retrans; i++) { - ra_output(rai_old); - sleep(MIN_DELAY_BETWEEN_RAS); + 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); } - rmconfig(idx); + 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; } - TAILQ_INSERT_TAIL(&railist, rai, rai_next); - /* set timer */ - rai->rai_timer = rtadvd_add_timer(ra_timeout, ra_timer_update, - rai, rai); - ra_timer_update((void *)rai, &rai->rai_timer->rat_tm); - rtadvd_set_timer(&rai->rai_timer->rat_tm, rai->rai_timer); + return (ifi); - return (0); getconfig_free_rai: free(rai); - return (-1); + return (NULL); } void @@ -854,8 +1011,9 @@ get_prefix(struct rainfo *rai) struct ifaddrs *ifap, *ifa; 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, @@ -863,20 +1021,22 @@ get_prefix(struct rainfo *rai) __func__); exit(1); } + ifi = rai->rai_ifinfo; for (ifa = ifap; ifa; ifa = ifa->ifa_next) { int plen; - if (strcmp(ifa->ifa_name, rai->rai_ifname) != 0) + if (strcmp(ifa->ifa_name, ifi->ifi_ifname) != 0) continue; if (ifa->ifa_addr->sa_family != AF_INET6) continue; a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; if (IN6_IS_ADDR_LINKLOCAL(a)) continue; + /* get prefix length */ - m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; - lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len; + 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) { syslog(LOG_ERR, "<%s> failed to get prefixlen " @@ -897,8 +1057,8 @@ get_prefix(struct rainfo *rai) /* set prefix, sweep bits outside of prefixlen */ pfx->pfx_prefixlen = plen; memcpy(&pfx->pfx_prefix, a, sizeof(*a)); - p = (u_char *)&pfx->pfx_prefix; - ep = (u_char *)(&pfx->pfx_prefix + 1); + p = (char *)&pfx->pfx_prefix; + ep = (char *)(&pfx->pfx_prefix + 1); while (m < lim && p < ep) *p++ &= *m++; while (p < ep) @@ -910,7 +1070,7 @@ get_prefix(struct rainfo *rai) } syslog(LOG_DEBUG, "<%s> add %s/%d to prefix list on %s", - __func__, ntopbuf, pfx->pfx_prefixlen, rai->rai_ifname); + __func__, ntopbuf, pfx->pfx_prefixlen, ifi->ifi_ifname); /* set other fields with protocol defaults */ pfx->pfx_validlifetime = DEF_ADVVALIDLIFETIME; @@ -951,8 +1111,10 @@ static void add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr) { struct prefix *pfx; - u_char ntopbuf[INET6_ADDRSTRLEN]; + 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; @@ -968,11 +1130,9 @@ add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr) syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s", __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf, - sizeof(ntopbuf)), ipr->ipr_plen, rai->rai_ifname); + sizeof(ntopbuf)), ipr->ipr_plen, ifi->ifi_ifname); - /* reconstruct the packet */ rai->rai_pfxs++; - make_packet(rai); } /* @@ -983,30 +1143,34 @@ add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr) void delete_prefix(struct prefix *pfx) { - u_char ntopbuf[INET6_ADDRSTRLEN]; struct rainfo *rai; + struct ifinfo *ifi; + char ntopbuf[INET6_ADDRSTRLEN]; 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, &pfx->pfx_prefix, ntopbuf, - sizeof(ntopbuf)), pfx->pfx_prefixlen, rai->rai_ifname); + sizeof(ntopbuf)), pfx->pfx_prefixlen, ifi->ifi_ifname); if (pfx->pfx_timer) rtadvd_remove_timer(pfx->pfx_timer); free(pfx); + rai->rai_pfxs--; - make_packet(rai); } void invalidate_prefix(struct prefix *pfx) { - u_char ntopbuf[INET6_ADDRSTRLEN]; struct timeval 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", @@ -1017,7 +1181,7 @@ invalidate_prefix(struct prefix *pfx) syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, " "will expire in %ld seconds", __func__, inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, sizeof(ntopbuf)), - pfx->pfx_prefixlen, rai->rai_ifname, (long)prefix_timo); + pfx->pfx_prefixlen, ifi->ifi_ifname, (long)prefix_timo); /* set the expiration timer */ pfx->pfx_timer = rtadvd_add_timer(prefix_timeout, NULL, pfx, NULL); @@ -1043,10 +1207,12 @@ prefix_timeout(void *arg) void update_prefix(struct prefix *pfx) { - u_char ntopbuf[INET6_ADDRSTRLEN]; struct rainfo *rai; + struct ifinfo *ifi; + char ntopbuf[INET6_ADDRSTRLEN]; 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", @@ -1056,7 +1222,7 @@ update_prefix(struct prefix *pfx) syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s", __func__, inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, - sizeof(ntopbuf)), pfx->pfx_prefixlen, rai->rai_ifname); + sizeof(ntopbuf)), pfx->pfx_prefixlen, ifi->ifi_ifname); /* stop the expiration timer */ rtadvd_remove_timer(pfx->pfx_timer); @@ -1091,7 +1257,7 @@ 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)." @@ -1143,25 +1309,25 @@ make_packet(struct rainfo *rai) struct nd_router_advert *ra; struct nd_opt_prefix_info *ndopt_pi; struct nd_opt_mtu *ndopt_mtu; -#ifdef ROUTEINFO struct nd_opt_route_info *ndopt_rti; struct rtinfo *rti; -#endif struct 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 (rai->rai_advlinkopt) { - if ((lladdroptlen = lladdropt_length(rai->rai_sdl)) == 0) { + 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__, rai->rai_ifname); + __func__, ifi->ifi_ifname); rai->rai_advlinkopt = 0; } packlen += lladdroptlen; @@ -1170,11 +1336,11 @@ make_packet(struct rainfo *rai) packlen += sizeof(struct nd_opt_prefix_info) * rai->rai_pfxs; if (rai->rai_linkmtu) packlen += sizeof(struct nd_opt_mtu); -#ifdef ROUTEINFO + TAILQ_FOREACH(rti, &rai->rai_route, rti_next) packlen += sizeof(struct nd_opt_route_info) + ((rti->rti_prefixlen + 0x3f) >> 6) * 8; -#endif + TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) { struct rdnss_addr *rdna; @@ -1217,7 +1383,7 @@ make_packet(struct rainfo *rai) 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 & rai->rai_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 @@ -1234,7 +1400,7 @@ make_packet(struct rainfo *rai) buf += sizeof(*ra); if (rai->rai_advlinkopt) { - lladdropt_fill(rai->rai_sdl, (struct nd_opt_hdr *)buf); + lladdropt_fill(&ifi->ifi_sdl, (struct nd_opt_hdr *)buf); buf += lladdroptlen; } @@ -1248,7 +1414,7 @@ make_packet(struct rainfo *rai) } TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { - u_int32_t vltime, pltime; + uint32_t vltime, pltime; struct timeval now; ndopt_pi = (struct nd_opt_prefix_info *)buf; @@ -1270,7 +1436,7 @@ make_packet(struct rainfo *rai) if (pfx->pfx_vltimeexpire == 0) vltime = pfx->pfx_validlifetime; else - vltime = (pfx->pfx_vltimeexpire > now.tv_sec) ? + vltime = ((time_t)pfx->pfx_vltimeexpire > now.tv_sec) ? pfx->pfx_vltimeexpire - now.tv_sec : 0; } if (pfx->pfx_timer) @@ -1279,7 +1445,7 @@ make_packet(struct rainfo *rai) if (pfx->pfx_pltimeexpire == 0) pltime = pfx->pfx_preflifetime; else - pltime = (pfx->pfx_pltimeexpire > now.tv_sec) ? + pltime = ((time_t)pfx->pfx_pltimeexpire > now.tv_sec) ? pfx->pfx_pltimeexpire - now.tv_sec : 0; } if (vltime < pltime) { @@ -1297,9 +1463,8 @@ make_packet(struct rainfo *rai) buf += sizeof(struct nd_opt_prefix_info); } -#ifdef ROUTEINFO TAILQ_FOREACH(rti, &rai->rai_route, rti_next) { - u_int8_t psize = (rti->rti_prefixlen + 0x3f) >> 6; + 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; @@ -1310,7 +1475,7 @@ make_packet(struct rainfo *rai) memcpy(ndopt_rti + 1, &rti->rti_prefix, psize * 8); buf += sizeof(struct nd_opt_route_info) + psize * 8; } -#endif + TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) { struct rdnss_addr *rdna; @@ -1331,6 +1496,7 @@ make_packet(struct rainfo *rai) syslog(LOG_DEBUG, "<%s>: nd_opt_dnss_len = %d", __func__, ndopt_rdnss->nd_opt_rdnss_len); } + TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) { struct dnssl_addr *dnsa; diff --git a/usr.sbin/rtadvd/config.h b/usr.sbin/rtadvd/config.h index 01886a67b719..219390bb1512 100644 --- a/usr.sbin/rtadvd/config.h +++ b/usr.sbin/rtadvd/config.h @@ -30,9 +30,12 @@ * SUCH DAMAGE. */ -extern int getconfig(int); -extern int rmconfig(int); -extern int loadconfig(char *[], const int); +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 *); @@ -40,7 +43,6 @@ 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 diff --git a/usr.sbin/rtadvd/control.c b/usr.sbin/rtadvd/control.c new file mode 100644 index 000000000000..709fae398add --- /dev/null +++ b/usr.sbin/rtadvd/control.c @@ -0,0 +1,456 @@ +/*- + * 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$ + * + */ + +#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" + +int +cmsg_recv(int fd, char *buf) +{ + int n; + struct ctrl_msg_hdr *cm; + char *msg; + + 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); + + for (;;) { + 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 cmsg_recv_err; + } + if (cm->cm_len > CM_MSG_MAXLEN) { + syslog(LOG_WARNING, + "<%s> received a too large message.", __func__); + goto cmsg_recv_err; + } + if (cm->cm_version != CM_VERSION) { + syslog(LOG_WARNING, + "<%s> version mismatch", __func__); + goto cmsg_recv_err; + } + if (cm->cm_type >= CM_TYPE_MAX) { + syslog(LOG_WARNING, + "<%s> invalid msg type.", __func__); + goto cmsg_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 (;;) { + 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 cmsg_recv_err; + } + buf[CM_MSG_MAXLEN - 1] = '\0'; + } + + return (0); + +cmsg_recv_err: + close(fd); + return (-1); +} + +int +cmsg_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 * +cmsg_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 +cmsg_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 +cmsg_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 * +cmsg_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 000000000000..a7de2ce6ab6e --- /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$ + * + */ + +#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 cmsg_send(int, char *); +int cmsg_recv(int, char *); + +size_t cmsg_pl2bin(char *, struct ctrl_msg_pl *); +struct ctrl_msg_pl *cmsg_bin2pl(char *, struct ctrl_msg_pl *); +size_t cmsg_str2bin(char *, void *, size_t); +void *cmsg_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 000000000000..a78bcc92fabc --- /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$ + * + */ + +#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 +cmsg_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 = cmsg_send(fd, buf); + if (error) + syslog(LOG_WARNING, + "<%s> cmsg_send()", __func__); + state = CM_STATE_ACK_WAIT; + break; + case CM_STATE_ACK_WAIT: + error = cmsg_recv(fd, buf); + if (error) { + syslog(LOG_ERR, + "<%s> cmsg_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 000000000000..89a7d803158d --- /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$ + * + */ + +int cmsg_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 000000000000..7d2ddc510b45 --- /dev/null +++ b/usr.sbin/rtadvd/control_server.c @@ -0,0 +1,742 @@ +/*- + * 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$ + * + */ + +#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_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, cmsg_getprop_##key } + +static int cmsg_getprop_echo(struct ctrl_msg_pl *); +static int cmsg_getprop_version(struct ctrl_msg_pl *); +static int cmsg_getprop_ifilist(struct ctrl_msg_pl *); +static int cmsg_getprop_ifi(struct ctrl_msg_pl *); +static int cmsg_getprop_ifi_ra_timer(struct ctrl_msg_pl *); +static int cmsg_getprop_rai(struct ctrl_msg_pl *); +static int cmsg_getprop_pfx(struct ctrl_msg_pl *); +static int cmsg_getprop_rdnss(struct ctrl_msg_pl *); +static int cmsg_getprop_dnssl(struct ctrl_msg_pl *); +static int cmsg_getprop_rti(struct ctrl_msg_pl *); + +static int cmsg_setprop_reload(struct ctrl_msg_pl *); +static int cmsg_setprop_enable(struct ctrl_msg_pl *); +static int cmsg_setprop_disable(struct ctrl_msg_pl *); + +static struct dispatch_table { + const char *dt_comm; + int (*dt_act)(struct ctrl_msg_pl *cp); +} getprop_dtable[] = { + { "", cmsg_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 +cmsg_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 +cmsg_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 +cmsg_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 +cmsg_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 = cmsg_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 +cmsg_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 = cmsg_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 +cmsg_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 = cmsg_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 +cmsg_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 +cmsg_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 +cmsg_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 +cmsg_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 +cmsg_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 +cmsg_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) + cmsg_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) + cmsg_setprop_enable(cp); + else if (strncmp(cp->cp_key, "disable", sizeof("disable")) == 0) + cmsg_setprop_disable(cp); + else if (strncmp(cp->cp_key, "echo", 8) == 0) + ; /* do nothing */ + else + return (1); + + return (0); +} + +static int +cmsg_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 +cmsg_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 +cmsg_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); + } + + ifi->ifi_persist = 0; + + return (0); +} + +int +cmsg_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 = cmsg_send(fd, buf); + if (error) + syslog(LOG_WARNING, + "<%s> cmsg_send()", __func__); + state = CM_STATE_EOM; + break; + case CM_STATE_ACK_WAIT: + error = cmsg_recv(fd, buf); + if (error) { + syslog(LOG_ERR, + "<%s> cmsg_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 = cmsg_recv(fd, buf); + + if (error) { + syslog(LOG_ERR, + "<%s> cmsg_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: + cmsg_bin2pl(msg, &cp); + error = cmsg_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 += cmsg_pl2bin(msg, &cp); + } + if (cp.cp_val != NULL) + free(cp.cp_val); + break; + case CM_TYPE_REQ_SET_PROP: + cmsg_bin2pl(msg, &cp); + error = cmsg_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 000000000000..2aab0cd1113f --- /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$ + * + */ + +int cmsg_getprop(struct ctrl_msg_pl *); +int cmsg_setprop(struct ctrl_msg_pl *); + +int cmsg_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/dump.c b/usr.sbin/rtadvd/dump.c deleted file mode 100644 index fac3fb24de81..000000000000 --- a/usr.sbin/rtadvd/dump.c +++ /dev/null @@ -1,321 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: dump.c,v 1.32 2003/05/19 09:46:50 keiichi Exp $ */ - -/* - * Copyright (C) 2000 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -#include -#include -#include - -#include -#include -#include - -#include - -/* XXX: the following two are non-standard include files */ -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "rtadvd.h" -#include "timer.h" -#include "if.h" -#include "dump.h" - -static FILE *fp; - -extern struct rainfo *ralist; - -static char *ether_str(struct sockaddr_dl *); -static void if_dump(void); -static size_t dname_labeldec(char *, size_t, const char *); - -static const char *rtpref_str[] = { - "medium", /* 00 */ - "high", /* 01 */ - "rsv", /* 10 */ - "low" /* 11 */ -}; - -static char * -ether_str(struct sockaddr_dl *sdl) -{ - static char hbuf[32]; - u_char *cp; - - if (sdl->sdl_alen && sdl->sdl_alen > 5) { - cp = (u_char *)LLADDR(sdl); - snprintf(hbuf, sizeof(hbuf), "%x:%x:%x:%x:%x:%x", - cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]); - } else - snprintf(hbuf, sizeof(hbuf), "NONE"); - - return (hbuf); -} - -static void -if_dump(void) -{ - struct rainfo *rai; - struct prefix *pfx; -#ifdef ROUTEINFO - struct rtinfo *rti; -#endif - struct rdnss *rdn; - struct dnssl *dns; - char prefixbuf[INET6_ADDRSTRLEN]; - struct timeval now; - - gettimeofday(&now, NULL); /* XXX: unused in most cases */ - TAILQ_FOREACH(rai, &railist, rai_next) { - fprintf(fp, "%s:\n", rai->rai_ifname); - - fprintf(fp, " Status: %s\n", - (iflist[rai->rai_ifindex]->ifm_flags & IFF_UP) ? "UP" : - "DOWN"); - - /* control information */ - if (rai->rai_lastsent.tv_sec) { - /* note that ctime() appends CR by itself */ - fprintf(fp, " Last RA sent: %s", - ctime((time_t *)&rai->rai_lastsent.tv_sec)); - } - if (rai->rai_timer) - fprintf(fp, " Next RA will be sent: %s", - ctime((time_t *)&rai->rai_timer->rat_tm.tv_sec)); - else - fprintf(fp, " RA timer is stopped"); - fprintf(fp, " waits: %d, initcount: %d\n", - rai->rai_waiting, rai->rai_initcounter); - - /* statistics */ - fprintf(fp, " statistics: RA(out/in/inconsistent): " - "%llu/%llu/%llu, ", - (unsigned long long)rai->rai_raoutput, - (unsigned long long)rai->rai_rainput, - (unsigned long long)rai->rai_rainconsistent); - fprintf(fp, "RS(input): %llu\n", - (unsigned long long)rai->rai_rsinput); - - /* interface information */ - if (rai->rai_advlinkopt) - fprintf(fp, " Link-layer address: %s\n", - ether_str(rai->rai_sdl)); - fprintf(fp, " MTU: %d\n", rai->rai_phymtu); - - /* Router configuration variables */ - fprintf(fp, " DefaultLifetime: %d, MaxAdvInterval: %d, " - "MinAdvInterval: %d\n", rai->rai_lifetime, - rai->rai_maxinterval, rai->rai_mininterval); - fprintf(fp, " Flags: "); - if (rai->rai_managedflg || rai->rai_otherflg) { - fprintf(fp, "%s", rai->rai_managedflg ? "M" : ""); - fprintf(fp, "%s", rai->rai_otherflg ? "O" : ""); - } else - fprintf(fp, ""); - fprintf(fp, ", "); - fprintf(fp, "Preference: %s, ", - rtpref_str[(rai->rai_rtpref >> 3) & 0xff]); - fprintf(fp, "MTU: %d\n", rai->rai_linkmtu); - fprintf(fp, " ReachableTime: %d, RetransTimer: %d, " - "CurHopLimit: %d\n", rai->rai_reachabletime, - rai->rai_retranstimer, rai->rai_hoplimit); - if (rai->rai_clockskew) - fprintf(fp, " Clock skew: %ldsec\n", - rai->rai_clockskew); - TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { - if (pfx == TAILQ_FIRST(&rai->rai_prefix)) - fprintf(fp, " Prefixes:\n"); - fprintf(fp, " %s/%d(", - inet_ntop(AF_INET6, &pfx->pfx_prefix, prefixbuf, - sizeof(prefixbuf)), pfx->pfx_prefixlen); - switch (pfx->pfx_origin) { - case PREFIX_FROM_KERNEL: - fprintf(fp, "KERNEL, "); - break; - case PREFIX_FROM_CONFIG: - fprintf(fp, "CONFIG, "); - break; - case PREFIX_FROM_DYNAMIC: - fprintf(fp, "DYNAMIC, "); - break; - } - if (pfx->pfx_validlifetime == ND6_INFINITE_LIFETIME) - fprintf(fp, "vltime: infinity"); - else - fprintf(fp, "vltime: %ld", - (long)pfx->pfx_validlifetime); - if (pfx->pfx_vltimeexpire != 0) - fprintf(fp, "(decr,expire %ld), ", - (long)pfx->pfx_vltimeexpire > now.tv_sec ? - (long)pfx->pfx_vltimeexpire - now.tv_sec : - 0); - else - fprintf(fp, ", "); - if (pfx->pfx_preflifetime == ND6_INFINITE_LIFETIME) - fprintf(fp, "pltime: infinity"); - else - fprintf(fp, "pltime: %ld", - (long)pfx->pfx_preflifetime); - if (pfx->pfx_pltimeexpire != 0) - fprintf(fp, "(decr,expire %ld), ", - (long)pfx->pfx_pltimeexpire > now.tv_sec ? - (long)pfx->pfx_pltimeexpire - now.tv_sec : - 0); - else - fprintf(fp, ", "); - fprintf(fp, "flags: "); - if (pfx->pfx_onlinkflg || pfx->pfx_autoconfflg) { - fprintf(fp, "%s", - pfx->pfx_onlinkflg ? "L" : ""); - fprintf(fp, "%s", - pfx->pfx_autoconfflg ? "A" : ""); - } else - fprintf(fp, ""); - if (pfx->pfx_timer) { - struct timeval *rest; - - rest = rtadvd_timer_rest(pfx->pfx_timer); - if (rest) { /* XXX: what if not? */ - fprintf(fp, ", expire in: %ld", - (long)rest->tv_sec); - } - } - fprintf(fp, ")\n"); - } -#ifdef ROUTEINFO - TAILQ_FOREACH(rti, &rai->rai_route, rti_next) { - if (rti == TAILQ_FIRST(&rai->rai_route)) - fprintf(fp, " Route Information:\n"); - fprintf(fp, " %s/%d (", - inet_ntop(AF_INET6, &rti->rti_prefix, - prefixbuf, sizeof(prefixbuf)), - rti->rti_prefixlen); - fprintf(fp, "preference: %s, ", - rtpref_str[0xff & (rti->rti_rtpref >> 3)]); - if (rti->rti_ltime == ND6_INFINITE_LIFETIME) - fprintf(fp, "lifetime: infinity"); - else - fprintf(fp, "lifetime: %ld", - (long)rti->rti_ltime); - fprintf(fp, ")\n"); - } -#endif - TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) { - struct rdnss_addr *rdna; - - if (rdn == TAILQ_FIRST(&rai->rai_rdnss)) - fprintf(fp, " Recursive DNS servers:\n" - " Lifetime\tServers\n"); - - fprintf(fp, " %8u\t", rdn->rd_ltime); - TAILQ_FOREACH(rdna, &rdn->rd_list, ra_next) { - inet_ntop(AF_INET6, &rdna->ra_dns, - prefixbuf, sizeof(prefixbuf)); - - if (rdna != TAILQ_FIRST(&rdn->rd_list)) - fprintf(fp, " \t"); - fprintf(fp, "%s\n", prefixbuf); - } - fprintf(fp, "\n"); - } - - TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) { - struct dnssl_addr *dnsa; - char buf[NI_MAXHOST]; - - if (dns == TAILQ_FIRST(&rai->rai_dnssl)) - fprintf(fp, " DNS search list:\n" - " Lifetime\tDomains\n"); - - fprintf(fp, " %8u\t", dns->dn_ltime); - TAILQ_FOREACH(dnsa, &dns->dn_list, da_next) { - dname_labeldec(buf, sizeof(buf), dnsa->da_dom); - if (dnsa != TAILQ_FIRST(&dns->dn_list)) - fprintf(fp, " \t"); - fprintf(fp, "%s(%d)\n", buf, dnsa->da_len); - } - fprintf(fp, "\n"); - } - } -} - -void -rtadvd_dump_file(const char *dumpfile) -{ - syslog(LOG_DEBUG, "<%s> dump current status to %s", __func__, - dumpfile); - - if ((fp = fopen(dumpfile, "w")) == NULL) { - syslog(LOG_WARNING, "<%s> open a dump file(%s)", - __func__, dumpfile); - return; - } - - if_dump(); - - fclose(fp); -} - -/* 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++ = '.'; - syslog(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/if.c b/usr.sbin/rtadvd/if.c index 8722a056a9dc..b857356a2580 100644 --- a/usr.sbin/rtadvd/if.c +++ b/usr.sbin/rtadvd/if.c @@ -3,6 +3,7 @@ /* * 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 @@ -35,19 +36,24 @@ #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" @@ -59,14 +65,34 @@ ((ap)->sa_len ? ROUNDUP((ap)->sa_len, sizeof(u_long)) : \ sizeof(u_long))) -struct if_msghdr **iflist; -int iflist_init_ok; -size_t ifblock_size; -char *ifblock; +struct sockaddr_in6 sin6_linklocal_allnodes = { + .sin6_len = sizeof(sin6_linklocal_allnodes), + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LINKLOCAL_ALLNODES_INIT, +}; -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_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) @@ -83,133 +109,6 @@ get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) } } -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_version != RTM_VERSION) { - syslog(LOG_ERR, - "<%s> RTM_VERSION mismatch (%d != %d).", - __func__, ifm->ifm_version, RTM_VERSION); - continue; - } - 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) - goto end; - memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len); - -end: - free(buf); - return (ret_sdl); -} - -int -if_getmtu(char *name) -{ - struct ifaddrs *ifap, *ifa; - struct if_data *ifd; - 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) @@ -377,48 +276,24 @@ get_rtm_ifindex(char *buf) return (((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index); } -int -get_ifm_ifindex(char *buf) -{ - struct if_msghdr *ifm = (struct if_msghdr *)buf; - - return ((int)ifm->ifm_index); -} - -int -get_ifam_ifindex(char *buf) -{ - struct ifa_msghdr *ifam = (struct ifa_msghdr *)buf; - - return ((int)ifam->ifam_index); -} - -int -get_ifm_flags(char *buf) -{ - struct if_msghdr *ifm = (struct if_msghdr *)buf; - - return (ifm->ifm_flags); -} - int get_prefixlen(char *buf) { struct rt_msghdr *rtm = (struct rt_msghdr *)buf; struct sockaddr *sa, *rti_info[RTAX_MAX]; - u_char *p, *lim; + 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; @@ -458,140 +333,418 @@ prefixlen(u_char *p, u_char *lim) 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; - return (rtm->rtm_type); -} - -int -rtmsg_len(char *buf) -{ - struct rt_msghdr *rtm = (struct rt_msghdr *)buf; - - return (rtm->rtm_msglen); -} - -int -ifmsg_len(char *buf) -{ - struct if_msghdr *ifm = (struct if_msghdr *)buf; - - return (ifm->ifm_msglen); -} - -/* - * alloc buffer and get if_msghdrs block from kernel, - * and put them into the buffer - */ -static void -get_iflist(char **buf, size_t *size) -{ - int mib[6]; - - mib[0] = CTL_NET; - mib[1] = PF_ROUTE; - mib[2] = 0; - mib[3] = AF_INET6; - mib[4] = NET_RT_IFLIST; - mib[5] = 0; - - if (sysctl(mib, 6, NULL, size, NULL, 0) < 0) { - syslog(LOG_ERR, "<%s> sysctl: iflist size get failed", - __func__); - exit(1); - } - if ((*buf = malloc(*size)) == 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__); - exit(1); - } - return; -} - -/* - * alloc buffer and parse if_msghdrs block passed as arg, - * and init the buffer as list of pointers ot each of the if_msghdr. - */ -static void -parse_iflist(struct if_msghdr ***ifmlist_p, char *buf, size_t bufsize) -{ - int iflentry_size, malloc_size; - struct if_msghdr *ifm; - struct ifa_msghdr *ifam; - char *lim; - - /* - * Estimate least size of an iflist entry, to be obtained from kernel. - * Should add sizeof(sockaddr) ?? - */ - iflentry_size = sizeof(struct if_msghdr); - /* roughly estimate max list size of pointers to each if_msghdr */ - malloc_size = (bufsize/iflentry_size) * sizeof(size_t); - if ((*ifmlist_p = (struct if_msghdr **)malloc(malloc_size)) == NULL) { - syslog(LOG_ERR, "<%s> malloc failed", __func__); - exit(1); - } - - lim = buf + bufsize; - for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) { - if (ifm->ifm_msglen == 0) { - syslog(LOG_WARNING, "<%s> ifm_msglen is 0 " - "(buf=%p lim=%p ifm=%p)", __func__, - buf, lim, ifm); - return; - } - - if (ifm->ifm_type == RTM_IFINFO) { - (*ifmlist_p)[ifm->ifm_index] = ifm; + ifi = NULL; + ifindex = if_nametoindex(ifname); + TAILQ_FOREACH(ifi, ifi_head, ifi_next) { + if (ifindex != 0) { + if (ifindex == ifi->ifi_ifindex) + break; } else { - syslog(LOG_ERR, "out of sync parsing NET_RT_IFLIST\n" - "expected %d, got %d\n msglen = %d\n" - "buf:%p, ifm:%p, lim:%p\n", - RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen, - buf, ifm, lim); - exit (1); - } - for (ifam = (struct ifa_msghdr *) - ((char *)ifm + ifm->ifm_msglen); - ifam < (struct ifa_msghdr *)lim; - ifam = (struct ifa_msghdr *) - ((char *)ifam + ifam->ifam_msglen)) { - /* just for safety */ - if (!ifam->ifam_msglen) { - syslog(LOG_WARNING, "<%s> ifa_msglen is 0 " - "(buf=%p lim=%p ifam=%p)", __func__, - buf, lim, ifam); - return; - } - if (ifam->ifam_type != RTM_NEWADDR) + if (strncmp(ifname, ifi->ifi_ifname, + sizeof(ifi->ifi_ifname)) == 0) break; } - ifm = (struct if_msghdr *)ifam; } + + 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); + } + + ifi->ifi_persist = 1; + + 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); } -void -init_iflist(void) +int +update_ifinfo_nd_flags(struct ifinfo *ifi) { - syslog(LOG_DEBUG, - "<%s> generate iflist.", __func__); + struct in6_ndireq nd; + int s; + int error; - if (ifblock) { - free(ifblock); - ifblock_size = 0; + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, + "<%s> socket() failed.", __func__); + return (1); } - if (iflist) - free(iflist); - /* get iflist block from kernel */ - get_iflist(&ifblock, &ifblock_size); + /* 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); + syslog(LOG_ERR, + "<%s> ioctl() failed.", __func__); + return (1); + } + ifi->ifi_nd_flags = nd.ndi.flags; + close(s); - /* make list of pointers to each if_msghdr */ - parse_iflist(&iflist, ifblock, ifblock_size); + return (0); +} + +struct ifinfo * +update_ifinfo(struct ifilist_head_t *ifi_head, int ifindex) +{ + 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 ((msg = malloc(len)) == NULL) { + syslog(LOG_ERR, "<%s> malloc failed", __func__); + exit(1); + } + 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); + } + + 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; + + syslog(LOG_DEBUG, "<%s> ifm = %p, lim = %p, diff = %zu", + __func__, ifm, lim, (char *)lim - (char *)ifm); + + 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", __func__); + free(msg); + return (NULL); + } + + ifi_new = 0; + if (ifm->ifm_type == RTM_IFINFO) { + 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", + RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen); + exit(1); + } + + 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); + } + } + free(msg); + + if (mcastif != NULL) { + error = sock_mc_rr_update(&sock, mcastif); + if (error) + exit(1); + } + + return (ifi); +} + +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) +{ + 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); + } + 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); + + return (0); } diff --git a/usr.sbin/rtadvd/if.h b/usr.sbin/rtadvd/if.h index 8728e1991fac..6efdd562fb44 100644 --- a/usr.sbin/rtadvd/if.h +++ b/usr.sbin/rtadvd/if.h @@ -30,29 +30,32 @@ * SUCH DAMAGE. */ -#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 13329da347c6..248ee19f66e0 100644 --- a/usr.sbin/rtadvd/pathnames.h +++ b/usr.sbin/rtadvd/pathnames.h @@ -2,6 +2,5 @@ /* $FreeBSD$ */ #define _PATH_RTADVDCONF "/etc/rtadvd.conf" -#define _PATH_RTADVDDUMP "/var/run/rtadvd.dump" #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 660ed5397bcf..3e3631151556 100644 --- a/usr.sbin/rtadvd/rrenum.c +++ b/usr.sbin/rtadvd/rrenum.c @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -142,6 +143,7 @@ do_use_prefix(int len, struct rr_pco_match *rpm, { struct rr_pco_use *rpu, *rpulim; struct rainfo *rai; + struct ifinfo *ifi; struct prefix *pfx; rpu = (struct rr_pco_use *)(rpm + 1); @@ -207,8 +209,10 @@ do_use_prefix(int len, struct rr_pco_match *rpm, 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; TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { struct timeval now; @@ -250,7 +254,8 @@ 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); @@ -270,12 +275,18 @@ 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 ((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); @@ -305,8 +316,7 @@ 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; diff --git a/usr.sbin/rtadvd/rtadvd.8 b/usr.sbin/rtadvd/rtadvd.8 index b41f7c06a07d..b2e667023242 100644 --- a/usr.sbin/rtadvd/rtadvd.8 +++ b/usr.sbin/rtadvd/rtadvd.8 @@ -29,7 +29,7 @@ .\" .\" $FreeBSD$ .\" -.Dd December 22, 2010 +.Dd July 14, 2011 .Dt RTADVD 8 .Os .Sh NAME @@ -39,7 +39,6 @@ .Nm .Op Fl dDfRs .Op Fl c Ar configfile -.Op Fl F Ar dumpfile .Op Fl M Ar ifname .Op Fl p Ar pidfile .Ar interface ... @@ -129,13 +128,6 @@ 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 F -Specify an alternative file in which to dump internal states when -.Nm -receives signal -.Dv SIGUSR1 . -The default is -.Pa /var/run/rtadvd.dump . .It Fl M Specify an interface to join the all-routers site-local multicast group. By default, @@ -160,14 +152,6 @@ Do not add or delete prefixes dynamically. Only statically configured prefixes, if any, will be advertised. .El .Pp -Upon receipt of signal -.Dv SIGUSR1 , -.Nm -will dump the current internal state into -.Pa /var/run/rtadvd.dump -or the file specified with option -.Fl F . -.Pp Use .Dv SIGHUP to reload the configuration file @@ -196,10 +180,6 @@ to all the interfaces The default configuration file. .It Pa /var/run/rtadvd.pid The default process ID file. -.It Pa /var/run/rtadvd.dump -The default file in which -.Nm -dumps its internal state. .El .Sh EXIT STATUS .Ex -std diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c index e9b212c0bd5f..2d94b0670315 100644 --- a/usr.sbin/rtadvd/rtadvd.c +++ b/usr.sbin/rtadvd/rtadvd.c @@ -3,6 +3,7 @@ /* * 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 @@ -36,12 +37,14 @@ #include #include #include +#include #include #include +#include #include -#include #include +#include #include #include #include @@ -58,50 +61,52 @@ #include #include #include +#include #include #include +#include #include #include #include -#ifdef HAVE_POLL_H #include -#endif +#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 "pathnames.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; -volatile sig_atomic_t do_dump; -volatile sig_atomic_t do_die; -volatile sig_atomic_t do_reload; struct msghdr sndmhdr; struct iovec rcviov[2]; struct iovec sndiov[2]; struct sockaddr_in6 rcvfrom; -static const char *dumpfilename = _PATH_RTADVDDUMP; static const char *pidfilename = _PATH_RTADVDPID; const char *conffile = _PATH_RTADVDCONF; static struct pidfh *pfh; -static char *mcastif; -int sock; -int rtsock = -1; -int accept_rr = 0; int dflag = 0, sflag = 0; -static int ifl_len; -static char **ifl_names; +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 nd_optlist { TAILQ_ENTRY(nd_optlist) nol_next; @@ -134,7 +139,7 @@ union nd_opt { #define NDOPT_FLAG_RDNSS (1 << 5) #define NDOPT_FLAG_DNSSL (1 << 6) -u_int32_t ndopt_flags[] = { +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, @@ -144,30 +149,10 @@ u_int32_t ndopt_flags[] = { [ND_OPT_DNSSL] = NDOPT_FLAG_DNSSL, }; -struct sockaddr_in6 sin6_linklocal_allnodes = { - .sin6_len = sizeof(sin6_linklocal_allnodes), - .sin6_family = AF_INET6, - .sin6_addr = IN6ADDR_LINKLOCAL_ALLNODES_INIT, -}; - -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, -}; - -static void set_reload(int); -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 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 *, @@ -175,36 +160,31 @@ static void ra_input(int, struct nd_router_advert *, 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 *, u_int32_t); + union nd_opt *, uint32_t); static void free_ndopts(union nd_opt *); -static void rtmsg_input(void); -static void rtadvd_set_dump_file(int); -static void set_short_delay(struct rainfo *); -static int ifl_lookup(char *, char **, int); +static void rtmsg_input(struct sockinfo *); +static void set_short_delay(struct ifinfo *); static int check_accept_rtadv(int); -static int getinet6sysctl(int); int main(int argc, char *argv[]) { -#ifdef HAVE_POLL_H - struct pollfd set[2]; -#else - fd_set *fdsetp, *selectfdp; - int fdmasks; - int maxfd = 0; -#endif + struct pollfd set[PFD_MAX]; struct timeval *timeout; int i, ch; int fflag = 0, logopt; + int error; pid_t pid, otherpid; /* get command line options and arguments */ - while ((ch = getopt(argc, argv, "c:dDfF:M:p:Rs")) != -1) { + while ((ch = getopt(argc, argv, "c:C:dDfM:p:Rs")) != -1) { switch (ch) { case 'c': conffile = optarg; break; + case 'C': + ctrlsock.si_name = optarg; + break; case 'd': dflag++; break; @@ -229,9 +209,6 @@ main(int argc, char *argv[]) case 'p': pidfilename = optarg; break; - case 'F': - dumpfilename = optarg; - break; } } argc -= optind; @@ -239,7 +216,7 @@ main(int argc, char *argv[]) if (argc == 0) { fprintf(stderr, "usage: rtadvd [-dDfRs] [-c conffile] " - "[-F dumpfile] [-M ifname] " + "[-C ctrlsockname] [-M ifname] " "[-p pidfile] interfaces...\n"); exit(1); } @@ -265,16 +242,9 @@ main(int argc, char *argv[]) #ifdef __FreeBSD__ srandomdev(); #else - srandom((u_long)time(NULL)); + srandom((unsigned long)time(NULL)); #endif #endif - /* get iflist block from kernel */ - init_iflist(); - ifl_names = argv; - ifl_len = argc; - - loadconfig(argv, argc); - pfh = pidfile_open(pidfilename, 0600, &otherpid); if (pfh == NULL) { if (errno == EEXIST) @@ -284,70 +254,66 @@ main(int argc, char *argv[]) "<%s> failed to open the pid log file, run anyway.", __func__); } - if (!fflag) daemon(1, 0); - sock_open(); + sock_open(&sock); + + update_ifinfo(&ifilist, UPDATE_IFINFO_ALL); + for (i = 0; i < argc; i++) + update_persist_ifinfo(&ifilist, argv[i]); + + csock_open(&ctrlsock, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (ctrlsock.si_fd == -1) { + syslog(LOG_ERR, "<%s> cannot open control socket", __func__); + exit(1); + } /* record the current PID */ pid = getpid(); pidfile_write(pfh); -#ifdef HAVE_POLL_H - set[0].fd = sock; - set[0].events = POLLIN; + set[PFD_RAWSOCK].fd = sock.si_fd; + set[PFD_RAWSOCK].events = POLLIN; if (sflag == 0) { - rtsock_open(); - set[1].fd = rtsock; - set[1].events = POLLIN; + rtsock_open(&rtsock); + set[PFD_RTSOCK].fd = rtsock.si_fd; + set[PFD_RTSOCK].events = POLLIN; } else - set[1].fd = -1; -#else - maxfd = sock; - if (sflag == 0) { - rtsock_open(); - if (rtsock > sock) - maxfd = rtsock; - } 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); - fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask); - if ((fdsetp = malloc(fdmasks)) == NULL) { - err(1, "malloc"); - /*NOTREACHED*/ + error = csock_listen(&ctrlsock); + if (error) { + syslog(LOG_ERR, "<%s> listen failed", __func__); + exit(1); } - if ((selectfdp = malloc(fdmasks)) == NULL) { - err(1, "malloc"); - /*NOTREACHED*/ - } - memset(fdsetp, 0, fdmasks); - FD_SET(sock, fdsetp); - if (rtsock >= 0) - FD_SET(rtsock, fdsetp); -#endif - signal(SIGTERM, set_die); - signal(SIGUSR1, rtadvd_set_dump_file); - signal(SIGHUP, set_reload); + + /* load configuration file */ + set_do_reload(0); while (1) { -#ifndef HAVE_POLL_H - memcpy(selectfdp, fdsetp, fdmasks); /* reinitialize */ -#endif - if (do_dump) { /* SIGUSR1 */ - do_dump = 0; - rtadvd_dump_file(dumpfilename); + if (is_do_shutdown()) + rtadvd_shutdown(); + + 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*/ - } - - if (do_reload) { - loadconfig(argv, argc); - do_reload = 0; - } + /* timeout handler update for active interfaces */ + rtadvd_update_timeout_handler(); /* timer expiration check and reset the timer */ timeout = rtadvd_check_timer(); @@ -363,14 +329,9 @@ main(int argc, char *argv[]) "<%s> there's no timer. waiting for inputs", __func__); } -#ifdef HAVE_POLL_H - if ((i = poll(set, 2, timeout ? (timeout->tv_sec * 1000 + - timeout->tv_usec / 1000) : INFTIM)) < 0) -#else - if ((i = select(maxfd + 1, selectfdp, NULL, NULL, - timeout)) < 0) -#endif - { + if ((i = poll(set, sizeof(set)/sizeof(set[0]), + timeout ? (timeout->tv_sec * 1000 + + timeout->tv_usec / 1000) : INFTIM)) < 0) { /* EINTR would occur upon SIGUSR1 for status dump */ if (errno != EINTR) syslog(LOG_ERR, "<%s> select: %s", @@ -379,102 +340,118 @@ main(int argc, char *argv[]) } if (i == 0) /* timeout */ continue; -#ifdef HAVE_POLL_H - if (rtsock != -1 && set[1].revents & POLLIN) -#else - if (rtsock != -1 && FD_ISSET(rtsock, selectfdp)) -#endif - rtmsg_input(); -#ifdef HAVE_POLL_H - if (set[0].revents & POLLIN) -#else - if (FD_ISSET(sock, selectfdp)) -#endif - 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, "<%s> accept", __func__); + else { + cmsg_handler_server(fd); + close(fd); + } + } } exit(0); /* NOTREACHED */ } -static int -ifl_lookup(char *ifn, char **names, int len) -{ - while (len--) - if (strncmp(names[len], ifn, IFNAMSIZ) == 0) - return (0); - return (-1); -} - static void -rtadvd_set_dump_file(int sig __unused) -{ - - do_dump = 1; -} - -static void -set_reload(int sig __unused) -{ - - do_reload = 1; -} - -static void -set_die(int sig __unused) -{ - - do_die = 1; -} - -static void -die(void) +rtadvd_shutdown(void) { + struct ifinfo *ifi; struct rainfo *rai; struct rdnss *rdn; struct dnssl *dns; - int i; - const int retrans = MAX_FINAL_RTR_ADVERTISEMENTS; + + if (wait_shutdown) { + syslog(LOG_INFO, + "waiting expiration of the all RA timers\n"); + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (ifi->ifi_ra_timer != NULL) + break; + } + if (ifi == NULL) { + syslog(LOG_INFO, "gracefully terminated.\n"); + exit(0); + } + + sleep(1); + return; + } syslog(LOG_DEBUG, "<%s> cease to be an advertising router\n", __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; - make_packet(rai); } - for (i = 0; i < retrans; i++) { - TAILQ_FOREACH(rai, &railist, rai_next) - ra_output(rai); - sleep(MIN_DELAY_BETWEEN_RAS); - } - pidfile_remove(pfh); + 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; - exit(0); + 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); + } + syslog(LOG_INFO, + "<%s> final RA transmission started.\n", __func__); + + 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[IFNAMSIZ]; + 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; - int error; - n = read(rtsock, msg, sizeof(msg)); + if (s == NULL) { + syslog(LOG_ERR, "<%s> internal error", __func__); + exit(1); + } + 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__, rtmsg_type(msg), n); + "(type = %d, len = %d)", __func__, rtm->rtm_type, n); - if (n > rtmsg_len(msg)) { + if (n > rtm->rtm_msglen) { /* * This usually won't happen for messages received on * a routing socket. @@ -483,10 +460,10 @@ rtmsg_input(void) "<%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)); + __func__, n, rtm->rtm_msglen); #if 0 /* adjust length */ - n = rtmsg_len(msg); + n = rtm->rtm_msglen; #endif } @@ -503,7 +480,7 @@ rtmsg_input(void) 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: @@ -511,10 +488,10 @@ 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; case RTM_IFANNOUNCE: ifan = (struct if_announcemsghdr *)next; @@ -531,32 +508,29 @@ rtmsg_input(void) syslog(LOG_INFO, "<%s>: if_announcemsg (idx=%d:%d)", __func__, ifan->ifan_index, ifan->ifan_what); - init_iflist(); - error = ifl_lookup(ifan->ifan_name, - ifl_names, ifl_len); - if (error) { - syslog(LOG_INFO, "<%s>: not a target " - "interface (idx=%d)", __func__, - ifan->ifan_index); - continue; - } - switch (ifan->ifan_what) { case IFAN_ARRIVAL: - error = getconfig(ifan->ifan_index); - if (error) - syslog(LOG_ERR, - "<%s>: getconfig failed (idx=%d)" - " Ignored.", __func__, - ifan->ifan_index); + syslog(LOG_INFO, + "<%s>: interface added (idx=%d)", + __func__, ifan->ifan_index); + update_ifinfo(&ifilist, ifan->ifan_index); + loadconfig_index(ifan->ifan_index); break; case IFAN_DEPARTURE: - error = rmconfig(ifan->ifan_index); - if (error) - syslog(LOG_ERR, - "<%s>: rmconfig failed (idx=%d)" - " Ignored.", __func__, - ifan->ifan_index); + syslog(LOG_INFO, + "<%s>: interface removed (idx=%d)", + __func__, 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; @@ -568,23 +542,28 @@ rtmsg_input(void) if_indextoname(ifindex, ifname)); continue; } - - if ((rai = if_indextorainfo(ifindex)) == NULL) { + ifi = if_indextoifinfo(ifindex); + if (ifi == NULL) { syslog(LOG_DEBUG, - "<%s> route changed on " - "non advertising interface(%s)", - __func__, - if_indextoname(ifindex, ifname)); + "<%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 */ @@ -616,17 +595,13 @@ rtmsg_input(void) inet_ntop(AF_INET6, addr, (char *)addrbuf, sizeof(addrbuf)), - plen, rai->rai_ifname); + 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; @@ -648,7 +623,7 @@ rtmsg_input(void) "but it was not in list", __func__, inet_ntop(AF_INET6, addr, (char *)addrbuf, sizeof(addrbuf)), - plen, rai->rai_ifname); + plen, ifi->ifi_ifname); break; } invalidate_prefix(pfx); @@ -656,12 +631,7 @@ rtmsg_input(void) 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 */ @@ -674,33 +644,36 @@ rtmsg_input(void) /* check if an interface flag is changed */ if ((oldifflags & IFF_UP) && /* UP to DOWN */ - !(iflist[ifindex]->ifm_flags & IFF_UP)) { + !(ifi->ifi_flags & IFF_UP)) { syslog(LOG_INFO, "<%s> interface %s becomes down. stop timer.", - __func__, rai->rai_ifname); - rtadvd_remove_timer(rai->rai_timer); - rai->rai_timer = NULL; + __func__, ifi->ifi_ifname); + rtadvd_remove_timer(ifi->ifi_ra_timer); + ifi->ifi_ra_timer = NULL; } else if (!(oldifflags & IFF_UP) && /* DOWN to UP */ - (iflist[ifindex]->ifm_flags & IFF_UP)) { + (ifi->ifi_flags & IFF_UP)) { syslog(LOG_INFO, "<%s> interface %s becomes up. restart timer.", - __func__, rai->rai_ifname); + __func__, ifi->ifi_ifname); - rai->rai_initcounter = 0; /* reset the counter */ - rai->rai_waiting = 0; /* XXX */ - rai->rai_timer = rtadvd_add_timer(ra_timeout, - ra_timer_update, rai, rai); - ra_timer_update(rai, &rai->rai_timer->rat_tm); - rtadvd_set_timer(&rai->rai_timer->rat_tm, - rai->rai_timer); + 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 && - (iflist[ifindex]->ifm_flags & IFF_UP)) { + (ifi->ifi_flags & IFF_UP)) { /* * An advertised prefix has been added or invalidated. * Will notice the change in a short delay. */ - rai->rai_initcounter = 0; - set_short_delay(rai); + set_short_delay(ifi); } } @@ -708,7 +681,7 @@ rtmsg_input(void) } void -rtadvd_input(void) +rtadvd_input(struct sockinfo *s) { ssize_t i; int *hlimp = NULL; @@ -719,16 +692,23 @@ rtadvd_input(void) 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 */ @@ -764,13 +744,12 @@ rtadvd_input(void) * If we happen to receive data on an interface which is now gone * or down, just discard the data. */ - if (iflist[pi->ipi6_ifindex] == NULL || - (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__, - (iflist[pi->ipi6_ifindex] == NULL) ? "[gone]" : - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + (ifi == NULL) ? "[gone]" : ifi->ifi_ifname); return; } @@ -881,7 +860,7 @@ rtadvd_input(void) ra_input(i, (struct nd_router_advert *)icp, pi, &rcvfrom); break; case ICMP6_ROUTER_RENUMBERING: - if (accept_rr == 0) { + if (mcastif == NULL) { syslog(LOG_ERR, "<%s> received a router renumbering " "message, but not allowed to be accepted", __func__); @@ -909,10 +888,11 @@ static void rs_input(int len, struct nd_router_solicit *rs, struct in6_pktinfo *pi, struct sockaddr_in6 *from) { - u_char ntopbuf[INET6_ADDRSTRLEN]; - u_char ifnamebuf[IFNAMSIZ]; + char ntopbuf[INET6_ADDRSTRLEN]; + char ifnamebuf[IFNAMSIZ]; union nd_opt ndopts; struct rainfo *rai; + struct ifinfo *ifi; struct soliciter *sol; syslog(LOG_DEBUG, @@ -949,10 +929,14 @@ rs_input(int len, struct nd_router_solicit *rs, goto done; } - TAILQ_FOREACH(rai, &railist, rai_next) - if (pi->ipi6_ifindex == (unsigned int)rai->rai_ifindex) - break; - + 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; + } + rai = ifi->ifi_rainfo; if (rai == NULL) { syslog(LOG_INFO, "<%s> RS received on non advertising interface(%s)", @@ -961,7 +945,7 @@ rs_input(int len, struct nd_router_solicit *rs, goto done; } - rai->rai_rsinput++; /* increment statistics */ + rai->rai_ifinfo->ifi_rsinput++; /* * Decide whether to send RA according to the rate-limit @@ -981,10 +965,10 @@ rs_input(int len, struct nd_router_solicit *rs, * If there is already a waiting RS packet, don't * update the timer. */ - if (rai->rai_waiting++) + if (ifi->ifi_rs_waitcount++) goto done; - set_short_delay(rai); + set_short_delay(ifi); done: free_ndopts(&ndopts); @@ -992,7 +976,7 @@ rs_input(int len, struct nd_router_solicit *rs, } static void -set_short_delay(struct rainfo *rai) +set_short_delay(struct ifinfo *ifi) { long delay; /* must not be greater than 1000000 */ struct timeval interval, now, min_delay, tm_tmp, *rest; @@ -1011,7 +995,7 @@ set_short_delay(struct rainfo *rai) #endif interval.tv_sec = 0; interval.tv_usec = delay; - rest = rtadvd_timer_rest(rai->rai_timer); + rest = rtadvd_timer_rest(ifi->ifi_ra_timer); if (TIMEVAL_LT(rest, &interval)) { syslog(LOG_DEBUG, "<%s> random delay is larger than " "the rest of the current timer", __func__); @@ -1026,69 +1010,52 @@ set_short_delay(struct rainfo *rai) * previous advertisement was sent. */ gettimeofday(&now, NULL); - TIMEVAL_SUB(&now, &rai->rai_lastsent, &tm_tmp); + TIMEVAL_SUB(&now, &ifi->ifi_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, rai->rai_timer); + rtadvd_set_timer(&interval, ifi->ifi_ra_timer); } static int check_accept_rtadv(int idx) { - struct in6_ndireq nd; - u_char ifname[IFNAMSIZ]; - int s6; - int error; + struct ifinfo *ifi; - if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (ifi->ifi_ifindex == idx) + break; + } + if (ifi == NULL) { syslog(LOG_ERR, - "<%s> open socket failed for idx=%d.", + "<%s> if (idx=%d) not found. Why?", __func__, idx); return (0); } - if ((if_indextoname(idx, ifname)) == NULL) { +#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, - "<%s> ifindex->ifname failed (idx=%d).", + "<%s> nd6 flags failed (idx=%d)", __func__, idx); - close(s6); return (0); } - memset(&nd, 0, sizeof(nd)); - strncpy(nd.ifname, ifname, sizeof(nd.ifname)); - error = ioctl(s6, SIOCGIFINFO_IN6, &nd); - if (error) { - syslog(LOG_ERR, - "<%s> ioctl(SIOCGIFINFO_IN6) failed for idx=%d.", - __func__, idx); - nd.ndi.flags = 0; - } - close(s6); - return (nd.ndi.flags & ND6_IFF_ACCEPT_RTADV); -} - -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); - } - else - return (value); + return (ifi->ifi_nd_flags & ND6_IFF_ACCEPT_RTADV); +#endif } static void @@ -1096,11 +1063,12 @@ 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]; - u_char ifnamebuf[IFNAMSIZ]; + struct ifinfo *ifi; + char ntopbuf[INET6_ADDRSTRLEN]; + char ifnamebuf[IFNAMSIZ]; union nd_opt ndopts; const char *on_off[] = {"OFF", "ON"}; - u_int32_t reachabletime, retranstimer, mtu; + uint32_t reachabletime, retranstimer, mtu; int inconsistent = 0; int error; @@ -1108,16 +1076,6 @@ ra_input(int len, struct nd_router_advert *nra, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); - if (!check_accept_rtadv(pi->ipi6_ifindex)) { - syslog(LOG_INFO, - "<%s> An RA from %s on %s ignored (no ACCEPT_RTADV flag).", - __func__, - inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, - sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, - ifnamebuf)); - return; - } - /* ND option check */ memset(&ndopts, 0, sizeof(ndopts)); error = nd6_options((struct nd_opt_hdr *)(nra + 1), @@ -1137,8 +1095,8 @@ ra_input(int len, struct nd_router_advert *nra, /* * RA consistency check according to RFC 4861 6.2.7 */ - rai = if_indextorainfo(pi->ipi6_ifindex); - if (rai == 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)", @@ -1148,7 +1106,10 @@ ra_input(int len, struct nd_router_advert *nra, ifnamebuf)); goto done; } - rai->rai_rainput++; /* increment statistics */ + rai = ifi->ifi_rainfo; + ifi->ifi_rainput++; + syslog(LOG_DEBUG, "<%s> ifi->ifi_rainput = %" PRIu64 "\n", __func__, + ifi->ifi_rainput); /* Cur Hop Limit value */ if (nra->nd_ra_curhoplimit && rai->rai_hoplimit && @@ -1156,7 +1117,7 @@ ra_input(int len, struct nd_router_advert *nra, syslog(LOG_INFO, "<%s> CurHopLimit inconsistent on %s:" " %d from %s, %d from us", - __func__, rai->rai_ifname, nra->nd_ra_curhoplimit, + __func__, ifi->ifi_ifname, nra->nd_ra_curhoplimit, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), rai->rai_hoplimit); inconsistent++; @@ -1167,7 +1128,7 @@ ra_input(int len, struct nd_router_advert *nra, syslog(LOG_INFO, "<%s> M flag inconsistent on %s:" " %s from %s, %s from us", - __func__, rai->rai_ifname, on_off[!rai->rai_managedflg], + __func__, ifi->ifi_ifname, on_off[!rai->rai_managedflg], inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), on_off[rai->rai_managedflg]); inconsistent++; @@ -1178,7 +1139,7 @@ ra_input(int len, struct nd_router_advert *nra, syslog(LOG_INFO, "<%s> O flag inconsistent on %s:" " %s from %s, %s from us", - __func__, rai->rai_ifname, on_off[!rai->rai_otherflg], + __func__, ifi->ifi_ifname, on_off[!rai->rai_otherflg], inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), on_off[rai->rai_otherflg]); inconsistent++; @@ -1190,7 +1151,7 @@ ra_input(int len, struct nd_router_advert *nra, syslog(LOG_INFO, "<%s> ReachableTime inconsistent on %s:" " %d from %s, %d from us", - __func__, rai->rai_ifname, reachabletime, + __func__, ifi->ifi_ifname, reachabletime, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), rai->rai_reachabletime); inconsistent++; @@ -1202,7 +1163,7 @@ ra_input(int len, struct nd_router_advert *nra, syslog(LOG_INFO, "<%s> RetranceTimer inconsistent on %s:" " %d from %s, %d from us", - __func__, rai->rai_ifname, retranstimer, + __func__, ifi->ifi_ifname, retranstimer, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), rai->rai_retranstimer); inconsistent++; @@ -1214,7 +1175,7 @@ ra_input(int len, struct nd_router_advert *nra, syslog(LOG_INFO, "<%s> MTU option value inconsistent on %s:" " %d from %s, %d from us", - __func__, rai->rai_ifname, mtu, + __func__, ifi->ifi_ifname, mtu, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), rai->rai_linkmtu); inconsistent++; @@ -1235,7 +1196,7 @@ ra_input(int len, struct nd_router_advert *nra, } if (inconsistent) - rai->rai_rainconsistent++; + ifi->ifi_rainconsistent++; done: free_ndopts(&ndopts); @@ -1247,18 +1208,19 @@ static int prefix_check(struct nd_opt_prefix_info *pinfo, struct rainfo *rai, struct sockaddr_in6 *from) { - u_int32_t preferred_time, valid_time; + struct ifinfo *ifi; + uint32_t preferred_time, valid_time; struct prefix *pfx; int inconsistent = 0; - u_char ntopbuf[INET6_ADDRSTRLEN]; - u_char prefixbuf[INET6_ADDRSTRLEN]; + char ntopbuf[INET6_ADDRSTRLEN]; + char prefixbuf[INET6_ADDRSTRLEN]; struct timeval now; #if 0 /* impossible */ if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION) return (0); #endif - + ifi = rai->rai_ifinfo; /* * log if the adveritsed prefix has link-local scope(sanity check?) */ @@ -1271,7 +1233,7 @@ prefix_check(struct nd_opt_prefix_info *pinfo, sizeof(prefixbuf)), pinfo->nd_opt_pi_prefix_len, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, - sizeof(ntopbuf)), rai->rai_ifname); + sizeof(ntopbuf)), ifi->ifi_ifname); if ((pfx = find_prefix(rai, &pinfo->nd_opt_pi_prefix, pinfo->nd_opt_pi_prefix_len)) == NULL) { @@ -1282,7 +1244,7 @@ prefix_check(struct nd_opt_prefix_info *pinfo, sizeof(prefixbuf)), pinfo->nd_opt_pi_prefix_len, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, - sizeof(ntopbuf)), rai->rai_ifname); + sizeof(ntopbuf)), ifi->ifi_ifname); return (0); } @@ -1303,12 +1265,12 @@ prefix_check(struct nd_opt_prefix_info *pinfo, syslog(LOG_INFO, "<%s> preferred lifetime for %s/%d" " (decr. in real time) inconsistent on %s:" - " %d from %s, %ld from us", + " %" 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, - rai->rai_ifname, preferred_time, + ifi->ifi_ifname, preferred_time, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), pfx->pfx_pltimeexpire); inconsistent++; @@ -1322,7 +1284,7 @@ prefix_check(struct nd_opt_prefix_info *pinfo, inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, sizeof(prefixbuf)), pinfo->nd_opt_pi_prefix_len, - rai->rai_ifname, preferred_time, + ifi->ifi_ifname, preferred_time, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), pfx->pfx_preflifetime); @@ -1336,12 +1298,12 @@ prefix_check(struct nd_opt_prefix_info *pinfo, syslog(LOG_INFO, "<%s> valid lifetime for %s/%d" " (decr. in real time) inconsistent on %s:" - " %d from %s, %ld from us", + " %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, - rai->rai_ifname, preferred_time, + ifi->ifi_ifname, preferred_time, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), pfx->pfx_vltimeexpire); inconsistent++; @@ -1355,7 +1317,7 @@ prefix_check(struct nd_opt_prefix_info *pinfo, inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, sizeof(prefixbuf)), pinfo->nd_opt_pi_prefix_len, - rai->rai_ifname, valid_time, + ifi->ifi_ifname, valid_time, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), pfx->pfx_validlifetime); inconsistent++; @@ -1369,7 +1331,7 @@ find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen) { struct prefix *pfx; int bytelen, bitlen; - u_char bitmask; + char bitmask; TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { if (plen != pfx->pfx_prefixlen) @@ -1398,7 +1360,7 @@ prefix_match(struct in6_addr *p0, int plen0, struct in6_addr *p1, int plen1) { int bytelen, bitlen; - u_char bitmask; + char bitmask; if (plen0 < plen1) return (0); @@ -1421,7 +1383,7 @@ prefix_match(struct in6_addr *p0, int plen0, static int nd6_options(struct nd_opt_hdr *hdr, int limit, - union nd_opt *ndopts, u_int32_t optflags) + union nd_opt *ndopts, uint32_t optflags) { int optlen = 0; @@ -1546,18 +1508,22 @@ free_ndopts(union nd_opt *ndopts) } void -sock_open(void) +sock_open(struct sockinfo *s) { struct icmp6_filter filt; - struct ipv6_mreq mreq; - struct rainfo *rai; 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); + rcvcmsgbuf = (char *)malloc(rcvcmsgbuflen); if (rcvcmsgbuf == NULL) { syslog(LOG_ERR, "<%s> not enough core", __func__); exit(1); @@ -1565,19 +1531,19 @@ sock_open(void) sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)); - sndcmsgbuf = (u_char *)malloc(sndcmsgbuflen); + 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) { + 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; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, + if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0) { syslog(LOG_ERR, "<%s> IPV6_RECVPKTINFO: %s", __func__, strerror(errno)); @@ -1585,7 +1551,7 @@ sock_open(void) } on = 1; /* specify to tell value of hoplimit field of received IP6 hdr */ - if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, + if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)) < 0) { syslog(LOG_ERR, "<%s> IPV6_RECVHOPLIMIT: %s", __func__, strerror(errno)); @@ -1594,62 +1560,16 @@ sock_open(void) 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, + 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); } - /* - * join all routers multicast address on each advertising interface. - */ - memcpy(&mreq.ipv6mr_multiaddr.s6_addr, - &sin6_linklocal_allrouters.sin6_addr, - sizeof(mreq.ipv6mr_multiaddr.s6_addr)); - TAILQ_FOREACH(rai, &railist, rai_next) { - mreq.ipv6mr_interface = rai->rai_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__, rai->rai_ifname, strerror(errno)); - exit(1); - } - } - - /* - * When attending router renumbering, join all-routers site-local - * multicast group. - */ - if (accept_rr) { - memcpy(&mreq.ipv6mr_multiaddr.s6_addr, - &sin6_sitelocal_allrouters.sin6_addr, - sizeof(mreq.ipv6mr_multiaddr.s6_addr)); - 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 = - TAILQ_FIRST(&railist)->rai_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 : - TAILQ_FIRST(&railist)->rai_ifname, - strerror(errno)); - exit(1); - } - } - /* initialize msghdr for receiving packets */ rcviov[0].iov_base = (caddr_t)answer; rcviov[0].iov_len = sizeof(answer); @@ -1672,49 +1592,80 @@ sock_open(void) /* 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)); exit(1); } } -struct rainfo * -if_indextorainfo(int idx) +struct ifinfo * +if_indextoifinfo(int idx) { - struct rainfo *rai; + struct ifinfo *ifi; - TAILQ_FOREACH(rai, &railist, rai_next) { - syslog(LOG_DEBUG, "<%s> rai->rai_ifindex %d == idx %d?", - __func__, rai->rai_ifindex, idx); - if (rai->rai_ifindex == idx) - return (rai); + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (ifi->ifi_ifindex == idx) + return (ifi); } + 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 */ } void -ra_output(struct rainfo *rai) +ra_output(struct ifinfo *ifi) { int i; struct cmsghdr *cm; struct in6_pktinfo *pi; struct soliciter *sol; + struct rainfo *rai; - if ((iflist[rai->rai_ifindex]->ifm_flags & IFF_UP) == 0) { - syslog(LOG_DEBUG, "<%s> %s is not up, skip sending RA", - __func__, rai->rai_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 && (ACCEPT_RTADV || !ip6.forwarding) = no 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 @@ -1725,15 +1676,19 @@ ra_output(struct rainfo *rai) * 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(rai->rai_ifindex), - getinet6sysctl(IPV6CTL_FORWARDING), rai->rai_ifname); + "<%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 (check_accept_rtadv(rai->rai_ifindex)) { + if (check_accept_rtadv(ifi->ifi_ifindex)) { syslog(LOG_INFO, "<%s> non-zero lifetime RA " "on RA receiving interface %s." - " Ignored.", __func__, rai->rai_ifname); + " Ignored.", __func__, ifi->ifi_ifname); return; } if (getinet6sysctl(IPV6CTL_FORWARDING) == 0) { @@ -1758,7 +1713,7 @@ ra_output(struct rainfo *rai) cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); pi = (struct in6_pktinfo *)CMSG_DATA(cm); memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ - pi->ipi6_ifindex = rai->rai_ifindex; + pi->ipi6_ifindex = ifi->ifi_ifindex; /* specify the hop limit of the packet */ { @@ -1772,22 +1727,18 @@ ra_output(struct rainfo *rai) } syslog(LOG_DEBUG, - "<%s> send RA on %s, # of waitings = %d", - __func__, rai->rai_ifname, rai->rai_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 || (size_t)i != rai->rai_ra_datalen) { if (i < 0) { syslog(LOG_ERR, "<%s> sendmsg on %s: %s", - __func__, rai->rai_ifname, + __func__, ifi->ifi_ifname, strerror(errno)); } } - /* update counter */ - if (rai->rai_initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS) - rai->rai_initcounter++; - rai->rai_raoutput++; /* * unicast advertisements @@ -1800,70 +1751,143 @@ ra_output(struct rainfo *rai) } /* update timestamp */ - gettimeofday(&rai->rai_lastsent, NULL); + gettimeofday(&ifi->ifi_ra_lastsent, NULL); - /* reset waiting conter */ - rai->rai_waiting = 0; + /* update counter */ + ifi->ifi_rs_waitcount = 0; + ifi->ifi_raoutput++; + + 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) { + /* Inital burst finished. */ + if (ifi->ifi_rainfo_trans != NULL) + ifi->ifi_rainfo_trans = NULL; + } + + /* 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 *arg) { - struct rainfo *rai; + struct ifinfo *ifi; -#ifdef notyet - /* if necessary, reconstruct the packet. */ -#endif - rai = (struct rainfo *)arg; + ifi = (struct ifinfo *)arg; syslog(LOG_DEBUG, "<%s> RA timer on %s is expired", - __func__, rai->rai_ifname); + __func__, ifi->ifi_ifname); - ra_output(rai); + ra_output(ifi); - return (rai->rai_timer); + return (ifi->ifi_ra_timer); } /* update RA timer */ void ra_timer_update(void *arg, struct timeval *tm) { - long interval; + uint16_t interval; struct rainfo *rai; + struct ifinfo *ifi; - rai = (struct rainfo *)arg; - /* - * 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->rai_mininterval; + ifi = (struct ifinfo *)arg; + rai = ifi->ifi_rainfo; + interval = 0; + + 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); + interval += arc4random_uniform(rai->rai_maxinterval - + rai->rai_mininterval); #else - interval += random() % (rai->rai_maxinterval - - rai->rai_mininterval); + interval += random() % (rai->rai_maxinterval - + rai->rai_mininterval); #endif - - /* - * 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) - */ - if (rai->rai_initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS && - interval > MAX_INITIAL_RTR_ADVERT_INTERVAL) - interval = MAX_INITIAL_RTR_ADVERT_INTERVAL; + 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; syslog(LOG_DEBUG, "<%s> RA timer on %s is set to %ld:%ld", - __func__, rai->rai_ifname, + __func__, ifi->ifi_ifname, (long int)tm->tv_sec, (long int)tm->tv_usec); return; diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h index 190bb0d0a1c0..ac8ce9985a1f 100644 --- a/usr.sbin/rtadvd/rtadvd.h +++ b/usr.sbin/rtadvd/rtadvd.h @@ -3,6 +3,7 @@ /* * Copyright (C) 1998 WIDE Project. + * Copyright (C) 2011 Hiroki Sato * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,6 +31,17 @@ * SUCH DAMAGE. */ +#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 }}} @@ -101,28 +113,26 @@ struct prefix { */ struct rtadvd_timer *pfx_timer; - u_int32_t pfx_validlifetime; /* AdvValidLifetime */ - long pfx_vltimeexpire; /* Expiration of vltime */ - u_int32_t pfx_preflifetime; /* AdvPreferredLifetime */ - long pfx_pltimeexpire; /* Expiration of pltime */ - u_int pfx_onlinkflg; /* bool: AdvOnLinkFlag */ - u_int pfx_autoconfflg; /* bool: AdvAutonomousFlag */ + 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; }; -#ifdef ROUTEINFO struct rtinfo { TAILQ_ENTRY(rtinfo) rti_next; - u_int32_t rti_ltime; /* route lifetime */ - u_int rti_rtpref; /* route preference */ + uint32_t rti_ltime; /* route lifetime */ + int rti_rtpref; /* route preference */ int rti_prefixlen; struct in6_addr rti_prefix; }; -#endif struct rdnss_addr { TAILQ_ENTRY(rdnss_addr) ra_next; @@ -134,8 +144,7 @@ struct rdnss { TAILQ_ENTRY(rdnss) rd_next; TAILQ_HEAD(, rdnss_addr) rd_list; /* list of DNS servers */ - int rd_cnt; /* number of DNS servers */ - u_int32_t rd_ltime; /* number of seconds valid */ + uint32_t rd_ltime; /* number of seconds valid */ }; /* @@ -160,7 +169,7 @@ struct dnssl { TAILQ_ENTRY(dnssl) dn_next; TAILQ_HEAD(, dnssl_addr) dn_list; /* list of search domains */ - u_int32_t dn_ltime; /* number of seconds valid */ + uint32_t dn_ltime; /* number of seconds valid */ }; struct soliciter { @@ -173,70 +182,117 @@ struct rainfo { /* pointer for list */ TAILQ_ENTRY(rainfo) rai_next; - /* timer related parameters */ - struct rtadvd_timer *rai_timer; - /* counter for the first few advertisements */ - int rai_initcounter; - /* timestamp when the latest RA was sent */ - struct timeval rai_lastsent; - /* number of RS waiting for RA */ - int rai_waiting; - /* interface information */ - int rai_ifindex; - int rai_advlinkopt; /* bool: whether include link-layer addr opt */ + struct ifinfo *rai_ifinfo; + + int rai_advlinkopt; /* bool: whether include link-layer addr opt */ int rai_advifprefix; /* bool: gather IF prefixes? */ - struct sockaddr_dl *rai_sdl; - char rai_ifname[IFNAMSIZ]; - u_int32_t rai_phymtu; /* mtu of the physical interface */ /* Router configuration variables */ - u_short rai_lifetime; /* AdvDefaultLifetime */ - u_int rai_maxinterval; /* MaxRtrAdvInterval */ - u_int rai_mininterval; /* MinRtrAdvInterval */ + uint16_t rai_lifetime; /* AdvDefaultLifetime */ + uint16_t rai_maxinterval; /* MaxRtrAdvInterval */ + uint16_t rai_mininterval; /* MinRtrAdvInterval */ int rai_managedflg; /* AdvManagedFlag */ int rai_otherflg; /* AdvOtherConfigFlag */ int rai_rtpref; /* router preference */ - u_int32_t rai_linkmtu; /* AdvLinkMTU */ - u_int32_t rai_reachabletime; /* AdvReachableTime */ - u_int32_t rai_retranstimer; /* AdvRetransTimer */ - u_int rai_hoplimit; /* AdvCurHopLimit */ + uint32_t rai_linkmtu; /* AdvLinkMTU */ + uint32_t rai_reachabletime; /* AdvReachableTime */ + uint32_t rai_retranstimer; /* AdvRetransTimer */ + uint8_t rai_hoplimit; /* AdvCurHopLimit */ TAILQ_HEAD(, prefix) rai_prefix;/* AdvPrefixList(link head) */ int rai_pfxs; /* number of prefixes */ - long rai_clockskew; /* used for consisitency check of lifetimes */ + 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 */ -#ifdef ROUTEINFO TAILQ_HEAD(, rtinfo) rai_route; /* route information option (link head) */ int rai_routes; /* number of route information options */ -#endif /* actual RA packet data and its length */ size_t rai_ra_datalen; - u_char *rai_ra_data; - - /* statistics */ - u_quad_t rai_raoutput; /* # of RAs sent */ - u_quad_t rai_rainput; /* # of RAs received */ - u_quad_t rai_rainconsistent; /* # of RAs inconsistent with ours */ - u_quad_t rai_rsinput; /* # of RSs received */ + char *rai_ra_data; /* info about soliciter */ TAILQ_HEAD(, soliciter) rai_soliciter; /* recent solication source */ }; -/* Interface list including RA information */ +/* 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 + +struct ifinfo { + TAILQ_ENTRY(ifinfo) ifi_next; + + 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 timeval 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 timeval *); -void ra_output(struct rainfo *); +void ra_output(struct ifinfo *); int prefix_match(struct in6_addr *, int, struct in6_addr *, int); -struct rainfo *if_indextorainfo(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 8cad6ad9a0e8..2ea77b5c95bd 100644 --- a/usr.sbin/rtadvd/timer.c +++ b/usr.sbin/rtadvd/timer.c @@ -3,6 +3,7 @@ /* * Copyright (C) 1998 WIDE Project. + * Copyright (C) 2011 Hiroki Sato * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,15 +33,22 @@ #include #include +#include + +#include +#include +#include #include #include #include #include #include -#include "timer.h" +#include -#define MILLION 1000000 +#include "rtadvd.h" +#include "timer_subr.h" +#include "timer.h" struct rtadvd_timer_head_t ra_timer = TAILQ_HEAD_INITIALIZER(ra_timer); @@ -55,6 +63,43 @@ rtadvd_timer_init(void) 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; + } + } + + return; +} + struct rtadvd_timer * rtadvd_add_timer(struct rtadvd_timer *(*timeout)(void *), void (*update)(void *, struct timeval *), @@ -99,22 +144,6 @@ rtadvd_remove_timer(struct rtadvd_timer *rat) free(rat); } -void -rtadvd_set_timer(struct timeval *tm, struct rtadvd_timer *rat) -{ - struct timeval now; - - /* reset the timer */ - gettimeofday(&now, NULL); - TIMEVAL_ADD(&now, tm, &rat->rat_tm); - - /* update the next expiration time */ - if (TIMEVAL_LT(&rat->rat_tm, &tm_max)) - tm_max = rat->rat_tm; - - return; -} - /* * Check expiration for each timer. If a timer expires, * call the expire function for the timer and update the timer. @@ -151,55 +180,18 @@ rtadvd_check_timer(void) return (&returnval); } -struct timeval * -rtadvd_timer_rest(struct rtadvd_timer *rat) +void +rtadvd_set_timer(struct timeval *tm, struct rtadvd_timer *rat) { - static struct timeval returnval, now; + struct timeval now; + /* reset the timer */ gettimeofday(&now, NULL); - if (TIMEVAL_LEQ(&rat->rat_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(&rat->rat_tm, &now, &returnval); + TIMEVAL_ADD(&now, tm, &rat->rat_tm); - return (&returnval); -} - -/* result = a + b */ -void -TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result) -{ - long l; - - if ((l = a->tv_usec + b->tv_usec) < MILLION) { - result->tv_usec = l; - result->tv_sec = a->tv_sec + b->tv_sec; - } - else { - result->tv_usec = l - MILLION; - result->tv_sec = a->tv_sec + b->tv_sec + 1; - } -} - -/* - * result = a - b - * XXX: this function assumes that a >= b. - */ -void -TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result) -{ - long l; - - if ((l = a->tv_usec - b->tv_usec) >= 0) { - result->tv_usec = l; - result->tv_sec = a->tv_sec - b->tv_sec; - } - else { - result->tv_usec = MILLION + l; - result->tv_sec = a->tv_sec - b->tv_sec - 1; - } + /* update the next expiration time */ + if (TIMEVAL_LT(&rat->rat_tm, &tm_max)) + tm_max = rat->rat_tm; + + return; } diff --git a/usr.sbin/rtadvd/timer.h b/usr.sbin/rtadvd/timer.h index e2e0c654e1c1..f70e0d1fdc98 100644 --- a/usr.sbin/rtadvd/timer.h +++ b/usr.sbin/rtadvd/timer.h @@ -30,22 +30,6 @@ * SUCH DAMAGE. */ -/* a < b */ -#define TIMEVAL_LT(a, b) \ - (((a)->tv_sec < (b)->tv_sec) || \ - (((a)->tv_sec == (b)->tv_sec) && \ - ((a)->tv_usec < (b)->tv_usec))) - -/* a <= b */ -#define TIMEVAL_LEQ(a, b) \ - (((a)->tv_sec < (b)->tv_sec) || \ - (((a)->tv_sec == (b)->tv_sec) && \ - ((a)->tv_usec <= (b)->tv_usec))) - -#define TIMEVAL_EQUAL(a,b) \ - (((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 { TAILQ_ENTRY(rtadvd_timer) rat_next; @@ -59,14 +43,10 @@ struct rtadvd_timer { }; void rtadvd_timer_init(void); +void rtadvd_update_timeout_handler(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 *); diff --git a/usr.sbin/rtadvd/timer_subr.c b/usr.sbin/rtadvd/timer_subr.c new file mode 100644 index 000000000000..2bebdd3f5685 --- /dev/null +++ b/usr.sbin/rtadvd/timer_subr.c @@ -0,0 +1,126 @@ +/* $FreeBSD$ */ +/* $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 timeval * +rtadvd_timer_rest(struct rtadvd_timer *rat) +{ + static struct timeval returnval, now; + + gettimeofday(&now, NULL); + if (TIMEVAL_LEQ(&rat->rat_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(&rat->rat_tm, &now, &returnval); + + return (&returnval); +} + +/* result = a + b */ +void +TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result) +{ + long l; + + if ((l = a->tv_usec + b->tv_usec) < MILLION) { + result->tv_usec = l; + result->tv_sec = a->tv_sec + b->tv_sec; + } + else { + result->tv_usec = l - MILLION; + result->tv_sec = a->tv_sec + b->tv_sec + 1; + } +} + +/* + * result = a - b + * XXX: this function assumes that a >= b. + */ +void +TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result) +{ + long l; + + if ((l = a->tv_usec - b->tv_usec) >= 0) { + result->tv_usec = l; + result->tv_sec = a->tv_sec - b->tv_sec; + } + else { + result->tv_usec = MILLION + l; + result->tv_sec = a->tv_sec - b->tv_sec - 1; + } +} + +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/dump.h b/usr.sbin/rtadvd/timer_subr.h similarity index 66% rename from usr.sbin/rtadvd/dump.h rename to usr.sbin/rtadvd/timer_subr.h index 8696e1302f9d..9ceddad76958 100644 --- a/usr.sbin/rtadvd/dump.h +++ b/usr.sbin/rtadvd/timer_subr.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: dump.h,v 1.1 2000/05/23 11:31:26 itojun Exp $ */ +/* $KAME: timer.h,v 1.5 2002/05/31 13:30:38 jinmei Exp $ */ /* * Copyright (C) 1998 WIDE Project. @@ -30,4 +30,28 @@ * SUCH DAMAGE. */ -extern void rtadvd_dump_file(const char *); +#define SSBUFLEN 1024 +#define MILLION 1000000 + +/* 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))) + +#define TIMEVAL_EQUAL(a,b) \ + (((a)->tv_sec == (b)->tv_sec) && \ + ((a)->tv_usec == (b)->tv_usec)) + +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 *); +char *sec2str(uint32_t, char *buf);