]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet/tcp_usrreq.c
xnu-3789.1.32.tar.gz
[apple/xnu.git] / bsd / netinet / tcp_usrreq.c
index 350884ae144e7e95fd86a5692c0c583e7667c37c..67306d65d628e7ab1af45edd27d4ae2851c616f6 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2000-2014 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * unlawful or unlicensed copies of an Apple operating system, or to
  * circumvent, violate, or enable the circumvention or violation of, any
  * terms of an Apple operating system software license agreement.
- * 
+ *
  * Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -22,7 +22,7 @@
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 /*
@@ -121,6 +121,8 @@ void        tcp_fill_info(struct tcpcb *, struct tcp_info *);
 errno_t tcp_fill_info_for_info_tuple(struct info_tuple *, struct tcp_info *);
 
 int tcp_sysctl_info(struct sysctl_oid *, void *, int , struct sysctl_req *);
+static void tcp_connection_fill_info(struct tcpcb *tp,
+    struct tcp_connection_info *tci);
 
 /*
  * TCP protocol interface to socket abstraction.
@@ -134,13 +136,8 @@ static int tcp6_connect(struct tcpcb *, struct sockaddr *, struct proc *);
 static int     tcp6_usr_connect(struct socket *, struct sockaddr *,
                    struct proc *);
 #endif /* INET6 */
-static struct tcpcb *
-               tcp_disconnect(struct tcpcb *);
-static struct tcpcb *
-               tcp_usrclosed(struct tcpcb *);
-
-extern uint32_t tcp_autorcvbuf_max;
-
+static struct tcpcb *tcp_disconnect(struct tcpcb *);
+static struct tcpcb *tcp_usrclosed(struct tcpcb *);
 extern void tcp_sbrcv_trim(struct tcpcb *tp, struct sockbuf *sb);
 
 #if TCPDEBUG
@@ -181,7 +178,7 @@ tcp_usr_attach(struct socket *so, __unused int proto, struct proc *p)
                error = EISCONN;
                goto out;
        }
-       
+
        error = tcp_attach(so, p);
        if (error)
                goto out;
@@ -395,6 +392,27 @@ tcp6_usr_listen(struct socket *so, struct proc *p)
 }
 #endif /* INET6 */
 
+static int
+tcp_connect_complete(struct socket *so)
+{
+       struct tcpcb *tp = sototcpcb(so);
+       int error = 0;
+
+       /* TFO delays the tcp_output until later, when the app calls write() */
+       if (so->so_flags1 & SOF1_PRECONNECT_DATA) {
+               if (!necp_socket_is_allowed_to_send_recv(sotoinpcb(so), NULL, NULL))
+                       return (EHOSTUNREACH);
+
+               /* Initialize enough state so that we can actually send data */
+               tcp_mss(tp, -1, IFSCOPE_NONE);
+               tp->snd_wnd = tp->t_maxseg;
+       } else {
+               error = tcp_output(tp);
+       }
+
+       return (error);
+}
+
 /*
  * Initiate connection to peer.
  * Create a template for use in transmissions on this connection.
@@ -433,6 +451,7 @@ tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p)
                } else {
                        error = ENETDOWN;
                }
+
                return error;
        }
 #endif /* FLOW_DIVERT */
@@ -463,15 +482,18 @@ tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p)
 
        if ((error = tcp_connect(tp, nam, p)) != 0)
                goto out;
-       error = tcp_output(tp);
+
+       error = tcp_connect_complete(so);
+
        COMMON_END(PRU_CONNECT);
 }
 
 static int
 tcp_usr_connectx_common(struct socket *so, int af,
     struct sockaddr_list **src_sl, struct sockaddr_list **dst_sl,
-    struct proc *p, uint32_t ifscope, associd_t aid, connid_t *pcid,
-    uint32_t flags, void *arg, uint32_t arglen)
+    struct proc *p, uint32_t ifscope, sae_associd_t aid, sae_connid_t *pcid,
+    uint32_t flags, void *arg, uint32_t arglen, struct uio *auio,
+    user_ssize_t *bytes_written)
 {
 #pragma unused(aid)
 #if !MPTCP
@@ -480,6 +502,7 @@ tcp_usr_connectx_common(struct socket *so, int af,
        struct sockaddr_entry *src_se = NULL, *dst_se = NULL;
        struct inpcb *inp = sotoinpcb(so);
        int error;
+       user_ssize_t datalen = 0;
 
        if (inp == NULL)
                return (EINVAL);
@@ -499,7 +522,11 @@ tcp_usr_connectx_common(struct socket *so, int af,
 #if NECP
        inp_update_necp_policy(inp, src_se ? src_se->se_addr : NULL, dst_se ? dst_se->se_addr : NULL, ifscope);
 #endif /* NECP */
-       
+
+       if ((so->so_flags1 & SOF1_DATA_IDEMPOTENT) &&
+           (tcp_fastopen & TCP_FASTOPEN_CLIENT))
+               sototcpcb(so)->t_flagsext |= TF_FASTOPEN;
+
        /*
         * We get here for 2 cases:
         *
@@ -513,7 +540,7 @@ tcp_usr_connectx_common(struct socket *so, int af,
         *      bind to source address and/or interface as necessary.
         */
 #if MPTCP
-       if (flags & TCP_CONNREQF_MPTCP) {
+       if (flags & CONNREQF_MPTCP) {
                struct mptsub_connreq *mpcr = arg;
 
                /* Check to make sure this came down from MPTCP */
@@ -559,8 +586,37 @@ tcp_usr_connectx_common(struct socket *so, int af,
                /* NOTREACHED */
        }
 
+       if (error != 0)
+               return (error);
+
+       /* if there is data, copy it */
+       if (auio != NULL) {
+               socket_unlock(so, 0);
+
+               VERIFY(bytes_written != NULL);
+
+               datalen = uio_resid(auio);
+               error = so->so_proto->pr_usrreqs->pru_sosend(so, NULL,
+                   (uio_t)auio, NULL, NULL, 0);
+               socket_lock(so, 0);
+
+               if (error == 0 || error == EWOULDBLOCK)
+                       *bytes_written = datalen - uio_resid(auio);
+
+               /*
+                * sosend returns EWOULDBLOCK if it's a non-blocking
+                * socket or a timeout occured (this allows to return
+                * the amount of queued data through sendit()).
+                *
+                * However, connectx() returns EINPROGRESS in case of a
+                * blocking socket. So we change the return value here.
+                */
+               if (error == EWOULDBLOCK)
+                       error = EINPROGRESS;
+       }
+
        if (error == 0 && pcid != NULL)
-               *pcid = 1;      /* there is only 1 connection for a TCP */
+               *pcid = 1; /* there is only one connection in regular TCP */
 
        return (error);
 }
@@ -568,11 +624,12 @@ tcp_usr_connectx_common(struct socket *so, int af,
 static int
 tcp_usr_connectx(struct socket *so, struct sockaddr_list **src_sl,
     struct sockaddr_list **dst_sl, struct proc *p, uint32_t ifscope,
-    associd_t aid, connid_t *pcid, uint32_t flags, void *arg,
-    uint32_t arglen)
+    sae_associd_t aid, sae_connid_t *pcid, uint32_t flags, void *arg,
+    uint32_t arglen, struct uio *uio, user_ssize_t *bytes_written)
 {
        return (tcp_usr_connectx_common(so, AF_INET, src_sl, dst_sl,
-           p, ifscope, aid, pcid, flags, arg, arglen));
+           p, ifscope, aid, pcid, flags, arg, arglen, uio,
+           bytes_written));
 }
 
 #if INET6
@@ -607,6 +664,7 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p)
                } else {
                        error = ENETDOWN;
                }
