tcp: add support for optional TFO backup key to net.ipv4.tcp_fastopen_key

Add the ability to add a backup TFO key as:

# echo "x-x-x-x,x-x-x-x" > /proc/sys/net/ipv4/tcp_fastopen_key

The key before the comma acks as the primary TFO key and the key after the
comma is the backup TFO key. This change is intended to be backwards
compatible since if only one key is set, userspace will simply read back
that single key as follows:

# echo "x-x-x-x" > /proc/sys/net/ipv4/tcp_fastopen_key
# cat /proc/sys/net/ipv4/tcp_fastopen_key
x-x-x-x

Signed-off-by: Jason Baron <jbaron@akamai.com>
Signed-off-by: Christoph Paasch <cpaasch@apple.com>
Acked-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Jason Baron 2019-05-29 12:33:59 -04:00 committed by David S. Miller
parent 0f1ce02368
commit aa1236cdfa

View File

@ -277,55 +277,97 @@ static int proc_allowed_congestion_control(struct ctl_table *ctl,
return ret; return ret;
} }
static int sscanf_key(char *buf, __le32 *key)
{
u32 user_key[4];
int i, ret = 0;
if (sscanf(buf, "%x-%x-%x-%x", user_key, user_key + 1,
user_key + 2, user_key + 3) != 4) {
ret = -EINVAL;
} else {
for (i = 0; i < ARRAY_SIZE(user_key); i++)
key[i] = cpu_to_le32(user_key[i]);
}
pr_debug("proc TFO key set 0x%x-%x-%x-%x <- 0x%s: %u\n",
user_key[0], user_key[1], user_key[2], user_key[3], buf, ret);
return ret;
}
static int proc_tcp_fastopen_key(struct ctl_table *table, int write, static int proc_tcp_fastopen_key(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, void __user *buffer, size_t *lenp,
loff_t *ppos) loff_t *ppos)
{ {
struct net *net = container_of(table->data, struct net, struct net *net = container_of(table->data, struct net,
ipv4.sysctl_tcp_fastopen); ipv4.sysctl_tcp_fastopen);
struct ctl_table tbl = { .maxlen = (TCP_FASTOPEN_KEY_LENGTH * 2 + 10) }; /* maxlen to print the list of keys in hex (*2), with dashes
struct tcp_fastopen_context *ctxt; * separating doublewords and a comma in between keys.
u32 user_key[4]; /* 16 bytes, matching TCP_FASTOPEN_KEY_LENGTH */ */
__le32 key[4]; struct ctl_table tbl = { .maxlen = ((TCP_FASTOPEN_KEY_LENGTH *
int ret, i; 2 * TCP_FASTOPEN_KEY_MAX) +
(TCP_FASTOPEN_KEY_MAX * 5)) };
struct tcp_fastopen_context *ctx;
u32 user_key[TCP_FASTOPEN_KEY_MAX * 4];
__le32 key[TCP_FASTOPEN_KEY_MAX * 4];
char *backup_data;
int ret, i = 0, off = 0, n_keys = 0;
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(); rcu_read_lock();
ctxt = rcu_dereference(net->ipv4.tcp_fastopen_ctx); ctx = rcu_dereference(net->ipv4.tcp_fastopen_ctx);
if (ctxt) if (ctx) {
memcpy(key, ctxt->key, TCP_FASTOPEN_KEY_LENGTH); n_keys = tcp_fastopen_context_len(ctx);
else memcpy(&key[0], &ctx->key[0], TCP_FASTOPEN_KEY_LENGTH * n_keys);
memset(key, 0, sizeof(key)); }
rcu_read_unlock(); rcu_read_unlock();
for (i = 0; i < ARRAY_SIZE(key); i++) if (!n_keys) {
memset(&key[0], 0, TCP_FASTOPEN_KEY_LENGTH);
n_keys = 1;
}
for (i = 0; i < n_keys * 4; i++)
user_key[i] = le32_to_cpu(key[i]); user_key[i] = le32_to_cpu(key[i]);
snprintf(tbl.data, tbl.maxlen, "%08x-%08x-%08x-%08x", for (i = 0; i < n_keys; i++) {
user_key[0], user_key[1], user_key[2], user_key[3]); off += snprintf(tbl.data + off, tbl.maxlen - off,
"%08x-%08x-%08x-%08x",
user_key[i * 4],
user_key[i * 4 + 1],
user_key[i * 4 + 2],
user_key[i * 4 + 3]);
if (i + 1 < n_keys)
off += snprintf(tbl.data + off, tbl.maxlen - off, ",");
}
ret = proc_dostring(&tbl, write, buffer, lenp, ppos); ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
if (write && ret == 0) { if (write && ret == 0) {
if (sscanf(tbl.data, "%x-%x-%x-%x", user_key, user_key + 1, backup_data = strchr(tbl.data, ',');
user_key + 2, user_key + 3) != 4) { if (backup_data) {
*backup_data = '\0';
backup_data++;
}
if (sscanf_key(tbl.data, key)) {
ret = -EINVAL; ret = -EINVAL;
goto bad_key; goto bad_key;
} }
if (backup_data) {
for (i = 0; i < ARRAY_SIZE(user_key); i++) if (sscanf_key(backup_data, key + 4)) {
key[i] = cpu_to_le32(user_key[i]); ret = -EINVAL;
goto bad_key;
tcp_fastopen_reset_cipher(net, NULL, key, NULL, }
}
tcp_fastopen_reset_cipher(net, NULL, key,
backup_data ? key + 4 : NULL,
TCP_FASTOPEN_KEY_LENGTH); TCP_FASTOPEN_KEY_LENGTH);
} }
bad_key: bad_key:
pr_debug("proc FO key set 0x%x-%x-%x-%x <- 0x%s: %u\n",
user_key[0], user_key[1], user_key[2], user_key[3],
(char *)tbl.data, ret);
kfree(tbl.data); kfree(tbl.data);
return ret; return ret;
} }
@ -933,7 +975,12 @@ static struct ctl_table ipv4_net_table[] = {
.procname = "tcp_fastopen_key", .procname = "tcp_fastopen_key",
.mode = 0600, .mode = 0600,
.data = &init_net.ipv4.sysctl_tcp_fastopen, .data = &init_net.ipv4.sysctl_tcp_fastopen,
.maxlen = ((TCP_FASTOPEN_KEY_LENGTH * 2) + 10), /* maxlen to print the list of keys in hex (*2), with dashes
* separating doublewords and a comma in between keys.
*/
.maxlen = ((TCP_FASTOPEN_KEY_LENGTH *
2 * TCP_FASTOPEN_KEY_MAX) +
(TCP_FASTOPEN_KEY_MAX * 5)),
.proc_handler = proc_tcp_fastopen_key, .proc_handler = proc_tcp_fastopen_key,
}, },
{ {