Header Encryption

This commit is contained in:
Logan007 2020-06-23 00:34:19 +05:45
parent 159a088696
commit 789dd90272
7 changed files with 626 additions and 538 deletions

View File

@ -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);

View File

@ -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) \

View File

@ -154,7 +154,7 @@ static void help() {
#ifndef __APPLE__
"[-D] "
#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)
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");
#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

View File

@ -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;

View File

@ -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);
}

216
src/sn.c
View File

@ -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(&reg, &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(&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
@ -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 */

View File

@ -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(&reg, &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, &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;
}
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(&reg, &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(&reg, &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, &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);
}
}
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