From 832f8d224926758a9ae0b23a6b45353e44fbc87a Mon Sep 17 00:00:00 2001 From: ume Date: Mon, 11 Jun 2001 12:39:29 +0000 Subject: Sync with recent KAME. This work was based on kame-20010528-freebsd43-snap.tgz and some critical problem after the snap was out were fixed. There are many many changes since last KAME merge. TODO: - The definitions of SADB_* in sys/net/pfkeyv2.h are still different from RFC2407/IANA assignment because of binary compatibility issue. It should be fixed under 5-CURRENT. - ip6po_m member of struct ip6_pktopts is no longer used. But, it is still there because of binary compatibility issue. It should be removed under 5-CURRENT. Reviewed by: itojun Obtained from: KAME MFC after: 3 weeks --- usr.sbin/rtadvd/advcap.c | 8 +- usr.sbin/rtadvd/advcap.h | 4 +- usr.sbin/rtadvd/config.c | 396 +++++++++++++++++++++++++++++++++--------- usr.sbin/rtadvd/config.h | 2 +- usr.sbin/rtadvd/dump.c | 42 ++++- usr.sbin/rtadvd/dump.h | 2 +- usr.sbin/rtadvd/if.c | 15 +- usr.sbin/rtadvd/if.h | 37 ++-- usr.sbin/rtadvd/pathnames.h | 2 +- usr.sbin/rtadvd/rrenum.c | 96 ++++++++-- usr.sbin/rtadvd/rrenum.h | 7 +- usr.sbin/rtadvd/rtadvd.8 | 45 ++++- usr.sbin/rtadvd/rtadvd.c | 209 +++++++++++++++++----- usr.sbin/rtadvd/rtadvd.conf.5 | 104 ++++++++++- usr.sbin/rtadvd/rtadvd.h | 25 ++- usr.sbin/rtadvd/timer.c | 2 +- usr.sbin/rtadvd/timer.h | 2 +- 17 files changed, 799 insertions(+), 199 deletions(-) (limited to 'usr.sbin/rtadvd') diff --git a/usr.sbin/rtadvd/advcap.c b/usr.sbin/rtadvd/advcap.c index 55a43ce..5cba1f5 100644 --- a/usr.sbin/rtadvd/advcap.c +++ b/usr.sbin/rtadvd/advcap.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: advcap.c,v 1.3 2000/05/16 13:34:13 itojun Exp $ */ +/* $KAME: advcap.c,v 1.5 2001/02/01 09:12:08 jinmei Exp $ */ /* * Copyright (c) 1983 The Regents of the University of California. @@ -96,7 +96,7 @@ int getent __P((char *, char *, char *)); int tnchktc __P((void)); int tnamatch __P((char *)); static char *tskip __P((char *)); -int tgetnum __P((char *)); +long long tgetnum __P((char *)); int tgetflag __P((char *)); char *tgetstr __P((char *, char **)); static char *tdecode __P((char *, char **)); @@ -307,11 +307,11 @@ breakbreak: * a # character. If the option is not found we return -1. * Note that we handle octal numbers beginning with 0. */ -int +long long tgetnum(id) char *id; { - register long int i; + register long long i; register int base; register char *bp = tbuf; diff --git a/usr.sbin/rtadvd/advcap.h b/usr.sbin/rtadvd/advcap.h index dc3f428..7b3715a 100644 --- a/usr.sbin/rtadvd/advcap.h +++ b/usr.sbin/rtadvd/advcap.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME$ */ +/* $KAME: advcap.h,v 1.3 2001/02/01 09:12:08 jinmei Exp $ */ /* * Copyright (C) 1994,1995 by Andrey A. Chernov, Moscow, Russia. @@ -38,7 +38,7 @@ __BEGIN_DECLS extern int agetent __P((char *, const char *)); extern int agetflag __P((const char *)); -extern int agetnum __P((const char *)); +extern long long agetnum __P((const char *)); extern char *agetstr __P((const char *, char **)); __END_DECLS diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c index ec294f1..01cfb4c 100644 --- a/usr.sbin/rtadvd/config.c +++ b/usr.sbin/rtadvd/config.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: config.c,v 1.11 2000/05/16 13:34:13 itojun Exp $ */ +/* $KAME: config.c,v 1.37 2001/05/25 07:34:00 itojun Exp $ */ /* * Copyright (C) 1998 WIDE Project. @@ -34,6 +34,7 @@ #include #include #include +#include #include #if defined(__FreeBSD__) && __FreeBSD__ >= 3 @@ -62,6 +63,7 @@ #include #endif #include +#include #include "rtadvd.h" #include "advcap.h" @@ -71,6 +73,7 @@ static void makeentry __P((char *, int, char *, int)); static void get_prefix __P((struct rainfo *)); +static int getinet6sysctl __P((int)); extern struct rainfo *ralist; @@ -82,9 +85,11 @@ getconfig(intface) char tbuf[BUFSIZ]; struct rainfo *tmp; long val; + long long val64; char buf[BUFSIZ]; char *bp = buf; char *addr; + static int forwarding = -1; #define MUSTHAVE(var, cap) \ do { \ @@ -114,6 +119,13 @@ getconfig(intface) tmp = (struct rainfo *)malloc(sizeof(*ralist)); memset(tmp, 0, sizeof(*tmp)); tmp->prefix.next = tmp->prefix.prev = &tmp->prefix; + tmp->route.next = tmp->route.prev = &tmp->route; + + /* check if we are allowed to forward packets (if not determined) */ + if (forwarding < 0) { + if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0) + exit(1); + } /* get interface information */ if (agetflag("nolladdr")) @@ -164,12 +176,22 @@ getconfig(intface) tmp->hoplimit = val & 0xff; MAYHAVE(val, "raflags", 0); - tmp->managedflg= val & ND_RA_FLAG_MANAGED; + tmp->managedflg = val & ND_RA_FLAG_MANAGED; tmp->otherflg = val & ND_RA_FLAG_OTHER; #ifdef MIP6 if (mobileip6) tmp->haflg = val & ND_RA_FLAG_HA; #endif +#ifndef ND_RA_FLAG_RTPREF_MASK +#define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ +#define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */ +#endif + tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK; + if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) { + syslog(LOG_ERR, "<%s> invalid router preference on %s", + __FUNCTION__, intface); + exit(1); + } MAYHAVE(val, "rltime", tmp->maxinterval * 3); if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) { @@ -180,6 +202,21 @@ getconfig(intface) tmp->maxinterval, MAXROUTERLIFETIME); exit(1); } + /* + * Basically, hosts MUST NOT send Router Advertisement messages at any + * time (RFC 2461, Section 6.2.3). However, it would sometimes be + * useful to allow hosts to advertise some parameters such as prefix + * information and link MTU. Thus, we allow hosts to invoke rtadvd + * only when router lifetime (on every advertising interface) is + * explicitly set zero. (see also the above section) + */ + if (val && forwarding == 0) { + syslog(LOG_WARNING, + "<%s> non zero router lifetime is specified for %s, " + "which must not be allowed for hosts.", + __FUNCTION__, intface); + exit(1); + } tmp->lifetime = val & 0xffff; MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME); @@ -191,20 +228,23 @@ getconfig(intface) } tmp->reachabletime = (u_int32_t)val; - MAYHAVE(val, "retrans", DEF_ADVRETRANSTIMER); - if (val < 0 || val > 0xffffffff) { + MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER); + if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, "<%s> retrans time out of range", __FUNCTION__); exit(1); } - tmp->retranstimer = (u_int32_t)val; + tmp->retranstimer = (u_int32_t)val64; -#ifdef MIP6 - if (!mobileip6) +#ifndef MIP6 + if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) { + syslog(LOG_ERR, + "<%s> mobile-ip6 configuration not supported", + __FUNCTION__); + exit(1); + } #else - if (1) -#endif - { + if (!mobileip6) { if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) { syslog(LOG_ERR, "<%s> mobile-ip6 configuration without " @@ -212,9 +252,7 @@ getconfig(intface) __FUNCTION__); exit(1); } - } -#ifdef MIP6 - else { + } else { tmp->hapref = 0; if ((val = agetnum("hapref")) >= 0) tmp->hapref = (int16_t)val; @@ -233,6 +271,15 @@ getconfig(intface) #endif /* prefix information */ + + /* + * This is an implementation specific parameter to consinder + * link propagation delays and poorly synchronized clocks when + * checking consistency of advertised lifetimes. + */ + MAYHAVE(val, "clockskew", 0); + tmp->clockskew = val; + if ((pfxs = agetnum("addrs")) < 0) { /* auto configure prefix information */ if (agetstr("addr", &bp) || agetstr("addr1", &bp)) { @@ -281,7 +328,7 @@ getconfig(intface) { MAYHAVE(val, entbuf, (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO| - ND_OPT_PI_FLAG_RTADDR)); + ND_OPT_PI_FLAG_ROUTER)); } else #endif { @@ -291,29 +338,44 @@ getconfig(intface) pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK; pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO; #ifdef MIP6 - if (mobileip6) - pfx->routeraddr = val & ND_OPT_PI_FLAG_RTADDR; + pfx->routeraddr = val & ND_OPT_PI_FLAG_ROUTER; #endif makeentry(entbuf, i, "vltime", added); - MAYHAVE(val, entbuf, DEF_ADVVALIDLIFETIME); - if (val < 0 || val > 0xffffffff) { + MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); + if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, "<%s> vltime out of range", __FUNCTION__); exit(1); } - pfx->validlifetime = (u_int32_t)val; + pfx->validlifetime = (u_int32_t)val64; + + makeentry(entbuf, i, "vltimedecr", added); + if (agetflag(entbuf)) { + struct timeval now; + gettimeofday(&now, 0); + pfx->vltimeexpire = + now.tv_sec + pfx->validlifetime; + } makeentry(entbuf, i, "pltime", added); - MAYHAVE(val, entbuf, DEF_ADVPREFERREDLIFETIME); - if (val < 0 || val > 0xffffffff) { + MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME); + if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, "<%s> pltime out of range", __FUNCTION__); exit(1); } - pfx->preflifetime = (u_int32_t)val; + pfx->preflifetime = (u_int32_t)val64; + + makeentry(entbuf, i, "pltimedecr", added); + if (agetflag(entbuf)) { + struct timeval now; + gettimeofday(&now, 0); + pfx->pltimeexpire = + now.tv_sec + pfx->preflifetime; + } makeentry(entbuf, i, "addr", added); addr = (char *)agetstr(entbuf, &bp); @@ -368,6 +430,106 @@ getconfig(intface) exit(1); } + /* route information */ + + MAYHAVE(val, "routes", 0); + if (val < 0 || val > 0xffffffff) { + syslog(LOG_ERR, + "<%s> number of route information improper", __FUNCTION__); + exit(1); + } + tmp->routes = val; + for (i = 0; i < tmp->routes; i++) { + struct rtinfo *rti; + char entbuf[256]; + int added = (tmp->routes > 1) ? 1 : 0; + + /* allocate memory to store prefix information */ + if ((rti = malloc(sizeof(struct rtinfo))) == NULL) { + syslog(LOG_ERR, + "<%s> can't allocate enough memory", + __FUNCTION__); + exit(1); + } + memset(rti, 0, sizeof(*rti)); + + /* link into chain */ + insque(rti, &tmp->route); + + makeentry(entbuf, i, "rtrplen", added); + MAYHAVE(val, entbuf, 64); + if (val < 0 || val > 128) { + syslog(LOG_ERR, + "<%s> prefixlen out of range", + __FUNCTION__); + exit(1); + } + rti->prefixlen = (int)val; + + makeentry(entbuf, i, "rtrflags", added); + MAYHAVE(val, entbuf, 0); + rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK; + if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) { + syslog(LOG_ERR, "<%s> invalid router preference", + __FUNCTION__); + exit(1); + } + + makeentry(entbuf, i, "rtrltime", added); + /* + * XXX: since default value of route lifetime is not defined in + * draft-draves-route-selection-01.txt, I took the default + * value of valid lifetime of prefix as its default. + * It need be much considered. + */ + MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); + if (val64 < 0 || val64 > 0xffffffff) { + syslog(LOG_ERR, + "<%s> rtrltime out of range", + __FUNCTION__); + exit(1); + } + rti->ltime = (u_int32_t)val64; + + makeentry(entbuf, i, "rtrprefix", added); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) { + syslog(LOG_ERR, + "<%s> need %s as an route for " + "interface %s", + __FUNCTION__, entbuf, intface); + exit(1); + } + if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) { + syslog(LOG_ERR, + "<%s> inet_pton failed for %s", + __FUNCTION__, addr); + exit(1); + } +#if 0 + /* + * XXX: currently there's no restriction in route information + * prefix according to draft-draves-route-selection-01.txt, + * however I think the similar restriction be necessary. + */ + MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); + if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) { + syslog(LOG_ERR, + "<%s> multicast route (%s) must " + "not be advertised (IF=%s)", + __FUNCTION__, addr, intface); + exit(1); + } + if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) { + syslog(LOG_NOTICE, + "<%s> link-local route (%s) must " + "not be advertised on %s", + __FUNCTION__, addr, intface); + exit(1); + } +#endif + } + /* okey */ tmp->next = ralist; ralist = tmp; @@ -385,33 +547,26 @@ getconfig(intface) static void get_prefix(struct rainfo *rai) { - size_t len; - u_char *buf, *lim, *next; + struct ifaddrs *ifap, *ifa; + struct prefix *pp; + struct in6_addr *a; + u_char *p, *ep, *m, *lim; u_char ntopbuf[INET6_ADDRSTRLEN]; - if ((len = rtbuf_len()) < 0) { + if (getifaddrs(&ifap) < 0) { syslog(LOG_ERR, - "<%s> can't get buffer length for routing info", + "<%s> can't get interface addresses", __FUNCTION__); exit(1); } - if ((buf = malloc(len)) == NULL) { - syslog(LOG_ERR, - "<%s> can't allocate buffer", __FUNCTION__); - exit(1); - } - if (get_rtinfo(buf, &len) < 0) { - syslog(LOG_ERR, - "<%s> can't get routing inforamtion", __FUNCTION__); - exit(1); - } - - lim = buf + len; - next = get_next_msg(buf, lim, rai->ifindex, &len, - RTADV_TYPE2BITMASK(RTM_GET)); - while (next < lim) { - struct prefix *pp; - struct in6_addr *a; + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (strcmp(ifa->ifa_name, rai->ifname) != 0) + continue; + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + if (IN6_IS_ADDR_LINKLOCAL(a)) + continue; /* allocate memory to store prefix info. */ if ((pp = malloc(sizeof(*pp))) == NULL) { @@ -422,21 +577,35 @@ get_prefix(struct rainfo *rai) } memset(pp, 0, sizeof(*pp)); - /* set prefix and its length */ - a = get_addr(next); - memcpy(&pp->prefix, a, sizeof(*a)); - if ((pp->prefixlen = get_prefixlen(next)) < 0) { + /* set prefix length */ + m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; + lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len; + pp->prefixlen = prefixlen(m, lim); + if (pp->prefixlen < 0 || pp->prefixlen > 128) { syslog(LOG_ERR, "<%s> failed to get prefixlen " - "or prefixl is invalid", + "or prefix is invalid", __FUNCTION__); exit(1); } + + /* set prefix, sweep bits outside of prefixlen */ + memcpy(&pp->prefix, a, sizeof(*a)); + p = (u_char *)&pp->prefix; + ep = (u_char *)(&pp->prefix + 1); + while (m < lim) + *p++ &= *m++; + while (p < ep) + *p++ = 0x00; + + if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf, + sizeof(ntopbuf))) { + syslog(LOG_ERR, "<%s> inet_ntop failed", __FUNCTION__); + exit(1); + } syslog(LOG_DEBUG, "<%s> add %s/%d to prefix list on %s", - __FUNCTION__, - inet_ntop(AF_INET6, a, ntopbuf, INET6_ADDRSTRLEN), - pp->prefixlen, rai->ifname); + __FUNCTION__, ntopbuf, pp->prefixlen, rai->ifname); /* set other fields with protocol defaults */ pp->validlifetime = DEF_ADVVALIDLIFETIME; @@ -450,14 +619,9 @@ get_prefix(struct rainfo *rai) /* counter increment */ rai->pfxs++; - - /* forward pointer and get next prefix(if any) */ - next += len; - next = get_next_msg(next, lim, rai->ifindex, - &len, RTADV_TYPE2BITMASK(RTM_GET)); } - free(buf); + freeifaddrs(ifap); } static void @@ -493,6 +657,7 @@ add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr) __FUNCTION__); return; /* XXX: error or exit? */ } + memset(prefix, 0, sizeof(*prefix)); prefix->prefix = ipr->ipr_prefix.sin6_addr; prefix->prefixlen = ipr->ipr_plen; prefix->validlifetime = ipr->ipr_vltime; @@ -510,7 +675,7 @@ add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr) /* free the previous packet */ free(rai->ra_data); - rai->ra_data = 0; + rai->ra_data = NULL; /* reconstruct the packet */ rai->pfxs++; @@ -617,10 +782,12 @@ make_packet(struct rainfo *rainfo) struct nd_opt_prefix_info *ndopt_pi; struct nd_opt_mtu *ndopt_mtu; #ifdef MIP6 - struct nd_opt_advint *ndopt_advint; - struct nd_opt_hai *ndopt_hai; + struct nd_opt_advinterval *ndopt_advint; + struct nd_opt_homeagent_info *ndopt_hai; #endif + struct nd_opt_route_info *ndopt_rti; struct prefix *pfx; + struct rtinfo *rti; /* calculate total length */ packlen = sizeof(struct nd_router_advert); @@ -641,9 +808,14 @@ make_packet(struct rainfo *rainfo) packlen += sizeof(struct nd_opt_mtu); #ifdef MIP6 if (mobileip6 && rainfo->maxinterval) - packlen += sizeof(struct nd_opt_advint); + packlen += sizeof(struct nd_opt_advinterval); if (mobileip6 && rainfo->hatime) - packlen += sizeof(struct nd_opt_hai); + packlen += sizeof(struct nd_opt_homeagent_info); +#endif +#ifdef ND_OPT_ROUTE_INFO + for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) + packlen += sizeof(struct nd_opt_route_info) + + ((rti->prefixlen + 0x3f) >> 6) * 8; #endif /* allocate memory for the packet */ @@ -653,6 +825,11 @@ make_packet(struct rainfo *rainfo) __FUNCTION__); exit(1); } + if (rainfo->ra_data) { + /* free the previous packet */ + free(rainfo->ra_data); + rainfo->ra_data = NULL; + } rainfo->ra_data = buf; /* XXX: what if packlen > 576? */ rainfo->ra_datalen = packlen; @@ -665,7 +842,12 @@ make_packet(struct rainfo *rainfo) ra->nd_ra_code = 0; ra->nd_ra_cksum = 0; ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit); - ra->nd_ra_flags_reserved = 0; + ra->nd_ra_flags_reserved = 0; /* just in case */ + /* + * XXX: the router preference field, which is a 2-bit field, should be + * initialized before other fields. + */ + ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref; ra->nd_ra_flags_reserved |= rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0; ra->nd_ra_flags_reserved |= @@ -689,36 +871,39 @@ make_packet(struct rainfo *rainfo) ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU; ndopt_mtu->nd_opt_mtu_len = 1; ndopt_mtu->nd_opt_mtu_reserved = 0; - ndopt_mtu->nd_opt_mtu_mtu = ntohl(rainfo->linkmtu); + ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu); buf += sizeof(struct nd_opt_mtu); } #ifdef MIP6 if (mobileip6 && rainfo->maxinterval) { - ndopt_advint = (struct nd_opt_advint *)buf; - ndopt_advint->nd_opt_int_type = ND_OPT_ADV_INTERVAL; - ndopt_advint->nd_opt_int_len = 1; - ndopt_advint->nd_opt_int_reserved = 0; - ndopt_advint->nd_opt_int_interval = ntohl(rainfo->maxinterval * + ndopt_advint = (struct nd_opt_advinterval *)buf; + ndopt_advint->nd_opt_adv_type = ND_OPT_ADVINTERVAL; + ndopt_advint->nd_opt_adv_len = 1; + ndopt_advint->nd_opt_adv_reserved = 0; + ndopt_advint->nd_opt_adv_interval = htonl(rainfo->maxinterval * 1000); - buf += sizeof(struct nd_opt_advint); + buf += sizeof(struct nd_opt_advinterval); } #endif #ifdef MIP6 if (rainfo->hatime) { - ndopt_hai = (struct nd_opt_hai *)buf; - ndopt_hai->nd_opt_hai_type = ND_OPT_HA_INFORMATION; + ndopt_hai = (struct nd_opt_homeagent_info *)buf; + ndopt_hai->nd_opt_hai_type = ND_OPT_HOMEAGENT_INFO; ndopt_hai->nd_opt_hai_len = 1; ndopt_hai->nd_opt_hai_reserved = 0; - ndopt_hai->nd_opt_hai_pref = ntohs(rainfo->hapref); - ndopt_hai->nd_opt_hai_lifetime = ntohs(rainfo->hatime); - buf += sizeof(struct nd_opt_hai); + ndopt_hai->nd_opt_hai_preference = htons(rainfo->hapref); + ndopt_hai->nd_opt_hai_lifetime = htons(rainfo->hatime); + buf += sizeof(struct nd_opt_homeagent_info); } #endif for (pfx = rainfo->prefix.next; pfx != &rainfo->prefix; pfx = pfx->next) { + u_int32_t vltime, pltime; + struct timeval now; + ndopt_pi = (struct nd_opt_prefix_info *)buf; ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; ndopt_pi->nd_opt_pi_len = 4; @@ -733,16 +918,69 @@ make_packet(struct rainfo *rainfo) #ifdef MIP6 if (pfx->routeraddr) ndopt_pi->nd_opt_pi_flags_reserved |= - ND_OPT_PI_FLAG_RTADDR; + ND_OPT_PI_FLAG_ROUTER; #endif - ndopt_pi->nd_opt_pi_valid_time = ntohl(pfx->validlifetime); - ndopt_pi->nd_opt_pi_preferred_time = - ntohl(pfx->preflifetime); + if (pfx->vltimeexpire || pfx->pltimeexpire) + gettimeofday(&now, NULL); + if (pfx->vltimeexpire == 0) + vltime = pfx->validlifetime; + else + vltime = (pfx->vltimeexpire > now.tv_sec) ? + pfx->vltimeexpire - now.tv_sec : 0; + if (pfx->pltimeexpire == 0) + pltime = pfx->preflifetime; + else + pltime = (pfx->pltimeexpire > now.tv_sec) ? + pfx->pltimeexpire - now.tv_sec : 0; + if (vltime < pltime) { + /* + * this can happen if vltime is decrement but pltime + * is not. + */ + pltime = vltime; + } + ndopt_pi->nd_opt_pi_valid_time = htonl(vltime); + ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime); ndopt_pi->nd_opt_pi_reserved2 = 0; ndopt_pi->nd_opt_pi_prefix = pfx->prefix; buf += sizeof(struct nd_opt_prefix_info); } +#ifdef ND_OPT_ROUTE_INFO + for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) { + u_int8_t psize = (rti->prefixlen + 0x3f) >> 6; + + ndopt_rti = (struct nd_opt_route_info *)buf; + ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO; + ndopt_rti->nd_opt_rti_len = 1 + psize; + ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen; + ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref; + ndopt_rti->nd_opt_rti_lifetime = rti->ltime; + memcpy(ndopt_rti + 1, &rti->prefix, psize * 8); + buf += sizeof(struct nd_opt_route_info) + psize * 8; + } +#endif + return; } + +static int +getinet6sysctl(int code) +{ + int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 }; + int value; + size_t size; + + mib[3] = code; + size = sizeof(value); + if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) + < 0) { + syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s", + __FUNCTION__, code, + strerror(errno)); + return(-1); + } + else + return(value); +} diff --git a/usr.sbin/rtadvd/config.h b/usr.sbin/rtadvd/config.h index f08a702..9000461 100644 --- a/usr.sbin/rtadvd/config.h +++ b/usr.sbin/rtadvd/config.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME$ */ +/* $KAME: config.h,v 1.3 2000/05/16 13:34:13 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. diff --git a/usr.sbin/rtadvd/dump.c b/usr.sbin/rtadvd/dump.c index 4b2801d..4e4be1a 100644 --- a/usr.sbin/rtadvd/dump.c +++ b/usr.sbin/rtadvd/dump.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: dump.c,v 1.10 2000/05/23 11:31:25 itojun Exp $ */ +/* $KAME: dump.c,v 1.16 2001/03/21 17:41:13 jinmei Exp $ */ /* * Copyright (C) 2000 WIDE Project. @@ -31,6 +31,7 @@ */ #include #include +#include #include #if defined(__FreeBSD__) && __FreeBSD__ >= 3 @@ -71,6 +72,13 @@ static void if_dump __P((void)); #define LONGLONG "%llu" #endif +static char *rtpref_str[] = { + "medium", /* 00 */ + "high", /* 01 */ + "rsv", /* 10 */ + "low" /* 11 */ +}; + static char * ether_str(sdl) struct sockaddr_dl *sdl; @@ -97,7 +105,9 @@ if_dump() struct prefix *pfx; char prefixbuf[INET6_ADDRSTRLEN]; int first; + struct timeval now; + gettimeofday(&now, NULL); /* XXX: unused in most cases */ for (rai = ralist; rai; rai = rai->next) { fprintf(fp, "%s:\n", rai->ifname); @@ -141,12 +151,15 @@ if_dump() " DefaultLifetime: %d, MaxAdvInterval: %d, " "MinAdvInterval: %d\n", rai->lifetime, rai->maxinterval, rai->mininterval); - fprintf(fp, " Flags: %s%s%s MTU: %d\n", + fprintf(fp, " Flags: %s%s%s, ", rai->managedflg ? "M" : "", rai->otherflg ? "O" : "", #ifdef MIP6 rai->haflg ? "H" : #endif - "", rai->linkmtu); + ""); + fprintf(fp, "Preference: %s, ", + rtpref_str[(rai->rtpref >> 3) & 0xff]); + fprintf(fp, "MTU: %d\n", rai->linkmtu); fprintf(fp, " ReachableTime: %d, RetransTimer: %d, " "CurHopLimit: %d\n", rai->reachabletime, rai->retranstimer, rai->hoplimit); @@ -155,6 +168,9 @@ if_dump() rai->hapref, rai->hatime); #endif + if (rai->clockskew) + fprintf(fp, " Clock skew: %ldsec\n", + rai->clockskew); for (first = 1, pfx = rai->prefix.next; pfx != &rai->prefix; pfx = pfx->next) { if (first) { @@ -177,15 +193,27 @@ if_dump() break; } if (pfx->validlifetime == ND6_INFINITE_LIFETIME) - fprintf(fp, "vltime: infinity, "); + fprintf(fp, "vltime: infinity"); else - fprintf(fp, "vltime: %ld, ", + fprintf(fp, "vltime: %ld", (long)pfx->validlifetime); + if (pfx->vltimeexpire != 0) + fprintf(fp, "(decr,expire %ld), ", (long) + pfx->vltimeexpire > now.tv_sec ? + pfx->vltimeexpire - now.tv_sec : 0); + else + fprintf(fp, ", "); if (pfx->preflifetime == ND6_INFINITE_LIFETIME) - fprintf(fp, "pltime: infinity, "); + fprintf(fp, "pltime: infinity"); else - fprintf(fp, "pltime: %ld, ", + fprintf(fp, "pltime: %ld", (long)pfx->preflifetime); + if (pfx->pltimeexpire != 0) + fprintf(fp, "(decr,expire %ld), ", (long) + pfx->pltimeexpire > now.tv_sec ? + pfx->pltimeexpire - now.tv_sec : 0); + else + fprintf(fp, ", "); fprintf(fp, "flags: %s%s%s", pfx->onlinkflg ? "L" : "", pfx->autoconfflg ? "A" : "", diff --git a/usr.sbin/rtadvd/dump.h b/usr.sbin/rtadvd/dump.h index 03dff70..cc3b472 100644 --- a/usr.sbin/rtadvd/dump.h +++ b/usr.sbin/rtadvd/dump.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME$ */ +/* $KAME: dump.h,v 1.1 2000/05/23 11:31:26 itojun Exp $ */ /* * Copyright (C) 1998 WIDE Project. diff --git a/usr.sbin/rtadvd/if.c b/usr.sbin/rtadvd/if.c index b77f472..c6aa2e6 100644 --- a/usr.sbin/rtadvd/if.c +++ b/usr.sbin/rtadvd/if.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: if.c,v 1.14 2000/10/25 04:28:34 jinmei Exp $ */ +/* $KAME: if.c,v 1.17 2001/01/21 15:27:30 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -259,17 +259,6 @@ rtbuf_len() return(len); } -int -get_rtinfo(char *buf, size_t *len) -{ - int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0}; - - if (sysctl(mib, 6, buf, len, NULL, 0) < 0) - return(-1); - - return(0); -} - #define FILTER_MATCH(type, filter) ((0x1 << type) & filter) #define SIN6(s) ((struct sockaddr_in6 *)(s)) #define SDL(s) ((struct sockaddr_dl *)(s)) @@ -357,7 +346,7 @@ get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter) return (char *)rtm; } -#undef FILTER_MATCH(type, filter) +#undef FILTER_MATCH struct in6_addr * get_addr(char *buf) diff --git a/usr.sbin/rtadvd/if.h b/usr.sbin/rtadvd/if.h index f91e8d4..23f9a98 100644 --- a/usr.sbin/rtadvd/if.h +++ b/usr.sbin/rtadvd/if.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: if.h,v 1.2 2000/05/16 13:34:13 itojun Exp $ */ +/* $KAME: if.h,v 1.6 2001/01/21 15:37:14 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. @@ -37,23 +37,22 @@ extern size_t ifblock_size; extern char *ifblock; struct nd_opt_hdr; -struct sockaddr_dl *if_nametosdl __P((char *name)); -int if_getmtu __P((char *name)); -int if_getflags __P((int ifindex, int oifflags)); -int lladdropt_length __P((struct sockaddr_dl *sdl)); -void lladdropt_fill __P((struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)); +struct sockaddr_dl *if_nametosdl __P((char *)); +int if_getmtu __P((char *)); +int if_getflags __P((int, int)); +int lladdropt_length __P((struct sockaddr_dl *)); +void lladdropt_fill __P((struct sockaddr_dl *, struct nd_opt_hdr *)); int rtbuf_len __P((void)); -int get_rtinfo __P((char *buf, size_t *len)); -char *get_next_msg __P((char *buf, char *lim, int ifindex, size_t *lenp, - int filter)); -struct in6_addr *get_addr __P((char *buf)); -int get_rtm_ifindex __P((char *buf)); -int get_ifm_ifindex __P((char *buf)); -int get_ifam_ifindex __P((char *buf)); -int get_ifm_flags __P((char *buf)); -int get_prefixlen __P((char *buf)); -int rtmsg_type __P((char *buf)); -int ifmsg_type __P((char *buf)); -int rtmsg_len __P((char *buf)); -int ifmsg_len __P((char *buf)); +char *get_next_msg __P((char *, char *, int, size_t *, int)); +struct in6_addr *get_addr __P((char *)); +int get_rtm_ifindex __P((char *)); +int get_ifm_ifindex __P((char *)); +int get_ifam_ifindex __P((char *)); +int get_ifm_flags __P((char *)); +int get_prefixlen __P((char *)); +int prefixlen __P((u_char *, u_char *)); +int rtmsg_type __P((char *)); +int ifmsg_type __P((char *)); +int rtmsg_len __P((char *)); +int ifmsg_len __P((char *)); void init_iflist __P((void)); diff --git a/usr.sbin/rtadvd/pathnames.h b/usr.sbin/rtadvd/pathnames.h index 9c68cc4..3afee55 100644 --- a/usr.sbin/rtadvd/pathnames.h +++ b/usr.sbin/rtadvd/pathnames.h @@ -1,4 +1,4 @@ +/* $KAME: pathnames.h,v 1.2 2000/05/16 13:34:13 itojun Exp $ */ /* $FreeBSD$ */ -/* $KAME$ */ #define _PATH_RTADVDCONF "/etc/rtadvd.conf" diff --git a/usr.sbin/rtadvd/rrenum.c b/usr.sbin/rtadvd/rrenum.c index a0edf9f..a5fbe20 100644 --- a/usr.sbin/rtadvd/rrenum.c +++ b/usr.sbin/rtadvd/rrenum.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: rrenum.c,v 1.3 2000/05/16 13:34:14 itojun Exp $ */ +/* $KAME: rrenum.c,v 1.10 2001/01/21 15:32:16 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - +#include #include #include #include @@ -50,6 +50,7 @@ #include #include #include +#include "rtadvd.h" #include "rrenum.h" #include "if.h" @@ -138,13 +139,17 @@ rr_pco_check(int len, struct rr_pco_match *rpm) } static void -do_use_prefix(int len, struct rr_pco_match *rpm, struct in6_rrenumreq *irr) { +do_use_prefix(int len, struct rr_pco_match *rpm, + struct in6_rrenumreq *irr, int ifindex) +{ struct rr_pco_use *rpu, *rpulim; + struct rainfo *rai; + struct prefix *pp; rpu = (struct rr_pco_use *)(rpm + 1); rpulim = (struct rr_pco_use *)((char *)rpm + len); - if (rpu == rpulim) { + if (rpu == rpulim) { /* no use prefix */ if (rpm->rpm_code == RPM_PCO_ADD) return; @@ -176,16 +181,16 @@ do_use_prefix(int len, struct rr_pco_match *rpm, struct in6_rrenumreq *irr) { (rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK); irr->irr_raf_mask_auto = (rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_AUTO); - irr->irr_vltime = rpu->rpu_vltime; - irr->irr_pltime = rpu->rpu_pltime; + irr->irr_vltime = ntohl(rpu->rpu_vltime); + irr->irr_pltime = ntohl(rpu->rpu_pltime); irr->irr_raf_onlink = - (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK); + (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK) == 0 ? 0 : 1; irr->irr_raf_auto = - (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_AUTO); + (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_AUTO) == 0 ? 0 : 1; irr->irr_rrf_decrvalid = - (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME); + (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME) == 0 ? 0 : 1; irr->irr_rrf_decrprefd = - (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME); + (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME) == 0 ? 0 : 1; irr->irr_useprefix.sin6_len = sizeof(irr->irr_useprefix); irr->irr_useprefix.sin6_family = AF_INET6; irr->irr_useprefix.sin6_addr = rpu->rpu_prefix; @@ -194,6 +199,40 @@ do_use_prefix(int len, struct rr_pco_match *rpm, struct in6_rrenumreq *irr) { errno != EADDRNOTAVAIL) syslog(LOG_ERR, "<%s> ioctl: %s", __FUNCTION__, strerror(errno)); + + /* very adhoc: should be rewritten */ + if (rpm->rpm_code == RPM_PCO_CHANGE && + IN6_ARE_ADDR_EQUAL(&rpm->rpm_prefix, &rpu->rpu_prefix) && + rpm->rpm_matchlen == rpu->rpu_uselen && + rpu->rpu_uselen == rpu->rpu_keeplen) { + if ((rai = if_indextorainfo(ifindex)) == NULL) + continue; /* non-advertising IF */ + + for (pp = rai->prefix.next; pp != &rai->prefix; + pp = pp->next) { + struct timeval now; + + if (prefix_match(&pp->prefix, pp->prefixlen, + &rpm->rpm_prefix, + rpm->rpm_matchlen)) { + /* change parameters */ + pp->validlifetime = ntohl(rpu->rpu_vltime); + pp->preflifetime = ntohl(rpu->rpu_pltime); + if (irr->irr_rrf_decrvalid) { + gettimeofday(&now, 0); + pp->vltimeexpire = + now.tv_sec + pp->validlifetime; + } else + pp->vltimeexpire = 0; + if (irr->irr_rrf_decrprefd) { + gettimeofday(&now, 0); + pp->pltimeexpire = + now.tv_sec + pp->preflifetime; + } else + pp->pltimeexpire = 0; + } + } + } } } @@ -234,7 +273,7 @@ do_pco(struct icmp6_router_renum *rr, int len, struct rr_pco_match *rpm) (iflist[ifindex]->ifm_flags & IFF_UP) == 0) continue; /* TODO: interface scope check */ - do_use_prefix(len, rpm, &irr); + do_use_prefix(len, rpm, &irr, ifindex); } if (errno == ENXIO) return 0; @@ -392,9 +431,40 @@ rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi, inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); - rr_rcvifindex = pi->ipi6_ifindex; + /* packet validation based on Section 4.1 of RFC2894 */ + if (len < sizeof(struct icmp6_router_renum)) { + syslog(LOG_NOTICE, + "<%s>: RR short message (size %d) from %s to %s on %s", + __FUNCTION__, len, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf[0], INET6_ADDRSTRLEN), + inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } - /* TODO: some consistency check. */ + /* + * If the IPv6 destination address is neither an All Routers multicast + * address [AARCH] nor one of the receiving router's unicast addresses, + * the message MUST be discarded and SHOULD be logged to network + * management. + * We rely on the kernel input routine for unicast addresses, and thus + * check multicast destinations only. + */ + if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && + !IN6_ARE_ADDR_EQUAL(&in6a_site_allrouters, &pi->ipi6_addr)) { + syslog(LOG_NOTICE, + "<%s>: RR message with invalid destination (%s) " + "from %s on %s", + __FUNCTION__, + inet_ntop(AF_INET6, &dst, ntopbuf[0], INET6_ADDRSTRLEN), + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf[1], INET6_ADDRSTRLEN), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } + + rr_rcvifindex = pi->ipi6_ifindex; switch (rr->rr_code) { case ICMP6_ROUTER_RENUMBERING_COMMAND: diff --git a/usr.sbin/rtadvd/rrenum.h b/usr.sbin/rtadvd/rrenum.h index 3ab1e7d..ce94015 100644 --- a/usr.sbin/rtadvd/rrenum.h +++ b/usr.sbin/rtadvd/rrenum.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME$ */ +/* $KAME: rrenum.h,v 1.3 2001/01/21 15:37:14 itojun Exp $ */ /* * Copyright (C) 1998 WIDE Project. @@ -30,6 +30,5 @@ * SUCH DAMAGE. */ -void rr_input __P((int len, struct icmp6_router_renum *rr, - struct in6_pktinfo *pi, struct sockaddr_in6 *from, - struct in6_addr *dst)); +void rr_input __P((int, struct icmp6_router_renum *, struct in6_pktinfo *, + struct sockaddr_in6 *, struct in6_addr *)); diff --git a/usr.sbin/rtadvd/rtadvd.8 b/usr.sbin/rtadvd/rtadvd.8 index 3ddc77d..dcdcdab 100644 --- a/usr.sbin/rtadvd/rtadvd.8 +++ b/usr.sbin/rtadvd/rtadvd.8 @@ -1,5 +1,5 @@ .\" $FreeBSD$ -.\" $KAME: rtadvd.8,v 1.9 2000/05/27 13:37:01 jinmei Exp $ +.\" $KAME: rtadvd.8,v 1.17 2001/02/04 05:34:38 jinmei Exp $ .\" .\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. .\" All rights reserved. @@ -36,8 +36,8 @@ .Nd router advertisement daemon .Sh SYNOPSIS .Nm +.Op Fl dDfMRs .Op Fl c Ar configfile -.Op Fl dDfRs .Ar interface ... .Sh DESCRIPTION .Nm @@ -77,6 +77,15 @@ Moreover, if the status of an advertising interface changes, will start or stop sending router advertisements according to the latest status. .Pp +Basically, hosts MUST NOT send Router Advertisement messages at any +time (RFC 2461, Section 6.2.3). +However, it would sometimes be useful to allow hosts to advertise some +parameters such as prefix information and link MTU. +Thus, +.Nm +can be invoked if router lifetime is explicitly set zero on every +advertising interface. +.Pp The command line options are: .Bl -tag -width indent .\" @@ -93,6 +102,15 @@ Print debugging information. Even more debugging information is printed. .It Fl f Foreground mode (useful when debugging). +.It Fl M +Specify an interface to join the all-routers site-local multicast group. +By default, +.Nm +tries to join the first advertising interface appeared in the command +line. +This option has meaning only with the +.Fl R +option, which enables routing renumbering protocol support. .\".It Fl m .\"Enables mobile IPv6 support. .\"This changes the content of router advertisement option, as well as @@ -100,6 +118,12 @@ Foreground mode (useful when debugging). .It Fl R Accept router renumbering requests. If you enable it, certain IPsec setup is suggested for security reasons. +On KAME-based systems, +.Xr rrenumd 8 +generates router renumbering request packets. +This option is currently disabled, and is ignored by +.Nm +with a warning message. .It Fl s Do not add or delete prefixes dynamically. Only statically configured prefixes, if any, will be advertised. @@ -138,17 +162,20 @@ in which dumps its internal state. .El .Sh SEE ALSO -.Xr daemon 3 , .Xr rtadvd.conf 5 , -.Xr rtsol 8 +.Xr rtsol 8 , +.Xr rrenumd 8 .Sh HISTORY The .Nm command first appeared in WIDE Hydrangea IPv6 protocol stack kit. .Sh CAVEAT -Router advertisements should only be performed downstream. -Erroneous upstream advertisements will cause +There used to be some text that recommended users not to let +.Nm +advertise Router Advertisement messages on an upstream link to avoid +undesirable .Xr icmp6 4 -redirect packet storms in the subnet, as (per the specification) the -advertising router is assumed to become the default router for -end hosts in the subnet. +redirect messages. +However, based on the later discussion in the IETF ipng working group, +all routers should rather advertise the messages regardless of +the network topology, in order to ensure reachability. diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c index 000e612..e9931df 100644 --- a/usr.sbin/rtadvd/rtadvd.c +++ b/usr.sbin/rtadvd/rtadvd.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: rtadvd.c,v 1.30 2000/06/22 20:16:12 itojun Exp $ */ +/* $KAME: rtadvd.c,v 1.50 2001/02/04 06:15:15 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -68,14 +69,18 @@ static size_t rcvcmsgbuflen; static u_char *sndcmsgbuf = NULL; static size_t sndcmsgbuflen; static int do_dump; +static int do_die; struct msghdr sndmhdr; struct iovec rcviov[2]; struct iovec sndiov[2]; struct sockaddr_in6 from; struct sockaddr_in6 sin6_allnodes = {sizeof(sin6_allnodes), AF_INET6}; +struct in6_addr in6a_site_allrouters; static char *dumpfilename = "/var/run/rtadvd.dump"; /* XXX: should be configurable */ static char *pidfilename = "/var/run/rtadvd.pid"; /* should be configurable */ -int sock, rtsock; +static char *mcastif; +int sock; +int rtsock = -1; #ifdef MIP6 int mobileip6 = 0; #endif @@ -120,7 +125,8 @@ u_int32_t ndopt_flags[] = { }; int main __P((int, char *[])); -static void die __P((int)); +static void set_die __P((int)); +static void die __P((void)); static void sock_open __P((void)); static void rtsock_open __P((void)); static void rtadvd_input __P((void)); @@ -133,7 +139,6 @@ static int prefix_check __P((struct nd_opt_prefix_info *, struct rainfo *, static int nd6_options __P((struct nd_opt_hdr *, int, union nd_opts *, u_int32_t)); static void free_ndopts __P((union nd_opts *)); -static struct rainfo *if_indextorainfo __P((int)); static void ra_output __P((struct rainfo *)); static void rtmsg_input __P((void)); static void rtadvd_set_dump_file __P((void)); @@ -157,9 +162,9 @@ main(argc, argv) /* get command line options and arguments */ #ifdef MIP6 -#define OPTIONS "c:dDfmRs" +#define OPTIONS "c:dDfM:mRs" #else -#define OPTIONS "c:dDfRs" +#define OPTIONS "c:dDfM:Rs" #endif while ((ch = getopt(argc, argv, OPTIONS)) != -1) { #undef OPTIONS @@ -176,13 +181,19 @@ main(argc, argv) case 'f': fflag = 1; break; + case 'M': + mcastif = optarg; + break; #ifdef MIP6 case 'm': mobileip6 = 1; break; #endif case 'R': - accept_rr = 1; + fprintf(stderr, "rtadvd: " + "the -R option is currently ignored.\n"); + /* accept_rr = 1; */ + /* run anyway... */ break; case 's': sflag = 1; @@ -194,9 +205,9 @@ main(argc, argv) if (argc == 0) { fprintf(stderr, #ifdef MIP6 - "usage: rtadvd [-dDfmRs] [-c conffile] " + "usage: rtadvd [-dDfMmRs] [-c conffile] " #else - "usage: rtadvd [-dDfRs] [-c conffile] " + "usage: rtadvd [-dDfMRs] [-c conffile] " #endif "interfaces...\n"); exit(1); @@ -243,12 +254,15 @@ main(argc, argv) FD_ZERO(&fdset); FD_SET(sock, &fdset); maxfd = sock; - rtsock_open(); - FD_SET(rtsock, &fdset); - if (rtsock > sock) + if (sflag == 0) { + rtsock_open(); + FD_SET(rtsock, &fdset); + if (rtsock > sock) maxfd = rtsock; + } else + rtsock = -1; - signal(SIGTERM, (void *)die); + signal(SIGTERM, (void *)set_die); signal(SIGUSR1, (void *)rtadvd_set_dump_file); while (1) { @@ -259,6 +273,11 @@ main(argc, argv) rtadvd_dump_file(dumpfilename); } + if (do_die) { + die(); + /*NOTREACHED*/ + } + /* timer expiration check and reset the timer */ timeout = rtadvd_check_timer(); @@ -285,7 +304,7 @@ main(argc, argv) } if (i == 0) /* timeout */ continue; - if (sflag == 0 && FD_ISSET(rtsock, &select_fd)) + if (rtsock != -1 && FD_ISSET(rtsock, &select_fd)) rtmsg_input(); if (FD_ISSET(sock, &select_fd)) rtadvd_input(); @@ -300,9 +319,15 @@ rtadvd_set_dump_file() } static void -die(sig) +set_die(sig) int sig; { + do_die = 1; +} + +static void +die() +{ struct rainfo *ra; int i; const int retrans = MAX_FINAL_RTR_ADVERTISEMENTS; @@ -331,13 +356,13 @@ rtmsg_input() int n, type, ifindex = 0, plen; size_t len; char msg[2048], *next, *lim; - u_char ifname[16]; + u_char ifname[IF_NAMESIZE]; struct prefix *prefix; struct rainfo *rai; struct in6_addr *addr; char addrbuf[INET6_ADDRSTRLEN]; - n = read(rtsock, msg, 2048); + n = read(rtsock, msg, sizeof(msg)); if (dflag > 1) { syslog(LOG_DEBUG, "<%s> received a routing message " @@ -876,8 +901,8 @@ ra_input(int len, struct nd_router_advert *ra, memset(&ndopts, 0, sizeof(ndopts)); if (nd6_options((struct nd_opt_hdr *)(ra + 1), len - sizeof(struct nd_router_advert), - &ndopts, - NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU)) { + &ndopts, NDOPT_FLAG_SRCLINKADDR | + NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU)) { syslog(LOG_ERR, "<%s> ND option check failed for an RA from %s on %s", __FUNCTION__, @@ -905,7 +930,7 @@ ra_input(int len, struct nd_router_advert *ra, /* Cur Hop Limit value */ if (ra->nd_ra_curhoplimit && rai->hoplimit && ra->nd_ra_curhoplimit != rai->hoplimit) { - syslog(LOG_WARNING, + syslog(LOG_INFO, "<%s> CurHopLimit inconsistent on %s:" " %d from %s, %d from us", __FUNCTION__, @@ -919,7 +944,7 @@ ra_input(int len, struct nd_router_advert *ra, /* M flag */ if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != rai->managedflg) { - syslog(LOG_WARNING, + syslog(LOG_INFO, "<%s> M flag inconsistent on %s:" " %s from %s, %s from us", __FUNCTION__, @@ -933,7 +958,7 @@ ra_input(int len, struct nd_router_advert *ra, /* O flag */ if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != rai->otherflg) { - syslog(LOG_WARNING, + syslog(LOG_INFO, "<%s> O flag inconsistent on %s:" " %s from %s, %s from us", __FUNCTION__, @@ -948,7 +973,7 @@ ra_input(int len, struct nd_router_advert *ra, reachabletime = ntohl(ra->nd_ra_reachable); if (reachabletime && rai->reachabletime && reachabletime != rai->reachabletime) { - syslog(LOG_WARNING, + syslog(LOG_INFO, "<%s> ReachableTime inconsistent on %s:" " %d from %s, %d from us", __FUNCTION__, @@ -963,7 +988,7 @@ ra_input(int len, struct nd_router_advert *ra, retranstimer = ntohl(ra->nd_ra_retransmit); if (retranstimer && rai->retranstimer && retranstimer != rai->retranstimer) { - syslog(LOG_WARNING, + syslog(LOG_INFO, "<%s> RetranceTimer inconsistent on %s:" " %d from %s, %d from us", __FUNCTION__, @@ -978,7 +1003,7 @@ ra_input(int len, struct nd_router_advert *ra, if (ndopts.nd_opts_mtu) { mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); if (mtu && rai->linkmtu && mtu != rai->linkmtu) { - syslog(LOG_WARNING, + syslog(LOG_INFO, "<%s> MTU option value inconsistent on %s:" " %d from %s, %d from us", __FUNCTION__, @@ -992,7 +1017,7 @@ ra_input(int len, struct nd_router_advert *ra, /* Preferred and Valid Lifetimes for prefixes */ { struct nd_optlist *optp = ndopts.nd_opts_list; - + if (ndopts.nd_opts_pi) { if (prefix_check(ndopts.nd_opts_pi, rai, from)) inconsistent++; @@ -1005,10 +1030,8 @@ ra_input(int len, struct nd_router_advert *ra, } } - if (inconsistent) { - printf("RA input %d inconsistents\n", inconsistent); + if (inconsistent) rai->rainconsistent++; - } done: free_ndopts(&ndopts); @@ -1024,6 +1047,7 @@ prefix_check(struct nd_opt_prefix_info *pinfo, struct prefix *pp; int inconsistent = 0; u_char ntopbuf[INET6_ADDRSTRLEN], prefixbuf[INET6_ADDRSTRLEN]; + struct timeval now; #if 0 /* impossible */ if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION) @@ -1061,8 +1085,36 @@ prefix_check(struct nd_opt_prefix_info *pinfo, } preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time); - if (preferred_time != pp->preflifetime) { - syslog(LOG_WARNING, + if (pp->pltimeexpire) { + /* + * The lifetime is decremented in real time, so we should + * compare the expiration time. + * (RFC 2461 Section 6.2.7.) + * XXX: can we really expect that all routers on the link + * have synchronized clocks? + */ + gettimeofday(&now, NULL); + preferred_time += now.tv_sec; + + if (rai->clockskew && + abs(preferred_time - pp->pltimeexpire) > rai->clockskew) { + syslog(LOG_INFO, + "<%s> prefeerred lifetime for %s/%d" + " (decr. in real time) inconsistent on %s:" + " %d from %s, %ld from us", + __FUNCTION__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + rai->ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + pp->pltimeexpire); + inconsistent++; + } + } + else if (preferred_time != pp->preflifetime) { + syslog(LOG_INFO, "<%s> prefeerred lifetime for %s/%d" " inconsistent on %s:" " %d from %s, %d from us", @@ -1074,12 +1126,32 @@ prefix_check(struct nd_opt_prefix_info *pinfo, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, INET6_ADDRSTRLEN), pp->preflifetime); - inconsistent++; } valid_time = ntohl(pinfo->nd_opt_pi_valid_time); - if (valid_time != pp->validlifetime) { - syslog(LOG_WARNING, + if (pp->vltimeexpire) { + gettimeofday(&now, NULL); + valid_time += now.tv_sec; + + if (rai->clockskew && + abs(valid_time - pp->vltimeexpire) > rai->clockskew) { + syslog(LOG_INFO, + "<%s> valid lifetime for %s/%d" + " (decr. in real time) inconsistent on %s:" + " %d from %s, %ld from us", + __FUNCTION__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefixbuf, INET6_ADDRSTRLEN), + pinfo->nd_opt_pi_prefix_len, + rai->ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, + ntopbuf, INET6_ADDRSTRLEN), + pp->vltimeexpire); + inconsistent++; + } + } + else if (valid_time != pp->validlifetime) { + syslog(LOG_INFO, "<%s> valid lifetime for %s/%d" " inconsistent on %s:" " %d from %s, %d from us", @@ -1118,6 +1190,26 @@ find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen) return(NULL); } +/* check if p0/plen0 matches p1/plen1; return 1 if matches, otherwise 0. */ +int +prefix_match(struct in6_addr *p0, int plen0, + struct in6_addr *p1, int plen1) +{ + int bytelen, bitlen; + + if (plen0 < plen1) + return(0); + bytelen = plen1 / 8; + bitlen = plen1 % 8; + if (memcmp((void *)p0, (void *)p1, bytelen)) + return(0); + if (p0->s6_addr[bytelen] >> (8 - bitlen) == + p1->s6_addr[bytelen] >> (8 - bitlen)) + return(1); + + return(0); +} + static int nd6_options(struct nd_opt_hdr *hdr, int limit, union nd_opts *ndopts, u_int32_t optflags) @@ -1276,7 +1368,7 @@ sock_open() __FUNCTION__, strerror(errno)); exit(1); } -#endif +#endif ICMP6_FILTER_SETBLOCKALL(&filt); ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt); @@ -1293,7 +1385,8 @@ sock_open() /* * join all routers multicast address on each advertising interface. */ - if (inet_pton(AF_INET6, ALLROUTERS, &mreq.ipv6mr_multiaddr.s6_addr) + if (inet_pton(AF_INET6, ALLROUTERS_LINK, + &mreq.ipv6mr_multiaddr.s6_addr) != 1) { syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)", __FUNCTION__); @@ -1301,15 +1394,47 @@ sock_open() } while(ra) { mreq.ipv6mr_interface = ra->ifindex; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, - &mreq, + if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) { - syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP on %s: %s", + syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP(link) on %s: %s", __FUNCTION__, ra->ifname, strerror(errno)); exit(1); } ra = ra->next; } + + /* + * When attending router renumbering, join all-routers site-local + * multicast group. + */ + if (accept_rr) { + if (inet_pton(AF_INET6, ALLROUTERS_SITE, + &in6a_site_allrouters) != 1) { + syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)", + __FUNCTION__); + exit(1); + } + mreq.ipv6mr_multiaddr = in6a_site_allrouters; + if (mcastif) { + if ((mreq.ipv6mr_interface = if_nametoindex(mcastif)) + == 0) { + syslog(LOG_ERR, + "<%s> invalid interface: %s", + __FUNCTION__, mcastif); + exit(1); + } + } else + mreq.ipv6mr_interface = ralist->ifindex; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq, sizeof(mreq)) < 0) { + syslog(LOG_ERR, + "<%s> IPV6_JOIN_GROUP(site) on %s: %s", + __FUNCTION__, + mcastif ? mcastif : ralist->ifname, + strerror(errno)); + exit(1); + } + } /* initialize msghdr for receiving packets */ rcviov[0].iov_base = (caddr_t)answer; @@ -1342,7 +1467,7 @@ rtsock_open() } } -static struct rainfo * +struct rainfo * if_indextorainfo(int index) { struct rainfo *rai = ralist; @@ -1370,6 +1495,8 @@ struct rainfo *rainfo; return; } + make_packet(rainfo); /* XXX: inefficient */ + sndmhdr.msg_name = (caddr_t)&sin6_allnodes; sndmhdr.msg_iov[0].iov_base = (caddr_t)rainfo->ra_data; sndmhdr.msg_iov[0].iov_len = rainfo->ra_datalen; @@ -1474,7 +1601,7 @@ ra_timer_update(void *data, struct timeval *tm) * Whenever a multicast advertisement is sent from an interface, * the timer is reset to a uniformly-distributed random value * between the interface's configured MinRtrAdvInterval and - * MaxRtrAdvInterval(discovery-v2-02 6.2.4). + * MaxRtrAdvInterval (RFC2461 6.2.4). */ interval = rai->mininterval; interval += random() % (rai->maxinterval - rai->mininterval); diff --git a/usr.sbin/rtadvd/rtadvd.conf.5 b/usr.sbin/rtadvd/rtadvd.conf.5 index b6d4f5c..9b9faaa 100644 --- a/usr.sbin/rtadvd/rtadvd.conf.5 +++ b/usr.sbin/rtadvd/rtadvd.conf.5 @@ -1,5 +1,5 @@ .\" $FreeBSD$ -.\" $KAME: rtadvd.conf.5,v 1.32 2001/01/19 05:32:05 jinmei Exp $ +.\" $KAME: rtadvd.conf.5,v 1.35 2001/05/25 07:40:22 jinmei Exp $ .\" .\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. .\" All rights reserved. @@ -117,6 +117,16 @@ and Bit 6 .Li 0x40 .Pc means Other stateful configuration flag bit. +Bit 4 +.Po +.Li 0x10 +.Pc +and Bit 3 +.Po +.Li 0x08 +.Pc +are used to encode router preference. +0x01 means high, 0x00 means medium, and 0x11 means low. The default value is 0. .It Cm \&rltime (num) Router lifetime field @@ -145,6 +155,17 @@ These items can be omitted, then will automatically get appropriate prefixes from the kernel's routing table, and advertise the prefixes with the default parameters. .Bl -tag -width indent +.It Cm \&clockskew +(num) Time skew to adjust link propagation delays and clock skews +betwen routers on the link +.Pq unit: seconds . +This value is used in consistency check for locally-configured and +advertised prefix lifetimes, and has its meaning when the local router +configures a prefix on the link with a lifetime that decrements in +real time. +If the value is 0, it means the consistency check will be skipped +for such prefixes. +The default value is 0. .It Cm \&addrs (num) Number of prefixes. Its default is 0, so it must explicitly be set to positve values @@ -193,10 +214,16 @@ is more than 0. (num) Valid lifetime field .Pq unit: seconds . The default value is 2592000 (30 days). +.It Cm \&vltimedecr +(bool) This item means the advertised valid lifetime will decrements +in real time, which is disabled by default. .It Cm \&pltime (num) Preferred lifetime field .Pq unit: seconds . The default value is 604800 (7 days). +.It Cm \&pltimedecr +(bool) This item means the advertised preferred lifetime will decrements +in real time, which is disabled by default. .El .Pp The following item is for ICMPv6 MTU option, @@ -237,6 +264,75 @@ will not attach source link-layer address option to router advertisement packets. .El .Pp +The following item controls ICMPV6 home agent information option, +which was defined with mobile IPv6 support. +It will be attached to router advertisement header just like other options do. +.Bl -tag -width indent +.It Cm \&hapref +(num) Specifies home agent preference. +If set to non-zero, +.Cm \&hatime +must be present as well. +.It Cm \&hatime +(num) Specifies home agent lifetime. +.El +.Pp +When mobile IPv6 support is turned on for +.Xr rtadvd 8 , +advertisement interval option will be attached to router advertisement +packet, by configuring +.Cm \&maxinterval +explicitly. +.Pp +The following items are for ICMPv6 route information option, +which will be attached to router advertisement header. +These items are optional. +.Bl -tag -width indent +.It Cm \&routes +(num) Number of routes. +Its default is 0, so it must explicitly be set to positve values +if you want to specify any route information option. +If its value is 0, no route information is sent. +If its value is more than 1, you must specify the index of the routes +for each item below. +Indices vary from 0 to N-1, where N is the +value of +.Cm routes. +Each index shall follow the name of each item, e.g., +.Dq rtrplen2 . +.It Cm \&rtrplen +(num) Prefix length field in route information option. +The default value is 64. +.It Cm \&rtrflags +(num) Flags field in route information option. +Bit 4 +.Po +.Li 0x10 +.Pc +and +and Bit 3 +.Po +.Li 0x08 +.Pc +are used to encode router preference for the route. +The default value is 0x00, i.e. medium router preference. +.It Cm \&rtrprefix +(str) The prefix filled into the Prefix field of route information option. +Since +.Dq \&: +is used for +.Xr termcap 5 +file format as well as IPv6 numeric address, the field MUST be quoted by +doublequote character. +This field cannot be +omitted if the value of +.Cm addrs +is more than 0. +.It Cm \&rtrltime +(num) route lifetime field in route information option. +.Pq unit: seconds . +The default value is 2592000 (30 days). (not specified in draft-draves-router-selection-01.txt now) +.El You can also refer one line from another by using .Cm tc capability. @@ -293,6 +389,12 @@ Thomas Narten, Erik Nordmark and W. A. Simpson, Neighbor Discovery for IP version 6 (IPv6) .Dc , RFC 2461 +.Pp +Richard Draves, +.Do +Default Router Preferences and More-Specific Routes +.Dc , +draft-ietf-ipngwg-router-selection-01.txt .Sh HISTORY The .Xr rtadvd 8 diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h index 3ffc7e6..c05dcf4 100644 --- a/usr.sbin/rtadvd/rtadvd.h +++ b/usr.sbin/rtadvd/rtadvd.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: rtadvd.h,v 1.8 2000/05/16 13:34:14 itojun Exp $ */ +/* $KAME: rtadvd.h,v 1.16 2001/04/10 15:08:31 suz Exp $ */ /* * Copyright (C) 1998 WIDE Project. @@ -31,7 +31,8 @@ */ #define ALLNODES "ff02::1" -#define ALLROUTERS "ff02::2" +#define ALLROUTERS_LINK "ff02::2" +#define ALLROUTERS_SITE "ff05::2" #define ANY "::" #define RTSOLLEN 8 @@ -74,7 +75,9 @@ struct prefix { struct prefix *prev; /* previous link */ u_int32_t validlifetime; /* AdvValidLifetime */ + long vltimeexpire; /* expiration of vltime; decrement case only */ u_int32_t preflifetime; /* AdvPreferredLifetime */ + long pltimeexpire; /* expiration of pltime; decrement case only */ u_int onlinkflg; /* bool: AdvOnLinkFlag */ u_int autoconfflg; /* bool: AdvAutonomousFlag */ #ifdef MIP6 @@ -85,6 +88,16 @@ struct prefix { struct in6_addr prefix; }; +struct rtinfo { + struct rtinfo *prev; /* previous link */ + struct rtinfo *next; /* forward link */ + + u_int32_t ltime; /* route lifetime */ + u_int rtpref; /* router preference */ + int prefixlen; + struct in6_addr prefix; +}; + struct soliciter { struct soliciter *next; struct sockaddr_in6 addr; @@ -116,17 +129,21 @@ struct rainfo { #ifdef MIP6 int haflg; /* HAFlag */ #endif + int rtpref; /* router preference */ u_int32_t linkmtu; /* AdvLinkMTU */ u_int32_t reachabletime; /* AdvReachableTime */ u_int32_t retranstimer; /* AdvRetransTimer */ u_int hoplimit; /* AdvCurHopLimit */ struct prefix prefix; /* AdvPrefixList(link head) */ int pfxs; /* number of prefixes */ + long clockskew; /* used for consisitency check of lifetimes */ #ifdef MIP6 u_short hapref; /* Home Agent Preference */ u_short hatime; /* Home Agent Lifetime */ #endif + struct rtinfo route; /* route information option (link head) */ + int routes; /* number of route information options */ /* actual RA packet data and its length */ size_t ra_datalen; @@ -145,6 +162,10 @@ struct rainfo { void ra_timeout __P((void *)); void ra_timer_update __P((void *, struct timeval *)); +int prefix_match __P((struct in6_addr *, int, struct in6_addr *, int)); +struct rainfo *if_indextorainfo __P((int)); + +extern struct in6_addr in6a_site_allrouters; #ifdef MIP6 extern int mobileip6; #endif diff --git a/usr.sbin/rtadvd/timer.c b/usr.sbin/rtadvd/timer.c index 7ccfb7e..439cbb2 100644 --- a/usr.sbin/rtadvd/timer.c +++ b/usr.sbin/rtadvd/timer.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: timer.c,v 1.3 2000/05/22 22:23:07 itojun Exp $ */ +/* $KAME: timer.c,v 1.4 2000/05/27 11:30:43 jinmei Exp $ */ /* * Copyright (C) 1998 WIDE Project. diff --git a/usr.sbin/rtadvd/timer.h b/usr.sbin/rtadvd/timer.h index c5f8b85..3baf0d0 100644 --- a/usr.sbin/rtadvd/timer.h +++ b/usr.sbin/rtadvd/timer.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: timer.h,v 1.2 2000/05/16 13:34:14 itojun Exp $ */ +/* $KAME: timer.h,v 1.3 2000/05/27 11:30:43 jinmei Exp $ */ /* * Copyright (C) 1998 WIDE Project. -- cgit v1.1