summaryrefslogtreecommitdiff
path: root/netcon
diff options
context:
space:
mode:
Diffstat (limited to 'netcon')
-rwxr-xr-xnetcon/Intercept.h222
-rw-r--r--netcon/LWIPStack.hpp193
-rw-r--r--netcon/NetconEthernetTap.cpp906
-rw-r--r--netcon/NetconEthernetTap.hpp226
-rw-r--r--netcon/NetconService.hpp148
-rw-r--r--netcon/NetconUtilities.cpp165
-rw-r--r--netcon/NetconUtilities.hpp38
7 files changed, 1898 insertions, 0 deletions
diff --git a/netcon/Intercept.h b/netcon/Intercept.h
new file mode 100755
index 00000000..7a8864aa
--- /dev/null
+++ b/netcon/Intercept.h
@@ -0,0 +1,222 @@
+/*
+ * 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>
+
+
+/* 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_FD_MAP_COMPLETION 20 // Give the service the value we "see" for the new buffer fd
+#define RPC_RETVAL 21 // not RPC per se, but something we should codify
+#define RPC_KILL_INTERCEPT 22 // 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];
+};
+
+#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 POLL_SIG struct pollfd *__fds, nfds_t __nfds, int __timeout
+#define IOCTL_SIG int __fd, unsigned long int __request, ...
+#define FCNTL_SIG int __fd, int __cmd, ...
+#define CLONE_SIG int (*fn) (void *arg), void *child_stack, int flags, void *arg
+#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
+
+
+/* LWIP error beautification */
+
+/*
+const char *lwiperror(int n)
+{
+ switch(n)
+ {
+ case 0:
+ return "ERR_OK";
+ case -1:
+ return "ERR_MEM (out of memory)";
+ case -2:
+ return "ERR_BUF (buffer error)";
+ case -3:
+ return "ERR_TIMEOUT (timeout)";
+ case -4:
+ return "ERR_RTE (routing problem)";
+ case -5:
+ return "ERR_INPROGRESS (operation in progress)";
+ case -6:
+ return "ERR_VAL (illegal value)";
+ case -7:
+ return "ERR_WOULDBLOCK (operation would block)";
+ case -8:
+ return "ERR_USE (address in use)";
+ case -9:
+ return "ERR_ISCONN (already connected)";
+ case -10:
+ return "Fatal: ERR_ABRT (connection aborted)";
+ case -11:
+ return "Fatal: ERR_RST (connection reset)";
+ case -12:
+ return "Fatal: ERR_CLSD (connection closed)";
+ case -13:
+ return "Fatal: ERR_CONN (not connected)";
+ case -14:
+ return "Fatal: ERR_ARG (illegal argument)";
+ case -15:
+ return "Fatal: ERR_IF (low level netif error)";
+ default:
+ return "UNKNOWN_RET_VAL";
+ }
+}
+*/
+
+#endif
diff --git a/netcon/LWIPStack.hpp b/netcon/LWIPStack.hpp
new file mode 100644
index 00000000..a938dd1e
--- /dev/null
+++ b/netcon/LWIPStack.hpp
@@ -0,0 +1,193 @@
+/*
+ * 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 "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+/*
+#include "lwip/timers.h"
+#include "lwip/opt.h"
+#include "lwip/init.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+//#include "lwip/tcp_impl.h"
+//#include "lwip/inet_chksum.h"
+#include "lwip/tcpip.h"
+//#include "lwip/ip_addr.h"
+#include "lwip/debug.h"
+//#include "lwip/ip.h"
+//#include "lwip/ip_frag.h"
+*/
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+#ifndef LWIPSTACK_H
+#define LWIPSTACK_H
+
+#ifdef D_GNU_SOURCE
+ #define _GNU_SOURCE
+#endif
+
+
+typedef ip_addr ip_addr_t;
+
+
+#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 TAPIF_INIT_SIG struct netif *netif
+//#define TAPIF_INPUT_SIG LWIPStack* ls, struct netif *netif
+#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
+
+
+
+class LWIPStack{
+ void* libref;
+
+public:
+
+ 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);
+ //err_t (*tapif_init)(TAPIF_INIT_SIG);
+ //void (*tapif_input)(TAPIF_INPUT_SIG);
+ 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);
+
+
+
+ LWIPStack(const char* path)
+ {
+ libref = dlmopen(LM_ID_NEWLM, path, RTLD_NOW);
+ if(libref == NULL)
+ printf("dlerror(): %s\n", dlerror());
+
+ /* assign function pointers to symbols in dynamically-loaded lib */
+ 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");
+ //tapif_init = (err_t(*)(TAPIF_INIT_SIG))dlsym(libref, "tapif_init");
+ //tapif_input = (void(*)(TAPIF_INPUT_SIG))dlsym(libref, "tapif_input");
+ 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");
+ }
+};
+
+#endif
diff --git a/netcon/NetconEthernetTap.cpp b/netcon/NetconEthernetTap.cpp
new file mode 100644
index 00000000..0895cb44
--- /dev/null
+++ b/netcon/NetconEthernetTap.cpp
@@ -0,0 +1,906 @@
+/*
+ * 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 "NetconEthernetTap.hpp"
+
+#include "../node/Utils.hpp"
+#include "../osdep/OSUtils.hpp"
+#include "../osdep/Phy.hpp"
+
+#include "lwip/tcp_impl.h"
+#include "netif/etharp.h"
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip_frag.h"
+#include "lwip/tcp.h"
+
+#include "LWIPStack.hpp"
+#include "NetconService.hpp"
+#include "Intercept.h"
+#include "NetconUtilities.hpp"
+
+#define APPLICATION_POLL_FREQ 1
+
+namespace ZeroTier {
+
+
+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) :
+ _phy(this,false,true),
+ _unixListenSocket((PhySocket *)0),
+ _handler(handler),
+ _arg(arg),
+ _nwid(nwid),
+ _mac(mac),
+ _homePath(homePath),
+ _mtu(mtu),
+ _enabled(true),
+ _run(true)
+{
+ char sockPath[4096];
+ Utils::snprintf(sockPath,sizeof(sockPath),"/tmp/.ztnc_%.16llx",(unsigned long long)nwid);
+ _dev = sockPath;
+
+ lwipstack = new LWIPStack("ext/bin/lwip/liblwip.so"); // ext/bin/liblwip.so.debug for debug symbols
+ if(!lwipstack) // TODO double check this check
+ throw std::runtime_error("unable to load lwip lib.");
+ lwipstack->lwip_init();
+
+ _unixListenSocket = _phy.unixListen(sockPath,(void *)this);
+ 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);
+}
+
+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;
+ //fprintf(stderr, "_put(%s,%s,%.4x,[data],%u)\n",from.toString().c_str(),to.toString().c_str(),etherType,len);
+ if (!_enabled)
+ return;
+
+ //printf(">> %.4x %s\n",etherType,Utils::hex(data,len).c_str());
+ 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)) {
+ fprintf(stderr,"_put(): Dropped packet: first pbuf smaller than ethernet header\n");
+ return;
+ }
+ memcpy(q->payload,&ethhdr,sizeof(ethhdr));
+ memcpy(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 {
+ fprintf(stderr, "_put(): Dropped packet: no pbufs available\n");
+ return;
+ }
+
+ //printf("p->len == %u, p->payload == %s\n",p->len,Utils::hex(p->payload,p->len).c_str());
+ if(interface.input(p, &interface) != ERR_OK) {
+ fprintf(stderr, "_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);
+}
+
+NetconConnection *NetconEthernetTap::getConnectionByPCB(struct tcp_pcb *pcb)
+{
+ NetconConnection *c;
+ for(size_t i=0; i<clients.size(); i++) {
+ c = clients[i]->containsPCB(pcb);
+ if(c) return c;
+ }
+ return NULL;
+}
+
+NetconConnection *NetconEthernetTap::getConnectionByThisFD(int fd)
+{
+ for(size_t i=0; i<clients.size(); i++) {
+ for(size_t j=0; j<clients[i]->connections.size(); j++) {
+ if(_phy.getDescriptor(clients[i]->connections[j]->sock) == fd)
+ return clients[i]->connections[j];
+ }
+ }
+ return NULL;
+}
+
+NetconClient *NetconEthernetTap::getClientByPCB(struct tcp_pcb *pcb)
+{
+ for(size_t i=0; i<clients.size(); i++) {
+ if(clients[i]->containsPCB(pcb))
+ return clients[i];
+ }
+ return NULL;
+}
+
+void NetconEthernetTap::closeAllClients()
+{
+ for(size_t i=0; i<clients.size(); i++){
+ closeClient(clients[i]);
+ }
+}
+
+/*
+ * Closes a NetconConnection and associated LWIP PCB strcuture.
+ */
+void NetconEthernetTap::closeConnection(NetconConnection *conn)
+{
+ NetconClient *client = conn->owner;
+ lwipstack->tcp_arg(conn->pcb, NULL);
+ 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_close(conn->pcb);
+ _phy.close(conn->sock);
+ lwipstack->tcp_close(conn->pcb);
+ client->removeConnection(conn->sock);
+}
+
+/*
+ * Closes a NetconClient and all associated NetconConnections (rpc, data, and unmapped)
+ */
+void NetconEthernetTap::closeClient(NetconClient *client)
+{
+ closeConnection(client->rpc);
+ closeConnection(client->unmapped_conn);
+ for(size_t i=0; i<client->connections.size(); i++)
+ {
+ close(_phy.getDescriptor(client->connections[i]->sock));
+ lwipstack->tcp_close(client->connections[i]->pcb);
+ delete client->connections[i];
+ client->connections.erase(client->connections.begin() + i);
+ }
+}
+
+#define ZT_LWIP_TCP_TIMER_INTERVAL 10
+
+void NetconEthernetTap::threadMain()
+ throw()
+{
+ uint64_t prev_tcp_time = 0;
+ uint64_t prev_etharp_time = 0;
+
+ /*
+ fprintf(stderr, "- MEM_SIZE = %dM\n", MEM_SIZE / (1024*1024));
+ fprintf(stderr, "- TCP_SND_BUF = %dK\n", TCP_SND_BUF / 1024);
+ 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", MEMP_NUM_TCP_SEG);
+ fprintf(stderr, "- PBUF_POOL_SIZE = %d\n", PBUF_POOL_SIZE);
+ fprintf(stderr, "- TCP_SND_QUEUELEN = %d\n", TCP_SND_QUEUELEN);
+ fprintf(stderr, "- IP_REASSEMBLY = %d\n", IP_REASSEMBLY);
+ fprintf(stderr, "- TCP_WND = %d\n", TCP_WND);
+ fprintf(stderr, "- TCP_MSS = %d\n", TCP_MSS);
+ fprintf(stderr, "- NO_SYS = %d\n", NO_SYS);
+ fprintf(stderr, "- LWIP_SOCKET = %d\n", LWIP_SOCKET);
+ fprintf(stderr, "- LWIP_NETCONN = %d\n", LWIP_NETCONN);
+ 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);
+ fprintf(stderr, "- DEFAULT_READ_BUFFER_SIZE = %d\n", DEFAULT_READ_BUFFER_SIZE);
+ */
+
+ //fprintf(stderr, "- LWIP_DEBUG = %d\n", LWIP_DEBUG);
+ fprintf(stderr, "- TCP_DEBUG = %d\n", TCP_DEBUG);
+
+ // 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 tcp_remaining = ZT_LWIP_TCP_TIMER_INTERVAL;
+ uint64_t etharp_remaining = ARP_TMR_INTERVAL;
+
+ 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));
+ }
+ closeAllClients();
+ // TODO: cleanup -- destroy LWIP state, kill any clients, unload .so, etc.
+}
+
+void NetconEthernetTap::phyOnUnixClose(PhySocket *sock,void **uptr)
+{
+ fprintf(stderr, "phyOnUnixClose()\n");
+ close(_phy.getDescriptor(sock));
+}
+
+/*
+ * 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) {
+ int r;
+ NetconConnection *c = ((NetconClient*)*uptr)->getConnection(sock);
+ if(c->idx < DEFAULT_READ_BUFFER_SIZE) {
+ if((r = read(_phy.getDescriptor(sock), (&c->buf)+c->idx, DEFAULT_READ_BUFFER_SIZE-(c->idx))) > 0) {
+ c->idx += r;
+ handle_write(c);
+ }
+ }
+ }
+}
+
+// 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) {}
+
+/*
+ * Creates a new NetconClient for the accepted RPC connection (unix domain socket)
+ *
+ * Subsequent socket connections from this client will be associated with this
+ * NetconClient object.
+ */
+void NetconEthernetTap::phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN)
+{
+ NetconClient *newClient = new NetconClient();
+ newClient->rpc = newClient->addConnection(RPC, sockN);
+ *uptrN = newClient;
+ clients.push_back(newClient);
+}
+
+/*
+ * Processes incoming data on a client-specific RPC connection
+ */
+void NetconEthernetTap::phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len)
+{
+ unsigned char *buf = (unsigned char*)data;
+ NetconClient *client = (NetconClient*)*uptr;
+
+ switch(buf[0])
+ {
+ case RPC_SOCKET:
+ fprintf(stderr, "RPC_SOCKET\n");
+ struct socket_st socket_rpc;
+ memcpy(&socket_rpc, &buf[1], sizeof(struct socket_st));
+ client->tid = socket_rpc.__tid;
+ handle_socket(client, &socket_rpc);
+ break;
+ case RPC_LISTEN:
+ fprintf(stderr, "RPC_LISTEN\n");
+ struct listen_st listen_rpc;
+ memcpy(&listen_rpc, &buf[1], sizeof(struct listen_st));
+ client->tid = listen_rpc.__tid;
+ handle_listen(client, &listen_rpc);
+ break;
+ case RPC_BIND:
+ fprintf(stderr, "RPC_BIND\n");
+ struct bind_st bind_rpc;
+ memcpy(&bind_rpc, &buf[1], sizeof(struct bind_st));
+ client->tid = bind_rpc.__tid;
+ handle_bind(client, &bind_rpc);
+ break;
+ case RPC_KILL_INTERCEPT:
+ fprintf(stderr, "RPC_KILL_INTERCEPT\n");
+ closeClient(client);
+ break;
+ case RPC_CONNECT:
+ fprintf(stderr, "RPC_CONNECT\n");
+ struct connect_st connect_rpc;
+ memcpy(&connect_rpc, &buf[1], sizeof(struct connect_st));
+ client->tid = connect_rpc.__tid;
+ handle_connect(client, &connect_rpc);
+ break;
+ case RPC_FD_MAP_COMPLETION:
+ fprintf(stderr, "RPC_FD_MAP_COMPLETION\n");
+ handle_retval(client, buf);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Send a return value to the client for an RPC
+ */
+int NetconEthernetTap::send_return_value(NetconClient *client, int retval)
+{
+ char retmsg[4];
+ memset(&retmsg, '\0', sizeof(retmsg));
+ retmsg[0]=RPC_RETVAL;
+ memcpy(&retmsg[1], &retval, sizeof(retval));
+ int n = write(_phy.getDescriptor(client->rpc->sock), &retmsg, sizeof(retmsg));
+ if(n > 0) {
+ // signal that we've satisfied this requirement
+ client->waiting_for_retval = false;
+ }
+ else {
+ fprintf(stderr, "unable to send return value to the intercept\n");
+ closeClient(client);
+ }
+ return n;
+}
+
+/*------------------------------------------------------------------------------
+--------------------------------- LWIP callbacks -------------------------------
+------------------------------------------------------------------------------*/
+
+/*
+ * 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;
+ NetconConnection *c = l->tap->getConnectionByPCB(tpcb);
+ NetconEthernetTap *tap = l->tap;
+ if(c)
+ tap->handle_write(c);
+ return ERR_OK;
+}
+
+/*
+ * 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
+ *
+ */
+err_t NetconEthernetTap::nc_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+ Larg *l = (Larg*)arg;
+ int larg_fd = l->tap->_phy.getDescriptor(l->sock);
+ NetconEthernetTap *tap = l->tap;
+ NetconConnection *c = tap->getConnectionByThisFD(larg_fd);
+ if(c) {
+ NetconClient *client = c->owner;
+ if(!client){
+ fprintf(stderr, "nc_accpet(%d): unable to locate client for this PCB\n", larg_fd);
+ return -1;
+ }
+ ZT_PHY_SOCKFD_TYPE fds[2];
+ socketpair(PF_LOCAL, SOCK_STREAM, 0, fds);
+ NetconConnection *new_conn = client->addConnection(BUFFER, tap->_phy.wrapSocket(fds[0], client));
+ client->connections.push_back(new_conn);
+ new_conn->pcb = newpcb;
+ int send_fd = tap->_phy.getDescriptor(client->rpc->sock);
+ int n = write(larg_fd, "z", 1);
+ if(n > 0) {
+ if(sock_fd_write(send_fd, fds[1]) > 0) {
+ client->unmapped_conn = new_conn;
+ }
+ else {
+ fprintf(stderr, "nc_accept(%d): unable to send fd to client\n", larg_fd);
+ }
+ }
+ else {
+ fprintf(stderr, "nc_accept(%d): error writing signal byte (send_fd = %d, their_fd = %d)\n", larg_fd, send_fd, fds[1]);
+ return -1;
+ }
+ tap->lwipstack->tcp_arg(newpcb, new Larg(tap, new_conn->sock));
+ 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(c->pcb);
+ return ERR_OK;
+ }
+ else {
+ fprintf(stderr, "nc_accept(%d): can't locate Connection object for PCB.\n", larg_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)
+{
+ Larg *l = (Larg*)arg;
+ NetconConnection *c = l->tap->getConnectionByPCB(tpcb);
+ NetconEthernetTap *tap = l->tap;
+
+ int n;
+ struct pbuf* q = p;
+
+ if(!c) {
+ fprintf(stderr, "nc_recved(): no connection object\n");
+ return ERR_OK; // ?
+ }
+ if(p == NULL) {
+ if(c) {
+ fprintf(stderr, "nc_recved(): closing connection\n");
+ tap->_phy.close(c->sock);
+ tap->closeConnection(c);
+ }
+ else {
+ fprintf(stderr, "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 = tap->_phy.streamSend(c->sock,p->payload, p->len)) > 0) {
+ if(n < p->len) {
+ fprintf(stderr, "nc_recved(): unable to write entire pbuf to buffer\n");
+ }
+ tap->lwipstack->tcp_recved(tpcb, n);
+ }
+ else {
+ fprintf(stderr, "nc_recved(): No data written to intercept buffer\n");
+ }
+ p = p->next;
+ }
+ 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)
+{
+ fprintf(stderr, "nc_err\n");
+ Larg *l = (Larg*)arg;
+ NetconEthernetTap *tap = l->tap;
+ NetconConnection *c = tap->getConnectionByThisFD(tap->_phy.getDescriptor(l->sock));
+ if(c) {
+ tap->closeConnection(c);
+ }
+ else {
+ fprintf(stderr, "can't locate connection object for PCB\n");
+ }
+}
+
+/*
+ * Callback from LWIP
+ *
+ * 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)
+{
+ //fprintf(stderr, "nc_sent\n");
+ return len;
+}
+
+/*
+ * 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)
+{
+ fprintf(stderr, "nc_connected\n");
+ Larg *l = (Larg*)arg;
+ NetconEthernetTap *tap = l->tap;
+ for(size_t i=0; i<tap->clients.size(); i++) {
+ if(tap->clients[i]->containsPCB(tpcb)) {
+ tap->send_return_value(tap->clients[i],err);
+ }
+ }
+ return err;
+}
+
+
+
+/*------------------------------------------------------------------------------
+----------------------------- RPC Handler functions ----------------------------
+------------------------------------------------------------------------------*/
+
+/*
+ * Handles an RPC to bind an LWIP PCB to a given address and port
+ *
+ * @param Client that is making the RPC
+ * @param structure containing the data and parameters for this client's RPC
+ *
+ */
+void NetconEthernetTap::handle_bind(NetconClient *client, 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());
+ NetconConnection *c = client->getConnectionByTheirFD(bind_rpc->sockfd);
+ if(c) {
+ if(c->pcb->state == CLOSED){
+ int err = lwipstack->tcp_bind(c->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;
+ fprintf(stderr, "handle_bind(): error binding to %d.%d.%d.%d : %d\n", d[0],d[1],d[2],d[3], conn_port);
+ }
+ }
+ else fprintf(stderr, "handle_bind(): PCB not in CLOSED state. Ignoring BIND request.\n");
+ }
+ else fprintf(stderr, "handle_bind(): can't locate connection for PCB\n");
+}
+
+/*
+ * Handles an RPC to put an LWIP PCB into LISTEN mode
+ *
+ * @param Client that is making the RPC
+ * @param structure containing the data and parameters for this client's RPC
+ *
+ */
+void NetconEthernetTap::handle_listen(NetconClient *client, struct listen_st *listen_rpc)
+{
+ NetconConnection *c = client->getConnectionByTheirFD(listen_rpc->sockfd);
+ if(c) {
+ if(c->pcb->state == LISTEN) {
+ fprintf(stderr, "handle_listen(): PCB is already in listening state.\n");
+ return;
+ }
+ struct tcp_pcb* listening_pcb = lwipstack->tcp_listen(c->pcb);
+ if(listening_pcb != NULL) {
+ c->pcb = listening_pcb;
+ lwipstack->tcp_accept(listening_pcb, nc_accept);
+ lwipstack->tcp_arg(listening_pcb, new Larg(this, c->sock));
+ /* we need to wait for the client to send us the fd allocated on their end
+ for this listening socket */
+ client->waiting_for_retval=true;
+ }
+ else {
+ fprintf(stderr, "handle_listen(): unable to allocate memory for new listening PCB\n");
+ }
+ }
+ else {
+ fprintf(stderr, "handle_listen(): can't locate connection for PCB\n");
+ }
+}
+
+/**
+ * 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 Client that is making the RPC
+ * @param structure containing the data and parameters for this client's RPC
+ *
+ */
+void NetconEthernetTap::handle_retval(NetconClient *client, unsigned char* buf)
+{
+ if(client->unmapped_conn != NULL) {
+ memcpy(&(client->unmapped_conn->their_fd), &buf[1], sizeof(int));
+ client->connections.push_back(client->unmapped_conn);
+ client->unmapped_conn = NULL;
+ }
+}
+
+/*
+ * 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 Client that is making the RPC
+ * @param structure containing the data and parameters for this client's RPC
+ *
+ */
+void NetconEthernetTap::handle_socket(NetconClient *client, struct socket_st* socket_rpc)
+{
+ struct tcp_pcb *pcb = lwipstack->tcp_new();
+ if(pcb != NULL) {
+ ZT_PHY_SOCKFD_TYPE fds[2];
+ socketpair(PF_LOCAL, SOCK_STREAM, 0, fds);
+ NetconConnection *new_conn = client->addConnection(BUFFER, _phy.wrapSocket(fds[0], client));
+ new_conn->pcb = pcb;
+ PhySocket *sock = client->rpc->sock;
+ sock_fd_write(_phy.getDescriptor(sock), fds[1]);
+ /* Once the client tells us what its fd is for the other end,
+ we can then complete the mapping */
+ client->unmapped_conn = new_conn;
+ }
+ else {
+ fprintf(stderr, "handle_socket(): Memory not available for new PCB\n");
+ }
+}
+
+/*
+ * Handles an RPC to connect to a given address and port
+ *
+ * @param Client that is making the RPC
+ * @param structure containing the data and parameters for this client's RPC
+ *
+ */
+void NetconEthernetTap::handle_connect(NetconClient *client, struct connect_st* connect_rpc)
+{
+ 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);
+ NetconConnection *c = client->getConnectionByTheirFD(connect_rpc->__fd);
+
+ if(c != NULL) {
+ lwipstack->tcp_sent(c->pcb, nc_sent); // FIXME: Move?
+ lwipstack->tcp_recv(c->pcb, nc_recved);
+ lwipstack->tcp_err(c->pcb, nc_err);
+ lwipstack->tcp_poll(c->pcb, nc_poll, APPLICATION_POLL_FREQ);
+ lwipstack->tcp_arg(c->pcb, new Larg(this, c->sock));
+
+ int err = 0;
+ if((err = lwipstack->tcp_connect(c->pcb,&conn_addr,conn_port, nc_connected)) < 0)
+ {
+ fprintf(stderr, "handle_connect(): unable to connect\n");
+ // 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!
+ send_return_value(client, err);
+ }
+ // Everything seems to be ok, but we don't have enough info to retval
+ client->waiting_for_retval=true;
+ }
+ else {
+ fprintf(stderr, "could not locate PCB based on their fd\n");
+ }
+}
+
+/*
+ * 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.
+ * *
+ * @param Client that is making the RPC
+ * @param structure containing the data and parameters for this client's RPC
+ *
+ * TODO: Optimize write logic (should we stop using poll?)
+ */
+void NetconEthernetTap::handle_write(NetconConnection *c)
+{
+ if(c) {
+ int sndbuf = c->pcb->snd_buf;
+ float avail = (float)sndbuf;
+ float max = (float)TCP_SND_BUF;
+ float load = 1.0 - (avail / max);
+
+ if(load >= 0.9) {
+ return;
+ }
+ int sz, write_allowance = sndbuf < c->idx ? sndbuf : c->idx;
+ if(write_allowance > 0) {
+ int err = lwipstack->tcp_write(c->pcb, &c->buf, write_allowance, TCP_WRITE_FLAG_COPY);
+ if(err != ERR_OK) {
+ fprintf(stderr, "handle_write(): error while writing to PCB\n");
+ return;
+ }
+ else {
+ sz = (c->idx)-write_allowance;
+ if(sz) {
+ memmove(&c->buf, (c->buf+write_allowance), sz);
+ }
+ c->idx -= write_allowance;
+ return;
+ }
+ }
+ else {
+ fprintf(stderr, "handle_write(): LWIP stack full\n");
+ return;
+ }
+ }
+ else {
+ fprintf(stderr, "handle_write(): could not locate connection for this fd\n");
+ }
+}
+
+} // namespace ZeroTier
+
+#endif // ZT_ENABLE_NETCON
diff --git a/netcon/NetconEthernetTap.hpp b/netcon/NetconEthernetTap.hpp
new file mode 100644
index 00000000..3f8e724c
--- /dev/null
+++ b/netcon/NetconEthernetTap.hpp
@@ -0,0 +1,226 @@
+/*
+ * 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 "NetconService.hpp"
+#include "NetconUtilities.hpp"
+
+#include "netif/etharp.h"
+
+namespace ZeroTier {
+
+class NetconEthernetTap;
+
+/**
+ * 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 handle_bind(NetconClient *client, struct bind_st *bind_rpc);
+ void handle_listen(NetconClient *client, struct listen_st *listen_rpc);
+ void handle_retval(NetconClient *client, unsigned char* buf);
+ void handle_socket(NetconClient *client, struct socket_st* socket_rpc);
+ void handle_connect(NetconClient *client, struct connect_st* connect_rpc);
+ void handle_write(NetconConnection *c);
+
+ 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);
+
+ int send_return_value(NetconClient *client, int retval);
+
+ 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
+ NetconConnection *getConnectionByThisFD(int fd);
+ NetconConnection *getConnectionByPCB(struct tcp_pcb *pcb);
+ NetconClient *getClientByPCB(struct tcp_pcb *pcb);
+ void closeAllClients();
+ void closeConnection(NetconConnection *conn);
+
+ void closeClient(NetconClient *client);
+
+ Phy<NetconEthernetTap *> _phy;
+ PhySocket *_unixListenSocket;
+
+ std::vector<NetconClient*> clients;
+ 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;
+};
+
+
+/*------------------------------------------------------------------------------
+------------------------ low-level Interface functions -------------------------
+------------------------------------------------------------------------------*/
+
+static err_t low_level_output(struct netif *netif, struct pbuf *p);
+
+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[ZT1_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));
+ //printf("low_level_output(): length = %d -- ethertype = %d\n", tot_len - sizeof(struct eth_hdr), Utils::ntoh((uint16_t)ethhdr->type));
+ return ERR_OK;
+}
+
+} // namespace ZeroTier
+
+#endif // ZT_ENABLE_NETCON
+
+#endif
diff --git a/netcon/NetconService.hpp b/netcon/NetconService.hpp
new file mode 100644
index 00000000..ab093b07
--- /dev/null
+++ b/netcon/NetconService.hpp
@@ -0,0 +1,148 @@
+/*
+ * 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 <sys/poll.h>
+#include <string>
+
+#include "../osdep/Phy.hpp"
+#include "NetconEthernetTap.hpp"
+
+#include "Intercept.h"
+#include "LWIPStack.hpp"
+
+#ifndef _NETCON_SERVICE_HPP
+#define _NETCON_SERVICE_HPP
+
+using namespace std;
+
+namespace ZeroTier {
+
+ enum NetconConnectionType { RPC, BUFFER };
+
+ class NetconEthernetTap;
+
+ /*
+ * A helper class for passing a reference to _phy to LWIP callbacks as a "state"
+ */
+ class Larg
+ {
+ public:
+ NetconEthernetTap *tap;
+ PhySocket *sock;
+ Larg(NetconEthernetTap *_tap, PhySocket *_sock) : tap(_tap), sock(_sock) {}
+ };
+
+ // prototypes
+ class NetconClient;
+ class NetconConnection;
+
+ /*
+ * A data connection, any number of these may be associated with a NetconClient
+ */
+ class NetconConnection
+ {
+ public:
+ int their_fd;
+ unsigned char buf[DEFAULT_READ_BUFFER_SIZE];
+ int idx;
+ NetconConnectionType type;
+ struct tcp_pcb *pcb;
+ PhySocket *sock;
+ NetconClient *owner;
+
+ NetconConnection(NetconConnectionType type, PhySocket *sock) : type(type), sock(sock) {}
+ };
+
+ /*
+ * A "harnessed" client with associated rpc and data connections.
+ */
+ class NetconClient
+ {
+ public:
+ vector<NetconConnection*> connections;
+
+ int tid;
+ bool waiting_for_retval;
+ NetconConnection *rpc;
+ NetconConnection* unmapped_conn;
+
+ NetconConnection *addConnection(NetconConnectionType type, PhySocket *sock)
+ {
+ NetconConnection *new_conn = new NetconConnection(type, sock);
+ new_conn->owner = this;
+ return new_conn;
+ }
+
+ // Check data and RPC connections
+ NetconConnection *getConnection(PhySocket *sock)
+ {
+ if(sock && sock == rpc->sock) {
+ return rpc;
+ }
+ for(size_t i=0; i<connections.size(); i++) {
+ if(sock == connections[i]->sock) return connections[i];
+ }
+ return NULL;
+ }
+
+ //
+ NetconConnection *getConnectionByTheirFD(int fd)
+ {
+ for(size_t i=0; i<connections.size(); i++) {
+ if(connections[i]->their_fd == fd) return connections[i];
+ }
+ return NULL;
+ }
+
+ //
+ NetconConnection *getConnectionByPCB(struct tcp_pcb *pcb)
+ {
+ for(size_t i=0; i<connections.size(); i++) {
+ if(connections[i]->pcb == pcb) return connections[i];
+ }
+ return NULL;
+ }
+
+ NetconConnection *containsPCB(struct tcp_pcb *pcb)
+ {
+ for(size_t i=0; i<connections.size(); i++) {
+ if(connections[i]->pcb == pcb) return connections[i];
+ }
+ return NULL;
+ }
+
+ void removeConnection(PhySocket *sock)
+ {
+ for(size_t i=0; i<connections.size(); i++) {
+ if(connections[i]->sock == sock)
+ connections.erase(connections.begin() + i);
+ }
+ }
+ };
+} // namespace ZeroTier
+
+#endif
diff --git a/netcon/NetconUtilities.cpp b/netcon/NetconUtilities.cpp
new file mode 100644
index 00000000..584c4944
--- /dev/null
+++ b/netcon/NetconUtilities.cpp
@@ -0,0 +1,165 @@
+
+/*
+ * 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 <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/socket.h>
+
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip_frag.h"
+
+#ifndef _NETCON_UTILITIES_CPP
+#define _NETCON_UTILITIES_CPP
+
+namespace ZeroTier
+{
+ /*
+ 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;
+ }
+*/
+
+/*
+ ip_addr_t ip_addr_sin(register struct sockaddr_in *sin) {
+ ip_addr_t ip;
+ *((struct sockaddr_in*) &ip) = *sin;
+ return ip;
+ }
+*/
+ // Functions used to pass file descriptors between processes
+
+ 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;
+ }
+}
+
+#endif
diff --git a/netcon/NetconUtilities.hpp b/netcon/NetconUtilities.hpp
new file mode 100644
index 00000000..ac6042e6
--- /dev/null
+++ b/netcon/NetconUtilities.hpp
@@ -0,0 +1,38 @@
+
+/*
+ * 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 _NETCON_UTILITIES_H
+#define _NETCON_UTILITIES_H
+
+namespace ZeroTier
+{
+ ip_addr_t ip_addr_sin(register struct sockaddr_in *sin);
+ ssize_t sock_fd_write(int sock, int fd);
+ ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd);
+}
+#endif