# HG changeset patch # User Matt Johnston # Date 1186585926 0 # Node ID 7e43f5e473b929263538aa8932d8443992bd990d # Parent 29953de278ae6d55922b37f52af3e96798635a37 - Add -K keepalive flag for dropbear and dbclient - Try to reduce the frequency of select() timeouts - Add a max receive window size of 1MB diff -r 29953de278ae -r 7e43f5e473b9 cli-runopts.c --- a/cli-runopts.c Sat Jul 28 08:59:24 2007 +0000 +++ b/cli-runopts.c Wed Aug 08 15:12:06 2007 +0000 @@ -63,11 +63,14 @@ #ifdef ENABLE_CLI_REMOTETCPFWD "-R Remote port forwarding\n" #endif - "-W (default %d, larger may be faster)\n" + "-W (default %d, larger may be faster, max 1MB)\n" + "-K (0 is never, default %d)\n" #ifdef DEBUG_TRACE "-v verbose\n" #endif - ,DROPBEAR_VERSION, cli_opts.progname, DEFAULT_RECV_WINDOW); + ,DROPBEAR_VERSION, cli_opts.progname, + DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE); + } void cli_getopts(int argc, char ** argv) { @@ -112,6 +115,7 @@ */ opts.recv_window = DEFAULT_RECV_WINDOW; char* recv_window_arg = NULL; + char* keepalive_arg = NULL; /* Iterate all the arguments */ for (i = 1; i < (unsigned int)argc; i++) { @@ -207,6 +211,9 @@ case 'W': next = &recv_window_arg; break; + case 'K': + next = &keepalive_arg; + break; #ifdef DEBUG_TRACE case 'v': debug_trace = 1; @@ -302,11 +309,19 @@ if (recv_window_arg) { opts.recv_window = atol(recv_window_arg); - if (opts.recv_window == 0) + if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW) { dropbear_exit("Bad recv window '%s'", recv_window_arg); } } + if (keepalive_arg) { + opts.keepalive_secs = strtoul(keepalive_arg, NULL, 10); + if (opts.keepalive_secs == 0 && errno == EINVAL) + { + dropbear_exit("Bad keepalive '%s'", keepalive_arg); + } + } + } #ifdef ENABLE_CLI_PUBKEY_AUTH diff -r 29953de278ae -r 7e43f5e473b9 common-kex.c --- a/common-kex.c Sat Jul 28 08:59:24 2007 +0000 +++ b/common-kex.c Wed Aug 08 15:12:06 2007 +0000 @@ -188,8 +188,6 @@ /* Reset the kex state, ready for a new negotiation */ static void kexinitialise() { - struct timeval tv; - TRACE(("kexinitialise()")) /* sent/recv'd MSG_KEXINIT */ @@ -206,10 +204,7 @@ ses.kexstate.datatrans = 0; ses.kexstate.datarecv = 0; - if (gettimeofday(&tv, 0) < 0) { - dropbear_exit("Error getting time"); - } - ses.kexstate.lastkextime = tv.tv_sec; + ses.kexstate.lastkextime = time(NULL); } diff -r 29953de278ae -r 7e43f5e473b9 common-session.c --- a/common-session.c Sat Jul 28 08:59:24 2007 +0000 +++ b/common-session.c Wed Aug 08 15:12:06 2007 +0000 @@ -34,8 +34,10 @@ #include "kex.h" #include "channel.h" #include "atomicio.h" +#include "runopts.h" static void checktimeouts(); +static long select_timeout(); static int ident_readln(int fd, char* buf, int count); struct sshsession ses; /* GLOBAL */ @@ -59,7 +61,8 @@ ses.sock = sock; ses.maxfd = sock; - ses.connecttimeout = 0; + ses.connect_time = 0; + ses.last_packet_time = 0; if (pipe(ses.signal_pipe) < 0) { dropbear_exit("signal pipe failed"); @@ -129,7 +132,7 @@ /* main loop, select()s for all sockets in use */ for(;;) { - timeout.tv_sec = SELECT_TIMEOUT; + timeout.tv_sec = select_timeout(); timeout.tv_usec = 0; FD_ZERO(&writefd); FD_ZERO(&readfd); @@ -359,20 +362,22 @@ return pos+1; } +void send_msg_ignore() { + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_IGNORE); + buf_putstring(ses.writepayload, "", 0); + encrypt_packet(); +} + /* Check all timeouts which are required. Currently these are the time for * user authentication, and the automatic rekeying. */ static void checktimeouts() { - struct timeval tv; - long secs; + time_t now; - if (gettimeofday(&tv, 0) < 0) { - dropbear_exit("Error getting time"); - } - - secs = tv.tv_sec; + now = time(NULL); - if (ses.connecttimeout != 0 && secs > ses.connecttimeout) { + if (ses.connect_time != 0 && now - ses.connect_time >= AUTH_TIMEOUT) { dropbear_close("Timeout before auth"); } @@ -382,10 +387,27 @@ } if (!ses.kexstate.sentkexinit - && (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT - || ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){ + && (now - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT + || ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)) { TRACE(("rekeying after timeout or max data reached")) send_msg_kexinit(); } + + if (opts.keepalive_secs > 0 + && now - ses.last_packet_time >= opts.keepalive_secs) { + send_msg_ignore(); + } } +static long select_timeout() { + /* determine the minimum timeout that might be required, so + as to avoid waking when unneccessary */ + long ret = LONG_MAX; + if (KEX_REKEY_TIMEOUT > 0) + ret = MIN(KEX_REKEY_TIMEOUT, ret); + if (AUTH_TIMEOUT > 0) + ret = MIN(AUTH_TIMEOUT, ret); + if (opts.keepalive_secs > 0) + ret = MIN(opts.keepalive_secs, ret); + return ret; +} diff -r 29953de278ae -r 7e43f5e473b9 dbclient.1 --- a/dbclient.1 Sat Jul 28 08:59:24 2007 +0000 +++ b/dbclient.1 Wed Aug 08 15:12:06 2007 +0000 @@ -79,6 +79,13 @@ Specify the per-channel receive window buffer size. Increasing this may improve network performance at the expense of memory use. Use -h to see the default buffer size. +.TP +.B \-K \fItimeout_seconds +Ensure that traffic is transmitted at a certain interval in seconds. This is +useful for working around firewalls or routers that drop connections after +a certain period of inactivity. The trade-off is that a session may be +closed if there is a temporary lapse of network connectivity. A setting +if 0 disables keepalives. .SH AUTHOR Matt Johnston (matt@ucc.asn.au). .br diff -r 29953de278ae -r 7e43f5e473b9 dropbear.8 --- a/dropbear.8 Sat Jul 28 08:59:24 2007 +0000 +++ b/dropbear.8 Wed Aug 08 15:12:06 2007 +0000 @@ -87,6 +87,13 @@ Specify the per-channel receive window buffer size. Increasing this may improve network performance at the expense of memory use. Use -h to see the default buffer size. +.TP +.B \-K \fItimeout_seconds +Ensure that traffic is transmitted at a certain interval in seconds. This is +useful for working around firewalls or routers that drop connections after +a certain period of inactivity. The trade-off is that a session may be +closed if there is a temporary lapse of network connectivity. A setting +if 0 disables keepalives. .SH AUTHOR Matt Johnston (matt@ucc.asn.au). .br diff -r 29953de278ae -r 7e43f5e473b9 includes.h --- a/includes.h Sat Jul 28 08:59:24 2007 +0000 +++ b/includes.h Wed Aug 08 15:12:06 2007 +0000 @@ -56,6 +56,7 @@ #include #include #include +#include #ifdef HAVE_UTMP_H #include diff -r 29953de278ae -r 7e43f5e473b9 kex.h --- a/kex.h Sat Jul 28 08:59:24 2007 +0000 +++ b/kex.h Wed Aug 08 15:12:06 2007 +0000 @@ -53,7 +53,7 @@ unsigned donefirstkex : 1; /* Set to 1 after the first kex has completed, ie the transport layer has been set up */ - long lastkextime; /* time of the last kex */ + time_t lastkextime; /* time of the last kex */ unsigned int datatrans; /* data transmitted since last kex */ unsigned int datarecv; /* data received since last kex */ diff -r 29953de278ae -r 7e43f5e473b9 options.h --- a/options.h Sat Jul 28 08:59:24 2007 +0000 +++ b/options.h Wed Aug 08 15:12:06 2007 +0000 @@ -231,6 +231,9 @@ though increasing it may not make a significant difference. */ #define TRANS_MAX_PAYLOAD_LEN 16384 +/* Ensure that data is transmitted every KEEPALIVE seconds. This can +be overridden at runtime with -K. 0 disables keepalives */ +#define DEFAULT_KEEPALIVE 0 /******************************************************************* * You shouldn't edit below here unless you know you need to. @@ -287,9 +290,6 @@ #define _PATH_CP "/bin/cp" -/* Timeouts in seconds */ -#define SELECT_TIMEOUT 20 - /* success/failure defines */ #define DROPBEAR_SUCCESS 0 #define DROPBEAR_FAILURE -1 @@ -343,6 +343,7 @@ #define RECV_WINDOWEXTEND (opts.recv_window / 3) /* We send a "window extend" every RECV_WINDOWEXTEND bytes */ +#define MAX_RECV_WINDOW (1024*1024) /* 1 MB should be enough */ #define MAX_CHANNELS 100 /* simple mem restriction, includes each tcp/x11 connection, so can't be _too_ small */ diff -r 29953de278ae -r 7e43f5e473b9 packet.c --- a/packet.c Sat Jul 28 08:59:24 2007 +0000 +++ b/packet.c Wed Aug 08 15:12:06 2007 +0000 @@ -71,6 +71,8 @@ dropbear_exit("error writing"); } } + + ses.last_packet_time = time(NULL); if (written == 0) { ses.remoteclosed(); diff -r 29953de278ae -r 7e43f5e473b9 process-packet.c --- a/process-packet.c Sat Jul 28 08:59:24 2007 +0000 +++ b/process-packet.c Wed Aug 08 15:12:06 2007 +0000 @@ -56,8 +56,8 @@ switch(type) { case SSH_MSG_IGNORE: + goto out; case SSH_MSG_DEBUG: - TRACE(("received SSH_MSG_IGNORE or SSH_MSG_DEBUG")) goto out; case SSH_MSG_UNIMPLEMENTED: diff -r 29953de278ae -r 7e43f5e473b9 runopts.h --- a/runopts.h Sat Jul 28 08:59:24 2007 +0000 +++ b/runopts.h Wed Aug 08 15:12:06 2007 +0000 @@ -37,6 +37,7 @@ int listen_fwd_all; #endif unsigned int recv_window; + time_t keepalive_secs; } runopts; diff -r 29953de278ae -r 7e43f5e473b9 session.h --- a/session.h Sat Jul 28 08:59:24 2007 +0000 +++ b/session.h Wed Aug 08 15:12:06 2007 +0000 @@ -45,6 +45,7 @@ void session_loop(void(*loophandler)()); void common_session_cleanup(); void session_identification(); +void send_msg_ignore(); /* Server */ @@ -92,8 +93,9 @@ /* Is it a client or server? */ unsigned char isserver; - long connecttimeout; /* time to disconnect if we have a timeout (for - userauth etc), or 0 for no timeout */ + time_t connect_time; /* time the connection was established + (cleared after auth once we're not + respecting AUTH_TIMEOUT any more) */ int sock; @@ -131,6 +133,9 @@ int signal_pipe[2]; /* stores endpoints of a self-pipe used for race-free signal handling */ + + time_t last_packet_time; /* time of the last packet transmission, for + keepalive purposes */ /* KEX/encryption related */ struct KEXState kexstate; diff -r 29953de278ae -r 7e43f5e473b9 svr-auth.c --- a/svr-auth.c Sat Jul 28 08:59:24 2007 +0000 +++ b/svr-auth.c Wed Aug 08 15:12:06 2007 +0000 @@ -357,7 +357,7 @@ encrypt_packet(); ses.authstate.authdone = 1; - ses.connecttimeout = 0; + ses.connect_time = 0; if (ses.authstate.pw->pw_uid == 0) { diff -r 29953de278ae -r 7e43f5e473b9 svr-main.c --- a/svr-main.c Sat Jul 28 08:59:24 2007 +0000 +++ b/svr-main.c Wed Aug 08 15:12:06 2007 +0000 @@ -111,7 +111,6 @@ #ifdef NON_INETD_MODE void main_noinetd() { fd_set fds; - struct timeval seltimeout; unsigned int i, j; int val; int maxsock = -1; @@ -175,9 +174,6 @@ FD_ZERO(&fds); - seltimeout.tv_sec = 60; - seltimeout.tv_usec = 0; - /* listening sockets */ for (i = 0; i < listensockcount; i++) { FD_SET(listensocks[i], &fds); @@ -191,7 +187,7 @@ } } - val = select(maxsock+1, &fds, NULL, NULL, &seltimeout); + val = select(maxsock+1, &fds, NULL, NULL, NULL); if (exitflag) { unlink(svr_opts.pidfile); @@ -199,7 +195,7 @@ } if (val == 0) { - /* timeout reached */ + /* timeout reached - shouldn't happen. eh */ continue; } diff -r 29953de278ae -r 7e43f5e473b9 svr-runopts.c --- a/svr-runopts.c Sat Jul 28 08:59:24 2007 +0000 +++ b/svr-runopts.c Wed Aug 08 15:12:06 2007 +0000 @@ -80,7 +80,8 @@ #ifdef INETD_MODE "-i Start for inetd\n" #endif - "-W (default %d, larger may be faster)\n" + "-W (default %d, larger may be faster, max 1MB)\n" + "-K (0 is never, default %d)\n" #ifdef DEBUG_TRACE "-v verbose\n" #endif @@ -91,7 +92,8 @@ #ifdef DROPBEAR_RSA RSA_PRIV_FILENAME, #endif - DROPBEAR_MAX_PORTS, DROPBEAR_DEFPORT, DROPBEAR_PIDFILE, DEFAULT_RECV_WINDOW); + DROPBEAR_MAX_PORTS, DROPBEAR_DEFPORT, DROPBEAR_PIDFILE, + DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE); } void svr_getopts(int argc, char ** argv) { @@ -99,6 +101,8 @@ unsigned int i; char ** next = 0; int nextisport = 0; + char* recv_window_arg = NULL; + char* keepalive_arg = NULL; /* see printhelp() for options */ svr_opts.rsakeyfile = NULL; @@ -130,7 +134,8 @@ svr_opts.usingsyslog = 1; #endif opts.recv_window = DEFAULT_RECV_WINDOW; - char* recv_window_arg = NULL; + opts.keepalive_secs = DEFAULT_KEEPALIVE; + #ifdef ENABLE_SVR_REMOTETCPFWD opts.listen_fwd_all = 0; #endif @@ -210,6 +215,9 @@ case 'W': next = &recv_window_arg; break; + case 'K': + next = &keepalive_arg; + break; #if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH) case 's': svr_opts.noauthpass = 1; @@ -274,14 +282,21 @@ } - if (recv_window_arg) - { + if (recv_window_arg) { opts.recv_window = atol(recv_window_arg); - if (opts.recv_window == 0) + if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW) { dropbear_exit("Bad recv window '%s'", recv_window_arg); } } + + if (keepalive_arg) { + opts.keepalive_secs = strtoul(keepalive_arg, NULL, 10); + if (opts.keepalive_secs == 0 && errno == EINVAL) + { + dropbear_exit("Bad keepalive '%s'", keepalive_arg); + } + } } static void addportandaddress(char* spec) { diff -r 29953de278ae -r 7e43f5e473b9 svr-session.c --- a/svr-session.c Sat Jul 28 08:59:24 2007 +0000 +++ b/svr-session.c Wed Aug 08 15:12:06 2007 +0000 @@ -77,8 +77,6 @@ void svr_session(int sock, int childpipe, char* remotehost, char *addrstring) { - struct timeval timeout; - reseedrandom(); crypto_init(); @@ -91,11 +89,7 @@ chaninitialise(svr_chantypes); svr_chansessinitialise(); - if (gettimeofday(&timeout, 0) < 0) { - dropbear_exit("Error getting time"); - } - - ses.connecttimeout = timeout.tv_sec + AUTH_TIMEOUT; + ses.connect_time = time(NULL); /* set up messages etc */ ses.remoteclosed = svr_remoteclosed;