]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Implement LZ4 compression.
authorGert Doering <gert@greenie.muc.de>
Wed, 1 Jan 2014 21:57:58 +0000 (22:57 +0100)
committerGert Doering <gert@greenie.muc.de>
Mon, 6 Jan 2014 11:18:53 +0000 (12:18 +0100)
Implement LZ4 compression, similar to the existing snappy / push-peer-info
model: a LZ4 capable client will send IV_LZ4=1 to the server, and the
algorithm is selected by pushing "compress lz4" back.

LZ4 does not compress as well as LZO or Snappy, but needs far less CPU
and is much faster, thus better suited for mobile devices.  See
https://code.google.com/p/lz4/ for more details.

LZ4 include and library path can be specified by specifying LZ4_LIBS=...
and LZ4_CFLAGS=... on the configure command line.

Signed-off-by: Gert Doering <gert@greenie.muc.de>
Acked-by: Arne Schwabe <arne@rfc2549.org>
Message-Id: <1388613479-22377-1-git-send-email-gert@greenie.muc.de>
URL: http://article.gmane.org/gmane.network.openvpn.devel/8153

configure.ac
src/openvpn/Makefile.am
src/openvpn/comp-lz4.c [new file with mode: 0644]
src/openvpn/comp-lz4.h [new file with mode: 0644]
src/openvpn/comp.c
src/openvpn/comp.h
src/openvpn/init.c
src/openvpn/options.c
src/openvpn/syshead.h

index 23e76f6ce9677b6d9d9c5568d0d2669dbf5b6c1d..497dd449b3e18c24f338219323cea4b1514efe64 100644 (file)
@@ -52,6 +52,12 @@ AC_ARG_ENABLE(snappy,
        [enable_snappy="yes"]
 )
 
