Skip to content

Commit cf60af0

Browse files
yuchungchengdavem330
authored andcommitted
net-tcp: Fast Open client - sendmsg(MSG_FASTOPEN)
sendmsg() (or sendto()) with MSG_FASTOPEN is a combo of connect(2) and write(2). The application should replace connect() with it to send data in the opening SYN packet. For blocking socket, sendmsg() blocks until all the data are buffered locally and the handshake is completed like connect() call. It returns similar errno like connect() if the TCP handshake fails. For non-blocking socket, it returns the number of bytes queued (and transmitted in the SYN-data packet) if cookie is available. If cookie is not available, it transmits a data-less SYN packet with Fast Open cookie request option and returns -EINPROGRESS like connect(). Using MSG_FASTOPEN on connecting or connected socket will result in simlar errno like repeating connect() calls. Therefore the application should only use this flag on new sockets. The buffer size of sendmsg() is independent of the MSS of the connection. Signed-off-by: Yuchung Cheng <[email protected]> Acked-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 8e4178c commit cf60af0

File tree

7 files changed

+92
-12
lines changed

7 files changed

+92
-12
lines changed

Documentation/networking/ip-sysctl.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,17 @@ tcp_syncookies - BOOLEAN
468468
SYN flood warnings in logs not being really flooded, your server
469469
is seriously misconfigured.
470470

471+
tcp_fastopen - INTEGER
472+
Enable TCP Fast Open feature (draft-ietf-tcpm-fastopen) to send data
473+
in the opening SYN packet. To use this feature, the client application
474+
must not use connect(). Instead, it should use sendmsg() or sendto()
475+
with MSG_FASTOPEN flag which performs a TCP handshake automatically.
476+
477+
The values (bitmap) are:
478+
1: Enables sending data in the opening SYN on the client
479+
480+
Default: 0
481+
471482
tcp_syn_retries - INTEGER
472483
Number of times initial SYNs for an active TCP connection attempt
473484
will be retransmitted. Should not be higher than 255. Default value

include/linux/socket.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ struct ucred {
268268
#define MSG_SENDPAGE_NOTLAST 0x20000 /* sendpage() internal : not the last page */
269269
#define MSG_EOF MSG_FIN
270270

271+
#define MSG_FASTOPEN 0x20000000 /* Send data in TCP SYN */
271272
#define MSG_CMSG_CLOEXEC 0x40000000 /* Set close_on_exit for file
272273
descriptor received through
273274
SCM_RIGHTS */

include/net/inet_common.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ struct sockaddr;
1414
struct socket;
1515

1616
extern int inet_release(struct socket *sock);
17-
extern int inet_stream_connect(struct socket *sock, struct sockaddr * uaddr,
17+
extern int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
1818
int addr_len, int flags);
19-
extern int inet_dgram_connect(struct socket *sock, struct sockaddr * uaddr,
19+
extern int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
20+
int addr_len, int flags);
21+
extern int inet_dgram_connect(struct socket *sock, struct sockaddr *uaddr,
2022
int addr_len, int flags);
2123
extern int inet_accept(struct socket *sock, struct socket *newsock, int flags);
2224
extern int inet_sendmsg(struct kiocb *iocb, struct socket *sock,

include/net/tcp.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo);
212212
/* TCP initial congestion window as per draft-hkchu-tcpm-initcwnd-01 */
213213
#define TCP_INIT_CWND 10
214214

215+
/* Bit Flags for sysctl_tcp_fastopen */
216+
#define TFO_CLIENT_ENABLE 1
217+
215218
extern struct inet_timewait_death_row tcp_death_row;
216219

217220
/* sysctl variables for tcp */

net/ipv4/af_inet.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -585,8 +585,8 @@ static long inet_wait_for_connect(struct sock *sk, long timeo, int writebias)
585585
* Connect to a remote host. There is regrettably still a little
586586
* TCP 'magic' in here.
587587
*/
588-
int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
589-
int addr_len, int flags)
588+
int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
589+
int addr_len, int flags)
590590
{
591591
struct sock *sk = sock->sk;
592592
int err;
@@ -595,8 +595,6 @@ int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
595595
if (addr_len < sizeof(uaddr->sa_family))
596596
return -EINVAL;
597597

598-
lock_sock(sk);
599-
600598
if (uaddr->sa_family == AF_UNSPEC) {
601599
err = sk->sk_prot->disconnect(sk, flags);
602600
sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;
@@ -663,7 +661,6 @@ int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
663661
sock->state = SS_CONNECTED;
664662
err = 0;
665663
out:
666-
release_sock(sk);
667664
return err;
668665

669666
sock_error:
@@ -673,6 +670,18 @@ int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
673670
sock->state = SS_DISCONNECTING;
674671
goto out;
675672
}
673+
EXPORT_SYMBOL(__inet_stream_connect);
674+
675+
int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
676+
int addr_len, int flags)
677+
{
678+
int err;
679+
680+
lock_sock(sock->sk);
681+
err = __inet_stream_connect(sock, uaddr, addr_len, flags);
682+
release_sock(sock->sk);
683+
return err;
684+
}
676685
EXPORT_SYMBOL(inet_stream_connect);
677686

