negotiated MTU after decompression. We reserve some extra
           space to handle that */
        int receive_mtu = MAX(16384, vpninfo->ip_info.mtu);
-       struct pkt *new = malloc(sizeof(struct pkt) + receive_mtu);
+       struct pkt *new = alloc_pkt(vpninfo, receive_mtu);
        const char *comprname = "";
 
        if (!new)
 
 #ifndef _WIN32
        vpninfo->tun_fd = -1;
 #endif
+       init_pkt_queue(&vpninfo->free_queue);
        init_pkt_queue(&vpninfo->incoming_queue);
        init_pkt_queue(&vpninfo->outgoing_queue);
        init_pkt_queue(&vpninfo->tcp_control_queue);
        free_pkt(vpninfo, vpninfo->tun_pkt);
        free_pkt(vpninfo, vpninfo->dtls_pkt);
        free_pkt(vpninfo, vpninfo->cstp_pkt);
+       struct pkt *pkt;
+       while ((pkt = dequeue_packet(&vpninfo->free_queue)))
+               free(pkt);
+
        free(vpninfo->bearer_token);
        free(vpninfo);
 }
 
 
 #include "openconnect-internal.h"
 
-int queue_new_packet(struct pkt_q *q, void *buf, int len)
+int queue_new_packet(struct openconnect_info *vpninfo,
+                    struct pkt_q *q, void *buf, int len)
 {
-       struct pkt *new = malloc(sizeof(struct pkt) + len);
+       struct pkt *new = alloc_pkt(vpninfo, len);
        if (!new)
                return -ENOMEM;
 
 
 
 static int queue_esp_control(struct openconnect_info *vpninfo, int enable)
 {
-       struct pkt *new = malloc(sizeof(*new) + 13);
+       struct pkt *new = alloc_pkt(vpninfo, esp_enable_pkt.len);
        if (!new)
                return -ENOMEM;
 
-       memcpy(new, &esp_enable_pkt, sizeof(*new) + 13);
+       new->oncp = esp_enable_pkt.oncp;
+       new->len = esp_enable_pkt.len;
+
+       memcpy(new->data, esp_enable_pkt.data, esp_enable_pkt.len);
        new->data[12] = enable;
+
        queue_packet(&vpninfo->tcp_control_queue, new);
        return 0;
 }
                        }
 
                        /* OK, we have a whole packet, and we have stuff after it */
-                       queue_new_packet(&vpninfo->incoming_queue, vpninfo->cstp_pkt->data, iplen);
+                       queue_new_packet(vpninfo, &vpninfo->incoming_queue,
+                                        vpninfo->cstp_pkt->data, iplen);
                        kmplen -= iplen;
                        if (kmplen) {
                                /* Still data packets to come in this KMP300 */
 
 /****************************************************************************/
 
 struct pkt {
+       int alloc_len;
        int len;
        struct pkt *next;
        union {
        q->tail = &q->head;
 }
 
-
-static inline struct pkt *alloc_pkt(struct openconnect_info *vpninfo, int len)
-{
-       return malloc(sizeof(struct pkt) + len);
-}
-
-static inline void free_pkt(struct openconnect_info *vpninfo, struct pkt *pkt)
-{
-       free(pkt);
-}
-
 #define TLS_OVERHEAD 5 /* packet + header */
 #define DTLS_OVERHEAD (1 /* packet + header */ + 13 /* DTLS header */ + \
         20 /* biggest supported MAC (SHA1) */ +  32 /* biggest supported IV (AES-256) */ + \
        int got_pause_cmd;
        char cancel_type;
 
+       struct pkt_q free_queue;
        struct pkt_q incoming_queue;
        struct pkt_q outgoing_queue;
        struct pkt_q tcp_control_queue;         /* Control packets to be sent via TCP */
        int (*ssl_write)(struct openconnect_info *vpninfo, char *buf, size_t len);
 };
 
+
+static inline struct pkt *alloc_pkt(struct openconnect_info *vpninfo, int len)
+{
+       int alloc_len = sizeof(struct pkt) + len;
+
+       if (vpninfo->free_queue.head &&
+           vpninfo->free_queue.head->alloc_len >= alloc_len)
+               return dequeue_packet(&vpninfo->free_queue);
+
+       if (alloc_len < 2048)
+               alloc_len = 2048;
+
+       struct pkt *pkt = malloc(alloc_len);
+       if (pkt)
+               pkt->alloc_len = alloc_len;
+       return pkt;
+}
+
+static inline void free_pkt(struct openconnect_info *vpninfo, struct pkt *pkt)
+{
+       if (!pkt)
+               return;
+
+       if (vpninfo->free_queue.count < vpninfo->max_qlen * 2)
+               requeue_packet(&vpninfo->free_queue, pkt);
+       else
+               free(pkt);
+}
+
 #ifdef _WIN32
 #define monitor_read_fd(_v, _n) _v->_n##_monitored |= FD_READ
 #define monitor_write_fd(_v, _n) _v->_n##_monitored |= FD_WRITE
 
 /* mainloop.c */
 int tun_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable);
-int queue_new_packet(struct pkt_q *q, void *buf, int len);
+int queue_new_packet(struct openconnect_info *vpninfo,
+                    struct pkt_q *q, void *buf, int len);
 int keepalive_action(struct keepalive_info *ka, int *timeout);
 int ka_stalled_action(struct keepalive_info *ka, int *timeout);
 int ka_check_deadline(int *timeout, time_t now, time_t due);
 
        /* Every byte in payload and 2-byte FCS potentially expands to two bytes,
         * plus 2 for flag (0x7e) at start and end. We know that we will output
         * at least 4 bytes so we can stash those in the header. */
-       struct pkt *p = malloc(sizeof(struct pkt) + len*2 + 2);
+       struct pkt *p = alloc_pkt(vpninfo, len*2 + 2);
        if (!p)
                return NULL;
 
        return buf_append_ppp_tlv(buf, tag, 4, &val_be);
 }
 
-static int queue_config_packet(struct openconnect_info *vpninfo,
-                               uint16_t proto, int id, int code, int len, const void *payload)
+static int queue_config_packet(struct openconnect_info *vpninfo, uint16_t proto,
+                              int id, int code, int len, const void *payload)
 {
-       struct pkt *p = malloc(sizeof(struct pkt) + len + 4);
+       struct pkt *p = alloc_pkt(vpninfo, len + 4);
 
        if (!p)
                return -ENOMEM;