+AC_ARG_ENABLE(lz4,
+       [  --disable-lz4           Disable LZ4 compression support],
+       [enable_lz4="$enableval"],
+       [enable_lz4="yes"]
+)
+
 AC_ARG_ENABLE(comp-stub,
        [  --enable-comp-stub      Don't compile compression support but still allow limited interoperability with compression-enabled peers],
        [enable_comp_stub="$enableval"],
@@ -934,6 +940,47 @@ if test "$enable_snappy" = "yes" && test "$enable_comp_stub" = "no"; then
     CFLAGS="${saved_CFLAGS}"
 fi
 
+dnl
+dnl check for LZ4 library
+dnl
+
+AC_ARG_VAR([LZ4_CFLAGS], [C compiler flags for lz4])
+AC_ARG_VAR([LZ4_LIBS], [linker flags for lz4])
+if test "$enable_lz4" = "yes" && test "$enable_comp_stub" = "no"; then
+    AC_CHECKING([for LZ4 Library and Header files])
+    havelz4lib=1
+
+    # if LZ4_LIBS is set, we assume it will work, otherwise test
+    if test -z "${LZ4_LIBS}"; then
+       AC_CHECK_LIB(lz4, LZ4_compress,
+           [ LZ4_LIBS="-llz4" ],
+           [
+               AC_MSG_RESULT([LZ4 library not found.])
+               havelz4lib=0
+           ])
+    fi
+
+    saved_CFLAGS="${CFLAGS}"
+    CFLAGS="${CFLAGS} ${LZ4_CFLAGS}"
+    AC_CHECK_HEADER(lz4.h,
+       ,
+       [
+          AC_MSG_RESULT([LZ4 headers not found.])
+          havelz4lib=0
+       ])
+
+    if test $havelz4lib = 0 ; then
+       AC_MSG_RESULT([LZ4 library available from http://code.google.com/p/lz4/])
+        AC_MSG_ERROR([Or try ./configure --disable-lz4 OR ./configure --enable-comp-stub])
+    fi
+    OPTIONAL_LZ4_CFLAGS="${LZ4_CFLAGS}"
+    OPTIONAL_LZ4_LIBS="${LZ4_LIBS}"
+    AC_DEFINE(ENABLE_LZ4, 1, [Enable LZ4 compression library])
+    CFLAGS="${saved_CFLAGS}"
+fi
+
+
+
 
 AC_MSG_CHECKING([git checkout])
 GIT_CHECKOUT="no"
@@ -1045,6 +1092,7 @@ fi
 if test "${enable_comp_stub}" = "yes"; then
        test "${enable_lzo}" = "yes" && AC_MSG_ERROR([Cannot have both comp stub and lzo enabled (use --disable-lzo)])
        test "${enable_snappy}" = "yes" && AC_MSG_ERROR([Cannot have both comp stub and snappy enabled (use --disable-snappy)])
+       test "${enable_lz4}" = "yes" && AC_MSG_ERROR([Cannot have both comp stub and LZ4 enabled (use --disable-lz4)])
        AC_DEFINE([ENABLE_COMP_STUB], [1], [Enable compression stub capability])
 fi
 
@@ -1101,6 +1149,8 @@ AC_SUBST([OPTIONAL_LZO_CFLAGS])
 AC_SUBST([OPTIONAL_LZO_LIBS])
 AC_SUBST([OPTIONAL_SNAPPY_CFLAGS])
 AC_SUBST([OPTIONAL_SNAPPY_LIBS])
+AC_SUBST([OPTIONAL_LZ4_CFLAGS])
+AC_SUBST([OPTIONAL_LZ4_LIBS])
 AC_SUBST([OPTIONAL_PKCS11_HELPER_CFLAGS])
 AC_SUBST([OPTIONAL_PKCS11_HELPER_LIBS])
 
index 70e19c2cbbed61de1550de0722f90c2320817a4e..0b79e104498cc6b7c466b1e8eb762615dd736001 100644 (file)
@@ -27,6 +27,7 @@ AM_CFLAGS = \
        $(OPTIONAL_CRYPTO_CFLAGS) \
        $(OPTIONAL_LZO_CFLAGS) \
        $(OPTIONAL_SNAPPY_CFLAGS) \
+       $(OPTIONAL_LZ4_CFLAGS) \
        $(OPTIONAL_PKCS11_HELPER_CFLAGS)
 if WIN32
 # we want unicode entry point but not the macro
@@ -43,6 +44,7 @@ openvpn_SOURCES = \
        clinat.c clinat.h \
        common.h \
        comp.c comp.h compstub.c \
+       comp-lz4.c comp-lz4.h \
        crypto.c crypto.h crypto_backend.h \
        crypto_openssl.c crypto_openssl.h \
        crypto_polarssl.c crypto_polarssl.h \
@@ -120,6 +122,7 @@ openvpn_LDADD = \
        $(SOCKETS_LIBS) \
        $(OPTIONAL_LZO_LIBS) \
        $(OPTIONAL_SNAPPY_LIBS) \
+       $(OPTIONAL_LZ4_LIBS) \
        $(OPTIONAL_PKCS11_HELPER_LIBS) \
        $(OPTIONAL_CRYPTO_LIBS) \
        $(OPTIONAL_SELINUX_LIBS) \
diff --git a/src/openvpn/comp-lz4.c b/src/openvpn/comp-lz4.c
new file mode 100644 (file)
index 0000000..ce89573
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2012 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ *  Copyright (C) 2013      Gert Doering <gert@greenie.muc.de>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#include "syshead.h"
+
+#if defined(ENABLE_LZ4)
+
+#include "lz4.h"
+
+#include "comp.h"
+#include "error.h"
+
+#include "memdbg.h"
+
+/* Initial command byte to tell our peer if we compressed */
+#define LZ4_COMPRESS_BYTE 0x69
+
+static void
+lz4_compress_init (struct compress_context *compctx)
+{
+  msg (D_INIT_MEDIUM, "LZ4 compression initializing");
+  ASSERT(compctx->flags & COMP_F_SWAP);
+}
+
+static void
+lz4_compress_uninit (struct compress_context *compctx)
+{
+}
+
+static void
+lz4_compress (struct buffer *buf, struct buffer work,
+              struct compress_context *compctx,
+              const struct frame* frame)
+{
+  int result;
+  bool compressed = false;
+
+  if (buf->len <= 0)
+    return;
+
+  /*
+   * In order to attempt compression, length must be at least COMPRESS_THRESHOLD.
+   */
+  if (buf->len >= COMPRESS_THRESHOLD)
+    {
+      const size_t ps = PAYLOAD_SIZE (frame);
+      int zlen_max = ps + COMP_EXTRA_BUFFER (ps);
+      int zlen;
+
+      ASSERT (buf_init (&work, FRAME_HEADROOM (frame)));
+      ASSERT (buf_safe (&work, zlen_max));
+
+      if (buf->len > ps)
+       {
+         dmsg (D_COMP_ERRORS, "LZ4 compression buffer overflow");
+         buf->len = 0;
+         return;
+       }
+
+      zlen = LZ4_compress_limitedOutput((const char *)BPTR(buf), (char *)BPTR(&work), BLEN(buf), zlen_max );
+
+      if (zlen <= 0)
+       {
+         dmsg (D_COMP_ERRORS, "LZ4 compression error");
+         buf->len = 0;
+         return;
+       }
+
+      ASSERT (buf_safe (&work, zlen));
+      work.len = zlen;
+      compressed = true;
+
+      dmsg (D_COMP, "LZ4 compress %d -> %d", buf->len, work.len);
+      compctx->pre_compress += buf->len;
+      compctx->post_compress += work.len;
+    }
+
+  /* did compression save us anything? */
+  {
+    uint8_t comp_head_byte = NO_COMPRESS_BYTE_SWAP;
+    if (compressed && work.len < buf->len)
+      {
+       *buf = work;
+       comp_head_byte = LZ4_COMPRESS_BYTE;
+      }
+
+    {
+      uint8_t *head = BPTR (buf);
+      uint8_t *tail  = BEND (buf);
+      ASSERT (buf_safe (buf, 1));
+      ++buf->len;
+
+      /* move head byte of payload to tail */
+      *tail = *head;
+      *head = comp_head_byte;
+    }
+  }
+}
+
+static void
+lz4_decompress (struct buffer *buf, struct buffer work,
+                struct compress_context *compctx,
+                const struct frame* frame)
+{
+  size_t zlen_max = EXPANDED_SIZE (frame);
+  int uncomp_len;
+  uint8_t c;           /* flag indicating whether or not our peer compressed */
+
+  if (buf->len <= 0)
+    return;
+
+  ASSERT (buf_init (&work, FRAME_HEADROOM (frame)));
+
+  /* do unframing/swap (assumes buf->len > 0) */
+  {
+    uint8_t *head = BPTR (buf);
+    c = *head;
+    --buf->len;
+    *head = *BEND (buf);
+  }
+
+  if (c == LZ4_COMPRESS_BYTE)  /* packet was compressed */
+    {
+      ASSERT (buf_safe (&work, zlen_max));
+      uncomp_len = LZ4_decompress_safe((const char *)BPTR(buf), (char *)BPTR(&work), (size_t)BLEN(buf), zlen_max);
+      if (uncomp_len <= 0)
+       {
+         dmsg (D_COMP_ERRORS, "LZ4 decompression error: %d", uncomp_len);
+         buf->len = 0;
+         return;
+       }
+
+      ASSERT (buf_safe (&work, uncomp_len));
+      work.len = uncomp_len;
+
+      dmsg (D_COMP, "LZ4 decompress %d -> %d", buf->len, work.len);
+      compctx->pre_decompress += buf->len;
+      compctx->post_decompress += work.len;
+
+      *buf = work;
+    }
+  else if (c == NO_COMPRESS_BYTE_SWAP) /* packet was not compressed */
+    {
+      ;
+    }
+  else
+    {
+      dmsg (D_COMP_ERRORS, "Bad LZ4 decompression header byte: %d", c);
+      buf->len = 0;
+    }
+}
+
+const struct compress_alg lz4_alg = {
+  "lz4",
+  lz4_compress_init,
+  lz4_compress_uninit,
+  lz4_compress,
+  lz4_decompress
+};
+
+#else
+static void dummy(void) {}
+#endif /* ENABLE_LZ4 */
diff --git a/src/openvpn/comp-lz4.h b/src/openvpn/comp-lz4.h
new file mode 100644 (file)
index 0000000..ca1dfa9
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2012 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ *  Copyright (C) 2013      Gert Doering <gert@greenie.muc.de>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef OPENVPN_COMP_LZ4_H
+#define OPENVPN_COMP_LZ4_H
+
+#if defined(ENABLE_LZ4)
+
+#include "buffer.h"
+
+extern const struct compress_alg lz4_alg;
+
+struct lz4_workspace
+{
+};
+
+#endif /* ENABLE_LZ4 */
+#endif
index 96922571cc4ee8c1c1cf108366c49db8016600ce..4ac589f95c0612f71c885c0e6acb5b9ad118fbf8 100644 (file)
@@ -65,6 +65,14 @@ comp_init(const struct compress_options *opt)
       compctx->alg = snappy_alg;
       (*compctx->alg.compress_init)(compctx);
       break;
+#endif
+#ifdef ENABLE_LZ4
+    case COMP_ALG_LZ4:
+      ALLOC_OBJ_CLEAR (compctx, struct compress_context);
+      compctx->flags = opt->flags;
+      compctx->alg = lz4_alg;
+      (*compctx->alg.compress_init)(compctx);
+      break;
 #endif
     }
   return compctx;
@@ -118,6 +126,9 @@ comp_generate_peer_info_string(const struct compress_options *opt, struct buffer
       bool lzo_avail = false;
       if (!(opt->flags & COMP_F_ADVERTISE_STUBS_ONLY))
        {
+#if defined(ENABLE_LZ4)
+         buf_printf (out, "IV_LZ4=1\n");
+#endif
 #if defined(ENABLE_SNAPPY)
          buf_printf (out, "IV_SNAPPY=1\n");
 #endif
index 0d2f1bc9914b9343864adb8214ab9ba279101b7b..bfa25fd3efa5258ca081814c3ffa008a31ba2088 100644 (file)
@@ -24,7 +24,7 @@
 
 /*
  * Generic compression support.  Currently we support
- * Snappy and LZO 2.
+ * Snappy, LZO 2 and LZ4.
  */
 #ifndef OPENVPN_COMP_H
 #define OPENVPN_COMP_H
@@ -41,6 +41,7 @@
 #define COMP_ALG_STUB   1 /* support compression command byte and framing without actual compression */
 #define COMP_ALG_LZO    2 /* LZO algorithm */
 #define COMP_ALG_SNAPPY 3 /* Snappy algorithm */
+#define COMP_ALG_LZ4    4 /* LZ4 algorithm */
 
 /* Compression flags */
 #define COMP_F_ADAPTIVE   (1<<0) /* COMP_ALG_LZO only */
@@ -64,6 +65,7 @@
  *
  * LZO:    len + len/8 + 128 + 3
  * Snappy: len + len/6 + 32
+ * LZ4:    len + len/255 + 16  (LZ4_COMPRESSBOUND(len))
  */
 #define COMP_EXTRA_BUFFER(len) ((len)/6 + 128 + 3 + COMP_PREFIX_LEN)
 
@@ -103,6 +105,10 @@ struct compress_alg
 #include "snappy.h"
 #endif
 
+#ifdef ENABLE_LZ4
+#include "comp-lz4.h"
+#endif
+
 /*
  * Information that basically identifies a compression
  * algorithm and related flags.
@@ -124,6 +130,9 @@ union compress_workspace_union
 #ifdef ENABLE_SNAPPY
   struct snappy_workspace snappy;
 #endif
+#ifdef ENABLE_LZ4
+  struct lz4_workspace lz4;
+#endif
 };
 
 /*
index 1b8ee22e78edb60b707460e0791bc020f66a55ba..d324166a45009cd3bf7352618ffa7f42c2e88594 100644 (file)
@@ -2379,7 +2379,7 @@ do_init_frame (struct context *c)
     {
       comp_add_to_extra_frame (&c->c2.frame);
 
-#if !defined(ENABLE_SNAPPY)
+#if !defined(ENABLE_SNAPPY) && !defined(ENABLE_LZ4)
       /*
        * Compression usage affects buffer alignment when non-swapped algs
        * such as LZO is used.
@@ -2394,7 +2394,7 @@ do_init_frame (struct context *c)
        * dispatch if packet is uncompressed) at the cost of requiring
        * decryption output to be written to an unaligned buffer, so
        * it's more of a tradeoff than an optimal solution and we don't
-       * include it when we are doing a modern build with Snappy.
+       * include it when we are doing a modern build with Snappy or LZ4.
        * Strictly speaking, on the server it would be better to execute
        * this code for every connection after we decide the compression
        * method, but currently the frame code doesn't appear to be
index 6d9c3b8f71a84f4c273d1cb35562903da24764f1..6165faa06ecd856246e708ded65391b7ab97f376 100644 (file)
@@ -92,6 +92,9 @@ const char title_string[] =
 #ifdef ENABLE_SNAPPY
   " [SNAPPY]"
 #endif
+#ifdef ENABLE_LZ4
+  " [LZ4]"
+#endif
 #ifdef ENABLE_COMP_STUB
   " [COMP_STUB]"
 #endif
@@ -6258,6 +6261,13 @@ add_option (struct options *options,
              options->comp.alg = COMP_ALG_SNAPPY;
              options->comp.flags = COMP_F_SWAP;
            }
+#endif
+#if defined(ENABLE_LZ4)
+         else if (streq (p[1], "lz4"))
+           {
+             options->comp.alg = COMP_ALG_LZ4;
+             options->comp.flags = COMP_F_SWAP;
+           }
 #endif
          else
            {
index 51b24028f32bb7bb16289070172f2b7c61b3c3f6..ab6fa01f7e51b16f860c212565478fae90b3262e 100644 (file)
@@ -715,7 +715,8 @@ socket_defined (const socket_descriptor_t sd)
 /*
  * Compression support
  */
-#if defined(ENABLE_SNAPPY) || defined(ENABLE_LZO) || defined(ENABLE_COMP_STUB)
+#if defined(ENABLE_SNAPPY) || defined(ENABLE_LZO) || defined(ENABLE_LZ4) || \
+    defined(ENABLE_COMP_STUB)
 #define USE_COMP
 #endif