Commit f19008e6 authored by Jason Baron's avatar Jason Baron Committed by David S. Miller

tcp: correct read of TFO keys on big endian systems

When TFO keys are read back on big endian systems either via the global
sysctl interface or via getsockopt() using TCP_FASTOPEN_KEY, the values
don't match what was written.

For example, on s390x:

# echo "1-2-3-4" > /proc/sys/net/ipv4/tcp_fastopen_key
# cat /proc/sys/net/ipv4/tcp_fastopen_key
02000000-01000000-04000000-03000000

Instead of:

# cat /proc/sys/net/ipv4/tcp_fastopen_key
00000001-00000002-00000003-00000004

Fix this by converting to the correct endianness on read. This was
reported by Colin Ian King when running the 'tcp_fastopen_backup_key' net
selftest on s390x, which depends on the read value matching what was
written. I've confirmed that the test now passes on big and little endian
systems.
Signed-off-by: default avatarJason Baron <jbaron@akamai.com>
Fixes: 438ac880 ("net: fastopen: robustness and endianness fixes for SipHash")
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Eric Dumazet <edumazet@google.com>
Reported-and-tested-by: default avatarColin Ian King <colin.king@canonical.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 56e287b3
...@@ -1672,6 +1672,8 @@ void tcp_fastopen_destroy_cipher(struct sock *sk); ...@@ -1672,6 +1672,8 @@ void tcp_fastopen_destroy_cipher(struct sock *sk);
void tcp_fastopen_ctx_destroy(struct net *net); void tcp_fastopen_ctx_destroy(struct net *net);
int tcp_fastopen_reset_cipher(struct net *net, struct sock *sk, int tcp_fastopen_reset_cipher(struct net *net, struct sock *sk,
void *primary_key, void *backup_key); void *primary_key, void *backup_key);
int tcp_fastopen_get_cipher(struct net *net, struct inet_connection_sock *icsk,
u64 *key);
void tcp_fastopen_add_skb(struct sock *sk, struct sk_buff *skb); 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 sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
struct request_sock *req, struct request_sock *req,
......
...@@ -301,24 +301,16 @@ static int proc_tcp_fastopen_key(struct ctl_table *table, int write, ...@@ -301,24 +301,16 @@ static int proc_tcp_fastopen_key(struct ctl_table *table, int write,
struct ctl_table tbl = { .maxlen = ((TCP_FASTOPEN_KEY_LENGTH * struct ctl_table tbl = { .maxlen = ((TCP_FASTOPEN_KEY_LENGTH *
2 * TCP_FASTOPEN_KEY_MAX) + 2 * TCP_FASTOPEN_KEY_MAX) +
(TCP_FASTOPEN_KEY_MAX * 5)) }; (TCP_FASTOPEN_KEY_MAX * 5)) };
struct tcp_fastopen_context *ctx; u32 user_key[TCP_FASTOPEN_KEY_BUF_LENGTH / sizeof(u32)];
u32 user_key[TCP_FASTOPEN_KEY_MAX * 4]; __le32 key[TCP_FASTOPEN_KEY_BUF_LENGTH / sizeof(__le32)];
__le32 key[TCP_FASTOPEN_KEY_MAX * 4];
char *backup_data; char *backup_data;
int ret, i = 0, off = 0, n_keys = 0; int ret, i = 0, off = 0, n_keys;
tbl.data = kmalloc(tbl.maxlen, GFP_KERNEL); tbl.data = kmalloc(tbl.maxlen, GFP_KERNEL);
if (!tbl.data) if (!tbl.data)
return -ENOMEM; return -ENOMEM;
rcu_read_lock(); n_keys = tcp_fastopen_get_cipher(net, NULL, (u64 *)key);
ctx = rcu_dereference(net->ipv4.tcp_fastopen_ctx);
if (ctx) {
n_keys = tcp_fastopen_context_len(ctx);
memcpy(&key[0], &ctx->key[0], TCP_FASTOPEN_KEY_LENGTH * n_keys);
}
rcu_read_unlock();
if (!n_keys) { if (!n_keys) {
memset(&key[0], 0, TCP_FASTOPEN_KEY_LENGTH); memset(&key[0], 0, TCP_FASTOPEN_KEY_LENGTH);
n_keys = 1; n_keys = 1;
......
...@@ -3685,22 +3685,14 @@ static int do_tcp_getsockopt(struct sock *sk, int level, ...@@ -3685,22 +3685,14 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
return 0; return 0;
case TCP_FASTOPEN_KEY: { case TCP_FASTOPEN_KEY: {
__u8 key[TCP_FASTOPEN_KEY_BUF_LENGTH]; u64 key[TCP_FASTOPEN_KEY_BUF_LENGTH / sizeof(u64)];
struct tcp_fastopen_context *ctx; unsigned int key_len;
unsigned int key_len = 0;
if (get_user(len, optlen)) if (get_user(len, optlen))
return -EFAULT; return -EFAULT;
rcu_read_lock(); key_len = tcp_fastopen_get_cipher(net, icsk, key) *
ctx = rcu_dereference(icsk->icsk_accept_queue.fastopenq.ctx);
if (ctx) {
key_len = tcp_fastopen_context_len(ctx) *
TCP_FASTOPEN_KEY_LENGTH; TCP_FASTOPEN_KEY_LENGTH;
memcpy(&key[0], &ctx->key[0], key_len);
}
rcu_read_unlock();
len = min_t(unsigned int, len, key_len); len = min_t(unsigned int, len, key_len);
if (put_user(len, optlen)) if (put_user(len, optlen))
return -EFAULT; return -EFAULT;
......
...@@ -108,6 +108,29 @@ int tcp_fastopen_reset_cipher(struct net *net, struct sock *sk, ...@@ -108,6 +108,29 @@ int tcp_fastopen_reset_cipher(struct net *net, struct sock *sk,
return err; return err;
} }
int tcp_fastopen_get_cipher(struct net *net, struct inet_connection_sock *icsk,
u64 *key)
{
struct tcp_fastopen_context *ctx;
int n_keys = 0, i;
rcu_read_lock();
if (icsk)
ctx = rcu_dereference(icsk->icsk_accept_queue.fastopenq.ctx);
else
ctx = rcu_dereference(net->ipv4.tcp_fastopen_ctx);
if (ctx) {
n_keys = tcp_fastopen_context_len(ctx);
for (i = 0; i < n_keys; i++) {
put_unaligned_le64(ctx->key[i].key[0], key + (i * 2));
put_unaligned_le64(ctx->key[i].key[1], key + (i * 2) + 1);
}
}
rcu_read_unlock();
return n_keys;
}
static bool __tcp_fastopen_cookie_gen_cipher(struct request_sock *req, static bool __tcp_fastopen_cookie_gen_cipher(struct request_sock *req,
struct sk_buff *syn, struct sk_buff *syn,
const siphash_key_t *key, const siphash_key_t *key,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment