OpenVPN
comp-lz4.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-2017 OpenVPN Technologies, Inc. <sales@openvpn.net>
9  * Copyright (C) 2013-2017 Gert Doering <gert@greenie.muc.de>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2
13  * as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #elif defined(_MSC_VER)
28 #include "config-msvc.h"
29 #endif
30 
31 #include "syshead.h"
32 
33 #if defined(ENABLE_LZ4)
34 
35 #if defined(NEED_COMPAT_LZ4)
36 #include "compat-lz4.h"
37 #else
38 #include "lz4.h"
39 #endif
40 
41 #include "comp.h"
42 #include "error.h"
43 
44 #include "memdbg.h"
45 
46 
47 static void
48 lz4_compress_init(struct compress_context *compctx)
49 {
50  msg(D_INIT_MEDIUM, "LZ4 compression initializing");
51  ASSERT(compctx->flags & COMP_F_SWAP);
52 }
53 
54 static void
55 lz4v2_compress_init(struct compress_context *compctx)
56 {
57  msg(D_INIT_MEDIUM, "LZ4v2 compression initializing");
58 }
59 
60 static void
61 lz4_compress_uninit(struct compress_context *compctx)
62 {
63 }
64 
65 static bool
66 do_lz4_compress(struct buffer *buf,
67  struct buffer *work,
68  struct compress_context *compctx,
69  const struct frame *frame)
70 {
71  /*
72  * In order to attempt compression, length must be at least COMPRESS_THRESHOLD.
73  */
74  if (buf->len >= COMPRESS_THRESHOLD)
75  {
76  const size_t ps = PAYLOAD_SIZE(frame);
77  int zlen_max = ps + COMP_EXTRA_BUFFER(ps);
78  int zlen;
79 
80  ASSERT(buf_init(work, FRAME_HEADROOM(frame)));
81  ASSERT(buf_safe(work, zlen_max));
82 
83  if (buf->len > ps)
84  {
85  dmsg(D_COMP_ERRORS, "LZ4 compression buffer overflow");
86  buf->len = 0;
87  return false;
88  }
89 
90  zlen = LZ4_compress_default((const char *)BPTR(buf), (char *)BPTR(work), BLEN(buf), zlen_max);
91 
92  if (zlen <= 0)
93  {
94  dmsg(D_COMP_ERRORS, "LZ4 compression error");
95  buf->len = 0;
96  return false;
97  }
98 
99  ASSERT(buf_safe(work, zlen));
100  work->len = zlen;
101 
102 
103  dmsg(D_COMP, "LZ4 compress %d -> %d", buf->len, work->len);
104  compctx->pre_compress += buf->len;
105  compctx->post_compress += work->len;
106  return true;
107  }
108  return false;
109 }
110 
111 
112 static void
113 lz4_compress(struct buffer *buf, struct buffer work,
114  struct compress_context *compctx,
115  const struct frame *frame)
116 {
117  bool compressed;
118  if (buf->len <= 0)
119  {
120  return;
121  }
122 
123  compressed = do_lz4_compress(buf, &work, compctx, frame);
124 
125  /* On error do_lz4_compress sets buf len to zero, just return */
126  if (buf->len == 0)
127  {
128  return;
129  }
130 
131  /* did compression save us anything? */
132  {
133  uint8_t comp_head_byte = NO_COMPRESS_BYTE_SWAP;
134  if (compressed && work.len < buf->len)
135  {
136  *buf = work;
137  comp_head_byte = LZ4_COMPRESS_BYTE;
138  }
139 
140  {
141  uint8_t *head = BPTR(buf);
142  uint8_t *tail = BEND(buf);
143  ASSERT(buf_safe(buf, 1));
144  ++buf->len;
145 
146  /* move head byte of payload to tail */
147  *tail = *head;
148  *head = comp_head_byte;
149  }
150  }
151 }
152 
153 
154 static void
155 lz4v2_compress(struct buffer *buf, struct buffer work,
156  struct compress_context *compctx,
157  const struct frame *frame)
158 {
159  bool compressed;
160  if (buf->len <= 0)
161  {
162  return;
163  }
164 
165  compressed = do_lz4_compress(buf, &work, compctx, frame);
166 
167  /* On Error just return */
168  if (buf->len == 0)
169  {
170  return;
171  }
172 
173  /* did compression save us anything? Include 2 byte compression header
174  * in calculation */
175  if (compressed && work.len + 2 < buf->len)
176  {
177  ASSERT(buf_prepend(&work, 2));
178  uint8_t *head = BPTR(&work);
179  head[0] = COMP_ALGV2_INDICATOR_BYTE;
180  head[1] = COMP_ALGV2_LZ4_BYTE;
181  *buf = work;
182  }
183  else
184  {
185  compv2_escape_data_ifneeded(buf);
186  }
187 }
188 
189 static void
190 do_lz4_decompress(size_t zlen_max,
191  struct buffer *work,
192  struct buffer *buf,
193  struct compress_context *compctx)
194 {
195  int uncomp_len;
196  ASSERT(buf_safe(work, zlen_max));
197  uncomp_len = LZ4_decompress_safe((const char *)BPTR(buf), (char *)BPTR(work), (size_t)BLEN(buf), zlen_max);
198  if (uncomp_len <= 0)
199  {
200  dmsg(D_COMP_ERRORS, "LZ4 decompression error: %d", uncomp_len);
201  buf->len = 0;
202  return;
203  }
204 
205  ASSERT(buf_safe(work, uncomp_len));
206  work->len = uncomp_len;
207 
208  dmsg(D_COMP, "LZ4 decompress %d -> %d", buf->len, work->len);
209  compctx->pre_decompress += buf->len;
210  compctx->post_decompress += work->len;
211 
212  *buf = *work;
213 }
214 
215 static void
216 lz4_decompress(struct buffer *buf, struct buffer work,
217  struct compress_context *compctx,
218  const struct frame *frame)
219 {
220  size_t zlen_max = EXPANDED_SIZE(frame);
221  uint8_t c; /* flag indicating whether or not our peer compressed */
222 
223  if (buf->len <= 0)
224  {
225  return;
226  }
227 
228  ASSERT(buf_init(&work, FRAME_HEADROOM(frame)));
229 
230  /* do unframing/swap (assumes buf->len > 0) */
231  {
232  uint8_t *head = BPTR(buf);
233  c = *head;
234  --buf->len;
235  *head = *BEND(buf);
236  }
237 
238  if (c == LZ4_COMPRESS_BYTE) /* packet was compressed */
239  {
240  do_lz4_decompress(zlen_max, &work, buf, compctx);
241  }
242  else if (c == NO_COMPRESS_BYTE_SWAP) /* packet was not compressed */
243  {
244  }
245  else
246  {
247  dmsg(D_COMP_ERRORS, "Bad LZ4 decompression header byte: %d", c);
248  buf->len = 0;
249  }
250 }
251 
252 static void
253 lz4v2_decompress(struct buffer *buf, struct buffer work,
254  struct compress_context *compctx,
255  const struct frame *frame)
256 {
257  size_t zlen_max = EXPANDED_SIZE(frame);
258  uint8_t c; /* flag indicating whether or not our peer compressed */
259 
260  if (buf->len <= 0)
261  {
262  return;
263  }
264 
265  ASSERT(buf_init(&work, FRAME_HEADROOM(frame)));
266 
267  /* do unframing/swap (assumes buf->len > 0) */
268  uint8_t *head = BPTR(buf);
269  c = *head;
270 
271  /* Not compressed */
272  if (c != COMP_ALGV2_INDICATOR_BYTE)
273  {
274  return;
275  }
276 
277  /* Packet to short to make sense */
278  if (buf->len <= 1)
279  {
280  buf->len = 0;
281  return;
282  }
283 
284  c = head[1];
285  if (c == COMP_ALGV2_LZ4_BYTE) /* packet was compressed */
286  {
287  buf_advance(buf,2);
288  do_lz4_decompress(zlen_max, &work, buf, compctx);
289  }
290  else if (c == COMP_ALGV2_UNCOMPRESSED_BYTE)
291  {
292  buf_advance(buf,2);
293  }
294  else
295  {
296  dmsg(D_COMP_ERRORS, "Bad LZ4v2 decompression header byte: %d", c);
297  buf->len = 0;
298  }
299 }
300 
301 const struct compress_alg lz4_alg = {
302  "lz4",
303  lz4_compress_init,
304  lz4_compress_uninit,
305  lz4_compress,
306  lz4_decompress
307 };
308 
309 const struct compress_alg lz4v2_alg = {
310  "lz4v2",
311  lz4v2_compress_init,
312  lz4_compress_uninit,
313  lz4v2_compress,
314  lz4v2_decompress
315 };
316 
317 #else /* if defined(ENABLE_LZ4) */
318 static void
319 dummy(void)
320 {
321 }
322 #endif /* ENABLE_LZ4 */
#define D_INIT_MEDIUM
Definition: errlevel.h:105
static bool buf_advance(struct buffer *buf, int size)
Definition: buffer.h:613
Packet geometry parameters.
Definition: mtu.h:93
#define D_COMP_ERRORS
Definition: errlevel.h:61
static bool buf_safe(const struct buffer *buf, int len)
Definition: buffer.h:515
#define ASSERT(x)
Definition: error.h:221
static void dummy(void)
Definition: comp-lz4.c:319
LZ4LIB_API int LZ4_decompress_safe(const char *source, char *dest, int compressedSize, int maxDecompressedSize)
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:601
#define FRAME_HEADROOM(f)
Definition: mtu.h:187
#define EXPANDED_SIZE(f)
Definition: mtu.h:172
#define PAYLOAD_SIZE(f)
Definition: mtu.h:165
#define dmsg
Definition: error.h:174
#define BLEN(buf)
Definition: buffer.h:127
unsigned __int8 uint8_t
Definition: config-msvc.h:123
LZ4LIB_API int LZ4_compress_default(const char *source, char *dest, int sourceSize, int maxDestSize)
#define msg
Definition: error.h:173
Wrapper structure for dynamically allocated memory.
Definition: buffer.h:60
#define buf_init(buf, offset)
Definition: buffer.h:198
#define BEND(buf)
Definition: buffer.h:125
#define D_COMP
Definition: errlevel.h:161