From 0a880ea6074bb247fdec6f35a19507bb78069918 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Sat, 9 Mar 2019 21:13:12 -0500 Subject: [PATCH 1/5] Use TCP Fast Open for client sockets Set the TCP_FASTOPEN_CONNECT option on client sockets to signal desire to use TCP Fast Open. See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=19f6d3f3c8422d65b5e3d2162e30ef07c6e21ea2 --- common.c | 6 +++++- common.h | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/common.c b/common.c index bc405b6..71c8076 100644 --- a/common.c +++ b/common.c @@ -277,6 +277,11 @@ int connect_addr(struct connection *cnx, int fd_from) log_message(LOG_ERR, "forward to %s failed:socket: %s\n", cnx->proto->name, strerror(errno)); } else { + one = 1; + // indicate desire to use TCP Fast Open + setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &one, sizeof(one)); + // no need to check return value; if it's not supported, that's okay + if (cfg.transparent) { res = bind_peer(fd, fd_from); CHECK_RES_RETURN(res, "bind_peer"); @@ -288,7 +293,6 @@ int connect_addr(struct connection *cnx, int fd_from) close(fd); } else { if (cnx->proto->keepalive) { - one = 1; res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(one)); CHECK_RES_RETURN(res, "setsockopt(SO_KEEPALIVE)"); } diff --git a/common.h b/common.h index be3183d..ae00fdf 100644 --- a/common.h +++ b/common.h @@ -67,6 +67,10 @@ #define IP_FREEBIND 0 #endif +#ifndef TCP_FASTOPEN_CONNECT +#define TCP_FASTOPEN_CONNECT 30 /* Attempt FastOpen with connect. */ +#endif + enum connection_state { ST_PROBING=1, /* Waiting for timeout to find where to forward */ ST_SHOVELING /* Connexion is established */ From 15f733e572f3ea460f0bf98867e87512f959b2fc Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 10 Mar 2019 09:46:06 +0100 Subject: [PATCH 2/5] add tfo_ok configuration setting --- example.cfg | 19 +++++++++---------- sslh-conf.c | 11 ++++++++++- sslh-conf.h | 3 ++- sslhconf.cfg | 6 ++++++ 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/example.cfg b/example.cfg index 3a746d6..ffbc2ba 100644 --- a/example.cfg +++ b/example.cfg @@ -41,6 +41,7 @@ listen: # connection (default is off) # fork: Should a new process be forked for this protocol? # (only useful for sslh-select) +# tfo_ok: Set to true if the server supports TCP FAST OPEN # # Probe-specific options: # (sslh will try each probe in order they are declared, and @@ -66,28 +67,26 @@ listen: protocols: ( - { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; keepalive: true; fork: true; -listen: ( { host: "hello"; port: "xmpp" }, { host: "world"; -port: "dns" } ), - }, + { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; + keepalive: true; fork: true; tfo_ok: true }, { name: "http"; host: "localhost"; port: "80"; }, # match BOTH ALPN/SNI - { name: "tls"; host: "localhost"; port: "5223"; alpn_protocols: [ "xmpp-client" ]; sni_hostnames: [ "im.somethingelse.net" ]; log_level: 0;}, + { name: "tls"; host: "localhost"; port: "5223"; alpn_protocols: [ "xmpp-client" ]; sni_hostnames: [ "im.somethingelse.net" ]; log_level: 0; tfo_ok: true }, # just match ALPN - { name: "tls"; host: "localhost"; port: "443"; alpn_protocols: [ "h2", "http/1.1", "spdy/1", "spdy/2", "spdy/3" ]; log_level: 0; }, - { name: "tls"; host: "localhost"; port: "xmpp-client"; alpn_protocols: [ "xmpp-client" ]; log_level: 0;}, + { name: "tls"; host: "localhost"; port: "443"; alpn_protocols: [ "h2", "http/1.1", "spdy/1", "spdy/2", "spdy/3" ]; log_level: 0; tfo_ok: true }, + { name: "tls"; host: "localhost"; port: "xmpp-client"; alpn_protocols: [ "xmpp-client" ]; log_level: 0; tfo_ok: true }, # just match SNI - { name: "tls"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net", "mail.englishintoulouse.com" ]; log_level: 0; }, - { name: "tls"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net", "im.englishintoulouse.com" ]; log_level: 0;}, + { name: "tls"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net", "mail.englishintoulouse.com" ]; log_level: 0; tfo_ok: true }, + { name: "tls"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net", "im.englishintoulouse.com" ]; log_level: 0; tfo_ok: true }, # Let's Encrypt (tls-sni-* challenges) { name: "tls"; host: "localhost"; port: "letsencrypt-client"; sni_hostnames: [ "*.*.acme.invalid" ]; log_level: 0;}, # catch anything else TLS - { name: "tls"; host: "localhost"; port: "443"; }, + { name: "tls"; host: "localhost"; port: "443"; tfo_ok: true }, # Regex examples -- better use the built-in probes for real-world use! # OpenVPN diff --git a/sslh-conf.c b/sslh-conf.c index 0fba913..08bf7ca 100644 --- a/sslh-conf.c +++ b/sslh-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sat Mar 9 12:35:49 2019. */ + * on Sun Mar 10 09:37:57 2019. */ #define _GNU_SOURCE #include @@ -10,6 +10,7 @@ static void sslhcfg_protocols_init(struct sslhcfg_protocols_item* cfg) { memset(cfg, 0, sizeof(*cfg)); cfg->fork = 0; + cfg->tfo_ok = 0; cfg->log_level = 1; cfg->keepalive = 0; } @@ -94,6 +95,12 @@ static int sslhcfg_protocols_parser( return 0; } ; } + if (config_setting_lookup(cfg, "tfo_ok")) { + if (config_setting_lookup_bool(cfg, "tfo_ok", &sslhcfg_protocols->tfo_ok) == CONFIG_FALSE) { + *errmsg = "Parsing of option \"tfo_ok\" failed"; + return 0; + } ; + } if (config_setting_lookup(cfg, "log_level")) { if (config_setting_lookup_int(cfg, "log_level", &sslhcfg_protocols->log_level) == CONFIG_FALSE) { *errmsg = "Parsing of option \"log_level\" failed"; @@ -358,6 +365,8 @@ static void sslhcfg_protocols_fprint( indent(out, depth); fprintf(out, "fork: %d\n", sslhcfg_protocols->fork); indent(out, depth); + fprintf(out, "tfo_ok: %d\n", sslhcfg_protocols->tfo_ok); + indent(out, depth); fprintf(out, "log_level: %d\n", sslhcfg_protocols->log_level); indent(out, depth); fprintf(out, "keepalive: %d\n", sslhcfg_protocols->keepalive); diff --git a/sslh-conf.h b/sslh-conf.h index 7391cdd..05477bd 100644 --- a/sslh-conf.h +++ b/sslh-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sat Mar 9 12:35:49 2019. */ + * on Sun Mar 10 09:37:57 2019. */ #ifndef C2S_SSLHCFG_H #define C2S_SSLHCFG_H @@ -23,6 +23,7 @@ struct sslhcfg_protocols_item { int service_is_present; const char* service; int fork; + int tfo_ok; int log_level; int keepalive; size_t sni_hostnames_len; diff --git a/sslhconf.cfg b/sslhconf.cfg index 0c52c11..40ca38b 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -66,6 +66,8 @@ config: { { name: "port"; type: "string"; var: true; }, { name: "service"; type: "string"; optional: true; }, { name: "fork"; type: "boolean"; default: false }, + { name: "tfo_ok"; type: "boolean"; default: false; + description: "Set to true if this protocol supports TCP FAST OPEN" }, { name: "log_level"; type: "int"; default: 1 }, { name: "keepalive"; type: "boolean"; default: false }, { name: "sni_hostnames", @@ -119,6 +121,7 @@ cl_groups: ( { path: "host"; value: "$1" }, { path: "port"; value: "$2" }, { path: "fork"; value: 1 } + { path: "tfo_ok"; value: 1 } ); }, { name: "tls"; pattern: "(\w+):(\w+)"; description: "Set up TLS/SSL target"; @@ -129,6 +132,7 @@ cl_groups: ( { path: "name"; value: "tls" }, { path: "host"; value: "$1" }, { path: "port"; value: "$2" } + { path: "tfo_ok"; value: 1 } ); }, { name: "openvpn"; pattern: "(\w+):(\w+)"; description: "Set up OpenVPN target"; @@ -139,6 +143,7 @@ cl_groups: ( { path: "name"; value: "openvpn" }, { path: "host"; value: "$1" }, { path: "port"; value: "$2" } + { path: "tfo_ok"; value: 1 } ); }, { name: "tinc"; pattern: "(\w+):(\w+)"; description: "Set up tinc target"; @@ -149,6 +154,7 @@ cl_groups: ( { path: "name"; value: "openvpn" }, { path: "host"; value: "$1" }, { path: "port"; value: "$2" } + { path: "tfo_ok"; value: 1 } ); }, { name: "xmpp"; pattern: "(\w+):(\w+)"; description: "Set up XMPP target"; From b0c3c8fdbc1be2789081b9f33faf1e561c39b4be Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 10 Mar 2019 09:53:52 +0100 Subject: [PATCH 3/5] manage TFO already done in connect call --- common.c | 16 +++++++++++----- sslh-conf.c | 6 +++++- sslh-conf.h | 2 +- sslhconf.cfg | 8 ++++---- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/common.c b/common.c index 71c8076..79ad8b4 100644 --- a/common.c +++ b/common.c @@ -278,9 +278,8 @@ int connect_addr(struct connection *cnx, int fd_from) cnx->proto->name, strerror(errno)); } else { one = 1; - // indicate desire to use TCP Fast Open setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &one, sizeof(one)); - // no need to check return value; if it's not supported, that's okay + /* no need to check return value; if it's not supported, that's okay */ if (cfg.transparent) { res = bind_peer(fd, fd_from); @@ -288,9 +287,16 @@ int connect_addr(struct connection *cnx, int fd_from) } res = connect(fd, a->ai_addr, a->ai_addrlen); if (res == -1) { - log_message(LOG_ERR, "forward to %s failed:connect: %s\n", - cnx->proto->name, strerror(errno)); - close(fd); + switch (errno) { + case EINPROGRESS: + /* Can't be done yet, or TFO already done */ + break; + + default: + log_message(LOG_ERR, "forward to %s failed:connect: %s\n", + cnx->proto->name, strerror(errno)); + close(fd); + } } else { if (cnx->proto->keepalive) { res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(one)); diff --git a/sslh-conf.c b/sslh-conf.c index 08bf7ca..7888010 100644 --- a/sslh-conf.c +++ b/sslh-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sun Mar 10 09:37:57 2019. */ + * on Sun Mar 10 09:52:17 2019. */ #define _GNU_SOURCE #include @@ -655,6 +655,7 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) { group->port = calloc(1, param_len + 1); memcpy(group->port, sslhcfg_ssh->sval [cl_i]+pmatch[2].rm_so, param_len); group->fork = *((char*)"1") - '0'; + group->tfo_ok = *((char*)"1") - '0'; } #define MAX_MATCH 10 for (cl_i = 0; cl_i < sslhcfg_tls->count; cl_i++) { @@ -704,6 +705,7 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) { param_len = pmatch[2].rm_eo - pmatch[2].rm_so; group->port = calloc(1, param_len + 1); memcpy(group->port, sslhcfg_tls->sval [cl_i]+pmatch[2].rm_so, param_len); + group->tfo_ok = *((char*)"1") - '0'; } #define MAX_MATCH 10 for (cl_i = 0; cl_i < sslhcfg_openvpn->count; cl_i++) { @@ -753,6 +755,7 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) { param_len = pmatch[2].rm_eo - pmatch[2].rm_so; group->port = calloc(1, param_len + 1); memcpy(group->port, sslhcfg_openvpn->sval [cl_i]+pmatch[2].rm_so, param_len); + group->tfo_ok = *((char*)"1") - '0'; } #define MAX_MATCH 10 for (cl_i = 0; cl_i < sslhcfg_tinc->count; cl_i++) { @@ -802,6 +805,7 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) { param_len = pmatch[2].rm_eo - pmatch[2].rm_so; group->port = calloc(1, param_len + 1); memcpy(group->port, sslhcfg_tinc->sval [cl_i]+pmatch[2].rm_so, param_len); + group->tfo_ok = *((char*)"1") - '0'; } #define MAX_MATCH 10 for (cl_i = 0; cl_i < sslhcfg_xmpp->count; cl_i++) { diff --git a/sslh-conf.h b/sslh-conf.h index 05477bd..5aafa73 100644 --- a/sslh-conf.h +++ b/sslh-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sun Mar 10 09:37:57 2019. */ + * on Sun Mar 10 09:52:17 2019. */ #ifndef C2S_SSLHCFG_H #define C2S_SSLHCFG_H diff --git a/sslhconf.cfg b/sslhconf.cfg index 40ca38b..dd29bc8 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -120,7 +120,7 @@ cl_groups: ( { path: "name"; value: "ssh" }, { path: "host"; value: "$1" }, { path: "port"; value: "$2" }, - { path: "fork"; value: 1 } + { path: "fork"; value: 1 }, { path: "tfo_ok"; value: 1 } ); }, @@ -131,7 +131,7 @@ cl_groups: ( targets: ( { path: "name"; value: "tls" }, { path: "host"; value: "$1" }, - { path: "port"; value: "$2" } + { path: "port"; value: "$2" }, { path: "tfo_ok"; value: 1 } ); }, @@ -142,7 +142,7 @@ cl_groups: ( targets: ( { path: "name"; value: "openvpn" }, { path: "host"; value: "$1" }, - { path: "port"; value: "$2" } + { path: "port"; value: "$2" }, { path: "tfo_ok"; value: 1 } ); }, @@ -153,7 +153,7 @@ cl_groups: ( targets: ( { path: "name"; value: "openvpn" }, { path: "host"; value: "$1" }, - { path: "port"; value: "$2" } + { path: "port"; value: "$2" }, { path: "tfo_ok"; value: 1 } ); }, From 4e725e1520b39ea79809c2947b0a3560c459e14a Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 10 Mar 2019 10:11:06 +0100 Subject: [PATCH 4/5] added TFO for listening socket --- common.c | 21 +++++++++++++++++++++ common.h | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/common.c b/common.c index 79ad8b4..10dca14 100644 --- a/common.c +++ b/common.c @@ -86,6 +86,24 @@ int get_fd_sockets(int *sockfd[]) return sd; } +/* Set TCP_FASTOPEN on listening socket if all client protocols support it */ +int make_listen_tfo(int s) +{ + int i, qlen = 5; + + /* Don't do it if not supported */ + if (!TCP_FASTOPEN) + return; + + /* Don't do it if any protocol does not specify it */ + for (i = 0; i < cfg.protocols_len; i++) { + if (! cfg.protocols[i].tfo_ok) + return; + } + + return setsockopt(s, SOL_SOCKET, TCP_FASTOPEN, (char*)&qlen, sizeof(qlen)); +} + /* Starts listening sockets on specified addresses. * IN: addr[], num_addr * OUT: *sockfd[] pointer to newly-allocated array of file descriptors @@ -134,6 +152,9 @@ int start_listen_sockets(int *sockfd[], struct addrinfo *addr_list) res = setsockopt((*sockfd)[i], SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)); check_res_dump(CR_DIE, res, addr, "setsockopt(SO_REUSEADDR)"); + res = make_listen_tfo((*sockfd)[i]); + check_res_dump(CR_WARN, res, addr, "setsockopt(TCP_FASTOPEN)"); + if (addr->ai_flags & SO_KEEPALIVE) { res = setsockopt((*sockfd)[i], SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(one)); check_res_dump(CR_DIE, res, addr, "setsockopt(SO_KEEPALIVE)"); diff --git a/common.h b/common.h index ae00fdf..cf944a9 100644 --- a/common.h +++ b/common.h @@ -67,6 +67,10 @@ #define IP_FREEBIND 0 #endif +#ifndef TCP_FASTOPEN +#define TCP_FASTOPEN 0 +#endif + #ifndef TCP_FASTOPEN_CONNECT #define TCP_FASTOPEN_CONNECT 30 /* Attempt FastOpen with connect. */ #endif From 2705face30990b750002a672e02f77bfa6459bc6 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 10 Mar 2019 10:11:28 +0100 Subject: [PATCH 5/5] TCP_FASTOPEN changelog --- ChangeLog | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 0e96d4b..335ec77 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,9 @@ -vNEXT: +vNEXT: + Added TCP_FASTOPEN support for client sockets (if + tfo_ok is specified in their configuration) and for + listenint socket, if all client protocols support it. + (Craig Andrews) + Added 'minlength' option to skip a probe if less than that many bytes have been received (mostly for regex)