Browse Source

Merge pull request #271 from Logan007/he

Header Encryption
pull/275/head
Luca Deri 4 years ago
committed by GitHub
parent
commit
a5dc46af30
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      include/header_encryption.h
  2. 5
      include/n2n_define.h
  3. 13
      src/edge.c
  4. 28
      src/edge_utils.c
  5. 96
      src/header_encryption.c
  6. 220
      src/sn.c
  7. 782
      src/sn_utils.c

12
include/header_encryption.h

@ -17,19 +17,11 @@
*/ */
uint32_t packet_header_decrypt (uint8_t packet[], uint16_t packet_len,
/* 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,
char * community_name, he_context_t * ctx); 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); 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);

5
include/n2n_define.h

@ -60,6 +60,11 @@
bits of transform_id; will be obsolete as soon as compression gets bits of transform_id; will be obsolete as soon as compression gets
its own field in the packet. REVISIT then. */ 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 DEFAULT_MTU 1290
#define HASH_ADD_PEER(head,add) \ #define HASH_ADD_PEER(head,add) \

13
src/edge.c

@ -154,7 +154,7 @@ static void help() {
#ifndef __APPLE__ #ifndef __APPLE__
"[-D] " "[-D] "
#endif #endif
"[-r] [-E] [-v] [-i <reg_interval>] [-L <reg_ttl>] [-t <mgmt port>] [-A[<cipher>]] [-z[<compression algo>]] [-h]\n\n"); "[-r] [-E] [-v] [-i <reg_interval>] [-L <reg_ttl>] [-t <mgmt port>] [-A[<cipher>]] [-H] [-z[<compression algo>]] [-h]\n\n");
#if defined(N2N_CAN_NAME_IFACE) #if defined(N2N_CAN_NAME_IFACE)
printf("-d <tun device> | tun device name\n"); printf("-d <tun device> | tun device name\n");
@ -192,6 +192,7 @@ static void help() {
printf("-A4 | Use ChaCha20 for payload encryption. Requires a key.\n"); printf("-A4 | Use ChaCha20 for payload encryption. Requires a key.\n");
#endif #endif
printf("-A5 | Use Speck for payload encryption. Requires a key.\n"); 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"); printf("-z1 or -z | Enable lzo1x compression for outgoing data packets\n");
#ifdef N2N_HAVE_ZSTD #ifdef N2N_HAVE_ZSTD
printf("-z2 | Enable zstd compression for outgoing data packets\n"); 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; 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': case 'z':
{ {
int compression; int compression;
@ -551,7 +560,7 @@ static int loadFromCLI(int argc, char *argv[], n2n_edge_conf_t *conf, n2n_priv_c
u_char c; u_char c;
while((c = getopt_long(argc, argv, 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__ #ifdef __linux__
"T:n:" "T:n:"
#endif #endif

28
src/edge_utils.c

@ -17,6 +17,7 @@
*/ */
#include "n2n.h" #include "n2n.h"
#include "header_encryption.h"
/* heap allocation for compression as per lzo example doc */ /* 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) ] #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; 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) if(eee->transop.no_encryption)
traceEvent(TRACE_WARNING, "Encryption is disabled in edge"); 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", traceEvent(TRACE_DEBUG, "send REGISTER_SUPER to %s",
sock_to_cstr(sockbuf, supernode)); 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); /* 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" ); 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) ); 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", traceEvent(TRACE_INFO, "Send REGISTER to %s",
sock_to_cstr(sockbuf, remote_peer)); 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); /* 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", traceEvent(TRACE_INFO, "send REGISTER_ACK %s",
sock_to_cstr(sockbuf, remote_peer)); 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); /* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, remote_peer);
} }
@ -1441,6 +1459,9 @@ static void send_packet2net(n2n_edge_t * eee,
idx=0; idx=0;
encode_PACKET(pktbuf, &idx, &cmn, &pkt); 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, idx += eee->transop.fwd(&eee->transop,
pktbuf+idx, N2N_PKT_BUF_SIZE-idx, pktbuf+idx, N2N_PKT_BUF_SIZE-idx,
tap_pkt, len, pkt.dstMac); 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", traceEvent(TRACE_DEBUG, "### Rx N2N UDP (%d) from %s",
(signed int)recvlen, sock_to_cstr(sockbuf1, &sender)); (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); */ /* hexdump(udp_buf, recvlen); */
rem = recvlen; /* Counts down bytes of packet to protect against buffer overruns. */ 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->local_port = 0 /* any port */;
conf->mgmt_port = N2N_EDGE_MGMT_PORT; /* 5644 by default */ conf->mgmt_port = N2N_EDGE_MGMT_PORT; /* 5644 by default */
conf->transop_id = N2N_TRANSFORM_ID_NULL; conf->transop_id = N2N_TRANSFORM_ID_NULL;
conf->header_encryption = HEADER_ENCRYPTION_NONE;
conf->compression = N2N_COMPRESSION_ID_NONE; conf->compression = N2N_COMPRESSION_ID_NONE;
conf->drop_multicast = 1; conf->drop_multicast = 1;
conf->allow_p2p = 1; conf->allow_p2p = 1;

96
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) { char * community_name, he_context_t * ctx) {
// assemble IV // 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!" // the first 96 bits of the packet get padded with ASCII "n2n!"
// to full 128 bit IV // to full 128 bit IV
memcpy (iv, packet, 12); memcpy (iv, packet, 12);
// alternatively, consider: pearson_hash_128 (iv, packet, 12);
// try community name as possible key and check for magic bytes // try community name as possible key and check for magic bytes
uint32_t magic = 0x6E326E00; // ="n2n_" uint32_t magic = 0x6E326E00; // ="n2n_"
uint32_t test_magic; 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); speck_he ((uint8_t*)&test_magic, &packet[12], 4, iv, (speck_context_t*)ctx);
test_magic = be32toh (test_magic); test_magic = be32toh (test_magic);
if ( ((test_magic << 8) == magic) if ( (((test_magic >> 8) << 8) == magic) // check the thre uppermost bytes
&& ((test_magic >> 24) <= packet_len) // (test_masgic >> 24) is header_len && (((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 // restore original packet order
memcpy (&packet[0], &packet[16], 4); memcpy (&packet[0], &packet[16], 4);
memcpy (&packet[4], community_name, N2N_COMMUNITY_SIZE); 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) { int32_t packet_header_encrypt (uint8_t packet[], uint8_t header_len, he_context_t * ctx) {
uint8_t iv[16]; uint8_t iv[16];
uint32_t *iv32 = (uint32_t*)&iv;
uint64_t *iv64 = (uint64_t*)&iv; uint64_t *iv64 = (uint64_t*)&iv;
const uint32_t magic = 0x006E326E; const uint32_t magic = 0x6E326E21; // = ASCII "n2n!"
if (header_len < 20) if (header_len < 20) {
traceEvent(TRACE_DEBUG, "packet_header_encrypt dropped a packet too short to be valid.");
return (-1); return (-1);
}
memcpy (&packet[16], &packet[00], 4); memcpy (&packet[16], &packet[00], 4);
iv64[0] = n2n_rand();
iv64[1] = n2n_rand();
iv64[3] = htobe32(magic);
iv[12] = header_len;
speck_he (&packet[12], &packet[12], header_len - 12, iv, (speck_context_t*)ctx); 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); 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]; uint8_t key[16];
pearson_hash_128 (key, (uint8_t*)community_name, N2N_COMMUNITY_SIZE); pearson_hash_128 (key, (uint8_t*)community_name, N2N_COMMUNITY_SIZE);
ctx = calloc(1, sizeof(speck_context_t)); *ctx = (he_context_t*)calloc(1, sizeof (speck_context_t));
speck_expand_key_he (key, (speck_context_t*)ctx); speck_expand_key_he (key, (speck_context_t*)*ctx);
} }

220
src/sn.c

@ -33,12 +33,14 @@
#define HASH_FIND_COMMUNITY(head,name,out) HASH_FIND_STR(head,name,out) #define HASH_FIND_COMMUNITY(head,name,out) HASH_FIND_STR(head,name,out)
static int try_forward(n2n_sn_t * sss, static int try_forward(n2n_sn_t * sss,
const struct sn_community *comm,
const n2n_common_t * cmn, const n2n_common_t * cmn,
const n2n_mac_t dstMac, const n2n_mac_t dstMac,
const uint8_t * pktbuf, const uint8_t * pktbuf,
size_t pktsize); size_t pktsize);
static int try_broadcast(n2n_sn_t * sss, static int try_broadcast(n2n_sn_t * sss,
const struct sn_community *comm,
const n2n_common_t * cmn, const n2n_common_t * cmn,
const n2n_mac_t srcMac, const n2n_mac_t srcMac,
const uint8_t * pktbuf, 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, static int try_forward(n2n_sn_t * sss,
const struct sn_community *comm,
const n2n_common_t * cmn, const n2n_common_t * cmn,
const n2n_mac_t dstMac, const n2n_mac_t dstMac,
const uint8_t * pktbuf, const uint8_t * pktbuf,
size_t pktsize) size_t pktsize)
{ {
struct peer_info * scan; struct peer_info * scan;
struct sn_community *community;
macstr_t mac_buf; macstr_t mac_buf;
n2n_sock_str_t sockbuf; n2n_sock_str_t sockbuf;
HASH_FIND_COMMUNITY(sss->communities, (char*)cmn->community, community); HASH_FIND_PEER(comm->edges, dstMac, scan);
if(!community) {
traceEvent(TRACE_DEBUG, "try_forward unknown community %s", cmn->community);
return(-1);
}
HASH_FIND_PEER(community->edges, dstMac, scan);
if(NULL != scan) if(NULL != scan)
{ {
@ -248,51 +243,44 @@ static int try_forward(n2n_sn_t * sss,
* the supernode. * the supernode.
*/ */
static int try_broadcast(n2n_sn_t * sss, static int try_broadcast(n2n_sn_t * sss,
const struct sn_community *comm,
const n2n_common_t * cmn, const n2n_common_t * cmn,
const n2n_mac_t srcMac, const n2n_mac_t srcMac,
const uint8_t * pktbuf, const uint8_t * pktbuf,
size_t pktsize) size_t pktsize)
{ {
struct peer_info *scan, *tmp; struct peer_info *scan, *tmp;
struct sn_community *community;
macstr_t mac_buf; macstr_t mac_buf;
n2n_sock_str_t sockbuf; 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) {
if(community) { /* REVISIT: exclude if the destination socket is where the packet came from. */
HASH_ITER(hh, community->edges, scan, tmp) { int data_sent_len;
if(memcmp(srcMac, scan->mac_addr, sizeof(n2n_mac_t)) != 0) {
/* REVISIT: exclude if the destination socket is where the packet came from. */ data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize);
int data_sent_len;
if(data_sent_len != pktsize)
data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize); {
++(sss->stats.errors);
if(data_sent_len != pktsize) traceEvent(TRACE_WARNING, "multicast %lu to [%s] %s failed %s",
{ pktsize,
++(sss->stats.errors); sock_to_cstr(sockbuf, &(scan->sock)),
traceEvent(TRACE_WARNING, "multicast %lu to [%s] %s failed %s", macaddr_str(mac_buf, scan->mac_addr),
pktsize, strerror(errno));
sock_to_cstr(sockbuf, &(scan->sock)), }
macaddr_str(mac_buf, scan->mac_addr), else
strerror(errno)); {
} ++(sss->stats.broadcast);
else traceEvent(TRACE_DEBUG, "multicast %lu to [%s] %s",
{ pktsize,
++(sss->stats.broadcast); sock_to_cstr(sockbuf, &(scan->sock)),
traceEvent(TRACE_DEBUG, "multicast %lu to [%s] %s", macaddr_str(mac_buf, scan->mac_addr));
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;
} }
@ -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, /* we do not know if header encryption is used in this community,
* first packet will show. just in case, setup the key. */ * first packet will show. just in case, setup the key. */
s->header_encryption = HEADER_ENCRYPTION_UNKNOWN; 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); HASH_ADD_STR(sss->communities, community, s);
num_communities++; num_communities++;
@ -452,21 +440,75 @@ static int process_udp(n2n_sn_t * sss,
n2n_common_t cmn; /* common fields in the packet header */ n2n_common_t cmn; /* common fields in the packet header */
size_t rem; size_t rem;
size_t idx; size_t idx;
int8_t he = HEADER_ENCRYPTION_UNKNOWN;
size_t msg_type; size_t msg_type;
uint8_t from_supernode; uint8_t from_supernode;
macstr_t mac_buf; macstr_t mac_buf;
macstr_t mac_buf2; macstr_t mac_buf2;
n2n_sock_str_t sockbuf; n2n_sock_str_t sockbuf;
char buf[32]; char buf[32];
struct sn_community *comm, *tmp;
traceEvent(TRACE_DEBUG, "Processing incoming UDP packet [len: %lu][sender: %s:%u]", 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)), udp_size, intoa(ntohl(sender_sock->sin_addr.s_addr), buf, sizeof(buf)),
ntohs(sender_sock->sin_port)); ntohs(sender_sock->sin_port));
he = packet_header_decrypt_if_required (udp_buf, udp_size, sss->communities); /* check if header is unenrypted. the following check is around 99.99962 percent reliable.
if (he < 0) * it heavily relies on the structure of packet's common part
return -1; /* something wrong during packet decryption */ * 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: /* 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]; uint8_t encbuf[N2N_SN_PKTBUF_SIZE];
size_t encx=0; size_t encx=0;
int unicast; /* non-zero if unicast */ 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; sss->stats.last_fwd=now;
decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx); decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx);
@ -535,6 +581,9 @@ static int process_udp(n2n_sn_t * sss,
/* Re-encode the header. */ /* Re-encode the header. */
encode_PACKET(encbuf, &encx, &cmn2, &pkt); 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 */ /* Copy the original payload unchanged */
encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx)); encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx));
} else { } else {
@ -545,13 +594,16 @@ static int process_udp(n2n_sn_t * sss,
rec_buf = udp_buf; rec_buf = udp_buf;
encx = udp_size; 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. */ /* Common section to forward the final product. */
if(unicast) if(unicast)
try_forward(sss, &cmn, pkt.dstMac, rec_buf, encx); try_forward(sss, comm, &cmn, pkt.dstMac, rec_buf, encx);
else else
try_broadcast(sss, &cmn, pkt.srcMac, rec_buf, encx); try_broadcast(sss, comm, &cmn, pkt.srcMac, rec_buf, encx);
break; break;
} }
case MSG_TYPE_REGISTER: case MSG_TYPE_REGISTER:
@ -563,7 +615,12 @@ static int process_udp(n2n_sn_t * sss,
uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; uint8_t encbuf[N2N_SN_PKTBUF_SIZE];
size_t encx=0; size_t encx=0;
int unicast; /* non-zero if unicast */ 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; sss->stats.last_fwd=now;
decode_REGISTER(&reg, &cmn, udp_buf, &rem, &idx); decode_REGISTER(&reg, &cmn, udp_buf, &rem, &idx);
@ -601,7 +658,10 @@ static int process_udp(n2n_sn_t * sss,
encx = udp_size; 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 } else
traceEvent(TRACE_ERROR, "Rx REGISTER with multicast destination"); traceEvent(TRACE_ERROR, "Rx REGISTER with multicast destination");
break; break;
@ -616,15 +676,12 @@ static int process_udp(n2n_sn_t * sss,
n2n_common_t cmn2; n2n_common_t cmn2;
uint8_t ackbuf[N2N_SN_PKTBUF_SIZE]; uint8_t ackbuf[N2N_SN_PKTBUF_SIZE];
size_t encx=0; size_t encx=0;
struct sn_community *comm;
/* Edge requesting registration with us. */ /* Edge requesting registration with us. */
sss->stats.last_reg_super=now; sss->stats.last_reg_super=now;
++(sss->stats.reg_super); ++(sss->stats.reg_super);
decode_REGISTER_SUPER(&reg, &cmn, udp_buf, &rem, &idx); decode_REGISTER_SUPER(&reg, &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 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 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); 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, sendto(sss->sock, ackbuf, encx, 0,
(struct sockaddr *)sender_sock, sizeof(struct sockaddr_in)); (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'", traceEvent(TRACE_INFO, "Discarded registration: unallowed community '%s'",
(char*)cmn.community); (char*)cmn.community);
break; break;
} case MSG_TYPE_QUERY_PEER: { }
case MSG_TYPE_QUERY_PEER: {
n2n_QUERY_PEER_t query; n2n_QUERY_PEER_t query;
uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; uint8_t encbuf[N2N_SN_PKTBUF_SIZE];
size_t encx=0; size_t encx=0;
n2n_common_t cmn2; n2n_common_t cmn2;
n2n_PEER_INFO_t pi; 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 ); 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_buf, query.srcMac ),
macaddr_str( mac_buf2, query.targetMac ) ); 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) { if (scan) {
struct peer_info *scan; cmn2.ttl = N2N_DEFAULT_TTL;
HASH_FIND_PEER(community->edges, query.targetMac, scan); cmn2.pc = n2n_peer_info;
cmn2.flags = N2N_FLAGS_FROM_SUPERNODE;
memcpy( cmn2.community, cmn.community, sizeof(n2n_community_t) );
if (scan) { pi.aflags = 0;
cmn2.ttl = N2N_DEFAULT_TTL; memcpy( pi.mac, query.targetMac, sizeof(n2n_mac_t) );
cmn2.pc = n2n_peer_info; pi.sock = scan->sock;
cmn2.flags = N2N_FLAGS_FROM_SUPERNODE;
memcpy( cmn2.community, cmn.community, sizeof(n2n_community_t) );
pi.aflags = 0; encode_PEER_INFO( encbuf, &encx, &cmn2, &pi );
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, sendto( sss->sock, encbuf, encx, 0,
(struct sockaddr *)sender_sock, sizeof(struct sockaddr_in) ); (struct sockaddr *)sender_sock, sizeof(struct sockaddr_in) );
traceEvent( TRACE_DEBUG, "Tx PEER_INFO to %s", traceEvent( TRACE_DEBUG, "Tx PEER_INFO to %s",
macaddr_str( mac_buf, query.srcMac ) ); macaddr_str( mac_buf, query.srcMac ) );
} else { } else {
traceEvent( TRACE_DEBUG, "Ignoring QUERY_PEER for unknown edge %s", traceEvent( TRACE_DEBUG, "Ignoring QUERY_PEER for unknown edge %s",
macaddr_str( mac_buf, query.targetMac ) ); macaddr_str( mac_buf, query.targetMac ) );
}
} }
break; break;
} }
default: default:
/* Not a known message type */ /* Not a known message type */

