summaryrefslogtreecommitdiffstats
path: root/sys/netinet6/nd6.c
diff options
context:
space:
mode:
authorsuz <suz@FreeBSD.org>2005-10-21 16:23:01 +0000
committersuz <suz@FreeBSD.org>2005-10-21 16:23:01 +0000
commitc2b19f24a4ba01108e047a35a4a060cbfdf28a17 (patch)
treeb24292a814b8011ab44cabf6cac263a8a214a60a /sys/netinet6/nd6.c
parent6ee4447c50d54cfee8b2556c2cdcc45dc05bca37 (diff)
downloadFreeBSD-src-c2b19f24a4ba01108e047a35a4a060cbfdf28a17.zip
FreeBSD-src-c2b19f24a4ba01108e047a35a4a060cbfdf28a17.tar.gz
sync with KAME regarding NDP
- introduced fine-grain-timer to manage ND-caches and IPv6 Multicast-Listeners - supports Router-Preference <draft-ietf-ipv6-router-selection-07.txt> - better prefix lifetime management - more spec-comformant DAD advertisement - updated RFC/internet-draft revisions Obtained from: KAME Reviewed by: ume, gnn MFC after: 2 month
Diffstat (limited to 'sys/netinet6/nd6.c')
-rw-r--r--sys/netinet6/nd6.c546
1 files changed, 337 insertions, 209 deletions
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index 8331ea1..da2a617 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -67,6 +67,8 @@
#include <netinet6/nd6.h>
#include <netinet/icmp6.h>
+#include <sys/limits.h>
+
#include <net/net_osdep.h>
#define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */
@@ -87,6 +89,7 @@ int nd6_gctimer = (60 * 60 * 24); /* 1 day: garbage collection timer */
int nd6_maxndopt = 10; /* max # of ND options allowed */
int nd6_maxnudhint = 0; /* max # of subsequent upper layer hints */
+int nd6_maxqueuelen = 1; /* max # of packets cached in unresolved ND entries */
#ifdef ND6_DEBUG
int nd6_debug = 1;
@@ -109,6 +112,8 @@ static int nd6_is_new_addr_neighbor __P((struct sockaddr_in6 *,
static void nd6_setmtu0 __P((struct ifnet *, struct nd_ifinfo *));
static void nd6_slowtimo __P((void *));
static int regen_tmpaddr __P((struct in6_ifaddr *));
+static struct llinfo_nd6 *nd6_free __P((struct rtentry *, int));
+static void nd6_llinfo_timer __P((void *));
struct callout nd6_slowtimo_ch;
struct callout nd6_timer_ch;
@@ -383,124 +388,150 @@ skip1:
}
/*
- * ND6 timer routine to expire default route list and prefix list
+ * ND6 timer routine to handle ND6 entries
*/
void
-nd6_timer(ignored_arg)
- void *ignored_arg;
+nd6_llinfo_settimer(ln, tick)
+ struct llinfo_nd6 *ln;
+ long tick;
+{
+ if (tick < 0) {
+ ln->ln_expire = 0;
+ ln->ln_ntick = 0;
+ callout_stop(&ln->ln_timer_ch);
+ } else {
+ ln->ln_expire = time_second + tick / hz;
+ if (tick > INT_MAX) {
+ ln->ln_ntick = tick - INT_MAX;
+ callout_reset(&ln->ln_timer_ch, INT_MAX,
+ nd6_llinfo_timer, ln);
+ } else {
+ ln->ln_ntick = 0;
+ callout_reset(&ln->ln_timer_ch, tick,
+ nd6_llinfo_timer, ln);
+ }
+ }
+}
+
+static void
+nd6_llinfo_timer(arg)
+ void *arg;
{
- int s;
struct llinfo_nd6 *ln;
- struct nd_defrouter *dr;
- struct nd_prefix *pr;
+ struct rtentry *rt;
+ struct in6_addr *dst;
struct ifnet *ifp;
- struct in6_ifaddr *ia6, *nia6;
- struct in6_addrlifetime *lt6;
-
- s = splnet();
- callout_reset(&nd6_timer_ch, nd6_prune * hz,
- nd6_timer, NULL);
+ struct nd_ifinfo *ndi = NULL;
- ln = llinfo_nd6.ln_next;
- while (ln && ln != &llinfo_nd6) {
- struct rtentry *rt;
- struct sockaddr_in6 *dst;
- struct llinfo_nd6 *next = ln->ln_next;
- /* XXX: used for the DELAY case only: */
- struct nd_ifinfo *ndi = NULL;
+ ln = (struct llinfo_nd6 *)arg;
- if ((rt = ln->ln_rt) == NULL) {
- ln = next;
- continue;
- }
- if ((ifp = rt->rt_ifp) == NULL) {
- ln = next;
- continue;
+ if (ln->ln_ntick > 0) {
+ if (ln->ln_ntick > INT_MAX) {
+ ln->ln_ntick -= INT_MAX;
+ nd6_llinfo_settimer(ln, INT_MAX);
+ } else {
+ ln->ln_ntick = 0;
+ nd6_llinfo_settimer(ln, ln->ln_ntick);
}
- ndi = ND_IFINFO(ifp);
- dst = (struct sockaddr_in6 *)rt_key(rt);
+ return;
+ }
- if (ln->ln_expire > time_second) {
- ln = next;
- continue;
- }
+ if ((rt = ln->ln_rt) == NULL)
+ panic("ln->ln_rt == NULL");
+ if ((ifp = rt->rt_ifp) == NULL)
+ panic("ln->ln_rt->rt_ifp == NULL");
+ ndi = ND_IFINFO(ifp);
- /* sanity check */
- if (!rt)
- panic("rt=0 in nd6_timer(ln=%p)", ln);
- if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln)
- panic("rt_llinfo(%p) is not equal to ln(%p)",
- rt->rt_llinfo, ln);
- if (!dst)
- panic("dst=0 in nd6_timer(ln=%p)", ln);
-
- switch (ln->ln_state) {
- case ND6_LLINFO_INCOMPLETE:
- if (ln->ln_asked < nd6_mmaxtries) {
- ln->ln_asked++;
- ln->ln_expire = time_second +
- ND_IFINFO(ifp)->retrans / 1000;
- nd6_ns_output(ifp, NULL, &dst->sin6_addr,
- ln, 0);
- } else {
- struct mbuf *m = ln->ln_hold;
- if (m) {
- /*
- * assuming every packet in ln_hold has
- * the same IP header
- */
- ln->ln_hold = NULL;
- icmp6_error2(m, ICMP6_DST_UNREACH,
- ICMP6_DST_UNREACH_ADDR, 0,
- rt->rt_ifp);
- }
- next = nd6_free(rt);
- }
- break;
- case ND6_LLINFO_REACHABLE:
- if (ln->ln_expire) {
- ln->ln_state = ND6_LLINFO_STALE;
- ln->ln_expire = time_second + nd6_gctimer;
- }
- break;
+ /* sanity check */
+ if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln)
+ panic("rt_llinfo(%p) is not equal to ln(%p)",
+ rt->rt_llinfo, ln);
+ if (rt_key(rt) == NULL)
+ panic("rt key is NULL in nd6_timer(ln=%p)", ln);
- case ND6_LLINFO_STALE:
- /* Garbage Collection(RFC 2461 5.3) */
- if (ln->ln_expire)
- next = nd6_free(rt);
- break;
+ dst = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
- case ND6_LLINFO_DELAY:
- if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) {
- /* We need NUD */
- ln->ln_asked = 1;
- ln->ln_state = ND6_LLINFO_PROBE;
- ln->ln_expire = time_second +
- ndi->retrans / 1000;
- nd6_ns_output(ifp, &dst->sin6_addr,
- &dst->sin6_addr,
- ln, 0);
- } else {
- ln->ln_state = ND6_LLINFO_STALE; /* XXX */
- ln->ln_expire = time_second + nd6_gctimer;
- }
- break;
- case ND6_LLINFO_PROBE:
- if (ln->ln_asked < nd6_umaxtries) {
- ln->ln_asked++;
- ln->ln_expire = time_second +
- ND_IFINFO(ifp)->retrans / 1000;
- nd6_ns_output(ifp, &dst->sin6_addr,
- &dst->sin6_addr, ln, 0);
- } else {
- next = nd6_free(rt);
+ switch (ln->ln_state) {
+ case ND6_LLINFO_INCOMPLETE:
+ if (ln->ln_asked < nd6_mmaxtries) {
+ ln->ln_asked++;
+ nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
+ nd6_ns_output(ifp, NULL, dst, ln, 0);
+ } else {
+ struct mbuf *m = ln->ln_hold;
+ if (m) {
+ /*
+ * assuming every packet in ln_hold has the
+ * same IP header
+ */
+ ln->ln_hold = NULL;
+ icmp6_error2(m, ICMP6_DST_UNREACH,
+ ICMP6_DST_UNREACH_ADDR, 0, rt->rt_ifp);
}
- break;
+ if (rt)
+ (void)nd6_free(rt, 0);
+ ln = NULL;
+ }
+ break;
+ case ND6_LLINFO_REACHABLE:
+ if (!ND6_LLINFO_PERMANENT(ln)) {
+ ln->ln_state = ND6_LLINFO_STALE;
+ nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
}
- ln = next;
+ break;
+
+ case ND6_LLINFO_STALE:
+ /* Garbage Collection(RFC 2461 5.3) */
+ if (!ND6_LLINFO_PERMANENT(ln)) {
+ (void)nd6_free(rt, 1);
+ ln = NULL;
+ }
+ break;
+
+ case ND6_LLINFO_DELAY:
+ if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) {
+ /* We need NUD */
+ ln->ln_asked = 1;
+ ln->ln_state = ND6_LLINFO_PROBE;
+ nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
+ nd6_ns_output(ifp, dst, dst, ln, 0);
+ } else {
+ ln->ln_state = ND6_LLINFO_STALE; /* XXX */
+ nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
+ }
+ break;
+ case ND6_LLINFO_PROBE:
+ if (ln->ln_asked < nd6_umaxtries) {
+ ln->ln_asked++;
+ nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
+ nd6_ns_output(ifp, dst, dst, ln, 0);
+ } else {
+ (void)nd6_free(rt, 0);
+ ln = NULL;
+ }
+ break;
}
+}
+
+
+/*
+ * ND6 timer routine to expire default route list and prefix list
+ */
+void
+nd6_timer(ignored_arg)
+ void *ignored_arg;
+{
+ int s;
+ struct nd_defrouter *dr;
+ struct nd_prefix *pr;
+ struct in6_ifaddr *ia6, *nia6;
+ struct in6_addrlifetime *lt6;
+
+ callout_reset(&nd6_timer_ch, nd6_prune * hz,
+ nd6_timer, NULL);
/* expire default router list */
+ s = splnet();
dr = TAILQ_FIRST(&nd_defrouter);
while (dr) {
if (dr->expire && dr->expire < time_second) {
@@ -594,7 +625,8 @@ nd6_timer(ignored_arg)
* since pltime is just for autoconf, pltime processing for
* prefix is not necessary.
*/
- if (pr->ndpr_expire && pr->ndpr_expire < time_second) {
+ if (pr->ndpr_vltime != ND6_INFINITE_LIFETIME &&
+ time_second - pr->ndpr_lastupdate > pr->ndpr_vltime) {
struct nd_prefix *t;
t = pr->ndpr_next;
@@ -663,7 +695,7 @@ regen_tmpaddr(ia6)
if (public_ifa6 != NULL) {
int e;
- if ((e = in6_tmpifadd(public_ifa6, 0)) != 0) {
+ if ((e = in6_tmpifadd(public_ifa6, 0, 0)) != 0) {
log(LOG_NOTICE, "regen_tmpaddr: failed to create a new"
" tmp addr,errno=%d\n", e);
return (-1);
@@ -683,21 +715,29 @@ nd6_purge(ifp)
struct ifnet *ifp;
{
struct llinfo_nd6 *ln, *nln;
- struct nd_defrouter *dr, *ndr, drany;
+ struct nd_defrouter *dr, *ndr;
struct nd_prefix *pr, *npr;
- /* Nuke default router list entries toward ifp */
- if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) {
- /*
- * The first entry of the list may be stored in
- * the routing table, so we'll delete it later.
- */
- for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = ndr) {
- ndr = TAILQ_NEXT(dr, dr_entry);
- if (dr->ifp == ifp)
- defrtrlist_del(dr);
- }
- dr = TAILQ_FIRST(&nd_defrouter);
+ /*
+ * Nuke default router list entries toward ifp.
+ * We defer removal of default router list entries that is installed
+ * in the routing table, in order to keep additional side effects as
+ * small as possible.
+ */
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = ndr) {
+ ndr = TAILQ_NEXT(dr, dr_entry);
+ if (dr->installed)
+ continue;
+
+ if (dr->ifp == ifp)
+ defrtrlist_del(dr);
+ }
+
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = ndr) {
+ ndr = TAILQ_NEXT(dr, dr_entry);
+ if (!dr->installed)
+ continue;
+
if (dr->ifp == ifp)
defrtrlist_del(dr);
}
@@ -707,6 +747,14 @@ nd6_purge(ifp)
npr = pr->ndpr_next;
if (pr->ndpr_ifp == ifp) {
/*
+ * Because if_detach() does *not* release prefixes
+ * while purging addresses the reference count will
+ * still be above zero. We therefore reset it to
+ * make sure that the prefix really gets purged.
+ */
+ pr->ndpr_refcnt = 0;
+
+ /*
* Previously, pr->ndpr_addr is removed as well,
* but I strongly believe we don't have to do it.
* nd6_purge() is only called from in6_ifdetach(),
@@ -724,8 +772,6 @@ nd6_purge(ifp)
if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */
/* refresh default router list */
- bzero(&drany, sizeof(drany));
- defrouter_delreq(&drany, 0);
defrouter_select();
}
@@ -746,7 +792,7 @@ nd6_purge(ifp)
rt->rt_gateway->sa_family == AF_LINK) {
sdl = (struct sockaddr_dl *)rt->rt_gateway;
if (sdl->sdl_index == ifp->if_index)
- nln = nd6_free(rt);
+ nln = nd6_free(rt, 0);
}
ln = nln;
}
@@ -833,6 +879,10 @@ nd6_lookup(addr6, create, ifp)
* own address on a non-loopback interface. Instead, we should
* use rt->rt_ifa->ifa_ifp, which would specify the REAL
* interface.
+ * Note also that ifa_ifp and ifp may differ when we connect two
+ * interfaces to a same link, install a link prefix to an interface,
+ * and try to install a neighbor cache on an interface that does not
+ * have a route to the prefix.
*/
if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 ||
rt->rt_gateway->sa_family != AF_LINK || rt->rt_llinfo == NULL ||
@@ -861,6 +911,7 @@ nd6_is_new_addr_neighbor(addr, ifp)
struct ifnet *ifp;
{
struct nd_prefix *pr;
+ struct ifaddr *dstaddr;
/*
* A link-local address is always a neighbor.
@@ -904,6 +955,14 @@ nd6_is_new_addr_neighbor(addr, ifp)
}
/*
+ * If the address is assigned on the node of the other side of
+ * a p2p interface, the address should be a neighbor.
+ */
+ dstaddr = ifa_ifwithdstaddr((struct sockaddr *)addr);
+ if ((dstaddr != NULL) && (dstaddr->ifa_ifp == ifp))
+ return (1);
+
+ /*
* If the default router list is empty, all addresses are regarded
* as on-link, and thus, as a neighbor.
* XXX: we restrict the condition to hosts, because routers usually do
@@ -943,10 +1002,14 @@ nd6_is_addr_neighbor(addr, ifp)
/*
* Free an nd6 llinfo entry.
+ * Since the function would cause significant changes in the kernel, DO NOT
+ * make it global, unless you have a strong reason for the change, and are sure
+ * that the change is safe.
*/
-struct llinfo_nd6 *
-nd6_free(rt)
+static struct llinfo_nd6 *
+nd6_free(rt, gc)
struct rtentry *rt;
+ int gc;
{
struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo, *next;
struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
@@ -957,12 +1020,38 @@ nd6_free(rt)
* even though it is not harmful, it was not really necessary.
*/
- if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */
+ /* cancel timer */
+ nd6_llinfo_settimer(ln, -1);
+
+ if (!ip6_forwarding) {
int s;
s = splnet();
dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
rt->rt_ifp);
+ if (dr != NULL && dr->expire &&
+ ln->ln_state == ND6_LLINFO_STALE && gc) {
+ /*
+ * If the reason for the deletion is just garbage
+ * collection, and the neighbor is an active default
+ * router, do not delete it. Instead, reset the GC
+ * timer using the router's lifetime.
+ * Simply deleting the entry would affect default
+ * router selection, which is not necessarily a good
+ * thing, especially when we're using router preference
+ * values.
+ * XXX: the check for ln_state would be redundant,
+ * but we intentionally keep it just in case.
+ */
+ if (dr->expire > time_second)
+ nd6_llinfo_settimer(ln,
+ (dr->expire - time_second) * hz);
+ else
+ nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
+ splx(s);
+ return (ln->ln_next);
+ }
+
if (ln->ln_router || dr) {
/*
* rt6_flush must be called whether or not the neighbor
@@ -996,19 +1085,10 @@ nd6_free(rt)
*/
pfxlist_onlink_check();
- if (dr == TAILQ_FIRST(&nd_defrouter)) {
- /*
- * It is used as the current default router,
- * so we have to move it to the end of the
- * list and choose a new one.
- * XXX: it is not very efficient if this is
- * the only router.
- */
- TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
- TAILQ_INSERT_TAIL(&nd_defrouter, dr, dr_entry);
-
- defrouter_select();
- }
+ /*
+ * refresh default router list
+ */
+ defrouter_select();
}
splx(s);
}
@@ -1079,9 +1159,10 @@ nd6_nud_hint(rt, dst6, force)
}
ln->ln_state = ND6_LLINFO_REACHABLE;
- if (ln->ln_expire)
- ln->ln_expire = time_second +
- ND_IFINFO(rt->rt_ifp)->reachable;
+ if (!ND6_LLINFO_PERMANENT(ln)) {
+ nd6_llinfo_settimer(ln,
+ (long)ND_IFINFO(rt->rt_ifp)->reachable * hz);
+ }
}
void
@@ -1143,12 +1224,13 @@ nd6_rtrequest(req, rt, info)
* SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
* rt->rt_flags |= RTF_CLONING;
*/
- if (rt->rt_flags & (RTF_CLONING | RTF_LLINFO)) {
+ if ((rt->rt_flags & RTF_CLONING) ||
+ ((rt->rt_flags & RTF_LLINFO) && ln == NULL)) {
/*
- * Case 1: This route should come from
- * a route to interface. RTF_LLINFO flag is set
- * for a host route whose destination should be
- * treated as on-link.
+ * Case 1: This route should come from a route to
+ * interface (RTF_CLONING case) or the route should be
+ * treated as on-link but is currently not
+ * (RTF_LLINFO && ln == NULL case).
*/
rt_setgate(rt, rt_key(rt),
(struct sockaddr *)&null_sdl);
@@ -1156,11 +1238,7 @@ nd6_rtrequest(req, rt, info)
SDL(gate)->sdl_type = ifp->if_type;
SDL(gate)->sdl_index = ifp->if_index;
if (ln)
- ln->ln_expire = time_second;
- if (ln && ln->ln_expire == 0) {
- /* kludge for desktops */
- ln->ln_expire = 1;
- }
+ nd6_llinfo_settimer(ln, 0);
if ((rt->rt_flags & RTF_CLONING) != 0)
break;
}
@@ -1215,6 +1293,8 @@ nd6_rtrequest(req, rt, info)
nd6_allocated++;
bzero(ln, sizeof(*ln));
ln->ln_rt = rt;
+ callout_init(&ln->ln_timer_ch, 0);
+
/* this is required for "ndp" command. - shin */
if (req == RTM_ADD) {
/*
@@ -1230,7 +1310,7 @@ nd6_rtrequest(req, rt, info)
* initialized in rtrequest(), so rt_expire is 0.
*/
ln->ln_state = ND6_LLINFO_NOSTATE;
- ln->ln_expire = time_second;
+ nd6_llinfo_settimer(ln, 0);
}
rt->rt_flags |= RTF_LLINFO;
ln->ln_next = llinfo_nd6.ln_next;
@@ -1246,7 +1326,7 @@ nd6_rtrequest(req, rt, info)
&SIN6(rt_key(rt))->sin6_addr);
if (ifa) {
caddr_t macp = nd6_ifptomac(ifp);
- ln->ln_expire = 0;
+ nd6_llinfo_settimer(ln, -1);
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
if (macp) {
@@ -1270,7 +1350,7 @@ nd6_rtrequest(req, rt, info)
}
}
} else if (rt->rt_flags & RTF_ANNOUNCE) {
- ln->ln_expire = 0;
+ nd6_llinfo_settimer(ln, -1);
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
@@ -1286,7 +1366,8 @@ nd6_rtrequest(req, rt, info)
llsol.s6_addr8[12] = 0xff;
if (in6_setscope(&llsol, ifp, NULL))
break;
- if (!in6_addmulti(&llsol, ifp, &error)) {
+ if (in6_addmulti(&llsol, ifp,
+ &error, 0) == NULL) {
nd6log((LOG_ERR, "%s: failed to join "
"%s (errno=%d)\n", if_name(ifp),
ip6_sprintf(&llsol), error));
@@ -1320,6 +1401,7 @@ nd6_rtrequest(req, rt, info)
ln->ln_next->ln_prev = ln->ln_prev;
ln->ln_prev->ln_next = ln->ln_next;
ln->ln_prev = NULL;
+ nd6_llinfo_settimer(ln, -1);
rt->rt_llinfo = 0;
rt->rt_flags &= ~RTF_LLINFO;
if (ln->ln_hold)
@@ -1339,7 +1421,7 @@ nd6_ioctl(cmd, data, ifp)
struct in6_ndireq *ndi = (struct in6_ndireq *)data;
struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data;
struct in6_ndifreq *ndif = (struct in6_ndifreq *)data;
- struct nd_defrouter *dr, any;
+ struct nd_defrouter *dr;
struct nd_prefix *pr;
struct rtentry *rt;
int i = 0, error = 0;
@@ -1392,7 +1474,22 @@ nd6_ioctl(cmd, data, ifp)
oprl->prefix[i].vltime = pr->ndpr_vltime;
oprl->prefix[i].pltime = pr->ndpr_pltime;
oprl->prefix[i].if_index = pr->ndpr_ifp->if_index;
- oprl->prefix[i].expire = pr->ndpr_expire;
+ if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME)
+ oprl->prefix[i].expire = 0;
+ else {
+ time_t maxexpire;
+
+ /* XXX: we assume time_t is signed. */
+ maxexpire = (-1) &
+ ~(1 << ((sizeof(maxexpire) * 8) - 1));
+ if (pr->ndpr_vltime <
+ maxexpire - pr->ndpr_lastupdate) {
+ oprl->prefix[i].expire =
+ pr->ndpr_lastupdate +
+ pr->ndpr_vltime;
+ } else
+ oprl->prefix[i].expire = maxexpire;
+ }
pfr = pr->ndpr_advrtrs.lh_first;
j = 0;
@@ -1430,7 +1527,6 @@ nd6_ioctl(cmd, data, ifp)
break;
case SIOCGIFINFO_IN6:
ND = *ND_IFINFO(ifp);
- ND.linkmtu = IN6_LINKMTU(ifp);
break;
case SIOCSIFINFO_IN6:
/*
@@ -1465,15 +1561,9 @@ nd6_ioctl(cmd, data, ifp)
break;
#undef ND
case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */
- /* flush default router list */
- /*
- * xxx sumikawa: should not delete route if default
- * route equals to the top of default router list
- */
- bzero(&any, sizeof(any));
- defrouter_delreq(&any, 0);
+ /* sync kernel routing table with the default router list */
+ defrouter_reset();
defrouter_select();
- /* xxx sumikawa: flush prefix list */
break;
case SIOCSPFXFLUSH_IN6:
{
@@ -1511,17 +1601,12 @@ nd6_ioctl(cmd, data, ifp)
struct nd_defrouter *dr, *next;
s = splnet();
- if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) {
- /*
- * The first entry of the list may be stored in
- * the routing table, so we'll delete it later.
- */
- for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = next) {
- next = TAILQ_NEXT(dr, dr_entry);
- defrtrlist_del(dr);
- }
- defrtrlist_del(TAILQ_FIRST(&nd_defrouter));
+ defrouter_reset();
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = next) {
+ next = TAILQ_NEXT(dr, dr_entry);
+ defrtrlist_del(dr);
}
+ defrouter_select();
splx(s);
break;
}
@@ -1613,7 +1698,7 @@ nd6_cache_lladdr(ifp, from, lladdr, lladdrlen, type, code)
return NULL;
if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) {
fail:
- (void)nd6_free(rt);
+ (void)nd6_free(rt, 0);
return NULL;
}
ln = (struct llinfo_nd6 *)rt->rt_llinfo;
@@ -1682,20 +1767,36 @@ fail:
* we must set the timer now, although it is actually
* meaningless.
*/
- ln->ln_expire = time_second + nd6_gctimer;
+ nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
if (ln->ln_hold) {
- /*
- * we assume ifp is not a p2p here, so just
- * set the 2nd argument as the 1st one.
- */
- nd6_output(ifp, ifp, ln->ln_hold,
- (struct sockaddr_in6 *)rt_key(rt), rt);
+ struct mbuf *m_hold, *m_hold_next;
+ for (m_hold = ln->ln_hold; m_hold;
+ m_hold = m_hold_next) {
+ struct mbuf *mpkt = NULL;
+
+ m_hold_next = m_hold->m_nextpkt;
+ mpkt = m_copym(m_hold, 0, M_COPYALL, M_DONTWAIT);
+ if (mpkt == NULL) {
+ m_freem(m_hold);
+ break;
+ }
+ mpkt->m_nextpkt = NULL;
+
+ /*
+ * we assume ifp is not a p2p here, so
+ * just set the 2nd argument as the
+ * 1st one.
+ */
+ nd6_output(ifp, ifp, mpkt,
+ (struct sockaddr_in6 *)rt_key(rt),
+ rt);
+ }
ln->ln_hold = NULL;
}
} else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
/* probe right away */
- ln->ln_expire = time_second;
+ nd6_llinfo_settimer((void *)ln, 0);
}
}
@@ -1789,7 +1890,6 @@ static void
nd6_slowtimo(ignored_arg)
void *ignored_arg;
{
- int s = splnet();
struct nd_ifinfo *nd6if;
struct ifnet *ifp;
@@ -1811,7 +1911,6 @@ nd6_slowtimo(ignored_arg)
}
}
IFNET_RUNLOCK();
- splx(s);
}
#define senderr(e) { error = (e); goto bad;}
@@ -1931,7 +2030,7 @@ again:
if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
ln->ln_state < ND6_LLINFO_REACHABLE) {
ln->ln_state = ND6_LLINFO_STALE;
- ln->ln_expire = time_second + nd6_gctimer;
+ nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
}
/*
@@ -1944,7 +2043,7 @@ again:
if (ln->ln_state == ND6_LLINFO_STALE) {
ln->ln_asked = 0;
ln->ln_state = ND6_LLINFO_DELAY;
- ln->ln_expire = time_second + nd6_delay;
+ nd6_llinfo_settimer(ln, (long)nd6_delay * hz);
}
/*
@@ -1957,26 +2056,44 @@ again:
/*
* There is a neighbor cache entry, but no ethernet address
- * response yet. Replace the held mbuf (if any) with this
- * latest one.
- *
- * This code conforms to the rate-limiting rule described in Section
- * 7.2.2 of RFC 2461, because the timer is set correctly after sending
- * an NS below.
+ * response yet. Append this latest packet to the end of the
+ * packet queue in the mbuf, unless the number of the packet
+ * does not exceed nd6_maxqueuelen. When it exceeds nd6_maxqueuelen,
+ * the oldest packet in the queue will be removed.
*/
if (ln->ln_state == ND6_LLINFO_NOSTATE)
ln->ln_state = ND6_LLINFO_INCOMPLETE;
- if (ln->ln_hold)
- m_freem(ln->ln_hold);
- ln->ln_hold = m;
- if (ln->ln_expire) {
- if (ln->ln_asked < nd6_mmaxtries &&
- ln->ln_expire < time_second) {
- ln->ln_asked++;
- ln->ln_expire = time_second +
- ND_IFINFO(ifp)->retrans / 1000;
- nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
+ if (ln->ln_hold) {
+ struct mbuf *m_hold;
+ int i;
+
+ i = 0;
+ for (m_hold = ln->ln_hold; m_hold; m_hold = m_hold->m_nextpkt) {
+ i++;
+ if (m_hold->m_nextpkt == NULL) {
+ m_hold->m_nextpkt = m;
+ break;
+ }
+ }
+ while (i >= nd6_maxqueuelen) {
+ m_hold = ln->ln_hold;
+ ln->ln_hold = ln->ln_hold->m_nextpkt;
+ m_free(m_hold);
+ i--;
}
+ } else {
+ ln->ln_hold = m;
+ }
+
+ /*
+ * If there has been no NS for the neighbor after entering the
+ * INCOMPLETE state, send the first solicitation.
+ */
+ if (!ND6_LLINFO_PERMANENT(ln) && ln->ln_asked == 0) {
+ ln->ln_asked++;
+ nd6_llinfo_settimer(ln,
+ (long)ND_IFINFO(ifp)->retrans * hz / 1000);
+ nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
}
return (0);
@@ -2128,6 +2245,8 @@ SYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_DRLIST, nd6_drlist,
CTLFLAG_RD, nd6_sysctl_drlist, "");
SYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_PRLIST, nd6_prlist,
CTLFLAG_RD, nd6_sysctl_prlist, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MAXQLEN, nd6_maxqueuelen,
+ CTLFLAG_RW, &nd6_maxqueuelen, 1, "");
static int
nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS)
@@ -2151,12 +2270,7 @@ nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS)
d->rtaddr.sin6_family = AF_INET6;
d->rtaddr.sin6_len = sizeof(d->rtaddr);
d->rtaddr.sin6_addr = dr->rtaddr;
- if (sa6_recoverscope(&d->rtaddr)) {
- log(LOG_ERR,
- "scope error in router list (%s)\n",
- ip6_sprintf(&d->rtaddr.sin6_addr));
- /* XXX: press on... */
- }
+ sa6_recoverscope(&d->rtaddr);
d->flags = dr->flags;
d->rtlifetime = dr->rtlifetime;
d->expire = dr->expire;
@@ -2209,7 +2323,21 @@ nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS)
p->vltime = pr->ndpr_vltime;
p->pltime = pr->ndpr_pltime;
p->if_index = pr->ndpr_ifp->if_index;
- p->expire = pr->ndpr_expire;
+ if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME)
+ p->expire = 0;
+ else {
+ time_t maxexpire;
+
+ /* XXX: we assume time_t is signed. */
+ maxexpire = (-1) &
+ ~(1 << ((sizeof(maxexpire) * 8) - 1));
+ if (pr->ndpr_vltime <
+ maxexpire - pr->ndpr_lastupdate) {
+ p->expire = pr->ndpr_lastupdate +
+ pr->ndpr_vltime;
+ } else
+ p->expire = maxexpire;
+ }
p->refcnt = pr->ndpr_refcnt;
p->flags = pr->ndpr_stateflags;
p->origin = PR_ORIG_RA;
OpenPOWER on IntegriCloud