diff --git a/.gitignore b/.gitignore
index 735ec3a..ed4a7c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
+Info.plist
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 68d0097..5b8bbc4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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
diff --git a/Makefile.in b/Makefile.in
index ef523f3..69a0395 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -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 $@
diff --git a/example_edge_embed.c b/example_edge_embed.c
index 477c209..5c91683 100644
--- a/example_edge_embed.c
+++ b/example_edge_embed.c
@@ -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
- *
- */
-
#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));
-}
+static int keep_running;
+
+int main()
+{
+ n2n_edge_conf_t conf;
+ tuntap_dev tuntap;
+ n2n_edge_t *eee;
+ int rc;
+
+ 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
+
+ if (edge_verify_conf(&conf) != 0)
+ {
+ return -1;
+ }
+
+ 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;
+}
\ No newline at end of file
diff --git a/example_edge_embed_quick_edge_init.c b/example_edge_embed_quick_edge_init.c
new file mode 100644
index 0000000..591d830
--- /dev/null
+++ b/example_edge_embed_quick_edge_init.c
@@ -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
+ *
+ */
+
+#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));
+}
\ No newline at end of file
diff --git a/example_sn_embed.c b/example_sn_embed.c
new file mode 100644
index 0000000..4b8aaf3
--- /dev/null
+++ b/example_sn_embed.c
@@ -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;
+}
\ No newline at end of file
diff --git a/n2n.h b/n2n.h
index e5018db..6d4c2b8 100644
--- a/n2n.h
+++ b/n2n.h
@@ -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_ */
diff --git a/sn_utils.c b/sn_utils.c
new file mode 100644
index 0000000..b727625
--- /dev/null
+++ b/sn_utils.c
@@ -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
+
+extern int try_forward(n2n_sn_t *sss,
+ const n2n_common_t *cmn,
+ const n2n_mac_t dstMac,
+ const uint8_t *pktbuf,
+ size_t pktsize);
+
+extern ssize_t sendto_sock(n2n_sn_t *sss,
+ const n2n_sock_t *sock,
+ const uint8_t *pktbuf,
+ size_t pktsize);
+
+extern int try_broadcast(n2n_sn_t *sss,
+ const n2n_common_t *cmn,
+ const n2n_mac_t srcMac,
+ const uint8_t *pktbuf,
+ size_t pktsize);
+
+extern uint16_t reg_lifetime(n2n_sn_t *sss);
+
+extern 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);
+
+extern 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);
+
+extern 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);
+
+extern 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
+ */
+extern 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.
+ */
+extern 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 */
+extern 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. */
+extern 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.
+ */
+extern 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. */
+extern 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;
+}
+
+extern 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.
+ *
+ */
+extern 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(®, &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, ®);
+
+ /* 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(®, &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. */
+extern 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;
+}
\ No newline at end of file