From 52ae5efe24ccf6f0a09b0913683883372720cd7a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 23 Sep 2008 16:25:40 -0700 Subject: [PATCH] Sort out DPD and Keepalive This could be cleaner -- and shared between DTLS and SSL. But this seems to work... --- anyconnect.h | 6 +++-- dtls.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- ssl.c | 55 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 125 insertions(+), 14 deletions(-) diff --git a/anyconnect.h b/anyconnect.h index f6686fd..a68d7c8 100644 --- a/anyconnect.h +++ b/anyconnect.h @@ -51,6 +51,7 @@ struct anyconnect_info { int ssl_dpd; time_t last_ssl_tx; time_t last_ssl_rx; + time_t last_ssl_dpd; int ssl_pfd; z_stream inflate_strm; @@ -64,6 +65,7 @@ struct anyconnect_info { int dtls_dpd; time_t last_dtls_tx; time_t last_dtls_rx; + time_t last_dtls_dpd; unsigned char dtls_session_id[32]; unsigned char dtls_secret[48]; @@ -91,8 +93,8 @@ struct anyconnect_info { /* Packet types */ #define AC_PKT_DATA 0 /* Uncompressed data */ -#define AC_PKT_DPD_OUT 3 /* DPD client->server */ -#define AC_PKT_DPD_RESP 4 /* DPD response server->client */ +#define AC_PKT_DPD_OUT 3 /* Dead Peer Detection */ +#define AC_PKT_DPD_RESP 4 /* DPD response */ #define AC_PKT_DISCONN 5 /* Client disconnection notice */ #define AC_PKT_KEEPALIVE 7 /* Keepalive */ #define AC_PKT_COMPRESSED 8 /* Compressed data */ diff --git a/dtls.c b/dtls.c index fcc96fa..e5bfd15 100644 --- a/dtls.c +++ b/dtls.c @@ -153,10 +153,10 @@ static int connect_dtls_socket(struct anyconnect_info *vpninfo, int dtls_port) close(dtls_fd); return -EINVAL; } - printf("DTLS Connection successful!\n"); vpninfo->dtls_fd = dtls_fd; vpninfo->dtls_ssl = dtls_ssl; + return 0; } @@ -202,7 +202,11 @@ int setup_dtls(struct anyconnect_info *vpninfo) fcntl(vpninfo->dtls_fd, F_SETFL, fcntl(vpninfo->dtls_fd, F_GETFL) | O_NONBLOCK); vpn_add_pollfd(vpninfo, vpninfo->ssl_fd, POLLIN|POLLHUP|POLLERR); - vpninfo->last_ssl_tx = vpninfo->last_ssl_tx = time(NULL); + vpninfo->last_dtls_rx = vpninfo->last_dtls_tx = time(NULL); + + if (verbose) + printf("DTLS connected. DPD %d, Keepalive %d\n", + vpninfo->dtls_dpd, vpninfo->dtls_keepalive); return 0; } @@ -218,21 +222,24 @@ int dtls_mainloop(struct anyconnect_info *vpninfo, int *timeout) printf("Received DTLS packet of %d bytes\n", len); printf("Packet starts %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); - } + } + vpninfo->last_dtls_rx = time(NULL); switch(buf[0]) { case 0: queue_new_packet(&vpninfo->incoming_queue, AF_INET, buf+1, len-1); work_done = 1; break; - case 4: /* keepalive response */ + case 4: /* DPD response */ + if (verbose) + printf("Got DTLS DPD response\n"); break; default: fprintf(stderr, "Unknown DTLS packet type %02x\n", buf[0]); - break; + vpninfo->quit_reason = "Unknown packet received"; + return 1; } - } while (vpninfo->outgoing_queue) { struct pkt *this = vpninfo->outgoing_queue; @@ -244,12 +251,71 @@ int dtls_mainloop(struct anyconnect_info *vpninfo, int *timeout) memcpy(buf + 1, this->data, this->len); ret = SSL_write(vpninfo->dtls_ssl, buf, this->len + 1); + vpninfo->last_dtls_tx = time(NULL); if (verbose) { printf("Sent DTLS packet of %d bytes; SSL_write() returned %d\n", this->len, ret); } } + /* DPD is bidirectional -- PKT 3 out, PKT 4 back */ + if (vpninfo->dtls_dpd) { + time_t now = time(NULL); + time_t due = vpninfo->last_dtls_rx + vpninfo->dtls_dpd; + time_t overdue = vpninfo->last_dtls_rx + (5 * vpninfo->dtls_dpd); + + /* If we already have DPD outstanding, don't flood */ + if (vpninfo->last_dtls_dpd > vpninfo->last_dtls_rx) + due = vpninfo->last_dtls_dpd + vpninfo->dtls_dpd; + + if (now > overdue) { + fprintf(stderr, "DTLS Dead Peer Detection detected dead peer!\n"); + /* FIXME: Can we call fack to SSL instead? */ + vpninfo->quit_reason = "DTLS DPD detected dead peer"; + return 1; + } + if (now >= due) { + static unsigned char dtls_dpd_pkt[1] = { 3 }; + /* Haven't heard anything from the other end for a while. + Check if it's still there */ + /* FIXME: If isn't, we should act on that */ + SSL_write(vpninfo->dtls_ssl, dtls_dpd_pkt, 1); + vpninfo->last_dtls_tx = now; + + due = now + vpninfo->dtls_dpd; + if (verbose) + printf("Sent DTLS DPD\n"); + } + + printf("Next DTLS DPD due in %d seconds\n", (due - now)); + if (*timeout > (due - now) * 1000) + *timeout = (due - now) * 1000; + } + + /* Keepalive is just client -> server */ + if (vpninfo->dtls_keepalive) { + time_t now = time(NULL); + time_t due = vpninfo->last_dtls_tx + vpninfo->dtls_keepalive; + + if (now >= due) { + static unsigned char dtls_keepalive_pkt[1] = { 7 }; + + /* Send something (which is discarded), to keep + the connection alive. */ + SSL_write(vpninfo->dtls_ssl, dtls_keepalive_pkt, 1); + vpninfo->last_dtls_tx = now; + + due = now + vpninfo->dtls_keepalive; + if (verbose) + printf("Sent DTLS Keepalive\n"); + } + + printf("Next DTLS Keepalive due in %d seconds\n", (due - now)); + if (*timeout > (due - now) * 1000) + *timeout = (due - now) * 1000; + } + + /* FIXME: Keepalive */ return work_done; } diff --git a/ssl.c b/ssl.c index 46494f1..1ef625c 100644 --- a/ssl.c +++ b/ssl.c @@ -249,7 +249,8 @@ static int start_ssl_connection(struct anyconnect_info *vpninfo) } if (verbose) - printf("Connected!\n"); + printf("SSL connected. DPD %d, Keepalive %d\n", + vpninfo->ssl_dpd, vpninfo->ssl_keepalive); BIO_set_nbio(SSL_get_rbio(vpninfo->https_ssl),1); BIO_set_nbio(SSL_get_wbio(vpninfo->https_ssl),1); @@ -257,7 +258,7 @@ static int start_ssl_connection(struct anyconnect_info *vpninfo) fcntl(vpninfo->ssl_fd, F_SETFL, fcntl(vpninfo->ssl_fd, F_GETFL) | O_NONBLOCK); vpninfo->ssl_pfd = vpn_add_pollfd(vpninfo, vpninfo->ssl_fd, POLLIN|POLLHUP|POLLERR); - vpninfo->last_ssl_tx = vpninfo->last_ssl_tx = time(NULL); + vpninfo->last_ssl_rx = vpninfo->last_ssl_tx = time(NULL); return 0; } @@ -362,10 +363,11 @@ int ssl_mainloop(struct anyconnect_info *vpninfo, int *timeout) buf[4], buf[5], buf[6], buf[7]); continue; } + vpninfo->last_ssl_rx = time(NULL); switch(buf[6]) { case 4: /* Keepalive response */ if (verbose) - printf("Got keepalive response\n"); + printf("Got SSL DPD response\n"); continue; case 0: /* Uncompressed Data */ @@ -458,20 +460,61 @@ int ssl_mainloop(struct anyconnect_info *vpninfo, int *timeout) vpninfo->last_ssl_tx = time(NULL); } + /* DPD is bidirectional -- PKT 3 out, PKT 4 back */ + if (vpninfo->ssl_dpd) { + time_t now = time(NULL); + time_t due = vpninfo->last_ssl_rx + vpninfo->ssl_dpd; + time_t overdue = vpninfo->last_ssl_rx + (5 * vpninfo->ssl_dpd); + + /* If we already have DPD outstanding, don't flood */ + if (vpninfo->last_ssl_dpd > vpninfo->last_ssl_rx) + due = vpninfo->last_ssl_dpd + vpninfo->ssl_dpd; + + if (now > overdue) { + fprintf(stderr, "SSL Dead Peer Detection detected dead peer!\n"); + vpninfo->quit_reason = "SSL DPD detected dead peer"; + return 1; + } + + if (now >= due) { + static unsigned char cstp_dpd[8] = + {'S', 'T', 'F', 1, 0, 0, 3, 0}; + /* Haven't heard anything from the other end for a while. + Check if it's still there */ + /* FIXME: If isn't, we should act on that */ + SSL_write(vpninfo->https_ssl, cstp_dpd, 8); + vpninfo->last_ssl_tx = now; + + due = now + vpninfo->ssl_dpd; + if (verbose) + printf("Sent SSL DPD\n"); + } + + printf("Next SSL DPD due in %d seconds\n", (due - now)); + if (*timeout > (due - now) * 1000) + *timeout = (due - now) * 1000; + } + + /* Keepalive is just client -> server */ if (vpninfo->ssl_keepalive) { time_t now = time(NULL); time_t due = vpninfo->last_ssl_tx + vpninfo->ssl_keepalive; + if (now >= due) { static unsigned char cstp_keepalive[8] = - {'S', 'T', 'F', 1, 0, 0, 3, 0}; - + {'S', 'T', 'F', 1, 0, 0, 7, 0}; + + /* Send something (which is discarded), to keep + the connection alive. */ SSL_write(vpninfo->https_ssl, cstp_keepalive, 8); vpninfo->last_ssl_tx = now; + due = now + vpninfo->ssl_keepalive; if (verbose) - printf("Sent keepalive\n"); + printf("Sent SSL Keepalive\n"); } + printf("Next SSL Keepalive due in %d seconds\n", (due - now)); if (*timeout > (due - now) * 1000) *timeout = (due - now) * 1000; } -- 2.7.4