git: c9df1a6cf9be - main - tcp: improve SEG.SEQ validation for RST segments
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 03 Aug 2026 11:16:18 UTC
The branch main has been updated by tuexen:
URL: https://cgit.FreeBSD.org/src/commit/?id=c9df1a6cf9be9d44eacc8616ebba1cd19010c7fc
commit c9df1a6cf9be9d44eacc8616ebba1cd19010c7fc
Author: Michael Tuexen <tuexen@FreeBSD.org>
AuthorDate: 2026-08-03 11:07:32 +0000
Commit: Michael Tuexen <tuexen@FreeBSD.org>
CommitDate: 2026-08-03 11:07:32 +0000
tcp: improve SEG.SEQ validation for RST segments
A RST segment can be sent in response to
(a) received segment or
(b) by the upper layer protocol.
The SEG.SEQ validation consists of two checks:
(1) the in-window check of SEG.SEQ and
(2) the exact match check of SEG.SEQ.
For the in-window check (1), the left edge of the window needs to be
based on tp->last_ack_sent to cover the delayed ACK case, whereas the
right edge needs to be based on tp->rcv_nxt + tp->rcv_wnd. This both
assumes that tp->rcv_wnd is not zero. For the special case of
tp->rcv_wnd being zero, add checks against tp->last_ack_sent for (a)
and on tp->rcv_nxt for (b). This applies to all TCP stacks.
When the exact match (2) of SEG.SEQ is performed, it should be based
on tp->last_ack_sent for (a) and on tp->rcv_nxt for (b). To cover both,
check for both. Add this only to the base stack, since the RACK and
BBR stacks already do this.
PR: 296594
Reviewed by: rscheff
MFC after: 3 days
MFC to: stable/14
MFC to: stable/15
Sponsored by: Netflix, Inc.
Differential Revision: https://reviews.freebsd.org/D58594
---
sys/netinet/tcp_input.c | 18 ++++++++++++------
sys/netinet/tcp_stacks/rack_bbr_common.c | 22 ++++++++++++++--------
2 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index bfef374af2d9..1e60774b3fb0 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -2125,20 +2125,26 @@ tcp_do_segment(struct tcpcb *tp, struct mbuf *m, struct tcphdr *th,
* - RST drops connection only if SEG.SEQ == RCV.NXT.
* - If RST is in window, we send challenge ACK.
*
- * Note: to take into account delayed ACKs, we should
- * test against last_ack_sent instead of rcv_nxt.
+ * Note 1: to take into account delayed ACKs, we should
+ * test against last_ack_sent in addition to rcv_nxt.
* Note 2: we handle special case of closed window, not
* covered by the RFC.
+ * Note 3 (XXXMT): check against rcv_adv instead of
+ * tp->rcv_nxt + tp->rcv_wnd.
*/
- if ((SEQ_GEQ(th->th_seq, tp->last_ack_sent) &&
- SEQ_LT(th->th_seq, tp->last_ack_sent + tp->rcv_wnd)) ||
- (tp->rcv_wnd == 0 && tp->last_ack_sent == th->th_seq)) {
+ if ((tp->rcv_wnd > 0 &&
+ SEQ_GEQ(th->th_seq, tp->last_ack_sent) &&
+ SEQ_LT(th->th_seq, tp->rcv_nxt + tp->rcv_wnd)) ||
+ (tp->rcv_wnd == 0 &&
+ (tp->last_ack_sent == th->th_seq ||
+ tp->rcv_nxt == th->th_seq))) {
KASSERT(tp->t_state != TCPS_SYN_SENT,
("%s: TH_RST for TCPS_SYN_SENT th %p tp %p",
__func__, th, tp));
if (V_tcp_insecure_rst ||
- tp->last_ack_sent == th->th_seq) {
+ tp->last_ack_sent == th->th_seq ||
+ tp->rcv_nxt == th->th_seq) {
TCPSTAT_INC(tcps_drops);
/* Drop the connection. */
switch (tp->t_state) {
diff --git a/sys/netinet/tcp_stacks/rack_bbr_common.c b/sys/netinet/tcp_stacks/rack_bbr_common.c
index 51120f96ebce..2022aeb7233e 100644
--- a/sys/netinet/tcp_stacks/rack_bbr_common.c
+++ b/sys/netinet/tcp_stacks/rack_bbr_common.c
@@ -697,18 +697,24 @@ ctf_process_rst(struct mbuf *m, struct tcphdr *th, struct socket *so,
/*
* RFC5961 Section 3.2
*
- * - RST drops connection only if SEG.SEQ == RCV.NXT. - If RST is in
- * window, we send challenge ACK.
+ * - RST drops connection only if SEG.SEQ == RCV.NXT.
+ * - If RST is in window, we send challenge ACK.
*
- * Note: to take into account delayed ACKs, we should test against
- * last_ack_sent instead of rcv_nxt. Note 2: we handle special case
- * of closed window, not covered by the RFC.
+ * Note 1: to take into account delayed ACKs, we should
+ * test against last_ack_sent in addition to rcv_nxt.
+ * Note 2: we handle special case of closed window, not
+ * covered by the RFC.
+ * Note 3 (XXXMT): check against rcv_adv instead of
+ * tp->rcv_nxt + tp->rcv_wnd.
*/
int dropped = 0;
- if ((SEQ_GEQ(th->th_seq, tp->last_ack_sent) &&
- SEQ_LT(th->th_seq, tp->last_ack_sent + tp->rcv_wnd)) ||
- (tp->rcv_wnd == 0 && tp->last_ack_sent == th->th_seq)) {
+ if ((tp->rcv_wnd > 0 &&
+ SEQ_GEQ(th->th_seq, tp->last_ack_sent) &&
+ SEQ_LT(th->th_seq, tp->rcv_nxt + tp->rcv_wnd)) ||
+ (tp->rcv_wnd == 0 &&
+ (tp->last_ack_sent == th->th_seq ||
+ tp->rcv_nxt == th->th_seq))) {
KASSERT(tp->t_state != TCPS_SYN_SENT,
("%s: TH_RST for TCPS_SYN_SENT th %p tp %p",
__func__, th, tp));