summaryrefslogtreecommitdiffstats
path: root/sys/netinet6/nd6_nbr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6/nd6_nbr.c')
-rw-r--r--sys/netinet6/nd6_nbr.c250
1 files changed, 157 insertions, 93 deletions
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
index d3fa831..7527d43 100644
--- a/sys/netinet6/nd6_nbr.c
+++ b/sys/netinet6/nd6_nbr.c
@@ -1,5 +1,5 @@
/* $FreeBSD$ */
-/* $KAME: nd6_nbr.c,v 1.37 2000/06/04 12:46:13 itojun Exp $ */
+/* $KAME: nd6_nbr.c,v 1.64 2001/05/17 03:48:30 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -45,6 +45,7 @@
#include <sys/errno.h>
#include <sys/syslog.h>
#include <sys/queue.h>
+#include <sys/callout.h>
#include <net/if.h>
#include <net/if_types.h>
@@ -72,6 +73,8 @@
struct dadq;
static struct dadq *nd6_dad_find __P((struct ifaddr *));
+static void nd6_dad_starttimer __P((struct dadq *, int));
+static void nd6_dad_stoptimer __P((struct dadq *));
static void nd6_dad_timer __P((struct ifaddr *));
static void nd6_dad_ns_output __P((struct dadq *, struct ifaddr *));
static void nd6_dad_ns_input __P((struct ifaddr *));
@@ -106,10 +109,25 @@ nd6_ns_input(m, off, icmp6len)
union nd_opts ndopts;
struct sockaddr_dl *proxydl = NULL;
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, icmp6len,);
+ nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off);
+#else
+ IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len);
+ if (nd_ns == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return;
+ }
+#endif
+ ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */
+ taddr6 = nd_ns->nd_ns_target;
+
if (ip6->ip6_hlim != 255) {
- log(LOG_ERR,
- "nd6_ns_input: invalid hlim %d\n", ip6->ip6_hlim);
- goto freeit;
+ nd6log((LOG_ERR,
+ "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n",
+ ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src),
+ ip6_sprintf(&ip6->ip6_dst), if_name(ifp)));
+ goto bad;
}
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
@@ -121,26 +139,14 @@ nd6_ns_input(m, off, icmp6len)
&& daddr6.s6_addr8[12] == 0xff) {
; /*good*/
} else {
- log(LOG_INFO, "nd6_ns_input: bad DAD packet "
- "(wrong ip6 dst)\n");
+ nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet "
+ "(wrong ip6 dst)\n"));
goto bad;
}
}
-#ifndef PULLDOWN_TEST
- IP6_EXTHDR_CHECK(m, off, icmp6len,);
- nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off);
-#else
- IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len);
- if (nd_ns == NULL) {
- icmp6stat.icp6s_tooshort++;
- return;
- }
-#endif
- taddr6 = nd_ns->nd_ns_target;
-
if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
- log(LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n");
+ nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n"));
goto bad;
}
@@ -150,8 +156,10 @@ nd6_ns_input(m, off, icmp6len)
icmp6len -= sizeof(*nd_ns);
nd6_option_init(nd_ns + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) {
- log(LOG_INFO, "nd6_ns_input: invalid ND option, ignored\n");
- goto bad;
+ nd6log((LOG_INFO,
+ "nd6_ns_input: invalid ND option, ignored\n"));
+ /* nd6_options have incremented stats */
+ goto freeit;
}
if (ndopts.nd_opts_src_lladdr) {
@@ -160,8 +168,8 @@ nd6_ns_input(m, off, icmp6len)
}
if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) {
- log(LOG_INFO, "nd6_ns_input: bad DAD packet "
- "(link-layer address option)\n");
+ nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet "
+ "(link-layer address option)\n"));
goto bad;
}
@@ -223,7 +231,7 @@ nd6_ns_input(m, off, icmp6len)
}
if (!ifa) {
/*
- * We've got a NS packet, and we don't have that adddress
+ * We've got an NS packet, and we don't have that adddress
* assigned for us. We MUST silently ignore it.
* See RFC2461 7.2.3.
*/
@@ -236,10 +244,11 @@ nd6_ns_input(m, off, icmp6len)
goto freeit;
if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
- log(LOG_INFO,
+ nd6log((LOG_INFO,
"nd6_ns_input: lladdrlen mismatch for %s "
"(if %d, NS packet %d)\n",
- ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2);
+ ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2));
+ goto bad;
}
if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) {
@@ -306,9 +315,10 @@ nd6_ns_input(m, off, icmp6len)
return;
bad:
- log(LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6));
- log(LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6));
- log(LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6));
+ nd6log((LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6)));
+ nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6)));
+ nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6)));
+ icmp6stat.icp6s_badns++;
m_freem(m);
}
@@ -324,7 +334,7 @@ nd6_ns_input(m, off, icmp6len)
void
nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
struct ifnet *ifp;
- struct in6_addr *daddr6, *taddr6;
+ const struct in6_addr *daddr6, *taddr6;
struct llinfo_nd6 *ln; /* for source address determination */
int dad; /* duplicated address detection */
{
@@ -362,6 +372,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
}
if (m == NULL)
return;
+ m->m_pkthdr.rcvif = NULL;
if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) {
m->m_flags |= M_MCAST;
@@ -495,7 +506,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
#ifdef IPSEC
/* Don't lookup socket */
- ipsec_setsocket(m, NULL);
+ (void)ipsec_setsocket(m, NULL);
#endif
ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif);
if (outif) {
@@ -541,9 +552,11 @@ nd6_na_input(m, off, icmp6len)
union nd_opts ndopts;
if (ip6->ip6_hlim != 255) {
- log(LOG_ERR,
- "nd6_na_input: invalid hlim %d\n", ip6->ip6_hlim);
- goto freeit;
+ nd6log((LOG_ERR,
+ "nd6_na_input: invalid hlim (%d) from %s to %s on %s\n",
+ ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src),
+ ip6_sprintf(&ip6->ip6_dst), if_name(ifp)));
+ goto bad;
}
#ifndef PULLDOWN_TEST
@@ -566,22 +579,24 @@ nd6_na_input(m, off, icmp6len)
taddr6.s6_addr16[1] = htons(ifp->if_index);
if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
- log(LOG_ERR,
+ nd6log((LOG_ERR,
"nd6_na_input: invalid target address %s\n",
- ip6_sprintf(&taddr6));
- goto freeit;
+ ip6_sprintf(&taddr6)));
+ goto bad;
}
if (IN6_IS_ADDR_MULTICAST(&daddr6))
if (is_solicited) {
- log(LOG_ERR,
- "nd6_na_input: a solicited adv is multicasted\n");
- goto freeit;
+ nd6log((LOG_ERR,
+ "nd6_na_input: a solicited adv is multicasted\n"));
+ goto bad;
}
icmp6len -= sizeof(*nd_na);
nd6_option_init(nd_na + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) {
- log(LOG_INFO, "nd6_na_input: invalid ND option, ignored\n");
+ nd6log((LOG_INFO,
+ "nd6_na_input: invalid ND option, ignored\n"));
+ /* nd6_options have incremented stats */
goto freeit;
}
@@ -616,10 +631,11 @@ nd6_na_input(m, off, icmp6len)
}
if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
- log(LOG_INFO,
+ nd6log((LOG_INFO,
"nd6_na_input: lladdrlen mismatch for %s "
"(if %d, NA packet %d)\n",
- ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2);
+ ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2));
+ goto bad;
}
/*
@@ -649,10 +665,19 @@ nd6_na_input(m, off, icmp6len)
ln->ln_byhint = 0;
if (ln->ln_expire)
ln->ln_expire = time_second +
- nd_ifinfo[rt->rt_ifp->if_index].reachable;
- } else
+ nd_ifinfo[rt->rt_ifp->if_index].reachable;
+ } else {
ln->ln_state = ND6_LLINFO_STALE;
- ln->ln_router = is_router;
+ ln->ln_expire = time_second + nd6_gctimer;
+ }
+ if ((ln->ln_router = is_router) != 0) {
+ /*
+ * This means a router's state has changed from
+ * non-reachable to probably reachable, and might
+ * affect the status of associated prefixes..
+ */
+ pfxlist_onlink_check();
+ }
} else {
int llchange;
@@ -695,8 +720,10 @@ nd6_na_input(m, off, icmp6len)
* If state is REACHABLE, make it STALE.
* no other updates should be done.
*/
- if (ln->ln_state == ND6_LLINFO_REACHABLE)
+ if (ln->ln_state == ND6_LLINFO_REACHABLE) {
ln->ln_state = ND6_LLINFO_STALE;
+ ln->ln_expire = time_second + nd6_gctimer;
+ }
goto freeit;
} else if (is_override /* (2a) */
|| (!is_override && (lladdr && !llchange)) /* (2b) */
@@ -719,11 +746,13 @@ nd6_na_input(m, off, icmp6len)
ln->ln_byhint = 0;
if (ln->ln_expire) {
ln->ln_expire = time_second +
- nd_ifinfo[ifp->if_index].reachable;
+ nd_ifinfo[ifp->if_index].reachable;
}
} else {
- if (lladdr && llchange)
+ if (lladdr && llchange) {
ln->ln_state = ND6_LLINFO_STALE;
+ ln->ln_expire = time_second + nd6_gctimer;
+ }
}
}
@@ -759,21 +788,22 @@ nd6_na_input(m, off, icmp6len)
rt->rt_flags &= ~RTF_REJECT;
ln->ln_asked = 0;
if (ln->ln_hold) {
-#ifdef OLDIP6OUTPUT
- (*ifp->if_output)(ifp, ln->ln_hold, rt_key(rt), rt);
-#else
/*
* 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);
-#endif
ln->ln_hold = 0;
}
freeit:
m_freem(m);
+ return;
+
+ bad:
+ icmp6stat.icp6s_badna++;
+ m_freem(m);
}
/*
@@ -788,7 +818,7 @@ nd6_na_input(m, off, icmp6len)
void
nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
struct ifnet *ifp;
- struct in6_addr *daddr6, *taddr6;
+ const struct in6_addr *daddr6, *taddr6;
u_long flags;
int tlladdr; /* 1 if include target link-layer address */
struct sockaddr *sdl0; /* sockaddr_dl (= proxy NA) or NULL */
@@ -824,6 +854,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
}
if (m == NULL)
return;
+ m->m_pkthdr.rcvif = NULL;
if (IN6_IS_ADDR_MULTICAST(daddr6)) {
m->m_flags |= M_MCAST;
@@ -917,7 +948,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
#ifdef IPSEC
/* Don't lookup socket */
- ipsec_setsocket(m, NULL);
+ (void)ipsec_setsocket(m, NULL);
#endif
ip6_output(m, NULL, NULL, 0, &im6o, &outif);
if (outif) {
@@ -935,6 +966,10 @@ nd6_ifptomac(ifp)
case IFT_ARCNET:
case IFT_ETHER:
case IFT_FDDI:
+ case IFT_IEEE1394:
+#ifdef IFT_IEEE80211
+ case IFT_IEEE80211:
+#endif
return ((caddr_t)(ifp + 1));
break;
default:
@@ -951,10 +986,11 @@ struct dadq {
int dad_ns_ocount; /* NS sent so far */
int dad_ns_icount;
int dad_na_icount;
- struct callout_handle dad_timer;
+ struct callout dad_timer_ch;
};
static struct dadq_head dadq;
+static int dad_init = 0;
static struct dadq *
nd6_dad_find(ifa)
@@ -969,6 +1005,24 @@ nd6_dad_find(ifa)
return NULL;
}
+static void
+nd6_dad_starttimer(dp, ticks)
+ struct dadq *dp;
+ int ticks;
+{
+
+ callout_reset(&dp->dad_timer_ch, ticks,
+ (void (*) __P((void *)))nd6_dad_timer, (void *)dp->dad_ifa);
+}
+
+static void
+nd6_dad_stoptimer(dp)
+ struct dadq *dp;
+{
+
+ callout_stop(&dp->dad_timer_ch);
+}
+
/*
* Start Duplicated Address Detection (DAD) for specified interface address.
*/
@@ -979,7 +1033,6 @@ nd6_dad_start(ifa, tick)
{
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
struct dadq *dp;
- static int dad_init = 0;
if (!dad_init) {
TAILQ_INIT(&dadq);
@@ -1026,12 +1079,11 @@ nd6_dad_start(ifa, tick)
return;
}
bzero(dp, sizeof(*dp));
+ callout_init(&dp->dad_timer_ch, 0);
TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list);
-#ifdef ND6_DEBUG
- log(LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp),
- ip6_sprintf(&ia->ia_addr.sin6_addr));
-#endif
+ nd6log((LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp),
+ ip6_sprintf(&ia->ia_addr.sin6_addr)));
/*
* Send NS packet for DAD, ip6_dad_count times.
@@ -1040,15 +1092,14 @@ nd6_dad_start(ifa, tick)
* (re)initialization.
*/
dp->dad_ifa = ifa;
- ifa->ifa_refcnt++; /*just for safety*/
+ IFAREF(ifa); /*just for safety*/
dp->dad_count = ip6_dad_count;
dp->dad_ns_icount = dp->dad_na_icount = 0;
dp->dad_ns_ocount = dp->dad_ns_tcount = 0;
if (!tick) {
nd6_dad_ns_output(dp, ifa);
- dp->dad_timer =
- timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa,
- nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000);
+ nd6_dad_starttimer(dp,
+ nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000);
} else {
int ntick;
@@ -1057,12 +1108,35 @@ nd6_dad_start(ifa, tick)
else
ntick = *tick + random() % (hz / 2);
*tick = ntick;
- dp->dad_timer =
- timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa,
- ntick);
+ nd6_dad_starttimer(dp, ntick);
}
}
+/*
+ * terminate DAD unconditionally. used for address removals.
+ */
+void
+nd6_dad_stop(ifa)
+ struct ifaddr *ifa;
+{
+ struct dadq *dp;
+
+ if (!dad_init)
+ return;
+ dp = nd6_dad_find(ifa);
+ if (!dp) {
+ /* DAD wasn't started yet */
+ return;
+ }
+
+ nd6_dad_stoptimer(dp);
+
+ TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
+ free(dp, M_IP6NDP);
+ dp = NULL;
+ IFAFREE(ifa);
+}
+
static void
nd6_dad_timer(ifa)
struct ifaddr *ifa;
@@ -1100,8 +1174,8 @@ nd6_dad_timer(ifa)
/* timeouted with IFF_{RUNNING,UP} check */
if (dp->dad_ns_tcount > dad_maxtry) {
- log(LOG_ERR, "%s: could not run DAD, driver problem?\n",
- if_name(ifa->ifa_ifp));
+ nd6log((LOG_INFO, "%s: could not run DAD, driver problem?\n",
+ if_name(ifa->ifa_ifp)));
TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
free(dp, M_IP6NDP);
@@ -1116,9 +1190,8 @@ nd6_dad_timer(ifa)
* We have more NS to go. Send NS packet for DAD.
*/
nd6_dad_ns_output(dp, ifa);
- dp->dad_timer =
- timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa,
- nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000);
+ nd6_dad_starttimer(dp,
+ nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000);
} else {
/*
* We have transmitted sufficient number of DAD packets.
@@ -1177,12 +1250,10 @@ nd6_dad_timer(ifa)
*/
ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
-#ifdef ND6_DEBUG
- log(LOG_INFO,
+ nd6log((LOG_DEBUG,
"%s: DAD complete for %s - no duplicates found\n",
if_name(ifa->ifa_ifp),
- ip6_sprintf(&ia->ia_addr.sin6_addr));
-#endif
+ ip6_sprintf(&ia->ia_addr.sin6_addr)));
TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
free(dp, M_IP6NDP);
@@ -1208,18 +1279,16 @@ nd6_dad_duplicated(ifa)
return;
}
- log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: %d NS, "
- "%d NA\n", if_name(ifa->ifa_ifp),
- ip6_sprintf(&ia->ia_addr.sin6_addr),
- dp->dad_ns_icount, dp->dad_na_icount);
+ log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: "
+ "NS in/out=%d/%d, NA in=%d\n",
+ if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr),
+ dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount);
ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
ia->ia6_flags |= IN6_IFF_DUPLICATED;
/* We are done with DAD, with duplicated address found. (failure) */
- untimeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa
- , dp->dad_timer
- );
+ nd6_dad_stoptimer(dp);
log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n",
if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr));
@@ -1264,7 +1333,7 @@ nd6_dad_ns_input(ifa)
{
struct in6_ifaddr *ia;
struct ifnet *ifp;
- struct in6_addr *taddr6;
+ const struct in6_addr *taddr6;
struct dadq *dp;
int duplicate;
@@ -1277,17 +1346,12 @@ nd6_dad_ns_input(ifa)
duplicate = 0;
dp = nd6_dad_find(ifa);
- /*
- * If it is from myself, ignore this.
- */
- if (ifp && (ifp->if_flags & IFF_LOOPBACK))
- return;
-
/* Quickhack - completely ignore DAD NS packets */
if (dad_ignore_ns) {
- log(LOG_INFO, "nd6_dad_ns_input: ignoring DAD NS packet for "
+ nd6log((LOG_INFO,
+ "nd6_dad_ns_input: ignoring DAD NS packet for "
"address %s(%s)\n", ip6_sprintf(taddr6),
- if_name(ifa->ifa_ifp));
+ if_name(ifa->ifa_ifp)));
return;
}
OpenPOWER on IntegriCloud