+
                return error;
        }
 #endif /* FLOW_DIVERT */
@@ -648,27 +706,28 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p)
                inp->inp_vflag &= ~INP_IPV6;
                if ((error = tcp_connect(tp, (struct sockaddr *)&sin, p)) != 0)
                        goto out;
-               error = tcp_output(tp);
+
+               error = tcp_connect_complete(so);
                goto out;
        }
        inp->inp_vflag &= ~INP_IPV4;
        inp->inp_vflag |= INP_IPV6;
        if ((error = tcp6_connect(tp, nam, p)) != 0)
                goto out;
-       error = tcp_output(tp);
-       if (error)
-               goto out;
+
+       error = tcp_connect_complete(so);
        COMMON_END(PRU_CONNECT);
 }
 
 static int
 tcp6_usr_connectx(struct socket *so, struct sockaddr_list **src_sl,
     struct sockaddr_list **dst_sl, struct proc *p, uint32_t ifscope,
-    associd_t aid, connid_t *pcid, uint32_t flags, void *arg,
-    uint32_t arglen)
+    sae_associd_t aid, sae_connid_t *pcid, uint32_t flags, void *arg,
+    uint32_t arglen, struct uio *uio, user_ssize_t *bytes_written)
 {
        return (tcp_usr_connectx_common(so, AF_INET6, src_sl, dst_sl,
-           p, ifscope, aid, pcid, flags, arg, arglen));
+           p, ifscope, aid, pcid, flags, arg, arglen, uio,
+           bytes_written));
 }
 #endif /* INET6 */
 
@@ -704,10 +763,10 @@ tcp_usr_disconnect(struct socket *so)
  * User-protocol pru_disconnectx callback.
  */
 static int
-tcp_usr_disconnectx(struct socket *so, associd_t aid, connid_t cid)
+tcp_usr_disconnectx(struct socket *so, sae_associd_t aid, sae_connid_t cid)
 {
 #pragma unused(cid)
-       if (aid != ASSOCID_ANY && aid != ASSOCID_ALL)
+       if (aid != SAE_ASSOCID_ANY && aid != SAE_ASSOCID_ALL)
                return (EINVAL);
 
        return (tcp_usr_disconnect(so));
@@ -871,7 +930,13 @@ tcp_usr_rcvd(struct socket *so, __unused int flags)
                goto out;
        tcp_sbrcv_trim(tp, &so->so_rcv);
 
-       tcp_output(tp);
+       /*
+        * This tcp_output is solely there to trigger window-updates.
+        * However, we really do not want these window-updates while we
+        * are still in SYN_SENT or SYN_RECEIVED.
+        */
+       if (TCPS_HAVEESTABLISHED(tp->t_state))
+               tcp_output(tp);
 
 #if CONTENT_FILTER
        cfil_sock_buf_update(&so->so_rcv);
@@ -995,7 +1060,7 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
        VERIFY(!(so->so_flags & SOF_MP_SUBFLOW) ||
            (so->so_snd.sb_flags & SB_NOCOMPRESS));
 
-       if(!(flags & PRUS_OOB)) {
+       if(!(flags & PRUS_OOB) || (so->so_flags1 & SOF1_PRECONNECT_DATA)) {
                /* Call msg send if message delivery is enabled */
                if (so->so_flags & SOF_ENABLE_MSGS)
                        sbappendmsg_snd(&so->so_snd, m);
@@ -1003,6 +1068,7 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
                        sbappendstream(&so->so_snd, m);
 
                if (nam && tp->t_state < TCPS_SYN_SENT) {
+
                        /*
                         * Do implied connect if not yet connected,
                         * initialize window to default value, and
@@ -1076,6 +1142,19 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
                error = tcp_output(tp);
                tp->t_flagsext &= ~TF_FORCE;
        }
+
+
+       /*
+        * We wait for the socket to successfully connect before returning.
+        * This allows us to signal a timeout to the application.
+        */
+       if (so->so_state & SS_ISCONNECTING) {
+               if (so->so_state & SS_NBIO)
+                       error = EWOULDBLOCK;
+               else
+                       error = sbwait(&so->so_snd);
+       }
+
        COMMON_END((flags & PRUS_OOB) ? PRU_SENDOOB : 
                   ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND));
 }
@@ -1128,11 +1207,36 @@ tcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags)
        }
        m->m_len = 1;
        *mtod(m, caddr_t) = tp->t_iobc;
