svn commit: r261847 - head/sys/netinet6
Alexander V. Chernikov
melifaro at FreeBSD.org
Thu Feb 13 19:09:04 UTC 2014
Author: melifaro
Date: Thu Feb 13 19:09:04 2014
New Revision: 261847
URL: http://svnweb.freebsd.org/changeset/base/261847
Log:
Further simplify nd6_output_lle.
Currently we have 3 usage patterns:
1) nd6_output (most traffic flow, no lle supplied, lle RLOCK sufficient)
2) corner cases for output (no lle, STALE lle, so on). lle WLOCK needed.
3) nd* iunternal machinery (WLOCK'ed lle provided, perform packet queing).
We separate case 1 and implement it inside its only customer - nd6_output.
This leads to some code duplication (especialy SEND stuff, which should be
hooked to output in a different way), but simplifies locking and control
flow logic fir nd6_output_lle.
Reviewed by: ae
MFC after: 3 weeks
Sponsored by: Yandex LLC
Modified:
head/sys/netinet6/nd6.c
Modified: head/sys/netinet6/nd6.c
==============================================================================
--- head/sys/netinet6/nd6.c Thu Feb 13 18:51:37 2014 (r261846)
+++ head/sys/netinet6/nd6.c Thu Feb 13 19:09:04 2014 (r261847)
@@ -1832,34 +1832,114 @@ nd6_slowtimo(void *arg)
CURVNET_RESTORE();
}
+/*
+ * IPv6 packet output - light version.
+ * Checks if destination LLE exists and is in proper state
+ * (e.g no modification required). If not true, fall back to
+ * "heavy" version.
+ */
int
-nd6_output(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m0,
+nd6_output(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m,
struct sockaddr_in6 *dst, struct rtentry *rt0)
{
+ struct llentry *ln = NULL;
+ int error = 0;
+
+ /* discard the packet if IPv6 operation is disabled on the interface */
+ if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)) {
+ m_freem(m);
+ return (ENETDOWN); /* better error? */
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
+ goto sendpkt;
+
+ if (nd6_need_cache(ifp) == 0)
+ goto sendpkt;
+
+ IF_AFDATA_RLOCK(ifp);
+ ln = nd6_lookup(&dst->sin6_addr, 0, ifp);
+ IF_AFDATA_RUNLOCK(ifp);
+
+ /*
+ * Perform fast path for the following cases:
+ * 1) lle state is REACHABLE
+ * 2) lle state is DELAY (NS message sentNS message sent)
+ *
+ * Every other case involves lle modification, so we handle
+ * them separately.
+ */
+ if (ln == NULL || (ln->ln_state != ND6_LLINFO_REACHABLE &&
+ ln->ln_state != ND6_LLINFO_DELAY)) {
+ /* Fall back to slow processing path */
+ if (ln != NULL)
+ LLE_RUNLOCK(ln);
+ return (nd6_output_lle(ifp, origifp, m, dst, rt0, NULL, NULL));
+ }
+
+sendpkt:
+ if (ln != NULL)
+ LLE_RUNLOCK(ln);
+
+#ifdef MAC
+ mac_netinet6_nd6_send(ifp, m);
+#endif
+
+ /*
+ * If called from nd6_ns_output() (NS), nd6_na_output() (NA),
+ * icmp6_redirect_output() (REDIRECT) or from rip6_output() (RS, RA
+ * as handled by rtsol and rtadvd), mbufs will be tagged for SeND
+ * to be diverted to user space. When re-injected into the kernel,
+ * send_output() will directly dispatch them to the outgoing interface.
+ */
+ if (send_sendso_input_hook != NULL) {
+ struct m_tag *mtag;
+ struct ip6_hdr *ip6;
+ int ip6len;
+ mtag = m_tag_find(m, PACKET_TAG_ND_OUTGOING, NULL);
+ if (mtag != NULL) {
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6len = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen);
+ /* Use the SEND socket */
+ error = send_sendso_input_hook(m, ifp, SND_OUT,
+ ip6len);
+ /* -1 == no app on SEND socket */
+ if (error == 0 || error != -1)
+ return (error);
+ }
+ }
+
+ m_clrprotoflags(m); /* Avoid confusing lower layers. */
+ IP_PROBE(send, NULL, NULL, mtod(m, struct ip6_hdr *), ifp, NULL,
+ mtod(m, struct ip6_hdr *));
- return (nd6_output_lle(ifp, origifp, m0, dst, rt0, NULL, NULL));
+ if ((ifp->if_flags & IFF_LOOPBACK) == 0)
+ origifp = ifp;
+
+ error = (*ifp->if_output)(origifp, m, (struct sockaddr *)dst, NULL);
+ return (error);
}
/*
- * Note that I'm not enforcing any global serialization
- * lle state or asked changes here as the logic is too
- * complicated to avoid having to always acquire an exclusive
- * lock
- * KMM
+ * Output IPv6 packet - heavy version.
+ * Function assume that either
+ * 1) destination LLE does not exist, is invalid or stale, so
+ * ND6_EXCLUSIVE lock needs to be acquired
+ * 2) destination lle is provided (with ND6_EXCLUSIVE lock),
+ * in that case packets are queued in &chain.
*
*/
int
-nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m0,
+nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m,
struct sockaddr_in6 *dst, struct rtentry *rt0, struct llentry *lle,
struct mbuf **chain)
{
- struct mbuf *m = m0;
struct m_tag *mtag;
- struct llentry *ln = lle;
struct ip6_hdr *ip6;
int error = 0;
int flags = 0;
+ int has_lle = 0;
int ip6len;
#ifdef INVARIANTS
@@ -1877,6 +1957,9 @@ nd6_output_lle(struct ifnet *ifp, struct
return (ENETDOWN); /* better error? */
}
+ if (lle != NULL)
+ has_lle = 1;
+
if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
goto sendpkt;
@@ -1884,23 +1967,16 @@ nd6_output_lle(struct ifnet *ifp, struct
goto sendpkt;
/*
- * next hop determination. This routine is derived from ether_output.
- */
-
- /*
* Address resolution or Neighbor Unreachability Detection
* for the next hop.
* At this point, the destination of the packet must be a unicast
* or an anycast address(i.e. not a multicast).
*/
-
- flags = (lle != NULL) ? LLE_EXCLUSIVE : 0;
- if (ln == NULL) {
- retry:
+ if (lle == NULL) {
IF_AFDATA_RLOCK(ifp);
- ln = lla_lookup(LLTABLE6(ifp), flags, (struct sockaddr *)dst);
+ lle = nd6_lookup(&dst->sin6_addr, ND6_EXCLUSIVE, ifp);
IF_AFDATA_RUNLOCK(ifp);
- if ((ln == NULL) && nd6_is_addr_neighbor(dst, ifp)) {
+ if ((lle == NULL) && nd6_is_addr_neighbor(dst, ifp)) {
/*
* Since nd6_is_addr_neighbor() internally calls nd6_lookup(),
* the condition below is not very efficient. But we believe
@@ -1908,34 +1984,31 @@ nd6_output_lle(struct ifnet *ifp, struct
*/
flags = ND6_CREATE | ND6_EXCLUSIVE;
IF_AFDATA_LOCK(ifp);
- ln = nd6_lookup(&dst->sin6_addr, flags, ifp);
+ lle = nd6_lookup(&dst->sin6_addr, flags, ifp);
IF_AFDATA_UNLOCK(ifp);
}
}
- if (ln == NULL) {
+ if (lle == NULL) {
if ((ifp->if_flags & IFF_POINTOPOINT) == 0 &&
!(ND_IFINFO(ifp)->flags & ND6_IFF_PERFORMNUD)) {
char ip6buf[INET6_ADDRSTRLEN];
log(LOG_DEBUG,
"nd6_output: can't allocate llinfo for %s "
"(ln=%p)\n",
- ip6_sprintf(ip6buf, &dst->sin6_addr), ln);
+ ip6_sprintf(ip6buf, &dst->sin6_addr), lle);
m_freem(m);
return (ENOBUFS);
}
goto sendpkt; /* send anyway */
}
+ LLE_WLOCK_ASSERT(lle);
+
/* We don't have to do link-layer address resolution on a p2p link. */
if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
- ln->ln_state < ND6_LLINFO_REACHABLE) {
- if ((flags & LLE_EXCLUSIVE) == 0) {
- flags |= LLE_EXCLUSIVE;
- LLE_RUNLOCK(ln);
- goto retry;
- }
- ln->ln_state = ND6_LLINFO_STALE;
- nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz);
+ lle->ln_state < ND6_LLINFO_REACHABLE) {
+ lle->ln_state = ND6_LLINFO_STALE;
+ nd6_llinfo_settimer_locked(lle, (long)V_nd6_gctimer * hz);
}
/*
@@ -1945,15 +2018,10 @@ nd6_output_lle(struct ifnet *ifp, struct
* neighbor unreachability detection on expiration.
* (RFC 2461 7.3.3)
*/
- if (ln->ln_state == ND6_LLINFO_STALE) {
- if ((flags & LLE_EXCLUSIVE) == 0) {
- flags |= LLE_EXCLUSIVE;
- LLE_RUNLOCK(ln);
- goto retry;
- }
- ln->la_asked = 0;
- ln->ln_state = ND6_LLINFO_DELAY;
- nd6_llinfo_settimer_locked(ln, (long)V_nd6_delay * hz);
+ if (lle->ln_state == ND6_LLINFO_STALE) {
+ lle->la_asked = 0;
+ lle->ln_state = ND6_LLINFO_DELAY;
+ nd6_llinfo_settimer_locked(lle, (long)V_nd6_delay * hz);
}
/*
@@ -1961,7 +2029,7 @@ nd6_output_lle(struct ifnet *ifp, struct
* (i.e. its link-layer address is already resolved), just
* send the packet.
*/
- if (ln->ln_state > ND6_LLINFO_INCOMPLETE)
+ if (lle->ln_state > ND6_LLINFO_INCOMPLETE)
goto sendpkt;
/*
@@ -1971,23 +2039,15 @@ nd6_output_lle(struct ifnet *ifp, struct
* 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 (lle->ln_state == ND6_LLINFO_NOSTATE)
+ lle->ln_state = ND6_LLINFO_INCOMPLETE;
- if ((flags & LLE_EXCLUSIVE) == 0) {
- flags |= LLE_EXCLUSIVE;
- LLE_RUNLOCK(ln);
- goto retry;
- }
-
- LLE_WLOCK_ASSERT(ln);
-
- if (ln->la_hold) {
+ if (lle->la_hold != NULL) {
struct mbuf *m_hold;
int i;
i = 0;
- for (m_hold = ln->la_hold; m_hold; m_hold = m_hold->m_nextpkt) {
+ for (m_hold = lle->la_hold; m_hold; m_hold = m_hold->m_nextpkt){
i++;
if (m_hold->m_nextpkt == NULL) {
m_hold->m_nextpkt = m;
@@ -1995,35 +2055,34 @@ nd6_output_lle(struct ifnet *ifp, struct
}
}
while (i >= V_nd6_maxqueuelen) {
- m_hold = ln->la_hold;
- ln->la_hold = ln->la_hold->m_nextpkt;
+ m_hold = lle->la_hold;
+ lle->la_hold = lle->la_hold->m_nextpkt;
m_freem(m_hold);
i--;
}
} else {
- ln->la_hold = m;
+ lle->la_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->la_asked == 0) {
- ln->la_asked++;
+ if (!ND6_LLINFO_PERMANENT(lle) && lle->la_asked == 0) {
+ lle->la_asked++;
- nd6_llinfo_settimer_locked(ln,
+ nd6_llinfo_settimer_locked(lle,
(long)ND_IFINFO(ifp)->retrans * hz / 1000);
- LLE_WUNLOCK(ln);
- nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
- if (lle != NULL && ln == lle)
+ LLE_WUNLOCK(lle);
+ nd6_ns_output(ifp, NULL, &dst->sin6_addr, lle, 0);
+ if (has_lle != 0)
LLE_WLOCK(lle);
-
- } else if (lle == NULL || ln != lle) {
+ } else if (has_lle == 0) {
/*
* We did the lookup (no lle arg) so we
* need to do the unlock here.
*/
- LLE_WUNLOCK(ln);
+ LLE_WUNLOCK(lle);
}
return (0);
@@ -2033,12 +2092,8 @@ nd6_output_lle(struct ifnet *ifp, struct
* ln is valid and the caller did not pass in
* an llentry
*/
- if ((ln != NULL) && (lle == NULL)) {
- if (flags & LLE_EXCLUSIVE)
- LLE_WUNLOCK(ln);
- else
- LLE_RUNLOCK(ln);
- }
+ if (lle != NULL && has_lle == 0)
+ LLE_WUNLOCK(lle);
#ifdef MAC
mac_netinet6_nd6_send(ifp, m);
@@ -2072,7 +2127,7 @@ nd6_output_lle(struct ifnet *ifp, struct
* a list of mbufs to send and transmit them in the caller
* after the lock is dropped
*/
- if (lle != NULL) {
+ if (has_lle != 0) {
if (*chain == NULL)
*chain = m;
else {
More information about the svn-src-head
mailing list