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
482 1 Send AccECN option sparingly according to the minimum option482 1 Send AccECN option sparingly according to the minimum option
483 rules outlined in draft-ietf-tcpm-accurate-ecn.483 rules outlined in draft-ietf-tcpm-accurate-ecn.
484 2 Send AccECN option on every packet whenever it fits into TCP484 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.
486 = ============================================================488 = ============================================================
487 489
488 Default: 2490 Default: 2
include/linux/tcp.h+2−1
@@ -291,7 +291,8 @@ struct tcp_sock { · 290 unmodified lines
291 u8 nonagle : 4,/* Disable Nagle algorithm? */291 u8 nonagle : 4,/* Disable Nagle algorithm? */
292 rate_app_limited:1; /* rate_{delivered,interval_us} limited? */292 rate_app_limited:1; /* rate_{delivered,interval_us} limited? */
293 u8 received_ce_pending:4, /* Not yet transmit cnt of received_ce */293 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;
295 u8 accecn_minlen:2,/* Minimum length of AccECN option sent */296 u8 accecn_minlen:2,/* Minimum length of AccECN option sent */
296 est_ecnfield:2,/* ECN field for AccECN delivered estimates */297 est_ecnfield:2,/* ECN field for AccECN delivered estimates */
297 accecn_opt_demand:2,/* Demand AccECN option for n next ACKs */298 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
29 TCP_ACCECN_OPTION_DISABLED = 0,29 TCP_ACCECN_OPTION_DISABLED = 0,
30 TCP_ACCECN_OPTION_MINIMUM = 1,30 TCP_ACCECN_OPTION_MINIMUM = 1,
31 TCP_ACCECN_OPTION_FULL = 2,31 TCP_ACCECN_OPTION_FULL = 2,
32 TCP_ACCECN_OPTION_PERSIST = 3,
32};33};
33 34
34/* Apply either ECT(0) or ECT(1) based on TCP_CONG_ECT_1_NEGOTIATION flag */35/* 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
406 tp->received_ce_pending = 0;407 tp->received_ce_pending = 0;
407 __tcp_accecn_init_bytes_counters(tp->received_ecn_bytes);408 __tcp_accecn_init_bytes_counters(tp->received_ecn_bytes);
408 __tcp_accecn_init_bytes_counters(tp->delivered_ecn_bytes);409 __tcp_accecn_init_bytes_counters(tp->delivered_ecn_bytes);
410 tp->accecn_opt_sent_w_dsack = 0;
409 tp->accecn_minlen = 0;411 tp->accecn_minlen = 0;
410 tp->accecn_opt_demand = 0;412 tp->accecn_opt_demand = 0;
411 tp->est_ecnfield = 0;413 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
749 .mode = 0644,749 .mode = 0644,
750 .proc_handler = proc_dou8vec_minmax,750 .proc_handler = proc_dou8vec_minmax,
751 .extra1 = SYSCTL_ZERO,751 .extra1 = SYSCTL_ZERO,
752 .extra2 = SYSCTL_TWO,752 .extra2 = SYSCTL_THREE,
753 },753 },
754 {754 {
755 .procname = "tcp_ecn_option_beacon",755 .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
5046 tcp_sack_extend(tp->duplicate_sack, seq, end_seq);5046 tcp_sack_extend(tp->duplicate_sack, seq, end_seq);
5047}5047}
5048 5048
5049static void tcp_rcv_spurious_retrans(struct sock *sk, const struct sk_buff *skb)5049static void tcp_rcv_spurious_retrans(struct sock *sk,
5050 const struct sk_buff *skb)
5050{5051{
5052 struct tcp_sock *tp = tcp_sk(sk);
5053
5051 /* When the ACK path fails or drops most ACKs, the sender would5054 /* When the ACK path fails or drops most ACKs, the sender would
5052 * timeout and spuriously retransmit the same segment repeatedly.5055 * timeout and spuriously retransmit the same segment repeatedly.
5053 * If it seems our ACKs are not reaching the other side,5056 * 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
5067 /* Save last flowlabel after a spurious retrans. */5070 /* Save last flowlabel after a spurious retrans. */
5068 tcp_save_lrcv_flowlabel(sk, skb);5071 tcp_save_lrcv_flowlabel(sk, skb);
5069#endif5072#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);
5070}5081}
5071 5082
5072static void tcp_send_dupack(struct sock *sk, const struct sk_buff *skb)5083static 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
715 if (tp) {715 if (tp) {
716 tp->accecn_minlen = 0;716 tp->accecn_minlen = 0;
717 tp->accecn_opt_tstamp = tp->tcp_mstamp;717 tp->accecn_opt_tstamp = tp->tcp_mstamp;
718 tp->accecn_opt_sent_w_dsack = tp->rx_opt.dsack;
718 if (tp->accecn_opt_demand)719 if (tp->accecn_opt_demand)
719 tp->accecn_opt_demand--;720 tp->accecn_opt_demand--;
720 }721 }
722 } else if (tp) {
723 tp->accecn_opt_sent_w_dsack = 0;
721 }724 }
722 725
723 if (unlikely(OPTION_SACK_ADVERTISE & options)) {726 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
1189 if (tcp_ecn_mode_accecn(tp)) {1192 if (tcp_ecn_mode_accecn(tp)) {
1190 int ecn_opt = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_ecn_option);1193 int ecn_opt = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_ecn_option);
1191 1194
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)) &&
1193 (ecn_opt >= TCP_ACCECN_OPTION_FULL || tp->accecn_opt_demand ||1198 (ecn_opt >= TCP_ACCECN_OPTION_FULL || tp->accecn_opt_demand ||
1194 tcp_accecn_option_beacon_check(sk))) {1199 tcp_accecn_option_beacon_check(sk))) {
1195 opts->use_synack_ecn_bytes = 0;1200 opts->use_synack_ecn_bytes = 0;