From c9eedd68f06cfe76fda6a817f960ab8fa61f5509 Mon Sep 17 00:00:00 2001 From: Francesco Carli <62562180+fcarli3@users.noreply.github.com> Date: Sun, 11 Oct 2020 11:44:40 +0200 Subject: [PATCH] Initialize the federation of supernodes (#460) * Add supernode2sock() and add_sn_to_federation_by_mac_or_sock() * Update sn_utils.c * Update sn.c and sn_utils.c * Update REG_SUPER_ACK payload * Update add_sn_to_federation_by_mac_or_sock() --- include/n2n.h | 12 ++- include/n2n_wire.h | 2 - src/sn.c | 82 ++++++++++++------ src/sn_utils.c | 207 ++++++++++++++++++++++++++------------------- 4 files changed, 183 insertions(+), 120 deletions(-) diff --git a/include/n2n.h b/include/n2n.h index cc4e67d..ddf3008 100644 --- a/include/n2n.h +++ b/include/n2n.h @@ -190,24 +190,26 @@ typedef char macstr_t[N2N_MACSTR_SIZE]; typedef char dec_ip_str_t[N2N_NETMASK_STR_SIZE]; typedef char dec_ip_bit_str_t[N2N_NETMASK_STR_SIZE + 4]; +typedef struct speck_context_t he_context_t; +typedef char n2n_sn_name_t[N2N_EDGE_SN_HOST_SIZE]; + struct peer_info { n2n_mac_t mac_addr; n2n_ip_subnet_t dev_addr; n2n_sock_t sock; int timeout; + uint8_t purgeable; time_t last_seen; time_t last_p2p; time_t last_sent_query; uint64_t last_valid_time_stamp; + char *ip_addr; UT_hash_handle hh; /* makes this structure hashable */ }; -typedef struct speck_context_t he_context_t; -typedef char n2n_sn_name_t[N2N_EDGE_SN_HOST_SIZE]; - typedef struct n2n_route { in_addr_t net_addr; uint8_t net_bitlen; @@ -396,7 +398,7 @@ typedef struct n2n_sn int lock_communities; /* If true, only loaded and matching communities can be used. */ struct sn_community *communities; struct sn_community_regular_expression *rules; - char federation[N2N_COMMUNITY_SIZE]; + struct sn_community *federation; } n2n_sn_t; /* ************************************** */ @@ -443,6 +445,7 @@ uint32_t bitlen2mask(uint8_t bitlen); uint8_t mask2bitlen(uint32_t mask); char* macaddr_str(macstr_t buf, const n2n_mac_t mac); int str2mac( uint8_t * outmac /* 6 bytes */, const char * s ); +int supernode2sock(n2n_sock_t * sn, const n2n_sn_name_t addrIn); uint8_t is_multi_broadcast(const uint8_t * dest_mac); char* msg_type2str(uint16_t msg_type); void hexdump(const uint8_t * buf, size_t len); @@ -497,6 +500,7 @@ int quick_edge_init(char *device_name, char *community_name, int comm_init(struct sn_community *comm, char *cmn); int sn_init(n2n_sn_t *sss); void sn_term(n2n_sn_t *sss); +struct peer_info* add_sn_to_federation_by_mac_or_sock(n2n_sn_t *sss, n2n_sock_t *sock, n2n_mac_t *mac); int run_sn_loop(n2n_sn_t *sss, int *keep_running); int assign_one_ip_subnet(n2n_sn_t *sss, struct sn_community *comm); const char* compression_str(uint8_t cmpr); diff --git a/include/n2n_wire.h b/include/n2n_wire.h index f1593f7..dbe32cd 100644 --- a/include/n2n_wire.h +++ b/include/n2n_wire.h @@ -169,8 +169,6 @@ typedef struct n2n_REGISTER_SUPER_ACK { uint8_t num_sn; /**< Number of supernodes that were send * even if we cannot store them all. If * non-zero then sn_bak is valid. */ - n2n_sock_t sn_bak; /**< Socket of the first backup supernode */ - n2n_mac_t mac_addr; } n2n_REGISTER_SUPER_ACK_t; diff --git a/src/sn.c b/src/sn.c index f58bf2e..75cfb40 100644 --- a/src/sn.c +++ b/src/sn.c @@ -24,6 +24,7 @@ #define HASH_FIND_COMMUNITY(head, name, out) HASH_FIND_STR(head, name, out) static n2n_sn_t sss_node; +static const n2n_mac_t null_mac = {0, 0, 0, 0, 0, 0}; /** Load the list of allowed communities. Existing/previous ones will be removed * @@ -176,8 +177,9 @@ static void help() { "or\n" ); printf("supernode "); - printf("-l "); + printf("-p "); printf("-c "); + printf("-l "); #if defined(N2N_HAVE_DAEMON) printf("[-f] "); #endif @@ -191,8 +193,9 @@ static void help() { printf("[-v] "); printf("\n\n"); - printf("-l | Set UDP main listen port to \n"); + printf("-p | Set UDP main listen port to \n"); printf("-c | File containing the allowed communities.\n"); + printf("-l | Supernode IP:port.\n"); #if defined(N2N_HAVE_DAEMON) printf("-f | Run in foreground.\n"); #endif /* #if defined(N2N_HAVE_DAEMON) */ @@ -219,7 +222,7 @@ static int setOption(int optkey, char *_optarg, n2n_sn_t *sss) { //traceEvent(TRACE_NORMAL, "Option %c = %s", optkey, _optarg ? _optarg : ""); switch (optkey) { - case 'l': /* local-port */ + case 'p': /* local-port */ sss->lport = atoi(_optarg); break; @@ -227,6 +230,45 @@ static int setOption(int optkey, char *_optarg, n2n_sn_t *sss) { sss->mport = atoi(_optarg); break; + case 'l': { /* supernode:port */ + n2n_sock_t *socket; + struct peer_info *anchor_sn; + size_t length; + int rv; + + length = strlen(_optarg); + if(length >= N2N_EDGE_SN_HOST_SIZE) { + traceEvent(TRACE_WARNING, "Size of -l argument too long: %zu. Maximum size is %d",length,N2N_EDGE_SN_HOST_SIZE); + break; + } + + if(sss->federation != NULL) { + socket = (n2n_sock_t *)calloc(1,sizeof(n2n_sock_t)); + + anchor_sn = add_sn_to_federation_by_mac_or_sock(sss,socket, (n2n_mac_t*) null_mac); + + if(anchor_sn != NULL){ + anchor_sn->ip_addr = calloc(1,N2N_EDGE_SN_HOST_SIZE); + if(anchor_sn->ip_addr){ + strncpy(anchor_sn->ip_addr,_optarg,N2N_EDGE_SN_HOST_SIZE-1); + rv = supernode2sock(socket,_optarg); + + if(rv != 0){ + traceEvent(TRACE_WARNING, "Invalid socket"); + break; + } + + memcpy(&(anchor_sn->sock), socket, sizeof(n2n_sock_t)); + memcpy(&(anchor_sn->mac_addr),null_mac,sizeof(n2n_mac_t)); + anchor_sn->purgeable = SN_UNPURGEABLE; + anchor_sn->last_valid_time_stamp = initial_time_stamp(); + } + } + } + + break; + } + case 'a': { dec_ip_str_t ip_min_str = {'\0'}; dec_ip_str_t ip_max_str = {'\0'}; @@ -280,15 +322,12 @@ static int setOption(int optkey, char *_optarg, n2n_sn_t *sss) { #endif case 'F': { /* federation name */ - struct sn_community *fed; - - HASH_FIND_COMMUNITY(sss->communities, FEDERATION_NAME, fed); - if(fed != NULL){ - snprintf(fed->community,N2N_COMMUNITY_SIZE-1,"*%s",_optarg); - strncpy(sss->federation, fed->community, N2N_COMMUNITY_SIZE-1); - sss->federation[N2N_COMMUNITY_SIZE-1] = '\0'; + if(sss->federation->community != NULL){ + snprintf(sss->federation->community,N2N_COMMUNITY_SIZE-1,"*%s",_optarg); + sss->federation->community[N2N_COMMUNITY_SIZE-1] = '\0'; } + break; } @@ -327,7 +366,7 @@ static int setOption(int optkey, char *_optarg, n2n_sn_t *sss) { static const struct option long_options[] = { {"communities", required_argument, NULL, 'c'}, {"foreground", no_argument, NULL, 'f'}, - {"local-port", required_argument, NULL, 'l'}, + {"local-port", required_argument, NULL, 'p'}, {"mgmt-port", required_argument, NULL, 't'}, {"autoip", required_argument, NULL, 'a'}, {"help", no_argument, NULL, 'h'}, @@ -341,7 +380,7 @@ static const struct option long_options[] = { static int loadFromCLI(int argc, char * const argv[], n2n_sn_t *sss) { u_char c; - while((c = getopt_long(argc, argv, "fl:u:g:t:a:c:F:m:vh", + while((c = getopt_long(argc, argv, "fp:l:u:g:t:a:c:F:m:vh", long_options, NULL)) != '?') { if(c == 255) break; setOption(c, optarg, sss); @@ -437,26 +476,15 @@ static int loadFromFile(const char *path, n2n_sn_t *sss) { /* Add the federation to the communities list of a supernode */ static int add_federation_to_communities(n2n_sn_t *sss){ - struct sn_community *fed; uint32_t num_communities = 0; - fed = (struct sn_community *)calloc(1,sizeof(struct sn_community)); - comm_init(fed,sss->federation); - - if(fed != NULL) { - /* enable the flag for federation */ - fed->is_federation = IS_FEDERATION; - fed->purgeable = COMMUNITY_UNPURGEABLE; - /* header encryption enabled by default */ - fed->header_encryption = HEADER_ENCRYPTION_ENABLED; - /*setup the encryption key */ - packet_header_setup_key(fed->community, &(fed->header_encryption_ctx), &(fed->header_iv_ctx)); - HASH_ADD_STR(sss->communities, community, fed); + if(sss->federation != NULL) { + HASH_ADD_STR(sss->communities, community, sss->federation); num_communities = HASH_COUNT(sss->communities); traceEvent(TRACE_INFO, "Added federation '%s' to the list of communities [total: %u]", - (char*)fed->community, num_communities); + (char*)sss->federation->community, num_communities); } return 0; @@ -615,5 +643,3 @@ int main(int argc, char * const argv[]) { keep_running = 1; return run_sn_loop(&sss_node, &keep_running); } - - diff --git a/src/sn_utils.c b/src/sn_utils.c index 89d1deb..f603075 100644 --- a/src/sn_utils.c +++ b/src/sn_utils.c @@ -52,10 +52,6 @@ static int update_edge(n2n_sn_t *sss, const n2n_sock_t *sender_sock, time_t now); -static int re_register_and_purge_supernodes(n2n_sn_t *sss, - struct sn_community *comm, - time_t now); - static int purge_expired_communities(n2n_sn_t *sss, time_t* p_last_purge, time_t now); @@ -70,16 +66,14 @@ static int process_mgmt(n2n_sn_t *sss, size_t mgmt_size, time_t now); -static int add_sn_to_federation_from_register_super_ack(n2n_sn_t *sss, - n2n_REGISTER_SUPER_ACK_t ack, - 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); +static const n2n_mac_t null_mac = {0, 0, 0, 0, 0, 0}; + /* ************************************** */ static int try_forward(n2n_sn_t * sss, @@ -244,8 +238,20 @@ int sn_init(n2n_sn_t *sss) { sss->max_auto_ip_net.net_addr = inet_addr(N2N_SN_MAX_AUTO_IP_NET_DEFAULT); sss->max_auto_ip_net.net_addr = ntohl(sss->max_auto_ip_net.net_addr); sss->max_auto_ip_net.net_bitlen = N2N_SN_AUTO_IP_NET_BIT_DEFAULT; - strncpy(sss->federation, (char*)FEDERATION_NAME, N2N_COMMUNITY_SIZE-1); - sss->federation[N2N_COMMUNITY_SIZE-1] = '\0'; + sss->federation = (struct sn_community *)calloc(1,sizeof(struct sn_community)); + + /* Initialize the federation */ + if(sss->federation){ + strncpy(sss->federation->community, (char*)FEDERATION_NAME, N2N_COMMUNITY_SIZE-1); + sss->federation->community[N2N_COMMUNITY_SIZE-1] = '\0'; + /* enable the flag for federation */ + sss->federation->is_federation = IS_FEDERATION; + sss->federation->purgeable = COMMUNITY_UNPURGEABLE; + /* header encryption enabled by default */ + sss->federation->header_encryption = HEADER_ENCRYPTION_ENABLED; + /*setup the encryption key */ + packet_header_setup_key(sss->federation->community, &(sss->federation->header_encryption_ctx), &(sss->federation->header_iv_ctx)); + } n2n_srand (n2n_seed()); @@ -513,7 +519,7 @@ static int re_register_and_purge_supernodes(n2n_sn_t *sss, struct sn_community * 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) */ + if((time < PURGE_FEDERATION_NODE_INTERVAL) || (peer->purgeable == SN_UNPURGEABLE)){ /* re-regitser (send REGISTER_SUPER) */ uint8_t pktbuf[N2N_PKT_BUF_SIZE] = {0}; size_t idx; /* ssize_t sent; */ @@ -724,43 +730,45 @@ static int sendto_mgmt(n2n_sn_t *sss, } -/** 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, time_t now){ - 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 = now - TEST_TIME; - HASH_ADD_PEER(fed->edges, peer); - } - tmp_sock += sizeof(n2n_mac_t); - tmp_mac += sizeof(n2n_sock_t); - } - } +/** Search for a node in the federation list. If it has to add a new node, it creates a new peer_info and initializes it + * Evaluate first the MAC parameter and if it's zero-MAC, then it can skip HASH_FIND_PEER by MAC and search by socket + */ +struct peer_info* add_sn_to_federation_by_mac_or_sock(n2n_sn_t *sss,n2n_sock_t *sock, n2n_mac_t *mac){ + struct peer_info *scan, *tmp, *peer; + int found = 0; + + if(sss->federation != NULL){ + if(memcmp(mac,null_mac,sizeof(n2n_mac_t)) != 0){ /* not zero MAC */ + HASH_FIND_PEER(sss->federation->edges, mac, peer); + + //REVISIT: make this dependent from last_seen and update socket + } + + if(peer == NULL){ /* zero MAC, search by socket */ + HASH_ITER(hh,sss->federation->edges,scan,tmp){ + if(memcmp(&(scan->sock), sock, sizeof(n2n_sock_t))){ + memcpy(&(scan->mac_addr), sock, sizeof(n2n_mac_t)); + peer = scan; + break; + } + } + + if(peer == NULL){ + peer = (struct peer_info*)calloc(1,sizeof(struct peer_info)); + if(peer){ + memcpy(&(peer->sock),sock,sizeof(n2n_sock_t)); + memcpy(&(peer->mac_addr),mac, sizeof(n2n_mac_t)); + HASH_ADD_PEER(sss->federation->edges,peer); + } + } + } + } - return 0; /* OK */ + return peer; } - /** Examine a datagram and determine what to do with it. * */ @@ -1044,23 +1052,24 @@ static int process_udp(n2n_sn_t * sss, n2n_REGISTER_SUPER_ACK_t ack; n2n_common_t cmn2; uint8_t ackbuf[N2N_SN_PKTBUF_SIZE]; + uint8_t tmpbuf[MAX_AVAILABLE_SPACE_FOR_ENTRIES]; size_t encx=0; struct sn_community *fed; struct sn_community_regular_expression *re, *tmp_re; - struct peer_info *peer, *tmp_peer, *p; + struct peer_info *peer, *tmp_peer, *p; int8_t allowed_match = -1; uint8_t match = 0; - int match_length = 0; + int match_length = 0; n2n_ip_subnet_t ipaddr; - int num = 0; - n2n_sock_t *tmp_sock; - n2n_mac_t *tmp_mac; + int num = 0; + n2n_sock_t *tmp_sock; + n2n_mac_t *tmp_mac; if(from_supernode != comm->is_federation){ traceEvent(TRACE_DEBUG, "process_udp dropped REGISTER_SUPER: from_supernode value doesn't correspond to the internal federation marking"); return -1; } - + memset(&ack, 0, sizeof(n2n_REGISTER_SUPER_ACK_t)); /* Edge/supernode requesting registration with us. */ @@ -1103,11 +1112,11 @@ static int process_udp(n2n_sn_t * sss, } if(!comm && (!sss->lock_communities || (match == 1))) { - + comm = (struct sn_community*)calloc(1,sizeof(struct sn_community)); - + if(comm) { - comm_init(comm,(char *)cmn.community); + comm_init(comm,(char *)cmn.community); /* new communities introduced by REGISTERs could not have had encrypted header... */ comm->header_encryption = HEADER_ENCRYPTION_NONE; comm->header_encryption_ctx = NULL; @@ -1122,7 +1131,6 @@ static int process_udp(n2n_sn_t * sss, } if(comm) { - HASH_FIND_COMMUNITY(sss->communities, sss->federation, fed); cmn2.ttl = N2N_DEFAULT_TTL; cmn2.pc = n2n_register_super_ack; cmn2.flags = N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; @@ -1143,25 +1151,29 @@ static int process_udp(n2n_sn_t * sss, ack.sock.port = ntohs(sender_sock->sin_port); memcpy(ack.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE); - if(fed != NULL){ - HASH_FIND_PEER(fed->edges, reg.edgeMac, p); - if(p != NULL){ - p->last_seen = now; - tmp_sock = &(ack.sn_bak); - tmp_mac = &(ack.mac_addr); - - HASH_ITER(hh, fed->edges, peer, tmp_peer) { - if((now - peer->last_seen) >= ALLOWED_TIME) continue; /* skip long-time-not-seen supernodes */ - if(((++num)*ENTRY_SIZE) > MAX_AVAILABLE_SPACE_FOR_ENTRIES) break; /* no more space available in REGISTER_SUPER_ACK payload */ - memcpy(tmp_sock, &(peer->sock), sizeof(n2n_sock_t)); - memcpy(tmp_mac, &(peer->mac_addr), sizeof(n2n_mac_t)); - tmp_sock += sizeof(n2n_mac_t); - tmp_mac += sizeof(n2n_sock_t); - } - ack.num_sn = num; - } - } - + /* Add sender's data to federation (or update it) */ + if(comm->is_federation == IS_FEDERATION){ + p = add_sn_to_federation_by_mac_or_sock(sss,&(ack.sock),&(reg.edgeMac)); + if(p) p->last_seen = now; + } + + tmp_sock = (void*)tmpbuf; + tmp_mac = (void*)tmpbuf + sizeof(n2n_sock_t); + + // REVISIT: consider adding last_seen + + /* Assembling supernode list for REGISTER_SUPER_ACK payload */ + HASH_ITER(hh, sss->federation->edges, peer, tmp_peer) { + if((now - peer->last_seen) >= ALLOWED_TIME) continue; /* skip long-time-not-seen supernodes */ + if(((++num)*ENTRY_SIZE) > MAX_AVAILABLE_SPACE_FOR_ENTRIES) break; /* no more space available in REGISTER_SUPER_ACK payload */ + memcpy((void*)tmpbuf, (void*)&(peer->sock), sizeof(n2n_sock_t)); + memcpy((void*)tmpbuf, (void*)&(peer->mac_addr), sizeof(n2n_mac_t)); + tmp_sock += ENTRY_SIZE; + tmp_mac += ENTRY_SIZE; + } + ack.num_sn = num; + + traceEvent(TRACE_DEBUG, "Rx REGISTER_SUPER for %s [%s]", macaddr_str(mac_buf, reg.edgeMac), sock_to_cstr(sockbuf, &(ack.sock))); @@ -1171,6 +1183,7 @@ static int process_udp(n2n_sn_t * sss, } encode_REGISTER_SUPER_ACK(ackbuf, &encx, &cmn2, &ack); + encode_buf(ackbuf,&encx,tmpbuf,(num*ENTRY_SIZE)); if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) packet_header_encrypt (ackbuf, encx, comm->header_encryption_ctx, @@ -1194,12 +1207,15 @@ static int process_udp(n2n_sn_t * sss, 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; + struct peer_info *scan, *tmp; + n2n_sock_str_t sockbuf1; + n2n_sock_str_t sockbuf2; + macstr_t mac_buf1; + n2n_sock_t sender; + n2n_sock_t *orig_sender; + n2n_sock_t *tmp_sock; + n2n_mac_t *tmp_mac; + int i; sender.family = AF_INET; sender.port = ntohs(sender_sock->sin_port); @@ -1208,6 +1224,11 @@ static int process_udp(n2n_sn_t * sss, memset(&ack, 0, sizeof(n2n_REGISTER_SUPER_ACK_t)); + if(!comm) { + traceEvent(TRACE_DEBUG, "process_udp REGISTER_SUPER_ACK with unknown community %s", cmn.community); + return -1; + } + 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; @@ -1226,20 +1247,33 @@ static int process_udp(n2n_sn_t * sss, } traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK myMAC=%s [%s] (external %s)", - macaddr_str(mac_buf1, ack.mac_addr), + macaddr_str(mac_buf1, ack.edgeMac), 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(comm->is_federation == IS_FEDERATION) { + HASH_FIND_PEER(sss->federation->edges, ack.edgeMac, scan); if(scan != NULL){ scan->last_seen = now; - } + } else { + traceEvent(TRACE_DEBUG, "process_udp dropped REGISTER_SUPER_ACK due to an unknown supernode."); + break; + } } - if(ack.num_sn > 0) add_sn_to_federation_from_register_super_ack(sss,ack,now); + tmp_sock = (void*)&(ack.num_sn) + sizeof(ack.num_sn); + tmp_mac = (void*)tmp_sock + sizeof(n2n_sock_t); + + for(i=0; ilast_seen = now - TEST_TIME; + } + + tmp_sock += ENTRY_SIZE; + tmp_mac += ENTRY_SIZE; + } break; } @@ -1342,7 +1376,7 @@ int run_sn_loop(n2n_sn_t *sss, int *keep_running) if (rc > 0) { - if (FD_ISSET(sss->sock, &socket_mask)) + if (FD_ISSET(sss->sock, &socket_mask)) { struct sockaddr_in sender_sock; socklen_t i; @@ -1400,6 +1434,7 @@ int run_sn_loop(n2n_sn_t *sss, int *keep_running) traceEvent(TRACE_DEBUG, "timeout"); } + re_register_and_purge_supernodes(sss, sss->federation, now); purge_expired_communities(sss, &last_purge_edges, now); sort_communities (sss, &last_sort_communities, now); } /* while */