From 8dfffd9fbef06b97e50b125e307e0348a1984bcb Mon Sep 17 00:00:00 2001 From: Francesco Carli <62562180+fcarli3@users.noreply.github.com> Date: Thu, 1 Oct 2020 16:23:10 +0200 Subject: [PATCH] Implement purge-and-re-registration process for supernodes (#445) --- include/n2n.h | 2 +- include/n2n_define.h | 3 +- src/edge_utils.c | 4 +- src/n2n.c | 4 +- src/sn_utils.c | 147 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 154 insertions(+), 6 deletions(-) diff --git a/include/n2n.h b/include/n2n.h index b522dc2..cc4e67d 100644 --- a/include/n2n.h +++ b/include/n2n.h @@ -467,7 +467,7 @@ int time_stamp_verify_and_update (uint64_t stamp, uint64_t * previous_stamp, int size_t purge_peer_list( struct peer_info ** peer_list, time_t purge_before ); size_t clear_peer_list( struct peer_info ** peer_list ); -size_t purge_expired_registrations( struct peer_info ** peer_list, time_t* p_last_purge ); +size_t purge_expired_registrations( struct peer_info ** peer_list, time_t* p_last_purge, int timeout ); /* Edge conf */ void edge_init_conf_defaults(n2n_edge_conf_t *conf); diff --git a/include/n2n_define.h b/include/n2n_define.h index d3827d7..b9882fc 100644 --- a/include/n2n_define.h +++ b/include/n2n_define.h @@ -40,13 +40,14 @@ #define SOCKET_TIMEOUT_INTERVAL_SECS 10 #define REGISTER_SUPER_INTERVAL_DFL 20 /* sec, usually UDP NAT entries in a firewall expire after 30 seconds */ #define ALLOWED_TIME 20 /* sec, indicates supernodes that are proven to be alive */ -#define TEST_TIME (SORT_COMMUNITIES_INTERVAL - ALLOWED_TIME)/2 /* sec, indicates supernodes with unsure status, must be tested to check if they are alive */ +#define TEST_TIME (PURGE_FEDERATION_NODE_INTERVAL - ALLOWED_TIME)/2 /* sec, indicates supernodes with unsure status, must be tested to check if they are alive */ #define IFACE_UPDATE_INTERVAL (30) /* sec. How long it usually takes to get an IP lease. */ #define TRANSOP_TICK_INTERVAL (10) /* sec */ #define PURGE_REGISTRATION_FREQUENCY 30 #define REGISTRATION_TIMEOUT 60 +#define PURGE_FEDERATION_NODE_INTERVAL 90 #define SORT_COMMUNITIES_INTERVAL 90 /* sec. until supernode sorts communities' hash list again */ diff --git a/src/edge_utils.c b/src/edge_utils.c index a2cb8b0..d063810 100644 --- a/src/edge_utils.c +++ b/src/edge_utils.c @@ -2139,8 +2139,8 @@ int run_edge_loop(n2n_edge_t * eee, int *keep_running) { /* Finished processing select data. */ update_supernode_reg(eee, nowTime); - numPurged = purge_expired_registrations(&eee->known_peers, &last_purge_known); - numPurged += purge_expired_registrations(&eee->pending_peers, &last_purge_pending); + numPurged = purge_expired_registrations(&eee->known_peers, &last_purge_known, PURGE_REGISTRATION_FREQUENCY); + numPurged += purge_expired_registrations(&eee->pending_peers, &last_purge_pending, PURGE_REGISTRATION_FREQUENCY); if(numPurged > 0) { traceEvent(TRACE_INFO, "%u peers removed. now: pending=%u, operational=%u", diff --git a/src/n2n.c b/src/n2n.c index 554fae2..6dafb0a 100644 --- a/src/n2n.c +++ b/src/n2n.c @@ -280,11 +280,11 @@ void print_n2n_version() { /* *********************************************** */ -size_t purge_expired_registrations(struct peer_info ** peer_list, time_t* p_last_purge) { +size_t purge_expired_registrations(struct peer_info ** peer_list, time_t* p_last_purge, int timeout) { time_t now = time(NULL); size_t num_reg = 0; - if((now - (*p_last_purge)) < PURGE_REGISTRATION_FREQUENCY) return 0; + if((now - (*p_last_purge)) < timeout) return 0; traceEvent(TRACE_DEBUG, "Purging old registrations"); diff --git a/src/sn_utils.c b/src/sn_utils.c index c452325..7b1fbd6 100644 --- a/src/sn_utils.c +++ b/src/sn_utils.c @@ -496,6 +496,62 @@ static int find_edge_time_stamp_and_verify (struct peer_info * edges, return ( time_stamp_verify_and_update (stamp, previous_stamp, allow_jitter) ); } + +static int re_register_and_purge_supernodes(n2n_sn_t *sss, struct sn_community *comm, time_t now){ + time_t time; + struct peer_info *peer, *tmp; + + if(comm != NULL){ + HASH_ITER(hh,comm->edges,peer,tmp){ + time = now - peer->last_seen; + if(time <= ALLOWED_TIME) continue; + if((time > ALLOWED_TIME) && (time < PURGE_FEDERATION_NODE_INTERVAL)){ /* re-regitser (send REGISTER_SUPER) */ + uint8_t pktbuf[N2N_PKT_BUF_SIZE] = {0}; + size_t idx; + /* ssize_t sent; */ + n2n_common_t cmn; + n2n_cookie_t cookie; + n2n_REGISTER_SUPER_t reg; + n2n_sock_str_t sockbuf; + + memset(&cmn, 0, sizeof(cmn)); + memset(®, 0, sizeof(reg)); + + cmn.ttl = N2N_DEFAULT_TTL; + cmn.pc = n2n_register_super; + cmn.flags = 0; + memcpy(cmn.community, comm->community, N2N_COMMUNITY_SIZE); + + for (idx = 0; idx < N2N_COOKIE_SIZE; ++idx) /* aggiungi sn_idx */ + cookie[idx] = n2n_rand() % 0xff; + + memcpy(reg.cookie, cookie, N2N_COOKIE_SIZE); + reg.dev_addr.net_addr = ntohl(peer->dev_addr.net_addr); + reg.dev_addr.net_bitlen = mask2bitlen(ntohl(peer->dev_addr.net_bitlen)); + reg.auth.scheme = 0; /* No auth yet */ + + idx = 0; + encode_mac(reg.edgeMac, &idx, peer->mac_addr); + + idx = 0; + encode_REGISTER_SUPER(pktbuf, &idx, &cmn, ®); + + traceEvent(TRACE_DEBUG, "send REGISTER_SUPER to %s", + sock_to_cstr(sockbuf, &(peer->sock))); + + packet_header_encrypt(pktbuf, idx, comm->header_encryption_ctx, + comm->header_iv_ctx, + time_stamp(), pearson_hash_16(pktbuf, idx)); + + /* sent = */ sendto_sock(sss, &(peer->sock), pktbuf, N2N_PKT_BUF_SIZE); + } + if(time >= PURGE_FEDERATION_NODE_INTERVAL) purge_expired_registrations(&(comm->edges),&time,PURGE_FEDERATION_NODE_INTERVAL);/* purge not-seen-long-time supernodes*/ + } +} + + return 0; /* OK */ +} + static int purge_expired_communities(n2n_sn_t *sss, time_t* p_last_purge, time_t now) @@ -659,6 +715,44 @@ static int sendto_mgmt(n2n_sn_t *sss, return 0; } + +/** Iterate through REGISTER_SUPER_ACK payload and add new supernodes with a legal timestamp + * (half of the difference between the adjustable 20 seconds and 90 seconds limit) to the federation list + */ +static int add_sn_to_federation_from_register_super_ack(n2n_sn_t *sss, n2n_REGISTER_SUPER_ACK_t ack){ + struct sn_community *fed; + struct peer_info *peer; + n2n_sock_t *tmp_sock; + n2n_mac_t *tmp_mac; + int i; + + HASH_FIND_COMMUNITY(sss->communities, sss->federation, fed); + + if(fed != NULL){ + tmp_sock = &(ack.sn_bak); + tmp_mac = &(ack.mac_addr); + for(i=0; iedges, &tmp_mac, peer); + if(peer == NULL){ + peer = (struct peer_info *)calloc(1,sizeof(struct peer_info)); + memcpy(&(peer->sock),tmp_sock,sizeof(n2n_sock_t)); + memcpy(&(peer->mac_addr), &tmp_mac, sizeof(n2n_mac_t)); + peer->dev_addr.net_addr = ntohs(ack.dev_addr.net_addr); + peer->dev_addr.net_bitlen = mask2bitlen(ntohl(ack.dev_addr.net_bitlen)); + peer->last_seen = TEST_TIME; + HASH_ADD_PEER(fed->edges, peer); + } + tmp_sock += sizeof(n2n_mac_t); + tmp_mac += sizeof(n2n_sock_t); + } + } + + return 0; /* OK */ + +} + + + /** Examine a datagram and determine what to do with it. * */ @@ -1086,6 +1180,59 @@ static int process_udp(n2n_sn_t * sss, } break; } + case MSG_TYPE_REGISTER_SUPER_ACK: { + n2n_REGISTER_SUPER_ACK_t ack; + size_t encx=0; + struct sn_community *fed; + struct peer_info *scan; + n2n_sock_str_t sockbuf1; + n2n_sock_str_t sockbuf2; + macstr_t mac_buf1; + n2n_sock_t sender; + n2n_sock_t *orig_sender; + + sender.family = AF_INET; + sender.port = ntohs(sender_sock->sin_port); + memcpy(&(sender.addr.v4), &(sender_sock->sin_addr.s_addr), IPV4_SIZE); + orig_sender = &sender; + + memset(&ack, 0, sizeof(n2n_REGISTER_SUPER_ACK_t)); + + if(from_supernode != comm->is_federation){ + traceEvent(TRACE_DEBUG, "process_udp dropped REGISTER_SUPER_ACK: from_supernode value doesn't correspond to the internal federation marking."); + return -1; + } + + decode_REGISTER_SUPER_ACK(&ack,&cmn,udp_buf,&rem,&idx); + orig_sender = &(ack.sock); + + if (comm) { + if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { + if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, ack.edgeMac, stamp, TIME_STAMP_NO_JITTER)) { + traceEvent(TRACE_DEBUG, "process_udp dropped REGISTER_SUPER_ACK due to time stamp error."); + return -1; + } + } + } + + traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK myMAC=%s [%s] (external %s)", + macaddr_str(mac_buf1, ack.mac_addr), + sock_to_cstr(sockbuf1, &sender), + sock_to_cstr(sockbuf2, orig_sender)); + + HASH_FIND_COMMUNITY(sss->communities, sss->federation, fed); + + if(fed != NULL) { + HASH_FIND_PEER(fed->edges, ack.edgeMac, scan); + if(scan != NULL){ + scan->last_seen = now; + } + } + + if(ack.num_sn > 0) add_sn_to_federation_from_register_super_ack(sss,ack); + + break; +} case MSG_TYPE_QUERY_PEER: { n2n_QUERY_PEER_t query; uint8_t encbuf[N2N_SN_PKTBUF_SIZE];