Skip to content

Commit d48ecb4

Browse files
committed
Merge branch 'add-TFO-backup-key'
Jason Baron says: ==================== add TFO backup key Christoph, Igor, and I have worked on an API that facilitates TFO key rotation. This is a follow up to the series that Christoph previously posted, with an API that meets both of our use-cases. Here's a link to the previous work: https://patchwork.ozlabs.org/cover/1013753/ Changes in v2: -spelling fixes in ip-sysctl.txt (Jeremy Sowden) -re-base to latest net-next ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 5b5d331 + 10fbcdd commit d48ecb4

File tree

11 files changed

+694
-118
lines changed

11 files changed

+694
-118
lines changed

Documentation/networking/ip-sysctl.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,26 @@ tcp_fastopen_blackhole_timeout_sec - INTEGER
648648
0 to disable the blackhole detection.
649649
By default, it is set to 1hr.
650650

651+
tcp_fastopen_key - list of comma separated 32-digit hexadecimal INTEGERs
652+
The list consists of a primary key and an optional backup key. The
653+
primary key is used for both creating and validating cookies, while the
654+
optional backup key is only used for validating cookies. The purpose of
655+
the backup key is to maximize TFO validation when keys are rotated.
656+
657+
A randomly chosen primary key may be configured by the kernel if
658+
the tcp_fastopen sysctl is set to 0x400 (see above), or if the
659+
TCP_FASTOPEN setsockopt() optname is set and a key has not been
660+
previously configured via sysctl. If keys are configured via
661+
setsockopt() by using the TCP_FASTOPEN_KEY optname, then those
662+
per-socket keys will be used instead of any keys that are specified via
663+
sysctl.
664+
665+
A key is specified as 4 8-digit hexadecimal integers which are separated
666+
by a '-' as: xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx. Leading zeros may be
667+
omitted. A primary and a backup key may be specified by separating them
668+
by a comma. If only one key is specified, it becomes the primary key and
669+
any previously configured backup keys are removed.
670+
651671
tcp_syn_retries - INTEGER
652672
Number of times initial SYNs for an active TCP connection attempt
653673
will be retransmitted. Should not be higher than 127. Default value

include/net/tcp.h

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,7 +1614,8 @@ void tcp_free_fastopen_req(struct tcp_sock *tp);
16141614
void tcp_fastopen_destroy_cipher(struct sock *sk);
16151615
void tcp_fastopen_ctx_destroy(struct net *net);
16161616
int tcp_fastopen_reset_cipher(struct net *net, struct sock *sk,
1617-
void *key, unsigned int len);
1617+
void *primary_key, void *backup_key,
1618+
unsigned int len);
16181619
void tcp_fastopen_add_skb(struct sock *sk, struct sk_buff *skb);
16191620
struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
16201621
struct request_sock *req,
@@ -1625,11 +1626,14 @@ bool tcp_fastopen_cookie_check(struct sock *sk, u16 *mss,
16251626
struct tcp_fastopen_cookie *cookie);
16261627
bool tcp_fastopen_defer_connect(struct sock *sk, int *err);
16271628
#define TCP_FASTOPEN_KEY_LENGTH 16
1629+
#define TCP_FASTOPEN_KEY_MAX 2
1630+
#define TCP_FASTOPEN_KEY_BUF_LENGTH \
1631+
(TCP_FASTOPEN_KEY_LENGTH * TCP_FASTOPEN_KEY_MAX)
16281632

16291633
/* Fastopen key context */
16301634
struct tcp_fastopen_context {
1631-
struct crypto_cipher *tfm;
1632-
__u8 key[TCP_FASTOPEN_KEY_LENGTH];
1635+
struct crypto_cipher *tfm[TCP_FASTOPEN_KEY_MAX];
1636+
__u8 key[TCP_FASTOPEN_KEY_BUF_LENGTH];
16331637
struct rcu_head rcu;
16341638
};
16351639

