mirror of
https://github.com/clearml/dropbear
synced 2025-04-08 06:34:23 +00:00
Make -K keepalive behave like OpenSSH's ServerAliveInterval
This commit is contained in:
parent
1ccac01cee
commit
c884e5000e
@ -163,6 +163,8 @@ void cli_getopts(int argc, char ** argv) {
|
|||||||
opts.ipv6 = 1;
|
opts.ipv6 = 1;
|
||||||
*/
|
*/
|
||||||
opts.recv_window = DEFAULT_RECV_WINDOW;
|
opts.recv_window = DEFAULT_RECV_WINDOW;
|
||||||
|
opts.keepalive_secs = DEFAULT_KEEPALIVE;
|
||||||
|
opts.idle_timeout_secs = DEFAULT_IDLE_TIMEOUT;
|
||||||
|
|
||||||
fill_own_user();
|
fill_own_user();
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ int exitflag = 0; /* GLOBAL */
|
|||||||
|
|
||||||
/* called only at the start of a session, set up initial state */
|
/* called only at the start of a session, set up initial state */
|
||||||
void common_session_init(int sock_in, int sock_out) {
|
void common_session_init(int sock_in, int sock_out) {
|
||||||
|
time_t now;
|
||||||
|
|
||||||
TRACE(("enter session_init"))
|
TRACE(("enter session_init"))
|
||||||
|
|
||||||
@ -58,9 +59,12 @@ void common_session_init(int sock_in, int sock_out) {
|
|||||||
ses.sock_out = sock_out;
|
ses.sock_out = sock_out;
|
||||||
ses.maxfd = MAX(sock_in, sock_out);
|
ses.maxfd = MAX(sock_in, sock_out);
|
||||||
|
|
||||||
ses.connect_time = 0;
|
now = monotonic_now();
|
||||||
ses.last_trx_packet_time = 0;
|
ses.connect_time = now;
|
||||||
ses.last_packet_time = 0;
|
ses.last_packet_time_keepalive_recv = now;
|
||||||
|
ses.last_packet_time_idle = now;
|
||||||
|
ses.last_packet_time_any_sent = 0;
|
||||||
|
ses.last_packet_time_keepalive_sent = 0;
|
||||||
|
|
||||||
if (pipe(ses.signal_pipe) < 0) {
|
if (pipe(ses.signal_pipe) < 0) {
|
||||||
dropbear_exit("Signal pipe failed");
|
dropbear_exit("Signal pipe failed");
|
||||||
@ -387,11 +391,21 @@ static int ident_readln(int fd, char* buf, int count) {
|
|||||||
return pos+1;
|
return pos+1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_msg_ignore() {
|
static void send_msg_keepalive() {
|
||||||
CHECKCLEARTOWRITE();
|
CHECKCLEARTOWRITE();
|
||||||
buf_putbyte(ses.writepayload, SSH_MSG_IGNORE);
|
time_t old_time_idle = ses.last_packet_time_idle;
|
||||||
buf_putstring(ses.writepayload, "", 0);
|
/* Try to force a response from the other end. Some peers will
|
||||||
|
reply with SSH_MSG_REQUEST_FAILURE, some will reply with SSH_MSG_UNIMPLEMENTED */
|
||||||
|
buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST);
|
||||||
|
/* A short string */
|
||||||
|
buf_putstring(ses.writepayload, "k@dropbear.nl", 0);
|
||||||
|
buf_putbyte(ses.writepayload, 1); /* want_reply */
|
||||||
encrypt_packet();
|
encrypt_packet();
|
||||||
|
|
||||||
|
ses.last_packet_time_keepalive_sent = monotonic_now();
|
||||||
|
|
||||||
|
/* keepalives shouldn't update idle timeout, reset it back */
|
||||||
|
ses.last_packet_time_idle = old_time_idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check all timeouts which are required. Currently these are the time for
|
/* Check all timeouts which are required. Currently these are the time for
|
||||||
@ -401,7 +415,7 @@ static void checktimeouts() {
|
|||||||
time_t now;
|
time_t now;
|
||||||
now = monotonic_now();
|
now = monotonic_now();
|
||||||
|
|
||||||
if (ses.connect_time != 0 && now - ses.connect_time >= AUTH_TIMEOUT) {
|
if (now - ses.connect_time >= AUTH_TIMEOUT) {
|
||||||
dropbear_close("Timeout before auth");
|
dropbear_close("Timeout before auth");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,13 +431,27 @@ static void checktimeouts() {
|
|||||||
send_msg_kexinit();
|
send_msg_kexinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.keepalive_secs > 0
|
if (opts.keepalive_secs > 0) {
|
||||||
&& now - ses.last_trx_packet_time >= opts.keepalive_secs) {
|
/* Send keepalives if we've been idle */
|
||||||
send_msg_ignore();
|
if (now - ses.last_packet_time_any_sent >= opts.keepalive_secs) {
|
||||||
|
send_msg_keepalive();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Also send an explicit keepalive message to trigger a response
|
||||||
|
if the remote end hasn't sent us anything */
|
||||||
|
if (now - ses.last_packet_time_keepalive_recv >= opts.keepalive_secs
|
||||||
|
&& now - ses.last_packet_time_keepalive_sent >= opts.keepalive_secs) {
|
||||||
|
send_msg_keepalive();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now - ses.last_packet_time_keepalive_recv
|
||||||
|
>= opts.keepalive_secs * DEFAULT_KEEPALIVE_LIMIT) {
|
||||||
|
dropbear_exit("Keepalive timeout");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.idle_timeout_secs > 0 && ses.last_packet_time > 0
|
if (opts.idle_timeout_secs > 0
|
||||||
&& now - ses.last_packet_time >= opts.idle_timeout_secs) {
|
&& now - ses.last_packet_time_idle >= opts.idle_timeout_secs) {
|
||||||
dropbear_close("Idle timeout");
|
dropbear_close("Idle timeout");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,6 +308,11 @@ much traffic. */
|
|||||||
be overridden at runtime with -K. 0 disables keepalives */
|
be overridden at runtime with -K. 0 disables keepalives */
|
||||||
#define DEFAULT_KEEPALIVE 0
|
#define DEFAULT_KEEPALIVE 0
|
||||||
|
|
||||||
|
/* If this many KEEPALIVES are sent with no packets received from the
|
||||||
|
other side, exit. Not run-time configurable - if you have a need
|
||||||
|
for runtime configuration please mail the Dropbear list */
|
||||||
|
#define DEFAULT_KEEPALIVE_LIMIT 3
|
||||||
|
|
||||||
/* Ensure that data is received within IDLE_TIMEOUT seconds. This can
|
/* Ensure that data is received within IDLE_TIMEOUT seconds. This can
|
||||||
be overridden at runtime with -I. 0 disables idle timeouts */
|
be overridden at runtime with -I. 0 disables idle timeouts */
|
||||||
#define DEFAULT_IDLE_TIMEOUT 0
|
#define DEFAULT_IDLE_TIMEOUT 0
|
||||||
|
25
packet.c
25
packet.c
@ -57,9 +57,7 @@ void write_packet() {
|
|||||||
|
|
||||||
int len, written;
|
int len, written;
|
||||||
buffer * writebuf = NULL;
|
buffer * writebuf = NULL;
|
||||||
time_t now;
|
|
||||||
unsigned packet_type;
|
unsigned packet_type;
|
||||||
int all_ignore = 1;
|
|
||||||
#ifdef HAVE_WRITEV
|
#ifdef HAVE_WRITEV
|
||||||
struct iovec *iov = NULL;
|
struct iovec *iov = NULL;
|
||||||
int i;
|
int i;
|
||||||
@ -90,7 +88,6 @@ void write_packet() {
|
|||||||
packet_type = writebuf->data[writebuf->len-1];
|
packet_type = writebuf->data[writebuf->len-1];
|
||||||
len = writebuf->len - 1 - writebuf->pos;
|
len = writebuf->len - 1 - writebuf->pos;
|
||||||
dropbear_assert(len > 0);
|
dropbear_assert(len > 0);
|
||||||
all_ignore &= (packet_type == SSH_MSG_IGNORE);
|
|
||||||
TRACE2(("write_packet writev #%d type %d len %d/%d", i, packet_type,
|
TRACE2(("write_packet writev #%d type %d len %d/%d", i, packet_type,
|
||||||
len, writebuf->len-1))
|
len, writebuf->len-1))
|
||||||
iov[i].iov_base = buf_getptr(writebuf, len);
|
iov[i].iov_base = buf_getptr(writebuf, len);
|
||||||
@ -146,7 +143,6 @@ void write_packet() {
|
|||||||
dropbear_exit("Error writing: %s", strerror(errno));
|
dropbear_exit("Error writing: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
all_ignore = (packet_type == SSH_MSG_IGNORE);
|
|
||||||
|
|
||||||
if (written == 0) {
|
if (written == 0) {
|
||||||
ses.remoteclosed();
|
ses.remoteclosed();
|
||||||
@ -163,13 +159,6 @@ void write_packet() {
|
|||||||
}
|
}
|
||||||
#endif /* writev */
|
#endif /* writev */
|
||||||
|
|
||||||
now = monotonic_now();
|
|
||||||
ses.last_trx_packet_time = now;
|
|
||||||
|
|
||||||
if (!all_ignore) {
|
|
||||||
ses.last_packet_time = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE2(("leave write_packet"))
|
TRACE2(("leave write_packet"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,6 +504,8 @@ void encrypt_packet() {
|
|||||||
unsigned char packet_type;
|
unsigned char packet_type;
|
||||||
unsigned int len, encrypt_buf_size;
|
unsigned int len, encrypt_buf_size;
|
||||||
unsigned char mac_bytes[MAX_MAC_LEN];
|
unsigned char mac_bytes[MAX_MAC_LEN];
|
||||||
|
|
||||||
|
time_t now;
|
||||||
|
|
||||||
TRACE2(("enter encrypt_packet()"))
|
TRACE2(("enter encrypt_packet()"))
|
||||||
|
|
||||||
@ -622,6 +613,18 @@ void encrypt_packet() {
|
|||||||
ses.kexstate.datatrans += writebuf->len;
|
ses.kexstate.datatrans += writebuf->len;
|
||||||
ses.transseq++;
|
ses.transseq++;
|
||||||
|
|
||||||
|
now = monotonic_now();
|
||||||
|
ses.last_packet_time_any_sent = now;
|
||||||
|
/* idle timeout shouldn't be affected by responses to keepalives.
|
||||||
|
send_msg_keepalive() itself also does tricks with
|
||||||
|
ses.last_packet_idle_time - read that if modifying this code */
|
||||||
|
if (packet_type != SSH_MSG_REQUEST_FAILURE
|
||||||
|
&& packet_type != SSH_MSG_UNIMPLEMENTED
|
||||||
|
&& packet_type != SSH_MSG_IGNORE) {
|
||||||
|
ses.last_packet_time_idle = now;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
TRACE2(("leave encrypt_packet()"))
|
TRACE2(("leave encrypt_packet()"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ void process_packet() {
|
|||||||
|
|
||||||
unsigned char type;
|
unsigned char type;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
time_t now;
|
||||||
|
|
||||||
TRACE2(("enter process_packet"))
|
TRACE2(("enter process_packet"))
|
||||||
|
|
||||||
@ -52,7 +53,8 @@ void process_packet() {
|
|||||||
|
|
||||||
ses.lastpacket = type;
|
ses.lastpacket = type;
|
||||||
|
|
||||||
ses.last_packet_time = monotonic_now();
|
now = monotonic_now();
|
||||||
|
ses.last_packet_time_keepalive_recv = now;
|
||||||
|
|
||||||
/* These packets we can receive at any time */
|
/* These packets we can receive at any time */
|
||||||
switch(type) {
|
switch(type) {
|
||||||
@ -65,13 +67,21 @@ void process_packet() {
|
|||||||
case SSH_MSG_UNIMPLEMENTED:
|
case SSH_MSG_UNIMPLEMENTED:
|
||||||
/* debugging XXX */
|
/* debugging XXX */
|
||||||
TRACE(("SSH_MSG_UNIMPLEMENTED"))
|
TRACE(("SSH_MSG_UNIMPLEMENTED"))
|
||||||
dropbear_exit("Received SSH_MSG_UNIMPLEMENTED");
|
goto out;
|
||||||
|
|
||||||
case SSH_MSG_DISCONNECT:
|
case SSH_MSG_DISCONNECT:
|
||||||
/* TODO cleanup? */
|
/* TODO cleanup? */
|
||||||
dropbear_close("Disconnect received");
|
dropbear_close("Disconnect received");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ignore these packet types so that keepalives don't interfere with
|
||||||
|
idle detection. This is slightly incorrect since a tcp forwarded
|
||||||
|
global request with failure won't trigger the idle timeout,
|
||||||
|
but that's probably acceptable */
|
||||||
|
if (!(type == SSH_MSG_GLOBAL_REQUEST || type == SSH_MSG_REQUEST_FAILURE)) {
|
||||||
|
ses.last_packet_time_idle = now;
|
||||||
|
}
|
||||||
|
|
||||||
/* This applies for KEX, where the spec says the next packet MUST be
|
/* This applies for KEX, where the spec says the next packet MUST be
|
||||||
* NEWKEYS */
|
* NEWKEYS */
|
||||||
if (ses.requirenext != 0) {
|
if (ses.requirenext != 0) {
|
||||||
|
@ -37,8 +37,8 @@ typedef struct runopts {
|
|||||||
int listen_fwd_all;
|
int listen_fwd_all;
|
||||||
#endif
|
#endif
|
||||||
unsigned int recv_window;
|
unsigned int recv_window;
|
||||||
time_t keepalive_secs;
|
time_t keepalive_secs; /* Time between sending keepalives. 0 is off */
|
||||||
time_t idle_timeout_secs;
|
time_t idle_timeout_secs; /* Exit if no traffic is sent/received in this time */
|
||||||
|
|
||||||
#ifndef DISABLE_ZLIB
|
#ifndef DISABLE_ZLIB
|
||||||
/* TODO: add a commandline flag. Currently this is on by default if compression
|
/* TODO: add a commandline flag. Currently this is on by default if compression
|
||||||
|
11
session.h
11
session.h
@ -147,11 +147,14 @@ struct sshsession {
|
|||||||
int signal_pipe[2]; /* stores endpoints of a self-pipe used for
|
int signal_pipe[2]; /* stores endpoints of a self-pipe used for
|
||||||
race-free signal handling */
|
race-free signal handling */
|
||||||
|
|
||||||
time_t last_trx_packet_time; /* time of the last packet transmission, for
|
/* time of the last packet send/receive, for keepalive. Not real-world clock */
|
||||||
keepalive purposes. Not real-world clock */
|
time_t last_packet_time_keepalive_sent;
|
||||||
|
time_t last_packet_time_keepalive_recv;
|
||||||
|
time_t last_packet_time_any_sent;
|
||||||
|
|
||||||
time_t last_packet_time; /* time of the last packet transmission or receive, for
|
time_t last_packet_time_idle; /* time of the last packet transmission or receive, for
|
||||||
idle timeout purposes. Not real-world clock */
|
idle timeout purposes so ignores SSH_MSG_IGNORE
|
||||||
|
or responses to keepalives. Not real-world clock */
|
||||||
|
|
||||||
|
|
||||||
/* KEX/encryption related */
|
/* KEX/encryption related */
|
||||||
|
@ -95,8 +95,6 @@ void svr_session(int sock, int childpipe) {
|
|||||||
chaninitialise(svr_chantypes);
|
chaninitialise(svr_chantypes);
|
||||||
svr_chansessinitialise();
|
svr_chansessinitialise();
|
||||||
|
|
||||||
ses.connect_time = monotonic_now();
|
|
||||||
|
|
||||||
/* for logging the remote address */
|
/* for logging the remote address */
|
||||||
get_socket_address(ses.sock_in, NULL, NULL, &host, &port, 0);
|
get_socket_address(ses.sock_in, NULL, NULL, &host, &port, 0);
|
||||||
len = strlen(host) + strlen(port) + 2;
|
len = strlen(host) + strlen(port) + 2;
|
||||||
|
Loading…
Reference in New Issue
Block a user