+       so->so_state &= ~SS_RCVATMARK;
        if ((flags & MSG_PEEK) == 0)
                tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA);
        COMMON_END(PRU_RCVOOB);
 }
 
+static int
+tcp_usr_preconnect(struct socket *so)
+{
+       struct inpcb *inp = sotoinpcb(so);
+       int error = 0;
+
+#if NECP
+       if (necp_socket_should_use_flow_divert(inp)) {
+               /* May happen, if in tcp_usr_connect we did not had a chance
+                * to set the usrreqs (due to some error). So, let's get out
+                * of here.
+                */
+               goto out;
+       }
+#endif /* NECP */
+
+       error = tcp_output(sototcpcb(so));
+
+       /* One read has been done. This was enough. Get back to "normal" behavior. */
+       so->so_flags1 &= ~SOF1_PRECONNECT_DATA;
+
+       COMMON_END(PRU_PRECONNECT);
+}
+
 /* xxx - should be const */
 struct pr_usrreqs tcp_usrreqs = {
        .pru_abort =            tcp_usr_abort,
@@ -1154,6 +1258,7 @@ struct pr_usrreqs tcp_usrreqs = {
        .pru_sockaddr =         in_getsockaddr,
        .pru_sosend =           sosend,
        .pru_soreceive =        soreceive,
+       .pru_preconnect =       tcp_usr_preconnect,
 };
 
 #if INET6
@@ -1177,6 +1282,7 @@ struct pr_usrreqs tcp6_usrreqs = {
        .pru_sockaddr =         in6_mapped_sockaddr,
        .pru_sosend =           sosend,
        .pru_soreceive =        soreceive,
+       .pru_preconnect =       tcp_usr_preconnect,
 };
 #endif /* INET6 */
 
@@ -1205,18 +1311,13 @@ struct pr_usrreqs tcp6_usrreqs = {
  *     in_pcbladdr:EADDRNOTAVAIL       Address not available
  */
 static int
-tcp_connect(tp, nam, p)
-       register struct tcpcb *tp;
-       struct sockaddr *nam;
-       struct proc *p;
+tcp_connect(struct tcpcb *tp, struct sockaddr *nam, struct proc *p)
 {
        struct inpcb *inp = tp->t_inpcb, *oinp;
        struct socket *so = inp->inp_socket;
        struct tcpcb *otp;
        struct sockaddr_in *sin = (struct sockaddr_in *)(void *)nam;
        struct in_addr laddr;
-       struct rmxp_tao *taop;
-       struct rmxp_tao tao_noncached;
        int error = 0;
        struct ifnet *outif = NULL;
 
@@ -1231,7 +1332,7 @@ tcp_connect(tp, nam, p)
         * earlier incarnation of this same connection still in
         * TIME_WAIT state, creating an ADDRINUSE error.
         */
-       error = in_pcbladdr(inp, nam, &laddr, IFSCOPE_NONE, &outif);
+       error = in_pcbladdr(inp, nam, &laddr, IFSCOPE_NONE, &outif, 0);
        if (error)
                goto done;
 
@@ -1294,7 +1395,7 @@ skip_oinp:
        if (inp->inp_flowhash == 0)
                inp->inp_flowhash = inp_calc_flowhash(inp);
 
-       tcp_set_max_rwinscale(tp, so);
+       tcp_set_max_rwinscale(tp, so, TCP_AUTORCVBUF_MAX(outif));
 
        soisconnecting(so);
        tcpstat.tcps_connattempt++;
@@ -1305,24 +1406,6 @@ skip_oinp:
        if (nstat_collect)
                nstat_route_connect_attempt(inp->inp_route.ro_rt);
 
-       /*
-        * Generate a CC value for this connection and
-        * check whether CC or CCnew should be used.
-        */
-       if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) {
-               taop = &tao_noncached;
-               bzero(taop, sizeof(*taop));
-       }
-
-       tp->cc_send = CC_INC(tcp_ccgen);
-       if (taop->tao_ccsent != 0 &&
-           CC_GEQ(tp->cc_send, taop->tao_ccsent)) {
-               taop->tao_ccsent = tp->cc_send;
-       } else {
-               taop->tao_ccsent = 0;
-               tp->t_flags |= TF_SENDCCNEW;
-       }
-
 done:
        if (outif != NULL)
                ifnet_release(outif);
@@ -1332,18 +1415,13 @@ done:
 
 #if INET6
 static int
-tcp6_connect(tp, nam, p)
-       register struct tcpcb *tp;
-       struct sockaddr *nam;
-       struct proc *p;
+tcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct proc *p)
 {
        struct inpcb *inp = tp->t_inpcb, *oinp;
        struct socket *so = inp->inp_socket;
        struct tcpcb *otp;
        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(void *)nam;
        struct in6_addr addr6;
-       struct rmxp_tao *taop;
-       struct rmxp_tao tao_noncached;
        int error = 0;
        struct ifnet *outif = NULL;
 
@@ -1411,7 +1489,7 @@ tcp6_connect(tp, nam, p)
                    (htonl(inp->inp_flowhash) & IPV6_FLOWLABEL_MASK);
        }
 
-       tcp_set_max_rwinscale(tp, so);
+       tcp_set_max_rwinscale(tp, so, TCP_AUTORCVBUF_MAX(outif));
 
        soisconnecting(so);
        tcpstat.tcps_connattempt++;
@@ -1423,24 +1501,6 @@ tcp6_connect(tp, nam, p)
        if (nstat_collect)
                nstat_route_connect_attempt(inp->inp_route.ro_rt);
 
-       /*
-        * Generate a CC value for this connection and
-        * check whether CC or CCnew should be used.
-        */
-       if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) {
-               taop = &tao_noncached;
-               bzero(taop, sizeof(*taop));
-       }
-
-       tp->cc_send = CC_INC(tcp_ccgen);
-       if (taop->tao_ccsent != 0 &&
-           CC_GEQ(tp->cc_send, taop->tao_ccsent)) {
-               taop->tao_ccsent = tp->cc_send;
-       } else {
-               taop->tao_ccsent = 0;
-               tp->t_flags |= TF_SENDCCNEW;
-       }
-
 done:
        if (outif != NULL)
                ifnet_release(outif);
