summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet/tcp_usrreq.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netinet/tcp_usrreq.c')
-rw-r--r--freebsd/sys/netinet/tcp_usrreq.c106
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 :