Merge pull request #209 from pojntfx/dev

Enable embedding the supernode, add more embedding examples (clean)
This commit is contained in:
Luca Deri 2020-04-23 23:51:02 +02:00 committed by GitHub
commit a13247f6c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 977 additions and 77 deletions

3
.gitignore vendored
View File

@ -7,7 +7,9 @@ config.*
Makefile
autom4te.cache
edge
example_edge_embed_quick_edge_init
example_edge_embed
example_sn_embed
supernode
tools/n2n-benchmark
tools/n2n-decode
@ -20,3 +22,4 @@ packages/etc/systemd/system/edge-ntopng@.service
packages/etc/systemd/system/edge.service
packages/etc/systemd/system/edge@.service
packages/etc/systemd/system/supernode.service
*dSYM*

View File

@ -54,6 +54,7 @@ endif()
# Add SHARED to build DLL
add_library(n2n n2n.c
edge_utils.c
sn_utils.c
wire.c
minilzo.c
twofish.c
@ -82,6 +83,15 @@ target_link_libraries(edge n2n)
add_executable(supernode sn.c)
target_link_libraries(supernode n2n)
add_executable(example_edge_embed_quick_edge_init example_edge_embed_quick_edge_init.c)
target_link_libraries(example_edge_embed_quick_edge_init n2n)
add_executable(example_edge_embed example_edge_embed.c)
target_link_libraries(example_edge_embed n2n)
add_executable(example_sn_embed example_sn_embed.c)
target_link_libraries(example_sn_embed n2n)
install(TARGETS edge supernode
RUNTIME DESTINATION sbin
LIBRARY DESTINATION lib

View File

@ -47,7 +47,7 @@ MAN8DIR=$(MANDIR)/man8
N2N_LIB=libn2n.a
N2N_OBJS=n2n.o wire.o minilzo.o twofish.o \
edge_utils.o \
edge_utils.o sn_utils.o \
transform_null.o transform_tf.o transform_aes.o \
tuntap_freebsd.o tuntap_netbsd.o tuntap_linux.o \
tuntap_osx.o
@ -62,7 +62,9 @@ endif
APPS=edge
APPS+=supernode
APPS+=example_edge_embed_quick_edge_init
APPS+=example_edge_embed
APPS+=example_sn_embed
DOCS=edge.8.gz supernode.1.gz n2n.7.gz
@ -78,6 +80,12 @@ edge: edge.c $(N2N_LIB) n2n_wire.h n2n.h Makefile
supernode: sn.c $(N2N_LIB) n2n.h Makefile
$(CC) $(CFLAGS) $< $(N2N_LIB) $(LIBS_SN) -o $@
example_edge_embed_quick_edge_init: example_edge_embed_quick_edge_init.c $(N2N_LIB) n2n.h
$(CC) $(CFLAGS) $< $(N2N_LIB) $(LIBS_EDGE) -o $@
example_sn_embed: example_sn_embed.c $(N2N_LIB) n2n.h
$(CC) $(CFLAGS) $< $(N2N_LIB) $(LIBS_EDGE) -o $@
example_edge_embed: example_edge_embed.c $(N2N_LIB) n2n.h
$(CC) $(CFLAGS) $< $(N2N_LIB) $(LIBS_EDGE) -o $@

View File

@ -1,54 +1,57 @@
/**
* (C) 2007-18 - ntop.org and contributors
*
* 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
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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/>
*
*/
#include "n2n.h"
/*
This tool demonstrates how to easily embed
n2n on an existing application
*/
static int keep_running;
int main(int argc, char* argv[]) {
char *device_name = (char*)"n2n0";
char *network_name = (char*)"mynetwork";
char *secret_key = (char*)"mysecret";
char *my_mac_address = (char*)"DE:AD:BE:EF:01:10";
char *my_ipv4_addr = (char*)"1.2.3.4";
char *supernode = (char*)"7.8.9.10:1234";
int keep_on_running = 1;
/* Increase tracelevel to see what's happening */
setTraceLevel(10);
int main()
{
n2n_edge_conf_t conf;
tuntap_dev tuntap;
n2n_edge_t *eee;
int rc;
/* Random seed */
srand(time(NULL));
edge_init_conf_defaults(&conf);
conf.allow_p2p = 1; // Whether to allow peer-to-peer communication
conf.allow_routing = 1; // Whether to allow the edge to route packets to other edges
snprintf((char *)conf.community_name, sizeof(conf.community_name), "%s", "mycommunity"); // Community to connect to
conf.disable_pmtu_discovery = 1; // Whether to disable the path MTU discovery
conf.drop_multicast = 0; // Whether to disable multicast
conf.dyn_ip_mode = 0; // Whether the IP address is set dynamically (see IP mode; 0 if static, 1 if dynamic)
conf.encrypt_key = "mysecret"; // Secret to decrypt & encrypt with
conf.local_port = 0; // What port to use (0 = any port)
conf.mgmt_port = N2N_EDGE_MGMT_PORT; // Edge management port (5644 by default)
conf.register_interval = 1; // Interval for both UDP NAT hole punching and supernode registration
conf.register_ttl = 1; // Interval for UDP NAT hole punching through supernode
edge_conf_add_supernode(&conf, "localhost:1234"); // Supernode to connect to
conf.tos = 16; // Type of service for sent packets
conf.transop_id = N2N_TRANSFORM_ID_TWOFISH; // Use the twofish encryption
/*
NOTE
if (edge_verify_conf(&conf) != 0)
{
return -1;
}
As the function below won't end, you should
call it inside a separate thread
*/
return(quick_edge_init(device_name,
network_name,
secret_key,
my_mac_address,
my_ipv4_addr,
supernode,
&keep_on_running));
}
if (tuntap_open(&tuntap,
"edge0", // Name of the device to create
"static", // IP mode; static|dhcp
"10.0.0.1", // Set ip address
"255.255.255.0", // Netmask to use
"DE:AD:BE:EF:01:10", // Set mac address
DEFAULT_MTU) < 0) // MTU to use
{
return -1;
}
eee = edge_init(&tuntap, &conf, &rc);
if (eee == NULL)
{
exit(1);
}
keep_running = 1;
rc = run_edge_loop(eee, &keep_running);
edge_term(eee);
tuntap_close(&tuntap);
return rc;
}

View File

@ -0,0 +1,54 @@
/**
* (C) 2007-18 - ntop.org and contributors
*
* 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
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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/>
*
*/
#include "n2n.h"
/*
This tool demonstrates how to easily embed
n2n on an existing application
*/
int main(int argc, char* argv[]) {
char *device_name = (char*)"n2n0";
char *network_name = (char*)"mynetwork";
char *secret_key = (char*)"mysecret";
char *my_mac_address = (char*)"DE:AD:BE:EF:01:10";
char *my_ipv4_addr = (char*)"1.2.3.4";
char *supernode = (char*)"7.8.9.10:1234";
int keep_on_running = 1;
/* Increase tracelevel to see what's happening */
setTraceLevel(10);
/* Random seed */
srand(time(NULL));
/*
NOTE
As the function below won't end, you should
call it inside a separate thread
*/
return(quick_edge_init(device_name,
network_name,
secret_key,
my_mac_address,
my_ipv4_addr,
supernode,
&keep_on_running));
}

32
example_sn_embed.c Normal file
View File

@ -0,0 +1,32 @@
#include "n2n.h"
static int keep_running;
int main()
{
n2n_sn_t sss_node;
int rc;
sn_init(&sss_node);
sss_node.daemon = 0; // Whether to daemonize
sss_node.lport = 1234; // Main UDP listen port
sss_node.sock = open_socket(sss_node.lport, 1);
if (-1 == sss_node.sock)
{
exit(-2);
}
sss_node.mgmt_sock = open_socket(5645, 0); // Main UDP management port
if (-1 == sss_node.mgmt_sock)
{
exit(-2);
}
keep_running = 1;
rc = run_sn_loop(&sss_node, &keep_running);
sn_term(&sss_node);
return rc;
}

34
n2n.h
View File

@ -224,6 +224,37 @@ typedef struct n2n_edge_conf {
typedef struct n2n_edge n2n_edge_t; /* Opaque, see edge_utils.c */
typedef struct sn_stats
{
size_t errors; /* Number of errors encountered. */
size_t reg_super; /* Number of REGISTER_SUPER requests received. */
size_t reg_super_nak; /* Number of REGISTER_SUPER requests declined. */
size_t fwd; /* Number of messages forwarded. */
size_t broadcast; /* Number of messages broadcast to a community. */
time_t last_fwd; /* Time when last message was forwarded. */
time_t last_reg_super; /* Time when last REGISTER_SUPER was received. */
} sn_stats_t;
typedef struct n2n_sn
{
time_t start_time; /* Used to measure uptime. */
sn_stats_t stats;
int daemon; /* If non-zero then daemonise. */
uint16_t lport; /* Local UDP port to bind to. */
int sock; /* Main socket for UDP traffic with edges. */
int mgmt_sock; /* management socket. */
int lock_communities; /* If true, only loaded communities can be used. */
struct sn_community *communities;
} n2n_sn_t;
struct sn_community
{
char community[N2N_COMMUNITY_SIZE];
struct peer_info *edges; /* Link list of registered edges. */
UT_hash_handle hh; /* makes this structure hashable */
};
/* ************************************** */
#ifdef __ANDROID_NDK__
@ -315,5 +346,8 @@ int quick_edge_init(char *device_name, char *community_name,
char *local_ip_address,
char *supernode_ip_address_port,
int *keep_on_running);
int sn_init(n2n_sn_t *sss);
void sn_term(n2n_sn_t *sss);
int run_sn_loop(n2n_sn_t *sss, int *keep_running);
#endif /* _N2N_H_ */

28
sn.c
View File

@ -29,34 +29,6 @@
#define N2N_SN_MGMT_PORT 5645
typedef struct sn_stats {
size_t errors; /* Number of errors encountered. */
size_t reg_super; /* Number of REGISTER_SUPER requests received. */
size_t reg_super_nak; /* Number of REGISTER_SUPER requests declined. */
size_t fwd; /* Number of messages forwarded. */
size_t broadcast; /* Number of messages broadcast to a community. */
time_t last_fwd; /* Time when last message was forwarded. */
time_t last_reg_super; /* Time when last REGISTER_SUPER was received. */
} sn_stats_t;
struct sn_community {
char community[N2N_COMMUNITY_SIZE];
struct peer_info *edges; /* Link list of registered edges. */
UT_hash_handle hh; /* makes this structure hashable */
};
typedef struct n2n_sn {
time_t start_time; /* Used to measure uptime. */
sn_stats_t stats;
int daemon; /* If non-zero then daemonise. */
uint16_t lport; /* Local UDP port to bind to. */
int sock; /* Main socket for UDP traffic with edges. */
int mgmt_sock; /* management socket. */
int lock_communities; /* If true, only loaded communities can be used. */
struct sn_community *communities;
} n2n_sn_t;
#define HASH_FIND_COMMUNITY(head,name,out) HASH_FIND_STR(head,name,out)
static int try_forward(n2n_sn_t * sss,

784
sn_utils.c Normal file
View File

@ -0,0 +1,784 @@
#include "n2n.h"
#define HASH_FIND_COMMUNITY(head, name, out) HASH_FIND_STR(head, name, out)
#define N2N_SN_LPORT_DEFAULT 7654
#define N2N_SN_PKTBUF_SIZE 2048
static int try_forward(n2n_sn_t *sss,
const n2n_common_t *cmn,
const n2n_mac_t dstMac,
const uint8_t *pktbuf,
size_t pktsize);
static ssize_t sendto_sock(n2n_sn_t *sss,
const n2n_sock_t *sock,
const uint8_t *pktbuf,
size_t pktsize);
static int try_broadcast(n2n_sn_t *sss,
const n2n_common_t *cmn,
const n2n_mac_t srcMac,
const uint8_t *pktbuf,
size_t pktsize);
static uint16_t reg_lifetime(n2n_sn_t *sss);
static int update_edge(n2n_sn_t *sss,
const n2n_mac_t edgeMac,
struct sn_community *comm,
const n2n_sock_t *sender_sock,
time_t now);
static int process_mgmt(n2n_sn_t *sss,
const struct sockaddr_in *sender_sock,
const uint8_t *mgmt_buf,
size_t mgmt_size,
time_t now);
static int process_udp(n2n_sn_t *sss,
const struct sockaddr_in *sender_sock,
const uint8_t *udp_buf,
size_t udp_size,
time_t now);
static int try_forward(n2n_sn_t *sss,
const n2n_common_t *cmn,
const n2n_mac_t dstMac,
const uint8_t *pktbuf,
size_t pktsize)
{
struct peer_info *scan;
struct sn_community *community;
macstr_t mac_buf;
n2n_sock_str_t sockbuf;
HASH_FIND_COMMUNITY(sss->communities, (char *)cmn->community, community);
if (!community)
{
traceEvent(TRACE_DEBUG, "try_forward unknown community %s", cmn->community);
return (-1);
}
HASH_FIND_PEER(community->edges, dstMac, scan);
if (NULL != scan)
{
int data_sent_len;
data_sent_len = sendto_sock(sss, &(scan->sock), 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));
}
}
else
{
traceEvent(TRACE_DEBUG, "try_forward unknown MAC");
/* Not a known MAC so drop. */
return (-2);
}
return (0);
}
/** Send a datagram to the destination embodied in a n2n_sock_t.
*
* @return -1 on error otherwise number of bytes sent
*/
static ssize_t sendto_sock(n2n_sn_t *sss,
const n2n_sock_t *sock,
const uint8_t *pktbuf,
size_t pktsize)
{
n2n_sock_str_t sockbuf;
if (AF_INET == sock->family)
{
struct sockaddr_in udpsock;
udpsock.sin_family = AF_INET;
udpsock.sin_port = htons(sock->port);
memcpy(&(udpsock.sin_addr.s_addr), &(sock->addr.v4), IPV4_SIZE);
traceEvent(TRACE_DEBUG, "sendto_sock %lu to [%s]",
pktsize,
sock_to_cstr(sockbuf, sock));
return sendto(sss->sock, pktbuf, pktsize, 0,
(const struct sockaddr *)&udpsock, sizeof(struct sockaddr_in));
}
else
{
/* AF_INET6 not implemented */
errno = EAFNOSUPPORT;
return -1;
}
}
/** Try and broadcast a message to all edges in the community.
*
* This will send the exact same datagram to zero or more edges registered to
* the supernode.
*/
static int try_broadcast(n2n_sn_t *sss,
const n2n_common_t *cmn,
const n2n_mac_t srcMac,
const uint8_t *pktbuf,
size_t pktsize)
{
struct peer_info *scan, *tmp;
struct sn_community *community;
macstr_t mac_buf;
n2n_sock_str_t sockbuf;
traceEvent(TRACE_DEBUG, "try_broadcast");
HASH_FIND_COMMUNITY(sss->communities, (char *)cmn->community, community);
if (community)
{
HASH_ITER(hh, community->edges, scan, tmp)
{
if (memcmp(srcMac, scan->mac_addr, sizeof(n2n_mac_t)) != 0)
{
/* REVISIT: exclude if the destination socket is where the packet came from. */
int data_sent_len;
data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize);
if (data_sent_len != pktsize)
{
++(sss->stats.errors);
traceEvent(TRACE_WARNING, "multicast %lu to [%s] %s failed %s",
pktsize,
sock_to_cstr(sockbuf, &(scan->sock)),
macaddr_str(mac_buf, scan->mac_addr),
strerror(errno));
}
else
{
++(sss->stats.broadcast);
traceEvent(TRACE_DEBUG, "multicast %lu to [%s] %s",
pktsize,
sock_to_cstr(sockbuf, &(scan->sock)),
macaddr_str(mac_buf, scan->mac_addr));
}
}
}
}
else
traceEvent(TRACE_INFO, "ignoring broadcast on unknown community %s\n",
cmn->community);
return 0;
}
/** Initialise the supernode structure */
int sn_init(n2n_sn_t *sss)
{
#ifdef WIN32
initWin32();
#endif
memset(sss, 0, sizeof(n2n_sn_t));
sss->daemon = 1; /* By defult run as a daemon. */
sss->lport = N2N_SN_LPORT_DEFAULT;
sss->sock = -1;
sss->mgmt_sock = -1;
return 0; /* OK */
}
/** Deinitialise the supernode structure and deallocate any memory owned by
* it. */
void sn_term(n2n_sn_t *sss)
{
struct sn_community *community, *tmp;
if (sss->sock >= 0)
{
closesocket(sss->sock);
}
sss->sock = -1;
if (sss->mgmt_sock >= 0)
{
closesocket(sss->mgmt_sock);
}
sss->mgmt_sock = -1;
HASH_ITER(hh, sss->communities, community, tmp)
{
clear_peer_list(&community->edges);
HASH_DEL(sss->communities, community);
free(community);
}
}
/** Determine the appropriate lifetime for new registrations.
*
* If the supernode has been put into a pre-shutdown phase then this lifetime
* should not allow registrations to continue beyond the shutdown point.
*/
static uint16_t reg_lifetime(n2n_sn_t *sss)
{
/* NOTE: UDP firewalls usually have a 30 seconds timeout */
return 15;
}
/** Update the edge table with the details of the edge which contacted the
* supernode. */
static int update_edge(n2n_sn_t *sss,
const n2n_mac_t edgeMac,
struct sn_community *comm,
const n2n_sock_t *sender_sock,
time_t now)
{
macstr_t mac_buf;
n2n_sock_str_t sockbuf;
struct peer_info *scan;
traceEvent(TRACE_DEBUG, "update_edge for %s [%s]",
macaddr_str(mac_buf, edgeMac),
sock_to_cstr(sockbuf, sender_sock));
HASH_FIND_PEER(comm->edges, edgeMac, scan);
if (NULL == scan)
{
/* Not known */
scan = (struct peer_info *)calloc(1,
sizeof(struct peer_info)); /* deallocated in purge_expired_registrations */
memcpy(&(scan->mac_addr), edgeMac, sizeof(n2n_mac_t));
memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t));
HASH_ADD_PEER(comm->edges, scan);
traceEvent(TRACE_INFO, "update_edge created %s ==> %s",
macaddr_str(mac_buf, edgeMac),
sock_to_cstr(sockbuf, sender_sock));
}
else
{
/* Known */
if (!sock_equal(sender_sock, &(scan->sock)))
{
memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t));
traceEvent(TRACE_INFO, "update_edge updated %s ==> %s",
macaddr_str(mac_buf, edgeMac),
sock_to_cstr(sockbuf, sender_sock));
}
else
{
traceEvent(TRACE_DEBUG, "update_edge unchanged %s ==> %s",
macaddr_str(mac_buf, edgeMac),
sock_to_cstr(sockbuf, sender_sock));
}
}
scan->last_seen = now;
return 0;
}
static int process_mgmt(n2n_sn_t *sss,
const struct sockaddr_in *sender_sock,
const uint8_t *mgmt_buf,
size_t mgmt_size,
time_t now)
{
char resbuf[N2N_SN_PKTBUF_SIZE];
size_t ressize = 0;
uint32_t num_edges = 0;
ssize_t r;
struct sn_community *community, *tmp;
traceEvent(TRACE_DEBUG, "process_mgmt");
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"----------------\n");
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"uptime %lu\n", (now - sss->start_time));
HASH_ITER(hh, sss->communities, community, tmp)
{
num_edges += HASH_COUNT(community->edges);
}
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"edges %u\n",
num_edges);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"errors %u\n",
(unsigned int)sss->stats.errors);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"reg_sup %u\n",
(unsigned int)sss->stats.reg_super);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"reg_nak %u\n",
(unsigned int)sss->stats.reg_super_nak);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"fwd %u\n",
(unsigned int)sss->stats.fwd);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"broadcast %u\n",
(unsigned int)sss->stats.broadcast);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"last fwd %lu sec ago\n",
(long unsigned int)(now - sss->stats.last_fwd));
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"last reg %lu sec ago\n",
(long unsigned int)(now - sss->stats.last_reg_super));
r = sendto(sss->mgmt_sock, resbuf, ressize, 0 /*flags*/,
(struct sockaddr *)sender_sock, sizeof(struct sockaddr_in));
if (r <= 0)
{
++(sss->stats.errors);
traceEvent(TRACE_ERROR, "process_mgmt : sendto failed. %s", strerror(errno));
}
return 0;
}
/** Examine a datagram and determine what to do with it.
*
*/
static int process_udp(n2n_sn_t *sss,
const struct sockaddr_in *sender_sock,
const uint8_t *udp_buf,
size_t udp_size,
time_t now)
{
n2n_common_t cmn; /* common fields in the packet header */
size_t rem;
size_t idx;
size_t msg_type;
uint8_t from_supernode;
macstr_t mac_buf;
macstr_t mac_buf2;
n2n_sock_str_t sockbuf;
char buf[32];
traceEvent(TRACE_DEBUG, "Processing incoming UDP packet [len: %lu][sender: %s:%u]",
udp_size, intoa(ntohl(sender_sock->sin_addr.s_addr), buf, sizeof(buf)),
ntohs(sender_sock->sin_port));
/* Use decode_common() to determine the kind of packet then process it:
*
* REGISTER_SUPER adds an edge and generate a return REGISTER_SUPER_ACK
*
* REGISTER, REGISTER_ACK and PACKET messages are forwarded to their
* destination edge. If the destination is not known then PACKETs are
* broadcast.
*/
rem = udp_size; /* Counts down bytes of packet to protect against buffer overruns. */
idx = 0; /* marches through packet header as parts are decoded. */
if (decode_common(&cmn, udp_buf, &rem, &idx) < 0)
{
traceEvent(TRACE_ERROR, "Failed to decode common section");
return -1; /* failed to decode packet */
}
msg_type = cmn.pc; /* packet code */
from_supernode = cmn.flags & N2N_FLAGS_FROM_SUPERNODE;
if (cmn.ttl < 1)
{
traceEvent(TRACE_WARNING, "Expired TTL");
return 0; /* Don't process further */
}
--(cmn.ttl); /* The value copied into all forwarded packets. */
switch (msg_type)
{
case MSG_TYPE_PACKET:
{
/* PACKET from one edge to another edge via supernode. */
/* pkt will be modified in place and recoded to an output of potentially
* different size due to addition of the socket.*/
n2n_PACKET_t pkt;
n2n_common_t cmn2;
uint8_t encbuf[N2N_SN_PKTBUF_SIZE];
size_t encx = 0;
int unicast; /* non-zero if unicast */
const uint8_t *rec_buf; /* either udp_buf or encbuf */
sss->stats.last_fwd = now;
decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx);
unicast = (0 == is_multi_broadcast(pkt.dstMac));
traceEvent(TRACE_DEBUG, "RX PACKET (%s) %s -> %s %s",
(unicast ? "unicast" : "multicast"),
macaddr_str(mac_buf, pkt.srcMac),
macaddr_str(mac_buf2, pkt.dstMac),
(from_supernode ? "from sn" : "local"));
if (!from_supernode)
{
memcpy(&cmn2, &cmn, sizeof(n2n_common_t));
/* We are going to add socket even if it was not there before */
cmn2.flags |= N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE;
pkt.sock.family = AF_INET;
pkt.sock.port = ntohs(sender_sock->sin_port);
memcpy(pkt.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE);
rec_buf = encbuf;
/* Re-encode the header. */
encode_PACKET(encbuf, &encx, &cmn2, &pkt);
/* Copy the original payload unchanged */
encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx));
}
else
{
/* Already from a supernode. Nothing to modify, just pass to
* destination. */
traceEvent(TRACE_DEBUG, "Rx PACKET fwd unmodified");
rec_buf = udp_buf;
encx = udp_size;
}
/* Common section to forward the final product. */
if (unicast)
try_forward(sss, &cmn, pkt.dstMac, rec_buf, encx);
else
try_broadcast(sss, &cmn, pkt.srcMac, rec_buf, encx);
break;
}
case MSG_TYPE_REGISTER:
{
/* Forwarding a REGISTER from one edge to the next */
n2n_REGISTER_t reg;
n2n_common_t cmn2;
uint8_t encbuf[N2N_SN_PKTBUF_SIZE];
size_t encx = 0;
int unicast; /* non-zero if unicast */
const uint8_t *rec_buf; /* either udp_buf or encbuf */
sss->stats.last_fwd = now;
decode_REGISTER(&reg, &cmn, udp_buf, &rem, &idx);
unicast = (0 == is_multi_broadcast(reg.dstMac));
if (unicast)
{
traceEvent(TRACE_DEBUG, "Rx REGISTER %s -> %s %s",
macaddr_str(mac_buf, reg.srcMac),
macaddr_str(mac_buf2, reg.dstMac),
((cmn.flags & N2N_FLAGS_FROM_SUPERNODE) ? "from sn" : "local"));
if (0 == (cmn.flags & N2N_FLAGS_FROM_SUPERNODE))
{
memcpy(&cmn2, &cmn, sizeof(n2n_common_t));
/* We are going to add socket even if it was not there before */
cmn2.flags |= N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE;
reg.sock.family = AF_INET;
reg.sock.port = ntohs(sender_sock->sin_port);
memcpy(reg.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE);
rec_buf = encbuf;
/* Re-encode the header. */
encode_REGISTER(encbuf, &encx, &cmn2, &reg);
/* Copy the original payload unchanged */
encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx));
}
else
{
/* Already from a supernode. Nothing to modify, just pass to
* destination. */
rec_buf = udp_buf;
encx = udp_size;
}
try_forward(sss, &cmn, reg.dstMac, rec_buf, encx); /* unicast only */
}
else
traceEvent(TRACE_ERROR, "Rx REGISTER with multicast destination");
break;
}
case MSG_TYPE_REGISTER_ACK:
traceEvent(TRACE_DEBUG, "Rx REGISTER_ACK (NOT IMPLEMENTED) SHould not be via supernode");
break;
case MSG_TYPE_REGISTER_SUPER:
{
n2n_REGISTER_SUPER_t reg;
n2n_REGISTER_SUPER_ACK_t ack;
n2n_common_t cmn2;
uint8_t ackbuf[N2N_SN_PKTBUF_SIZE];
size_t encx = 0;
struct sn_community *comm;
/* Edge requesting registration with us. */
sss->stats.last_reg_super = now;
++(sss->stats.reg_super);
decode_REGISTER_SUPER(&reg, &cmn, udp_buf, &rem, &idx);
HASH_FIND_COMMUNITY(sss->communities, (char *)cmn.community, comm);
/*
Before we move any further, we need to check if the requested
community is allowed by the supernode. In case it is not we do
not report any message back to the edge to hide the supernode
existance (better from the security standpoint)
*/
if (!comm && !sss->lock_communities)
{
comm = calloc(1, sizeof(struct sn_community));
if (comm)
{
strncpy(comm->community, (char *)cmn.community, N2N_COMMUNITY_SIZE - 1);
comm->community[N2N_COMMUNITY_SIZE - 1] = '\0';
HASH_ADD_STR(sss->communities, community, comm);
traceEvent(TRACE_INFO, "New community: %s", comm->community);
}
}
if (comm)
{
cmn2.ttl = N2N_DEFAULT_TTL;
cmn2.pc = n2n_register_super_ack;
cmn2.flags = N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE;
memcpy(cmn2.community, cmn.community, sizeof(n2n_community_t));
memcpy(&(ack.cookie), &(reg.cookie), sizeof(n2n_cookie_t));
memcpy(ack.edgeMac, reg.edgeMac, sizeof(n2n_mac_t));
ack.lifetime = reg_lifetime(sss);
ack.sock.family = AF_INET;
ack.sock.port = ntohs(sender_sock->sin_port);
memcpy(ack.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE);
ack.num_sn = 0; /* No backup */
memset(&(ack.sn_bak), 0, sizeof(n2n_sock_t));
traceEvent(TRACE_DEBUG, "Rx REGISTER_SUPER for %s [%s]",
macaddr_str(mac_buf, reg.edgeMac),
sock_to_cstr(sockbuf, &(ack.sock)));
update_edge(sss, reg.edgeMac, comm, &(ack.sock), now);
encode_REGISTER_SUPER_ACK(ackbuf, &encx, &cmn2, &ack);
sendto(sss->sock, ackbuf, encx, 0,
(struct sockaddr *)sender_sock, sizeof(struct sockaddr_in));
traceEvent(TRACE_DEBUG, "Tx REGISTER_SUPER_ACK for %s [%s]",
macaddr_str(mac_buf, reg.edgeMac),
sock_to_cstr(sockbuf, &(ack.sock)));
}
else
traceEvent(TRACE_INFO, "Discarded registration: unallowed community '%s'",
(char *)cmn.community);
break;
}
case MSG_TYPE_QUERY_PEER:
{
n2n_QUERY_PEER_t query;
uint8_t encbuf[N2N_SN_PKTBUF_SIZE];
size_t encx = 0;
n2n_common_t cmn2;
n2n_PEER_INFO_t pi;
struct sn_community *community;
decode_QUERY_PEER(&query, &cmn, udp_buf, &rem, &idx);
traceEvent(TRACE_DEBUG, "Rx QUERY_PEER from %s for %s",
macaddr_str(mac_buf, query.srcMac),
macaddr_str(mac_buf2, query.targetMac));
HASH_FIND_COMMUNITY(sss->communities, (char *)cmn.community, community);
if (community)
{
struct peer_info *scan;
HASH_FIND_PEER(community->edges, query.targetMac, scan);
if (scan)
{
cmn2.ttl = N2N_DEFAULT_TTL;
cmn2.pc = n2n_peer_info;
cmn2.flags = N2N_FLAGS_FROM_SUPERNODE;
memcpy(cmn2.community, cmn.community, sizeof(n2n_community_t));
pi.aflags = 0;
memcpy(pi.mac, query.targetMac, sizeof(n2n_mac_t));
pi.sock = scan->sock;
encode_PEER_INFO(encbuf, &encx, &cmn2, &pi);
sendto(sss->sock, encbuf, encx, 0,
(struct sockaddr *)sender_sock, sizeof(struct sockaddr_in));
traceEvent(TRACE_DEBUG, "Tx PEER_INFO to %s",
macaddr_str(mac_buf, query.srcMac));
}
else
{
traceEvent(TRACE_DEBUG, "Ignoring QUERY_PEER for unknown edge %s",
macaddr_str(mac_buf, query.targetMac));
}
}
break;
}
default:
/* Not a known message type */
traceEvent(TRACE_WARNING, "Unable to handle packet type %d: ignored", (signed int)msg_type);
} /* switch(msg_type) */
return 0;
}
/** Long lived processing entry point. Split out from main to simply
* daemonisation on some platforms. */
int run_sn_loop(n2n_sn_t *sss, int *keep_running)
{
uint8_t pktbuf[N2N_SN_PKTBUF_SIZE];
time_t last_purge_edges = 0;
struct sn_community *comm, *tmp;
sss->start_time = time(NULL);
while (*keep_running)
{
int rc;
ssize_t bread;
int max_sock;
fd_set socket_mask;
struct timeval wait_time;
time_t now = 0;
FD_ZERO(&socket_mask);
max_sock = MAX(sss->sock, sss->mgmt_sock);
FD_SET(sss->sock, &socket_mask);
FD_SET(sss->mgmt_sock, &socket_mask);
wait_time.tv_sec = 10;
wait_time.tv_usec = 0;
rc = select(max_sock + 1, &socket_mask, NULL, NULL, &wait_time);
now = time(NULL);
if (rc > 0)
{
if (FD_ISSET(sss->sock, &socket_mask))
{
struct sockaddr_in sender_sock;
socklen_t i;
i = sizeof(sender_sock);
bread = recvfrom(sss->sock, pktbuf, N2N_SN_PKTBUF_SIZE, 0 /*flags*/,
(struct sockaddr *)&sender_sock, (socklen_t *)&i);
if ((bread < 0)
#ifdef WIN32
&& (WSAGetLastError() != WSAECONNRESET)
#endif
)
{
/* For UDP bread of zero just means no data (unlike TCP). */
/* The fd is no good now. Maybe we lost our interface. */
traceEvent(TRACE_ERROR, "recvfrom() failed %d errno %d (%s)", bread, errno, strerror(errno));
#ifdef WIN32
traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastError());
#endif
keep_running = 0;
break;
}
/* We have a datagram to process */
if (bread > 0)
{
/* And the datagram has data (not just a header) */
process_udp(sss, &sender_sock, pktbuf, bread, now);
}
}
if (FD_ISSET(sss->mgmt_sock, &socket_mask))
{
struct sockaddr_in sender_sock;
size_t i;
i = sizeof(sender_sock);
bread = recvfrom(sss->mgmt_sock, pktbuf, N2N_SN_PKTBUF_SIZE, 0 /*flags*/,
(struct sockaddr *)&sender_sock, (socklen_t *)&i);
if (bread <= 0)
{
traceEvent(TRACE_ERROR, "recvfrom() failed %d errno %d (%s)", bread, errno, strerror(errno));
keep_running = 0;
break;
}
/* We have a datagram to process */
process_mgmt(sss, &sender_sock, pktbuf, bread, now);
}
}
else
{
traceEvent(TRACE_DEBUG, "timeout");
}
HASH_ITER(hh, sss->communities, comm, tmp)
{
purge_expired_registrations(&comm->edges, &last_purge_edges);
if ((comm->edges == NULL) && (!sss->lock_communities))
{
traceEvent(TRACE_INFO, "Purging idle community %s", comm->community);
HASH_DEL(sss->communities, comm);
free(comm);
}
}
} /* while */
sn_term(sss);
return 0;
}