From 789dd902720564e0ec2c8a3bb181652dd74f10fd Mon Sep 17 00:00:00 2001 From: Logan007 Date: Tue, 23 Jun 2020 00:34:19 +0545 Subject: [PATCH] Header Encryption --- include/header_encryption.h | 12 +- include/n2n_define.h | 5 + src/edge.c | 13 +- src/edge_utils.c | 28 ++ src/header_encryption.c | 100 ++--- src/sn.c | 216 ++++++---- src/sn_utils.c | 790 +++++++++++++++++++----------------- 7 files changed, 626 insertions(+), 538 deletions(-) diff --git a/include/header_encryption.h b/include/header_encryption.h index 6f8bdee..74ad647 100644 --- a/include/header_encryption.h +++ b/include/header_encryption.h @@ -17,19 +17,11 @@ */ - -/* Header encryption indicators */ -#define HEADER_ENCRYPTION_UNKNOWN 0 -#define HEADER_ENCRYPTION_NONE 1 -#define HEADER_ENCRYPTION_ENABLED 2 - -uint32_t packet_header_decrypt (uint8_t packet[], uint8_t packet_len, +uint32_t packet_header_decrypt (uint8_t packet[], uint16_t packet_len, char * community_name, he_context_t * ctx); -int8_t packet_header_decrypt_if_required (uint8_t packet[], uint16_t packet_len, - struct sn_community * communities); int32_t packet_header_encrypt (uint8_t packet[], uint8_t header_len, he_context_t * ctx); -void packet_header_setup_key (char * community_name, he_context_t * ctx); +void packet_header_setup_key (const char * community_name, he_context_t ** ctx); diff --git a/include/n2n_define.h b/include/n2n_define.h index 74fea21..304f443 100644 --- a/include/n2n_define.h +++ b/include/n2n_define.h @@ -60,6 +60,11 @@ bits of transform_id; will be obsolete as soon as compression gets its own field in the packet. REVISIT then. */ +/* Header encryption indicators */ +#define HEADER_ENCRYPTION_UNKNOWN 0 +#define HEADER_ENCRYPTION_NONE 1 +#define HEADER_ENCRYPTION_ENABLED 2 + #define DEFAULT_MTU 1290 #define HASH_ADD_PEER(head,add) \ diff --git a/src/edge.c b/src/edge.c index 9000a41..db6bd09 100644 --- a/src/edge.c +++ b/src/edge.c @@ -154,7 +154,7 @@ static void help() { #ifndef __APPLE__ "[-D] " #endif - "[-r] [-E] [-v] [-i ] [-L ] [-t ] [-A[]] [-z[]] [-h]\n\n"); + "[-r] [-E] [-v] [-i ] [-L ] [-t ] [-A[]] [-H] [-z[]] [-h]\n\n"); #if defined(N2N_CAN_NAME_IFACE) printf("-d | tun device name\n"); @@ -192,6 +192,7 @@ static void help() { printf("-A4 | Use ChaCha20 for payload encryption. Requires a key.\n"); #endif printf("-A5 | Use Speck for payload encryption. Requires a key.\n"); + printf("-H | Enable full header encryption. Requires supernode with fixed community.\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"); @@ -393,6 +394,14 @@ static int setOption(int optkey, char *optargument, n2n_priv_config_t *ec, n2n_e break; } + case 'H': /* indicate header encryption */ + { + /* we cannot be sure if this gets parsed before the community name is set. + * so, only an indicator is set, action is taken later*/ + conf->header_encryption = HEADER_ENCRYPTION_ENABLED; + break; + } + case 'z': { int compression; @@ -551,7 +560,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::A::" + "k:a:bc:Eu:g:m:M:s:d:l:p:fvhrt:i:SDL:z::A::H" #ifdef __linux__ "T:n:" #endif diff --git a/src/edge_utils.c b/src/edge_utils.c index ecb616c..0b412c4 100644 --- a/src/edge_utils.c +++ b/src/edge_utils.c @@ -17,6 +17,7 @@ */ #include "n2n.h" +#include "header_encryption.h" /* heap allocation for compression as per lzo example doc */ #define HEAP_ALLOC(var,size) lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ] @@ -254,6 +255,12 @@ n2n_edge_t* edge_init(const tuntap_dev *dev, const n2n_edge_conf_t *conf, int *r goto edge_init_error; } + /* Set the key schedule (context) for header encryption if enabled */ + if (conf->header_encryption == HEADER_ENCRYPTION_ENABLED) { + traceEvent(TRACE_NORMAL, "Header encryption is enabled."); + packet_header_setup_key ((char *)(conf->community_name), &(eee->conf.header_encryption_ctx)); + } + if(eee->transop.no_encryption) traceEvent(TRACE_WARNING, "Encryption is disabled in edge"); @@ -735,6 +742,9 @@ static void send_register_super(n2n_edge_t * eee, traceEvent(TRACE_DEBUG, "send REGISTER_SUPER to %s", sock_to_cstr(sockbuf, supernode)); + if (eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx); + /* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, supernode); } @@ -763,6 +773,9 @@ static void send_query_peer( n2n_edge_t * eee, traceEvent( TRACE_DEBUG, "send QUERY_PEER to supernode" ); + if (eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx); + sendto_sock( eee->udp_sock, pktbuf, idx, &(eee->supernode) ); } @@ -806,6 +819,9 @@ static void send_register(n2n_edge_t * eee, traceEvent(TRACE_INFO, "Send REGISTER to %s", sock_to_cstr(sockbuf, remote_peer)); + if (eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx); + /* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, remote_peer); } @@ -845,6 +861,8 @@ static void send_register_ack(n2n_edge_t * eee, traceEvent(TRACE_INFO, "send REGISTER_ACK %s", sock_to_cstr(sockbuf, remote_peer)); + if (eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx); /* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, remote_peer); } @@ -1441,6 +1459,9 @@ static void send_packet2net(n2n_edge_t * eee, idx=0; encode_PACKET(pktbuf, &idx, &cmn, &pkt); + if (eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx); + idx += eee->transop.fwd(&eee->transop, pktbuf+idx, N2N_PKT_BUF_SIZE-idx, tap_pkt, len, pkt.dstMac); @@ -1566,6 +1587,12 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) { traceEvent(TRACE_DEBUG, "### Rx N2N UDP (%d) from %s", (signed int)recvlen, sock_to_cstr(sockbuf1, &sender)); + if (eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) + if ( packet_header_decrypt (udp_buf, recvlen, (char *)eee->conf.community_name, eee->conf.header_encryption_ctx) < 0) { + traceEvent(TRACE_DEBUG, "readFromIPSocket failed to decrypt header."); + return; + } + /* hexdump(udp_buf, recvlen); */ rem = recvlen; /* Counts down bytes of packet to protect against buffer overruns. */ @@ -2346,6 +2373,7 @@ void edge_init_conf_defaults(n2n_edge_conf_t *conf) { conf->local_port = 0 /* any port */; conf->mgmt_port = N2N_EDGE_MGMT_PORT; /* 5644 by default */ conf->transop_id = N2N_TRANSFORM_ID_NULL; + conf->header_encryption = HEADER_ENCRYPTION_NONE; conf->compression = N2N_COMPRESSION_ID_NONE; conf->drop_multicast = 1; conf->allow_p2p = 1; diff --git a/src/header_encryption.c b/src/header_encryption.c index ff61588..4151432 100644 --- a/src/header_encryption.c +++ b/src/header_encryption.c @@ -23,7 +23,7 @@ /* ********************************************************************** */ -uint32_t packet_header_decrypt (uint8_t packet[], uint8_t packet_len, +uint32_t packet_header_decrypt (uint8_t packet[], uint16_t packet_len, char * community_name, he_context_t * ctx) { // assemble IV @@ -33,18 +33,19 @@ uint32_t packet_header_decrypt (uint8_t packet[], uint8_t packet_len, // the first 96 bits of the packet get padded with ASCII "n2n!" // to full 128 bit IV memcpy (iv, packet, 12); - // alternatively, consider: pearson_hash_128 (iv, packet, 12); // try community name as possible key and check for magic bytes uint32_t magic = 0x6E326E00; // ="n2n_" uint32_t test_magic; - // check for magic bytes and resonable value in header len field + // check for magic bytes and reasonable value in header len field + // so, as a first step, decrypt 4 bytes only starting at byte 12 speck_he ((uint8_t*)&test_magic, &packet[12], 4, iv, (speck_context_t*)ctx); test_magic = be32toh (test_magic); - if ( ((test_magic << 8) == magic) - && ((test_magic >> 24) <= packet_len) // (test_masgic >> 24) is header_len + if ( (((test_magic >> 8) << 8) == magic) // check the thre uppermost bytes + && (((uint8_t)test_magic) <= packet_len) // lowest 8 bit of test_magic are header_len ) { - speck_he (&packet[12], &packet[12], (test_magic >> 24) - 12, iv, (speck_context_t*)ctx); + // decrypt the complete header + speck_he (&packet[12], &packet[12], (uint8_t)(test_magic) - 12, iv, (speck_context_t*)ctx); // restore original packet order memcpy (&packet[0], &packet[16], 4); memcpy (&packet[4], community_name, N2N_COMMUNITY_SIZE); @@ -55,89 +56,38 @@ uint32_t packet_header_decrypt (uint8_t packet[], uint8_t packet_len, /* ********************************************************************** */ -int8_t packet_header_decrypt_if_required (uint8_t packet[], uint16_t packet_len, - struct sn_community *communities) { - - struct sn_community *c, *tmp; - - - if (packet_len < 20) - return (-1); - - // first, check if header is unenrypted to put it into the fast-lane then - - // the following check is around 99.99962 percent reliable - // it heavily relies on the structure of packet's common part - // changes to wire.c:encode/decode_common need to go together with this code - if ( (packet[19] == (uint8_t)0x00) // null terminated community name - && (packet[00] == N2N_PKT_VERSION) // correct packet version - // && (packet[01] <= N2N_DEFAULT_TTL) // reasonable TTL -- might interfere with hole-punching-related or cli passed higher values ?! - && ((be16toh (*(uint16_t*)&(packet[02])) & N2N_FLAGS_TYPE_MASK ) <= MSG_TYPE_MAX_TYPE ) // message type - && ( be16toh (*(uint16_t*)&(packet[02])) < N2N_FLAGS_OPTIONS) // flags - ) { - - // most probably unencrypted - - // make sure, no downgrading happens here and no unencrypted packets can be - // injected in a community which definitely deals with encrypted headers - HASH_FIND_COMMUNITY(communities, (char *)&packet[04], c); - if (!c) - if (c->header_encryption == HEADER_ENCRYPTION_ENABLED) - return (-2); - // set 'no encryption' in case it is not set yet - c->header_encryption = HEADER_ENCRYPTION_NONE; - c->header_encryption_ctx = NULL; - return (HEADER_ENCRYPTION_NONE); - } else { - - // most probably encrypted - // cycle through the known communities (as keys) to eventually decrypt - int32_t ret; - HASH_ITER (hh, communities, c, tmp) { - // skip the definitely unencrypted communities - if (c->header_encryption == HEADER_ENCRYPTION_NONE) - continue; - if ( (ret = packet_header_decrypt (packet, packet_len, c->community, c->header_encryption_ctx)) ) { - // set 'encrypted' in case it is not set yet - c->header_encryption = HEADER_ENCRYPTION_ENABLED; - // no need to test further communities - return (HEADER_ENCRYPTION_ENABLED); - } - } - // no matching key/community - return (-3); - } -} - -/* ********************************************************************** */ - int32_t packet_header_encrypt (uint8_t packet[], uint8_t header_len, he_context_t * ctx) { - uint8_t iv[16]; - uint64_t *iv64 = (uint64_t*)&iv; - const uint32_t magic = 0x006E326E; - if (header_len < 20) + uint8_t iv[16]; + uint32_t *iv32 = (uint32_t*)&iv; + uint64_t *iv64 = (uint64_t*)&iv; + const uint32_t magic = 0x6E326E21; // = ASCII "n2n!" + + if (header_len < 20) { + traceEvent(TRACE_DEBUG, "packet_header_encrypt dropped a packet too short to be valid."); return (-1); + } memcpy (&packet[16], &packet[00], 4); - - iv64[0] = n2n_rand(); - iv64[1] = n2n_rand(); - iv64[3] = htobe32(magic); - iv[12] = header_len; + + iv64[0] = n2n_rand (); + iv32[2] = n2n_rand (); + iv32[3] = htobe32 (magic); + + memcpy (packet, iv, 16); + packet[15] = header_len; speck_he (&packet[12], &packet[12], header_len - 12, iv, (speck_context_t*)ctx); - return (0); } /* ********************************************************************** */ -void packet_header_setup_key (char * community_name, he_context_t * ctx) { +void packet_header_setup_key (const char * community_name, he_context_t ** ctx) { uint8_t key[16]; pearson_hash_128 (key, (uint8_t*)community_name, N2N_COMMUNITY_SIZE); - ctx = calloc(1, sizeof(speck_context_t)); - speck_expand_key_he (key, (speck_context_t*)ctx); + *ctx = (he_context_t*)calloc(1, sizeof (speck_context_t)); + speck_expand_key_he (key, (speck_context_t*)*ctx); } diff --git a/src/sn.c b/src/sn.c index 636b3b3..ec6d211 100644 --- a/src/sn.c +++ b/src/sn.c @@ -33,12 +33,14 @@ #define HASH_FIND_COMMUNITY(head,name,out) HASH_FIND_STR(head,name,out) static int try_forward(n2n_sn_t * sss, + const struct sn_community *comm, const n2n_common_t * cmn, const n2n_mac_t dstMac, const uint8_t * pktbuf, size_t pktsize); static int try_broadcast(n2n_sn_t * sss, + const struct sn_community *comm, const n2n_common_t * cmn, const n2n_mac_t srcMac, const uint8_t * pktbuf, @@ -188,24 +190,17 @@ static ssize_t sendto_sock(n2n_sn_t * sss, } static int try_forward(n2n_sn_t * sss, + const struct sn_community *comm, const n2n_common_t * cmn, const n2n_mac_t dstMac, const uint8_t * pktbuf, size_t pktsize) { struct peer_info * scan; - struct sn_community *community; macstr_t mac_buf; n2n_sock_str_t sockbuf; - HASH_FIND_COMMUNITY(sss->communities, (char*)cmn->community, community); - - if(!community) { - traceEvent(TRACE_DEBUG, "try_forward unknown community %s", cmn->community); - return(-1); - } - - HASH_FIND_PEER(community->edges, dstMac, scan); + HASH_FIND_PEER(comm->edges, dstMac, scan); if(NULL != scan) { @@ -248,51 +243,44 @@ static int try_forward(n2n_sn_t * sss, * the supernode. */ static int try_broadcast(n2n_sn_t * sss, + const struct sn_community *comm, const n2n_common_t * cmn, const n2n_mac_t srcMac, const uint8_t * pktbuf, size_t pktsize) { struct peer_info *scan, *tmp; - struct sn_community *community; macstr_t mac_buf; n2n_sock_str_t sockbuf; traceEvent(TRACE_DEBUG, "try_broadcast"); - HASH_FIND_COMMUNITY(sss->communities, (char*)cmn->community, community); + HASH_ITER(hh, comm->edges, scan, tmp) { + if(memcmp(srcMac, scan->mac_addr, sizeof(n2n_mac_t)) != 0) { + /* REVISIT: exclude if the destination socket is where the packet came from. */ + int data_sent_len; - if(community) { - HASH_ITER(hh, community->edges, scan, tmp) { - if(memcmp(srcMac, scan->mac_addr, sizeof(n2n_mac_t)) != 0) { - /* REVISIT: exclude if the destination socket is where the packet came from. */ - int data_sent_len; + data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize); - data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize); - - if(data_sent_len != pktsize) - { - ++(sss->stats.errors); - traceEvent(TRACE_WARNING, "multicast %lu to [%s] %s failed %s", - pktsize, - sock_to_cstr(sockbuf, &(scan->sock)), - macaddr_str(mac_buf, scan->mac_addr), - strerror(errno)); - } - else - { - ++(sss->stats.broadcast); - traceEvent(TRACE_DEBUG, "multicast %lu to [%s] %s", - pktsize, - sock_to_cstr(sockbuf, &(scan->sock)), - macaddr_str(mac_buf, scan->mac_addr)); - } + if(data_sent_len != pktsize) + { + ++(sss->stats.errors); + traceEvent(TRACE_WARNING, "multicast %lu to [%s] %s failed %s", + pktsize, + sock_to_cstr(sockbuf, &(scan->sock)), + macaddr_str(mac_buf, scan->mac_addr), + strerror(errno)); + } + else + { + ++(sss->stats.broadcast); + traceEvent(TRACE_DEBUG, "multicast %lu to [%s] %s", + pktsize, + sock_to_cstr(sockbuf, &(scan->sock)), + macaddr_str(mac_buf, scan->mac_addr)); } } - } else - traceEvent(TRACE_INFO, "ignoring broadcast on unknown community %s\n", - cmn->community); - + } return 0; } @@ -420,7 +408,7 @@ static int load_allowed_sn_community(n2n_sn_t *sss, char *path) { /* we do not know if header encryption is used in this community, * first packet will show. just in case, setup the key. */ s->header_encryption = HEADER_ENCRYPTION_UNKNOWN; - packet_header_setup_key (s->community, s->header_encryption_ctx); + packet_header_setup_key (s->community, &(s->header_encryption_ctx)); HASH_ADD_STR(sss->communities, community, s); num_communities++; @@ -452,21 +440,75 @@ static int process_udp(n2n_sn_t * sss, n2n_common_t cmn; /* common fields in the packet header */ size_t rem; size_t idx; - int8_t he = HEADER_ENCRYPTION_UNKNOWN; size_t msg_type; uint8_t from_supernode; macstr_t mac_buf; macstr_t mac_buf2; n2n_sock_str_t sockbuf; char buf[32]; + struct sn_community *comm, *tmp; traceEvent(TRACE_DEBUG, "Processing incoming UDP packet [len: %lu][sender: %s:%u]", udp_size, intoa(ntohl(sender_sock->sin_addr.s_addr), buf, sizeof(buf)), ntohs(sender_sock->sin_port)); - he = packet_header_decrypt_if_required (udp_buf, udp_size, sss->communities); - if (he < 0) - return -1; /* something wrong during packet decryption */ + /* check if header is unenrypted. the following check is around 99.99962 percent reliable. + * it heavily relies on the structure of packet's common part + * changes to wire.c:encode/decode_common need to go together with this code */ + if (udp_size < 20) { + traceEvent(TRACE_DEBUG, "process_udp dropped a packet too short to be valid."); + return -1; + } + if ( (udp_buf[19] == (uint8_t)0x00) // null terminated community name + && (udp_buf[00] == N2N_PKT_VERSION) // correct packet version + && ((be16toh (*(uint16_t*)&(udp_buf[02])) & N2N_FLAGS_TYPE_MASK ) <= MSG_TYPE_MAX_TYPE ) // message type + && ( be16toh (*(uint16_t*)&(udp_buf[02])) < N2N_FLAGS_OPTIONS) // flags + ) { + /* most probably unencrypted */ + /* make sure, no downgrading happens here and no unencrypted packets can be + * injected in a community which definitely deals with encrypted headers */ + HASH_FIND_COMMUNITY(sss->communities, (char *)&udp_buf[04], comm); + if (comm) { + if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { + traceEvent(TRACE_DEBUG, "process_udp dropped a packet with unencrypted header " + "addressed to community '%s' which uses encrypted headers.", + comm->community); + return -1; + } + if (comm->header_encryption == HEADER_ENCRYPTION_UNKNOWN) { + traceEvent (TRACE_INFO, "process_udp locked community '%s' to using " + "unencrypted headers.", comm->community); + /* set 'no encryption' in case it is not set yet */ + comm->header_encryption = HEADER_ENCRYPTION_NONE; + comm->header_encryption_ctx = NULL; + } + } + } else { + /* most probably encrypted */ + /* cycle through the known communities (as keys) to eventually decrypt */ + uint32_t ret = 0; + HASH_ITER (hh, sss->communities, comm, tmp) { + /* skip the definitely unencrypted communities */ + if (comm->header_encryption == HEADER_ENCRYPTION_NONE) + continue; + if ( (ret = packet_header_decrypt (udp_buf, udp_size, comm->community, comm->header_encryption_ctx)) ) { + if (comm->header_encryption == HEADER_ENCRYPTION_UNKNOWN) { + traceEvent (TRACE_INFO, "process_udp locked community '%s' to using " + "encrypted headers.", comm->community); + /* set 'encrypted' in case it is not set yet */ + comm->header_encryption = HEADER_ENCRYPTION_ENABLED; + } + // no need to test further communities + break; + } + } + if (!ret) { + // no matching key/community + traceEvent(TRACE_DEBUG, "process_udp dropped a packet with seemingly encrypted header " + "for which no matching community which uses encrypted headers was found."); + return -1; + } + } /* Use decode_common() to determine the kind of packet then process it: * @@ -506,8 +548,12 @@ static int process_udp(n2n_sn_t * sss, uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; size_t encx=0; int unicast; /* non-zero if unicast */ - const uint8_t * rec_buf; /* either udp_buf or encbuf */ + uint8_t * rec_buf; /* either udp_buf or encbuf */ + if(!comm) { + traceEvent(TRACE_DEBUG, "process_udp PACKET with unknown community %s", cmn.community); + return -1; + } sss->stats.last_fwd=now; decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx); @@ -535,6 +581,9 @@ static int process_udp(n2n_sn_t * sss, /* Re-encode the header. */ encode_PACKET(encbuf, &encx, &cmn2, &pkt); + if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (rec_buf, encx, comm->header_encryption_ctx); + /* Copy the original payload unchanged */ encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx)); } else { @@ -545,13 +594,16 @@ static int process_udp(n2n_sn_t * sss, rec_buf = udp_buf; encx = udp_size; + + if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (rec_buf, idx, comm->header_encryption_ctx); } /* Common section to forward the final product. */ if(unicast) - try_forward(sss, &cmn, pkt.dstMac, rec_buf, encx); + try_forward(sss, comm, &cmn, pkt.dstMac, rec_buf, encx); else - try_broadcast(sss, &cmn, pkt.srcMac, rec_buf, encx); + try_broadcast(sss, comm, &cmn, pkt.srcMac, rec_buf, encx); break; } case MSG_TYPE_REGISTER: @@ -563,7 +615,12 @@ static int process_udp(n2n_sn_t * sss, uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; size_t encx=0; int unicast; /* non-zero if unicast */ - const uint8_t * rec_buf; /* either udp_buf or encbuf */ + uint8_t * rec_buf; /* either udp_buf or encbuf */ + + if(!comm) { + traceEvent(TRACE_DEBUG, "process_udp REGISTER from unknown community %s", cmn.community); + return -1; + } sss->stats.last_fwd=now; decode_REGISTER(®, &cmn, udp_buf, &rem, &idx); @@ -601,7 +658,10 @@ static int process_udp(n2n_sn_t * sss, encx = udp_size; } - try_forward(sss, &cmn, reg.dstMac, rec_buf, encx); /* unicast only */ + if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (rec_buf, idx, comm->header_encryption_ctx); + + try_forward(sss, comm, &cmn, reg.dstMac, rec_buf, encx); /* unicast only */ } else traceEvent(TRACE_ERROR, "Rx REGISTER with multicast destination"); break; @@ -616,15 +676,12 @@ static int process_udp(n2n_sn_t * sss, n2n_common_t cmn2; uint8_t ackbuf[N2N_SN_PKTBUF_SIZE]; size_t encx=0; - struct sn_community *comm; /* Edge requesting registration with us. */ sss->stats.last_reg_super=now; ++(sss->stats.reg_super); decode_REGISTER_SUPER(®, &cmn, udp_buf, &rem, &idx); - HASH_FIND_COMMUNITY(sss->communities, (char*)cmn.community, comm); - /* Before we move any further, we need to check if the requested community is allowed by the supernode. In case it is not we do @@ -672,6 +729,9 @@ static int process_udp(n2n_sn_t * sss, encode_REGISTER_SUPER_ACK(ackbuf, &encx, &cmn2, &ack); + if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (ackbuf, encx, comm->header_encryption_ctx); + sendto(sss->sock, ackbuf, encx, 0, (struct sockaddr *)sender_sock, sizeof(struct sockaddr_in)); @@ -682,13 +742,18 @@ static int process_udp(n2n_sn_t * sss, traceEvent(TRACE_INFO, "Discarded registration: unallowed community '%s'", (char*)cmn.community); break; - } case MSG_TYPE_QUERY_PEER: { + } + case MSG_TYPE_QUERY_PEER: { n2n_QUERY_PEER_t query; uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; size_t encx=0; n2n_common_t cmn2; n2n_PEER_INFO_t pi; - struct sn_community *community; + + if(!comm) { + traceEvent(TRACE_DEBUG, "process_udp QUERY_PEER from unknown community %s", cmn.community); + return -1; + } decode_QUERY_PEER( &query, &cmn, udp_buf, &rem, &idx ); @@ -696,36 +761,35 @@ static int process_udp(n2n_sn_t * sss, macaddr_str( mac_buf, query.srcMac ), macaddr_str( mac_buf2, query.targetMac ) ); - HASH_FIND_COMMUNITY(sss->communities, (char*)cmn.community, community); + struct peer_info *scan; + HASH_FIND_PEER(comm->edges, query.targetMac, scan); - if(community) { - struct peer_info *scan; - HASH_FIND_PEER(community->edges, query.targetMac, scan); + if (scan) { + cmn2.ttl = N2N_DEFAULT_TTL; + cmn2.pc = n2n_peer_info; + cmn2.flags = N2N_FLAGS_FROM_SUPERNODE; + memcpy( cmn2.community, cmn.community, sizeof(n2n_community_t) ); - if (scan) { - cmn2.ttl = N2N_DEFAULT_TTL; - cmn2.pc = n2n_peer_info; - cmn2.flags = N2N_FLAGS_FROM_SUPERNODE; - memcpy( cmn2.community, cmn.community, sizeof(n2n_community_t) ); + pi.aflags = 0; + memcpy( pi.mac, query.targetMac, sizeof(n2n_mac_t) ); + pi.sock = scan->sock; - pi.aflags = 0; - memcpy( pi.mac, query.targetMac, sizeof(n2n_mac_t) ); - pi.sock = scan->sock; + encode_PEER_INFO( encbuf, &encx, &cmn2, &pi ); - encode_PEER_INFO( encbuf, &encx, &cmn2, &pi ); + if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (encbuf, encx, comm->header_encryption_ctx); - sendto( sss->sock, encbuf, encx, 0, - (struct sockaddr *)sender_sock, sizeof(struct sockaddr_in) ); + sendto( sss->sock, encbuf, encx, 0, + (struct sockaddr *)sender_sock, sizeof(struct sockaddr_in) ); - traceEvent( TRACE_DEBUG, "Tx PEER_INFO to %s", - macaddr_str( mac_buf, query.srcMac ) ); - } else { - traceEvent( TRACE_DEBUG, "Ignoring QUERY_PEER for unknown edge %s", - macaddr_str( mac_buf, query.targetMac ) ); - } + traceEvent( TRACE_DEBUG, "Tx PEER_INFO to %s", + macaddr_str( mac_buf, query.srcMac ) ); + } else { + traceEvent( TRACE_DEBUG, "Ignoring QUERY_PEER for unknown edge %s", + macaddr_str( mac_buf, query.targetMac ) ); } - break; + break; } default: /* Not a known message type */ diff --git a/src/sn_utils.c b/src/sn_utils.c index 256e3af..2c8ef90 100644 --- a/src/sn_utils.c +++ b/src/sn_utils.c @@ -5,22 +5,24 @@ #define N2N_SN_LPORT_DEFAULT 7654 #define N2N_SN_PKTBUF_SIZE 2048 -static int try_forward(n2n_sn_t *sss, - const n2n_common_t *cmn, - const n2n_mac_t dstMac, - const uint8_t *pktbuf, - size_t pktsize); +static int try_forward(n2n_sn_t * sss, + const struct sn_community *comm, + const n2n_common_t * cmn, + const n2n_mac_t dstMac, + const uint8_t * pktbuf, + size_t pktsize); static ssize_t sendto_sock(n2n_sn_t *sss, const n2n_sock_t *sock, const uint8_t *pktbuf, size_t pktsize); -static int try_broadcast(n2n_sn_t *sss, - const n2n_common_t *cmn, - const n2n_mac_t srcMac, - const uint8_t *pktbuf, - size_t pktsize); +static int try_broadcast(n2n_sn_t * sss, + const struct sn_community *comm, + const n2n_common_t * cmn, + const n2n_mac_t srcMac, + const uint8_t * pktbuf, + size_t pktsize); static uint16_t reg_lifetime(n2n_sn_t *sss); @@ -42,59 +44,51 @@ static int process_udp(n2n_sn_t *sss, size_t udp_size, time_t now); -static int try_forward(n2n_sn_t *sss, - const n2n_common_t *cmn, - const n2n_mac_t dstMac, - const uint8_t *pktbuf, - size_t pktsize) +static int try_forward(n2n_sn_t * sss, + const struct sn_community *comm, + const n2n_common_t * cmn, + const n2n_mac_t dstMac, + const uint8_t * pktbuf, + size_t pktsize) { - struct peer_info *scan; - struct sn_community *community; - macstr_t mac_buf; - n2n_sock_str_t sockbuf; + struct peer_info * scan; + macstr_t mac_buf; + n2n_sock_str_t sockbuf; - HASH_FIND_COMMUNITY(sss->communities, (char *)cmn->community, community); + HASH_FIND_PEER(comm->edges, dstMac, scan); - if (!community) + if(NULL != scan) { - traceEvent(TRACE_DEBUG, "try_forward unknown community %s", cmn->community); - return (-1); - } + int data_sent_len; + data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize); - HASH_FIND_PEER(community->edges, dstMac, scan); - - if (NULL != scan) - { - int data_sent_len; - data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize); - - if (data_sent_len == pktsize) + if(data_sent_len == pktsize) { - ++(sss->stats.fwd); - traceEvent(TRACE_DEBUG, "unicast %lu to [%s] %s", - pktsize, - sock_to_cstr(sockbuf, &(scan->sock)), - macaddr_str(mac_buf, scan->mac_addr)); + ++(sss->stats.fwd); + traceEvent(TRACE_DEBUG, "unicast %lu to [%s] %s", + pktsize, + sock_to_cstr(sockbuf, &(scan->sock)), + macaddr_str(mac_buf, scan->mac_addr)); } - else + else { - ++(sss->stats.errors); - traceEvent(TRACE_ERROR, "unicast %lu to [%s] %s FAILED (%d: %s)", - pktsize, - sock_to_cstr(sockbuf, &(scan->sock)), - macaddr_str(mac_buf, scan->mac_addr), - errno, strerror(errno)); + ++(sss->stats.errors); + traceEvent(TRACE_ERROR, "unicast %lu to [%s] %s FAILED (%d: %s)", + pktsize, + sock_to_cstr(sockbuf, &(scan->sock)), + macaddr_str(mac_buf, scan->mac_addr), + errno, strerror(errno)); } } - else + else { - traceEvent(TRACE_DEBUG, "try_forward unknown MAC"); + traceEvent(TRACE_DEBUG, "try_forward unknown MAC"); - /* Not a known MAC so drop. */ - return (-2); + /* Not a known MAC so drop. */ + return(-2); } - return (0); + return(0); } /** Send a datagram to the destination embodied in a n2n_sock_t. @@ -136,59 +130,49 @@ static ssize_t sendto_sock(n2n_sn_t *sss, * This will send the exact same datagram to zero or more edges registered to * the supernode. */ -static int try_broadcast(n2n_sn_t *sss, - const n2n_common_t *cmn, - const n2n_mac_t srcMac, - const uint8_t *pktbuf, - size_t pktsize) +static int try_broadcast(n2n_sn_t * sss, + const struct sn_community *comm, + const n2n_common_t * cmn, + const n2n_mac_t srcMac, + const uint8_t * pktbuf, + size_t pktsize) { - struct peer_info *scan, *tmp; - struct sn_community *community; - macstr_t mac_buf; - n2n_sock_str_t sockbuf; + struct peer_info *scan, *tmp; + macstr_t mac_buf; + n2n_sock_str_t sockbuf; - traceEvent(TRACE_DEBUG, "try_broadcast"); + traceEvent(TRACE_DEBUG, "try_broadcast"); - HASH_FIND_COMMUNITY(sss->communities, (char *)cmn->community, community); + HASH_ITER(hh, comm->edges, scan, tmp) { + if(memcmp(srcMac, scan->mac_addr, sizeof(n2n_mac_t)) != 0) { + /* REVISIT: exclude if the destination socket is where the packet came from. */ + int data_sent_len; - if (community) - { - HASH_ITER(hh, community->edges, scan, tmp) - { - if (memcmp(srcMac, scan->mac_addr, sizeof(n2n_mac_t)) != 0) - { - /* REVISIT: exclude if the destination socket is where the packet came from. */ - int data_sent_len; + data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize); - data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize); - - if (data_sent_len != pktsize) - { - ++(sss->stats.errors); - traceEvent(TRACE_WARNING, "multicast %lu to [%s] %s failed %s", - pktsize, - sock_to_cstr(sockbuf, &(scan->sock)), - macaddr_str(mac_buf, scan->mac_addr), - strerror(errno)); - } - else - { - ++(sss->stats.broadcast); - traceEvent(TRACE_DEBUG, "multicast %lu to [%s] %s", - pktsize, - sock_to_cstr(sockbuf, &(scan->sock)), - macaddr_str(mac_buf, scan->mac_addr)); - } - } - } + if(data_sent_len != pktsize) + { + ++(sss->stats.errors); + traceEvent(TRACE_WARNING, "multicast %lu to [%s] %s failed %s", + pktsize, + sock_to_cstr(sockbuf, &(scan->sock)), + macaddr_str(mac_buf, scan->mac_addr), + strerror(errno)); + } + else + { + ++(sss->stats.broadcast); + traceEvent(TRACE_DEBUG, "multicast %lu to [%s] %s", + pktsize, + sock_to_cstr(sockbuf, &(scan->sock)), + macaddr_str(mac_buf, scan->mac_addr)); + } } - else - traceEvent(TRACE_INFO, "ignoring broadcast on unknown community %s\n", - cmn->community); - - return 0; + } + return 0; } + /** Initialise the supernode structure */ int sn_init(n2n_sn_t *sss) { @@ -373,316 +357,372 @@ static int process_mgmt(n2n_sn_t *sss, /** Examine a datagram and determine what to do with it. * */ -static int process_udp(n2n_sn_t *sss, - const struct sockaddr_in *sender_sock, - uint8_t *udp_buf, - size_t udp_size, - time_t now) +static int process_udp(n2n_sn_t * sss, + const struct sockaddr_in * sender_sock, + uint8_t * udp_buf, + size_t udp_size, + time_t now) { - n2n_common_t cmn; /* common fields in the packet header */ - size_t rem; - size_t idx; - int8_t he = HEADER_ENCRYPTION_UNKNOWN; - size_t msg_type; - uint8_t from_supernode; - macstr_t mac_buf; - macstr_t mac_buf2; - n2n_sock_str_t sockbuf; - char buf[32]; + n2n_common_t cmn; /* common fields in the packet header */ + size_t rem; + size_t idx; + size_t msg_type; + uint8_t from_supernode; + macstr_t mac_buf; + macstr_t mac_buf2; + n2n_sock_str_t sockbuf; + char buf[32]; + struct sn_community *comm, *tmp; - traceEvent(TRACE_DEBUG, "Processing incoming UDP packet [len: %lu][sender: %s:%u]", - udp_size, intoa(ntohl(sender_sock->sin_addr.s_addr), buf, sizeof(buf)), - ntohs(sender_sock->sin_port)); + traceEvent(TRACE_DEBUG, "Processing incoming UDP packet [len: %lu][sender: %s:%u]", + udp_size, intoa(ntohl(sender_sock->sin_addr.s_addr), buf, sizeof(buf)), + ntohs(sender_sock->sin_port)); - he = packet_header_decrypt_if_required (udp_buf, udp_size, sss->communities); - if (he < 0) - return -1; /* something wrong during packet decryption */ - - /* Use decode_common() to determine the kind of packet then process it: - * - * REGISTER_SUPER adds an edge and generate a return REGISTER_SUPER_ACK - * - * REGISTER, REGISTER_ACK and PACKET messages are forwarded to their - * destination edge. If the destination is not known then PACKETs are - * broadcast. - */ - - rem = udp_size; /* Counts down bytes of packet to protect against buffer overruns. */ - idx = 0; /* marches through packet header as parts are decoded. */ - if (decode_common(&cmn, udp_buf, &rem, &idx) < 0) - { - traceEvent(TRACE_ERROR, "Failed to decode common section"); - return -1; /* failed to decode packet */ + /* check if header is unenrypted. the following check is around 99.99962 percent reliable. + * it heavily relies on the structure of packet's common part + * changes to wire.c:encode/decode_common need to go together with this code */ + if (udp_size < 20) { + traceEvent(TRACE_DEBUG, "process_udp dropped a packet too short to be valid."); + return -1; + } + if ( (udp_buf[19] == (uint8_t)0x00) // null terminated community name + && (udp_buf[00] == N2N_PKT_VERSION) // correct packet version + && ((be16toh (*(uint16_t*)&(udp_buf[02])) & N2N_FLAGS_TYPE_MASK ) <= MSG_TYPE_MAX_TYPE ) // message type + && ( be16toh (*(uint16_t*)&(udp_buf[02])) < N2N_FLAGS_OPTIONS) // flags + ) { + /* most probably unencrypted */ + /* make sure, no downgrading happens here and no unencrypted packets can be + * injected in a community which definitely deals with encrypted headers */ + HASH_FIND_COMMUNITY(sss->communities, (char *)&udp_buf[04], comm); + if (comm) { + if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { + traceEvent(TRACE_DEBUG, "process_udp dropped a packet with unencrypted header " + "addressed to community '%s' which uses encrypted headers.", + comm->community); + return -1; + } + if (comm->header_encryption == HEADER_ENCRYPTION_UNKNOWN) { + traceEvent (TRACE_INFO, "process_udp locked community '%s' to using " + "unencrypted headers.", comm->community); + /* set 'no encryption' in case it is not set yet */ + comm->header_encryption = HEADER_ENCRYPTION_NONE; + comm->header_encryption_ctx = NULL; + } } - - msg_type = cmn.pc; /* packet code */ - from_supernode = cmn.flags & N2N_FLAGS_FROM_SUPERNODE; - - if (cmn.ttl < 1) - { - traceEvent(TRACE_WARNING, "Expired TTL"); - return 0; /* Don't process further */ - } - - --(cmn.ttl); /* The value copied into all forwarded packets. */ - - switch (msg_type) - { - case MSG_TYPE_PACKET: - { - /* PACKET from one edge to another edge via supernode. */ - - /* pkt will be modified in place and recoded to an output of potentially - * different size due to addition of the socket.*/ - n2n_PACKET_t pkt; - n2n_common_t cmn2; - uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; - size_t encx = 0; - int unicast; /* non-zero if unicast */ - const uint8_t *rec_buf; /* either udp_buf or encbuf */ - - sss->stats.last_fwd = now; - decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx); - - unicast = (0 == is_multi_broadcast(pkt.dstMac)); - - traceEvent(TRACE_DEBUG, "RX PACKET (%s) %s -> %s %s", - (unicast ? "unicast" : "multicast"), - macaddr_str(mac_buf, pkt.srcMac), - macaddr_str(mac_buf2, pkt.dstMac), - (from_supernode ? "from sn" : "local")); - - if (!from_supernode) - { - memcpy(&cmn2, &cmn, sizeof(n2n_common_t)); - - /* We are going to add socket even if it was not there before */ - cmn2.flags |= N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; - - pkt.sock.family = AF_INET; - pkt.sock.port = ntohs(sender_sock->sin_port); - memcpy(pkt.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE); - - rec_buf = encbuf; - - /* Re-encode the header. */ - encode_PACKET(encbuf, &encx, &cmn2, &pkt); - - /* Copy the original payload unchanged */ - encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx)); + } else { + /* most probably encrypted */ + /* cycle through the known communities (as keys) to eventually decrypt */ + uint32_t ret = 0; + HASH_ITER (hh, sss->communities, comm, tmp) { + /* skip the definitely unencrypted communities */ + if (comm->header_encryption == HEADER_ENCRYPTION_NONE) + continue; + if ( (ret = packet_header_decrypt (udp_buf, udp_size, comm->community, comm->header_encryption_ctx)) ) { + if (comm->header_encryption == HEADER_ENCRYPTION_UNKNOWN) { + traceEvent (TRACE_INFO, "process_udp locked community '%s' to using " + "encrypted headers.", comm->community); + /* set 'encrypted' in case it is not set yet */ + comm->header_encryption = HEADER_ENCRYPTION_ENABLED; } - else - { - /* Already from a supernode. Nothing to modify, just pass to - * destination. */ - - traceEvent(TRACE_DEBUG, "Rx PACKET fwd unmodified"); - - rec_buf = udp_buf; - encx = udp_size; - } - - /* Common section to forward the final product. */ - if (unicast) - try_forward(sss, &cmn, pkt.dstMac, rec_buf, encx); - else - try_broadcast(sss, &cmn, pkt.srcMac, rec_buf, encx); + // no need to test further communities break; + } } - case MSG_TYPE_REGISTER: - { - /* Forwarding a REGISTER from one edge to the next */ - - n2n_REGISTER_t reg; - n2n_common_t cmn2; - uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; - size_t encx = 0; - int unicast; /* non-zero if unicast */ - const uint8_t *rec_buf; /* either udp_buf or encbuf */ - - sss->stats.last_fwd = now; - decode_REGISTER(®, &cmn, udp_buf, &rem, &idx); - - unicast = (0 == is_multi_broadcast(reg.dstMac)); - - if (unicast) - { - traceEvent(TRACE_DEBUG, "Rx REGISTER %s -> %s %s", - macaddr_str(mac_buf, reg.srcMac), - macaddr_str(mac_buf2, reg.dstMac), - ((cmn.flags & N2N_FLAGS_FROM_SUPERNODE) ? "from sn" : "local")); - - if (0 == (cmn.flags & N2N_FLAGS_FROM_SUPERNODE)) - { - memcpy(&cmn2, &cmn, sizeof(n2n_common_t)); - - /* We are going to add socket even if it was not there before */ - cmn2.flags |= N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; - - reg.sock.family = AF_INET; - reg.sock.port = ntohs(sender_sock->sin_port); - memcpy(reg.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE); - - rec_buf = encbuf; - - /* Re-encode the header. */ - encode_REGISTER(encbuf, &encx, &cmn2, ®); - - /* Copy the original payload unchanged */ - encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx)); - } - else - { - /* Already from a supernode. Nothing to modify, just pass to - * destination. */ - - rec_buf = udp_buf; - encx = udp_size; - } - - try_forward(sss, &cmn, reg.dstMac, rec_buf, encx); /* unicast only */ - } - else - traceEvent(TRACE_ERROR, "Rx REGISTER with multicast destination"); - break; + if (!ret) { + // no matching key/community + traceEvent(TRACE_DEBUG, "process_udp dropped a packet with seemingly encrypted header " + "for which no matching community which uses encrypted headers was found."); + return -1; } - case MSG_TYPE_REGISTER_ACK: - traceEvent(TRACE_DEBUG, "Rx REGISTER_ACK (NOT IMPLEMENTED) SHould not be via supernode"); - break; - case MSG_TYPE_REGISTER_SUPER: - { - n2n_REGISTER_SUPER_t reg; - n2n_REGISTER_SUPER_ACK_t ack; - n2n_common_t cmn2; - uint8_t ackbuf[N2N_SN_PKTBUF_SIZE]; - size_t encx = 0; - struct sn_community *comm; + } - /* Edge requesting registration with us. */ - sss->stats.last_reg_super = now; - ++(sss->stats.reg_super); - decode_REGISTER_SUPER(®, &cmn, udp_buf, &rem, &idx); + /* Use decode_common() to determine the kind of packet then process it: + * + * REGISTER_SUPER adds an edge and generate a return REGISTER_SUPER_ACK + * + * REGISTER, REGISTER_ACK and PACKET messages are forwarded to their + * destination edge. If the destination is not known then PACKETs are + * broadcast. + */ - HASH_FIND_COMMUNITY(sss->communities, (char *)cmn.community, comm); + rem = udp_size; /* Counts down bytes of packet to protect against buffer overruns. */ + idx = 0; /* marches through packet header as parts are decoded. */ + if(decode_common(&cmn, udp_buf, &rem, &idx) < 0) { + traceEvent(TRACE_ERROR, "Failed to decode common section"); + return -1; /* failed to decode packet */ + } - /* - Before we move any further, we need to check if the requested - community is allowed by the supernode. In case it is not we do - not report any message back to the edge to hide the supernode - existance (better from the security standpoint) - */ - if (!comm && !sss->lock_communities) - { - comm = calloc(1, sizeof(struct sn_community)); + msg_type = cmn.pc; /* packet code */ + from_supernode= cmn.flags & N2N_FLAGS_FROM_SUPERNODE; - if (comm) - { - strncpy(comm->community, (char *)cmn.community, N2N_COMMUNITY_SIZE - 1); - comm->community[N2N_COMMUNITY_SIZE - 1] = '\0'; - /* new communities introduced by REGISTERs could not have had encrypted header */ - comm->header_encryption = HEADER_ENCRYPTION_NONE; - comm->header_encryption_ctx = NULL; + if(cmn.ttl < 1) { + traceEvent(TRACE_WARNING, "Expired TTL"); + return 0; /* Don't process further */ + } - HASH_ADD_STR(sss->communities, community, comm); + --(cmn.ttl); /* The value copied into all forwarded packets. */ - traceEvent(TRACE_INFO, "New community: %s", comm->community); - } - } + switch(msg_type) { + case MSG_TYPE_PACKET: + { + /* PACKET from one edge to another edge via supernode. */ - if (comm) - { - cmn2.ttl = N2N_DEFAULT_TTL; - cmn2.pc = n2n_register_super_ack; - cmn2.flags = N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; - memcpy(cmn2.community, cmn.community, sizeof(n2n_community_t)); + /* pkt will be modified in place and recoded to an output of potentially + * different size due to addition of the socket.*/ + n2n_PACKET_t pkt; + n2n_common_t cmn2; + uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; + size_t encx=0; + int unicast; /* non-zero if unicast */ + uint8_t * rec_buf; /* either udp_buf or encbuf */ - memcpy(&(ack.cookie), &(reg.cookie), sizeof(n2n_cookie_t)); - memcpy(ack.edgeMac, reg.edgeMac, sizeof(n2n_mac_t)); - ack.lifetime = reg_lifetime(sss); - - ack.sock.family = AF_INET; - ack.sock.port = ntohs(sender_sock->sin_port); - memcpy(ack.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE); - - ack.num_sn = 0; /* No backup */ - memset(&(ack.sn_bak), 0, sizeof(n2n_sock_t)); - - traceEvent(TRACE_DEBUG, "Rx REGISTER_SUPER for %s [%s]", - macaddr_str(mac_buf, reg.edgeMac), - sock_to_cstr(sockbuf, &(ack.sock))); - - update_edge(sss, reg.edgeMac, comm, &(ack.sock), now); - - encode_REGISTER_SUPER_ACK(ackbuf, &encx, &cmn2, &ack); - - sendto(sss->sock, ackbuf, encx, 0, - (struct sockaddr *)sender_sock, sizeof(struct sockaddr_in)); - - traceEvent(TRACE_DEBUG, "Tx REGISTER_SUPER_ACK for %s [%s]", - macaddr_str(mac_buf, reg.edgeMac), - sock_to_cstr(sockbuf, &(ack.sock))); - } - else - traceEvent(TRACE_INFO, "Discarded registration: unallowed community '%s'", - (char *)cmn.community); - break; + if(!comm) { + traceEvent(TRACE_DEBUG, "process_udp PACKET with unknown community %s", cmn.community); + return -1; } - case MSG_TYPE_QUERY_PEER: - { - n2n_QUERY_PEER_t query; - uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; - size_t encx = 0; - n2n_common_t cmn2; - n2n_PEER_INFO_t pi; - struct sn_community *community; - decode_QUERY_PEER(&query, &cmn, udp_buf, &rem, &idx); + sss->stats.last_fwd=now; + decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx); - traceEvent(TRACE_DEBUG, "Rx QUERY_PEER from %s for %s", - macaddr_str(mac_buf, query.srcMac), - macaddr_str(mac_buf2, query.targetMac)); + unicast = (0 == is_multi_broadcast(pkt.dstMac)); - HASH_FIND_COMMUNITY(sss->communities, (char *)cmn.community, community); + traceEvent(TRACE_DEBUG, "RX PACKET (%s) %s -> %s %s", + (unicast?"unicast":"multicast"), + macaddr_str(mac_buf, pkt.srcMac), + macaddr_str(mac_buf2, pkt.dstMac), + (from_supernode?"from sn":"local")); - if (community) - { - struct peer_info *scan; - HASH_FIND_PEER(community->edges, query.targetMac, scan); + if(!from_supernode) { + memcpy(&cmn2, &cmn, sizeof(n2n_common_t)); - if (scan) - { - cmn2.ttl = N2N_DEFAULT_TTL; - cmn2.pc = n2n_peer_info; - cmn2.flags = N2N_FLAGS_FROM_SUPERNODE; - memcpy(cmn2.community, cmn.community, sizeof(n2n_community_t)); + /* We are going to add socket even if it was not there before */ + cmn2.flags |= N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; - pi.aflags = 0; - memcpy(pi.mac, query.targetMac, sizeof(n2n_mac_t)); - pi.sock = scan->sock; + pkt.sock.family = AF_INET; + pkt.sock.port = ntohs(sender_sock->sin_port); + memcpy(pkt.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE); - encode_PEER_INFO(encbuf, &encx, &cmn2, &pi); + rec_buf = encbuf; - sendto(sss->sock, encbuf, encx, 0, - (struct sockaddr *)sender_sock, sizeof(struct sockaddr_in)); + /* Re-encode the header. */ + encode_PACKET(encbuf, &encx, &cmn2, &pkt); - traceEvent(TRACE_DEBUG, "Tx PEER_INFO to %s", - macaddr_str(mac_buf, query.srcMac)); - } - else - { - traceEvent(TRACE_DEBUG, "Ignoring QUERY_PEER for unknown edge %s", - macaddr_str(mac_buf, query.targetMac)); - } - } + if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (rec_buf, encx, comm->header_encryption_ctx); - break; + /* Copy the original payload unchanged */ + encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx)); + } else { + /* Already from a supernode. Nothing to modify, just pass to + * destination. */ + + traceEvent(TRACE_DEBUG, "Rx PACKET fwd unmodified"); + + rec_buf = udp_buf; + encx = udp_size; + + if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (rec_buf, idx, comm->header_encryption_ctx); } - default: - /* Not a known message type */ - traceEvent(TRACE_WARNING, "Unable to handle packet type %d: ignored", (signed int)msg_type); - } /* switch(msg_type) */ - return 0; + /* Common section to forward the final product. */ + if(unicast) + try_forward(sss, comm, &cmn, pkt.dstMac, rec_buf, encx); + else + try_broadcast(sss, comm, &cmn, pkt.srcMac, rec_buf, encx); + break; + } + case MSG_TYPE_REGISTER: + { + /* Forwarding a REGISTER from one edge to the next */ + + n2n_REGISTER_t reg; + n2n_common_t cmn2; + uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; + size_t encx=0; + int unicast; /* non-zero if unicast */ + uint8_t * rec_buf; /* either udp_buf or encbuf */ + + if(!comm) { + traceEvent(TRACE_DEBUG, "process_udp REGISTER from unknown community %s", cmn.community); + return -1; + } + + sss->stats.last_fwd=now; + decode_REGISTER(®, &cmn, udp_buf, &rem, &idx); + + unicast = (0 == is_multi_broadcast(reg.dstMac)); + + if(unicast) { + traceEvent(TRACE_DEBUG, "Rx REGISTER %s -> %s %s", + macaddr_str(mac_buf, reg.srcMac), + macaddr_str(mac_buf2, reg.dstMac), + ((cmn.flags & N2N_FLAGS_FROM_SUPERNODE)?"from sn":"local")); + + if(0 == (cmn.flags & N2N_FLAGS_FROM_SUPERNODE)) { + memcpy(&cmn2, &cmn, sizeof(n2n_common_t)); + + /* We are going to add socket even if it was not there before */ + cmn2.flags |= N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; + + reg.sock.family = AF_INET; + reg.sock.port = ntohs(sender_sock->sin_port); + memcpy(reg.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE); + + rec_buf = encbuf; + + /* Re-encode the header. */ + encode_REGISTER(encbuf, &encx, &cmn2, ®); + + /* Copy the original payload unchanged */ + encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx)); + } else { + /* Already from a supernode. Nothing to modify, just pass to + * destination. */ + + rec_buf = udp_buf; + encx = udp_size; + } + + if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (rec_buf, idx, comm->header_encryption_ctx); + + try_forward(sss, comm, &cmn, reg.dstMac, rec_buf, encx); /* unicast only */ + } else + traceEvent(TRACE_ERROR, "Rx REGISTER with multicast destination"); + break; + } + case MSG_TYPE_REGISTER_ACK: + traceEvent(TRACE_DEBUG, "Rx REGISTER_ACK (NOT IMPLEMENTED) SHould not be via supernode"); + break; + case MSG_TYPE_REGISTER_SUPER: + { + n2n_REGISTER_SUPER_t reg; + n2n_REGISTER_SUPER_ACK_t ack; + n2n_common_t cmn2; + uint8_t ackbuf[N2N_SN_PKTBUF_SIZE]; + size_t encx=0; + + /* Edge requesting registration with us. */ + sss->stats.last_reg_super=now; + ++(sss->stats.reg_super); + decode_REGISTER_SUPER(®, &cmn, udp_buf, &rem, &idx); + + /* + Before we move any further, we need to check if the requested + community is allowed by the supernode. In case it is not we do + not report any message back to the edge to hide the supernode + existance (better from the security standpoint) + */ + if(!comm && !sss->lock_communities) { + comm = calloc(1, sizeof(struct sn_community)); + + if(comm) { + strncpy(comm->community, (char*)cmn.community, N2N_COMMUNITY_SIZE-1); + comm->community[N2N_COMMUNITY_SIZE-1] = '\0'; + /* new communities introduced by REGISTERs could not have had encrypted header */ + comm->header_encryption = HEADER_ENCRYPTION_NONE; + comm->header_encryption_ctx = NULL; + + HASH_ADD_STR(sss->communities, community, comm); + + traceEvent(TRACE_INFO, "New community: %s", comm->community); + } + } + + if(comm) { + cmn2.ttl = N2N_DEFAULT_TTL; + cmn2.pc = n2n_register_super_ack; + cmn2.flags = N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; + memcpy(cmn2.community, cmn.community, sizeof(n2n_community_t)); + + memcpy(&(ack.cookie), &(reg.cookie), sizeof(n2n_cookie_t)); + memcpy(ack.edgeMac, reg.edgeMac, sizeof(n2n_mac_t)); + ack.lifetime = reg_lifetime(sss); + + ack.sock.family = AF_INET; + ack.sock.port = ntohs(sender_sock->sin_port); + memcpy(ack.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE); + + ack.num_sn=0; /* No backup */ + memset(&(ack.sn_bak), 0, sizeof(n2n_sock_t)); + + traceEvent(TRACE_DEBUG, "Rx REGISTER_SUPER for %s [%s]", + macaddr_str(mac_buf, reg.edgeMac), + sock_to_cstr(sockbuf, &(ack.sock))); + + update_edge(sss, reg.edgeMac, comm, &(ack.sock), now); + + encode_REGISTER_SUPER_ACK(ackbuf, &encx, &cmn2, &ack); + + if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (ackbuf, encx, comm->header_encryption_ctx); + + sendto(sss->sock, ackbuf, encx, 0, + (struct sockaddr *)sender_sock, sizeof(struct sockaddr_in)); + + traceEvent(TRACE_DEBUG, "Tx REGISTER_SUPER_ACK for %s [%s]", + macaddr_str(mac_buf, reg.edgeMac), + sock_to_cstr(sockbuf, &(ack.sock))); + } else + traceEvent(TRACE_INFO, "Discarded registration: unallowed community '%s'", + (char*)cmn.community); + break; + } + case MSG_TYPE_QUERY_PEER: { + n2n_QUERY_PEER_t query; + uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; + size_t encx=0; + n2n_common_t cmn2; + n2n_PEER_INFO_t pi; + + if(!comm) { + traceEvent(TRACE_DEBUG, "process_udp QUERY_PEER from unknown community %s", cmn.community); + return -1; + } + + decode_QUERY_PEER( &query, &cmn, udp_buf, &rem, &idx ); + + traceEvent( TRACE_DEBUG, "Rx QUERY_PEER from %s for %s", + macaddr_str( mac_buf, query.srcMac ), + macaddr_str( mac_buf2, query.targetMac ) ); + + struct peer_info *scan; + HASH_FIND_PEER(comm->edges, query.targetMac, scan); + + if (scan) { + cmn2.ttl = N2N_DEFAULT_TTL; + cmn2.pc = n2n_peer_info; + cmn2.flags = N2N_FLAGS_FROM_SUPERNODE; + memcpy( cmn2.community, cmn.community, sizeof(n2n_community_t) ); + + pi.aflags = 0; + memcpy( pi.mac, query.targetMac, sizeof(n2n_mac_t) ); + pi.sock = scan->sock; + + encode_PEER_INFO( encbuf, &encx, &cmn2, &pi ); + + if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) + packet_header_encrypt (encbuf, encx, comm->header_encryption_ctx); + + sendto( sss->sock, encbuf, encx, 0, + (struct sockaddr *)sender_sock, sizeof(struct sockaddr_in) ); + + traceEvent( TRACE_DEBUG, "Tx PEER_INFO to %s", + macaddr_str( mac_buf, query.srcMac ) ); + } else { + traceEvent( TRACE_DEBUG, "Ignoring QUERY_PEER for unknown edge %s", + macaddr_str( mac_buf, query.targetMac ) ); + } + + break; + } + default: + /* Not a known message type */ + traceEvent(TRACE_WARNING, "Unable to handle packet type %d: ignored", (signed int)msg_type); + } /* switch(msg_type) */ + + return 0; } /** Long lived processing entry point. Split out from main to simply