Skip to content

Commit 783237e

Browse files
yuchungchengdavem330
authored andcommitted
net-tcp: Fast Open client - sending SYN-data
This patch implements sending SYN-data in tcp_connect(). The data is from tcp_sendmsg() with flag MSG_FASTOPEN (implemented in a later patch). The length of the cookie in tcp_fastopen_req, init'd to 0, controls the type of the SYN. If the cookie is not cached (len==0), the host sends data-less SYN with Fast Open cookie request option to solicit a cookie from the remote. If cookie is not available (len > 0), the host sends a SYN-data with Fast Open cookie option. If cookie length is negative, the SYN will not include any Fast Open option (for fall back operations). To deal with middleboxes that may drop SYN with data or experimental TCP option, the SYN-data is only sent once. SYN retransmits do not include data or Fast Open options. The connection will fall back to regular TCP handshake. Signed-off-by: Yuchung Cheng <ycheng@google.com> Acked-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 1fe4c48 commit 783237e

File tree

6 files changed

+130
-12
lines changed

6 files changed

+130
-12
lines changed

include/linux/snmp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ enum
238238
LINUX_MIB_TCPOFOMERGE, /* TCPOFOMerge */
239239
LINUX_MIB_TCPCHALLENGEACK, /* TCPChallengeACK */
240240
LINUX_MIB_TCPSYNCHALLENGE, /* TCPSYNChallenge */
241+
LINUX_MIB_TCPFASTOPENACTIVE, /* TCPFastOpenActive */
241242
__LINUX_MIB_MAX
242243
};
243244

