Browse Source

added optional rtt based supernode selection: federation (#580)

pull/582/head
Logan oos Even 4 years ago
committed by GitHub
parent
commit
8761ae849b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      doc/Building.md
  2. 4
      doc/Federation.md
  3. 291
      src/sn_selection.c

10
doc/Building.md

@ -137,3 +137,13 @@ which then will include ZSTD if found on the system. It will be available via `-
`./configure --with-zstd --with-openssl CFLAGS="-O3 -march=native"` `./configure --with-zstd --with-openssl CFLAGS="-O3 -march=native"`
Again, and this needs to be reiterated sufficiently often, please do no forget to `make clean` after (re-)configuration and before building (again) using `make`. Again, and this needs to be reiterated sufficiently often, please do no forget to `make clean` after (re-)configuration and before building (again) using `make`.
## Federation – Supernode Selection by Round Trip Time
If used with multiple supernodes, by default, an edge choses the least loaded supernode to connect to. This selection strategy is part of the [federation](Federation.md) feature and aims at a fair workload distribution among the supernodes. To serve special scenarios, an edge can be compiled to always connect to the supernode with the lowest round trip time, i.e. the "closest" with the lowest ping. However, this could result in not so fair workload distribution among supernodes. This option can be configured by defining the macro `SN_SELECTION_RTT` and affects edge's behaviour only:
`./configure CFLAGS="-DSN_SELECTION_RTT"`
which of course can be combined with the compiler optimizations mentioned above…
Note that the activation of this strategy requires a sufficiently accurate local day-of-time clock. It probably will fail on smaller systems using `uclibc` (instead of `glibc`) whose day-of-time clock is said to not provide sub-second accuracy.

4
doc/Federation.md

@ -1,4 +1,4 @@
# Supernode Federation # Supernode Federation
## Idea ## Idea
To enhance resilience in terms of backup and fail-over, also for load-balancing, multiple supernodes can easily interconnect and form a special community, called **federation**. To enhance resilience in terms of backup and fail-over, also for load-balancing, multiple supernodes can easily interconnect and form a special community, called **federation**.
@ -33,3 +33,5 @@ Once edges have saved those informations, it is up to them choosing the supernod
An edge connects to the supernode with the lowest work-load and it is re-considered from time to time, with each re-registration. We used a stickyness factor to avoid too much jumping between supernodes. An edge connects to the supernode with the lowest work-load and it is re-considered from time to time, with each re-registration. We used a stickyness factor to avoid too much jumping between supernodes.
Thanks to this last feature, n2n is now able to handle security attacks (e.g., DoS against supernodes) and it can redistribute the entire load of the network in a fair manner between all the supernodes. Thanks to this last feature, n2n is now able to handle security attacks (e.g., DoS against supernodes) and it can redistribute the entire load of the network in a fair manner between all the supernodes.
To serve scenarios in which an edge is supposed to select the supernode by round trip time, i.e. choosing the "closest" one, a [compile-time option](Building.md) is offered. Note, that workload distribution among supernodes is not so fair then.

291
src/sn_selection.c

@ -1,130 +1,161 @@
/** /**
* (C) 2007-20 - ntop.org and contributors * (C) 2007-21 - ntop.org and contributors
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not see see <http://www.gnu.org/licenses/> * along with this program; if not see see <http://www.gnu.org/licenses/>
* *
*/ */
#include "n2n.h"
#include "n2n.h"
static SN_SELECTION_CRITERION_DATA_TYPE sn_selection_criterion_common_read (n2n_edge_t *eee);
static int sn_selection_criterion_sort (peer_info_t *a, peer_info_t *b); static SN_SELECTION_CRITERION_DATA_TYPE sn_selection_criterion_common_read (n2n_edge_t *eee);
static int sn_selection_criterion_sort (peer_info_t *a, peer_info_t *b);
/* ****************************************************************************** */
/* Initialize selection_criterion field in peer_info structure*/ /* Initialize selection_criterion field in peer_info structure*/
int sn_selection_criterion_init (peer_info_t *peer) { int sn_selection_criterion_init (peer_info_t *peer) {
if(peer != NULL) { if(peer != NULL) {
sn_selection_criterion_default(&(peer->selection_criterion)); sn_selection_criterion_default(&(peer->selection_criterion));
} }
return 0; /* OK */ return 0; /* OK */
} }
/* Set selection_criterion field to default value according to selected strategy. */
int sn_selection_criterion_default (SN_SELECTION_CRITERION_DATA_TYPE *selection_criterion) { /* Set selection_criterion field to default value according to selected strategy. */
int sn_selection_criterion_default (SN_SELECTION_CRITERION_DATA_TYPE *selection_criterion) {
*selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE) UINT32_MAX >> 1;
*selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE) UINT32_MAX >> 1;
return 0; /* OK */
} return 0; /* OK */
}
/* Take data from PEER_INFO payload and transform them into a selection_criterion.
* This function is highly dependant of the chosen selection criterion.
*/ /* Take data from PEER_INFO payload and transform them into a selection_criterion.
int sn_selection_criterion_calculate (n2n_edge_t *eee, peer_info_t *peer, SN_SELECTION_CRITERION_DATA_TYPE *data) { * This function is highly dependant of the chosen selection criterion.
*/
SN_SELECTION_CRITERION_DATA_TYPE common_data; int sn_selection_criterion_calculate (n2n_edge_t *eee, peer_info_t *peer, SN_SELECTION_CRITERION_DATA_TYPE *data) {
int sum = 0;
SN_SELECTION_CRITERION_DATA_TYPE common_data;
common_data = sn_selection_criterion_common_read(eee); int sum = 0;
peer->selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE)(be32toh(*data) + common_data);
common_data = sn_selection_criterion_common_read(eee);
/* Mitigation of the real supernode load in order to see less oscillations.
* Edges jump from a supernode to another back and forth due to purging. #ifndef SN_SELECTION_RTT
* Because this behavior has a cost of switching, the real load is mitigated with a stickyness factor. peer->selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE)(be32toh(*data) + common_data);
* This factor is dynamically calculated basing on network size and prevent that unnecessary switching */
if(peer == eee->curr_sn) { /* Mitigation of the real supernode load in order to see less oscillations.
sum = HASH_COUNT(eee->known_peers) + HASH_COUNT(eee->pending_peers); * Edges jump from a supernode to another back and forth due to purging.
peer->selection_criterion = peer->selection_criterion * sum / (sum + 1); * Because this behavior has a cost of switching, the real load is mitigated with a stickyness factor.
} * This factor is dynamically calculated basing on network size and prevent that unnecessary switching */
if(peer == eee->curr_sn) {
return 0; /* OK */ sum = HASH_COUNT(eee->known_peers) + HASH_COUNT(eee->pending_peers);
} peer->selection_criterion = peer->selection_criterion * sum / (sum + 1);
}
/* Set sn_selection_criterion_common_data field to default value. */ #else
int sn_selection_criterion_common_data_default (n2n_edge_t *eee) { peer->selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE)(time_stamp() >> 22) - common_data;
#endif
SN_SELECTION_CRITERION_DATA_TYPE tmp = 0;
return 0; /* OK */
tmp = HASH_COUNT(eee->pending_peers); }
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
tmp *= 2;
} /* Set sn_selection_criterion_common_data field to default value. */
eee->sn_selection_criterion_common_data = tmp / HASH_COUNT(eee->conf.supernodes); int sn_selection_criterion_common_data_default (n2n_edge_t *eee) {
return 0; /* OK */ #ifndef SN_SELECTION_RTT
} SN_SELECTION_CRITERION_DATA_TYPE tmp = 0;
/* Return the value of sn_selection_criterion_common_data field. */ tmp = HASH_COUNT(eee->pending_peers);
static SN_SELECTION_CRITERION_DATA_TYPE sn_selection_criterion_common_read (n2n_edge_t *eee) { if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
tmp *= 2;
return eee->sn_selection_criterion_common_data; }
} eee->sn_selection_criterion_common_data = tmp / HASH_COUNT(eee->conf.supernodes);
#else
/* Function that compare two selection_criterion fields and sorts them in ascending order. */ eee->sn_selection_criterion_common_data = (SN_SELECTION_CRITERION_DATA_TYPE)(time_stamp() >> 22);
static int sn_selection_criterion_sort (peer_info_t *a, peer_info_t *b) { #endif
// comparison function for sorting supernodes in ascending order of their selection_criterion. return 0; /* OK */
return (a->selection_criterion - b->selection_criterion); }
}
/* Function that sorts peer_list using sn_selection_criterion_sort. */ /* Return the value of sn_selection_criterion_common_data field. */
int sn_selection_sort (peer_info_t **peer_list) { static SN_SELECTION_CRITERION_DATA_TYPE sn_selection_criterion_common_read (n2n_edge_t *eee) {
HASH_SORT(*peer_list, sn_selection_criterion_sort); return eee->sn_selection_criterion_common_data;
}
return 0; /* OK */
}
/* Function that compare two selection_criterion fields and sorts them in ascending order. */
/* Function that gathers requested data on a supernode. */ static int sn_selection_criterion_sort (peer_info_t *a, peer_info_t *b) {
SN_SELECTION_CRITERION_DATA_TYPE sn_selection_criterion_gather_data (n2n_sn_t *sss) {
// comparison function for sorting supernodes in ascending order of their selection_criterion.
SN_SELECTION_CRITERION_DATA_TYPE data = 0, tmp = 0; return (a->selection_criterion - b->selection_criterion);
struct sn_community *comm, *tmp_comm; }
HASH_ITER(hh, sss->communities, comm, tmp_comm) {
tmp = HASH_COUNT(comm->edges) + 1; /* number of nodes in the community + the community itself. */ /* Function that sorts peer_list using sn_selection_criterion_sort. */
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { /*double-count encrypted communities (and their nodes): they exert more load on supernode. */ int sn_selection_sort (peer_info_t **peer_list) {
tmp *= 2;
} HASH_SORT(*peer_list, sn_selection_criterion_sort);
data += tmp;
} return 0; /* OK */
}
return htobe32(data);
}
/* Function that gathers requested data on a supernode.
/* Convert selection_criterion field in a string for management port output. */ * it remains unaffected by SN_SELECT_RTT macro because it refers to edge behaviour only
extern char * sn_selection_criterion_str (selection_criterion_str_t out, peer_info_t *peer) { */
SN_SELECTION_CRITERION_DATA_TYPE sn_selection_criterion_gather_data (n2n_sn_t *sss) {
if(NULL == out) {
return NULL; SN_SELECTION_CRITERION_DATA_TYPE data = 0, tmp = 0;
} struct sn_community *comm, *tmp_comm;
memset(out, 0, SN_SELECTION_CRITERION_BUF_SIZE);
snprintf(out, SN_SELECTION_CRITERION_BUF_SIZE - 1, "ld = %d", (short int)(peer->selection_criterion)); HASH_ITER(hh, sss->communities, comm, tmp_comm) {
// number of nodes in the community + the community itself
return out; tmp = HASH_COUNT(comm->edges) + 1;
} if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
// double-count encrypted communities (and their nodes): they exert more load on supernode
tmp *= 2;
}
data += tmp;
}
return htobe32(data);
}
/* Convert selection_criterion field in a string for management port output. */
extern char * sn_selection_criterion_str (selection_criterion_str_t out, peer_info_t *peer) {
if(NULL == out) {
return NULL;
}
memset(out, 0, SN_SELECTION_CRITERION_BUF_SIZE);
#ifndef SN_SELECTION_RTT
snprintf(out, SN_SELECTION_CRITERION_BUF_SIZE - 1,
(int16_t)(peer->selection_criterion) != -1 ? "ld = %7d" :
"ld = _______", peer->selection_criterion);
#else
snprintf(out, SN_SELECTION_CRITERION_BUF_SIZE - 1,
(int16_t)(peer->selection_criterion) != -1 ? "rtt %5d ms" :
"rtt _____ ms", peer->selection_criterion);
#endif
return out;
}

Loading…
Cancel
Save