From: Chia-Yu Chang <[email protected]>

According to Sections 3.1.2 and 3.1.3 of AccECN spec (RFC9768).

In Section 3.1.2, it says an AccECN implementation has no need to
recognize or support the Server response labelled 'Nonce' or ECN-nonce
feedback more generally, as RFC 3540 has been reclassified as Historic.
AccECN is compatible with alternative ECN feedback integrity approaches
to the nonce. The SYN/ACK labelled 'Nonce' with (AE,CWR,ECE) = (1,0,1)
is reserved for future use. A TCP Client (A) that receives such a SYN/ACK
follows the procedure for forward compatibility given in Section 3.1.3.

Then in Section 3.1.3, it says 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 onfirmed that the Server supported AccECN and as
if it fed back that the IP-ECN field on the SYN had arrived unchanged.

Fixes: 3cae34274c79 ("tcp: accecn: AccECN negotiation").
Signed-off-by: Chia-Yu Chang <[email protected]>
Acked-by: Paolo Abeni <[email protected]>
Reviewed-by: Eric Dumazet <[email protected]>

---
v10:
- Update commie message with full texts from RFC.

v5:
- Add "Fixes" tag.

v3:
- Update commit message to fix old AccECN commits.
---
 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 2e1637edf1d3..a709fb1756eb 100644
--- a/include/net/tcp_ecn.h
+++ b/include/net/tcp_ecn.h
@@ -473,6 +473,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)
@@ -495,13 +515,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 |
@@ -509,20 +527,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