aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Tuexen <tuexen@FreeBSD.org>2024-07-25 11:54:52 +0000
committerMichael Tuexen <tuexen@FreeBSD.org>2024-07-25 11:54:52 +0000
commit40299c55a05ff008102e24269d5f2d7fa7b6842d (patch)
treed14629f3642cb48eb9c752f1625136f012782b93
parent399362bac312d4fa77a3fd918ea002c0782bc315 (diff)
downloadsrc-40299c55a05ff008102e24269d5f2d7fa7b6842d.tar.gz
src-40299c55a05ff008102e24269d5f2d7fa7b6842d.zip
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
-rw-r--r--sys/netinet/tcp_input.c15
-rw-r--r--sys/netinet/tcp_subr.c39
-rw-r--r--sys/netinet/tcp_var.h6
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
@@ -2182,6 +2182,45 @@ tcp_respond(struct tcpcb *tp, void *ipgen, struct tcphdr *th, struct mbuf *m,
}
/*
+ * 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
* come from the zone allocator set up by tcpcbstor declaration.
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 *);