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. */
 
                            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;
 
 {
        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;
                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) {
 
 
 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;
 };
        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;
        }
 }
                        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));
 }
 
 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;
                        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;
 
        }
 
        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;
                }
        }
 
 
                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);