aboutsummaryrefslogtreecommitdiff
path: root/sys/netinet/udp_usrreq.c
diff options
context:
space:
mode:
authorRandall Stewart <rrs@FreeBSD.org>2015-07-21 09:54:31 +0000
committerRandall Stewart <rrs@FreeBSD.org>2015-07-21 09:54:31 +0000
commitc0d1be08f6dd7c7ccd8de396e43d7b03ac9c36df (patch)
tree6473dd1e338f98dc3ceee66dfc25a37d798fd7a1 /sys/netinet/udp_usrreq.c
parent71b282bb925aa41696c6da1684dff6539c25f2b2 (diff)
downloadsrc-c0d1be08f6dd7c7ccd8de396e43d7b03ac9c36df.tar.gz
src-c0d1be08f6dd7c7ccd8de396e43d7b03ac9c36df.zip
When a tunneling protocol is being used with UDP we must release the
lock on the INP before calling the tunnel protocol, else a LOR may occur (it does with SCTP for sure). Instead we must acquire a ref count and release the lock, taking care to allow for the case where the UDP socket has gone away and *not* unlocking since the refcnt decrement on the inp will do the unlock in that case. Reviewed by: tuexen MFC after: 3 weeks
Notes
Notes: svn path=/head/; revision=285740
Diffstat (limited to 'sys/netinet/udp_usrreq.c')
-rw-r--r--sys/netinet/udp_usrreq.c38
1 files changed, 27 insertions, 11 deletions
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index d13821fbb5c3..415fc8c30a6e 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -293,8 +293,17 @@ udplite_destroy(void)
* contains the source address. If the socket ends up being an IPv6 socket,
* udp_append() will convert to a sockaddr_in6 before passing the address
* into the socket code.
+ *
+ * In the normal case udp_append() will return 0, indicating that you
+ * must unlock the inp. However if a tunneling protocol is in place we increment
+ * the inpcb refcnt and unlock the inp, on return from the tunneling protocol we
+ * then decrement the reference count. If the inp_rele returns 1, indicating the
+ * inp is gone, we return that to the caller to tell them *not* to unlock
+ * the inp. In the case of multi-cast this will cause the distribution
+ * to stop (though most tunneling protocols known currently do *not* use
+ * multicast).
*/
-static void
+static int
udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off,
struct sockaddr_in *udp_in)
{
@@ -313,9 +322,12 @@ udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off,
*/
up = intoudpcb(inp);
if (up->u_tun_func != NULL) {
+ in_pcbref(inp);
+ INP_RUNLOCK(inp);
(*up->u_tun_func)(n, off, inp, (struct sockaddr *)udp_in,
up->u_tun_ctx);
- return;
+ INP_RLOCK(inp);
+ return (in_pcbrele_rlocked(inp));
}
off += sizeof(struct udphdr);
@@ -324,7 +336,7 @@ udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off,
/* Check AH/ESP integrity. */
if (ipsec4_in_reject(n, inp)) {
m_freem(n);
- return;
+ return (0);
}
#ifdef IPSEC_NAT_T
up = intoudpcb(inp);
@@ -332,14 +344,14 @@ udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off,
if (up->u_flags & UF_ESPINUDP_ALL) { /* IPSec UDP encaps. */
n = udp4_espdecap(inp, n, off);
if (n == NULL) /* Consumed. */
- return;
+ return (0);
}
#endif /* IPSEC_NAT_T */
#endif /* IPSEC */
#ifdef MAC
if (mac_inpcb_check_deliver(inp, n) != 0) {
m_freem(n);
- return;
+ return (0);
}
#endif /* MAC */
if (inp->inp_flags & INP_CONTROLOPTS ||
@@ -373,6 +385,7 @@ udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off,
UDPSTAT_INC(udps_fullsock);
} else
sorwakeup_locked(so);
+ return (0);
}
int
@@ -579,8 +592,10 @@ udp_input(struct mbuf **mp, int *offp, int proto)
if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
UDP_PROBE(receive, NULL, last, ip,
last, uh);
- udp_append(last, ip, n, iphlen,
- &udp_in);
+ if (udp_append(last, ip, n, iphlen,
+ &udp_in)) {
+ goto inp_lost;
+ }
}
INP_RUNLOCK(last);
}
@@ -611,8 +626,9 @@ udp_input(struct mbuf **mp, int *offp, int proto)
goto badunlocked;
}
UDP_PROBE(receive, NULL, last, ip, last, uh);
- udp_append(last, ip, m, iphlen, &udp_in);
- INP_RUNLOCK(last);
+ if (udp_append(last, ip, m, iphlen, &udp_in) == 0)
+ INP_RUNLOCK(last);
+ inp_lost:
INP_INFO_RUNLOCK(pcbinfo);
return (IPPROTO_DONE);
}
@@ -700,8 +716,8 @@ udp_input(struct mbuf **mp, int *offp, int proto)
}
UDP_PROBE(receive, NULL, inp, ip, inp, uh);
- udp_append(inp, ip, m, iphlen, &udp_in);
- INP_RUNLOCK(inp);
+ if (udp_append(inp, ip, m, iphlen, &udp_in) == 0)
+ INP_RUNLOCK(inp);
return (IPPROTO_DONE);
badunlocked: