Skip to content

Commit 1046716

Browse files
hkchudavem330
authored andcommitted
tcp: TCP Fast Open Server - header & support functions
This patch adds all the necessary data structure and support functions to implement TFO server side. It also documents a number of flags for the sysctl_tcp_fastopen knob, and adds a few Linux extension MIBs. In addition, it includes the following: 1. a new TCP_FASTOPEN socket option an application must call to supply a max backlog allowed in order to enable TFO on its listener. 2. A number of key data structures: "fastopen_rsk" in tcp_sock - for a big socket to access its request_sock for retransmission and ack processing purpose. It is non-NULL iff 3WHS not completed. "fastopenq" in request_sock_queue - points to a per Fast Open listener data structure "fastopen_queue" to keep track of qlen (# of outstanding Fast Open requests) and max_qlen, among other things. "listener" in tcp_request_sock - to point to the original listener for book-keeping purpose, i.e., to maintain qlen against max_qlen as part of defense against IP spoofing attack. 3. various data structure and functions, many in tcp_fastopen.c, to support server side Fast Open cookie operations, including /proc/sys/net/ipv4/tcp_fastopen_key to allow manual rekeying. Signed-off-by: H.K. Jerry Chu <[email protected]> Cc: Yuchung Cheng <[email protected]> Cc: Neal Cardwell <[email protected]> Cc: Eric Dumazet <[email protected]> Cc: Tom Herbert <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 2a35cfa commit 1046716

File tree

9 files changed

+276
-20
lines changed

9 files changed

+276
-20
lines changed

Documentation/networking/ip-sysctl.txt

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -467,16 +467,31 @@ tcp_syncookies - BOOLEAN
467467
tcp_fastopen - INTEGER
468468
Enable TCP Fast Open feature (draft-ietf-tcpm-fastopen) to send data
469469
in the opening SYN packet. To use this feature, the client application
470-
must not use connect(). Instead, it should use sendmsg() or sendto()
471-
with MSG_FASTOPEN flag which performs a TCP handshake automatically.
472-
473-
The values (bitmap) are:
474-
1: Enables sending data in the opening SYN on the client
475-
5: Enables sending data in the opening SYN on the client regardless
476-
of cookie availability.
470+
must use sendmsg() or sendto() with MSG_FASTOPEN flag rather than
471+
connect() to perform a TCP handshake automatically.
472+
473+
The values (bitmap) are
474+
1: Enables sending data in the opening SYN on the client.
475+
2: Enables TCP Fast Open on the server side, i.e., allowing data in
476+
a SYN packet to be accepted and passed to the application before
477+
3-way hand shake finishes.
478+
4: Send data in the opening SYN regardless of cookie availability and
479+
without a cookie option.
480+
0x100: Accept SYN data w/o validating the cookie.
481+
0x200: Accept data-in-SYN w/o any cookie option present.
482+
0x400/0x800: Enable Fast Open on all listeners regardless of the
483+
TCP_FASTOPEN socket option. The two different flags designate two
484+
different ways of setting max_qlen without the TCP_FASTOPEN socket
485+
option.
477486

478487
Default: 0
479488

489+
Note that the client & server side Fast Open flags (1 and 2
490+
respectively) must be also enabled before the rest of flags can take
491+
effect.
492+
493+
See include/net/tcp.h and the code for more details.
494+
480495
tcp_syn_retries - INTEGER
481496
Number of times initial SYNs for an active TCP connection attempt
482497
will be retransmitted. Should not be higher than 255. Default value

include/linux/snmp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,10 @@ enum
241241
LINUX_MIB_TCPCHALLENGEACK, /* TCPChallengeACK */
242242
LINUX_MIB_TCPSYNCHALLENGE, /* TCPSYNChallenge */
243243
LINUX_MIB_TCPFASTOPENACTIVE, /* TCPFastOpenActive */
244+
LINUX_MIB_TCPFASTOPENPASSIVE, /* TCPFastOpenPassive*/
245+
LINUX_MIB_TCPFASTOPENPASSIVEFAIL, /* TCPFastOpenPassiveFail */
246+
LINUX_MIB_TCPFASTOPENLISTENOVERFLOW, /* TCPFastOpenListenOverflow */
247+
LINUX_MIB_TCPFASTOPENCOOKIEREQD, /* TCPFastOpenCookieReqd */
244248
__LINUX_MIB_MAX
245249
};
246250

include/linux/tcp.h

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ enum {
110110
#define TCP_REPAIR_QUEUE 20
111111
#define TCP_QUEUE_SEQ 21
112112
#define TCP_REPAIR_OPTIONS 22
113+
#define TCP_FASTOPEN 23 /* Enable FastOpen on listeners */
113114

114115
struct tcp_repair_opt {
115116
__u32 opt_code;
@@ -246,6 +247,7 @@ static inline unsigned int tcp_optlen(const struct sk_buff *skb)
246247
/* TCP Fast Open */
247248
#define TCP_FASTOPEN_COOKIE_MIN 4 /* Min Fast Open Cookie size in bytes */
248249
#define TCP_FASTOPEN_COOKIE_MAX 16 /* Max Fast Open Cookie size in bytes */
250+
#define TCP_FASTOPEN_COOKIE_SIZE 8 /* the size employed by this impl. */
249251

250252
/* TCP Fast Open Cookie as stored in memory */
251253
struct tcp_fastopen_cookie {
@@ -312,9 +314,14 @@ struct tcp_request_sock {
312314
/* Only used by TCP MD5 Signature so far. */
313315
const struct tcp_request_sock_ops *af_specific;
314316
#endif
317+
struct sock *listener; /* needed for TFO */
315318
u32 rcv_isn;
316319
u32 snt_isn;
317320
u32 snt_synack; /* synack sent time */
321+
u32 rcv_nxt; /* the ack # by SYNACK. For
322+
* FastOpen it's the seq#
323+
* after data-in-SYN.
324+
*/
318325
};
319326

320327
static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req)
@@ -505,14 +512,18 @@ struct tcp_sock {
505512
struct tcp_md5sig_info __rcu *md5sig_info;
506513
#endif
507514

508-
/* TCP fastopen related information */
509-
struct tcp_fastopen_request *fastopen_req;
510-
511515
/* When the cookie options are generated and exchanged, then this
512516
* object holds a reference to them (cookie_values->kref). Also
513517
* contains related tcp_cookie_transactions fields.
514518
*/
515519
struct tcp_cookie_values *cookie_values;
520+
521+
/* TCP fastopen related information */
522+
struct tcp_fastopen_request *fastopen_req;
523+
/* fastopen_rsk points to request_sock that resulted in this big
524+
* socket. Used to retransmit SYNACKs etc.
525+
*/
526+
struct request_sock *fastopen_rsk;
516527
};
517528

518529
enum tsq_flags {
@@ -552,6 +563,34 @@ static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk)
552563
return (struct tcp_timewait_sock *)sk;
553564
}
554565

566+
static inline bool tcp_passive_fastopen(const struct sock *sk)
567+
{
568+
return (sk->sk_state == TCP_SYN_RECV &&
569+
tcp_sk(sk)->fastopen_rsk != NULL);
570+
}
571+
572+
static inline bool fastopen_cookie_present(struct tcp_fastopen_cookie *foc)
573+
{
574+
return foc->len != -1;
575+
}
576+
577+
static inline int fastopen_init_queue(struct sock *sk, int backlog)
578+
{
579+
struct request_sock_queue *queue =
580+
&inet_csk(sk)->icsk_accept_queue;
581+
582+
if (queue->fastopenq == NULL) {
583+
queue->fastopenq = kzalloc(
584+
sizeof(struct fastopen_queue),
585+
sk->sk_allocation);
586+
if (queue->fastopenq == NULL)
587+
return -ENOMEM;
588+
spin_lock_init(&queue->fastopenq->lock);
589+
}
590+
queue->fastopenq->max_qlen = backlog;
591+
return 0;
592+
}
593+
555594
#endif /* __KERNEL__ */
556595

557596
#endif /* _LINUX_TCP_H */

include/net/request_sock.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,34 @@ struct listen_sock {
106106
struct request_sock *syn_table[0];
107107
};
108108

109+
/*
110+
* For a TCP Fast Open listener -
111+
* lock - protects the access to all the reqsk, which is co-owned by
112+
* the listener and the child socket.
113+
* qlen - pending TFO requests (still in TCP_SYN_RECV).
114+
* max_qlen - max TFO reqs allowed before TFO is disabled.
115+
*
116+
* XXX (TFO) - ideally these fields can be made as part of "listen_sock"
117+
* structure above. But there is some implementation difficulty due to
118+
* listen_sock being part of request_sock_queue hence will be freed when
119+
* a listener is stopped. But TFO related fields may continue to be
120+
* accessed even after a listener is closed, until its sk_refcnt drops
121+
* to 0 implying no more outstanding TFO reqs. One solution is to keep
122+
* listen_opt around until sk_refcnt drops to 0. But there is some other
123+
* complexity that needs to be resolved. E.g., a listener can be disabled
124+
* temporarily through shutdown()->tcp_disconnect(), and re-enabled later.
125+
*/
126+
struct fastopen_queue {
127+
struct request_sock *rskq_rst_head; /* Keep track of past TFO */
128+
struct request_sock *rskq_rst_tail; /* requests that caused RST.
129+
* This is part of the defense
130+
* against spoofing attack.
131+
*/
132+
spinlock_t lock;
133+
int qlen; /* # of pending (TCP_SYN_RECV) reqs */
134+
int max_qlen; /* != 0 iff TFO is currently enabled */
135+
};
136+
109137
/** struct request_sock_queue - queue of request_socks
110138
*
111139
* @rskq_accept_head - FIFO head of established children
@@ -129,13 +157,21 @@ struct request_sock_queue {
129157
u8 rskq_defer_accept;
130158
/* 3 bytes hole, try to pack */
131159
struct listen_sock *listen_opt;
160+
struct fastopen_queue *fastopenq; /* This is non-NULL iff TFO has been
161+
* enabled on this listener. Check
162+
* max_qlen != 0 in fastopen_queue
163+
* to determine if TFO is enabled
164+
* right at this moment.
165+
*/
132166
};
133167