include/linux/tcp.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,8 @@ struct tcp_sock {
386386
unused : 1;
387387
u8 repair_queue;
388388
u8 do_early_retrans:1,/* Enable RFC5827 early-retransmit */
389-
early_retrans_delayed:1; /* Delayed ER timer installed */
389+
early_retrans_delayed:1, /* Delayed ER timer installed */
390+
syn_fastopen:1; /* SYN includes Fast Open option */
390391

391392
/* RTT measurement */
392393
u32 srtt; /* smoothed round trip time << 3 */
@@ -500,6 +501,9 @@ struct tcp_sock {
500501
struct tcp_md5sig_info __rcu *md5sig_info;
501502
#endif
502503

504+
/* TCP fastopen related information */
505+
struct tcp_fastopen_request *fastopen_req;
506+
503507
/* When the cookie options are generated and exchanged, then this
504508
* object holds a reference to them (cookie_values->kref). Also
505509
* contains related tcp_cookie_transactions fields.

include/net/tcp.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,6 +1289,15 @@ extern int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff
12891289
extern int tcp_md5_hash_key(struct tcp_md5sig_pool *hp,
12901290
const struct tcp_md5sig_key *key);
12911291

1292+
struct tcp_fastopen_request {
1293+
/* Fast Open cookie. Size 0 means a cookie request */
1294+
struct tcp_fastopen_cookie cookie;
1295+
struct msghdr *data; /* data in MSG_FASTOPEN */
1296+
u16 copied; /* queued in tcp_connect() */
1297+
};
1298+
1299+
void tcp_free_fastopen_req(struct tcp_sock *tp);
1300+
12921301
/* write queue abstraction */
12931302
static inline void tcp_write_queue_purge(struct sock *sk)
12941303
{

net/ipv4/af_inet.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -556,11 +556,12 @@ int inet_dgram_connect(struct socket *sock, struct sockaddr *uaddr,
556556
}
557557
EXPORT_SYMBOL(inet_dgram_connect);
558558

559-
static long inet_wait_for_connect(struct sock *sk, long timeo)
559+
static long inet_wait_for_connect(struct sock *sk, long timeo, int writebias)
560560
{
561561
DEFINE_WAIT(wait);
562562

563563
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
564+
sk->sk_write_pending += writebias;
564565

565566
/* Basic assumption: if someone sets sk->sk_err, he _must_
566567
* change state of the socket from TCP_SYN_*.
@@ -576,6 +577,7 @@ static long inet_wait_for_connect(struct sock *sk, long timeo)
576577
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
577578
}
578579
finish_wait(sk_sleep(sk), &wait);
580+
sk->sk_write_pending -= writebias;
579581
return timeo;
580582
}
581583

@@ -634,8 +636,12 @@ int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
634636
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
635637

636638
if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
639+
int writebias = (sk->sk_protocol == IPPROTO_TCP) &&
640+
tcp_sk(sk)->fastopen_req &&
641+
tcp_sk(sk)->fastopen_req->data ? 1 : 0;
642+
637643
/* Error code is set above */
638-
if (!timeo || !inet_wait_for_connect(sk, timeo))
644+
if (!timeo || !inet_wait_for_connect(sk, timeo, writebias))
639645
goto out;
640646

641647
err = sock_intr_errno(timeo);

net/ipv4/proc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ static const struct snmp_mib snmp4_net_list[] = {
262262
SNMP_MIB_ITEM("TCPOFOMerge", LINUX_MIB_TCPOFOMERGE),
263263
SNMP_MIB_ITEM("TCPChallengeACK", LINUX_MIB_TCPCHALLENGEACK),
264264
SNMP_MIB_ITEM("TCPSYNChallenge", LINUX_MIB_TCPSYNCHALLENGE),
265+
SNMP_MIB_ITEM("TCPFastOpenActive", LINUX_MIB_TCPFASTOPENACTIVE),
265266
SNMP_MIB_SENTINEL
266267
};
267268

net/ipv4/tcp_output.c

Lines changed: 106 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,7 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
596596
u8 cookie_size = (!tp->rx_opt.cookie_out_never && cvp != NULL) ?
597597
tcp_cookie_size_check(cvp->cookie_desired) :
598598
0;
599+
struct tcp_fastopen_request *fastopen = tp->fastopen_req;
599600

600601
#ifdef CONFIG_TCP_MD5SIG
601602
*md5 = tp->af_specific->md5_lookup(sk, sk);
@@ -636,6 +637,16 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
636637
remaining -= TCPOLEN_SACKPERM_ALIGNED;
637638
}
638639

640+
if (fastopen && fastopen->cookie.len >= 0) {
641+
u32 need = TCPOLEN_EXP_FASTOPEN_BASE + fastopen->cookie.len;
642+
need = (need + 3) & ~3U; /* Align to 32 bits */
643+
if (remaining >= need) {
644+
opts->options |= OPTION_FAST_OPEN_COOKIE;
645+
opts->fastopen_cookie = &fastopen->cookie;
646+
remaining -= need;
647+
tp->syn_fastopen = 1;
648+
}
649+
}
639650
/* Note that timestamps are required by the specification.
640651
*
641652
* Odd numbers of bytes are prohibited by the specification, ensuring
@@ -2824,6 +2835,96 @@ void tcp_connect_init(struct sock *sk)
28242835
tcp_clear_retrans(tp);
28252836
}
28262837

2838+
static void tcp_connect_queue_skb(struct sock *sk, struct sk_buff *skb)
2839+
{
2840+
struct tcp_sock *tp = tcp_sk(sk);
2841+
struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
2842+
2843+
tcb->end_seq += skb->len;
2844+
skb_header_release(skb);
2845+
__tcp_add_write_queue_tail(sk, skb);
2846+
sk->sk_wmem_queued += skb->truesize;
2847+
sk_mem_charge(sk, skb->truesize);
2848+
tp->write_seq = tcb->end_seq;
2849+
tp->packets_out += tcp_skb_pcount(skb);
2850+
}
2851+
2852+
/* Build and send a SYN with data and (cached) Fast Open cookie. However,
2853+
* queue a data-only packet after the regular SYN, such that regular SYNs
2854+
* are retransmitted on timeouts. Also if the remote SYN-ACK acknowledges
2855+
* only the SYN sequence, the data are retransmitted in the first ACK.
2856+
* If cookie is not cached or other error occurs, falls back to send a
2857+
* regular SYN with Fast Open cookie request option.
2858+
*/
2859+
static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn)
2860+
{
2861+
struct tcp_sock *tp = tcp_sk(sk);
2862+
struct tcp_fastopen_request *fo = tp->fastopen_req;
2863+
int space, i, err = 0, iovlen = fo->data->msg_iovlen;
2864+
struct sk_buff *syn_data = NULL, *data;
2865+
2866+
tcp_fastopen_cache_get(sk, &tp->rx_opt.mss_clamp, &fo->cookie);
2867+
if (fo->cookie.len <= 0)
2868+
goto fallback;
2869+
2870+
/* MSS for SYN-data is based on cached MSS and bounded by PMTU and
2871+
* user-MSS. Reserve maximum option space for middleboxes that add
2872+
* private TCP options. The cost is reduced data space in SYN :(
2873+
*/
2874+
if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < tp->rx_opt.mss_clamp)
2875+
tp->rx_opt.mss_clamp = tp->rx_opt.user_mss;
2876+
space = tcp_mtu_to_mss(sk, inet_csk(sk)->icsk_pmtu_cookie) -
2877+
MAX_TCP_OPTION_SPACE;
2878+
2879+
syn_data = skb_copy_expand(syn, skb_headroom(syn), space,
2880+
sk->sk_allocation);
2881+
if (syn_data == NULL)
2882+
goto fallback;
2883+
2884+
for (i = 0; i < iovlen && syn_data->len < space; ++i) {
2885+
struct iovec *iov = &fo->data->msg_iov[i];
2886+
unsigned char __user *from = iov->iov_base;
2887+
int len = iov->iov_len;
2888+
2889+
if (syn_data->len + len > space)
2890+
len = space - syn_data->len;
2891+
else if (i + 1 == iovlen)
2892+
/* No more data pending in inet_wait_for_connect() */
2893+
fo->data = NULL;
2894+
2895+
if (skb_add_data(syn_data, from, len))
2896+
goto fallback;
2897+
}
2898+
2899+
/* Queue a data-only packet after the regular SYN for retransmission */
2900+
data = pskb_copy(syn_data, sk->sk_allocation);
2901+
if (data == NULL)
2902+
goto fallback;
2903+
TCP_SKB_CB(data)->seq++;
2904+
TCP_SKB_CB(data)->tcp_flags &= ~TCPHDR_SYN;
2905+
TCP_SKB_CB(data)->tcp_flags = (TCPHDR_ACK|TCPHDR_PSH);
2906+
tcp_connect_queue_skb(sk, data);
2907+
fo->copied = data->len;
2908+
2909+
if (tcp_transmit_skb(sk, syn_data, 0, sk->sk_allocation) == 0) {
2910+
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPFASTOPENACTIVE);
2911+
goto done;
2912+
}
2913+
syn_data = NULL;
2914+
2915+
fallback:
2916+
/* Send a regular SYN with Fast Open cookie request option */
2917+
if (fo->cookie.len > 0)
2918+
fo->cookie.len = 0;
2919+
err = tcp_transmit_skb(sk, syn, 1, sk->sk_allocation);
2920+
if (err)
2921+
tp->syn_fastopen = 0;
2922+
kfree_skb(syn_data);
2923+
done:
2924+
fo->cookie.len = -1; /* Exclude Fast Open option for SYN retries */
2925+
return err;
2926+
}
2927+
28272928
/* Build a SYN and send it off. */
28282929
int tcp_connect(struct sock *sk)
28292930
{
@@ -2841,17 +2942,13 @@ int tcp_connect(struct sock *sk)
28412942
skb_reserve(buff, MAX_TCP_HEADER);
28422943

28432944
tcp_init_nondata_skb(buff, tp->write_seq++, TCPHDR_SYN);
2945+
tp->retrans_stamp = TCP_SKB_CB(buff)->when = tcp_time_stamp;
2946+
tcp_connect_queue_skb(sk, buff);
28442947
TCP_ECN_send_syn(sk, buff);
28452948

2846-
/* Send it off. */
2847-
TCP_SKB_CB(buff)->when = tcp_time_stamp;
2848-
tp->retrans_stamp = TCP_SKB_CB(buff)->when;
2849-
skb_header_release(buff);
2850-
__tcp_add_write_queue_tail(sk, buff);
2851-
sk->sk_wmem_queued += buff->truesize;
2852-
sk_mem_charge(sk, buff->truesize);
2853-
tp->packets_out += tcp_skb_pcount(buff);
2854-
err = tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);
2949+
/* Send off SYN; include data in Fast Open. */
2950+
err = tp->fastopen_req ? tcp_send_syn_data(sk, buff) :
2951+
tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);
28552952
if (err == -ECONNREFUSED)
28562953
return err;
28572954

0 commit comments

Comments
 (0)