History

tcp: accecn: detect loss ACK w/ AccECN option and add TCP_ACCECN_OPTION_PERSIST

Detect spurious retransmission of a previously sent ACK carrying the AccECN option after the second retransmission. Since this might be caused by the middlebox dropping ACK with options it does not recognize, disable the sending of the AccECN option in all subsequent ACKs. This patch follows Section 3.2.3.2.2 of AccECN spec (RFC9768), and a new field (accecn_opt_sent_w_dsack) is added to indicate that an AccECN option was sent with duplicate SACK info. Also, a new AccECN option sending mode is added to tcp_ecn_option sysctl: (TCP_ECN_OPTION_PERSIST), which ignores the AccECN fallback policy and persistently sends AccECN option once it fits into TCP option space.

Chia-Yu Chang committed 9 months ago

Commit 1247fb11 parent 2ed66126 files · +26 −5Browse files

Unified · Split

6 files
  1. Documentation/networking/ip-sysctl.rst +3 −1
  2. include/linux/tcp.h +2 −1
  3. include/net/tcp_ecn.h +2 −0
  4. net/ipv4/sysctl_net_ipv4.c +1 −1
  5. net/ipv4/tcp_input.c +12 −1
  6. net/ipv4/tcp_output.c +6 −1
Documentation/networking/ip-sysctl.rst+3−1
@@ -482,7 +482,9 @@ tcp_ecn_option - INTEGER · 481 unmodified lines
482482 1 Send AccECN option sparingly according to the minimum option
483483 rules outlined in draft-ietf-tcpm-accurate-ecn.
484484 2 Send AccECN option on every packet whenever it fits into TCP
485− option space.
485+ option space except when AccECN fallback is triggered.
486+ 3 Send AccECN option on every packet whenever it fits into TCP
487+ option space even when AccECN fallback is triggered.
486488 = ============================================================
487489
488490 Default: 2
include/linux/tcp.h+2−1
@@ -291,7 +291,8 @@ struct tcp_sock { · 290 unmodified lines
291291 u8 nonagle : 4,/* Disable Nagle algorithm? */
292292 rate_app_limited:1; /* rate_{delivered,interval_us} limited? */
293293 u8 received_ce_pending:4, /* Not yet transmit cnt of received_ce */
294− unused2:4;
294+ accecn_opt_sent_w_dsack:1,/* Sent ACCECN opt in previous ACK w/ D-SACK */
295+ unused2:3;
295296 u8 accecn_minlen:2,/* Minimum length of AccECN option sent */
296297 est_ecnfield:2,/* ECN field for AccECN delivered estimates */
297298 accecn_opt_demand:2,/* Demand AccECN option for n next ACKs */
include/net/tcp_ecn.h+2−0
@@ -29,6 +29,7 @@ enum tcp_accecn_option { · 28 unmodified lines
2929 TCP_ACCECN_OPTION_DISABLED = 0,
3030 TCP_ACCECN_OPTION_MINIMUM = 1,
3131 TCP_ACCECN_OPTION_FULL = 2,
32+ TCP_ACCECN_OPTION_PERSIST = 3,
3233 };
3334
3435 /* Apply either ECT(0) or ECT(1) based on TCP_CONG_ECT_1_NEGOTIATION flag */
@@ -406,6 +407,7 @@ static inline void tcp_accecn_init_counters(struct tcp_sock *tp) · 371 unmodified lines
406407 tp->received_ce_pending = 0;
407408 __tcp_accecn_init_bytes_counters(tp->received_ecn_bytes);
408409 __tcp_accecn_init_bytes_counters(tp->delivered_ecn_bytes);
410+ tp->accecn_opt_sent_w_dsack = 0;
409411 tp->accecn_minlen = 0;
410412 tp->accecn_opt_demand = 0;
411413 tp->est_ecnfield = 0;
net/ipv4/sysctl_net_ipv4.c+1−1
@@ -749,7 +749,7 @@ static struct ctl_table ipv4_net_table[] = { · 748 unmodified lines
749749 .mode = 0644,
750750 .proc_handler = proc_dou8vec_minmax,
751751 .extra1 = SYSCTL_ZERO,
752− .extra2 = SYSCTL_TWO,
752+ .extra2 = SYSCTL_THREE,
753753 },
754754 {
755755 .procname = "tcp_ecn_option_beacon",
net/ipv4/tcp_input.c+12−1
@@ -5046,8 +5046,11 @@ static void tcp_dsack_extend(struct sock *sk, u32 seq, u32 end_seq) · 5045 unmodified lines
50465046 tcp_sack_extend(tp->duplicate_sack, seq, end_seq);
50475047 }
50485048
5049−static void tcp_rcv_spurious_retrans(struct sock *sk, const struct sk_buff *skb)
5049+static void tcp_rcv_spurious_retrans(struct sock *sk,
5050+ const struct sk_buff *skb)
50505051 {
5052+ struct tcp_sock *tp = tcp_sk(sk);
5053+
50515054 /* When the ACK path fails or drops most ACKs, the sender would
50525055 * timeout and spuriously retransmit the same segment repeatedly.
50535056 * If it seems our ACKs are not reaching the other side,
@@ -5067,6 +5070,14 @@ static void tcp_rcv_spurious_retrans(struct sock *sk, const struct sk_buff *skb) · 13 unmodified lines
50675070 /* Save last flowlabel after a spurious retrans. */
50685071 tcp_save_lrcv_flowlabel(sk, skb);
50695072 #endif
5073+ /* Check DSACK info to detect that the previous ACK carrying the
5074+ * AccECN option was lost after the second retransmision, and then
5075+ * stop sending AccECN option in all subsequent ACKs.
5076+ */
5077+ if (tcp_ecn_mode_accecn(tp) &&
5078+ tp->accecn_opt_sent_w_dsack &&
5079+ TCP_SKB_CB(skb)->seq == tp->duplicate_sack[0].start_seq)
5080+ tcp_accecn_fail_mode_set(tp, TCP_ACCECN_OPT_FAIL_SEND);
50705081 }
50715082
50725083 static void tcp_send_dupack(struct sock *sk, const struct sk_buff *skb)
net/ipv4/tcp_output.c+6−1
@@ -715,9 +715,12 @@ static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp, · 714 unmodified lines
715715 if (tp) {
716716 tp->accecn_minlen = 0;
717717 tp->accecn_opt_tstamp = tp->tcp_mstamp;
718+ tp->accecn_opt_sent_w_dsack = tp->rx_opt.dsack;
718719 if (tp->accecn_opt_demand)
719720 tp->accecn_opt_demand--;
720721 }
722+ } else if (tp) {
723+ tp->accecn_opt_sent_w_dsack = 0;
721724 }
722725
723726 if (unlikely(OPTION_SACK_ADVERTISE & options)) {
@@ -1189,7 +1192,9 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb · 465 unmodified lines
11891192 if (tcp_ecn_mode_accecn(tp)) {
11901193 int ecn_opt = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_ecn_option);
11911194
1192− if (ecn_opt && tp->saw_accecn_opt && !tcp_accecn_opt_fail_send(tp) &&
1195+ if (ecn_opt && tp->saw_accecn_opt &&
1196+ (ecn_opt >= TCP_ACCECN_OPTION_PERSIST ||
1197+ !tcp_accecn_opt_fail_send(tp)) &&
11931198 (ecn_opt >= TCP_ACCECN_OPTION_FULL || tp->accecn_opt_demand ||
11941199 tcp_accecn_option_beacon_check(sk))) {
11951200 opts->use_synack_ecn_bytes = 0;