134168
extern int reqsk_queue_alloc(struct request_sock_queue *queue,
135169
unsigned int nr_table_entries);
136170

137171
extern void __reqsk_queue_destroy(struct request_sock_queue *queue);
138172
extern void reqsk_queue_destroy(struct request_sock_queue *queue);
173+
extern void reqsk_fastopen_remove(struct sock *sk,
174+
struct request_sock *req, bool reset);
139175

140176
static inline struct request_sock *
141177
reqsk_queue_yank_acceptq(struct request_sock_queue *queue)

include/net/tcp.h

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,24 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo);
224224

225225
/* Bit Flags for sysctl_tcp_fastopen */
226226
#define TFO_CLIENT_ENABLE 1
227+
#define TFO_SERVER_ENABLE 2
227228
#define TFO_CLIENT_NO_COOKIE 4 /* Data in SYN w/o cookie option */
228229

230+
/* Process SYN data but skip cookie validation */
231+
#define TFO_SERVER_COOKIE_NOT_CHKED 0x100
232+
/* Accept SYN data w/o any cookie option */
233+
#define TFO_SERVER_COOKIE_NOT_REQD 0x200
234+
235+
/* Force enable TFO on all listeners, i.e., not requiring the
236+
* TCP_FASTOPEN socket option. SOCKOPT1/2 determine how to set max_qlen.
237+
*/
238+
#define TFO_SERVER_WO_SOCKOPT1 0x400
239+
#define TFO_SERVER_WO_SOCKOPT2 0x800
240+
/* Always create TFO child sockets on a TFO listener even when
241+
* cookie/data not present. (For testing purpose!)
242+
*/
243+
#define TFO_SERVER_ALWAYS 0x1000
244+
229245
extern struct inet_timewait_death_row tcp_death_row;
230246

