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()
This commit is contained in:
Francesco Carli 2020-10-11 11:44:40 +02:00 committed by GitHub
parent e3f64bfd1e
commit c9eedd68f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 180 additions and 117 deletions

View File

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

View File

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

View File

@ -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 <local port> ");
printf("-p <local port> ");
printf("-c <path> ");
printf("-l <supernode:port> ");
#if defined(N2N_HAVE_DAEMON)
printf("[-f] ");
#endif
@ -191,8 +193,9 @@ static void help() {
printf("[-v] ");
printf("\n\n");
printf("-l <port> | Set UDP main listen port to <port>\n");
printf("-p <port> | Set UDP main listen port to <port>\n");
printf("-c <path> | File containing the allowed communities.\n");
printf("-l <sn:port> | 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);
}

View File

@ -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;
/** 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;
HASH_FIND_COMMUNITY(sss->communities, sss->federation, fed);
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);
if(fed != NULL){
tmp_sock = &(ack.sn_bak);
tmp_mac = &(ack.mac_addr);
for(i=0; i<ack.num_sn; i++){
HASH_FIND_PEER(fed->edges, &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);
}
}
//REVISIT: make this dependent from last_seen and update socket
}
return 0; /* OK */
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 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);
/* 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;
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;
}
}
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; i<ack.num_sn; i++){
tmp = add_sn_to_federation_by_mac_or_sock(sss,tmp_sock,tmp_mac);
if(tmp){
tmp->last_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 */