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