mirror of
https://github.com/ntop/n2n.git
synced 2024-09-19 16:41:11 +02:00
added more targeted routing for packet forwarding in federation (#643)
This commit is contained in:
parent
683dbc5106
commit
c8fabbdfe0
|
@ -671,18 +671,28 @@ typedef struct sn_stats {
|
||||||
time_t last_reg_super; /* Time when last REGISTER_SUPER was received. */
|
time_t last_reg_super; /* Time when last REGISTER_SUPER was received. */
|
||||||
} sn_stats_t;
|
} sn_stats_t;
|
||||||
|
|
||||||
struct sn_community {
|
typedef struct node_supernode_association {
|
||||||
char community[N2N_COMMUNITY_SIZE];
|
|
||||||
uint8_t is_federation; /* if not-zero, then the current community is the federation of supernodes */
|
n2n_mac_t mac; /* mac address of an edge */
|
||||||
uint8_t purgeable; /* indicates purgeable community (fixed-name, predetermined (-c parameter) communties usually are unpurgeable) */
|
const struct sockaddr_in sock; /* network order socket of that edge's supernode */
|
||||||
uint8_t header_encryption; /* Header encryption indicator. */
|
time_t last_seen; /* time mark to keep track of purging requirements */
|
||||||
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. */
|
|
||||||
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. */
|
|
||||||
|
|
||||||
UT_hash_handle hh; /* makes this structure hashable */
|
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 */
|
||||||
|
uint8_t purgeable; /* indicates purgeable community (fixed-name, predetermined (-c parameter) communties usually are unpurgeable) */
|
||||||
|
uint8_t header_encryption; /* Header encryption indicator. */
|
||||||
|
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. */
|
||||||
|
|
||||||
|
UT_hash_handle hh; /* makes this structure hashable */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Typedef'd pointer to get abstract datatype. */
|
/* Typedef'd pointer to get abstract datatype. */
|
||||||
|
|
193
src/sn_utils.c
193
src/sn_utils.c
|
@ -20,13 +20,6 @@
|
||||||
|
|
||||||
#define HASH_FIND_COMMUNITY(head, name, out) HASH_FIND_STR(head, name, out)
|
#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,
|
static ssize_t sendto_peer (n2n_sn_t *sss,
|
||||||
const struct peer_info *peer,
|
const struct peer_info *peer,
|
||||||
|
@ -38,14 +31,6 @@ static int sendto_mgmt (n2n_sn_t *sss,
|
||||||
const uint8_t *mgmt_buf,
|
const uint8_t *mgmt_buf,
|
||||||
size_t mgmt_size);
|
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 uint16_t reg_lifetime (n2n_sn_t *sss);
|
||||||
|
|
||||||
static int update_edge (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) {
|
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");
|
traceEvent(TRACE_DEBUG, "try_broadcast");
|
||||||
|
|
||||||
/* We have to make sure that a broadcast reaches the other supernodes and edges
|
/* 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
|
* 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
|
* do forward to edges of community only. If unset, forward to all locally known
|
||||||
* nodes and all supernodes */
|
* nodes of community AND all supernodes associated with the community */
|
||||||
|
|
||||||
if (!from_supernode) {
|
if (!from_supernode) {
|
||||||
HASH_ITER(hh, sss->federation->edges, scan, tmp) {
|
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 **/
|
/** Initialise some fields of the community structure **/
|
||||||
int comm_init (struct sn_community *comm, char *cmn) {
|
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 *community, *tmp;
|
||||||
struct sn_community_regular_expression *re, *tmp_re;
|
struct sn_community_regular_expression *re, *tmp_re;
|
||||||
n2n_tcp_connection_t *conn, *tmp_conn;
|
n2n_tcp_connection_t *conn, *tmp_conn;
|
||||||
|
node_supernode_association_t *assoc, *tmp_assoc;
|
||||||
|
|
||||||
if(sss->sock >= 0) {
|
if(sss->sock >= 0) {
|
||||||
closesocket(sss->sock);
|
closesocket(sss->sock);
|
||||||
|
@ -465,6 +462,11 @@ void sn_term (n2n_sn_t *sss) {
|
||||||
if(NULL != community->header_encryption_ctx) {
|
if(NULL != community->header_encryption_ctx) {
|
||||||
free(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);
|
HASH_DEL(sss->communities, community);
|
||||||
free(community);
|
free(community);
|
||||||
}
|
}
|
||||||
|
@ -484,6 +486,29 @@ void sn_term (n2n_sn_t *sss) {
|
||||||
#endif
|
#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.
|
/** 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* p_last_purge,
|
||||||
time_t now) {
|
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_reg = 0;
|
||||||
|
size_t num_assoc = 0;
|
||||||
|
|
||||||
if((now - (*p_last_purge)) < PURGE_REGISTRATION_FREQUENCY) {
|
if((now - (*p_last_purge)) < PURGE_REGISTRATION_FREQUENCY) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -912,25 +939,42 @@ static int purge_expired_communities (n2n_sn_t *sss,
|
||||||
|
|
||||||
traceEvent(TRACE_DEBUG, "Purging old communities and edges");
|
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()
|
// federation is taken care of in re_register_and_purge_supernodes()
|
||||||
if(comm->is_federation == IS_FEDERATION)
|
if(comm->is_federation == IS_FEDERATION)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// purge the community's local peers
|
||||||
num_reg += purge_peer_list(&comm->edges, sss->sock, sss->tcp_connections, now - REGISTRATION_TIMEOUT);
|
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)) {
|
if((comm->edges == NULL) && (comm->purgeable == COMMUNITY_PURGEABLE)) {
|
||||||
traceEvent(TRACE_INFO, "Purging idle community %s", comm->community);
|
traceEvent(TRACE_INFO, "Purging idle community %s", comm->community);
|
||||||
if(NULL != comm->header_encryption_ctx) {
|
if(NULL != comm->header_encryption_ctx) {
|
||||||
/* this should not happen as 'purgeable' and thus only communities w/o encrypted header here */
|
/* this should not happen as 'purgeable' and thus only communities w/o encrypted header here */
|
||||||
free(comm->header_encryption_ctx);
|
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);
|
HASH_DEL(sss->communities, comm);
|
||||||
free(comm);
|
free(comm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(*p_last_purge) = now;
|
(*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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1405,6 +1449,7 @@ static int process_udp (n2n_sn_t * sss,
|
||||||
struct sn_community *fed;
|
struct sn_community *fed;
|
||||||
struct sn_community_regular_expression *re, *tmp_re;
|
struct sn_community_regular_expression *re, *tmp_re;
|
||||||
struct peer_info *peer, *tmp_peer, *p;
|
struct peer_info *peer, *tmp_peer, *p;
|
||||||
|
node_supernode_association_t *assoc;
|
||||||
int8_t allowed_match = -1;
|
int8_t allowed_match = -1;
|
||||||
uint8_t match = 0;
|
uint8_t match = 0;
|
||||||
int match_length = 0;
|
int match_length = 0;
|
||||||
|
@ -1565,7 +1610,6 @@ static int process_udp (n2n_sn_t * sss,
|
||||||
if(!(cmn.flags & N2N_FLAGS_SOCKET)) {
|
if(!(cmn.flags & N2N_FLAGS_SOCKET)) {
|
||||||
// ... forward to all other supernodes (note try_broadcast()'s behavior with
|
// ... forward to all other supernodes (note try_broadcast()'s behavior with
|
||||||
// NULL comm and from_supernode parameter)
|
// NULL comm and from_supernode parameter)
|
||||||
|
|
||||||
// exception: do not forward auto ip draw
|
// exception: do not forward auto ip draw
|
||||||
if(!is_null_mac(reg.edgeMac)) {
|
if(!is_null_mac(reg.edgeMac)) {
|
||||||
reg.sock.family = AF_INET;
|
reg.sock.family = AF_INET;
|
||||||
|
@ -1602,8 +1646,10 @@ static int process_udp (n2n_sn_t * sss,
|
||||||
macaddr_str(mac_buf, reg.edgeMac),
|
macaddr_str(mac_buf, reg.edgeMac),
|
||||||
sock_to_cstr(sockbuf, &(ack.sock)));
|
sock_to_cstr(sockbuf, &(ack.sock)));
|
||||||
} else {
|
} else {
|
||||||
// this is an edge with valid authentication registering with another supernode
|
// this is an edge with valid authentication registering with another supernode, so ...
|
||||||
// so we can delete it here if present (can happen)
|
// 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);
|
HASH_FIND_PEER(comm->edges, reg.edgeMac, peer);
|
||||||
if(peer != NULL) {
|
if(peer != NULL) {
|
||||||
if((peer->socket_fd != sss->sock) && (peer->socket_fd >= 0)) {
|
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);
|
HASH_FIND_PEER(comm->edges, pi.srcMac, peer);
|
||||||
if(peer != NULL) {
|
if(peer != NULL) {
|
||||||
if((comm->is_federation == IS_NO_FEDERATION) && (!is_null_mac(pi.srcMac))) {
|
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,
|
// 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)
|
// i.e. re-assemble (memcpy of udpbuf to encbuf could be sufficient as well)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user