net: fastopen: robustness and endianness fixes for SipHash
authorArd Biesheuvel <ard.biesheuvel@linaro.org>
Wed, 19 Jun 2019 21:46:28 +0000 (23:46 +0200)
committerDavid S. Miller <davem@davemloft.net>
Sat, 22 Jun 2019 23:30:37 +0000 (16:30 -0700)
Some changes to the TCP fastopen code to make it more robust
against future changes in the choice of key/cookie size, etc.

- Instead of keeping the SipHash key in an untyped u8[] buffer
  and casting it to the right type upon use, use the correct
  type directly. This ensures that the key will appear at the
  correct alignment if we ever change the way these data
  structures are allocated. (Currently, they are only allocated
  via kmalloc so they always appear at the correct alignment)

- Use DIV_ROUND_UP when sizing the u64[] array to hold the
  cookie, so it is always of sufficient size, even if
  TCP_FASTOPEN_COOKIE_MAX is no longer a multiple of 8.

- Drop the 'len' parameter from the tcp_fastopen_reset_cipher()
  function, which is no longer used.

- Add endian swabbing when setting the keys and calculating the hash,
  to ensure that cookie values are the same for a given key and
  source/destination address pair regardless of the endianness of
  the server.

Note that none of these are functional changes wrt the current
state of the code, with the exception of the swabbing, which only
affects big endian systems.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/tcp.h
include/net/tcp.h
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp.c
net/ipv4/tcp_fastopen.c

index 2689b0b0b68a6e1170b9b2cd5ad6bbc21e280bfa..f3a85a7fb4b1b527667ee387e187d46701e19443 100644 (file)
@@ -58,7 +58,7 @@ static inline unsigned int tcp_optlen(const struct sk_buff *skb)
 
 /* TCP Fast Open Cookie as stored in memory */
 struct tcp_fastopen_cookie {
-       u64     val[TCP_FASTOPEN_COOKIE_MAX / sizeof(u64)];
+       __le64  val[DIV_ROUND_UP(TCP_FASTOPEN_COOKIE_MAX, sizeof(u64))];
        s8      len;
        bool    exp;    /* In RFC6994 experimental option format */
 };
index 573c9e9b0d72e9472347c943ea482a320fbff30e..9d36cc88d043468db278b2ed3bf57599dc9e3251 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/seq_file.h>
 #include <linux/memcontrol.h>
 #include <linux/bpf-cgroup.h>
+#include <linux/siphash.h>
 
 extern struct inet_hashinfo tcp_hashinfo;
 
@@ -1612,8 +1613,7 @@ void tcp_free_fastopen_req(struct tcp_sock *tp);
 void tcp_fastopen_destroy_cipher(struct sock *sk);
 void tcp_fastopen_ctx_destroy(struct net *net);
 int tcp_fastopen_reset_cipher(struct net *net, struct sock *sk,
-                             void *primary_key, void *backup_key,
-                             unsigned int len);
+                             void *primary_key, void *backup_key);
 void tcp_fastopen_add_skb(struct sock *sk, struct sk_buff *skb);
 struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
                              struct request_sock *req,
@@ -1623,14 +1623,14 @@ void tcp_fastopen_init_key_once(struct net *net);
 bool tcp_fastopen_cookie_check(struct sock *sk, u16 *mss,
                             struct tcp_fastopen_cookie *cookie);
 bool tcp_fastopen_defer_connect(struct sock *sk, int *err);
-#define TCP_FASTOPEN_KEY_LENGTH 16
+#define TCP_FASTOPEN_KEY_LENGTH sizeof(siphash_key_t)
 #define TCP_FASTOPEN_KEY_MAX 2
 #define TCP_FASTOPEN_KEY_BUF_LENGTH \
        (TCP_FASTOPEN_KEY_LENGTH * TCP_FASTOPEN_KEY_MAX)
 
 /* Fastopen key context */
 struct tcp_fastopen_context {
-       __u8            key[TCP_FASTOPEN_KEY_MAX][TCP_FASTOPEN_KEY_LENGTH];
+       siphash_key_t   key[TCP_FASTOPEN_KEY_MAX];
        int             num;
        struct rcu_head rcu;
 };
index 7d802acde04016320b91e0c4e56dc008df7f61d2..7d66306b5f39719ac432178948ceac256018d52a 100644 (file)
@@ -365,8 +365,7 @@ static int proc_tcp_fastopen_key(struct ctl_table *table, int write,
                        }
                }
                tcp_fastopen_reset_cipher(net, NULL, key,
-                                         backup_data ? key + 4 : NULL,
-                                         TCP_FASTOPEN_KEY_LENGTH);
+                                         backup_data ? key + 4 : NULL);
        }
 
 bad_key:
index efd7f2b1d1f01cc7a8974791703a8e223f8f6d7b..47c217905864d74f822e15ffb4e7048477afcf0e 100644 (file)
@@ -2822,8 +2822,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
                if (optlen == TCP_FASTOPEN_KEY_BUF_LENGTH)
                        backup_key = key + TCP_FASTOPEN_KEY_LENGTH;
 
