Commit 63803c4a authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'tcp-add-a-new-paws_ack-drop-reason'

Eric Dumazet says:

====================
tcp: add a new PAWS_ACK drop reason

Current TCP_RFC7323_PAWS drop reason is too generic and can
cause confusion.

One common source for these drops are ACK packets coming too late.

A prior packet with payload already changed tp->rcv_nxt.

Add TCP_RFC7323_PAWS_ACK new drop reason, and do not
generate a DUPACK for such old ACK.
====================

Link: https://patch.msgid.link/20250113135558.3180360-1-edumazet@google.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 652aac7e d16b3447
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@
	FN(TCP_OVERWINDOW)		\
	FN(TCP_OFOMERGE)		\
	FN(TCP_RFC7323_PAWS)		\
	FN(TCP_RFC7323_PAWS_ACK)	\
	FN(TCP_OLD_SEQUENCE)		\
	FN(TCP_INVALID_SEQUENCE)	\
	FN(TCP_INVALID_ACK_SEQUENCE)	\
@@ -259,6 +260,11 @@ enum skb_drop_reason {
	 * LINUX_MIB_PAWSESTABREJECTED, LINUX_MIB_PAWSACTIVEREJECTED
	 */
	SKB_DROP_REASON_TCP_RFC7323_PAWS,
	/**
	 * @SKB_DROP_REASON_TCP_RFC7323_PAWS_ACK: PAWS check, old ACK packet.
	 * Corresponds to LINUX_MIB_PAWS_OLD_ACK.
	 */
	SKB_DROP_REASON_TCP_RFC7323_PAWS_ACK,
	/** @SKB_DROP_REASON_TCP_OLD_SEQUENCE: Old SEQ field (duplicate packet) */
	SKB_DROP_REASON_TCP_OLD_SEQUENCE,
	/** @SKB_DROP_REASON_TCP_INVALID_SEQUENCE: Not acceptable SEQ field */
+1 −0
Original line number Diff line number Diff line
@@ -186,6 +186,7 @@ enum
	LINUX_MIB_TIMEWAITKILLED,		/* TimeWaitKilled */
	LINUX_MIB_PAWSACTIVEREJECTED,		/* PAWSActiveRejected */
	LINUX_MIB_PAWSESTABREJECTED,		/* PAWSEstabRejected */
	LINUX_MIB_PAWS_OLD_ACK,			/* PAWSOldAck */
	LINUX_MIB_DELAYEDACKS,			/* DelayedACKs */
	LINUX_MIB_DELAYEDACKLOCKED,		/* DelayedACKLocked */
	LINUX_MIB_DELAYEDACKLOST,		/* DelayedACKLost */
+1 −0
Original line number Diff line number Diff line
@@ -189,6 +189,7 @@ static const struct snmp_mib snmp4_net_list[] = {
	SNMP_MIB_ITEM("TWKilled", LINUX_MIB_TIMEWAITKILLED),
	SNMP_MIB_ITEM("PAWSActive", LINUX_MIB_PAWSACTIVEREJECTED),
	SNMP_MIB_ITEM("PAWSEstab", LINUX_MIB_PAWSESTABREJECTED),
	SNMP_MIB_ITEM("PAWSOldAck", LINUX_MIB_PAWS_OLD_ACK),
	SNMP_MIB_ITEM("DelayedACKs", LINUX_MIB_DELAYEDACKS),
	SNMP_MIB_ITEM("DelayedACKLocked", LINUX_MIB_DELAYEDACKLOCKED),
	SNMP_MIB_ITEM("DelayedACKLost", LINUX_MIB_DELAYEDACKLOST),
+51 −33
Original line number Diff line number Diff line
@@ -4450,34 +4450,40 @@ static u32 tcp_tsval_replay(const struct sock *sk)
	return inet_csk(sk)->icsk_rto * 1200 / HZ;
}

static int tcp_disordered_ack(const struct sock *sk, const struct sk_buff *skb)
static enum skb_drop_reason tcp_disordered_ack_check(const struct sock *sk,
						     const struct sk_buff *skb)
{
	const struct tcp_sock *tp = tcp_sk(sk);
	const struct tcphdr *th = tcp_hdr(skb);
	u32 seq = TCP_SKB_CB(skb)->seq;
	SKB_DR_INIT(reason, TCP_RFC7323_PAWS);
	u32 ack = TCP_SKB_CB(skb)->ack_seq;
	u32 seq = TCP_SKB_CB(skb)->seq;

	return	/* 1. Pure ACK with correct sequence number. */
		(th->ack && seq == TCP_SKB_CB(skb)->end_seq && seq == tp->rcv_nxt) &&
	/* 1. Is this not a pure ACK ? */
	if (!th->ack || seq != TCP_SKB_CB(skb)->end_seq)
		return reason;

		/* 2. ... and duplicate ACK. */
		ack == tp->snd_una &&
	/* 2. Is its sequence not the expected one ? */
	if (seq != tp->rcv_nxt)
		return before(seq, tp->rcv_nxt) ?
			SKB_DROP_REASON_TCP_RFC7323_PAWS_ACK :
			reason;

		/* 3. ... and does not update window. */
		!tcp_may_update_window(tp, ack, seq, ntohs(th->window) << tp->rx_opt.snd_wscale) &&
	/* 3. Is this not a duplicate ACK ? */
	if (ack != tp->snd_una)
		return reason;

		/* 4. ... and sits in replay window. */
		(s32)(tp->rx_opt.ts_recent - tp->rx_opt.rcv_tsval) <=
		tcp_tsval_replay(sk);
}
	/* 4. Is this updating the window ? */
	if (tcp_may_update_window(tp, ack, seq, ntohs(th->window) <<
						tp->rx_opt.snd_wscale))
		return reason;

static inline bool tcp_paws_discard(const struct sock *sk,
				   const struct sk_buff *skb)
{
	const struct tcp_sock *tp = tcp_sk(sk);
	/* 5. Is this not in the replay window ? */
	if ((s32)(tp->rx_opt.ts_recent - tp->rx_opt.rcv_tsval) >
	    tcp_tsval_replay(sk))
		return reason;

	return !tcp_paws_check(&tp->rx_opt, TCP_PAWS_WINDOW) &&
	       !tcp_disordered_ack(sk, skb);
	return 0;
}

/* Check segment sequence number for validity.
@@ -5949,23 +5955,35 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
	SKB_DR(reason);

	/* RFC1323: H1. Apply PAWS check first. */
	if (tcp_fast_parse_options(sock_net(sk), skb, th, tp) &&
	    tp->rx_opt.saw_tstamp &&
	    tcp_paws_discard(sk, skb)) {
		if (!th->rst) {
	if (!tcp_fast_parse_options(sock_net(sk), skb, th, tp) ||
	    !tp->rx_opt.saw_tstamp ||
	    tcp_paws_check(&tp->rx_opt, TCP_PAWS_WINDOW))
		goto step1;

	reason = tcp_disordered_ack_check(sk, skb);
	if (!reason)
		goto step1;
	/* Reset is accepted even if it did not pass PAWS. */
	if (th->rst)
		goto step1;
	if (unlikely(th->syn))
		goto syn_challenge;

	/* Old ACK are common, increment PAWS_OLD_ACK
	 * and do not send a dupack.
	 */
	if (reason == SKB_DROP_REASON_TCP_RFC7323_PAWS_ACK) {
		NET_INC_STATS(sock_net(sk), LINUX_MIB_PAWS_OLD_ACK);
		goto discard;
	}
	NET_INC_STATS(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
	if (!tcp_oow_rate_limited(sock_net(sk), skb,
				  LINUX_MIB_TCPACKSKIPPEDPAWS,
				  &tp->last_oow_ack_time))
		tcp_send_dupack(sk, skb);
			SKB_DR_SET(reason, TCP_RFC7323_PAWS);
	goto discard;
		}
		/* Reset is accepted even if it did not pass PAWS. */
	}

step1:
	/* Step 1: check sequence number */
	reason = tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
	if (reason) {