The branch main has been updated by tuexen:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=40299c55a05ff008102e24269d5f2d7fa7b6842d

commit 40299c55a05ff008102e24269d5f2d7fa7b6842d
Author:     Michael Tuexen <tue...@freebsd.org>
AuthorDate: 2024-07-25 11:54:52 +0000
Commit:     Michael Tuexen <tue...@freebsd.org>
CommitDate: 2024-07-25 11:54:52 +0000

    tcp: implement challenge ACK throttling for the base stack
    
    Implement ACK throttling of challenge ACKs as described in RFC 5961.
    
    Reviewed by:            Peter Lei, rscheff, cc
    MFC after:              1 week
    Sponsored by:           Netflix, Inc.
    Differential Revision:  https://reviews.freebsd.org/D46066
---
 sys/netinet/tcp_input.c | 15 +++------------
 sys/netinet/tcp_subr.c  | 39 +++++++++++++++++++++++++++++++++++++++
 sys/netinet/tcp_var.h   |  6 ++++++
 3 files changed, 48 insertions(+), 12 deletions(-)

diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index ad17a9fdfb93..b64a71837ab4 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -2208,10 +2208,7 @@ tcp_do_segment(struct tcpcb *tp, struct mbuf *m, struct 
tcphdr *th,
                                }
                        } else {
                                TCPSTAT_INC(tcps_badrst);
-                               /* Send challenge ACK. */
-                               tcp_respond(tp, mtod(m, void *), th, m,
-                                   tp->rcv_nxt, tp->snd_nxt, TH_ACK);
-                               tp->last_ack_sent = tp->rcv_nxt;
+                               tcp_send_challenge_ack(tp, th, m);
                                m = NULL;
                        }
                }
@@ -2233,10 +2230,7 @@ tcp_do_segment(struct tcpcb *tp, struct mbuf *m, struct 
tcphdr *th,
                        rstreason = BANDLIM_UNLIMITED;
                } else {
                        tcp_ecn_input_syn_sent(tp, thflags, iptos);
-                       /* Send challenge ACK. */
-                       tcp_respond(tp, mtod(m, void *), th, m, tp->rcv_nxt,
-                           tp->snd_nxt, TH_ACK);
-                       tp->last_ack_sent = tp->rcv_nxt;
+                       tcp_send_challenge_ack(tp, th, m);
                        m = NULL;
                }
                goto drop;
@@ -2474,10 +2468,7 @@ tcp_do_segment(struct tcpcb *tp, struct mbuf *m, struct 
tcphdr *th,
                                TCPSTAT_INC(tcps_rcvghostack);
                        else
                                TCPSTAT_INC(tcps_rcvacktooold);
-                       /* Send a challenge ACK. */
-                       tcp_respond(tp, mtod(m, void *), th, m,
-                           tp->rcv_nxt, tp->snd_nxt, TH_ACK);
-                       tp->last_ack_sent = tp->rcv_nxt;
+                       tcp_send_challenge_ack(tp, th, m);
                        m = NULL;
                        goto drop;
                }
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
index 42d29d64e09f..0665564955d2 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -2181,6 +2181,45 @@ tcp_respond(struct tcpcb *tp, void *ipgen, struct tcphdr 
*th, struct mbuf *m,
                lgb->tlb_errno = output_ret;
 }
 
+/*
+ * Send a challenge ack (no data, no SACK option), but not more than
+ * tcp_ack_war_cnt per tcp_ack_war_time_window (per TCP connection).
+ */
+void
+tcp_send_challenge_ack(struct tcpcb *tp, struct tcphdr *th, struct mbuf *m)
+{
+       sbintime_t now;
+       bool send_challenge_ack;
+
+       if (tcp_ack_war_time_window == 0 || tcp_ack_war_cnt == 0) {
+               /* ACK war protection is disabled. */
+               send_challenge_ack = true;
+       } else {
+               /* Start new epoch, if the previous one is already over. */
+               now = getsbinuptime();
+               if (tp->t_challenge_ack_end < now) {
+                       tp->t_challenge_ack_cnt = 0;
+                       tp->t_challenge_ack_end = now +
+                           tcp_ack_war_time_window * SBT_1MS;
+               }
+               /*
+                * Send a challenge ACK, if less than tcp_ack_war_cnt have been
+                * sent in the current epoch.
+                */
+               if (tp->t_challenge_ack_cnt < tcp_ack_war_cnt) {
+                       send_challenge_ack = true;
+                       tp->t_challenge_ack_cnt++;
+               } else {
+                       send_challenge_ack = false;
+               }
+       }
+       if (send_challenge_ack) {
+               tcp_respond(tp, mtod(m, void *), th, m, tp->rcv_nxt,
+                   tp->snd_nxt, TH_ACK);
+               tp->last_ack_sent = tp->rcv_nxt;
+       }
+}
+
 /*
  * Create a new TCP control block, making an empty reassembly queue and hooking
  * it to the argument protocol control block.  The `inp' parameter must have
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
index c06efa601a81..af82d0bfeaaa 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -465,6 +465,11 @@ struct tcpcb {
        /* TCP Fast Open */
        uint8_t t_tfo_client_cookie_len; /* TFO client cookie length */
        uint32_t t_end_info_status;     /* Status flag of end info */
+       sbintime_t t_challenge_ack_end; /* End of the challenge ack epoch */
+       uint32_t t_challenge_ack_cnt;   /* Number of challenge ACKs sent in
+                                        * current epoch
+                                        */
+
        unsigned int *t_tfo_pending;    /* TFO server pending counter */
        union {
                uint8_t client[TCP_FASTOPEN_MAX_COOKIE_LEN];
@@ -1460,6 +1465,7 @@ int        tcp_default_output(struct tcpcb *);
 void    tcp_state_change(struct tcpcb *, int);
 void    tcp_respond(struct tcpcb *, void *,
            struct tcphdr *, struct mbuf *, tcp_seq, tcp_seq, uint16_t);
+void    tcp_send_challenge_ack(struct tcpcb *, struct tcphdr *, struct mbuf *);
 bool    tcp_twcheck(struct inpcb *, struct tcpopt *, struct tcphdr *,
            struct mbuf *, int);
 void    tcp_setpersist(struct tcpcb *);

Reply via email to