+extern int slowlink_wsize; /* window correction for slow links */
+
+extern u_int32_t dlil_filter_disable_tso_count;
+extern u_int32_t kipf_count;
+
+static int tcp_ip_output(struct socket *, struct tcpcb *, struct mbuf *,
+ int, struct mbuf *, int, int, boolean_t);
+static int tcp_recv_throttle(struct tcpcb *tp);
+
+static int32_t
+tcp_tfo_check(struct tcpcb *tp, int32_t len)
+{
+ struct socket *so = tp->t_inpcb->inp_socket;
+ unsigned int optlen = 0;
+ unsigned int cookie_len;
+
+ if (tp->t_flags & TF_NOOPT) {
+ goto fallback;
+ }
+
+ if (!(tp->t_flagsext & TF_FASTOPEN_FORCE_ENABLE) &&
+ !tcp_heuristic_do_tfo(tp)) {
+ tp->t_tfo_stats |= TFO_S_HEURISTICS_DISABLE;
+ tcpstat.tcps_tfo_heuristics_disable++;
+ goto fallback;
+ }
+
+ if (so->so_flags1 & SOF1_DATA_AUTHENTICATED) {
+ return len;
+ }
+
+ optlen += TCPOLEN_MAXSEG;
+
+ if (tp->t_flags & TF_REQ_SCALE) {
+ optlen += 4;
+ }
+
+#if MPTCP
+ if ((so->so_flags & SOF_MP_SUBFLOW) && mptcp_enable &&
+ (tp->t_rxtshift <= mptcp_mpcap_retries ||
+ (tptomptp(tp)->mpt_mpte->mpte_flags & MPTE_FORCE_ENABLE))) {
+ optlen += sizeof(struct mptcp_mpcapable_opt_common) + sizeof(mptcp_key_t);
+ }
+#endif /* MPTCP */
+
+ if (tp->t_flags & TF_REQ_TSTMP) {
+ optlen += TCPOLEN_TSTAMP_APPA;
+ }
+
+ if (SACK_ENABLED(tp)) {
+ optlen += TCPOLEN_SACK_PERMITTED;
+ }
+
+ /* Now, decide whether to use TFO or not */
+
+ /* Don't even bother trying if there is no space at all... */
+ if (MAX_TCPOPTLEN - optlen < TCPOLEN_FASTOPEN_REQ) {
+ goto fallback;
+ }
+
+ cookie_len = tcp_cache_get_cookie_len(tp);
+ if (cookie_len == 0) {
+ /* No cookie, so we request one */
+ return 0;
+ }
+
+ /* There is not enough space for the cookie, so we cannot do TFO */
+ if (MAX_TCPOPTLEN - optlen < cookie_len) {
+ goto fallback;
+ }
+
+ /* Do not send SYN+data if there is more in the queue than MSS */
+ if (so->so_snd.sb_cc > (tp->t_maxopd - MAX_TCPOPTLEN)) {
+ goto fallback;
+ }
+
+ /* Ok, everything looks good. We can go on and do TFO */
+ return len;
+
+fallback:
+ tcp_disable_tfo(tp);
+ return 0;
+}
+
+/* Returns the number of bytes written to the TCP option-space */
+static unsigned int
+tcp_tfo_write_cookie_rep(struct tcpcb *tp, unsigned int optlen, u_char *opt)
+{
+ u_char out[CCAES_BLOCK_SIZE];
+ unsigned ret = 0;
+ u_char *bp;
+
+ if ((MAX_TCPOPTLEN - optlen) <
+ (TCPOLEN_FASTOPEN_REQ + TFO_COOKIE_LEN_DEFAULT)) {
+ return ret;
+ }
+
+ tcp_tfo_gen_cookie(tp->t_inpcb, out, sizeof(out));
+
+ bp = opt + optlen;
+
+ *bp++ = TCPOPT_FASTOPEN;
+ *bp++ = 2 + TFO_COOKIE_LEN_DEFAULT;
+ memcpy(bp, out, TFO_COOKIE_LEN_DEFAULT);
+ ret += 2 + TFO_COOKIE_LEN_DEFAULT;
+
+ tp->t_tfo_stats |= TFO_S_COOKIE_SENT;
+ tcpstat.tcps_tfo_cookie_sent++;
+
+ return ret;
+}
+
+static unsigned int
+tcp_tfo_write_cookie(struct tcpcb *tp, unsigned int optlen, int32_t len,
+ u_char *opt)
+{
+ uint8_t tfo_len;
+ struct socket *so = tp->t_inpcb->inp_socket;
+ unsigned ret = 0;
+ int res;
+ u_char *bp;
+
+ if (TCPOLEN_FASTOPEN_REQ > MAX_TCPOPTLEN - optlen) {
+ return 0;
+ }
+ tfo_len = (uint8_t)(MAX_TCPOPTLEN - optlen - TCPOLEN_FASTOPEN_REQ);
+
+ if (so->so_flags1 & SOF1_DATA_AUTHENTICATED) {
+ /* If there is some data, let's track it */
+ if (len > 0) {
+ tp->t_tfo_stats |= TFO_S_SYN_DATA_SENT;
+ tcpstat.tcps_tfo_syn_data_sent++;
+ }
+
+ return 0;
+ }
+
+ bp = opt + optlen;
+
+ /*
+ * The cookie will be copied in the appropriate place within the
+ * TCP-option space. That way we avoid the need for an intermediate
+ * variable.
+ */
+ res = tcp_cache_get_cookie(tp, bp + TCPOLEN_FASTOPEN_REQ, &tfo_len);
+ if (res == 0) {
+ *bp++ = TCPOPT_FASTOPEN;
+ *bp++ = TCPOLEN_FASTOPEN_REQ;
+ ret += TCPOLEN_FASTOPEN_REQ;
+
+ tp->t_tfo_flags |= TFO_F_COOKIE_REQ;
+
+ tp->t_tfo_stats |= TFO_S_COOKIE_REQ;
+ tcpstat.tcps_tfo_cookie_req++;
+ } else {
+ *bp++ = TCPOPT_FASTOPEN;
+ *bp++ = TCPOLEN_FASTOPEN_REQ + tfo_len;
+
+ ret += TCPOLEN_FASTOPEN_REQ + tfo_len;
+
+ tp->t_tfo_flags |= TFO_F_COOKIE_SENT;
+
+ /* If there is some data, let's track it */
+ if (len > 0) {
+ tp->t_tfo_stats |= TFO_S_SYN_DATA_SENT;
+ tcpstat.tcps_tfo_syn_data_sent++;
+ }
+ }
+
+ return ret;
+}
+
+static inline bool
+tcp_send_ecn_flags_on_syn(struct tcpcb *tp)
+{
+ return !(tp->ecn_flags & TE_SETUPSENT);
+}
+
+void
+tcp_set_ecn(struct tcpcb *tp, struct ifnet *ifp)
+{
+ boolean_t inbound;
+
+ /*
+ * Socket option has precedence
+ */
+ if (tp->ecn_flags & TE_ECN_MODE_ENABLE) {
+ tp->ecn_flags |= TE_ENABLE_ECN;
+ goto check_heuristic;
+ }
+
+ if (tp->ecn_flags & TE_ECN_MODE_DISABLE) {
+ tp->ecn_flags &= ~TE_ENABLE_ECN;
+ return;
+ }
+ /*
+ * Per interface setting comes next
+ */
+ if (ifp != NULL) {
+ if (ifp->if_eflags & IFEF_ECN_ENABLE) {
+ tp->ecn_flags |= TE_ENABLE_ECN;
+ goto check_heuristic;
+ }
+
+ if (ifp->if_eflags & IFEF_ECN_DISABLE) {
+ tp->ecn_flags &= ~TE_ENABLE_ECN;
+ return;
+ }
+ }
+ /*
+ * System wide settings come last
+ */
+ inbound = (tp->t_inpcb->inp_socket->so_head != NULL);
+ if ((inbound && tcp_ecn_inbound == 1) ||
+ (!inbound && tcp_ecn_outbound == 1)) {
+ tp->ecn_flags |= TE_ENABLE_ECN;
+ goto check_heuristic;
+ } else {
+ tp->ecn_flags &= ~TE_ENABLE_ECN;
+ }
+
+ return;
+
+check_heuristic:
+ if (!tcp_heuristic_do_ecn(tp)) {
+ tp->ecn_flags &= ~TE_ENABLE_ECN;
+ }
+
+ /*
+ * If the interface setting, system-level setting and heuristics
+ * allow to enable ECN, randomly select 5% of connections to
+ * enable it
+ */
+ if ((tp->ecn_flags & (TE_ECN_MODE_ENABLE | TE_ECN_MODE_DISABLE
+ | TE_ENABLE_ECN)) == TE_ENABLE_ECN) {
+ /*
+ * Use the random value in iss for randomizing
+ * this selection
+ */
+ if ((tp->iss % 100) >= tcp_ecn_setup_percentage) {
+ tp->ecn_flags &= ~TE_ENABLE_ECN;
+ }
+ }
+}
+
+int
+tcp_flight_size(struct tcpcb *tp)
+{
+ int ret;
+
+ VERIFY(tp->sackhint.sack_bytes_acked >= 0);
+ VERIFY(tp->sackhint.sack_bytes_rexmit >= 0);
+
+ /*
+ * RFC6675, SetPipe (), SACK'd bytes are discounted. All the rest is still in-flight.
+ */
+ ret = tp->snd_nxt - tp->snd_una - tp->sackhint.sack_bytes_acked;
+
+ if (ret < 0) {
+ /*
+ * This happens when the RTO-timer fires because snd_nxt gets artificially
+ * decreased. If we then receive some SACK-blogs, sack_bytes_acked is
+ * going to be high.
+ */
+ ret = 0;
+ }
+
+ return ret;
+}