Sort out DPD and Keepalive
authorDavid Woodhouse <David.Woodhouse@intel.com>
Tue, 23 Sep 2008 23:25:40 +0000 (16:25 -0700)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Tue, 23 Sep 2008 23:25:40 +0000 (16:25 -0700)
This could be cleaner -- and shared between DTLS and SSL. But this seems
to work...

anyconnect.h
dtls.c
ssl.c

index f6686fd..a68d7c8 100644 (file)
@@ -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 (file)
--- 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 (file)
--- 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;
        }