summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet6/udp6_usrreq.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netinet6/udp6_usrreq.c')
-rw-r--r--freebsd/sys/netinet6/udp6_usrreq.c58
1 files changed, 31 insertions, 27 deletions
diff --git a/freebsd/sys/netinet6/udp6_usrreq.c b/freebsd/sys/netinet6/udp6_usrreq.c
index 941b3950..2c2559fb 100644
--- a/freebsd/sys/netinet6/udp6_usrreq.c
+++ b/freebsd/sys/netinet6/udp6_usrreq.c
@@ -275,6 +275,13 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
}
/*
+ * Detached PCBs can linger in the list if someone
+ * holds a reference. (e.g. udp_pcblist)
+ */
+ if (inp->inp_socket == NULL)
+ continue;
+
+ /*
* Handle socket delivery policy for any-source
* and source-specific multicast. [RFC3678]
*/
@@ -398,6 +405,15 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
}
INP_RLOCK(inp);
INP_INFO_RUNLOCK(&V_udbinfo);
+
+ /*
+ * Detached PCBs can linger in the hash table if someone holds a
+ * reference. (e.g. udp_pcblist)
+ */
+ if (inp->inp_socket == NULL) {
+ INP_RUNLOCK(inp);
+ goto badunlocked;
+ }
up = intoudpcb(inp);
if (up->u_tun_func == NULL) {
udp6_append(inp, m, off, &fromsa);
@@ -658,8 +674,11 @@ udp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6,
goto release;
}
if (inp->inp_lport == 0 &&
- (error = in6_pcbsetport(laddr, inp, td->td_ucred)) != 0)
+ (error = in6_pcbsetport(laddr, inp, td->td_ucred)) != 0) {
+ /* Undo an address bind that may have occurred. */
+ inp->in6p_laddr = in6addr_any;
goto release;
+ }
} else {
if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
error = ENOTCONN;
@@ -908,43 +927,41 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
INP_INFO_WLOCK(&V_udbinfo);
INP_WLOCK(inp);
- if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 &&
- IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
struct sockaddr_in sin;
+ if ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0) {
+ error = EINVAL;
+ goto out;
+ }
if (inp->inp_faddr.s_addr != INADDR_ANY) {
error = EISCONN;
goto out;
}
in6_sin6_2_sin(&sin, sin6);
+ inp->inp_vflag |= INP_IPV4;
+ inp->inp_vflag &= ~INP_IPV6;
error = prison_remote_ip4(td->td_ucred, &sin.sin_addr);
if (error != 0)
goto out;
error = in_pcbconnect(inp, (struct sockaddr *)&sin,
td->td_ucred);
- if (error == 0) {
- inp->inp_vflag |= INP_IPV4;
- inp->inp_vflag &= ~INP_IPV6;
+ if (error == 0)
soisconnected(so);
- }
goto out;
}
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
error = EISCONN;
goto out;
}
+ inp->inp_vflag &= ~INP_IPV4;
+ inp->inp_vflag |= INP_IPV6;
error = prison_remote_ip6(td->td_ucred, &sin6->sin6_addr);
if (error != 0)
goto out;
error = in6_pcbconnect(inp, nam, td->td_ucred);
- if (error == 0) {
- if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) {
- /* should be non mapped addr */
- inp->inp_vflag &= ~INP_IPV4;
- inp->inp_vflag |= INP_IPV6;
- }
+ if (error == 0)
soisconnected(so);
- }
out:
INP_WUNLOCK(inp);
INP_INFO_WUNLOCK(&V_udbinfo);
@@ -1046,18 +1063,6 @@ udp6_send(struct socket *so, int flags, struct mbuf *m,
if (hasv4addr) {
struct pr_usrreqs *pru;
- if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) &&
- !IN6_IS_ADDR_V4MAPPED(&inp->in6p_laddr)) {
- /*
- * When remote addr is IPv4-mapped address,
- * local addr should not be an IPv6 address;
- * since you cannot determine how to map IPv6
- * source address to IPv4.
- */
- error = EINVAL;
- goto out;
- }
-
/*
* XXXRW: We release UDP-layer locks before calling
* udp_send() in order to avoid recursion. However,
@@ -1081,7 +1086,6 @@ udp6_send(struct socket *so, int flags, struct mbuf *m,
mac_inpcb_create_mbuf(inp, m);
#endif
error = udp6_output(inp, m, addr, control, td);
-out:
INP_WUNLOCK(inp);
INP_INFO_WUNLOCK(&V_udbinfo);
return (error);