Commit 10467163 authored by Jerry Chu's avatar Jerry Chu Committed by David S. Miller

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: default avatarH.K. Jerry Chu <hkchu@google.com>
Cc: Yuchung Cheng <ycheng@google.com>
Cc: Neal Cardwell <ncardwell@google.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Tom Herbert <therbert@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2a35cfa5
...@@ -467,16 +467,31 @@ tcp_syncookies - BOOLEAN ...@@ -467,16 +467,31 @@ tcp_syncookies - BOOLEAN
tcp_fastopen - INTEGER tcp_fastopen - INTEGER
Enable TCP Fast Open feature (draft-ietf-tcpm-fastopen) to send data Enable TCP Fast Open feature (draft-ietf-tcpm-fastopen) to send data
in the opening SYN packet. To use this feature, the client application in the opening SYN packet. To use this feature, the client application
must not use connect(). Instead, it should use sendmsg() or sendto() must use sendmsg() or sendto() with MSG_FASTOPEN flag rather than
with MSG_FASTOPEN flag which performs a TCP handshake automatically. connect() to perform a TCP handshake automatically.
The values (bitmap) are: The values (bitmap) are
1: Enables sending data in the opening SYN on the client 1: Enables sending data in the opening SYN on the client.
5: Enables sending data in the opening SYN on the client regardless 2: Enables TCP Fast Open on the server side, i.e., allowing data in
of cookie availability. a SYN packet to be accepted and passed to the application before
3-way hand shake finishes.
4: Send data in the opening SYN regardless of cookie availability and
without a cookie option.
0x100: Accept SYN data w/o validating the cookie.
0x200: Accept data-in-SYN w/o any cookie option present.
0x400/0x800: Enable Fast Open on all listeners regardless of the
TCP_FASTOPEN socket option. The two different flags designate two
different ways of setting max_qlen without the TCP_FASTOPEN socket
option.
Default: 0 Default: 0
Note that the client & server side Fast Open flags (1 and 2
respectively) must be also enabled before the rest of flags can take
effect.
See include/net/tcp.h and the code for more details.
tcp_syn_retries - INTEGER tcp_syn_retries - INTEGER
Number of times initial SYNs for an active TCP connection attempt Number of times initial SYNs for an active TCP connection attempt
will be retransmitted. Should not be higher than 255. Default value will be retransmitted. Should not be higher than 255. Default value
......
...@@ -241,6 +241,10 @@ enum ...@@ -241,6 +241,10 @@ enum
LINUX_MIB_TCPCHALLENGEACK, /* TCPChallengeACK */ LINUX_MIB_TCPCHALLENGEACK, /* TCPChallengeACK */
LINUX_MIB_TCPSYNCHALLENGE, /* TCPSYNChallenge */ LINUX_MIB_TCPSYNCHALLENGE, /* TCPSYNChallenge */
LINUX_MIB_TCPFASTOPENACTIVE, /* TCPFastOpenActive */ LINUX_MIB_TCPFASTOPENACTIVE, /* TCPFastOpenActive */
LINUX_MIB_TCPFASTOPENPASSIVE, /* TCPFastOpenPassive*/
LINUX_MIB_TCPFASTOPENPASSIVEFAIL, /* TCPFastOpenPassiveFail */
LINUX_MIB_TCPFASTOPENLISTENOVERFLOW, /* TCPFastOpenListenOverflow */
LINUX_MIB_TCPFASTOPENCOOKIEREQD, /* TCPFastOpenCookieReqd */
__LINUX_MIB_MAX __LINUX_MIB_MAX
}; };
......
...@@ -110,6 +110,7 @@ enum { ...@@ -110,6 +110,7 @@ enum {
#define TCP_REPAIR_QUEUE 20 #define TCP_REPAIR_QUEUE 20
#define TCP_QUEUE_SEQ 21 #define TCP_QUEUE_SEQ 21
#define TCP_REPAIR_OPTIONS 22 #define TCP_REPAIR_OPTIONS 22
#define TCP_FASTOPEN 23 /* Enable FastOpen on listeners */
struct tcp_repair_opt { struct tcp_repair_opt {
__u32 opt_code; __u32 opt_code;
...@@ -246,6 +247,7 @@ static inline unsigned int tcp_optlen(const struct sk_buff *skb) ...@@ -246,6 +247,7 @@ static inline unsigned int tcp_optlen(const struct sk_buff *skb)
/* TCP Fast Open */ /* TCP Fast Open */
#define TCP_FASTOPEN_COOKIE_MIN 4 /* Min Fast Open Cookie size in bytes */ #define TCP_FASTOPEN_COOKIE_MIN 4 /* Min Fast Open Cookie size in bytes */
#define TCP_FASTOPEN_COOKIE_MAX 16 /* Max Fast Open Cookie size in bytes */ #define TCP_FASTOPEN_COOKIE_MAX 16 /* Max Fast Open Cookie size in bytes */
#define TCP_FASTOPEN_COOKIE_SIZE 8 /* the size employed by this impl. */
/* TCP Fast Open Cookie as stored in memory */ /* TCP Fast Open Cookie as stored in memory */
struct tcp_fastopen_cookie { struct tcp_fastopen_cookie {
...@@ -312,9 +314,14 @@ struct tcp_request_sock { ...@@ -312,9 +314,14 @@ struct tcp_request_sock {
/* Only used by TCP MD5 Signature so far. */ /* Only used by TCP MD5 Signature so far. */
const struct tcp_request_sock_ops *af_specific; const struct tcp_request_sock_ops *af_specific;
#endif #endif
struct sock *listener; /* needed for TFO */
u32 rcv_isn; u32 rcv_isn;
u32 snt_isn; u32 snt_isn;
u32 snt_synack; /* synack sent time */ u32 snt_synack; /* synack sent time */
u32 rcv_nxt; /* the ack # by SYNACK. For
* FastOpen it's the seq#
* after data-in-SYN.
*/
}; };
static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req) static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req)
...@@ -505,14 +512,18 @@ struct tcp_sock { ...@@ -505,14 +512,18 @@ struct tcp_sock {
struct tcp_md5sig_info __rcu *md5sig_info; struct tcp_md5sig_info __rcu *md5sig_info;
#endif #endif
/* TCP fastopen related information */
struct tcp_fastopen_request *fastopen_req;
/* When the cookie options are generated and exchanged, then this /* When the cookie options are generated and exchanged, then this
* object holds a reference to them (cookie_values->kref). Also * object holds a reference to them (cookie_values->kref). Also
* contains related tcp_cookie_transactions fields. * contains related tcp_cookie_transactions fields.
*/ */
struct tcp_cookie_values *cookie_values; struct tcp_cookie_values *cookie_values;
/* TCP fastopen related information */
struct tcp_fastopen_request *fastopen_req;
/* fastopen_rsk points to request_sock that resulted in this big
* socket. Used to retransmit SYNACKs etc.
*/
struct request_sock *fastopen_rsk;
}; };
enum tsq_flags { enum tsq_flags {
...@@ -552,6 +563,34 @@ static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk) ...@@ -552,6 +563,34 @@ static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk)
return (struct tcp_timewait_sock *)sk; return (struct tcp_timewait_sock *)sk;
} }
static inline bool tcp_passive_fastopen(const struct sock *sk)
{
return (sk->sk_state == TCP_SYN_RECV &&
tcp_sk(sk)->fastopen_rsk != NULL);
}
static inline bool fastopen_cookie_present(struct tcp_fastopen_cookie *foc)
{
return foc->len != -1;
}
static inline int fastopen_init_queue(struct sock *sk, int backlog)
{
struct request_sock_queue *queue =
&inet_csk(sk)->icsk_accept_queue;
if (queue->fastopenq == NULL) {
queue->fastopenq = kzalloc(
sizeof(struct fastopen_queue),
sk->sk_allocation);
if (queue->fastopenq == NULL)
return -ENOMEM;
spin_lock_init(&queue->fastopenq->lock);
}
queue->fastopenq->max_qlen = backlog;
return 0;
}
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_TCP_H */ #endif /* _LINUX_TCP_H */
...@@ -106,6 +106,34 @@ struct listen_sock { ...@@ -106,6 +106,34 @@ struct listen_sock {
struct request_sock *syn_table[0]; struct request_sock *syn_table[0];
}; };
/*
* For a TCP Fast Open listener -
* lock - protects the access to all the reqsk, which is co-owned by
* the listener and the child socket.
* qlen - pending TFO requests (still in TCP_SYN_RECV).
* max_qlen - max TFO reqs allowed before TFO is disabled.
*
* XXX (TFO) - ideally these fields can be made as part of "listen_sock"
* structure above. But there is some implementation difficulty due to
* listen_sock being part of request_sock_queue hence will be freed when
* a listener is stopped. But TFO related fields may continue to be
* accessed even after a listener is closed, until its sk_refcnt drops
* to 0 implying no more outstanding TFO reqs. One solution is to keep
* listen_opt around until sk_refcnt drops to 0. But there is some other
* complexity that needs to be resolved. E.g., a listener can be disabled
* temporarily through shutdown()->tcp_disconnect(), and re-enabled later.
*/
struct fastopen_queue {
struct request_sock *rskq_rst_head; /* Keep track of past TFO */
struct request_sock *rskq_rst_tail; /* requests that caused RST.
* This is part of the defense
* against spoofing attack.
*/
spinlock_t lock;
int qlen; /* # of pending (TCP_SYN_RECV) reqs */
int max_qlen; /* != 0 iff TFO is currently enabled */
};
/** struct request_sock_queue - queue of request_socks /** struct request_sock_queue - queue of request_socks
* *
* @rskq_accept_head - FIFO head of established children * @rskq_accept_head - FIFO head of established children
...@@ -129,6 +157,12 @@ struct request_sock_queue { ...@@ -129,6 +157,12 @@ struct request_sock_queue {
u8 rskq_defer_accept; u8 rskq_defer_accept;
/* 3 bytes hole, try to pack */ /* 3 bytes hole, try to pack */
struct listen_sock *listen_opt; struct listen_sock *listen_opt;
struct fastopen_queue *fastopenq; /* This is non-NULL iff TFO has been
* enabled on this listener. Check
* max_qlen != 0 in fastopen_queue
* to determine if TFO is enabled
* right at this moment.
*/
}; };
extern int reqsk_queue_alloc(struct request_sock_queue *queue, extern int reqsk_queue_alloc(struct request_sock_queue *queue,
...@@ -136,6 +170,8 @@ extern int reqsk_queue_alloc(struct request_sock_queue *queue, ...@@ -136,6 +170,8 @@ extern int reqsk_queue_alloc(struct request_sock_queue *queue,
extern void __reqsk_queue_destroy(struct request_sock_queue *queue); extern void __reqsk_queue_destroy(struct request_sock_queue *queue);
extern void reqsk_queue_destroy(struct request_sock_queue *queue); extern void reqsk_queue_destroy(struct request_sock_queue *queue);
extern void reqsk_fastopen_remove(struct sock *sk,
struct request_sock *req, bool reset);
static inline struct request_sock * static inline struct request_sock *
reqsk_queue_yank_acceptq(struct request_sock_queue *queue) reqsk_queue_yank_acceptq(struct request_sock_queue *queue)
......
...@@ -224,8 +224,24 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); ...@@ -224,8 +224,24 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo);
/* Bit Flags for sysctl_tcp_fastopen */ /* Bit Flags for sysctl_tcp_fastopen */
#define TFO_CLIENT_ENABLE 1 #define TFO_CLIENT_ENABLE 1
#define TFO_SERVER_ENABLE 2
#define TFO_CLIENT_NO_COOKIE 4 /* Data in SYN w/o cookie option */ #define TFO_CLIENT_NO_COOKIE 4 /* Data in SYN w/o cookie option */
/* Process SYN data but skip cookie validation */
#define TFO_SERVER_COOKIE_NOT_CHKED 0x100
/* Accept SYN data w/o any cookie option */
#define TFO_SERVER_COOKIE_NOT_REQD 0x200
/* Force enable TFO on all listeners, i.e., not requiring the
* TCP_FASTOPEN socket option. SOCKOPT1/2 determine how to set max_qlen.
*/
#define TFO_SERVER_WO_SOCKOPT1 0x400
#define TFO_SERVER_WO_SOCKOPT2 0x800
/* Always create TFO child sockets on a TFO listener even when
* cookie/data not present. (For testing purpose!)
*/
#define TFO_SERVER_ALWAYS 0x1000
extern struct inet_timewait_death_row tcp_death_row; extern struct inet_timewait_death_row tcp_death_row;
/* sysctl variables for tcp */ /* sysctl variables for tcp */
...@@ -421,12 +437,6 @@ extern void tcp_metrics_init(void); ...@@ -421,12 +437,6 @@ extern void tcp_metrics_init(void);
extern bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst, bool paws_check); extern bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst, bool paws_check);
extern bool tcp_remember_stamp(struct sock *sk); extern bool tcp_remember_stamp(struct sock *sk);
extern bool tcp_tw_remember_stamp(struct inet_timewait_sock *tw); extern bool tcp_tw_remember_stamp(struct inet_timewait_sock *tw);
extern void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
struct tcp_fastopen_cookie *cookie,
int *syn_loss, unsigned long *last_syn_loss);
extern void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
struct tcp_fastopen_cookie *cookie,
bool syn_lost);
extern void tcp_fetch_timewait_stamp(struct sock *sk, struct dst_entry *dst); extern void tcp_fetch_timewait_stamp(struct sock *sk, struct dst_entry *dst);
extern void tcp_disable_fack(struct tcp_sock *tp); extern void tcp_disable_fack(struct tcp_sock *tp);
extern void tcp_close(struct sock *sk, long timeout); extern void tcp_close(struct sock *sk, long timeout);
...@@ -537,6 +547,7 @@ extern void tcp_send_delayed_ack(struct sock *sk); ...@@ -537,6 +547,7 @@ extern void tcp_send_delayed_ack(struct sock *sk);
extern void tcp_cwnd_application_limited(struct sock *sk); extern void tcp_cwnd_application_limited(struct sock *sk);
extern void tcp_resume_early_retransmit(struct sock *sk); extern void tcp_resume_early_retransmit(struct sock *sk);
extern void tcp_rearm_rto(struct sock *sk); extern void tcp_rearm_rto(struct sock *sk);
extern void tcp_reset(struct sock *sk);
/* tcp_timer.c */ /* tcp_timer.c */
extern void tcp_init_xmit_timers(struct sock *); extern void tcp_init_xmit_timers(struct sock *);
...@@ -586,6 +597,7 @@ extern int tcp_mtu_to_mss(struct sock *sk, int pmtu); ...@@ -586,6 +597,7 @@ extern int tcp_mtu_to_mss(struct sock *sk, int pmtu);
extern int tcp_mss_to_mtu(struct sock *sk, int mss); extern int tcp_mss_to_mtu(struct sock *sk, int mss);
extern void tcp_mtup_init(struct sock *sk); extern void tcp_mtup_init(struct sock *sk);
extern void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt); extern void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt);
extern void tcp_init_buffer_space(struct sock *sk);
static inline void tcp_bound_rto(const struct sock *sk) static inline void tcp_bound_rto(const struct sock *sk)
{ {
...@@ -1104,6 +1116,7 @@ static inline void tcp_openreq_init(struct request_sock *req, ...@@ -1104,6 +1116,7 @@ static inline void tcp_openreq_init(struct request_sock *req,
req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */ req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */
req->cookie_ts = 0; req->cookie_ts = 0;
tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq; tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq;
tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
req->mss = rx_opt->mss_clamp; req->mss = rx_opt->mss_clamp;
req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0; req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0;
ireq->tstamp_ok = rx_opt->tstamp_ok; 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 ...@@ -1308,15 +1321,34 @@ extern int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff
extern int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, extern int tcp_md5_hash_key(struct tcp_md5sig_pool *hp,
const struct tcp_md5sig_key *key); const struct tcp_md5sig_key *key);
/* From tcp_fastopen.c */
extern void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
struct tcp_fastopen_cookie *cookie,
int *syn_loss, unsigned long *last_syn_loss);
extern void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
struct tcp_fastopen_cookie *cookie,
bool syn_lost);
struct tcp_fastopen_request { struct tcp_fastopen_request {
/* Fast Open cookie. Size 0 means a cookie request */ /* Fast Open cookie. Size 0 means a cookie request */
struct tcp_fastopen_cookie cookie; struct tcp_fastopen_cookie cookie;
struct msghdr *data; /* data in MSG_FASTOPEN */ struct msghdr *data; /* data in MSG_FASTOPEN */
u16 copied; /* queued in tcp_connect() */ u16 copied; /* queued in tcp_connect() */
}; };
void tcp_free_fastopen_req(struct tcp_sock *tp); void tcp_free_fastopen_req(struct tcp_sock *tp);
extern struct tcp_fastopen_context __rcu *tcp_fastopen_ctx;
int tcp_fastopen_reset_cipher(void *key, unsigned int len);
void tcp_fastopen_cookie_gen(__be32 addr, struct tcp_fastopen_cookie *foc);
#define TCP_FASTOPEN_KEY_LENGTH 16
/* Fastopen key context */
struct tcp_fastopen_context {
struct crypto_cipher __rcu *tfm;
__u8 key[TCP_FASTOPEN_KEY_LENGTH];
struct rcu_head rcu;
};
/* write queue abstraction */ /* write queue abstraction */
static inline void tcp_write_queue_purge(struct sock *sk) static inline void tcp_write_queue_purge(struct sock *sk)
{ {
......
...@@ -263,6 +263,10 @@ static const struct snmp_mib snmp4_net_list[] = { ...@@ -263,6 +263,10 @@ static const struct snmp_mib snmp4_net_list[] = {
SNMP_MIB_ITEM("TCPChallengeACK", LINUX_MIB_TCPCHALLENGEACK), SNMP_MIB_ITEM("TCPChallengeACK", LINUX_MIB_TCPCHALLENGEACK),
SNMP_MIB_ITEM("TCPSYNChallenge", LINUX_MIB_TCPSYNCHALLENGE), SNMP_MIB_ITEM("TCPSYNChallenge", LINUX_MIB_TCPSYNCHALLENGE),
SNMP_MIB_ITEM("TCPFastOpenActive", LINUX_MIB_TCPFASTOPENACTIVE), SNMP_MIB_ITEM("TCPFastOpenActive", LINUX_MIB_TCPFASTOPENACTIVE),
SNMP_MIB_ITEM("TCPFastOpenPassive", LINUX_MIB_TCPFASTOPENPASSIVE),
SNMP_MIB_ITEM("TCPFastOpenPassiveFail", LINUX_MIB_TCPFASTOPENPASSIVEFAIL),
SNMP_MIB_ITEM("TCPFastOpenListenOverflow", LINUX_MIB_TCPFASTOPENLISTENOVERFLOW),
SNMP_MIB_ITEM("TCPFastOpenCookieReqd", LINUX_MIB_TCPFASTOPENCOOKIEREQD),
SNMP_MIB_SENTINEL SNMP_MIB_SENTINEL
}; };
......
...@@ -232,6 +232,45 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write, ...@@ -232,6 +232,45 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write,
return 0; return 0;
} }
int proc_tcp_fastopen_key(ctl_table *ctl, int write, void __user *buffer,
size_t *lenp, loff_t *ppos)
{
ctl_table tbl = { .maxlen = (TCP_FASTOPEN_KEY_LENGTH * 2 + 10) };
struct tcp_fastopen_context *ctxt;
int ret;
u32 user_key[4]; /* 16 bytes, matching TCP_FASTOPEN_KEY_LENGTH */
tbl.data = kmalloc(tbl.maxlen, GFP_KERNEL);
if (!tbl.data)
return -ENOMEM;
rcu_read_lock();
ctxt = rcu_dereference(tcp_fastopen_ctx);
if (ctxt)
memcpy(user_key, ctxt->key, TCP_FASTOPEN_KEY_LENGTH);
rcu_read_unlock();
snprintf(tbl.data, tbl.maxlen, "%08x-%08x-%08x-%08x",
user_key[0], user_key[1], user_key[2], user_key[3]);
ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
if (write && ret == 0) {
if (sscanf(tbl.data, "%x-%x-%x-%x", user_key, user_key + 1,
user_key + 2, user_key + 3) != 4) {
ret = -EINVAL;
goto bad_key;
}
tcp_fastopen_reset_cipher(user_key, TCP_FASTOPEN_KEY_LENGTH);
}
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);
return ret;
}
static struct ctl_table ipv4_table[] = { static struct ctl_table ipv4_table[] = {
{ {
.procname = "tcp_timestamps", .procname = "tcp_timestamps",
...@@ -385,6 +424,12 @@ static struct ctl_table ipv4_table[] = { ...@@ -385,6 +424,12 @@ static struct ctl_table ipv4_table[] = {
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec, .proc_handler = proc_dointvec,
}, },
{
.procname = "tcp_fastopen_key",
.mode = 0600,
.maxlen = ((TCP_FASTOPEN_KEY_LENGTH * 2) + 10),
.proc_handler = proc_tcp_fastopen_key,
},
{ {
.procname = "tcp_tw_recycle", .procname = "tcp_tw_recycle",
.data = &tcp_death_row.sysctl_tw_recycle, .data = &tcp_death_row.sysctl_tw_recycle,
......
#include <linux/err.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/list.h>
#include <linux/tcp.h>
#include <linux/rcupdate.h>
#include <linux/rculist.h>
#include <net/inetpeer.h>
#include <net/tcp.h>
int sysctl_tcp_fastopen; int sysctl_tcp_fastopen __read_mostly;
struct tcp_fastopen_context __rcu *tcp_fastopen_ctx;
static DEFINE_SPINLOCK(tcp_fastopen_ctx_lock);
static void tcp_fastopen_ctx_free(struct rcu_head *head)
{
struct tcp_fastopen_context *ctx =
container_of(head, struct tcp_fastopen_context, rcu);
crypto_free_cipher(ctx->tfm);
kfree(ctx);
}
int tcp_fastopen_reset_cipher(void *key, unsigned int len)
{
int err;
struct tcp_fastopen_context *ctx, *octx;
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->tfm = crypto_alloc_cipher("aes", 0, 0);
if (IS_ERR(ctx->tfm)) {
err = PTR_ERR(ctx->tfm);
error: kfree(ctx);
pr_err("TCP: TFO aes cipher alloc error: %d\n", err);
return err;
}
err = crypto_cipher_setkey(ctx->tfm, key, len);
if (err) {
pr_err("TCP: TFO cipher key error: %d\n", err);
crypto_free_cipher(ctx->tfm);
goto error;
}
memcpy(ctx->key, key, len);
spin_lock(&tcp_fastopen_ctx_lock);
octx = rcu_dereference_protected(tcp_fastopen_ctx,
lockdep_is_held(&tcp_fastopen_ctx_lock));
rcu_assign_pointer(tcp_fastopen_ctx, ctx);
spin_unlock(&tcp_fastopen_ctx_lock);
if (octx)
call_rcu(&octx->rcu, tcp_fastopen_ctx_free);
return err;
}
/* Computes the fastopen cookie for the peer.
* The peer address is a 128 bits long (pad with zeros for IPv4).
*
* The caller must check foc->len to determine if a valid cookie
* has been generated successfully.
*/
void tcp_fastopen_cookie_gen(__be32 addr, struct tcp_fastopen_cookie *foc)
{
__be32 peer_addr[4] = { addr, 0, 0, 0 };
struct tcp_fastopen_context *ctx;
rcu_read_lock();
ctx = rcu_dereference(tcp_fastopen_ctx);
if (ctx) {
crypto_cipher_encrypt_one(ctx->tfm,
foc->val,
(__u8 *)peer_addr);
foc->len = TCP_FASTOPEN_COOKIE_SIZE;
}
rcu_read_unlock();
}
static int __init tcp_fastopen_init(void) static int __init tcp_fastopen_init(void)
{ {
__u8 key[TCP_FASTOPEN_KEY_LENGTH];
get_random_bytes(key, sizeof(key));
tcp_fastopen_reset_cipher(key, sizeof(key));
return 0; return 0;
} }
......
...@@ -378,7 +378,7 @@ static void tcp_fixup_rcvbuf(struct sock *sk) ...@@ -378,7 +378,7 @@ static void tcp_fixup_rcvbuf(struct sock *sk)
/* 4. Try to fixup all. It is made immediately after connection enters /* 4. Try to fixup all. It is made immediately after connection enters
* established state. * established state.
*/ */
static void tcp_init_buffer_space(struct sock *sk) void tcp_init_buffer_space(struct sock *sk)
{ {
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
int maxwin; int maxwin;
...@@ -4038,7 +4038,7 @@ static inline bool tcp_sequence(const struct tcp_sock *tp, u32 seq, u32 end_seq) ...@@ -4038,7 +4038,7 @@ static inline bool tcp_sequence(const struct tcp_sock *tp, u32 seq, u32 end_seq)
} }
/* When we get a reset we do this. */ /* When we get a reset we do this. */
static void tcp_reset(struct sock *sk) void tcp_reset(struct sock *sk)
{ {
/* We want the right error as BSD sees it (and indeed as we do). */ /* We want the right error as BSD sees it (and indeed as we do). */
switch (sk->sk_state) { switch (sk->sk_state) {
......
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