231247
/* sysctl variables for tcp */
@@ -421,12 +437,6 @@ extern void tcp_metrics_init(void);
421437
extern bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst, bool paws_check);
422438
extern bool tcp_remember_stamp(struct sock *sk);
423439
extern bool tcp_tw_remember_stamp(struct inet_timewait_sock *tw);
424-
extern void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
425-
struct tcp_fastopen_cookie *cookie,
426-
int *syn_loss, unsigned long *last_syn_loss);
427-
extern void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
428-
struct tcp_fastopen_cookie *cookie,
429-
bool syn_lost);
430440
extern void tcp_fetch_timewait_stamp(struct sock *sk, struct dst_entry *dst);
431441
extern void tcp_disable_fack(struct tcp_sock *tp);
432442
extern void tcp_close(struct sock *sk, long timeout);
@@ -537,6 +547,7 @@ extern void tcp_send_delayed_ack(struct sock *sk);
537547
extern void tcp_cwnd_application_limited(struct sock *sk);
538548
extern void tcp_resume_early_retransmit(struct sock *sk);
539549
extern void tcp_rearm_rto(struct sock *sk);
550+
extern void tcp_reset(struct sock *sk);
540551

541552
/* tcp_timer.c */
542553
extern void tcp_init_xmit_timers(struct sock *);
@@ -586,6 +597,7 @@ extern int tcp_mtu_to_mss(struct sock *sk, int pmtu);
586597
extern int tcp_mss_to_mtu(struct sock *sk, int mss);
587598
extern void tcp_mtup_init(struct sock *sk);
588599
extern void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt);
600+
extern void tcp_init_buffer_space(struct sock *sk);
589601

590602
static inline void tcp_bound_rto(const struct sock *sk)
591603
{
@@ -1104,6 +1116,7 @@ static inline void tcp_openreq_init(struct request_sock *req,
11041116
req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */
11051117
req->cookie_ts = 0;
11061118
tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq;
1119+
tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
11071120
req->mss = rx_opt->mss_clamp;
11081121
req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0;
11091122
ireq->tstamp_ok = rx_opt->tstamp_ok;
@@ -1308,15 +1321,34 @@ extern int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff
13081321
extern int tcp_md5_hash_key(struct tcp_md5sig_pool *hp,
13091322
const struct tcp_md5sig_key *key);
13101323

1324+
/* From tcp_fastopen.c */
1325+
extern void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
1326+
struct tcp_fastopen_cookie *cookie,
1327+
int *syn_loss, unsigned long *last_syn_loss);
1328+
extern void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
1329+
struct tcp_fastopen_cookie *cookie,
1330+
bool syn_lost);
13111331
struct tcp_fastopen_request {
13121332
/* Fast Open cookie. Size 0 means a cookie request */
13131333
struct tcp_fastopen_cookie cookie;
13141334
struct msghdr *data; /* data in MSG_FASTOPEN */
13151335
u16 copied; /* queued in tcp_connect() */
13161336
};
1317-
13181337
void tcp_free_fastopen_req(struct tcp_sock *tp);
13191338

