added more targeted routing for packet forwarding in federation (#643)

This commit is contained in:
Logan oos Even 2021-02-23 18:51:14 +05:45 committed by GitHub
parent 683dbc5106
commit c8fabbdfe0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 141 additions and 82 deletions

View File

@ -671,6 +671,15 @@ typedef struct sn_stats {
time_t last_reg_super; /* Time when last REGISTER_SUPER was received. */
} sn_stats_t;
typedef struct node_supernode_association {
n2n_mac_t mac; /* mac address of an edge */
const struct sockaddr_in sock; /* network order socket of that edge's supernode */
time_t last_seen; /* time mark to keep track of purging requirements */
UT_hash_handle hh; /* makes this structure hashable */
} node_supernode_association_t;
struct sn_community {
char community[N2N_COMMUNITY_SIZE];
uint8_t is_federation; /* if not-zero, then the current community is the federation of supernodes */
@ -679,6 +688,7 @@ struct sn_community {
he_context_t *header_encryption_ctx; /* Header encryption cipher context. */
he_context_t *header_iv_ctx; /* Header IV ecnryption cipher context, REMOVE as soon as seperate fields for checksum and replay protection available */
struct peer_info *edges; /* Link list of registered edges. */
node_supernode_association_t *assoc; /* list of other edges from this community and their supernodes */
int64_t number_enc_packets; /* Number of encrypted packets handled so far, required for sorting from time to time */
n2n_ip_subnet_t auto_ip_net; /* Address range of auto ip address service. */

View File

@ -20,13 +20,6 @@
#define HASH_FIND_COMMUNITY(head, name, out) HASH_FIND_STR(head, name, out)
static int try_forward (n2n_sn_t * sss,
const struct sn_community *comm,
const n2n_common_t * cmn,
const n2n_mac_t dstMac,
uint8_t from_supernode,
const uint8_t * pktbuf,
size_t pktsize);
static ssize_t sendto_peer (n2n_sn_t *sss,
const struct peer_info *peer,
@ -38,14 +31,6 @@ static int sendto_mgmt (n2n_sn_t *sss,
const uint8_t *mgmt_buf,
size_t mgmt_size);
static int try_broadcast (n2n_sn_t * sss,
const struct sn_community *comm,
const n2n_common_t * cmn,
const n2n_mac_t srcMac,
uint8_t from_supernode,
const uint8_t * pktbuf,
size_t pktsize);
static uint16_t reg_lifetime (n2n_sn_t *sss);
static int update_edge (n2n_sn_t *sss,
@ -81,54 +66,6 @@ static int process_udp (n2n_sn_t *sss,
/* ************************************** */
static int try_forward (n2n_sn_t * sss,
const struct sn_community *comm,
const n2n_common_t * cmn,
const n2n_mac_t dstMac,
uint8_t from_supernode,
const uint8_t * pktbuf,
size_t pktsize) {
struct peer_info * scan;
macstr_t mac_buf;
n2n_sock_str_t sockbuf;
HASH_FIND_PEER(comm->edges, dstMac, scan);
if(NULL != scan) {
int data_sent_len;
data_sent_len = sendto_peer(sss, scan, pktbuf, pktsize);
if(data_sent_len == pktsize) {
++(sss->stats.fwd);
traceEvent(TRACE_DEBUG, "unicast %lu to [%s] %s",
pktsize,
sock_to_cstr(sockbuf, &(scan->sock)),
macaddr_str(mac_buf, scan->mac_addr));
} else {
++(sss->stats.errors);
traceEvent(TRACE_ERROR, "unicast %lu to [%s] %s FAILED (%d: %s)",
pktsize,
sock_to_cstr(sockbuf, &(scan->sock)),
macaddr_str(mac_buf, scan->mac_addr),
errno, strerror(errno));
return -1;
}
} else {
if(!from_supernode) {
/* Forwarding packet to all federated supernodes. */
traceEvent(TRACE_DEBUG, "Unknown MAC. Broadcasting packet to all federated supernodes.");
try_broadcast(sss, NULL, cmn, sss->mac_addr, from_supernode, pktbuf, pktsize);
} else {
traceEvent(TRACE_DEBUG, "try_forward unknown MAC. Dropping the packet.");
/* Not a known MAC so drop. */
return -2;
}
}
return 0;
}
static void close_tcp_connection(n2n_sn_t *sss, n2n_tcp_connection_t *conn) {
@ -299,9 +236,9 @@ static int try_broadcast (n2n_sn_t * sss,
traceEvent(TRACE_DEBUG, "try_broadcast");
/* We have to make sure that a broadcast reaches the other supernodes and edges
* connected to them. try_broadcast needs a from_supernode parameter: if set
* do forward to edges of community only. If unset. forward to all locally known
* nodes and all supernodes */
* connected to them. try_broadcast needs a from_supernode parameter: if set,
* do forward to edges of community only. If unset, forward to all locally known
* nodes of community AND all supernodes associated with the community */
if (!from_supernode) {
HASH_ITER(hh, sss->federation->edges, scan, tmp) {
@ -356,6 +293,65 @@ static int try_broadcast (n2n_sn_t * sss,
}
static int try_forward (n2n_sn_t * sss,
const struct sn_community *comm,
const n2n_common_t * cmn,
const n2n_mac_t dstMac,
uint8_t from_supernode,
const uint8_t * pktbuf,
size_t pktsize) {
struct peer_info * scan;
node_supernode_association_t *assoc;
macstr_t mac_buf;
n2n_sock_str_t sockbuf;
HASH_FIND_PEER(comm->edges, dstMac, scan);
if(NULL != scan) {
int data_sent_len;
data_sent_len = sendto_peer(sss, scan, pktbuf, pktsize);
if(data_sent_len == pktsize) {
++(sss->stats.fwd);
traceEvent(TRACE_DEBUG, "unicast %lu to [%s] %s",
pktsize,
sock_to_cstr(sockbuf, &(scan->sock)),
macaddr_str(mac_buf, scan->mac_addr));
} else {
++(sss->stats.errors);
traceEvent(TRACE_ERROR, "unicast %lu to [%s] %s FAILED (%d: %s)",
pktsize,
sock_to_cstr(sockbuf, &(scan->sock)),
macaddr_str(mac_buf, scan->mac_addr),
errno, strerror(errno));
return -1;
}
} else {
if(!from_supernode) {
// check if target edge is associated with a certain supernode
HASH_FIND(hh, comm->assoc, dstMac, sizeof(n2n_mac_t), assoc);
if(assoc) {
traceEvent(TRACE_DEBUG, "try_forward found mac address associated with a known supernode, forwarding packet to that supernode");
sendto_sock(sss, sss->sock,
(const struct sockaddr*)&(assoc->sock),
pktbuf, pktsize);
} else {
// forwarding packet to all federated supernodes
traceEvent(TRACE_DEBUG, "try_forward sees unknown mac address, broadcasting packet to all federated supernodes");
try_broadcast(sss, NULL, cmn, sss->mac_addr, from_supernode, pktbuf, pktsize);
}
} else {
traceEvent(TRACE_DEBUG, "try_forward sees unknown mac address in packet from a supernode, dropping the packet");
/* Not a known MAC so drop. */
return -2;
}
}
return 0;
}
/** Initialise some fields of the community structure **/
int comm_init (struct sn_community *comm, char *cmn) {
@ -435,6 +431,7 @@ void sn_term (n2n_sn_t *sss) {
struct sn_community *community, *tmp;
struct sn_community_regular_expression *re, *tmp_re;
n2n_tcp_connection_t *conn, *tmp_conn;
node_supernode_association_t *assoc, *tmp_assoc;
if(sss->sock >= 0) {
closesocket(sss->sock);
@ -465,6 +462,11 @@ void sn_term (n2n_sn_t *sss) {
if(NULL != community->header_encryption_ctx) {
free(community->header_encryption_ctx);
}
// remove all associations
HASH_ITER(hh, community->assoc, assoc, tmp_assoc) {
HASH_DEL(community->assoc, assoc);
free(assoc);
}
HASH_DEL(sss->communities, community);
free(community);
}
@ -484,6 +486,29 @@ void sn_term (n2n_sn_t *sss) {
#endif
}
void update_node_supernode_association (struct sn_community *comm,
n2n_mac_t *edgeMac, const struct sockaddr_in *sender_sock,
time_t now) {
node_supernode_association_t *assoc;
HASH_FIND(hh, comm->assoc, edgeMac, sizeof(n2n_mac_t), assoc);
if(!assoc) {
// create a new association
assoc = (node_supernode_association_t*)malloc(sizeof(node_supernode_association_t));
if(assoc) {
memcpy(&(assoc->mac), edgeMac, sizeof(n2n_mac_t));
memcpy((struct sockaddr_in*)&(assoc->sock), sender_sock, sizeof(struct sockaddr_in));
assoc->last_seen = now;
HASH_ADD(hh, comm->assoc, mac, sizeof(n2n_mac_t), assoc);
} else {
// already there, update socket and time only
memcpy((struct sockaddr_in*)&(assoc->sock), sender_sock, sizeof(struct sockaddr_in));
assoc->last_seen = now;
}
}
}
/** Determine the appropriate lifetime for new registrations.
*
@ -903,8 +928,10 @@ static int purge_expired_communities (n2n_sn_t *sss,
time_t* p_last_purge,
time_t now) {
struct sn_community *comm, *tmp;
struct sn_community *comm, *tmp_comm;
node_supernode_association_t *assoc, *tmp_assoc;
size_t num_reg = 0;
size_t num_assoc = 0;
if((now - (*p_last_purge)) < PURGE_REGISTRATION_FREQUENCY) {
return 0;
@ -912,25 +939,42 @@ static int purge_expired_communities (n2n_sn_t *sss,
traceEvent(TRACE_DEBUG, "Purging old communities and edges");
HASH_ITER(hh, sss->communities, comm, tmp) {
HASH_ITER(hh, sss->communities, comm, tmp_comm) {
// federation is taken care of in re_register_and_purge_supernodes()
if(comm->is_federation == IS_FEDERATION)
continue;
// purge the community's local peers
num_reg += purge_peer_list(&comm->edges, sss->sock, sss->tcp_connections, now - REGISTRATION_TIMEOUT);
// purge the community's associated peers (connected to other supernodes)
HASH_ITER(hh, comm->assoc, assoc, tmp_assoc) {
if(comm->assoc->last_seen < (now - 3 * REGISTRATION_TIMEOUT)) {
HASH_DEL(comm->assoc, assoc);
free(assoc);
num_assoc++;
}
}
if((comm->edges == NULL) && (comm->purgeable == COMMUNITY_PURGEABLE)) {
traceEvent(TRACE_INFO, "Purging idle community %s", comm->community);
if(NULL != comm->header_encryption_ctx) {
/* this should not happen as 'purgeable' and thus only communities w/o encrypted header here */
free(comm->header_encryption_ctx);
}
// remove all associations
HASH_ITER(hh, comm->assoc, assoc, tmp_assoc) {
HASH_DEL(comm->assoc, assoc);
free(assoc);
}
HASH_DEL(sss->communities, comm);
free(comm);
}
}
(*p_last_purge) = now;
traceEvent(TRACE_DEBUG, "Remove %ld edges", num_reg);
traceEvent(TRACE_DEBUG, "purge_expired_communities removed %ld locally registered edges and %ld remotely associated edges",
num_reg, num_assoc);
return 0;
}
@ -1405,6 +1449,7 @@ static int process_udp (n2n_sn_t * sss,
struct sn_community *fed;
struct sn_community_regular_expression *re, *tmp_re;
struct peer_info *peer, *tmp_peer, *p;
node_supernode_association_t *assoc;
int8_t allowed_match = -1;
uint8_t match = 0;
int match_length = 0;
@ -1565,7 +1610,6 @@ static int process_udp (n2n_sn_t * sss,
if(!(cmn.flags & N2N_FLAGS_SOCKET)) {
// ... forward to all other supernodes (note try_broadcast()'s behavior with
// NULL comm and from_supernode parameter)
// exception: do not forward auto ip draw
if(!is_null_mac(reg.edgeMac)) {
reg.sock.family = AF_INET;
@ -1602,8 +1646,10 @@ static int process_udp (n2n_sn_t * sss,
macaddr_str(mac_buf, reg.edgeMac),
sock_to_cstr(sockbuf, &(ack.sock)));
} else {
// this is an edge with valid authentication registering with another supernode
// so we can delete it here if present (can happen)
// this is an edge with valid authentication registering with another supernode, so ...
// 1- ... associate it with that other supernode
update_node_supernode_association(comm, &(reg.edgeMac), sender_sock, now);
// 2- ... we can delete it from regular list if present (can happen)
HASH_FIND_PEER(comm->edges, reg.edgeMac, peer);
if(peer != NULL) {
if((peer->socket_fd != sss->sock) && (peer->socket_fd >= 0)) {
@ -1986,6 +2032,9 @@ static int process_udp (n2n_sn_t * sss,
HASH_FIND_PEER(comm->edges, pi.srcMac, peer);
if(peer != NULL) {
if((comm->is_federation == IS_NO_FEDERATION) && (!is_null_mac(pi.srcMac))) {
// snoop on the information to use for supernode forwarding (do not wait until first remote REGISTER_SUPER)
update_node_supernode_association(comm, &(pi.mac), sender_sock, now);
// this is a PEER_INFO for one of the edges conencted to this supernode, forward,
// i.e. re-assemble (memcpy of udpbuf to encbuf could be sufficient as well)