+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
+tcp_tfo_write_cookie_rep(struct tcpcb *tp, unsigned 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
+tcp_tfo_write_cookie(struct tcpcb *tp, unsigned optlen, int32_t len,
+ u_char *opt)
+{
+ u_int8_t tfo_len = MAX_TCPOPTLEN - optlen - TCPOLEN_FASTOPEN_REQ;
+ struct socket *so = tp->t_inpcb->inp_socket;
+ unsigned ret = 0;
+ int res;
+ u_char *bp;
+
+ 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, struct socket *so)
+{
+ return !((tp->ecn_flags & TE_SETUPSENT ||
+ (so->so_flags & SOF_MP_SUBFLOW) ||
+ (tfo_enabled(tp))));
+}
+
+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;
+ }
+}
+