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-2023 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 #endif
31 
32 #include "syshead.h"
33 
34 #if defined(ENABLE_LZO)
35 
36 #include "comp.h"
37 #include "error.h"
38 #include "otime.h"
39 
40 #include "memdbg.h"
41 
49 static bool
50 lzo_adaptive_compress_test(struct lzo_adaptive_compress *ac)
51 {
52  const bool save = ac->compress_state;
53  const time_t local_now = now;
54 
55  if (!ac->compress_state)
56  {
57  if (local_now >= ac->next)
58  {
59  if (ac->n_total > AC_MIN_BYTES
60  && (ac->n_total - ac->n_comp) < (ac->n_total / (100 / AC_SAVE_PCT)))
61  {
62  ac->compress_state = true;
63  ac->next = local_now + AC_OFF_SEC;
64  }
65  else
66  {
67  ac->next = local_now + AC_SAMP_SEC;
68  }
69  dmsg(D_COMP, "lzo_adaptive_compress_test: comp=%d total=%d", ac->n_comp, ac->n_total);
70  ac->n_total = ac->n_comp = 0;
71  }
72  }
73  else
74  {
75  if (local_now >= ac->next)
76  {
77  ac->next = local_now + AC_SAMP_SEC;
78  ac->n_total = ac->n_comp = 0;
79  ac->compress_state = false;
80  }
81  }
82 
83  if (ac->compress_state != save)
84  {
85  dmsg(D_COMP_LOW, "Adaptive compression state %s", (ac->compress_state ? "OFF" : "ON"));
86  }
87 
88  return !ac->compress_state;
89 }
90 
91 static inline void
92 lzo_adaptive_compress_data(struct lzo_adaptive_compress *ac, int n_total, int n_comp)
93 {
94  ac->n_total += n_total;
95  ac->n_comp += n_comp;
96 }
97 
98 static void
99 lzo_compress_init(struct compress_context *compctx)
100 {
101  msg(D_INIT_MEDIUM, "LZO compression initializing");
102  ASSERT(!(compctx->flags & COMP_F_SWAP));
103  compctx->wu.lzo.wmem_size = LZO_WORKSPACE;
104 
105  int lzo_status = lzo_init();
106  if (lzo_status != LZO_E_OK)
107  {
108  msg(M_FATAL, "Cannot initialize LZO compression library (lzo_init() returns %d)", lzo_status);
109  }
110  compctx->wu.lzo.wmem = (lzo_voidp) lzo_malloc(compctx->wu.lzo.wmem_size);
111  check_malloc_return(compctx->wu.lzo.wmem);
112 }
113 
114 static void
115 lzo_compress_uninit(struct compress_context *compctx)
116 {
117  lzo_free(compctx->wu.lzo.wmem);
118  compctx->wu.lzo.wmem = NULL;
119 }
120 
121 static inline bool
122 lzo_compression_enabled(struct compress_context *compctx)
123 {
124  if (!(compctx->flags & COMP_F_ALLOW_COMPRESS))
125  {
126  return false;
127  }
128  else
129  {
130  if (compctx->flags & COMP_F_ADAPTIVE)
131  {
132  return lzo_adaptive_compress_test(&compctx->wu.lzo.ac);
133  }
134  else
135  {
136  return true;
137  }
138  }
139 }
140 
141 static void
142 lzo_compress(struct buffer *buf, struct buffer work,
143  struct compress_context *compctx,
144  const struct frame *frame)
145 {
146  lzo_uint zlen = 0;
147  int err;
148  bool compressed = false;
149 
150  if (buf->len <= 0)
151  {
152  return;
153  }
154 
155  /*
156  * In order to attempt compression, length must be at least COMPRESS_THRESHOLD,
157  * and our adaptive level must give the OK.
158  */
159  if (buf->len >= COMPRESS_THRESHOLD && lzo_compression_enabled(compctx))
160  {
161  const size_t ps = frame->buf.payload_size;
162  ASSERT(buf_init(&work, frame->buf.headroom));
163  ASSERT(buf_safe(&work, ps + COMP_EXTRA_BUFFER(ps)));
164 
165  if (buf->len > ps)
166  {
167  dmsg(D_COMP_ERRORS, "LZO compression buffer overflow");
168  buf->len = 0;
169  return;
170  }
171 
172  err = LZO_COMPRESS(BPTR(buf), BLEN(buf), BPTR(&work), &zlen, compctx->wu.lzo.wmem);
173  if (err != LZO_E_OK)
174  {
175  dmsg(D_COMP_ERRORS, "LZO compression error: %d", err);
176  buf->len = 0;
177  return;
178  }
179 
180  ASSERT(buf_safe(&work, zlen));
181  work.len = zlen;
182  compressed = true;
183 
184  dmsg(D_COMP, "LZO compress %d -> %d", buf->len, work.len);
185  compctx->pre_compress += buf->len;
186  compctx->post_compress += work.len;
187 
188  /* tell adaptive level about our success or lack thereof in getting any size reduction */
189  if (compctx->flags & COMP_F_ADAPTIVE)
190  {
191  lzo_adaptive_compress_data(&compctx->wu.lzo.ac, buf->len, work.len);
192  }
193  }
194 
195  /* did compression save us anything ? */
196  if (compressed && work.len < buf->len)
197  {
198  uint8_t *header = buf_prepend(&work, 1);
199  *header = LZO_COMPRESS_BYTE;
200  *buf = work;
201  }
202  else
203  {
204  uint8_t *header = buf_prepend(buf, 1);
205  *header = NO_COMPRESS_BYTE;
206  }
207 }
208 
209 static void
210 lzo_decompress(struct buffer *buf, struct buffer work,
211  struct compress_context *compctx,
212  const struct frame *frame)
213 {
214  lzo_uint zlen = frame->buf.payload_size;
215  int err;
216  uint8_t c; /* flag indicating whether or not our peer compressed */
217 
218  if (buf->len <= 0)
219  {
220  return;
221  }
222 
223  ASSERT(buf_init(&work, frame->buf.headroom));
224 
225  c = *BPTR(buf);
226  ASSERT(buf_advance(buf, 1));
227 
228  if (c == LZO_COMPRESS_BYTE) /* packet was compressed */
229  {
230  ASSERT(buf_safe(&work, zlen));
231  err = LZO_DECOMPRESS(BPTR(buf), BLEN(buf), BPTR(&work), &zlen,
232  compctx->wu.lzo.wmem);
233  if (err != LZO_E_OK)
234  {
235  dmsg(D_COMP_ERRORS, "LZO decompression error: %d", err);
236  buf->len = 0;
237  return;
238  }
239 
240  ASSERT(buf_safe(&work, zlen));
241  work.len = zlen;
242 
243  dmsg(D_COMP, "LZO decompress %d -> %d", buf->len, work.len);
244  compctx->pre_decompress += buf->len;
245  compctx->post_decompress += work.len;
246 
247  *buf = work;
248  }
249  else if (c == NO_COMPRESS_BYTE) /* packet was not compressed */
250  {
251  /* nothing to do */
252  }
253  else
254  {
255  dmsg(D_COMP_ERRORS, "Bad LZO decompression header byte: %d", c);
256  buf->len = 0;
257  }
258 }
259 
260 const struct compress_alg lzo_alg = {
261  "lzo",
262  lzo_compress_init,
263  lzo_compress_uninit,
264  lzo_compress,
265  lzo_decompress
266 };
267 #endif /* ENABLE_LZO */
buf_safe
static bool buf_safe(const struct buffer *buf, size_t len)
Definition: buffer.h:538
buffer::len
int len
Length in bytes of the actual content within the allocated memory.
Definition: buffer.h:66
M_FATAL
#define M_FATAL
Definition: error.h:95
buf_init
#define buf_init(buf, offset)
Definition: buffer.h:209
COMP_F_ALLOW_COMPRESS
#define COMP_F_ALLOW_COMPRESS
Definition: comp.h:36
dmsg
#define dmsg(flags,...)
Definition: error.h:154
frame
Packet geometry parameters.
Definition: mtu.h:98
ASSERT
#define ASSERT(x)
Definition: error.h:201
buf_advance
static bool buf_advance(struct buffer *buf, int size)
Definition: buffer.h:636
D_COMP
#define D_COMP
Definition: errlevel.h:166
BLEN
#define BLEN(buf)
Definition: buffer.h:127
frame::payload_size
int payload_size
the maximum size that a payload that our buffers can hold from either tun device or network link.
Definition: mtu.h:102
COMP_F_SWAP
#define COMP_F_SWAP
Definition: comp.h:37
D_COMP_ERRORS
#define D_COMP_ERRORS
Definition: errlevel.h:61
buffer
Wrapper structure for dynamically allocated memory.
Definition: buffer.h:60
frame::buf
struct frame::@6 buf
syshead.h
COMP_F_ADAPTIVE
#define COMP_F_ADAPTIVE
Definition: comp.h:35
BPTR
#define BPTR(buf)
Definition: buffer.h:124
D_COMP_LOW
#define D_COMP_LOW
Definition: errlevel.h:129
buf_prepend
static uint8_t * buf_prepend(struct buffer *buf, int size)
Definition: buffer.h:624
comp.h
otime.h
now
time_t now
Definition: otime.c:34
config.h
check_malloc_return
static void check_malloc_return(void *p)
Definition: buffer.h:1109
memdbg.h
msg
#define msg(flags,...)
Definition: error.h:150
frame::headroom
int headroom
the headroom in the buffer, this is choosen to allow all potential header to be added before the pack...
Definition: mtu.h:108
D_INIT_MEDIUM
#define D_INIT_MEDIUM
Definition: errlevel.h:104