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.c238
1 files changed, 209 insertions, 29 deletions
diff --git a/freebsd/sys/netinet/tcp_usrreq.c b/freebsd/sys/netinet/tcp_usrreq.c
index bf2cff4c..617f60d0 100644
--- a/freebsd/sys/netinet/tcp_usrreq.c
+++ b/freebsd/sys/netinet/tcp_usrreq.c
@@ -278,11 +278,12 @@ tcp_usr_detach(struct socket *so)
{
struct inpcb *inp;
int rlock = 0;
+ struct epoch_tracker et;
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("tcp_usr_detach: inp == NULL"));
if (!INP_INFO_WLOCKED(&V_tcbinfo)) {
- INP_INFO_RLOCK(&V_tcbinfo);
+ INP_INFO_RLOCK_ET(&V_tcbinfo, et);
rlock = 1;
}
INP_WLOCK(inp);
@@ -290,7 +291,7 @@ tcp_usr_detach(struct socket *so)
("tcp_usr_detach: inp_socket == NULL"));
tcp_detach(so, inp);
if (rlock)
- INP_INFO_RUNLOCK(&V_tcbinfo);
+ INP_INFO_RUNLOCK_ET(&V_tcbinfo, et);
}
#ifdef INET
@@ -379,6 +380,11 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
struct sockaddr_in sin;
in6_sin6_2_sin(&sin, sin6p);
+ if (IN_MULTICAST(ntohl(sin.sin_addr.s_addr))) {
+ error = EAFNOSUPPORT;
+ INP_HASH_WUNLOCK(&V_tcbinfo);
+ goto out;
+ }
inp->inp_vflag |= INP_IPV4;
inp->inp_vflag &= ~INP_IPV6;
error = in_pcbbind(inp, (struct sockaddr *)&sin,
@@ -608,6 +614,10 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
}
in6_sin6_2_sin(&sin, sin6p);
+ 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,
@@ -670,10 +680,11 @@ tcp_usr_disconnect(struct socket *so)
{
struct inpcb *inp;
struct tcpcb *tp = NULL;
+ struct epoch_tracker et;
int error = 0;
TCPDEBUG0;
- INP_INFO_RLOCK(&V_tcbinfo);
+ INP_INFO_RLOCK_ET(&V_tcbinfo, et);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("tcp_usr_disconnect: inp == NULL"));
INP_WLOCK(inp);
@@ -690,7 +701,7 @@ out:
TCPDEBUG2(PRU_DISCONNECT);
TCP_PROBE2(debug__user, tp, PRU_DISCONNECT);
INP_WUNLOCK(inp);
- INP_INFO_RUNLOCK(&V_tcbinfo);
+ INP_INFO_RUNLOCK_ET(&V_tcbinfo, et);
return (error);
}
@@ -749,6 +760,7 @@ tcp6_usr_accept(struct socket *so, struct sockaddr **nam)
struct tcpcb *tp = NULL;
struct in_addr addr;
struct in6_addr addr6;
+ struct epoch_tracker et;
in_port_t port = 0;
int v4 = 0;
TCPDEBUG0;
@@ -758,7 +770,7 @@ tcp6_usr_accept(struct socket *so, struct sockaddr **nam)
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("tcp6_usr_accept: inp == NULL"));
- INP_INFO_RLOCK(&V_tcbinfo);
+ INP_INFO_RLOCK_ET(&V_tcbinfo, et);
INP_WLOCK(inp);
if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) {
error = ECONNABORTED;
@@ -785,7 +797,7 @@ out:
TCPDEBUG2(PRU_ACCEPT);
TCP_PROBE2(debug__user, tp, PRU_ACCEPT);
INP_WUNLOCK(inp);
- INP_INFO_RUNLOCK(&V_tcbinfo);
+ INP_INFO_RUNLOCK_ET(&V_tcbinfo, et);
if (error == 0) {
if (v4)
*nam = in6_v4mapsin6_sockaddr(port, &addr);
@@ -805,9 +817,10 @@ tcp_usr_shutdown(struct socket *so)
int error = 0;
struct inpcb *inp;
struct tcpcb *tp = NULL;
+ struct epoch_tracker et;
TCPDEBUG0;
- INP_INFO_RLOCK(&V_tcbinfo);
+ INP_INFO_RLOCK_ET(&V_tcbinfo, et);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("inp == NULL"));
INP_WLOCK(inp);
@@ -826,7 +839,7 @@ out:
TCPDEBUG2(PRU_SHUTDOWN);
TCP_PROBE2(debug__user, tp, PRU_SHUTDOWN);
INP_WUNLOCK(inp);
- INP_INFO_RUNLOCK(&V_tcbinfo);
+ INP_INFO_RUNLOCK_ET(&V_tcbinfo, et);
return (error);
}
@@ -889,6 +902,13 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
int error = 0;
struct inpcb *inp;
struct tcpcb *tp = NULL;
+ struct epoch_tracker net_et;
+#ifdef INET
+#ifdef INET6
+ struct sockaddr_in sin;
+#endif
+ struct sockaddr_in *sinp;
+#endif
#ifdef INET6
int isipv6;
#endif
@@ -899,7 +919,7 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
* this call.
*/
if (flags & PRUS_EOF)
- INP_INFO_RLOCK(&V_tcbinfo);
+ INP_INFO_RLOCK_ET(&V_tcbinfo, net_et);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("tcp_usr_send: inp == NULL"));
INP_WLOCK(inp);
@@ -915,11 +935,124 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
error = ECONNRESET;
goto out;
}
-#ifdef INET6
- isipv6 = nam && nam->sa_family == AF_INET6;
-#endif /* INET6 */
tp = intotcpcb(inp);
TCPDEBUG1();
+ if (nam != NULL && tp->t_state < TCPS_SYN_SENT) {
+ switch (nam->sa_family) {
+#ifdef INET
+ case AF_INET:
+ sinp = (struct sockaddr_in *)nam;
+ if (sinp->sin_len != sizeof(struct sockaddr_in)) {
+ if (m)
+ m_freem(m);
+ error = EINVAL;
+ goto out;
+ }
+ if ((inp->inp_vflag & INP_IPV6) != 0) {
+ if (m)
+ m_freem(m);
+ error = EAFNOSUPPORT;
+ goto out;
+ }
+ if (IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) {
+ if (m)
+ m_freem(m);
+ error = EAFNOSUPPORT;
+ goto out;
+ }
+ if ((error = prison_remote_ip4(td->td_ucred,
+ &sinp->sin_addr))) {
+ if (m)
+ m_freem(m);
+ goto out;
+ }
+#ifdef INET6
+ isipv6 = 0;
+#endif
+ break;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *sin6p;
+
+ sin6p = (struct sockaddr_in6 *)nam;
+ if (sin6p->sin6_len != sizeof(struct sockaddr_in6)) {
+ if (m)
+ m_freem(m);
+ error = EINVAL;
+ goto out;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) {
+ if (m)
+ m_freem(m);
+ error = EAFNOSUPPORT;
+ goto out;
+ }
+ if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) {
+#ifdef INET
+ if ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0) {
+ error = EINVAL;
+ if (m)
+ m_freem(m);
+ goto out;
+ }
+ if ((inp->inp_vflag & INP_IPV4) == 0) {
+ error = EAFNOSUPPORT;
+ if (m)
+ m_freem(m);
+ goto out;
+ }
+ inp->inp_vflag &= ~INP_IPV6;
+ sinp = &sin;
+ in6_sin6_2_sin(sinp, sin6p);
+ if (IN_MULTICAST(
+ ntohl(sinp->sin_addr.s_addr))) {
+ error = EAFNOSUPPORT;
+ if (m)
+ m_freem(m);
+ goto out;
+ }
+ if ((error = prison_remote_ip4(td->td_ucred,
+ &sinp->sin_addr))) {
+ if (m)
+ m_freem(m);
+ goto out;
+ }
+ isipv6 = 0;
+#else /* !INET */
+ error = EAFNOSUPPORT;
+ if (m)
+ m_freem(m);
+ goto out;
+#endif /* INET */
+ } else {
+ if ((inp->inp_vflag & INP_IPV6) == 0) {
+ if (m)
+ m_freem(m);
+ error = EAFNOSUPPORT;
+ goto out;
+ }
+ inp->inp_vflag &= ~INP_IPV4;
+ inp->inp_inc.inc_flags |= INC_ISIPV6;
+ if ((error = prison_remote_ip6(td->td_ucred,
+ &sin6p->sin6_addr))) {
+ if (m)
+ m_freem(m);
+ goto out;
+ }
+ isipv6 = 1;
+ }
+ break;
+ }
+#endif /* INET6 */
+ default:
+ if (m)
+ m_freem(m);
+ error = EAFNOSUPPORT;
+ goto out;
+ }
+ }
if (control) {
/* TCP doesn't do control messages (rights, creds, etc) */
if (control->m_len) {
@@ -947,7 +1080,8 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
else
#endif
#ifdef INET
- error = tcp_connect(tp, nam, td);
+ error = tcp_connect(tp,
+ (struct sockaddr *)sinp, td);
#endif
if (error)
goto out;
@@ -1016,7 +1150,8 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
else
#endif
#ifdef INET
- error = tcp_connect(tp, nam, td);
+ error = tcp_connect(tp,
+ (struct sockaddr *)sinp, td);
#endif
if (error)
goto out;
@@ -1042,7 +1177,7 @@ out:
((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND));
INP_WUNLOCK(inp);
if (flags & PRUS_EOF)
- INP_INFO_RUNLOCK(&V_tcbinfo);
+ INP_INFO_RUNLOCK_ET(&V_tcbinfo, net_et);
return (error);
}
@@ -1081,12 +1216,13 @@ tcp_usr_abort(struct socket *so)
{
struct inpcb *inp;
struct tcpcb *tp = NULL;
+ struct epoch_tracker et;
TCPDEBUG0;
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("tcp_usr_abort: inp == NULL"));
- INP_INFO_RLOCK(&V_tcbinfo);
+ INP_INFO_RLOCK_ET(&V_tcbinfo, et);
INP_WLOCK(inp);
KASSERT(inp->inp_socket != NULL,
("tcp_usr_abort: inp_socket == NULL"));
@@ -1112,7 +1248,7 @@ tcp_usr_abort(struct socket *so)
}
INP_WUNLOCK(inp);
dropped:
- INP_INFO_RUNLOCK(&V_tcbinfo);
+ INP_INFO_RUNLOCK_ET(&V_tcbinfo, et);
}
/*
@@ -1123,12 +1259,13 @@ tcp_usr_close(struct socket *so)
{
struct inpcb *inp;
struct tcpcb *tp = NULL;
+ struct epoch_tracker et;
TCPDEBUG0;
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("tcp_usr_close: inp == NULL"));
- INP_INFO_RLOCK(&V_tcbinfo);
+ INP_INFO_RLOCK_ET(&V_tcbinfo, et);
INP_WLOCK(inp);
KASSERT(inp->inp_socket != NULL,
("tcp_usr_close: inp_socket == NULL"));
@@ -1152,7 +1289,7 @@ tcp_usr_close(struct socket *so)
inp->inp_flags |= INP_SOCKREF;
}
INP_WUNLOCK(inp);
- INP_INFO_RUNLOCK(&V_tcbinfo);
+ INP_INFO_RUNLOCK_ET(&V_tcbinfo, et);
}
/*
@@ -1304,7 +1441,9 @@ tcp_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td)
soisconnecting(so);
TCPSTAT_INC(tcps_connattempt);
tcp_state_change(tp, TCPS_SYN_SENT);
- tp->iss = tcp_new_isn(tp);
+ tp->iss = tcp_new_isn(&inp->inp_inc);
+ if (tp->t_flags & TF_REQ_TSTMP)
+ tp->ts_offset = tcp_new_ts_offset(&inp->inp_inc);
tcp_sendseqinit(tp);
return 0;
@@ -1343,7 +1482,9 @@ tcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td)
soisconnecting(inp->inp_socket);
TCPSTAT_INC(tcps_connattempt);
tcp_state_change(tp, TCPS_SYN_SENT);
- tp->iss = tcp_new_isn(tp);
+ tp->iss = tcp_new_isn(&inp->inp_inc);
+ if (tp->t_flags & TF_REQ_TSTMP)
+ tp->ts_offset = tcp_new_ts_offset(&inp->inp_inc);
tcp_sendseqinit(tp);
return 0;
@@ -1445,6 +1586,42 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt)
if (inp->inp_vflag & INP_IPV6PROTO) {
INP_WUNLOCK(inp);
error = ip6_ctloutput(so, sopt);
+ /*
+ * In case of the IPV6_USE_MIN_MTU socket option,
+ * the INC_IPV6MINMTU flag to announce a corresponding
+ * MSS during the initial handshake.
+ * If the TCP connection is not in the front states,
+ * just reduce the MSS being used.
+ * This avoids the sending of TCP segments which will
+ * be fragmented at the IPv6 layer.
+ */
+ if ((error == 0) &&
+ (sopt->sopt_dir == SOPT_SET) &&
+ (sopt->sopt_level == IPPROTO_IPV6) &&
+ (sopt->sopt_name == IPV6_USE_MIN_MTU)) {
+ INP_WLOCK(inp);
+ if ((inp->inp_flags &
+ (INP_TIMEWAIT | INP_DROPPED))) {
+ INP_WUNLOCK(inp);
+ return (ECONNRESET);
+ }
+ inp->inp_inc.inc_flags |= INC_IPV6MINMTU;
+ tp = intotcpcb(inp);
+ if ((tp->t_state >= TCPS_SYN_SENT) &&
+ (inp->inp_inc.inc_flags & INC_ISIPV6)) {
+ struct ip6_pktopts *opt;
+
+ opt = inp->in6p_outputopts;
+ if ((opt != NULL) &&
+ (opt->ip6po_minmtu ==
+ IP6PO_MINMTU_ALL)) {
+ if (tp->t_maxseg > TCP6_MSS) {
+ tp->t_maxseg = TCP6_MSS;
+ }
+ }
+ }
+ INP_WUNLOCK(inp);
+ }
}
#endif /* INET6 */
#if defined(INET6) && defined(INET)
@@ -1487,7 +1664,6 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt)
return (0);
}
if (tp->t_state != TCPS_CLOSED) {
- int error=EINVAL;
/*
* The user has advanced the state
* past the initial point, we may not
@@ -1500,7 +1676,8 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt)
* still be possible?
*/
error = (*blk->tfb_tcp_handoff_ok)(tp);
- }
+ } else
+ error = EINVAL;
if (error) {
refcount_release(&blk->tfb_refcnt);
INP_WUNLOCK(inp);
@@ -1724,6 +1901,7 @@ unlock_and_done:
*/
if (CC_ALGO(tp)->cb_destroy != NULL)
CC_ALGO(tp)->cb_destroy(tp->ccv);
+ CC_DATA(tp) = NULL;
CC_ALGO(tp) = algo;
/*
* If something goes pear shaped initialising the new
@@ -2045,6 +2223,7 @@ tcp_attach(struct socket *so)
{
struct tcpcb *tp;
struct inpcb *inp;
+ struct epoch_tracker et;
int error;
if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
@@ -2054,10 +2233,10 @@ tcp_attach(struct socket *so)
}
so->so_rcv.sb_flags |= SB_AUTOSIZE;
so->so_snd.sb_flags |= SB_AUTOSIZE;
- INP_INFO_RLOCK(&V_tcbinfo);
+ INP_INFO_RLOCK_ET(&V_tcbinfo, et);
error = in_pcballoc(so, &V_tcbinfo);
if (error) {
- INP_INFO_RUNLOCK(&V_tcbinfo);
+ INP_INFO_RUNLOCK_ET(&V_tcbinfo, et);
return (error);
}
inp = sotoinpcb(so);
@@ -2075,12 +2254,12 @@ tcp_attach(struct socket *so)
if (tp == NULL) {
in_pcbdetach(inp);
in_pcbfree(inp);
- INP_INFO_RUNLOCK(&V_tcbinfo);
+ INP_INFO_RUNLOCK_ET(&V_tcbinfo, et);
return (ENOBUFS);
}
tp->t_state = TCPS_CLOSED;
INP_WUNLOCK(inp);
- INP_INFO_RUNLOCK(&V_tcbinfo);
+ INP_INFO_RUNLOCK_ET(&V_tcbinfo, et);
TCPSTATES_INC(TCPS_CLOSED);
return (0);
}
@@ -2106,7 +2285,8 @@ tcp_disconnect(struct tcpcb *tp)
* Neither tcp_close() nor tcp_drop() should return NULL, as the
* socket is still open.
*/
- if (tp->t_state < TCPS_ESTABLISHED) {
+ if (tp->t_state < TCPS_ESTABLISHED &&
+ !(tp->t_state > TCPS_LISTEN && IS_FASTOPEN(tp->t_flags))) {
tp = tcp_close(tp);
KASSERT(tp != NULL,
("tcp_disconnect: tcp_close() returned NULL"));
@@ -2383,7 +2563,7 @@ db_print_tcpcb(struct tcpcb *tp, const char *name, int indent)
db_print_indent(indent);
db_printf("t_segq first: %p t_segqlen: %d t_dupacks: %d\n",
- LIST_FIRST(&tp->t_segq), tp->t_segqlen, tp->t_dupacks);
+ TAILQ_FIRST(&tp->t_segq), tp->t_segqlen, tp->t_dupacks);
db_print_indent(indent);
db_printf("tt_rexmt: %p tt_persist: %p tt_keep: %p\n",