@@ -1460,23 +1520,27 @@ tcp_fill_info(struct tcpcb *tp, struct tcp_info *ti)
        bzero(ti, sizeof(*ti));
 
        ti->tcpi_state = tp->t_state;
-       
+       ti->tcpi_flowhash = inp->inp_flowhash;
+
        if (tp->t_state > TCPS_LISTEN) {
-               if ((tp->t_flags & TF_REQ_TSTMP) && (tp->t_flags & TF_RCVD_TSTMP))
+               if (TSTMP_SUPPORTED(tp))
                        ti->tcpi_options |= TCPI_OPT_TIMESTAMPS;
-               if (tp->t_flags & TF_SACK_PERMIT)
+               if (SACK_ENABLED(tp))
                        ti->tcpi_options |= TCPI_OPT_SACK;
-               if ((tp->t_flags & TF_REQ_SCALE) && (tp->t_flags & TF_RCVD_SCALE)) {
+               if (TCP_WINDOW_SCALE_ENABLED(tp)) {
                        ti->tcpi_options |= TCPI_OPT_WSCALE;
                        ti->tcpi_snd_wscale = tp->snd_scale;
                        ti->tcpi_rcv_wscale = tp->rcv_scale;
                }
+               if (TCP_ECN_ENABLED(tp))
+                       ti->tcpi_options |= TCPI_OPT_ECN;
 
                /* Are we in retranmission episode */
-               if (tp->snd_max != tp->snd_nxt)
+               if (IN_FASTRECOVERY(tp) || tp->t_rxtshift > 0)
                        ti->tcpi_flags |= TCPI_FLAG_LOSSRECOVERY;
-               else
-                       ti->tcpi_flags &= ~TCPI_FLAG_LOSSRECOVERY;
+
+               if (tp->t_flags & TF_STREAMING_ON)
+                       ti->tcpi_flags |= TCPI_FLAG_STREAMING_ON;
 
                ti->tcpi_rto = tp->t_timer[TCPT_REXMT] ? tp->t_rxtcur : 0;
                ti->tcpi_snd_mss = tp->t_maxseg;
@@ -1489,7 +1553,7 @@ tcp_fill_info(struct tcpcb *tp, struct tcp_info *ti)
 
                ti->tcpi_snd_ssthresh = tp->snd_ssthresh;
                ti->tcpi_snd_cwnd = tp->snd_cwnd;
-               ti->tcpi_snd_sbbytes = tp->t_inpcb->inp_socket->so_snd.sb_cc;
+               ti->tcpi_snd_sbbytes = inp->inp_socket->so_snd.sb_cc;
        
                ti->tcpi_rcv_space = tp->rcv_wnd;
 
@@ -1535,6 +1599,48 @@ tcp_fill_info(struct tcpcb *tp, struct tcp_info *ti)
                ti->tcpi_wired_rxbytes = inp->inp_Wstat->rxbytes;
                ti->tcpi_wired_txpackets = inp->inp_Wstat->txpackets;
                ti->tcpi_wired_txbytes = inp->inp_Wstat->txbytes;
+               tcp_get_connectivity_status(tp, &ti->tcpi_connstatus);
+
+               ti->tcpi_tfo_syn_data_rcv = !!(tp->t_tfo_stats & TFO_S_SYNDATA_RCV);
+               ti->tcpi_tfo_cookie_req_rcv = !!(tp->t_tfo_stats & TFO_S_COOKIEREQ_RECV);
+               ti->tcpi_tfo_cookie_sent = !!(tp->t_tfo_stats & TFO_S_COOKIE_SENT);
+               ti->tcpi_tfo_cookie_invalid = !!(tp->t_tfo_stats & TFO_S_COOKIE_INVALID);
+
+               ti->tcpi_tfo_cookie_req = !!(tp->t_tfo_stats & TFO_S_COOKIE_REQ);
+               ti->tcpi_tfo_cookie_rcv = !!(tp->t_tfo_stats & TFO_S_COOKIE_RCV);
+               ti->tcpi_tfo_syn_data_sent = !!(tp->t_tfo_stats & TFO_S_SYN_DATA_SENT);
+               ti->tcpi_tfo_syn_data_acked = !!(tp->t_tfo_stats & TFO_S_SYN_DATA_ACKED);
+               ti->tcpi_tfo_syn_loss = !!(tp->t_tfo_stats & TFO_S_SYN_LOSS);
+               ti->tcpi_tfo_cookie_wrong = !!(tp->t_tfo_stats & TFO_S_COOKIE_WRONG);
+               ti->tcpi_tfo_no_cookie_rcv = !!(tp->t_tfo_stats & TFO_S_NO_COOKIE_RCV);
+               ti->tcpi_tfo_heuristics_disable = !!(tp->t_tfo_stats & TFO_S_HEURISTICS_DISABLE);
+               ti->tcpi_tfo_send_blackhole = !!(tp->t_tfo_stats & TFO_S_SEND_BLACKHOLE);
+               ti->tcpi_tfo_recv_blackhole = !!(tp->t_tfo_stats & TFO_S_RECV_BLACKHOLE);
+
+               ti->tcpi_ecn_client_setup = !!(tp->ecn_flags & TE_SETUPSENT);
+               ti->tcpi_ecn_server_setup = !!(tp->ecn_flags & TE_SETUPRECEIVED);
+               ti->tcpi_ecn_success = (tp->ecn_flags & TE_ECN_ON) == TE_ECN_ON ? 1 : 0;
+               ti->tcpi_ecn_lost_syn = !!(tp->ecn_flags & TE_LOST_SYN);
+               ti->tcpi_ecn_lost_synack = !!(tp->ecn_flags & TE_LOST_SYNACK);
+
+               ti->tcpi_local_peer = !!(tp->t_flags & TF_LOCAL);
+
+               if (tp->t_inpcb->inp_last_outifp != NULL) {
+                       if (IFNET_IS_CELLULAR(tp->t_inpcb->inp_last_outifp))
+                               ti->tcpi_if_cell = 1;
+                       else if (IFNET_IS_WIFI(tp->t_inpcb->inp_last_outifp))
+                               ti->tcpi_if_wifi = 1;
+               }
+
+               ti->tcpi_ecn_recv_ce = tp->t_ecn_recv_ce;
+               ti->tcpi_ecn_recv_cwr = tp->t_ecn_recv_cwr;
+
+               ti->tcpi_rcvoopack = tp->t_rcvoopack;
+               ti->tcpi_pawsdrop = tp->t_pawsdrop;
+               ti->tcpi_sack_recovery_episode = tp->t_sack_recovery_episode;
+               ti->tcpi_reordered_pkts = tp->t_reordered_pkts;
+               ti->tcpi_dsack_sent = tp->t_dsack_sent;
+               ti->tcpi_dsack_recvd = tp->t_dsack_recvd;
        }
 }
 
@@ -1599,6 +1705,64 @@ tcp_fill_info_for_info_tuple(struct info_tuple *itpl, struct tcp_info *ti)
        return 0;
 }
 
+static void
+tcp_connection_fill_info(struct tcpcb *tp, struct tcp_connection_info *tci)
+{
+       struct inpcb *inp = tp->t_inpcb;
+
+       bzero(tci, sizeof(*tci));
+       tci->tcpi_state = tp->t_state;
+       if (tp->t_state > TCPS_LISTEN) {
+               if (TSTMP_SUPPORTED(tp))
+                       tci->tcpi_options |= TCPCI_OPT_TIMESTAMPS;
+               if (SACK_ENABLED(tp))
+                       tci->tcpi_options |= TCPCI_OPT_SACK;
+               if (TCP_WINDOW_SCALE_ENABLED(tp)) {
+                       tci->tcpi_options |= TCPCI_OPT_WSCALE;
+                       tci->tcpi_snd_wscale = tp->snd_scale;
+                       tci->tcpi_rcv_wscale = tp->rcv_scale;
+               }
+               if (TCP_ECN_ENABLED(tp))
+                       tci->tcpi_options |= TCPCI_OPT_ECN;
+               if (IN_FASTRECOVERY(tp) || tp->t_rxtshift > 0)
+                       tci->tcpi_flags |= TCPCI_FLAG_LOSSRECOVERY;
+               if (tp->t_flagsext & TF_PKTS_REORDERED)
+                       tci->tcpi_flags |= TCPCI_FLAG_REORDERING_DETECTED;
+               tci->tcpi_rto = (tp->t_timer[TCPT_REXMT] > 0) ?
+                       tp->t_rxtcur : 0;
+               tci->tcpi_maxseg = tp->t_maxseg;
+               tci->tcpi_snd_ssthresh = tp->snd_ssthresh;
+               tci->tcpi_snd_cwnd = tp->snd_cwnd;
+               tci->tcpi_snd_wnd = tp->snd_wnd;
+               tci->tcpi_snd_sbbytes = inp->inp_socket->so_snd.sb_cc;
+               tci->tcpi_rcv_wnd = tp->rcv_wnd;
+               tci->tcpi_rttcur = tp->t_rttcur;
+               tci->tcpi_srtt = (tp->t_srtt >> TCP_RTT_SHIFT);
+               tci->tcpi_rttvar = (tp->t_rttvar >> TCP_RTTVAR_SHIFT);
+               tci->tcpi_txpackets = inp->inp_stat->txpackets;
+               tci->tcpi_txbytes = inp->inp_stat->txbytes;
+               tci->tcpi_txretransmitbytes = tp->t_stat.txretransmitbytes;
+               tci->tcpi_rxpackets = inp->inp_stat->rxpackets;
+               tci->tcpi_rxbytes = inp->inp_stat->rxbytes;
+               tci->tcpi_rxoutoforderbytes = tp->t_stat.rxoutoforderbytes;
+
+               tci->tcpi_tfo_syn_data_rcv = !!(tp->t_tfo_stats & TFO_S_SYNDATA_RCV);
+               tci->tcpi_tfo_cookie_req_rcv = !!(tp->t_tfo_stats & TFO_S_COOKIEREQ_RECV);
+               tci->tcpi_tfo_cookie_sent = !!(tp->t_tfo_stats & TFO_S_COOKIE_SENT);
+               tci->tcpi_tfo_cookie_invalid = !!(tp->t_tfo_stats & TFO_S_COOKIE_INVALID);
+               tci->tcpi_tfo_cookie_req = !!(tp->t_tfo_stats & TFO_S_COOKIE_REQ);
+               tci->tcpi_tfo_cookie_rcv = !!(tp->t_tfo_stats & TFO_S_COOKIE_RCV);
+               tci->tcpi_tfo_syn_data_sent = !!(tp->t_tfo_stats & TFO_S_SYN_DATA_SENT);
+               tci->tcpi_tfo_syn_data_acked = !!(tp->t_tfo_stats & TFO_S_SYN_DATA_ACKED);
+               tci->tcpi_tfo_syn_loss = !!(tp->t_tfo_stats & TFO_S_SYN_LOSS);
+               tci->tcpi_tfo_cookie_wrong = !!(tp->t_tfo_stats & TFO_S_COOKIE_WRONG);
+               tci->tcpi_tfo_no_cookie_rcv = !!(tp->t_tfo_stats & TFO_S_NO_COOKIE_RCV);
+               tci->tcpi_tfo_heuristics_disable = !!(tp->t_tfo_stats & TFO_S_HEURISTICS_DISABLE);
+               tci->tcpi_tfo_send_blackhole = !!(tp->t_tfo_stats & TFO_S_SEND_BLACKHOLE);
+               tci->tcpi_tfo_recv_blackhole = !!(tp->t_tfo_stats & TFO_S_RECV_BLACKHOLE);
+       }
+}
+
 
 __private_extern__ int 
 tcp_sysctl_info(__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req)
