Limit outgoing packet queue length
authorDavid Woodhouse <David.Woodhouse@intel.com>
Sun, 26 Oct 2008 10:40:26 +0000 (10:40 +0000)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Sun, 26 Oct 2008 10:40:26 +0000 (10:40 +0000)
If we were using TCP and the socket stalled, we'd just keep sucking
packets from the kernel, allocating memory and queuing them
internally with no limit except the size of the swap space. Not clever.

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
cstp.c
dtls.c
mainloop.c
openconnect.h
tun.c

diff --git a/cstp.c b/cstp.c
index 5e3c3be..affa616 100644 (file)
--- a/cstp.c
+++ b/cstp.c
@@ -549,6 +549,7 @@ int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout)
        while (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue) {
                struct pkt *this = vpninfo->outgoing_queue;
                vpninfo->outgoing_queue = this->next;
+               vpninfo->outgoing_qlen--;
 
                if (vpninfo->deflate) {
                        unsigned char *adler;
diff --git a/dtls.c b/dtls.c
index 97335d8..905605e 100644 (file)
--- a/dtls.c
+++ b/dtls.c
@@ -445,6 +445,7 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout)
                int ret;
 
                vpninfo->outgoing_queue = this->next;
+               vpninfo->outgoing_qlen--;
 
                /* One byte of header */
                this->hdr[7] = AC_PKT_DATA;
@@ -461,6 +462,7 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout)
                                ERR_print_errors_fp(stderr);
                                dtls_restart(vpninfo);
                                vpninfo->outgoing_queue = this;
+                               vpninfo->outgoing_qlen++;
                        }
                        return 1;
                }
index be6562b..2eb7be6 100644 (file)
@@ -93,7 +93,9 @@ int vpn_mainloop(struct openconnect_info *vpninfo)
                did_work += cstp_mainloop(vpninfo, &timeout);
                if (vpninfo->quit_reason)
                        break;
-               
+
+               /* Tun must be last because it will set/clear its bit
+                  in the select_rfds according to the queue length */
                did_work += tun_mainloop(vpninfo, &timeout);
                if (vpninfo->quit_reason)
                        break;
index 9130352..8784605 100644 (file)
@@ -53,6 +53,9 @@ struct vpn_option {
 #define KA_KEEPALIVE   3
 #define KA_REKEY       4
 
+
+#define MAX_Q_LEN 10
+
 struct keepalive_info {
        int dpd;
        int keepalive;
@@ -135,6 +138,7 @@ struct openconnect_info {
 
        struct pkt *incoming_queue;
        struct pkt *outgoing_queue;
+       int outgoing_qlen;
 
        socklen_t peer_addrlen;
        struct sockaddr *peer_addr;
diff --git a/tun.c b/tun.c
index 83ba6a0..2b0563d 100644 (file)
--- a/tun.c
+++ b/tun.c
@@ -304,9 +304,20 @@ int tun_mainloop(struct openconnect_info *vpninfo, int *timeout)
        int len;
        int work_done = 0;
 
-       while ( (len = read(vpninfo->tun_fd, buf, sizeof(buf))) > 0) {
-               queue_new_packet(&vpninfo->outgoing_queue, AF_INET, buf, len);
-               work_done = 1;
+       if (FD_ISSET(vpninfo->tun_fd, &vpninfo->select_rfds)) {
+               while ((len = read(vpninfo->tun_fd, buf, sizeof(buf))) > 0) {
+                       if (queue_new_packet(&vpninfo->outgoing_queue, AF_INET, buf, len))
+                               break;
+
+                       work_done = 1;
+                       vpninfo->outgoing_qlen++;
+                       if (vpninfo->outgoing_qlen == MAX_Q_LEN) {
+                               FD_CLR(vpninfo->tun_fd, &vpninfo->select_rfds);
+                               break;
+                       }
+               }
+       } else if (vpninfo->outgoing_qlen < MAX_Q_LEN) {
+               FD_SET(vpninfo->tun_fd, &vpninfo->select_rfds);
        }
 
        /* The kernel returns -ENOMEM when the queue is full, so theoretically