diff --git a/include/header_encryption.h b/include/header_encryption.h index 0e8a708..9ed7eaf 100644 --- a/include/header_encryption.h +++ b/include/header_encryption.h @@ -19,10 +19,12 @@ uint32_t packet_header_decrypt (uint8_t packet[], uint16_t packet_len, 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, - 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, diff --git a/include/n2n.h b/include/n2n.h index 29404d6..0f13eba 100644 --- a/include/n2n.h +++ b/include/n2n.h @@ -193,6 +193,7 @@ struct peer_info { time_t last_seen; time_t last_p2p; time_t last_sent_query; + uint64_t last_valid_time_stamp; 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_cookie_t last_cookie; /**< Cookie sent in last REGISTER_SUPER. */ n2n_route_t *sn_route_to_clean; /**< Supernode route to clean */ - n2n_edge_callbacks_t cb; /**< API callbacks */ - void *user_data; /**< Can hold user data */ + n2n_edge_callbacks_t cb; /**< API callbacks */ + void *user_data; /**< Can hold user data */ + uint64_t sn_last_valid_time_stamp;/*< last valid time stamp from supernode */ /* Sockets */ 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, 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. */ size_t purge_peer_list( struct peer_info ** peer_list, time_t purge_before ); diff --git a/src/edge_utils.c b/src/edge_utils.c index dc51a4f..766fa5b 100644 --- a/src/edge_utils.c +++ b/src/edge_utils.c @@ -17,7 +17,6 @@ */ #include "n2n.h" -#include "header_encryption.h" #include "edge_utils_win32.h" /* 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->pending_peers = NULL; eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; + eee->sn_last_valid_time_stamp = initial_time_stamp (); 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 @@ -432,6 +468,7 @@ static void register_with_new_peer(n2n_edge_t * eee, scan->sock = *peer; 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_valid_time_stamp = initial_time_stamp (); 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) 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); } @@ -763,9 +801,10 @@ static void send_query_peer( n2n_edge_t * eee, if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED){ packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx, - eee->conf.header_iv_ctx, pearson_hash_16 (pktbuf, idx)); - } - sendto_sock( eee->udp_sock, pktbuf, idx, &(eee->supernode) ); + eee->conf.header_iv_ctx, + time_stamp (), pearson_hash_16 (pktbuf, idx)); + } + sendto_sock( eee->udp_sock, pktbuf, idx, &(eee->supernode) ); } /** 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) 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); } @@ -853,7 +893,8 @@ static void send_register_ack(n2n_edge_t * eee, if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) 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); } @@ -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); 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_valid_time_stamp = initial_time_stamp (); 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) 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 { @@ -1564,6 +1607,7 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) { n2n_sock_t sender; n2n_sock_t * orig_sender=NULL; time_t now=0; + uint64_t stamp = 0; 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) { uint16_t checksum = 0; 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."); 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)) { traceEvent(TRACE_DEBUG, "readFromIPSocket dropped packet due to checksum error."); return; @@ -1630,23 +1680,30 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) { switch(msg_type) { case MSG_TYPE_PACKET: { - /* process PACKET - most frequent so first in list. */ - n2n_PACKET_t pkt; - - decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx); + /* process PACKET - most frequent so first in list. */ + n2n_PACKET_t pkt; - if(is_valid_peer_sock(&pkt.sock)) - orig_sender = &(pkt.sock); + decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx); - 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); - } + if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { + if(!find_peer_time_stamp_and_verify (eee, from_supernode, pkt.srcMac, stamp)) { + traceEvent(TRACE_DEBUG, "readFromIPSocket dropped PACKET due to time stamp error."); + return; + } + } + + 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]", sock_to_cstr(sockbuf1, &sender), @@ -1664,8 +1721,15 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) { decode_REGISTER(®, &cmn, udp_buf, &rem, &idx); - if(is_valid_peer_sock(®.sock)) - orig_sender = &(reg.sock); + if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { + 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(®.sock)) + orig_sender = &(reg.sock); 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); - if(is_valid_peer_sock(&ra.sock)) - orig_sender = &(ra.sock); + if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { + 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)", 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; - if(eee->sn_wait) - { - decode_REGISTER_SUPER_ACK(&ra, &cmn, udp_buf, &rem, &idx); - - if(is_valid_peer_sock(&ra.sock)) - orig_sender = &(ra.sock); - - traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK myMAC=%s [%s] (external %s). Attempts %u", - macaddr_str(mac_buf1, ra.edgeMac), - sock_to_cstr(sockbuf1, &sender), - sock_to_cstr(sockbuf2, orig_sender), - (unsigned int)eee->sup_attempts); - - if(0 == memcmp(ra.cookie, eee->last_cookie, N2N_COOKIE_SIZE)) - { - if(ra.num_sn > 0) - { - traceEvent(TRACE_NORMAL, "Rx REGISTER_SUPER_ACK backup supernode at %s", - sock_to_cstr(sockbuf1, &(ra.sn_bak))); - } - - eee->last_sup = now; - eee->sn_wait=0; - eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; /* refresh because we got a response */ - - if(eee->cb.sn_registration_updated) - eee->cb.sn_registration_updated(eee, now, &sender); - - /* NOTE: the register_interval should be chosen by the edge node - * based on its NAT configuration. */ - //eee->conf.register_interval = ra.lifetime; - } - else - { - 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; + if(eee->sn_wait) + { + decode_REGISTER_SUPER_ACK(&ra, &cmn, udp_buf, &rem, &idx); + + if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { + 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."); + return; + } + } + + if(is_valid_peer_sock(&ra.sock)) + orig_sender = &(ra.sock); + + traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK myMAC=%s [%s] (external %s). Attempts %u", + macaddr_str(mac_buf1, ra.edgeMac), + sock_to_cstr(sockbuf1, &sender), + sock_to_cstr(sockbuf2, orig_sender), + (unsigned int)eee->sup_attempts); + + if(0 == memcmp(ra.cookie, eee->last_cookie, N2N_COOKIE_SIZE)) + { + if(ra.num_sn > 0) + { + traceEvent(TRACE_NORMAL, "Rx REGISTER_SUPER_ACK backup supernode at %s", + sock_to_cstr(sockbuf1, &(ra.sn_bak))); + } + + eee->last_sup = now; + eee->sn_wait=0; + eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; /* refresh because we got a response */ + + if(eee->cb.sn_registration_updated) + eee->cb.sn_registration_updated(eee, now, &sender); + + /* NOTE: the register_interval should be chosen by the edge node + * based on its NAT configuration. */ + //eee->conf.register_interval = ra.lifetime; + } + else + { + 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: { - n2n_PEER_INFO_t pi; - struct peer_info * scan; - decode_PEER_INFO( &pi, &cmn, udp_buf, &rem, &idx ); - - 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; - } + n2n_PEER_INFO_t pi; + struct peer_info * scan; + decode_PEER_INFO( &pi, &cmn, udp_buf, &rem, &idx ); + + if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { + if(!find_peer_time_stamp_and_verify (eee, definitely_from_supernode, null_mac, stamp)) { + traceEvent(TRACE_DEBUG, "readFromIPSocket dropped PEER_INFO due to time stamp error."); + return; + } + } + + 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); if(scan) { diff --git a/src/header_encryption.c b/src/header_encryption.c index 32b6654..c526726 100644 --- a/src/header_encryption.c +++ b/src/header_encryption.c @@ -25,7 +25,7 @@ uint32_t packet_header_decrypt (uint8_t packet[], uint16_t packet_len, 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 // 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 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); *checksum = be16toh (((uint16_t*)iv)[5]); + *stamp = be64toh (((uint64_t*)iv)[0]); 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, - he_context_t * ctx_iv, uint16_t checksum) { + he_context_t * ctx_iv, uint64_t stamp, uint16_t checksum) { uint8_t iv[16]; 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); - iv64[0] = n2n_rand (); + iv64[0] = htobe64 (stamp); iv16[4] = n2n_rand (); iv16[5] = htobe16 (checksum); iv32[3] = htobe32 (magic); diff --git a/src/n2n.c b/src/n2n.c index 1592e99..8ec3223 100644 --- a/src/n2n.c +++ b/src/n2n.c @@ -22,6 +22,13 @@ #include + +#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 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 */ @@ -380,3 +387,88 @@ int sock_equal(const n2n_sock_t * a, 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 +} diff --git a/src/sn_utils.c b/src/sn_utils.c index 8418ff0..94b8056 100644 --- a/src/sn_utils.c +++ b/src/sn_utils.c @@ -1,5 +1,4 @@ #include "n2n.h" -#include "header_encryption.h" #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, time_t now); +/* ************************************** */ + static int try_forward(n2n_sn_t * sss, const struct sn_community *comm, 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->sock), sender_sock, sizeof(n2n_sock_t)); + scan->last_valid_time_stamp = initial_time_stamp (); HASH_ADD_PEER(comm->edges, scan); @@ -296,6 +298,31 @@ static int update_edge(n2n_sn_t *sss, 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, time_t* p_last_purge, time_t now) @@ -325,6 +352,7 @@ static int purge_expired_communities(n2n_sn_t *sss, return 0; } + static int process_mgmt(n2n_sn_t *sss, const struct sockaddr_in *sender_sock, const uint8_t *mgmt_buf, @@ -447,6 +475,7 @@ static int process_udp(n2n_sn_t * sss, n2n_sock_str_t sockbuf; char buf[32]; struct sn_community *comm, *tmp; + uint64_t stamp; 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)), @@ -493,7 +522,11 @@ static int process_udp(n2n_sn_t * sss, continue; uint16_t checksum = 0; 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)) { traceEvent(TRACE_DEBUG, "process_udp dropped packet due to checksum error."); return -1; @@ -564,6 +597,14 @@ static int process_udp(n2n_sn_t * sss, sss->stats.last_fwd=now; 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)); 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) 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 { /* 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) 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. */ @@ -635,6 +678,14 @@ static int process_udp(n2n_sn_t * sss, sss->stats.last_fwd=now; decode_REGISTER(®, &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)); if(unicast) { @@ -667,7 +718,8 @@ static int process_udp(n2n_sn_t * sss, if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) 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 */ } else @@ -690,6 +742,15 @@ static int process_udp(n2n_sn_t * sss, ++(sss->stats.reg_super); decode_REGISTER_SUPER(®, &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 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) 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, (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 ); + // 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", macaddr_str( mac_buf, query.srcMac ), 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) 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, (struct sockaddr *)sender_sock, sizeof(struct sockaddr_in) ); diff --git a/tools/benchmark.c b/tools/benchmark.c index 671b639..e1d3644 100644 --- a/tools/benchmark.c +++ b/tools/benchmark.c @@ -18,26 +18,6 @@ #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[]={ 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,