Browse Source

Merge pull request #327 from Logan007/rp

added replay protection
pull/329/head
Luca Deri 4 years ago
committed by GitHub
parent
commit
8542ba0db6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      include/header_encryption.h
  2. 11
      include/n2n.h
  3. 245
      src/edge_utils.c
  4. 9
      src/header_encryption.c
  5. 92
      src/n2n.c
  6. 85
      src/sn_utils.c
  7. 20
      tools/benchmark.c

6
include/header_encryption.h

@ -19,10 +19,12 @@
uint32_t packet_header_decrypt (uint8_t packet[], uint16_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,
he_context_t * ctx_iv, uint16_t * checksum); he_context_t * ctx_iv,
uint64_t * stamp, uint16_t * checksum);
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,
he_context_t * ctx_iv, uint16_t checksum); he_context_t * ctx_iv,
uint64_t stamp, uint16_t checksum);
void packet_header_setup_key (const char * community_name, he_context_t ** ctx, void packet_header_setup_key (const char * community_name, he_context_t ** ctx,

11
include/n2n.h

@ -193,6 +193,7 @@ struct peer_info {
time_t last_seen; time_t last_seen;
time_t last_p2p; time_t last_p2p;
time_t last_sent_query; time_t last_sent_query;
uint64_t last_valid_time_stamp;
UT_hash_handle hh; /* makes this structure hashable */ UT_hash_handle hh; /* makes this structure hashable */
}; };
@ -303,8 +304,9 @@ struct n2n_edge {
n2n_trans_op_t transop; /**< The transop to use when encoding */ n2n_trans_op_t transop; /**< The transop to use when encoding */
n2n_cookie_t last_cookie; /**< Cookie sent in last REGISTER_SUPER. */ n2n_cookie_t last_cookie; /**< Cookie sent in last REGISTER_SUPER. */
n2n_route_t *sn_route_to_clean; /**< Supernode route to clean */ n2n_route_t *sn_route_to_clean; /**< Supernode route to clean */
n2n_edge_callbacks_t cb; /**< API callbacks */ n2n_edge_callbacks_t cb; /**< API callbacks */
void *user_data; /**< Can hold user data */ void *user_data; /**< Can hold user data */
uint64_t sn_last_valid_time_stamp;/*< last valid time stamp from supernode */
/* Sockets */ /* Sockets */
n2n_sock_t supernode; n2n_sock_t supernode;
@ -428,6 +430,11 @@ SOCKET open_socket(int local_port, int bind_any);
int sock_equal( const n2n_sock_t * a, int sock_equal( const n2n_sock_t * a,
const n2n_sock_t * b ); const n2n_sock_t * b );
/* Header encryption */
uint64_t time_stamp(void);
uint64_t initial_time_stamp (void);
int time_stamp_verify_and_update (uint64_t stamp, uint64_t * previous_stamp);
/* Operations on peer_info lists. */ /* Operations on peer_info lists. */
size_t purge_peer_list( struct peer_info ** peer_list, size_t purge_peer_list( struct peer_info ** peer_list,
time_t purge_before ); time_t purge_before );

245
src/edge_utils.c

@ -17,7 +17,6 @@
*/ */
#include "n2n.h" #include "n2n.h"
#include "header_encryption.h"
#include "edge_utils_win32.h" #include "edge_utils_win32.h"
/* heap allocation for compression as per lzo example doc */ /* heap allocation for compression as per lzo example doc */
@ -194,6 +193,7 @@ n2n_edge_t* edge_init(const tuntap_dev *dev, const n2n_edge_conf_t *conf, int *r
eee->known_peers = NULL; eee->known_peers = NULL;
eee->pending_peers = NULL; eee->pending_peers = NULL;
eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS;
eee->sn_last_valid_time_stamp = initial_time_stamp ();
pearson_hash_init(); pearson_hash_init();
@ -381,6 +381,42 @@ static int supernode2addr(n2n_sock_t * sn, const n2n_sn_name_t addrIn) {
/* ************************************** */ /* ************************************** */
static const int definitely_from_supernode = 1;
/***
*
* For a given packet, find the apporopriate internal last valid time stamp for lookup
* and verify it (and also update, if applicable).
*/
static int find_peer_time_stamp_and_verify (n2n_edge_t * eee,
int from_supernode, n2n_mac_t mac,
uint64_t stamp) {
uint64_t * previous_stamp = NULL;
if(from_supernode) {
// from supernode
previous_stamp = &(eee->sn_last_valid_time_stamp);
} else {
// from (peer) edge
struct peer_info *peer;
HASH_FIND_PEER(eee->pending_peers, mac, peer);
if(!peer) {
HASH_FIND_PEER(eee->known_peers, mac, peer);
}
if(peer) {
// time_stamp_verify_and_update allows the pointer a previous stamp to be NULL
// if it is a (so far) unknown peer
previous_stamp = &(peer->last_valid_time_stamp);
}
}
// failure --> 0; success --> 1
return ( time_stamp_verify_and_update (stamp, previous_stamp) );
}
/* ************************************** */
/*** /***
* *
* Register over multicast in case there is a peer on the same network listening * Register over multicast in case there is a peer on the same network listening
@ -432,6 +468,7 @@ static void register_with_new_peer(n2n_edge_t * eee,
scan->sock = *peer; scan->sock = *peer;
scan->timeout = REGISTER_SUPER_INTERVAL_DFL; /* TODO: should correspond to the peer supernode registration timeout */ scan->timeout = REGISTER_SUPER_INTERVAL_DFL; /* TODO: should correspond to the peer supernode registration timeout */
scan->last_seen = time(NULL); /* Don't change this it marks the pending peer for removal. */ scan->last_seen = time(NULL); /* Don't change this it marks the pending peer for removal. */
scan->last_valid_time_stamp = initial_time_stamp ();
HASH_ADD_PEER(eee->pending_peers, scan); HASH_ADD_PEER(eee->pending_peers, scan);
@ -731,7 +768,8 @@ static void send_register_super(n2n_edge_t * eee,
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx, packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx,
eee->conf.header_iv_ctx, pearson_hash_16 (pktbuf, idx)); eee->conf.header_iv_ctx,
time_stamp (), pearson_hash_16 (pktbuf, idx));
/* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, supernode); /* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, supernode);
} }
@ -763,9 +801,10 @@ static void send_query_peer( n2n_edge_t * eee,
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED){ if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED){
packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx, packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx,
eee->conf.header_iv_ctx, pearson_hash_16 (pktbuf, idx)); eee->conf.header_iv_ctx,
} time_stamp (), pearson_hash_16 (pktbuf, idx));
sendto_sock( eee->udp_sock, pktbuf, idx, &(eee->supernode) ); }
sendto_sock( eee->udp_sock, pktbuf, idx, &(eee->supernode) );
} }
/** Send a REGISTER packet to another edge. */ /** Send a REGISTER packet to another edge. */
@ -810,7 +849,8 @@ static void send_register(n2n_edge_t * eee,
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx, packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx,
eee->conf.header_iv_ctx, pearson_hash_16 (pktbuf, idx)); eee->conf.header_iv_ctx,
time_stamp (), pearson_hash_16 (pktbuf, idx));
/* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, remote_peer); /* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, remote_peer);
} }
@ -853,7 +893,8 @@ static void send_register_ack(n2n_edge_t * eee,
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx, packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx,
eee->conf.header_iv_ctx, pearson_hash_16 (pktbuf, idx)); eee->conf.header_iv_ctx,
time_stamp (), pearson_hash_16 (pktbuf, idx));
/* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, remote_peer); /* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, remote_peer);
} }
@ -1242,6 +1283,7 @@ static int check_query_peer_info(n2n_edge_t *eee, time_t now, n2n_mac_t mac) {
memcpy(scan->mac_addr, mac, N2N_MAC_SIZE); memcpy(scan->mac_addr, mac, N2N_MAC_SIZE);
scan->timeout = REGISTER_SUPER_INTERVAL_DFL; /* TODO: should correspond to the peer supernode registration timeout */ scan->timeout = REGISTER_SUPER_INTERVAL_DFL; /* TODO: should correspond to the peer supernode registration timeout */
scan->last_seen = now; /* Don't change this it marks the pending peer for removal. */ scan->last_seen = now; /* Don't change this it marks the pending peer for removal. */
scan->last_valid_time_stamp = initial_time_stamp ();
HASH_ADD_PEER(eee->pending_peers, scan); HASH_ADD_PEER(eee->pending_peers, scan);
} }
@ -1471,7 +1513,8 @@ void edge_send_packet2net(n2n_edge_t * eee,
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt (pktbuf, headerIdx, eee->conf.header_encryption_ctx, packet_header_encrypt (pktbuf, headerIdx, eee->conf.header_encryption_ctx,
eee->conf.header_iv_ctx, pearson_hash_16 (pktbuf, idx)); eee->conf.header_iv_ctx,
time_stamp (), pearson_hash_16 (pktbuf, idx));
#ifdef MTU_ASSERT_VALUE #ifdef MTU_ASSERT_VALUE
{ {
@ -1564,6 +1607,7 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) {
n2n_sock_t sender; n2n_sock_t sender;
n2n_sock_t * orig_sender=NULL; n2n_sock_t * orig_sender=NULL;
time_t now=0; time_t now=0;
uint64_t stamp = 0;
size_t i; size_t i;
@ -1601,10 +1645,16 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) {
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
uint16_t checksum = 0; uint16_t checksum = 0;
if( packet_header_decrypt (udp_buf, recvlen, (char *)eee->conf.community_name, eee->conf.header_encryption_ctx, if( packet_header_decrypt (udp_buf, recvlen, (char *)eee->conf.community_name, eee->conf.header_encryption_ctx,
eee->conf.header_iv_ctx, &checksum) == 0) { eee->conf.header_iv_ctx,
&stamp, &checksum) == 0) {
traceEvent(TRACE_DEBUG, "readFromIPSocket failed to decrypt header."); traceEvent(TRACE_DEBUG, "readFromIPSocket failed to decrypt header.");
return; return;
} }
// time stamp verification follows in the packet specific section as it requires to determine the
// sender from the hash list by its MAC, or the packet might be from the supernode, this all depends
// on packet type, path taken (via supernode) and packet structure (MAC is not always in the same place)
if (checksum != pearson_hash_16 (udp_buf, recvlen)) { if (checksum != pearson_hash_16 (udp_buf, recvlen)) {
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped packet due to checksum error."); traceEvent(TRACE_DEBUG, "readFromIPSocket dropped packet due to checksum error.");
return; return;
@ -1630,23 +1680,30 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) {
switch(msg_type) { switch(msg_type) {
case MSG_TYPE_PACKET: case MSG_TYPE_PACKET:
{ {
/* process PACKET - most frequent so first in list. */ /* process PACKET - most frequent so first in list. */
n2n_PACKET_t pkt; n2n_PACKET_t pkt;
decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx);
if(is_valid_peer_sock(&pkt.sock)) decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx);
orig_sender = &(pkt.sock);
if(!from_supernode) { if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
/* This is a P2P packet from the peer. We purge a pending if(!find_peer_time_stamp_and_verify (eee, from_supernode, pkt.srcMac, stamp)) {
* registration towards the possibly nat-ted peer address as we now have traceEvent(TRACE_DEBUG, "readFromIPSocket dropped PACKET due to time stamp error.");
* a valid channel. We still use check_peer_registration_needed in return;
* handle_PACKET to double check this. }
*/ }
traceEvent(TRACE_DEBUG, "Got P2P packet");
find_and_remove_peer(&eee->pending_peers, pkt.srcMac); if(is_valid_peer_sock(&pkt.sock))
} orig_sender = &(pkt.sock);
if(!from_supernode) {
/* This is a P2P packet from the peer. We purge a pending
* registration towards the possibly nat-ted peer address as we now have
* a valid channel. We still use check_peer_registration_needed in
* handle_PACKET to double check this.
*/
traceEvent(TRACE_DEBUG, "Got P2P packet");
find_and_remove_peer(&eee->pending_peers, pkt.srcMac);
}
traceEvent(TRACE_INFO, "Rx PACKET from %s (sender=%s) [%u B]", traceEvent(TRACE_INFO, "Rx PACKET from %s (sender=%s) [%u B]",
sock_to_cstr(sockbuf1, &sender), sock_to_cstr(sockbuf1, &sender),
@ -1664,8 +1721,15 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) {
decode_REGISTER(&reg, &cmn, udp_buf, &rem, &idx); decode_REGISTER(&reg, &cmn, udp_buf, &rem, &idx);
if(is_valid_peer_sock(&reg.sock)) if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
orig_sender = &(reg.sock); if(!find_peer_time_stamp_and_verify (eee, from_supernode, reg.srcMac, stamp)) {
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER due to time stamp error.");
return;
}
}
if(is_valid_peer_sock(&reg.sock))
orig_sender = &(reg.sock);
via_multicast = !memcmp(reg.dstMac, null_mac, 6); via_multicast = !memcmp(reg.dstMac, null_mac, 6);
@ -1708,8 +1772,15 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) {
decode_REGISTER_ACK(&ra, &cmn, udp_buf, &rem, &idx); decode_REGISTER_ACK(&ra, &cmn, udp_buf, &rem, &idx);
if(is_valid_peer_sock(&ra.sock)) if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
orig_sender = &(ra.sock); if(!find_peer_time_stamp_and_verify (eee, !definitely_from_supernode, ra.srcMac, stamp)) {
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER_ACK due to time stamp error.");
return;
}
}
if(is_valid_peer_sock(&ra.sock))
orig_sender = &(ra.sock);
traceEvent(TRACE_INFO, "Rx REGISTER_ACK src=%s dst=%s from peer %s (%s)", traceEvent(TRACE_INFO, "Rx REGISTER_ACK src=%s dst=%s from peer %s (%s)",
macaddr_str(mac_buf1, ra.srcMac), macaddr_str(mac_buf1, ra.srcMac),
@ -1724,59 +1795,73 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) {
{ {
n2n_REGISTER_SUPER_ACK_t ra; n2n_REGISTER_SUPER_ACK_t ra;
if(eee->sn_wait) if(eee->sn_wait)
{ {
decode_REGISTER_SUPER_ACK(&ra, &cmn, udp_buf, &rem, &idx); decode_REGISTER_SUPER_ACK(&ra, &cmn, udp_buf, &rem, &idx);
if(is_valid_peer_sock(&ra.sock)) if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
orig_sender = &(ra.sock); if(!find_peer_time_stamp_and_verify (eee, definitely_from_supernode, null_mac, stamp)) {
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER_SUPER_ACK due to time stamp error.");
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK myMAC=%s [%s] (external %s). Attempts %u", return;
macaddr_str(mac_buf1, ra.edgeMac), }
sock_to_cstr(sockbuf1, &sender), }
sock_to_cstr(sockbuf2, orig_sender),
(unsigned int)eee->sup_attempts); if(is_valid_peer_sock(&ra.sock))
orig_sender = &(ra.sock);
if(0 == memcmp(ra.cookie, eee->last_cookie, N2N_COOKIE_SIZE))
{ traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK myMAC=%s [%s] (external %s). Attempts %u",
if(ra.num_sn > 0) macaddr_str(mac_buf1, ra.edgeMac),
{ sock_to_cstr(sockbuf1, &sender),
traceEvent(TRACE_NORMAL, "Rx REGISTER_SUPER_ACK backup supernode at %s", sock_to_cstr(sockbuf2, orig_sender),
sock_to_cstr(sockbuf1, &(ra.sn_bak))); (unsigned int)eee->sup_attempts);
}
if(0 == memcmp(ra.cookie, eee->last_cookie, N2N_COOKIE_SIZE))
eee->last_sup = now; {
eee->sn_wait=0; if(ra.num_sn > 0)
eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; /* refresh because we got a response */ {
traceEvent(TRACE_NORMAL, "Rx REGISTER_SUPER_ACK backup supernode at %s",
if(eee->cb.sn_registration_updated) sock_to_cstr(sockbuf1, &(ra.sn_bak)));
eee->cb.sn_registration_updated(eee, now, &sender); }
/* NOTE: the register_interval should be chosen by the edge node eee->last_sup = now;
* based on its NAT configuration. */ eee->sn_wait=0;
//eee->conf.register_interval = ra.lifetime; eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; /* refresh because we got a response */
}
else if(eee->cb.sn_registration_updated)
{ eee->cb.sn_registration_updated(eee, now, &sender);
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with wrong or old cookie.");
} /* NOTE: the register_interval should be chosen by the edge node
} * based on its NAT configuration. */
else //eee->conf.register_interval = ra.lifetime;
{ }
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with no outstanding REGISTER_SUPER."); else
} {
break; traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with wrong or old cookie.");
}
}
else
{
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with no outstanding REGISTER_SUPER.");
}
break;
} case MSG_TYPE_PEER_INFO: { } case MSG_TYPE_PEER_INFO: {
n2n_PEER_INFO_t pi; n2n_PEER_INFO_t pi;
struct peer_info * scan; struct peer_info * scan;
decode_PEER_INFO( &pi, &cmn, udp_buf, &rem, &idx ); decode_PEER_INFO( &pi, &cmn, udp_buf, &rem, &idx );
if(!is_valid_peer_sock(&pi.sock)) { if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
traceEvent(TRACE_DEBUG, "Skip invalid PEER_INFO %s [%s]", if(!find_peer_time_stamp_and_verify (eee, definitely_from_supernode, null_mac, stamp)) {
sock_to_cstr(sockbuf1, &pi.sock), traceEvent(TRACE_DEBUG, "readFromIPSocket dropped PEER_INFO due to time stamp error.");
macaddr_str(mac_buf1, pi.mac) ); return;
break; }
} }
if(!is_valid_peer_sock(&pi.sock)) {
traceEvent(TRACE_DEBUG, "Skip invalid PEER_INFO %s [%s]",
sock_to_cstr(sockbuf1, &pi.sock),
macaddr_str(mac_buf1, pi.mac) );
break;
}
HASH_FIND_PEER(eee->pending_peers, pi.mac, scan); HASH_FIND_PEER(eee->pending_peers, pi.mac, scan);
if(scan) { if(scan) {

9
src/header_encryption.c

@ -25,7 +25,7 @@
uint32_t packet_header_decrypt (uint8_t packet[], uint16_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,
he_context_t * ctx_iv, uint16_t * checksum) { he_context_t * ctx_iv, uint64_t * stamp, uint16_t * checksum) {
// assemble IV // assemble IV
// the last four are ASCII "n2n!" and do not get overwritten // the last four are ASCII "n2n!" and do not get overwritten
@ -35,9 +35,10 @@ uint32_t packet_header_decrypt (uint8_t packet[], uint16_t packet_len,
// to full 128 bit IV // to full 128 bit IV
memcpy (iv, packet, 12); memcpy (iv, packet, 12);
// extract checksum (last 16 bit) blended in IV // extract time stamp (first 64 bit) and checksum (last 16 bit) blended in IV
speck_he_iv_decrypt (iv, (speck_context_t*)ctx_iv); speck_he_iv_decrypt (iv, (speck_context_t*)ctx_iv);
*checksum = be16toh (((uint16_t*)iv)[5]); *checksum = be16toh (((uint16_t*)iv)[5]);
*stamp = be64toh (((uint64_t*)iv)[0]);
memcpy (iv, packet, 12); memcpy (iv, packet, 12);
@ -64,7 +65,7 @@ uint32_t packet_header_decrypt (uint8_t packet[], uint16_t packet_len,
/* ********************************************************************** */ /* ********************************************************************** */
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,
he_context_t * ctx_iv, uint16_t checksum) { he_context_t * ctx_iv, uint64_t stamp, uint16_t checksum) {
uint8_t iv[16]; uint8_t iv[16];
uint16_t *iv16 = (uint16_t*)&iv; uint16_t *iv16 = (uint16_t*)&iv;
@ -79,7 +80,7 @@ int32_t packet_header_encrypt (uint8_t packet[], uint8_t header_len, he_context_
memcpy (&packet[16], &packet[00], 4); memcpy (&packet[16], &packet[00], 4);
iv64[0] = n2n_rand (); iv64[0] = htobe64 (stamp);
iv16[4] = n2n_rand (); iv16[4] = n2n_rand ();
iv16[5] = htobe16 (checksum); iv16[5] = htobe16 (checksum);
iv32[3] = htobe32 (magic); iv32[3] = htobe32 (magic);

92
src/n2n.c

@ -22,6 +22,13 @@
#include <assert.h> #include <assert.h>
#define PURGE_REGISTRATION_FREQUENCY 30
#define REGISTRATION_TIMEOUT 60
#define TIME_STAMP_FRAME 0x0000001000000000LL /* clocks of different computers are allowed +/- 16 seconds to be off */
#define TIME_STAMP_JITTER 0x0000000027100000LL /* we allow a packet to arrive 160 ms (== 0x27100 us) before another
* set to 0x0000000000000000LL if increasing (or equal) time stamps allowed only */
static const uint8_t broadcast_addr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static const uint8_t broadcast_addr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
static const uint8_t multicast_addr[6] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0x00 }; /* First 3 bytes are meaningful */ static const uint8_t multicast_addr[6] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0x00 }; /* First 3 bytes are meaningful */
static const uint8_t ipv6_multicast_addr[6] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 }; /* First 2 bytes are meaningful */ static const uint8_t ipv6_multicast_addr[6] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 }; /* First 2 bytes are meaningful */
@ -380,3 +387,88 @@ int sock_equal(const n2n_sock_t * a,
return(1); return(1);
} }
/* *********************************************** */
#if defined(WIN32) && !defined(__GNUC__)
int gettimeofday(struct timeval *tp, void *tzp) {
time_t clock;
struct tm tm;
SYSTEMTIME wtm;
GetLocalTime(&wtm);
tm.tm_year = wtm.wYear - 1900;
tm.tm_mon = wtm.wMonth - 1;
tm.tm_mday = wtm.wDay;
tm.tm_hour = wtm.wHour;
tm.tm_min = wtm.wMinute;
tm.tm_sec = wtm.wSecond;
tm.tm_isdst = -1;
clock = mktime(&tm);
tp->tv_sec = clock;
tp->tv_usec = wtm.wMilliseconds * 1000;
return (0);
}
#endif
// returns a time stamp for use with replay protection
uint64_t time_stamp (void) {
struct timeval tod;
uint64_t micro_seconds;
gettimeofday (&tod, NULL);
/* We will (roughly) calculate the microseconds since 1970 leftbound into the return value.
The leading 32 bits are used for tv_sec. The following 20 bits (sufficent as microseconds
fraction never exceeds 1,000,000,) encode the value tv_usec. The remaining lowest 12 bits
are kept random for use in IV */
micro_seconds = n2n_rand();
micro_seconds = ( (((uint64_t)(tod.tv_sec) << 32) + (tod.tv_usec << 12))
| (micro_seconds >> 52) );
// more exact but more costly due to the multiplication:
// micro_seconds = (tod.tv_sec * 1000000 + tod.tv_usec) << 12) | ...
return (micro_seconds);
}
// returns an initial time stamp for use with replay protection
uint64_t initial_time_stamp (void) {
return ( time_stamp() - TIME_STAMP_FRAME );
}
// checks if a provided time stamp is consistent with current time and previously valid time stamps
// and, in case of validity, updates the "last valid time stamp"
int time_stamp_verify_and_update (uint64_t stamp, uint64_t * previous_stamp) {
int64_t diff; // do not change to unsigned
// is it around current time (+/- allowed deviation TIME_STAMP_FRAME)?
diff = stamp - time_stamp();
// abs()
diff = (diff < 0 ? -diff : diff);
if(diff >= TIME_STAMP_FRAME) {
traceEvent(TRACE_DEBUG, "time_stamp_verify_and_update found a timestamp out of allowed frame.");
return (0); // failure
}
// if applicable: is it higher than previous time stamp (including allowed deviation of TIME_STAMP_JITTER)?
if(NULL != previous_stamp) {
// if no jitter allowed, reset lowest three (random) nybbles; the codnition shoudl already be evaluated by the compiler
if(TIME_STAMP_JITTER == 0) {
stamp = (stamp >> 12) << 12;
*previous_stamp = (*previous_stamp >> 12) << 12;
}
diff = stamp - *previous_stamp + TIME_STAMP_JITTER;
if(diff <= 0) {
traceEvent(TRACE_DEBUG, "time_stamp_verify_and_update found a timestamp too old compared to previous.");
return (0); // failure
}
// for not allowing to exploit the allowed TIME_STAMP_JITTER to "turn the clock backwards",
// set the higher of the values
*previous_stamp = (stamp > *previous_stamp ? stamp : *previous_stamp);
}
return (1); // success
}

85
src/sn_utils.c

@ -1,5 +1,4 @@
#include "n2n.h" #include "n2n.h"
#include "header_encryption.h"
#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)
@ -51,6 +50,8 @@ 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 struct sn_community *comm, const struct sn_community *comm,
const n2n_common_t * cmn, const n2n_common_t * cmn,
@ -266,6 +267,7 @@ static int update_edge(n2n_sn_t *sss,
memcpy(&(scan->mac_addr), edgeMac, sizeof(n2n_mac_t)); memcpy(&(scan->mac_addr), edgeMac, sizeof(n2n_mac_t));
memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t)); memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t));
scan->last_valid_time_stamp = initial_time_stamp ();
HASH_ADD_PEER(comm->edges, scan); HASH_ADD_PEER(comm->edges, scan);
@ -296,6 +298,31 @@ static int update_edge(n2n_sn_t *sss,
return 0; return 0;
} }
/***
*
* For a given packet, find the apporopriate internal last valid time stamp for lookup
* and verify it (and also update, if applicable).
*/
static int find_edge_time_stamp_and_verify (struct peer_info * edges,
int from_supernode, n2n_mac_t mac,
uint64_t stamp) {
uint64_t * previous_stamp = NULL;
if(!from_supernode) {
struct peer_info *edge;
HASH_FIND_PEER(edges, mac, edge);
if(edge) {
// time_stamp_verify_and_update allows the pointer a previous stamp to be NULL
// if it is a (so far) unknown edge
previous_stamp = &(edge->last_valid_time_stamp);
}
}
// failure --> 0; success --> 1
return ( time_stamp_verify_and_update (stamp, previous_stamp) );
}
static int purge_expired_communities(n2n_sn_t *sss, static int purge_expired_communities(n2n_sn_t *sss,
time_t* p_last_purge, time_t* p_last_purge,
time_t now) time_t now)
@ -325,6 +352,7 @@ static int purge_expired_communities(n2n_sn_t *sss,
return 0; return 0;
} }
static int process_mgmt(n2n_sn_t *sss, static int process_mgmt(n2n_sn_t *sss,
const struct sockaddr_in *sender_sock, const struct sockaddr_in *sender_sock,
const uint8_t *mgmt_buf, const uint8_t *mgmt_buf,
@ -447,6 +475,7 @@ static int process_udp(n2n_sn_t * sss,
n2n_sock_str_t sockbuf; n2n_sock_str_t sockbuf;
char buf[32]; char buf[32];
struct sn_community *comm, *tmp; struct sn_community *comm, *tmp;
uint64_t stamp;
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)),
@ -493,7 +522,11 @@ static int process_udp(n2n_sn_t * sss,
continue; continue;
uint16_t checksum = 0; uint16_t checksum = 0;
if ( (ret = packet_header_decrypt (udp_buf, udp_size, comm->community, comm->header_encryption_ctx, if ( (ret = packet_header_decrypt (udp_buf, udp_size, comm->community, comm->header_encryption_ctx,
comm->header_iv_ctx, &checksum)) ) { comm->header_iv_ctx,
&stamp, &checksum)) ) {
// time stamp verification follows in the packet specific section as it requires to determine the
// sender from the hash list by its MAC, this all depends on packet type and packet structure
// (MAC is not always in the same place)
if (checksum != pearson_hash_16 (udp_buf, udp_size)) { if (checksum != pearson_hash_16 (udp_buf, udp_size)) {
traceEvent(TRACE_DEBUG, "process_udp dropped packet due to checksum error."); traceEvent(TRACE_DEBUG, "process_udp dropped packet due to checksum error.");
return -1; return -1;
@ -564,6 +597,14 @@ static int process_udp(n2n_sn_t * sss,
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);
// already checked for valid comm
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, pkt.srcMac, stamp)) {
traceEvent(TRACE_DEBUG, "process_udp dropped PACKET due to time stamp error.");
return -1;
}
}
unicast = (0 == is_multi_broadcast(pkt.dstMac)); unicast = (0 == is_multi_broadcast(pkt.dstMac));
traceEvent(TRACE_DEBUG, "RX PACKET (%s) %s -> %s %s", traceEvent(TRACE_DEBUG, "RX PACKET (%s) %s -> %s %s",
@ -593,7 +634,8 @@ static int process_udp(n2n_sn_t * sss,
if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt (rec_buf, oldEncx, comm->header_encryption_ctx, packet_header_encrypt (rec_buf, oldEncx, comm->header_encryption_ctx,
comm->header_iv_ctx, pearson_hash_16 (rec_buf, encx)); comm->header_iv_ctx,
time_stamp (), pearson_hash_16 (rec_buf, encx));
} else { } else {
/* Already from a supernode. Nothing to modify, just pass to /* Already from a supernode. Nothing to modify, just pass to
@ -606,7 +648,8 @@ static int process_udp(n2n_sn_t * sss,
if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt (rec_buf, idx, comm->header_encryption_ctx, packet_header_encrypt (rec_buf, idx, comm->header_encryption_ctx,
comm->header_iv_ctx, pearson_hash_16 (rec_buf, udp_size)); comm->header_iv_ctx,
time_stamp (), pearson_hash_16 (rec_buf, udp_size));
} }
/* Common section to forward the final product. */ /* Common section to forward the final product. */
@ -635,6 +678,14 @@ static int process_udp(n2n_sn_t * sss,
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);
// already checked for valid comm
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, reg.srcMac, stamp)) {
traceEvent(TRACE_DEBUG, "process_udp dropped REGISTER due to time stamp error.");
return -1;
}
}
unicast = (0 == is_multi_broadcast(reg.dstMac)); unicast = (0 == is_multi_broadcast(reg.dstMac));
if(unicast) { if(unicast) {
@ -667,7 +718,8 @@ static int process_udp(n2n_sn_t * sss,
if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt (rec_buf, encx, comm->header_encryption_ctx, packet_header_encrypt (rec_buf, encx, comm->header_encryption_ctx,
comm->header_iv_ctx, pearson_hash_16 (rec_buf, encx)); comm->header_iv_ctx,
time_stamp (), pearson_hash_16 (rec_buf, encx));
try_forward(sss, comm, &cmn, reg.dstMac, rec_buf, encx); /* unicast only */ try_forward(sss, comm, &cmn, reg.dstMac, rec_buf, encx); /* unicast only */
} else } else
@ -690,6 +742,15 @@ static int process_udp(n2n_sn_t * sss,
++(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);
if (comm) {
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, reg.edgeMac, stamp)) {
traceEvent(TRACE_DEBUG, "process_udp dropped REGISTER_SUPER due to time stamp error.");
return -1;
}
}
}
/* /*
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
@ -739,7 +800,8 @@ static int process_udp(n2n_sn_t * sss,
if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt (ackbuf, encx, comm->header_encryption_ctx, packet_header_encrypt (ackbuf, encx, comm->header_encryption_ctx,
comm->header_iv_ctx, pearson_hash_16 (ackbuf, encx)); comm->header_iv_ctx,
time_stamp (), pearson_hash_16 (ackbuf, encx));
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));
@ -766,6 +828,14 @@ static int process_udp(n2n_sn_t * sss,
decode_QUERY_PEER( &query, &cmn, udp_buf, &rem, &idx ); decode_QUERY_PEER( &query, &cmn, udp_buf, &rem, &idx );
// already checked for valid comm
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, query.srcMac, stamp)) {
traceEvent(TRACE_DEBUG, "process_udp dropped QUERY_PEER due to time stamp error.");
return -1;
}
}
traceEvent( TRACE_DEBUG, "Rx QUERY_PEER from %s for %s", traceEvent( TRACE_DEBUG, "Rx QUERY_PEER from %s for %s",
macaddr_str( mac_buf, query.srcMac ), macaddr_str( mac_buf, query.srcMac ),
macaddr_str( mac_buf2, query.targetMac ) ); macaddr_str( mac_buf2, query.targetMac ) );
@ -787,7 +857,8 @@ static int process_udp(n2n_sn_t * sss,
if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt (encbuf, encx, comm->header_encryption_ctx, packet_header_encrypt (encbuf, encx, comm->header_encryption_ctx,
comm->header_iv_ctx, pearson_hash_16 (encbuf, encx)); comm->header_iv_ctx,
time_stamp (), pearson_hash_16 (encbuf, encx));
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) );

20
tools/benchmark.c

@ -18,26 +18,6 @@
#include "n2n.h" #include "n2n.h"
#if defined(WIN32) && !defined(__GNUC__)
static int gettimeofday(struct timeval *tp, void *tzp)
{
time_t clock;
struct tm tm;
SYSTEMTIME wtm;
GetLocalTime(&wtm);
tm.tm_year = wtm.wYear - 1900;
tm.tm_mon = wtm.wMonth - 1;
tm.tm_mday = wtm.wDay;
tm.tm_hour = wtm.wHour;
tm.tm_min = wtm.wMinute;
tm.tm_sec = wtm.wSecond;
tm.tm_isdst = -1;
clock = mktime(&tm);
tp->tv_sec = clock;
tp->tv_usec = wtm.wMilliseconds * 1000;
return (0);
}
#endif
uint8_t PKT_CONTENT[]={ uint8_t PKT_CONTENT[]={
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,

Loading…
Cancel
Save