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-2018 OpenVPN Inc <sales@openvpn.net>
9  * Copyright (C) 2013-2018 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 #include <lz4.h>
35 
36 #include "comp.h"
37 #include "error.h"
38 
39 #include "memdbg.h"
40 
41 
42 static void
43 lz4_compress_init(struct compress_context *compctx)
44 {
45  msg(D_INIT_MEDIUM, "LZ4 compression initializing");
46  ASSERT(compctx->flags & COMP_F_SWAP);
47 }
48 
49 static void
50 lz4v2_compress_init(struct compress_context *compctx)
51 {
52  msg(D_INIT_MEDIUM, "LZ4v2 compression initializing");
53 }
54 
55 static void
56 lz4_compress_uninit(struct compress_context *compctx)
57 {
58 }
59 
60 static bool
61 do_lz4_compress(struct buffer *buf,
62  struct buffer *work,
63  struct compress_context *compctx,
64  const struct frame *frame)
65 {
66  /*
67  * In order to attempt compression, length must be at least COMPRESS_THRESHOLD.
68  * and asymmetric compression must be disabled
69  */
70  if (buf->len >= COMPRESS_THRESHOLD && (compctx->flags & COMP_F_ALLOW_COMPRESS))
71  {
72  const size_t ps = PAYLOAD_SIZE(frame);
73  int zlen_max = ps + COMP_EXTRA_BUFFER(ps);
74  int zlen;
75 
76  ASSERT(buf_init(work, FRAME_HEADROOM(frame)));
77  ASSERT(buf_safe(work, zlen_max));
78 
79  if (buf->len > ps)
80  {
81  dmsg(D_COMP_ERRORS, "LZ4 compression buffer overflow");
82  buf->len = 0;
83  return false;
84  }
85 
86  zlen = LZ4_compress_default((const char *)BPTR(buf), (char *)BPTR(work), BLEN(buf), zlen_max);
87 
88  if (zlen <= 0)
89  {
90  dmsg(D_COMP_ERRORS, "LZ4 compression error");
91  buf->len = 0;
92  return false;
93  }
94 
95  ASSERT(buf_safe(work, zlen));
96  work->len = zlen;
97 
98 
99  dmsg(D_COMP, "LZ4 compress %d -> %d", buf->len, work->len);
100  compctx->pre_compress += buf->len;
101  compctx->post_compress += work->len;
102  return true;
103  }
104  return false;
105 }
106 
107 
108 static void
109 lz4_compress(struct buffer *buf, struct buffer work,
110  struct compress_context *compctx,
111  const struct frame *frame)
112 {
113  bool compressed;
114  if (buf->len <= 0)
115  {
116  return;
117  }
118 
119  compressed = do_lz4_compress(buf, &work, compctx, frame);
120 
121  /* On error do_lz4_compress sets buf len to zero, just return */
122  if (buf->len == 0)
123  {
124  return;
125  }
126 
127  /* did compression save us anything? */
128  {
129  uint8_t comp_head_byte = NO_COMPRESS_BYTE_SWAP;
130  if (compressed && work.len < buf->len)
131  {
132  *buf = work;
133  comp_head_byte = LZ4_COMPRESS_BYTE;
134  }
135 
136  {
137  uint8_t *head = BPTR(buf);
138  uint8_t *tail = BEND(buf);
139  ASSERT(buf_safe(buf, 1));
140  ++buf->len;
141 
142  /* move head byte of payload to tail */
143  *tail = *head;
144  *head = comp_head_byte;
145  }
146  }
147 }
148 
149 
150 static void
151 lz4v2_compress(struct buffer *buf, struct buffer work,
152  struct compress_context *compctx,
153  const struct frame *frame)
154 {
155  bool compressed;
156  if (buf->len <= 0)
157  {
158  return;
159  }
160 
161  compressed = do_lz4_compress(buf, &work, compctx, frame);
162 
163  /* On Error just return */
164  if (buf->len == 0)
165  {
166  return;
167  }
168 
169  /* did compression save us anything? Include 2 byte compression header
170  * in calculation */
171  if (compressed && work.len + 2 < buf->len)
172  {
173  ASSERT(buf_prepend(&work, 2));
174  uint8_t *head = BPTR(&work);
175  head[0] = COMP_ALGV2_INDICATOR_BYTE;
176  head[1] = COMP_ALGV2_LZ4_BYTE;
177  *buf = work;
178  }
179  else
180  {
181  compv2_escape_data_ifneeded(buf);
182  }
183 }
184 
185 static void
186 do_lz4_decompress(size_t zlen_max,
187  struct buffer *work,
188  struct buffer *buf,
189  struct compress_context *compctx)
190 {
191  int uncomp_len;
192  ASSERT(buf_safe(work, zlen_max));
193  uncomp_len = LZ4_decompress_safe((const char *)BPTR(buf), (char *)BPTR(work), (size_t)BLEN(buf), zlen_max);
194  if (uncomp_len <= 0)
195  {
196  dmsg(D_COMP_ERRORS, "LZ4 decompression error: %d", uncomp_len);
197  buf->len = 0;
198  return;
199  }
200 
201  ASSERT(buf_safe(work, uncomp_len));
202  work->len = uncomp_len;
203 
204  dmsg(D_COMP, "LZ4 decompress %d -> %d", buf->len, work->len);
205  compctx->pre_decompress += buf->len;
206  compctx->post_decompress += work->len;
207 
208  *buf = *work;
209 }
210 
211 static void
212 lz4_decompress(struct buffer *buf, struct buffer work,
213  struct compress_context *compctx,
214  const struct frame *frame)
215 {
216  size_t zlen_max = EXPANDED_SIZE(frame);
217  uint8_t c; /* flag indicating whether or not our peer compressed */
218 
219  if (buf->len <= 0)
220  {
221  return;
222  }
223 
224  ASSERT(buf_init(&work, FRAME_HEADROOM(frame)));
225 
226  /* do unframing/swap (assumes buf->len > 0) */
227  {
228  uint8_t *head = BPTR(buf);
229  c = *head;
230  --buf->len;
231  *head = *BEND(buf);
232  }
233 
234  if (c == LZ4_COMPRESS_BYTE) /* packet was compressed */
235  {
236  do_lz4_decompress(zlen_max, &work, buf, compctx);
237  }
238  else if (c == NO_COMPRESS_BYTE_SWAP) /* packet was not compressed */
239  {
240  }
241  else
242  {
243  dmsg(D_COMP_ERRORS, "Bad LZ4 decompression header byte: %d", c);
244  buf->len = 0;
245  }
246 }
247 
248 static void
249 lz4v2_decompress(struct buffer *buf, struct buffer work,
250  struct compress_context *compctx,
251  const struct frame *frame)
252 {
253  size_t zlen_max = EXPANDED_SIZE(frame);
254  uint8_t c; /* flag indicating whether or not our peer compressed */
255 
256  if (buf->len <= 0)
257  {
258  return;
259  }
260 
261  ASSERT(buf_init(&work, FRAME_HEADROOM(frame)));
262 
263  /* do unframing/swap (assumes buf->len > 0) */
264  uint8_t *head = BPTR(buf);
265  c = *head;
266 
267  /* Not compressed */
268  if (c != COMP_ALGV2_INDICATOR_BYTE)
269  {
270  return;
271  }
272 
273  /* Packet to short to make sense */
274  if (buf->len <= 1)
275  {
276  buf->len = 0;
277  return;
278  }
279 
280  c = head[1];
281  if (c == COMP_ALGV2_LZ4_BYTE) /* packet was compressed */
282  {
283  buf_advance(buf,2);
284  do_lz4_decompress(zlen_max, &work, buf, compctx);
285  }
286  else if (c == COMP_ALGV2_UNCOMPRESSED_BYTE)
287  {
288  buf_advance(buf,2);
289  }
290  else
291  {
292  dmsg(D_COMP_ERRORS, "Bad LZ4v2 decompression header byte: %d", c);
293  buf->len = 0;
294  }
295 }
296 
297 const struct compress_alg lz4_alg = {
298  "lz4",
299  lz4_compress_init,
300  lz4_compress_uninit,
301  lz4_compress,
302  lz4_decompress
303 };
304 
305 const struct compress_alg lz4v2_alg = {
306  "lz4v2",
307  lz4v2_compress_init,
308  lz4_compress_uninit,
309  lz4v2_compress,
310  lz4v2_decompress
311 };
312 #endif /* ENABLE_LZ4 */
#define D_INIT_MEDIUM
Definition: errlevel.h:106
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(flags,...)
Definition: error.h:153
#define ASSERT(x)
Definition: error.h:200
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
#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 BLEN(buf)
Definition: buffer.h:127
#define msg(flags,...)
Definition: error.h:149
static bool buf_safe(const struct buffer *buf, size_t len)
Definition: buffer.h:541
Wrapper structure for dynamically allocated memory.
Definition: buffer.h:60
#define buf_init(buf, offset)
Definition: buffer.h:196
#define BEND(buf)
Definition: buffer.h:125
#define D_COMP
Definition: errlevel.h:164