diff options
author | rrs <rrs@FreeBSD.org> | 2015-07-21 09:54:31 +0000 |
---|---|---|
committer | rrs <rrs@FreeBSD.org> | 2015-07-21 09:54:31 +0000 |
commit | e4870a15db964e2a684550b8d1eb30c1087792a0 (patch) | |
tree | 6473dd1e338f98dc3ceee66dfc25a37d798fd7a1 | |
parent | 7117e1104e17e73cd1e89f369a3e364a5ee8fabc (diff) | |
download | FreeBSD-src-e4870a15db964e2a684550b8d1eb30c1087792a0.zip FreeBSD-src-e4870a15db964e2a684550b8d1eb30c1087792a0.tar.gz |
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
-rw-r--r-- | sys/netinet/udp_usrreq.c | 38 | ||||
-rw-r--r-- | sys/netinet6/udp6_usrreq.c | 24 |
2 files changed, 42 insertions, 20 deletions
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index d13821f..415fc8c 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: diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c index ab4dc7a..15e5034 100644 --- a/sys/netinet6/udp6_usrreq.c +++ b/sys/netinet6/udp6_usrreq.c @@ -136,7 +136,7 @@ __FBSDID("$FreeBSD$"); extern struct protosw inetsw[]; static void udp6_detach(struct socket *so); -static void +static int udp6_append(struct inpcb *inp, struct mbuf *n, int off, struct sockaddr_in6 *fromsa) { @@ -151,21 +151,24 @@ udp6_append(struct inpcb *inp, 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 *)fromsa, up->u_tun_ctx); - return; + INP_RLOCK(inp); + return (in_pcbrele_rlocked(inp)); } #ifdef IPSEC /* Check AH/ESP integrity. */ if (ipsec6_in_reject(n, inp)) { m_freem(n); - return; + return (0); } #endif /* IPSEC */ #ifdef MAC if (mac_inpcb_check_deliver(inp, n) != 0) { m_freem(n); - return; + return (0); } #endif opts = NULL; @@ -185,6 +188,7 @@ udp6_append(struct inpcb *inp, struct mbuf *n, int off, UDPSTAT_INC(udps_fullsock); } else sorwakeup_locked(so); + return (0); } int @@ -367,7 +371,8 @@ udp6_input(struct mbuf **mp, int *offp, int proto) INP_RLOCK(last); UDP_PROBE(receive, NULL, last, ip6, last, uh); - udp6_append(last, n, off, &fromsa); + if (udp6_append(last, n, off, &fromsa)) + goto inp_lost; INP_RUNLOCK(last); } } @@ -398,8 +403,9 @@ udp6_input(struct mbuf **mp, int *offp, int proto) INP_RLOCK(last); INP_INFO_RUNLOCK(pcbinfo); UDP_PROBE(receive, NULL, last, ip6, last, uh); - udp6_append(last, m, off, &fromsa); - INP_RUNLOCK(last); + if (udp6_append(last, m, off, &fromsa)) + INP_RUNLOCK(last); + inp_lost: return (IPPROTO_DONE); } /* @@ -477,8 +483,8 @@ udp6_input(struct mbuf **mp, int *offp, int proto) } } UDP_PROBE(receive, NULL, inp, ip6, inp, uh); - udp6_append(inp, m, off, &fromsa); - INP_RUNLOCK(inp); + if (udp6_append(inp, m, off, &fromsa) == 0) + INP_RUNLOCK(inp); return (IPPROTO_DONE); badheadlocked: |