diff --git a/doc/shadowsocks-libev.asciidoc b/doc/shadowsocks-libev.asciidoc index d1dceee1..64debe17 100644 --- a/doc/shadowsocks-libev.asciidoc +++ b/doc/shadowsocks-libev.asciidoc @@ -126,6 +126,11 @@ Enable TCP fast open. + Not available in redir nor tunnel mode, with Linux kernel > 3.7.0. +--reuse-port:: +Enable port reuse. ++ +Only available with Linux kernel > 3.9.0. + --acl :: Enable ACL (Access Control List) and specify config file. + @@ -166,6 +171,7 @@ The config file equivalent of command line options is listed as example below. | -t 60 | "timeout": 60 | -a nobody | "user": "nobody" | --fast-open | "fast_open": true +| --reuse-port | "reuse_port": true | --plugin "obfs-server" | "plugin": "obfs-server" | --plugin-opts "obfs=http" | "plugin_opts": "obfs=http" | -6 | "ipv6_first": true diff --git a/doc/ss-local.asciidoc b/doc/ss-local.asciidoc index 826c69b9..151060ac 100644 --- a/doc/ss-local.asciidoc +++ b/doc/ss-local.asciidoc @@ -13,7 +13,7 @@ SYNOPSIS [-k ] [-m ] [-f ] [-t ] [-c ] [-i ] [-a ] [-b ] - [--fast-open] [--acl ] [--mtu ] + [--fast-open] [--reuse-port] [--acl ] [--mtu ] [--plugin ] [--plugin-opts 3.7.0. +--reuse-port:: +Enable port reuse. ++ +Only available with Linux kernel > 3.9.0. + --acl :: Enable ACL (Access Control List) and specify config file. diff --git a/doc/ss-manager.asciidoc b/doc/ss-manager.asciidoc index ac1e553d..41e875e9 100644 --- a/doc/ss-manager.asciidoc +++ b/doc/ss-manager.asciidoc @@ -15,6 +15,7 @@ SYNOPSIS [-b ] [-a ] [--manager-address ] [--executable ] + [--fast-open] [--reuse-port] [--plugin ] [--plugin-opts ] DESCRIPTION @@ -93,6 +94,11 @@ Enable TCP fast open. + Only available with Linux kernel > 3.7.0. +--reuse-port:: +Enable port reuse. ++ +Only available with Linux kernel > 3.9.0. + --acl :: Enable ACL (Access Control List) and specify config file. diff --git a/doc/ss-redir.asciidoc b/doc/ss-redir.asciidoc index be85662a..dce386ce 100644 --- a/doc/ss-redir.asciidoc +++ b/doc/ss-redir.asciidoc @@ -99,6 +99,11 @@ Enable Multipath TCP. + Only available with MPTCP enabled Linux kernel. +--reuse-port:: +Enable port reuse. ++ +Only available with Linux kernel > 3.9.0. + --plugin :: Enable SIP003 plugin. (Experimental) diff --git a/doc/ss-server.asciidoc b/doc/ss-server.asciidoc index 775b15ba..cd367f67 100644 --- a/doc/ss-server.asciidoc +++ b/doc/ss-server.asciidoc @@ -13,7 +13,7 @@ SYNOPSIS [-k ] [-m ] [-f ] [-t ] [-c ] [-i ] [-a ] [-d ] [-n ] - [-b ] [--mtu ] [--manager-address ] [--plugin ] [--plugin-opts 3.7.0. +--reuse-port:: +Enable port reuse. ++ +Only available with Linux kernel > 3.9.0. + --acl :: Enable ACL (Access Control List) and specify config file. diff --git a/doc/ss-tunnel.asciidoc b/doc/ss-tunnel.asciidoc index 1e24c3e1..41838b8f 100644 --- a/doc/ss-tunnel.asciidoc +++ b/doc/ss-tunnel.asciidoc @@ -13,7 +13,7 @@ SYNOPSIS [-k ] [-m ] [-f ] [-t ] [-c ] [-i ] [-b ] [-a ] [-n ] - [-L addr:port] [--mtu ] + [-L addr:port] [--mtu ] [--mptcp] [--reuse-port] [--plugin ] [--plugin-opts 3.9.0. + --plugin :: Enable SIP003 plugin. (Experimental) diff --git a/src/common.h b/src/common.h index f983a879..cd16e185 100644 --- a/src/common.h +++ b/src/common.h @@ -57,4 +57,18 @@ int send_traffic_stat(uint64_t tx, uint64_t rx); #define STAGE_RESOLVE 4 /* Resolve the hostname */ #define STAGE_STREAM 5 /* Stream between client and server */ +/* Vals for long options */ +enum { + GETOPT_VAL_HELP = 257, + GETOPT_VAL_REUSE_PORT, + GETOPT_VAL_FAST_OPEN, + GETOPT_VAL_ACL, + GETOPT_VAL_MTU, + GETOPT_VAL_MPTCP, + GETOPT_VAL_PLUGIN, + GETOPT_VAL_PLUGIN_OPTS, + GETOPT_VAL_MANAGER_ADDRESS, + GETOPT_VAL_EXECUTABLE +}; + #endif // _COMMON_H diff --git a/src/jconf.c b/src/jconf.c index 72b34641..769896c2 100644 --- a/src/jconf.c +++ b/src/jconf.c @@ -211,6 +211,10 @@ read_jconf(const char *file) check_json_value_type(value, json_boolean, "invalid config file: option 'fast_open' must be a boolean"); conf.fast_open = value->u.boolean; + } else if (strcmp(name, "reuse_port") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'reuse_port' must be a boolean"); + conf.reuse_port = value->u.boolean; } else if (strcmp(name, "auth") == 0) { check_json_value_type(value, json_boolean, "invalid config file: option 'auth' must be a boolean"); diff --git a/src/jconf.h b/src/jconf.h index 0f660f2e..5411b57b 100644 --- a/src/jconf.h +++ b/src/jconf.h @@ -60,6 +60,7 @@ typedef struct { char *plugin_opts; int auth; int fast_open; + int reuse_port; int nofile; char *nameserver; char *tunnel_address; diff --git a/src/local.c b/src/local.c index f01283ac..4f417c40 100644 --- a/src/local.c +++ b/src/local.c @@ -86,6 +86,7 @@ #endif int verbose = 0; +int reuse_port = 0; int keep_resolving = 1; #ifdef ANDROID @@ -173,9 +174,11 @@ create_and_bind(const char *addr, const char *port) #ifdef SO_NOSIGPIPE setsockopt(listen_sock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); #endif - int err = set_reuseport(listen_sock); - if (err == 0) { - LOGI("tcp port reuse enabled"); + if (reuse_port) { + int err = set_reuseport(listen_sock); + if (err == 0) { + LOGI("tcp port reuse enabled"); + } } s = bind(listen_sock, rp->ai_addr, rp->ai_addrlen); @@ -1187,12 +1190,14 @@ main(int argc, char **argv) char *remote_port = NULL; static struct option long_options[] = { + { "reuse-port", no_argument, NULL, GETOPT_VAL_REUSE_PORT }, { "fast-open", no_argument, NULL, GETOPT_VAL_FAST_OPEN }, { "acl", required_argument, NULL, GETOPT_VAL_ACL }, { "mtu", required_argument, NULL, GETOPT_VAL_MTU }, { "mptcp", no_argument, NULL, GETOPT_VAL_MPTCP }, { "plugin", required_argument, NULL, GETOPT_VAL_PLUGIN }, { "plugin-opts", required_argument, NULL, GETOPT_VAL_PLUGIN_OPTS }, + { "port-reuse", no_argument , NULL, GETOPT_VAL_REUSE_PORT }, { "help", no_argument, NULL, GETOPT_VAL_HELP }, { NULL, 0, NULL, 0 } }; @@ -1230,6 +1235,9 @@ main(int argc, char **argv) case GETOPT_VAL_PLUGIN_OPTS: plugin_opts = optarg; break; + case GETOPT_VAL_REUSE_PORT: + reuse_port = 1; + break; case 's': if (remote_num < MAX_REMOTE_NUM) { remote_addr[remote_num].host = optarg; @@ -1345,6 +1353,9 @@ main(int argc, char **argv) if (plugin_opts == NULL) { plugin_opts = conf->plugin_opts; } + if (reuse_port == 0) { + reuse_port = conf->reuse_port; + } if (fast_open == 0) { fast_open = conf->fast_open; } diff --git a/src/manager.c b/src/manager.c index edc6aee0..1e033e7d 100644 --- a/src/manager.c +++ b/src/manager.c @@ -159,6 +159,10 @@ construct_command_line(struct manager_ctx *manager, struct server *server) int len = strlen(cmd); snprintf(cmd + len, BUF_SIZE - len, " --fast-open"); } + if (manager->reuse_port) { + int len = strlen(cmd); + snprintf(cmd + len, BUF_SIZE - len, " --reuse-port"); + } if (manager->ipv6first) { int len = strlen(cmd); snprintf(cmd + len, BUF_SIZE - len, " -6"); @@ -313,7 +317,7 @@ create_and_bind(const char *host, const char *port, int protocol) { struct addrinfo hints; struct addrinfo *result, *rp, *ipv4v6bindall; - int s, listen_sock = -1, is_port_reuse = 0; + int s, listen_sock = -1, is_reuse_port = 0; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ @@ -372,14 +376,14 @@ create_and_bind(const char *host, const char *port, int protocol) if (verbose) { LOGI("%s port reuse enabled", protocol == IPPROTO_TCP ? "tcp" : "udp"); } - is_port_reuse = 1; + is_reuse_port = 1; } s = bind(listen_sock, rp->ai_addr, rp->ai_addrlen); if (s == 0) { /* We managed to bind successfully! */ - if (!is_port_reuse) { + if (!is_reuse_port) { if (verbose) { LOGI("close sock due to %s port reuse disabled", protocol == IPPROTO_TCP ? "tcp" : "udp"); } @@ -401,7 +405,7 @@ create_and_bind(const char *host, const char *port, int protocol) return -1; } - return is_port_reuse ? listen_sock : -2; + return is_reuse_port ? listen_sock : -2; } static void @@ -876,11 +880,12 @@ main(int argc, char **argv) char *plugin = NULL; char *plugin_opts = NULL; - int auth = 0; - int fast_open = 0; - int mode = TCP_ONLY; - int mtu = 0; - int ipv6first = 0; + int auth = 0; + int fast_open = 0; + int reuse_port = 0; + int mode = TCP_ONLY; + int mtu = 0; + int ipv6first = 0; #ifdef HAVE_SETRLIMIT static int nofile = 0; @@ -896,6 +901,7 @@ main(int argc, char **argv) static struct option long_options[] = { { "fast-open", no_argument, NULL, GETOPT_VAL_FAST_OPEN }, + { "reuse-port", no_argument, NULL, GETOPT_VAL_REUSE_PORT }, { "acl", required_argument, NULL, GETOPT_VAL_ACL }, { "manager-address", required_argument, NULL, GETOPT_VAL_MANAGER_ADDRESS }, @@ -915,6 +921,9 @@ main(int argc, char **argv) while ((c = getopt_long(argc, argv, "f:s:l:k:t:m:c:i:d:a:n:6huUvA", long_options, NULL)) != -1) switch (c) { + case GETOPT_VAL_REUSE_PORT: + reuse_port = 1; + break; case GETOPT_VAL_FAST_OPEN: fast_open = 1; break; @@ -1023,11 +1032,12 @@ main(int argc, char **argv) if (user == NULL) { user = conf->user; } -#ifdef TCP_FASTOPEN if (fast_open == 0) { fast_open = conf->fast_open; } -#endif + if (reuse_port == 0) { + reuse_port = conf->reuse_port; + } if (conf->nameserver != NULL) { nameservers[nameserver_num++] = conf->nameserver; } @@ -1105,6 +1115,7 @@ main(int argc, char **argv) struct manager_ctx manager; memset(&manager, 0, sizeof(struct manager_ctx)); + manager.reuse_port = reuse_port; manager.fast_open = fast_open; manager.verbose = verbose; manager.mode = mode; diff --git a/src/manager.h b/src/manager.h index 964d39cc..ccdcaded 100644 --- a/src/manager.h +++ b/src/manager.h @@ -35,6 +35,7 @@ struct manager_ctx { ev_io io; int fd; int fast_open; + int reuse_port; int verbose; int mode; int auth; diff --git a/src/redir.c b/src/redir.c index 8ab7b98d..dfd01856 100644 --- a/src/redir.c +++ b/src/redir.c @@ -87,6 +87,7 @@ static void free_server(server_t *server); static void close_and_free_server(EV_P_ server_t *server); int verbose = 0; +int reuse_port = 0; int keep_resolving = 1; static crypto_t *crypto; @@ -155,9 +156,11 @@ create_and_bind(const char *addr, const char *port) #ifdef SO_NOSIGPIPE setsockopt(listen_sock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); #endif - int err = set_reuseport(listen_sock); - if (err == 0) { - LOGI("tcp port reuse enabled"); + if (reuse_port) { + int err = set_reuseport(listen_sock); + if (err == 0) { + LOGI("tcp port reuse enabled"); + } } s = bind(listen_sock, rp->ai_addr, rp->ai_addrlen); @@ -803,6 +806,7 @@ main(int argc, char **argv) { "mptcp", no_argument, NULL, GETOPT_VAL_MPTCP }, { "plugin", required_argument, NULL, GETOPT_VAL_PLUGIN }, { "plugin-opts", required_argument, NULL, GETOPT_VAL_PLUGIN_OPTS }, + { "port-reuse", no_argument, NULL, GETOPT_VAL_REUSE_PORT }, { "help", no_argument, NULL, GETOPT_VAL_HELP }, { NULL, 0, NULL, 0 } }; @@ -828,6 +832,9 @@ main(int argc, char **argv) case GETOPT_VAL_PLUGIN_OPTS: plugin_opts = optarg; break; + case GETOPT_VAL_REUSE_PORT: + reuse_port = 1; + break; case 's': if (remote_num < MAX_REMOTE_NUM) { remote_addr[remote_num].host = optarg; @@ -942,6 +949,9 @@ main(int argc, char **argv) if (mptcp == 0) { mptcp = conf->mptcp; } + if (reuse_port == 0) { + reuse_port = conf->reuse_port; + } #ifdef HAVE_SETRLIMIT if (nofile == 0) { nofile = conf->nofile; diff --git a/src/server.c b/src/server.c index e899553c..b76873f5 100644 --- a/src/server.c +++ b/src/server.c @@ -99,7 +99,8 @@ static void close_and_free_server(EV_P_ server_t *server); static void server_resolve_cb(struct sockaddr *addr, void *data); static void query_free_cb(void *data); -int verbose = 0; +int verbose = 0; +int reuse_port = 0; static crypto_t *crypto; @@ -379,9 +380,11 @@ create_and_bind(const char *host, const char *port, int mptcp) #ifdef SO_NOSIGPIPE setsockopt(listen_sock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); #endif - int err = set_reuseport(listen_sock); - if (err == 0) { - LOGI("tcp port reuse enabled"); + if (reuse_port) { + int err = set_reuseport(listen_sock); + if (err == 0) { + LOGI("tcp port reuse enabled"); + } } if (mptcp == 1) { @@ -1353,6 +1356,7 @@ main(int argc, char **argv) static struct option long_options[] = { { "fast-open", no_argument, NULL, GETOPT_VAL_FAST_OPEN }, + { "reuse-port", no_argument, NULL, GETOPT_VAL_REUSE_PORT }, { "acl", required_argument, NULL, GETOPT_VAL_ACL }, { "manager-address", required_argument, NULL, GETOPT_VAL_MANAGER_ADDRESS }, @@ -1360,6 +1364,7 @@ main(int argc, char **argv) { "help", no_argument, NULL, GETOPT_VAL_HELP }, { "plugin", required_argument, NULL, GETOPT_VAL_PLUGIN }, { "plugin-opts", required_argument, NULL, GETOPT_VAL_PLUGIN_OPTS }, + { "port-reuse", no_argument, NULL, GETOPT_VAL_REUSE_PORT }, #ifdef __linux__ { "mptcp", no_argument, NULL, GETOPT_VAL_MPTCP }, #endif @@ -1397,6 +1402,9 @@ main(int argc, char **argv) mptcp = 1; LOGI("enable multipath TCP"); break; + case GETOPT_VAL_REUSE_PORT: + reuse_port = 1; + break; case 's': if (server_num < MAX_REMOTE_NUM) { server_host[server_num++] = optarg; @@ -1512,11 +1520,12 @@ main(int argc, char **argv) if (mptcp == 0) { mptcp = conf->mptcp; } -#ifdef TCP_FASTOPEN + if (reuse_port == 0) { + reuse_port = conf->reuse_port; + } if (fast_open == 0) { fast_open = conf->fast_open; } -#endif #ifdef HAVE_SETRLIMIT if (nofile == 0) { nofile = conf->nofile; diff --git a/src/tunnel.c b/src/tunnel.c index 9809741e..f05e442f 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -85,6 +85,7 @@ int vpn = 0; #endif int verbose = 0; +int reuse_port = 0; int keep_resolving = 1; static crypto_t *crypto; @@ -137,9 +138,11 @@ create_and_bind(const char *addr, const char *port) #ifdef SO_NOSIGPIPE setsockopt(listen_sock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); #endif - int err = set_reuseport(listen_sock); - if (err == 0) { - LOGI("tcp port reuse enabled"); + if (reuse_port) { + int err = set_reuseport(listen_sock); + if (err == 0) { + LOGI("tcp port reuse enabled"); + } } s = bind(listen_sock, rp->ai_addr, rp->ai_addrlen); @@ -750,6 +753,7 @@ main(int argc, char **argv) { "mptcp", no_argument, NULL, GETOPT_VAL_MPTCP }, { "plugin", required_argument, NULL, GETOPT_VAL_PLUGIN }, { "plugin-opts", required_argument, NULL, GETOPT_VAL_PLUGIN_OPTS }, + { "port-reuse", no_argument, NULL, GETOPT_VAL_REUSE_PORT }, { "help", no_argument, NULL, GETOPT_VAL_HELP }, { NULL, 0, NULL, 0} }; @@ -780,6 +784,9 @@ main(int argc, char **argv) case GETOPT_VAL_PLUGIN_OPTS: plugin_opts = optarg; break; + case GETOPT_VAL_REUSE_PORT: + reuse_port = 1; + break; case 's': if (remote_num < MAX_REMOTE_NUM) { remote_addr[remote_num].host = optarg; @@ -911,6 +918,9 @@ main(int argc, char **argv) if (mptcp == 0) { mptcp = conf->mptcp; } + if (reuse_port == 0) { + reuse_port = conf->reuse_port; + } #ifdef HAVE_SETRLIMIT if (nofile == 0) { nofile = conf->nofile; diff --git a/src/udprelay.c b/src/udprelay.c index 561e5f09..3f64558f 100644 --- a/src/udprelay.c +++ b/src/udprelay.c @@ -92,6 +92,7 @@ extern int vpn; #endif extern int verbose; +extern int reuse_port; #ifdef MODULE_REMOTE extern uint64_t tx; extern uint64_t rx; @@ -425,9 +426,11 @@ create_server_socket(const char *host, const char *port) #ifdef SO_NOSIGPIPE set_nosigpipe(server_sock); #endif - int err = set_reuseport(server_sock); - if (err == 0) { - LOGI("udp port reuse enabled"); + if (reuse_port) { + int err = set_reuseport(server_sock); + if (err == 0) { + LOGI("udp port reuse enabled"); + } } #ifdef IP_TOS // Set QoS flag diff --git a/src/utils.c b/src/utils.c index ea76f143..bf497e60 100644 --- a/src/utils.c +++ b/src/utils.c @@ -337,6 +337,8 @@ usage() printf( " [-d ] Name servers for internal DNS resolver.\n"); #endif + printf( + " [--reuse-port] Enable port reuse.\n"); #if defined(MODULE_REMOTE) || defined(MODULE_LOCAL) printf( " [--fast-open] Enable TCP fast open.\n"); diff --git a/src/utils.h b/src/utils.h index a4ec27d1..e3b44175 100644 --- a/src/utils.h +++ b/src/utils.h @@ -161,18 +161,6 @@ extern int use_syslog; #endif // if ANDROID -/* Vals for long options */ -enum { GETOPT_VAL_HELP = 257, - GETOPT_VAL_FAST_OPEN, - GETOPT_VAL_ACL, - GETOPT_VAL_MTU, - GETOPT_VAL_MPTCP, - GETOPT_VAL_PLUGIN, - GETOPT_VAL_PLUGIN_OPTS, - GETOPT_VAL_MANAGER_ADDRESS, - GETOPT_VAL_EXECUTABLE -}; - void ERROR(const char *s); char *ss_itoa(int i);