diff options
Diffstat (limited to 'freebsd/sys/netinet/tcp_usrreq.c')
-rw-r--r-- | freebsd/sys/netinet/tcp_usrreq.c | 106 |
1 files changed, 80 insertions, 26 deletions
diff --git a/freebsd/sys/netinet/tcp_usrreq.c b/freebsd/sys/netinet/tcp_usrreq.c index 809ea35d..eab13eeb 100644 --- a/freebsd/sys/netinet/tcp_usrreq.c +++ b/freebsd/sys/netinet/tcp_usrreq.c @@ -346,23 +346,25 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td) int error = 0; struct inpcb *inp; struct tcpcb *tp = NULL; - struct sockaddr_in6 *sin6p; + struct sockaddr_in6 *sin6; + u_char vflagsav; - sin6p = (struct sockaddr_in6 *)nam; - if (nam->sa_len != sizeof (*sin6p)) + sin6 = (struct sockaddr_in6 *)nam; + if (nam->sa_len != sizeof (*sin6)) return (EINVAL); /* * Must check for multicast addresses and disallow binding * to them. */ - if (sin6p->sin6_family == AF_INET6 && - IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) + if (sin6->sin6_family == AF_INET6 && + IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) return (EAFNOSUPPORT); TCPDEBUG0; inp = sotoinpcb(so); KASSERT(inp != NULL, ("tcp6_usr_bind: inp == NULL")); INP_WLOCK(inp); + vflagsav = inp->inp_vflag; if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { error = EINVAL; goto out; @@ -374,12 +376,12 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td) inp->inp_vflag |= INP_IPV6; #ifdef INET if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { - if (IN6_IS_ADDR_UNSPECIFIED(&sin6p->sin6_addr)) + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) inp->inp_vflag |= INP_IPV4; - else if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { + else if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { struct sockaddr_in sin; - in6_sin6_2_sin(&sin, sin6p); + in6_sin6_2_sin(&sin, sin6); if (IN_MULTICAST(ntohl(sin.sin_addr.s_addr))) { error = EAFNOSUPPORT; INP_HASH_WUNLOCK(&V_tcbinfo); @@ -397,6 +399,8 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td) error = in6_pcbbind(inp, nam, td->td_ucred); INP_HASH_WUNLOCK(&V_tcbinfo); out: + if (error != 0) + inp->inp_vflag = vflagsav; TCPDEBUG2(PRU_BIND); TCP_PROBE2(debug__user, tp, PRU_BIND); INP_WUNLOCK(inp); @@ -459,6 +463,7 @@ tcp6_usr_listen(struct socket *so, int backlog, struct thread *td) int error = 0; struct inpcb *inp; struct tcpcb *tp = NULL; + u_char vflagsav; TCPDEBUG0; inp = sotoinpcb(so); @@ -468,6 +473,7 @@ tcp6_usr_listen(struct socket *so, int backlog, struct thread *td) error = EINVAL; goto out; } + vflagsav = inp->inp_vflag; tp = intotcpcb(inp); TCPDEBUG1(); SOCK_LOCK(so); @@ -493,6 +499,9 @@ tcp6_usr_listen(struct socket *so, int backlog, struct thread *td) if (IS_FASTOPEN(tp->t_flags)) tp->t_tfo_pending = tcp_fastopen_alloc_counter(); + if (error != 0) + inp->inp_vflag = vflagsav; + out: TCPDEBUG2(PRU_LISTEN); TCP_PROBE2(debug__user, tp, PRU_LISTEN); @@ -568,23 +577,27 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) int error = 0; struct inpcb *inp; struct tcpcb *tp = NULL; - struct sockaddr_in6 *sin6p; + struct sockaddr_in6 *sin6; + u_int8_t incflagsav; + u_char vflagsav; TCPDEBUG0; - sin6p = (struct sockaddr_in6 *)nam; - if (nam->sa_len != sizeof (*sin6p)) + sin6 = (struct sockaddr_in6 *)nam; + if (nam->sa_len != sizeof (*sin6)) return (EINVAL); /* * Must disallow TCP ``connections'' to multicast addresses. */ - if (sin6p->sin6_family == AF_INET6 - && IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) + if (sin6->sin6_family == AF_INET6 + && IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) return (EAFNOSUPPORT); inp = sotoinpcb(so); KASSERT(inp != NULL, ("tcp6_usr_connect: inp == NULL")); INP_WLOCK(inp); + vflagsav = inp->inp_vflag; + incflagsav = inp->inp_inc.inc_flags; if (inp->inp_flags & INP_TIMEWAIT) { error = EADDRINUSE; goto out; @@ -601,7 +614,7 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) * therefore probably require the hash lock, which isn't held here. * Is this a significant problem? */ - if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { struct sockaddr_in sin; if ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0) { @@ -613,16 +626,16 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) goto out; } - in6_sin6_2_sin(&sin, sin6p); + in6_sin6_2_sin(&sin, sin6); if (IN_MULTICAST(ntohl(sin.sin_addr.s_addr))) { error = EAFNOSUPPORT; goto out; } - inp->inp_vflag |= INP_IPV4; - inp->inp_vflag &= ~INP_IPV6; if ((error = prison_remote_ip4(td->td_ucred, &sin.sin_addr)) != 0) goto out; + inp->inp_vflag |= INP_IPV4; + inp->inp_vflag &= ~INP_IPV6; if ((error = tcp_connect(tp, (struct sockaddr *)&sin, td)) != 0) goto out; #ifdef TCP_OFFLOAD @@ -640,11 +653,11 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) } } #endif + if ((error = prison_remote_ip6(td->td_ucred, &sin6->sin6_addr)) != 0) + goto out; inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; inp->inp_inc.inc_flags |= INC_ISIPV6; - if ((error = prison_remote_ip6(td->td_ucred, &sin6p->sin6_addr)) != 0) - goto out; if ((error = tcp6_connect(tp, nam, td)) != 0) goto out; #ifdef TCP_OFFLOAD @@ -657,6 +670,15 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) error = tp->t_fb->tfb_tcp_output(tp); out: + /* + * If the implicit bind in the connect call fails, restore + * the flags we modified. + */ + if (error != 0 && inp->inp_lport == 0) { + inp->inp_vflag = vflagsav; + inp->inp_inc.inc_flags = incflagsav; + } + TCPDEBUG2(PRU_CONNECT); TCP_PROBE2(debug__user, tp, PRU_CONNECT); INP_WUNLOCK(inp); @@ -912,6 +934,9 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, #ifdef INET6 int isipv6; #endif + u_int8_t incflagsav; + u_char vflagsav; + bool restoreflags; TCPDEBUG0; /* @@ -923,6 +948,9 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, inp = sotoinpcb(so); KASSERT(inp != NULL, ("tcp_usr_send: inp == NULL")); INP_WLOCK(inp); + vflagsav = inp->inp_vflag; + incflagsav = inp->inp_inc.inc_flags; + restoreflags = false; if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { if (control) m_freem(control); @@ -974,22 +1002,22 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, #ifdef INET6 case AF_INET6: { - struct sockaddr_in6 *sin6p; + struct sockaddr_in6 *sin6; - sin6p = (struct sockaddr_in6 *)nam; - if (sin6p->sin6_len != sizeof(struct sockaddr_in6)) { + sin6 = (struct sockaddr_in6 *)nam; + if (sin6->sin6_len != sizeof(*sin6)) { if (m) m_freem(m); error = EINVAL; goto out; } - if (IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) { + if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { if (m) m_freem(m); error = EAFNOSUPPORT; goto out; } - if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { #ifdef INET if ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0) { error = EINVAL; @@ -1003,9 +1031,10 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, m_freem(m); goto out; } + restoreflags = true; inp->inp_vflag &= ~INP_IPV6; sinp = &sin; - in6_sin6_2_sin(sinp, sin6p); + in6_sin6_2_sin(sinp, sin6); if (IN_MULTICAST( ntohl(sinp->sin_addr.s_addr))) { error = EAFNOSUPPORT; @@ -1033,10 +1062,11 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, error = EAFNOSUPPORT; goto out; } + restoreflags = true; inp->inp_vflag &= ~INP_IPV4; inp->inp_inc.inc_flags |= INC_ISIPV6; if ((error = prison_remote_ip6(td->td_ucred, - &sin6p->sin6_addr))) { + &sin6->sin6_addr))) { if (m) m_freem(m); goto out; @@ -1083,6 +1113,14 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, error = tcp_connect(tp, (struct sockaddr *)sinp, td); #endif + /* + * The bind operation in tcp_connect succeeded. We + * no longer want to restore the flags if later + * operations fail. + */ + if (error == 0 || inp->inp_lport != 0) + restoreflags = false; + if (error) goto out; if (IS_FASTOPEN(tp->t_flags)) @@ -1153,6 +1191,14 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, error = tcp_connect(tp, (struct sockaddr *)sinp, td); #endif + /* + * The bind operation in tcp_connect succeeded. We + * no longer want to restore the flags if later + * operations fail. + */ + if (error == 0 || inp->inp_lport != 0) + restoreflags = false; + if (error) goto out; tp->snd_wnd = TTCP_CLIENT_SND_WND; @@ -1171,6 +1217,14 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, TCP_LOG_USERSEND, error, 0, NULL, false); out: + /* + * If the request was unsuccessful and we changed flags, + * restore the original flags. + */ + if (error != 0 && restoreflags) { + inp->inp_vflag = vflagsav; + inp->inp_inc.inc_flags = incflagsav; + } TCPDEBUG2((flags & PRUS_OOB) ? PRU_SENDOOB : ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); TCP_PROBE2(debug__user, tp, (flags & PRUS_OOB) ? PRU_SENDOOB : |