782
src/sn_utils.c

@ -5,22 +5,24 @@
#define N2N_SN_LPORT_DEFAULT 7654 #define N2N_SN_LPORT_DEFAULT 7654
#define N2N_SN_PKTBUF_SIZE 2048 #define N2N_SN_PKTBUF_SIZE 2048
static int try_forward(n2n_sn_t *sss, static int try_forward(n2n_sn_t * sss,
const n2n_common_t *cmn, const struct sn_community *comm,
const n2n_mac_t dstMac, const n2n_common_t * cmn,
const uint8_t *pktbuf, const n2n_mac_t dstMac,
size_t pktsize); const uint8_t * pktbuf,
size_t pktsize);
static ssize_t sendto_sock(n2n_sn_t *sss, static ssize_t sendto_sock(n2n_sn_t *sss,
const n2n_sock_t *sock, const n2n_sock_t *sock,
const uint8_t *pktbuf, const uint8_t *pktbuf,
size_t pktsize); size_t pktsize);
static int try_broadcast(n2n_sn_t *sss, static int try_broadcast(n2n_sn_t * sss,
const n2n_common_t *cmn, const struct sn_community *comm,
const n2n_mac_t srcMac, const n2n_common_t * cmn,
const uint8_t *pktbuf, const n2n_mac_t srcMac,
size_t pktsize); const uint8_t * pktbuf,
size_t pktsize);
static uint16_t reg_lifetime(n2n_sn_t *sss); 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, size_t udp_size,
time_t now); time_t now);
static int try_forward(n2n_sn_t *sss, static int try_forward(n2n_sn_t * sss,
const n2n_common_t *cmn, const struct sn_community *comm,
const n2n_mac_t dstMac, const n2n_common_t * cmn,
const uint8_t *pktbuf, const n2n_mac_t dstMac,
size_t pktsize) const uint8_t * pktbuf,
size_t pktsize)
{ {
struct peer_info *scan; struct peer_info * scan;
struct sn_community *community; macstr_t mac_buf;
macstr_t mac_buf; n2n_sock_str_t sockbuf;
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) if(NULL != scan)
{ {
int data_sent_len; 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) if(data_sent_len == pktsize)
{ {
++(sss->stats.fwd); ++(sss->stats.fwd);
traceEvent(TRACE_DEBUG, "unicast %lu to [%s] %s", traceEvent(TRACE_DEBUG, "unicast %lu to [%s] %s",
pktsize, pktsize,
sock_to_cstr(sockbuf, &(scan->sock)), sock_to_cstr(sockbuf, &(scan->sock)),
macaddr_str(mac_buf, scan->mac_addr)); macaddr_str(mac_buf, scan->mac_addr));
} }
else else
{ {
++(sss->stats.errors); ++(sss->stats.errors);
traceEvent(TRACE_ERROR, "unicast %lu to [%s] %s FAILED (%d: %s)", traceEvent(TRACE_ERROR, "unicast %lu to [%s] %s FAILED (%d: %s)",
pktsize, pktsize,
sock_to_cstr(sockbuf, &(scan->sock)), sock_to_cstr(sockbuf, &(scan->sock)),
macaddr_str(mac_buf, scan->mac_addr), macaddr_str(mac_buf, scan->mac_addr),
errno, strerror(errno)); 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. */ /* Not a known MAC so drop. */
return (-2); return(-2);
} }
return (0); return(0);
} }
/** Send a datagram to the destination embodied in a n2n_sock_t. /** 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 * This will send the exact same datagram to zero or more edges registered to
* the supernode. * the supernode.
*/ */
static int try_broadcast(n2n_sn_t *sss, static int try_broadcast(n2n_sn_t * sss,
const n2n_common_t *cmn, const struct sn_community *comm,
const n2n_mac_t srcMac, const n2n_common_t * cmn,
const uint8_t *pktbuf, const n2n_mac_t srcMac,
size_t pktsize) const uint8_t * pktbuf,
size_t pktsize)
{ {
struct peer_info *scan, *tmp; struct peer_info *scan, *tmp;
struct sn_community *community; macstr_t mac_buf;
macstr_t mac_buf; n2n_sock_str_t sockbuf;
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) data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize);
{
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); if(data_sent_len != pktsize)
{
if (data_sent_len != pktsize) ++(sss->stats.errors);
{ traceEvent(TRACE_WARNING, "multicast %lu to [%s] %s failed %s",
++(sss->stats.errors); pktsize,
traceEvent(TRACE_WARNING, "multicast %lu to [%s] %s failed %s", sock_to_cstr(sockbuf, &(scan->sock)),
pktsize, macaddr_str(mac_buf, scan->mac_addr),
sock_to_cstr(sockbuf, &(scan->sock)), strerror(errno));
macaddr_str(mac_buf, scan->mac_addr), }
strerror(errno)); else
} {
else ++(sss->stats.broadcast);
{ traceEvent(TRACE_DEBUG, "multicast %lu to [%s] %s",
++(sss->stats.broadcast); pktsize,
traceEvent(TRACE_DEBUG, "multicast %lu to [%s] %s", sock_to_cstr(sockbuf, &(scan->sock)),
pktsize, macaddr_str(mac_buf, scan->mac_addr));
sock_to_cstr(sockbuf, &(scan->sock)), }
macaddr_str(mac_buf, scan->mac_addr));
}
}
}
} }
else }
traceEvent(TRACE_INFO, "ignoring broadcast on unknown community %s\n", return 0;
cmn->community);
return 0;
} }
/** Initialise the supernode structure */ /** Initialise the supernode structure */
int sn_init(n2n_sn_t *sss) 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. /** Examine a datagram and determine what to do with it.
* *
*/ */
static int process_udp(n2n_sn_t *sss, static int process_udp(n2n_sn_t * sss,
const struct sockaddr_in *sender_sock, const struct sockaddr_in * sender_sock,
uint8_t *udp_buf, uint8_t * udp_buf,
size_t udp_size, size_t udp_size,
time_t now) time_t now)
{ {
n2n_common_t cmn; /* common fields in the packet header */ n2n_common_t cmn; /* common fields in the packet header */
size_t rem; size_t rem;
size_t idx; size_t idx;
int8_t he = HEADER_ENCRYPTION_UNKNOWN; size_t msg_type;
size_t msg_type; uint8_t from_supernode;
uint8_t from_supernode; macstr_t mac_buf;
macstr_t mac_buf; macstr_t mac_buf2;
macstr_t mac_buf2; n2n_sock_str_t sockbuf;
n2n_sock_str_t sockbuf; char buf[32];
char buf[32]; struct sn_community *comm, *tmp;
traceEvent(TRACE_DEBUG, "Processing incoming UDP packet [len: %lu][sender: %s:%u]", 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)), udp_size, intoa(ntohl(sender_sock->sin_addr.s_addr), buf, sizeof(buf)),
ntohs(sender_sock->sin_port)); ntohs(sender_sock->sin_port));
he = packet_header_decrypt_if_required (udp_buf, udp_size, sss->communities); /* check if header is unenrypted. the following check is around 99.99962 percent reliable.
if (he < 0) * it heavily relies on the structure of packet's common part
return -1; /* something wrong during packet decryption */ * changes to wire.c:encode/decode_common need to go together with this code */
if (udp_size < 20) {
/* Use decode_common() to determine the kind of packet then process it: traceEvent(TRACE_DEBUG, "process_udp dropped a packet too short to be valid.");
* return -1;
* REGISTER_SUPER adds an edge and generate a return REGISTER_SUPER_ACK }
* if ( (udp_buf[19] == (uint8_t)0x00) // null terminated community name
* REGISTER, REGISTER_ACK and PACKET messages are forwarded to their && (udp_buf[00] == N2N_PKT_VERSION) // correct packet version
* destination edge. If the destination is not known then PACKETs are && ((be16toh (*(uint16_t*)&(udp_buf[02])) & N2N_FLAGS_TYPE_MASK ) <= MSG_TYPE_MAX_TYPE ) // message type
* broadcast. && ( be16toh (*(uint16_t*)&(udp_buf[02])) < N2N_FLAGS_OPTIONS) // flags
*/ ) {
/* most probably unencrypted */
rem = udp_size; /* Counts down bytes of packet to protect against buffer overruns. */ /* make sure, no downgrading happens here and no unencrypted packets can be
idx = 0; /* marches through packet header as parts are decoded. */ * injected in a community which definitely deals with encrypted headers */
if (decode_common(&cmn, udp_buf, &rem, &idx) < 0) HASH_FIND_COMMUNITY(sss->communities, (char *)&udp_buf[04], comm);
{ if (comm) {
traceEvent(TRACE_ERROR, "Failed to decode common section"); if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
return -1; /* failed to decode packet */ traceEvent(TRACE_DEBUG, "process_udp dropped a packet with unencrypted header "
} "addressed to community '%s' which uses encrypted headers.",
comm->community);
msg_type = cmn.pc; /* packet code */ return -1;
from_supernode = cmn.flags & N2N_FLAGS_FROM_SUPERNODE; }
if (comm->header_encryption == HEADER_ENCRYPTION_UNKNOWN) {
if (cmn.ttl < 1) traceEvent (TRACE_INFO, "process_udp locked community '%s' to using "
{ "unencrypted headers.", comm->community);
traceEvent(TRACE_WARNING, "Expired TTL"); /* set 'no encryption' in case it is not set yet */
return 0; /* Don't process further */ comm->header_encryption = HEADER_ENCRYPTION_NONE;
comm->header_encryption_ctx = NULL;
}
} }
} else {
--(cmn.ttl); /* The value copied into all forwarded packets. */ /* most probably encrypted */
/* cycle through the known communities (as keys) to eventually decrypt */
switch (msg_type) uint32_t ret = 0;
{ HASH_ITER (hh, sss->communities, comm, tmp) {
case MSG_TYPE_PACKET: /* skip the definitely unencrypted communities */
{ if (comm->header_encryption == HEADER_ENCRYPTION_NONE)
/* PACKET from one edge to another edge via supernode. */ continue;
if ( (ret = packet_header_decrypt (udp_buf, udp_size, comm->community, comm->header_encryption_ctx)) ) {
/* pkt will be modified in place and recoded to an output of potentially if (comm->header_encryption == HEADER_ENCRYPTION_UNKNOWN) {
* different size due to addition of the socket.*/ traceEvent (TRACE_INFO, "process_udp locked community '%s' to using "
n2n_PACKET_t pkt; "encrypted headers.", comm->community);
n2n_common_t cmn2; /* set 'encrypted' in case it is not set yet */
uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; comm->header_encryption = HEADER_ENCRYPTION_ENABLED;
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 // no need to test further communities
{
/* 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);
break; 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:
*
* 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 */
}
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 */
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;
} }
case MSG_TYPE_REGISTER:
{
/* Forwarding a REGISTER from one edge to the next */
n2n_REGISTER_t reg; sss->stats.last_fwd=now;
n2n_common_t cmn2; decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx);
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; unicast = (0 == is_multi_broadcast(pkt.dstMac));
decode_REGISTER(&reg, &cmn, udp_buf, &rem, &idx);
unicast = (0 == is_multi_broadcast(reg.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 (unicast) if(!from_supernode) {
{ memcpy(&cmn2, &cmn, sizeof(n2n_common_t));
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)) /* We are going to add socket even if it was not there before */
{ cmn2.flags |= N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE;
memcpy(&cmn2, &cmn, sizeof(n2n_common_t));
/* We are going to add socket even if it was not there before */ pkt.sock.family = AF_INET;
cmn2.flags |= N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; pkt.sock.port = ntohs(sender_sock->sin_port);
memcpy(pkt.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE);
reg.sock.family = AF_INET; rec_buf = encbuf;
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_PACKET(encbuf, &encx, &cmn2, &pkt);
/* Re-encode the header. */ if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED)
encode_REGISTER(encbuf, &encx, &cmn2, &reg); packet_header_encrypt (rec_buf, encx, comm->header_encryption_ctx);
/* Copy the original payload unchanged */ /* Copy the original payload unchanged */
encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx)); encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx));
} } else {
else /* Already from a supernode. Nothing to modify, just pass to
{ * destination. */
/* Already from a supernode. Nothing to modify, just pass to
* destination. */
rec_buf = udp_buf; traceEvent(TRACE_DEBUG, "Rx PACKET fwd unmodified");
encx = udp_size;
}
try_forward(sss, &cmn, reg.dstMac, rec_buf, encx); /* unicast only */ rec_buf = udp_buf;
} encx = udp_size;
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;
struct sn_community *comm;
/* Edge requesting registration with us. */
sss->stats.last_reg_super = now;
++(sss->stats.reg_super);
decode_REGISTER_SUPER(&reg, &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
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); if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt (rec_buf, idx, comm->header_encryption_ctx);
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);
sendto(sss->sock, ackbuf, encx, 0, /* Common section to forward the final product. */
(struct sockaddr *)sender_sock, sizeof(struct sockaddr_in)); 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;
}
traceEvent(TRACE_DEBUG, "Tx REGISTER_SUPER_ACK for %s [%s]", sss->stats.last_fwd=now;
macaddr_str(mac_buf, reg.edgeMac), decode_REGISTER(&reg, &cmn, udp_buf, &rem, &idx);
sock_to_cstr(sockbuf, &(ack.sock)));
} unicast = (0 == is_multi_broadcast(reg.dstMac));
else
traceEvent(TRACE_INFO, "Discarded registration: unallowed community '%s'", if(unicast) {
(char *)cmn.community); traceEvent(TRACE_DEBUG, "Rx REGISTER %s -> %s %s",
break; 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, &reg);
/* 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(&reg, &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);
}
} }
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); 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;
}
traceEvent(TRACE_DEBUG, "Rx QUERY_PEER from %s for %s", decode_QUERY_PEER( &query, &cmn, udp_buf, &rem, &idx );
macaddr_str(mac_buf, query.srcMac),
macaddr_str(mac_buf2, query.targetMac));
HASH_FIND_COMMUNITY(sss->communities, (char *)cmn.community, community); traceEvent( TRACE_DEBUG, "Rx QUERY_PEER from %s for %s",
macaddr_str( mac_buf, query.srcMac ),
macaddr_str( mac_buf2, query.targetMac ) );
if (community) struct peer_info *scan;
{ HASH_FIND_PEER(comm->edges, query.targetMac, scan);
struct peer_info *scan;
HASH_FIND_PEER(community->edges, query.targetMac, scan);
if (scan) if (scan) {
{ cmn2.ttl = N2N_DEFAULT_TTL;
cmn2.ttl = N2N_DEFAULT_TTL; cmn2.pc = n2n_peer_info;
cmn2.pc = n2n_peer_info; cmn2.flags = N2N_FLAGS_FROM_SUPERNODE;
cmn2.flags = N2N_FLAGS_FROM_SUPERNODE; memcpy( cmn2.community, cmn.community, sizeof(n2n_community_t) );
memcpy(cmn2.community, cmn.community, sizeof(n2n_community_t));
pi.aflags = 0; pi.aflags = 0;
memcpy(pi.mac, query.targetMac, sizeof(n2n_mac_t)); memcpy( pi.mac, query.targetMac, sizeof(n2n_mac_t) );
pi.sock = scan->sock; pi.sock = scan->sock;
encode_PEER_INFO(encbuf, &encx, &cmn2, &pi); encode_PEER_INFO( encbuf, &encx, &cmn2, &pi );
sendto(sss->sock, encbuf, encx, 0, if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED)
(struct sockaddr *)sender_sock, sizeof(struct sockaddr_in)); packet_header_encrypt (encbuf, encx, comm->header_encryption_ctx);
traceEvent(TRACE_DEBUG, "Tx PEER_INFO to %s", sendto( sss->sock, encbuf, encx, 0,
macaddr_str(mac_buf, query.srcMac)); (struct sockaddr *)sender_sock, sizeof(struct sockaddr_in) );
}
else
{
traceEvent(TRACE_DEBUG, "Ignoring QUERY_PEER for unknown edge %s",
macaddr_str(mac_buf, query.targetMac));
}
}
break; 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 ) );
} }
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; 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 /** Long lived processing entry point. Split out from main to simply

Loading…
Cancel
Save