summaryrefslogtreecommitdiff
path: root/netcon
diff options
context:
space:
mode:
Diffstat (limited to 'netcon')
-rw-r--r--netcon/Intercept.c1088
-rw-r--r--netcon/Intercept.h192
-rw-r--r--netcon/LWIPStack.hpp217
-rw-r--r--netcon/NetconEthernetTap.cpp1555
-rw-r--r--netcon/NetconEthernetTap.hpp187
-rw-r--r--netcon/README.md108
-rw-r--r--netcon/README.orig.md139
-rw-r--r--netcon/common.inc.c197
-rw-r--r--netcon/docker-test/_results/FAIL.httpd-2.4.16-1.fc23.x86_64.txt1
-rw-r--r--netcon/docker-test/_results/FAIL.nginx-1.8.0-13.fc23.x86_64.txt1
-rw-r--r--netcon/docker-test/_results/FAIL.nginx-1.8.0-14.fc23.x86_64.txt1
-rw-r--r--netcon/docker-test/_results/FAIL.nodejs-0.10.36-4.fc23.txt1
-rw-r--r--netcon/docker-test/_results/OK.redis-3.0.4-1.fc23.x86_64.txt1
-rwxr-xr-xnetcon/docker-test/build.sh8
-rwxr-xr-xnetcon/docker-test/build_single_image.sh32
-rwxr-xr-xnetcon/docker-test/build_tests.sh27
-rwxr-xr-xnetcon/docker-test/build_zt.sh24
-rw-r--r--netcon/docker-test/e5cd7a9e1c5311ab.conf0
-rw-r--r--netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/httpd-2.4.16-1.fc23.x86_64.name0
-rw-r--r--netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/monitor_dockerfile23
-rw-r--r--netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/monitor_entrypoint.sh80
-rw-r--r--netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/netcon_dockerfile39
-rw-r--r--netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/netcon_entrypoint.sh47
-rw-r--r--netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/httpd-2.4.17-3.fc23.x86_64.name0
-rw-r--r--netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/monitor_dockerfile23
-rw-r--r--netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/monitor_entrypoint.sh80
-rw-r--r--netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/netcon_dockerfile39
-rw-r--r--netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/netcon_entrypoint.sh48
-rw-r--r--netcon/docker-test/httpd/httpd_demo/htdocs/ZeroTierIcon.pngbin0 -> 26898 bytes
-rw-r--r--netcon/docker-test/httpd/httpd_demo/htdocs/index.html69
-rw-r--r--netcon/docker-test/httpd/httpd_demo/httpd_demo.name0
-rw-r--r--netcon/docker-test/httpd/httpd_demo/monitor_dockerfile25
-rw-r--r--netcon/docker-test/httpd/httpd_demo/monitor_entrypoint.sh86
-rw-r--r--netcon/docker-test/httpd/httpd_demo/netcon_dockerfile47
-rw-r--r--netcon/docker-test/httpd/httpd_demo/netcon_entrypoint.sh54
-rw-r--r--netcon/docker-test/monitor_identity.public1
-rw-r--r--netcon/docker-test/monitor_identity.secret1
-rw-r--r--netcon/docker-test/netcon_identity.public1
-rw-r--r--netcon/docker-test/netcon_identity.secret1
-rw-r--r--netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/monitor_dockerfile23
-rw-r--r--netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/monitor_entrypoint.sh80
-rw-r--r--netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/netcon_dockerfile39
-rw-r--r--netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/netcon_entrypoint.sh48
-rw-r--r--netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/nginx-1.8.0-13.fc23.x86_64.name0
-rw-r--r--netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/trace.txt943
-rw-r--r--netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/monitor_dockerfile23
-rw-r--r--netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/monitor_entrypoint.sh80
-rw-r--r--netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/netcon_dockerfile39
-rw-r--r--netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/netcon_entrypoint.sh48
-rw-r--r--netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/nginx-1.8.0-14.fc23.x86_64.name0
-rw-r--r--netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/httpserver.js7
-rw-r--r--netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/monitor_dockerfile23
-rw-r--r--netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/monitor_entrypoint.sh57
-rw-r--r--netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/netcon_dockerfile42
-rw-r--r--netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/netcon_entrypoint.sh37
-rw-r--r--netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/nodejs-0.10.36-4.fc23.name0
-rw-r--r--netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/hello.lua3
-rw-r--r--netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/monitor_dockerfile27
-rw-r--r--netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/monitor_entrypoint.sh56
-rw-r--r--netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/netcon_dockerfile39
-rw-r--r--netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/netcon_entrypoint.sh37
-rw-r--r--netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/redis-3.0.4-1.fc23.x86_64.name0
-rwxr-xr-xnetcon/docker-test/run_tests.sh28
-rw-r--r--netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/monitor_dockerfile23
-rw-r--r--netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/monitor_entrypoint.sh57
-rw-r--r--netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/netcon_dockerfile39
-rw-r--r--netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/netcon_entrypoint.sh53
-rw-r--r--netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/openssh-server-7.1p1-3.fc23.x86_64.name0
-rwxr-xr-xnetcon/docker-test/test.sh32
-rwxr-xr-xnetcon/docker-test/two_party_test.sh17
-rwxr-xr-xnetcon/docker-test/zerotier-clibin0 -> 747928 bytes
-rwxr-xr-xnetcon/docker-test/zerotier-intercept54
-rwxr-xr-xnetcon/docker-test/zerotier-onebin0 -> 747928 bytes
-rw-r--r--netcon/make-intercept.mk53
-rw-r--r--netcon/make-liblwip.mk106
-rwxr-xr-xnetcon/zerotier-intercept54
76 files changed, 6610 insertions, 0 deletions
diff --git a/netcon/Intercept.c b/netcon/Intercept.c
new file mode 100644
index 00000000..5d0a5d78
--- /dev/null
+++ b/netcon/Intercept.c
@@ -0,0 +1,1088 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifdef USE_GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <unistd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <strings.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <pwd.h>
+#include <errno.h>
+#include <linux/errno.h>
+#include <stdarg.h>
+#include <netdb.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+
+#include "Intercept.h"
+
+#include "common.inc.c"
+
+#ifdef CHECKS
+ #include <sys/resource.h>
+ #include <linux/net.h> /* for NPROTO */
+ #define SOCK_MAX (SOCK_PACKET + 1)
+ #define SOCK_TYPE_MASK 0xf
+#endif
+
+/* Global Declarations */
+static int (*realconnect)(CONNECT_SIG);
+static int (*realselect)(SELECT_SIG);
+static int (*realbind)(BIND_SIG);
+static int (*realaccept)(ACCEPT_SIG);
+static int (*reallisten)(LISTEN_SIG);
+static int (*realsocket)(SOCKET_SIG);
+static int (*realsetsockopt)(SETSOCKOPT_SIG);
+static int (*realgetsockopt)(GETSOCKOPT_SIG);
+static int (*realaccept4)(ACCEPT4_SIG);
+static long (*realsyscall)(SYSCALL_SIG);
+static int (*realclose)(CLOSE_SIG);
+static int (*realclone)(CLONE_SIG);
+static int (*realdup2)(DUP2_SIG);
+static int (*realdup3)(DUP3_SIG);
+
+/* Exported Function Prototypes */
+void my_init(void);
+int connect(CONNECT_SIG);
+int select(SELECT_SIG);
+int bind(BIND_SIG);
+int accept(ACCEPT_SIG);
+int listen(LISTEN_SIG);
+int socket(SOCKET_SIG);
+int setsockopt(SETSOCKOPT_SIG);
+int getsockopt(GETSOCKOPT_SIG);
+int accept4(ACCEPT4_SIG);
+long syscall(SYSCALL_SIG);
+int close(CLOSE_SIG);
+int clone(CLONE_SIG);
+int dup2(DUP2_SIG);
+int dup3(DUP3_SIG);
+
+int connect_to_service(void);
+int init_service_connection();
+void load_symbols(void);
+void set_up_intercept();
+
+#define SERVICE_CONNECT_ATTEMPTS 30
+#define RPC_FD 1023
+
+ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd);
+
+/* threading */
+static pthread_mutex_t lock;
+
+void handle_error(char *name, char *info, int err)
+{
+#ifdef ERRORS_ARE_FATAL
+ if(err < 0) {
+ dwr(MSG_DEBUG,"handle_error(%s)=%d: FATAL: %s\n", name, err, info);
+ exit(-1);
+ }
+#endif
+#ifdef VERBOSE
+ dwr(MSG_DEBUG,"%s()=%d\n", name, err);
+#endif
+}
+
+static unsigned long rpc_count = 0;
+
+/*------------------------------------------------------------------------------
+------------------- Intercept<--->Service Comm mechanisms-----------------------
+------------------------------------------------------------------------------*/
+
+#define ZT_NC_NWID_ENV "ZT_NC_NWID"
+
+static int is_initialized = 0;
+static int fdret_sock; /* used for fd-transfers */
+static int newfd; /* used for "this_end" socket */
+static int thispid = -1;
+static int instance_count = 0;
+
+/*
+ * Check for forking
+ */
+void checkpid()
+{
+ /* Do noting if not configured (sanity check -- should never get here in this case) */
+ if (!getenv(ZT_NC_NWID_ENV))
+ return;
+
+ if (thispid != getpid()) {
+ printf("clone/fork detected. re-initializing this instance.\n");
+ set_up_intercept();
+ fdret_sock = init_service_connection();
+ thispid = getpid();
+ }
+}
+
+/*
+ * Sends an RPC command to the service
+ */
+int send_command(int rpc_fd, char *cmd)
+{
+ char metabuf[BUF_SZ]; // portion of buffer which contains RPC metadata for debugging
+#ifdef VERBOSE
+ /*
+ #define IDX_PID 0
+ #define IDX_TID sizeof(pid_t)
+ #define IDX_COUNT IDX_TID + sizeof(pid_t)
+ #define IDX_TIME IDX_COUNT + sizeof(int)
+ #define IDX_CMD IDX_TIME + 20 // 20 being the length of the timestamp string
+ #define IDX_PAYLOAD IDX_TIME + sizeof(char)
+ */
+ /* [pid_t] [pid_t] [rpc_count] [int] [...] */
+ memset(metabuf, '\0', BUF_SZ);
+ pid_t pid = syscall(SYS_getpid);
+ pid_t tid = syscall(SYS_gettid);
+ rpc_count++;
+ char timestring[20];
+ time_t timestamp;
+ timestamp = time(NULL);
+ strftime(timestring, sizeof(timestring), "%H:%M:%S", localtime(&timestamp));
+ memcpy(&metabuf[IDX_PID], &pid, sizeof(pid_t) ); /* pid */
+ memcpy(&metabuf[IDX_TID], &tid, sizeof(pid_t) ); /* tid */
+ memcpy(&metabuf[IDX_COUNT], &rpc_count, sizeof(rpc_count) ); /* rpc_count */
+ memcpy(&metabuf[IDX_TIME], &timestring, 20 ); /* timestamp */
+#endif
+ /* Combine command flag+payload with RPC metadata */
+ memcpy(&metabuf[IDX_PAYLOAD], cmd, PAYLOAD_SZ);
+ int n_write = write(rpc_fd, &metabuf, BUF_SZ);
+ if(n_write < 0){
+ dwr(MSG_DEBUG,"Error writing command to service (CMD = %d)\n", cmd[0]);
+ errno = 0;
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Reads a return value from the service and sets errno (if applicable)
+ */
+int get_retval()
+{
+ dwr(MSG_DEBUG,"get_retval()\n");
+ if(fdret_sock >= 0) {
+ int retval;
+ int sz = sizeof(char) + sizeof(retval) + sizeof(errno);
+ char retbuf[BUF_SZ];
+ memset(&retbuf, '\0', sz);
+ int n_read = read(fdret_sock, &retbuf, sz);
+ if(n_read > 0) {
+ memcpy(&retval, &retbuf[1], sizeof(retval));
+ memcpy(&errno, &retbuf[1+sizeof(retval)], sizeof(errno));
+ return retval;
+ }
+ }
+ dwr(MSG_DEBUG,"unable to read return value\n");
+ return -1;
+}
+
+/* Reads a new file descriptor from the service */
+int get_new_fd(int oversock)
+{
+ char buf[BUF_SZ];
+ int newfd;
+ ssize_t size = sock_fd_read(oversock, buf, sizeof(buf), &newfd);
+
+ if(size > 0){
+ dwr(MSG_DEBUG, "get_new_fd(): RX: fd = (%d) over (%d)\n", newfd, oversock);
+ return newfd;
+ }
+ dwr(MSG_ERROR, "get_new_fd(): ERROR: unable to read fd over (%d)\n", oversock);
+ return -1;
+}
+
+/* Check whether the socket is mapped to the service or not. We
+need to know if this is a regular AF_LOCAL socket or an end of a socketpair
+that the service uses. We don't want to keep state in the intercept, so
+we simply ask the service via an RPC */
+int is_mapped_to_service(int sockfd)
+{
+ dwr(MSG_DEBUG,"is_mapped_to_service()\n");
+ char cmd[BUF_SZ];
+ memset(cmd, '\0', BUF_SZ);
+ cmd[0] = RPC_MAP_REQ;
+ memcpy(&cmd[1], &sockfd, sizeof(sockfd));
+ pthread_mutex_lock(&lock);
+ if(send_command(fdret_sock, cmd) < 0)
+ return -1;
+ int err = get_retval();
+ pthread_mutex_unlock(&lock);
+ return err;
+}
+
+/*------------------------------------------------------------------------------
+---------- Unix-domain socket lazy initializer (for fd-transfers)--------------
+------------------------------------------------------------------------------*/
+
+/* Sets up the connection pipes and sockets to the service */
+int init_service_connection()
+{
+ struct sockaddr_un addr;
+ int tfd = -1, attempts = 0, conn_err = -1;
+ const char *network_id;
+ char af_sock_name[1024];
+
+ network_id = getenv(ZT_NC_NWID_ENV);
+ if ((!network_id)||(strlen(network_id) != 16))
+ return -1;
+ snprintf(af_sock_name,sizeof(af_sock_name),"/tmp/.ztnc_%s",network_id);
+
+ instance_count++;
+
+ dwr(MSG_DEBUG,"init_service_connection()\n");
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, af_sock_name, sizeof(addr.sun_path)-1);
+ if ( (tfd = realsocket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ return -1;
+ /*perror("socket error");
+ exit(-1);*/
+ }
+
+ while(conn_err < 0 && attempts < SERVICE_CONNECT_ATTEMPTS) {
+ conn_err = realconnect(tfd, (struct sockaddr*)&addr, sizeof(addr));
+ if(conn_err < 0) {
+ dwr(MSG_DEBUG,"re-attempting connection in %ds\n", 1+attempts);
+ sleep(1);
+ }
+ else {
+ dwr(MSG_DEBUG,"AF_UNIX connection established: %d\n", tfd);
+ is_initialized = 1;
+ int newtfd = realdup2(tfd, RPC_FD-instance_count);
+ dwr(MSG_DEBUG,"dup'd to rpc_fd = %d\n", newtfd);
+ close(tfd);
+ return newtfd;
+ }
+ attempts++;
+ }
+
+ return -1;
+}
+
+/*------------------------------------------------------------------------------
+------------------------ ctors and dtors (and friends)-------------------------
+------------------------------------------------------------------------------*/
+
+void my_dest(void) __attribute__ ((destructor));
+void my_dest(void) {
+ dwr(MSG_DEBUG,"closing connections to service...\n");
+ close(fdret_sock);
+ pthread_mutex_destroy(&lock);
+}
+
+void load_symbols(void)
+{
+#ifdef USE_OLD_DLSYM
+ void *lib;
+#endif
+ /* possibly add check to beginning of each method to avoid needing to cll the constructor */
+ if(thispid == getpid()) {
+ dwr(MSG_DEBUG,"detected duplicate call to global ctor (pid=%d).\n", thispid);
+ }
+ thispid = getpid();
+
+#ifndef USE_OLD_DLSYM
+ realconnect = dlsym(RTLD_NEXT, "connect");
+ realbind = dlsym(RTLD_NEXT, "bind");
+ realaccept = dlsym(RTLD_NEXT, "accept");
+ reallisten = dlsym(RTLD_NEXT, "listen");
+ realsocket = dlsym(RTLD_NEXT, "socket");
+ realbind = dlsym(RTLD_NEXT, "bind");
+ realselect = dlsym(RTLD_NEXT, "select");
+ realsetsockopt = dlsym(RTLD_NEXT, "setsockopt");
+ realgetsockopt = dlsym(RTLD_NEXT, "getsockopt");
+ realaccept4 = dlsym(RTLD_NEXT, "accept4");
+ realclone = dlsym(RTLD_NEXT, "clone");
+ realclose = dlsym(RTLD_NEXT, "close");
+ realsyscall = dlsym(RTLD_NEXT, "syscall");
+ realdup2 = dlsym(RTLD_NEXT, "dup2");
+ realdup3 = dlsym(RTLD_NEXT, "dup3");
+#else
+ lib = dlopen(LIBCONNECT, RTLD_LAZY);
+ realconnect = dlsym(lib, "connect");
+ realbind = dlsym(lib, "bind");
+ realaccept = dlsym(lib, "accept");
+ reallisten = dlsym(lib, "listen");
+ realsocket = dlsym(lib, "socket");
+ realselect = dlsym(lib, "select");
+ realsetsockopt = dlsym(lib, "setsockopt");
+ realgetsockopt = dlsym(lib, "getsockopt");
+ realaccept4 = dlsym(lib), "accept4");
+ realclone = dlsym(lib, "clone");
+ realclose = dlsym(lib, "close");
+ realsyscall = dlsym(lib, "syscall");
+ realdup2 = dlsym(RTLD_NEXT, "dup2");
+ realdup3 = dlsym(RTLD_NEXT, "dup3");
+ dlclose(lib);
+ lib = dlopen(LIBC, RTLD_LAZY);
+ dlclose(lib);
+#endif
+}
+
+/* Private Function Prototypes */
+void _init(void) __attribute__ ((constructor));
+void _init(void) { set_up_intercept(); }
+
+/* get symbols and initialize mutexes */
+void set_up_intercept()
+{
+ /* If ZT_NC_NWID_ENV is not set, do nothing -- not configured */
+ if (!getenv(ZT_NC_NWID_ENV))
+ return;
+
+ /* Hook/intercept Posix net API symbols */
+ load_symbols();
+
+ if(pthread_mutex_init(&lock, NULL) != 0) {
+ dwr(MSG_ERROR, "error while initializing service call mutex\n");
+ }
+ if(pthread_mutex_init(&loglock, NULL) != 0) {
+ dwr(MSG_ERROR, "error while initializing log mutex mutex\n");
+ }
+}
+
+/*------------------------------------------------------------------------------
+--------------------------------- setsockopt() ---------------------------------
+------------------------------------------------------------------------------*/
+
+/* int socket, int level, int option_name, const void *option_value, socklen_t option_len */
+int setsockopt(SETSOCKOPT_SIG)
+{
+ if(realsetsockopt == NULL){
+ dwr(MSG_ERROR, "setsockopt(): SYMBOL NOT FOUND.\n");
+ return -1;
+ }
+ dwr(MSG_DEBUG,"setsockopt(%d)\n", socket);
+ /*
+ if(is_mapped_to_service(socket) < 0) { // First, check if the service manages this
+ return realsetsockopt(socket, level, option_name, option_value, option_len);
+ }
+ */
+ /* return(realsetsockopt(socket, level, option_name, option_value, option_len)); */
+ if(level == SOL_IPV6 && option_name == IPV6_V6ONLY)
+ return 0;
+ if(level == SOL_IP && option_name == IP_TTL)
+ return 0;
+ if(level == IPPROTO_TCP || (level == SOL_SOCKET && option_name == SO_KEEPALIVE))
+ return 0;
+ /* make sure we don't touch any standard outputs */
+ if(socket == STDIN_FILENO || socket == STDOUT_FILENO || socket == STDERR_FILENO)
+ return(realsetsockopt(socket, level, option_name, option_value, option_len));
+ int err = realsetsockopt(socket, level, option_name, option_value, option_len);
+ if(err < 0){
+ perror("setsockopt():\n");
+ }
+ return 0;
+}
+
+
+/*------------------------------------------------------------------------------
+--------------------------------- getsockopt() ---------------------------------
+------------------------------------------------------------------------------*/
+
+/* int sockfd, int level, int optname, void *optval, socklen_t *optlen */
+int getsockopt(GETSOCKOPT_SIG)
+{
+ if(realgetsockopt == NULL){
+ dwr(MSG_ERROR, "getsockopt(): SYMBOL NOT FOUND.\n");
+ return -1;
+ }
+ dwr(MSG_DEBUG,"getsockopt(%d)\n", sockfd);
+ /*
+ if(is_mapped_to_service(sockfd) < 0) { // First, check if the service manages this
+ return realgetsockopt(sockfd, level, optname, optval, optlen);
+ }
+ */
+ int err = realgetsockopt(sockfd, level, optname, optval, optlen);
+ /* TODO: this condition will need a little more intelligence later on
+ -- we will need to know if this fd is a local we are spoofing, or a true local */
+ if(optname == SO_TYPE)
+ {
+ int* val = (int*)optval;
+ *val = 2;
+ optval = (void*)val;
+ }
+ if(err < 0){
+ perror("setsockopt():\n");
+ }
+ return 0;
+}
+
+
+/*------------------------------------------------------------------------------
+----------------------------------- socket() -----------------------------------
+------------------------------------------------------------------------------*/
+
+/* int socket_family, int socket_type, int protocol
+ socket() intercept function */
+int socket(SOCKET_SIG)
+{
+ if(realsocket == NULL){
+ dwr(MSG_ERROR, "socket(): SYMBOL NOT FOUND.\n");
+ return -1;
+ }
+ dwr(MSG_DEBUG,"socket():\n");
+ int err;
+#ifdef CHECKS
+ /* Check that type makes sense */
+ int flags = socket_type & ~SOCK_TYPE_MASK;
+ if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) {
+ errno = EINVAL;
+ handle_error("socket", "", -1);
+ return -1;
+ }
+ socket_type &= SOCK_TYPE_MASK;
+ /* Check protocol is in range */
+ if (socket_family < 0 || socket_family >= NPROTO){
+ errno = EAFNOSUPPORT;
+ handle_error("socket", "", -1);
+ return -1;
+ }
+ if (socket_type < 0 || socket_type >= SOCK_MAX) {
+ errno = EINVAL;
+ handle_error("socket", "", -1);
+ return -1;
+ }
+ /* Check that we haven't hit the soft-limit file descriptors allowed */
+ /* FIXME: Find number of open fds
+ struct rlimit rl;
+ getrlimit(RLIMIT_NOFILE, &rl);
+ if(sockfd >= rl.rlim_cur){
+ errno = EMFILE;
+ return -1;
+ }
+ */
+ /* TODO: detect ENFILE condition */
+#endif
+ char cmd[BUF_SZ];
+ fdret_sock = !is_initialized ? init_service_connection() : fdret_sock;
+ if(fdret_sock < 0) {
+ dwr(MSG_DEBUG,"BAD service connection. exiting.\n");
+ handle_error("socket", "", -1);
+ exit(-1);
+ }
+ if(socket_family == AF_LOCAL
+ || socket_family == AF_NETLINK
+ || socket_family == AF_UNIX) {
+ int err = realsocket(socket_family, socket_type, protocol);
+ dwr(MSG_DEBUG,"realsocket, err = %d\n", err);
+ handle_error("socket", "", err);
+ return err;
+ }
+ /* Assemble and send RPC */
+ struct socket_st rpc_st;
+ rpc_st.socket_family = socket_family;
+ rpc_st.socket_type = socket_type;
+ rpc_st.protocol = protocol;
+ rpc_st.__tid = syscall(SYS_gettid);
+ memset(cmd, '\0', BUF_SZ);
+ cmd[0] = RPC_SOCKET;
+ memcpy(&cmd[1], &rpc_st, sizeof(struct socket_st));
+ pthread_mutex_lock(&lock);
+ send_command(fdret_sock, cmd);
+
+ /* get new fd */
+ newfd = get_new_fd(fdret_sock);
+ if(newfd > 0)
+ {
+ dwr(MSG_DEBUG,"sending fd = %d to Service over (%d)\n", newfd, fdret_sock);
+ /* send our local-fd number back to service so
+ it can complete its mapping table entry */
+ memset(cmd, '\0', BUF_SZ);
+ cmd[0] = RPC_MAP;
+ memcpy(&cmd[1], &newfd, sizeof(newfd));
+
+ if(newfd > -1) {
+ send_command(fdret_sock, cmd);
+ pthread_mutex_unlock(&lock);
+ errno = ERR_OK; /* OK */
+ handle_error("socket", "", newfd);
+ return newfd;
+ }
+ else { /* Try to read retval+errno since we RXed a bad fd */
+ dwr(MSG_DEBUG,"Error, service sent bad fd.\n");
+ err = get_retval();
+ pthread_mutex_unlock(&lock);
+ handle_error("socket", "", -1);
+ return err;
+ }
+
+ }
+ else {
+ dwr(MSG_DEBUG,"Error while receiving new FD.\n");
+ err = get_retval();
+ pthread_mutex_unlock(&lock);
+ handle_error("socket", "", -1);
+ return err;
+ }
+}
+
+/*------------------------------------------------------------------------------
+---------------------------------- connect() -----------------------------------
+------------------------------------------------------------------------------*/
+
+/* int __fd, const struct sockaddr * __addr, socklen_t __len
+ connect() intercept function */
+int connect(CONNECT_SIG)
+{
+ if(realconnect == NULL){
+ dwr(MSG_ERROR, "connect(): SYMBOL NOT FOUND.\n");
+ return -1;
+ }
+ dwr(MSG_DEBUG,"connect(%d):\n", __fd);
+ /* print_addr(__addr); */
+ struct sockaddr_in *connaddr;
+ connaddr = (struct sockaddr_in *) __addr;
+
+#ifdef CHECKS
+ /* Check that this is a valid fd */
+ if(fcntl(__fd, F_GETFD) < 0) {
+ errno = EBADF;
+ handle_error("connect", "EBADF", -1);
+ return -1;
+ }
+ /* Check that it is a socket */
+ int sock_type;
+ socklen_t sock_type_len = sizeof(sock_type);
+ if(getsockopt(__fd, SOL_SOCKET, SO_TYPE, (void *) &sock_type, &sock_type_len) < 0) {
+ errno = ENOTSOCK;
+ handle_error("connect", "ENOTSOCK", -1);
+ return -1;
+ }
+ /* Check family */
+ if (connaddr->sin_family < 0 || connaddr->sin_family >= NPROTO){
+ errno = EAFNOSUPPORT;
+ handle_error("connect", "EAFNOSUPPORT", -1);
+ return -1;
+ }
+ /* FIXME: Check that address is in user space, return EFAULT ? */
+#endif
+
+ /* make sure we don't touch any standard outputs */
+ if(__fd == STDIN_FILENO || __fd == STDOUT_FILENO || __fd == STDERR_FILENO){
+ if (realconnect == NULL) {
+ handle_error("connect", "Unresolved symbol [connect]", -1);
+ exit(-1);
+ }
+ return(realconnect(__fd, __addr, __len));
+ }
+
+ if(__addr != NULL && (connaddr->sin_family == AF_LOCAL
+ || connaddr->sin_family == PF_NETLINK
+ || connaddr->sin_family == AF_NETLINK
+ || connaddr->sin_family == AF_UNIX)) {
+ int err = realconnect(__fd, __addr, __len);
+ perror("connect():");
+ /* handle_error("connect", "Cannot connect to local socket", err); */
+ return err;
+ }
+
+ /* Assemble and send RPC */
+ int err;
+ char cmd[BUF_SZ];
+ memset(cmd, '\0', BUF_SZ);
+ struct connect_st rpc_st;
+ rpc_st.__tid = syscall(SYS_gettid);
+ rpc_st.__fd = __fd;
+ memcpy(&rpc_st.__addr, __addr, sizeof(struct sockaddr));
+ memcpy(&rpc_st.__len, &__len, sizeof(socklen_t));
+ cmd[0] = RPC_CONNECT;
+ memcpy(&cmd[1], &rpc_st, sizeof(struct connect_st));
+ pthread_mutex_lock(&lock);
+ send_command(fdret_sock, cmd);
+ /*
+ if(sock_type && O_NONBLOCK) {
+ pthread_mutex_unlock(&lock);
+ return EINPROGRESS;
+ }
+ */
+ err = get_retval();
+ pthread_mutex_unlock(&lock);
+ /* handle_error("connect", "", err); */
+ return err;
+}
+
+/*------------------------------------------------------------------------------
+---------------------------------- select() ------------------------------------
+------------------------------------------------------------------------------*/
+
+/* int n, fd_set *readfds, fd_set *writefds,
+fd_set *exceptfds, struct timeval *timeout */
+int select(SELECT_SIG)
+{
+ if(realselect == NULL){
+ dwr(MSG_ERROR, "select(): SYMBOL NOT FOUND.\n");
+ return -1;
+ }
+ /* dwr(MSG_DEBUG,"select():\n"); */
+ return realselect(n, readfds, writefds, exceptfds, timeout);
+}
+
+/*------------------------------------------------------------------------------
+------------------------------------ bind() ------------------------------------
+------------------------------------------------------------------------------*/
+
+/* int sockfd, const struct sockaddr *addr, socklen_t addrlen
+ bind() intercept function */
+int bind(BIND_SIG)
+{
+ if(realbind == NULL){
+ dwr(MSG_ERROR, "bind(): SYMBOL NOT FOUND.\n");
+ return -1;
+ }
+ dwr(MSG_DEBUG,"bind(%d):\n", sockfd);
+ /* print_addr(addr); */
+#ifdef CHECKS
+ /* Check that this is a valid fd */
+ if(fcntl(sockfd, F_GETFD) < 0) {
+ errno = EBADF;
+ handle_error("bind", "EBADF", -1);
+ return -1;
+ }
+ /* Check that it is a socket */
+ int opt = -1;
+ socklen_t opt_len;
+ if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &opt, &opt_len) < 0) {
+ errno = ENOTSOCK;
+ handle_error("bind", "ENOTSOCK", -1);
+ return -1;
+ }
+#endif
+
+ int err;
+ /* make sure we don't touch any standard outputs */
+ if(sockfd == STDIN_FILENO || sockfd == STDOUT_FILENO || sockfd == STDERR_FILENO)
+ return(realbind(sockfd, addr, addrlen));
+
+ /* If local, just use normal syscall */
+ struct sockaddr_in *connaddr;
+ connaddr = (struct sockaddr_in *)addr;
+
+ if(connaddr->sin_family == AF_LOCAL
+ || connaddr->sin_family == AF_NETLINK
+ || connaddr->sin_family == AF_UNIX) {
+ int err = realbind(sockfd, addr, addrlen);
+ dwr(MSG_DEBUG,"realbind, err = %d\n", err);
+ return err;
+ }
+ /* Assemble and send RPC */
+ char cmd[BUF_SZ];
+ struct bind_st rpc_st;
+ rpc_st.sockfd = sockfd;
+ rpc_st.__tid = syscall(SYS_gettid);
+ memcpy(&rpc_st.addr, addr, sizeof(struct sockaddr));
+ memcpy(&rpc_st.addrlen, &addrlen, sizeof(socklen_t));
+ cmd[0]=RPC_BIND;
+ memcpy(&cmd[1], &rpc_st, sizeof(struct bind_st));
+ pthread_mutex_lock(&lock);
+ send_command(fdret_sock, cmd);
+ err = get_retval();
+ pthread_mutex_unlock(&lock);
+ errno = ERR_OK;
+ handle_error("bind", "", err);
+ return err;
+}
+
+
+/*------------------------------------------------------------------------------
+----------------------------------- accept4() ----------------------------------
+------------------------------------------------------------------------------*/
+
+
+/* int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags */
+int accept4(ACCEPT4_SIG)
+{
+ if(realaccept4 == NULL){
+ dwr(MSG_ERROR, "accept4(): SYMBOL NOT FOUND.\n");
+ return -1;
+ }
+ dwr(MSG_DEBUG,"accept4(%d):\n", sockfd);
+#ifdef CHECKS
+ if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) {
+ errno = EINVAL;
+ return -1;
+ }
+#endif
+ int newfd = accept(sockfd, addr, addrlen);
+ if(newfd > 0) {
+ if(flags & SOCK_CLOEXEC)
+ fcntl(newfd, F_SETFL, FD_CLOEXEC);
+ if(flags & SOCK_NONBLOCK)
+ fcntl(newfd, F_SETFL, O_NONBLOCK);
+ }
+ handle_error("accept4", "", newfd);
+ return newfd;
+}
+
+
+/*------------------------------------------------------------------------------
+----------------------------------- accept() -----------------------------------
+------------------------------------------------------------------------------*/
+
+/* int sockfd struct sockaddr *addr, socklen_t *addrlen
+ accept() intercept function */
+int accept(ACCEPT_SIG)
+{
+ if(realaccept == NULL){
+ dwr(MSG_ERROR, "accept(): SYMBOL NOT FOUND.\n");
+ return -1;
+ }
+ dwr(MSG_DEBUG,"accept(%d):\n", sockfd);
+#ifdef CHECKS
+ /* Check that this is a valid fd */
+ if(fcntl(sockfd, F_GETFD) < 0) {
+ return -1;
+ errno = EBADF;
+ dwr(MSG_DEBUG,"EBADF\n");
+ handle_error("accept", "EBADF", -1);
+ return -1;
+ }
+ /* Check that it is a socket */
+ int opt;
+ socklen_t opt_len;
+ if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &opt, &opt_len) < 0) {
+ errno = ENOTSOCK;
+ dwr(MSG_DEBUG,"ENOTSOCK\n");
+ handle_error("accept", "ENOTSOCK", -1);
+ return -1;
+ }
+ /* Check that this socket supports accept() */
+ if(!(opt && (SOCK_STREAM | SOCK_SEQPACKET))) {
+ errno = EOPNOTSUPP;
+ dwr(MSG_DEBUG,"EOPNOTSUPP\n");
+ handle_error("accept", "EOPNOTSUPP", -1);
+ return -1;
+ }
+ /* Check that we haven't hit the soft-limit file descriptors allowed */
+ struct rlimit rl;
+ getrlimit(RLIMIT_NOFILE, &rl);
+ if(sockfd >= rl.rlim_cur){
+ errno = EMFILE;
+ dwr(MSG_DEBUG,"EMFILE\n");
+ handle_error("accept", "EMFILE", -1);
+ return -1;
+ }
+ /* Check address length */
+ if(addrlen < 0) {
+ errno = EINVAL;
+ dwr(MSG_DEBUG,"EINVAL\n");
+ handle_error("accept", "EINVAL", -1);
+ return -1;
+ }
+#endif
+
+ /* redirect calls for standard I/O descriptors to kernel */
+ if(sockfd == STDIN_FILENO || sockfd == STDOUT_FILENO || sockfd == STDERR_FILENO){
+ dwr(MSG_DEBUG,"realaccept():\n");
+ return(realaccept(sockfd, addr, addrlen));
+ }
+
+ if(addr)
+ addr->sa_family = AF_INET;
+ /* TODO: also get address info */
+
+ char cmd[BUF_SZ];
+ if(realaccept == NULL) {
+ handle_error("accept", "Unresolved symbol [accept]", -1);
+ return -1;
+ }
+
+ // if(opt & O_NONBLOCK)
+ fcntl(sockfd, F_SETFL, O_NONBLOCK); /* required by libuv in nodejs */
+
+
+ char c[1];
+ int new_conn_socket;
+ int n = read(sockfd, c, sizeof(c)); /* Read signal byte */
+
+ if(n > 0)
+ {
+ new_conn_socket = get_new_fd(fdret_sock);
+ dwr(MSG_DEBUG, " accept(): RX: fd = (%d) over (%d)\n", new_conn_socket, fdret_sock);
+ if(new_conn_socket > 0) {
+ /* Send our local-fd number back to service so it can complete its mapping table */
+ memset(cmd, '\0', BUF_SZ);
+ cmd[0] = RPC_MAP;
+ memcpy(&cmd[1], &new_conn_socket, sizeof(new_conn_socket));
+ pthread_mutex_lock(&lock);
+
+ dwr(MSG_DEBUG, "accept(): sending perceived fd (%d) to service.\n", new_conn_socket);
+ int n_write = send_command(fdret_sock, cmd);
+
+ if(n_write < 0) {
+ errno = ECONNABORTED; /* TODO: Closest match, service unreachable */
+ handle_error("accept", "ECONNABORTED - Error sending perceived FD to service", -1);
+ return -1;
+ }
+ pthread_mutex_unlock(&lock);
+ errno = ERR_OK;
+ dwr(MSG_DEBUG,"*accept()=%d\n", new_conn_socket);
+ handle_error("accept", "", new_conn_socket);
+ return new_conn_socket; /* OK */
+ }
+ else {
+ errno = ECONNABORTED; /* TODO: Closest match, service unreachable */
+ handle_error("accept", "ECONNABORTED - Error receiving new FD from service", -1);
+ return -1;
+ }
+ }
+
+ errno = EAGAIN; /* necessary? */
+ handle_error("accept", "EAGAIN - Error reading signal byte from service", -1);
+ return -EAGAIN;
+
+/* Prevents libuv in nodejs from accepting properly (it looks for a -EAGAIN) */
+/*
+ errno = EBADF;
+ handle_error("accept", "EBADF - Error reading signal byte from service", -1);
+ return -1;
+*/
+}
+
+
+/*------------------------------------------------------------------------------
+------------------------------------- listen()----------------------------------
+------------------------------------------------------------------------------*/
+
+/* int sockfd, int backlog
+ listen() intercept function */
+int listen(LISTEN_SIG)
+{
+ if(reallisten == NULL){
+ dwr(MSG_ERROR, "listen(): SYMBOL NOT FOUND.\n");
+ return -1;
+ }
+ dwr(MSG_DEBUG,"listen(%d):\n", sockfd);
+ int sock_type;
+ socklen_t sock_type_len = sizeof(sock_type);
+
+ #ifdef CHECKS
+ /* Check that this is a valid fd */
+ if(fcntl(sockfd, F_GETFD) < 0) {
+ errno = EBADF;
+ handle_error("listen", "EBADF", -1);
+ return -1;
+ }
+ /* Check that it is a socket */
+ if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &sock_type, &sock_type_len) < 0) {
+ errno = ENOTSOCK;
+ handle_error("listen", "ENOTSOCK", -1);
+ return -1;
+ }
+ /* Check that this socket supports accept() */
+ if(!(sock_type && (SOCK_STREAM | SOCK_SEQPACKET))) {
+ errno = EOPNOTSUPP;
+ handle_error("listen", "EOPNOTSUPP", -1);
+ return -1;
+ }
+ #endif
+
+ /* make sure we don't touch any standard outputs */
+ if(sockfd == STDIN_FILENO || sockfd == STDOUT_FILENO || sockfd == STDERR_FILENO)
+ return(reallisten(sockfd, backlog));
+
+ if(is_mapped_to_service(sockfd) < 0) {
+ /* We now know this socket is not one of our socketpairs */
+ int err = reallisten(sockfd, backlog);
+ dwr(MSG_DEBUG,"reallisten()=%d\n", err);
+ return err;
+ }
+
+ /* Assemble and send RPC */
+ char cmd[BUF_SZ];
+ memset(cmd, '\0', BUF_SZ);
+ struct listen_st rpc_st;
+ rpc_st.sockfd = sockfd;
+ rpc_st.backlog = backlog;
+ rpc_st.__tid = syscall(SYS_gettid);
+ cmd[0] = RPC_LISTEN;
+ memcpy(&cmd[1], &rpc_st, sizeof(struct listen_st));
+ pthread_mutex_lock(&lock);
+ send_command(fdret_sock, cmd);
+ get_retval();
+ pthread_mutex_unlock(&lock);
+ handle_error("listen", "", ERR_OK);
+ return ERR_OK;
+}
+
+/*------------------------------------------------------------------------------
+-------------------------------------- clone()----------------------------------
+------------------------------------------------------------------------------*/
+
+/* int (*fn)(void *), void *child_stack, int flags, void *arg, ... */
+int clone(CLONE_SIG)
+{
+ if(realclone == NULL){
+ dwr(MSG_ERROR, "clone(): SYMBOL NOT FOUND.\n");
+ return -1;
+ }
+ dwr(MSG_DEBUG,"clone()\n");
+ int err = realclone(fn, child_stack, flags, arg);
+ checkpid();
+ return err;
+}
+
+
+/*------------------------------------------------------------------------------
+-------------------------------------- poll()-----------------------------------
+------------------------------------------------------------------------------*/
+
+/* struct pollfd *fds, nfds_t nfds, int timeout */
+/*
+int poll(POLL_SIG)
+{
+ dwr(MSG_DEBUG,"poll()\n");
+ return realpoll(fds, nfds, timeout);
+}
+*/
+
+/*------------------------------------------------------------------------------
+-------------------------------------- close()-----------------------------------
+------------------------------------------------------------------------------*/
+
+/* int fd */
+int close(CLOSE_SIG)
+{
+ //checkpid(); // Required for httpd-2.4.17-3.x86_64 -- After clone, some symbols aren't initialized yet */
+ if(realclose == NULL){
+ dwr(MSG_ERROR, "close(): SYMBOL NOT FOUND.\n");
+ return -1;
+ }
+ /* dwr(MSG_DEBUG,"close(%d)\n", fd); */
+ if(fd == fdret_sock)
+ return -1; /* TODO: Ignore request to shut down our rpc fd, this is *almost always* safe */
+ if(fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
+ return realclose(fd);
+ return -1;
+}
+
+/*------------------------------------------------------------------------------
+-------------------------------------- dup2()-----------------------------------
+------------------------------------------------------------------------------*/
+
+/* int oldfd, int newfd */
+int dup2(DUP2_SIG)
+{
+ if(realdup2 == NULL){
+ dwr(MSG_ERROR, "dup2(): SYMBOL NOT FOUND.\n");
+ return -1;
+ }
+ dwr(MSG_DEBUG,"dup2(%d, %d)\n", oldfd, newfd);
+ if(oldfd == fdret_sock) {
+ dwr(MSG_DEBUG,"client application attempted to dup2 RPC socket (%d). This is not allowed.\n", oldfd);
+ errno = EBADF;
+ return -1;
+ }
+ if(oldfd != STDIN_FILENO && oldfd != STDOUT_FILENO && oldfd != STDERR_FILENO)
+ if(newfd != STDIN_FILENO && newfd != STDOUT_FILENO && newfd != STDERR_FILENO)
+ return realdup2(oldfd, newfd);
+ return -1;
+}
+
+/*------------------------------------------------------------------------------
+-------------------------------------- dup3()-----------------------------------
+------------------------------------------------------------------------------*/
+
+/* int oldfd, int newfd, int flags */
+int dup3(DUP3_SIG)
+{
+ if(realdup3 == NULL){
+ dwr(MSG_ERROR, "dup3(): SYMBOL NOT FOUND.\n");
+ return -1;
+ }
+ dwr(MSG_DEBUG,"dup3(%d, %d, %d)\n", oldfd, newfd, flags);
+#ifdef DEBUG
+ /* Only do this check if we want to debug the intercept, otherwise, dont mess with
+ the client application's logging methods */
+ if(newfd == STDIN_FILENO || newfd == STDOUT_FILENO || newfd == STDERR_FILENO)
+ return newfd; /* FIXME: This is to prevent httpd from dup'ing over our stderr
+ and preventing us from debugging */
+ else
+#endif
+ return realdup3(oldfd, newfd, flags);
+}
+
+
+/*------------------------------------------------------------------------------
+------------------------------------ syscall()----------------------------------
+------------------------------------------------------------------------------*/
+
+long syscall(SYSCALL_SIG){
+ if(realsyscall == NULL){
+ dwr(MSG_ERROR, "syscall(): SYMBOL NOT FOUND.\n");
+ return -1;
+ }
+ dwr(MSG_DEBUG_EXTRA,"syscall(%u, ...):\n", number);
+
+ va_list ap;
+ uintptr_t a,b,c,d,e,f;
+ va_start(ap, number);
+ a=va_arg(ap, uintptr_t);
+ b=va_arg(ap, uintptr_t);
+ c=va_arg(ap, uintptr_t);
+ d=va_arg(ap, uintptr_t);
+ e=va_arg(ap, uintptr_t);
+ f=va_arg(ap, uintptr_t);
+ va_end(ap);
+
+#if defined(__i386__)
+ /* TODO: Implement for 32-bit systems: syscall(__NR_socketcall, 18, args);
+ args[0] = (unsigned long) fd;
+ args[1] = (unsigned long) addr;
+ args[2] = (unsigned long) addrlen;
+ args[3] = (unsigned long) flags;
+ */
+#else
+ if(number == __NR_accept4) {
+ int sockfd = a;
+ struct sockaddr * addr = (struct sockaddr*)b;
+ socklen_t * addrlen = (socklen_t*)c;
+ int flags = d;
+ int old_errno = errno;
+ int err = accept4(sockfd, addr, addrlen, flags);
+
+ errno = old_errno;
+ if(err == -EBADF)
+ err = -EAGAIN;
+ return err;
+ }
+#endif
+ return realsyscall(number,a,b,c,d,e,f);
+}
diff --git a/netcon/Intercept.h b/netcon/Intercept.h
new file mode 100644
index 00000000..ef10dc64
--- /dev/null
+++ b/netcon/Intercept.h
@@ -0,0 +1,192 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+
+#ifndef _INTERCEPT_H
+#define _INTERCEPT_H 1
+
+#include <sys/socket.h>
+
+#define IDX_PID 0
+#define IDX_TID sizeof(pid_t)
+#define IDX_COUNT IDX_TID + sizeof(pid_t)
+#define IDX_TIME IDX_COUNT + sizeof(int)
+#define IDX_PAYLOAD IDX_TIME + 20 /* 20 being the length of the timestamp string */
+
+#define BUF_SZ 256
+#define PAYLOAD_SZ 223 /* BUF_SZ-IDX_PAYLOAD */
+
+#define ERR_OK 0
+
+/* Userland RPC codes */
+#define RPC_UNDEFINED 0
+#define RPC_CONNECT 1
+#define RPC_CONNECT_SOCKARG 2
+#define RPC_SELECT 3
+#define RPC_POLL 4
+#define RPC_CLOSE 5
+#define RPC_READ 6
+#define RPC_WRITE 7
+#define RPC_BIND 8
+#define RPC_ACCEPT 9
+#define RPC_LISTEN 10
+#define RPC_SOCKET 11
+#define RPC_SHUTDOWN 12
+
+/* Administration RPC codes */
+#define RPC_MAP 20 /* Give the service the value we "see" for the new buffer fd */
+#define RPC_MAP_REQ 21 /* A call to determine whether an fd is mapped to the service */
+#define RPC_RETVAL 22 /* not RPC per se, but something we should codify */
+#define RPC_KILL_INTERCEPT 23 /* Tells the service we need to shut down all connections */
+
+/* Connection statuses */
+#define UNSTARTED 0
+#define CONNECTING 1
+#define CONNECTED 2
+#define SENDING 3
+#define RECEIVING 4
+#define SENTV4REQ 5
+#define GOTV4REQ 6
+#define SENTV5METHOD 7
+#define GOTV5METHOD 8
+#define SENTV5AUTH 9
+#define GOTV5AUTH 10
+#define SENTV5CONNECT 11
+#define GOTV5CONNECT 12
+#define DONE 13
+#define FAILED 14
+
+/* Flags to indicate what events a
+ socket was select()ed for */
+#define READ (POLLIN|POLLRDNORM)
+#define WRITE (POLLOUT|POLLWRNORM|POLLWRBAND)
+#define EXCEPT (POLLRDBAND|POLLPRI)
+#define READWRITE (READ|WRITE)
+#define READWRITEEXCEPT (READ|WRITE|EXCEPT)
+
+
+/* for AF_UNIX sockets */
+#define MAX_PATH_NAME_SIZE 64
+
+/* bind */
+#define BIND_SIG int sockfd, const struct sockaddr *addr, socklen_t addrlen
+struct bind_st
+{
+ int sockfd;
+ struct sockaddr addr;
+ socklen_t addrlen;
+ int __tid;
+};
+
+/* connect */
+#define CONNECT_SIG int __fd, const struct sockaddr * __addr, socklen_t __len
+struct connect_st
+{
+ int __fd;
+ struct sockaddr __addr;
+ socklen_t __len;
+ int __tid;
+};
+
+/* close */
+#define CLOSE_SIG int fd
+struct close_st
+{
+ int fd;
+};
+
+/* read */
+#define DEFAULT_READ_BUFFER_SIZE 1024 * 63
+/* read buffer sizes (on test machine) min: 4096 default: 87380 max:6147872 */
+#define READ_SIG int __fd, void *__buf, size_t __nbytes
+struct read_st
+{
+ int fd;
+ size_t count;
+ unsigned char buf[DEFAULT_READ_BUFFER_SIZE];
+};
+
+/* write */
+#define DEFAULT_WRITE_BUFFER_SIZE 1024 * 63
+/* write buffer sizes (on test machine) min: 4096 default: 16384 max:4194304 */
+#define WRITE_SIG int __fd, const void *__buf, size_t __n
+struct write_st
+{
+ int fd;
+ size_t count;
+ char buf[DEFAULT_WRITE_BUFFER_SIZE];
+};
+
+#define LISTEN_SIG int sockfd, int backlog
+struct listen_st
+{
+ int sockfd;
+ int backlog;
+ int __tid;
+};
+
+#define SOCKET_SIG int socket_family, int socket_type, int protocol
+struct socket_st
+{
+ int socket_family;
+ int socket_type;
+ int protocol;
+ int __tid;
+};
+
+#define ACCEPT4_SIG int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags
+#define ACCEPT_SIG int sockfd, struct sockaddr *addr, socklen_t *addrlen
+struct accept_st
+{
+ int sockfd;
+ struct sockaddr addr;
+ socklen_t addrlen;
+ int __tid;
+};
+
+#define SHUTDOWN_SIG int socket, int how
+struct shutdown_st
+{
+ int socket;
+ int how;
+};
+
+#define CONNECT_SOCKARG struct sockaddr *
+#define SELECT_SIG int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout
+#define IOCTL_SIG int __fd, unsigned long int __request, ...
+#define FCNTL_SIG int __fd, int __cmd, ...
+#define DAEMON_SIG int nochdir, int noclose
+#define SETSOCKOPT_SIG int socket, int level, int option_name, const void *option_value, socklen_t option_len
+#define GETSOCKOPT_SIG int sockfd, int level, int optname, void *optval, socklen_t *optlen
+#define SYSCALL_SIG long number, ...
+#define CLONE_SIG int (*fn)(void *), void *child_stack, int flags, void *arg, ...
+#define POLL_SIG struct pollfd *fds, nfds_t nfds, int timeout
+
+#define DUP2_SIG int oldfd, int newfd
+#define DUP3_SIG int oldfd, int newfd, int flags
+
+#endif
diff --git a/netcon/LWIPStack.hpp b/netcon/LWIPStack.hpp
new file mode 100644
index 00000000..fedbdd5f
--- /dev/null
+++ b/netcon/LWIPStack.hpp
@@ -0,0 +1,217 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef ZT_LWIPSTACK_H
+#define ZT_LWIPSTACK_H
+
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+#include "../node/Mutex.hpp"
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+#ifdef D_GNU_SOURCE
+ #define _GNU_SOURCE
+#endif
+
+typedef ip_addr ip_addr_t;
+struct tcp_pcb;
+
+#define TCP_WRITE_SIG struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags
+#define TCP_SENT_SIG struct tcp_pcb * pcb, err_t (* sent)(void * arg, struct tcp_pcb * tpcb, u16_t len)
+#define TCP_NEW_SIG void
+#define TCP_SNDBUF_SIG struct tcp_pcb * pcb
+#define TCP_CONNECT_SIG struct tcp_pcb * pcb, struct ip_addr * ipaddr, u16_t port, err_t (* connected)(void * arg, struct tcp_pcb * tpcb, err_t err)
+#define TCP_RECV_SIG struct tcp_pcb * pcb, err_t (* recv)(void * arg, struct tcp_pcb * tpcb, struct pbuf * p, err_t err)
+#define TCP_RECVED_SIG struct tcp_pcb * pcb, u16_t len
+#define TCP_ERR_SIG struct tcp_pcb * pcb, void (* err)(void * arg, err_t err)
+#define TCP_POLL_SIG struct tcp_pcb * pcb, err_t (* poll)(void * arg, struct tcp_pcb * tpcb), u8_t interval
+#define TCP_ARG_SIG struct tcp_pcb * pcb, void * arg
+#define TCP_CLOSE_SIG struct tcp_pcb * pcb
+#define TCP_ABORT_SIG struct tcp_pcb * pcb
+#define TCP_OUTPUT_SIG struct tcp_pcb * pcb
+#define TCP_ACCEPT_SIG struct tcp_pcb * pcb, err_t (* accept)(void * arg, struct tcp_pcb * newpcb, err_t err)
+#define TCP_LISTEN_SIG struct tcp_pcb * pcb
+#define TCP_LISTEN_WITH_BACKLOG_SIG struct tcp_pcb * pcb, u8_t backlog
+#define TCP_BIND_SIG struct tcp_pcb * pcb, struct ip_addr * ipaddr, u16_t port
+#define PBUF_FREE_SIG struct pbuf *p
+#define PBUF_ALLOC_SIG pbuf_layer layer, u16_t length, pbuf_type type
+#define LWIP_HTONS_SIG u16_t x
+#define LWIP_NTOHS_SIG u16_t x
+#define IPADDR_NTOA_SIG const ip_addr_t *addr
+#define ETHARP_OUTPUT_SIG struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr
+#define ETHERNET_INPUT_SIG struct pbuf *p, struct netif *netif
+#define TCP_INPUT_SIG struct pbuf *p, struct netif *inp
+#define IP_INPUT_SIG struct pbuf *p, struct netif *inp
+#define NETIF_SET_DEFAULT_SIG struct netif *netif
+#define NETIF_ADD_SIG struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input
+#define NETIF_SET_UP_SIG struct netif *netif
+#define NETIF_POLL_SIG struct netif *netif
+
+namespace ZeroTier {
+
+/**
+ * Loads an instance of liblwip.so in a private memory arena
+ *
+ * This uses dlmopen() to load an instance of the LWIP stack into its
+ * own private memory space. This is done to get around the stack's
+ * lack of thread-safety or multi-instance support. The alternative
+ * would be to massively refactor the stack so everything lives in a
+ * state object instead of static memory space.
+ */
+class LWIPStack
+{
+public:
+ void *_libref;
+
+ void (*_lwip_init)();
+ err_t (*_tcp_write)(TCP_WRITE_SIG);
+ void (*_tcp_sent)(TCP_SENT_SIG);
+ struct tcp_pcb * (*_tcp_new)(TCP_NEW_SIG);
+ u16_t (*_tcp_sndbuf)(TCP_SNDBUF_SIG);
+ err_t (*_tcp_connect)(TCP_CONNECT_SIG);
+ void (*_tcp_recv)(TCP_RECV_SIG);
+ void (*_tcp_recved)(TCP_RECVED_SIG);
+ void (*_tcp_err)(TCP_ERR_SIG);
+ void (*_tcp_poll)(TCP_POLL_SIG);
+ void (*_tcp_arg)(TCP_ARG_SIG);
+ err_t (*_tcp_close)(TCP_CLOSE_SIG);
+ void (*_tcp_abort)(TCP_ABORT_SIG);
+ err_t (*_tcp_output)(TCP_OUTPUT_SIG);
+ void (*_tcp_accept)(TCP_ACCEPT_SIG);
+ struct tcp_pcb * (*_tcp_listen)(TCP_LISTEN_SIG);
+ struct tcp_pcb * (*_tcp_listen_with_backlog)(TCP_LISTEN_WITH_BACKLOG_SIG);
+ err_t (*_tcp_bind)(TCP_BIND_SIG);
+ void (*_etharp_tmr)(void);
+ void (*_tcp_tmr)(void);
+ u8_t (*_pbuf_free)(PBUF_FREE_SIG);
+ struct pbuf * (*_pbuf_alloc)(PBUF_ALLOC_SIG);
+ u16_t (*_lwip_htons)(LWIP_HTONS_SIG);
+ u16_t (*_lwip_ntohs)(LWIP_NTOHS_SIG);
+ char* (*_ipaddr_ntoa)(IPADDR_NTOA_SIG);
+ err_t (*_etharp_output)(ETHARP_OUTPUT_SIG);
+ err_t (*_ethernet_input)(ETHERNET_INPUT_SIG);
+ void (*_tcp_input)(TCP_INPUT_SIG);
+ err_t (*_ip_input)(IP_INPUT_SIG);
+ void (*_netif_set_default)(NETIF_SET_DEFAULT_SIG);
+ struct netif * (*_netif_add)(NETIF_ADD_SIG);
+ void (*_netif_set_up)(NETIF_SET_UP_SIG);
+ void (*_netif_poll)(NETIF_POLL_SIG);
+
+
+ Mutex _lock;
+
+ LWIPStack(const char* path) :
+ _libref(NULL)
+ {
+ _libref = dlmopen(LM_ID_NEWLM, path, RTLD_NOW);
+ if(_libref == NULL)
+ printf("dlerror(): %s\n", dlerror());
+
+ _lwip_init = (void(*)(void))dlsym(_libref, "lwip_init");
+ _tcp_write = (err_t(*)(TCP_WRITE_SIG))dlsym(_libref, "tcp_write");
+ _tcp_sent = (void(*)(TCP_SENT_SIG))dlsym(_libref, "tcp_sent");
+ _tcp_new = (struct tcp_pcb*(*)(TCP_NEW_SIG))dlsym(_libref, "tcp_new");
+ _tcp_sndbuf = (u16_t(*)(TCP_SNDBUF_SIG))dlsym(_libref, "tcp_sndbuf");
+ _tcp_connect = (err_t(*)(TCP_CONNECT_SIG))dlsym(_libref, "tcp_connect");
+ _tcp_recv = (void(*)(TCP_RECV_SIG))dlsym(_libref, "tcp_recv");
+ _tcp_recved = (void(*)(TCP_RECVED_SIG))dlsym(_libref, "tcp_recved");
+ _tcp_err = (void(*)(TCP_ERR_SIG))dlsym(_libref, "tcp_err");
+ _tcp_poll = (void(*)(TCP_POLL_SIG))dlsym(_libref, "tcp_poll");
+ _tcp_arg = (void(*)(TCP_ARG_SIG))dlsym(_libref, "tcp_arg");
+ _tcp_close = (err_t(*)(TCP_CLOSE_SIG))dlsym(_libref, "tcp_close");
+ _tcp_abort = (void(*)(TCP_ABORT_SIG))dlsym(_libref, "tcp_abort");
+ _tcp_output = (err_t(*)(TCP_OUTPUT_SIG))dlsym(_libref, "tcp_output");
+ _tcp_accept = (void(*)(TCP_ACCEPT_SIG))dlsym(_libref, "tcp_accept");
+ _tcp_listen = (struct tcp_pcb*(*)(TCP_LISTEN_SIG))dlsym(_libref, "tcp_listen");
+ _tcp_listen_with_backlog = (struct tcp_pcb*(*)(TCP_LISTEN_WITH_BACKLOG_SIG))dlsym(_libref, "tcp_listen_with_backlog");
+ _tcp_bind = (err_t(*)(TCP_BIND_SIG))dlsym(_libref, "tcp_bind");
+ _etharp_tmr = (void(*)(void))dlsym(_libref, "etharp_tmr");
+ _tcp_tmr = (void(*)(void))dlsym(_libref, "tcp_tmr");
+ _pbuf_free = (u8_t(*)(PBUF_FREE_SIG))dlsym(_libref, "pbuf_free");
+ _pbuf_alloc = (struct pbuf*(*)(PBUF_ALLOC_SIG))dlsym(_libref, "pbuf_alloc");
+ _lwip_htons = (u16_t(*)(LWIP_HTONS_SIG))dlsym(_libref, "lwip_htons");
+ _lwip_ntohs = (u16_t(*)(LWIP_NTOHS_SIG))dlsym(_libref, "lwip_ntohs");
+ _ipaddr_ntoa = (char*(*)(IPADDR_NTOA_SIG))dlsym(_libref, "ipaddr_ntoa");
+ _etharp_output = (err_t(*)(ETHARP_OUTPUT_SIG))dlsym(_libref, "etharp_output");
+ _ethernet_input = (err_t(*)(ETHERNET_INPUT_SIG))dlsym(_libref, "ethernet_input");
+ _tcp_input = (void(*)(TCP_INPUT_SIG))dlsym(_libref, "tcp_input");
+ _ip_input = (err_t(*)(IP_INPUT_SIG))dlsym(_libref, "ip_input");
+ _netif_set_default = (void(*)(NETIF_SET_DEFAULT_SIG))dlsym(_libref, "netif_set_default");
+ _netif_add = (struct netif*(*)(NETIF_ADD_SIG))dlsym(_libref, "netif_add");
+ _netif_set_up = (void(*)(NETIF_SET_UP_SIG))dlsym(_libref, "netif_set_up");
+ _netif_poll = (void(*)(NETIF_POLL_SIG))dlsym(_libref, "netif_poll");
+ }
+
+ ~LWIPStack()
+ {
+ if (_libref)
+ dlclose(_libref);
+ }
+
+ inline void lwip_init() throw() { Mutex::Lock _l(_lock); return _lwip_init(); }
+ inline err_t tcp_write(TCP_WRITE_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_write(pcb,arg,len,apiflags); }
+ inline void tcp_sent(TCP_SENT_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_sent(pcb,sent); }
+ inline struct tcp_pcb * tcp_new(TCP_NEW_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_new(); }
+ inline u16_t tcp_sndbuf(TCP_SNDBUF_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_sndbuf(pcb); }
+ inline err_t tcp_connect(TCP_CONNECT_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_connect(pcb,ipaddr,port,connected); }
+ inline void tcp_recv(TCP_RECV_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_recv(pcb,recv); }
+ inline void tcp_recved(TCP_RECVED_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_recved(pcb,len); }
+ inline void tcp_err(TCP_ERR_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_err(pcb,err); }
+ inline void tcp_poll(TCP_POLL_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_poll(pcb,poll,interval); }
+ inline void tcp_arg(TCP_ARG_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_arg(pcb,arg); }
+ inline err_t tcp_close(TCP_CLOSE_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_close(pcb); }
+ inline void tcp_abort(TCP_ABORT_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_abort(pcb); }
+ inline err_t tcp_output(TCP_OUTPUT_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_output(pcb); }
+ inline void tcp_accept(TCP_ACCEPT_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_accept(pcb,accept); }
+ inline struct tcp_pcb * tcp_listen(TCP_LISTEN_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_listen(pcb); }
+ inline struct tcp_pcb * tcp_listen_with_backlog(TCP_LISTEN_WITH_BACKLOG_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_listen_with_backlog(pcb,backlog); }
+ inline err_t tcp_bind(TCP_BIND_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_bind(pcb,ipaddr,port); }
+ inline void etharp_tmr(void) throw() { Mutex::Lock _l(_lock); return _etharp_tmr(); }
+ inline void tcp_tmr(void) throw() { Mutex::Lock _l(_lock); return _tcp_tmr(); }
+ inline u8_t pbuf_free(PBUF_FREE_SIG) throw() { Mutex::Lock _l(_lock); return _pbuf_free(p); }
+ inline struct pbuf * pbuf_alloc(PBUF_ALLOC_SIG) throw() { Mutex::Lock _l(_lock); return _pbuf_alloc(layer,length,type); }
+ inline u16_t lwip_htons(LWIP_HTONS_SIG) throw() { Mutex::Lock _l(_lock); return _lwip_htons(x); }
+ inline u16_t lwip_ntohs(LWIP_NTOHS_SIG) throw() { Mutex::Lock _l(_lock); return _lwip_ntohs(x); }
+ inline char* ipaddr_ntoa(IPADDR_NTOA_SIG) throw() { Mutex::Lock _l(_lock); return _ipaddr_ntoa(addr); }
+ inline err_t etharp_output(ETHARP_OUTPUT_SIG) throw() { Mutex::Lock _l(_lock); return _etharp_output(netif,q,ipaddr); }
+ inline err_t ethernet_input(ETHERNET_INPUT_SIG) throw() { Mutex::Lock _l(_lock); return _ethernet_input(p,netif); }
+ inline void tcp_input(TCP_INPUT_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_input(p,inp); }
+ inline err_t ip_input(IP_INPUT_SIG) throw() { Mutex::Lock _l(_lock); return _ip_input(p,inp); }
+ inline void netif_set_default(NETIF_SET_DEFAULT_SIG) throw() { Mutex::Lock _l(_lock); return _netif_set_default(netif); }
+ inline struct netif * netif_add(NETIF_ADD_SIG) throw() { Mutex::Lock _l(_lock); return _netif_add(netif,ipaddr,netmask,gw,state,init,input); }
+ inline void netif_set_up(NETIF_SET_UP_SIG) throw() { Mutex::Lock _l(_lock); return _netif_set_up(netif); }
+ inline void netif_poll(NETIF_POLL_SIG) throw() { Mutex::Lock _l(_lock); return _netif_poll(netif); }
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/netcon/NetconEthernetTap.cpp b/netcon/NetconEthernetTap.cpp
new file mode 100644
index 00000000..3f772883
--- /dev/null
+++ b/netcon/NetconEthernetTap.cpp
@@ -0,0 +1,1555 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifdef ZT_ENABLE_NETCON
+
+#include <algorithm>
+#include <utility>
+#include <dlfcn.h>
+#include <sys/poll.h>
+
+#include "NetconEthernetTap.hpp"
+
+#include "../node/Utils.hpp"
+#include "../osdep/OSUtils.hpp"
+#include "../osdep/Phy.hpp"
+
+#include "Intercept.h"
+#include "LWIPStack.hpp"
+
+#include "lwip/tcp_impl.h"
+#include "netif/etharp.h"
+#include "lwip/api.h"
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip_frag.h"
+#include "lwip/tcp.h"
+
+#include "common.inc.c"
+
+#define APPLICATION_POLL_FREQ 20
+#define ZT_LWIP_TCP_TIMER_INTERVAL 5
+#define STATUS_TMR_INTERVAL 3000 // How often we check connection statuses
+
+namespace ZeroTier {
+
+// ---------------------------------------------------------------------------
+
+/*
+static void clearscreen(){
+ fprintf(stderr, "\033[2J");
+}
+static void gotoxy(int x,int y) {
+ fprintf(stderr, "%c[%d;%df",0x1B,y,x);
+}
+*/
+
+// Gets the process/path name associated with a pid
+static void get_path_from_pid(char* dest, int pid)
+{
+ char ppath[80];
+ sprintf(ppath, "/proc/%d/exe", pid);
+ if (readlink (ppath, dest, 80) != -1){
+ }
+}
+
+// Gets the process/path name associated with a fd
+/*
+static void get_path_from_fd(char* dest, int pid, int fd)
+{
+ char ppfd[80];
+ sprintf(ppfd, "/proc/%d/fd/%d", pid, fd);
+ if (readlink (ppfd, dest, 80) != -1){
+ }
+}
+*/
+
+static err_t tapif_init(struct netif *netif)
+{
+ // Actual init functionality is in addIp() of tap
+ return ERR_OK;
+}
+
+static err_t low_level_output(struct netif *netif, struct pbuf *p)
+{
+ struct pbuf *q;
+ char buf[ZT_MAX_MTU+32];
+ char *bufptr;
+ int tot_len = 0;
+
+ ZeroTier::NetconEthernetTap *tap = (ZeroTier::NetconEthernetTap*)netif->state;
+
+ /* initiate transfer(); */
+ bufptr = buf;
+
+ for(q = p; q != NULL; q = q->next) {
+ /* Send the data from the pbuf to the interface, one pbuf at a
+ time. The size of the data in each pbuf is kept in the ->len
+ variable. */
+ /* send data from(q->payload, q->len); */
+ memcpy(bufptr, q->payload, q->len);
+ bufptr += q->len;
+ tot_len += q->len;
+ }
+
+ // [Send packet to network]
+ // Split ethernet header and feed into handler
+ struct eth_hdr *ethhdr;
+ ethhdr = (struct eth_hdr *)buf;
+
+ ZeroTier::MAC src_mac;
+ ZeroTier::MAC dest_mac;
+
+ src_mac.setTo(ethhdr->src.addr, 6);
+ dest_mac.setTo(ethhdr->dest.addr, 6);
+
+ tap->_handler(tap->_arg,tap->_nwid,src_mac,dest_mac,
+ Utils::ntoh((uint16_t)ethhdr->type),0,buf + sizeof(struct eth_hdr),tot_len - sizeof(struct eth_hdr));
+ return ERR_OK;
+}
+
+/*
+ * TCP connection administered by service
+ */
+class TcpConnection
+{
+public:
+ int perceived_fd;
+ int their_fd;
+ bool pending;
+ bool listening;
+ int pid;
+
+ unsigned long written;
+ unsigned long acked;
+
+ PhySocket *rpcSock;
+ PhySocket *dataSock;
+ struct tcp_pcb *pcb;
+
+ unsigned char buf[DEFAULT_READ_BUFFER_SIZE];
+ int idx;
+};
+
+/*
+ * A helper class for passing a reference to _phy to LWIP callbacks as a "state"
+ */
+class Larg
+{
+public:
+ NetconEthernetTap *tap;
+ TcpConnection *conn;
+ Larg(NetconEthernetTap *_tap, TcpConnection *conn) : tap(_tap), conn(conn) {}
+};
+
+// ---------------------------------------------------------------------------
+
+NetconEthernetTap::NetconEthernetTap(
+ const char *homePath,
+ const MAC &mac,
+ unsigned int mtu,
+ unsigned int metric,
+ uint64_t nwid,
+ const char *friendlyName,
+ void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+ void *arg) :
+ _nwid(nwid),
+ _handler(handler),
+ _arg(arg),
+ _phy(this,false,true),
+ _unixListenSocket((PhySocket *)0),
+ _mac(mac),
+ _homePath(homePath),
+ _mtu(mtu),
+ _enabled(true),
+ _run(true)
+{
+ char sockPath[4096],lwipPath[4096];
+ rpc_counter = -1;
+ Utils::snprintf(sockPath,sizeof(sockPath),"/tmp/.ztnc_%.16llx",_nwid,ZT_PATH_SEPARATOR_S,(unsigned long long)nwid);
+ _dev = sockPath; // in netcon mode, set device to be just the network ID
+
+ Utils::snprintf(lwipPath,sizeof(lwipPath),"%s%sliblwip.so",homePath,ZT_PATH_SEPARATOR_S);
+ lwipstack = new LWIPStack(lwipPath);
+ if(!lwipstack)
+ throw std::runtime_error("unable to dynamically load a new instance of liblwip.so (searched ZeroTier home path)");
+ lwipstack->lwip_init();
+
+ _unixListenSocket = _phy.unixListen(sockPath,(void *)this);
+ dwr(MSG_INFO, " NetconEthernetTap initialized!\n", _phy.getDescriptor(_unixListenSocket));
+ if (!_unixListenSocket)
+ throw std::runtime_error(std::string("unable to bind to ")+sockPath);
+ _thread = Thread::start(this);
+}
+
+NetconEthernetTap::~NetconEthernetTap()
+{
+ _run = false;
+ _phy.whack();
+ _phy.whack();
+ Thread::join(_thread);
+ _phy.close(_unixListenSocket,false);
+ delete lwipstack;
+}
+
+void NetconEthernetTap::setEnabled(bool en)
+{
+ _enabled = en;
+}
+
+bool NetconEthernetTap::enabled() const
+{
+ return _enabled;
+}
+
+bool NetconEthernetTap::addIp(const InetAddress &ip)
+{
+ Mutex::Lock _l(_ips_m);
+ if (std::find(_ips.begin(),_ips.end(),ip) == _ips.end()) {
+ _ips.push_back(ip);
+ std::sort(_ips.begin(),_ips.end());
+
+ if (ip.isV4()) {
+ // Set IP
+ static ip_addr_t ipaddr, netmask, gw;
+ IP4_ADDR(&gw,192,168,0,1);
+ ipaddr.addr = *((u32_t *)ip.rawIpData());
+ netmask.addr = *((u32_t *)ip.netmask().rawIpData());
+
+ // Set up the lwip-netif for LWIP's sake
+ lwipstack->netif_add(&interface,&ipaddr, &netmask, &gw, NULL, tapif_init, lwipstack->_ethernet_input);
+ interface.state = this;
+ interface.output = lwipstack->_etharp_output;
+ _mac.copyTo(interface.hwaddr, 6);
+ interface.mtu = _mtu;
+ interface.name[0] = 't';
+ interface.name[1] = 'p';
+ interface.linkoutput = low_level_output;
+ interface.hwaddr_len = 6;
+ interface.flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP;
+ lwipstack->netif_set_default(&interface);
+ lwipstack->netif_set_up(&interface);
+ }
+ }
+ return true;
+}
+
+bool NetconEthernetTap::removeIp(const InetAddress &ip)
+{
+ Mutex::Lock _l(_ips_m);
+ std::vector<InetAddress>::iterator i(std::find(_ips.begin(),_ips.end(),ip));
+ if (i == _ips.end())
+ return false;
+ _ips.erase(i);
+ if (ip.isV4()) {
+ // TODO: dealloc from LWIP
+ }
+ return true;
+}
+
+std::vector<InetAddress> NetconEthernetTap::ips() const
+{
+ Mutex::Lock _l(_ips_m);
+ return _ips;
+}
+
+void NetconEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
+{
+ struct pbuf *p,*q;
+ if (!_enabled)
+ return;
+
+ struct eth_hdr ethhdr;
+ from.copyTo(ethhdr.src.addr, 6);
+ to.copyTo(ethhdr.dest.addr, 6);
+ ethhdr.type = Utils::hton((uint16_t)etherType);
+
+ // We allocate a pbuf chain of pbufs from the pool.
+ p = lwipstack->pbuf_alloc(PBUF_RAW, len+sizeof(struct eth_hdr), PBUF_POOL);
+
+ if (p != NULL) {
+ const char *dataptr = reinterpret_cast<const char *>(data);
+
+ // First pbuf gets ethernet header at start
+ q = p;
+ if (q->len < sizeof(ethhdr)) {
+ dwr(MSG_ERROR, "_put(): Dropped packet: first pbuf smaller than ethernet header\n");
+ return;
+ }
+ memcpy(q->payload,&ethhdr,sizeof(ethhdr));
+ memcpy((char*)q->payload + sizeof(ethhdr),dataptr,q->len - sizeof(ethhdr));
+ dataptr += q->len - sizeof(ethhdr);
+
+ // Remaining pbufs (if any) get rest of data
+ while ((q = q->next)) {
+ memcpy(q->payload,dataptr,q->len);
+ dataptr += q->len;
+ }
+ } else {
+ dwr(MSG_ERROR, "put(): Dropped packet: no pbufs available\n");
+ return;
+ }
+
+ {
+ Mutex::Lock _l2(lwipstack->_lock);
+ if(interface.input(p, &interface) != ERR_OK) {
+ dwr(MSG_ERROR, "put(): Error while RXing packet (netif->input)\n");
+ }
+ }
+}
+
+std::string NetconEthernetTap::deviceName() const
+{
+ return _dev;
+}
+
+void NetconEthernetTap::setFriendlyName(const char *friendlyName) {
+}
+
+void NetconEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed)
+{
+ std::vector<MulticastGroup> newGroups;
+ Mutex::Lock _l(_multicastGroups_m);
+
+ // TODO: get multicast subscriptions from LWIP
+
+ std::vector<InetAddress> allIps(ips());
+ for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
+ newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip));
+
+ std::sort(newGroups.begin(),newGroups.end());
+ std::unique(newGroups.begin(),newGroups.end());
+
+ for(std::vector<MulticastGroup>::iterator m(newGroups.begin());m!=newGroups.end();++m) {
+ if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m))
+ added.push_back(*m);
+ }
+ for(std::vector<MulticastGroup>::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) {
+ if (!std::binary_search(newGroups.begin(),newGroups.end(),*m))
+ removed.push_back(*m);
+ }
+ _multicastGroups.swap(newGroups);
+}
+
+TcpConnection *NetconEthernetTap::getConnectionByTheirFD(PhySocket *sock, int fd)
+{
+ for(size_t i=0; i<tcp_connections.size(); i++) {
+ if(tcp_connections[i]->perceived_fd == fd && tcp_connections[i]->rpcSock == sock)
+ return tcp_connections[i];
+ }
+ return NULL;
+}
+
+/*
+ * Dumps service state in 80x25 when debug mode is off
+ */
+void NetconEthernetTap::compact_dump()
+{
+ /*
+ clearscreen();
+ gotoxy(0,0);
+
+ fprintf(stderr, "ZeroTier - Network Containers Service [State Dump]\n\r");
+ fprintf(stderr, " RPC Sockets = %d\n\r", rpc_sockets.size());
+ fprintf(stderr, " TCP Connections = %d\n\r", tcp_connections.size());
+
+ for(size_t i=0; i<rpc_sockets.size(); i++) {
+ int rpc_fd = _phy.getDescriptor(rpc_sockets[i]);
+ char buf[80];
+ int pid = pidmap[rpc_sockets[i]];
+ memset(&buf, '\0', 80);
+ get_path_from_pid(buf, pid);
+ fprintf(stderr, "\n Client(addr=0x%x, rpc=%d, pid=%d) %s\n", rpc_sockets[i], rpc_fd, pid, buf);
+ for(size_t j=0; j<tcp_connections.size(); j++) {
+ memset(&buf, '\0', 80);
+ get_path_from_pid(buf, tcp_connections[j]->pid);
+ if(tcp_connections[j]->rpcSock==rpc_sockets[i]) {
+ fprintf(stderr, "\t\tpath\t\t= %s\n", buf);
+ }
+ }
+ }
+ */
+ for(size_t i=0; i<rpc_sockets.size(); i++) {
+ fprintf(stderr, "\n\n\nrpc(%d)\n", _phy.getDescriptor(rpc_sockets[i]));
+ for(size_t j=0; j<tcp_connections.size(); j++) {
+ if(_phy.getDescriptor(tcp_connections[j]->rpcSock) == _phy.getDescriptor(rpc_sockets[i]))
+ fprintf(stderr, "\t(%d) ----> (%d)\n\n", _phy.getDescriptor(tcp_connections[j]->dataSock), tcp_connections[j]->perceived_fd);
+ }
+ }
+}
+
+/*
+ * Dumps service state
+ */
+void NetconEthernetTap::dump()
+{
+ fprintf(stderr, "\n\n---\n\ndie(): BEGIN SERVICE STATE DUMP\n");
+ fprintf(stderr, "*** IF YOU SEE THIS, EMAIL THE DUMP TEXT TO joseph.henry@zerotier.com ***\n");
+ fprintf(stderr, " tcp_conns = %lu, rpc_socks = %lu\n", tcp_connections.size(), rpc_sockets.size());
+
+ // TODO: Add logic to detect bad mapping conditions
+ for(size_t i=0; i<rpc_sockets.size(); i++) {
+ for(size_t j=0; j<rpc_sockets.size(); j++) {
+ if(j != i && rpc_sockets[i] == rpc_sockets[j]) {
+ fprintf(stderr, "Duplicate PhySockets found! (0x%p)\n", rpc_sockets[i]);
+ }
+ }
+ }
+
+ // Dump the state of the service mapping
+ for(size_t i=0; i<rpc_sockets.size(); i++) {
+ int rpc_fd = _phy.getDescriptor(rpc_sockets[i]);
+ char buf[80];
+ int pid = pidmap[rpc_sockets[i]];
+ get_path_from_pid(buf, pid);
+
+ fprintf(stderr, "\nClient(addr=0x%p, rpc=%d, pid=%d) %s\n", rpc_sockets[i], rpc_fd, pid, buf);
+ for(size_t j=0; j<tcp_connections.size(); j++) {
+ get_path_from_pid(buf, tcp_connections[j]->pid);
+ if(tcp_connections[j]->rpcSock==rpc_sockets[i]){
+ fprintf(stderr, " |\n");
+ fprintf(stderr, " |-Connection(0x%p):\n", tcp_connections[j]);
+ fprintf(stderr, " | path\t\t\t= %s\n", buf);
+ fprintf(stderr, " | perceived_fd\t\t= %d\t(fd)\n", tcp_connections[j]->perceived_fd);
+ fprintf(stderr, " | their_fd\t\t= %d\t(fd)\n", tcp_connections[j]->their_fd);
+ fprintf(stderr, " | dataSock(0x%p)\t= %d\t(fd)\n", tcp_connections[j]->dataSock, _phy.getDescriptor(tcp_connections[j]->dataSock));
+ fprintf(stderr, " | rpcSock(0x%p)\t= %d\t(fd)\n", tcp_connections[j]->rpcSock, _phy.getDescriptor(tcp_connections[j]->rpcSock));
+ fprintf(stderr, " | pending\t\t= %d\n", tcp_connections[j]->pending);
+ fprintf(stderr, " | listening\t\t= %d\n", tcp_connections[j]->listening);
+ fprintf(stderr, " \\------pcb(0x%p)->state\t= %d\n", tcp_connections[j]->pcb, tcp_connections[j]->pcb->state);
+ }
+ }
+ }
+ fprintf(stderr, "\n\ndie(): END SERVICE STATE DUMP\n\n---\n\n");
+}
+
+/*
+ * Dumps service state and then exits
+ */
+void NetconEthernetTap::die(int exret) {
+ dump();
+ exit(exret);
+}
+
+/*
+ * Closes a TcpConnection and associated LWIP PCB strcuture.
+ */
+void NetconEthernetTap::closeConnection(TcpConnection *conn)
+{
+ if(!conn)
+ return;
+ dwr(MSG_DEBUG, " closeConnection(%x, %d)\n", conn->pcb, _phy.getDescriptor(conn->dataSock));
+ //lwipstack->_tcp_sent(conn->pcb, NULL);
+ //lwipstack->_tcp_recv(conn->pcb, NULL);
+ //lwipstack->_tcp_err(conn->pcb, NULL);
+ //lwipstack->_tcp_poll(conn->pcb, NULL, 0);
+ //lwipstack->_tcp_arg(conn->pcb, NULL);
+ if(lwipstack->_tcp_close(conn->pcb) != ERR_OK) {
+ dwr(MSG_ERROR, " closeConnection(): Error while calling tcp_close()\n");
+ exit(0);
+ }
+ else {
+ if(conn->dataSock) {
+ close(_phy.getDescriptor(conn->dataSock));
+ _phy.close(conn->dataSock,false);
+ }
+ /* Eventually we might want to use a map here instead */
+ for(int i=0; i<tcp_connections.size(); i++) {
+ if(tcp_connections[i] == conn) {
+ tcp_connections.erase(tcp_connections.begin() + i);
+ delete conn;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Close a single RPC connection and associated PhySocket
+ */
+void NetconEthernetTap::closeClient(PhySocket *sock)
+{
+ for(size_t i=0; i<rpc_sockets.size(); i++) {
+ if(rpc_sockets[i] == sock){
+ rpc_sockets.erase(rpc_sockets.begin() + i);
+ break;
+ }
+ }
+ close(_phy.getDescriptor(sock));
+ _phy.close(sock);
+}
+
+/*
+ * Close all RPC and TCP connections
+ */
+void NetconEthernetTap::closeAll()
+{
+ while(rpc_sockets.size())
+ closeClient(rpc_sockets.front());
+ while(tcp_connections.size())
+ closeConnection(tcp_connections.front());
+}
+
+#include <sys/resource.h>
+
+void NetconEthernetTap::threadMain()
+ throw()
+{
+ uint64_t prev_tcp_time = 0;
+ uint64_t prev_status_time = 0;
+ uint64_t prev_etharp_time = 0;
+
+/*
+ fprintf(stderr, "- MEM_SIZE = %dM\n", MEM_SIZE / (1024*1024));
+ fprintf(stderr, "- PBUF_POOL_SIZE = %d\n", PBUF_POOL_SIZE);
+ fprintf(stderr, "- PBUF_POOL_BUFSIZE = %d\n", PBUF_POOL_BUFSIZE);
+ fprintf(stderr, "- MEMP_NUM_PBUF = %d\n", MEMP_NUM_PBUF);
+ fprintf(stderr, "- MEMP_NUM_TCP_PCB = %d\n", MEMP_NUM_TCP_PCB);
+ fprintf(stderr, "- MEMP_NUM_TCP_PCB_LISTEN = %d\n", MEMP_NUM_TCP_PCB_LISTEN);
+ fprintf(stderr, "- MEMP_NUM_TCP_SEG = %d\n\n", MEMP_NUM_TCP_SEG);
+
+ fprintf(stderr, "- TCP_SND_BUF = %dK\n", TCP_SND_BUF / 1024);
+ fprintf(stderr, "- TCP_SND_QUEUELEN = %d\n\n", TCP_SND_QUEUELEN);
+
+ fprintf(stderr, "- TCP_WND = %d\n", TCP_WND);
+ fprintf(stderr, "- TCP_MSS = %d\n", TCP_MSS);
+ fprintf(stderr, "- TCP_MAXRTX = %d\n", TCP_MAXRTX);
+ fprintf(stderr, "- IP_REASSEMBLY = %d\n\n", IP_REASSEMBLY);
+ fprintf(stderr, "- ARP_TMR_INTERVAL = %d\n", ARP_TMR_INTERVAL);
+ fprintf(stderr, "- TCP_TMR_INTERVAL = %d\n", TCP_TMR_INTERVAL);
+ fprintf(stderr, "- IP_TMR_INTERVAL = %d\n", IP_TMR_INTERVAL);
+*/
+
+ // Main timer loop
+ while (_run) {
+ uint64_t now = OSUtils::now();
+ uint64_t since_tcp = now - prev_tcp_time;
+ uint64_t since_etharp = now - prev_etharp_time;
+ uint64_t since_status = now - prev_status_time;
+ uint64_t tcp_remaining = ZT_LWIP_TCP_TIMER_INTERVAL;
+ uint64_t etharp_remaining = ARP_TMR_INTERVAL;
+ uint64_t status_remaining = STATUS_TMR_INTERVAL;
+
+ // Connection prunning
+ if (since_status >= STATUS_TMR_INTERVAL) {
+ //compact_dump();
+ prev_status_time = now;
+ status_remaining = STATUS_TMR_INTERVAL - since_status;
+ if(rpc_sockets.size() || tcp_connections.size()) {
+
+ // dump();
+ // Here we will periodically check the list of rpc_sockets for those that
+ // do not currently have any data connection associated with them. If they are
+ // unused, then we will try to read from them, if they fail, we can safely assume
+ // that the client has closed their end and we can close ours
+ for(size_t i = 0; i<tcp_connections.size(); i++) {
+ if(tcp_connections[i]->listening) {
+ char c;
+ if (read(_phy.getDescriptor(tcp_connections[i]->dataSock), &c, 1) < 0) {
+ // Still in listening state
+ }
+ else {
+ // Here we should handle the case there there is incoming data (?)
+ dwr(MSG_DEBUG, " tap_thread(): Listening socketpair closed. Removing RPC connection (%d)\n",
+ _phy.getDescriptor(tcp_connections[i]->dataSock));
+ closeConnection(tcp_connections[i]);
+ }
+ }
+ }
+ }
+ //dwr(4, " tap_thread(): tcp_conns = %d, rpc_socks = %d\n", tcp_connections.size(), rpc_sockets.size());
+ for(size_t i=0, associated = 0; i<rpc_sockets.size(); i++, associated = 0) {
+ for(size_t j=0; j<tcp_connections.size(); j++) {
+ if (tcp_connections[j]->rpcSock == rpc_sockets[i])
+ associated++;
+ }
+ if(!associated){
+ // No TCP connections are associated, this is a candidate for removal
+ int fd = _phy.getDescriptor(rpc_sockets[i]);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ unsigned char tmpbuf[BUF_SZ];
+ int n;
+ if((n = read(fd,&tmpbuf,BUF_SZ)) < 0) {
+ dwr(MSG_DEBUG, " tap_thread(): closing RPC (%d)\n", _phy.getDescriptor(rpc_sockets[i]));
+ closeClient(rpc_sockets[i]);
+ }
+ // < 0 is failure
+ // 0 nothing to read, RPC still active
+ // > 0 RPC data read, handle it
+ else if (n > 0) {
+ // Handle RPC call, this is rare
+ dwr(MSG_DEBUG, " tap_thread(): RPC read during connection check (%d bytes)\n", n);
+ phyOnUnixData(rpc_sockets[i],_phy.getuptr(rpc_sockets[i]),&tmpbuf,BUF_SZ);
+ }
+ }
+ }
+ }
+ // Main TCP/ETHARP timer section
+ if (since_tcp >= ZT_LWIP_TCP_TIMER_INTERVAL) {
+ prev_tcp_time = now;
+ lwipstack->tcp_tmr();
+ } else {
+ tcp_remaining = ZT_LWIP_TCP_TIMER_INTERVAL - since_tcp;
+ }
+ if (since_etharp >= ARP_TMR_INTERVAL) {
+ prev_etharp_time = now;
+ lwipstack->etharp_tmr();
+ } else {
+ etharp_remaining = ARP_TMR_INTERVAL - since_etharp;
+ }
+ _phy.poll((unsigned long)std::min(tcp_remaining,etharp_remaining));
+ }
+ closeAll();
+ dlclose(lwipstack->_libref);
+}
+
+// Unused -- no UDP or TCP from this thread/Phy<>
+void NetconEthernetTap::phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len) {}
+void NetconEthernetTap::phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) {}
+void NetconEthernetTap::phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) {}
+void NetconEthernetTap::phyOnTcpClose(PhySocket *sock,void **uptr) {}
+void NetconEthernetTap::phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) {}
+void NetconEthernetTap::phyOnTcpWritable(PhySocket *sock,void **uptr) {}
+
+void NetconEthernetTap::phyOnUnixClose(PhySocket *sock,void **uptr) {
+ dwr(MSG_DEBUG, " phyOnUnixClose(sock=0x%x, uptr=0x%x): fd = %d\n", sock, uptr, _phy.getDescriptor(sock));
+ TcpConnection *conn = (TcpConnection*)*uptr;
+ closeConnection(conn);
+}
+
+/*
+ * Handles data on a client's data buffer. Data is sent to LWIP to be enqueued.
+ */
+void NetconEthernetTap::phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable)
+{
+ if(readable) {
+ TcpConnection *conn = (TcpConnection*)*uptr;
+ if(conn->dataSock) { // Sometimes a connection may be closed via nc_recved, check first
+ lwipstack->_lock.lock();
+ handle_write(conn);
+ lwipstack->_lock.unlock();
+ }
+ }
+ else {
+ dwr(MSG_ERROR, "phyOnFileDescriptorActivity(): PhySocket not readable\n");
+ }
+}
+
+/*
+ * Add a new PhySocket for the client connections
+ */
+void NetconEthernetTap::phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN) {
+ dwr(MSG_DEBUG, " phyOnUnixAccept(): accepting new connection\n");
+ if(find(rpc_sockets.begin(), rpc_sockets.end(), sockN) != rpc_sockets.end()){
+ dwr(MSG_ERROR, " phyOnUnixAccept(): SockN (0x%x) already exists!\n", sockN);
+ return;
+ }
+ rpc_sockets.push_back(sockN);
+}
+
+/*
+ * Processes incoming data on a client-specific RPC connection
+ */
+void NetconEthernetTap::phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len)
+{
+ pid_t pid, tid;
+ int rpc_count;
+ char cmd, timestamp[20];
+ void *payload;
+ unload_rpc(data, pid, tid, rpc_count, timestamp, cmd, payload);
+ dwr(MSG_DEBUG, "\n\nRPC: (pid=%d, tid=%d, rpc_count=%d, timestamp=%s, cmd=%d\n", pid, tid, rpc_count, timestamp, cmd);
+ unsigned char *buf = (unsigned char*)data;
+
+ switch(cmd)
+ {
+ case RPC_SOCKET:
+ dwr(MSG_DEBUG, "RPC_SOCKET\n");
+ struct socket_st socket_rpc;
+ memcpy(&socket_rpc, &buf[IDX_PAYLOAD+1], sizeof(struct socket_st));
+
+ if(rpc_count==rpc_counter) {
+ dwr(MSG_ERROR, "Detected repeat RPC.\n");
+ //return;
+ }
+ else {
+ rpc_counter = rpc_count;
+ }
+
+ TcpConnection * new_conn;
+ if((new_conn = handle_socket(sock, uptr, &socket_rpc))) {
+ pidmap[sock] = pid;
+ new_conn->pid = pid;
+ }
+ break;
+ case RPC_LISTEN:
+ dwr(MSG_DEBUG, "RPC_LISTEN\n");
+ struct listen_st listen_rpc;
+ memcpy(&listen_rpc, &buf[IDX_PAYLOAD+1], sizeof(struct listen_st));
+ handle_listen(sock, uptr, &listen_rpc);
+ break;
+ case RPC_BIND:
+ dwr(MSG_DEBUG, "RPC_BIND\n");
+ struct bind_st bind_rpc;
+ memcpy(&bind_rpc, &buf[IDX_PAYLOAD+1], sizeof(struct bind_st));
+ handle_bind(sock, uptr, &bind_rpc);
+ break;
+ case RPC_CONNECT:
+ dwr(MSG_DEBUG, "RPC_CONNECT\n");
+ struct connect_st connect_rpc;
+ memcpy(&connect_rpc, &buf[IDX_PAYLOAD+1], sizeof(struct connect_st));
+ handle_connect(sock, uptr, &connect_rpc);
+ break;
+ case RPC_MAP:
+ dwr(MSG_DEBUG, "RPC_MAP (len = %d)\n", len);
+ int newfd;
+ memcpy(&newfd, &buf[IDX_PAYLOAD+1], sizeof(int));
+ handle_retval(sock, uptr, rpc_count, newfd);
+ break;
+ case RPC_MAP_REQ:
+ dwr(MSG_DEBUG, "RPC_MAP_REQ\n");
+ handle_map_request(sock, uptr, buf);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Send a 'retval' and 'errno' to the client for an RPC over connection->rpcSock
+ */
+int NetconEthernetTap::send_return_value(TcpConnection *conn, int retval, int _errno = 0)
+{
+ if(conn) {
+ int n = send_return_value(_phy.getDescriptor(conn->rpcSock), retval, _errno);
+ if(n > 0)
+ conn->pending = false;
+ else {
+ dwr(MSG_ERROR, " Unable to send return value to the intercept. Closing connection\n");
+ closeConnection(conn);
+ }
+ return n;
+ }
+ return -1;
+}
+
+int NetconEthernetTap::send_return_value(int fd, int retval, int _errno = 0)
+{
+ dwr(MSG_DEBUG, " send_return_value(): fd = %d, retval = %d, errno = %d\n", fd, retval, _errno);
+ int sz = sizeof(char) + sizeof(retval) + sizeof(errno);
+ char retmsg[sz];
+ memset(&retmsg, '\0', sizeof(retmsg));
+ retmsg[0]=RPC_RETVAL;
+ memcpy(&retmsg[1], &retval, sizeof(retval));
+ memcpy(&retmsg[1]+sizeof(retval), &_errno, sizeof(_errno));
+ return write(fd, &retmsg, sz);
+}
+
+/*------------------------------------------------------------------------------
+--------------------------------- LWIP callbacks -------------------------------
+------------------------------------------------------------------------------*/
+
+// NOTE: these are called from within LWIP, meaning that lwipstack->_lock is ALREADY
+// locked in this case!
+
+/*
+ * Callback from LWIP for when a connection has been accepted and the PCB has been
+ * put into an ACCEPT state.
+ *
+ * A socketpair is created, one end is kept and wrapped into a PhySocket object
+ * for use in the main ZT I/O loop, and one end is sent to the client. The client
+ * is then required to tell the service what new file descriptor it has allocated
+ * for this connection. After the mapping is complete, the accepted socket can be
+ * used.
+ *
+ * @param associated service state object
+ * @param newly allocated PCB
+ * @param error code
+ * @return ERR_OK if everything is ok, -1 otherwise
+
+ i := should be implemented in intercept lib
+ I := is implemented in intercept lib
+ X := is implemented in service
+ ? := required treatment Unknown
+ - := Not needed
+
+ [ ] EAGAIN or EWOULDBLOCK - The socket is marked nonblocking and no connections are present
+ to be accepted. POSIX.1-2001 allows either error to be returned for
+ this case, and does not require these constants to have the same value,
+ so a portable application should check for both possibilities.
+ [I] EBADF - The descriptor is invalid.
+ [I] ECONNABORTED - A connection has been aborted.
+ [i] EFAULT - The addr argument is not in a writable part of the user address space.
+ [-] EINTR - The system call was interrupted by a signal that was caught before a valid connection arrived; see signal(7).
+ [I] EINVAL - Socket is not listening for connections, or addrlen is invalid (e.g., is negative).
+ [I] EINVAL - (accept4()) invalid value in flags.
+ [I] EMFILE - The per-process limit of open file descriptors has been reached.
+ [ ] ENFILE - The system limit on the total number of open files has been reached.
+ [ ] ENOBUFS, ENOMEM - Not enough free memory. This often means that the memory allocation is
+ limited by the socket buffer limits, not by the system memory.
+ [I] ENOTSOCK - The descriptor references a file, not a socket.
+ [I] EOPNOTSUPP - The referenced socket is not of type SOCK_STREAM.
+ [ ] EPROTO - Protocol error.
+
+ *
+ */
+err_t NetconEthernetTap::nc_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+ dwr(MSG_DEBUG, " nc_accept()\n");
+ Larg *l = (Larg*)arg;
+ TcpConnection *conn = l->conn;
+ NetconEthernetTap *tap = l->tap;
+ int listening_fd = tap->_phy.getDescriptor(conn->dataSock);
+
+ if(conn) {
+ ZT_PHY_SOCKFD_TYPE fds[2];
+ if(socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) {
+ if(errno < 0) {
+ l->tap->send_return_value(conn, -1, errno);
+ dwr(MSG_ERROR, " nc_accept(): unable to create socketpair\n");
+ return ERR_MEM;
+ }
+ }
+ TcpConnection *new_tcp_conn = new TcpConnection();
+ new_tcp_conn->dataSock = tap->_phy.wrapSocket(fds[0], new_tcp_conn);
+ new_tcp_conn->rpcSock = conn->rpcSock;
+ new_tcp_conn->pcb = newpcb;
+ new_tcp_conn->their_fd = fds[1];
+ tap->tcp_connections.push_back(new_tcp_conn);
+ dwr(MSG_DEBUG, " nc_accept(): socketpair = {%d, %d}\n", fds[0], fds[1]);
+ int n, send_fd = tap->_phy.getDescriptor(conn->rpcSock);
+ if((n = send(listening_fd, "z", 1, MSG_NOSIGNAL)) < 0) {
+ dwr(MSG_ERROR, " nc_accept(): Error: [send(listening_fd,...) = MSG_NOSIGNAL].\n");
+ return -1;
+ }
+ else if(n > 0) {
+ if(sock_fd_write(send_fd, fds[1]) > 0) {
+ close(fds[1]); // close other end of socketpair
+ new_tcp_conn->pending = true;
+ }
+ else {
+ dwr(MSG_ERROR, " nc_accept(%d): unable to send fd to client\n", listening_fd);
+ }
+ }
+ else {
+ dwr(MSG_ERROR, " nc_accept(%d): error writing signal byte (send_fd = %d, perceived_fd = %d)\n", listening_fd, send_fd, fds[1]);
+ return -1;
+ }
+ tap->lwipstack->_tcp_arg(newpcb, new Larg(tap, new_tcp_conn));
+ tap->lwipstack->_tcp_recv(newpcb, nc_recved);
+ tap->lwipstack->_tcp_err(newpcb, nc_err);
+ tap->lwipstack->_tcp_sent(newpcb, nc_sent);
+ tap->lwipstack->_tcp_poll(newpcb, nc_poll, 1);
+ tcp_accepted(conn->pcb); // Let lwIP know that it can queue additional incoming connections
+ return ERR_OK;
+ }
+ else {
+ dwr(MSG_ERROR, " nc_accept(%d): can't locate Connection object for PCB.\n", listening_fd);
+ }
+ return -1;
+}
+
+/*
+ * Callback from LWIP for when data is available to be read from the network.
+ *
+ * Data is in the form of a linked list of struct pbufs, it is then recombined and
+ * send to the client over the associated unix socket.
+ *
+ * @param associated service state object
+ * @param allocated PCB
+ * @param chain of pbufs
+ * @param error code
+ * @return ERR_OK if everything is ok, -1 otherwise
+ *
+ */
+err_t NetconEthernetTap::nc_recved(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
+{
+ dwr(MSG_DEBUG, " nc_recved()\n");
+ Larg *l = (Larg*)arg;
+ int n;
+ struct pbuf* q = p;
+
+ if(!l->conn) {
+ dwr(MSG_ERROR, " nc_recved(): no connection object\n");
+ return ERR_OK; // ?
+ }
+ if(p == NULL) {
+ if(l->conn) {
+ dwr(MSG_INFO, " nc_recved(): closing connection\n");
+ l->tap->closeConnection(l->conn);
+ return ERR_ABRT;
+ }
+ else {
+ dwr(MSG_ERROR, " nc_recved(): can't locate connection via (arg)\n");
+ }
+ return err;
+ }
+ q = p;
+ while(p != NULL) { // Cycle through pbufs and write them to the socket
+ if(p->len <= 0)
+ break; // ?
+ if((n = l->tap->_phy.streamSend(l->conn->dataSock,p->payload, p->len)) > 0) {
+ if(n < p->len) {
+ dwr(MSG_INFO, " nc_recved(): unable to write entire pbuf to buffer\n");
+ }
+ l->tap->lwipstack->_tcp_recved(tpcb, n); // TODO: would it be more efficient to call this once at the end?
+ dwr(MSG_DEBUG, " nc_recved(): wrote %d bytes to (%d)\n", n, l->tap->_phy.getDescriptor(l->conn->dataSock));
+ }
+ else {
+ dwr(MSG_INFO, " nc_recved(): No data written to intercept buffer (%d)\n", l->tap->_phy.getDescriptor(l->conn->dataSock));
+ }
+ p = p->next;
+ }
+ l->tap->lwipstack->_pbuf_free(q); // free pbufs
+ return ERR_OK;
+}
+
+/*
+ * Callback from LWIP when an internal error is associtated with the given (arg)
+ *
+ * Since the PCB related to this error might no longer exist, only its perviously
+ * associated (arg) is provided to us.
+ *
+ * @param associated service state object
+ * @param error code
+ *
+ */
+void NetconEthernetTap::nc_err(void *arg, err_t err)
+{
+ dwr(MSG_DEBUG, "nc_err()\n");
+ Larg *l = (Larg*)arg;
+ if(!l->conn)
+ dwr(MSG_ERROR, "nc_err(): Connection is NULL!\n");
+
+ switch(err)
+ {
+ case ERR_MEM:
+ dwr(MSG_ERROR, "nc_err(): ERR_MEM->ENOMEM\n");
+ l->tap->send_return_value(l->conn, -1, ENOMEM);
+ break;
+ case ERR_BUF:
+ dwr(MSG_ERROR, "nc_err(): ERR_BUF->ENOBUFS\n");
+ l->tap->send_return_value(l->conn, -1, ENOBUFS);
+ break;
+ case ERR_TIMEOUT:
+ dwr(MSG_ERROR, "nc_err(): ERR_TIMEOUT->ETIMEDOUT\n");
+ l->tap->send_return_value(l->conn, -1, ETIMEDOUT);
+ break;
+ case ERR_RTE:
+ dwr(MSG_ERROR, "nc_err(): ERR_RTE->ENETUNREACH\n");
+ l->tap->send_return_value(l->conn, -1, ENETUNREACH);
+ break;
+ case ERR_INPROGRESS:
+ dwr(MSG_ERROR, "nc_err(): ERR_INPROGRESS->EINPROGRESS\n");
+ l->tap->send_return_value(l->conn, -1, EINPROGRESS);
+ break;
+ case ERR_VAL:
+ dwr(MSG_ERROR, "nc_err(): ERR_VAL->EINVAL\n");
+ l->tap->send_return_value(l->conn, -1, EINVAL);
+ break;
+ case ERR_WOULDBLOCK:
+ dwr(MSG_ERROR, "nc_err(): ERR_WOULDBLOCK->EWOULDBLOCK\n");
+ l->tap->send_return_value(l->conn, -1, EWOULDBLOCK);
+ break;
+ case ERR_USE:
+ dwr(MSG_ERROR, "nc_err(): ERR_USE->EADDRINUSE\n");
+ l->tap->send_return_value(l->conn, -1, EADDRINUSE);
+ break;
+ case ERR_ISCONN:
+ dwr(MSG_ERROR, "nc_err(): ERR_ISCONN->EISCONN\n");
+ l->tap->send_return_value(l->conn, -1, EISCONN);
+ break;
+ case ERR_ABRT:
+ dwr(MSG_ERROR, "nc_err(): ERR_ABRT->ECONNREFUSED\n");
+ l->tap->send_return_value(l->conn, -1, ECONNREFUSED);
+ break;
+
+ // FIXME: Below are errors which don't have a standard errno correlate
+
+ case ERR_RST:
+ l->tap->send_return_value(l->conn, -1, -1);
+ break;
+ case ERR_CLSD:
+ l->tap->send_return_value(l->conn, -1, -1);
+ break;
+ case ERR_CONN:
+ l->tap->send_return_value(l->conn, -1, -1);
+ break;
+ case ERR_ARG:
+ l->tap->send_return_value(l->conn, -1, -1);
+ break;
+ case ERR_IF:
+ l->tap->send_return_value(l->conn, -1, -1);
+ break;
+ default:
+ break;
+ }
+ dwr(MSG_ERROR, "nc_err(): closing connection\n");
+ l->tap->closeConnection(l->conn);
+}
+
+/*
+ * Callback from LWIP to do whatever work we might need to do.
+ *
+ * @param associated service state object
+ * @param PCB we're polling on
+ * @return ERR_OK if everything is ok, -1 otherwise
+ *
+ */
+err_t NetconEthernetTap::nc_poll(void* arg, struct tcp_pcb *tpcb)
+{
+ //Larg *l = (Larg*)arg;
+ /*
+ Larg *l = (Larg*)arg;
+ TcpConnection *conn = l->conn;
+ NetconEthernetTap *tap = l->tap;
+ if(conn && conn->idx) // if valid connection and non-zero index (indicating data present)
+ tap->handle_write(conn);
+ */
+ return ERR_OK;
+}
+
+/*
+ * Callback from LWIP to signal that 'len' bytes have successfully been sent.
+ * As a result, we should put our socket back into a notify-on-readability state
+ * since there is now room on the PCB buffer to write to.
+ *
+ * NOTE: This could be used to track the amount of data sent by a connection.
+ *
+ * @param associated service state object
+ * @param relevant PCB
+ * @param length of data sent
+ * @return ERR_OK if everything is ok, -1 otherwise
+ *
+ */
+err_t NetconEthernetTap::nc_sent(void* arg, struct tcp_pcb *tpcb, u16_t len)
+{
+ //dwr(5, " nc_sent()\n");
+ Larg *l = (Larg*)arg;
+ if(len) {
+ l->conn->acked+=len;
+ //dwr("W = %d, A = %d\n", l->conn->written, l->conn->acked);
+ //dwr("ACK = %d\n", len);
+ l->tap->_phy.setNotifyReadable(l->conn->dataSock, true);
+ l->tap->_phy.whack();
+ }
+ return ERR_OK;
+}
+
+/*
+ * Callback from LWIP which sends a return value to the client to signal that
+ * a connection was established for this PCB
+ *
+ * @param associated service state object
+ * @param relevant PCB
+ * @param error code
+ * @return ERR_OK if everything is ok, -1 otherwise
+ *
+ */
+err_t NetconEthernetTap::nc_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+ dwr(MSG_DEBUG, " nc_connected()\n");
+ Larg *l = (Larg*)arg;
+ l->tap->send_return_value(l->conn, ERR_OK);
+ return ERR_OK;
+}
+
+/*------------------------------------------------------------------------------
+----------------------------- RPC Handler functions ----------------------------
+------------------------------------------------------------------------------*/
+
+/* Unpacks the buffer from an RPC command */
+void NetconEthernetTap::unload_rpc(void *data, pid_t &pid, pid_t &tid, int &rpc_count, char (timestamp[20]), char &cmd, void* &payload)
+{
+ unsigned char *buf = (unsigned char*)data;
+ memcpy(&pid, &buf[IDX_PID], sizeof(pid_t));
+ memcpy(&tid, &buf[IDX_TID], sizeof(pid_t));
+ memcpy(&rpc_count, &buf[IDX_COUNT], sizeof(int));
+ memcpy(timestamp, &buf[IDX_TIME], 20);
+ memcpy(&cmd, &buf[IDX_PAYLOAD], sizeof(char));
+}
+
+/*
+ Responds to a request from the [intercept] to determine whether a local socket is
+ mapped to this service. In other words, how do the intercept's overridden calls
+ tell the difference between regular AF_LOCAL sockets and one of our socketpairs
+ that is used to communicate over the network?
+*/
+void NetconEthernetTap::handle_map_request(PhySocket *sock, void **uptr, unsigned char* buf)
+{
+ dwr(4, " handle_map_request()\n");
+ TcpConnection *conn = (TcpConnection*)*uptr;
+ int req_fd;
+ memcpy(&req_fd, &buf[IDX_PAYLOAD+1], sizeof(req_fd));
+ for(size_t i=0; i<tcp_connections.size(); i++) {
+ if(tcp_connections[i]->rpcSock == conn->rpcSock && tcp_connections[i]->perceived_fd == req_fd){
+ send_return_value(conn, 1, ERR_OK); // True
+ dwr(MSG_DEBUG, " handle_map_request(their=%d): MAPPED (to %d)\n", req_fd,
+ _phy.getDescriptor(tcp_connections[i]->dataSock));
+ return;
+ }
+ }
+ send_return_value(conn, 0, ERR_OK); // False
+ dwr(MSG_DEBUG, " handle_map_request(their=%d): NOT MAPPED\n", req_fd);
+}
+
+/**
+ * Handles a return value (client's perceived fd) and completes a mapping
+ * so that we know what connection an RPC call should be associated with.
+ *
+ * @param PhySocket associated with this RPC connection
+ * @param structure containing the data and parameters for this client's RPC
+ *
+ */
+void NetconEthernetTap::handle_retval(PhySocket *sock, void **uptr, int rpc_count, int newfd)
+{
+ dwr(MSG_DEBUG, " handle_retval()\n");
+ TcpConnection *conn = (TcpConnection*)*uptr;
+ if(!conn->pending)
+ return;
+ conn->pending = false;
+ conn->perceived_fd = newfd;
+ if(rpc_count==rpc_counter) {
+ dwr(MSG_ERROR, " handle_retval(): Detected repeat RPC.\n");
+ //return;
+ }
+ else
+ rpc_counter = rpc_count;
+
+ dwr(MSG_DEBUG, " handle_retval(): CONN:%x - Mapping [our=%d -> their=%d]\n",conn,
+ _phy.getDescriptor(conn->dataSock), conn->perceived_fd);
+
+ /* Check for pre-existing connection for this socket ---
+ This block is in response to interesting behaviour from redis-server. A
+ socket is created, setsockopt is called and the socket is set to IPV6 but fails (for now),
+ then it is closed and re-opened and consequently remapped. With two pipes mapped
+ to the same socket, makes it possible that we write to the wrong pipe and fail. So
+ this block merely searches for a possible duplicate mapping and erases it
+ */
+ for(size_t i=0; i<tcp_connections.size(); i++) {
+ if(tcp_connections[i] == conn)
+ continue;
+ if(tcp_connections[i]->rpcSock == conn->rpcSock) {
+ if(tcp_connections[i]->perceived_fd == conn->perceived_fd) {
+ int n;
+ if((n = send(_phy.getDescriptor(tcp_connections[i]->dataSock), "z", 1, MSG_NOSIGNAL)) < 0) {
+ dwr(MSG_DEBUG, " handle_retval(): CONN:%x - Socket (%d) already mapped (originally CONN:%x)\n", conn, tcp_connections[i]->perceived_fd, tcp_connections[i]);
+ closeConnection(tcp_connections[i]);
+ }
+ else {
+ dwr(MSG_ERROR, " handle_retval(): CONN:%x - This socket is mapped to two different pipes (?). Exiting.\n", conn);
+ //die(0); // FIXME: Print service mapping state and exit
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Handles an RPC to bind an LWIP PCB to a given address and port
+ *
+ * @param PhySocket associated with this RPC connection
+ * @param structure containing the data and parameters for this client's RPC
+ *
+
+ i := should be implemented in intercept lib
+ I := is implemented in intercept lib
+ X := is implemented in service
+ ? := required treatment Unknown
+ - := Not needed
+
+ [ ] EACCES - The address is protected, and the user is not the superuser.
+ [X] EADDRINUSE - The given address is already in use.
+ [I] EBADF - sockfd is not a valid descriptor.
+ [X] EINVAL - The socket is already bound to an address.
+ [I] ENOTSOCK - sockfd is a descriptor for a file, not a socket.
+
+ [X] ENOMEM - Insufficient kernel memory was available.
+
+ - The following errors are specific to UNIX domain (AF_UNIX) sockets:
+
+ [-] EACCES - Search permission is denied on a component of the path prefix. (See also path_resolution(7).)
+ [-] EADDRNOTAVAIL - A nonexistent interface was requested or the requested address was not local.
+ [-] EFAULT - addr points outside the user's accessible address space.
+ [-] EINVAL - The addrlen is wrong, or the socket was not in the AF_UNIX family.
+ [-] ELOOP - Too many symbolic links were encountered in resolving addr.
+ [-] ENAMETOOLONG - s addr is too long.
+ [-] ENOENT - The file does not exist.
+ [-] ENOTDIR - A component of the path prefix is not a directory.
+ [-] EROFS - The socket inode would reside on a read-only file system.
+
+ */
+void NetconEthernetTap::handle_bind(PhySocket *sock, void **uptr, struct bind_st *bind_rpc)
+{
+
+ struct sockaddr_in *connaddr;
+ connaddr = (struct sockaddr_in *) &bind_rpc->addr;
+ int conn_port = lwipstack->ntohs(connaddr->sin_port);
+ ip_addr_t conn_addr;
+ conn_addr.addr = *((u32_t *)_ips[0].rawIpData());
+ TcpConnection *conn = getConnectionByTheirFD(sock, bind_rpc->sockfd);
+
+ dwr(MSG_DEBUG, " handle_bind(%d)\n", bind_rpc->sockfd);
+
+ if(conn) {
+ if(conn->pcb->state == CLOSED){
+ int err = lwipstack->tcp_bind(conn->pcb, &conn_addr, conn_port);
+ if(err != ERR_OK) {
+ int ip = connaddr->sin_addr.s_addr;
+ unsigned char d[4];
+ d[0] = ip & 0xFF;
+ d[1] = (ip >> 8) & 0xFF;
+ d[2] = (ip >> 16) & 0xFF;
+ d[3] = (ip >> 24) & 0xFF;
+ dwr(MSG_ERROR, " handle_bind(): error binding to %d.%d.%d.%d : %d\n", d[0],d[1],d[2],d[3], conn_port);
+ dwr(MSG_ERROR, " handle_bind(): err = %d\n", err);
+
+ if(err == ERR_USE)
+ send_return_value(conn, -1, EADDRINUSE);
+ if(err == ERR_MEM)
+ send_return_value(conn, -1, ENOMEM);
+ if(err == ERR_BUF)
+ send_return_value(conn, -1, ENOMEM); // FIXME: Closest match
+ }
+ else
+ send_return_value(conn, ERR_OK, ERR_OK); // Success
+ }
+ else {
+ dwr(MSG_ERROR, " handle_bind(): PCB (%x) not in CLOSED state. Ignoring BIND request.\n", conn->pcb);
+ send_return_value(conn, -1, EINVAL);
+ }
+ }
+ else {
+ dwr(MSG_ERROR, " handle_bind(): can't locate connection for PCB\n");
+ send_return_value(conn, -1, EBADF);
+ }
+}
+
+/*
+ * Handles an RPC to put an LWIP PCB into LISTEN mode
+ *
+ * @param PhySocket associated with this RPC connection
+ * @param structure containing the data and parameters for this client's RPC
+ *
+
+ i := should be implemented in intercept lib
+ I := is implemented in intercept lib
+ X := is implemented in service
+ ? := required treatment Unknown
+ - := Not needed
+
+[?] EADDRINUSE - Another socket is already listening on the same port.
+[IX] EBADF - The argument sockfd is not a valid descriptor.
+[I] ENOTSOCK - The argument sockfd is not a socket.
+[I] EOPNOTSUPP - The socket is not of a type that supports the listen() operation.
+
+ */
+void NetconEthernetTap::handle_listen(PhySocket *sock, void **uptr, struct listen_st *listen_rpc)
+{
+ dwr(3, " handle_listen(their=%d):\n", listen_rpc->sockfd);
+ TcpConnection *conn = getConnectionByTheirFD(sock, listen_rpc->sockfd);
+ if(!conn){
+ dwr(MSG_ERROR, " handle_listen(): unable to locate connection object\n");
+ // ? send_return_value(conn, -1, EBADF);
+ return;
+ }
+ dwr(3, " handle_listen(our=%d -> their=%d)\n", _phy.getDescriptor(conn->dataSock), conn->perceived_fd);
+
+ if(conn->pcb->state == LISTEN) {
+ dwr(MSG_ERROR, " handle_listen(): PCB is already in listening state.\n");
+ return;
+ }
+ struct tcp_pcb* listening_pcb;
+
+#ifdef TCP_LISTEN_BACKLOG
+ listening_pcb = lwipstack->tcp_listen_with_backlog(conn->pcb, listen_rpc->backlog);
+#else
+ listening_pcb = lwipstack->tcp_listen(conn->pcb);
+#endif
+
+ if(listening_pcb != NULL) {
+ conn->pcb = listening_pcb;
+ lwipstack->tcp_accept(listening_pcb, nc_accept);
+ lwipstack->tcp_arg(listening_pcb, new Larg(this, conn));
+ /* we need to wait for the client to send us the fd allocated on their end
+ for this listening socket */
+ fcntl(_phy.getDescriptor(conn->dataSock), F_SETFL, O_NONBLOCK);
+ conn->listening = true;
+ conn->pending = true;
+ send_return_value(conn, ERR_OK, ERR_OK);
+ }
+ else {
+ /*
+ dwr"handle_listen(): unable to allocate memory for new listening PCB\n");
+ // FIXME: This does not have an equivalent errno value
+ // lwip will reclaim space with a tcp_listen call since a PCB in a LISTEN
+ // state takes up less space. If something goes wrong during the creation of a
+ // new listening socket we should return an error that implies we can't use this
+ // socket, even if the reason isn't describing what really happened internally.
+ // See: http://lwip.wikia.com/wiki/Raw/TCP
+ send_return_value(conn, -1, EBADF);
+ */
+ }
+}
+
+/*
+ * Handles an RPC to create a socket (LWIP PCB and associated socketpair)
+ *
+ * A socketpair is created, one end is kept and wrapped into a PhySocket object
+ * for use in the main ZT I/O loop, and one end is sent to the client. The client
+ * is then required to tell the service what new file descriptor it has allocated
+ * for this connection. After the mapping is complete, the socket can be used.
+ *
+ * @param PhySocket associated with this RPC connection
+ * @param structure containing the data and parameters for this client's RPC
+ *
+
+ i := should be implemented in intercept lib
+ I := is implemented in intercept lib
+ X := is implemented in service
+ ? := required treatment Unknown
+ - := Not needed
+
+ [-] EACCES - Permission to create a socket of the specified type and/or protocol is denied.
+ [I] EAFNOSUPPORT - The implementation does not support the specified address family.
+ [I] EINVAL - Unknown protocol, or protocol family not available.
+ [I] EINVAL - Invalid flags in type.
+ [I] EMFILE - Process file table overflow.
+ [?] ENFILE - The system limit on the total number of open files has been reached.
+ [X] ENOBUFS or ENOMEM - Insufficient memory is available. The socket cannot be created until sufficient resources are freed.
+ [?] EPROTONOSUPPORT - The protocol type or the specified protocol is not supported within this domain.
+
+ */
+TcpConnection * NetconEthernetTap::handle_socket(PhySocket *sock, void **uptr, struct socket_st* socket_rpc)
+{
+ int rpc_fd = _phy.getDescriptor(sock);
+ struct tcp_pcb *newpcb = lwipstack->tcp_new();
+ dwr(MSG_DEBUG, " handle_socket(): pcb=%x\n", newpcb);
+ if(newpcb != NULL) {
+ ZT_PHY_SOCKFD_TYPE fds[2];
+ if(socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) {
+ if(errno < 0) {
+ send_return_value(rpc_fd, -1, errno);
+ return NULL;
+ }
+ }
+ dwr(MSG_DEBUG, " handle_socket(): socketpair = {%d, %d}\n", fds[0], fds[1]);
+ TcpConnection *new_conn = new TcpConnection();
+ new_conn->dataSock = _phy.wrapSocket(fds[0], new_conn);
+ *uptr = new_conn;
+ new_conn->rpcSock = sock;
+ new_conn->pcb = newpcb;
+ new_conn->their_fd = fds[1];
+ tcp_connections.push_back(new_conn);
+ sock_fd_write(_phy.getDescriptor(sock), fds[1]);
+ close(fds[1]); // close other end of socketpair
+ // Once the client tells us what its fd is on the other end, we can then complete the mapping
+ new_conn->pending = true;
+ return new_conn;
+ }
+ else {
+ sock_fd_write(rpc_fd, -1); // Send a bad fd, to signal error
+ dwr(MSG_ERROR, " handle_socket(): Memory not available for new PCB\n");
+ send_return_value(rpc_fd, -1, ENOMEM);
+ return NULL;
+ }
+}
+
+/*
+ * Handles an RPC to connect to a given address and port
+ *
+ * @param PhySocket associated with this RPC connection
+ * @param structure containing the data and parameters for this client's RPC
+
+ --- Error handling in this method will only catch problems which are immedately
+ apprent. Some errors will need to be caught in the nc_connected(0 callback
+
+ i := should be implemented in intercept lib
+ I := is implemented in intercept lib
+ X := is implemented in service
+ ? := required treatment Unknown
+ - := Not needed
+
+ [-] EACCES - For UNIX domain sockets, which are identified by pathname: Write permission is denied ...
+ [?] EACCES, EPERM - The user tried to connect to a broadcast address without having the socket broadcast flag enabled ...
+ [X] EADDRINUSE - Local address is already in use.
+ [I] EAFNOSUPPORT - The passed address didn't have the correct address family in its sa_family field.
+ [X] EAGAIN - No more free local ports or insufficient entries in the routing cache.
+ [ ] EALREADY - The socket is nonblocking and a previous connection attempt has not yet been completed.
+ [IX] EBADF - The file descriptor is not a valid index in the descriptor table.
+ [ ] ECONNREFUSED - No-one listening on the remote address.
+ [i] EFAULT - The socket structure address is outside the user's address space.
+ [ ] EINPROGRESS - The socket is nonblocking and the connection cannot be completed immediately.
+ [-] EINTR - The system call was interrupted by a signal that was caught.
+ [X] EISCONN - The socket is already connected.
+ [X] ENETUNREACH - Network is unreachable.
+ [I] ENOTSOCK - The file descriptor is not associated with a socket.
+ [X] ETIMEDOUT - Timeout while attempting connection.
+
+ [X] EINVAL - Invalid argument, SVr4, generally makes sense to set this
+
+ *
+ */
+void NetconEthernetTap::handle_connect(PhySocket *sock, void **uptr, struct connect_st* connect_rpc)
+{
+ dwr(MSG_DEBUG, " handle_connect()\n");
+ TcpConnection *conn = (TcpConnection*)*uptr;
+ struct sockaddr_in *connaddr;
+ connaddr = (struct sockaddr_in *) &connect_rpc->__addr;
+ int conn_port = lwipstack->ntohs(connaddr->sin_port);
+ ip_addr_t conn_addr = convert_ip((struct sockaddr_in *)&connect_rpc->__addr);
+
+ if(conn != NULL) {
+ lwipstack->tcp_sent(conn->pcb, nc_sent);
+ lwipstack->tcp_recv(conn->pcb, nc_recved);
+ lwipstack->tcp_err(conn->pcb, nc_err);
+ lwipstack->tcp_poll(conn->pcb, nc_poll, APPLICATION_POLL_FREQ);
+ lwipstack->tcp_arg(conn->pcb, new Larg(this, conn));
+
+ int err = 0;
+ if((err = lwipstack->tcp_connect(conn->pcb,&conn_addr,conn_port, nc_connected)) < 0)
+ {
+ if(err == ERR_ISCONN) {
+ send_return_value(conn, -1, EISCONN); // Already in connected state
+ return;
+ }
+ if(err == ERR_USE) {
+ send_return_value(conn, -1, EADDRINUSE); // Already in use
+ return;
+ }
+ if(err == ERR_VAL) {
+ send_return_value(conn, -1, EINVAL); // Invalid ipaddress parameter
+ return;
+ }
+ if(err == ERR_RTE) {
+ send_return_value(conn, -1, ENETUNREACH); // No route to host
+ return;
+ }
+ if(err == ERR_BUF) {
+ send_return_value(conn, -1, EAGAIN); // No more ports available
+ return;
+ }
+ if(err == ERR_MEM)
+ {
+ /* Can occur for the following reasons: tcp_enqueue_flags()
+
+ 1) tcp_enqueue_flags is always called with either SYN or FIN in flags.
+ We need one available snd_buf byte to do that.
+ This means we can't send FIN while snd_buf==0. A better fix would be to
+ not include SYN and FIN sequence numbers in the snd_buf count.
+
+ 2) Cannot allocate new pbuf
+ 3) Cannot allocate new TCP segment
+
+ */
+ send_return_value(conn, -1, EAGAIN); // FIXME: Doesn't describe the problem well, but closest match
+ return;
+ }
+
+ // We should only return a value if failure happens immediately
+ // Otherwise, we still need to wait for a callback from lwIP.
+ // - This is because an ERR_OK from tcp_connect() only verifies
+ // that the SYN packet was enqueued onto the stack properly,
+ // that's it!
+ // - Most instances of a retval for a connect() should happen
+ // in the nc_connect() and nc_err() callbacks!
+ dwr(MSG_ERROR, " handle_connect(): unable to connect\n");
+ send_return_value(conn, -1, EAGAIN);
+ }
+ // Everything seems to be ok, but we don't have enough info to retval
+ conn->pending=true;
+ }
+ else {
+ dwr(MSG_ERROR, " handle_connect(): could not locate PCB based on their fd\n");
+ send_return_value(conn, -1, EBADF);
+ }
+}
+
+
+void NetconEthernetTap::handle_write(TcpConnection *conn)
+{
+ //dwr(MSG_DEBUG, " handle_write()\n");
+ float max = (float)TCP_SND_BUF;
+ int r;
+
+ if(!conn) {
+ dwr(MSG_ERROR, " handle_write(): could not locate connection for this fd\n");
+ return;
+ }
+ if(conn->idx < max) {
+ if(!conn->pcb) {
+ dwr(MSG_ERROR, " handle_write(): conn->pcb == NULL. Failed to write.\n");
+ return;
+ }
+ int sndbuf = conn->pcb->snd_buf; // How much we are currently allowed to write to the connection
+ /* PCB send buffer is full,turn off readability notifications for the
+ corresponding PhySocket until nc_sent() is called and confirms that there is
+ now space on the buffer */
+ if(sndbuf == 0) {
+ _phy.setNotifyReadable(conn->dataSock, false);
+ return;
+ }
+ if(!conn->listening)
+ lwipstack->_tcp_output(conn->pcb);
+
+ if(conn->dataSock) {
+ int read_fd = _phy.getDescriptor(conn->dataSock);
+ if((r = read(read_fd, (&conn->buf)+conn->idx, sndbuf)) > 0) {
+ conn->idx += r;
+ /* Writes data pulled from the client's socket buffer to LWIP. This merely sends the
+ * data to LWIP to be enqueued and eventually sent to the network. */
+ if(r > 0) {
+ int sz;
+ // NOTE: this assumes that lwipstack->_lock is locked, either
+ // because we are in a callback or have locked it manually.
+ int err = lwipstack->_tcp_write(conn->pcb, &conn->buf, r, TCP_WRITE_FLAG_COPY);
+ lwipstack->_tcp_output(conn->pcb);
+ if(err != ERR_OK) {
+ dwr(MSG_ERROR, " handle_write(): error while writing to PCB, (err = %d)\n", err);
+ return;
+ }
+ else {
+ sz = (conn->idx)-r;
+ if(sz) {
+ memmove(&conn->buf, (conn->buf+r), sz);
+ }
+ conn->idx -= r;
+ conn->written+=r;
+ return;
+ }
+ }
+ else {
+ dwr(MSG_INFO, " handle_write(): LWIP stack full\n");
+ return;
+ }
+ }
+ }
+ }
+}
+
+} // namespace ZeroTier
+
+#endif // ZT_ENABLE_NETCON
diff --git a/netcon/NetconEthernetTap.hpp b/netcon/NetconEthernetTap.hpp
new file mode 100644
index 00000000..dd51a19d
--- /dev/null
+++ b/netcon/NetconEthernetTap.hpp
@@ -0,0 +1,187 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef ZT_NETCONETHERNETTAP_HPP
+#define ZT_NETCONETHERNETTAP_HPP
+
+#ifdef ZT_ENABLE_NETCON
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+#include <stdexcept>
+
+#include "../node/Constants.hpp"
+#include "../node/MulticastGroup.hpp"
+#include "../node/Mutex.hpp"
+#include "../node/InetAddress.hpp"
+#include "../osdep/Thread.hpp"
+#include "../osdep/Phy.hpp"
+
+#include "netif/etharp.h"
+
+struct tcp_pcb;
+struct socket_st;
+struct listen_st;
+struct bind_st;
+struct connect_st;
+
+namespace ZeroTier {
+
+class NetconEthernetTap;
+class TcpConnection;
+class Larg;
+class LWIPStack;
+
+/**
+ * Network Containers instance -- emulates an Ethernet tap device as far as OneService knows
+ */
+class NetconEthernetTap
+{
+ friend class Phy<NetconEthernetTap *>;
+
+public:
+ NetconEthernetTap(
+ const char *homePath,
+ const MAC &mac,
+ unsigned int mtu,
+ unsigned int metric,
+ uint64_t nwid,
+ const char *friendlyName,
+ void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+ void *arg);
+
+ ~NetconEthernetTap();
+
+ void setEnabled(bool en);
+ bool enabled() const;
+ bool addIp(const InetAddress &ip);
+ bool removeIp(const InetAddress &ip);
+ std::vector<InetAddress> ips() const;
+ void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
+ std::string deviceName() const;
+ void setFriendlyName(const char *friendlyName);
+ void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
+
+ void threadMain()
+ throw();
+
+ LWIPStack *lwipstack;
+ uint64_t _nwid;
+ void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
+ void *_arg;
+
+private:
+ // LWIP callbacks
+ static err_t nc_poll(void* arg, struct tcp_pcb *tpcb);
+ static err_t nc_accept(void *arg, struct tcp_pcb *newpcb, err_t err);
+ static err_t nc_recved(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
+ static void nc_err(void *arg, err_t err);
+ static void nc_close(struct tcp_pcb *tpcb);
+ static err_t nc_send(struct tcp_pcb *tpcb);
+ static err_t nc_sent(void *arg, struct tcp_pcb *tpcb, u16_t len);
+ static err_t nc_connected(void *arg, struct tcp_pcb *tpcb, err_t err);
+
+ // RPC handlers (from NetconIntercept)
+ void unload_rpc(void *data, pid_t &pid, pid_t &tid, int &rpc_count, char (timestamp[20]), char &cmd, void* &payload);
+
+ void handle_bind(PhySocket *sock, void **uptr, struct bind_st *bind_rpc);
+ void handle_listen(PhySocket *sock, void **uptr, struct listen_st *listen_rpc);
+ void handle_map_request(PhySocket *sock, void **uptr, unsigned char* buf);
+ void handle_retval(PhySocket *sock, void **uptr, int rpc_count, int newfd);
+ TcpConnection * handle_socket(PhySocket *sock, void **uptr, struct socket_st* socket_rpc);
+ void handle_connect(PhySocket *sock, void **uptr, struct connect_st* connect_rpc);
+ void handle_write(TcpConnection *conn);
+
+ int send_return_value(TcpConnection *conn, int retval, int _errno);
+ int send_return_value(int fd, int retval, int _errno);
+
+ void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len);
+ void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success);
+ void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from);
+ void phyOnTcpClose(PhySocket *sock,void **uptr);
+ void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len);
+ void phyOnTcpWritable(PhySocket *sock,void **uptr);
+ void phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN);
+ void phyOnUnixClose(PhySocket *sock,void **uptr);
+ void phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len);
+ void phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable);
+
+ ip_addr_t convert_ip(struct sockaddr_in * addr)
+ {
+ ip_addr_t conn_addr;
+ struct sockaddr_in *ipv4 = addr;
+ short a = ip4_addr1(&(ipv4->sin_addr));
+ short b = ip4_addr2(&(ipv4->sin_addr));
+ short c = ip4_addr3(&(ipv4->sin_addr));
+ short d = ip4_addr4(&(ipv4->sin_addr));
+ IP4_ADDR(&conn_addr, a,b,c,d);
+ return conn_addr;
+ }
+
+ // Client helpers
+ TcpConnection *getConnectionByTheirFD(PhySocket *sock, int fd);
+ void closeConnection(TcpConnection *conn);
+ void closeAll();
+ void closeClient(PhySocket *sock);
+ void compact_dump();
+ void dump();
+ void die(int exret);
+
+ Phy<NetconEthernetTap *> _phy;
+ PhySocket *_unixListenSocket;
+
+ std::vector<TcpConnection*> tcp_connections;
+ std::vector<PhySocket*> rpc_sockets;
+ std::map<PhySocket*, pid_t> pidmap;
+ pid_t rpc_counter;
+
+ netif interface;
+
+ MAC _mac;
+ Thread _thread;
+ std::string _homePath;
+ std::string _dev; // path to Unix domain socket
+
+ std::vector<MulticastGroup> _multicastGroups;
+ Mutex _multicastGroups_m;
+
+ std::vector<InetAddress> _ips;
+ Mutex _ips_m;
+
+ unsigned int _mtu;
+ volatile bool _enabled;
+ volatile bool _run;
+};
+
+} // namespace ZeroTier
+
+#endif // ZT_ENABLE_NETCON
+
+#endif
diff --git a/netcon/README.md b/netcon/README.md
new file mode 100644
index 00000000..b6bff667
--- /dev/null
+++ b/netcon/README.md
@@ -0,0 +1,108 @@
+Network Containers (beta)
+======
+
+ZeroTier Network Containers offers a microkernel-like networking paradigm for containerized applications and application-specific virtual networking.
+
+Network Containers couples the ZeroTier core Ethernet virtualization engine with a user-space TCP/IP stack and a library that intercepts calls to the Posix network API. Our intercept library implements full binary compatibility with the standard network API, permitting servers and applications to be used without modification or recompilation.
+
+It can be used to run services on virtual networks without requiring the creation of kernel-mode virtual network ports or modification of system network settings and without special privileges. It's ideal for containerized microservices that are designed exclusively for use on an isolated virtual network and that are to be deployed on commodity container hosting infrastructure. With Network Containers such services can be deployed without special permissions and connected to arbitrary virtual networks without configuration changes to the host node.
+
+Network Containers is ideal for use with [Docker](http://http://www.docker.com), [LXC](https://linuxcontainers.org), or [Rkt](https://coreos.com/rkt/docs/latest/), allowing connectivity to a virtual network to be built into and deployed with containers without host awareness or configuration. It can also be used without containers to network-containerize applications on an ordinary VM or bare metal host. It works entirely at the library/application level and requires no special kernel extensions.
+
+Our long term goal with network containers is to facilitate the total commoditization of the container host by allowing virtual networking without elevated privileges or host configuration. We think this will help ease the path toward commodity multi-tenant container hosting and total application portability across hosts, data centers, and cloud providers.
+
+Network Containers are currently in **beta** and are suitable for testing, experimentation, and prototyping. There are still some issues with compatibility with some applications, as documented in the compatibility matrix below. There's also some remaining work to be done on performance and overall stability before this will be ready for production use.
+
+# Limitations and Compatibility
+
+The current version of Network Containers **only supports TCP over IPv4**. There is no IPv6 support and no support for UDP or ICMP (or RAW sockets). That means network-containerizing *ping* won't work, nor will UDP-based apps like VoIP servers, DNS servers, or P2P apps.
+
+The virtual TCP/IP stack will respond to *incoming* ICMP ECHO requests, which means that you can ping it from another host on the same ZeroTier virtual network. This is useful for testing.
+
+**Network Containers are currently all or nothing.** If engaged, the intercept library intercepts all network I/O calls and redirects them through the new path. A network-containerized application cannot communicate over the regular network connection of its host or container or with anything else except other hosts on its ZeroTier virtual LAN. Support for optional "fall-through" to the host IP stack for outgoing connections outside the virtual network and for gateway routes within the virtual network is also planned for the near future.
+
+#### Compatibility Test Results
+
+ sshd [ WORKS as of 20151112 ]
+ ssh [ WORKS as of 20151112 ]
+ sftp [ WORKS as of 20151022 ]
+ curl [ WORKS as of 20151021 ]
+ apache (debug mode) [ WORKS as of 20150810 ]
+ apache (prefork MPM) [ WORKS as of 20151123 ] (2.4.6-31.x86-64 on Centos 7), (2.4.16-1.x84-64 on F22), (2.4.17-3.x86-64 on F22)
+ nginx [ MARGINAL as of 20151123 ] Broken on Centos 7, unreliable on Fedora 23
+ nodejs [ WORKS as of 20151123 ]
+ java [ WORKS as of 20151010 ]
+ MongoDB [ WORKS as of 20151028 ]
+ Redis-server [ WORKS as of 20151123 ]
+
+It is *likely* to work with other things but there are no guarantees. UDP, ICMP/RAW, and IPv6 support are planned for the near future.
+
+# Building Network Containers
+
+Network Containers are currently only for Linux. To build the network container host and intercept library, from the base of the ZeroTier One tree type:
+
+ make netcon
+
+This will build a binary called *zerotier-netcon-service* and a library called *libzerotierintercept.so*. The former is the same as a regular ZeroTier One build except instead of creating virtual network ports using Linux's */dev/net/tun* interface, it instead creates instances of a user-space TCP/IP stack for each virtual network and provides RPC access to this stack via a Unix domain socket called */tmp/.ztnc_##NETWORK_ID##*. The latter is a library that can be loaded with the Linux *LD\_PRELOAD* environment variable or by placement into */etc/ld.so.preload* on a Linux system or container.
+
+The intercept library does nothing unless the *ZT\_NC\_NWID* environment variable is set. If on program launch (or fork) it detects the presence of this environment variable, it will attempt to connect to a running *zerotier-netcon-service* at the aforementioned Unix domain socket location and will intercept calls to the Posix sockets API and redirect network traffic through the virtual network.
+
+Unlike *zerotier-one*, *zerotier-netcon-service* does not need to be run with root privileges and will not modify the host's network configuration in any way.
+
+# Starting the Network Containers Service
+
+You don't need Docker or any other container engine to try Network Containers. A simple test can be performed in user space in your own home directory.
+
+First, build the netcon service and intercept library as describe above. Then create a directory to act as a temporary ZeroTier home for your test netcon service instance.
+
+ mkdir /tmp/netcon-test-home
+
+Now you can run the service (no sudo needed):
+
+ ./zerotier-netcon-service -d /tmp/netcon-test-home
+
+As with ZeroTier One in its normal incarnation, you'll need to join a network:
+
+ ./zerotier-cli -D/tmp/netcon-test-home join 8056c2e21c000001
+
+(If you don't want to use [Earth](https://www.zerotier.com/public.shtml) for this test, replace its network ID with one of your own.)
+
+Note the *-D* option. This tells *zerotier-cli* not to look in /var/lib/zerotier-one for information about a running instance of the ZeroTier system service but instead to look in /tmp/netcon-test-home. That's because *even if you do happen to be running ZeroTier on your local machine, what you are doing now has no impact on it and does not involve it in any way.* So if you have *zerotier-one* running, forget about it. It doesn't matter for this test.
+
+Now type:
+
+ ./zerotier-cli -D/tmp/netcon-test-home listnetworks
+
+Try it a few times until you see that you've successfully joined the network and have an IP address.
+
+You'll also want to have ZeroTier One (the normal build, not network containers) running somewhere else, such as on another Linux system or VM. Technically you could run it on the *same* Linux system and it wouldn't matter at all, but many people find this intensely confusing until they grasp just what exactly is happening here.
+
+On the other Linux system, join the same network if you haven't already (8056c2e21c000001 if you're using Earth) and wait until you have an IP address. Then try pinging the IP address your netcon instance received. You should see ping replies.
+
+Back on the host that's running *zerotier-netcon-service*, type *ip list all* or *ifconfig* (ifconfig is technically deprecated so some Linux systems might not have it). Notice that the IP address of the network containers endpoint is not listed and no network device is listed for it either. That's because as far as the Linux kernel is concerned it doesn't exist.
+
+What are you pinging? What is happening here?
+
+The *zerotier-netcon-service* binary has joined a *virtual* network and is running a *virtual* TCP/IP stack entirely in user space. As far as your system is concerned it's just another program exchanging UDP packets with a few other hosts on the Internet and nothing out of the ordinary is happening at all. That's why you never had to type *sudo*. It didn't change anything on the host.
+
+Now you can run a containerized application. Open another terminal window (since you might not want these environment variables to stick elsewhere) on the same machine the netcon service is running on and install something like *darkhttpd* (a simple http server) to act as a test app:
+
+On Debian and Ubuntu:
+
+ sudo apt-get install darkhttpd
+
+Or for CentOS/EPEL or Fedora:
+
+ sudo yum install darkhttpd
+
+Now try:
+
+ export LD_PRELOAD=/path/to/ZeroTierOne/libzerotierintercept.so
+ export ZT_NC_NWID=8056c2e21c000001
+ darkhttpd . --port 8080
+
+Going to port 8080 on your machine won't work. Darkhttpd is listening, but only inside the network container. To reach it, go to the other system where you joined the same network with a conventional ZeroTier instance and try:
+
+ curl http://NETCON.INSTANCE.IP:8080/README.md
+
+Replace *NETCON.INSTANCE.IP* with the IP address that *zerotier-netcon-service* was assigned on the virtual network. (This is the same IP you pinged in your first test.) If everything works, you should get back a copy of ZeroTier One's main README.md file.
diff --git a/netcon/README.orig.md b/netcon/README.orig.md
new file mode 100644
index 00000000..bf6ae939
--- /dev/null
+++ b/netcon/README.orig.md
@@ -0,0 +1,139 @@
+ZeroTier Network Containers
+======
+
+### Functional Overview:
+
+This system exists as a dynamically-linked library, and a service/IP-stack built into ZeroTier
+
+If you care about the technicals,
+
+The intercept is compiled as a shared library and installed in some user-accessible directory. When you want to intercept
+a user application you dynamically link the shared library to the application during runtime. When the application starts, the
+intercept's global constructor is called which sets up a hidden pipe which is used to communicate remote procedure calls (RPC) to the host Netcon service running in the background.
+
+When an RPC for a socket() is received by the Netcon service from the intercepted application, the Netcon service will ask the lwIP stack for a new PCB structure (used to represent a connection), if the system permits its allocation, it will be passed to Netcon where a PCB/socket table entry will be created. The table is used for mapping [callbacks from lwIP] and [RPCs from the intercept] to the correct connections.
+
+Upon the first call to a intercept-overriden system call, a Unix-domain socket is opened between the Netcon service and the application's intercept. This socket provides us the ability to pass file descriptors of newly-created socketpairs to the intercept (used as the read/write buffer). More specifically, after the socketpair creation, one end is kept in a table entry in Netcon and one end is sent to the intercept.
+
+### Building from Source (and Installing)
+
+Build zerotier-intercept library:
+
+ make -f make-intercept.mk
+
+Install:
+
+ make -f make-intercept.mk install
+
+Build LWIP library:
+
+ make -f make-liblwip.mk
+
+Run automated tests (from netcon/docker-test/ directory):
+
+ ./build.sh
+ ./test.sh
+
+
+
+
+### Running
+
+To intercept a specific application (requires an already running instance of Zerotier-One with Network Containers enabled):
+
+ zerotier-intercept my_app
+
+
+### Unit Tests
+
+To run unit tests:
+
+1) Set up your own network, use its network id as follows:
+
+2) Place a blank network config file in this directory (e.g. "e5cd7a9e1c5311ab.conf")
+ - This will be used to inform test-specific scripts what network to use for testing
+
+3) run build.sh
+ - Builds ZeroTier-One with Network Containers enabled
+ - Builds LWIP library
+ - Builds intercept library
+ - Copies all aformentioned files into unit test directory to be used for building docker files
+
+4) run test.sh
+ - Will execute each unit test's (test.sh) one at a time and populate _results/
+
+
+### Anatomy of a unit test
+
+A) Each unit test's test.sh will:
+ - temporarily copy all built files into local directory
+ - build test container
+ - build monitor container
+ - remove temporary files
+ - run each container and perform test and monitoring specified in netcon_entrypoint.sh and monitor_entrypoint.sh
+
+B) Results will be written to the 'netcon/docker-test/_results/' directory
+ - Results will be a combination of raw and formatted dumps to files whose names reflect the test performed
+ - In the event of failure, 'FAIL.' will be appended to the result file's name
+ - (e.g. FAIL.my_application_1.0.2.x86_64)
+ - In the event of success, 'OK.' will be appended
+
+
+### Compatibility
+
+Network Containers have been tested with the following:
+
+ sshd [ WORKS as of 20151112]
+ ssh [ WORKS as of 20151112]
+ sftp [ WORKS as of 20151022]
+ curl [ WORKS as of 20151021]
+ apache (debug mode) [ WORKS as of 20150810]
+ apache (prefork MPM) [ WORKS as of 20151123] (2.4.6-31.x86-64 on Centos 7), (2.4.16-1.x84-64 on F22), (2.4.17-3.x86-64 on F22)
+ nginx [ WORKS as of 20151123] Broken on Centos 7, unreliable on Fedora 23
+ nodejs [ WORKS as of 20151123]
+ java [ WORKS as of 20151010]
+ MongoDB [ WORKS as of 20151028]
+ Redis-server [ WORKS as of 20151123]
+
+Future:
+
+ GET many different files via HTTP (web stress)
+ LARGE continuous transfer (e.g. /dev/urandom all night)
+ Open and close many TCP connections constantly
+ Simulate packet loss (can be done with iptables)
+ Many parallel TCP transfers
+ Multithreaded software (e.g. apache in thread mode)
+ UDP support
+
+
+
+### Extended Version Notes
+
+20151028 Added MongoDB support:
+
+ - Added logic (RPC_MAP_REQ) to check whether a given AF_LOCAL socket is mapped to anything
+ inside the service instance.
+
+20151027 Added Redis-server support:
+
+ - Added extra logic to detect socket re-issuing and consequent service-side double mapping.
+ Redis appears to try to set its initial listen socket to IPV6 only, this currently fails. As
+ a result, Redis will close the socket and re-open it. The server will now test for closures
+ during mapping and will eliminate any mappings to broken pipes.
+
+20151021 Added Node.js support:
+
+ - syscall(long number, ...) is now intercepted and re-directs the __NR_accept4 call to our intercepted accept4() function
+
+ - accept() now returns -EAGAIN in the case that we cannot read a signal byte from the descriptor linked to the service. This
+ is because the uv__server_io() function in libuv used by Node.js looks for this return value upon failure, without it we
+ were observing an innfinite loop in the I/O polling code in libuv.
+
+ - accept4() now correctly sets given flags for descriptor returned by accept()
+
+ - setsockopt() was modified to return success on any call with the following conditions:
+ level == IPPROTO_TCP || (level == SOL_SOCKET && option_name == SO_KEEPALIVE)
+ This might be unnecessary or might need a better workaround
+
+ - Careful attention should be given to how arguments are passed in the intercepted syscall() function, this differs for
+ 32/64-bit systems
diff --git a/netcon/common.inc.c b/netcon/common.inc.c
new file mode 100644
index 00000000..2a852b67
--- /dev/null
+++ b/netcon/common.inc.c
@@ -0,0 +1,197 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <fcntl.h>
+
+
+#define DEBUG_LEVEL 3
+
+#define MSG_ERROR 0 // Errors
+#define MSG_INFO 1 // Information which is generally useful to any user
+#define MSG_DEBUG 2 // Information which is only useful to someone debugging
+#define MSG_DEBUG_EXTRA 3 // If nothing in your world makes sense
+
+#ifdef NETCON_INTERCEPT
+
+static pthread_mutex_t loglock;
+
+void print_addr(struct sockaddr *addr)
+{
+ char *s = NULL;
+ switch(addr->sa_family) {
+ case AF_INET: {
+ struct sockaddr_in *addr_in = (struct sockaddr_in *)addr;
+ s = malloc(INET_ADDRSTRLEN);
+ inet_ntop(AF_INET, &(addr_in->sin_addr), s, INET_ADDRSTRLEN);
+ break;
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr;
+ s = malloc(INET6_ADDRSTRLEN);
+ inet_ntop(AF_INET6, &(addr_in6->sin6_addr), s, INET6_ADDRSTRLEN);
+ break;
+ }
+ default:
+ break;
+ }
+ fprintf(stderr, "IP address: %s\n", s);
+ free(s);
+}
+#endif
+
+#ifdef NETCON_SERVICE
+ namespace ZeroTier {
+#endif
+ void dwr(int level, const char *fmt, ... )
+ {
+ if(level > DEBUG_LEVEL)
+ return;
+ int saveerr;
+ saveerr = errno;
+ va_list ap;
+ va_start(ap, fmt);
+ #ifdef VERBOSE // So we can cut out some clutter in the strace output while debugging
+ char timestring[20];
+ time_t timestamp;
+ timestamp = time(NULL);
+ strftime(timestring, sizeof(timestring), "%H:%M:%S", localtime(&timestamp));
+ pid_t pid = getpid();
+ fprintf(stderr, "%s [pid=%7d] ", timestring, pid);
+ #endif
+ vfprintf(stderr, fmt, ap);
+ fflush(stderr);
+
+ errno = saveerr;
+ va_end(ap);
+ }
+#ifdef NETCON_SERVICE
+}
+#endif
+
+ssize_t sock_fd_write(int sock, int fd);
+ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd);
+
+ssize_t sock_fd_write(int sock, int fd)
+{
+ ssize_t size;
+ struct msghdr msg;
+ struct iovec iov;
+ char buf = '\0';
+ int buflen = 1;
+
+ union {
+ struct cmsghdr cmsghdr;
+ char control[CMSG_SPACE(sizeof (int))];
+ } cmsgu;
+ struct cmsghdr *cmsg;
+
+ iov.iov_base = &buf;
+ iov.iov_len = buflen;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ if (fd != -1) {
+ msg.msg_control = cmsgu.control;
+ msg.msg_controllen = sizeof(cmsgu.control);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof (int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *((int *) CMSG_DATA(cmsg)) = fd;
+ } else {
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ }
+
+ size = sendmsg(sock, &msg, 0);
+ if (size < 0)
+ perror ("sendmsg");
+ return size;
+}
+
+ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd)
+{
+ ssize_t size;
+ if (fd) {
+ struct msghdr msg;
+ struct iovec iov;
+ union {
+ struct cmsghdr cmsghdr;
+ char control[CMSG_SPACE(sizeof (int))];
+ } cmsgu;
+ struct cmsghdr *cmsg;
+
+ iov.iov_base = buf;
+ iov.iov_len = bufsize;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsgu.control;
+ msg.msg_controllen = sizeof(cmsgu.control);
+ size = recvmsg (sock, &msg, 0);
+ if (size < 0) {
+ perror ("recvmsg");
+ exit(1);
+ }
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
+ if (cmsg->cmsg_level != SOL_SOCKET) {
+ fprintf (stderr, "invalid cmsg_level %d\n",cmsg->cmsg_level);
+ exit(1);
+ }
+ if (cmsg->cmsg_type != SCM_RIGHTS) {
+ fprintf (stderr, "invalid cmsg_type %d\n",cmsg->cmsg_type);
+ exit(1);
+ }
+
+ *fd = *((int *) CMSG_DATA(cmsg));
+ } else *fd = -1;
+ } else {
+ size = read (sock, buf, bufsize);
+ if (size < 0) {
+ perror("read");
+ exit(1);
+ }
+ }
+ return size;
+}
diff --git a/netcon/docker-test/_results/FAIL.httpd-2.4.16-1.fc23.x86_64.txt b/netcon/docker-test/_results/FAIL.httpd-2.4.16-1.fc23.x86_64.txt
new file mode 100644
index 00000000..d65ad568
--- /dev/null
+++ b/netcon/docker-test/_results/FAIL.httpd-2.4.16-1.fc23.x86_64.txt
@@ -0,0 +1 @@
+Test: md5 sum did not match!
diff --git a/netcon/docker-test/_results/FAIL.nginx-1.8.0-13.fc23.x86_64.txt b/netcon/docker-test/_results/FAIL.nginx-1.8.0-13.fc23.x86_64.txt
new file mode 100644
index 00000000..d65ad568
--- /dev/null
+++ b/netcon/docker-test/_results/FAIL.nginx-1.8.0-13.fc23.x86_64.txt
@@ -0,0 +1 @@
+Test: md5 sum did not match!
diff --git a/netcon/docker-test/_results/FAIL.nginx-1.8.0-14.fc23.x86_64.txt b/netcon/docker-test/_results/FAIL.nginx-1.8.0-14.fc23.x86_64.txt
new file mode 100644
index 00000000..d65ad568
--- /dev/null
+++ b/netcon/docker-test/_results/FAIL.nginx-1.8.0-14.fc23.x86_64.txt
@@ -0,0 +1 @@
+Test: md5 sum did not match!
diff --git a/netcon/docker-test/_results/FAIL.nodejs-0.10.36-4.fc23.txt b/netcon/docker-test/_results/FAIL.nodejs-0.10.36-4.fc23.txt
new file mode 100644
index 00000000..81615d90
--- /dev/null
+++ b/netcon/docker-test/_results/FAIL.nodejs-0.10.36-4.fc23.txt
@@ -0,0 +1 @@
+Test: nodejs server did NOT respond!
diff --git a/netcon/docker-test/_results/OK.redis-3.0.4-1.fc23.x86_64.txt b/netcon/docker-test/_results/OK.redis-3.0.4-1.fc23.x86_64.txt
new file mode 100644
index 00000000..7cb05a71
--- /dev/null
+++ b/netcon/docker-test/_results/OK.redis-3.0.4-1.fc23.x86_64.txt
@@ -0,0 +1 @@
+Test: redis-server responded!
diff --git a/netcon/docker-test/build.sh b/netcon/docker-test/build.sh
new file mode 100755
index 00000000..4eaa246c
--- /dev/null
+++ b/netcon/docker-test/build.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+echo "*** Building Zerotier-One, libraries, and test/monitor images..."
+
+./build_zt.sh
+./build_tests.sh
+
+echo "*** Done" \ No newline at end of file
diff --git a/netcon/docker-test/build_single_image.sh b/netcon/docker-test/build_single_image.sh
new file mode 100755
index 00000000..a76be2ed
--- /dev/null
+++ b/netcon/docker-test/build_single_image.sh
@@ -0,0 +1,32 @@
+# Builds a test docker image
+
+test_name=${PWD##*/}
+echo 'Building dockerfiles for test: ' "$test_name"
+touch "$test_name".name
+
+# Docker won't allow the inclusion of files outside of the build directory
+cp ../../*.conf .
+cp ../../zerotier-one zerotier-one
+cp ../../zerotier-cli zerotier-cli
+cp ../../zerotier-intercept zerotier-intercept
+cp ../../libzerotierintercept.so.1.0 libzerotierintercept.so.1.0
+cp ../../liblwip.so liblwip.so
+cp ../../netcon_identity.public netcon_identity.public
+cp ../../netcon_identity.secret netcon_identity.secret
+cp ../../monitor_identity.public monitor_identity.public
+cp ../../monitor_identity.secret monitor_identity.secret
+
+docker build --tag="$test_name" -f netcon_dockerfile .
+docker build --tag="$test_name"_monitor -f monitor_dockerfile .
+
+rm -f zerotier-one
+rm -f zerotier-cli
+rm -f zerotier-intercept
+rm -f libzerotierintercept.so.1.0
+rm -f liblwip.so
+rm -f netcon_identity.public
+rm -f netcon_identity.secret
+rm -f monitor_identity.public
+rm -f monitor_identity.secret
+rm -f *.conf
+rm -f *.name \ No newline at end of file
diff --git a/netcon/docker-test/build_tests.sh b/netcon/docker-test/build_tests.sh
new file mode 100755
index 00000000..b8ab7c5c
--- /dev/null
+++ b/netcon/docker-test/build_tests.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# Merely BUILDS test images
+
+# Remove previous test results
+rm _results/*.txt
+
+# How long we shall wait for each test to conclude
+export netcon_test_wait_time=60s
+
+export image_build_script=build_single_image.sh
+
+# Iterate over all depth=2 (relatively-speaking) directories and perform each test
+find . -mindepth 2 -maxdepth 2 -type d | while read testdir; do
+ echo "*** Building: '$testdir'..."
+ rm _results/*.tmp
+
+ # Stage scripts
+ cp $image_build_script $testdir/$image_build_script
+ cd $testdir
+
+ # Build test docker images
+ ./$image_build_script
+ rm $image_build_script
+
+ cd ../../
+done \ No newline at end of file
diff --git a/netcon/docker-test/build_zt.sh b/netcon/docker-test/build_zt.sh
new file mode 100755
index 00000000..189a1e0d
--- /dev/null
+++ b/netcon/docker-test/build_zt.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Builds Zerotier-One and libraries required for Network Containers, then
+# copies the binaries into the test directory.
+
+cd ../../
+
+make clean
+make
+cd netcon
+make -f make-intercept.mk lib
+rm *.o
+rm liblwip.so
+make -f make-liblwip.mk
+
+cd docker-test
+
+cp ../../zerotier-one zerotier-one
+cp ../../zerotier-cli zerotier-cli
+
+cp ../liblwip.so liblwip.so
+cp ../libzerotierintercept.so.1.0 libzerotierintercept.so.1.0
+cp ../zerotier-intercept zerotier-intercept
+
diff --git a/netcon/docker-test/e5cd7a9e1c5311ab.conf b/netcon/docker-test/e5cd7a9e1c5311ab.conf
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/netcon/docker-test/e5cd7a9e1c5311ab.conf
diff --git a/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/httpd-2.4.16-1.fc23.x86_64.name b/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/httpd-2.4.16-1.fc23.x86_64.name
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/httpd-2.4.16-1.fc23.x86_64.name
diff --git a/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/monitor_dockerfile b/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/monitor_dockerfile
new file mode 100644
index 00000000..e8f803cd
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/monitor_dockerfile
@@ -0,0 +1,23 @@
+# ZT Network Containers Test Monitor
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+EXPOSE 9993/udp
+
+# Install LWIP library used by service
+ADD liblwip.so /var/lib/zerotier-one/liblwip.so
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD monitor_identity.public /var/lib/zerotier-one/identity.public
+ADD monitor_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Start ZeroTier-One
+ADD monitor_entrypoint.sh /monitor_entrypoint.sh
+RUN chmod -v +x /monitor_entrypoint.sh
+CMD ["./monitor_entrypoint.sh"]
diff --git a/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/monitor_entrypoint.sh b/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/monitor_entrypoint.sh
new file mode 100644
index 00000000..52470efd
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/monitor_entrypoint.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+netcon_wait_time=35 # wait for test container to come online
+app_timeout_time=25 # app-specific timeout
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+fail=FAIL. # appended to result file in event of failure
+ok=OK. # appended to result file in event of success
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+bigfile_name=bigfile # large, random test transfer file
+rx_md5sumfile="$file_path"rx_"$bigfile_name"_md5sum"$tmp_ext"
+tx_md5sumfile="$file_path"tx_"$bigfile_name"_md5sum"$tmp_ext"
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test Monitor'
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+virtip4=""
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+done
+echo '*** Starting Test...'
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Sleeping for (' "$netcon_wait_time" 's ) while we wait for the Network Container to come online...'
+sleep "$netcon_wait_time"s
+ncvirtip=$(<$address_file)
+
+
+# --- Test section ---
+echo '*** Curling from intercepted server at' $ncvirtip
+rm -rf "$file_path"*."$file_base"
+touch "$bigfile_name"
+
+# Perform test
+# curl --connect-timeout "$app_timeout_time" -v -o "$file_path$file_base" http://"$ncvirtip"/index.html
+# Large transfer test
+curl --connect-timeout "$app_timeout_time" -v -o "$bigfile_name" http://"$ncvirtip"/"$bigfile_name"
+
+# Check md5
+md5sum < "$bigfile_name" >> "$rx_md5sumfile"
+rx_md5sum=$(<$rx_md5sumfile)
+tx_md5sum=$(<$tx_md5sumfile)
+
+echo '*** Comparing md5: ' "$rx_md5sum" ' and ' "$tx_md5sum"
+
+if [ "$rx_md5sum" != "$tx_md5sum" ];
+then
+ echo 'MD5 FAIL'
+ touch "$file_path$fail$test_name.txt"
+ printf 'Test: md5 sum did not match!\n' >> "$file_path$fail$test_name.txt"
+else
+ echo 'MD5 OK'
+ touch "$file_path$ok$test_name.txt"
+ printf 'Test: md5 sum ok!\n' >> "$file_path$ok$test_name.txt"
+ cat "$rx_md5sumfile" >> "$file_path$ok$test_name.txt"
+ cat "$tx_md5sumfile" >> "$file_path$ok$test_name.txt"
+fi
+
+
+
+
+
+
+
+
+
+
+
diff --git a/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/netcon_dockerfile b/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/netcon_dockerfile
new file mode 100644
index 00000000..3de8ed3f
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/netcon_dockerfile
@@ -0,0 +1,39 @@
+# ZT Network Containers Test
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+# Install apps
+RUN yum -y update
+RUN yum -y install httpd-2.4.16-1.fc23.x86_64
+RUN yum clean all
+
+EXPOSE 9993/udp 80/udp
+
+# Install LWIP library used by service
+ADD liblwip.so /var/lib/zerotier-one/liblwip.so
+
+# Install syscall intercept library
+ADD zerotier-intercept /
+ADD libzerotierintercept.so.1.0 /
+RUN cp libzerotierintercept.so.1.0 lib/libzerotierintercept.so.1.0
+RUN cp libzerotierintercept.so.1.0 /lib/libzerotierintercept.so.1.0
+RUN ln -sf /lib/libzerotierintercept.so.1.0 /lib/libzerotierintercept
+RUN /usr/bin/install -c zerotier-intercept /usr/bin
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD netcon_identity.public /var/lib/zerotier-one/identity.public
+ADD netcon_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Install test scripts
+ADD netcon_entrypoint.sh /netcon_entrypoint.sh
+RUN chmod -v +x /netcon_entrypoint.sh
+
+# Start ZeroTier-One
+CMD ["./netcon_entrypoint.sh"]
diff --git a/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/netcon_entrypoint.sh b/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/netcon_entrypoint.sh
new file mode 100644
index 00000000..1c5dcbd1
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd-2.4.16-1.fc23.x86_64/netcon_entrypoint.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+bigfile_name=bigfile
+bigfile_size=10M # size of file we want to use for the test
+tx_md5sumfile="$file_path"tx_"$bigfile_name"_md5sum"$tmp_ext"
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test: ' "$test_name"
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+virtip4=""
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+ dev=`/zerotier-cli listnetworks | grep -F "" | cut -d ' ' -f 8 | cut -d "_" -f 2 | sed "s/^<dev>//" | tr '\n' '\0'`
+done
+echo '--- Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Writing address to ' "$address_file"
+echo $virtip4 > "$address_file"
+
+export ZT_NC_NWID=$dev
+
+# --- Test section ---
+# Generate large random file for transfer test, share md5sum for monitor container to check
+echo '*** Generating ' "$bigfile_size" ' file'
+dd if=/dev/urandom of=/var/www/html/"$bigfile_name" bs="$bigfile_size" count=1
+#md5sum /var/www/html/"$bigfile_name" >> "$tx_md5sumfile"
+md5sum < /var/www/html/"$bigfile_name" >> "$tx_md5sumfile"
+echo '*** Wrote MD5 sum to ' "$tx_md5sumfile"
+
+echo '*** Starting application...'
+sleep 0.5
+rm -rf /run/httpd/* /tmp/httpd*
+zerotier-intercept /usr/sbin/httpd -D FOREGROUND >>/tmp/apache.out 2>&1
diff --git a/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/httpd-2.4.17-3.fc23.x86_64.name b/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/httpd-2.4.17-3.fc23.x86_64.name
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/httpd-2.4.17-3.fc23.x86_64.name
diff --git a/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/monitor_dockerfile b/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/monitor_dockerfile
new file mode 100644
index 00000000..e8f803cd
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/monitor_dockerfile
@@ -0,0 +1,23 @@
+# ZT Network Containers Test Monitor
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+EXPOSE 9993/udp
+
+# Install LWIP library used by service
+ADD liblwip.so /var/lib/zerotier-one/liblwip.so
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD monitor_identity.public /var/lib/zerotier-one/identity.public
+ADD monitor_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Start ZeroTier-One
+ADD monitor_entrypoint.sh /monitor_entrypoint.sh
+RUN chmod -v +x /monitor_entrypoint.sh
+CMD ["./monitor_entrypoint.sh"]
diff --git a/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/monitor_entrypoint.sh b/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/monitor_entrypoint.sh
new file mode 100644
index 00000000..52470efd
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/monitor_entrypoint.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+netcon_wait_time=35 # wait for test container to come online
+app_timeout_time=25 # app-specific timeout
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+fail=FAIL. # appended to result file in event of failure
+ok=OK. # appended to result file in event of success
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+bigfile_name=bigfile # large, random test transfer file
+rx_md5sumfile="$file_path"rx_"$bigfile_name"_md5sum"$tmp_ext"
+tx_md5sumfile="$file_path"tx_"$bigfile_name"_md5sum"$tmp_ext"
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test Monitor'
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+virtip4=""
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+done
+echo '*** Starting Test...'
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Sleeping for (' "$netcon_wait_time" 's ) while we wait for the Network Container to come online...'
+sleep "$netcon_wait_time"s
+ncvirtip=$(<$address_file)
+
+
+# --- Test section ---
+echo '*** Curling from intercepted server at' $ncvirtip
+rm -rf "$file_path"*."$file_base"
+touch "$bigfile_name"
+
+# Perform test
+# curl --connect-timeout "$app_timeout_time" -v -o "$file_path$file_base" http://"$ncvirtip"/index.html
+# Large transfer test
+curl --connect-timeout "$app_timeout_time" -v -o "$bigfile_name" http://"$ncvirtip"/"$bigfile_name"
+
+# Check md5
+md5sum < "$bigfile_name" >> "$rx_md5sumfile"
+rx_md5sum=$(<$rx_md5sumfile)
+tx_md5sum=$(<$tx_md5sumfile)
+
+echo '*** Comparing md5: ' "$rx_md5sum" ' and ' "$tx_md5sum"
+
+if [ "$rx_md5sum" != "$tx_md5sum" ];
+then
+ echo 'MD5 FAIL'
+ touch "$file_path$fail$test_name.txt"
+ printf 'Test: md5 sum did not match!\n' >> "$file_path$fail$test_name.txt"
+else
+ echo 'MD5 OK'
+ touch "$file_path$ok$test_name.txt"
+ printf 'Test: md5 sum ok!\n' >> "$file_path$ok$test_name.txt"
+ cat "$rx_md5sumfile" >> "$file_path$ok$test_name.txt"
+ cat "$tx_md5sumfile" >> "$file_path$ok$test_name.txt"
+fi
+
+
+
+
+
+
+
+
+
+
+
diff --git a/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/netcon_dockerfile b/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/netcon_dockerfile
new file mode 100644
index 00000000..3a17acdd
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/netcon_dockerfile
@@ -0,0 +1,39 @@
+# ZT Network Containers Test
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+# Install apps
+RUN yum -y update
+RUN yum -y install httpd-2.4.17-3.fc23.x86_64
+RUN yum clean all
+
+EXPOSE 9993/udp 80/udp
+
+# Install LWIP library used by service
+ADD liblwip.so /var/lib/zerotier-one/liblwip.so
+
+# Install syscall intercept library
+ADD zerotier-intercept /
+ADD libzerotierintercept.so.1.0 /
+RUN cp libzerotierintercept.so.1.0 lib/libzerotierintercept.so.1.0
+RUN cp libzerotierintercept.so.1.0 /lib/libzerotierintercept.so.1.0
+RUN ln -sf /lib/libzerotierintercept.so.1.0 /lib/libzerotierintercept
+RUN /usr/bin/install -c zerotier-intercept /usr/bin
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD netcon_identity.public /var/lib/zerotier-one/identity.public
+ADD netcon_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Install test scripts
+ADD netcon_entrypoint.sh /netcon_entrypoint.sh
+RUN chmod -v +x /netcon_entrypoint.sh
+
+# Start ZeroTier-One
+CMD ["./netcon_entrypoint.sh"]
diff --git a/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/netcon_entrypoint.sh b/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/netcon_entrypoint.sh
new file mode 100644
index 00000000..44f8db12
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd-2.4.17-3.fc23.x86_64/netcon_entrypoint.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+bigfile_name=bigfile
+bigfile_size=10M # size of file we want to use for the test
+tx_md5sumfile="$file_path"tx_"$bigfile_name"_md5sum"$tmp_ext"
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test: ' "$test_name"
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+virtip4=""
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+ dev=`/zerotier-cli listnetworks | grep -F "" | cut -d ' ' -f 8 | cut -d "_" -f 2 | sed "s/^<dev>//" | tr '\n' '\0'`
+done
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Writing address to ' "$address_file"
+echo $virtip4 > "$address_file"
+
+export ZT_NC_NWID=$dev
+
+# --- Test section ---
+# Generate large random file for transfer test, share md5sum for monitor container to check
+echo '*** Generating ' "$bigfile_size" ' file'
+dd if=/dev/urandom of=/var/www/html/"$bigfile_name" bs="$bigfile_size" count=1
+#md5sum /var/www/html/"$bigfile_name" >> "$tx_md5sumfile"
+md5sum < /var/www/html/"$bigfile_name" >> "$tx_md5sumfile"
+echo '*** Wrote MD5 sum to ' "$tx_md5sumfile"
+
+echo '*** Starting application...'
+sleep 0.5
+rm -rf /run/httpd/* /tmp/httpd*
+zerotier-intercept /usr/sbin/httpd -D FOREGROUND >>/tmp/apache.out 2>&1
diff --git a/netcon/docker-test/httpd/httpd_demo/htdocs/ZeroTierIcon.png b/netcon/docker-test/httpd/httpd_demo/htdocs/ZeroTierIcon.png
new file mode 100644
index 00000000..4d9641b3
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd_demo/htdocs/ZeroTierIcon.png
Binary files differ
diff --git a/netcon/docker-test/httpd/httpd_demo/htdocs/index.html b/netcon/docker-test/httpd/httpd_demo/htdocs/index.html
new file mode 100644
index 00000000..017e4493
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd_demo/htdocs/index.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link rel="stylesheet" href="http://bootswatch.com/cosmo/bootstrap.min.css">
+ <title>ZeroTier Network Containers Preview</title>
+</head>
+<body>
+
+<br><br>
+
+<div class="container-fluid">
+ <div class="row">
+ <div class="col-xs-1 col-xs-offset-1"><img src="ZeroTierIcon.png" style="width: 100%; height: 100%;"></div>
+ <div class="col-xs-9">
+ <h1>ZeroTier Network Containers Preview</h1>
+ (a.k.a. super bleeding edge pre-alphe pre-release demo)
+ </div>
+ </div>
+</div>
+
+<br>
+<hr>
+<br>
+
+<div class="container-fluid"><div class="row"><div class="col-xs-10 col-xs-offset-1 lead">
+
+<p><b>This page is being served from a Docker container with its own private TCP/IP microservice.</b></p>
+
+<p>
+It's connected to a virtual network, but if you "docker exec" into it and look around you won't find any special devices. No special privileges or configuration changes on the Docker host were needed. Everything is completely "stock" and completely self-contained.
+</p>
+
+<p>
+There's nothing special about the web server. It's just Apache. There's nothing special about the Linux image. It's based on a regular Fedora Docker base image. Other than Apache, the only thing this image contains is the ZeroTier network containers microservice and dynamic library.
+</p>
+
+<p>
+When Apache is run, our launcher script configures it to load a special dynamic library. This library intercepts calls to the Linux C networking API, redirecting network I/O to our private network stack microservice instead of the standard Linux kernel network path. This microservice takes care of the rest, automatically encapsulating traffic and sending it over the virtual network instead of the physical.
+</p>
+
+<p>
+It's a bit like how networking would work on a microkernel: modular, composable, portable, and independent.
+</p>
+
+<p>
+Network Containers allows a Docker (or LXC, CoreOS/rkt, runc, OpenVZ, SmartOS/Triton, <a target="_blank" href="https://github.com/p8952/bocker">bocker</a>, or even just bare metal Linux) system to connect to virtual networks without requiring <u>any</u> special permissions or special configuration on the host node. Processes inside the container don't even need to run with root permissions. It's 100% user-space, making it ideal for multi-tenant deployments or any other situation where modifying the configuration of the host node is impossible or just inconvenient.
+</p>
+
+<p>
+Once properly tuned and optimized, Network Containers also has the potential to be much faster than tun/tap or pcap based network overlays. It imposes only a single context switch from application/service to virtual network microservice as opposed to at least four for tun/tap and pcap-based solutions, since the latter require two trips through the kernel network stack. We believe it may be possible to approach or even equal the performance of VXLAN/IPSec or other fully kernel-mode configurations, but with the ease and total independence of a fully container-based solution.
+</p>
+
+<p>
+We created this container image to show you a preview of one of the projects we've been working on at ZeroTier. We still have a good deal of packaging, testing, and performance optimization work to do before Network Containers will be ready for a real public beta release. Follow the <a href="https://www.zerotier.com/blog">blog</a> or <a href="https://twitter.com/zerotier">@zerotier</a> for updates and announcements.
+</p>
+
+<p>
+P.S. If you want to use ZeroTier in Docker today, you can do it with the same ZeroTier One endpoint service you're using to access this network. The only catch is that you have to launch your containers with "--device=/dev/net/tun --cap-add=NET_ADMIN". Network Containers eliminates the need for these special options.
+</p>
+
+</div></div></div>
+
+<hr>
+
+</body>
+</html>
diff --git a/netcon/docker-test/httpd/httpd_demo/httpd_demo.name b/netcon/docker-test/httpd/httpd_demo/httpd_demo.name
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd_demo/httpd_demo.name
diff --git a/netcon/docker-test/httpd/httpd_demo/monitor_dockerfile b/netcon/docker-test/httpd/httpd_demo/monitor_dockerfile
new file mode 100644
index 00000000..08f08ae1
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd_demo/monitor_dockerfile
@@ -0,0 +1,25 @@
+# ZT Network Containers Test Monitor
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+EXPOSE 9993/udp
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD monitor_identity.public /var/lib/zerotier-one/identity.public
+ADD monitor_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Install LWIP library used by service
+ADD liblwip.so /
+RUN mkdir -p ext/bin/lwip
+RUN cp liblwip.so ext/bin/lwip/liblwip.so
+
+# Start ZeroTier-One
+ADD monitor_entrypoint.sh /monitor_entrypoint.sh
+RUN chmod -v +x /monitor_entrypoint.sh
+CMD ["./monitor_entrypoint.sh"]
diff --git a/netcon/docker-test/httpd/httpd_demo/monitor_entrypoint.sh b/netcon/docker-test/httpd/httpd_demo/monitor_entrypoint.sh
new file mode 100644
index 00000000..79d4391d
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd_demo/monitor_entrypoint.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+netcon_wait_time=25 # wait for test container to come online
+app_timeout_time=15 # app-specific timeout
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+fail=FAIL. # appended to result file in event of failure
+ok=OK. # appended to result file in event of success
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+bigfile_name=bigfile # large, random test transfer file
+rx_md5sumfile="$file_path"rx_"$bigfile_name"_md5sum"$tmp_ext"
+tx_md5sumfile="$file_path"tx_"$bigfile_name"_md5sum"$tmp_ext"
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test Monitor'
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+echo '*** Waiting for initial identity generation...'
+while [ ! -s /var/lib/zerotier-one/identity.secret ]; do
+ sleep 0.2
+done
+echo '*** Waiting for network config...'
+virtip4=""
+while [ ! -s /var/lib/zerotier-one/networks.d/"$nwconf" ]; do
+ sleep 0.2
+done
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+done
+echo '*** Starting Test...'
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Sleeping for (' "$netcon_wait_time" 's ) while we wait for the Network Container to come online...'
+sleep "$netcon_wait_time"s
+ncvirtip=$(<$address_file)
+
+
+# --- Test section ---
+echo '*** Curling from intercepted server at' $ncvirtip
+rm -rf "$file_path"*."$file_base"
+touch "$bigfile_name"
+
+# Perform test
+# curl --connect-timeout "$app_timeout_time" -v -o "$file_path$file_base" http://"$ncvirtip"/index.html
+# Large transfer test
+curl --connect-timeout "$app_timeout_time" -v -o "$bigfile_name" http://"$ncvirtip"/"$bigfile_name"
+
+# Check md5
+md5sum < "$bigfile_name" >> "$rx_md5sumfile"
+rx_md5sum=$(<$rx_md5sumfile)
+tx_md5sum=$(<$tx_md5sumfile)
+
+echo '*** Comparing md5: ' "$rx_md5sum" ' and ' "$tx_md5sum"
+
+if [ $rx_md5sum != $tx_md5sum ];
+then
+ echo 'MD5 FAIL'
+ touch "$file_path$fail$test_name.txt"
+ printf 'Test: md5 sum did not match!\n' >> "$file_path$fail$test_name.txt"
+else
+ echo 'MD5 OK'
+ touch "$file_path$ok$test_name.txt"
+ printf 'Test: md5 sum ok!\n' >> "$file_path$ok$test_name.txt"
+fi
+
+
+
+
+
+
+
+
+
+
+
diff --git a/netcon/docker-test/httpd/httpd_demo/netcon_dockerfile b/netcon/docker-test/httpd/httpd_demo/netcon_dockerfile
new file mode 100644
index 00000000..be9d3f3f
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd_demo/netcon_dockerfile
@@ -0,0 +1,47 @@
+# ZT Network Containers Test
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+# Install apps
+RUN yum -y update
+RUN yum -y install httpd-2.4.17-3.fc23.x86_64
+RUN yum clean all
+
+EXPOSE 9993/udp
+
+#include Apache
+ADD htdocs/index.html /
+ADD htdocs/ZeroTierIcon.png /
+RUN mv index.html /var/www/html/index.html
+RUN mv ZeroTierIcon.png /var/www/html/ZeroTierIcon.png
+
+# Install syscall intercept library
+ADD zerotier-intercept /
+ADD libzerotierintercept.so.1.0 /
+RUN cp libzerotierintercept.so.1.0 lib/libzerotierintercept.so.1.0
+RUN cp libzerotierintercept.so.1.0 /lib/libzerotierintercept.so.1.0
+RUN ln -sf /lib/libzerotierintercept.so.1.0 /lib/libzerotierintercept
+RUN /usr/bin/install -c zerotier-intercept /usr/bin
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD netcon_identity.public /var/lib/zerotier-one/identity.public
+ADD netcon_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Install test scripts
+ADD netcon_entrypoint.sh /netcon_entrypoint.sh
+RUN chmod -v +x /netcon_entrypoint.sh
+
+# Install LWIP library used by service
+ADD liblwip.so /
+RUN mkdir -p ext/bin/lwip
+RUN cp liblwip.so ext/bin/lwip/liblwip.so
+
+# Start ZeroTier-One
+CMD ["./netcon_entrypoint.sh"]
diff --git a/netcon/docker-test/httpd/httpd_demo/netcon_entrypoint.sh b/netcon/docker-test/httpd/httpd_demo/netcon_entrypoint.sh
new file mode 100644
index 00000000..fd47cf38
--- /dev/null
+++ b/netcon/docker-test/httpd/httpd_demo/netcon_entrypoint.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+bigfile_name=bigfile
+bigfile_size=10M # size of file we want to use for the test
+tx_md5sumfile="$file_path"tx_"$bigfile_name"_md5sum"$tmp_ext"
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test: ' "$test_name"
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+echo '*** Waiting for initial identity generation...'
+while [ ! -s /var/lib/zerotier-one/identity.secret ]; do
+ sleep 0.2
+done
+echo '*** Waiting for network config...'
+virtip4=""
+while [ ! -s /var/lib/zerotier-one/networks.d/"$nwconf" ]; do
+ sleep 0.2
+done
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+done
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Writing address to ' "$address_file"
+echo $virtip4 > "$address_file"
+
+
+# --- Test section ---
+# Generate large random file for transfer test, share md5sum for monitor container to check
+echo '*** Generating ' "$bigfile_size" ' file'
+dd if=/dev/urandom of=/var/www/html/"$bigfile_name" bs="$bigfile_size" count=1
+#md5sum /var/www/html/"$bigfile_name" >> "$tx_md5sumfile"
+md5sum < /var/www/html/"$bigfile_name" >> "$tx_md5sumfile"
+echo '*** Wrote MD5 sum to ' "$tx_md5sumfile"
+
+echo '*** Starting application...'
+sleep 0.5
+rm -rf /run/httpd/* /tmp/httpd*
+zerotier-intercept /usr/sbin/httpd -D FOREGROUND >>/tmp/apache.out 2>&1
diff --git a/netcon/docker-test/monitor_identity.public b/netcon/docker-test/monitor_identity.public
new file mode 100644
index 00000000..7ee33756
--- /dev/null
+++ b/netcon/docker-test/monitor_identity.public
@@ -0,0 +1 @@
+e5b0e7dc2a:0:f257ee08ad335ef87caf30598a5a17a758f193da58085b7b471c9cc48413585cba5e7830c23508907a2f991308a7ddc01912023cc454a7de80cdad68edebcc63 \ No newline at end of file
diff --git a/netcon/docker-test/monitor_identity.secret b/netcon/docker-test/monitor_identity.secret
new file mode 100644
index 00000000..05c10132
--- /dev/null
+++ b/netcon/docker-test/monitor_identity.secret
@@ -0,0 +1 @@
+e5b0e7dc2a:0:f257ee08ad335ef87caf30598a5a17a758f193da58085b7b471c9cc48413585cba5e7830c23508907a2f991308a7ddc01912023cc454a7de80cdad68edebcc63:21e5f68984b729acdaedd6c4c057d0a5447cefa66ec5de8d10982f77efc148e65ce954ee17604716398a91ab74563b0b25263f84b45cecd97fa1bc35d1ef2bd7 \ No newline at end of file
diff --git a/netcon/docker-test/netcon_identity.public b/netcon/docker-test/netcon_identity.public
new file mode 100644
index 00000000..36b8ec69
--- /dev/null
+++ b/netcon/docker-test/netcon_identity.public
@@ -0,0 +1 @@
+ba2488cd53:0:a8bd3d0febd1da023b11382f45df6d246be9407ac62dc32c0c60aa2b96d3df15aa303a0eb28297916f7bcdbcd73a960f404a202f336cd35103d837bce1fb896f \ No newline at end of file
diff --git a/netcon/docker-test/netcon_identity.secret b/netcon/docker-test/netcon_identity.secret
new file mode 100644
index 00000000..6f06053e
--- /dev/null
+++ b/netcon/docker-test/netcon_identity.secret
@@ -0,0 +1 @@
+ba2488cd53:0:a8bd3d0febd1da023b11382f45df6d246be9407ac62dc32c0c60aa2b96d3df15aa303a0eb28297916f7bcdbcd73a960f404a202f336cd35103d837bce1fb896f:07d0caef50f3c94897035a954cdc73dc391f77a267caa54d52d4841edbec24388d601d27522d415a943764f9c91b7870754c08b4e04780b29c7c69edade2f638 \ No newline at end of file
diff --git a/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/monitor_dockerfile b/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/monitor_dockerfile
new file mode 100644
index 00000000..e8f803cd
--- /dev/null
+++ b/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/monitor_dockerfile
@@ -0,0 +1,23 @@
+# ZT Network Containers Test Monitor
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+EXPOSE 9993/udp
+
+# Install LWIP library used by service
+ADD liblwip.so /var/lib/zerotier-one/liblwip.so
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD monitor_identity.public /var/lib/zerotier-one/identity.public
+ADD monitor_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Start ZeroTier-One
+ADD monitor_entrypoint.sh /monitor_entrypoint.sh
+RUN chmod -v +x /monitor_entrypoint.sh
+CMD ["./monitor_entrypoint.sh"]
diff --git a/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/monitor_entrypoint.sh b/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/monitor_entrypoint.sh
new file mode 100644
index 00000000..52470efd
--- /dev/null
+++ b/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/monitor_entrypoint.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+netcon_wait_time=35 # wait for test container to come online
+app_timeout_time=25 # app-specific timeout
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+fail=FAIL. # appended to result file in event of failure
+ok=OK. # appended to result file in event of success
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+bigfile_name=bigfile # large, random test transfer file
+rx_md5sumfile="$file_path"rx_"$bigfile_name"_md5sum"$tmp_ext"
+tx_md5sumfile="$file_path"tx_"$bigfile_name"_md5sum"$tmp_ext"
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test Monitor'
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+virtip4=""
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+done
+echo '*** Starting Test...'
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Sleeping for (' "$netcon_wait_time" 's ) while we wait for the Network Container to come online...'
+sleep "$netcon_wait_time"s
+ncvirtip=$(<$address_file)
+
+
+# --- Test section ---
+echo '*** Curling from intercepted server at' $ncvirtip
+rm -rf "$file_path"*."$file_base"
+touch "$bigfile_name"
+
+# Perform test
+# curl --connect-timeout "$app_timeout_time" -v -o "$file_path$file_base" http://"$ncvirtip"/index.html
+# Large transfer test
+curl --connect-timeout "$app_timeout_time" -v -o "$bigfile_name" http://"$ncvirtip"/"$bigfile_name"
+
+# Check md5
+md5sum < "$bigfile_name" >> "$rx_md5sumfile"
+rx_md5sum=$(<$rx_md5sumfile)
+tx_md5sum=$(<$tx_md5sumfile)
+
+echo '*** Comparing md5: ' "$rx_md5sum" ' and ' "$tx_md5sum"
+
+if [ "$rx_md5sum" != "$tx_md5sum" ];
+then
+ echo 'MD5 FAIL'
+ touch "$file_path$fail$test_name.txt"
+ printf 'Test: md5 sum did not match!\n' >> "$file_path$fail$test_name.txt"
+else
+ echo 'MD5 OK'
+ touch "$file_path$ok$test_name.txt"
+ printf 'Test: md5 sum ok!\n' >> "$file_path$ok$test_name.txt"
+ cat "$rx_md5sumfile" >> "$file_path$ok$test_name.txt"
+ cat "$tx_md5sumfile" >> "$file_path$ok$test_name.txt"
+fi
+
+
+
+
+
+
+
+
+
+
+
diff --git a/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/netcon_dockerfile b/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/netcon_dockerfile
new file mode 100644
index 00000000..e7b4ad34
--- /dev/null
+++ b/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/netcon_dockerfile
@@ -0,0 +1,39 @@
+# ZT Network Containers Test
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+# Install apps
+RUN yum -y update
+RUN yum -y install nginx-1:1.8.0-13.fc23.x86_64
+RUN yum clean all
+
+EXPOSE 9993/udp 80/udp
+
+# Install LWIP library used by service
+ADD liblwip.so /var/lib/zerotier-one/liblwip.so
+
+# Install syscall intercept library
+ADD zerotier-intercept /
+ADD libzerotierintercept.so.1.0 /
+RUN cp libzerotierintercept.so.1.0 lib/libzerotierintercept.so.1.0
+RUN cp libzerotierintercept.so.1.0 /lib/libzerotierintercept.so.1.0
+RUN ln -sf /lib/libzerotierintercept.so.1.0 /lib/libzerotierintercept
+RUN /usr/bin/install -c zerotier-intercept /usr/bin
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD netcon_identity.public /var/lib/zerotier-one/identity.public
+ADD netcon_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Install test scripts
+ADD netcon_entrypoint.sh /netcon_entrypoint.sh
+RUN chmod -v +x /netcon_entrypoint.sh
+
+# Start ZeroTier-One
+CMD ["./netcon_entrypoint.sh"]
diff --git a/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/netcon_entrypoint.sh b/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/netcon_entrypoint.sh
new file mode 100644
index 00000000..12d0d998
--- /dev/null
+++ b/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/netcon_entrypoint.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+bigfile_name=bigfile
+bigfile_size=10M # size of file we want to use for the test
+tx_md5sumfile="$file_path"tx_"$bigfile_name"_md5sum"$tmp_ext"
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test: ' "$test_name"
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+virtip4=""
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+ dev=`/zerotier-cli listnetworks | grep -F "" | cut -d ' ' -f 8 | cut -d "_" -f 2 | sed "s/^<dev>//" | tr '\n' '\0'`
+done
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Writing address to ' "$address_file"
+echo $virtip4 > "$address_file"
+
+export ZT_NC_NWID=$dev
+
+# --- Test section ---
+nginx_html_path=/usr/share/nginx/html/
+# Generate large random file for transfer test, share md5sum for monitor container to check
+echo '*** Generating ' "$bigfile_size" ' file'
+dd if=/dev/urandom of="$nginx_html_path$bigfile_name" bs="$bigfile_size" count=1
+#md5sum /var/www/html/"$bigfile_name" >> "$tx_md5sumfile"
+md5sum < "$nginx_html_path$bigfile_name" >> "$tx_md5sumfile"
+echo '*** Wrote MD5 sum to ' "$tx_md5sumfile"
+
+echo '*** Starting application...'
+sleep 0.5
+zerotier-intercept nginx
diff --git a/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/nginx-1.8.0-13.fc23.x86_64.name b/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/nginx-1.8.0-13.fc23.x86_64.name
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/nginx-1.8.0-13.fc23.x86_64.name
diff --git a/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/trace.txt b/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/trace.txt
new file mode 100644
index 00000000..b73e9fa6
--- /dev/null
+++ b/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64/trace.txt
@@ -0,0 +1,943 @@
+27096 execve("/usr/bin/zerotier-intercept", ["zerotier-intercept", "nginx"], [/* 30 vars */]) = 0
+27096 brk(0) = 0x56046800c000
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe905081000
+27096 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
+27096 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
+27096 fstat(3, {st_mode=S_IFREG|0644, st_size=87726, ...}) = 0
+27096 mmap(NULL, 87726, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe90506b000
+27096 close(3) = 0
+27096 open("/lib64/libtinfo.so.5", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@\316\0\0\0\0\0\0@\0\0\0\0\0\0\0 \231\2\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\33\0\32\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\fT\2\0\0\0\0\0\fT\2\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=172000, ...}) = 0
+27096 mmap(NULL, 2267936, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe904c38000
+27096 mprotect(0x7fe904c5e000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7fe904e5d000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7fe904e5d000
+27096 close(3) = 0
+27096 open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\16\0\0\0\0\0\0@\0\0\0\0\0\0\0\220C\0\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0 \0\37\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\320 \0\0\0\0\0\0\320 \0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=19344, ...}) = 0
+27096 mmap(NULL, 2109712, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe904a34000
+27096 mprotect(0x7fe904a37000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7fe904c36000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7fe904c36000
+27096 close(3) = 0
+27096 open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\6\2\0\0\0\0\0@\0\0\0\0\0\0\0\220\1 \0\0\0\0\0\0\0\0\0@\0008\0\n\0@\0J\0I\0\6\0\0\0\5\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0000\2\0\0\0\0\0\0000\2\0\0\0\0\0\0\10\0\0\0\0\0\0\0\3\0\0\0\4\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=2102288, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe90506a000
+27096 mmap(NULL, 3934784, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe904673000
+27096 mprotect(0x7fe90482a000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7fe904a2a000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b7000) = 0x7fe904a2a000
+27096 mmap(0x7fe904a30000, 14912, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe904a30000
+27096 close(3) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe905069000
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe905068000
+27096 arch_prctl(ARCH_SET_FS, 0x7fe905069700) = 0
+27096 mprotect(0x7fe904a2a000, 16384, PROT_READ) = 0
+27096 mprotect(0x7fe904c36000, 4096, PROT_READ) = 0
+27096 mprotect(0x7fe904e5d000, 16384, PROT_READ) = 0
+27096 mprotect(0x560466b3f000, 16384, PROT_READ) = 0
+27096 mprotect(0x7fe905082000, 4096, PROT_READ) = 0
+27096 munmap(0x7fe90506b000, 87726) = 0
+27096 open("/dev/tty", O_RDWR|O_NONBLOCK) = 3
+27096 close(3) = 0
+27096 brk(0) = 0x56046800c000
+27096 brk(0x56046802d000) = 0x56046802d000
+27096 brk(0) = 0x56046802d000
+27096 open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
+27096 fstat(3, {st_mode=S_IFREG|0644, st_size=110439344, ...}) = 0
+27096 mmap(NULL, 110439344, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe8fdd20000
+27096 close(3) = 0
+27096 getuid() = 0
+27096 getgid() = 0
+27096 geteuid() = 0
+27096 getegid() = 0
+27096 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+27096 brk(0) = 0x56046802d000
+27096 open("/proc/meminfo", O_RDONLY|O_CLOEXEC) = 3
+27096 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe905080000
+27096 read(3, "MemTotal: 3120024 kB\nMemFree: 1575724 kB\nMemAvailable: 2880692 kB\nBuffers: 134544 kB\nCached: "..., 1024) = 1024
+27096 close(3) = 0
+27096 munmap(0x7fe905080000, 4096) = 0
+27096 rt_sigaction(SIGCHLD, {SIG_DFL, [], SA_RESTORER|SA_RESTART, 0x7fe9046a7b20}, {SIG_DFL, [], 0}, 8) = 0
+27096 rt_sigaction(SIGCHLD, {SIG_DFL, [], SA_RESTORER|SA_RESTART, 0x7fe9046a7b20}, {SIG_DFL, [], SA_RESTORER|SA_RESTART, 0x7fe9046a7b20}, 8) = 0
+27096 rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fe9046a7b20}, {SIG_DFL, [], 0}, 8) = 0
+27096 rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fe9046a7b20}, {SIG_DFL, [], SA_RESTORER, 0x7fe9046a7b20}, 8) = 0
+27096 rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x7fe9046a7b20}, {SIG_DFL, [], 0}, 8) = 0
+27096 rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x7fe9046a7b20}, {SIG_DFL, [], SA_RESTORER, 0x7fe9046a7b20}, 8) = 0
+27096 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+27096 rt_sigaction(SIGQUIT, {SIG_IGN, [], SA_RESTORER, 0x7fe9046a7b20}, {SIG_DFL, [], SA_RESTORER, 0x7fe9046a7b20}, 8) = 0
+27096 uname({sysname="Linux", nodename="localhost.localdomain", ...}) = 0
+27096 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+27096 open("/usr/lib64/gconv/gconv-modules.cache", O_RDONLY) = 3
+27096 fstat(3, {st_mode=S_IFREG|0644, st_size=26254, ...}) = 0
+27096 mmap(NULL, 26254, PROT_READ, MAP_SHARED, 3, 0) = 0x7fe90507a000
+27096 close(3) = 0
+27096 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+27096 stat("/home/joseph/dev/zerotierone/netcon/docker-test/nginx/nginx-1.8.0-13.fc23.x86_64", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
+27096 stat(".", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
+27096 getpid() = 27096
+27096 getppid() = 27094
+27096 getpgrp() = 27094
+27096 rt_sigaction(SIGCHLD, {0x56046689c320, [], SA_RESTORER|SA_RESTART, 0x7fe9046a7b20}, {SIG_DFL, [], SA_RESTORER|SA_RESTART, 0x7fe9046a7b20}, 8) = 0
+27096 getrlimit(RLIMIT_NPROC, {rlim_cur=12104, rlim_max=12104}) = 0
+27096 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+27096 open("/usr/bin/zerotier-intercept", O_RDONLY) = 3
+27096 ioctl(3, TCGETS, 0x7fffea086420) = -1 ENOTTY (Inappropriate ioctl for device)
+27096 lseek(3, 0, SEEK_CUR) = 0
+27096 read(3, "#!/bin/sh\n# usage:\n# /usr/bin/intercept program <args>\n\nif [ $# = 0 ] ; then\n ", 80) = 80
+27096 lseek(3, 0, SEEK_SET) = 0
+27096 getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
+27096 fcntl(255, F_GETFD) = -1 EBADF (Bad file descriptor)
+27096 dup2(3, 255) = 255
+27096 close(3) = 0
+27096 fcntl(255, F_SETFD, FD_CLOEXEC) = 0
+27096 fcntl(255, F_GETFL) = 0x8000 (flags O_RDONLY|O_LARGEFILE)
+27096 fstat(255, {st_mode=S_IFREG|0755, st_size=982, ...}) = 0
+27096 lseek(255, 0, SEEK_CUR) = 0
+27096 read(255, "#!/bin/sh\n# usage:\n# /usr/bin/intercept program <args>\n\nif [ $# = 0 ] ; then\n echo \"$0: insufficient arguments\"\n exit\nfi\n\nca"..., 982) = 982
+27096 stat(".", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
+27096 stat("/usr/local/sbin/nginx", 0x7fffea085d10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/bin/nginx", 0x7fffea085d10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/sbin/nginx", {st_mode=S_IFREG|0755, st_size=1154208, ...}) = 0
+27096 stat("/usr/sbin/nginx", {st_mode=S_IFREG|0755, st_size=1154208, ...}) = 0
+27096 geteuid() = 0
+27096 getegid() = 0
+27096 getuid() = 0
+27096 getgid() = 0
+27096 access("/usr/sbin/nginx", X_OK) = 0
+27096 stat("/usr/sbin/nginx", {st_mode=S_IFREG|0755, st_size=1154208, ...}) = 0
+27096 geteuid() = 0
+27096 getegid() = 0
+27096 getuid() = 0
+27096 getgid() = 0
+27096 access("/usr/sbin/nginx", R_OK) = 0
+27096 stat("/usr/sbin/nginx", {st_mode=S_IFREG|0755, st_size=1154208, ...}) = 0
+27096 stat("/usr/sbin/nginx", {st_mode=S_IFREG|0755, st_size=1154208, ...}) = 0
+27096 geteuid() = 0
+27096 getegid() = 0
+27096 getuid() = 0
+27096 getgid() = 0
+27096 access("/usr/sbin/nginx", X_OK) = 0
+27096 stat("/usr/sbin/nginx", {st_mode=S_IFREG|0755, st_size=1154208, ...}) = 0
+27096 geteuid() = 0
+27096 getegid() = 0
+27096 getuid() = 0
+27096 getgid() = 0
+27096 access("/usr/sbin/nginx", R_OK) = 0
+27096 rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fe9046a7b20}, {SIG_DFL, [], SA_RESTORER, 0x7fe9046a7b20}, 8) = 0
+27096 rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x7fe9046a7b20}, {SIG_IGN, [], SA_RESTORER, 0x7fe9046a7b20}, 8) = 0
+27096 rt_sigaction(SIGCHLD, {SIG_DFL, [], SA_RESTORER|SA_RESTART, 0x7fe9046a7b20}, {0x56046689c320, [], SA_RESTORER|SA_RESTART, 0x7fe9046a7b20}, 8) = 0
+27096 execve("/usr/sbin/nginx", ["nginx"], [/* 29 vars */]) = 0
+27096 brk(0) = 0x564360913000
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b82000
+27096 open("/lib/libzerotierintercept.so.1.0", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\23\0\0\0\0\0\0@\0\0\0\0\0\0\0X\360\0\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0 \0\35\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`A\0\0\0\0\0\0`A\0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=63576, ...}) = 0
+27096 mmap(NULL, 2118280, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca275d000
+27096 mprotect(0x7f8ca2762000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8ca2961000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x4000) = 0x7f8ca2961000
+27096 close(3) = 0
+27096 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
+27096 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
+27096 fstat(3, {st_mode=S_IFREG|0644, st_size=87726, ...}) = 0
+27096 mmap(NULL, 87726, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f8ca2b6c000
+27096 close(3) = 0
+27096 open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260`\0\0\0\0\0\0@\0\0\0\0\0\0\0p;\2\0\0\0\0\0\0\0\0\0@\0008\0\t\0@\0)\0(\0\6\0\0\0\5\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0\370\1\0\0\0\0\0\0\370\1\0\0\0\0\0\0\10\0\0\0\0\0\0\0\3\0\0\0\4\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=148912, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b6b000
+27096 mmap(NULL, 2213040, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca2540000
+27096 mprotect(0x7f8ca2558000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8ca2757000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17000) = 0x7f8ca2757000
+27096 mmap(0x7f8ca2759000, 13488, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2759000
+27096 close(3) = 0
+27096 open("/lib64/libcrypt.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\r\0\0\0\0\0\0@\0\0\0\0\0\0\0\210\206\0\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0 \0\37\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\234g\0\0\0\0\0\0\234g\0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=36488, ...}) = 0
+27096 mmap(NULL, 2314784, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca230a000
+27096 mprotect(0x7f8ca2311000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8ca2510000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x7f8ca2510000
+27096 mmap(0x7f8ca2512000, 184864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2512000
+27096 close(3) = 0
+27096 open("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360\26\0\0\0\0\0\0@\0\0\0\0\0\0\0(\373\6\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\33\0\32\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\364\337\6\0\0\0\0\0\364\337\6\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=459240, ...}) = 0
+27096 mmap(NULL, 2552072, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca209a000
+27096 mprotect(0x7f8ca2108000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8ca2308000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6e000) = 0x7f8ca2308000
+27096 close(3) = 0
+27096 open("/lib64/libssl.so.10", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\271\1\0\0\0\0\0@\0\0\0\0\0\0\0008\223\7\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\35\0\34\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0t\317\6\0\0\0\0\0t\317\6\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=498296, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b6a000
+27096 mmap(NULL, 2591344, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca1e21000
+27096 mprotect(0x7f8ca1e8e000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8ca208e000, 49152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6d000) = 0x7f8ca208e000
+27096 close(3) = 0
+27096 open("/lib64/libcrypto.so.10", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\277\6\0\0\0\0\0@\0\0\0\0\0\0\0@\261$\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\35\0\34\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\304\2\"\0\0\0\0\0\304\2\"\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=2406528, ...}) = 0
+27096 mmap(NULL, 4506208, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca19d4000
+27096 mprotect(0x7f8ca1bf5000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8ca1df5000, 163840, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x221000) = 0x7f8ca1df5000
+27096 mmap(0x7f8ca1e1d000, 12896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8ca1e1d000
+27096 close(3) = 0
+27096 open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\16\0\0\0\0\0\0@\0\0\0\0\0\0\0\220C\0\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0 \0\37\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\320 \0\0\0\0\0\0\320 \0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=19344, ...}) = 0
+27096 mmap(NULL, 2109712, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca17d0000
+27096 mprotect(0x7f8ca17d3000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8ca19d2000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f8ca19d2000
+27096 close(3) = 0
+27096 open("/lib64/libz.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\"\0\0\0\0\0\0@\0\0\0\0\0\0\0\200V\1\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\34\0\33\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\354C\1\0\0\0\0\0\354C\1\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=89472, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b69000
+27096 mmap(NULL, 2183272, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca15ba000
+27096 mprotect(0x7f8ca15cf000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8ca17ce000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x14000) = 0x7f8ca17ce000
+27096 close(3) = 0
+27096 open("/lib64/libxml2.so.2", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340\344\2\0\0\0\0\0@\0\0\0\0\0\0\0\260\263\26\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\34\0\33\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0D\256\25\0\0\0\0\0D\256\25\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=1489584, ...}) = 0
+27096 mmap(NULL, 3561880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca1254000
+27096 mprotect(0x7f8ca13af000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8ca15af000, 40960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15b000) = 0x7f8ca15af000
+27096 mmap(0x7f8ca15b9000, 2456, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8ca15b9000
+27096 close(3) = 0
+27096 open("/lib64/libxslt.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\254\0\0\0\0\0\0@\0\0\0\0\0\0\0\250\345\3\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\35\0\34\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\354\302\3\0\0\0\0\0\354\302\3\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=257256, ...}) = 0
+27096 mmap(NULL, 2351336, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca1015000
+27096 mprotect(0x7f8ca1052000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8ca1251000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3c000) = 0x7f8ca1251000
+27096 mmap(0x7f8ca1253000, 232, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8ca1253000
+27096 close(3) = 0
+27096 open("/lib64/libexslt.so.0", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 =\0\0\0\0\0\0@\0\0\0\0\0\0\0\10N\1\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\34\0\33\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\304;\1\0\0\0\0\0\304;\1\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=87304, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b68000
+27096 mmap(NULL, 2180320, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca0e00000
+27096 mprotect(0x7f8ca0e14000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8ca1013000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x13000) = 0x7f8ca1013000
+27096 close(3) = 0
+27096 open("/lib64/libgd.so.3", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\352\0\0\0\0\0\0@\0\0\0\0\0\0\0 \210\6\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\33\0\32\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0t5\4\0\0\0\0\0t5\4\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=429792, ...}) = 0
+27096 mmap(NULL, 2542192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca0b93000
+27096 mprotect(0x7f8ca0bd7000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8ca0dd6000, 151552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x43000) = 0x7f8ca0dd6000
+27096 mmap(0x7f8ca0dfb000, 19056, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8ca0dfb000
+27096 close(3) = 0
+27096 open("/lib64/libGeoIP.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320o\0\0\0\0\0\0@\0\0\0\0\0\0\0\200\26\3\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\33\0\32\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\374\353\2\0\0\0\0\0\374\353\2\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=204096, ...}) = 0
+27096 mmap(NULL, 2298224, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca0961000
+27096 mprotect(0x7f8ca0990000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8ca0b90000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2f000) = 0x7f8ca0b90000
+27096 close(3) = 0
+27096 open("/lib64/libprofiler.so.0", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0PX\0\0\0\0\0\0@\0\0\0\0\0\0\0(\2\1\0\0\0\0\0\0\0\0\0@\0008\0\10\0@\0\34\0\33\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\356\0\0\0\0\0\0\1\356\0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=67880, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b67000
+27096 mmap(NULL, 2180896, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca074c000
+27096 mprotect(0x7f8ca075b000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8ca095b000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf000) = 0x7f8ca095b000
+27096 mmap(0x7f8ca095d000, 14112, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8ca095d000
+27096 close(3) = 0
+27096 open("/lib64/libperl.so.5.22", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\3405\3\0\0\0\0\0@\0\0\0\0\0\0\0(S\36\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\37\0\36\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\244\35\0\0\0\0\0\34\244\35\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=1989352, ...}) = 0
+27096 mmap(NULL, 4077384, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca0368000
+27096 mprotect(0x7f8ca0543000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8ca0742000, 40960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1da000) = 0x7f8ca0742000
+27096 close(3) = 0
+27096 open("/lib64/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\2009\0\0\0\0\0\0@\0\0\0\0\0\0\0H\267\1\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0!\0 \0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0,e\1\0\0\0\0\0,e\1\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=114568, ...}) = 0
+27096 mmap(NULL, 2206280, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8ca014d000
+27096 mprotect(0x7f8ca0164000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8ca0364000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17000) = 0x7f8ca0364000
+27096 mmap(0x7f8ca0366000, 6728, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8ca0366000
+27096 close(3) = 0
+27096 open("/lib64/libnsl.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240@\0\0\0\0\0\0@\0\0\0\0\0\0\0P\262\1\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0 \0\37\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0|\\\1\0\0\0\0\0|\\\1\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=113232, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b66000
+27096 mmap(NULL, 2198136, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9ff34000
+27096 mprotect(0x7f8c9ff4a000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8ca0149000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15000) = 0x7f8ca0149000
+27096 mmap(0x7f8ca014b000, 6776, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8ca014b000
+27096 close(3) = 0
+27096 open("/lib64/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0PU\0\0\0\0\0\0@\0\0\0\0\0\0\0 _\21\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0#\0\"\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\360\1\20\0\0\0\0\0\360\1\20\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=1140704, ...}) = 0
+27096 mmap(NULL, 3150104, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9fc32000
+27096 mprotect(0x7f8c9fd33000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9ff32000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x100000) = 0x7f8c9ff32000
+27096 close(3) = 0
+27096 open("/lib64/libutil.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\16\0\0\0\0\0\0@\0\0\0\0\0\0\0`0\0\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0 \0\37\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0X\33\0\0\0\0\0\0X\33\0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=14432, ...}) = 0
+27096 mmap(NULL, 2105616, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9fa2f000
+27096 mprotect(0x7f8c9fa31000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9fc30000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0x7f8c9fc30000
+27096 close(3) = 0
+27096 open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\6\2\0\0\0\0\0@\0\0\0\0\0\0\0\220\1 \0\0\0\0\0\0\0\0\0@\0008\0\n\0@\0J\0I\0\6\0\0\0\5\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0000\2\0\0\0\0\0\0000\2\0\0\0\0\0\0\10\0\0\0\0\0\0\0\3\0\0\0\4\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=2102288, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b65000
+27096 mmap(NULL, 3934784, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9f66e000
+27096 mprotect(0x7f8c9f825000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8c9fa25000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b7000) = 0x7f8c9fa25000
+27096 mmap(0x7f8c9fa2b000, 14912, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9fa2b000
+27096 close(3) = 0
+27096 open("/lib64/librt.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P!\0\0\0\0\0\0@\0\0\0\0\0\0\0(\240\0\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0$\0#\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0004c\0\0\0\0\0\0004c\0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=43304, ...}) = 0
+27096 mmap(NULL, 2128800, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9f466000
+27096 mprotect(0x7f8c9f46d000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9f66c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x7f8c9f66c000
+27096 close(3) = 0
+27096 open("/lib64/libfreebl3.so", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0009\0\0\0\0\0\0@\0\0\0\0\0\0\0\30\242\7\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\35\0\34\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0le\7\0\0\0\0\0le\7\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=502104, ...}) = 0
+27096 mmap(NULL, 2606920, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9f1e9000
+27096 mprotect(0x7f8c9f260000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9f45f000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x76000) = 0x7f8c9f45f000
+27096 mmap(0x7f8c9f462000, 14152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9f462000
+27096 close(3) = 0
+27096 open("/lib64/libgssapi_krb5.so.2", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\300\0\0\0\0\0\0@\0\0\0\0\0\0\0\360\312\4\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\34\0\33\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\224\216\4\0\0\0\0\0\224\216\4\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=315888, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b64000
+27096 mmap(NULL, 2406528, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9ef9d000
+27096 mprotect(0x7f8c9efe6000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8c9f1e6000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x49000) = 0x7f8c9f1e6000
+27096 close(3) = 0
+27096 open("/lib64/libkrb5.so.3", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360J\2\0\0\0\0\0@\0\0\0\0\0\0\0\30m\16\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\34\0\33\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\244L\r\0\0\0\0\0\244L\r\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=947224, ...}) = 0
+27096 mmap(NULL, 3032160, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9ecb8000
+27096 mprotect(0x7f8c9ed8d000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9ef8c000, 69632, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xd4000) = 0x7f8c9ef8c000
+27096 close(3) = 0
+27096 open("/lib64/libcom_err.so.2", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\25\0\0\0\0\0\0@\0\0\0\0\0\0\0\2004\0\0\0\0\0\0\0\0\0\0@\0008\0\10\0@\0\34\0\33\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0004&\0\0\0\0\0\0004&\0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=15232, ...}) = 0
+27096 mmap(NULL, 2109608, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9eab4000
+27096 mprotect(0x7f8c9eab7000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9ecb6000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f8c9ecb6000
+27096 close(3) = 0
+27096 open("/lib64/libk5crypto.so.3", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0pG\0\0\0\0\0\0@\0\0\0\0\0\0\0\340\33\3\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\34\0\33\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\334\357\2\0\0\0\0\0\334\357\2\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=205536, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b63000
+27096 mmap(NULL, 2301720, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9e882000
+27096 mprotect(0x7f8c9e8b1000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8c9eab1000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2f000) = 0x7f8c9eab1000
+27096 close(3) = 0
+27096 open("/lib64/liblzma.so.5", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\00000\0\0\0\0\0\0@\0\0\0\0\0\0\0\350[\2\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\33\0\32\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0DD\2\0\0\0\0\0DD\2\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=156328, ...}) = 0
+27096 mmap(NULL, 2248712, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9e65c000
+27096 mprotect(0x7f8c9e681000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9e880000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x24000) = 0x7f8c9e880000
+27096 mmap(0x7f8c9e881000, 8, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9e881000
+27096 close(3) = 0
+27096 open("/lib64/libgcrypt.so.20", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\211\0\0\0\0\0\0@\0\0\0\0\0\0\0\370i\16\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\34\0\33\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\264\257\r\0\0\0\0\0\264\257\r\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=946424, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b62000
+27096 mmap(NULL, 3031840, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9e377000
+27096 mprotect(0x7f8c9e452000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8c9e652000, 36864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xdb000) = 0x7f8c9e652000
+27096 mmap(0x7f8c9e65b000, 800, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9e65b000
+27096 close(3) = 0
+27096 open("/lib64/libgpg-error.so.0", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@'\0\0\0\0\0\0@\0\0\0\0\0\0\0p*\1\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\34\0\33\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\20\1\0\0\0\0\0\34\20\1\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=78192, ...}) = 0
+27096 mmap(NULL, 2171000, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9e164000
+27096 mprotect(0x7f8c9e176000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9e375000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x11000) = 0x7f8c9e375000
+27096 close(3) = 0
+27096 open("/lib64/libjpeg.so.62", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220<\0\0\0\0\0\0@\0\0\0\0\0\0\0p\227\5\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\33\0\32\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0,h\5\0\0\0\0\0,h\5\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=368176, ...}) = 0
+27096 mmap(NULL, 2457608, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9df0b000
+27096 mprotect(0x7f8c9df62000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8c9e162000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x57000) = 0x7f8c9e162000
+27096 mmap(0x7f8c9e163000, 8, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9e163000
+27096 close(3) = 0
+27096 open("/lib64/libpng16.so.16", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300T\0\0\0\0\0\0@\0\0\0\0\0\0\0x.\3\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\33\0\32\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0,\25\3\0\0\0\0\0,\25\3\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=210232, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b61000
+27096 mmap(NULL, 2301960, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9dcd8000
+27096 mprotect(0x7f8c9dd0a000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9df09000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x31000) = 0x7f8c9df09000
+27096 mmap(0x7f8c9df0a000, 8, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9df0a000
+27096 close(3) = 0
+27096 open("/lib64/libfreetype.so.6", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\354\0\0\0\0\0\0@\0\0\0\0\0\0\0\0\273\n\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\32\0\31\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\254*\n\0\0\0\0\0\254*\n\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=704896, ...}) = 0
+27096 mmap(NULL, 2789384, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9da2e000
+27096 mprotect(0x7f8c9dad1000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8c9dcd1000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xa3000) = 0x7f8c9dcd1000
+27096 mmap(0x7f8c9dcd7000, 8, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9dcd7000
+27096 close(3) = 0
+27096 open("/lib64/libfontconfig.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340m\0\0\0\0\0\0@\0\0\0\0\0\0\0\210K\4\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\33\0\32\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\315\3\0\0\0\0\0\4\315\3\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=283208, ...}) = 0
+27096 mmap(NULL, 2372648, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9d7ea000
+27096 mprotect(0x7f8c9d827000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8c9da27000, 28672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3d000) = 0x7f8c9da27000
+27096 close(3) = 0
+27096 open("/lib64/libXpm.so.4", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\3400\0\0\0\0\0\0@\0\0\0\0\0\0\0\230\26\1\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\33\0\32\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0,\6\1\0\0\0\0\0,\6\1\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=73048, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b60000
+27096 mmap(NULL, 2167088, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9d5d8000
+27096 mprotect(0x7f8c9d5e9000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9d7e8000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x10000) = 0x7f8c9d7e8000
+27096 close(3) = 0
+27096 open("/lib64/libX11.so.6", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340\337\1\0\0\0\0\0@\0\0\0\0\0\0\0h\21\24\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\33\0\32\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\231\23\0\0\0\0\0\34\231\23\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=1316904, ...}) = 0
+27096 mmap(NULL, 3406072, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9d298000
+27096 mprotect(0x7f8c9d3d2000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9d5d1000, 28672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x139000) = 0x7f8c9d5d1000
+27096 close(3) = 0
+27096 open("/lib64/libvpx.so.2", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P:\0\0\0\0\0\0@\0\0\0\0\0\0\0\0H\30\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\34\0\33\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\\\321\27\0\0\0\0\0\\\321\27\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=1593088, ...}) = 0
+27096 mmap(NULL, 3678456, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9cf15000
+27096 mprotect(0x7f8c9d093000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9d292000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17d000) = 0x7f8c9d292000
+27096 mmap(0x7f8c9d295000, 8440, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9d295000
+27096 close(3) = 0
+27096 open("/lib64/libtiff.so.5", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360\202\0\0\0\0\0\0@\0\0\0\0\0\0\0\300;\7\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\34\0\33\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0l\356\6\0\0\0\0\0l\356\6\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=475840, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b5f000
+27096 mmap(NULL, 2567024, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9cca2000
+27096 mprotect(0x7f8c9cd11000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8c9cf11000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6f000) = 0x7f8c9cf11000
+27096 close(3) = 0
+27096 open("/lib64/libunwind.so.8", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\25\0\0\0\0\0\0@\0\0\0\0\0\0\0`\246\0\0\0\0\0\0\0\0\0\0@\0008\0\10\0@\0\27\0\26\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\340\233\0\0\0\0\0\0\340\233\0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=44064, ...}) = 0
+27096 mmap(NULL, 2198984, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9ca89000
+27096 mprotect(0x7f8c9ca93000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9cc92000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x9000) = 0x7f8c9cc92000
+27096 mmap(0x7f8c9cc94000, 56776, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9cc94000
+27096 close(3) = 0
+27096 open("/lib64/libstdc++.so.6", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\237\10\0\0\0\0\0@\0\0\0\0\0\0\0\210\337\27\0\0\0\0\0\0\0\0\0@\0008\0\10\0@\0 \0\37\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\346\26\27\0\0\0\0\0\346\26\27\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=1566600, ...}) = 0
+27096 mmap(NULL, 3675328, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9c707000
+27096 mprotect(0x7f8c9c879000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8c9ca79000, 49152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x172000) = 0x7f8c9ca79000
+27096 mmap(0x7f8c9ca85000, 13504, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9ca85000
+27096 close(3) = 0
+27096 open("/lib64/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360*\0\0\0\0\0\0@\0\0\0\0\0\0\0\20c\1\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\36\0\35\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\264R\1\0\0\0\0\0\264R\1\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=92816, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b5e000
+27096 mmap(NULL, 2188320, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9c4f0000
+27096 mprotect(0x7f8c9c506000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9c705000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15000) = 0x7f8c9c705000
+27096 close(3) = 0
+27096 open("/lib64/libkrb5support.so.0", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\3406\0\0\0\0\0\0@\0\0\0\0\0\0\0`\350\0\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\34\0\33\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\244\317\0\0\0\0\0\0\244\317\0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=61280, ...}) = 0
+27096 mmap(NULL, 2155208, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9c2e1000
+27096 mprotect(0x7f8c9c2ee000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8c9c4ee000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xd000) = 0x7f8c9c4ee000
+27096 close(3) = 0
+27096 open("/lib64/libkeyutils.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\25\0\0\0\0\0\0@\0\0\0\0\0\0\0H4\0\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\33\0\32\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\214(\0\0\0\0\0\0\214(\0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=15112, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b5d000
+27096 mmap(NULL, 2109448, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9c0dd000
+27096 mprotect(0x7f8c9c0e0000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9c2df000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f8c9c2df000
+27096 mmap(0x7f8c9c2e0000, 8, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9c2e0000
+27096 close(3) = 0
+27096 open("/lib64/libbz2.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\27\0\0\0\0\0\0@\0\0\0\0\0\0\0\310\0\1\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\33\0\32\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0,\353\0\0\0\0\0\0,\353\0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=67464, ...}) = 0
+27096 mmap(NULL, 2161672, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9becd000
+27096 mprotect(0x7f8c9bedc000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9c0db000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xe000) = 0x7f8c9c0db000
+27096 close(3) = 0
+27096 open("/lib64/libexpat.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0>\0\0\0\0\0\0@\0\0\0\0\0\0\0\220\234\2\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\32\0\31\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0La\2\0\0\0\0\0La\2\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=172816, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b5c000
+27096 mmap(NULL, 2265096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9bca3000
+27096 mprotect(0x7f8c9bcca000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9bec9000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x26000) = 0x7f8c9bec9000
+27096 mmap(0x7f8c9becc000, 8, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9becc000
+27096 close(3) = 0
+27096 open("/lib64/libxcb.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\233\0\0\0\0\0\0@\0\0\0\0\0\0\0@\26\2\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\33\0\32\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\304\1\2\0\0\0\0\0\304\1\2\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=138496, ...}) = 0
+27096 mmap(NULL, 2232488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9ba81000
+27096 mprotect(0x7f8c9baa2000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9bca1000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x20000) = 0x7f8c9bca1000
+27096 close(3) = 0
+27096 open("/lib64/libjbig.so.2.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\20\0\0\0\0\0\0@\0\0\0\0\0\0\0\230\300\0\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\34\0\33\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\224\210\0\0\0\0\0\0\224\210\0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=51096, ...}) = 0
+27096 mmap(NULL, 2145256, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9b875000
+27096 mprotect(0x7f8c9b87e000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9ba7d000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x8000) = 0x7f8c9ba7d000
+27096 close(3) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b5b000
+27096 open("/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260b\0\0\0\0\0\0@\0\0\0\0\0\0\0X\16\2\0\0\0\0\0\0\0\0\0@\0008\0\10\0@\0\35\0\34\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0t\353\1\0\0\0\0\0t\353\1\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=136600, ...}) = 0
+27096 mmap(NULL, 2237248, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9b652000
+27096 mprotect(0x7f8c9b671000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8c9b871000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1f000) = 0x7f8c9b871000
+27096 mmap(0x7f8c9b873000, 4928, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9b873000
+27096 close(3) = 0
+27096 open("/lib64/libXau.so.6", O_RDONLY|O_CLOEXEC) = 3
+27096 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\16\0\0\0\0\0\0@\0\0\0\0\0\0\0X4\0\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\32\0\31\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\354\37\0\0\0\0\0\0\354\37\0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(3, {st_mode=S_IFREG|0755, st_size=15064, ...}) = 0
+27096 mmap(NULL, 2109472, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c9b44e000
+27096 mprotect(0x7f8c9b450000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8c9b650000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f8c9b650000
+27096 mmap(0x7f8c9b651000, 32, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9b651000
+27096 close(3) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b5a000
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b59000
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b58000
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b57000
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b56000
+27096 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b54000
+27096 arch_prctl(ARCH_SET_FS, 0x7f8ca2b54840) = 0
+27096 mprotect(0x7f8c9fa25000, 16384, PROT_READ) = 0
+27096 mprotect(0x7f8c9b650000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8ca2757000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8ca2308000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8ca19d2000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9b871000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9ba7d000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9bca1000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9bec9000, 12288, PROT_READ) = 0
+27096 mprotect(0x7f8c9c0db000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9c2df000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8ca0364000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9c4ee000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9c705000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9ff32000, 4096, PROT_READ) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b53000
+27096 mprotect(0x7f8c9ca79000, 40960, PROT_READ) = 0
+27096 mprotect(0x7f8c9cc92000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8ca17ce000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9e162000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9cf11000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9d292000, 8192, PROT_READ) = 0
+27096 mprotect(0x7f8c9d5d1000, 12288, PROT_READ) = 0
+27096 mprotect(0x7f8c9d7e8000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9df09000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9dcd1000, 24576, PROT_READ) = 0
+27096 mprotect(0x7f8c9da27000, 8192, PROT_READ) = 0
+27096 mprotect(0x7f8c9e375000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9e652000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9e880000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9eab1000, 8192, PROT_READ) = 0
+27096 mprotect(0x7f8c9ecb6000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9ef8c000, 61440, PROT_READ) = 0
+27096 mprotect(0x7f8c9f1e6000, 8192, PROT_READ) = 0
+27096 mprotect(0x7f8c9f45f000, 8192, PROT_READ) = 0
+27096 mprotect(0x7f8c9f66c000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8c9fc30000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8ca0149000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8ca2510000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8ca0742000, 20480, PROT_READ) = 0
+27096 mprotect(0x7f8ca095b000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8ca0b90000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8ca0dd6000, 24576, PROT_READ) = 0
+27096 mprotect(0x7f8ca15af000, 32768, PROT_READ) = 0
+27096 mprotect(0x7f8ca1251000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8ca1013000, 4096, PROT_READ) = 0
+27096 mprotect(0x7f8ca1df5000, 110592, PROT_READ) = 0
+27096 mprotect(0x7f8ca208e000, 20480, PROT_READ) = 0
+27096 mprotect(0x7f8ca2961000, 4096, PROT_READ) = 0
+27096 mprotect(0x56435eb57000, 8192, PROT_READ) = 0
+27096 mprotect(0x7f8ca2b83000, 4096, PROT_READ) = 0
+27096 munmap(0x7f8ca2b6c000, 87726) = 0
+27096 set_tid_address(0x7f8ca2b54b10) = 27096
+27096 set_robust_list(0x7f8ca2b54b20, 24) = 0
+27096 rt_sigaction(SIGRTMIN, {0x7f8ca2545b50, [], SA_RESTORER|SA_SIGINFO, 0x7f8ca25509f0}, NULL, 8) = 0
+27096 rt_sigaction(SIGRT_1, {0x7f8ca2545be0, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x7f8ca25509f0}, NULL, 8) = 0
+27096 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
+27096 getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
+27096 statfs("/sys/fs/selinux", 0x7ffff0926360) = -1 ENOENT (No such file or directory)
+27096 statfs("/selinux", 0x7ffff0926360) = -1 ENOENT (No such file or directory)
+27096 brk(0) = 0x564360913000
+27096 brk(0x564360934000) = 0x564360934000
+27096 open("/proc/filesystems", O_RDONLY) = 3
+27096 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b81000
+27096 read(3, "nodev\tsysfs\nnodev\trootfs\nnodev\tramfs\nnodev\tbdev\nnodev\tproc\nnodev\tcpuset\nnodev\tcgroup\nnodev\ttmpfs\nnodev\tdevtmpfs\nnodev\tconfigfs\nn"..., 1024) = 312
+27096 read(3, "", 1024) = 0
+27096 close(3) = 0
+27096 munmap(0x7f8ca2b81000, 4096) = 0
+27096 access("/etc/selinux/config", F_OK) = 0
+27096 access("/etc/system-fips", F_OK) = -1 ENOENT (No such file or directory)
+27096 rt_sigaction(SIGPROF, NULL, {SIG_DFL, [], 0}, 8) = 0
+27096 rt_sigaction(SIGPROF, {SIG_IGN, [], SA_RESTORER|SA_RESTART, 0x7f8ca25509f0}, NULL, 8) = 0
+27096 futex(0x7f8ca095c5a0, FUTEX_WAKE_PRIVATE, 2147483647) = 0
+27096 setitimer(ITIMER_PROF, {it_interval={0, 10000}, it_value={0, 10000}}, NULL) = 0
+27096 open("/proc/self/auxv", O_RDONLY) = 3
+27096 read(3, "!\0\0\0\0\0\0\0\0p\235\360\377\177\0\0", 16) = 16
+27096 open("/etc/localtime", O_RDONLY|O_CLOEXEC) = 4
+27096 fstat(4, {st_mode=S_IFREG|0644, st_size=2845, ...}) = 0
+27096 fstat(4, {st_mode=S_IFREG|0644, st_size=2845, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b81000
+27096 read(4, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0\5\0\0\0\0\0\0\0\272\0\0\0\5\0\0\0\24\200\0\0\0\236\246H\240\237\273\25\220\240\206*\240\241\232\367\220\313\211\32\240\322#\364p\322a&\20\326\376t \330\200\255\220\332\376\321\240\333\300\220\20\334\336\263\240\335\251\254\220\336\276\225\240\337\211\216\220\340\236w\240\341ip\220\342~Y\240\343IR\220\344^;\240"..., 4096) = 2845
+27096 lseek(4, -1811, SEEK_CUR) = 1034
+27096 read(4, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0\5\0\0\0\0\0\0\0\273\0\0\0\5\0\0\0\24\370\0\0\0\0\0\0\0\377\377\377\377^\4\32\300\377\377\377\377\236\246H\240\377\377\377\377\237\273\25\220\377\377\377\377\240\206*\240\377\377\377\377\241\232\367\220\377\377\377\377\313\211\32\240\377\377\377\377\322#\364p\377\377\377\377\322a&\20\377\377\377\377\326\376t \377\377\377\377"..., 4096) = 1811
+27096 close(4) = 0
+27096 munmap(0x7f8ca2b81000, 4096) = 0
+27096 write(2, "10:17:35 [pid= 27096] ", 23) = 23
+27096 write(2, "close(): SYMBOL NOT FOUND.\n", 27) = 27
+27096 open("/sys/devices/system/cpu/cpu0/tsc_freq_khz", O_RDONLY) = -1 ENOENT (No such file or directory)
+27096 open("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", O_RDONLY) = -1 ENOENT (No such file or directory)
+27096 open("/proc/cpuinfo", O_RDONLY) = 4
+27096 read(4, "processor\t: 0\nvendor_id\t: GenuineIntel\ncpu family\t: 6\nmodel\t\t: 61\nmodel name\t: Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz\nstepping"..., 1023) = 1023
+27096 read(4, "u MHz\t\t: 2700.000\ncache size\t: 3072 KB\nphysical id\t: 0\nsiblings\t: 2\ncore id\t\t: 1\ncpu cores\t: 2\napicid\t\t: 1\ninitial apicid\t: 1\nfp"..., 1021) = 719
+27096 read(4, "", 1023) = 0
+27096 stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=2845, ...}) = 0
+27096 write(2, "10:17:35 [pid= 27096] ", 23) = 23
+27096 write(2, "close(): SYMBOL NOT FOUND.\n", 27) = 27
+27096 futex(0x7ffff0926444, FUTEX_WAKE, 1) = 0
+27096 futex(0x7ffff0926444, FUTEX_WAKE_PRIVATE, 1) = 0
+27096 access("/etc/system-fips", F_OK) = -1 ENOENT (No such file or directory)
+27096 futex(0x7f8ca19d30c8, FUTEX_WAKE_PRIVATE, 2147483647) = 0
+27096 open("/var/log/nginx/error.log", O_WRONLY|O_CREAT|O_APPEND, 0644) = 5
+27096 open("/etc/pki/tls/openssl.cnf", O_RDONLY) = 6
+27096 fstat(6, {st_mode=S_IFREG|0644, st_size=10923, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b81000
+27096 read(6, "#\n# OpenSSL example configuration file.\n# This is mostly being used for generation of certificate requests.\n#\n\n# This definition"..., 4096) = 4096
+27096 read(6, "ancient versions of Netscape crash on BMPStrings or UTF8Strings.\nstring_mask = utf8only\n\n# req_extensions = v3_req # The extensi"..., 4096) = 4096
+27096 read(6, "gainst PKIX guidelines but some CAs do it and some software\n# requires this to avoid interpreting an end user certificate as a C"..., 4096) = 2731
+27096 read(6, "", 4096) = 0
+27096 close(6) = 0
+27096 munmap(0x7f8ca2b81000, 4096) = 0
+27096 brk(0) = 0x564360934000
+27096 brk(0x564360955000) = 0x564360955000
+27096 uname({sysname="Linux", nodename="localhost.localdomain", ...}) = 0
+27096 open("/sys/devices/system/cpu/online", O_RDONLY|O_CLOEXEC) = 6
+27096 read(6, "0-1\n", 8192) = 4
+27096 close(6) = 0
+27096 getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
+27096 stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=2845, ...}) = 0
+27096 uname({sysname="Linux", nodename="localhost.localdomain", ...}) = 0
+27096 open("/etc/nginx/nginx.conf", O_RDONLY) = 6
+27096 fstat(6, {st_mode=S_IFREG|0644, st_size=2324, ...}) = 0
+27096 pread(6, "# For more information on configuration, see:\n# * Official English Documentation: http://nginx.org/en/docs/\n# * Official Rus"..., 2324, 0) = 2324
+27096 geteuid() = 0
+27096 socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 7
+27096 connect(7, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
+27096 close(7) = 0
+27096 socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 7
+27096 connect(7, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
+27096 close(7) = 0
+27096 open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 7
+27096 fstat(7, {st_mode=S_IFREG|0644, st_size=1739, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b81000
+27096 read(7, "#\n# /etc/nsswitch.conf\n#\n# An example Name Service Switch config file. This file should be\n# sorted with the most-used services "..., 4096) = 1739
+27096 read(7, "", 4096) = 0
+27096 close(7) = 0
+27096 munmap(0x7f8ca2b81000, 4096) = 0
+27096 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 7
+27096 fstat(7, {st_mode=S_IFREG|0644, st_size=87726, ...}) = 0
+27096 mmap(NULL, 87726, PROT_READ, MAP_PRIVATE, 7, 0) = 0x7f8ca2b6c000
+27096 close(7) = 0
+27096 open("/lib64/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 7
+27096 read(7, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320!\0\0\0\0\0\0@\0\0\0\0\0\0\0\30\326\0\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0 \0\37\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\240\0\0\0\0\0\0\4\240\0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(7, {st_mode=S_IFREG|0755, st_size=56856, ...}) = 0
+27096 mmap(NULL, 2168600, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 7, 0) = 0x7f8c9b23c000
+27096 mprotect(0x7f8c9b247000, 2093056, PROT_NONE) = 0
+27096 mmap(0x7f8c9b446000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 7, 0xa000) = 0x7f8c9b446000
+27096 mmap(0x7f8c9b448000, 22296, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8c9b448000
+27096 close(7) = 0
+27096 mprotect(0x7f8c9b446000, 4096, PROT_READ) = 0
+27096 munmap(0x7f8ca2b6c000, 87726) = 0
+27096 open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 7
+27096 fstat(7, {st_mode=S_IFREG|0644, st_size=2603, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b81000
+27096 read(7, "root:x:0:0:root:/root:/bin/bash\nbin:x:1:1:bin:/bin:/sbin/nologin\ndaemon:x:2:2:daemon:/sbin:/sbin/nologin\nadm:x:3:4:adm:/var/adm:"..., 4096) = 2603
+27096 close(7) = 0
+27096 munmap(0x7f8ca2b81000, 4096) = 0
+27096 socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 7
+27096 connect(7, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
+27096 close(7) = 0
+27096 socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 7
+27096 connect(7, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
+27096 close(7) = 0
+27096 open("/etc/group", O_RDONLY|O_CLOEXEC) = 7
+27096 fstat(7, {st_mode=S_IFREG|0644, st_size=1019, ...}) = 0
+27096 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8ca2b81000
+27096 read(7, "root:x:0:\nbin:x:1:\ndaemon:x:2:\nsys:x:3:\nadm:x:4:\ntty:x:5:\ndisk:x:6:\nlp:x:7:\nmem:x:8:\nkmem:x:9:\nwheel:x:10:joseph\ncdrom:x:11:\nmai"..., 4096) = 1019
+27096 close(7) = 0
+27096 munmap(0x7f8ca2b81000, 4096) = 0
+27096 epoll_create(100) = 7
+27096 close(7) = 0
+27096 brk(0) = 0x564360955000
+27096 brk(0x564360976000) = 0x564360976000
+27096 futex(0x7f8ca15b9688, FUTEX_WAKE_PRIVATE, 2147483647) = 0
+27096 open("/etc/nginx/mime.types", O_RDONLY) = 7
+27096 fstat(7, {st_mode=S_IFREG|0644, st_size=30634, ...}) = 0
+27096 pread(7, "types {\napplication/A2L\t\t\t\t\ta2l;\napplication/AML\t\t\t\t\taml;\napplication/andrew-inset\t\t\tez;\napplication/ATF\t\t\t\t\tatf;\napplication/AT"..., 4096, 0) = 4096
+27096 pread(7, "rql-results+xml\t\t\tsrx;\napplication/sql\t\t\t\t\tsql;\napplication/srgs\t\t\t\tgram;\napplication/srgs+xml\t\t\t\tgrxml;\napplication/sru+xml\t\t\t\t"..., 4081, 4096) = 4081
+27096 pread(7, ".ecowin.chart\t\t\tmag;\napplication/vnd.enliven\t\t\t\tnml;\napplication/vnd.epson.esf\t\t\tesf;\napplication/vnd.epson.msf\t\t\tmsf;\napplicati"..., 4081, 8177) = 4081
+27096 pread(7, "\tlbe;\napplication/vnd.lotus-1-2-3\t\t\t123 wk4 wk3 wk1;\napplication/vnd.lotus-approach\t\t\tapr vew;\napplication/vnd.lotus-freelance\t\t"..., 4041, 12258) = 4041
+27096 brk(0) = 0x564360976000
+27096 brk(0x564360999000) = 0x564360999000
+27096 pread(7, "n/vnd.oasis.opendocument.text-template\t\tott;\napplication/vnd.oasis.opendocument.text-web\t\t\toth;\napplication/vnd.olpc-sugar\t\t\txo;"..., 4086, 16299) = 4086
+27096 pread(7, "dy-inter model-inter;\napplication/vnd.vectorworks\t\t\tvwx;\napplication/vnd.vidsoft.vidconference\t\tvsc;\napplication/vnd.visio\t\t\t\tvs"..., 4093, 20385) = 4093
+27096 pread(7, "vnd.zbrush.pcx\t\t\t\tpcx;\nmessage/global\t\t\t\t\tu8msg;\nmessage/global-delivery-status\t\t\tu8dsn;\nmessage/global-disposition-notification"..., 4090, 24478) = 4090
+27096 pread(7, "ication/x-futuresplash\t\t\tspl;\napplication/x-gtar\t\t\t\tgtar;\napplication/x-hdf\t\t\t\thdf;\napplication/x-java-archive\t\t\tjar;\napplicatio"..., 2066, 28568) = 2066
+27096 close(7) = 0
+27096 stat("/etc/nginx/conf.d", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
+27096 open("/etc/nginx/conf.d", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 7
+27096 fstat(7, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
+27096 getdents(7, /* 2 entries */, 32768) = 48
+27096 getdents(7, /* 0 entries */, 32768) = 0
+27096 close(7) = 0
+27096 stat("/etc/nginx/default.d", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
+27096 open("/etc/nginx/default.d", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 7
+27096 fstat(7, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
+27096 getdents(7, /* 2 entries */, 32768) = 48
+27096 getdents(7, /* 0 entries */, 32768) = 0
+27096 close(7) = 0
+27096 brk(0) = 0x564360999000
+27096 brk(0x5643609c7000) = 0x5643609c7000
+27096 rt_sigaction(SIGFPE, {SIG_IGN, [FPE], SA_RESTORER|SA_RESTART, 0x7f8c9f6a2b20}, {SIG_DFL, [], 0}, 8) = 0
+27096 getuid() = 0
+27096 geteuid() = 0
+27096 getgid() = 0
+27096 getegid() = 0
+27096 open("/dev/urandom", O_RDONLY) = 7
+27096 read(7, "\233\260\37\177", 4) = 4
+27096 close(7) = 0
+27096 readlink("/proc/self/exe", "/usr/sbin/nginx", 4095) = 15
+27096 getuid() = 0
+27096 geteuid() = 0
+27096 getgid() = 0
+27096 getegid() = 0
+27096 ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
+27096 lseek(0, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
+27096 ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0
+27096 lseek(1, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
+27096 ioctl(2, TCGETS, {B38400 opost isig icanon echo ...}) = 0
+27096 lseek(2, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
+27096 open("/dev/null", O_RDONLY) = 7
+27096 ioctl(7, TCGETS, 0x7ffff09256c0) = -1 ENOTTY (Inappropriate ioctl for device)
+27096 lseek(7, 0, SEEK_CUR) = 0
+27096 fcntl(7, F_SETFD, FD_CLOEXEC) = 0
+27096 fstat(7, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
+27096 getuid() = 0
+27096 geteuid() = 0
+27096 getgid() = 0
+27096 getegid() = 0
+27096 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
+27096 brk(0) = 0x5643609c7000
+27096 brk(0x5643609e8000) = 0x5643609e8000
+27096 stat("/usr/local/share/perl5/sitecustomize.pl", 0x5643609652f8) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/lib64/perl5/nginx.pmc", 0x7ffff0925250) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/lib64/perl5/nginx.pm", 0x7ffff0925180) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/share/perl5/nginx.pmc", 0x7ffff0925250) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/share/perl5/nginx.pm", 0x7ffff0925180) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/nginx.pmc", 0x7ffff0925250) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/nginx.pm", {st_mode=S_IFREG|0444, st_size=3302, ...}) = 0
+27096 open("/usr/lib64/perl5/vendor_perl/nginx.pm", O_RDONLY) = 8
+27096 ioctl(8, TCGETS, 0x7ffff0924f20) = -1 ENOTTY (Inappropriate ioctl for device)
+27096 lseek(8, 0, SEEK_CUR) = 0
+27096 read(8, "package nginx;\n\nuse 5.006001;\nuse strict;\nuse warnings;\n\nrequire Exporter;\n\nour @ISA = qw(Exporter);\n\nour @EXPORT = qw(\n OK\n "..., 8192) = 3302
+27096 stat("/usr/local/lib64/perl5/strict.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/lib64/perl5/strict.pm", 0x7ffff0924b10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/share/perl5/strict.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/share/perl5/strict.pm", 0x7ffff0924b10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/strict.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/strict.pm", 0x7ffff0924b10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/vendor_perl/strict.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/vendor_perl/strict.pm", 0x7ffff0924b10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/strict.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/strict.pm", 0x7ffff0924b10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/strict.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/strict.pm", {st_mode=S_IFREG|0644, st_size=4433, ...}) = 0
+27096 open("/usr/share/perl5/strict.pm", O_RDONLY) = 9
+27096 ioctl(9, TCGETS, 0x7ffff09248b0) = -1 ENOTTY (Inappropriate ioctl for device)
+27096 lseek(9, 0, SEEK_CUR) = 0
+27096 read(9, "package strict;\n\n$strict::VERSION = \"1.09\";\n\n# Verify that we're called correctly so that strictures will work.\nunless ( __FILE_"..., 8192) = 4433
+27096 --- SIGPROF {si_signo=SIGPROF, si_code=SI_KERNEL, si_value={int=875903538, ptr=0x4354552034353a32}} ---
+27096 lseek(9, 1513, SEEK_SET) = 1513
+27096 lseek(9, 0, SEEK_CUR) = 1513
+27096 close(9) = 0
+27096 stat("/usr/local/lib64/perl5/warnings.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/lib64/perl5/warnings.pm", 0x7ffff0924b10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/share/perl5/warnings.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/share/perl5/warnings.pm", 0x7ffff0924b10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/warnings.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/warnings.pm", 0x7ffff0924b10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/vendor_perl/warnings.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/vendor_perl/warnings.pm", 0x7ffff0924b10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/warnings.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/warnings.pm", 0x7ffff0924b10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/warnings.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/warnings.pm", {st_mode=S_IFREG|0644, st_size=44835, ...}) = 0
+27096 open("/usr/share/perl5/warnings.pm", O_RDONLY) = 9
+27096 ioctl(9, TCGETS, 0x7ffff09248b0) = -1 ENOTTY (Inappropriate ioctl for device)
+27096 lseek(9, 0, SEEK_CUR) = 0
+27096 read(9, "# -*- buffer-read-only: t -*-\n# !!!!!!! DO NOT EDIT THIS FILE !!!!!!!\n# This file is built by regen/warnings.pl.\n# Any chang"..., 8192) = 8192
+27096 brk(0) = 0x5643609e8000
+27096 brk(0x564360a0a000) = 0x564360a0a000
+27096 read(9, "\\x00\\x00\\x00\\x00\\x00\", # [21..25]\n 'signal'\t\t\t\t=> \"\\x00\\x00\\x00\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\", # ["..., 8192) = 8192
+27096 read(9, " 'void'\t\t\t\t=> \"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x00\\x00\", # [45]\n);\n\n# These are used by various thi"..., 8192) = 8192
+27096 brk(0) = 0x564360a0a000
+27096 brk(0x564360a2b000) = 0x564360a2b000
+27096 lseek(9, 21927, SEEK_SET) = 21927
+27096 lseek(9, 0, SEEK_CUR) = 21927
+27096 close(9) = 0
+27096 stat("/usr/local/lib64/perl5/constant.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/lib64/perl5/constant.pm", 0x7ffff0924b10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/share/perl5/constant.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/share/perl5/constant.pm", 0x7ffff0924b10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/constant.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/constant.pm", 0x7ffff0924b10) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/vendor_perl/constant.pmc", 0x7ffff0924be0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/vendor_perl/constant.pm", {st_mode=S_IFREG|0644, st_size=14724, ...}) = 0
+27096 open("/usr/share/perl5/vendor_perl/constant.pm", O_RDONLY) = 9
+27096 ioctl(9, TCGETS, 0x7ffff09248b0) = -1 ENOTTY (Inappropriate ioctl for device)
+27096 lseek(9, 0, SEEK_CUR) = 0
+27096 read(9, "package constant;\nuse 5.008;\nuse strict;\nuse warnings::register;\n\nour $VERSION = '1.33';\nour %declared;\n\n#======================"..., 8192) = 8192
+27096 stat("/usr/local/lib64/perl5/warnings/register.pmc", 0x7ffff0924570) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/lib64/perl5/warnings/register.pm", 0x7ffff09244a0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/share/perl5/warnings/register.pmc", 0x7ffff0924570) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/share/perl5/warnings/register.pm", 0x7ffff09244a0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/warnings/register.pmc", 0x7ffff0924570) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/warnings/register.pm", 0x7ffff09244a0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/vendor_perl/warnings/register.pmc", 0x7ffff0924570) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/vendor_perl/warnings/register.pm", 0x7ffff09244a0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/warnings/register.pmc", 0x7ffff0924570) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/warnings/register.pm", 0x7ffff09244a0) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/warnings/register.pmc", 0x7ffff0924570) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/warnings/register.pm", {st_mode=S_IFREG|0644, st_size=759, ...}) = 0
+27096 open("/usr/share/perl5/warnings/register.pm", O_RDONLY) = 10
+27096 ioctl(10, TCGETS, 0x7ffff0924240) = -1 ENOTTY (Inappropriate ioctl for device)
+27096 lseek(10, 0, SEEK_CUR) = 0
+27096 read(10, "package warnings::register;\n\nour $VERSION = '1.04';\nrequire warnings;\n\n# left here as cruft in case other users were using this "..., 8192) = 759
+27096 lseek(10, 487, SEEK_SET) = 487
+27096 lseek(10, 0, SEEK_CUR) = 487
+27096 close(10) = 0
+27096 brk(0) = 0x564360a2b000
+27096 brk(0x564360a4c000) = 0x564360a4c000
+27096 brk(0) = 0x564360a4c000
+27096 brk(0) = 0x564360a4c000
+27096 brk(0x564360a49000) = 0x564360a49000
+27096 brk(0) = 0x564360a49000
+27096 brk(0) = 0x564360a49000
+27096 brk(0x564360a6c000) = 0x564360a6c000
+27096 brk(0) = 0x564360a6c000
+27096 brk(0x564360a8e000) = 0x564360a8e000
+27096 brk(0) = 0x564360a8e000
+27096 brk(0x564360ab0000) = 0x564360ab0000
+27096 brk(0) = 0x564360ab0000
+27096 brk(0x564360ad2000) = 0x564360ad2000
+27096 brk(0) = 0x564360ad2000
+27096 brk(0x564360af4000) = 0x564360af4000
+27096 brk(0) = 0x564360af4000
+27096 brk(0x564360b18000) = 0x564360b18000
+27096 lseek(9, 5736, SEEK_SET) = 5736
+27096 lseek(9, 0, SEEK_CUR) = 5736
+27096 close(9) = 0
+27096 lseek(8, 2923, SEEK_SET) = 2923
+27096 lseek(8, 0, SEEK_CUR) = 2923
+27096 close(8) = 0
+27096 stat("/usr/local/lib64/perl5/Exporter.pmc", 0x7ffff0925250) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/lib64/perl5/Exporter.pm", 0x7ffff0925180) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/share/perl5/Exporter.pmc", 0x7ffff0925250) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/share/perl5/Exporter.pm", 0x7ffff0925180) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/Exporter.pmc", 0x7ffff0925250) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/Exporter.pm", 0x7ffff0925180) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/vendor_perl/Exporter.pmc", 0x7ffff0925250) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/vendor_perl/Exporter.pm", {st_mode=S_IFREG|0644, st_size=18746, ...}) = 0
+27096 open("/usr/share/perl5/vendor_perl/Exporter.pm", O_RDONLY) = 8
+27096 ioctl(8, TCGETS, 0x7ffff0924f20) = -1 ENOTTY (Inappropriate ioctl for device)
+27096 lseek(8, 0, SEEK_CUR) = 0
+27096 read(8, "package Exporter;\n\nrequire 5.006;\n\n# Be lean.\n#use strict;\n#no strict 'refs';\n\nour $Debug = 0;\nour $ExportLevel = 0;\nour $Verbos"..., 8192) = 8192
+27096 lseek(8, 2366, SEEK_SET) = 2366
+27096 lseek(8, 0, SEEK_CUR) = 2366
+27096 close(8) = 0
+27096 getuid() = 0
+27096 geteuid() = 0
+27096 getgid() = 0
+27096 getegid() = 0
+27096 stat("/usr/local/lib64/perl5/XSLoader.pmc", 0x7ffff0925250) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/lib64/perl5/XSLoader.pm", 0x7ffff0925180) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/share/perl5/XSLoader.pmc", 0x7ffff0925250) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/local/share/perl5/XSLoader.pm", 0x7ffff0925180) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/XSLoader.pmc", 0x7ffff0925250) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/XSLoader.pm", 0x7ffff0925180) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/vendor_perl/XSLoader.pmc", 0x7ffff0925250) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/vendor_perl/XSLoader.pm", 0x7ffff0925180) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/XSLoader.pmc", 0x7ffff0925250) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/XSLoader.pm", 0x7ffff0925180) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/XSLoader.pmc", 0x7ffff0925250) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/share/perl5/XSLoader.pm", {st_mode=S_IFREG|0644, st_size=10305, ...}) = 0
+27096 open("/usr/share/perl5/XSLoader.pm", O_RDONLY) = 8
+27096 ioctl(8, TCGETS, 0x7ffff0924f20) = -1 ENOTTY (Inappropriate ioctl for device)
+27096 lseek(8, 0, SEEK_CUR) = 0
+27096 read(8, "# Generated from XSLoader.pm.PL (resolved %Config::Config value)\n\npackage XSLoader;\n\n$VERSION = \"0.20\";\n\n#use strict;\n\npackage D"..., 8192) = 8192
+27096 lseek(8, 2957, SEEK_SET) = 2957
+27096 lseek(8, 0, SEEK_CUR) = 2957
+27096 close(8) = 0
+27096 stat("/usr/lib64/perl5/vendor_perl/auto/nginx/nginx.bs", 0x5643609652f8) = -1 ENOENT (No such file or directory)
+27096 stat("/usr/lib64/perl5/vendor_perl/auto/nginx/nginx.so", {st_mode=S_IFREG|0755, st_size=28056, ...}) = 0
+27096 open("/usr/lib64/perl5/vendor_perl/auto/nginx/nginx.so", O_RDONLY|O_CLOEXEC) = 8
+27096 read(8, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\25\0\0\0\0\0\0@\0\0\0\0\0\0\0\330f\0\0\0\0\0\0\0\0\0\0@\0008\0\7\0@\0\33\0\32\0\1\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\324N\0\0\0\0\0\0\324N\0\0\0\0\0\0\0\0 \0\0\0\0\0\1\0\0\0\6\0\0\0"..., 832) = 832
+27096 fstat(8, {st_mode=S_IFREG|0755, st_size=28056, ...}) = 0
+27096 mmap(NULL, 2122104, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 8, 0) = 0x7f8c9b035000
+27096 mprotect(0x7f8c9b03a000, 2097152, PROT_NONE) = 0
+27096 mmap(0x7f8c9b23a000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 8, 0x5000) = 0x7f8c9b23a000
+27096 close(8) = 0
+27096 mprotect(0x7f8c9b23a000, 4096, PROT_READ) = 0
+27096 close(7) = 0
+27096 close(6) = 0
+27096 mkdir("/var/lib/nginx/tmp/client_body", 0700) = -1 EEXIST (File exists)
+27096 stat("/var/lib/nginx/tmp/client_body", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0
+27096 mkdir("/var/lib/nginx/tmp/proxy", 0700) = -1 EEXIST (File exists)
+27096 stat("/var/lib/nginx/tmp/proxy", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0
+27096 mkdir("/var/lib/nginx/tmp/fastcgi", 0700) = -1 EEXIST (File exists)
+27096 stat("/var/lib/nginx/tmp/fastcgi", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0
+27096 mkdir("/var/lib/nginx/tmp/uwsgi", 0700) = -1 EEXIST (File exists)
+27096 stat("/var/lib/nginx/tmp/uwsgi", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0
+27096 mkdir("/var/lib/nginx/tmp/scgi", 0700) = -1 EEXIST (File exists)
+27096 stat("/var/lib/nginx/tmp/scgi", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0
+27096 open("/var/log/nginx/error.log", O_WRONLY|O_CREAT|O_APPEND, 0644) = 6
+27096 fcntl(6, F_SETFD, FD_CLOEXEC) = 0
+27096 open("/var/log/nginx/access.log", O_WRONLY|O_CREAT|O_APPEND, 0644) = 7
+27096 fcntl(7, F_SETFD, FD_CLOEXEC) = 0
+27096 stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=2845, ...}) = 0
+27096 write(2, "10:17:35 [pid= 27096] ", 23) = 23
+27096 write(2, "socket():\n", 10) = 10
+27096 stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=2845, ...}) = 0
+27096 write(2, "10:17:35 [pid= 27096] ", 23) = 23
+27096 write(2, "BAD service connection. exiting.\n", 33) = 33
+27096 stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=2845, ...}) = 0
+27096 write(2, "10:17:35 [pid= 27096] ", 23) = 23
+27096 write(2, "socket()=-1\n", 12) = 12
+27096 --- SIGPROF {si_signo=SIGPROF, si_code=SI_KERNEL, si_value={int=875903538, ptr=0x4354552034353a32}} ---
+27096 stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=2845, ...}) = 0
+27096 write(2, "10:17:35 [pid= 27096] ", 23) = 23
+27096 write(2, "closing connections to service...\n", 34) = 34
+27096 exit_group(-1) = ?
+27096 +++ exited with 255 +++
diff --git a/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/monitor_dockerfile b/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/monitor_dockerfile
new file mode 100644
index 00000000..e8f803cd
--- /dev/null
+++ b/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/monitor_dockerfile
@@ -0,0 +1,23 @@
+# ZT Network Containers Test Monitor
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+EXPOSE 9993/udp
+
+# Install LWIP library used by service
+ADD liblwip.so /var/lib/zerotier-one/liblwip.so
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD monitor_identity.public /var/lib/zerotier-one/identity.public
+ADD monitor_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Start ZeroTier-One
+ADD monitor_entrypoint.sh /monitor_entrypoint.sh
+RUN chmod -v +x /monitor_entrypoint.sh
+CMD ["./monitor_entrypoint.sh"]
diff --git a/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/monitor_entrypoint.sh b/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/monitor_entrypoint.sh
new file mode 100644
index 00000000..52470efd
--- /dev/null
+++ b/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/monitor_entrypoint.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+netcon_wait_time=35 # wait for test container to come online
+app_timeout_time=25 # app-specific timeout
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+fail=FAIL. # appended to result file in event of failure
+ok=OK. # appended to result file in event of success
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+bigfile_name=bigfile # large, random test transfer file
+rx_md5sumfile="$file_path"rx_"$bigfile_name"_md5sum"$tmp_ext"
+tx_md5sumfile="$file_path"tx_"$bigfile_name"_md5sum"$tmp_ext"
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test Monitor'
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+virtip4=""
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+done
+echo '*** Starting Test...'
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Sleeping for (' "$netcon_wait_time" 's ) while we wait for the Network Container to come online...'
+sleep "$netcon_wait_time"s
+ncvirtip=$(<$address_file)
+
+
+# --- Test section ---
+echo '*** Curling from intercepted server at' $ncvirtip
+rm -rf "$file_path"*."$file_base"
+touch "$bigfile_name"
+
+# Perform test
+# curl --connect-timeout "$app_timeout_time" -v -o "$file_path$file_base" http://"$ncvirtip"/index.html
+# Large transfer test
+curl --connect-timeout "$app_timeout_time" -v -o "$bigfile_name" http://"$ncvirtip"/"$bigfile_name"
+
+# Check md5
+md5sum < "$bigfile_name" >> "$rx_md5sumfile"
+rx_md5sum=$(<$rx_md5sumfile)
+tx_md5sum=$(<$tx_md5sumfile)
+
+echo '*** Comparing md5: ' "$rx_md5sum" ' and ' "$tx_md5sum"
+
+if [ "$rx_md5sum" != "$tx_md5sum" ];
+then
+ echo 'MD5 FAIL'
+ touch "$file_path$fail$test_name.txt"
+ printf 'Test: md5 sum did not match!\n' >> "$file_path$fail$test_name.txt"
+else
+ echo 'MD5 OK'
+ touch "$file_path$ok$test_name.txt"
+ printf 'Test: md5 sum ok!\n' >> "$file_path$ok$test_name.txt"
+ cat "$rx_md5sumfile" >> "$file_path$ok$test_name.txt"
+ cat "$tx_md5sumfile" >> "$file_path$ok$test_name.txt"
+fi
+
+
+
+
+
+
+
+
+
+
+
diff --git a/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/netcon_dockerfile b/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/netcon_dockerfile
new file mode 100644
index 00000000..1888071e
--- /dev/null
+++ b/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/netcon_dockerfile
@@ -0,0 +1,39 @@
+# ZT Network Containers Test
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+# Install apps
+RUN yum -y update
+RUN yum -y install nginx-1:1.8.0-14.fc23.x86_64
+RUN yum clean all
+
+EXPOSE 9993/udp 80/udp
+
+# Install LWIP library used by service
+ADD liblwip.so /var/lib/zerotier-one/liblwip.so
+
+# Install syscall intercept library
+ADD zerotier-intercept /
+ADD libzerotierintercept.so.1.0 /
+RUN cp libzerotierintercept.so.1.0 lib/libzerotierintercept.so.1.0
+RUN cp libzerotierintercept.so.1.0 /lib/libzerotierintercept.so.1.0
+RUN ln -sf /lib/libzerotierintercept.so.1.0 /lib/libzerotierintercept
+RUN /usr/bin/install -c zerotier-intercept /usr/bin
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD netcon_identity.public /var/lib/zerotier-one/identity.public
+ADD netcon_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Install test scripts
+ADD netcon_entrypoint.sh /netcon_entrypoint.sh
+RUN chmod -v +x /netcon_entrypoint.sh
+
+# Start ZeroTier-One
+CMD ["./netcon_entrypoint.sh"]
diff --git a/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/netcon_entrypoint.sh b/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/netcon_entrypoint.sh
new file mode 100644
index 00000000..de7a00d9
--- /dev/null
+++ b/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/netcon_entrypoint.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+bigfile_name=bigfile
+bigfile_size=10M # size of file we want to use for the test
+tx_md5sumfile="$file_path"tx_"$bigfile_name"_md5sum"$tmp_ext"
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test: ' "$test_name"
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+virtip4=""
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+ dev=`/zerotier-cli listnetworks | grep -F "" | cut -d ' ' -f 8 | cut -d "_" -f 2 | sed "s/^<dev>//" | tr '\n' '\0'`
+done
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Writing address to ' "$address_file"
+echo $virtip4 > "$address_file"
+
+export ZT_NC_NWID=$dev
+
+# --- Test section ---
+nginx_html_path=/usr/share/nginx/html/
+# Generate large random file for transfer test, share md5sum for monitor container to check
+echo '*** Generating ' "$bigfile_size" ' file'
+dd if=/dev/urandom of="$nginx_html_path$bigfile_name" bs="$bigfile_size" count=1
+#md5sum /var/www/html/"$bigfile_name" >> "$tx_md5sumfile"
+md5sum < "$nginx_html_path$bigfile_name" >> "$tx_md5sumfile"
+echo '*** Wrote MD5 sum to ' "$tx_md5sumfile"
+
+echo '*** Starting application...'
+sleep 0.5
+zerotier-intercept nginx -p /var/www/html
diff --git a/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/nginx-1.8.0-14.fc23.x86_64.name b/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/nginx-1.8.0-14.fc23.x86_64.name
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/netcon/docker-test/nginx/nginx-1.8.0-14.fc23.x86_64/nginx-1.8.0-14.fc23.x86_64.name
diff --git a/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/httpserver.js b/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/httpserver.js
new file mode 100644
index 00000000..b2401c50
--- /dev/null
+++ b/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/httpserver.js
@@ -0,0 +1,7 @@
+var http = require('http');
+var server = http.createServer(function (request, response) {
+ response.writeHead(200, {"Content-Type": "text/plain"});
+ response.end("welcome to the machine!\n");
+});
+server.listen(8080);
+console.log("Server running!");
diff --git a/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/monitor_dockerfile b/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/monitor_dockerfile
new file mode 100644
index 00000000..e8f803cd
--- /dev/null
+++ b/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/monitor_dockerfile
@@ -0,0 +1,23 @@
+# ZT Network Containers Test Monitor
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+EXPOSE 9993/udp
+
+# Install LWIP library used by service
+ADD liblwip.so /var/lib/zerotier-one/liblwip.so
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD monitor_identity.public /var/lib/zerotier-one/identity.public
+ADD monitor_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Start ZeroTier-One
+ADD monitor_entrypoint.sh /monitor_entrypoint.sh
+RUN chmod -v +x /monitor_entrypoint.sh
+CMD ["./monitor_entrypoint.sh"]
diff --git a/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/monitor_entrypoint.sh b/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/monitor_entrypoint.sh
new file mode 100644
index 00000000..1701a467
--- /dev/null
+++ b/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/monitor_entrypoint.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+netcon_wait_time=25 # wait for test container to come online
+app_timeout_time=15 # app-specific timeout
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+fail=FAIL. # appended to result file in event of failure
+ok=OK. # appended to result file in event of success
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test Monitor'
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+virtip4=""
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+done
+echo '*** Starting Test...'
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Sleeping for (' "$netcon_wait_time" 's ) while we wait for the Network Container to come online...'
+sleep "$netcon_wait_time"s
+ncvirtip=$(<$address_file)
+
+
+# --- Test section ---
+echo '*** Curling from intercepted server at' $ncvirtip
+response_string=$(curl --connect-timeout "$app_timeout_time" -v http://"$ncvirtip":8080/)
+
+if [[ $response_string == *"welcome to the machine!"* ]]
+then
+ echo 'NODEJS RESPONSE OK'
+ touch "$file_path$ok$test_name.txt"
+ printf 'Test: nodejs-server responded!\n' >> "$file_path$ok$test_name.txt"
+else
+ echo 'NODEJS RESPONSE FAIL'
+ touch "$file_path$fail$test_name.txt"
+ printf 'Test: nodejs server did NOT respond!\n' >> "$file_path$fail$test_name.txt"
+fi
+
+
+
+
+
+
diff --git a/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/netcon_dockerfile b/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/netcon_dockerfile
new file mode 100644
index 00000000..ea2abd68
--- /dev/null
+++ b/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/netcon_dockerfile
@@ -0,0 +1,42 @@
+# ZT Network Containers Test
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+# Install apps
+RUN yum -y update
+RUN yum -y install nodejs
+RUN yum clean all
+
+EXPOSE 9993/udp 8080/udp
+
+# Install LWIP library used by service
+ADD liblwip.so /var/lib/zerotier-one/liblwip.so
+
+#
+ADD httpserver.js /
+
+# Install syscall intercept library
+ADD zerotier-intercept /
+ADD libzerotierintercept.so.1.0 /
+RUN cp libzerotierintercept.so.1.0 lib/libzerotierintercept.so.1.0
+RUN cp libzerotierintercept.so.1.0 /lib/libzerotierintercept.so.1.0
+RUN ln -sf /lib/libzerotierintercept.so.1.0 /lib/libzerotierintercept
+RUN /usr/bin/install -c zerotier-intercept /usr/bin
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD netcon_identity.public /var/lib/zerotier-one/identity.public
+ADD netcon_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Install test scripts
+ADD netcon_entrypoint.sh /netcon_entrypoint.sh
+RUN chmod -v +x /netcon_entrypoint.sh
+
+# Start ZeroTier-One
+CMD ["./netcon_entrypoint.sh"]
diff --git a/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/netcon_entrypoint.sh b/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/netcon_entrypoint.sh
new file mode 100644
index 00000000..a6e1b6c1
--- /dev/null
+++ b/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/netcon_entrypoint.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test: ' "$test_name"
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+virtip4=""
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+ dev=`/zerotier-cli listnetworks | grep -F "" | cut -d ' ' -f 8 | cut -d "_" -f 2 | sed "s/^<dev>//" | tr '\n' '\0'`
+done
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Writing address to ' "$address_file"
+echo $virtip4 > "$address_file"
+
+export ZT_NC_NWID=$dev
+
+# --- Test section ---
+echo '*** Starting application...'
+sleep 0.5
+zerotier-intercept node httpserver.js \ No newline at end of file
diff --git a/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/nodejs-0.10.36-4.fc23.name b/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/nodejs-0.10.36-4.fc23.name
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/netcon/docker-test/nodejs/nodejs-0.10.36-4.fc23/nodejs-0.10.36-4.fc23.name
diff --git a/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/hello.lua b/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/hello.lua
new file mode 100644
index 00000000..59a2dea6
--- /dev/null
+++ b/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/hello.lua
@@ -0,0 +1,3 @@
+local msg = "welcome to the machine!"
+redis.call("SET", "msg", msg)
+return redis.call("GET", "msg")
diff --git a/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/monitor_dockerfile b/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/monitor_dockerfile
new file mode 100644
index 00000000..cbba716b
--- /dev/null
+++ b/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/monitor_dockerfile
@@ -0,0 +1,27 @@
+# ZT Network Containers Test Monitor
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+RUN yum -y install redis-3.0.4-1.fc23.x86_64
+
+EXPOSE 9993/udp
+
+# Install LWIP library used by service
+ADD liblwip.so /var/lib/zerotier-one/liblwip.so
+
+ADD hello.lua /
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD monitor_identity.public /var/lib/zerotier-one/identity.public
+ADD monitor_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Start ZeroTier-One
+ADD monitor_entrypoint.sh /monitor_entrypoint.sh
+RUN chmod -v +x /monitor_entrypoint.sh
+CMD ["./monitor_entrypoint.sh"]
diff --git a/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/monitor_entrypoint.sh b/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/monitor_entrypoint.sh
new file mode 100644
index 00000000..087f50bb
--- /dev/null
+++ b/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/monitor_entrypoint.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+netcon_wait_time=25 # wait for test container to come online
+app_timeout_time=15 # app-specific timeout
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+fail=FAIL. # appended to result file in event of failure
+ok=OK. # appended to result file in event of success
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test Monitor'
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+virtip4=""
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+done
+echo '*** Starting Test...'
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Sleeping for (' "$netcon_wait_time" 's ) while we wait for the Network Container to come online...'
+sleep "$netcon_wait_time"s
+ncvirtip=$(<$address_file)
+
+
+# --- Test section ---
+echo '*** Running lua script against redis host at' $ncvirtip
+redis-cli -h $ncvirtip EVAL "$(cat hello.lua)" 0 > redis_response.txt
+response_string=$(<redis_response.txt)
+
+if [[ $response_string == *"welcome to the machine!"* ]]
+then
+ echo 'REDIS RESPONSE OK'
+ touch "$file_path$ok$test_name.txt"
+ printf 'Test: redis-server responded!\n' >> "$file_path$ok$test_name.txt"
+else
+ echo 'REDIS RESPONSE FAIL'
+ touch "$file_path$fail$test_name.txt"
+ printf 'Test: redis server did NOT respond!\n' >> "$file_path$fail$test_name.txt"
+fi
+
+
+
+
diff --git a/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/netcon_dockerfile b/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/netcon_dockerfile
new file mode 100644
index 00000000..e2eed9fa
--- /dev/null
+++ b/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/netcon_dockerfile
@@ -0,0 +1,39 @@
+# ZT Network Containers Test
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+# Install apps
+RUN yum -y update
+RUN yum -y install redis-3.0.4-1.fc23.x86_64
+RUN yum clean all
+
+EXPOSE 9993/udp 6379/udp
+
+# Install LWIP library used by service
+ADD liblwip.so /var/lib/zerotier-one/liblwip.so
+
+# Install syscall intercept library
+ADD zerotier-intercept /
+ADD libzerotierintercept.so.1.0 /
+RUN cp libzerotierintercept.so.1.0 lib/libzerotierintercept.so.1.0
+RUN cp libzerotierintercept.so.1.0 /lib/libzerotierintercept.so.1.0
+RUN ln -sf /lib/libzerotierintercept.so.1.0 /lib/libzerotierintercept
+RUN /usr/bin/install -c zerotier-intercept /usr/bin
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD netcon_identity.public /var/lib/zerotier-one/identity.public
+ADD netcon_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Install test scripts
+ADD netcon_entrypoint.sh /netcon_entrypoint.sh
+RUN chmod -v +x /netcon_entrypoint.sh
+
+# Start ZeroTier-One
+CMD ["./netcon_entrypoint.sh"]
diff --git a/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/netcon_entrypoint.sh b/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/netcon_entrypoint.sh
new file mode 100644
index 00000000..8f7aefec
--- /dev/null
+++ b/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/netcon_entrypoint.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test: ' "$test_name"
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+virtip4=""
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+ dev=`/zerotier-cli listnetworks | grep -F "" | cut -d ' ' -f 8 | cut -d "_" -f 2 | sed "s/^<dev>//" | tr '\n' '\0'`
+done
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Writing address to ' "$address_file"
+echo $virtip4 > "$address_file"
+
+export ZT_NC_NWID=$dev
+
+# --- Test section ---
+echo '*** Starting application...'
+sleep 0.5
+zerotier-intercept /usr/bin/redis-server --port 6379
diff --git a/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/redis-3.0.4-1.fc23.x86_64.name b/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/redis-3.0.4-1.fc23.x86_64.name
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/netcon/docker-test/redis/redis-3.0.4-1.fc23.x86_64/redis-3.0.4-1.fc23.x86_64.name
diff --git a/netcon/docker-test/run_tests.sh b/netcon/docker-test/run_tests.sh
new file mode 100755
index 00000000..cd0bc54c
--- /dev/null
+++ b/netcon/docker-test/run_tests.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# Merely RUNS test images
+
+# Remove previous test results
+rm _results/*.txt
+
+# How long we shall wait for each test to conclude
+export netcon_test_wait_time=60s
+
+# Test structure, in later releases more complex multi-party scripts will be included
+export test_script=two_party_test.sh
+
+# Iterate over all depth=2 (relatively-speaking) directories and perform each test
+find . -mindepth 2 -maxdepth 2 -type d | while read testdir; do
+ echo "*** Testing: '$testdir'..."
+ rm _results/*.tmp
+
+ # Stage scripts
+ cp $test_script $testdir/$test_script
+ cd $testdir
+
+ # Run test
+ ./$test_script
+ rm $test_script
+
+ cd ../../
+done \ No newline at end of file
diff --git a/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/monitor_dockerfile b/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/monitor_dockerfile
new file mode 100644
index 00000000..e8f803cd
--- /dev/null
+++ b/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/monitor_dockerfile
@@ -0,0 +1,23 @@
+# ZT Network Containers Test Monitor
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+EXPOSE 9993/udp
+
+# Install LWIP library used by service
+ADD liblwip.so /var/lib/zerotier-one/liblwip.so
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD monitor_identity.public /var/lib/zerotier-one/identity.public
+ADD monitor_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Start ZeroTier-One
+ADD monitor_entrypoint.sh /monitor_entrypoint.sh
+RUN chmod -v +x /monitor_entrypoint.sh
+CMD ["./monitor_entrypoint.sh"]
diff --git a/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/monitor_entrypoint.sh b/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/monitor_entrypoint.sh
new file mode 100644
index 00000000..d617bd4a
--- /dev/null
+++ b/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/monitor_entrypoint.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+netcon_wait_time=25 # wait for test container to come online
+app_timeout_time=15 # app-specific timeout
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+fail=FAIL. # appended to result file in event of failure
+ok=OK. # appended to result file in event of success
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+bigfile_name=bigfile # large, random test transfer file
+rx_md5sumfile="$file_path"rx_"$bigfile_name"_md5sum"$tmp_ext"
+tx_md5sumfile="$file_path"tx_"$bigfile_name"_md5sum"$tmp_ext"
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test Monitor'
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+virtip4=""
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+done
+echo '*** Starting Test...'
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Sleeping for (' "$netcon_wait_time" 's ) while we wait for the Network Container to come online...'
+sleep "$netcon_wait_time"s
+ncvirtip=$(<$address_file)
+
+
+# --- Test section ---
+echo '*** Copying file to intercepted server at' $ncvirtip
+touch "$bigfile_name"
+
+# Check md5
+md5sum < "$bigfile_name" >> "$rx_md5sumfile"
+tx_md5sum=$(<$tx_md5sumfile)
+
+# ...
+
+
+
+
+
+
+
+
diff --git a/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/netcon_dockerfile b/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/netcon_dockerfile
new file mode 100644
index 00000000..ddb0a6c4
--- /dev/null
+++ b/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/netcon_dockerfile
@@ -0,0 +1,39 @@
+# ZT Network Containers Test
+FROM fedora:23
+MAINTAINER https://www.zerotier.com/
+
+# Install apps
+RUN yum -y update
+RUN yum -y install openssh-server
+RUN yum clean all
+
+EXPOSE 9993/udp
+
+# Install LWIP library used by service
+ADD liblwip.so /var/lib/zerotier-one/liblwip.so
+
+# Install syscall intercept library
+ADD zerotier-intercept /
+ADD libzerotierintercept.so.1.0 /
+RUN cp libzerotierintercept.so.1.0 lib/libzerotierintercept.so.1.0
+RUN cp libzerotierintercept.so.1.0 /lib/libzerotierintercept.so.1.0
+RUN ln -sf /lib/libzerotierintercept.so.1.0 /lib/libzerotierintercept
+RUN /usr/bin/install -c zerotier-intercept /usr/bin
+
+# Add ZT files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD netcon_identity.public /var/lib/zerotier-one/identity.public
+ADD netcon_identity.secret /var/lib/zerotier-one/identity.secret
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD *.name /
+
+ADD zerotier-one /
+ADD zerotier-cli /
+
+# Install test scripts
+ADD netcon_entrypoint.sh /netcon_entrypoint.sh
+RUN chmod -v +x /netcon_entrypoint.sh
+
+# Start ZeroTier-One
+CMD ["./netcon_entrypoint.sh"]
diff --git a/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/netcon_entrypoint.sh b/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/netcon_entrypoint.sh
new file mode 100644
index 00000000..9d8625aa
--- /dev/null
+++ b/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/netcon_entrypoint.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
+
+
+# --- Test Parameters ---
+test_namefile=$(ls *.name)
+test_name="${test_namefile%.*}" # test network id
+nwconf=$(ls *.conf) # blank test network config file
+nwid="${nwconf%.*}" # test network id
+file_path=/opt/results/ # test result output file path (fs shared between host and containers)
+file_base="$test_name".txt # test result output file
+tmp_ext=.tmp # temporary filetype used for sharing test data between containers
+address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
+bigfile_name=bigfile
+bigfile_size=10M # size of file we want to use for the test
+tx_md5sumfile="$file_path"tx_"$bigfile_name"_md5sum"$tmp_ext"
+
+
+# --- Network Config ---
+echo '*** ZeroTier Network Containers Test: ' "$test_name"
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+./zerotier-one -d -U -p9993
+virtip4=""
+while [ -z "$virtip4" ]; do
+ sleep 0.2
+ virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
+ dev=`/zerotier-cli listnetworks | grep -F "" | cut -d ' ' -f 8 | cut -d "_" -f 2 | sed "s/^<dev>//" | tr '\n' '\0'`
+done
+echo '*** Up and running at' $virtip4 ' on network: ' $nwid
+echo '*** Writing address to ' "$address_file"
+echo $virtip4 > "$address_file"
+
+export ZT_NC_NWID=$dev
+
+# --- Test section ---
+# Generate large random file for transfer test, share md5sum for monitor container to check
+echo '*** Generating ' "$bigfile_size" ' file'
+dd if=/dev/urandom of=/var/www/html/"$bigfile_name" bs="$bigfile_size" count=1
+#md5sum /var/www/html/"$bigfile_name" >> "$tx_md5sumfile"
+md5sum < /var/www/html/"$bigfile_name" >> "$tx_md5sumfile"
+echo '*** Wrote MD5 sum to ' "$tx_md5sumfile"
+
+echo '*** Starting application...'
+sleep 0.5
+
+# wait for rsa public key from monitor
+#while [ ! -s "$file_path$rsa_public_key_file" ]; do
+# sleep 0.2
+#done
+
+zerotier-intercept /usr/sbin/sshd
diff --git a/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/openssh-server-7.1p1-3.fc23.x86_64.name b/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/openssh-server-7.1p1-3.fc23.x86_64.name
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/netcon/docker-test/sshd/openssh-server-7.1p1-3.fc23.x86_64/openssh-server-7.1p1-3.fc23.x86_64.name
diff --git a/netcon/docker-test/test.sh b/netcon/docker-test/test.sh
new file mode 100755
index 00000000..cda8e700
--- /dev/null
+++ b/netcon/docker-test/test.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Runs test images
+
+echo "*** Running unit tests..."
+
+# Remove previous test results
+rm _results/*.txt
+
+# How long we shall wait for each test to conclude
+export netcon_test_wait_time=60s
+
+# Test structure, in later releases more complex multi-party scripts will be included
+export test_script=two_party_test.sh
+
+# Iterate over all depth=2 (relatively-speaking) directories and perform each test
+find . -mindepth 2 -maxdepth 2 -type d | while read testdir; do
+ echo "*** Testing: '$testdir'..."
+ rm _results/*.tmp
+
+ # Stage scripts
+ cp $test_script $testdir/$test_script
+ cd $testdir
+
+ # Run test
+ ./$test_script
+ rm $test_script
+
+ cd ../../
+done
+
+echo "*** Done" \ No newline at end of file
diff --git a/netcon/docker-test/two_party_test.sh b/netcon/docker-test/two_party_test.sh
new file mode 100755
index 00000000..164b0b2e
--- /dev/null
+++ b/netcon/docker-test/two_party_test.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+# Runs test image and monitor image as daemons
+
+test_name=${PWD##*/}
+echo 'Building dockerfiles for test: ' "$test_name"
+touch "$test_name".name
+
+# Start netcon container to be tested
+test_container=$(docker run -d -it -v $PWD/../../_results:/opt/results --device=/dev/net/tun "$test_name":latest)
+monitor_container=$(docker run -d -it -v $PWD/../../_results:/opt/results --device=/dev/net/tun "$test_name"_monitor:latest)
+
+echo "waiting $netcon_test_wait_time for test to complete."
+sleep $netcon_test_wait_time
+docker stop $(docker ps -a -q)
+docker rm $test_container
+docker rm $monitor_container \ No newline at end of file
diff --git a/netcon/docker-test/zerotier-cli b/netcon/docker-test/zerotier-cli
new file mode 100755
index 00000000..95e82b3d
--- /dev/null
+++ b/netcon/docker-test/zerotier-cli
Binary files differ
diff --git a/netcon/docker-test/zerotier-intercept b/netcon/docker-test/zerotier-intercept
new file mode 100755
index 00000000..85e9fba1
--- /dev/null
+++ b/netcon/docker-test/zerotier-intercept
@@ -0,0 +1,54 @@
+#!/bin/sh
+# usage:
+# /usr/bin/intercept program <args>
+
+if [ $# = 0 ] ; then
+ echo "$0: insufficient arguments"
+ exit
+fi
+
+case "$1" in
+ on)
+ if [ -z "$LD_PRELOAD" ]
+ then
+ export LD_PRELOAD="/lib/libzerotierintercept.so.1.0"
+ else
+ echo $LD_PRELOAD | grep -q "/lib/libzerotierintercept\.so.1.0" || \
+ export LD_PRELOAD="/lib/libzerotierintercept.so $LD_PRELOAD"
+ fi
+ ;;
+ off)
+ export LD_PRELOAD=`echo -n $LD_PRELOAD | sed 's/\/lib\/libzerotierintercept.so.1.0 \?//'`
+ if [ -z "$LD_PRELOAD" ]
+ then
+ unset LD_PRELOAD
+ fi
+ ;;
+ show|sh)
+ echo "LD_PRELOAD=\"$LD_PRELOAD\""
+ ;;
+ -h|-?)
+ echo ""
+ ;;
+ *)
+ if [ -z "$LD_PRELOAD" ]
+ then
+ export LD_PRELOAD="/lib/libzerotierintercept.so.1.0"
+ else
+ echo $LD_PRELOAD | grep -q "/lib/libzerotierintercept\.so.1.0" || \
+ export LD_PRELOAD="/lib/libzerotierintercept.so.1.0 $LD_PRELOAD"
+ fi
+
+ if [ $# = 0 ]
+ then
+ ${SHELL:-/bin/sh}
+ fi
+
+ if [ $# -gt 0 ]
+ then
+ exec "$@"
+ fi
+ ;;
+esac
+
+#EOF
diff --git a/netcon/docker-test/zerotier-one b/netcon/docker-test/zerotier-one
new file mode 100755
index 00000000..95e82b3d
--- /dev/null
+++ b/netcon/docker-test/zerotier-one
Binary files differ
diff --git a/netcon/make-intercept.mk b/netcon/make-intercept.mk
new file mode 100644
index 00000000..71d6d4dc
--- /dev/null
+++ b/netcon/make-intercept.mk
@@ -0,0 +1,53 @@
+#
+# ZeroTier One - Network Virtualization Everywhere
+# Copyright (C) 2011-2015 ZeroTier, Inc.
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# --
+#
+# ZeroTier may be used and distributed under the terms of the GPLv3, which
+# are available at: http://www.gnu.org/licenses/gpl-3.0.html
+#
+# If you would like to embed ZeroTier into a commercial application or
+# redistribute it in a modified binary form, please contact ZeroTier Networks
+# LLC. Start here: http://www.zerotier.com/
+#
+
+SHCC=gcc
+
+intercept_CFLAGS = -c -fPIC -g -O2 -Wall -std=c99 -DVERBOSE -DDEBUG_RPC -DCHECKS -D_GNU_SOURCE -DNETCON_INTERCEPT
+#LIB_NAME = intercept
+SHLIB_EXT=dylib
+SHLIB_MAJOR = 1
+SHLIB_MINOR = 8
+COMMON = Common
+OBJS= Intercept.o
+#SHLIB = ${LIB_NAME}.${SHLIB_EXT}.${SHLIB_MAJOR}.${SHLIB_MINOR}
+SHLDFLAGS = -g -O2 -Wall -I. -nostdlib -shared
+LIBS = -ldl -lc -lrt -lpthread
+
+lib:
+ ${SHCC} $(intercept_CFLAGS) -I. Intercept.c -o Intercept.o
+ ${SHCC} $(SHLDFLAGS) Intercept.o -o libzerotierintercept.so.1.0 $(LIBS)
+
+install:
+ cp libzerotierintercept.so.1.0 /lib/libzerotierintercept.so.1.0
+ ln -sf /lib/libzerotierintercept.so.1.0 /lib/libzerotierintercept
+ /usr/bin/install -c zerotier-intercept /usr/bin
+
+uninstall:
+ rm -r /lib/libzerotierintercept.so.1.0
+ rm -r /lib/libzerotierintercept
+ rm -r /usr/bin/zerotier-intercept
diff --git a/netcon/make-liblwip.mk b/netcon/make-liblwip.mk
new file mode 100644
index 00000000..9f16b483
--- /dev/null
+++ b/netcon/make-liblwip.mk
@@ -0,0 +1,106 @@
+#
+# Copyright (c) 2001, 2002 Swedish Institute of Computer Science.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+# SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+# OF SUCH DAMAGE.
+#
+# This file is part of the lwIP TCP/IP stack.
+#
+# Author: Adam Dunkels <adam@sics.se>
+#
+
+CONTRIBDIR=../ext/contrib
+LWIPARCH=$(CONTRIBDIR)/ports/unix
+
+#Set this to where you have the lwip core module checked out from CVS
+#default assumes it's a dir named lwip at the same level as the contrib module
+LWIPDIR=../ext/lwip/src
+
+
+CCDEP=gcc
+CC=gcc
+CFLAGS=-O3 -g -Wall -DIPv4 -fPIC
+
+CFLAGS:=$(CFLAGS) \
+ -I$(LWIPDIR)/include -I$(LWIPARCH)/include -I$(LWIPDIR)/include/ipv4 \
+ -I$(LWIPDIR) -I.
+
+
+# COREFILES, CORE4FILES: The minimum set of files needed for lwIP.
+COREFILES=$(LWIPDIR)/core/mem.c $(LWIPDIR)/core/memp.c $(LWIPDIR)/core/netif.c \
+ $(LWIPDIR)/core/pbuf.c $(LWIPDIR)/core/raw.c $(LWIPDIR)/core/stats.c \
+ $(LWIPDIR)/core/sys.c $(LWIPDIR)/core/tcp.c $(LWIPDIR)/core/tcp_in.c \
+ $(LWIPDIR)/core/tcp_out.c $(LWIPDIR)/core/udp.c $(LWIPDIR)/core/dhcp.c \
+ $(LWIPDIR)/core/init.c $(LWIPDIR)/core/timers.c $(LWIPDIR)/core/def.c
+CORE4FILES=$(wildcard $(LWIPDIR)/core/ipv4/*.c) $(LWIPDIR)/core/ipv4/inet.c \
+ $(LWIPDIR)/core/ipv4/inet_chksum.c
+
+# SNMPFILES: Extra SNMPv1 agent
+SNMPFILES=$(LWIPDIR)/core/snmp/asn1_dec.c $(LWIPDIR)/core/snmp/asn1_enc.c \
+ $(LWIPDIR)/core/snmp/mib2.c $(LWIPDIR)/core/snmp/mib_structs.c \
+ $(LWIPDIR)/core/snmp/msg_in.c $(LWIPDIR)/core/snmp/msg_out.c
+
+# APIFILES: The files which implement the sequential and socket APIs.
+APIFILES=$(LWIPDIR)/api/api_lib.c $(LWIPDIR)/api/api_msg.c $(LWIPDIR)/api/tcpip.c \
+ $(LWIPDIR)/api/err.c $(LWIPDIR)/api/sockets.c $(LWIPDIR)/api/netbuf.c $(LWIPDIR)/api/netdb.c
+
+# NETIFFILES: Files implementing various generic network interface functions.'
+NETIFFILES=$(LWIPDIR)/netif/etharp.c $(LWIPDIR)/netif/slipif.c
+
+# NETIFFILES: Add PPP netif
+NETIFFILES+=$(LWIPDIR)/netif/ppp/auth.c $(LWIPDIR)/netif/ppp/chap.c \
+ $(LWIPDIR)/netif/ppp/chpms.c $(LWIPDIR)/netif/ppp/fsm.c \
+ $(LWIPDIR)/netif/ppp/ipcp.c $(LWIPDIR)/netif/ppp/lcp.c \
+ $(LWIPDIR)/netif/ppp/magic.c $(LWIPDIR)/netif/ppp/md5.c \
+ $(LWIPDIR)/netif/ppp/pap.c $(LWIPDIR)/netif/ppp/ppp.c \
+ $(LWIPDIR)/netif/ppp/randm.c $(LWIPDIR)/netif/ppp/vj.c
+
+# ARCHFILES: Architecture specific files.
+ARCHFILES=$(wildcard $(LWIPARCH)/*.c $(LWIPARCH)tapif.c $(LWIPARCH)/netif/list.c $(LWIPARCH)/netif/tcpdump.c)
+
+
+# LWIPFILES: All the above.
+LWIPFILES=$(COREFILES) $(CORE4FILES) $(SNMPFILES) $(APIFILES) $(NETIFFILES) $(ARCHFILES)
+LWIPFILESW=$(wildcard $(LWIPFILES))
+LWIPOBJS=$(notdir $(LWIPFILESW:.c=.o))
+
+LWIPLIB=liblwip.so
+
+%.o:
+ $(CC) $(CFLAGS) -c $(<:.o=.c)
+
+all: $(LWIPLIB)
+.PHONY: all
+
+clean:
+ rm -f *.o $(LWIPLIB) *.s .depend* *.core core
+
+depend dep: .depend
+
+include .depend
+
+$(LWIPLIB): $(LWIPOBJS)
+ $(CC) -g -nostartfiles -shared -o $@ $^
+
+.depend: $(LWIPFILES)
+ $(CCDEP) $(CFLAGS) -MM $^ > .depend || rm -f .depend
diff --git a/netcon/zerotier-intercept b/netcon/zerotier-intercept
new file mode 100755
index 00000000..85e9fba1
--- /dev/null
+++ b/netcon/zerotier-intercept
@@ -0,0 +1,54 @@
+#!/bin/sh
+# usage:
+# /usr/bin/intercept program <args>
+
+if [ $# = 0 ] ; then
+ echo "$0: insufficient arguments"
+ exit
+fi
+
+case "$1" in
+ on)
+ if [ -z "$LD_PRELOAD" ]
+ then
+ export LD_PRELOAD="/lib/libzerotierintercept.so.1.0"
+ else
+ echo $LD_PRELOAD | grep -q "/lib/libzerotierintercept\.so.1.0" || \
+ export LD_PRELOAD="/lib/libzerotierintercept.so $LD_PRELOAD"
+ fi
+ ;;
+ off)
+ export LD_PRELOAD=`echo -n $LD_PRELOAD | sed 's/\/lib\/libzerotierintercept.so.1.0 \?//'`
+ if [ -z "$LD_PRELOAD" ]
+ then
+ unset LD_PRELOAD
+ fi
+ ;;
+ show|sh)
+ echo "LD_PRELOAD=\"$LD_PRELOAD\""
+ ;;
+ -h|-?)
+ echo ""
+ ;;
+ *)
+ if [ -z "$LD_PRELOAD" ]
+ then
+ export LD_PRELOAD="/lib/libzerotierintercept.so.1.0"
+ else
+ echo $LD_PRELOAD | grep -q "/lib/libzerotierintercept\.so.1.0" || \
+ export LD_PRELOAD="/lib/libzerotierintercept.so.1.0 $LD_PRELOAD"
+ fi
+
+ if [ $# = 0 ]
+ then
+ ${SHELL:-/bin/sh}
+ fi
+
+ if [ $# -gt 0 ]
+ then
+ exec "$@"
+ fi
+ ;;
+esac
+
+#EOF