@@ -1714,9 +1878,7 @@ tcp_getconninfo(struct socket *so, struct conninfo_tcp *tcp_ci)
  * splnet() any more.  This needs more examination.)
  */
 int
-tcp_ctloutput(so, sopt)
-       struct socket *so;
-       struct sockopt *sopt;
+tcp_ctloutput(struct socket *so, struct sockopt *sopt)
 {
        int     error, opt, optval;
        struct  inpcb *inp;
@@ -1752,7 +1914,6 @@ tcp_ctloutput(so, sopt)
                case TCP_NODELAY:
                case TCP_NOOPT:
                case TCP_NOPUSH:
-               case TCP_ENABLE_ECN:
                        error = sooptcopyin(sopt, &optval, sizeof optval,
                                            sizeof optval);
                        if (error)
@@ -1768,9 +1929,6 @@ tcp_ctloutput(so, sopt)
                        case TCP_NOPUSH:
                                opt = TF_NOPUSH;
                                break;
-                       case TCP_ENABLE_ECN:
-                               opt = TF_ENABLE_ECN;
-                               break;
                        default:
                                opt = 0; /* dead code to fool gcc */
                                break;
@@ -1938,6 +2096,21 @@ tcp_ctloutput(so, sopt)
                        }
                        break;
 
+               case TCP_KEEPALIVE_OFFLOAD:
+                       error = sooptcopyin(sopt, &optval, sizeof(optval),
+                               sizeof(optval));
+                       if (error)
+                               break;
+                       if (optval < 0 || optval > INT32_MAX) {
+                               error = EINVAL;
+                               break;
+                       } 
+                       if (optval != 0)
+                               inp->inp_flags2 |= INP2_KEEPALIVE_OFFLOAD;
+                       else
+                               inp->inp_flags2 &= ~INP2_KEEPALIVE_OFFLOAD;
+                       break;
+
                case PERSIST_TIMEOUT:
                        error = sooptcopyin(sopt, &optval, sizeof optval,
                                                sizeof optval);
@@ -1978,11 +2151,11 @@ tcp_ctloutput(so, sopt)
                        break;
                case TCP_ADAPTIVE_READ_TIMEOUT:
                        error = sooptcopyin(sopt, &optval, sizeof (optval),
-                               sizeof(optval));
+                           sizeof(optval));
                        if (error)
                                break;
                        if (optval < 0 || 
-                               optval > TCP_ADAPTIVE_TIMEOUT_MAX) {
+                           optval > TCP_ADAPTIVE_TIMEOUT_MAX) {
                                error = EINVAL;
                                break;
                        } else if (optval == 0) {
@@ -1994,11 +2167,11 @@ tcp_ctloutput(so, sopt)
                        break;
                case TCP_ADAPTIVE_WRITE_TIMEOUT:
                        error = sooptcopyin(sopt, &optval, sizeof (optval),
-                               sizeof (optval));
+                           sizeof (optval));
                        if (error)
                                break;
                        if (optval < 0 || 
-                               optval > TCP_ADAPTIVE_TIMEOUT_MAX) {
+                           optval > TCP_ADAPTIVE_TIMEOUT_MAX) {
                                error = EINVAL;
                                break;
                        } else {
@@ -2023,13 +2196,13 @@ tcp_ctloutput(so, sopt)
                                }
 
                                /*
-                                * allocate memory for storing message 
+                                * allocate memory for storing message
                                 * related state
                                 */
                                VERIFY(so->so_msg_state == NULL);
-                               MALLOC(so->so_msg_state, 
+                               MALLOC(so->so_msg_state,
                                        struct msg_state *,
-                                       sizeof(struct msg_state), 
+                                       sizeof(struct msg_state),
                                        M_TEMP, M_WAITOK | M_ZERO);
                                if (so->so_msg_state == NULL) {
                                        error = ENOMEM;
@@ -2039,9 +2212,9 @@ tcp_ctloutput(so, sopt)
                                /* Enable message delivery */
                                so->so_flags |= SOF_ENABLE_MSGS;
                        } else {
-                               /* 
-                                * Can't disable message delivery on socket 
-                                * because of restrictions imposed by 
+                               /*
+                                * Can't disable message delivery on socket
+                                * because of restrictions imposed by
                                 * encoding/decoding
                                 */
                                error = EINVAL;
@@ -2076,6 +2249,83 @@ tcp_ctloutput(so, sopt)
                                        tcp_pmtud_revert_segment_size(tp);
                        }
                        break;
+               case TCP_FASTOPEN:
+                       if (!(tcp_fastopen & TCP_FASTOPEN_SERVER)) {
+                               error = ENOTSUP;
+                               break;
+                       }
+
+                       error = sooptcopyin(sopt, &optval, sizeof(optval),
+                               sizeof(optval));
+                       if (error)
+                               break;
+                       if (optval < 0 || optval > 1) {
+                               error = EINVAL;
+                               break;
+                       }
+                       if (tp->t_state != TCPS_LISTEN) {
+                               error =  EINVAL;
+                               break;
+                       }
+                       if (optval)
+                               tp->t_flagsext |= TF_FASTOPEN;
+                       else
+                               tcp_disable_tfo(tp);
+                       break;
+               case TCP_ENABLE_ECN:
+                       error = sooptcopyin(sopt, &optval, sizeof optval,
+                                           sizeof optval);
+                       if (error)
+                               break;
+                       if (optval) {
+                               tp->ecn_flags |= TE_ECN_MODE_ENABLE;
+                               tp->ecn_flags &= ~TE_ECN_MODE_DISABLE;
+                       } else {
+                               tp->ecn_flags &= ~TE_ECN_MODE_ENABLE;
+                       }
+                       break;
+               case TCP_ECN_MODE:
+                       error = sooptcopyin(sopt, &optval, sizeof optval,
+                                           sizeof optval);
+                       if (error)
+                               break;
+                       if (optval == ECN_MODE_DEFAULT) {
+                               tp->ecn_flags &= ~TE_ECN_MODE_ENABLE;
+                               tp->ecn_flags &= ~TE_ECN_MODE_DISABLE;
+                       } else if (optval == ECN_MODE_ENABLE) {
+                               tp->ecn_flags |= TE_ECN_MODE_ENABLE;
+                               tp->ecn_flags &= ~TE_ECN_MODE_DISABLE;
+                       } else if (optval == ECN_MODE_DISABLE) {
+                               tp->ecn_flags &= ~TE_ECN_MODE_ENABLE;
+                               tp->ecn_flags |= TE_ECN_MODE_DISABLE;
+                       } else {
+                               error = EINVAL;
+                       }
+                       break;
+               case TCP_NOTIFY_ACKNOWLEDGEMENT:
+                       error = sooptcopyin(sopt, &optval,
+                           sizeof(optval), sizeof(optval));
+                       if (error)
+                               break;
+                       if (optval <= 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       if (tp->t_notify_ack_count >= TCP_MAX_NOTIFY_ACK) {
+                               error = ETOOMANYREFS;
+                               break;
+                       }
+
+                       /*
+                        * validate that the given marker id is not
+                        * a duplicate to avoid ambiguity
+                        */
+                       if ((error = tcp_notify_ack_id_valid(tp, so,
+                           optval)) != 0) {
+                               break;
+                       }
+                       error = tcp_add_notify_ack_marker(tp, optval);
+                       break;
                case SO_FLUSH:
                        if ((error = sooptcopyin(sopt, &optval, sizeof (optval),
                            sizeof (optval))) != 0)
@@ -2113,13 +2363,25 @@ tcp_ctloutput(so, sopt)
                        optval = tp->t_maxseg;
                        break;
                case TCP_KEEPALIVE:
-                       optval = tp->t_keepidle / TCP_RETRANSHZ;
+                       if (tp->t_keepidle > 0)
+                               optval = tp->t_keepidle / TCP_RETRANSHZ;
+                       else
+                               optval = tcp_keepidle  / TCP_RETRANSHZ;
                        break;
                case TCP_KEEPINTVL:
-                       optval = tp->t_keepintvl / TCP_RETRANSHZ;
+                       if (tp->t_keepintvl > 0)
+                               optval = tp->t_keepintvl / TCP_RETRANSHZ;
+                       else
+                               optval = tcp_keepintvl / TCP_RETRANSHZ;
                        break;
                case TCP_KEEPCNT:
-                       optval = tp->t_keepcnt;
+                       if (tp->t_keepcnt > 0)
+                               optval = tp->t_keepcnt;
+                       else
+                               optval = tcp_keepcnt;
+                       break;
+               case TCP_KEEPALIVE_OFFLOAD:
+                       optval = !!(inp->inp_flags2 & INP2_KEEPALIVE_OFFLOAD);
                        break;
                case TCP_NOOPT:
                        optval = tp->t_flags & TF_NOOPT;
@@ -2128,7 +2390,15 @@ tcp_ctloutput(so, sopt)
                        optval = tp->t_flags & TF_NOPUSH;
                        break;
                case TCP_ENABLE_ECN:
-                       optval = (tp->t_flags & TF_ENABLE_ECN) ? 1 : 0; 
+                       optval = (tp->ecn_flags & TE_ECN_MODE_ENABLE) ? 1 : 0;
+                       break;
+               case TCP_ECN_MODE:
+                       if (tp->ecn_flags & TE_ECN_MODE_ENABLE)
+                               optval = ECN_MODE_ENABLE;
+                       else if (tp->ecn_flags & TE_ECN_MODE_DISABLE)
+                               optval = ECN_MODE_DISABLE;
+                       else
+                               optval = ECN_MODE_DEFAULT;
                        break;
                case TCP_CONNECTIONTIMEOUT:
                        optval = tp->t_keepinit / TCP_RETRANSHZ;
@@ -2145,6 +2415,14 @@ tcp_ctloutput(so, sopt)
                case TCP_NOTIMEWAIT:
                        optval = (tp->t_flagsext & TF_NOTIMEWAIT) ? 1 : 0;
                        break;
+               case TCP_FASTOPEN:
+                       if (tp->t_state != TCPS_LISTEN ||
+                           !(tcp_fastopen & TCP_FASTOPEN_SERVER)) {
+                               error = ENOTSUP;
+                               break;
+                       }
+                       optval = tfo_enabled(tp);
+                       break;
                case TCP_MEASURE_SND_BW:
                        optval = tp->t_flagsext & TF_MEASURESNDBW;
                        break;
@@ -2156,6 +2434,13 @@ tcp_ctloutput(so, sopt)
                        goto done;
                        /* NOT REACHED */
                }
+               case TCP_CONNECTION_INFO: {
+                       struct tcp_connection_info tci;
+                       tcp_connection_fill_info(tp, &tci);
+                       error = sooptcopyout(sopt, &tci,
+                           sizeof(struct tcp_connection_info));
+                       goto done;
+               }
                case TCP_MEASURE_BW_BURST: {
                        struct tcp_measure_bw_burst out;
                        if ((tp->t_flagsext & TF_MEASURESNDBW) == 0 ||
@@ -2209,9 +2494,24 @@ tcp_ctloutput(so, sopt)
                        optval = tp->t_adaptive_wtimo;
                        break;
                case SO_TRAFFIC_MGT_BACKGROUND:
-                       optval = (so->so_traffic_mgt_flags &
-                           TRAFFIC_MGT_SO_BACKGROUND) ? 1 : 0;
+                       optval = (so->so_flags1 &
+                           SOF1_TRAFFIC_MGT_SO_BACKGROUND) ? 1 : 0;
                        break;
+               case TCP_NOTIFY_ACKNOWLEDGEMENT: {
+                       struct tcp_notify_ack_complete retid;
+
+                       if (sopt->sopt_valsize != sizeof (retid)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       bzero(&retid, sizeof (retid));
+                       tcp_get_notify_ack_count(tp, &retid);
+                       if (retid.notify_complete_count > 0)
+                               tcp_get_notify_ack_ids(tp, &retid);
+
+                       error = sooptcopyout(sopt, &retid, sizeof (retid));
+                       goto done;
+               }
                default:
                        error = ENOPROTOOPT;
                        break;
@@ -2271,7 +2571,6 @@ SYSCTL_PROC(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, CTLTYPE_INT | CTLFLAG_RW
 SYSCTL_PROC(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
     &tcp_recvspace , 0, &sysctl_tcp_sospace, "IU", "Maximum incoming TCP datagram size");
 
-
 /*
  * Attach TCP protocol to socket, allocating
  * internet protocol control block, tcp control block,
@@ -2284,11 +2583,9 @@ SYSCTL_PROC(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, CTLTYPE_INT | CTLFLAG_RW
  *     soreserve:ENOBUFS
  */
 static int
-tcp_attach(so, p)
-       struct socket *so;
-       struct proc *p;
+tcp_attach(struct socket *so, struct proc *p)
 {
-       register struct tcpcb *tp;
+       struct tcpcb *tp;
        struct inpcb *inp;
        int error;
 #if INET6
@@ -2306,6 +2603,11 @@ tcp_attach(so, p)
                if (error)
                        return (error);
        }
+
+       if (so->so_snd.sb_preconn_hiwat == 0) {
+               soreserve_preconnect(so, 2048);
+       }
+
        if ((so->so_rcv.sb_flags & SB_USRSIZE) == 0)
                so->so_rcv.sb_flags |= SB_AUTOSIZE;
        if ((so->so_snd.sb_flags & SB_USRSIZE) == 0)
@@ -2348,8 +2650,7 @@ tcp_attach(so, p)
  * send segment to peer (with FIN).
  */
 static struct tcpcb *
-tcp_disconnect(tp)
-       register struct tcpcb *tp;
+tcp_disconnect(struct tcpcb *tp)
 {
        struct socket *so = tp->t_inpcb->inp_socket;
 
@@ -2384,10 +2685,8 @@ tcp_disconnect(tp)
  * We can let the user exit from the close as soon as the FIN is acked.
  */
 static struct tcpcb *
-tcp_usrclosed(tp)
-       register struct tcpcb *tp;
+tcp_usrclosed(struct tcpcb *tp)
 {
-
        switch (tp->t_state) {
 
        case TCPS_CLOSED: