Skip to content

Commit

Permalink
tcp: simplify fast open cookie processing
Browse files Browse the repository at this point in the history
Consolidate various cookie checking and generation code to simplify
the fast open processing. The main goal is to reduce code duplication
in tcp_v4_conn_request() for IPv6 support.

Removes two experimental sysctl flags TFO_SERVER_ALWAYS and
TFO_SERVER_COOKIE_NOT_CHKD used primarily for developmental debugging
purposes.

Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Daniel Lee <longinus00@gmail.com>
Signed-off-by: Jerry Chu <hkchu@google.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Neal Cardwell <ncardwell@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Yuchung Cheng authored and David S. Miller committed May 13, 2014
1 parent 5b7ed08 commit 89278c9
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 64 deletions.
5 changes: 0 additions & 5 deletions include/linux/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,11 +366,6 @@ static inline bool tcp_passive_fastopen(const struct sock *sk)
tcp_sk(sk)->fastopen_rsk != NULL);
}

static inline bool fastopen_cookie_present(struct tcp_fastopen_cookie *foc)
{
return foc->len != -1;
}

extern void tcp_sock_destruct(struct sock *sk);

static inline int fastopen_init_queue(struct sock *sk, int backlog)
Expand Down
9 changes: 1 addition & 8 deletions include/net/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);
#define TFO_SERVER_ENABLE 2
#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

Expand All @@ -230,10 +228,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);
*/
#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;

Expand Down Expand Up @@ -1335,8 +1329,7 @@ int tcp_fastopen_create_child(struct sock *sk,
struct request_sock *req);
bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
struct tcp_fastopen_cookie *foc,
struct tcp_fastopen_cookie *valid_foc);
struct tcp_fastopen_cookie *foc);
void tcp_fastopen_init_key_once(bool publish);
#define TCP_FASTOPEN_KEY_LENGTH 16

Expand Down
71 changes: 28 additions & 43 deletions net/ipv4/tcp_fastopen.c
Original file line number Diff line number Diff line change
Expand Up @@ -228,59 +228,44 @@ static bool tcp_fastopen_queue_check(struct sock *sk)
return true;
}

/* Returns true if we should perform Fast Open on the SYN. The cookie (foc)
* may be updated and return the client in the SYN-ACK later. E.g., Fast Open
* cookie request (foc->len == 0).
*/
bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
struct tcp_fastopen_cookie *foc,
struct tcp_fastopen_cookie *valid_foc)
struct tcp_fastopen_cookie *foc)
{
bool skip_cookie = false;

if (likely(!fastopen_cookie_present(foc))) {
/* See include/net/tcp.h for the meaning of these knobs */
if ((sysctl_tcp_fastopen & TFO_SERVER_ALWAYS) ||
((sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD) &&
(TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1)))
skip_cookie = true; /* no cookie to validate */
else
return false;
}
/* A FO option is present; bump the counter. */
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVE);
struct tcp_fastopen_cookie valid_foc = { .len = -1 };
bool syn_data = TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1;

if ((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) == 0 ||
!tcp_fastopen_queue_check(sk))
if (!((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) &&
(syn_data || foc->len >= 0) &&
tcp_fastopen_queue_check(sk))) {
foc->len = -1;
return false;

if (skip_cookie) {
tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
return true;
}

if (foc->len == TCP_FASTOPEN_COOKIE_SIZE) {
if ((sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_CHKED) == 0) {
tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr,
ip_hdr(skb)->daddr, valid_foc);
if ((valid_foc->len != TCP_FASTOPEN_COOKIE_SIZE) ||
memcmp(&foc->val[0], &valid_foc->val[0],
TCP_FASTOPEN_COOKIE_SIZE) != 0)
return false;
valid_foc->len = -1;
}
/* Acknowledge the data received from the peer. */
if (syn_data && (sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD))
goto fastopen;

tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr,
ip_hdr(skb)->daddr, &valid_foc);

if (foc->len == TCP_FASTOPEN_COOKIE_SIZE &&
foc->len == valid_foc.len &&
!memcmp(foc->val, valid_foc.val, foc->len)) {
fastopen:
tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
foc->len = -1;
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVE);
return true;
} else if (foc->len == 0) { /* Client requesting a cookie */
tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr,
ip_hdr(skb)->daddr, valid_foc);
NET_INC_STATS_BH(sock_net(sk),
LINUX_MIB_TCPFASTOPENCOOKIEREQD);
} else {
/* Client sent a cookie with wrong size. Treat it
* the same as invalid and return a valid one.
*/
tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr,
ip_hdr(skb)->daddr, valid_foc);
}

NET_INC_STATS_BH(sock_net(sk), foc->len ?
LINUX_MIB_TCPFASTOPENPASSIVEFAIL :
LINUX_MIB_TCPFASTOPENCOOKIEREQD);
*foc = valid_foc;
return false;
}
EXPORT_SYMBOL(tcp_fastopen_check);
10 changes: 3 additions & 7 deletions net/ipv4/tcp_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -1273,7 +1273,6 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
bool want_cookie = false;
struct flowi4 fl4;
struct tcp_fastopen_cookie foc = { .len = -1 };
struct tcp_fastopen_cookie valid_foc = { .len = -1 };
struct sk_buff *skb_synack;
int do_fastopen;

Expand Down Expand Up @@ -1381,7 +1380,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
if (dst == NULL)
goto drop_and_free;
}
do_fastopen = tcp_fastopen_check(sk, skb, req, &foc, &valid_foc);
do_fastopen = !want_cookie &&
tcp_fastopen_check(sk, skb, req, &foc);

/* We don't call tcp_v4_send_synack() directly because we need
* to make sure a child socket can be created successfully before
Expand All @@ -1394,8 +1394,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
* latter to remove its dependency on the current implementation
* of tcp_v4_send_synack()->tcp_select_initial_window().
*/
skb_synack = tcp_make_synack(sk, dst, req,
fastopen_cookie_present(&valid_foc) ? &valid_foc : NULL);
skb_synack = tcp_make_synack(sk, dst, req, &foc);

if (skb_synack) {
__tcp_v4_send_check(skb_synack, ireq->ir_loc_addr, ireq->ir_rmt_addr);
Expand All @@ -1415,9 +1414,6 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
tcp_rsk(req)->listener = NULL;
/* Add the request_sock to the SYN table */
inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
if (fastopen_cookie_present(&foc) && foc.len != 0)
NET_INC_STATS_BH(sock_net(sk),
LINUX_MIB_TCPFASTOPENPASSIVEFAIL);
} else if (tcp_fastopen_create_child(sk, skb, skb_synack, req))
goto drop_and_release;

Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/tcp_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ static unsigned int tcp_synack_options(struct sock *sk,
if (unlikely(!ireq->tstamp_ok))
remaining -= TCPOLEN_SACKPERM_ALIGNED;
}
if (foc != NULL) {
if (foc != NULL && foc->len >= 0) {
u32 need = TCPOLEN_EXP_FASTOPEN_BASE + foc->len;
need = (need + 3) & ~3U; /* Align to 32 bits */
if (remaining >= need) {
Expand Down

0 comments on commit 89278c9

Please sign in to comment.