git: 25d9ade9aa50 - stable/14 - tcp: implement challenge ACK throttling for the base stack

From: Michael Tuexen <tuexen_at_FreeBSD.org>
Date: Sat, 03 Aug 2024 23:13:05 UTC
The branch stable/14 has been updated by tuexen:

URL: https://cgit.FreeBSD.org/src/commit/?id=25d9ade9aa50b22eb5fb0b6aaabbc808fb076c18

commit 25d9ade9aa50b22eb5fb0b6aaabbc808fb076c18
Author:     Michael Tuexen <tuexen@FreeBSD.org>
AuthorDate: 2024-07-25 11:54:52 +0000
Commit:     Michael Tuexen <tuexen@FreeBSD.org>
CommitDate: 2024-08-03 23:12:28 +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
    Sponsored by:           Netflix, Inc.
    Differential Revision:  https://reviews.freebsd.org/D46066
    
    (cherry picked from commit 40299c55a05ff008102e24269d5f2d7fa7b6842d)
---
 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 cb72ddbef947..500d208b7756 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -2187,10 +2187,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;
 			}
 		}
@@ -2212,10 +2209,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;
@@ -2453,10 +2447,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 9fc744c42e0c..e2b120223bc0 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -2222,6 +2222,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 e7cccd60a95e..f41fdaca13ac 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -461,6 +461,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];
@@ -1469,6 +1474,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 *);