From f2524196b13daf3a0a54a880f3ea43813bf9ae66 Mon Sep 17 00:00:00 2001 From: Logan007 Date: Wed, 20 May 2020 01:12:22 +0545 Subject: [PATCH 1/3] 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_ */ From 048b7c7794063e4d8081731b02ac603c28a7b58e Mon Sep 17 00:00:00 2001 From: lishuxiang Date: Sun, 31 May 2020 10:30:15 +0800 Subject: [PATCH 2/3] Fix a typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ef019e..862232f 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ n2n edge nodes use twofish encryption by default for compatibility reasons with of the edge nodes, their IP address and the community are sent in cleartext. When encryption is enabled, the supernode will not be able to decrypt the traffic exchanged between -two edge nodes, but it will now that edge A is talking with edge B. +two edge nodes, but it will know that edge A is talking with edge B. Recently AES encryption support has been implemented, which increases both security and performance, so it is recommended to enable it on all the edge nodes by specifying the `-A` option. From b8fcf091779468def5493f7db9dc55334c826003 Mon Sep 17 00:00:00 2001 From: Luca Deri Date: Sun, 7 Jun 2020 10:00:18 +0200 Subject: [PATCH 3/3] Compilation fixes --- README.md | 3 +- edge.c | 87 ++++++++++++++++++++++++++---------------------- transform_cc20.c | 6 ---- 3 files changed, 50 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 862232f..fb99257 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,8 @@ When encryption is enabled, the supernode will not be able to decrypt the traffi two edge nodes, but it will know that edge A is talking with edge B. Recently AES encryption support has been implemented, which increases both security and performance, -so it is recommended to enable it on all the edge nodes by specifying the `-A` option. +so it is recommended to enable it on all the edge nodes that must have the -Ax value. When possible +(i.e. when n2n is compiled with OpenSSL 1.1) we recommend to use -A4 A benchmark of the encryption methods is available when compiled from source with `tools/n2n-benchmark`. diff --git a/edge.c b/edge.c index da0cd4f..fe2eb41 100644 --- a/edge.c +++ b/edge.c @@ -189,12 +189,12 @@ static void help() { #endif printf("-r | Enable packet forwarding through n2n community.\n"); printf("-A1 | Disable payload encryption. Do not use with -k.\n"); - printf("-A2 | Use Twofish for payload encryption (default). Requires a key.\n"); + printf("-A2 | Use Twofish for payload encryption (default). Requires a key (-k).\n"); #ifdef N2N_HAVE_AES - printf("-A3 or -A (deprecated) | Use AES-CBC for payload encryption. Requires a key.\n"); + printf("-A3 or -A (deprecated) | Use AES-CBC for payload encryption. Requires a key (-k).\n"); #endif #ifdef HAVE_OPENSSL_1_1 - printf("-A4 | Use ChaCha20 for payload encryption. Requires a key.\n"); + printf("-A4 | Use ChaCha20 for payload encryption. Requires a key (-k).\n"); #endif printf("-z | Enable lzo1x compression for outgoing data packets\n"); printf(" | (default=disabled).\n"); @@ -220,6 +220,46 @@ static void help() { /* *************************************************** */ +static void setPayloadEncryption( n2n_edge_conf_t *conf, int cipher) { + /* even though 'cipher' and 'conf->transop_id' share the same encoding scheme, + * a switch-statement under conditional compilation is used to sort out the + * unsupported ciphers */ + switch (cipher) { + case 1: + { + conf->transop_id = N2N_TRANSFORM_ID_NULL; + break; + } + case 2: + { + conf->transop_id = N2N_TRANSFORM_ID_TWOFISH; + break; + } +#ifdef N2N_HAVE_AES + case 3: + { + conf->transop_id = N2N_TRANSFORM_ID_AESCBC; + break; + } +#endif +#ifdef HAVE_OPENSSL_1_1 + case 4: + { + conf->transop_id = N2N_TRANSFORM_ID_CHACHA20; + break; + } +#endif + default: + { + conf->transop_id = N2N_TRANSFORM_ID_INVAL; + traceEvent(TRACE_NORMAL, "the %s cipher given by -A_ option is not supported in this version.", transop_str(cipher)); + exit(1); + } + } +} + +/* *************************************************** */ + static int setOption(int optkey, char *optargument, n2n_priv_config_t *ec, n2n_edge_conf_t *conf) { /* traceEvent(TRACE_NORMAL, "Option %c = %s", optkey, optargument ? optargument : ""); */ @@ -308,48 +348,17 @@ static int setOption(int optkey, char *optargument, n2n_priv_config_t *ec, n2n_e case 'A': { - int cipher = N2N_TRANSFORM_ID_AESCBC; // default, if '-A' only + int cipher; + if (optargument) { cipher = atoi(optargument); } else { traceEvent(TRACE_NORMAL, "the use of the solitary -A switch is deprecated and might not be supported in future versions. " "please use -A3 instead to choose a the AES-CBC cipher for payload encryption."); + cipher = N2N_TRANSFORM_ID_AESCBC; // default, if '-A' only } - /* even though 'cipher' and 'conf->transop_id' share the same encoding scheme, - * a switch-statement under conditional compilation is used to sort out the - * unsupported ciphers */ - switch (cipher) { - case 1: - { - conf->transop_id = N2N_TRANSFORM_ID_NULL; - break; - } - case 2: - { - conf->transop_id = N2N_TRANSFORM_ID_TWOFISH; - break; - } -#ifdef N2N_HAVE_AES - case 3: - { - conf->transop_id = N2N_TRANSFORM_ID_AESCBC; - break; - } -#endif -#ifdef HAVE_OPENSSL_1_1 - case 4: - { - conf->transop_id = N2N_TRANSFORM_ID_CHACHA20; - break; - } -#endif - default: - { - conf->transop_id = N2N_TRANSFORM_ID_INVAL; - traceEvent(TRACE_NORMAL, "the %s cipher given by -A_ option is not supported in this version.", transop_str(cipher)); - exit(1); - } - } + + setPayloadEncryption(conf, cipher); break; } diff --git a/transform_cc20.c b/transform_cc20.c index 389cfe5..f937775 100644 --- a/transform_cc20.c +++ b/transform_cc20.c @@ -120,9 +120,6 @@ static int transop_encode_cc20(n2n_trans_op_t * arg, /* Generate and encode the IV. */ set_cc20_iv(priv, enc_ivec); encode_buf(outbuf, &idx, &enc_ivec, N2N_CC20_IVEC_SIZE); - traceEvent(TRACE_DEBUG, "encode_cc20 iv=%016llx:%016llx", - htobe64(*(uint64_t*)&enc_ivec[0]), - htobe64(*(uint64_t*)&enc_ivec[8]) ); /* Encrypt the assembly contents and write the ciphertext after the iv. */ /* len is set to the length of the cipher plain text to be encrpyted @@ -198,9 +195,6 @@ static int transop_decode_cc20(n2n_trans_op_t * arg, /* Get the IV */ decode_buf((uint8_t *)&dec_ivec, N2N_CC20_IVEC_SIZE, inbuf, &rem, &idx); - traceEvent(TRACE_DEBUG, "decode_cc20 iv=%016llx:%016llx", - htobe64(*(uint64_t*)&dec_ivec[0]), - htobe64(*(uint64_t*)&dec_ivec[8]) ); EVP_CIPHER_CTX *ctx = priv->dec_ctx; int evp_len;