@@ -1639,6 +1643,37 @@ bool tcp_fastopen_active_should_disable(struct sock *sk);
16391643
void tcp_fastopen_active_disable_ofo_check(struct sock *sk);
16401644
void tcp_fastopen_active_detect_blackhole(struct sock *sk, bool expired);
16411645

1646+
/* Caller needs to wrap with rcu_read_(un)lock() */
1647+
static inline
1648+
struct tcp_fastopen_context *tcp_fastopen_get_ctx(const struct sock *sk)
1649+
{
1650+
struct tcp_fastopen_context *ctx;
1651+
1652+
ctx = rcu_dereference(inet_csk(sk)->icsk_accept_queue.fastopenq.ctx);
1653+
if (!ctx)
1654+
ctx = rcu_dereference(sock_net(sk)->ipv4.tcp_fastopen_ctx);
1655+
return ctx;
1656+
}
1657+
1658+
static inline
1659+
bool tcp_fastopen_cookie_match(const struct tcp_fastopen_cookie *foc,
1660+
const struct tcp_fastopen_cookie *orig)
1661+
{
1662+
if (orig->len == TCP_FASTOPEN_COOKIE_SIZE &&
1663+
orig->len == foc->len &&
1664+
!memcmp(orig->val, foc->val, foc->len))
1665+
return true;
1666+
return false;
1667+
}
1668+
1669+
static inline
1670+
int tcp_fastopen_context_len(const struct tcp_fastopen_context *ctx)
1671+
{
1672+
if (ctx->tfm[1])
1673+
return 2;
1674+
return 1;
1675+
}
1676+
16421677
/* Latencies incurred by various limits for a sender. They are
16431678
* chronograph-like stats that are mutually exclusive.
16441679
*/

include/uapi/linux/snmp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ enum
283283
LINUX_MIB_TCPACKCOMPRESSED, /* TCPAckCompressed */
284284
LINUX_MIB_TCPZEROWINDOWDROP, /* TCPZeroWindowDrop */
285285
LINUX_MIB_TCPRCVQDROP, /* TCPRcvQDrop */
286+
LINUX_MIB_TCPFASTOPENPASSIVEALTKEY, /* TCPFastOpenPassiveAltKey */
286287
__LINUX_MIB_MAX
287288
};
288289

net/ipv4/proc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ static const struct snmp_mib snmp4_net_list[] = {
291291
SNMP_MIB_ITEM("TCPAckCompressed", LINUX_MIB_TCPACKCOMPRESSED),
292292
SNMP_MIB_ITEM("TCPZeroWindowDrop", LINUX_MIB_TCPZEROWINDOWDROP),
293293
SNMP_MIB_ITEM("TCPRcvQDrop", LINUX_MIB_TCPRCVQDROP),
294+
SNMP_MIB_ITEM("TCPFastOpenPassiveAltKey", LINUX_MIB_TCPFASTOPENPASSIVEALTKEY),
294295
SNMP_MIB_SENTINEL
295296
};
296297

net/ipv4/sysctl_net_ipv4.c

Lines changed: 70 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -277,55 +277,97 @@ static int proc_allowed_congestion_control(struct ctl_table *ctl,
277277
return ret;
278278
}
279279

