summaryrefslogtreecommitdiffstats
path: root/sys/netinet6
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/nd6.c87
-rw-r--r--sys/netinet6/nd6.h2
-rw-r--r--sys/netinet6/nd6_nbr.c40
3 files changed, 79 insertions, 50 deletions
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index 4098ffb..23e7a9b 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -134,6 +134,7 @@ static int regen_tmpaddr(struct in6_ifaddr *);
static void nd6_free(struct llentry *, int);
static void nd6_free_redirect(const struct llentry *);
static void nd6_llinfo_timer(void *);
+static void nd6_llinfo_settimer_locked(struct llentry *, long);
static void clear_llinfo_pqueue(struct llentry *);
static void nd6_rtrequest(int, struct rtentry *, struct rt_addrinfo *);
static int nd6_resolve_slow(struct ifnet *, struct mbuf *,
@@ -483,7 +484,7 @@ skip1:
/*
* ND6 timer routine to handle ND6 entries
*/
-void
+static void
nd6_llinfo_settimer_locked(struct llentry *ln, long tick)
{
int canceled;
@@ -512,12 +513,13 @@ nd6_llinfo_settimer_locked(struct llentry *ln, long tick)
}
/*
-* Gets source address of the first packet in hold queue
-* and stores it in @src.
-* Returns pointer to @src (if hold queue is not empty) or NULL.
-*
-*/
-static struct in6_addr *
+ * Gets source address of the first packet in hold queue
+ * and stores it in @src.
+ * Returns pointer to @src (if hold queue is not empty) or NULL.
+ *
+ * Set noinline to be dtrace-friendly
+ */
+static __noinline struct in6_addr *
nd6_llinfo_get_holdsrc(struct llentry *ln, struct in6_addr *src)
{
struct ip6_hdr hdr;
@@ -530,7 +532,7 @@ nd6_llinfo_get_holdsrc(struct llentry *ln, struct in6_addr *src)
* assume every packet in la_hold has the same IP header
*/
m = ln->la_hold;
- if (sizeof(hdr) < m->m_len)
+ if (sizeof(hdr) > m->m_len)
return (NULL);
m_copydata(m, 0, sizeof(hdr), (caddr_t)&hdr);
@@ -541,8 +543,10 @@ nd6_llinfo_get_holdsrc(struct llentry *ln, struct in6_addr *src)
/*
* Switch @lle state to new state optionally arming timers.
+ *
+ * Set noinline to be dtrace-friendly
*/
-void
+__noinline void
nd6_llinfo_setstate(struct llentry *lle, int newstate)
{
struct ifnet *ifp;
@@ -576,17 +580,12 @@ nd6_llinfo_setstate(struct llentry *lle, int newstate)
lle->ln_state = newstate;
}
-
-void
-nd6_llinfo_settimer(struct llentry *ln, long tick)
-{
-
- LLE_WLOCK(ln);
- nd6_llinfo_settimer_locked(ln, tick);
- LLE_WUNLOCK(ln);
-}
-
-static void
+/*
+ * Timer-dependent part of nd state machine.
+ *
+ * Set noinline to be dtrace-friendly
+ */
+static __noinline void
nd6_llinfo_timer(void *arg)
{
struct llentry *ln;
@@ -810,8 +809,31 @@ nd6_timer(void *arg)
goto addrloop;
}
}
+ } else if ((ia6->ia6_flags & IN6_IFF_TENTATIVE) != 0) {
+ /*
+ * Schedule DAD for a tentative address. This happens
+ * if the interface was down or not running
+ * when the address was configured.
+ */
+ int delay;
+
+ delay = arc4random() %
+ (MAX_RTR_SOLICITATION_DELAY * hz);
+ nd6_dad_start((struct ifaddr *)ia6, delay);
} else {
/*
+ * Check status of the interface. If it is down,
+ * mark the address as tentative for future DAD.
+ */
+ if ((ia6->ia_ifp->if_flags & IFF_UP) == 0 ||
+ (ia6->ia_ifp->if_drv_flags & IFF_DRV_RUNNING)
+ == 0 ||
+ (ND_IFINFO(ia6->ia_ifp)->flags &
+ ND6_IFF_IFDISABLED) != 0) {
+ ia6->ia6_flags &= ~IN6_IFF_DUPLICATED;
+ ia6->ia6_flags |= IN6_IFF_TENTATIVE;
+ }
+ /*
* A new RA might have made a deprecated address
* preferred.
*/
@@ -1156,8 +1178,10 @@ nd6_is_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
* 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.
+ *
+ * Set noinline to be dtrace-friendly
*/
-static void
+static __noinline void
nd6_free(struct llentry *ln, int gc)
{
struct nd_defrouter *dr;
@@ -1452,7 +1476,8 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp)
/* Mark all IPv6 address as tentative. */
ND_IFINFO(ifp)->flags |= ND6_IFF_IFDISABLED;
- if ((ND_IFINFO(ifp)->flags & ND6_IFF_NO_DAD) == 0) {
+ if (V_ip6_dad_count > 0 &&
+ (ND_IFINFO(ifp)->flags & ND6_IFF_NO_DAD) == 0) {
IF_ADDR_RLOCK(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrhead,
ifa_link) {
@@ -1725,9 +1750,12 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr,
lltable_link_entry(LLTABLE6(ifp), ln);
IF_AFDATA_WUNLOCK(ifp);
if (ln_tmp == NULL) {
- /* No existing lle, mark as new entry */
+ /* No existing lle, mark as new entry (6,7) */
is_newentry = 1;
nd6_llinfo_setstate(ln, ND6_LLINFO_STALE);
+ if (lladdr != NULL) /* (7) */
+ EVENTHANDLER_INVOKE(lle_event, ln,
+ LLENTRY_RESOLVED);
} else {
lltable_free_entry(LLTABLE6(ifp), ln);
ln = ln_tmp;
@@ -1764,10 +1792,9 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr,
*/
do_update = 0;
- if (!is_newentry && llchange != 0)
+ if (is_newentry == 0 && llchange != 0) {
do_update = 1; /* (3,5) */
- if (lladdr) { /* (3-5) and (7) */
/*
* Record source link-layer address
* XXX is it dependent to ifp->if_type?
@@ -1778,10 +1805,8 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr,
EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED);
- if (do_update) {
- if (ln->la_hold != NULL)
- nd6_grab_holdchain(ln, &chain, &sin6);
- }
+ if (ln->la_hold != NULL)
+ nd6_grab_holdchain(ln, &chain, &sin6);
}
/* Calculates new router status */
@@ -2011,8 +2036,10 @@ nd6_resolve(struct ifnet *ifp, int is_gw, struct mbuf *m,
* Heavy version.
* Function assume that destination LLE does not exist,
* is invalid or stale, so LLE_EXCLUSIVE lock needs to be acquired.
+ *
+ * Set noinline to be dtrace-friendly
*/
-static int
+static __noinline int
nd6_resolve_slow(struct ifnet *ifp, struct mbuf *m,
const struct sockaddr_in6 *dst, u_char *desten, uint32_t *pflags)
{
diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h
index c5f0eea..4c39a09 100644
--- a/sys/netinet6/nd6.h
+++ b/sys/netinet6/nd6.h
@@ -408,8 +408,6 @@ struct llentry *nd6_lookup(const struct in6_addr *, int, struct ifnet *);
struct llentry *nd6_alloc(const struct in6_addr *, int, struct ifnet *);
void nd6_setmtu(struct ifnet *);
void nd6_llinfo_setstate(struct llentry *lle, int newstate);
-void nd6_llinfo_settimer(struct llentry *, long);
-void nd6_llinfo_settimer_locked(struct llentry *, long);
void nd6_timer(void *);
void nd6_purge(struct ifnet *);
int nd6_resolve(struct ifnet *, int, struct mbuf *,
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
index 7c3c87c..199dbee 100644
--- a/sys/netinet6/nd6_nbr.c
+++ b/sys/netinet6/nd6_nbr.c
@@ -85,11 +85,11 @@ static struct dadq *nd6_dad_find(struct ifaddr *, struct nd_opt_nonce *);
static void nd6_dad_add(struct dadq *dp);
static void nd6_dad_del(struct dadq *dp);
static void nd6_dad_rele(struct dadq *);
-static void nd6_dad_starttimer(struct dadq *, int);
+static void nd6_dad_starttimer(struct dadq *, int, int);
static void nd6_dad_stoptimer(struct dadq *);
static void nd6_dad_timer(struct dadq *);
static void nd6_dad_duplicated(struct ifaddr *, struct dadq *);
-static void nd6_dad_ns_output(struct dadq *, struct ifaddr *);
+static void nd6_dad_ns_output(struct dadq *);
static void nd6_dad_ns_input(struct ifaddr *, struct nd_opt_nonce *);
static void nd6_dad_na_input(struct ifaddr *);
static void nd6_na_output_fib(struct ifnet *, const struct in6_addr *,
@@ -1199,9 +1199,11 @@ nd6_dad_find(struct ifaddr *ifa, struct nd_opt_nonce *n)
}
static void
-nd6_dad_starttimer(struct dadq *dp, int ticks)
+nd6_dad_starttimer(struct dadq *dp, int ticks, int send_ns)
{
+ if (send_ns != 0)
+ nd6_dad_ns_output(dp);
callout_reset(&dp->dad_timer_ch, ticks,
(void (*)(void *))nd6_dad_timer, (void *)dp);
}
@@ -1240,6 +1242,7 @@ nd6_dad_start(struct ifaddr *ifa, int delay)
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
struct dadq *dp;
char ip6buf[INET6_ADDRSTRLEN];
+ int send_ns;
/*
* If we don't need DAD, don't do it.
@@ -1276,8 +1279,10 @@ nd6_dad_start(struct ifaddr *ifa, int delay)
return;
}
if ((dp = nd6_dad_find(ifa, NULL)) != NULL) {
- /* DAD already in progress */
- nd6_dad_rele(dp);
+ /*
+ * DAD already in progress. Let the existing entry
+ * to finish it.
+ */
return;
}
@@ -1310,13 +1315,12 @@ nd6_dad_start(struct ifaddr *ifa, int delay)
dp->dad_ns_lcount = dp->dad_loopbackprobe = 0;
refcount_init(&dp->dad_refcnt, 1);
nd6_dad_add(dp);
+ send_ns = 0;
if (delay == 0) {
- nd6_dad_ns_output(dp, ifa);
- nd6_dad_starttimer(dp,
- (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
- } else {
- nd6_dad_starttimer(dp, delay);
+ send_ns = 1;
+ delay = (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000;
}
+ nd6_dad_starttimer(dp, delay, send_ns);
}
/*
@@ -1386,7 +1390,8 @@ nd6_dad_timer(struct dadq *dp)
if ((dp->dad_ns_tcount > V_dad_maxtry) &&
(((ifp->if_flags & IFF_UP) == 0) ||
((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0))) {
- nd6log((LOG_INFO, "%s: could not run DAD, driver problem?\n",
+ nd6log((LOG_INFO, "%s: could not run DAD "
+ "because the interface was down or not running.\n",
if_name(ifa->ifa_ifp)));
goto err;
}
@@ -1396,9 +1401,8 @@ nd6_dad_timer(struct dadq *dp)
/*
* We have more NS to go. Send NS packet for DAD.
*/
- nd6_dad_ns_output(dp, ifa);
nd6_dad_starttimer(dp,
- (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
+ (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000, 1);
goto done;
} else {
/*
@@ -1426,11 +1430,11 @@ nd6_dad_timer(struct dadq *dp)
* Send an NS immediately and increase dad_count by
* V_nd6_mmaxtries - 1.
*/
- nd6_dad_ns_output(dp, ifa);
dp->dad_count =
dp->dad_ns_ocount + V_nd6_mmaxtries - 1;
nd6_dad_starttimer(dp,
- (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
+ (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000,
+ 1);
goto done;
} else {
/*
@@ -1517,10 +1521,10 @@ nd6_dad_duplicated(struct ifaddr *ifa, struct dadq *dp)
}
static void
-nd6_dad_ns_output(struct dadq *dp, struct ifaddr *ifa)
+nd6_dad_ns_output(struct dadq *dp)
{
- struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
- struct ifnet *ifp = ifa->ifa_ifp;
+ struct in6_ifaddr *ia = (struct in6_ifaddr *)dp->dad_ifa;
+ struct ifnet *ifp = dp->dad_ifa->ifa_ifp;
int i;
dp->dad_ns_tcount++;
OpenPOWER on IntegriCloud