David Täht
2011-Nov-28 16:48 UTC
RFC: [PATCH] Add TCP congestion control and Diffserv options
In the bufferbloat age, anything that can make for a kinder, gentler bulk transfer protocol seems desirable. This add support for user and server selectable congestion control algorithms. As examples: --congestion-alg=lp # For the tcp-lp algorithm on the command line Or in a subsection: [mystuff] congestion alg = westwood # for a wireless connection And diffserv support: --diffserv=8 for setting the CS1 bit This could be improved by being able to specify a list of congestion algorithms to try, symbolic names for diffserv (CS1), a better name than 'congestion-alg', and some error checking. Signed-off-by: Dave Taht <d at taht.net> --- loadparm.c | 8 ++++++++ options.c | 8 ++++++++ socket.c | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 0 deletions(-) diff --git a/loadparm.c b/loadparm.c index 965b771..51b1b06 100644 --- a/loadparm.c +++ b/loadparm.c @@ -109,6 +109,7 @@ typedef struct { char *auth_users; char *charset; char *comment; + char *congestion_alg; char *dont_compress; char *exclude; char *exclude_from; @@ -138,6 +139,7 @@ typedef struct { int max_verbosity; int syslog_facility; int timeout; + int diffserv; BOOL fake_super; BOOL forward_lookup; @@ -185,6 +187,7 @@ static const all_vars Defaults = { /* auth_users; */ NULL, /* charset; */ NULL, /* comment; */ NULL, + /* congestion_alg; */ NULL, /* dont_compress; */ DEFAULT_DONT_COMPRESS, /* exclude; */ NULL, /* exclude_from; */ NULL, @@ -212,6 +215,7 @@ static const all_vars Defaults = { /* max_verbosity; */ 1, /* syslog_facility; */ LOG_DAEMON, /* timeout; */ 0, + /* diffserv; */ 8, /* fake_super; */ False, /* forward_lookup; */ True, @@ -322,6 +326,8 @@ static struct parm_struct parm_table[] {"auth users", P_STRING, P_LOCAL, &Vars.l.auth_users, NULL,0}, {"charset", P_STRING, P_LOCAL, &Vars.l.charset, NULL,0}, {"comment", P_STRING, P_LOCAL, &Vars.l.comment, NULL,0}, + {"congestion alg", P_STRING, P_LOCAL, &Vars.l.congestion_alg, NULL,0}, + {"diffserv", P_INTEGER, P_LOCAL, &Vars.l.diffserv, NULL,0}, {"dont compress", P_STRING, P_LOCAL, &Vars.l.dont_compress, NULL,0}, {"exclude from", P_STRING, P_LOCAL, &Vars.l.exclude_from, NULL,0}, {"exclude", P_STRING, P_LOCAL, &Vars.l.exclude, NULL,0}, @@ -454,6 +460,7 @@ FN_GLOBAL_INTEGER(lp_rsync_port, &Vars.g.rsync_port) FN_LOCAL_STRING(lp_auth_users, auth_users) FN_LOCAL_STRING(lp_charset, charset) FN_LOCAL_STRING(lp_comment, comment) +FN_LOCAL_STRING(lp_congestion_alg, congestion_alg) FN_LOCAL_STRING(lp_dont_compress, dont_compress) FN_LOCAL_STRING(lp_exclude, exclude) FN_LOCAL_STRING(lp_exclude_from, exclude_from) @@ -481,6 +488,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max_connections) FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity) FN_LOCAL_INTEGER(lp_syslog_facility, syslog_facility) FN_LOCAL_INTEGER(lp_timeout, timeout) +FN_LOCAL_INTEGER(lp_diffserv, diffserv) FN_LOCAL_BOOL(lp_fake_super, fake_super) FN_LOCAL_BOOL(lp_forward_lookup, forward_lookup) diff --git a/options.c b/options.c index 9e95c86..6085444 100644 --- a/options.c +++ b/options.c @@ -69,6 +69,8 @@ int delete_during = 0; int delete_before = 0; int delete_after = 0; int delete_excluded = 0; +int diffserv = 8; +char *congestion_alg = NULL; int remove_source_files = 0; int one_file_system = 0; int protocol_version = PROTOCOL_VERSION; @@ -776,6 +778,8 @@ void usage(enum logcode F) rprintf(F," --address=ADDRESS bind address for outgoing socket to daemon\n"); rprintf(F," --port=PORT specify double-colon alternate port number\n"); rprintf(F," --sockopts=OPTIONS specify custom TCP options\n"); + rprintf(F," --diffserv=[0-63] specify diffserv setting \n"); + rprintf(F," --congestion-alg=STRING choose a congestion algo\n"); rprintf(F," --blocking-io use blocking I/O for the remote shell\n"); rprintf(F," --stats give some file-transfer stats\n"); rprintf(F," -8, --8-bit-output leave high-bit chars unescaped in output\n"); @@ -1027,6 +1031,8 @@ static struct poptOption long_options[] = { {"no-blocking-io", 0, POPT_ARG_VAL, &blocking_io, 0, 0, 0 }, {"remote-option", 'M', POPT_ARG_STRING, 0, 'M', 0, 0 }, {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 }, + {"congestion-alg", 0, POPT_ARG_STRING, &congestion_alg, 0, 0, 0 }, + {"diffserv", 0, POPT_ARG_INT, &diffserv, 0, 0, 0 }, {"checksum-seed", 0, POPT_ARG_INT, &checksum_seed, 0, 0, 0 }, {"server", 0, POPT_ARG_NONE, 0, OPT_SERVER, 0, 0 }, {"sender", 0, POPT_ARG_NONE, 0, OPT_SENDER, 0, 0 }, @@ -1054,6 +1060,8 @@ static void daemon_usage(enum logcode F) rprintf(F," --log-file=FILE override the \"log file\" setting\n"); rprintf(F," --log-file-format=FMT override the \"log format\" setting\n"); rprintf(F," --sockopts=OPTIONS specify custom TCP options\n"); + rprintf(F," --diffserv=[0-63] specify diffserv setting \n"); + rprintf(F," --congestion-alg=STRING choose a congestion algo\n"); rprintf(F," -v, --verbose increase verbosity\n"); rprintf(F," -4, --ipv4 prefer IPv4\n"); rprintf(F," -6, --ipv6 prefer IPv6\n"); diff --git a/socket.c b/socket.c index 84f9b0c..4ee901e 100644 --- a/socket.c +++ b/socket.c @@ -38,6 +38,8 @@ extern char *bind_address; extern char *sockopts; extern int default_af_hint; extern int connect_timeout; +extern int diffserv; +extern char *congestion_alg; #ifdef HAVE_SIGACTION static struct sigaction sigact; @@ -166,6 +168,40 @@ static RETSIGTYPE contimeout_handler(UNUSED(int val)) connect_timeout = -1; } +/* Set special socket options + * + * Diffserv is a value in the range of 0-63, and needs to be shifted left + * 2 places AND treated differently for ipv4 and ipv6. + * Setting TCP congestion control is rather Linux specific (at the moment) + * and sends a varying length string to setsockopt rather than an integer + * or character. +*/ + +void set_special_sockopts(int s) +{ +#if defined(TCP_CONGESTION) + if(congestion_alg) { + if(setsockopt(s, SOL_TCP, TCP_CONGESTION, congestion_alg, + strlen(congestion_alg)) == -1) + rprintf(FINFO, "Couldn't set %s congestion algorithm\n", + congestion_alg); + } +#endif + +/* setting the diffserv/tos bits is different on ipv6 and + * ipv4, so we just hammer down on both and ignore the result + * And ipv6 demands an int for v. I didn't write the spec. + */ + if(diffserv) { + int v = (diffserv & 63) <<2; + setsockopt(s,IPPROTO_IP, IP_TOS, &v, sizeof(v)); + +#if defined(IPV6_TCLASS) + setsockopt(s,IPPROTO_IPV6, IPV6_TCLASS, &v, sizeof(v)); +#endif + } +} + /* Open a socket to a tcp remote host with the specified port. * * Based on code from Warren. Proxy support by Stephen Rothwell. @@ -275,6 +311,7 @@ int open_socket_out(char *host, int port, const char *bind_addr, alarm(connect_timeout); } + set_special_sockopts(s); set_socket_options(s, sockopts); while (connect(s, res->ai_addr, res->ai_addrlen) < 0) { if (connect_timeout < 0) @@ -449,6 +486,7 @@ static int *open_socket_in(int type, int port, const char *bind_addr, continue; } + set_special_sockopts(s); setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof one); if (sockopts) -- 1.7.5.4 -- Dave T?ht