History

tcp: accecn: AccECN option failure handling

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.

Chia-Yu Chang committed 1 year ago

Commit b40671b1 parent aa55a7d7 files · +111 −9Browse files

Unified · Split

7 files
  1. include/linux/tcp.h +3 −1
  2. include/net/tcp_ecn.h +48 −3
  3. include/uapi/linux/tcp.h +2 −0
  4. net/ipv4/tcp.c +3 −0
  5. net/ipv4/tcp_input.c +33 −2
  6. net/ipv4/tcp_minisocks.c +14 −0
  7. net/ipv4/tcp_output.c +8 −3
include/linux/tcp.h+3−1
@@ -173,6 +173,7 @@ struct tcp_request_sock { · 172 unmodified lines
173 u8 syn_ect_snt: 2,173 u8 syn_ect_snt: 2,
174 syn_ect_rcv: 2,174 syn_ect_rcv: 2,
175 accecn_fail_mode:4;175 accecn_fail_mode:4;
176 u8 saw_accecn_opt :2;
176#ifdef CONFIG_TCP_AO177#ifdef CONFIG_TCP_AO
177 u8 ao_keyid;178 u8 ao_keyid;
178 u8 ao_rcv_next;179 u8 ao_rcv_next;
@@ -407,7 +408,8 @@ struct tcp_sock { · 228 unmodified lines
407 syn_fastopen_child:1; /* created TFO passive child socket */408 syn_fastopen_child:1; /* created TFO passive child socket */
408 409
409 u8 keepalive_probes; /* num of allowed keep alive probes */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 u32 tcp_tx_delay; /* delay (in usec) added to TX packets */413 u32 tcp_tx_delay; /* delay (in usec) added to TX packets */
412 414
413/* RTT measurement */415/* RTT measurement */
include/net/tcp_ecn.h+48−3
@@ -91,6 +91,11 @@ static inline void tcp_accecn_fail_mode_set(struct tcp_sock *tp, u8 mode) · 90 unmodified lines
91 tp->accecn_fail_mode |= mode;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
94static inline u8 tcp_accecn_ace(const struct tcphdr *th)99static inline u8 tcp_accecn_ace(const struct tcphdr *th)
95{100{
96 return (th->ae << 2) | (th->cwr << 1) | th->ece;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 return true;151 return true;
147}152}
148 153
154static 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/* Validate the 3rd ACK based on the ACE field, see Table 4 of AccECN spec */162/* Validate the 3rd ACK based on the ACE field, see Table 4 of AccECN spec */
150static inline void tcp_accecn_third_ack(struct sock *sk,163static inline void tcp_accecn_third_ack(struct sock *sk,
151 const struct sk_buff *skb, u8 sent_ect)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
444static 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/* See Table 2 of the AccECN draft */470/* See Table 2 of the AccECN draft */
432static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct tcphdr *th,471static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct sk_buff *skb,
433 u8 ip_dsfield)472 const struct tcphdr *th, u8 ip_dsfield)
434{473{
435 struct tcp_sock *tp = tcp_sk(sk);474 struct tcp_sock *tp = tcp_sk(sk);
436 u8 ace = tcp_accecn_ace(th);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 default:508 default:
470 tcp_ecn_mode_set(tp, TCP_ECN_MODE_ACCECN);509 tcp_ecn_mode_set(tp, TCP_ECN_MODE_ACCECN);
471 tp->syn_ect_rcv = ip_dsfield & INET_ECN_MASK;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 if (INET_ECN_is_ce(ip_dsfield) &&518 if (INET_ECN_is_ce(ip_dsfield) &&
474 tcp_accecn_validate_syn_feedback(sk, ace,519 tcp_accecn_validate_syn_feedback(sk, ace,
475 tp->syn_ect_snt)) {520 tp->syn_ect_snt)) {
include/uapi/linux/tcp.h+2−0
@@ -323,6 +323,8 @@ struct tcp_info { · 322 unmodified lines
323 __u32 tcpi_received_e1_bytes;323 __u32 tcpi_received_e1_bytes;
324 __u32 tcpi_received_e0_bytes;324 __u32 tcpi_received_e0_bytes;
325 __u32 tcpi_received_ce_bytes;325 __u32 tcpi_received_ce_bytes;
326 __u16 tcpi_accecn_fail_mode;
327 __u16 tcpi_accecn_opt_seen;
326};328};
327 329
328/* netlink attributes types for SCM_TIMESTAMPING_OPT_STATS */330/* netlink attributes types for SCM_TIMESTAMPING_OPT_STATS */
net/ipv4/tcp.c+3−0
@@ -3409,6 +3409,7 @@ int tcp_disconnect(struct sock *sk, int flags) · 3408 unmodified lines
3409 tp->delivered = 0;3409 tp->delivered = 0;
3410 tp->delivered_ce = 0;3410 tp->delivered_ce = 0;
3411 tp->accecn_fail_mode = 0;3411 tp->accecn_fail_mode = 0;
3412 tp->saw_accecn_opt = TCP_ACCECN_OPT_NOT_SEEN;
3412 tcp_accecn_init_counters(tp);3413 tcp_accecn_init_counters(tp);
3413 tp->prev_ecnfield = 0;3414 tp->prev_ecnfield = 0;
3414 tp->accecn_opt_tstamp = 0;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 if (tp->rto_stamp)4288 if (tp->rto_stamp)
4288 info->tcpi_total_rto_time += tcp_clock_ms() - tp->rto_stamp;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 info->tcpi_received_ce = tp->received_ce;4293 info->tcpi_received_ce = tp->received_ce;
4291 info->tcpi_delivered_e1_bytes = tp->delivered_ecn_bytes[ect1_idx];4294 info->tcpi_delivered_e1_bytes = tp->delivered_ecn_bytes[ect1_idx];
4292 info->tcpi_delivered_e0_bytes = tp->delivered_ecn_bytes[ect0_idx];4295 info->tcpi_delivered_e0_bytes = tp->delivered_ecn_bytes[ect0_idx];
net/ipv4/tcp_input.c+33−2
@@ -398,7 +398,22 @@ static bool tcp_accecn_process_option(struct tcp_sock *tp, · 397 unmodified lines
398 unsigned int i;398 unsigned int i;
399 u8 *ptr;399 u8 *ptr;
400 400
401 if (tcp_accecn_opt_fail_recv(tp))
402 return false;
403
401 if (!(flag & FLAG_SLOWPATH) || !tp->rx_opt.accecn) {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 if (estimate_ecnfield) {417 if (estimate_ecnfield) {
403 u8 ecnfield = estimate_ecnfield - 1;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 order1 = (ptr[0] == TCPOPT_ACCECN1);430 order1 = (ptr[0] == TCPOPT_ACCECN1);
416 ptr += 2;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 res = !!estimate_ecnfield;440 res = !!estimate_ecnfield;
419 for (i = 0; i < 3; i++) {441 for (i = 0; i < 3; i++) {
420 u32 init_offset;442 u32 init_offset;
@@ -6123,7 +6145,13 @@ step1: · 5702 unmodified lines
6123 if (th->syn) {6145 if (th->syn) {
6124 if (tcp_ecn_mode_accecn(tp)) {6146 if (tcp_ecn_mode_accecn(tp)) {
6125 accecn_reflector = true;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 if (sk->sk_state == TCP_SYN_RECV && sk->sk_socket && th->ack &&6156 if (sk->sk_state == TCP_SYN_RECV && sk->sk_socket && th->ack &&
6129 TCP_SKB_CB(skb)->seq + 1 == TCP_SKB_CB(skb)->end_seq &&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 if (tcp_ecn_mode_any(tp))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 tcp_init_wl(tp, TCP_SKB_CB(skb)->seq);6640 tcp_init_wl(tp, TCP_SKB_CB(skb)->seq);
6612 tcp_try_undo_spurious_syn(sk);6641 tcp_try_undo_spurious_syn(sk);
@@ -7177,6 +7206,8 @@ static void tcp_openreq_init(struct request_sock *req, · 564 unmodified lines
7177 tcp_rsk(req)->snt_tsval_first = 0;7206 tcp_rsk(req)->snt_tsval_first = 0;
7178 tcp_rsk(req)->last_oow_ack_time = 0;7207 tcp_rsk(req)->last_oow_ack_time = 0;
7179 tcp_rsk(req)->accecn_ok = 0;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 tcp_rsk(req)->syn_ect_rcv = 0;7211 tcp_rsk(req)->syn_ect_rcv = 0;
7181 tcp_rsk(req)->syn_ect_snt = 0;7212 tcp_rsk(req)->syn_ect_snt = 0;
7182 req->mss = rx_opt->mss_clamp;7213 req->mss = rx_opt->mss_clamp;
net/ipv4/tcp_minisocks.c+14−0
@@ -463,6 +463,7 @@ static void tcp_ecn_openreq_child(struct sock *sk, · 462 unmodified lines
463 tcp_ecn_mode_set(tp, TCP_ECN_MODE_ACCECN);463 tcp_ecn_mode_set(tp, TCP_ECN_MODE_ACCECN);
464 tp->syn_ect_snt = treq->syn_ect_snt;464 tp->syn_ect_snt = treq->syn_ect_snt;
465 tcp_accecn_third_ack(sk, skb, treq->syn_ect_snt);465 tcp_accecn_third_ack(sk, skb, treq->syn_ect_snt);
466 tp->saw_accecn_opt = treq->saw_accecn_opt;
466 tp->prev_ecnfield = treq->syn_ect_rcv;467 tp->prev_ecnfield = treq->syn_ect_rcv;
467 tp->accecn_opt_demand = 1;468 tp->accecn_opt_demand = 1;
468 tcp_ecn_received_counters_payload(sk, skb);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 bool own_req;679 bool own_req;
679 680
680 tmp_opt.saw_tstamp = 0;681 tmp_opt.saw_tstamp = 0;
682 tmp_opt.accecn = 0;
681 if (th->doff > (sizeof(struct tcphdr)>>2)) {683 if (th->doff > (sizeof(struct tcphdr)>>2)) {
682 tcp_parse_options(sock_net(sk), skb, &tmp_opt, 0, NULL);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 if (!(flg & TCP_FLAG_ACK))857 if (!(flg & TCP_FLAG_ACK))
856 return NULL;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 /* For Fast Open no more processing is needed (sk is the872 /* For Fast Open no more processing is needed (sk is the
859 * child socket).873 * child socket).
860 */874 */
net/ipv4/tcp_output.c+8−3
@@ -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 if (unlikely((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_ACK) &&993 if (unlikely((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_ACK) &&
990 tcp_ecn_mode_accecn(tp) &&994 tcp_ecn_mode_accecn(tp) &&
995 inet_csk(sk)->icsk_retransmits < 2 &&
991 READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_ecn_option) &&996 READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_ecn_option) &&
992 remaining >= TCPOLEN_ACCECN_BASE)) {997 remaining >= TCPOLEN_ACCECN_BASE)) {
993 opts->use_synack_ecn_bytes = 1;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 if (treq->accecn_ok &&1082 if (treq->accecn_ok &&
1078 READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_ecn_option) &&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 opts->use_synack_ecn_bytes = 1;1085 opts->use_synack_ecn_bytes = 1;
1081 remaining -= tcp_options_fit_accecn(opts, 0, remaining);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 if (tcp_ecn_mode_accecn(tp)) {1161 if (tcp_ecn_mode_accecn(tp)) {
1157 int ecn_opt = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_ecn_option);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 (ecn_opt >= TCP_ACCECN_OPTION_FULL || tp->accecn_opt_demand ||1165 (ecn_opt >= TCP_ACCECN_OPTION_FULL || tp->accecn_opt_demand ||
1161 tcp_accecn_option_beacon_check(sk))) {1166 tcp_accecn_option_beacon_check(sk))) {
1162 opts->use_synack_ecn_bytes = 0;1167 opts->use_synack_ecn_bytes = 0;

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t