From f2524196b13daf3a0a54a880f3ea43813bf9ae66 Mon Sep 17 00:00:00 2001 From: Logan007 Date: Wed, 20 May 2020 01:12:22 +0545 Subject: [PATCH] added optional payload compression (zstd) for all transforms --- configure.seed | 14 ++++++++-- edge.c | 43 +++++++++++++++++++++++++----- edge_utils.c | 71 +++++++++++++++++++++++++++++++++++++++++++------- n2n.h | 13 ++++++--- 4 files changed, 119 insertions(+), 22 deletions(-) diff --git a/configure.seed b/configure.seed index d1f0004..313fd18 100644 --- a/configure.seed +++ b/configure.seed @@ -13,14 +13,24 @@ else GIT_RELEASE=${N2N_VERSION_SHORT} fi +N2N_LIBS= + +AC_CHECK_LIB([zstd], [ZSTD_compress]) + +if test "x$ac_cv_lib_zstd_ZSTD_compress" != xyes; then + AC_MSG_RESULT(Building n2n without ZSTD support) +else + AC_DEFINE([N2N_HAVE_ZSTD], [], [Have ZSTD support]) + N2N_LIBS="-lzstd ${N2N_LIBS}" +fi + AC_CHECK_LIB([crypto], [AES_cbc_encrypt]) -N2N_LIBS= if test "x$ac_cv_lib_crypto_AES_cbc_encrypt" != xyes; then AC_MSG_RESULT(Building n2n without AES support) else AC_DEFINE([N2N_HAVE_AES], [], [Have AES support]) - N2N_LIBS=-lcrypto + N2N_LIBS="-lcrypto ${N2N_LIBS}" fi OLD_CFLAGS="${CFLAGS}" diff --git a/edge.c b/edge.c index f00aa05..6e6ad73 100644 --- a/edge.c +++ b/edge.c @@ -143,7 +143,7 @@ static void help() { #ifndef __APPLE__ "[-D] " #endif - "[-r] [-E] [-v] [-i ] [-L ] [-t ] [-A] [-h]\n\n"); + "[-r] [-E] [-v] [-i ] [-L ] [-t ] [-A] [-z[]] [-h]\n\n"); #if defined(N2N_CAN_NAME_IFACE) printf("-d | tun device name\n"); @@ -175,8 +175,11 @@ static void help() { #ifdef N2N_HAVE_AES printf("-A | Use AES CBC for encryption (default=use twofish).\n"); #endif - printf("-z | Enable lzo1x compression for outgoing data packets\n"); - printf(" | (default=disabled).\n"); + printf("-z1 or -z | Enable lzo1x compression for outgoing data packets\n"); +#ifdef N2N_HAVE_ZSTD + printf("-z2 | Enable zstd compression for outgoing data packets\n"); +#endif + printf(" | (default=compression disabled)\n"); printf("-E | Accept multicast MAC addresses (default=drop).\n"); printf("-S | Do not connect P2P. Always use the supernode.\n"); #ifdef __linux__ @@ -295,7 +298,33 @@ static int setOption(int optkey, char *optargument, n2n_priv_config_t *ec, n2n_e case 'z': { - conf->compression = N2N_COMPRESSION_ID_LZO; + int compression = N2N_COMPRESSION_ID_LZO; // default, if '-z' only + if (optargument) { + compression = atoi(optargument); + } + /* even though 'compression' and 'conf->compression' share the same encoding scheme, + * a switch-statement under conditional compilation is used to sort out the + * unsupported optarguments */ + switch (compression) { + case 1: + { + conf->compression = N2N_COMPRESSION_ID_LZO; + break; + } +#ifdef N2N_HAVE_ZSTD + case 2: + { + conf->compression = N2N_COMPRESSION_ID_ZSTD; + break; + } +#endif + default: + { + conf->compression = N2N_COMPRESSION_ID_NONE; + traceEvent(TRACE_NORMAL, "the %s compression given by -z_ option is not supported in this version.", compression_str(compression)); + exit(1); // to make the user aware + } + } break; } @@ -406,7 +435,7 @@ static int loadFromCLI(int argc, char *argv[], n2n_edge_conf_t *conf, n2n_priv_c u_char c; while((c = getopt_long(argc, argv, - "k:a:bc:Eu:g:m:M:s:d:l:p:fvhrt:i:SDL:z" + "k:a:bc:Eu:g:m:M:s:d:l:p:fvhrt:i:SDL:z::" #ifdef N2N_HAVE_AES "A" #endif @@ -690,7 +719,9 @@ int main(int argc, char* argv[]) { #if defined(HAVE_OPENSSL_1_1) traceEvent(TRACE_NORMAL, "Using %s", OpenSSL_version(0)); #endif - + + traceEvent(TRACE_NORMAL, "Using compression: %s.", compression_str(conf.compression)); + /* Random seed */ srand(time(NULL)); diff --git a/edge_utils.c b/edge_utils.c index 38910ed..d97ac79 100644 --- a/edge_utils.c +++ b/edge_utils.c @@ -18,6 +18,7 @@ #include "n2n.h" #include "lzoconf.h" +#include #ifdef WIN32 #include @@ -148,6 +149,17 @@ static const char* transop_str(enum n2n_transform tr) { /* ************************************** */ +const char* compression_str(uint8_t cmpr) { + switch(cmpr) { + case N2N_COMPRESSION_ID_NONE: return("none"); + case N2N_COMPRESSION_ID_LZO: return("lzo1x"); + case N2N_COMPRESSION_ID_ZSTD: return("zstd"); + default: return("invalid"); + }; +} + +/* ************************************** */ + /** Destination 01:00:5E:00:00:00 - 01:00:5E:7F:FF:FF is multicast ethernet. */ static int is_ethMulticast(const void * buf, size_t bufsize) { @@ -227,6 +239,10 @@ n2n_edge_t* edge_init(const tuntap_dev *dev, const n2n_edge_conf_t *conf, int *r goto edge_init_error; } +#ifdef N2N_HAVE_ZSTD + // zstd does not require initialization. if it were required, this would be a good place +#endif + for(i=0; isn_num; ++i) traceEvent(TRACE_NORMAL, "supernode %u => %s\n", i, (conf->sn_ip_array[i])); @@ -967,20 +983,37 @@ static int handle_PACKET(n2n_edge_t * eee, /* decompress if necessary */ uint8_t * deflation_buffer = 0; - uint32_t deflated_len; + int32_t deflated_len; switch (rx_compression_id) { + case N2N_COMPRESSION_ID_NONE: + break; // continue afterwards + case N2N_COMPRESSION_ID_LZO: - deflation_buffer = malloc (N2N_PKT_BUF_SIZE); + deflation_buffer = malloc (N2N_PKT_BUF_SIZE); lzo1x_decompress (eth_payload, eth_size, deflation_buffer, (lzo_uint*)&deflated_len, NULL); break; - - default: +#ifdef N2N_HAVE_ZSTD + case N2N_COMPRESSION_ID_ZSTD: + deflated_len = N2N_PKT_BUF_SIZE; + deflation_buffer = malloc (deflated_len); + deflated_len = (int32_t)ZSTD_decompress (deflation_buffer, deflated_len, eth_payload, eth_size); + if (ZSTD_isError(deflated_len)) { + traceEvent (TRACE_ERROR, "payload decompression failed with zstd error '%s'.", + ZSTD_getErrorName(deflated_len)); + free (deflation_buffer); + return (-1); // cannot help it + } break; +#endif + default: + traceEvent (TRACE_ERROR, "payload decompression failed: received packet indicating unsupported %s compression.", + compression_str(rx_compression_id)); + return (-1); // cannot handle it } if (rx_compression_id) { - traceEvent (TRACE_DEBUG, "payload decompression [id: %u]: deflated %u bytes to %u bytes", - rx_compression_id, eth_size, (int)deflated_len); + traceEvent (TRACE_DEBUG, "payload decompression [%s]: deflated %u bytes to %u bytes", + compression_str(rx_compression_id), eth_size, (int)deflated_len); memcpy(eth_payload ,deflation_buffer, deflated_len ); eth_size = deflated_len; free (deflation_buffer); @@ -1345,9 +1378,11 @@ static void send_packet2net(n2n_edge_t * eee, // compression needs to be tried before encode_PACKET is called for compression indication gets encoded there pkt.compression = N2N_COMPRESSION_ID_NONE; + if (eee->conf.compression) { uint8_t * compression_buffer; - uint32_t compression_len; + int32_t compression_len; + switch (eee->conf.compression) { case N2N_COMPRESSION_ID_LZO: compression_buffer = malloc (len + len / 16 + 64 + 3); @@ -1357,14 +1392,30 @@ static void send_packet2net(n2n_edge_t * eee, } } break; - +#ifdef N2N_HAVE_ZSTD + case N2N_COMPRESSION_ID_ZSTD: + compression_len = N2N_PKT_BUF_SIZE + 128; + compression_buffer = malloc (compression_len); // leaves enough room, for exact size call compression_len = ZSTD_compressBound (len); (slower) + compression_len = (int32_t)ZSTD_compress(compression_buffer, compression_len, tap_pkt, len, ZSTD_COMPRESSION_LEVEL) ; + if (!ZSTD_isError(compression_len)) { + if (compression_len < len) { + pkt.compression = N2N_COMPRESSION_ID_ZSTD; + } + } else { + traceEvent (TRACE_ERROR, "payload compression failed with zstd error '%s'.", + ZSTD_getErrorName(compression_len)); + free (compression_buffer); + // continue with unset without pkt.compression --> will send uncompressed + } + break; +#endif default: break; } if (pkt.compression) { - traceEvent (TRACE_DEBUG, "payload compression [id: %u]: compressed %u bytes to %u bytes\n", - pkt.compression, len, compression_len); + traceEvent (TRACE_DEBUG, "payload compression [%s]: compressed %u bytes to %u bytes\n", + compression_str(pkt.compression), len, compression_len); memcpy (tap_pkt, compression_buffer, compression_len); len = compression_len; diff --git a/n2n.h b/n2n.h index ada157f..3ae8e49 100644 --- a/n2n.h +++ b/n2n.h @@ -165,9 +165,15 @@ typedef struct tuntap_dev { /* N2N compression indicators. */ /* Compression is disabled by default for outgoing packets if no cli * option is given. All edges are built with decompression support so - * they are able to understand each other. */ + * they are able to understand each other (this applies to lzo only). */ #define N2N_COMPRESSION_ID_NONE 0 /* default, see edge_init_conf_defaults(...) in edge_utils.c */ -#define N2N_COMPRESSION_ID_LZO 1 /* set if '-z' cli option is present, see setOption(...) in edge.c */ +#define N2N_COMPRESSION_ID_LZO 1 /* set if '-z1' or '-z' cli option is present, see setOption(...) in edge.c */ +#ifdef N2N_HAVE_ZSTD +#define N2N_COMPRESSION_ID_ZSTD 2 /* set if '-z2' cli option is present, available only if compiled with zstd lib */ +#define ZSTD_COMPRESSION_LEVEL 7 /* 1 (faster) ... 22 (more compression) */ +#endif +// with the next major packet structure update, make '0' = invalid, and '1' = no compression +// '2' = LZO, '3' = ZSTD, ... REVISIT then (also: change all occurences in source). #define N2N_COMPRESSION_ID_BITLEN 3 /* number of bits used for encoding compression id in the uppermost bits of transform_id; will be obsolete as soon as compression gets @@ -201,7 +207,6 @@ struct peer_info { HASH_ADD(hh,head,mac_addr,sizeof(n2n_mac_t),add) #define HASH_FIND_PEER(head,mac,out) \ HASH_FIND(hh,head,mac,sizeof(n2n_mac_t),out) - #define N2N_EDGE_SN_HOST_SIZE 48 #define N2N_EDGE_NUM_SUPERNODES 2 #define N2N_EDGE_SUP_ATTEMPTS 3 /* Number of failed attmpts before moving on to next supernode. */ @@ -357,5 +362,5 @@ int quick_edge_init(char *device_name, char *community_name, int sn_init(n2n_sn_t *sss); void sn_term(n2n_sn_t *sss); int run_sn_loop(n2n_sn_t *sss, int *keep_running); - +const char* compression_str(uint8_t cmpr); #endif /* _N2N_H_ */