Browse Source

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
pull/2174/head
Jared Hillier 6 years ago
committed by Max Lv
parent
commit
b7f3e2226c
2 changed files with 136 additions and 12 deletions
  1. 147
      src/tunnel.c
  2. 1
      src/tunnel.h

147
src/tunnel.c

@ -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);

1
src/tunnel.h

@ -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;

Loading…
Cancel
Save