AccECN option may fail in various way, handle these: - Attempt to negotiate the use of AccECN on the 1st retransmitted SYN - From the 2nd retransmitted SYN, stop AccECN negotiation - Remove option from SYN/ACK rexmits to handle blackholes - If no option arrives in SYN/ACK, assume Option is not usable - If an option arrives later, re-enabled - If option is zeroed, disable AccECN option processing This patch use existing padding bits in tcp_request_sock and holes in tcp_sock without increasing the size.
Unified · Split
| @@ -173,6 +173,7 @@ struct tcp_request_sock { · 172 unmodified lines | ||
| 173 | 173 | u8 syn_ect_snt: 2, |
| 174 | 174 | syn_ect_rcv: 2, |
| 175 | 175 | accecn_fail_mode:4; |
| 176 | + u8 saw_accecn_opt :2; | |
| 176 | 177 | #ifdef CONFIG_TCP_AO |
| 177 | 178 | u8 ao_keyid; |
| 178 | 179 | u8 ao_rcv_next; |
| @@ -407,7 +408,8 @@ struct tcp_sock { · 228 unmodified lines | ||
| 407 | 408 | syn_fastopen_child:1; /* created TFO passive child socket */ |
| 408 | 409 | |
| 409 | 410 | u8 keepalive_probes; /* num of allowed keep alive probes */ |
| 410 | − u8 accecn_fail_mode:4; /* AccECN failure handling */ | |
| 411 | + u8 accecn_fail_mode:4, /* AccECN failure handling */ | |
| 412 | + saw_accecn_opt:2; /* An AccECN option was seen */ | |
| 411 | 413 | u32 tcp_tx_delay; /* delay (in usec) added to TX packets */ |
| 412 | 414 | |
| 413 | 415 | /* RTT measurement */ |
| @@ -91,6 +91,11 @@ static inline void tcp_accecn_fail_mode_set(struct tcp_sock *tp, u8 mode) · 90 unmodified lines | ||
| 91 | 91 | tp->accecn_fail_mode |= mode; |
| 92 | 92 | } |
| 93 | 93 | |
| 94 | +#define TCP_ACCECN_OPT_NOT_SEEN 0x0 | |
| 95 | +#define TCP_ACCECN_OPT_EMPTY_SEEN 0x1 | |
| 96 | +#define TCP_ACCECN_OPT_COUNTER_SEEN 0x2 | |
| 97 | +#define TCP_ACCECN_OPT_FAIL_SEEN 0x3 | |
| 98 | + | |
| 94 | 99 | static inline u8 tcp_accecn_ace(const struct tcphdr *th) |
| 95 | 100 | { |
| 96 | 101 | return (th->ae << 2) | (th->cwr << 1) | th->ece; |
| @@ -146,6 +151,14 @@ static inline bool tcp_accecn_validate_syn_feedback(struct sock *sk, u8 ace, · 49 unmodified lines | ||
| 146 | 151 | return true; |
| 147 | 152 | } |
| 148 | 153 | |
| 154 | +static inline void tcp_accecn_saw_opt_fail_recv(struct tcp_sock *tp, | |
| 155 | + u8 saw_opt) | |
| 156 | +{ | |
| 157 | + tp->saw_accecn_opt = saw_opt; | |
| 158 | + if (tp->saw_accecn_opt == TCP_ACCECN_OPT_FAIL_SEEN) | |
| 159 | + tcp_accecn_fail_mode_set(tp, TCP_ACCECN_OPT_FAIL_RECV); | |
| 160 | +} | |
| 161 | + | |
| 149 | 162 | /* Validate the 3rd ACK based on the ACE field, see Table 4 of AccECN spec */ |
| 150 | 163 | static inline void tcp_accecn_third_ack(struct sock *sk, |
| 151 | 164 | const struct sk_buff *skb, u8 sent_ect) |
| @@ -428,9 +441,35 @@ static inline void tcp_accecn_set_ace(struct tcp_sock *tp, struct sk_buff *skb, · 276 unmodified lines | ||
| 428 | 441 | } |
| 429 | 442 | } |
| 430 | 443 | |
| 444 | +static inline u8 tcp_accecn_option_init(const struct sk_buff *skb, | |
| 445 | + u8 opt_offset) | |
| 446 | +{ | |
| 447 | + u8 *ptr = skb_transport_header(skb) + opt_offset; | |
| 448 | + unsigned int optlen = ptr[1] - 2; | |
| 449 | + | |
| 450 | + if (WARN_ON_ONCE(ptr[0] != TCPOPT_ACCECN0 && ptr[0] != TCPOPT_ACCECN1)) | |
| 451 | + return TCP_ACCECN_OPT_FAIL_SEEN; | |
| 452 | + ptr += 2; | |
| 453 | + | |
| 454 | + /* Detect option zeroing: an AccECN connection "MAY check that the | |
| 455 | + * initial value of the EE0B field or the EE1B field is non-zero" | |
| 456 | + */ | |
| 457 | + if (optlen < TCPOLEN_ACCECN_PERFIELD) | |
| 458 | + return TCP_ACCECN_OPT_EMPTY_SEEN; | |
| 459 | + if (get_unaligned_be24(ptr) == 0) | |
| 460 | + return TCP_ACCECN_OPT_FAIL_SEEN; | |
| 461 | + if (optlen < TCPOLEN_ACCECN_PERFIELD * 3) | |
| 462 | + return TCP_ACCECN_OPT_COUNTER_SEEN; | |
| 463 | + ptr += TCPOLEN_ACCECN_PERFIELD * 2; | |
| 464 | + if (get_unaligned_be24(ptr) == 0) | |
| 465 | + return TCP_ACCECN_OPT_FAIL_SEEN; | |
| 466 | + | |
| 467 | + return TCP_ACCECN_OPT_COUNTER_SEEN; | |
| 468 | +} | |
| 469 | + | |
| 431 | 470 | /* See Table 2 of the AccECN draft */ |
| 432 | −static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct tcphdr *th, | |
| 433 | − u8 ip_dsfield) | |
| 471 | +static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct sk_buff *skb, | |
| 472 | + const struct tcphdr *th, u8 ip_dsfield) | |
| 434 | 473 | { |
| 435 | 474 | struct tcp_sock *tp = tcp_sk(sk); |
| 436 | 475 | u8 ace = tcp_accecn_ace(th); |
| @@ -469,7 +508,13 @@ static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct tcphdr *th, · 32 unmodified lines | ||
| 469 | 508 | default: |
| 470 | 509 | tcp_ecn_mode_set(tp, TCP_ECN_MODE_ACCECN); |
| 471 | 510 | tp->syn_ect_rcv = ip_dsfield & INET_ECN_MASK; |
| 472 | − tp->accecn_opt_demand = 2; | |
| 511 | + if (tp->rx_opt.accecn && | |
| 512 | + tp->saw_accecn_opt < TCP_ACCECN_OPT_COUNTER_SEEN) { | |
| 513 | + u8 saw_opt = tcp_accecn_option_init(skb, tp->rx_opt.accecn); | |
| 514 | + | |
| 515 | + tcp_accecn_saw_opt_fail_recv(tp, saw_opt); | |
| 516 | + tp->accecn_opt_demand = 2; | |
| 517 | + } | |
| 473 | 518 | if (INET_ECN_is_ce(ip_dsfield) && |
| 474 | 519 | tcp_accecn_validate_syn_feedback(sk, ace, |
| 475 | 520 | tp->syn_ect_snt)) { |
| @@ -323,6 +323,8 @@ struct tcp_info { · 322 unmodified lines | ||
| 323 | 323 | __u32 tcpi_received_e1_bytes; |
| 324 | 324 | __u32 tcpi_received_e0_bytes; |
| 325 | 325 | __u32 tcpi_received_ce_bytes; |
| 326 | + __u16 tcpi_accecn_fail_mode; | |
| 327 | + __u16 tcpi_accecn_opt_seen; | |
| 326 | 328 | }; |
| 327 | 329 | |
| 328 | 330 | /* netlink attributes types for SCM_TIMESTAMPING_OPT_STATS */ |
| @@ -3409,6 +3409,7 @@ int tcp_disconnect(struct sock *sk, int flags) · 3408 unmodified lines | ||
| 3409 | 3409 | tp->delivered = 0; |
| 3410 | 3410 | tp->delivered_ce = 0; |
| 3411 | 3411 | tp->accecn_fail_mode = 0; |
| 3412 | + tp->saw_accecn_opt = TCP_ACCECN_OPT_NOT_SEEN; | |
| 3412 | 3413 | tcp_accecn_init_counters(tp); |
| 3413 | 3414 | tp->prev_ecnfield = 0; |
| 3414 | 3415 | tp->accecn_opt_tstamp = 0; |
| @@ -4287,6 +4288,8 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) · 872 unmodified lines | ||
| 4287 | 4288 | if (tp->rto_stamp) |
| 4288 | 4289 | info->tcpi_total_rto_time += tcp_clock_ms() - tp->rto_stamp; |
| 4289 | 4290 | |
| 4291 | + info->tcpi_accecn_fail_mode = tp->accecn_fail_mode; | |
| 4292 | + info->tcpi_accecn_opt_seen = tp->saw_accecn_opt; | |
| 4290 | 4293 | info->tcpi_received_ce = tp->received_ce; |
| 4291 | 4294 | info->tcpi_delivered_e1_bytes = tp->delivered_ecn_bytes[ect1_idx]; |
| 4292 | 4295 | info->tcpi_delivered_e0_bytes = tp->delivered_ecn_bytes[ect0_idx]; |
| @@ -398,7 +398,22 @@ static bool tcp_accecn_process_option(struct tcp_sock *tp, · 397 unmodified lines | ||
| 398 | 398 | unsigned int i; |
| 399 | 399 | u8 *ptr; |
| 400 | 400 | |
| 401 | + if (tcp_accecn_opt_fail_recv(tp)) | |
| 402 | + return false; | |
| 403 | + | |
| 401 | 404 | if (!(flag & FLAG_SLOWPATH) || !tp->rx_opt.accecn) { |
| 405 | + if (!tp->saw_accecn_opt) { | |
| 406 | + /* Too late to enable after this point due to | |
| 407 | + * potential counter wraps | |
| 408 | + */ | |
| 409 | + if (tp->bytes_sent >= (1 << 23) - 1) { | |
| 410 | + u8 saw_opt = TCP_ACCECN_OPT_FAIL_SEEN; | |
| 411 | + | |
| 412 | + tcp_accecn_saw_opt_fail_recv(tp, saw_opt); | |
| 413 | + } | |
| 414 | + return false; | |
| 415 | + } | |
| 416 | + | |
| 402 | 417 | if (estimate_ecnfield) { |
| 403 | 418 | u8 ecnfield = estimate_ecnfield - 1; |
| 404 | 419 | |
| @@ -415,6 +430,13 @@ static bool tcp_accecn_process_option(struct tcp_sock *tp, · 10 unmodified lines | ||
| 415 | 430 | order1 = (ptr[0] == TCPOPT_ACCECN1); |
| 416 | 431 | ptr += 2; |
| 417 | 432 | |
| 433 | + if (tp->saw_accecn_opt < TCP_ACCECN_OPT_COUNTER_SEEN) { | |
| 434 | + tp->saw_accecn_opt = tcp_accecn_option_init(skb, | |
| 435 | + tp->rx_opt.accecn); | |
| 436 | + if (tp->saw_accecn_opt == TCP_ACCECN_OPT_FAIL_SEEN) | |
| 437 | + tcp_accecn_fail_mode_set(tp, TCP_ACCECN_OPT_FAIL_RECV); | |
| 438 | + } | |
| 439 | + | |
| 418 | 440 | res = !!estimate_ecnfield; |
| 419 | 441 | for (i = 0; i < 3; i++) { |
| 420 | 442 | u32 init_offset; |
| @@ -6123,7 +6145,13 @@ step1: · 5702 unmodified lines | ||
| 6123 | 6145 | if (th->syn) { |
| 6124 | 6146 | if (tcp_ecn_mode_accecn(tp)) { |
| 6125 | 6147 | accecn_reflector = true; |
| 6126 | − tcp_accecn_opt_demand_min(sk, 1); | |
| 6148 | + if (tp->rx_opt.accecn && | |
| 6149 | + tp->saw_accecn_opt < TCP_ACCECN_OPT_COUNTER_SEEN) { | |
| 6150 | + u8 saw_opt = tcp_accecn_option_init(skb, tp->rx_opt.accecn); | |
| 6151 | + | |
| 6152 | + tcp_accecn_saw_opt_fail_recv(tp, saw_opt); | |
| 6153 | + tcp_accecn_opt_demand_min(sk, 1); | |
| 6154 | + } | |
| 6127 | 6155 | } |
| 6128 | 6156 | if (sk->sk_state == TCP_SYN_RECV && sk->sk_socket && th->ack && |
| 6129 | 6157 | TCP_SKB_CB(skb)->seq + 1 == TCP_SKB_CB(skb)->end_seq && |
| @@ -6606,7 +6634,8 @@ consume: · 476 unmodified lines | ||
| 6606 | 6634 | */ |
| 6607 | 6635 | |
| 6608 | 6636 | if (tcp_ecn_mode_any(tp)) |
| 6609 | − tcp_ecn_rcv_synack(sk, th, TCP_SKB_CB(skb)->ip_dsfield); | |
| 6637 | + tcp_ecn_rcv_synack(sk, skb, th, | |
| 6638 | + TCP_SKB_CB(skb)->ip_dsfield); | |
| 6610 | 6639 | |
| 6611 | 6640 | tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); |
| 6612 | 6641 | tcp_try_undo_spurious_syn(sk); |
| @@ -7177,6 +7206,8 @@ static void tcp_openreq_init(struct request_sock *req, · 564 unmodified lines | ||
| 7177 | 7206 | tcp_rsk(req)->snt_tsval_first = 0; |
| 7178 | 7207 | tcp_rsk(req)->last_oow_ack_time = 0; |
| 7179 | 7208 | tcp_rsk(req)->accecn_ok = 0; |
| 7209 | + tcp_rsk(req)->saw_accecn_opt = TCP_ACCECN_OPT_NOT_SEEN; | |
| 7210 | + tcp_rsk(req)->accecn_fail_mode = 0; | |
| 7180 | 7211 | tcp_rsk(req)->syn_ect_rcv = 0; |
| 7181 | 7212 | tcp_rsk(req)->syn_ect_snt = 0; |
| 7182 | 7213 | req->mss = rx_opt->mss_clamp; |
| @@ -463,6 +463,7 @@ static void tcp_ecn_openreq_child(struct sock *sk, · 462 unmodified lines | ||
| 463 | 463 | tcp_ecn_mode_set(tp, TCP_ECN_MODE_ACCECN); |
| 464 | 464 | tp->syn_ect_snt = treq->syn_ect_snt; |
| 465 | 465 | tcp_accecn_third_ack(sk, skb, treq->syn_ect_snt); |
| 466 | + tp->saw_accecn_opt = treq->saw_accecn_opt; | |
| 466 | 467 | tp->prev_ecnfield = treq->syn_ect_rcv; |
| 467 | 468 | tp->accecn_opt_demand = 1; |
| 468 | 469 | tcp_ecn_received_counters_payload(sk, skb); |
| @@ -678,6 +679,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, · 209 unmodified lines | ||
| 678 | 679 | bool own_req; |
| 679 | 680 | |
| 680 | 681 | tmp_opt.saw_tstamp = 0; |
| 682 | + tmp_opt.accecn = 0; | |
| 681 | 683 | if (th->doff > (sizeof(struct tcphdr)>>2)) { |
| 682 | 684 | tcp_parse_options(sock_net(sk), skb, &tmp_opt, 0, NULL); |
| 683 | 685 | |
| @@ -855,6 +857,18 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, · 171 unmodified lines | ||
| 855 | 857 | if (!(flg & TCP_FLAG_ACK)) |
| 856 | 858 | return NULL; |
| 857 | 859 | |
| 860 | + if (tcp_rsk(req)->accecn_ok && tmp_opt.accecn && | |
| 861 | + tcp_rsk(req)->saw_accecn_opt < TCP_ACCECN_OPT_COUNTER_SEEN) { | |
| 862 | + u8 saw_opt = tcp_accecn_option_init(skb, tmp_opt.accecn); | |
| 863 | + | |
| 864 | + tcp_rsk(req)->saw_accecn_opt = saw_opt; | |
| 865 | + if (tcp_rsk(req)->saw_accecn_opt == TCP_ACCECN_OPT_FAIL_SEEN) { | |
| 866 | + u8 fail_mode = TCP_ACCECN_OPT_FAIL_RECV; | |
| 867 | + | |
| 868 | + tcp_rsk(req)->accecn_fail_mode |= fail_mode; | |
| 869 | + } | |
| 870 | + } | |
| 871 | + | |
| 858 | 872 | /* For Fast Open no more processing is needed (sk is the |
| 859 | 873 | * child socket). |
| 860 | 874 | */ |
| @@ -985,9 +985,14 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, · 984 unmodified lines | ||
| 985 | 985 | } |
| 986 | 986 | } |
| 987 | 987 | |
| 988 | − /* Simultaneous open SYN/ACK needs AccECN option but not SYN */ | |
| 988 | + /* Simultaneous open SYN/ACK needs AccECN option but not SYN. | |
| 989 | + * It is attempted to negotiate the use of AccECN also on the first | |
| 990 | + * retransmitted SYN, as mentioned in "3.1.4.1. Retransmitted SYNs" | |
| 991 | + * of AccECN draft. | |
| 992 | + */ | |
| 989 | 993 | if (unlikely((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_ACK) && |
| 990 | 994 | tcp_ecn_mode_accecn(tp) && |
| 995 | + inet_csk(sk)->icsk_retransmits < 2 && | |
| 991 | 996 | READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_ecn_option) && |
| 992 | 997 | remaining >= TCPOLEN_ACCECN_BASE)) { |
| 993 | 998 | opts->use_synack_ecn_bytes = 1; |
| @@ -1076,7 +1081,7 @@ static unsigned int tcp_synack_options(const struct sock *sk, · 82 unmodified lines | ||
| 1076 | 1081 | |
| 1077 | 1082 | if (treq->accecn_ok && |
| 1078 | 1083 | READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_ecn_option) && |
| 1079 | − remaining >= TCPOLEN_ACCECN_BASE) { | |
| 1084 | + req->num_timeout < 1 && remaining >= TCPOLEN_ACCECN_BASE) { | |
| 1080 | 1085 | opts->use_synack_ecn_bytes = 1; |
| 1081 | 1086 | remaining -= tcp_options_fit_accecn(opts, 0, remaining); |
| 1082 | 1087 | } |
| @@ -1156,7 +1161,7 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb · 73 unmodified lines | ||
| 1156 | 1161 | if (tcp_ecn_mode_accecn(tp)) { |
| 1157 | 1162 | int ecn_opt = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_ecn_option); |
| 1158 | 1163 | |
| 1159 | − if (ecn_opt && | |
| 1164 | + if (ecn_opt && tp->saw_accecn_opt && !tcp_accecn_opt_fail_send(tp) && | |
| 1160 | 1165 | (ecn_opt >= TCP_ACCECN_OPTION_FULL || tp->accecn_opt_demand || |
| 1161 | 1166 | tcp_accecn_option_beacon_check(sk))) { |
| 1162 | 1167 | opts->use_synack_ecn_bytes = 0; |