diff options
author | Joseph Henry <joseph.henry@zerotier.com> | 2015-09-10 13:56:01 -0400 |
---|---|---|
committer | Joseph Henry <joseph.henry@zerotier.com> | 2015-09-10 13:56:01 -0400 |
commit | 750352836f72c5dc0136b6aae96ec28f8fe356cb (patch) | |
tree | 934d15eec15cf2c1bcf4bb60970e274d07ddd57f /netcon | |
parent | a43c3fbf2e03f99c51383123423d86656ac252bf (diff) | |
download | infinitytier-750352836f72c5dc0136b6aae96ec28f8fe356cb.tar.gz infinitytier-750352836f72c5dc0136b6aae96ec28f8fe356cb.zip |
initial commit
Diffstat (limited to 'netcon')
-rwxr-xr-x | netcon/Intercept.h | 219 | ||||
-rw-r--r-- | netcon/LWIPStack.hpp | 178 | ||||
-rw-r--r-- | netcon/NetconEthernetTap.cpp | 528 | ||||
-rw-r--r-- | netcon/NetconEthernetTap.hpp | 29 | ||||
-rw-r--r-- | netcon/NetconService.h | 345 | ||||
-rw-r--r-- | netcon/NetconUtilities.cpp | 132 | ||||
-rw-r--r-- | netcon/NetconUtilities.hpp | 11 | ||||
-rwxr-xr-x | netcon/liblwip.so | bin | 0 -> 224696 bytes |
8 files changed, 1436 insertions, 6 deletions
diff --git a/netcon/Intercept.h b/netcon/Intercept.h new file mode 100755 index 00000000..97075dcb --- /dev/null +++ b/netcon/Intercept.h @@ -0,0 +1,219 @@ +/* + * 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..14edd1d5 --- /dev/null +++ b/netcon/LWIPStack.hpp @@ -0,0 +1,178 @@ +/* + * 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/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 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 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 + + + +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 (*netif_set_default)(NETIF_SET_DEFAULT_SIG); + //struct netif* (*netif_add)(NETIF_ADD_SIG); + //void (*netif_set_up)(NETIF_SET_UP_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); + + + + 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"); + //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"); + 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"); + } +}; + +#endif diff --git a/netcon/NetconEthernetTap.cpp b/netcon/NetconEthernetTap.cpp index bc4cb928..dd788813 100644 --- a/netcon/NetconEthernetTap.cpp +++ b/netcon/NetconEthernetTap.cpp @@ -36,6 +36,19 @@ #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 "LWIPStack.hpp" +#include "NetconService.h" +#include "Intercept.h" +#include "NetconUtilities.hpp" + +#define APPLICATION_POLL_FREQ 1 + namespace ZeroTier { NetconEthernetTap::NetconEthernetTap( @@ -61,10 +74,17 @@ NetconEthernetTap::NetconEthernetTap( Utils::snprintf(sockPath,sizeof(sockPath),"/tmp/.ztnc_%.16llx",(unsigned long long)nwid); _dev = sockPath; + lwipstack = new LWIPStack("/root/dev/netcon/liblwip.so"); + if(!lwipstack) // TODO double check this check + throw std::runtime_error("unable to load lwip lib."); + lwipstack->lwip_init(); + nc_service = new NetconService(lwipstack, sockPath); // Netcon Service + _unixListenSocket = _phy.unixListen(sockPath,(void *)this); if (!_unixListenSocket) throw std::runtime_error(std::string("unable to bind to ")+sockPath); - + else + _unixListenSocket.uptr = (void*) new NetconSocket(_unixListenSocket.sock, NetconSocketType.RPC); _thread = Thread::start(this); } @@ -95,6 +115,7 @@ bool NetconEthernetTap::addIp(const InetAddress &ip) std::sort(_ips.begin(),_ips.end()); // TODO: alloc IP in LWIP + //netif_set_addr(netif, ipaddr, netmask, gw); } } @@ -140,12 +161,61 @@ void NetconEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,s void NetconEthernetTap::threadMain() throw() { - while (_run) { - unsigned long pollTimeout = 500; - - // TODO: compute timeout from LWIP stuff + static ip_addr_t ipaddr, netmask, gw; + char ip_str[16] = {0}, nm_str[16] = {0}, gw_str[16] = {0}; + IP4_ADDR(&gw, 192,168,0,1); + IP4_ADDR(&netmask, 255,255,255,0); + IP4_ADDR(&ipaddr, 192,168,0,2); + strncpy(ip_str, lwipstack->ipaddr_ntoa(&ipaddr), sizeof(ip_str)); + strncpy(nm_str, lwipstack->ipaddr_ntoa(&netmask), sizeof(nm_str)); + strncpy(gw_str, lwipstack->ipaddr_ntoa(&gw), sizeof(gw_str)); + + unsigned long tcp_time = ARP_TMR_INTERVAL / 5000; + unsigned long ipreass_time = TCP_TMR_INTERVAL / 1000; + unsigned long etharp_time = IP_TMR_INTERVAL / 1000; + unsigned long prev_tcp_time = 0; + unsigned long prev_etharp_time = 0; + unsigned long curr_time; + unsigned long since_tcp; + unsigned long since_etharp; + + struct timeval tv; + struct timeval tv_sel; - _phy.poll(pollTimeout); + while (_run) { + gettimeofday(&tv, NULL); + curr_time = (unsigned long)(tv.tv_sec) * 1000 + (unsigned long)(tv.tv_usec) / 1000; + + since_tcp = curr_time - prev_tcp_time; + since_etharp = curr_time - prev_etharp_time; + int min_time = min(since_tcp, since_etharp) * 1000; // usec + + if(since_tcp > tcp_time) + { + prev_tcp_time = curr_time+1; + lwipstack->tcp_tmr(); + } + + if(since_etharp > etharp_time) + { + prev_etharp_time = curr_time; + lwipstack->etharp_tmr(); + } + // should be set every time since tv_sel is modified after each select() call + tv_sel.tv_sec = 0; + tv_sel.tv_usec = min_time; + + // Assemble/copy our fd_sets to poll on + if(nc_service->possible_state_change) { + nc_service->assemble_fd_sets(); + } + memcpy(&(nc_service->fdset), &(nc_service->cached_fdset), sizeof(nc_service->cached_fdset)); + memcpy(&(nc_service->exfdset), &(nc_service->cached_exfdset), sizeof(nc_service->cached_exfdset)); + memcpy(&(nc_service->alltypes), &(nc_service->cached_alltypes), sizeof(nc_service->cached_alltypes)); + nc_service->maxfd = nc_service->cached_maxfd; + nc_service->sz = nc_service->cached_sz; + + _phy.poll(min_time * 1000); // conversion from usec to millisec, TODO: double check } // TODO: cleanup -- destroy LWIP state, kill any clients, unload .so, etc. @@ -153,6 +223,7 @@ void NetconEthernetTap::threadMain() // 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) {} @@ -169,12 +240,457 @@ void NetconEthernetTap::phyOnUnixClose(PhySocket *sock,void **uptr) void NetconEthernetTap::phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len) { + Phy<NetconEthernetTap*>::PhySocketImpl &sws = *(reinterpret_cast<Phy<NetconEthernetTap*>::PhySocketImpl *>(sock)); + + int r; + nc_service->possible_state_change = true; + if(sws->uptr->type == NetconSocketType.BUFFER) { + NetconConnection* c = nc_service->get_connection_by_buf_sock(sws->sock); + if(c) { + if(c->idx < DEFAULT_READ_BUFFER_SIZE) { + //tcp_output(c->pcb); + if((r = read(sws->sock, (&c->buf)+c->idx, DEFAULT_READ_BUFFER_SIZE-(c->idx))) > 0) { + c->idx += r; + handle_write(c); + } + } + } + else { + //dwr(-1, "can't find connection for this fd: %d\n", ns->allfds[i].fd); + } + } + if(sws->uptr->type == NetconSocketType.RPC) + { + NetconIntercept *h = nc_service->get_intercept_by_rpc(sws->sock); + switch(data[0]) + { + case RPC_SOCKET: + struct socket_st socket_rpc; + memcpy(&socket_rpc, &data[1], sizeof(struct socket_st)); + h->tid = socket_rpc.__tid; + //dwr(h->tid,"__RPC_SOCKET\n"); + handle_socket(h, &socket_rpc); + break; + case RPC_LISTEN: + struct listen_st listen_rpc; + memcpy(&listen_rpc, &data[1], sizeof(struct listen_st)); + h->tid = listen_rpc.__tid; + //dwr(h->tid,"__RPC_LISTEN\n"); + handle_listen(h, &listen_rpc); + break; + case RPC_BIND: + struct bind_st bind_rpc; + memcpy(&bind_rpc, &data[1], sizeof(struct bind_st)); + h->tid = bind_rpc.__tid; + //dwr(h->tid,"__RPC_BIND\n"); + handle_bind(h, &bind_rpc); + break; + case RPC_KILL_INTERCEPT: + //dwr(h->tid,"__RPC_KILL_INTERCEPT\n"); + handle_kill_intercept(h); + break; + case RPC_CONNECT: + struct connect_st connect_rpc; + memcpy(&connect_rpc, &data[1], sizeof(struct connect_st)); + h->tid = connect_rpc.__tid; + //dwr("__RPC_CONNECT\n"); + handle_connect(h, &connect_rpc); + break; + case RPC_FD_MAP_COMPLETION: + //dwr("__RPC_FD_MAP_COMPLETION\n"); + handle_retval(h, data); + break; + default: + break; + } + } } void NetconEthernetTap::phyOnUnixWritable(PhySocket *sock,void **uptr) { } +void NetconEthernetTap::handle_kill_intercept(NetconIntercept* h) { + nc_service->possible_state_change = true; + // Close all owned connections + for(size_t i=0; i<h->owned_connections.size(); i++) { + nc_close(h->owned_connections[i]->pcb); + close(h->owned_connections[i]->our_fd); + } + // Close RPC socketpair for this intercept + close(h->rpc); + nc_service->remove_intercept(h); +} + +int NetconEthernetTap::send_return_value(NetconIntercept *h, int retval) +{ + if(!h->waiting_for_retval){ + //dwr(h->tid, "ERROR: intercept isn't waiting for return value. Why are we here?\n"); + return 0; + } + char retmsg[4]; + memset(&retmsg, '\0', sizeof(retmsg)); + retmsg[0]=RPC_RETVAL; + memcpy(&retmsg[1], &retval, sizeof(retval)); + int n = write(h->rpc, &retmsg, sizeof(retmsg)); + + if(n > 0) { + /* signal that we've satisfied this requirement */ + h->waiting_for_retval = false; + } + else { + /* in the event that we can't write to the intercept's RPC, we + should assume that it has failed to connect */ + //dwr(h->tid, "ERROR: unable to send return value to the intercept\n"); + //dwr(h->tid, "removing intercept.\n"); + nc_service->remove_intercept(h); + } + return n; +} + +/*------------------------------------------------------------------------------ +--------------------------------- LWIP callbacks ------------------------------- +------------------------------------------------------------------------------*/ + +err_t NetconEthernetTap::nc_poll(void* arg, struct tcp_pcb *tpcb) +{ + NetconConnection* c = nc_service->get_connection_by_buf_sock((intptr_t)arg); + if(c) + handle_write(c); + return ERR_OK; +} + +err_t NetconEthernetTap::nc_accept(void* arg, struct tcp_pcb *newpcb, err_t err) +{ + nc_service->possible_state_change = true; + NetconConnection *c = nc_service->get_connection_by_buf_sock((intptr_t)arg); + if(c && c->owner) { + // Generate new socketpair and Connection. Use newly-allocated PCB + int fd[2]; + socketpair(PF_LOCAL, SOCK_STREAM, 0, fd); + NetconConnection *new_connection = nc_service->add_connection(c->owner, c->owner->tid, fd[0], -1, newpcb); + //dwr(c->owner->tid, "socketpair { fd[0]=%d, fd[1]=%d }\n", fd[0], fd[1]); + if(new_connection == NULL) { + //printf("error adding new connection\n"); + return -1; + } + new_connection->owner->unmapped_conn = new_connection; + // write byte to let accept call know we have a new connection + int n = write(c->our_fd, "z", 1); + if(n > 0) { + //dwr(c->owner->tid, "sending socketpair fd... %d\n", fd[1]); + sock_fd_write(c->owner->rpc, fd[1]); + } + else { + //dwr(c->owner->tid, "nc_accept() - unknown error writing signal byte to listening socket\n"); + return -1; + } + // Set PCB-specific callbacks + //dwr(c->owner->tid, "tcp_arg(pcb, %d)\n", new_connection->our_fd); + lwipstack->tcp_arg(newpcb, (void*)(intptr_t)(new_connection->our_fd)); + lwipstack->tcp_recv(newpcb, nc_recved); + lwipstack->tcp_err(newpcb, nc_err); + lwipstack->tcp_sent(newpcb, nc_sent); + lwipstack->tcp_poll(newpcb, nc_poll, APPLICATION_POLL_FREQ); + tcp_accepted(c->pcb); + return ERR_OK; + } + else { + //dwr("can't locate Connection object for PCB\n"); + } + return -1; +} + +err_t NetconEthernetTap::nc_recved(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) +{ + int n; + struct pbuf* q = p; + NetconConnection *c = nc_service->get_connection_by_buf_sock((intptr_t)arg); + if(c) { + //dwr(c->owner->tid, "nc_recved(%d)\n", (intptr_t)arg); + } + else { + //dwr(-1, "nc_recved(%d): unable to locate connection\n", (intptr_t)arg); + return ERR_OK; // ? + } + if(p == NULL) { + //dwr(c->owner->tid, "nc_recved() = %s\n", lwiperror(err)); + if(c) + //dwr(c->owner->tid, "nc_recved()\n"); + if(c) { + //dwr(c->owner->tid, "closing connection\n"); + nc_close(tpcb); + close(c->our_fd); /* TODO: Check logic */ + nc_service->remove_connection(c); + nc_service->possible_state_change = true; + } + else { + //dwr(-1, "can't locate connection via (arg)\n"); + } + return err; + } + q = p; + while(p != NULL) { // Cycle through pbufs and write them to the socket + //dwr(c->owner->tid, "writing data to mapped sock (%d)\n", c->our_fd); + if(p->len <= 0) + break; // ? + if((n = write(c->our_fd, p->payload, p->len)) > 0) { + if(n < p->len) { + //dwr(c->owner->tid, "ERROR: unable to write entire pbuf to buffer\n"); + } + lwipstack->tcp_recved(tpcb, n); + } + else { + //dwr(c->owner->tid, "ERROR: No data written to intercept buffer.\n"); + } + p = p->next; + } + lwipstack->pbuf_free(q); /* free pbufs */ + return ERR_OK; +} + +void NetconEthernetTap::nc_err(void *arg, err_t err) +{ + nc_service->possible_state_change = true; + NetconConnection *c = nc_service->get_connection_by_this_fd((intptr_t)arg); + if(c) { + //dwr(c->owner->tid, "nc_err: %s\n", lwiperror(err)); + nc_service->remove_connection(c); + //tcp_close(c->pcb); + //dwr(-1, "connection removed.\n"); + } + else { + //dwr("ERROR: can't locate connection object for PCB.\n"); + } + //nc_service->print_fd_set(); +} + +void NetconEthernetTap::nc_close(struct tcp_pcb* tpcb) +{ + nc_service->possible_state_change = true; + + NetconConnection *c = nc_service->get_connection_by_pcb(tpcb); + if(c) { + //dwr(c->owner->tid, "nc_close(): closing connection (their=%d, our=%d)\n", c->their_fd, c->our_fd); + } + else { + //dwr(-1, "nc_close(): closing connection\n"); + } + lwipstack->tcp_arg(tpcb, NULL); + lwipstack->tcp_sent(tpcb, NULL); + lwipstack->tcp_recv(tpcb, NULL); + lwipstack->tcp_err(tpcb, NULL); + lwipstack->tcp_poll(tpcb, NULL, 0); + int err = lwipstack->tcp_close(tpcb); + //dwr(-1, "tcp_close: %s\n", lwiperror(err)); +} + +err_t NetconEthernetTap::nc_send(struct tcp_pcb *tpcb) +{ + return ERR_OK; +} + +err_t NetconEthernetTap::nc_sent(void* arg, struct tcp_pcb *tpcb, u16_t len) +{ + NetconConnection *c = nc_service->get_connection_by_pcb(tpcb); + if(c) + c->data_sent += len; + return len; +} + +err_t NetconEthernetTap::nc_connected(void *arg, struct tcp_pcb *tpcb, err_t err) +{ + nc_service->possible_state_change = true; + NetconIntercept *h = nc_service->get_intercept_by_pcb(tpcb); + if(h) { + //dwr(h->tid, "nc_connected()\n"); + send_return_value(h,err); + } + return err; +} + +/*------------------------------------------------------------------------------ +----------------------------- RPC Handler functions ---------------------------- +------------------------------------------------------------------------------*/ + +void NetconEthernetTap::handle_bind(NetconIntercept *h, struct bind_st *bind_rpc) +{ + // FIXME: Is this hack still needed? + struct sockaddr_in *connaddr; + connaddr = (struct sockaddr_in *) &bind_rpc->addr; + int conn_port = lwipstack->ntohs(connaddr->sin_port); + ip_addr_t conn_addr; + IP4_ADDR(&conn_addr, 192,168,0,2); + + int ip = connaddr->sin_addr.s_addr; + unsigned char bytes[4]; + bytes[0] = ip & 0xFF; + bytes[1] = (ip >> 8) & 0xFF; + bytes[2] = (ip >> 16) & 0xFF; + bytes[3] = (ip >> 24) & 0xFF; + //dwr(h->tid, "binding to: %d.%d.%d.%d\n", bytes[0], bytes[1], bytes[2], bytes[3]); + + NetconConnection *c = nc_service->get_connection_by_that_fd(h, 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) { + //dwr(h->tid, "ERROR: while binding to addr/port\n"); + } + else { + //dwr(h->tid, "bind successful (fd=%d)\n", bind_rpc->sockfd); + } + } + else { + //dwr(h->tid, "PCB not in CLOSED state. Ignoring BIND request.\n"); + } + } + else { + //dwr(h->tid, "can't locate connection for PCB (%d)\n", bind_rpc->sockfd); + } +} + +void NetconEthernetTap::handle_listen(NetconIntercept *h, struct listen_st *listen_rpc) +{ + NetconConnection *c = nc_service->get_connection_by_that_fd(h, listen_rpc->sockfd); + if(c) { + //dwr(c->owner->tid, "listen(%d, backlog=%d) from (tid=%d)\n", listen_rpc->sockfd, listen_rpc->backlog, listen_rpc->__tid); + if(c->pcb->state == LISTEN) { + //dwr(c->owner->tid, "PCB is already in listening state.\n"); + return; + } + struct tcp_pcb* listening_pcb = lwipstack->tcp_listen(c->pcb); + if(listening_pcb != NULL) { + //dwr(h->tid, "created new listening PCB\n"); + c->pcb = listening_pcb; + lwipstack->tcp_accept(listening_pcb, nc_accept); + //dwr(h->tid, "tcp_arg(pcb, %d)\n", (void*)(intptr_t)c->our_fd); + lwipstack->tcp_arg(listening_pcb, (void*)(intptr_t)c->our_fd); + h->waiting_for_retval=true; + } + else { + //dwr(h->tid, "unable to allocate memory for new listening PCB\n"); + } + } + else { + //dwr(h->tid, "can't locate connection for PCB (%d)\n", listen_rpc->sockfd); + } +} + +void NetconEthernetTap::handle_retval(NetconIntercept *h, unsigned char* buf) +{ + if(h->unmapped_conn != NULL) { + memcpy(&(h->unmapped_conn->their_fd), &buf[1], sizeof(int)); + //dwr(h->tid, "intercept_fd(%d) -> service_fd(%d)\n", + // h->unmapped_conn->their_fd, h->unmapped_conn->our_fd); + h->unmapped_conn = NULL; + } +} + +void NetconEthernetTap::handle_socket(NetconIntercept *h, struct socket_st* socket_rpc) +{ + struct tcp_pcb *pcb = lwipstack->tcp_new(); + if(pcb != NULL) { + int fd[2]; + socketpair(PF_LOCAL, SOCK_STREAM, 0, fd); + NetconConnection* new_connection = nc_service->add_connection(h, h->tid, fd[0], -1, pcb); + //dwr(h->tid, "socketpair { fd[0]=%d, fd[1]=%d }\n", fd[0], fd[1]); + //dwr(h->tid, "connections = %d\n", nc_service->connections.size()); + //dwr(h->tid, "added new socket entry\n"); + //dwr(h->tid, "sending socketpair buffer fd... %d\n", fd[1]); + sock_fd_write(h->rpc, fd[1]); + h->unmapped_conn = new_connection; + } + else { + //dwr(h->tid, "ERROR: Memory not available for new PCB\n"); + } +} + +void NetconEthernetTap::handle_connect(NetconIntercept *h, struct connect_st* connect_rpc) +{ + // FIXME: Parse out address information -- Probably a more elegant way to do this + 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 = nc_service->get_connection_by_that_fd(h, connect_rpc->__fd); + //print_ip(connaddr->sin_addr.s_addr); + + if(c!= NULL) { + //dwr(-1, "connect(): TCP_SNDBUF = %d\n", tcp_sndbuf(nc->pcb)); + lwipstack->tcp_sent(c->pcb, nc_sent); // FIXME: Move? + lwipstack->tcp_recv(c->pcb, nc_recved); + lwipstack->tcp_err(c->pcb, ZeroTier::NetconEthernetTap::nc_err); + lwipstack->tcp_poll(c->pcb, nc_poll, APPLICATION_POLL_FREQ); + lwipstack->tcp_arg(c->pcb,(void*)(intptr_t)c->our_fd); + + int err = 0; + if((err = lwipstack->tcp_connect(c->pcb,&conn_addr,conn_port, nc_connected)) < 0) + { + // dwr(h->tid, "tcp_connect() = %s\n", lwiperror(err)); + // 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(h, err); + } + // Everything seems to be ok, but we don't have enough info to retval + h->waiting_for_retval=true; + } + else { + //dwr(h->tid, "ERROR: could not locate PCB based on their_fd (%d)", connect_rpc->__fd); + } +} + +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 write_allowance = sndbuf < c->idx ? sndbuf : c->idx; + int sz; + + if(write_allowance > 0) + { + // FIXME: Copying data is expensive, we actually want TCP_WRITE_FLAG_MORE! + int err = lwipstack->tcp_write(c->pcb, &c->buf, write_allowance, TCP_WRITE_FLAG_COPY); + if(err != ERR_OK) { + //dwr(c->owner->tid, "ERROR(%d): while writing to PCB, %s\n", err, lwiperror(err)); + return; + } + else { + sz = (c->idx)-write_allowance; + if(sz) { + //printf(" w = %d\n\n", c->written); + //printf("sz = %d\n", (c->idx)-write_allowance); + memmove(&c->buf, (c->buf+write_allowance), sz); + } + c->idx -= write_allowance; + c->data_sent += write_allowance; + return; + } + } + else { + //dwr(c->owner->tid, "ERROR: lwIP stack full.\n"); + return; + } + } + else { + //dwr("ERROR: could not locate connection for this fd\n"); + } +} + } // namespace ZeroTier #endif // ZT_ENABLE_NETCON diff --git a/netcon/NetconEthernetTap.hpp b/netcon/NetconEthernetTap.hpp index 1ecbdcaf..5ae3b19a 100644 --- a/netcon/NetconEthernetTap.hpp +++ b/netcon/NetconEthernetTap.hpp @@ -44,6 +44,8 @@ #include "../osdep/Thread.hpp" #include "../osdep/Phy.hpp" +#include "NetconService.h" + namespace ZeroTier { class NetconEthernetTap; @@ -93,12 +95,39 @@ private: void phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len); void phyOnUnixWritable(PhySocket *sock,void **uptr); + void handle_kill_intercept(NetconIntercept* h); + int send_return_value(NetconIntercept *h, int retval); + + // For LWIP Callbacks + err_t nc_poll(void* arg, struct tcp_pcb *tpcb); + err_t nc_accept(void* arg, struct tcp_pcb *newpcb, err_t err); + err_t nc_recved(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); + void nc_err(void *arg, err_t err); + void nc_close(struct tcp_pcb* tpcb); + err_t nc_send(struct tcp_pcb *tpcb); + err_t nc_sent(void* arg, struct tcp_pcb *tpcb, u16_t len); + err_t nc_connected(void *arg, struct tcp_pcb *tpcb, err_t err); + + // RPC handlers (from NetconIntercept) + void handle_bind(NetconIntercept *h, struct bind_st *bind_rpc); + void handle_listen(NetconIntercept *h, struct listen_st *listen_rpc); + void handle_retval(NetconIntercept *h, unsigned char* buf); + void handle_socket(NetconIntercept *h, struct socket_st* socket_rpc); + void handle_connect(NetconIntercept *h, struct connect_st* connect_rpc); + void handle_write(NetconConnection *c); + void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int); void *_arg; + // Logging helper + + Phy<NetconEthernetTap *> _phy; PhySocket *_unixListenSocket; + LWIPStack *lwipstack; + NetconService *nc_service; + uint64_t _nwid; Thread _thread; std::string _homePath; diff --git a/netcon/NetconService.h b/netcon/NetconService.h new file mode 100644 index 00000000..08250fdb --- /dev/null +++ b/netcon/NetconService.h @@ -0,0 +1,345 @@ +/* + * 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 "Intercept.h" +#include "LWIPStack.hpp" + +#ifndef _NETCON_SERVICE_H_ +#define _NETCON_SERVICE_H_ + +using namespace std; + +enum NetconSocketType { RPC, BUFFER }; + +class NetconIntercept; +class NetconConnection; +class NetconSocket; + +class NetconSocket{ + int fd; + NetconSocketType type; +}; + + +class NetconConnection +{ +public: + + int tid; + int our_fd; + int their_fd; /* what the user app sees -- and what they will send us */ + struct tcp_pcb *pcb; /* for handling lwIP calls/data */ + bool unacked; + + unsigned char *write_buf; /* we need this reference so we can grab data from the buffer during a lwip-poll callback */ + long unsigned int write_count; + long unsigned int write_total; + + unsigned char buf[DEFAULT_READ_BUFFER_SIZE]; + int idx; + + unsigned long data_acked; + unsigned long data_sent; + unsigned long data_read; + unsigned long data_recvd; + + NetconIntercept* owner; + + NetconConnection(NetconIntercept* owner, + int tid, + int our_fd, + int their_fd, + struct tcp_pcb *pcb) + : + write_total(0), + write_count(0), + write_buf(NULL), + owner(owner), + tid(tid), + our_fd(our_fd), + their_fd(their_fd), + pcb(pcb) + {} +}; + +class NetconIntercept +{ +public: + + // State (this needs to be evaluated) + NetconConnection *unmapped_conn; + bool waiting_for_retval; + + // Connections created for this intercept + vector<NetconConnection*> owned_connections; + + int tid; /* just for uniqueness */ + int rpc; + + NetconIntercept(int tid, int rpc) + : + waiting_for_retval(false), + unmapped_conn(NULL), + tid(tid), + rpc(rpc) + {} +}; + + +#define POLL_SZ ZT_PHY_MAX_SOCKETS+(ZT_PHY_MAX_INTERCEPTS*2)+2 +class NetconService +{ +public: + + LWIPStack *ls; + + // TODO: shall replace with map + vector<NetconIntercept*> intercepts; + vector<NetconConnection*> connections; + + /* fd_sets for main I/O polling */ + fd_set fdset; + fd_set exfdset; + int maxfd; + size_t sz; + int tapfd; + + // Sets of file descriptors we will poll() on + int default_rpc_pipe; + struct pollfd allfds[POLL_SZ]; + int alltypes[POLL_SZ]; + + /* cached fd_sets */ + bool possible_state_change; + fd_set cached_fdset; + fd_set cached_exfdset; + int cached_alltypes[POLL_SZ]; + int cached_maxfd; + int cached_sz; + + NetconService(LWIPStack *ls, string _handle) + : + ls(ls), + maxfd(0), + sz(0), + possible_state_change(true) + {} + + /* Assemble single poll list */ + void assemble_fd_sets() + { + sz = 2 + connections.size() + intercepts.size(); + // initialize + for(size_t i=0; i<sz; i++){ + allfds[i].fd = 0; + allfds[i].events = 0; + } + int idx = 0; + // default rpc fd + allfds[0].fd = default_rpc_pipe; + allfds[0].events = POLLIN; + alltypes[0] = 1; + // netif fd + allfds[1].fd=tapfd; + allfds[1].events = POLLIN; + alltypes[1] = 2; + // buffers + for(size_t i=0; i<connections.size(); i++) { + idx = i + 2; + allfds[idx].fd = connections[i]->our_fd; + allfds[idx].events = POLLIN; + alltypes[idx] = 3; + } + // established connections + for(size_t i=0; i<intercepts.size(); i++) { + idx = i + connections.size() + 2; + allfds[idx].fd = intercepts[i]->rpc; + allfds[idx].events = POLLIN; + alltypes[idx] = 4; + } + FD_ZERO(&fdset); + FD_ZERO(&exfdset); + // populate master fd_set + for(size_t i=0; i<sz; i++) { + FD_SET(allfds[i].fd, &fdset); + FD_SET(allfds[i].fd, &exfdset); + } + // get maxfd + for(size_t i=0; i<sz; i++) + { + if(allfds[i].fd > maxfd) + maxfd = allfds[i].fd; + } + // cache copy of valid fd_set + possible_state_change = false; + memcpy(&cached_fdset, &fdset, sizeof(fdset)); + memcpy(&cached_exfdset, &exfdset, sizeof(exfdset)); + memcpy(&cached_alltypes, &alltypes, sizeof(alltypes)); + cached_maxfd = maxfd; + cached_sz = sz; + } + + + NetconConnection *get_connection_by_pcb(struct tcp_pcb *pcb) { + for(size_t i=0; i<connections.size(); i++) { + if(connections[i]->pcb == pcb) { + return connections[i]; + } + } + return NULL; + } + + NetconConnection *get_connection_by_buf_sock(int fd) { + for(size_t i=0; i<connections.size(); i++) { + if(connections[i]->our_fd==fd) { + return connections[i]; + } + } + return NULL; + } + + // FIXME: This will be a common operation and thus should be done via + // some sort of hashing, not iterative lookup. + NetconIntercept *get_intercept_by_pcb(struct tcp_pcb* pcb) { + for(size_t i=0; i<connections.size(); i++) { + if(connections[i]->pcb==pcb) { + return connections[i]->owner; + } + } + return NULL; + } + + + NetconIntercept *get_intercept_by_rpc(int af) { + for(size_t i=0; i<intercepts.size(); i++) { + if(intercepts[i]->rpc==af) { + return intercepts[i]; + } + } + return NULL; + } + + NetconConnection *get_connection_by_that_fd(NetconIntercept* h, int fd) + { + for(size_t i=0; i<h->owned_connections.size(); i++) { + if(h->owned_connections[i]->their_fd==fd) { + return h->owned_connections[i]; + } + } + return NULL; + } + + NetconConnection *get_connection_by_this_fd(int fd) + { + for(size_t i=0; i<connections.size(); i++) { + if(connections[i]->our_fd==fd) { + return connections[i]; + } + } + return NULL; + } + + NetconConnection *get_connection_by_that_fd(int fd) + { + for(size_t i=0; i<connections.size(); i++) { + if(connections[i]->their_fd==fd) { + return connections[i]; + } + } + return NULL; + } + + NetconConnection *add_connection(NetconIntercept* owner, + int tid, + int our_fd, + int their_fd, + struct tcp_pcb* pcb) + { + possible_state_change = true; + if(connections.size() >= ZT_PHY_MAX_SOCKETS) { + return NULL; + } + NetconConnection *new_conn = new NetconConnection(owner, tid, our_fd, their_fd, pcb); + connections.push_back(new_conn); + + NetconIntercept *h; + for(size_t i=0; i<intercepts.size(); i++) { + if(intercepts[i]->tid == tid) { + h = intercepts[i]; + } + } + if(h) + h->owned_connections.push_back(new_conn); + return new_conn; + } + + // Removes a Connection from the Service and updates poll lists + void remove_connection(NetconConnection *c) + { + possible_state_change = true; + for(size_t i=0; i<connections.size(); i++) { + if(c == connections[i]) { + close(connections[i]->our_fd); + ls->tcp_close(c->pcb); + delete c; + connections.erase(connections.begin() + i); + } + } + } + + int add_intercept(int listen_sock) { + possible_state_change = true; + int accept_socket = accept(listen_sock, NULL, NULL); + intercepts.push_back(new NetconIntercept(999, accept_socket)); + return accept_socket; + } + + + // Removes an Intercept from the Service + bool remove_intercept(NetconIntercept *h) + { + possible_state_change = true; + // remove all connections owned by this Intercept + for(size_t i=0; i<h->owned_connections.size(); i++) { + remove_connection(h->owned_connections[i]); + } + // find and remove Intercept + for(size_t i=0; i<intercepts.size(); i++) + { + if(h == intercepts[i]) { + delete intercepts[i]; + intercepts.erase(intercepts.begin() + i); + return true; + } + } + return false; + } +}; + +#endif diff --git a/netcon/NetconUtilities.cpp b/netcon/NetconUtilities.cpp new file mode 100644 index 00000000..8f501ce2 --- /dev/null +++ b/netcon/NetconUtilities.cpp @@ -0,0 +1,132 @@ + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/socket.h> + +#ifndef _NETCON_UTILITIES_CPP +#define _NETCON_UTILITIES_CPP + + + +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 sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd); + +ssize_t sock_fd_write(int sock, int fd) +{ + ssize_t size; + struct msghdr msg; + struct iovec iov; + char buf = '\0'; + int buflen = 1; + + union + { + struct cmsghdr cmsghdr; + char control[CMSG_SPACE(sizeof (int))]; + } cmsgu; + struct cmsghdr *cmsg; + + iov.iov_base = &buf; + iov.iov_len = buflen; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + if (fd != -1) { + msg.msg_control = cmsgu.control; + msg.msg_controllen = sizeof(cmsgu.control); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof (int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *((int *) CMSG_DATA(cmsg)) = fd; + } else { + msg.msg_control = NULL; + msg.msg_controllen = 0; + } + + size = sendmsg(sock, &msg, 0); + if (size < 0) + perror ("sendmsg"); + return size; +} + + +ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd) +{ + ssize_t size; + + if (fd) { + struct msghdr msg; + struct iovec iov; + union + { + struct cmsghdr cmsghdr; + char control[CMSG_SPACE(sizeof (int))]; + } cmsgu; + struct cmsghdr *cmsg; + + iov.iov_base = buf; + iov.iov_len = bufsize; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgu.control; + msg.msg_controllen = sizeof(cmsgu.control); + size = recvmsg (sock, &msg, 0); + if (size < 0) { + perror ("recvmsg"); + exit(1); + } + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { + if (cmsg->cmsg_level != SOL_SOCKET) { + fprintf (stderr, "invalid cmsg_level %d\n", + cmsg->cmsg_level); + exit(1); + } + if (cmsg->cmsg_type != SCM_RIGHTS) { + fprintf (stderr, "invalid cmsg_type %d\n", + cmsg->cmsg_type); + exit(1); + } + + *fd = *((int *) CMSG_DATA(cmsg)); + } else + *fd = -1; + } else { + size = read (sock, buf, bufsize); + if (size < 0) { + perror("read"); + exit(1); + } + } + return size; +} + +#endif diff --git a/netcon/NetconUtilities.hpp b/netcon/NetconUtilities.hpp new file mode 100644 index 00000000..56b0ed94 --- /dev/null +++ b/netcon/NetconUtilities.hpp @@ -0,0 +1,11 @@ + +#ifndef _NETCON_UTILITIES_H +#define _NETCON_UTILITIES_H + +ip_addr_t convert_ip(struct sockaddr_in * addr); +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 diff --git a/netcon/liblwip.so b/netcon/liblwip.so Binary files differnew file mode 100755 index 00000000..18c04c3d --- /dev/null +++ b/netcon/liblwip.so |