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