tcp: RFC7413 option support for Fast Open client
authorDaniel Lee <Longinus00@gmail.com>
Mon, 6 Apr 2015 21:37:27 +0000 (14:37 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 7 Apr 2015 22:36:39 +0000 (18:36 -0400)
Fast Open has been using an experimental option with a magic number
(RFC6994). This patch makes the client by default use the RFC7413
option (34) to get and send Fast Open cookies.  This patch makes
the client solicit cookies from a given server first with the
RFC7413 option. If that fails to elicit a cookie, then it tries
the RFC6994 experimental option. If that also fails, it uses the
RFC7413 option on all subsequent connect attempts.  If the server
returns a Fast Open cookie then the client caches the form of the
option that successfully elicited a cookie, and uses that form on
later connects when it presents that cookie.

The idea is to gradually obsolete the use of experimental options as
the servers and clients upgrade, while keeping the interoperability
meanwhile.

Signed-off-by: Daniel Lee <Longinus00@gmail.com>
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/tcp.h
include/net/tcp.h
net/ipv4/tcp_input.c
net/ipv4/tcp_metrics.c
net/ipv4/tcp_output.c
net/ipv4/tcp_timer.c

index a48d00318683cc726c101ee3a7dd101fbd3bd912..0caa3a2d4106eab0137d20ac75518af7964281ba 100644 (file)
@@ -189,6 +189,7 @@ struct tcp_sock {
        u8      do_early_retrans:1,/* Enable RFC5827 early-retransmit  */
                syn_data:1,     /* SYN includes data */
                syn_fastopen:1, /* SYN includes Fast Open option */
+               syn_fastopen_exp:1,/* SYN includes Fast Open exp. option */
                syn_data_acked:1,/* data in SYN is acked by SYN-ACK */
                is_cwnd_limited:1;/* forward progress limited by snd_cwnd? */
        u32     tlp_high_seq;   /* snd_nxt at the time of TLP retransmit. */
index 7292c3c575bc9eddf2736199776fa6df0c406ab5..9598871485ce3d7d36f96be9643d94e7ca85cfe0 100644 (file)
@@ -1339,7 +1339,8 @@ void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
                            struct tcp_fastopen_cookie *cookie, int *syn_loss,
                            unsigned long *last_syn_loss);
 void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
-                           struct tcp_fastopen_cookie *cookie, bool syn_lost);
+                           struct tcp_fastopen_cookie *cookie, bool syn_lost,
+                           u16 try_exp);
 struct tcp_fastopen_request {
        /* Fast Open cookie. Size 0 means a cookie request */
        struct tcp_fastopen_cookie      cookie;
index 24f1630b2afb2c7b9a6d32efb9536845dda7176b..031cf72cd05c8094de8a9a76cd4cffa13e45c0d5 100644 (file)
@@ -5378,8 +5378,8 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack,
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *data = tp->syn_data ? tcp_write_queue_head(sk) : NULL;
-       u16 mss = tp->rx_opt.mss_clamp;
-       bool syn_drop;
+       u16 mss = tp->rx_opt.mss_clamp, try_exp = 0;
+       bool syn_drop = false;
 
        if (mss == tp->rx_opt.user_mss) {
                struct tcp_options_received opt;
@@ -5391,16 +5391,25 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack,
                mss = opt.mss_clamp;
        }
 
-       if (!tp->syn_fastopen)  /* Ignore an unsolicited cookie */
+       if (!tp->syn_fastopen) {
+               /* Ignore an unsolicited cookie */
                cookie->len = -1;
+       } else if (tp->total_retrans) {
+               /* SYN timed out and the SYN-ACK neither has a cookie nor
+                * acknowledges data. Presumably the remote received only
+                * the retransmitted (regular) SYNs: either the original
+                * SYN-data or the corresponding SYN-ACK was dropped.
+                */
+               syn_drop = (cookie->len < 0 && data);
+       } else if (cookie->len < 0 && !tp->syn_data) {
+               /* We requested a cookie but didn't get it. If we did not use
+                * the (old) exp opt format then try so next time (try_exp=1).
+                * Otherwise we go back to use the RFC7413 opt (try_exp=2).
+                */
+               try_exp = tp->syn_fastopen_exp ? 2 : 1;
+       }
 
-       /* The SYN-ACK neither has cookie nor acknowledges the data. Presumably
-        * the remote receives only the retransmitted (regular) SYNs: either
-        * the original SYN-data or the corresponding SYN-ACK is lost.
-        */
-       syn_drop = (cookie->len <= 0 && data && tp->total_retrans);
-
-       tcp_fastopen_cache_set(sk, mss, cookie, syn_drop);
+       tcp_fastopen_cache_set(sk, mss, cookie, syn_drop, try_exp);
 
        if (data) { /* Retransmit unacked data in SYN */
                tcp_for_write_queue_from(data, sk) {
index 78ecc4a017128adf389c33418c937134f111e39d..a51d63a43e33af5fc751e4f0f3369b9394776975 100644 (file)
@@ -28,7 +28,8 @@ static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *s
 
 struct tcp_fastopen_metrics {
        u16     mss;
-       u16     syn_loss:10;            /* Recurring Fast Open SYN losses */
+       u16     syn_loss:10,            /* Recurring Fast Open SYN losses */
+               try_exp:2;              /* Request w/ exp. option (once) */
        unsigned long   last_syn_loss;  /* Last Fast Open SYN loss */
        struct  tcp_fastopen_cookie     cookie;
 };
@@ -131,6 +132,8 @@ static void tcpm_suck_dst(struct tcp_metrics_block *tm,
        if (fastopen_clear) {
                tm->tcpm_fastopen.mss = 0;
                tm->tcpm_fastopen.syn_loss = 0;
+               tm->tcpm_fastopen.try_exp = 0;
+               tm->tcpm_fastopen.cookie.exp = false;
                tm->tcpm_fastopen.cookie.len = 0;
        }
 }
@@ -713,6 +716,8 @@ void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
                        if (tfom->mss)
                                *mss = tfom->mss;
                        *cookie = tfom->cookie;
+                       if (cookie->len <= 0 && tfom->try_exp == 1)
+                               cookie->exp = true;
                        *syn_loss = tfom->syn_loss;
                        *last_syn_loss = *syn_loss ? tfom->last_syn_loss : 0;
                } while (read_seqretry(&fastopen_seqlock, seq));
@@ -721,7 +726,8 @@ void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
 }
 
 void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
-                           struct tcp_fastopen_cookie *cookie, bool syn_lost)
+                           struct tcp_fastopen_cookie *cookie, bool syn_lost,
+                           u16 try_exp)
 {
        struct dst_entry *dst = __sk_dst_get(sk);
        struct tcp_metrics_block *tm;
@@ -738,6 +744,9 @@ void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
                        tfom->mss = mss;
                if (cookie && cookie->len > 0)
                        tfom->cookie = *cookie;
+               else if (try_exp > tfom->try_exp &&
+                        tfom->cookie.len <= 0 && !tfom->cookie.exp)
+                       tfom->try_exp = try_exp;
                if (syn_lost) {
                        ++tfom->syn_loss;
                        tfom->last_syn_loss = jiffies;
index 464bd8c5de69682ad9c9c901d7dc98c6b2b6fa4f..e662d85d1635d0269b669bb0f726760be3bae0d2 100644 (file)
@@ -592,13 +592,17 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
        }
 
        if (fastopen && fastopen->cookie.len >= 0) {
-               u32 need = TCPOLEN_EXP_FASTOPEN_BASE + fastopen->cookie.len;
+               u32 need = fastopen->cookie.len;
+
+               need += fastopen->cookie.exp ? TCPOLEN_EXP_FASTOPEN_BASE :
+                                              TCPOLEN_FASTOPEN_BASE;
                need = (need + 3) & ~3U;  /* Align to 32 bits */
                if (remaining >= need) {
                        opts->options |= OPTION_FAST_OPEN_COOKIE;
                        opts->fastopen_cookie = &fastopen->cookie;
                        remaining -= need;
                        tp->syn_fastopen = 1;
+                       tp->syn_fastopen_exp = fastopen->cookie.exp ? 1 : 0;
                }
        }
 
index 2568fd282873b7436ca2299e20c283e1affd8688..8c65dc147d8bcfb58e14c20b774711ffbcc30d5a 100644 (file)
@@ -167,7 +167,7 @@ static int tcp_write_timeout(struct sock *sk)
                if (icsk->icsk_retransmits) {
                        dst_negative_advice(sk);
                        if (tp->syn_fastopen || tp->syn_data)
-                               tcp_fastopen_cache_set(sk, 0, NULL, true);
+                               tcp_fastopen_cache_set(sk, 0, NULL, true, 0);
                        if (tp->syn_data)
                                NET_INC_STATS_BH(sock_net(sk),
                                                 LINUX_MIB_TCPFASTOPENACTIVEFAIL);