678687
/*

net/ipv4/tcp.c

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@
270270
#include <linux/slab.h>
271271

272272
#include <net/icmp.h>
273+
#include <net/inet_common.h>
273274
#include <net/tcp.h>
274275
#include <net/xfrm.h>
275276
#include <net/ip.h>
@@ -982,26 +983,67 @@ static inline int select_size(const struct sock *sk, bool sg)
982983
return tmp;
983984
}
984985

986+
void tcp_free_fastopen_req(struct tcp_sock *tp)
987+
{
988+
if (tp->fastopen_req != NULL) {
989+
kfree(tp->fastopen_req);
990+
tp->fastopen_req = NULL;
991+
}
992+
}
993+
994+
static int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg, int *size)
995+
{
996+
struct tcp_sock *tp = tcp_sk(sk);
997+
int err, flags;
998+
999+
if (!(sysctl_tcp_fastopen & TFO_CLIENT_ENABLE))
1000+
return -EOPNOTSUPP;
1001+
if (tp->fastopen_req != NULL)
1002+
return -EALREADY; /* Another Fast Open is in progress */
1003+
1004+
tp->fastopen_req = kzalloc(sizeof(struct tcp_fastopen_request),
1005+
sk->sk_allocation);
1006+
if (unlikely(tp->fastopen_req == NULL))
1007+
return -ENOBUFS;
1008+
tp->fastopen_req->data = msg;
1009+
1010+
flags = (msg->msg_flags & MSG_DONTWAIT) ? O_NONBLOCK : 0;
1011+
err = __inet_stream_connect(sk->sk_socket, msg->msg_name,
1012+
msg->msg_namelen, flags);
1013+
*size = tp->fastopen_req->copied;
1014+
tcp_free_fastopen_req(tp);
1015+
return err;
1016+
}
1017+
9851018
int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
9861019
size_t size)
9871020
{
9881021
struct iovec *iov;
9891022
struct tcp_sock *tp = tcp_sk(sk);
9901023
struct sk_buff *skb;
991-
int iovlen, flags, err, copied;
992-
int mss_now = 0, size_goal;
1024+
int iovlen, flags, err, copied = 0;
1025+
int mss_now = 0, size_goal, copied_syn = 0, offset = 0;
9931026
bool sg;
9941027
long timeo;
9951028

9961029
lock_sock(sk);
9971030

9981031
flags = msg->msg_flags;
1032+
if (flags & MSG_FASTOPEN) {
1033+
err = tcp_sendmsg_fastopen(sk, msg, &copied_syn);
1034+
if (err == -EINPROGRESS && copied_syn > 0)
1035+
goto out;
1036+
else if (err)
1037+
goto out_err;
1038+
offset = copied_syn;
1039+
}
1040+
9991041
timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
10001042

10011043
/* Wait for a connection to finish. */
10021044
if ((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
10031045
if ((err = sk_stream_wait_connect(sk, &timeo)) != 0)
1004-
goto out_err;
1046+
goto do_error;
10051047

10061048
if (unlikely(tp->repair)) {
10071049
if (tp->repair_queue == TCP_RECV_QUEUE) {
@@ -1037,6 +1079,15 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
10371079
unsigned char __user *from = iov->iov_base;
10381080

10391081
iov++;
1082+
if (unlikely(offset > 0)) { /* Skip bytes copied in SYN */
1083+
if (offset >= seglen) {
1084+
offset -= seglen;
1085+
continue;
1086+
}
1087+
seglen -= offset;
1088+
from += offset;
1089+
offset = 0;
1090+
}
10401091

10411092
while (seglen > 0) {
10421093
int copy = 0;
@@ -1199,7 +1250,7 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
11991250
if (copied && likely(!tp->repair))
12001251
tcp_push(sk, flags, mss_now, tp->nonagle);
12011252
release_sock(sk);
1202-
return copied;
1253+
return copied + copied_syn;
12031254

12041255
do_fault:
12051256
if (!skb->len) {
@@ -1212,7 +1263,7 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
12121263
}
12131264

12141265
do_error:
1215-
if (copied)
1266+
if (copied + copied_syn)
12161267
goto out;
12171268
out_err:
12181269
err = sk_stream_error(sk, flags, err);

net/ipv4/tcp_ipv4.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1952,6 +1952,9 @@ void tcp_v4_destroy_sock(struct sock *sk)
19521952
tp->cookie_values = NULL;
19531953
}
19541954

1955+
/* If socket is aborted during connect operation */
1956+
tcp_free_fastopen_req(tp);
1957+
19551958
sk_sockets_allocated_dec(sk);
19561959
sock_release_memcg(sk);
19571960
}

0 commit comments

Comments
 (0)