git: c9df1a6cf9be - main - tcp: improve SEG.SEQ validation for RST segments
Michael Tuexen <[email protected]> Mon, 03 Aug 2026 11:16:18 +0000
| Newsgroups | gmane.os.freebsd.devel.cvs.src |
|---|---|
| Message-ID | <6a707882.188a3.505d6563__5037.2992687457$1785755817$gmane$org@gitrepo.freebsd.org> |
The branch main has been updated by tuexen: URL: https://cgit.FreeBSD.org/src/commit/?id=c9df1a6cf9be9d44eacc8616ebba1cd19010c7fc commit c9df1a6cf9be9d44eacc8616ebba1cd19010c7fc Author: Michael Tuexen <[email protected]> AuthorDate: 2026-08-03 11:07:32 +0000 Commit: Michael Tuexen <[email protected]> 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));