diff options
Diffstat (limited to 'netcon')
-rwxr-xr-x | netcon/Intercept.h | 222 | ||||
-rw-r--r-- | netcon/LWIPStack.hpp | 193 | ||||
-rw-r--r-- | netcon/NetconEthernetTap.cpp | 906 | ||||
-rw-r--r-- | netcon/NetconEthernetTap.hpp | 226 | ||||
-rw-r--r-- | netcon/NetconService.hpp | 148 | ||||
-rw-r--r-- | netcon/NetconUtilities.cpp | 165 | ||||
-rw-r--r-- | netcon/NetconUtilities.hpp | 38 |
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,ðhdr,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 |