choose supernode selection strategy at run-time '--select-rtt' (#864)

This commit is contained in:
Logan oos Even 2021-10-24 12:53:25 +05:45 committed by GitHub
parent 7b7449c813
commit c61b62ab1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 113 additions and 55 deletions

View File

@ -173,16 +173,6 @@ which then will include ZSTD if found on the system. It will be available via `-
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.
## SPECK ARM NEON Hardware Acceleration ## SPECK ARM NEON Hardware Acceleration
By default, SPECK does not take advantage of ARM NEON hardware acceleration even if compiled with `-march=native`. The reason is that the NEON implementation proved to be slower than the 64-bit scalar code on Raspberry Pi 3B+, see [here](https://github.com/ntop/n2n/issues/563). By default, SPECK does not take advantage of ARM NEON hardware acceleration even if compiled with `-march=native`. The reason is that the NEON implementation proved to be slower than the 64-bit scalar code on Raspberry Pi 3B+, see [here](https://github.com/ntop/n2n/issues/563).

View File

@ -36,4 +36,4 @@ An edge connects to the supernode with the lowest work-load and it is re-conside
Thanks to this feature, n2n is now able to handle security attacks such as DoS against supernodes and it can redistribute the entire load of the network in a fair manner between all the supernodes. Thanks to this feature, n2n is now able to handle security attacks such as 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](https://github.com/ntop/n2n/blob/dev/doc/Building.md#federation--supernode-selection-by-round-trip-time) is available. Note, that workload distribution among supernodes is not so fair then. To serve scenarios in which an edge is supposed to select the supernode by round trip time, i.e. choosing the "closest" one, the `--select-rtt` command line option is available at the edge. Note, that workload distribution among supernodes might not be so fair then.

4
edge.8
View File

@ -101,6 +101,10 @@ use header encryption, supernode needs fixed community
.TP .TP
\fB\-z1\fR ... \fB\-z2\fR \fB\-z1\fR ... \fB\-z2\fR
compress outgoing data packets, -z1 = lzo1x, disabled by default compress outgoing data packets, -z1 = lzo1x, disabled by default
.TP
\fB\--select-rtt\fR
select supernode by round trip time if several to choose from (federation),
defaults to load-based selection strategy if not provided.
.SH TAP DEVICE AND OVERLAY NETWORK CONFIGURATION .SH TAP DEVICE AND OVERLAY NETWORK CONFIGURATION
.TP .TP
\fB\-a \fR[\fImode\fR]<\fIip\fR>[\fI/n\fR] \fB\-a \fR[\fImode\fR]<\fIip\fR>[\fI/n\fR]

View File

@ -197,7 +197,7 @@ enum skip_add {SN_ADD = 0, SN_ADD_SKIP = 1, SN_ADD_ADDED = 2};
#define N2N_THREAD_PARAMETER_DATATYPE void* #define N2N_THREAD_PARAMETER_DATATYPE void*
#endif #endif
#define SN_SELECTION_CRITERION_DATA_TYPE uint32_t #define SN_SELECTION_CRITERION_DATA_TYPE uint64_t
#define SN_SELECTION_CRITERION_BUF_SIZE 16 #define SN_SELECTION_CRITERION_BUF_SIZE 16
#define N2N_TRANSFORM_ID_USER_START 64 #define N2N_TRANSFORM_ID_USER_START 64

View File

@ -286,6 +286,11 @@ typedef enum n2n_pc {
typedef char n2n_version_t[N2N_VERSION_STRING_SIZE]; typedef char n2n_version_t[N2N_VERSION_STRING_SIZE];
#define SN_SELECTION_STRATEGY_LOAD 1
#define SN_SELECTION_STRATEGY_RTT 2
#define SN_SELECTION_STRATEGY_MAC 3 /* REVISIT: not implemented yet */
typedef struct n2n_ip_subnet { typedef struct n2n_ip_subnet {
uint32_t net_addr; /* Host order IP address. */ uint32_t net_addr; /* Host order IP address. */
uint8_t net_bitlen; /* Subnet prefix. */ uint8_t net_bitlen; /* Subnet prefix. */
@ -414,7 +419,7 @@ typedef struct n2n_PEER_INFO {
n2n_mac_t mac; n2n_mac_t mac;
n2n_sock_t sock; n2n_sock_t sock;
n2n_sock_t preferred_sock; n2n_sock_t preferred_sock;
SN_SELECTION_CRITERION_DATA_TYPE data; uint32_t load;
n2n_version_t version; n2n_version_t version;
time_t uptime; time_t uptime;
} n2n_PEER_INFO_t; } n2n_PEER_INFO_t;
@ -668,6 +673,7 @@ typedef struct n2n_edge_conf {
n2n_auth_t auth; n2n_auth_t auth;
filter_rule_t *network_traffic_filter_rules; filter_rule_t *network_traffic_filter_rules;
int metric; /**< Network interface metric (Windows only). */ int metric; /**< Network interface metric (Windows only). */
uint8_t sn_selection_strategy; /**< encodes currently chosen supernode selection strategy. */
uint8_t number_max_sn_pings; /**< Number of maximum concurrently allowed supernode pings. */ uint8_t number_max_sn_pings; /**< Number of maximum concurrently allowed supernode pings. */
} n2n_edge_conf_t; } n2n_edge_conf_t;

View File

@ -40,7 +40,7 @@ int sn_selection_sort (peer_info_t **peer_list);
SN_SELECTION_CRITERION_DATA_TYPE sn_selection_criterion_gather_data (n2n_sn_t *sss); SN_SELECTION_CRITERION_DATA_TYPE sn_selection_criterion_gather_data (n2n_sn_t *sss);
/* management port output function */ /* management port output function */
extern char * sn_selection_criterion_str (selection_criterion_str_t out, peer_info_t *peer); extern char * sn_selection_criterion_str (n2n_edge_t *eee, selection_criterion_str_t out, peer_info_t *peer);
#endif /* _SN_SELECTION_ */ #endif /* _SN_SELECTION_ */

View File

@ -179,6 +179,8 @@ static void help (int level) {
"[-z<compression>] " "[-z<compression>] "
"\n " "\n "
"[-e <preferred local IP address>] [-S<level of solitude>]" "[-e <preferred local IP address>] [-S<level of solitude>]"
"\n "
"[--select-rtt]"
"\n\n tap device and " "\n\n tap device and "
"[-a [static:|dhcp:]<tap IP address>[/<cidr suffix>]] " "[-a [static:|dhcp:]<tap IP address>[/<cidr suffix>]] "
"\n overlay network " "\n overlay network "
@ -227,6 +229,7 @@ static void help (int level) {
"\n flag options [-H] enable header encryption" "\n flag options [-H] enable header encryption"
"\n [-r] enable packet forwarding through n2n community" "\n [-r] enable packet forwarding through n2n community"
"\n [-E] accept multicast MAC addresses" "\n [-E] accept multicast MAC addresses"
"\n [--select-rtt] select supernode by round trip time"
#ifndef WIN32 #ifndef WIN32
"\n [-f] do not fork but run in foreground" "\n [-f] do not fork but run in foreground"
#endif #endif
@ -285,6 +288,9 @@ static void help (int level) {
"-z2 = zstd, " "-z2 = zstd, "
#endif #endif
"disabled by default\n"); "disabled by default\n");
printf("--select-rtt | supernode selection based on round trip time (default:\n"
" | by load)\n");
printf ("\n"); printf ("\n");
printf (" TAP DEVICE AND OVERLAY NETWORK CONFIGURATION\n"); printf (" TAP DEVICE AND OVERLAY NETWORK CONFIGURATION\n");
printf (" --------------------------------------------\n\n"); printf (" --------------------------------------------\n\n");
@ -720,6 +726,13 @@ static int setOption (int optkey, char *optargument, n2n_tuntap_priv_config_t *e
break; break;
} }
case '[': /* round-trip-time-based supernode selection strategy */ {
// overwrites the default load-based strategy
conf->sn_selection_strategy = SN_SELECTION_STRATEGY_RTT;
break;
}
case 'h': /* quick reference */ { case 'h': /* quick reference */ {
return 2; return 2;
} }
@ -771,8 +784,9 @@ static const struct option long_options[] =
{ "tap-device", required_argument, NULL, 'd' }, { "tap-device", required_argument, NULL, 'd' },
{ "euid", required_argument, NULL, 'u' }, { "euid", required_argument, NULL, 'u' },
{ "egid", required_argument, NULL, 'g' }, { "egid", required_argument, NULL, 'g' },
{ "help" , no_argument, NULL, '@' }, /* special character '@' to identify long help case */
{ "verbose", no_argument, NULL, 'v' }, { "verbose", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, '@' }, /* internal special character '@' to identify long help case */
{ "select-rtt", no_argument, NULL, '[' }, /* '[' rtt selection strategy */
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };

View File

@ -134,7 +134,7 @@ static void mgmt_supernodes (n2n_edge_t *eee, char *udp_buf, const struct sockad
(peer == eee->curr_sn) ? (eee->sn_wait ? 2 : 1 ) : 0, (peer == eee->curr_sn) ? (eee->sn_wait ? 2 : 1 ) : 0,
is_null_mac(peer->mac_addr) ? "" : macaddr_str(mac_buf, peer->mac_addr), is_null_mac(peer->mac_addr) ? "" : macaddr_str(mac_buf, peer->mac_addr),
sock_to_cstr(sockbuf, &(peer->sock)), sock_to_cstr(sockbuf, &(peer->sock)),
sn_selection_criterion_str(sel_buf, peer), sn_selection_criterion_str(eee, sel_buf, peer),
peer->last_seen, peer->last_seen,
peer->uptime); peer->uptime);

View File

@ -1982,7 +1982,7 @@ static void readFromMgmtSocket (n2n_edge_t *eee) {
(peer == eee->curr_sn) ? (eee->sn_wait ? "." : "*" ) : "", (peer == eee->curr_sn) ? (eee->sn_wait ? "." : "*" ) : "",
is_null_mac(peer->mac_addr) ? "" : macaddr_str(mac_buf, peer->mac_addr), is_null_mac(peer->mac_addr) ? "" : macaddr_str(mac_buf, peer->mac_addr),
sock_to_cstr(sockbuf, &(peer->sock)), sock_to_cstr(sockbuf, &(peer->sock)),
sn_selection_criterion_str(sel_buf, peer), sn_selection_criterion_str(eee, sel_buf, peer),
(peer->last_seen) ? time_buf : "", (peer->last_seen) ? time_buf : "",
(peer->uptime) ? uptime_buf : ""); (peer->uptime) ? uptime_buf : "");
@ -2827,7 +2827,8 @@ void process_udp (n2n_edge_t *eee, const struct sockaddr_in *sender_sock, const
scan->uptime = pi.uptime; scan->uptime = pi.uptime;
memcpy(scan->version, pi.version, sizeof(n2n_version_t)); memcpy(scan->version, pi.version, sizeof(n2n_version_t));
/* The data type depends on the actual selection strategy that has been chosen. */ /* The data type depends on the actual selection strategy that has been chosen. */
sn_selection_criterion_calculate(eee, scan, &pi.data); SN_SELECTION_CRITERION_DATA_TYPE sn_sel_tmp = pi.load;
sn_selection_criterion_calculate(eee, scan, &sn_sel_tmp);
traceEvent(TRACE_INFO, "Rx PONG from supernode %s", traceEvent(TRACE_INFO, "Rx PONG from supernode %s",
macaddr_str(mac_buf1, pi.srcMac)); macaddr_str(mac_buf1, pi.srcMac));
@ -3700,6 +3701,7 @@ void edge_init_conf_defaults (n2n_edge_conf_t *conf) {
generate_private_key(*(conf->shared_secret), getenv("N2N_PASSWORD")); generate_private_key(*(conf->shared_secret), getenv("N2N_PASSWORD"));
} }
conf->sn_selection_strategy = SN_SELECTION_STRATEGY_LOAD;
conf->metric = 0; conf->metric = 0;
} }

View File

@ -38,7 +38,7 @@ int sn_selection_criterion_init (peer_info_t *peer) {
/* Set selection_criterion field to default value according to selected strategy. */ /* Set selection_criterion field to default value according to selected strategy. */
int sn_selection_criterion_default (SN_SELECTION_CRITERION_DATA_TYPE *selection_criterion) { int sn_selection_criterion_default (SN_SELECTION_CRITERION_DATA_TYPE *selection_criterion) {
*selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE) (UINT32_MAX >> 1) - 1; *selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE)(UINT64_MAX >> 1) - 1;
return 0; /* OK */ return 0; /* OK */
} }
@ -47,7 +47,7 @@ int sn_selection_criterion_default (SN_SELECTION_CRITERION_DATA_TYPE *selection_
/* Set selection_criterion field to 'bad' value (worse than default) according to selected strategy. */ /* Set selection_criterion field to 'bad' value (worse than default) according to selected strategy. */
int sn_selection_criterion_bad (SN_SELECTION_CRITERION_DATA_TYPE *selection_criterion) { int sn_selection_criterion_bad (SN_SELECTION_CRITERION_DATA_TYPE *selection_criterion) {
*selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE) (UINT32_MAX >> 1); *selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE)(UINT64_MAX >> 1);
return 0; /* OK */ return 0; /* OK */
} }
@ -55,7 +55,7 @@ int sn_selection_criterion_bad (SN_SELECTION_CRITERION_DATA_TYPE *selection_crit
/* Set selection_criterion field to 'good' value (better than default) according to selected strategy. */ /* Set selection_criterion field to 'good' value (better than default) according to selected strategy. */
int sn_selection_criterion_good (SN_SELECTION_CRITERION_DATA_TYPE *selection_criterion) { int sn_selection_criterion_good (SN_SELECTION_CRITERION_DATA_TYPE *selection_criterion) {
*selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE) (UINT32_MAX >> 1) - 2; *selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE)(UINT64_MAX >> 1) - 2;
return 0; /* OK */ return 0; /* OK */
} }
@ -71,7 +71,9 @@ int sn_selection_criterion_calculate (n2n_edge_t *eee, peer_info_t *peer, SN_SEL
common_data = sn_selection_criterion_common_read(eee); common_data = sn_selection_criterion_common_read(eee);
#ifndef SN_SELECTION_RTT switch(eee->conf.sn_selection_strategy) {
case SN_SELECTION_STRATEGY_LOAD: {
peer->selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE)(be32toh(*data) + common_data); peer->selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE)(be32toh(*data) + common_data);
/* Mitigation of the real supernode load in order to see less oscillations. /* Mitigation of the real supernode load in order to see less oscillations.
@ -82,9 +84,20 @@ int sn_selection_criterion_calculate (n2n_edge_t *eee, peer_info_t *peer, SN_SEL
sum = HASH_COUNT(eee->known_peers) + HASH_COUNT(eee->pending_peers); sum = HASH_COUNT(eee->known_peers) + HASH_COUNT(eee->pending_peers);
peer->selection_criterion = peer->selection_criterion * sum / (sum + 1); peer->selection_criterion = peer->selection_criterion * sum / (sum + 1);
} }
#else break;
}
case SN_SELECTION_STRATEGY_RTT: {
peer->selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE)(time_stamp() >> 22) - common_data; peer->selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE)(time_stamp() >> 22) - common_data;
#endif break;
}
default: {
// this should never happen
traceEvent(TRACE_ERROR, "selection_criterion unknown selection strategy configuration");
break;
}
}
return 0; /* OK */ return 0; /* OK */
} }
@ -93,7 +106,9 @@ int sn_selection_criterion_calculate (n2n_edge_t *eee, peer_info_t *peer, SN_SEL
/* Set sn_selection_criterion_common_data field to default value. */ /* Set sn_selection_criterion_common_data field to default value. */
int sn_selection_criterion_common_data_default (n2n_edge_t *eee) { int sn_selection_criterion_common_data_default (n2n_edge_t *eee) {
#ifndef SN_SELECTION_RTT switch(eee->conf.sn_selection_strategy) {
case SN_SELECTION_STRATEGY_LOAD: {
SN_SELECTION_CRITERION_DATA_TYPE tmp = 0; SN_SELECTION_CRITERION_DATA_TYPE tmp = 0;
tmp = HASH_COUNT(eee->pending_peers); tmp = HASH_COUNT(eee->pending_peers);
@ -101,9 +116,20 @@ int sn_selection_criterion_common_data_default (n2n_edge_t *eee) {
tmp *= 2; tmp *= 2;
} }
eee->sn_selection_criterion_common_data = tmp / HASH_COUNT(eee->conf.supernodes); eee->sn_selection_criterion_common_data = tmp / HASH_COUNT(eee->conf.supernodes);
#else break;
}
case SN_SELECTION_STRATEGY_RTT: {
eee->sn_selection_criterion_common_data = (SN_SELECTION_CRITERION_DATA_TYPE)(time_stamp() >> 22); eee->sn_selection_criterion_common_data = (SN_SELECTION_CRITERION_DATA_TYPE)(time_stamp() >> 22);
#endif break;
}
default: {
// this should never happen
traceEvent(TRACE_ERROR, "selection_criterion unknown selection strategy configuration");
break;
}
}
return 0; /* OK */ return 0; /* OK */
} }
@ -134,7 +160,7 @@ int sn_selection_sort (peer_info_t **peer_list) {
/* Function that gathers requested data on a supernode. /* Function that gathers requested data on a supernode.
* it remains unaffected by SN_SELECT_RTT macro because it refers to edge behaviour only * it remains unaffected by selection strategy because it refers to edge behaviour only
*/ */
SN_SELECTION_CRITERION_DATA_TYPE sn_selection_criterion_gather_data (n2n_sn_t *sss) { SN_SELECTION_CRITERION_DATA_TYPE sn_selection_criterion_gather_data (n2n_sn_t *sss) {
@ -156,7 +182,10 @@ SN_SELECTION_CRITERION_DATA_TYPE sn_selection_criterion_gather_data (n2n_sn_t *s
/* Convert selection_criterion field in a string for management port output. */ /* 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) { extern char * sn_selection_criterion_str (n2n_edge_t *eee, selection_criterion_str_t out, peer_info_t *peer) {
int chars = 0;
if(NULL == out) { if(NULL == out) {
return NULL; return NULL;
@ -168,15 +197,28 @@ extern char * sn_selection_criterion_str (selection_criterion_str_t out, peer_in
// Alternatively, typecast to (int16_t) and check for greater or equal zero // Alternatively, typecast to (int16_t) and check for greater or equal zero
if(peer->selection_criterion < (UINT32_MAX >> 2)) { if(peer->selection_criterion < (UINT32_MAX >> 2)) {
#ifndef SN_SELECTION_RTT switch(eee->conf.sn_selection_strategy) {
int chars = snprintf(out, SN_SELECTION_CRITERION_BUF_SIZE, "load = %8d", peer->selection_criterion);
#else
int chars = snprintf(out, SN_SELECTION_CRITERION_BUF_SIZE, "rtt = %6d ms", peer->selection_criterion);
#endif
/* this test is to make "-Wformat-truncation" less sad */ case SN_SELECTION_STRATEGY_LOAD: {
chars = snprintf(out, SN_SELECTION_CRITERION_BUF_SIZE, "load = %8ld", peer->selection_criterion);
break;
}
case SN_SELECTION_STRATEGY_RTT: {
chars = snprintf(out, SN_SELECTION_CRITERION_BUF_SIZE, "rtt = %6ld ms", peer->selection_criterion);
break;
}
default: {
// this should never happen
traceEvent(TRACE_ERROR, "selection_criterion unknown selection strategy configuration");
break;
}
}
// this test is to make "-Wformat-truncation" less sad
if(chars > SN_SELECTION_CRITERION_BUF_SIZE) { if(chars > SN_SELECTION_CRITERION_BUF_SIZE) {
traceEvent(TRACE_INFO, "selection_criterion buffer overflow"); traceEvent(TRACE_ERROR, "selection_criterion buffer overflow");
} }
} }

View File

@ -2549,7 +2549,7 @@ static int process_udp (n2n_sn_t * sss,
pi.sock.family = AF_INET; pi.sock.family = AF_INET;
pi.sock.port = ntohs(sender_sock->sin_port); pi.sock.port = ntohs(sender_sock->sin_port);
memcpy(pi.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE); memcpy(pi.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE);
pi.data = sn_selection_criterion_gather_data(sss); pi.load = sn_selection_criterion_gather_data(sss);
snprintf(pi.version, sizeof(pi.version), "%s", sss->version); snprintf(pi.version, sizeof(pi.version), "%s", sss->version);
pi.uptime = now - sss->start_time; pi.uptime = now - sss->start_time;

View File

@ -663,7 +663,7 @@ int encode_PEER_INFO (uint8_t *base,
if(cmn->flags & N2N_FLAGS_SOCKET) { if(cmn->flags & N2N_FLAGS_SOCKET) {
retval += encode_sock(base, idx, &pkt->preferred_sock); retval += encode_sock(base, idx, &pkt->preferred_sock);
} }
retval += encode_buf(base, idx, &pkt->data, sizeof(SN_SELECTION_CRITERION_DATA_TYPE)); retval += encode_uint32(base, idx, (uint32_t)pkt->load);
retval += encode_uint32(base, idx, (uint32_t)pkt->uptime); retval += encode_uint32(base, idx, (uint32_t)pkt->uptime);
retval += encode_buf(base, idx, pkt->version, sizeof(n2n_version_t)); retval += encode_buf(base, idx, pkt->version, sizeof(n2n_version_t));
@ -687,7 +687,7 @@ int decode_PEER_INFO (n2n_PEER_INFO_t *pkt,
if(cmn->flags & N2N_FLAGS_SOCKET) { if(cmn->flags & N2N_FLAGS_SOCKET) {
retval += decode_sock(&pkt->preferred_sock, base, rem, idx); retval += decode_sock(&pkt->preferred_sock, base, rem, idx);
} }
retval += decode_buf((uint8_t*)&pkt->data, sizeof(SN_SELECTION_CRITERION_DATA_TYPE), base, rem, idx); retval += decode_uint32(&pkt->load, base, rem, idx);
retval += decode_uint32((uint32_t*)&pkt->uptime, base, rem, idx); retval += decode_uint32((uint32_t*)&pkt->uptime, base, rem, idx);
retval += decode_buf((uint8_t*)pkt->version, sizeof(n2n_version_t), base, rem, idx); retval += decode_buf((uint8_t*)pkt->version, sizeof(n2n_version_t), base, rem, idx);