aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Scheffenegger <rscheff@FreeBSD.org>2022-08-31 12:49:25 +0000
committerMark Johnston <markj@FreeBSD.org>2022-11-01 13:28:11 +0000
commitdd35207e2025fd0056417ad4d7846086b09cf059 (patch)
treeb364c34c0f8601876e2c7577e4907b4e360f96fe
parentfff5c5fe911e7067390e6ff73c4c58489a30c849 (diff)
downloadsrc-dd35207e2025fd0056417ad4d7846086b09cf059.tar.gz
src-dd35207e2025fd0056417ad4d7846086b09cf059.zip
tcp: finish SACK loss recovery on sudden lack of SACK blocks
While a receiver should continue sending SACK blocks for the duration of a SACK loss recovery, if for some reason the TCP options no longer contain these SACK blocks, but we already started maintaining the Scoreboard, keep on handling incoming ACKs (without SACK) as belonging to the SACK recovery. Approved by: so Security: FreeBSD-EN-22:25.tcp Reported by: thj Reviewed by: tuexen, #transport MFC after: 2 weeks Sponsored by: NetApp, Inc. Differential Revision: https://reviews.freebsd.org/D36046 (cherry picked from commit c21b7b55bea2cc2bf3b420c70a9018e703ed6f00) (cherry picked from commit 2b8ee332b9384596cefc91fead1c294fdd9252b4)
-rw-r--r--sys/netinet/tcp_input.c39
1 files changed, 22 insertions, 17 deletions
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index 2c7d15368483..b1846822b207 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -287,6 +287,20 @@ kmod_tcpstat_add(int statnum, int val)
counter_u64_add(VNET(tcpstat)[statnum], val);
}
+/*
+ * Make sure that we only start a SACK loss recovery when
+ * receiving a duplicate ACK with a SACK block, and also
+ * complete SACK loss recovery in case the other end
+ * reneges.
+ */
+static bool inline
+tcp_is_sack_recovery(struct tcpcb *tp, struct tcpopt *to)
+{
+ return ((tp->t_flags & TF_SACK_PERMIT) &&
+ ((to->to_flags & TOF_SACK) ||
+ (!TAILQ_EMPTY(&tp->snd_holes))));
+}
+
#ifdef TCP_HHOOK
/*
* Wrapper for the TCP established input helper hook.
@@ -2525,9 +2539,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
TCPSTAT_INC(tcps_rcvacktoomuch);
goto dropafterack;
}
- if ((tp->t_flags & TF_SACK_PERMIT) &&
- ((to.to_flags & TOF_SACK) ||
- !TAILQ_EMPTY(&tp->snd_holes)))
+ if (tcp_is_sack_recovery(tp, &to))
sack_changed = tcp_sack_doack(tp, &to, th->th_ack);
else
/*
@@ -2597,8 +2609,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
* duplicating packets or a possible DoS attack.
*/
if (th->th_ack != tp->snd_una ||
- ((tp->t_flags & TF_SACK_PERMIT) &&
- (to.to_flags & TOF_SACK) &&
+ (tcp_is_sack_recovery(tp, &to) &&
!sack_changed))
break;
else if (!tcp_timer_active(tp, TT_REXMT))
@@ -2610,8 +2621,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
if (V_tcp_do_prr &&
IN_FASTRECOVERY(tp->t_flags)) {
tcp_do_prr_ack(tp, th, &to);
- } else if ((tp->t_flags & TF_SACK_PERMIT) &&
- (to.to_flags & TOF_SACK) &&
+ } else if (tcp_is_sack_recovery(tp, &to) &&
IN_FASTRECOVERY(tp->t_flags)) {
int awnd;
@@ -2684,8 +2694,7 @@ enter_recovery:
* snd_ssthresh is already updated by
* cc_cong_signal.
*/
- if ((tp->t_flags & TF_SACK_PERMIT) &&
- (to.to_flags & TOF_SACK)) {
+ if (tcp_is_sack_recovery(tp, &to)) {
tp->sackhint.prr_delivered =
tp->sackhint.sacked_bytes;
} else {
@@ -2697,8 +2706,7 @@ enter_recovery:
tp->sackhint.recover_fs = max(1,
tp->snd_nxt - tp->snd_una);
}
- if ((tp->t_flags & TF_SACK_PERMIT) &&
- (to.to_flags & TOF_SACK)) {
+ if (tcp_is_sack_recovery(tp, &to)) {
TCPSTAT_INC(
tcps_sack_recovery_episode);
tp->snd_recover = tp->snd_nxt;
@@ -2790,8 +2798,7 @@ enter_recovery:
* from the left side. Such partial ACKs should not be
* counted as dupacks here.
*/
- if ((tp->t_flags & TF_SACK_PERMIT) &&
- (to.to_flags & TOF_SACK) &&
+ if (tcp_is_sack_recovery(tp, &to) &&
sack_changed) {
tp->t_dupacks++;
/* limit overhead by setting maxseg last */
@@ -3973,8 +3980,7 @@ tcp_do_prr_ack(struct tcpcb *tp, struct tcphdr *th, struct tcpopt *to)
* (del_data) and an estimate of how many bytes are in the
* network.
*/
- if (((tp->t_flags & TF_SACK_PERMIT) &&
- (to->to_flags & TOF_SACK)) ||
+ if (tcp_is_sack_recovery(tp, to) ||
(IN_CONGRECOVERY(tp->t_flags) &&
!IN_FASTRECOVERY(tp->t_flags))) {
del_data = tp->sackhint.delivered_data;
@@ -4018,8 +4024,7 @@ tcp_do_prr_ack(struct tcpcb *tp, struct tcphdr *th, struct tcpopt *to)
* accordingly.
*/
if (IN_FASTRECOVERY(tp->t_flags)) {
- if ((tp->t_flags & TF_SACK_PERMIT) &&
- (to->to_flags & TOF_SACK)) {
+ if (tcp_is_sack_recovery(tp, to)) {
tp->snd_cwnd = tp->snd_nxt - tp->snd_recover +
tp->sackhint.sack_bytes_rexmit +
(snd_cnt * maxseg);