diff options
author | Matt Johnston <matt@ucc.asn.au> | 2007-08-08 15:12:06 +0000 |
---|---|---|
committer | Matt Johnston <matt@ucc.asn.au> | 2007-08-08 15:12:06 +0000 |
commit | 75ec4d6510aba8780effddb53eee4a0296015608 (patch) | |
tree | cdbb9481b94425022d83d4b00bd2ef6fcfc7dd4b | |
parent | a7649c250fccd3a901488dc5135c2eca4cc56ef1 (diff) |
- Add -K keepalive flag for dropbear and dbclient
- Try to reduce the frequency of select() timeouts
- Add a max receive window size of 1MB
--HG--
extra : convert_revision : 9aa22036cb511cddb35fbc0e09ad05acb39b64d1
-rw-r--r-- | cli-runopts.c | 21 | ||||
-rw-r--r-- | common-kex.c | 7 | ||||
-rw-r--r-- | common-session.c | 46 | ||||
-rw-r--r-- | dbclient.1 | 7 | ||||
-rw-r--r-- | dropbear.8 | 7 | ||||
-rw-r--r-- | includes.h | 1 | ||||
-rw-r--r-- | kex.h | 2 | ||||
-rw-r--r-- | options.h | 7 | ||||
-rw-r--r-- | packet.c | 2 | ||||
-rw-r--r-- | process-packet.c | 2 | ||||
-rw-r--r-- | runopts.h | 1 | ||||
-rw-r--r-- | session.h | 9 | ||||
-rw-r--r-- | svr-auth.c | 2 | ||||
-rw-r--r-- | svr-main.c | 8 | ||||
-rw-r--r-- | svr-runopts.c | 27 | ||||
-rw-r--r-- | svr-session.c | 8 |
16 files changed, 109 insertions, 48 deletions
diff --git a/cli-runopts.c b/cli-runopts.c index f38ccd2..68990fa 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -63,11 +63,14 @@ static void printhelp() { #ifdef ENABLE_CLI_REMOTETCPFWD "-R <listenport:remotehost:remoteport> Remote port forwarding\n" #endif - "-W <receive_window_buffer> (default %d, larger may be faster)\n" + "-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n" + "-K <keepalive> (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 @@ void cli_getopts(int argc, char ** argv) { */ 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 @@ void cli_getopts(int argc, char ** argv) { 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 @@ void cli_getopts(int argc, char ** argv) { 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 --git a/common-kex.c b/common-kex.c index dd36cd1..e9c655d 100644 --- a/common-kex.c +++ b/common-kex.c @@ -188,8 +188,6 @@ void kexfirstinitialise() { /* 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 @@ static void kexinitialise() { 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 --git a/common-session.c b/common-session.c index 9b248cf..79313f2 100644 --- a/common-session.c +++ b/common-session.c @@ -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 @@ void common_session_init(int sock, char* remotehost) { 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 @@ void session_loop(void(*loophandler)()) { /* 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 @@ static int ident_readln(int fd, char* buf, int count) { 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; - - if (gettimeofday(&tv, 0) < 0) { - dropbear_exit("Error getting time"); - } + time_t now; - 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 @@ static void checktimeouts() { } 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; +} @@ -79,6 +79,13 @@ connection will abort as normal. 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 @@ -87,6 +87,13 @@ Allow remote hosts to connect to forwarded ports. 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 @@ -56,6 +56,7 @@ #include <ctype.h> #include <stdarg.h> #include <dirent.h> +#include <time.h> #ifdef HAVE_UTMP_H #include <utmp.h> @@ -53,7 +53,7 @@ struct KEXState { 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 */ @@ -231,6 +231,9 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */ 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 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */ #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 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */ #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 */ @@ -71,6 +71,8 @@ void write_packet() { dropbear_exit("error writing"); } } + + ses.last_packet_time = time(NULL); if (written == 0) { ses.remoteclosed(); diff --git a/process-packet.c b/process-packet.c index ba39d9f..d96c1cb 100644 --- a/process-packet.c +++ b/process-packet.c @@ -56,8 +56,8 @@ void process_packet() { 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: @@ -37,6 +37,7 @@ typedef struct runopts { int listen_fwd_all; #endif unsigned int recv_window; + time_t keepalive_secs; } runopts; @@ -45,6 +45,7 @@ void common_session_init(int sock, char* remotehost); void session_loop(void(*loophandler)()); void common_session_cleanup(); void session_identification(); +void send_msg_ignore(); /* Server */ @@ -92,8 +93,9 @@ struct sshsession { /* 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 @@ struct sshsession { 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; @@ -357,7 +357,7 @@ void send_msg_userauth_success() { encrypt_packet(); ses.authstate.authdone = 1; - ses.connecttimeout = 0; + ses.connect_time = 0; if (ses.authstate.pw->pw_uid == 0) { @@ -111,7 +111,6 @@ static void main_inetd() { #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 @@ void main_noinetd() { 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 @@ void main_noinetd() { } } - 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 @@ void main_noinetd() { } if (val == 0) { - /* timeout reached */ + /* timeout reached - shouldn't happen. eh */ continue; } diff --git a/svr-runopts.c b/svr-runopts.c index f78461b..83c75c3 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -80,7 +80,8 @@ static void printhelp(const char * progname) { #ifdef INETD_MODE "-i Start for inetd\n" #endif - "-W <receive_window_buffer> (default %d, larger may be faster)\n" + "-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n" + "-K <keepalive> (0 is never, default %d)\n" #ifdef DEBUG_TRACE "-v verbose\n" #endif @@ -91,7 +92,8 @@ static void printhelp(const char * progname) { #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 @@ void svr_getopts(int argc, char ** argv) { 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 @@ void svr_getopts(int argc, char ** argv) { 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 @@ void svr_getopts(int argc, char ** argv) { 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 @@ void svr_getopts(int argc, char ** argv) { } - 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 --git a/svr-session.c b/svr-session.c index 3701597..18fab6b 100644 --- a/svr-session.c +++ b/svr-session.c @@ -77,8 +77,6 @@ static const struct ChanType *svr_chantypes[] = { void svr_session(int sock, int childpipe, char* remotehost, char *addrstring) { - struct timeval timeout; - reseedrandom(); crypto_init(); @@ -91,11 +89,7 @@ void svr_session(int sock, int childpipe, 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; |