280+
static int sscanf_key(char *buf, __le32 *key)
281+
{
282+
u32 user_key[4];
283+
int i, ret = 0;
284+
285+
if (sscanf(buf, "%x-%x-%x-%x", user_key, user_key + 1,
286+
user_key + 2, user_key + 3) != 4) {
287+
ret = -EINVAL;
288+
} else {
289+
for (i = 0; i < ARRAY_SIZE(user_key); i++)
290+
key[i] = cpu_to_le32(user_key[i]);
291+
}
292+
pr_debug("proc TFO key set 0x%x-%x-%x-%x <- 0x%s: %u\n",
293+
user_key[0], user_key[1], user_key[2], user_key[3], buf, ret);
294+
295+
return ret;
296+
}
297+
280298
static int proc_tcp_fastopen_key(struct ctl_table *table, int write,
281299
void __user *buffer, size_t *lenp,
282300
loff_t *ppos)
283301
{
284302
struct net *net = container_of(table->data, struct net,
285303
ipv4.sysctl_tcp_fastopen);
286-
struct ctl_table tbl = { .maxlen = (TCP_FASTOPEN_KEY_LENGTH * 2 + 10) };
287-
struct tcp_fastopen_context *ctxt;
288-
u32 user_key[4]; /* 16 bytes, matching TCP_FASTOPEN_KEY_LENGTH */
289-
__le32 key[4];
290-
int ret, i;
304+
/* maxlen to print the list of keys in hex (*2), with dashes
305+
* separating doublewords and a comma in between keys.
306+
*/
307+
struct ctl_table tbl = { .maxlen = ((TCP_FASTOPEN_KEY_LENGTH *
308+
2 * TCP_FASTOPEN_KEY_MAX) +
309+
(TCP_FASTOPEN_KEY_MAX * 5)) };
310+
struct tcp_fastopen_context *ctx;
311+
u32 user_key[TCP_FASTOPEN_KEY_MAX * 4];
312+
__le32 key[TCP_FASTOPEN_KEY_MAX * 4];
313+
char *backup_data;
314+
int ret, i = 0, off = 0, n_keys = 0;
291315

292316
tbl.data = kmalloc(tbl.maxlen, GFP_KERNEL);
293317
if (!tbl.data)
294318
return -ENOMEM;
295319

296320
rcu_read_lock();
297-
ctxt = rcu_dereference(net->ipv4.tcp_fastopen_ctx);
298-
if (ctxt)
299-
memcpy(key, ctxt->key, TCP_FASTOPEN_KEY_LENGTH);
300-
else
301-
memset(key, 0, sizeof(key));
321+
ctx = rcu_dereference(net->ipv4.tcp_fastopen_ctx);
322+
if (ctx) {
323+
n_keys = tcp_fastopen_context_len(ctx);
324+
memcpy(&key[0], &ctx->key[0], TCP_FASTOPEN_KEY_LENGTH * n_keys);
325+
}
302326
rcu_read_unlock();
303327

304-
for (i = 0; i < ARRAY_SIZE(key); i++)
328+
if (!n_keys) {
329+
memset(&key[0], 0, TCP_FASTOPEN_KEY_LENGTH);
330+
n_keys = 1;
331+
}
332+
333+
for (i = 0; i < n_keys * 4; i++)
305334
user_key[i] = le32_to_cpu(key[i]);
306335

307-
snprintf(tbl.data, tbl.maxlen, "%08x-%08x-%08x-%08x",
308-
user_key[0], user_key[1], user_key[2], user_key[3]);
336+
for (i = 0; i < n_keys; i++) {
337+
off += snprintf(tbl.data + off, tbl.maxlen - off,
338+
"%08x-%08x-%08x-%08x",
339+
user_key[i * 4],
340+
user_key[i * 4 + 1],
341+
user_key[i * 4 + 2],
342+
user_key[i * 4 + 3]);
343+
if (i + 1 < n_keys)
344+
off += snprintf(tbl.data + off, tbl.maxlen - off, ",");
345+
}
346+
309347
ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
310348

311349
if (write && ret == 0) {
312-
if (sscanf(tbl.data, "%x-%x-%x-%x", user_key, user_key + 1,
313-
user_key + 2, user_key + 3) != 4) {
350+
backup_data = strchr(tbl.data, ',');
351+
if (backup_data) {
352+
*backup_data = '\0';
353+
backup_data++;
354+
}
355+
if (sscanf_key(tbl.data, key)) {
314356
ret = -EINVAL;
315357
goto bad_key;
316358
}
317-
318-
for (i = 0; i < ARRAY_SIZE(user_key); i++)
319-
key[i] = cpu_to_le32(user_key[i]);
320-
359+
if (backup_data) {
360+
if (sscanf_key(backup_data, key + 4)) {
361+
ret = -EINVAL;
362+
goto bad_key;
363+
}
364+
}
321365
tcp_fastopen_reset_cipher(net, NULL, key,
366+
backup_data ? key + 4 : NULL,
322367
TCP_FASTOPEN_KEY_LENGTH);
323368
}
324369

325370
bad_key:
326-
pr_debug("proc FO key set 0x%x-%x-%x-%x <- 0x%s: %u\n",
327-
user_key[0], user_key[1], user_key[2], user_key[3],
328-
(char *)tbl.data, ret);
329371
kfree(tbl.data);
330372
return ret;
331373
}
@@ -933,7 +975,12 @@ static struct ctl_table ipv4_net_table[] = {
933975
.procname = "tcp_fastopen_key",
934976
.mode = 0600,
935977
.data = &init_net.ipv4.sysctl_tcp_fastopen,
936-
.maxlen = ((TCP_FASTOPEN_KEY_LENGTH * 2) + 10),
978+
/* maxlen to print the list of keys in hex (*2), with dashes
979+
* separating doublewords and a comma in between keys.
980+
*/
981+
.maxlen = ((TCP_FASTOPEN_KEY_LENGTH *
982+
2 * TCP_FASTOPEN_KEY_MAX) +
983+
(TCP_FASTOPEN_KEY_MAX * 5)),
937984
.proc_handler = proc_tcp_fastopen_key,
938985
},
939986
{

net/ipv4/tcp.c

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2790,15 +2790,24 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
27902790
return err;
27912791
}
27922792
case TCP_FASTOPEN_KEY: {
2793-
__u8 key[TCP_FASTOPEN_KEY_LENGTH];
2793+
__u8 key[TCP_FASTOPEN_KEY_BUF_LENGTH];
2794+
__u8 *backup_key = NULL;
27942795

2795-
if (optlen != sizeof(key))
2796+
/* Allow a backup key as well to facilitate key rotation
2797+
* First key is the active one.
2798+
*/
2799+
if (optlen != TCP_FASTOPEN_KEY_LENGTH &&
2800+
optlen != TCP_FASTOPEN_KEY_BUF_LENGTH)
27962801
return -EINVAL;
27972802

27982803
if (copy_from_user(key, optval, optlen))
27992804
return -EFAULT;
28002805

2801-
return tcp_fastopen_reset_cipher(net, sk, key, sizeof(key));
2806+
if (optlen == TCP_FASTOPEN_KEY_BUF_LENGTH)
2807+
backup_key = key + TCP_FASTOPEN_KEY_LENGTH;
2808+
2809+
return tcp_fastopen_reset_cipher(net, sk, key, backup_key,
2810+
TCP_FASTOPEN_KEY_LENGTH);
28022811
}
28032812
default:
28042813
/* fallthru */
@@ -3452,21 +3461,23 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
34523461
return 0;
34533462

34543463
case TCP_FASTOPEN_KEY: {
3455-
__u8 key[TCP_FASTOPEN_KEY_LENGTH];
3464+
__u8 key[TCP_FASTOPEN_KEY_BUF_LENGTH];
34563465
struct tcp_fastopen_context *ctx;
3466+
unsigned int key_len = 0;
34573467

34583468
if (get_user(len, optlen))
34593469
return -EFAULT;
34603470

34613471
rcu_read_lock();
34623472
ctx = rcu_dereference(icsk->icsk_accept_queue.fastopenq.ctx);
3463-
if (ctx)
3464-
memcpy(key, ctx->key, sizeof(key));
3465-
else
3466-
len = 0;
3473+
if (ctx) {
3474+
key_len = tcp_fastopen_context_len(ctx) *
3475+
TCP_FASTOPEN_KEY_LENGTH;
3476+
memcpy(&key[0], &ctx->key[0], key_len);
3477+
}
34673478
rcu_read_unlock();
34683479

3469-
len = min_t(unsigned int, len, sizeof(key));
3480+
len = min_t(unsigned int, len, key_len);
34703481
if (put_user(len, optlen))
34713482
return -EFAULT;
34723483
if (copy_to_user(optval, key, len))

0 commit comments

Comments
 (0)