/*
- * 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,
* 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@
*/
/*
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.
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
error = EISCONN;
goto out;
}
-
+
error = tcp_attach(so, p);
if (error)
goto out;
}
#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.
} else {
error = ENETDOWN;
}
+
return error;
}
#endif /* FLOW_DIVERT */
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
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);
#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:
*
* 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 */
/* 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);
}
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
} else {
error = ENETDOWN;
}
+
return error;
}
#endif /* FLOW_DIVERT */
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 */
* 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));
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);
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);
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
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));
}
if (tp == NULL)
goto out;
tp = tcp_drop(tp, ECONNABORTED);
+ VERIFY(so->so_usecount > 0);
so->so_usecount--;
COMMON_END(PRU_ABORT);
}
}
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,
.pru_sockaddr = in_getsockaddr,
.pru_sosend = sosend,
.pru_soreceive = soreceive,
+ .pru_preconnect = tcp_usr_preconnect,
};
#if INET6
.pru_sockaddr = in6_mapped_sockaddr,
.pru_sosend = sosend,
.pru_soreceive = soreceive,
+ .pru_preconnect = tcp_usr_preconnect,
};
#endif /* INET6 */
* 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;
* 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;
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++;
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);
#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;
(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++;
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);
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;
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;
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;
}
}
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)
* 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;
case TCP_NODELAY:
case TCP_NOOPT:
case TCP_NOPUSH:
- case TCP_ENABLE_ECN:
error = sooptcopyin(sopt, &optval, sizeof optval,
sizeof optval);
if (error)
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;
}
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);
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) {
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 {
}
/*
- * 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;
/* 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;
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)
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;
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;
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;
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 ||
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;
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,
* 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
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)
* 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;
* 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: