OpenVPN
lzo.c
Go to the documentation of this file.
1 /*
2  * OpenVPN -- An application to securely tunnel IP networks
3  * over a single UDP port, with support for SSL/TLS-based
4  * session authentication and key exchange,
5  * packet encryption, packet authentication, and
6  * packet compression.
7  *
8  * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2
12  * as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #elif defined(_MSC_VER)
31 #include "config-msvc.h"
32 #endif
33 
34 #include "syshead.h"
35 
36 #if defined(ENABLE_LZO)
37 
38 #include "comp.h"
39 #include "error.h"
40 #include "otime.h"
41 
42 #include "memdbg.h"
43 
51 static bool
52 lzo_adaptive_compress_test(struct lzo_adaptive_compress *ac)
53 {
54  const bool save = ac->compress_state;
55  const time_t local_now = now;
56 
57  if (!ac->compress_state)
58  {
59  if (local_now >= ac->next)
60  {
61  if (ac->n_total > AC_MIN_BYTES
62  && (ac->n_total - ac->n_comp) < (ac->n_total / (100 / AC_SAVE_PCT)))
63  {
64  ac->compress_state = true;
65  ac->next = local_now + AC_OFF_SEC;
66  }
67  else
68  {
69  ac->next = local_now + AC_SAMP_SEC;
70  }
71  dmsg(D_COMP, "lzo_adaptive_compress_test: comp=%d total=%d", ac->n_comp, ac->n_total);
72  ac->n_total = ac->n_comp = 0;
73  }
74  }
75  else
76  {
77  if (local_now >= ac->next)
78  {
79  ac->next = local_now + AC_SAMP_SEC;
80  ac->n_total = ac->n_comp = 0;
81  ac->compress_state = false;
82  }
83  }
84 
85  if (ac->compress_state != save)
86  {
87  dmsg(D_COMP_LOW, "Adaptive compression state %s", (ac->compress_state ? "OFF" : "ON"));
88  }
89 
90  return !ac->compress_state;
91 }
92 
93 static inline void
94 lzo_adaptive_compress_data(struct lzo_adaptive_compress *ac, int n_total, int n_comp)
95 {
96  ac->n_total += n_total;
97  ac->n_comp += n_comp;
98 }
99 
100 static void
101 lzo_compress_init(struct compress_context *compctx)
102 {
103  msg(D_INIT_MEDIUM, "LZO compression initializing");
104  ASSERT(!(compctx->flags & COMP_F_SWAP));
105  compctx->wu.lzo.wmem_size = LZO_WORKSPACE;
106 
107  int lzo_status = lzo_init();
108  if (lzo_status != LZO_E_OK)
109  {
110  msg(M_FATAL, "Cannot initialize LZO compression library (lzo_init() returns %d)", lzo_status);
111  }
112  compctx->wu.lzo.wmem = (lzo_voidp) lzo_malloc(compctx->wu.lzo.wmem_size);
113  check_malloc_return(compctx->wu.lzo.wmem);
114 }
115 
116 static void
117 lzo_compress_uninit(struct compress_context *compctx)
118 {
119  lzo_free(compctx->wu.lzo.wmem);
120  compctx->wu.lzo.wmem = NULL;
121 }
122 
123 static inline bool
124 lzo_compression_enabled(struct compress_context *compctx)
125 {
126  if (compctx->flags & COMP_F_ASYM)
127  {
128  return false;
129  }
130  else
131  {
132  if (compctx->flags & COMP_F_ADAPTIVE)
133  {
134  return lzo_adaptive_compress_test(&compctx->wu.lzo.ac);
135  }
136  else
137  {
138  return true;
139  }
140  }
141 }
142 
143 static void
144 lzo_compress(struct buffer *buf, struct buffer work,
145  struct compress_context *compctx,
146  const struct frame *frame)
147 {
148  lzo_uint zlen = 0;
149  int err;
150  bool compressed = false;
151 
152  if (buf->len <= 0)
153  {
154  return;
155  }
156 
157  /*
158  * In order to attempt compression, length must be at least COMPRESS_THRESHOLD,
159  * and our adaptive level must give the OK.
160  */
161  if (buf->len >= COMPRESS_THRESHOLD && lzo_compression_enabled(compctx))
162  {
163  const size_t ps = PAYLOAD_SIZE(frame);
164  ASSERT(buf_init(&work, FRAME_HEADROOM(frame)));
165  ASSERT(buf_safe(&work, ps + COMP_EXTRA_BUFFER(ps)));
166 
167  if (buf->len > ps)
168  {
169  dmsg(D_COMP_ERRORS, "LZO compression buffer overflow");
170  buf->len = 0;
171  return;
172  }
173 
174  err = LZO_COMPRESS(BPTR(buf), BLEN(buf), BPTR(&work), &zlen, compctx->wu.lzo.wmem);
175  if (err != LZO_E_OK)
176  {
177  dmsg(D_COMP_ERRORS, "LZO compression error: %d", err);
178  buf->len = 0;
179  return;
180  }
181 
182  ASSERT(buf_safe(&work, zlen));
183  work.len = zlen;
184  compressed = true;
185 
186  dmsg(D_COMP, "LZO compress %d -> %d", buf->len, work.len);
187  compctx->pre_compress += buf->len;
188  compctx->post_compress += work.len;
189 
190  /* tell adaptive level about our success or lack thereof in getting any size reduction */
191  if (compctx->flags & COMP_F_ADAPTIVE)
192  {
193  lzo_adaptive_compress_data(&compctx->wu.lzo.ac, buf->len, work.len);
194  }
195  }
196 
197  /* did compression save us anything ? */
198  if (compressed && work.len < buf->len)
199  {
200  uint8_t *header = buf_prepend(&work, 1);
201  *header = LZO_COMPRESS_BYTE;
202  *buf = work;
203  }
204  else
205  {
206  uint8_t *header = buf_prepend(buf, 1);
207  *header = NO_COMPRESS_BYTE;
208  }
209 }
210 
211 static void
212 lzo_decompress(struct buffer *buf, struct buffer work,
213  struct compress_context *compctx,
214  const struct frame *frame)
215 {
216  lzo_uint zlen = EXPANDED_SIZE(frame);
217  int err;
218  uint8_t c; /* flag indicating whether or not our peer compressed */
219 
220  if (buf->len <= 0)
221  {
222  return;
223  }
224 
225  ASSERT(buf_init(&work, FRAME_HEADROOM(frame)));
226 
227  c = *BPTR(buf);
228  ASSERT(buf_advance(buf, 1));
229 
230  if (c == LZO_COMPRESS_BYTE) /* packet was compressed */
231  {
232  ASSERT(buf_safe(&work, zlen));
233  err = LZO_DECOMPRESS(BPTR(buf), BLEN(buf), BPTR(&work), &zlen,
234  compctx->wu.lzo.wmem);
235  if (err != LZO_E_OK)
236  {
237  dmsg(D_COMP_ERRORS, "LZO decompression error: %d", err);
238  buf->len = 0;
239  return;
240  }
241 
242  ASSERT(buf_safe(&work, zlen));
243  work.len = zlen;
244 
245  dmsg(D_COMP, "LZO decompress %d -> %d", buf->len, work.len);
246  compctx->pre_decompress += buf->len;
247  compctx->post_decompress += work.len;
248 
249  *buf = work;
250  }
251  else if (c == NO_COMPRESS_BYTE) /* packet was not compressed */
252  {
253  }
254  else
255  {
256  dmsg(D_COMP_ERRORS, "Bad LZO decompression header byte: %d", c);
257  buf->len = 0;
258  }
259 }
260 
261 const struct compress_alg lzo_alg = {
262  "lzo",
263  lzo_compress_init,
264  lzo_compress_uninit,
265  lzo_compress,
266  lzo_decompress
267 };
268 
269 #else /* if defined(ENABLE_LZO) */
270 static void
271 dummy(void)
272 {
273 }
274 #endif /* ENABLE_LZO */
#define D_INIT_MEDIUM
Definition: errlevel.h:105
#define D_COMP_LOW
Definition: errlevel.h:125
static bool buf_advance(struct buffer *buf, int size)
Definition: buffer.h:639
Packet geometry parameters.
Definition: mtu.h:93
#define D_COMP_ERRORS
Definition: errlevel.h:61
#define dmsg
Definition: error.h:174
static bool buf_safe(const struct buffer *buf, int len)
Definition: buffer.h:541
#define ASSERT(x)
Definition: error.h:221
int len
Length in bytes of the actual content within the allocated memory.
Definition: buffer.h:66
#define BPTR(buf)
Definition: buffer.h:124
static uint8_t * buf_prepend(struct buffer *buf, int size)
Definition: buffer.h:627
static void dummy(void)
Definition: lzo.c:271
time_t now
Definition: otime.c:36
#define FRAME_HEADROOM(f)
Definition: mtu.h:187
#define EXPANDED_SIZE(f)
Definition: mtu.h:172
#define msg
Definition: error.h:173
#define PAYLOAD_SIZE(f)
Definition: mtu.h:165
#define BLEN(buf)
Definition: buffer.h:127
unsigned __int8 uint8_t
Definition: config-msvc.h:123
static void check_malloc_return(const void *p)
Definition: buffer.h:1093
Wrapper structure for dynamically allocated memory.
Definition: buffer.h:60
#define M_FATAL
Definition: error.h:94
#define buf_init(buf, offset)
Definition: buffer.h:196
#define D_COMP
Definition: errlevel.h:160