mirror of
https://github.com/shadowsocks/shadowsocks-libev.git
synced 2026-10-22 06:53:46 +00:00
TCP Fast Open support for ss-tunnel (#2150)
* ... * ... * ... * Update README.md * Update README.md * TCP Fast Open support * TCP Fast Open support * TCP Fast Open CONNECT_IN_PROGRESS * TCP Fast Open support * TCP Fast Open support * TCP Fast Open support * TCP Fast Open support * TCP Fast Open support via conf * Cross-platform support for TCP Fast Open * Cross-platform support for TCP Fast Open * Enable SO_KEEPALIVE client side * TCP Fast Open is not supported on this platform * TCP Fast Open support * Update tunnel.c * It's from my forked repo, so please ignore any changes in README.md * Fix trailing whitespace * Fix #1376
This commit is contained in:
+135
-12
@@ -96,6 +96,7 @@ static int mode = TCP_ONLY;
|
||||
static int nofile = 0;
|
||||
#endif
|
||||
static int no_delay = 0;
|
||||
static int fast_open = 0;
|
||||
static int ret_val = 0;
|
||||
|
||||
static struct ev_signal sigint_watcher;
|
||||
@@ -387,14 +388,20 @@ remote_send_cb(EV_P_ ev_io *w, int revents)
|
||||
remote_t *remote = remote_send_ctx->remote;
|
||||
server_t *server = remote->server;
|
||||
|
||||
ev_timer_stop(EV_A_ & remote_send_ctx->watcher);
|
||||
|
||||
if (!remote_send_ctx->connected) {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof(struct sockaddr_storage);
|
||||
|
||||
int r = 0;
|
||||
|
||||
if (remote->addr == NULL) {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof(struct sockaddr_storage);
|
||||
r = getpeername(remote->fd, (struct sockaddr *)&addr, &len);
|
||||
}
|
||||
|
||||
int r = getpeername(remote->fd, (struct sockaddr *)&addr, &len);
|
||||
if (r == 0) {
|
||||
remote_send_ctx->connected = 1;
|
||||
ev_timer_stop(EV_A_ & remote_send_ctx->watcher);
|
||||
|
||||
assert(remote->buf->len == 0);
|
||||
buffer_t *abuf = remote->buf;
|
||||
@@ -470,8 +477,101 @@ remote_send_cb(EV_P_ ev_io *w, int revents)
|
||||
return;
|
||||
} else {
|
||||
// has data to send
|
||||
ssize_t s = send(remote->fd, remote->buf->data + remote->buf->idx,
|
||||
ssize_t s = -1;
|
||||
if (remote->addr != NULL) {
|
||||
#if defined(TCP_FASTOPEN_WINSOCK)
|
||||
DWORD s = -1;
|
||||
DWORD err = 0;
|
||||
do {
|
||||
int optval = 1;
|
||||
// Set fast open option
|
||||
if (setsockopt(remote->fd, IPPROTO_TCP, TCP_FASTOPEN,
|
||||
&optval, sizeof(optval)) != 0) {
|
||||
ERROR("setsockopt");
|
||||
break;
|
||||
}
|
||||
// Load ConnectEx function
|
||||
LPFN_CONNECTEX ConnectEx = winsock_getconnectex();
|
||||
if (ConnectEx == NULL) {
|
||||
LOGE("Cannot load ConnectEx() function");
|
||||
err = WSAENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
// ConnectEx requires a bound socket
|
||||
if (winsock_dummybind(remote->fd,
|
||||
(struct sockaddr *)&(remote->addr)) != 0) {
|
||||
ERROR("bind");
|
||||
break;
|
||||
}
|
||||
// Call ConnectEx to send data
|
||||
memset(&remote->olap, 0, sizeof(remote->olap));
|
||||
remote->connect_ex_done = 0;
|
||||
if (ConnectEx(remote->fd, (const struct sockaddr *)&(remote->addr),
|
||||
get_sockaddr_len(remote->addr), remote->buf->data, remote->buf->len,
|
||||
&s, &remote->olap)) {
|
||||
remote->connect_ex_done = 1;
|
||||
break;
|
||||
}
|
||||
// XXX: ConnectEx pending, check later in remote_send
|
||||
if (WSAGetLastError() == ERROR_IO_PENDING) {
|
||||
err = CONNECT_IN_PROGRESS;
|
||||
break;
|
||||
}
|
||||
ERROR("ConnectEx");
|
||||
} while (0);
|
||||
// Set error number
|
||||
if (err) {
|
||||
SetLastError(err);
|
||||
}
|
||||
#elif defined(CONNECT_DATA_IDEMPOTENT)
|
||||
((struct sockaddr_in *)&(remote->addr))->sin_len = sizeof(struct sockaddr_in);
|
||||
sa_endpoints_t endpoints;
|
||||
memset((char *)&endpoints, 0, sizeof(endpoints));
|
||||
endpoints.sae_dstaddr = (struct sockaddr *)&(remote->addr);
|
||||
endpoints.sae_dstaddrlen = get_sockaddr_len(remote->addr);
|
||||
s = connectx(remote->fd, &endpoints, SAE_ASSOCID_ANY,
|
||||
CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
|
||||
NULL, 0, NULL, NULL);
|
||||
#elif defined(TCP_FASTOPEN_CONNECT)
|
||||
int optval = 1;
|
||||
if(setsockopt(remote->fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
|
||||
(void *)&optval, sizeof(optval)) < 0)
|
||||
FATAL("failed to set TCP_FASTOPEN_CONNECT");
|
||||
s = connect(remote->fd, remote->addr, get_sockaddr_len(remote->addr));
|
||||
if (s == 0)
|
||||
s = send(remote->fd, remote->buf->data, remote->buf->len, 0);
|
||||
#elif defined(MSG_FASTOPEN)
|
||||
s = sendto(remote->fd, remote->buf->data + remote->buf->idx,
|
||||
remote->buf->len, MSG_FASTOPEN, remote->addr,
|
||||
get_sockaddr_len(remote->addr));
|
||||
#else
|
||||
FATAL("tcp fast open is not supported on this platform");
|
||||
#endif
|
||||
|
||||
remote->addr = NULL;
|
||||
|
||||
if (s == -1) {
|
||||
if (errno == CONNECT_IN_PROGRESS) {
|
||||
ev_io_start(EV_A_ & remote_send_ctx->io);
|
||||
ev_timer_start(EV_A_ & remote_send_ctx->watcher);
|
||||
} else {
|
||||
fast_open = 0;
|
||||
if (errno == EOPNOTSUPP || errno == EPROTONOSUPPORT ||
|
||||
errno == ENOPROTOOPT) {
|
||||
LOGE("fast open is not supported on this platform");
|
||||
} else {
|
||||
ERROR("fast_open_connect");
|
||||
}
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
close_and_free_server(EV_A_ server);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
s = send(remote->fd, remote->buf->data + remote->buf->idx,
|
||||
remote->buf->len, 0);
|
||||
}
|
||||
|
||||
if (s == -1) {
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
ERROR("send");
|
||||
@@ -654,6 +754,8 @@ accept_cb(EV_P_ ev_io *w, int revents)
|
||||
}
|
||||
#endif
|
||||
|
||||
int keepAlive = 1;
|
||||
setsockopt(remotefd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
|
||||
setsockopt(remotefd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt));
|
||||
#ifdef SO_NOSIGPIPE
|
||||
setsockopt(remotefd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));
|
||||
@@ -693,18 +795,23 @@ accept_cb(EV_P_ ev_io *w, int revents)
|
||||
server->remote = remote;
|
||||
remote->server = server;
|
||||
|
||||
int r = connect(remotefd, remote_addr, get_sockaddr_len(remote_addr));
|
||||
if (fast_open) {
|
||||
remote->addr = remote_addr;
|
||||
} else {
|
||||
int r = connect(remotefd, remote_addr, get_sockaddr_len(remote_addr));
|
||||
|
||||
if (r == -1 && errno != CONNECT_IN_PROGRESS) {
|
||||
ERROR("connect");
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
close_and_free_server(EV_A_ server);
|
||||
return;
|
||||
if (r == -1 && errno != CONNECT_IN_PROGRESS) {
|
||||
ERROR("connect");
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
close_and_free_server(EV_A_ server);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// listen to remote connected event
|
||||
ev_io_start(EV_A_ & remote->send_ctx->io);
|
||||
ev_timer_start(EV_A_ & remote->send_ctx->watcher);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -791,6 +898,7 @@ main(int argc, char **argv)
|
||||
char *tunnel_addr_str = NULL;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{ "fast-open", no_argument, NULL, GETOPT_VAL_FAST_OPEN },
|
||||
{ "mtu", required_argument, NULL, GETOPT_VAL_MTU },
|
||||
{ "no-delay", no_argument, NULL, GETOPT_VAL_NODELAY },
|
||||
{ "mptcp", no_argument, NULL, GETOPT_VAL_MPTCP },
|
||||
@@ -815,6 +923,9 @@ main(int argc, char **argv)
|
||||
long_options, NULL)) != -1) {
|
||||
#endif
|
||||
switch (c) {
|
||||
case GETOPT_VAL_FAST_OPEN:
|
||||
fast_open = 1;
|
||||
break;
|
||||
case GETOPT_VAL_MTU:
|
||||
mtu = atoi(optarg);
|
||||
LOGI("set MTU to %d", mtu);
|
||||
@@ -983,6 +1094,9 @@ main(int argc, char **argv)
|
||||
if (reuse_port == 0) {
|
||||
reuse_port = conf->reuse_port;
|
||||
}
|
||||
if (fast_open == 0) {
|
||||
fast_open = conf->fast_open;
|
||||
}
|
||||
#ifdef HAVE_SETRLIMIT
|
||||
if (nofile == 0) {
|
||||
nofile = conf->nofile;
|
||||
@@ -1044,7 +1158,16 @@ main(int argc, char **argv)
|
||||
if (local_addr == NULL) {
|
||||
local_addr = "127.0.0.1";
|
||||
}
|
||||
|
||||
|
||||
if (fast_open == 1) {
|
||||
#ifdef TCP_FASTOPEN
|
||||
LOGI("using tcp fast open");
|
||||
#else
|
||||
LOGE("tcp fast open is not supported by this environment");
|
||||
fast_open = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
USE_SYSLOG(argv[0], pid_flags);
|
||||
if (pid_flags) {
|
||||
daemonize(pid_path);
|
||||
|
||||
@@ -76,6 +76,7 @@ typedef struct remote {
|
||||
struct remote_ctx *recv_ctx;
|
||||
struct remote_ctx *send_ctx;
|
||||
struct server *server;
|
||||
struct sockaddr *addr;
|
||||
uint32_t counter;
|
||||
} remote_t;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user