diff options
author | Randall Stewart <rrs@FreeBSD.org> | 2015-07-21 09:54:31 +0000 |
---|---|---|
committer | Randall Stewart <rrs@FreeBSD.org> | 2015-07-21 09:54:31 +0000 |
commit | c0d1be08f6dd7c7ccd8de396e43d7b03ac9c36df (patch) | |
tree | 6473dd1e338f98dc3ceee66dfc25a37d798fd7a1 /sys/netinet/udp_usrreq.c | |
parent | 71b282bb925aa41696c6da1684dff6539c25f2b2 (diff) | |
download | src-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.c | 38 |
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: |