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