1339+
extern struct tcp_fastopen_context __rcu *tcp_fastopen_ctx;
1340+
int tcp_fastopen_reset_cipher(void *key, unsigned int len);
1341+
void tcp_fastopen_cookie_gen(__be32 addr, struct tcp_fastopen_cookie *foc);
1342+
1343+
#define TCP_FASTOPEN_KEY_LENGTH 16
1344+
1345+
/* Fastopen key context */
1346+
struct tcp_fastopen_context {
1347+
struct crypto_cipher __rcu *tfm;
1348+
__u8 key[TCP_FASTOPEN_KEY_LENGTH];
1349+
struct rcu_head rcu;
1350+
};
1351+
13201352
/* write queue abstraction */
13211353
static inline void tcp_write_queue_purge(struct sock *sk)
13221354
{

net/ipv4/proc.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,10 @@ static const struct snmp_mib snmp4_net_list[] = {
263263
SNMP_MIB_ITEM("TCPChallengeACK", LINUX_MIB_TCPCHALLENGEACK),
264264
SNMP_MIB_ITEM("TCPSYNChallenge", LINUX_MIB_TCPSYNCHALLENGE),
265265
SNMP_MIB_ITEM("TCPFastOpenActive", LINUX_MIB_TCPFASTOPENACTIVE),
266+
SNMP_MIB_ITEM("TCPFastOpenPassive", LINUX_MIB_TCPFASTOPENPASSIVE),
267+
SNMP_MIB_ITEM("TCPFastOpenPassiveFail", LINUX_MIB_TCPFASTOPENPASSIVEFAIL),
268+
SNMP_MIB_ITEM("TCPFastOpenListenOverflow", LINUX_MIB_TCPFASTOPENLISTENOVERFLOW),
269+
SNMP_MIB_ITEM("TCPFastOpenCookieReqd", LINUX_MIB_TCPFASTOPENCOOKIEREQD),
266270
SNMP_MIB_SENTINEL
267271
};
268272

net/ipv4/sysctl_net_ipv4.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,45 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write,
232232
return 0;
233233
}
234234

235+
int proc_tcp_fastopen_key(ctl_table *ctl, int write, void __user *buffer,
236+
size_t *lenp, loff_t *ppos)
237+
{
238+
ctl_table tbl = { .maxlen = (TCP_FASTOPEN_KEY_LENGTH * 2 + 10) };
239+
struct tcp_fastopen_context *ctxt;
240+
int ret;
241+
u32 user_key[4]; /* 16 bytes, matching TCP_FASTOPEN_KEY_LENGTH */
242+
243+
tbl.data = kmalloc(tbl.maxlen, GFP_KERNEL);
244+
if (!tbl.data)
245+
return -ENOMEM;
246+
247+
rcu_read_lock();
248+
ctxt = rcu_dereference(tcp_fastopen_ctx);
249+
if (ctxt)
250+
memcpy(user_key, ctxt->key, TCP_FASTOPEN_KEY_LENGTH);
251+
rcu_read_unlock();
252+
253+
snprintf(tbl.data, tbl.maxlen, "%08x-%08x-%08x-%08x",
254+
user_key[0], user_key[1], user_key[2], user_key[3]);
255+
ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
256+
257+
if (write && ret == 0) {
258+
if (sscanf(tbl.data, "%x-%x-%x-%x", user_key, user_key + 1,
259+
user_key + 2, user_key + 3) != 4) {
260+
ret = -EINVAL;
261+
goto bad_key;
262+
}
263+
tcp_fastopen_reset_cipher(user_key, TCP_FASTOPEN_KEY_LENGTH);
264+
}
265+
266+
bad_key:
267+
pr_debug("proc FO key set 0x%x-%x-%x-%x <- 0x%s: %u\n",
268+
user_key[0], user_key[1], user_key[2], user_key[3],
269+
(char *)tbl.data, ret);
270+
kfree(tbl.data);
271+
return ret;
272+
}
273+
235274
static struct ctl_table ipv4_table[] = {
236275
{
237276
.procname = "tcp_timestamps",
@@ -385,6 +424,12 @@ static struct ctl_table ipv4_table[] = {
385424
.mode = 0644,
386425
.proc_handler = proc_dointvec,
387426
},
427+
{
428+
.procname = "tcp_fastopen_key",
429+
.mode = 0600,
430+
.maxlen = ((TCP_FASTOPEN_KEY_LENGTH * 2) + 10),
431+
.proc_handler = proc_tcp_fastopen_key,
432+
},
388433
{
389434
.procname = "tcp_tw_recycle",
390435
.data = &tcp_death_row.sysctl_tw_recycle,

0 commit comments

Comments
 (0)