summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrrs <rrs@FreeBSD.org>2015-07-21 09:54:31 +0000
committerrrs <rrs@FreeBSD.org>2015-07-21 09:54:31 +0000
commite4870a15db964e2a684550b8d1eb30c1087792a0 (patch)
tree6473dd1e338f98dc3ceee66dfc25a37d798fd7a1
parent7117e1104e17e73cd1e89f369a3e364a5ee8fabc (diff)
downloadFreeBSD-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.c38
-rw-r--r--sys/netinet6/udp6_usrreq.c24
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:
OpenPOWER on IntegriCloud