mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-20 04:02:15 +02:00
Added compression methods LZO, LZ4, and Snappy.
Note that only LZO has been tested yet.
This commit is contained in:
parent
5ac932f842
commit
b31a80da6b
@ -29,6 +29,8 @@ namespace openvpn {
|
||||
OPENVPN_SIMPLE_EXCEPTION_INHERIT(buffer_exception, buffer_const_index);
|
||||
OPENVPN_SIMPLE_EXCEPTION_INHERIT(buffer_exception, buffer_push_front_headroom);
|
||||
OPENVPN_SIMPLE_EXCEPTION_INHERIT(buffer_exception, buffer_no_reset_impl);
|
||||
OPENVPN_SIMPLE_EXCEPTION_INHERIT(buffer_exception, buffer_pop_back);
|
||||
OPENVPN_SIMPLE_EXCEPTION_INHERIT(buffer_exception, buffer_set_size);
|
||||
|
||||
template <typename T>
|
||||
class BufferType {
|
||||
@ -122,12 +124,9 @@ namespace openvpn {
|
||||
// of T objects.
|
||||
void set_size(const size_t size)
|
||||
{
|
||||
size_ = std::min(max_size(), size);
|
||||
}
|
||||
|
||||
void inc_size(const size_t size)
|
||||
{
|
||||
size_ = std::min(max_size(), size_ + size);
|
||||
if (size > max_size())
|
||||
OPENVPN_BUFFER_THROW(buffer_set_size);
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
// append a T object to array, with possible resize
|
||||
@ -148,6 +147,13 @@ namespace openvpn {
|
||||
*data() = value;
|
||||
}
|
||||
|
||||
T pop_back()
|
||||
{
|
||||
if (!size_)
|
||||
OPENVPN_BUFFER_THROW(buffer_pop_back);
|
||||
return *(data()+(--size_));
|
||||
}
|
||||
|
||||
T pop_front()
|
||||
{
|
||||
T ret = (*this)[0];
|
||||
|
21
openvpn/compress/compnull.hpp
Normal file
21
openvpn/compress/compnull.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef OPENVPN_COMPRESS_COMPNULL_H
|
||||
#define OPENVPN_COMPRESS_COMPNULL_H
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class CompressNull : public Compress
|
||||
{
|
||||
public:
|
||||
CompressNull(const Frame::Ptr& frame, const ProtoStats::Ptr& stats)
|
||||
: Compress(frame, stats)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void compress(BufferAllocated& buf, const bool hint) {}
|
||||
virtual void decompress(BufferAllocated& buf) {}
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMPRESS_COMPNULL_H
|
@ -8,32 +8,82 @@
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/log/protostats.hpp>
|
||||
|
||||
#if defined(OPENVPN_DEBUG_COMPRESS)
|
||||
#define OPENVPN_LOG_COMPRESS(x) OPENVPN_LOG(x)
|
||||
#else
|
||||
#define OPENVPN_LOG_COMPRESS(x)
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
class Compress : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef boost::intrusive_ptr<Compress> Ptr;
|
||||
|
||||
// Compression method implemented by underlying compression class
|
||||
// Compression method implemented by underlying compression class.
|
||||
// hint should normally be true to compress the data. If hint is
|
||||
// false, the data may be uncompressible or already compressed.
|
||||
// false, the data may be uncompressible or already compressed,
|
||||
// so method shouldn't attempt compression.
|
||||
virtual void compress(BufferAllocated& buf, const bool hint) = 0;
|
||||
|
||||
// Decompression method implemented by underlying compression class.
|
||||
virtual void decompress(BufferAllocated& buf) = 0;
|
||||
|
||||
protected:
|
||||
// magic numbers to indicate no compression
|
||||
enum {
|
||||
NO_COMPRESS = 0xFA,
|
||||
NO_COMPRESS_SWAP = 0xFB, // for better alignment handling, replace this byte with last byte of packet
|
||||
};
|
||||
|
||||
Compress(const Frame::Ptr& frame_arg,
|
||||
const ProtoStats::Ptr& stats_arg)
|
||||
: frame(frame_arg), stats(stats_arg) {}
|
||||
|
||||
void error(BufferAllocated& buf)
|
||||
{
|
||||
stats->error(ProtoStats::COMPRESS_ERROR);
|
||||
buf.reset_size();
|
||||
}
|
||||
|
||||
void do_swap(Buffer& buf, unsigned char op)
|
||||
{
|
||||
if (buf.size())
|
||||
{
|
||||
buf.push_back(buf[0]);
|
||||
buf[0] = op;
|
||||
}
|
||||
else
|
||||
buf.push_back(op);
|
||||
}
|
||||
|
||||
void do_unswap(Buffer& buf)
|
||||
{
|
||||
if (buf.size() >= 2)
|
||||
{
|
||||
const unsigned char first = buf.pop_back();
|
||||
buf.push_front(first);
|
||||
}
|
||||
}
|
||||
|
||||
Frame::Ptr frame;
|
||||
ProtoStats::Ptr stats;
|
||||
};
|
||||
}// namespace openvpn
|
||||
|
||||
// include compressor implementations here
|
||||
#include <openvpn/compress/lzostub.hpp>
|
||||
#include <openvpn/compress/compnull.hpp>
|
||||
#include <openvpn/compress/compstub.hpp>
|
||||
|
||||
#ifdef HAVE_LZO
|
||||
#include <openvpn/compress/lzo.hpp>
|
||||
#endif
|
||||
#ifdef HAVE_LZ4
|
||||
#include <openvpn/compress/lz4.hpp>
|
||||
#endif
|
||||
#ifdef HAVE_SNAPPY
|
||||
#include <openvpn/compress/snappy.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
class CompressContext
|
||||
@ -41,25 +91,91 @@ namespace openvpn {
|
||||
public:
|
||||
enum Type {
|
||||
NONE,
|
||||
LZO_STUB,
|
||||
COMP_STUB, // generic compression stub
|
||||
ANY, // placeholder for any method on client, before server assigns it
|
||||
ANY_LZO, // placeholder for LZO or LZO_STUB methods on client, before server assigns it
|
||||
LZO,
|
||||
LZO_SWAP,
|
||||
LZO_STUB,
|
||||
LZ4,
|
||||
SNAPPY,
|
||||
};
|
||||
|
||||
OPENVPN_SIMPLE_EXCEPTION(compress_instantiate);
|
||||
OPENVPN_SIMPLE_EXCEPTION(compressor_unavailable);
|
||||
|
||||
CompressContext() : type_(NONE) {}
|
||||
explicit CompressContext(const Type t) : type_(t) {}
|
||||
|
||||
unsigned int extra_payload_bytes() const { return 1; }
|
||||
explicit CompressContext(const Type t)
|
||||
{
|
||||
if (!compressor_available(t))
|
||||
throw compressor_unavailable();
|
||||
type_ = t;
|
||||
}
|
||||
|
||||
unsigned int extra_payload_bytes() const { return type_ == NONE ? 0 : 1; }
|
||||
|
||||
Compress::Ptr new_compressor(const Frame::Ptr& frame, const ProtoStats::Ptr& stats)
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case NONE:
|
||||
return new CompressNull(frame, stats);
|
||||
case ANY:
|
||||
case ANY_LZO:
|
||||
case LZO_STUB:
|
||||
return new CompressLZOStub(frame, stats);
|
||||
return new CompressStub(frame, stats, false);
|
||||
case COMP_STUB:
|
||||
return new CompressStub(frame, stats, true);
|
||||
#ifdef HAVE_LZO
|
||||
case LZO:
|
||||
return new CompressLZO(frame, stats, false);
|
||||
case LZO_SWAP:
|
||||
return new CompressLZO(frame, stats, true);
|
||||
#endif
|
||||
#ifdef HAVE_LZ4
|
||||
case LZ4:
|
||||
return new CompressLZ4(frame, stats);
|
||||
#endif
|
||||
#ifdef HAVE_SNAPPY
|
||||
case SNAPPY:
|
||||
return new CompressSnappy(frame, stats);
|
||||
#endif
|
||||
default:
|
||||
throw compress_instantiate();
|
||||
throw compressor_unavailable();
|
||||
}
|
||||
}
|
||||
|
||||
static bool compressor_available(const Type t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case NONE:
|
||||
case ANY:
|
||||
case ANY_LZO:
|
||||
case LZO_STUB:
|
||||
case COMP_STUB:
|
||||
return true;
|
||||
case LZO:
|
||||
case LZO_SWAP:
|
||||
#ifdef HAVE_LZO
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
case LZ4:
|
||||
#ifdef HAVE_LZ4
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
case SNAPPY:
|
||||
#ifdef HAVE_SNAPPY
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,10 +183,49 @@ namespace openvpn {
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
#ifdef HAVE_LZO
|
||||
case LZO:
|
||||
return NULL;
|
||||
return "IV_LZO=1\n";
|
||||
case LZO_SWAP:
|
||||
return "IV_LZO_SWAP=1\n";
|
||||
#endif
|
||||
#ifdef HAVE_LZ4
|
||||
case LZ4:
|
||||
return "IV_LZ4=1\n";
|
||||
#endif
|
||||
#ifdef HAVE_SNAPPY
|
||||
case SNAPPY:
|
||||
return "IV_SNAPPY=1\n";
|
||||
#endif
|
||||
case LZO_STUB:
|
||||
return "IV_LZO_STUB=1\n";
|
||||
case COMP_STUB:
|
||||
return "IV_COMP_STUB=1\n";
|
||||
case ANY:
|
||||
return
|
||||
#ifdef HAVE_LZO
|
||||
"IV_LZO=1\n"
|
||||
"IV_LZO_SWAP=1\n"
|
||||
#else
|
||||
"IV_LZO_STUB=1\n"
|
||||
#endif
|
||||
#ifdef HAVE_LZ4
|
||||
"IV_LZ4=1\n"
|
||||
#endif
|
||||
#ifdef HAVE_SNAPPY
|
||||
"IV_SNAPPY=1\n"
|
||||
#endif
|
||||
"IV_COMP_STUB=1\n"
|
||||
;
|
||||
case ANY_LZO:
|
||||
return
|
||||
#ifdef HAVE_LZO
|
||||
"IV_LZO=1\n"
|
||||
"IV_LZO_SWAP=1\n"
|
||||
#else
|
||||
"IV_LZO_STUB=1\n"
|
||||
#endif
|
||||
;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@ -82,6 +237,12 @@ namespace openvpn {
|
||||
{
|
||||
case LZO:
|
||||
case LZO_STUB:
|
||||
case SNAPPY:
|
||||
case LZ4:
|
||||
case LZO_SWAP:
|
||||
case COMP_STUB:
|
||||
case ANY:
|
||||
case ANY_LZO:
|
||||
return "comp-lzo";
|
||||
default:
|
||||
return NULL;
|
||||
@ -94,13 +255,50 @@ namespace openvpn {
|
||||
{
|
||||
case LZO:
|
||||
return "LZO";
|
||||
case LZO_SWAP:
|
||||
return "LZO_SWAP";
|
||||
case LZ4:
|
||||
return "LZ4";
|
||||
case SNAPPY:
|
||||
return "SNAPPY";
|
||||
case LZO_STUB:
|
||||
return "LZO_STUB";
|
||||
case COMP_STUB:
|
||||
return "COMP_STUB";
|
||||
case ANY:
|
||||
return "ANY";
|
||||
case ANY_LZO:
|
||||
return "ANY_LZO";
|
||||
default:
|
||||
return "NONE";
|
||||
}
|
||||
}
|
||||
|
||||
static Type parse_method(const std::string& method)
|
||||
{
|
||||
if (method == "lzo")
|
||||
return LZO;
|
||||
else if (method == "lzo-swap")
|
||||
return LZO_SWAP;
|
||||
else if (method == "lzo-stub")
|
||||
return LZO_STUB;
|
||||
else if (method == "lz4")
|
||||
return LZ4;
|
||||
else if (method == "snappy")
|
||||
return SNAPPY;
|
||||
else if (method == "stub")
|
||||
return COMP_STUB;
|
||||
else
|
||||
return NONE;
|
||||
}
|
||||
|
||||
static void init_static()
|
||||
{
|
||||
#ifdef HAVE_LZO
|
||||
CompressLZO::init_static();
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
};
|
||||
|
52
openvpn/compress/compstub.hpp
Normal file
52
openvpn/compress/compstub.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef OPENVPN_COMPRESS_COMPSTUB_H
|
||||
#define OPENVPN_COMPRESS_COMPSTUB_H
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class CompressStub : public Compress
|
||||
{
|
||||
public:
|
||||
CompressStub(const Frame::Ptr& frame, const ProtoStats::Ptr& stats, const bool support_swap_arg)
|
||||
: Compress(frame, stats),
|
||||
support_swap(support_swap_arg)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void compress(BufferAllocated& buf, const bool hint)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
// indicate that we didn't compress
|
||||
if (support_swap)
|
||||
do_swap(buf, NO_COMPRESS_SWAP);
|
||||
else
|
||||
buf.push_front(NO_COMPRESS);
|
||||
}
|
||||
|
||||
virtual void decompress(BufferAllocated& buf)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
const unsigned char c = buf.pop_front();
|
||||
switch (c)
|
||||
{
|
||||
case NO_COMPRESS_SWAP:
|
||||
do_unswap(buf);
|
||||
case NO_COMPRESS:
|
||||
break;
|
||||
default:
|
||||
error(buf); // unknown op
|
||||
}
|
||||
}
|
||||
|
||||
const bool support_swap;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMPRESS_COMPSTUB_H
|
116
openvpn/compress/lz4.hpp
Normal file
116
openvpn/compress/lz4.hpp
Normal file
@ -0,0 +1,116 @@
|
||||
#ifndef OPENVPN_COMPRESS_LZ4_H
|
||||
#define OPENVPN_COMPRESS_LZ4_H
|
||||
|
||||
// Implement LZ4 compression.
|
||||
// Should only be included by compress.hpp
|
||||
|
||||
#include <algorithm> // for std::max
|
||||
|
||||
#include <lz4.h>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class CompressLZ4 : public Compress
|
||||
{
|
||||
// magic number for LZ4 compression
|
||||
enum {
|
||||
LZ4_COMPRESS = 0x69,
|
||||
};
|
||||
|
||||
public:
|
||||
CompressLZ4(const Frame::Ptr& frame, const ProtoStats::Ptr& stats)
|
||||
: Compress(frame, stats)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void compress(BufferAllocated& buf, const bool hint)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
if (hint)
|
||||
{
|
||||
// initialize work buffer
|
||||
frame->prepare(Frame::COMPRESS_WORK, work);
|
||||
|
||||
// verify that input data length is not too large
|
||||
if (lz4_extra_buffer(buf.size()) > work.max_size())
|
||||
{
|
||||
error(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
// do compress
|
||||
const int comp_size = LZ4_compress((char *)buf.c_data(), (char *)work.data(), (int)buf.size());
|
||||
|
||||
// did compression actually reduce data length?
|
||||
if (comp_size < buf.size())
|
||||
{
|
||||
if (comp_size < 0)
|
||||
{
|
||||
error(buf);
|
||||
return;
|
||||
}
|
||||
work.set_size(comp_size);
|
||||
do_swap(work, LZ4_COMPRESS);
|
||||
buf.swap(work);
|
||||
OPENVPN_LOG_COMPRESS("LZ4 compress");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// indicate that we didn't compress
|
||||
do_swap(buf, NO_COMPRESS_SWAP);
|
||||
}
|
||||
|
||||
virtual void decompress(BufferAllocated& buf)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
const unsigned char c = buf.pop_front();
|
||||
switch (c)
|
||||
{
|
||||
case NO_COMPRESS_SWAP:
|
||||
do_unswap(buf);
|
||||
break;
|
||||
case LZ4_COMPRESS:
|
||||
{
|
||||
do_unswap(buf);
|
||||
|
||||
// initialize work buffer
|
||||
const int payload_size = frame->prepare(Frame::DECOMPRESS_WORK, work);
|
||||
|
||||
// do uncompress
|
||||
const int decomp_size = LZ4_uncompress_unknownOutputSize((const char *)buf.c_data(), (char *)work.data(), (int)buf.size(), payload_size);
|
||||
if (decomp_size < 0)
|
||||
{
|
||||
error(buf);
|
||||
return;
|
||||
}
|
||||
work.set_size(decomp_size);
|
||||
buf.swap(work);
|
||||
OPENVPN_LOG_COMPRESS("LZ4 uncompress");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error(buf); // unknown op
|
||||
}
|
||||
}
|
||||
|
||||
// worst case size expansion on compress
|
||||
// from LZ4 docs: worst case size is : "inputsize + 0.4%", with "0.4%" being at least 8 bytes.
|
||||
size_t lz4_extra_buffer(const size_t len)
|
||||
{
|
||||
return len + std::max(len/128, size_t(8)); // for speed, use a more conservative 0.78%
|
||||
}
|
||||
|
||||
BufferAllocated work;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMPRESS_LZ4_H
|
137
openvpn/compress/lzo.hpp
Normal file
137
openvpn/compress/lzo.hpp
Normal file
@ -0,0 +1,137 @@
|
||||
#ifndef OPENVPN_COMPRESS_LZO_H
|
||||
#define OPENVPN_COMPRESS_LZO_H
|
||||
|
||||
// Implement LZO compression.
|
||||
// Should only be included by compress.hpp
|
||||
|
||||
#include "lzo/lzoutil.h"
|
||||
#include "lzo/lzo1x.h"
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class CompressLZO : public Compress
|
||||
{
|
||||
// magic number for LZO compression
|
||||
enum {
|
||||
LZO_COMPRESS = 0x66,
|
||||
LZO_COMPRESS_SWAP = 0x67,
|
||||
};
|
||||
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(lzo_init_failed);
|
||||
|
||||
CompressLZO(const Frame::Ptr& frame, const ProtoStats::Ptr& stats, const bool support_swap_arg)
|
||||
: Compress(frame, stats),
|
||||
support_swap(support_swap_arg)
|
||||
{
|
||||
lzo_workspace.init(LZO1X_1_15_MEM_COMPRESS, BufferAllocated::ARRAY);
|
||||
}
|
||||
|
||||
static void init_static()
|
||||
{
|
||||
if (::lzo_init() != LZO_E_OK)
|
||||
throw lzo_init_failed();
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void compress(BufferAllocated& buf, const bool hint)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
if (hint)
|
||||
{
|
||||
// initialize work buffer
|
||||
frame->prepare(Frame::COMPRESS_WORK, work);
|
||||
|
||||
// verify that input data length is not too large
|
||||
if (lzo_extra_buffer(buf.size()) > work.max_size())
|
||||
{
|
||||
error(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
// do compress
|
||||
lzo_uint zlen = 0;
|
||||
const int err = ::lzo1x_1_15_compress(buf.c_data(), buf.size(), work.data(), &zlen, lzo_workspace.data());
|
||||
|
||||
// check for errors
|
||||
if (err != LZO_E_OK)
|
||||
{
|
||||
error(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
// did compression actually reduce data length?
|
||||
if (zlen < buf.size())
|
||||
{
|
||||
work.set_size(zlen);
|
||||
if (support_swap)
|
||||
do_swap(work, LZO_COMPRESS_SWAP);
|
||||
else
|
||||
work.push_front(LZO_COMPRESS);
|
||||
buf.swap(work);
|
||||
OPENVPN_LOG_COMPRESS("LZO compress");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// indicate that we didn't compress
|
||||
if (support_swap)
|
||||
do_swap(buf, NO_COMPRESS_SWAP);
|
||||
else
|
||||
buf.push_front(NO_COMPRESS);
|
||||
}
|
||||
|
||||
virtual void decompress(BufferAllocated& buf)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
const unsigned char c = buf.pop_front();
|
||||
switch (c)
|
||||
{
|
||||
case NO_COMPRESS_SWAP:
|
||||
do_unswap(buf);
|
||||
case NO_COMPRESS:
|
||||
break;
|
||||
case LZO_COMPRESS_SWAP:
|
||||
do_unswap(buf);
|
||||
case LZO_COMPRESS:
|
||||
{
|
||||
// initialize work buffer
|
||||
lzo_uint zlen = frame->prepare(Frame::DECOMPRESS_WORK, work);
|
||||
|
||||
// do uncompress
|
||||
const int err = lzo1x_decompress_safe(buf.c_data(), buf.size(), work.data(), &zlen, lzo_workspace.data());
|
||||
if (err != LZO_E_OK)
|
||||
{
|
||||
error(buf);
|
||||
break;
|
||||
}
|
||||
work.set_size(zlen);
|
||||
buf.swap(work);
|
||||
OPENVPN_LOG_COMPRESS("LZO uncompress");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error(buf); // unknown op
|
||||
}
|
||||
}
|
||||
|
||||
// worst case size expansion on compress
|
||||
size_t lzo_extra_buffer(const size_t len)
|
||||
{
|
||||
return len + len/8 + 128 + 3;
|
||||
}
|
||||
|
||||
const bool support_swap;
|
||||
BufferAllocated work;
|
||||
BufferAllocated lzo_workspace;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMPRESS_LZO_H
|
@ -1,49 +0,0 @@
|
||||
#ifndef OPENVPN_COMPRESS_LZOSTUB_H
|
||||
#define OPENVPN_COMPRESS_LZOSTUB_H
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class CompressLZOStub : public Compress
|
||||
{
|
||||
// magic numbers to tell our peer if we compressed or not
|
||||
enum {
|
||||
YES_COMPRESS = 0x66,
|
||||
NO_COMPRESS = 0xFA,
|
||||
};
|
||||
|
||||
public:
|
||||
CompressLZOStub(const Frame::Ptr& frame, const ProtoStats::Ptr& stats)
|
||||
: Compress(frame, stats)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void compress(BufferAllocated& buf, const bool hint)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
// indicate that we didn't compress
|
||||
buf.push_front(NO_COMPRESS);
|
||||
}
|
||||
|
||||
virtual void decompress(BufferAllocated& buf)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
const unsigned char c = buf.pop_front();
|
||||
if (c == NO_COMPRESS) // what we're expecting
|
||||
return;
|
||||
|
||||
// error
|
||||
stats->error(ProtoStats::COMPRESS_ERROR);
|
||||
buf.reset_size();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMPRESS_LZOSTUB_H
|
109
openvpn/compress/snappy.hpp
Normal file
109
openvpn/compress/snappy.hpp
Normal file
@ -0,0 +1,109 @@
|
||||
#ifndef OPENVPN_COMPRESS_SNAPPY_H
|
||||
#define OPENVPN_COMPRESS_SNAPPY_H
|
||||
|
||||
// Implement Snappy compression.
|
||||
// Should only be included by compress.hpp
|
||||
|
||||
#include <snappy.h>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class CompressSnappy : public Compress
|
||||
{
|
||||
// magic number for Snappy compression
|
||||
enum {
|
||||
SNAPPY_COMPRESS = 0x68,
|
||||
};
|
||||
|
||||
public:
|
||||
CompressSnappy(const Frame::Ptr& frame, const ProtoStats::Ptr& stats)
|
||||
: Compress(frame, stats)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void compress(BufferAllocated& buf, const bool hint)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
if (hint)
|
||||
{
|
||||
// initialize work buffer
|
||||
frame->prepare(Frame::COMPRESS_WORK, work);
|
||||
|
||||
// verify that input data length is not too large
|
||||
if (snappy::MaxCompressedLength(buf.size()) > work.max_size())
|
||||
{
|
||||
error(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
// do compress
|
||||
size_t comp_size;
|
||||
snappy::RawCompress((const char *)buf.c_data(), buf.size(), (char *)work.data(), &comp_size);
|
||||
|
||||
// did compression actually reduce data length?
|
||||
if (comp_size < buf.size())
|
||||
{
|
||||
work.set_size(comp_size);
|
||||
do_swap(work, SNAPPY_COMPRESS);
|
||||
buf.swap(work);
|
||||
OPENVPN_LOG_COMPRESS("SNAPPY compress");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// indicate that we didn't compress
|
||||
do_swap(buf, NO_COMPRESS_SWAP);
|
||||
}
|
||||
|
||||
virtual void decompress(BufferAllocated& buf)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
const unsigned char c = buf.pop_front();
|
||||
switch (c)
|
||||
{
|
||||
case NO_COMPRESS_SWAP:
|
||||
do_unswap(buf);
|
||||
break;
|
||||
case SNAPPY_COMPRESS:
|
||||
{
|
||||
do_unswap(buf);
|
||||
|
||||
// initialize work buffer
|
||||
const size_t payload_size = frame->prepare(Frame::DECOMPRESS_WORK, work);
|
||||
|
||||
// do uncompress
|
||||
size_t decomp_size;
|
||||
if (!snappy::GetUncompressedLength((const char *)buf.c_data(), buf.size(), &decomp_size)
|
||||
|| decomp_size > payload_size)
|
||||
{
|
||||
error(buf);
|
||||
break;
|
||||
}
|
||||
if (!snappy::RawUncompress((const char *)buf.c_data(), buf.size(), (char *)work.data()))
|
||||
{
|
||||
error(buf);
|
||||
break;
|
||||
}
|
||||
work.set_size(decomp_size);
|
||||
buf.swap(work);
|
||||
OPENVPN_LOG_COMPRESS("SNAPPY uncompress");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error(buf); // unknown op
|
||||
}
|
||||
}
|
||||
|
||||
BufferAllocated work;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMPRESS_SNAPPY_H
|
@ -17,6 +17,8 @@ namespace openvpn {
|
||||
enum {
|
||||
ENCRYPT_WORK=0,
|
||||
DECRYPT_WORK,
|
||||
COMPRESS_WORK,
|
||||
DECOMPRESS_WORK,
|
||||
READ_LINK_UDP,
|
||||
READ_LINK_TCP,
|
||||
READ_TUN,
|
||||
@ -73,10 +75,11 @@ namespace openvpn {
|
||||
|
||||
// Calculate a starting offset into a buffer object, dealing with
|
||||
// headroom and alignment issues.
|
||||
void prepare(Buffer& buf) const
|
||||
size_t prepare(Buffer& buf) const
|
||||
{
|
||||
buf.reset(capacity(), buffer_flags());
|
||||
buf.init_headroom(actual_headroom(buf.c_data_raw()));
|
||||
return payload();
|
||||
}
|
||||
|
||||
// Realign a buffer to headroom
|
||||
@ -177,10 +180,10 @@ namespace openvpn {
|
||||
|
||||
// Calculate a starting offset into a buffer object, dealing with
|
||||
// headroom and alignment issues. context should be one of
|
||||
// the context types above.
|
||||
void prepare(const unsigned int context, BufferAllocated& buf) const
|
||||
// the context types above. Returns payload size of buffer.
|
||||
size_t prepare(const unsigned int context, BufferAllocated& buf) const
|
||||
{
|
||||
(*this)[context].prepare(buf);
|
||||
return (*this)[context].prepare(buf);
|
||||
}
|
||||
|
||||
size_t n_contexts() const { return N_ALIGN_CONTEXTS; }
|
||||
|
@ -162,6 +162,7 @@ namespace openvpn {
|
||||
|
||||
OPENVPN_SIMPLE_EXCEPTION(peer_psid_undef);
|
||||
OPENVPN_SIMPLE_EXCEPTION(bad_auth_prefix);
|
||||
OPENVPN_EXCEPTION(process_server_push_error);
|
||||
|
||||
static unsigned int mtu()
|
||||
{
|
||||
@ -247,7 +248,7 @@ namespace openvpn {
|
||||
expire = Time::Duration::seconds(7200);
|
||||
keepalive_ping = Time::Duration::seconds(8);
|
||||
keepalive_timeout = Time::Duration::seconds(40);
|
||||
comp_ctx = CompressContext(CompressContext::LZO_STUB);
|
||||
comp_ctx = CompressContext(CompressContext::NONE);
|
||||
|
||||
// tcp/udp
|
||||
{
|
||||
@ -305,6 +306,100 @@ namespace openvpn {
|
||||
tls_auth_digest = digest;
|
||||
}
|
||||
}
|
||||
|
||||
// compression
|
||||
{
|
||||
const Option *o;
|
||||
o = opt.get_ptr("compress");
|
||||
if (o)
|
||||
{
|
||||
if (o->size() >= 2)
|
||||
{
|
||||
const std::string meth_name = (*o)[1];
|
||||
CompressContext::Type meth = CompressContext::parse_method(meth_name);
|
||||
if (meth == CompressContext::NONE)
|
||||
OPENVPN_THROW(option_error, "Unknown compressor: '" << meth_name << '\'');
|
||||
comp_ctx = CompressContext(meth);
|
||||
}
|
||||
else
|
||||
comp_ctx = CompressContext(CompressContext::ANY);
|
||||
}
|
||||
else
|
||||
{
|
||||
o = opt.get_ptr("comp-lzo");
|
||||
if (o)
|
||||
{
|
||||
comp_ctx = CompressContext(CompressContext::ANY_LZO);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void process_push(const OptionList& opt)
|
||||
{
|
||||
// cipher
|
||||
std::string new_cipher;
|
||||
try {
|
||||
const Option *o = opt.get_ptr("cipher");
|
||||
if (o)
|
||||
{
|
||||
new_cipher = o->get(1);
|
||||
cipher = Cipher(new_cipher);
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
OPENVPN_THROW(process_server_push_error, "Problem accepting server-pushed cipher '" << new_cipher << "': " << e.what());
|
||||
}
|
||||
|
||||
// digest
|
||||
std::string new_digest;
|
||||
try {
|
||||
const Option *o = opt.get_ptr("auth");
|
||||
if (o)
|
||||
{
|
||||
new_digest = o->get(1);
|
||||
digest = Digest(new_digest);
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
OPENVPN_THROW(process_server_push_error, "Problem accepting server-pushed digest '" << new_digest << "': " << e.what());
|
||||
}
|
||||
|
||||
// compression
|
||||
std::string new_comp;
|
||||
try {
|
||||
const Option *o;
|
||||
o = opt.get_ptr("compress");
|
||||
if (o)
|
||||
{
|
||||
new_comp = o->get(1);
|
||||
CompressContext::Type meth = CompressContext::parse_method(new_comp);
|
||||
if (meth != CompressContext::NONE)
|
||||
comp_ctx = CompressContext(meth);
|
||||
}
|
||||
else
|
||||
{
|
||||
o = opt.get_ptr("comp-lzo");
|
||||
if (o)
|
||||
{
|
||||
if (o->size() == 1)
|
||||
comp_ctx = CompressContext(CompressContext::LZO);
|
||||
else if (o->size() >= 2)
|
||||
{
|
||||
if ((*o)[1] == "yes")
|
||||
comp_ctx = CompressContext(CompressContext::LZO);
|
||||
else
|
||||
comp_ctx = CompressContext(CompressContext::LZO_STUB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
OPENVPN_THROW(process_server_push_error, "Problem accepting server-pushed compressor '" << new_comp << "': " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// generate a string summarizing options that will be
|
||||
@ -684,8 +779,13 @@ namespace openvpn {
|
||||
next_event = KEV_NEGOTIATE;
|
||||
next_event_time = construct_time + p.config->handshake_window;
|
||||
|
||||
construct_compressor();
|
||||
}
|
||||
|
||||
// construct compressor/decompressor
|
||||
compress = p.config->comp_ctx.new_compressor(p.config->frame, proto.stats);
|
||||
void construct_compressor()
|
||||
{
|
||||
compress = proto.config->comp_ctx.new_compressor(proto.config->frame, proto.stats);
|
||||
}
|
||||
|
||||
// need to call only on the initiator side of the connection (i.e. client)
|
||||
@ -1727,6 +1827,15 @@ namespace openvpn {
|
||||
enable_fast_transition();
|
||||
}
|
||||
|
||||
// Call on client with server-pushed options
|
||||
void process_push(const OptionList& opt)
|
||||
{
|
||||
config->process_push(opt);
|
||||
primary->construct_compressor();
|
||||
if (secondary)
|
||||
secondary->construct_compressor();
|
||||
}
|
||||
|
||||
// current time
|
||||
const Time& now() const { return *now_; }
|
||||
void update_now() { now_->update(); }
|
||||
|
@ -81,13 +81,13 @@ fi
|
||||
# LZO compression
|
||||
if [ "$LZO" = "1" ]; then
|
||||
LIBS="$LIBS -llzo2"
|
||||
CPPFLAGS="$CPPFLAGS -DUSE_LZO"
|
||||
CPPFLAGS="$CPPFLAGS -DHAVE_LZO"
|
||||
fi
|
||||
|
||||
# LZ4 compression
|
||||
if [ "$LZ4" = "1" ]; then
|
||||
EXTRA_SRC_OBJ="$EXTRA_SRC_OBJ $LZ4_DIR/lz4.o"
|
||||
CPPFLAGS="$CPPFLAGS -I$LZ4_DIR -DUSE_LZ4"
|
||||
CPPFLAGS="$CPPFLAGS -I$LZ4_DIR -DHAVE_LZ4"
|
||||
fi
|
||||
|
||||
# Snappy compression
|
||||
@ -97,7 +97,7 @@ if [ "$SNAP" = "1" ]; then
|
||||
CPPFLAGS="$CPPFLAGS -I$SNAPPY_DIR/include"
|
||||
fi
|
||||
LIBS="$LIBS -lsnappy"
|
||||
CPPFLAGS="$CPPFLAGS -DUSE_SNAPPY"
|
||||
CPPFLAGS="$CPPFLAGS -DHAVE_SNAPPY"
|
||||
fi
|
||||
|
||||
# ovpn3
|
||||
|
@ -5,4 +5,4 @@ unset OVPN3_DIR
|
||||
unset PLATFORM
|
||||
unset GPP_CMD
|
||||
unset STRIP_CMD
|
||||
[ "$ORIG_PATH" ] && export PATH="$ORIG_PATH"
|
||||
[ "$NEW_PATH" ] && export PATH="$NEW_PATH"
|
||||
|
Loading…
Reference in New Issue
Block a user