Fix keepalive bug

This commit is contained in:
Nicolas Viennot 2019-11-10 03:44:37 -05:00
parent 2b14611544
commit 2b86031308
3 changed files with 50 additions and 26 deletions

View File

@ -36,6 +36,9 @@ static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr)
struct evutil_addrinfo *ai; struct evutil_addrinfo *ai;
const char *host = ptr; const char *host = ptr;
evdns_base_free(tmate_session.ev_dnsbase, 0);
tmate_session.ev_dnsbase = NULL;
if (errcode) { if (errcode) {
struct tmate_session *session = &tmate_session; struct tmate_session *session = &tmate_session;
@ -81,9 +84,6 @@ static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr)
connect_ssh_client(ssh_clients[i]); connect_ssh_client(ssh_clients[i]);
evutil_freeaddrinfo(addr); evutil_freeaddrinfo(addr);
evdns_base_free(tmate_session.ev_dnsbase, 0);
tmate_session.ev_dnsbase = NULL;
} }
static void lookup_and_connect(void) static void lookup_and_connect(void)

View File

@ -167,57 +167,84 @@ static void request_passphrase(struct tmate_ssh_client *client)
data->password_cb_private = client; data->password_cb_private = client;
} }
#define KEEPALIVE_CNT 3 #define KEEPALIVE_IDLE 30
#define KEEPALIVE_IDLE 20 #define KEEPALIVE_CNT 4
#define KEEPALIVE_INTVL 10 #define KEEPALIVE_INTVL 11
#define WRITE_TIMEOUT 80
static void tune_socket_opts(int fd) static void tune_socket_opts(int fd)
{ {
#define SSO(level, optname, val) ({ \ #define SSO(level, optname, val) ({ \
int _flag = val; \ int _flag = val; \
if (setsockopt(fd, level, optname, &(_flag), sizeof(int)) < 0) { \ if (setsockopt(fd, level, optname, &(_flag), sizeof(int)) < 0) { \
tmate_debug("setsockopt(" #level ", " #optname ", %d) failed", val); \ tmate_info("setsockopt(" #level ", " #optname ", %d) failed", val); \
} \ } \
}) })
SSO(IPPROTO_TCP, TCP_NODELAY, 1); SSO(IPPROTO_TCP, TCP_NODELAY, 1);
SSO(SOL_SOCKET, SO_KEEPALIVE, 1); SSO(SOL_SOCKET, SO_KEEPALIVE, 1);
#ifdef TCP_KEEPALIVE #ifdef TCP_KEEPALIVE
/*
* The TCP_KEEPALIVE options enable to specify the amount of time, in
* seconds, that the connection must be idle before keepalive probes
* (if enabled) are sent.
*/
SSO(IPPROTO_TCP, TCP_KEEPALIVE, KEEPALIVE_IDLE); SSO(IPPROTO_TCP, TCP_KEEPALIVE, KEEPALIVE_IDLE);
#endif #endif
#ifdef TCP_KEEPCNT
SSO(IPPROTO_TCP, TCP_KEEPCNT, KEEPALIVE_CNT);
#endif
#ifdef TCP_KEEPIDLE #ifdef TCP_KEEPIDLE
/*
* Same as TCP_KEEPALIVE, but on different systems
*/
SSO(IPPROTO_TCP, TCP_KEEPIDLE, KEEPALIVE_IDLE); SSO(IPPROTO_TCP, TCP_KEEPIDLE, KEEPALIVE_IDLE);
#endif #endif
#ifdef TCP_KEEPCNT
/*
* When keepalive probes are enabled, this option will set the number
* of times a keepalive probe should be repeated if the peer is not
* responding. After this many probes, the connection will be closed.
*/
SSO(IPPROTO_TCP, TCP_KEEPCNT, KEEPALIVE_CNT);
#endif
#ifdef TCP_KEEPINTVL #ifdef TCP_KEEPINTVL
/*
* When keepalive probes are enabled, this option will set the amount
* of time in seconds between successive keepalives sent to probe an
* unresponsive peer.
*/
SSO(IPPROTO_TCP, TCP_KEEPINTVL, KEEPALIVE_INTVL); SSO(IPPROTO_TCP, TCP_KEEPINTVL, KEEPALIVE_INTVL);
#endif #endif
#ifdef TCP_USER_TIMEOUT
/*
* This option takes an unsigned int as an argument. When the
* value is greater than 0, it specifies the maximum amount of
* time in milliseconds that transmitted data may remain
* unacknowledged before TCP will forcibly close the
* corresponding connection and return ETIMEDOUT to the
* application.
*/
SSO(IPPROTO_TCP, TCP_USER_TIMEOUT, 1000*WRITE_TIMEOUT);
#endif
#undef SSO #undef SSO
} }
static void init_conn_fd(struct tmate_ssh_client *client, bool tune_socket) static void init_conn_fd(struct tmate_ssh_client *client)
{ {
int fd; int fd;
if (client->has_init_conn_fd) if (client->ev_ssh)
return; return;
if ((fd = ssh_get_fd(client->session)) < 0) if ((fd = ssh_get_fd(client->session)) < 0)
return; return;
if (tune_socket)
tune_socket_opts(fd); tune_socket_opts(fd);
assert(!client->ev_ssh); client->ev_ssh = event_new(client->tmate_session->ev_base, fd,
client->ev_ssh = event_new(client->tmate_session->ev_base, EV_READ | EV_PERSIST,
fd, EV_READ | EV_PERSIST, __on_ssh_client_event, client); __on_ssh_client_event, client);
if (!client->ev_ssh) if (!client->ev_ssh)
tmate_fatal("out of memory"); tmate_fatal("out of memory");
event_add(client->ev_ssh, NULL); event_add(client->ev_ssh, NULL);
client->has_init_conn_fd = true;
} }
static void on_ssh_client_event(struct tmate_ssh_client *client) static void on_ssh_client_event(struct tmate_ssh_client *client)
@ -273,14 +300,14 @@ static void on_ssh_client_event(struct tmate_ssh_client *client)
case SSH_CONNECT: case SSH_CONNECT:
switch (ssh_connect(session)) { switch (ssh_connect(session)) {
case SSH_AGAIN: case SSH_AGAIN:
init_conn_fd(client, false); init_conn_fd(client);
return; return;
case SSH_ERROR: case SSH_ERROR:
kill_ssh_client(client, "Error connecting: %s", kill_ssh_client(client, "Error connecting: %s",
ssh_get_error(session)); ssh_get_error(session));
return; return;
case SSH_OK: case SSH_OK:
init_conn_fd(client, true); init_conn_fd(client);
tmate_debug("Establishing connection to %s", client->server_ip); tmate_debug("Establishing connection to %s", client->server_ip);
client->state = SSH_AUTH_SERVER; client->state = SSH_AUTH_SERVER;
@ -482,10 +509,10 @@ static void kill_ssh_client(struct tmate_ssh_client *client,
tmate_debug("SSH client killed (%s)", client->server_ip); tmate_debug("SSH client killed (%s)", client->server_ip);
if (client->has_init_conn_fd) { if (client->ev_ssh) {
event_del(client->ev_ssh); event_del(client->ev_ssh);
event_free(client->ev_ssh); event_free(client->ev_ssh);
client->has_init_conn_fd = false; client->ev_ssh = NULL;
} }
if (client->state == SSH_READY) { if (client->state == SSH_READY) {
@ -548,7 +575,5 @@ struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session,
client->channel = NULL; client->channel = NULL;
client->has_encoder = 0; client->has_encoder = 0;
client->has_init_conn_fd = false;
return client; return client;
} }

View File

@ -141,7 +141,6 @@ struct tmate_ssh_client {
ssh_session session; ssh_session session;
ssh_channel channel; ssh_channel channel;
bool has_init_conn_fd;
struct event *ev_ssh; struct event *ev_ssh;
}; };
TAILQ_HEAD(tmate_ssh_clients, tmate_ssh_client); TAILQ_HEAD(tmate_ssh_clients, tmate_ssh_client);