From: Chia-Yu Chang chia-yu.chang@nokia-bell-labs.com
After successfully negotiating AccECN mode in the handshake, check the ACE field of the first data ACK. If zero (0b000), non-ECT packets are sent and any response to CE marking feedback is disabled. This follows Section 3.2.2.4 of AccECN spec (RFC9768).
Signed-off-by: Chia-Yu Chang chia-yu.chang@nokia-bell-labs.com --- net/ipv4/tcp_input.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 752251268cbe..f250b4912a0b 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -495,7 +495,7 @@ static void tcp_count_delivered(struct tcp_sock *tp, u32 delivered, /* Returns the ECN CE delta */ static u32 __tcp_accecn_process(struct sock *sk, const struct sk_buff *skb, u32 delivered_pkts, u32 delivered_bytes, - int flag) + u64 prior_bytes_acked, int flag) { u32 old_ceb = tcp_sk(sk)->delivered_ecn_bytes[INET_ECN_CE - 1]; const struct tcphdr *th = tcp_hdr(skb); @@ -534,6 +534,16 @@ static u32 __tcp_accecn_process(struct sock *sk, const struct sk_buff *skb, if (flag & FLAG_SYN_ACKED) return 0;
+ /* Verify ACE!=0 in the 1st data ACK after AccECN negotiation */ + if ((flag & FLAG_DATA_ACKED) && prior_bytes_acked <= tp->mss_cache) { + if (tcp_accecn_ace(tcp_hdr(skb)) == 0x0) { + INET_ECN_dontxmit(sk); + tcp_accecn_fail_mode_set(tp, TCP_ACCECN_ACE_FAIL_RECV | + TCP_ACCECN_OPT_FAIL_RECV); + return 0; + } + } + if (tp->received_ce_pending >= TCP_ACCECN_ACE_MAX_DELTA) inet_csk(sk)->icsk_ack.pending |= ICSK_ACK_NOW;
@@ -580,13 +590,14 @@ static u32 __tcp_accecn_process(struct sock *sk, const struct sk_buff *skb,
static void tcp_accecn_process(struct sock *sk, struct rate_sample *rs, const struct sk_buff *skb, u32 delivered_pkts, - u32 delivered_bytes, int *flag) + u32 delivered_bytes, u64 prior_bytes_acked, + int *flag) { struct tcp_sock *tp = tcp_sk(sk); u32 delta;
delta = __tcp_accecn_process(sk, skb, delivered_pkts, - delivered_bytes, *flag); + delivered_bytes, prior_bytes_acked, *flag); if (delta > 0) { tcp_count_delivered_ce(tp, delta); *flag |= FLAG_ECE; @@ -3997,6 +4008,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) struct tcp_sock *tp = tcp_sk(sk); struct tcp_sacktag_state sack_state; struct rate_sample rs = { .prior_delivered = 0, .ece_delta = 0 }; + u64 prior_bytes_acked = tp->bytes_acked; u32 prior_snd_una = tp->snd_una; bool is_sack_reneg = tp->is_sack_reneg; u32 ack_seq = TCP_SKB_CB(skb)->seq; @@ -4116,7 +4128,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
if (tcp_ecn_mode_accecn(tp)) tcp_accecn_process(sk, &rs, skb, tp->delivered - delivered, - sack_state.delivered_bytes, &flag); + sack_state.delivered_bytes, + prior_bytes_acked, &flag);
tcp_in_ack_event(sk, flag);
@@ -4154,7 +4167,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) no_queue: if (tcp_ecn_mode_accecn(tp)) tcp_accecn_process(sk, &rs, skb, tp->delivered - delivered, - sack_state.delivered_bytes, &flag); + sack_state.delivered_bytes, + prior_bytes_acked, &flag); tcp_in_ack_event(sk, flag); /* If data was DSACKed, see if we can undo a cwnd reduction. */ if (flag & FLAG_DSACKING_ACK) {