tcp: RFC7413 option support for Fast Open server
authorDaniel Lee <Longinus00@gmail.com>
Mon, 6 Apr 2015 21:37:26 +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 the experimental option with a magic number
(RFC6994) to request and grant Fast Open cookies. This patch enables
the server to support the official IANA option 34 in RFC7413 in
addition.

The change has passed all existing Fast Open tests with both
old and new options at Google.

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_fastopen.c
net/ipv4/tcp_input.c
net/ipv4/tcp_output.c

index f869ae8afbaf9f4092625d7f5c0317ef7538ca1f..a48d00318683cc726c101ee3a7dd101fbd3bd912 100644 (file)
@@ -58,6 +58,7 @@ static inline unsigned int tcp_optlen(const struct sk_buff *skb)
 struct tcp_fastopen_cookie {
        s8      len;
        u8      val[TCP_FASTOPEN_COOKIE_MAX];
+       bool    exp;    /* In RFC6994 experimental option format */
 };
 
 /* This defines a selective acknowledgement block. */
index 963303fb96ae227263e648fb0c8dbafdc9cbc945..7292c3c575bc9eddf2736199776fa6df0c406ab5 100644 (file)
@@ -179,6 +179,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);
 #define TCPOPT_SACK             5       /* SACK Block */
 #define TCPOPT_TIMESTAMP       8       /* Better RTT estimations/PAWS */
 #define TCPOPT_MD5SIG          19      /* MD5 Signature (RFC2385) */
+#define TCPOPT_FASTOPEN                34      /* Fast open (RFC7413) */
 #define TCPOPT_EXP             254     /* Experimental */
 /* Magic number to be after the option value for sharing TCP
  * experimental options. See draft-ietf-tcpm-experimental-options-00.txt
@@ -194,6 +195,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);
 #define TCPOLEN_SACK_PERM      2
 #define TCPOLEN_TIMESTAMP      10
 #define TCPOLEN_MD5SIG         18
+#define TCPOLEN_FASTOPEN_BASE  2
 #define TCPOLEN_EXP_FASTOPEN_BASE  4
 
 /* But this is what stacks really send out. */
index 5da55e2b5cd22941b29abde6ef2993b79d97a848..e3d87aca6be8fafe02bec5a8f862a88a6fe79d50 100644 (file)
@@ -303,6 +303,7 @@ fastopen:
        } else if (foc->len > 0) /* Client presents an invalid cookie */
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVEFAIL);
 
+       valid_foc.exp = foc->exp;
        *foc = valid_foc;
        return false;
 }
index c1ce304ba8d2b63930a8e70d11bd3b83370c134c..24f1630b2afb2c7b9a6d32efb9536845dda7176b 100644 (file)
@@ -3603,6 +3603,23 @@ old_ack:
        return 0;
 }
 
+static void tcp_parse_fastopen_option(int len, const unsigned char *cookie,
+                                     bool syn, struct tcp_fastopen_cookie *foc,
+                                     bool exp_opt)
+{
+       /* Valid only in SYN or SYN-ACK with an even length.  */
+       if (!foc || !syn || len < 0 || (len & 1))
+               return;
+
+       if (len >= TCP_FASTOPEN_COOKIE_MIN &&
+           len <= TCP_FASTOPEN_COOKIE_MAX)
+               memcpy(foc->val, cookie, len);
+       else if (len != 0)
+               len = -1;
+       foc->len = len;
+       foc->exp = exp_opt;
+}
+
 /* Look for tcp options. Normally only called on SYN and SYNACK packets.
  * But, this can also be called on packets in the established flow when
  * the fast version below fails.
@@ -3692,21 +3709,22 @@ void tcp_parse_options(const struct sk_buff *skb,
                                 */
                                break;
 #endif
+                       case TCPOPT_FASTOPEN:
+                               tcp_parse_fastopen_option(
+                                       opsize - TCPOLEN_FASTOPEN_BASE,
+                                       ptr, th->syn, foc, false);
+                               break;
+
                        case TCPOPT_EXP:
                                /* Fast Open option shares code 254 using a
-                                * 16 bits magic number. It's valid only in
-                                * SYN or SYN-ACK with an even size.
+                                * 16 bits magic number.
                                 */
-                               if (opsize < TCPOLEN_EXP_FASTOPEN_BASE ||
-                                   get_unaligned_be16(ptr) != TCPOPT_FASTOPEN_MAGIC ||
-                                   !foc || !th->syn || (opsize & 1))
-                                       break;
-                               foc->len = opsize - TCPOLEN_EXP_FASTOPEN_BASE;
-                               if (foc->len >= TCP_FASTOPEN_COOKIE_MIN &&
-                                   foc->len <= TCP_FASTOPEN_COOKIE_MAX)
-                                       memcpy(foc->val, ptr + 2, foc->len);
-                               else if (foc->len != 0)
-                                       foc->len = -1;
+                               if (opsize >= TCPOLEN_EXP_FASTOPEN_BASE &&
+                                   get_unaligned_be16(ptr) ==
+                                   TCPOPT_FASTOPEN_MAGIC)
+                                       tcp_parse_fastopen_option(opsize -
+                                               TCPOLEN_EXP_FASTOPEN_BASE,
+                                               ptr + 2, th->syn, foc, true);
                                break;
 
                        }
index 7404e5238e004395ce0a55a073c806076b2394c8..464bd8c5de69682ad9c9c901d7dc98c6b2b6fa4f 100644 (file)
@@ -518,17 +518,26 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp,
 
        if (unlikely(OPTION_FAST_OPEN_COOKIE & options)) {
                struct tcp_fastopen_cookie *foc = opts->fastopen_cookie;
+               u8 *p = (u8 *)ptr;
+               u32 len; /* Fast Open option length */
+
+               if (foc->exp) {
+                       len = TCPOLEN_EXP_FASTOPEN_BASE + foc->len;
+                       *ptr = htonl((TCPOPT_EXP << 24) | (len << 16) |
+                                    TCPOPT_FASTOPEN_MAGIC);
+                       p += TCPOLEN_EXP_FASTOPEN_BASE;
+               } else {
+                       len = TCPOLEN_FASTOPEN_BASE + foc->len;
+                       *p++ = TCPOPT_FASTOPEN;
+                       *p++ = len;
+               }
 
-               *ptr++ = htonl((TCPOPT_EXP << 24) |
-                              ((TCPOLEN_EXP_FASTOPEN_BASE + foc->len) << 16) |
-                              TCPOPT_FASTOPEN_MAGIC);
-
-               memcpy(ptr, foc->val, foc->len);
-               if ((foc->len & 3) == 2) {
-                       u8 *align = ((u8 *)ptr) + foc->len;
-                       align[0] = align[1] = TCPOPT_NOP;
+               memcpy(p, foc->val, foc->len);
+               if ((len & 3) == 2) {
+                       p[foc->len] = TCPOPT_NOP;
+                       p[foc->len + 1] = TCPOPT_NOP;
                }
-               ptr += (foc->len + 3) >> 2;
+               ptr += (len + 3) >> 2;
        }
 }
 
@@ -641,8 +650,11 @@ static unsigned int tcp_synack_options(struct sock *sk,
                if (unlikely(!ireq->tstamp_ok))
                        remaining -= TCPOLEN_SACKPERM_ALIGNED;
        }
-       if (foc && foc->len >= 0) {
-               u32 need = TCPOLEN_EXP_FASTOPEN_BASE + foc->len;
+       if (foc != NULL && foc->len >= 0) {
+               u32 need = foc->len;
+
+               need += foc->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;