OpenVPN 3 Core Library
Loading...
Searching...
No Matches
compress.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// Base class and factory for compression/decompression objects.
13// Currently we support LZO, Snappy, and LZ4 implementations.
14
15#ifndef OPENVPN_COMPRESS_COMPRESS_H
16#define OPENVPN_COMPRESS_COMPRESS_H
17
20#include <openvpn/common/rc.hpp>
26
27namespace openvpn {
28
30#ifndef OPENVPN_DEBUG_COMPRESS
31#define OPENVPN_DEBUG_COMPRESS 1
32#endif
33
34class Compress : public RC<thread_unsafe_refcount>,
35 public logging::LoggingMixin<OPENVPN_DEBUG_COMPRESS, logging::LOG_LEVEL_TRACE, Compress>
36{
37 public:
39
40 // Compressor name
41 virtual const char *name() const = 0;
42
43 // Compression method implemented by underlying compression class.
44 // hint should normally be true to compress the data. If hint is
45 // false, the data may be uncompressible or already compressed,
46 // so method shouldn't attempt compression.
47 virtual void compress(BufferAllocated &buf, const bool hint) = 0;
48
49 // Decompression method implemented by underlying compression class.
50 virtual void decompress(BufferAllocated &buf) = 0;
51
52 protected:
53 // magic numbers to indicate no compression
54 enum
55 {
57 NO_COMPRESS_SWAP = 0xFB, // for better alignment handling, replace this byte with last byte of packet
58 };
59
60 // Compress V2 constants
61 enum
62 {
64
65 // Compression algs
68 };
69
70 Compress(const Frame::Ptr &frame_arg,
71 const SessionStats::Ptr &stats_arg)
72 : frame(frame_arg), stats(stats_arg)
73 {
74 }
75
77 {
79 buf.reset_size();
80 }
81
82 void do_swap(Buffer &buf, unsigned char op)
83 {
84 if (!buf.empty())
85 {
86 buf.push_back(buf[0]);
87 buf[0] = op;
88 }
89 else
90 buf.push_back(op);
91 }
92
93 void do_unswap(Buffer &buf)
94 {
95 if (buf.size() >= 2)
96 {
97 const unsigned char first = buf.pop_back();
98 buf.push_front(first);
99 }
100 }
101
102 // Push a COMPRESS_V2 header byte (value).
103 // Pass value == 0 to omit push.
104 void v2_push(Buffer &buf, unsigned char value)
105 {
106 unsigned char uc = buf[0];
107 if (value == 0 && uc != COMPRESS_V2_ESCAPE)
108 return;
109 unsigned char *esc = buf.prepend_alloc(2);
110 esc[0] = COMPRESS_V2_ESCAPE;
111 esc[1] = value;
112 }
113
114 // Pull a COMPRESS_V2 header byte.
115 // Returns the compress op (> 0) on success.
116 // Returns 0 if no compress op.
117 int v2_pull(Buffer &buf)
118 {
119 unsigned char uc = buf[0];
120 if (uc != COMPRESS_V2_ESCAPE)
121 return 0;
122 uc = buf[1];
123 buf.advance(2);
124 return uc;
125 }
126
129};
130} // namespace openvpn
131
132// include compressor implementations here
135
136#ifndef NO_LZO
138#endif
139#ifdef HAVE_LZ4
141#endif
142#ifdef HAVE_SNAPPY
144#endif
145
146namespace openvpn {
148{
149 public:
150 enum Type
151 {
153 COMP_STUB, // generic compression stub
154 COMP_STUBv2, // generic compression stub using v2 protocol
155 ANY, // placeholder for any method on client, before server assigns it
156 ANY_LZO, // placeholder for LZO or LZO_STUB methods on client, before server assigns it
163 };
164
165 OPENVPN_SIMPLE_EXCEPTION(compressor_unavailable);
166
167 CompressContext() = default;
168
169 CompressContext(const Type t, const bool asym)
170 : asym_(asym) // asym indicates asymmetrical compression where only downlink is compressed
171 {
172 if (!compressor_available(t))
173 throw compressor_unavailable();
174 type_ = t;
175 }
176
177 Type type() const
178 {
179 return type_;
180 }
181 bool asym() const
182 {
183 return asym_;
184 }
185
186 unsigned int extra_payload_bytes() const
187 {
188 switch (type_)
189 {
190 case NONE:
191 return 0;
192 case COMP_STUBv2:
193 case LZ4v2:
194 return 2; // worst case
195 default:
196 return 1;
197 }
198 }
199
201 {
202 switch (type_)
203 {
204 case NONE:
205 return new CompressNull(frame, stats);
206 case ANY_LZO:
207 case LZO_STUB:
208 return new CompressStub(frame, stats, false);
209 case ANY:
210 case COMP_STUB:
211 return new CompressStub(frame, stats, true);
212 case COMP_STUBv2:
213 return new CompressStubV2(frame, stats);
214#ifndef NO_LZO
215 case LZO:
216 return new CompressLZO(frame, stats, false, asym_);
217 case LZO_SWAP:
218 return new CompressLZO(frame, stats, true, asym_);
219#endif
220#ifdef HAVE_LZ4
221 case LZ4:
222 return new CompressLZ4(frame, stats, asym_);
223 case LZ4v2:
224 return new CompressLZ4v2(frame, stats, asym_);
225#endif
226#ifdef HAVE_SNAPPY
227 case SNAPPY:
228 return new CompressSnappy(frame, stats, asym_);
229#endif
230 default:
231 throw compressor_unavailable();
232 }
233 }
234
235 static bool compressor_available(const Type t)
236 {
237 switch (t)
238 {
239 case NONE:
240 case ANY:
241 case ANY_LZO:
242 case LZO_STUB:
243 case COMP_STUB:
244 case COMP_STUBv2:
245 return true;
246 case LZO:
247 case LZO_SWAP:
248#ifndef NO_LZO
249 return true;
250#else
251 return false;
252#endif
253 case LZ4:
254#ifdef HAVE_LZ4
255 return true;
256#else
257 return false;
258#endif
259 case LZ4v2:
260#ifdef HAVE_LZ4
261 return true;
262#else
263 return false;
264#endif
265 case SNAPPY:
266#ifdef HAVE_SNAPPY
267 return true;
268#else
269 return false;
270#endif
271 default:
272 return false;
273 }
274 }
275
276 // On the client, used to tell server which compression methods we support.
277 // Includes compression V1 and V2 methods.
278 const char *peer_info_string() const
279 {
280 switch (type_)
281 {
282#ifndef NO_LZO
283 case LZO:
284 return "IV_LZO=1\n";
285 case LZO_SWAP:
286 return "IV_LZO_SWAP=1\n";
287#endif
288#ifdef HAVE_LZ4
289 case LZ4:
290 return "IV_LZ4=1\n";
291#endif
292#ifdef HAVE_LZ4
293 case LZ4v2:
294 return "IV_LZ4v2=1\n";
295#endif
296#ifdef HAVE_SNAPPY
297 case SNAPPY:
298 return "IV_SNAPPY=1\n";
299#endif
300 case LZO_STUB:
301 case COMP_STUB:
302 case COMP_STUBv2:
303 return "IV_LZO_STUB=1\n"
304 "IV_COMP_STUB=1\n"
305 "IV_COMP_STUBv2=1\n";
306 case ANY:
307 return
308#ifdef HAVE_SNAPPY
309 "IV_SNAPPY=1\n"
310#endif
311#ifndef NO_LZO
312 "IV_LZO=1\n"
313 "IV_LZO_SWAP=1\n"
314#else
315 "IV_LZO_STUB=1\n"
316#endif
317#ifdef HAVE_LZ4
318 "IV_LZ4=1\n"
319 "IV_LZ4v2=1\n"
320#endif
321 "IV_COMP_STUB=1\n"
322 "IV_COMP_STUBv2=1\n";
323 case ANY_LZO:
324 return
325#ifndef NO_LZO
326 "IV_LZO=1\n"
327 "IV_LZO_SWAP=1\n"
328#else
329 "IV_LZO_STUB=1\n"
330#endif
331 "IV_COMP_STUB=1\n"
332 "IV_COMP_STUBv2=1\n";
333 default:
334 return nullptr;
335 }
336 }
337
338 // On the client, used to tell server which compression methods we support.
339 // Limited only to compression V1 methods.
340 const char *peer_info_string_v1() const
341 {
342 switch (type_)
343 {
344#ifndef NO_LZO
345 case LZO:
346 return "IV_LZO=1\n";
347 case LZO_SWAP:
348 return "IV_LZO_SWAP=1\n";
349#endif
350#ifdef HAVE_LZ4
351 case LZ4:
352 return "IV_LZ4=1\n";
353#endif
354#ifdef HAVE_SNAPPY
355 case SNAPPY:
356 return "IV_SNAPPY=1\n";
357#endif
358 case LZO_STUB:
359 case COMP_STUB:
360 return "IV_LZO_STUB=1\n"
361 "IV_COMP_STUB=1\n";
362 case ANY:
363 return
364#ifdef HAVE_SNAPPY
365 "IV_SNAPPY=1\n"
366#endif
367#ifndef NO_LZO
368 "IV_LZO=1\n"
369 "IV_LZO_SWAP=1\n"
370#else
371 "IV_LZO_STUB=1\n"
372#endif
373#ifdef HAVE_LZ4
374 "IV_LZ4=1\n"
375#endif
376 "IV_COMP_STUB=1\n";
377 case ANY_LZO:
378 return
379#ifndef NO_LZO
380 "IV_LZO=1\n"
381 "IV_LZO_SWAP=1\n"
382#else
383 "IV_LZO_STUB=1\n"
384#endif
385 "IV_COMP_STUB=1\n";
386 default:
387 return nullptr;
388 }
389 }
390
391 const char *options_string() const
392 {
393 switch (type_)
394 {
395 case LZO:
396 case LZO_STUB:
397 case SNAPPY:
398 case LZ4:
399 case LZ4v2:
400 case LZO_SWAP:
401 case COMP_STUB:
402 case COMP_STUBv2:
403 case ANY:
404 case ANY_LZO:
405 return "comp-lzo";
406 default:
407 return nullptr;
408 }
409 }
410
411 const char *str() const
412 {
413 switch (type_)
414 {
415 case LZO:
416 return "LZO";
417 case LZO_SWAP:
418 return "LZO_SWAP";
419 case LZ4:
420 return "LZ4";
421 case LZ4v2:
422 return "LZ4v2";
423 case SNAPPY:
424 return "SNAPPY";
425 case LZO_STUB:
426 return "LZO_STUB";
427 case COMP_STUB:
428 return "COMP_STUB";
429 case COMP_STUBv2:
430 return "COMP_STUBv2";
431 case ANY:
432 return "ANY";
433 case ANY_LZO:
434 return "ANY_LZO";
435 default:
436 return "NONE";
437 }
438 }
439
440 /* This function returns a parseable string representation of the compress
441 * method. NOTE: returns nullptr if no mapping is possible */
442 const char *method_to_string() const
443 {
444 switch (type_)
445 {
446 case LZO:
447 return "lzo";
448 case LZO_SWAP:
449 return "lzo-swap";
450 case LZO_STUB:
451 return "lzo-stub";
452 case LZ4:
453 return "lz4";
454 case LZ4v2:
455 return "lz4v2";
456 case SNAPPY:
457 return "snappy";
458 case COMP_STUB:
459 return "stub";
460 case COMP_STUBv2:
461 return "stub-v2";
462 default:
463 return nullptr;
464 }
465 }
466
467 static Type parse_method(const std::string &method)
468 {
469 if (method == "stub-v2")
470 return COMP_STUBv2;
471 if (method == "lz4-v2")
472 return LZ4v2;
473 if (method == "lz4")
474 return LZ4;
475 if (method == "lzo")
476 return LZO;
477 if (method == "lzo-swap")
478 return LZO_SWAP;
479 if (method == "lzo-stub")
480 return LZO_STUB;
481 if (method == "snappy")
482 return SNAPPY;
483 if (method == "stub")
484 return COMP_STUB;
485 return NONE;
486 }
487
488 static Type stub(const Type t)
489 {
490 switch (t)
491 {
492 case COMP_STUBv2:
493 case LZ4v2:
494 return COMP_STUBv2;
495 default:
496 return COMP_STUB;
497 }
498 }
499
507 static bool is_any_stub(const Type t)
508 {
509 switch (t)
510 {
511 case LZO_STUB:
512 case COMP_STUB:
513 case COMP_STUBv2:
514 return true;
515 default:
516 return false;
517 }
518 }
519
520 static void init_static()
521 {
522#ifndef NO_LZO
524#endif
525 }
526
527 private:
529 bool asym_ = false;
530};
531
532} // namespace openvpn
533
534#endif // OPENVPN_COMPRESS_COMPRESS_H
static Type parse_method(const std::string &method)
Definition compress.hpp:467
const char * options_string() const
Definition compress.hpp:391
unsigned int extra_payload_bytes() const
Definition compress.hpp:186
const char * str() const
Definition compress.hpp:411
static Type stub(const Type t)
Definition compress.hpp:488
const char * method_to_string() const
Definition compress.hpp:442
Compress::Ptr new_compressor(const Frame::Ptr &frame, const SessionStats::Ptr &stats)
Definition compress.hpp:200
const char * peer_info_string() const
Definition compress.hpp:278
static bool is_any_stub(const Type t)
Definition compress.hpp:507
OPENVPN_SIMPLE_EXCEPTION(compressor_unavailable)
const char * peer_info_string_v1() const
Definition compress.hpp:340
static void init_static()
Definition compress.hpp:520
static bool compressor_available(const Type t)
Definition compress.hpp:235
CompressContext(const Type t, const bool asym)
Definition compress.hpp:169
static void init_static()
Definition lzoasym.hpp:45
void do_swap(Buffer &buf, unsigned char op)
Definition compress.hpp:82
virtual void decompress(BufferAllocated &buf)=0
void error(BufferAllocated &buf)
Definition compress.hpp:76
virtual const char * name() const =0
void do_unswap(Buffer &buf)
Definition compress.hpp:93
void v2_push(Buffer &buf, unsigned char value)
Definition compress.hpp:104
Frame::Ptr frame
Definition compress.hpp:127
virtual void compress(BufferAllocated &buf, const bool hint)=0
SessionStats::Ptr stats
Definition compress.hpp:128
Compress(const Frame::Ptr &frame_arg, const SessionStats::Ptr &stats_arg)
Definition compress.hpp:70
int v2_pull(Buffer &buf)
Definition compress.hpp:117
T * prepend_alloc(const size_t size)
Allocate space for prepending data to the buffer.
Definition buffer.hpp:1594
void push_back(const T &value)
Append a T object to the end of the array, resizing the array if necessary.
Definition buffer.hpp:1479
size_t size() const
Returns the size of the buffer in T objects.
Definition buffer.hpp:1241
T pop_back()
Removes and returns the last element from the buffer.
Definition buffer.hpp:1247
void advance(const size_t delta)
Advances the buffer by the specified delta.
Definition buffer.hpp:1276
bool empty() const
Returns true if the buffer is empty.
Definition buffer.hpp:1235
void push_front(const T &value)
Append a T object to the array, with possible resize.
Definition buffer.hpp:1487
void reset_size()
Resets the size of the buffer to zero.
Definition buffer.hpp:1169
Reference count base class for objects tracked by RCPtr. Disallows copying and assignment.
Definition rc.hpp:908
virtual void error(const size_t type, const std::string *text=nullptr)