+static boolean_t
+tcp_tfo_syn(tp, to)
+ struct tcpcb *tp;
+ struct tcpopt *to;
+{
+ u_char out[CCAES_BLOCK_SIZE];
+ unsigned char len;
+
+ if (!(to->to_flags & (TOF_TFO | TOF_TFOREQ)) ||
+ !(tcp_fastopen & TCP_FASTOPEN_SERVER))
+ return (FALSE);
+
+ if ((to->to_flags & TOF_TFOREQ)) {
+ tp->t_tfo_flags |= TFO_F_OFFER_COOKIE;
+
+ tp->t_tfo_stats |= TFO_S_COOKIEREQ_RECV;
+ tcpstat.tcps_tfo_cookie_req_rcv++;
+ return (FALSE);
+ }
+
+ /* Ok, then it must be an offered cookie. We need to check that ... */
+ tcp_tfo_gen_cookie(tp->t_inpcb, out, sizeof(out));
+
+ len = *to->to_tfo - TCPOLEN_FASTOPEN_REQ;
+ to->to_tfo++;
+ if (memcmp(out, to->to_tfo, len)) {
+ /* Cookies are different! Let's return and offer a new cookie */
+ tp->t_tfo_flags |= TFO_F_OFFER_COOKIE;
+
+ tp->t_tfo_stats |= TFO_S_COOKIE_INVALID;
+ tcpstat.tcps_tfo_cookie_invalid++;
+ return (FALSE);
+ }
+
+ if (OSIncrementAtomic(&tcp_tfo_halfcnt) >= tcp_tfo_backlog) {
+ /* Need to decrement again as we just increased it... */
+ OSDecrementAtomic(&tcp_tfo_halfcnt);
+ return (FALSE);
+ }
+
+ tp->t_tfo_flags |= TFO_F_COOKIE_VALID;
+
+ tp->t_tfo_stats |= TFO_S_SYNDATA_RCV;
+ tcpstat.tcps_tfo_syn_data_rcv++;
+
+ return (TRUE);
+}
+
+static void
+tcp_tfo_synack(tp, to)
+ struct tcpcb *tp;
+ struct tcpopt *to;
+{
+ if (to->to_flags & TOF_TFO) {
+ unsigned char len = *to->to_tfo - TCPOLEN_FASTOPEN_REQ;
+
+ /*
+ * If this happens, things have gone terribly wrong. len should
+ * have been check in tcp_dooptions.
+ */
+ VERIFY(len <= TFO_COOKIE_LEN_MAX);
+
+ to->to_tfo++;
+
+ tcp_cache_set_cookie(tp, to->to_tfo, len);
+ tcp_heuristic_tfo_success(tp);
+
+ tp->t_tfo_stats |= TFO_S_COOKIE_RCV;
+ tcpstat.tcps_tfo_cookie_rcv++;
+ } else {
+ /*
+ * Thus, no cookie in the response, but we either asked for one
+ * or sent SYN+DATA. Now, we need to check whether we had to
+ * rexmit the SYN. If that's the case, it's better to start
+ * backing of TFO-cookie requests.
+ */
+ if (tp->t_tfo_flags & TFO_F_SYN_LOSS)
+ tcp_heuristic_tfo_inc_loss(tp);
+ else
+ tcp_heuristic_tfo_reset_loss(tp);
+ }
+}
+
+static void
+tcp_tfo_rcv_probe(struct tcpcb *tp, int tlen)
+{
+ if (tlen == 0) {
+ tp->t_tfo_probe_state = TFO_PROBE_PROBING;
+
+ /*
+ * We send the probe out rather quickly (after one RTO). It does not
+ * really hurt that much, it's only one additional segment on the wire.
+ */
+ tp->t_timer[TCPT_KEEP] = OFFSET_FROM_START(tp, (TCP_REXMTVAL(tp)));
+ } else {
+ /* If SYN/ACK+data, don't probe. We got the data! */
+ tcp_heuristic_tfo_rcv_good(tp);
+ }
+}
+
+static void
+tcp_tfo_rcv_data(struct tcpcb *tp)
+{
+ /* Transition from PROBING to NONE as data has been received */
+ if (tp->t_tfo_probe_state >= TFO_PROBE_PROBING) {
+ tp->t_tfo_probe_state = TFO_PROBE_NONE;
+
+ /* Data has been received - we are good to go! */
+ tcp_heuristic_tfo_rcv_good(tp);
+ }
+}
+
+static void
+tcp_tfo_rcv_ack(struct tcpcb *tp, struct tcphdr *th)
+{
+ if (tp->t_tfo_probe_state == TFO_PROBE_PROBING &&
+ tp->t_tfo_probes > 0) {
+ if (th->th_seq == tp->rcv_nxt) {
+ /* No hole, so stop probing */
+ tp->t_tfo_probe_state = TFO_PROBE_NONE;
+ } else if (SEQ_GT(th->th_seq, tp->rcv_nxt)) {
+ /* There is a hole! Wait a bit for data... */
+ tp->t_tfo_probe_state = TFO_PROBE_WAIT_DATA;
+ tp->t_timer[TCPT_KEEP] = OFFSET_FROM_START(tp,
+ TCP_REXMTVAL(tp));
+ }
+ }
+}
+