-               return tcp_fastopen_reset_cipher(net, sk, key, backup_key,
-                                                TCP_FASTOPEN_KEY_LENGTH);
+               return tcp_fastopen_reset_cipher(net, sk, key, backup_key);
        }
        default:
                /* fallthru */
index f918599181dd33f5caf45862d44ffcf312094e06..3fd451271a7034aab65f02f2bc331254b49f2153 100644 (file)
@@ -7,7 +7,6 @@
 #include <linux/tcp.h>
 #include <linux/rcupdate.h>
 #include <linux/rculist.h>
-#include <linux/siphash.h>
 #include <net/inetpeer.h>
 #include <net/tcp.h>
 
@@ -31,7 +30,7 @@ void tcp_fastopen_init_key_once(struct net *net)
         * for a valid cookie, so this is an acceptable risk.
         */
        get_random_bytes(key, sizeof(key));
-       tcp_fastopen_reset_cipher(net, NULL, key, NULL, sizeof(key));
+       tcp_fastopen_reset_cipher(net, NULL, key, NULL);
 }
 
 static void tcp_fastopen_ctx_free(struct rcu_head *head)
@@ -68,8 +67,7 @@ void tcp_fastopen_ctx_destroy(struct net *net)
 }
 
 int tcp_fastopen_reset_cipher(struct net *net, struct sock *sk,
-                             void *primary_key, void *backup_key,
-                             unsigned int len)
+                             void *primary_key, void *backup_key)
 {
        struct tcp_fastopen_context *ctx, *octx;
        struct fastopen_queue *q;
@@ -81,9 +79,11 @@ int tcp_fastopen_reset_cipher(struct net *net, struct sock *sk,
                goto out;
        }
 
-       memcpy(ctx->key[0], primary_key, len);
+       ctx->key[0].key[0] = get_unaligned_le64(primary_key);
+       ctx->key[0].key[1] = get_unaligned_le64(primary_key + 8);
        if (backup_key) {
-               memcpy(ctx->key[1], backup_key, len);
+               ctx->key[1].key[0] = get_unaligned_le64(backup_key);
+               ctx->key[1].key[1] = get_unaligned_le64(backup_key + 8);
                ctx->num = 2;
        } else {
                ctx->num = 1;
@@ -110,19 +110,18 @@ out:
 
 static bool __tcp_fastopen_cookie_gen_cipher(struct request_sock *req,
                                             struct sk_buff *syn,
-                                            const u8 *key,
+                                            const siphash_key_t *key,
                                             struct tcp_fastopen_cookie *foc)
 {
-       BUILD_BUG_ON(TCP_FASTOPEN_KEY_LENGTH != sizeof(siphash_key_t));
        BUILD_BUG_ON(TCP_FASTOPEN_COOKIE_SIZE != sizeof(u64));
 
        if (req->rsk_ops->family == AF_INET) {
                const struct iphdr *iph = ip_hdr(syn);
 
-               foc->val[0] = siphash(&iph->saddr,
-                                     sizeof(iph->saddr) +
-                                     sizeof(iph->daddr),
-                                     (const siphash_key_t *)key);
+               foc->val[0] = cpu_to_le64(siphash(&iph->saddr,
+                                         sizeof(iph->saddr) +
+                                         sizeof(iph->daddr),
+                                         key));
                foc->len = TCP_FASTOPEN_COOKIE_SIZE;
                return true;
        }
@@ -130,10 +129,10 @@ static bool __tcp_fastopen_cookie_gen_cipher(struct request_sock *req,
        if (req->rsk_ops->family == AF_INET6) {
                const struct ipv6hdr *ip6h = ipv6_hdr(syn);
 
-               foc->val[0] = siphash(&ip6h->saddr,
-                                     sizeof(ip6h->saddr) +
-                                     sizeof(ip6h->daddr),
-                                     (const siphash_key_t *)key);
+               foc->val[0] = cpu_to_le64(siphash(&ip6h->saddr,
+                                         sizeof(ip6h->saddr) +
+                                         sizeof(ip6h->daddr),
+                                         key));
                foc->len = TCP_FASTOPEN_COOKIE_SIZE;
                return true;
        }
@@ -154,7 +153,7 @@ static void tcp_fastopen_cookie_gen(struct sock *sk,
        rcu_read_lock();
        ctx = tcp_fastopen_get_ctx(sk);
        if (ctx)
-               __tcp_fastopen_cookie_gen_cipher(req, syn, ctx->key[0], foc);
+               __tcp_fastopen_cookie_gen_cipher(req, syn, &ctx->key[0], foc);
        rcu_read_unlock();
 }
 
@@ -218,7 +217,7 @@ static int tcp_fastopen_cookie_gen_check(struct sock *sk,
        if (!ctx)
                goto out;
        for (i = 0; i < tcp_fastopen_context_len(ctx); i++) {
-               __tcp_fastopen_cookie_gen_cipher(req, syn, ctx->key[i], foc);
+               __tcp_fastopen_cookie_gen_cipher(req, syn, &ctx->key[i], foc);
                if (tcp_fastopen_cookie_match(foc, orig)) {
                        ret = i + 1;
                        goto out;