From: Chia-Yu Chang <chia-yu.ch...@nokia-bell-labs.com>

According to Section 3.1.2 of AccECN spec (RFC9768), if a TCP Client
has sent a SYN requesting AccECN feedback with (AE,CWR,ECE) = (1,1,1)
then receives a SYN/ACK with the currently reserved combination
(AE,CWR,ECE) = (1,0,1) but it does not have logic specific to such a
combination, the Client MUST enable AccECN mode as if the SYN/ACK
confirmed that the Server supported AccECN and as if it fed back that
the IP-ECN field on the SYN had arrived unchanged.

Signed-off-by: Chia-Yu Chang <chia-yu.ch...@nokia-bell-labs.com>
---
 include/net/tcp_ecn.h | 44 ++++++++++++++++++++++++++++++-------------
 1 file changed, 31 insertions(+), 13 deletions(-)

diff --git a/include/net/tcp_ecn.h b/include/net/tcp_ecn.h
index fb4fcb641d24..7ceb191893c6 100644
--- a/include/net/tcp_ecn.h
+++ b/include/net/tcp_ecn.h
@@ -467,6 +467,26 @@ static inline u8 tcp_accecn_option_init(const struct 
sk_buff *skb,
        return TCP_ACCECN_OPT_COUNTER_SEEN;
 }
 
+static inline void tcp_ecn_rcv_synack_accecn(struct sock *sk,
+                                            const struct sk_buff *skb, u8 dsf)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+
+       tcp_ecn_mode_set(tp, TCP_ECN_MODE_ACCECN);
+       tp->syn_ect_rcv = dsf & INET_ECN_MASK;
+       /* Demand Accurate ECN option in response to the SYN on the SYN/ACK
+        * and the TCP server will try to send one more packet with an AccECN
+        * Option at a later point during the connection.
+        */
+       if (tp->rx_opt.accecn &&
+           tp->saw_accecn_opt < TCP_ACCECN_OPT_COUNTER_SEEN) {
+               u8 saw_opt = tcp_accecn_option_init(skb, tp->rx_opt.accecn);
+
+               tcp_accecn_saw_opt_fail_recv(tp, saw_opt);
+               tp->accecn_opt_demand = 2;
+       }
+}
+
 /* See Table 2 of the AccECN draft */
 static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct sk_buff 
*skb,
                                      const struct tcphdr *th, u8 ip_dsfield)
@@ -489,13 +509,11 @@ static inline void tcp_ecn_rcv_synack(struct sock *sk, 
const struct sk_buff *skb
                tcp_ecn_mode_set(tp, TCP_ECN_DISABLED);
                break;
        case 0x1:
-       case 0x5:
                /* +========+========+============+=============+
                 * | A      | B      |  SYN/ACK   |  Feedback   |
                 * |        |        |    B->A    |  Mode of A  |
                 * |        |        | AE CWR ECE |             |
                 * +========+========+============+=============+
-                * | AccECN | Nonce  | 1   0   1  | (Reserved)  |
                 * | AccECN | ECN    | 0   0   1  | Classic ECN |
                 * | Nonce  | AccECN | 0   0   1  | Classic ECN |
                 * | ECN    | AccECN | 0   0   1  | Classic ECN |
@@ -503,20 +521,20 @@ static inline void tcp_ecn_rcv_synack(struct sock *sk, 
const struct sk_buff *skb
                 */
                if (tcp_ca_no_fallback_rfc3168(sk))
                        tcp_ecn_mode_set(tp, TCP_ECN_DISABLED);
-               else if (tcp_ecn_mode_pending(tp))
-                       /* Downgrade from AccECN, or requested initially */
+               else
                        tcp_ecn_mode_set(tp, TCP_ECN_MODE_RFC3168);
                break;
-       default:
-               tcp_ecn_mode_set(tp, TCP_ECN_MODE_ACCECN);
-               tp->syn_ect_rcv = ip_dsfield & INET_ECN_MASK;
-               if (tp->rx_opt.accecn &&
-                   tp->saw_accecn_opt < TCP_ACCECN_OPT_COUNTER_SEEN) {
-                       u8 saw_opt = tcp_accecn_option_init(skb, 
tp->rx_opt.accecn);
-
-                       tcp_accecn_saw_opt_fail_recv(tp, saw_opt);
-                       tp->accecn_opt_demand = 2;
+       case 0x5:
+               if (tcp_ecn_mode_pending(tp)) {
+                       tcp_ecn_rcv_synack_accecn(sk, skb, ip_dsfield);
+                       if (INET_ECN_is_ce(ip_dsfield)) {
+                               tp->received_ce++;
+                               tp->received_ce_pending++;
+                       }
                }
+               break;
+       default:
+               tcp_ecn_rcv_synack_accecn(sk, skb, ip_dsfield);
                if (INET_ECN_is_ce(ip_dsfield) &&
                    tcp_accecn_validate_syn_feedback(sk, ace,
                                                     tp->syn_ect_snt)) {
-- 
2.34.1


Reply via email to