diff --git a/README.md b/README.md index b0f0556..93bc328 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,51 @@ ZeroTier SDK ====== +[![irc](https://img.shields.io/badge/IRC-%23zerotier%20on%20freenode-orange.svg)](https://webchat.freenode.net/?channels=zerotier) *** +## Example + +``` + std::string str = "welcome to the machine"; + zts_start(path); + while(!zts_service_running()) + sleep(1); + zts_join_network(nwid); + int err, sockfd; + while(!zts_has_address(nwid)) + sleep(1); + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_connect(sockfd, (const struct sockaddr *)addr, sizeof(addr))) < 0) { + printf("error connecting to remote host (%d)\n", err); + exit(0); + } + int wrote = zts_write(sockfd, str.c_str(), str.length()); + err = zts_close(sockfd); +``` + +Bindings also exist for [many popular languages](). + ## Build Targets ### Static Library -`static_lib` + - `make static_lib`: Will output to `build/` + +### Tests + - `make tests`: Will output to `build/tests/` + +Then run the comprehensive test suite with whatever configuration you need. For instance: + +To run a single-test IPv4 server on port 8787: + + - Host 1: `./build/test/comprehensive c7cd7c9e1b0f52a2 simple 4 server 8787` + - Host 2: `./build/test/comprehensive c7cd7c9e1b0f52a2 simple 4 client 10.9.9.40 8787` ## Using Language Bindings `SDK_LANG_JAVA=1` `SDK_LANG_CSHARP=1` `SDK_LANG_PYTHON=1` -`SDK_LANG_=1` +`SDK_LANG_GO=1` diff --git a/api/clojure/README.md b/api/clojure/README.md new file mode 100644 index 0000000..28eeefb --- /dev/null +++ b/api/clojure/README.md @@ -0,0 +1,3 @@ +Clojure Language Binding API for the ZeroTier SDK +====== + diff --git a/api/go/README.md b/api/go/README.md new file mode 100644 index 0000000..bc192b5 --- /dev/null +++ b/api/go/README.md @@ -0,0 +1,3 @@ +Go Language Binding API for the ZeroTier SDK +====== + diff --git a/ext/picotcp/include/pico_device.h b/ext/picotcp/include/pico_device.h index 2b61d5f..98b3370 100644 --- a/ext/picotcp/include/pico_device.h +++ b/ext/picotcp/include/pico_device.h @@ -19,6 +19,7 @@ struct pico_ethdev { }; struct pico_device { + void *tap; char name[MAX_DEVICE_NAME]; uint32_t hash; uint32_t overhead; diff --git a/include/ZeroTierSDK.h b/include/ZeroTierSDK.h index c41049a..cf9c2e1 100644 --- a/include/ZeroTierSDK.h +++ b/include/ZeroTierSDK.h @@ -27,17 +27,13 @@ #define ZT_SDK_MTU ZT_MAX_MTU #define ZT_PHY_POLL_INTERVAL 50 // ms -#define ZT_ACCEPT_RECHECK_DELAY 250 // ms (for blocking zts_accept() calls) -// picoTCP +#define ZT_ACCEPT_RECHECK_DELAY 100 // ms (for blocking zts_accept() calls) +#define ZT_CONNECT_RECHECK_DELAY 100 // ms (for blocking zts_connect() calls) + #define MAX_PICO_FRAME_RX_BUF_SZ ZT_MAX_MTU * 128 -// TCP + #define ZT_TCP_TX_BUF_SZ 1024 * 1024 #define ZT_TCP_RX_BUF_SZ 1024 * 1024 -#define ZT_TCP_TX_BUF_SOFTMAX ZT_TCP_TX_BUF_SZ * 0.80 -#define ZT_TCP_TX_BUF_SOFTMIN ZT_TCP_TX_BUF_SZ * 0.20 -#define ZT_TCP_RX_BUF_SOFTMAX ZT_TCP_RX_BUF_SZ * 0.80 -#define ZT_TCP_RX_BUF_SOFTMIN ZT_TCP_RX_BUF_SZ * 0.20 -// UDP #define ZT_UDP_TX_BUF_SZ ZT_MAX_MTU #define ZT_UDP_RX_BUF_SZ ZT_MAX_MTU * 10 @@ -47,12 +43,17 @@ #define ZT_SDK_VERSION_MINOR 0 #define ZT_SDK_VERSION_REVISION 0 -#define ZT_MAX_IPADDR_LEN 64 -#define ZT_ID_LEN 10 -#define ZT_VER_STR_LEN 6 -#define ZT_HOME_PATH_MAX_LEN 128 +#define ZT_MAX_IPADDR_LEN 64 +#define ZT_ID_LEN 10 +#define ZT_VER_STR_LEN 6 +#define ZT_HOME_PATH_MAX_LEN 128 -#define ZT_ERR_OK 0 +#define ZT_ERR_OK 0 + +#define ZT_SOCK_STATE_NONE 100 +#define ZT_SOCK_STATE_UNHANDLED_CONNECTED 101 +#define ZT_SOCK_STATE_CONNECTED 102 +#define ZT_SOCK_STATE_LISTENING 103 /****************************************************************************/ /* Socket API Signatures */ @@ -407,7 +408,7 @@ void *_start_service(void *thread_id); #endif #if ZT_DEBUG_LEVEL >= ZT_MSG_ERROR - #define DEBUG_ERROR(fmt, args...) fprintf(stderr, ZT_RED "ZT_ERROR[%ld] : %14s:%4d:%25s: " fmt \ + #define DEBUG_ERROR(fmt, args...) fprintf(stderr, ZT_RED "ZT_ERROR[%ld] : %16s:%4d:%25s: " fmt \ "\n" ZT_RESET, ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, ##args) #else #define DEBUG_ERROR(fmt, args...) diff --git a/make-linux.mk b/make-linux.mk index 0b84006..7fe0f32 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -107,12 +107,13 @@ endif STACK_DRIVER_FILES:=src/picoTCP.cpp TAP_FILES:=src/SocketTap.cpp \ - src/ZeroTierSDK.cpp - + src/ZeroTierSDK.cpp \ + src/Utils.cpp + SDK_OBJS+= SocketTap.o \ - Socket.o \ picoTCP.o \ - ZeroTierSDK.o + ZeroTierSDK.o \ + Utils.o PICO_OBJS+= ext/picotcp/build/lib/pico_device.o \ ext/picotcp/build/lib/pico_frame.o \ @@ -140,7 +141,7 @@ picotcp: static_lib: picotcp $(ZTO_OBJS) $(CXX) $(CXXFLAGS) $(SDK_FLAGS) $(TAP_FILES) $(STACK_DRIVER_FILES) -c -DSDK_STATIC - libtool -static -o $(STATIC_LIB) $(ZTO_OBJS) $(SDK_OBJS) $(PICO_LIB) + libtool -o $(STATIC_LIB) $(ZTO_OBJS) $(SDK_OBJS) $(PICO_LIB) jni_static_lib: picotcp $(ZTO_OBJS) @@ -149,7 +150,7 @@ jni_static_lib: picotcp $(ZTO_OBJS) ############################################################################## UNIT_TEST_SRC_FILES:=$(wildcard $(UNIT_TEST_SRC_DIR)/*.cpp) -UNIT_TEST_OBJ_FILES:=$(addprefix $(TEST_BUILD_DIR)/,$(notdir $(UNIT_TEST_SRC_FILES:.cpp=.out))) +UNIT_TEST_OBJ_FILES:=$(addprefix $(TEST_BUILD_DIR)/,$(notdir $(UNIT_TEST_SRC_FILES:.cpp=))) UNIT_TEST_INCLUDES:=-Iinclude UNIT_TEST_LIBS:=-Lbuild -lzt @@ -164,10 +165,10 @@ unit_tests: $(UNIT_TEST_OBJ_FILES) ## Non-ZT Client/Server Tests ## ############################################################################## -DUMB_TEST_SRC_FILES=$(wildcard $(DUMB_TEST_SRC_DIR)/*.c) -DUMB_TEST_OBJ_FILES := $(addprefix $(TEST_BUILD_DIR)/,$(notdir $(DUMB_TEST_SRC_FILES:.c=.out))) +DUMB_TEST_SRC_FILES=$(wildcard $(DUMB_TEST_SRC_DIR)/*.cpp) +DUMB_TEST_OBJ_FILES := $(addprefix $(TEST_BUILD_DIR)/,$(notdir $(DUMB_TEST_SRC_FILES:.cpp=.out))) -$(TEST_BUILD_DIR)/%.out: $(DUMB_TEST_SRC_DIR)/%.c +$(TEST_BUILD_DIR)/%.out: $(DUMB_TEST_SRC_DIR)/%.cpp @mkdir -p $(TEST_BUILD_DIR) @-$(CC) -o $@ $< @-./check.sh $@ @@ -182,7 +183,7 @@ clean: -rm -rf $(BUILD)/* -rm -rf zerotier-cli zerotier-idtool -find . -type f \( -name $(ONE_SERVICE_NAME) -o -name $(SDK_SERVICE_NAME) \) -delete - -find . -type f \( -name '*.o' -o -name '*.so' -o -name '*.o.d' -o -name '*.out' -o -name '*.log' -o -name '*.dSYM' \) -delete + -find . -type f \( -name '*.o' -o -name '*.so' -o -name '*.a' -o -name '*.o.d' -o -name '*.out' -o -name '*.log' -o -name '*.dSYM' \) -delete # Check for the presence of built frameworks/bundles/libaries check: diff --git a/make-mac.mk b/make-mac.mk index c3f48e0..656f583 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -108,10 +108,12 @@ endif STACK_DRIVER_FILES:=src/picoTCP.cpp TAP_FILES:=src/SocketTap.cpp \ src/ZeroTierSDK.cpp \ + src/Utils.cpp SDK_OBJS+= SocketTap.o \ picoTCP.o \ - ZeroTierSDK.o + ZeroTierSDK.o \ + Utils.o PICO_OBJS+= ext/picotcp/build/lib/pico_device.o \ ext/picotcp/build/lib/pico_frame.o \ @@ -148,11 +150,11 @@ jni_static_lib: picotcp $(ZTO_OBJS) ############################################################################## UNIT_TEST_SRC_FILES:=$(wildcard $(UNIT_TEST_SRC_DIR)/*.cpp) -UNIT_TEST_OBJ_FILES:=$(addprefix $(TEST_BUILD_DIR)/,$(notdir $(UNIT_TEST_SRC_FILES:.cpp=.out))) +UNIT_TEST_OBJ_FILES:=$(addprefix $(TEST_BUILD_DIR)/,$(notdir $(UNIT_TEST_SRC_FILES:.cpp=))) UNIT_TEST_INCLUDES:=-Iinclude UNIT_TEST_LIBS:=-Lbuild -lzt -$(TEST_BUILD_DIR)/%.out: $(UNIT_TEST_SRC_DIR)/%.cpp +$(TEST_BUILD_DIR)/%: $(UNIT_TEST_SRC_DIR)/%.cpp @mkdir -p $(TEST_BUILD_DIR) @-$(CXX) $(UNIT_TEST_INCLUDES) -o $@ $< $(UNIT_TEST_LIBS) @-./check.sh $@ @@ -163,10 +165,10 @@ unit_tests: $(UNIT_TEST_OBJ_FILES) ## Non-ZT Client/Server Tests ## ############################################################################## -DUMB_TEST_SRC_FILES=$(wildcard $(DUMB_TEST_SRC_DIR)/*.c) -DUMB_TEST_OBJ_FILES := $(addprefix $(TEST_BUILD_DIR)/,$(notdir $(DUMB_TEST_SRC_FILES:.c=.out))) +DUMB_TEST_SRC_FILES=$(wildcard $(DUMB_TEST_SRC_DIR)/*.cpp) +DUMB_TEST_OBJ_FILES := $(addprefix $(TEST_BUILD_DIR)/,$(notdir $(DUMB_TEST_SRC_FILES:.cpp=))) -$(TEST_BUILD_DIR)/%.out: $(DUMB_TEST_SRC_DIR)/%.c +$(TEST_BUILD_DIR)/%: $(DUMB_TEST_SRC_DIR)/%.cpp @mkdir -p $(TEST_BUILD_DIR) @-$(CC) -o $@ $< @-./check.sh $@ @@ -181,7 +183,7 @@ clean: -rm -rf $(BUILD)/* -rm -rf zerotier-cli zerotier-idtool -find . -type f \( -name $(ONE_SERVICE_NAME) -o -name $(SDK_SERVICE_NAME) \) -delete - -find . -type f \( -name '*.o' -o -name '*.so' -o -name '*.o.d' -o -name '*.out' -o -name '*.log' -o -name '*.dSYM' \) -delete + -find . -type f \( -name '*.o' -o -name '*.so' -o -name '*.a' -o -name '*.o.d' -o -name '*.out' -o -name '*.log' -o -name '*.dSYM' \) -delete # Check for the presence of built frameworks/bundles/libaries check: diff --git a/src/Connection.hpp b/src/Connection.hpp index fcf1979..79bffdd 100644 --- a/src/Connection.hpp +++ b/src/Connection.hpp @@ -56,6 +56,7 @@ namespace ZeroTier { std::queue _AcceptedConnections; SocketTap *tap; // Reference to SocketTap + int state; // See ZeroTierSDK.h for (ZT_SOCK_STATE_*) Connection() { ZT_PHY_SOCKFD_TYPE fdpair[2]; @@ -69,5 +70,15 @@ namespace ZeroTier { app_fd = fdpair[1]; } }; + + /* + * A helper object for passing SocketTaps and Connections through the stack + */ + struct ConnectionPair + { + SocketTap *tap; + Connection *conn; + ConnectionPair(SocketTap *_tap, Connection *conn) : tap(_tap), conn(conn) {} + }; } #endif \ No newline at end of file diff --git a/src/SocketTap.cpp b/src/SocketTap.cpp index c622adb..c9649d4 100644 --- a/src/SocketTap.cpp +++ b/src/SocketTap.cpp @@ -37,302 +37,223 @@ namespace ZeroTier { -// Ignore these -void SocketTap::phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *local_address, - const struct sockaddr *from,void *data,unsigned long len) {} -void SocketTap::phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) {} -void SocketTap::phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN, - const struct sockaddr *from) {} -void SocketTap::phyOnTcpClose(PhySocket *sock,void **uptr) {} -void SocketTap::phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) {} -void SocketTap::phyOnTcpWritable(PhySocket *sock,void **uptr, bool stack_invoked) {} + /****************************************************************************/ + /* SocketTap Service */ + /* - For each joined network a SocketTap will be created to administer I/O */ + /* calls to the stack and the ZT virtual wire */ + /****************************************************************************/ -/****************************************************************************/ -/* SocketTap Service */ -/* - For each joined network a SocketTap will be created to administer I/O */ -/* calls to the stack and the ZT virtual wire */ -/****************************************************************************/ - -SocketTap::SocketTap( - const char *homePath, - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *friendlyName, - void (*handler)(void *,void*,uint64_t,const MAC &,const MAC &, - unsigned int,unsigned int,const void *,unsigned int), - void *arg) : - _homePath(homePath), - _mac(mac), - _mtu(mtu), - _nwid(nwid), - _handler(handler), - _arg(arg), - _phy(this,false,true), - _unixListenSocket((PhySocket *)0), - _enabled(true), - _run(true) -{ - /* - char sockPath[4096]; - Utils::snprintf(sockPath,sizeof(sockPath),"%s%s" ZT_SDK_RPC_DIR_PREFIX "/%.16llx", - homePath,ZT_PATH_SEPARATOR_S,_nwid,ZT_PATH_SEPARATOR_S,(unsigned long long)nwid); - _dev = sockPath; - _unixListenSocket = _phy.unixListen(sockPath,(void *)this); - chmod(sockPath, 0777); // make the RPC socket available to all users - if (!_unixListenSocket) - DEBUG_ERROR("unable to bind to: rpc = %s", sockPath); - else - DEBUG_INFO("rpc = %s", sockPath); - */ - _thread = Thread::start(this); -} - -SocketTap::~SocketTap() -{ - // TODO: Verify deletion of all objects - _run = false; - _phy.whack(); - Thread::join(_thread); - _phy.close(_unixListenSocket,false); -} - -void SocketTap::setEnabled(bool en) -{ - _enabled = en; -} - -bool SocketTap::enabled() const -{ - return _enabled; -} - -bool SocketTap::addIp(const InetAddress &ip) -{ - DEBUG_INFO(); - picotap = this; - picostack->pico_init_interface(this, ip); - _ips.push_back(ip); - return true; -} - -bool SocketTap::removeIp(const InetAddress &ip) -{ - DEBUG_INFO(); - Mutex::Lock _l(_ips_m); - std::vector::iterator i(std::find(_ips.begin(),_ips.end(),ip)); - if (i == _ips.end()) - return false; - _ips.erase(i); - if (ip.isV4()) { - // FIXME: De-register from network stacks + SocketTap::SocketTap( + const char *homePath, + const MAC &mac, + unsigned int mtu, + unsigned int metric, + uint64_t nwid, + const char *friendlyName, + void (*handler)(void *,void*,uint64_t,const MAC &,const MAC &, + unsigned int,unsigned int,const void *,unsigned int), + void *arg) : + _handler(handler), + _homePath(homePath), + _arg(arg), + _enabled(true), + _run(true), + _mac(mac), + _mtu(mtu), + _nwid(nwid), + _unixListenSocket((PhySocket *)0), + _phy(this,false,true) + { + _thread = Thread::start(this); } - return true; -} -std::vector SocketTap::ips() const -{ - Mutex::Lock _l(_ips_m); - return _ips; -} - -void SocketTap::put(const MAC &from,const MAC &to,unsigned int etherType, - const void *data,unsigned int len) -{ - DEBUG_INFO(); - // RX packet - picostack->pico_rx(this, from,to,etherType,data,len); - -} - -std::string SocketTap::deviceName() const -{ - return _dev; -} - -void SocketTap::setFriendlyName(const char *friendlyName) { - DEBUG_INFO(); -} - -void SocketTap::scanMulticastGroups(std::vector &added, - std::vector &removed) -{ - std::vector newGroups; - Mutex::Lock _l(_multicastGroups_m); - // TODO: get multicast subscriptions from network stack - std::vector allIps(ips()); - for(std::vector::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::iterator m(newGroups.begin());m!=newGroups.end();++m) { - if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m)) - added.push_back(*m); + SocketTap::~SocketTap() + { + // TODO: Verify deletion of all objects + _run = false; + _phy.whack(); + Thread::join(_thread); + _phy.close(_unixListenSocket,false); + for(int i=0; i<_Connections.size(); i++) delete _Connections[i]; } - for(std::vector::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) { - if (!std::binary_search(newGroups.begin(),newGroups.end(),*m)) - removed.push_back(*m); - } - _multicastGroups.swap(newGroups); -} -void SocketTap::threadMain() - throw() -{ - picostack->pico_loop(this); -} - -Connection *SocketTap::getConnection(PhySocket *sock) -{ - DEBUG_INFO(); - for(size_t i=0;i<_Connections.size();++i) { - if(_Connections[i]->sock == sock) - return _Connections[i]; + void SocketTap::setEnabled(bool en) + { + _enabled = en; } - return NULL; -} -Connection *SocketTap::getConnection(struct pico_socket *sock) -{ - DEBUG_INFO(); - for(size_t i=0;i<_Connections.size();++i) { - if(_Connections[i]->picosock == sock) - return _Connections[i]; + bool SocketTap::enabled() const + { + return _enabled; } - return NULL; -} -void SocketTap::closeConnection(PhySocket *sock) -{ - DEBUG_INFO(); - Mutex::Lock _l(_close_m); - // Here we assume _tcpconns_m is already locked by caller - if(!sock) { - DEBUG_EXTRA("invalid PhySocket"); - return; + bool SocketTap::addIp(const InetAddress &ip) + { + DEBUG_INFO(); + picostack->pico_init_interface(this, ip); + _ips.push_back(ip); + return true; } - picostack->pico_handleClose(sock); - Connection *conn = getConnection(sock); - if(!conn) - return; - for(size_t i=0;i<_Connections.size();++i) { - if(_Connections[i] == conn){ - _Connections.erase(_Connections.begin() + i); - delete conn; - break; + + bool SocketTap::removeIp(const InetAddress &ip) + { + DEBUG_INFO(); + Mutex::Lock _l(_ips_m); + std::vector::iterator i(std::find(_ips.begin(),_ips.end(),ip)); + if (i == _ips.end()) + return false; + _ips.erase(i); + if (ip.isV4()) { + // FIXME: De-register from network stacks } + if (ip.isV6()) { + // FIXME: De-register from network stacks + } + return true; } - if(!sock) - return; - close(_phy.getDescriptor(sock)); - _phy.close(sock, false); -} -void SocketTap::phyOnUnixClose(PhySocket *sock,void **uptr) { - DEBUG_INFO(); - //Mutex::Lock _l(_tcpconns_m); - //closeConnection(sock); - // FIXME: -} - -void SocketTap::handleRead(PhySocket *sock,void **uptr,bool stack_invoked) -{ - DEBUG_INFO(); - picostack->pico_handleRead(sock, uptr, stack_invoked); -} - -void SocketTap::phyOnUnixWritable(PhySocket *sock,void **uptr,bool stack_invoked) -{ - DEBUG_INFO(); - handleRead(sock,uptr,stack_invoked); -} - -void SocketTap::phyOnUnixData(PhySocket *sock, void **uptr, void *data, ssize_t len) -{ - DEBUG_INFO(); - Connection *conn = getConnection(sock); - if(!conn) - return; - if(len) { - conn->txsz += len; - handleWrite(conn); - } - return; -} - -/****************************************************************************/ -/* SDK Socket API */ -/****************************************************************************/ - -int SocketTap::Connect(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen) { - Mutex::Lock _l(_tcpconns_m); - return picostack->pico_Connect(conn, fd, addr, addrlen); -} - -int SocketTap::Bind(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen) { - Mutex::Lock _l(_tcpconns_m); - return picostack->pico_Bind(conn, fd, addr, addrlen); -} - -void SocketTap::Listen(Connection *conn, int fd, int backlog) { - Mutex::Lock _l(_tcpconns_m); - picostack->pico_Listen(conn, fd, backlog); -} - -int SocketTap::Accept(Connection *conn) { - Mutex::Lock _l(_tcpconns_m); - return picostack->pico_Accept(conn); -} - - - - - -/*------------------------------------------------------------------------------ ------------------------------ RPC Handler functions ---------------------------- -------------------------------------------------------------------------------*/ - -void SocketTap::handleGetsockname(PhySocket *sock, PhySocket *rpcSock, - void **uptr, struct getsockname_st *getsockname_rpc) -{ - DEBUG_INFO(); - Mutex::Lock _l(_tcpconns_m); - Connection *conn = getConnection(sock); - if(conn->local_addr == NULL){ - DEBUG_EXTRA("no address info available. is it bound?"); - struct sockaddr_storage storage; - memset(&storage, 0, sizeof(struct sockaddr_storage)); - write(_phy.getDescriptor(rpcSock), NULL, sizeof(struct sockaddr_storage)); - return; + std::vector SocketTap::ips() const + { + Mutex::Lock _l(_ips_m); + return _ips; } - write(_phy.getDescriptor(rpcSock), conn->local_addr, sizeof(struct sockaddr_storage)); -} -void SocketTap::handleGetpeername(PhySocket *sock, PhySocket *rpcSock, - void **uptr, struct getsockname_st *getsockname_rpc) -{ - DEBUG_INFO(); - Mutex::Lock _l(_tcpconns_m); - Connection *conn = getConnection(sock); - if(conn->peer_addr == NULL){ - DEBUG_EXTRA("no peer address info available. is it connected?"); - struct sockaddr_storage storage; - memset(&storage, 0, sizeof(struct sockaddr_storage)); - write(_phy.getDescriptor(rpcSock), NULL, sizeof(struct sockaddr_storage)); - return; + void SocketTap::put(const MAC &from,const MAC &to,unsigned int etherType, + const void *data,unsigned int len) + { + // RX packet + picostack->pico_rx(this, from,to,etherType,data,len); } - write(_phy.getDescriptor(rpcSock), conn->peer_addr, sizeof(struct sockaddr_storage)); -} -// Write to the network stack (and thus out onto the network) -void SocketTap::handleWrite(Connection *conn) -{ - DEBUG_INFO(); - picostack->pico_handleWrite(conn); -} + std::string SocketTap::deviceName() const + { + return _dev; + } + + void SocketTap::setFriendlyName(const char *friendlyName) { + DEBUG_INFO(); + } + + void SocketTap::scanMulticastGroups(std::vector &added, + std::vector &removed) + { + std::vector newGroups; + Mutex::Lock _l(_multicastGroups_m); + // TODO: get multicast subscriptions from network stack + std::vector allIps(ips()); + for(std::vector::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::iterator m(newGroups.begin());m!=newGroups.end();++m) { + if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m)) + added.push_back(*m); + } + for(std::vector::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) { + if (!std::binary_search(newGroups.begin(),newGroups.end(),*m)) + removed.push_back(*m); + } + _multicastGroups.swap(newGroups); + } + + void SocketTap::threadMain() + throw() + { + picostack->pico_loop(this); + } + + void SocketTap::phyOnUnixClose(PhySocket *sock,void **uptr) + { + Close((Connection*)uptr); + } + + void SocketTap::phyOnUnixData(PhySocket *sock, void **uptr, void *data, ssize_t len) + { + Connection *conn = (Connection*)*uptr; + if(!conn) + return; + if(len) { + unsigned char *buf = (unsigned char*)data; + memcpy(conn->txbuf + conn->txsz, buf, len); + conn->txsz += len; + Write(conn); + } + return; + } + + void SocketTap::phyOnUnixWritable(PhySocket *sock,void **uptr,bool stack_invoked) + { + Read(sock,uptr,stack_invoked); + } + + /****************************************************************************/ + /* SDK Socket API */ + /****************************************************************************/ + + int SocketTap::Connect(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen) { + Mutex::Lock _l(_tcpconns_m); + return picostack->pico_Connect(conn, fd, addr, addrlen); + } + + int SocketTap::Bind(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen) { + Mutex::Lock _l(_tcpconns_m); + return picostack->pico_Bind(conn, fd, addr, addrlen); + } + + void SocketTap::Listen(Connection *conn, int fd, int backlog) { + Mutex::Lock _l(_tcpconns_m); + picostack->pico_Listen(conn, fd, backlog); + } + + int SocketTap::Accept(Connection *conn) { + Mutex::Lock _l(_tcpconns_m); + return picostack->pico_Accept(conn); + } + + void SocketTap::Read(PhySocket *sock,void **uptr,bool stack_invoked) { + picostack->pico_Read(this, sock, uptr, stack_invoked); + } + + void SocketTap::Write(Connection *conn) { + picostack->pico_Write(conn); + } + + void SocketTap::Close(Connection *conn) { + Mutex::Lock _l(_close_m); + // Here we assume _tcpconns_m is already locked by caller + if(!conn->sock) { + DEBUG_EXTRA("invalid PhySocket"); + return; + } + if(!conn) + return; + for(size_t i=0;i<_Connections.size();++i) { + if(_Connections[i] == conn){ + _Connections.erase(_Connections.begin() + i); + delete conn; + break; + } + } + if(!conn->sock) + return; + close(_phy.getDescriptor(conn->sock)); + _phy.close(conn->sock, false); + } + + /****************************************************************************/ + /* Not used in this implementation */ + /****************************************************************************/ + + void SocketTap::phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *local_address, + const struct sockaddr *from,void *data,unsigned long len) {} + void SocketTap::phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) {} + void SocketTap::phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN, + const struct sockaddr *from) {} + void SocketTap::phyOnTcpClose(PhySocket *sock,void **uptr) {} + void SocketTap::phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) {} + void SocketTap::phyOnTcpWritable(PhySocket *sock,void **uptr, bool stack_invoked) {} } // namespace ZeroTier diff --git a/src/SocketTap.hpp b/src/SocketTap.hpp index cd40d13..8b034e5 100644 --- a/src/SocketTap.hpp +++ b/src/SocketTap.hpp @@ -45,34 +45,11 @@ #include "pico_icmp4.h" #include "pico_dev_tap.h" #include "pico_protocol.h" -#include "pico_socket.h" #include "pico_device.h" #include "pico_ipv6.h" -// ZT RPC structs -struct socket_st; -struct listen_st; -struct bind_st; -struct connect_st; -struct getsockname_st; -struct accept_st; - -struct pico_socket; - namespace ZeroTier { - - extern SocketTap *picotap; - /* - * A helper for passing a reference to _phy to stackrpc callbacks as a "state" - */ - struct Larg - { - SocketTap *tap; - Connection *conn; - Larg(SocketTap *_tap, Connection *conn) : tap(_tap), conn(conn) {} - }; - /* * Socket Tap -- emulates an Ethernet tap device */ @@ -97,19 +74,17 @@ namespace ZeroTier { bool enabled() const; /* - * + * Adds an address to the userspace stack interface associated with this SocketTap */ bool addIp(const InetAddress &ip); /* - * + * Removes an address from the userspace stack interface associated with this SocketTap */ bool removeIp(const InetAddress &ip); - std::vector ips() const; - std::vector _ips; /* - * + * Presents data to the userspace stack */ void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data, unsigned int len); @@ -135,73 +110,12 @@ namespace ZeroTier { void threadMain() throw(); - std::string _homePath; - MAC _mac; - unsigned int _mtu; - uint64_t _nwid; - /* - * + * For moving data onto the ZeroTier virtual wire */ void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int, const void *,unsigned int); - void *_arg; - Phy _phy; - PhySocket *_unixListenSocket; - volatile bool _enabled; - volatile bool _run; - - // picoTCP - unsigned char pico_frame_rxbuf[MAX_PICO_FRAME_RX_BUF_SZ]; - int pico_frame_rxbuf_tot; - Mutex _pico_frame_rxbuf_m; - - - - - /****************************************************************************/ - /* In these, we will call the stack's corresponding functions, this is * - * where one would put logic to select between different stacks */ - /****************************************************************************/ - - int Connect(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen); - int Bind(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen); - void Listen(Connection *conn, int fd, int backlog); - int Accept(Connection *conn); - - - - /* - * Return the address that the socket is bound to - */ - void handleGetsockname(PhySocket *sock, PhySocket *rpcsock, void **uptr, struct getsockname_st *getsockname_rpc); - - /* - * Return the address of the peer connected to this socket - */ - void handleGetpeername(PhySocket *sock, PhySocket *rpcsock, void **uptr, struct getsockname_st *getsockname_rpc); - - /* - * Writes data from the application's socket to the LWIP connection - */ - void handleWrite(Connection *conn); - - // Unused -- no UDP or TCP from this thread/Phy<> - void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *local_address, - 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, bool stack_invoked); - - /* - * - */ - void handleRead(PhySocket *sock,void **uptr,bool stack_invoked); - /* * Signals us to close the TcpConnection associated with this PhySocket */ @@ -217,21 +131,22 @@ namespace ZeroTier { */ void phyOnUnixWritable(PhySocket *sock,void **uptr,bool lwip_invoked); - /* - * Returns a pointer to a TcpConnection associated with a given PhySocket - */ - Connection *getConnection(PhySocket *sock); + /****************************************************************************/ + /* Vars */ + /****************************************************************************/ - /* - * Returns a pointer to a TcpConnection associated with a given pico_socket - */ - Connection *getConnection(struct pico_socket *socket); + std::vector ips() const; + std::vector _ips; - /* - * Closes a TcpConnection, associated connection strcuture, - * PhySocket, and underlying file descriptor - */ - void closeConnection(PhySocket *sock); + std::string _homePath; + void *_arg; + volatile bool _enabled; + volatile bool _run; + MAC _mac; + unsigned int _mtu; + uint64_t _nwid; + PhySocket *_unixListenSocket; + Phy _phy; std::vector _Connections; @@ -241,7 +156,86 @@ namespace ZeroTier { std::vector _multicastGroups; Mutex _multicastGroups_m; Mutex _ips_m, _tcpconns_m, _rx_buf_m, _close_m; + + + /****************************************************************************/ + /* Guarded RX Frame Buffer for picoTCP */ + /****************************************************************************/ + + unsigned char pico_frame_rxbuf[MAX_PICO_FRAME_RX_BUF_SZ]; + int pico_frame_rxbuf_tot; + Mutex _pico_frame_rxbuf_m; + + /****************************************************************************/ + /* In these, we will call the stack's corresponding functions, this is */ + /* where one would put logic to select between different stacks */ + /****************************************************************************/ + + /* + * Connect to a remote host via the userspace stack interface associated with this SocketTap + */ + int Connect(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen); + + /* + * Bind to the userspace stack interface associated with this SocketTap + */ + int Bind(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen); + + /* + * Listen for a Connection + */ + void Listen(Connection *conn, int fd, int backlog); + + /* + * Accepts an incoming Connection + */ + int Accept(Connection *conn); + + /* + * Move data from RX buffer to application's "socket" + */ + void Read(PhySocket *sock,void **uptr,bool stack_invoked); + + /* + * Move data from application's "socket" into network stack + */ + void Write(Connection *conn); + + /* + * Closes a Connection + */ + void Close(Connection *conn); + + + + + + + + /* + * Return the address that the socket is bound to + */ + void handleGetsockname(PhySocket *sock, PhySocket *rpcsock, void **uptr, struct getsockname_st *getsockname_rpc); + + /* + * Return the address of the peer connected to this socket + */ + void handleGetpeername(PhySocket *sock, PhySocket *rpcsock, void **uptr, struct getsockname_st *getsockname_rpc); + + /****************************************************************************/ + /* Not used in this implementation */ + /****************************************************************************/ + + void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *local_address, + 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, bool stack_invoked); }; + } // namespace ZeroTier #endif diff --git a/src/Utils.cpp b/src/Utils.cpp new file mode 100644 index 0000000..5f0168f --- /dev/null +++ b/src/Utils.cpp @@ -0,0 +1,84 @@ +/* + * ZeroTier SDK - Network Virtualization Everywhere + * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +void zt_dump_stacktrace(int sig) { + void *array[16]; + size_t size; + size = backtrace(array, 16); + fprintf(stderr, "Error: signal %d:\n", sig); + backtrace_symbols_fd(array, size, STDERR_FILENO); + exit(1); +} + +/* +char *beautify_pico_error(int err) +{ + switch(err){ + PICO_ERR_NOERR = 0, + PICO_ERR_EPERM = 1, + PICO_ERR_ENOENT = 2, + + PICO_ERR_EINTR = 4, + PICO_ERR_EIO = 5, + PICO_ERR_ENXIO = 6, + + PICO_ERR_EAGAIN = 11, + PICO_ERR_ENOMEM = 12, + PICO_ERR_EACCESS = 13, + PICO_ERR_EFAULT = 14, + + PICO_ERR_EBUSY = 16, + PICO_ERR_EEXIST = 17, + + PICO_ERR_EINVAL = 22, + + PICO_ERR_ENONET = 64, + + PICO_ERR_EPROTO = 71, + + PICO_ERR_ENOPROTOOPT = 92, + PICO_ERR_EPROTONOSUPPORT = 93, + + PICO_ERR_EOPNOTSUPP = 95, + PICO_ERR_EADDRINUSE = 98, + PICO_ERR_EADDRNOTAVAIL = 99, + PICO_ERR_ENETDOWN = 100, + PICO_ERR_ENETUNREACH = 101, + + PICO_ERR_ECONNRESET = 104, + + PICO_ERR_EISCONN = 106, + PICO_ERR_ENOTCONN = 107, + PICO_ERR_ESHUTDOWN = 108, + + PICO_ERR_ETIMEDOUT = 110, + PICO_ERR_ECONNREFUSED = 111, + PICO_ERR_EHOSTDOWN = 112, + PICO_ERR_EHOSTUNREACH = 113, + } + return err_text; +} +*/ \ No newline at end of file diff --git a/src/Utils.hpp b/src/Utils.hpp new file mode 100644 index 0000000..ea75732 --- /dev/null +++ b/src/Utils.hpp @@ -0,0 +1,27 @@ +/* + * ZeroTier SDK - Network Virtualization Everywhere + * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * + * 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 . + */ + +#ifndef SDK_UTILS_HPP +#define SDK_UTILS_HPP + +/* + * Print a stacktrace + */ +void zt_dump_stacktrace(int sig); + +#endif \ No newline at end of file diff --git a/src/ZeroTierSDK.cpp b/src/ZeroTierSDK.cpp index 6625f0d..b4b2f03 100644 --- a/src/ZeroTierSDK.cpp +++ b/src/ZeroTierSDK.cpp @@ -48,11 +48,24 @@ namespace ZeroTier { std::string homeDir; // The resultant platform-specific dir we *must* use internally std::string netDir; // Where network .conf files are to be written + /* + * Global reference to stack + */ picoTCP *picostack = NULL; + + /* + * "sockets" that have been created but not bound to a SocketTap interface yet + */ std::map UnassignedConnections; - std::map*> AssignedFileDescriptors; - Mutex _multiplexer_lock; - Mutex _accepted_connection_lock; + + // FIXME: make sure these are properly deleted + /* + * For fast lookup of Connections and SocketTaps via given file descriptor + */ + std::map*> AssignedConnections; + + ZeroTier::Mutex _multiplexer_lock; + ZeroTier::Mutex _accepted_connection_lock; } /****************************************************************************/ @@ -260,12 +273,12 @@ int zts_get_peer_address(char *peer, const char *devID) { void zts_enable_http_control_plane() { - + // TODO } void zts_disable_http_control_plane() { - + // TODO } /****************************************************************************/ @@ -278,6 +291,7 @@ void zts_disable_http_control_plane() /****************************************************************************/ int zts_socket(ZT_SOCKET_SIG) { + DEBUG_ERROR("UnassConn=%d, AssigFDs=%d", ZeroTier::UnassignedConnections.size(), ZeroTier::AssignedConnections.size()); DEBUG_INFO(); ZeroTier::_multiplexer_lock.lock(); ZeroTier::Connection *conn = new ZeroTier::Connection(); @@ -316,7 +330,7 @@ int zts_socket(ZT_SOCKET_SIG) { int zts_connect(ZT_CONNECT_SIG) { if(!zt1Service) { - DEBUG_ERROR("zt1Service = NULL"); + DEBUG_ERROR("Service not started. Call zts_start(path) first"); // errno = ? return -1; } @@ -341,12 +355,9 @@ int zts_connect(ZT_CONNECT_SIG) { (const void *)&((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, ipstr, INET6_ADDRSTRLEN); } iaddr.fromString(ipstr); - DEBUG_INFO("ipstr= %s", ipstr); DEBUG_INFO("iaddr= %s", iaddr.toString().c_str()); - tap = zt1Service->getTap(iaddr); - if(!tap) { // TODO: More canonical error? DEBUG_ERROR("no route to host"); @@ -354,13 +365,19 @@ int zts_connect(ZT_CONNECT_SIG) { err = -1; } else { - conn->sock = tap->_phy.wrapSocket(conn->sdk_fd, conn); // wrap the socketpair we created earlier - conn->picosock->priv = new ZeroTier::Larg(tap, conn); // pointer to tap we use in callbacks from the stack + // pointer to tap we use in callbacks from the stack + conn->picosock->priv = new ZeroTier::ConnectionPair(tap, conn); DEBUG_INFO("found appropriate SocketTap"); // TODO: Perhaps move this connect call outside of the lock - tap->_Connections.push_back(conn); // Give this Connection to the tap we decided on - err = tap->Connect(conn, fd, addr, addrlen); // Semantically: tap->stack->connect - conn->tap = tap; + // Semantically: tap->stack->connect + err = tap->Connect(conn, fd, addr, addrlen); + if(err == 0) { + tap->_Connections.push_back(conn); // Give this Connection to the tap we decided on + conn->tap = tap; + } + // Wrap the socketpair we created earlier + // For I/O loop participation and referencing the PhySocket's parent Connection in callbacks + conn->sock = tap->_phy.wrapSocket(conn->sdk_fd, conn); } } else { @@ -368,14 +385,50 @@ int zts_connect(ZT_CONNECT_SIG) { // errno = ? err = -1; } - ZeroTier::AssignedFileDescriptors[fd] = new std::pair(conn, tap); + ZeroTier::UnassignedConnections.erase(fd); + ZeroTier::AssignedConnections[fd] = new std::pair(conn, tap); ZeroTier::_multiplexer_lock.unlock(); + + // NOTE: pico_socket_connect() will return 0 if no error happens immediately, but that doesn't indicate + // the connection was completed, for that we must wait for a callback from the stack. During that + // callback we will place the Connection in a ZT_SOCK_STATE_UNHANDLED_CONNECTED state to signal + // to the multiplexer logic that this connection is complete and a success value can be sent to the + // user application + + // non-blocking + if(err == 0 && false) + { + errno = EINPROGRESS; + return -1; + } + + // blocking + if(err == 0 && true) { + while(true) + { + usleep(ZT_CONNECT_RECHECK_DELAY * 1000); + tap->_tcpconns_m.lock(); + for(int i=0; i_Connections.size(); i++) + { + if(tap->_Connections[i]->state == PICO_ERR_ECONNRESET) { + errno = ECONNRESET; + return -1; + } + if(tap->_Connections[i]->state == ZT_SOCK_STATE_UNHANDLED_CONNECTED) { + tap->_Connections[i]->state = ZT_SOCK_STATE_CONNECTED; + errno = 0; + return 0; // complete + } + } + tap->_tcpconns_m.unlock(); + } + } return err; } int zts_bind(ZT_BIND_SIG) { if(!zt1Service) { - DEBUG_ERROR("zt1Service = NULL"); + DEBUG_ERROR("Service not started. Call zts_start(path) first"); // errno = ? return -1; } @@ -415,7 +468,7 @@ int zts_bind(ZT_BIND_SIG) { else { DEBUG_INFO("found appropriate SocketTap"); DEBUG_INFO("conn->picosock = %p", conn->picosock); - conn->picosock->priv = new ZeroTier::Larg(tap, conn); + conn->picosock->priv = new ZeroTier::ConnectionPair(tap, conn); // TODO: Perhaps move this connect call outside of the lock tap->_Connections.push_back(conn); // Give this Connection to the tap we decided on err = tap->Bind(conn, fd, addr, addrlen); // Semantically: tap->stack->connect @@ -427,37 +480,43 @@ int zts_bind(ZT_BIND_SIG) { // errno = ? err = -1; } - ZeroTier::AssignedFileDescriptors[fd] = new std::pair(conn, tap); + ZeroTier::AssignedConnections[fd] = new std::pair(conn, tap); ZeroTier::_multiplexer_lock.unlock(); return err; } int zts_listen(ZT_LISTEN_SIG) { if(!zt1Service) { - DEBUG_ERROR("zt1Service = NULL"); + DEBUG_ERROR("Service not started. Call zts_start(path) first"); // errno = ? return -1; } int err; ZeroTier::_multiplexer_lock.lock(); - ZeroTier::Connection *conn = ZeroTier::AssignedFileDescriptors[fd]->first; - ZeroTier::SocketTap *tap = ZeroTier::AssignedFileDescriptors[fd]->second; + std::pair *p = ZeroTier::AssignedConnections[fd]; + if(!p) { + DEBUG_ERROR("unable to locate connection pair (did you zbind()?"); + return -1; + } + + ZeroTier::Connection *conn = p->first; + ZeroTier::SocketTap *tap = p->second; if(!tap || !conn) { DEBUG_ERROR("unable to locate tap interface for file descriptor"); - err = -1; + return -1; } tap->Listen(conn, fd, backlog); err = 0; - DEBUG_INFO("put %p into LISTENING state", conn); + DEBUG_INFO("put conn=%p into LISTENING state", conn); ZeroTier::_multiplexer_lock.unlock(); return err; } int zts_accept(ZT_ACCEPT_SIG) { int err; - ZeroTier::Connection *conn = ZeroTier::AssignedFileDescriptors[fd]->first; - ZeroTier::SocketTap *tap = ZeroTier::AssignedFileDescriptors[fd]->second; + ZeroTier::Connection *conn = ZeroTier::AssignedConnections[fd]->first; + ZeroTier::SocketTap *tap = ZeroTier::AssignedConnections[fd]->second; // BLOCKING: loop and keep checking until we find a newly accepted connection if(true) { while(true) { @@ -476,57 +535,75 @@ int zts_accept(ZT_ACCEPT_SIG) { #if defined(__linux__) int zts_accept4(ZT_ACCEPT4_SIG) { + // TODO return 0; } #endif int zts_setsockopt(ZT_SETSOCKOPT_SIG) { + // TODO return 0; } int zts_getsockopt(ZT_GETSOCKOPT_SIG) { + // TODO return 0; } int zts_getsockname(ZT_GETSOCKNAME_SIG) { + // TODO return 0; } int zts_getpeername(ZT_GETPEERNAME_SIG) { + // TODO return 0; } int zts_close(ZT_CLOSE_SIG) { + ZeroTier::_multiplexer_lock.lock(); + ZeroTier::Connection *conn = ZeroTier::AssignedConnections[fd]->first; + ZeroTier::SocketTap *tap = ZeroTier::AssignedConnections[fd]->second; + // Tell the tap to stop monitoring this PhySocket + tap->Close(conn); + // Tell the userspace stack to close this pico_socket + ZeroTier::picostack->pico_Close(conn); + ZeroTier::_multiplexer_lock.unlock(); return 0; } int zts_fcntl(ZT_FCNTL_SIG) { + // TODO return 0; } ssize_t zts_sendto(ZT_SENDTO_SIG) { + // TODO return 0; } ssize_t zts_sendmsg(ZT_SENDMSG_SIG) { + // TODO return 0; } ssize_t zts_recvfrom(ZT_RECVFROM_SIG) { + // TODO return 0; } ssize_t zts_recvmsg(ZT_RECVMSG_SIG) { + // TODO return 0; } @@ -666,7 +743,7 @@ void *_start_service(void *thread_id) { // Generate random port for new service instance unsigned int randp = 0; ZeroTier::Utils::getSecureRandom(&randp,sizeof(randp)); - int servicePort = 9000 + (randp % 10000); + int servicePort = 9000 + (randp % 1000); DEBUG_ERROR("servicePort = %d", servicePort); for(;;) { diff --git a/src/picoTCP.cpp b/src/picoTCP.cpp index 4843060..b20debe 100644 --- a/src/picoTCP.cpp +++ b/src/picoTCP.cpp @@ -27,6 +27,7 @@ #include "pico_ipv6.h" #include "ZeroTierSDK.h" +#include "Utils.hpp" #include "SocketTap.hpp" #include "picoTCP.hpp" @@ -36,13 +37,6 @@ #include "Constants.hpp" #include "Phy.hpp" -// stack locks -ZeroTier::Mutex _lock; -ZeroTier::Mutex _lock_mem; - -struct pico_socket; -struct pico_device; - extern "C" int pico_stack_init(void); extern "C" void pico_stack_tick(void); @@ -73,6 +67,8 @@ extern "C" struct pico_ipv6_link * pico_ipv6_link_add(PICO_IPV6_LINK_ADD_SIG); namespace ZeroTier { + //extern ZeroTier::Mutex _accepted_connection_lock; + // Reference to the tap interface // This is needed due to the fact that there's a lot going on in the tap interface // that needs to be updated on each of the network stack's callbacks and not every @@ -82,7 +78,6 @@ namespace ZeroTier { // will make it easier to maintain multiple active tap interfaces struct pico_device picodev; - SocketTap * picotap; int pico_eth_send(struct pico_device *dev, void *buf, int len); int pico_eth_poll(struct pico_device *dev, int loop_score); @@ -105,6 +100,7 @@ namespace ZeroTier { picodev.send = pico_eth_send; // tx picodev.poll = pico_eth_poll; // rx picodev.mtu = tap->_mtu; + picodev.tap = tap; if( 0 != pico_device_init(&(picodev), "p0", mac)) { DEBUG_ERROR("dev init failed"); return; @@ -125,6 +121,7 @@ namespace ZeroTier { pico_ipv6_link_add(&(picodev), ipaddr, netmask); picodev.send = pico_eth_send; // tx picodev.poll = pico_eth_poll; // rx + picodev.tap = tap; uint8_t mac[PICO_SIZE_ETH]; tap->_mac.copyTo(mac, PICO_SIZE_ETH); DEBUG_ATTN("mac = %s", tap->_mac.toString().c_str()); @@ -138,7 +135,6 @@ namespace ZeroTier { } } - // Main stack loop void picoTCP::pico_loop(SocketTap *tap) { while(tap->_run) @@ -151,7 +147,7 @@ namespace ZeroTier { void picoTCP::pico_cb_tcp_read(ZeroTier::SocketTap *tap, struct pico_socket *s) { DEBUG_INFO(); - Connection *conn = (Connection*)((Larg*)(s->priv))->conn; + Connection *conn = (Connection*)((ConnectionPair*)(s->priv))->conn; if(conn) { int r; uint16_t port = 0; @@ -180,7 +176,7 @@ namespace ZeroTier { void picoTCP::pico_cb_udp_read(SocketTap *tap, struct pico_socket *s) { DEBUG_INFO(); - Connection *conn = (Connection*)((Larg*)(s->priv))->conn; + Connection *conn = (Connection*)((ConnectionPair*)(s->priv))->conn; if(conn) { uint16_t port = 0; @@ -242,7 +238,7 @@ namespace ZeroTier { void picoTCP::pico_cb_tcp_write(SocketTap *tap, struct pico_socket *s) { DEBUG_INFO(); - Connection *conn = (Connection*)((Larg*)(s->priv))->conn; + Connection *conn = (Connection*)((ConnectionPair*)(s->priv))->conn; if(!conn) { DEBUG_ERROR("invalid connection"); @@ -276,10 +272,10 @@ namespace ZeroTier { { DEBUG_INFO(); // TODO: Test API out of order so this check isn't necessary - if(!(SocketTap*)((Larg*)(s->priv))) + if(!(SocketTap*)((ConnectionPair*)(s->priv))) return; - SocketTap *tap = (SocketTap*)((Larg*)(s->priv))->tap; - Connection *conn = (Connection*)((Larg*)(s->priv))->conn; + SocketTap *tap = (SocketTap*)((ConnectionPair*)(s->priv))->tap; + Connection *conn = (Connection*)((ConnectionPair*)(s->priv))->conn; int err; Mutex::Lock _l(tap->_tcpconns_m); @@ -289,82 +285,79 @@ namespace ZeroTier { // accept() if (ev & PICO_SOCK_EV_CONN) { - uint32_t peer; - uint16_t port; - struct pico_socket *client_psock = pico_socket_accept(s, &peer, &port); - DEBUG_INFO("accepted (pico_sock=%p) on (pico_sock=%p)", client_psock, conn->picosock); - if(!client_psock) { - DEBUG_EXTRA("unable to accept conn. (event might not be incoming, not necessarily an error), picosock=%p", (conn->picosock)); - } + if(conn->state == ZT_SOCK_STATE_LISTENING) + { + uint32_t peer; + uint16_t port; + struct pico_socket *client_psock = pico_socket_accept(s, &peer, &port); + if(!client_psock) { + if(pico_err == PICO_ERR_EINVAL) + DEBUG_ERROR("pico_err = PICO_ERR_EINVAL"); + if(pico_err == PICO_ERR_EAGAIN) + DEBUG_ERROR("pico_err = PICO_ERR_EAGAIN"); + return; + } - // Create a new Connection and add it to the queue, - // some time in the future a call to zts_multiplex_accept() will pick up - // this new connection, add it to the connection list and return its - // Connection->sock to the application + // Create a new Connection and add it to the queue, + // some time in the future a call to zts_multiplex_accept() will pick up + // this new connection, add it to the connection list and return its + // Connection->sock to the application - Connection *newConn = new Connection(); - newConn->socket_type = SOCK_STREAM; - newConn->sock = tap->_phy.wrapSocket(newConn->sdk_fd, newConn); - newConn->picosock = client_psock; - newConn->tap = tap; - newConn->picosock->priv = new Larg(tap,newConn); - - tap->_Connections.push_back(newConn); - conn->_AcceptedConnections.push(newConn); + Connection *newConn = new Connection(); + newConn->socket_type = SOCK_STREAM; + newConn->picosock = client_psock; + newConn->tap = tap; + newConn->picosock->priv = new ConnectionPair(tap,newConn); + tap->_Connections.push_back(newConn); + conn->_AcceptedConnections.push(newConn); + // For I/O loop participation and referencing the PhySocket's parent Connection in callbacks + newConn->sock = tap->_phy.wrapSocket(newConn->sdk_fd, newConn); + } + if(conn->state != ZT_SOCK_STATE_LISTENING) { + // set state so blocking socket multiplexer logic will pick this up + conn->state = ZT_SOCK_STATE_UNHANDLED_CONNECTED; + } } if (ev & PICO_SOCK_EV_FIN) { - DEBUG_INFO("socket closed. exit normally. picosock=%p\n\n", s); + DEBUG_INFO("socket closed. exit normally. picosock=%p", s); //pico_timer_add(2000, compare_results, NULL); } if (ev & PICO_SOCK_EV_ERR) { - DEBUG_INFO("socket error received" /*, strerror(pico_err)*/); + if(pico_err == PICO_ERR_ECONNRESET) { + DEBUG_ERROR("PICO_ERR_ECONNRESET"); + conn->state = PICO_ERR_ECONNRESET; + } + DEBUG_INFO("socket error received pico_err=%d, picosock=%p", pico_err, s); } if (ev & PICO_SOCK_EV_CLOSE) { err = pico_socket_close(s); DEBUG_INFO("socket closure = %d, picosock=%p", err, s); - if(err==0) { - tap->closeConnection(conn->sock); - } + if(err==0) + tap->Close(conn); return; } // Read from picoTCP socket if (ev & PICO_SOCK_EV_RD) { if(conn->socket_type==SOCK_STREAM) - pico_cb_tcp_read(picotap, s); + pico_cb_tcp_read(tap, s); if(conn->socket_type==SOCK_DGRAM) - pico_cb_udp_read(picotap, s); + pico_cb_udp_read(tap, s); } // Write to picoTCP socket if (ev & PICO_SOCK_EV_WR) - pico_cb_tcp_write(picotap, s); + pico_cb_tcp_write(tap, s); } - - // Called when an incoming ping is received - /* - static void pico_cb_ping(struct pico_icmp4_stats *s) - { - DEBUG_INFO(); - char host[30]; - pico_ipv4_to_string(host, s->dst.addr); - if (s->err == 0) { - printf("%lu bytes from %s: icmp_req=%lu ttl=%lu time=%lu ms\n", s->size, - host, s->seq, s->ttl, (long unsigned int)s->time); - } else { - printf("PING %lu to %s: Error %d\n", s->seq, host, s->err); - } - } - */ int pico_eth_send(struct pico_device *dev, void *buf, int len) { - DEBUG_INFO(); + SocketTap *tap = (SocketTap*)(dev->tap); struct pico_eth_hdr *ethhdr; ethhdr = (struct pico_eth_hdr *)buf; MAC src_mac; MAC dest_mac; src_mac.setTo(ethhdr->saddr, 6); dest_mac.setTo(ethhdr->daddr, 6); - picotap->_handler(picotap->_arg,NULL,picotap->_nwid,src_mac,dest_mac, + tap->_handler(tap->_arg,NULL,tap->_nwid,src_mac,dest_mac, Utils::ntoh((uint16_t)ethhdr->proto),0, ((char*)buf) + sizeof(struct pico_eth_hdr),len - sizeof(struct pico_eth_hdr)); return len; } @@ -372,7 +365,6 @@ namespace ZeroTier { void picoTCP::pico_rx(SocketTap *tap, const MAC &from,const MAC &to,unsigned int etherType, const void *data,unsigned int len) { - DEBUG_INFO(); // Since picoTCP only allows the reception of frames from within the polling function, we // must enqueue each frame into a memory structure shared by both threads. This structure will Mutex::Lock _l(tap->_pico_frame_rxbuf_m); @@ -385,85 +377,50 @@ namespace ZeroTier { int newlen = len + sizeof(int) + sizeof(struct pico_eth_hdr); int mylen; + // FIXME while(newlen > (MAX_PICO_FRAME_RX_BUF_SZ-tap->pico_frame_rxbuf_tot) && ethhdr.proto == 56710) { mylen = 0; - //DEBUG_FLOW(" [ ZTWIRE -> FBUF ] not enough space left on RX frame buffer, dropping oldest packet in buffer"); - /* - memcpy(&mylen, picotap->pico_frame_rxbuf, sizeof(len)); - memmove(tap->pico_frame_rxbuf, tap->pico_frame_rxbuf + mylen, MAX_PICO_FRAME_RX_BUF_SZ-mylen); // shift buffer - picotap->pico_frame_rxbuf_tot-=mylen; - */ memset(tap->pico_frame_rxbuf,0,MAX_PICO_FRAME_RX_BUF_SZ); - picotap->pico_frame_rxbuf_tot=0; + tap->pico_frame_rxbuf_tot=0; } memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot, &newlen, sizeof(newlen)); // size of frame + meta memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot + sizeof(newlen), ðhdr, sizeof(ethhdr)); // new eth header memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot + sizeof(newlen) + sizeof(ethhdr), data, len); // frame data tap->pico_frame_rxbuf_tot += newlen; - DEBUG_FLOW(" [ ZTWIRE -> FBUF ] Move FRAME(sz=%d) into FBUF(sz=%d), data_len=%d", newlen, picotap->pico_frame_rxbuf_tot, len); + DEBUG_FLOW(" [ ZWIRE -> FBUF ] Move FRAME(sz=%d) into FBUF(sz=%d), data_len=%d", newlen, tap->pico_frame_rxbuf_tot, len); } int pico_eth_poll(struct pico_device *dev, int loop_score) { - // OPTIMIZATION: The copy logic and/or buffer structure should be reworked for better performance after the BETA + SocketTap *tap = (SocketTap*)(dev->tap); + // FIXME: The copy logic and/or buffer structure should be reworked for better performance after the BETA // SocketTap *tap = (SocketTap*)netif->state; - Mutex::Lock _l(picotap->_pico_frame_rxbuf_m); + Mutex::Lock _l(tap->_pico_frame_rxbuf_m); unsigned char frame[ZT_SDK_MTU]; int len; - while (picotap->pico_frame_rxbuf_tot > 0 && loop_score > 0) { - //DEBUG_FLOW(" [ FBUF -> STACK] Frame buffer SZ=%d", picotap->pico_frame_rxbuf_tot); + int err; + while (tap->pico_frame_rxbuf_tot > 0 && loop_score > 0) { + //DEBUG_FLOW(" [ FBUF -> STACK] Frame buffer SZ=%d", tap->pico_frame_rxbuf_tot); memset(frame, 0, sizeof(frame)); len = 0; - memcpy(&len, picotap->pico_frame_rxbuf, sizeof(len)); // get frame len + memcpy(&len, tap->pico_frame_rxbuf, sizeof(len)); // get frame len if(len >= 0) { - //DEBUG_FLOW(" [ FBUF -> STACK] Moving FRAME of size (%d) from FBUF(sz=%d) into stack",len, picotap->pico_frame_rxbuf_tot-len); - memcpy(frame, picotap->pico_frame_rxbuf + sizeof(len), len-(sizeof(len)) ); // get frame data - memmove(picotap->pico_frame_rxbuf, picotap->pico_frame_rxbuf + len, MAX_PICO_FRAME_RX_BUF_SZ-len); // shift buffer - pico_stack_recv(dev, (uint8_t*)frame, (len-sizeof(len))); - picotap->pico_frame_rxbuf_tot-=len; + //DEBUG_FLOW(" [ FBUF -> STACK] Moving FRAME of size (%d) from FBUF(sz=%d) into stack",len, tap->pico_frame_rxbuf_tot-len); + memcpy(frame, tap->pico_frame_rxbuf + sizeof(len), len-(sizeof(len)) ); // get frame data + memmove(tap->pico_frame_rxbuf, tap->pico_frame_rxbuf + len, MAX_PICO_FRAME_RX_BUF_SZ-len); // shift buffer + err = pico_stack_recv(dev, (uint8_t*)frame, (len-sizeof(len))); + tap->pico_frame_rxbuf_tot-=len; } else { DEBUG_ERROR("Invalid frame size (%d). Exiting.",len); - exit(0); + zt_dump_stacktrace(0); } loop_score--; } return loop_score; } - void picoTCP::pico_handleWrite(Connection *conn) - { - DEBUG_INFO(); - if(!conn || !conn->picosock) { - DEBUG_ERROR(" invalid connection"); - return; - } - - int max, r, max_write_len = conn->txsz < ZT_SDK_MTU ? conn->txsz : ZT_SDK_MTU; - if((r = pico_socket_write(conn->picosock, &conn->txbuf, max_write_len)) < 0) { - DEBUG_ERROR("unable to write to picosock=%p, r=%d", (conn->picosock), r); - return; - } - - // adjust buffer - int sz = (conn->txsz)-r; - if(sz) - memmove(&conn->txbuf, (conn->txbuf+r), sz); - conn->txsz -= r; - - if(conn->socket_type == SOCK_STREAM) { - max = ZT_TCP_TX_BUF_SZ; - DEBUG_TRANS("[TCP TX] ---> :: {TX: %.3f%%, RX: %.3f%%, physock=%p} :: %d bytes", - (float)conn->txsz / (float)max, (float)conn->rxsz / max, conn->sock, r); - } - if(conn->socket_type == SOCK_DGRAM) { - max = ZT_UDP_TX_BUF_SZ; - DEBUG_TRANS("[UDP TX] ---> :: {TX: %.3f%%, RX: %.3f%%, physock=%p} :: %d bytes", - (float)conn->txsz / (float)max, (float)conn->rxsz / max, conn->sock, r); - } - } - int picoTCP::pico_Connect(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen) { int err; @@ -483,8 +440,8 @@ namespace ZeroTier { char ipv6_str[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN); pico_string_to_ipv6(ipv6_str, zaddr.addr); - DEBUG_ATTN("addr=%s:%d", ipv6_str, Utils::ntoh(addr->sin_port)); - err = pico_socket_connect(conn->picosock, &zaddr, (struct sockaddr_in *)&addr->sin_port); + DEBUG_ATTN("addr=%s:%d", ipv6_str, Utils::ntoh(in6->sin6_port)); + err = pico_socket_connect(conn->picosock, &zaddr, in6->sin6_port); #endif memcpy(&(conn->peer_addr), &addr, sizeof(struct sockaddr_storage)); @@ -516,7 +473,7 @@ namespace ZeroTier { inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN); pico_string_to_ipv6(ipv6_str, zaddr.addr); //DEBUG_ATTN("addr=%s:%d, physock=%p, picosock=%p", ipv6_str, Utils::ntoh(addr->sin_port), sock, (conn->picosock)); - err = pico_socket_bind(conn->picosock, &zaddr, (struct sockaddr_in *)&addr->sin_port); + err = pico_socket_bind(conn->picosock, &zaddr, (uint16_t *)in6->sin6_port); #endif if(err < 0) { DEBUG_ERROR("unable to bind pico_socket(%p), err=%d", (conn->picosock), err); @@ -546,16 +503,17 @@ namespace ZeroTier { if((err = pico_socket_listen(conn->picosock, backlog)) < 0) { if(err == PICO_ERR_EINVAL) { - DEBUG_ERROR("PICO_ERR_EINVAL - invalid argument"); + DEBUG_ERROR("PICO_ERR_EINVAL"); errno = EINVAL; return -1; } if(err == PICO_ERR_EISCONN) { - DEBUG_ERROR("PICO_ERR_EISCONN - socket is connected"); + DEBUG_ERROR("PICO_ERR_EISCONN"); errno = EISCONN; return -1; } } + conn->state = ZT_SOCK_STATE_LISTENING; return ZT_ERR_OK; } @@ -574,17 +532,17 @@ namespace ZeroTier { return err; } - void picoTCP::pico_handleRead(PhySocket *sock,void **uptr,bool lwip_invoked) + void picoTCP::pico_Read(SocketTap *tap, PhySocket *sock, void **uptr, bool stack_invoked) { DEBUG_INFO(); - if(!lwip_invoked) { + if(!stack_invoked) { // The stack thread writes to RXBUF as well - picotap->_tcpconns_m.lock(); - picotap->_rx_buf_m.lock(); + tap->_tcpconns_m.lock(); + tap->_rx_buf_m.lock(); } int tot = 0, n = -1, write_attempts = 0; - Connection *conn = picotap->getConnection(sock); + Connection *conn = (Connection*)uptr; if(conn && conn->rxsz) { // @@ -592,7 +550,7 @@ namespace ZeroTier { // Try to write ZT_SDK_MTU-sized chunk to app socket while(tot < ZT_SDK_MTU) { write_attempts++; - n = picotap->_phy.streamSend(conn->sock, (conn->rxbuf)+tot, ZT_SDK_MTU); + n = tap->_phy.streamSend(conn->sock, (conn->rxbuf)+tot, ZT_SDK_MTU); tot += n; DEBUG_FLOW(" [ ZTSOCK <- RXBUF] wrote = %d, errno=%d", n, errno); // If socket is unavailable, attempt to write N times before giving up @@ -620,7 +578,7 @@ namespace ZeroTier { // if(conn->socket_type==SOCK_STREAM) { DEBUG_TRANS("writing to conn->sock = %p", conn->sock); - n = picotap->_phy.streamSend(conn->sock, conn->rxbuf, conn->rxsz); + n = tap->_phy.streamSend(conn->sock, conn->rxbuf, conn->rxsz); if(conn->rxsz-n > 0) // If more remains on buffer memcpy(conn->rxbuf, conn->rxbuf+n, conn->rxsz - n); conn->rxsz -= n; @@ -636,37 +594,67 @@ namespace ZeroTier { #endif } if(conn->rxsz == 0) { - picotap->_phy.setNotifyWritable(sock, false); + tap->_phy.setNotifyWritable(sock, false); } else { - picotap->_phy.setNotifyWritable(sock, true); + tap->_phy.setNotifyWritable(sock, true); } } else { - picotap->_phy.setNotifyWritable(sock, false); + tap->_phy.setNotifyWritable(sock, false); } } - if(!lwip_invoked) { - picotap->_tcpconns_m.unlock(); - picotap->_rx_buf_m.unlock(); + if(!stack_invoked) { + tap->_tcpconns_m.unlock(); + tap->_rx_buf_m.unlock(); } // FIXME: Re-write debug traces DEBUG_FLOW(" [ ZTSOCK <- RXBUF] Emitted (%d) from RXBUF(%d) to socket", tot, conn->rxsz); } - void picoTCP::pico_handleClose(PhySocket *sock) + void picoTCP::pico_Write(Connection *conn) { DEBUG_INFO(); - /* - int ret; - if(conn && conn->picosock) { - if((ret = pico_socket_close(conn->picosock)) < 0) { - DEBUG_ERROR("error closing pico_socket(%p)", (void*)(conn->picosock)); - // sendReturnValue() - } + if(!conn || !conn->picosock) { + DEBUG_ERROR(" invalid connection"); + return; + } + int max, r, max_write_len = conn->txsz < ZT_SDK_MTU ? conn->txsz : ZT_SDK_MTU; + if((r = pico_socket_write(conn->picosock, &conn->txbuf, max_write_len)) < 0) { + DEBUG_ERROR("unable to write to picosock=%p, r=%d", (conn->picosock), r); return; + } + // adjust buffer + int sz = (conn->txsz)-r; + if(sz) + memmove(&conn->txbuf, (conn->txbuf+r), sz); + conn->txsz -= r; + + if(conn->socket_type == SOCK_STREAM) { + max = ZT_TCP_TX_BUF_SZ; + DEBUG_TRANS("[TCP TX] ---> :: {TX: %.3f%%, RX: %.3f%%, physock=%p} :: %d bytes", + (float)conn->txsz / (float)max, (float)conn->rxsz / max, conn->sock, r); + } + if(conn->socket_type == SOCK_DGRAM) { + max = ZT_UDP_TX_BUF_SZ; + DEBUG_TRANS("[UDP TX] ---> :: {TX: %.3f%%, RX: %.3f%%, physock=%p} :: %d bytes", + (float)conn->txsz / (float)max, (float)conn->rxsz / max, conn->sock, r); + } + } + + int picoTCP::pico_Close(Connection *conn) + { + DEBUG_INFO(); + int err; + if(conn && conn->picosock) { + if((err = pico_socket_close(conn->picosock)) < 0) { + errno = pico_err; + DEBUG_ERROR("error closing pico_socket(%p)", (void*)(conn->picosock)); + return -1; + } + return err; } DEBUG_ERROR("invalid connection or pico_socket"); - */ + return -1; } } diff --git a/src/picoTCP.hpp b/src/picoTCP.hpp index b128358..f4d1d2e 100644 --- a/src/picoTCP.hpp +++ b/src/picoTCP.hpp @@ -32,7 +32,7 @@ #include "SocketTap.hpp" /****************************************************************************/ -/* PicoTCP API Signatures */ +/* PicoTCP API Signatures (See ZeroTierSDK.h for the API an app should use) */ /****************************************************************************/ #define PICO_IPV4_TO_STRING_SIG char *ipbuf, const uint32_t ip @@ -100,19 +100,44 @@ namespace ZeroTier static void pico_cb_socket_activity(uint16_t ev, struct pico_socket *s); /* - * Where packets enter the stack + * Packets from the ZeroTier virtual wire enter the stack here */ void pico_rx(SocketTap *tap, const ZeroTier::MAC &from,const ZeroTier::MAC &to,unsigned int etherType,const void *data,unsigned int len); - void pico_handleRead(ZeroTier::PhySocket *sock,void **uptr,bool lwip_invoked); - void pico_handleClose(ZeroTier::PhySocket *sock); - void pico_handleWrite(Connection *conn); - - // common socket operations + /* + * Connect to remote host via userspace network stack interface - Called from SocketTap + */ int pico_Connect(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen); + + /* + * Bind to a userspace network stack interface - Called from SocketTap + */ int pico_Bind(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen); + + /* + * Listen for incoming connections - Called from SocketTap + */ int pico_Listen(Connection *conn, int fd, int backlog); + + /* + * Accept an incoming connection - Called from SocketTap + */ int pico_Accept(Connection *conn); + + /* + * Read from RX buffer to application - Called from SocketTap + */ + void pico_Read(SocketTap *tap, ZeroTier::PhySocket *sock,void **uptr,bool stack_invoked); + + /* + * Write to userspace network stack - Called from SocketTap + */ + void pico_Write(Connection *conn); + + /* + * Close a Connection - Called from SocketTap + */ + int pico_Close(Connection *conn); }; } diff --git a/test/dumb/tcpclient4.c b/test/dumb/tcpclient4.cpp similarity index 90% rename from test/dumb/tcpclient4.c rename to test/dumb/tcpclient4.cpp index cf4b345..0d2e731 100644 --- a/test/dumb/tcpclient4.c +++ b/test/dumb/tcpclient4.cpp @@ -5,6 +5,7 @@ #include #include #include +#include int atoi(const char *str); int close(int filedes); @@ -14,7 +15,7 @@ int close(int filedes); int main(int argc , char *argv[]) { if(argc < 3) { - printf("usage: client \n"); + printf("usage: client \n"); return 1; } @@ -37,7 +38,7 @@ int main(int argc , char *argv[]) } printf("connected\n"); - char *msg = "welcome to the machine!"; + char *msg = (char*)"welcome to the machine!"; while(1) { diff --git a/test/dumb/tcpclient6.c b/test/dumb/tcpclient6.cpp similarity index 85% rename from test/dumb/tcpclient6.c rename to test/dumb/tcpclient6.cpp index a4aadd4..00d2324 100644 --- a/test/dumb/tcpclient6.c +++ b/test/dumb/tcpclient6.cpp @@ -20,7 +20,7 @@ int main(int argc, char *argv[]) { char buffer[256] = "This is a string from client!"; if (argc < 3) { - fprintf(stderr, "Usage: %s \n", argv[0]); + printf("Usage: %s \n", argv[0]); exit(0); } portno = atoi(argv[2]); @@ -30,12 +30,12 @@ int main(int argc, char *argv[]) { //Sockets Layer Call: socket() sockfd = socket(AF_INET6, SOCK_STREAM, 0); if (sockfd < 0) - error("ERROR opening socket"); + printf("ERROR opening socket"); //Sockets Layer Call: gethostbyname2() server = gethostbyname2(argv[1],AF_INET6); if (server == NULL) { - fprintf(stderr, "ERROR, no such host\n"); + printf("ERROR, no such host\n"); exit(0); } @@ -47,13 +47,13 @@ int main(int argc, char *argv[]) { //Sockets Layer Call: connect() if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) - error("ERROR connecting"); + printf("ERROR connecting"); //Sockets Layer Call: send() n = send(sockfd,buffer, strlen(buffer)+1, 0); if (n < 0) - error("ERROR writing to socket"); + printf("ERROR writing to socket"); printf("sent %d bytes\n", n); memset(buffer, 0, 256); @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) { printf("reading...\n"); n = recv(sockfd, buffer, 255, 0); if (n < 0) - error("ERROR reading from socket"); + printf("ERROR reading from socket"); printf("Message from server: %s\n", buffer); //Sockets Layer Call: close() diff --git a/test/dumb/tcpserver4.c b/test/dumb/tcpserver4.cpp similarity index 81% rename from test/dumb/tcpserver4.c rename to test/dumb/tcpserver4.cpp index 1480dbb..b3d929a 100644 --- a/test/dumb/tcpserver4.c +++ b/test/dumb/tcpserver4.cpp @@ -46,16 +46,18 @@ int main(int argc , char *argv[]) unsigned long count = 0; while(1) { - count++; - int bytes_read = read(client_sock, client_message, msglen); - printf("[%lu] RX = (%d): ", count, bytes_read); + count++; + int bytes_read = read(client_sock, client_message, msglen); + printf("[%lu] RX = (%d): %s\n", count, bytes_read, client_message); + + /* for(int i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ZeroTierSDK.h" + +#define PASSED 0 +#define FAILED -1 + +std::string str = "welcome to the machine"; + +// [] random +// [OK] simple client ipv4 +// [OK] simple server ipv4 +// [?] simple client ipv6 +// [?] simple server ipv6 +// [OK] sustained client ipv4 +// [OK] sustained server ipv4 +// [?] sustained client ipv6 +// [?] sustained server ipv6 +// [] comprehensive client ipv4 +// [] comprehensive server ipv6 + +// --- + +// comprehensive client addr port +// comprehensive server port +// simple [4|6] client addr port +// simple [4|6] server port + +/****************************************************************************/ +/* SIMPLE CLIENT */ +/****************************************************************************/ + +// +int ipv4_tcp_client_test(char *path, char *nwid, struct sockaddr_in *addr, int port) +{ + int sockfd, err; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_connect(sockfd, (const struct sockaddr *)addr, sizeof(addr))) < 0) { + printf("error connecting to remote host (%d)\n", err); + exit(0); + } + int wrote = zts_write(sockfd, str.c_str(), str.length()); + err = zts_close(sockfd); + return (wrote == str.length() && !err) ? PASSED : FAILED; // if wrote correct number of bytes +} + +// +int ipv6_tcp_client_test(char *path, char *nwid, struct sockaddr_in6 *addr, int port) +{ + int sockfd, err; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_connect(sockfd, (const struct sockaddr *)addr, sizeof(addr))) < 0) { + printf("error connecting to remote host (%d)\n", err); + exit(0); + } + int wrote = zts_write(sockfd, str.c_str(), str.length()); + err = zts_close(sockfd); + return (wrote == str.length() && !err) ? PASSED : FAILED; // if wrote correct number of bytes +} + + + + +/****************************************************************************/ +/* SIMPLE SERVER */ +/****************************************************************************/ + +// +int ipv4_tcp_server_test(char *path, char *nwid, struct sockaddr_in *addr, int port) +{ + int sockfd, err; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_bind(sockfd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) < 0)) { + printf("error binding to interface (%d)\n", err); + exit(0); + } + if((err = zts_listen(sockfd, 1)) < 0) { + printf("error placing socket in LISTENING state (%d)\n", err); + exit(0); + } + // TODO: handle new address + if((err = zts_accept(sockfd, (struct sockaddr *)&addr, (socklen_t *)sizeof(addr)) < 0)) { + printf("error accepting connection (%d)\n", err); + exit(0); + } + int wrote = zts_write(sockfd, str.c_str(), str.length()); + err = zts_close(sockfd); + return (wrote == str.length() && !err) ? PASSED : FAILED; // if wrote correct number of bytes +} + +// +int ipv6_tcp_server_test(char *path, char *nwid, struct sockaddr_in6 *addr, int port) +{ + int sockfd, err; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_bind(sockfd, (struct sockaddr *)addr, sizeof(struct sockaddr_in6)) < 0)) { + printf("error binding to interface (%d)\n", err); + exit(0); + } + if((err = zts_listen(sockfd, 1)) < 0) { + printf("error placing socket in LISTENING state (%d)\n", err); + exit(0); + } + // TODO: handle new address + if((err = zts_accept(sockfd, (struct sockaddr *)&addr, (socklen_t *)sizeof(addr)) < 0)) { + printf("error accepting connection (%d)\n", err); + exit(0); + } + int wrote = zts_write(sockfd, str.c_str(), str.length()); + err = zts_close(sockfd); + return (wrote == str.length() && !err) ? PASSED : FAILED; // if wrote correct number of bytes +} + + + + + +/****************************************************************************/ +/* SUSTAINED CLIENT */ +/****************************************************************************/ + +// Maintain transfer for n_seconds OR n_times +int ipv4_tcp_client_sustained_test(char *path, char *nwid, struct sockaddr_in *addr, int port, int n_seconds, int n_times) +{ + int sockfd, err; + int msg_len = str.length(); + int wrote = 0; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_connect(sockfd, (const struct sockaddr *)addr, sizeof(addr))) < 0) { + printf("error connecting to remote host (%d)\n", err); + exit(0); + } + if(n_seconds) { + /* + printf("testing for (%d) seconds\n", n_seconds); + int wrote; + for(int i=0; ih_addr, server->h_length); + addr6.sin6_port = htons(port); + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv6_tcp_client_test(path, (char*)nwid.c_str(), &addr6, port) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + } + + // SIMPLE SERVER + if(mode == "server") + { + port = atoi(argv[5]); + printf("serving on port %s\n", argv[6]); + + // IPv4 + if(protocol == "4") { + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr("10.9.9.40"); + // addr.sin_addr.s_addr = htons(INADDR_ANY); + addr.sin_family = AF_INET; + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv4_tcp_server_test(path, (char*)nwid.c_str(), &addr, port) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + + // IPv6 + if(protocol == "6") { + server = gethostbyname2(argv[1],AF_INET6); + memset((char *) &addr6, 0, sizeof(addr6)); + addr6.sin6_flowinfo = 0; + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(port); + + addr6.sin6_addr = in6addr_any; + //memmove((char *) &addr6.sin6_addr.s6_addr, (char *) server->h_addr, server->h_length); + + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv6_tcp_server_test(path, (char*)nwid.c_str(), &addr6, port) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + } + } + + /****************************************************************************/ + /* SUSTAINED */ + /****************************************************************************/ + + // SUSTAINED + // Performs a stress test for benchmarking performance + if(type == "sustained") + { + protocol = argv[3]; // 4, 6 + mode = argv[4]; // client, server + + // SUSTAINED CLIENT + if(mode == "client") + { + port = atoi(argv[6]); + printf("connecting to %s on port %s\n", argv[5], argv[6]); + + // IPv4 + if(protocol == "4") { + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(argv[5]); + addr.sin_family = AF_INET; + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv4_tcp_client_sustained_test(path, (char*)nwid.c_str(), &addr, port, n_seconds, n_times) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + + // IPv6 + if(protocol == "6") { + server = gethostbyname2(argv[1],AF_INET6); + memset((char *) &addr6, 0, sizeof(addr6)); + addr6.sin6_flowinfo = 0; + addr6.sin6_family = AF_INET6; + memmove((char *) &addr6.sin6_addr.s6_addr, (char *) server->h_addr, server->h_length); + addr6.sin6_port = htons(port); + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv6_tcp_client_sustained_test(path, (char*)nwid.c_str(), &addr6, port, n_seconds, n_times) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + } + + // SUSTAINED SERVER + if(mode == "server") + { + port = atoi(argv[5]); + printf("serving on port %s\n", argv[6]); + + // IPv4 + if(protocol == "4") { + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr("10.9.9.0"); + // addr.sin_addr.s_addr = htons(INADDR_ANY); + addr.sin_family = AF_INET; + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv4_tcp_server_sustained_test(path, (char*)nwid.c_str(), &addr, port, n_seconds, n_times) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + + // IPv6 + if(protocol == "6") { + server = gethostbyname2(argv[1],AF_INET6); + memset((char *) &addr6, 0, sizeof(addr6)); + addr6.sin6_flowinfo = 0; + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(port); + + addr6.sin6_addr = in6addr_any; + //memmove((char *) &addr6.sin6_addr.s6_addr, (char *) server->h_addr, server->h_length); + + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv6_tcp_server_sustained_test(path, (char*)nwid.c_str(), &addr6, port, n_seconds, n_times) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + } + } + + + /****************************************************************************/ + /* COMPREHENSIVE */ + /****************************************************************************/ + + // COMPREHENSIVE + // Tests ALL API calls + if(type == "comprehensive") + { + + } + + + /****************************************************************************/ + /* RANDOM */ + /****************************************************************************/ + + // RANDOM + // performs random API calls with plausible (and random) arguments/data + if(type == "random") + { + + } + + printf("invalid configuration. exiting.\n"); + return 0; +} \ No newline at end of file diff --git a/test/unit/b.cpp b/test/unit/b.cpp new file mode 100644 index 0000000..486bc73 --- /dev/null +++ b/test/unit/b.cpp @@ -0,0 +1,554 @@ +// Comprehensive stress test for socket-like API + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ZeroTierSDK.h" + +#define PASSED 0 +#define FAILED -1 + +std::string str = "welcome to the machine"; + +// [] random +// [OK] simple client ipv4 +// [OK] simple server ipv4 +// [?] simple client ipv6 +// [?] simple server ipv6 +// [OK] sustained client ipv4 +// [OK] sustained server ipv4 +// [?] sustained client ipv6 +// [?] sustained server ipv6 +// [] comprehensive client ipv4 +// [] comprehensive server ipv6 + +// --- + +// comprehensive client addr port +// comprehensive server port +// simple [4|6] client addr port +// simple [4|6] server port + +/****************************************************************************/ +/* SIMPLE CLIENT */ +/****************************************************************************/ + +// +int ipv4_tcp_client_test(char *path, char *nwid, struct sockaddr_in *addr, int port) +{ + int sockfd, err; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_connect(sockfd, (const struct sockaddr *)addr, sizeof(addr))) < 0) { + printf("error connecting to remote host (%d)\n", err); + exit(0); + } + int wrote = zts_write(sockfd, str.c_str(), str.length()); + err = zts_close(sockfd); + return (wrote == str.length() && !err) ? PASSED : FAILED; // if wrote correct number of bytes +} + +// +int ipv6_tcp_client_test(char *path, char *nwid, struct sockaddr_in6 *addr, int port) +{ + int sockfd, err; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_connect(sockfd, (const struct sockaddr *)addr, sizeof(addr))) < 0) { + printf("error connecting to remote host (%d)\n", err); + exit(0); + } + int wrote = zts_write(sockfd, str.c_str(), str.length()); + err = zts_close(sockfd); + return (wrote == str.length() && !err) ? PASSED : FAILED; // if wrote correct number of bytes +} + + + + +/****************************************************************************/ +/* SIMPLE SERVER */ +/****************************************************************************/ + +// +int ipv4_tcp_server_test(char *path, char *nwid, struct sockaddr_in *addr, int port) +{ + int sockfd, err; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_bind(sockfd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) < 0)) { + printf("error binding to interface (%d)\n", err); + exit(0); + } + if((err = zts_listen(sockfd, 1)) < 0) { + printf("error placing socket in LISTENING state (%d)\n", err); + exit(0); + } + // TODO: handle new address + if((err = zts_accept(sockfd, (struct sockaddr *)&addr, (socklen_t *)sizeof(addr)) < 0)) { + printf("error accepting connection (%d)\n", err); + exit(0); + } + int wrote = zts_write(sockfd, str.c_str(), str.length()); + err = zts_close(sockfd); + return (wrote == str.length() && !err) ? PASSED : FAILED; // if wrote correct number of bytes +} + +// +int ipv6_tcp_server_test(char *path, char *nwid, struct sockaddr_in6 *addr, int port) +{ + int sockfd, err; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_bind(sockfd, (struct sockaddr *)addr, sizeof(struct sockaddr_in6)) < 0)) { + printf("error binding to interface (%d)\n", err); + exit(0); + } + if((err = zts_listen(sockfd, 1)) < 0) { + printf("error placing socket in LISTENING state (%d)\n", err); + exit(0); + } + // TODO: handle new address + if((err = zts_accept(sockfd, (struct sockaddr *)&addr, (socklen_t *)sizeof(addr)) < 0)) { + printf("error accepting connection (%d)\n", err); + exit(0); + } + int wrote = zts_write(sockfd, str.c_str(), str.length()); + err = zts_close(sockfd); + return (wrote == str.length() && !err) ? PASSED : FAILED; // if wrote correct number of bytes +} + + + + + +/****************************************************************************/ +/* SUSTAINED CLIENT */ +/****************************************************************************/ + +// Maintain transfer for n_seconds OR n_times +int ipv4_tcp_client_sustained_test(char *path, char *nwid, struct sockaddr_in *addr, int port, int n_seconds, int n_times) +{ + int sockfd, err; + int msg_len = str.length(); + int wrote = 0; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_connect(sockfd, (const struct sockaddr *)addr, sizeof(addr))) < 0) { + printf("error connecting to remote host (%d)\n", err); + exit(0); + } + if(n_seconds) { + /* + printf("testing for (%d) seconds\n", n_seconds); + int wrote; + for(int i=0; ih_addr, server->h_length); + addr6.sin6_port = htons(port); + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv6_tcp_client_test(path, (char*)nwid.c_str(), &addr6, port) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + } + + // SIMPLE SERVER + if(mode == "server") + { + port = atoi(argv[5]); + printf("serving on port %s\n", argv[6]); + + // IPv4 + if(protocol == "4") { + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr("10.9.9.40"); + // addr.sin_addr.s_addr = htons(INADDR_ANY); + addr.sin_family = AF_INET; + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv4_tcp_server_test(path, (char*)nwid.c_str(), &addr, port) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + + // IPv6 + if(protocol == "6") { + server = gethostbyname2(argv[1],AF_INET6); + memset((char *) &addr6, 0, sizeof(addr6)); + addr6.sin6_flowinfo = 0; + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(port); + + addr6.sin6_addr = in6addr_any; + //memmove((char *) &addr6.sin6_addr.s6_addr, (char *) server->h_addr, server->h_length); + + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv6_tcp_server_test(path, (char*)nwid.c_str(), &addr6, port) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + } + } + + /****************************************************************************/ + /* SUSTAINED */ + /****************************************************************************/ + + // SUSTAINED + // Performs a stress test for benchmarking performance + if(type == "sustained") + { + protocol = argv[3]; // 4, 6 + mode = argv[4]; // client, server + + // SUSTAINED CLIENT + if(mode == "client") + { + port = atoi(argv[6]); + printf("connecting to %s on port %s\n", argv[5], argv[6]); + + // IPv4 + if(protocol == "4") { + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(argv[5]); + addr.sin_family = AF_INET; + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv4_tcp_client_sustained_test(path, (char*)nwid.c_str(), &addr, port, n_seconds, n_times) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + + // IPv6 + if(protocol == "6") { + server = gethostbyname2(argv[1],AF_INET6); + memset((char *) &addr6, 0, sizeof(addr6)); + addr6.sin6_flowinfo = 0; + addr6.sin6_family = AF_INET6; + memmove((char *) &addr6.sin6_addr.s6_addr, (char *) server->h_addr, server->h_length); + addr6.sin6_port = htons(port); + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv6_tcp_client_sustained_test(path, (char*)nwid.c_str(), &addr6, port, n_seconds, n_times) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + } + + // SUSTAINED SERVER + if(mode == "server") + { + port = atoi(argv[5]); + printf("serving on port %s\n", argv[6]); + + // IPv4 + if(protocol == "4") { + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr("10.9.9.0"); + // addr.sin_addr.s_addr = htons(INADDR_ANY); + addr.sin_family = AF_INET; + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv4_tcp_server_sustained_test(path, (char*)nwid.c_str(), &addr, port, n_seconds, n_times) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + + // IPv6 + if(protocol == "6") { + server = gethostbyname2(argv[1],AF_INET6); + memset((char *) &addr6, 0, sizeof(addr6)); + addr6.sin6_flowinfo = 0; + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(port); + + addr6.sin6_addr = in6addr_any; + //memmove((char *) &addr6.sin6_addr.s6_addr, (char *) server->h_addr, server->h_length); + + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv6_tcp_server_sustained_test(path, (char*)nwid.c_str(), &addr6, port, n_seconds, n_times) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + } + } + + + /****************************************************************************/ + /* COMPREHENSIVE */ + /****************************************************************************/ + + // COMPREHENSIVE + // Tests ALL API calls + if(type == "comprehensive") + { + + } + + + /****************************************************************************/ + /* RANDOM */ + /****************************************************************************/ + + // RANDOM + // performs random API calls with plausible (and random) arguments/data + if(type == "random") + { + + } + + printf("invalid configuration. exiting.\n"); + return 0; +} \ No newline at end of file diff --git a/test/unit/client.cpp b/test/unit/client.cpp deleted file mode 100644 index c77a68d..0000000 --- a/test/unit/client.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// Comprehensive stress test for socket-like API - -#include - -/****************************************************************************/ -/* Test Functions */ -/****************************************************************************/ - -int test_for_correctness() -{ - return 0; -} - -int ipv4_udp_client() -{ - return 0; -} - -int ipv6_udp_client() -{ - return 0; -} - -int ipv4_tcp_client() -{ - return 0; -} - -int ipv6_tcp_client() -{ - return 0; -} - -/****************************************************************************/ -/* main */ -/****************************************************************************/ - -int main() -{ - int test_all = 1; - - if(test_all) - { - printf("Testing API calls for correctness\n"); - test_for_correctness(); - - printf("Testing as IPv4 UDP Client\n"); - ipv4_udp_client(); - - printf("Testing as IPv6 UDP Client\n"); - ipv6_udp_client(); - - printf("Testing as IPv4 TCP Client\n"); - ipv4_udp_client(); - - printf("Testing as IPv6 TCP Client\n"); - ipv6_udp_client(); - - printf("Testing \n"); - printf("\n"); - printf("\n"); - } - return 0; -} \ No newline at end of file diff --git a/test/unit/comprehensive.cpp b/test/unit/comprehensive.cpp index f8fe832..486bc73 100644 --- a/test/unit/comprehensive.cpp +++ b/test/unit/comprehensive.cpp @@ -1,64 +1,554 @@ // Comprehensive stress test for socket-like API +#include +#include +#include +#include +#include +#include +#include #include +#include +#include + +#include "ZeroTierSDK.h" + +#define PASSED 0 +#define FAILED -1 + +std::string str = "welcome to the machine"; + +// [] random +// [OK] simple client ipv4 +// [OK] simple server ipv4 +// [?] simple client ipv6 +// [?] simple server ipv6 +// [OK] sustained client ipv4 +// [OK] sustained server ipv4 +// [?] sustained client ipv6 +// [?] sustained server ipv6 +// [] comprehensive client ipv4 +// [] comprehensive server ipv6 + +// --- + +// comprehensive client addr port +// comprehensive server port +// simple [4|6] client addr port +// simple [4|6] server port /****************************************************************************/ -/* Test Functions */ +/* SIMPLE CLIENT */ /****************************************************************************/ -int test_for_correctness() +// +int ipv4_tcp_client_test(char *path, char *nwid, struct sockaddr_in *addr, int port) { - return 0; + int sockfd, err; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_connect(sockfd, (const struct sockaddr *)addr, sizeof(addr))) < 0) { + printf("error connecting to remote host (%d)\n", err); + exit(0); + } + int wrote = zts_write(sockfd, str.c_str(), str.length()); + err = zts_close(sockfd); + return (wrote == str.length() && !err) ? PASSED : FAILED; // if wrote correct number of bytes } -int ipv4_udp_server() +// +int ipv6_tcp_client_test(char *path, char *nwid, struct sockaddr_in6 *addr, int port) { - return 0; + int sockfd, err; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_connect(sockfd, (const struct sockaddr *)addr, sizeof(addr))) < 0) { + printf("error connecting to remote host (%d)\n", err); + exit(0); + } + int wrote = zts_write(sockfd, str.c_str(), str.length()); + err = zts_close(sockfd); + return (wrote == str.length() && !err) ? PASSED : FAILED; // if wrote correct number of bytes } -int ipv6_udp_server() + + + +/****************************************************************************/ +/* SIMPLE SERVER */ +/****************************************************************************/ + +// +int ipv4_tcp_server_test(char *path, char *nwid, struct sockaddr_in *addr, int port) { - return 0; + int sockfd, err; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_bind(sockfd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) < 0)) { + printf("error binding to interface (%d)\n", err); + exit(0); + } + if((err = zts_listen(sockfd, 1)) < 0) { + printf("error placing socket in LISTENING state (%d)\n", err); + exit(0); + } + // TODO: handle new address + if((err = zts_accept(sockfd, (struct sockaddr *)&addr, (socklen_t *)sizeof(addr)) < 0)) { + printf("error accepting connection (%d)\n", err); + exit(0); + } + int wrote = zts_write(sockfd, str.c_str(), str.length()); + err = zts_close(sockfd); + return (wrote == str.length() && !err) ? PASSED : FAILED; // if wrote correct number of bytes } -int ipv4_tcp_server() +// +int ipv6_tcp_server_test(char *path, char *nwid, struct sockaddr_in6 *addr, int port) { - return 0; + int sockfd, err; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_bind(sockfd, (struct sockaddr *)addr, sizeof(struct sockaddr_in6)) < 0)) { + printf("error binding to interface (%d)\n", err); + exit(0); + } + if((err = zts_listen(sockfd, 1)) < 0) { + printf("error placing socket in LISTENING state (%d)\n", err); + exit(0); + } + // TODO: handle new address + if((err = zts_accept(sockfd, (struct sockaddr *)&addr, (socklen_t *)sizeof(addr)) < 0)) { + printf("error accepting connection (%d)\n", err); + exit(0); + } + int wrote = zts_write(sockfd, str.c_str(), str.length()); + err = zts_close(sockfd); + return (wrote == str.length() && !err) ? PASSED : FAILED; // if wrote correct number of bytes } -int ipv6_tcp_server() + + + + +/****************************************************************************/ +/* SUSTAINED CLIENT */ +/****************************************************************************/ + +// Maintain transfer for n_seconds OR n_times +int ipv4_tcp_client_sustained_test(char *path, char *nwid, struct sockaddr_in *addr, int port, int n_seconds, int n_times) { - return 0; + int sockfd, err; + int msg_len = str.length(); + int wrote = 0; + if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket"); + exit(0); + } + if((err = zts_connect(sockfd, (const struct sockaddr *)addr, sizeof(addr))) < 0) { + printf("error connecting to remote host (%d)\n", err); + exit(0); + } + if(n_seconds) { + /* + printf("testing for (%d) seconds\n", n_seconds); + int wrote; + for(int i=0; ih_addr, server->h_length); + addr6.sin6_port = htons(port); + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv6_tcp_client_test(path, (char*)nwid.c_str(), &addr6, port) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + } + + // SIMPLE SERVER + if(mode == "server") + { + port = atoi(argv[5]); + printf("serving on port %s\n", argv[6]); + + // IPv4 + if(protocol == "4") { + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr("10.9.9.40"); + // addr.sin_addr.s_addr = htons(INADDR_ANY); + addr.sin_family = AF_INET; + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv4_tcp_server_test(path, (char*)nwid.c_str(), &addr, port) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + + // IPv6 + if(protocol == "6") { + server = gethostbyname2(argv[1],AF_INET6); + memset((char *) &addr6, 0, sizeof(addr6)); + addr6.sin6_flowinfo = 0; + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(port); + + addr6.sin6_addr = in6addr_any; + //memmove((char *) &addr6.sin6_addr.s6_addr, (char *) server->h_addr, server->h_length); + + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv6_tcp_server_test(path, (char*)nwid.c_str(), &addr6, port) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + } + } + + /****************************************************************************/ + /* SUSTAINED */ + /****************************************************************************/ + + // SUSTAINED + // Performs a stress test for benchmarking performance + if(type == "sustained") + { + protocol = argv[3]; // 4, 6 + mode = argv[4]; // client, server + + // SUSTAINED CLIENT + if(mode == "client") + { + port = atoi(argv[6]); + printf("connecting to %s on port %s\n", argv[5], argv[6]); + + // IPv4 + if(protocol == "4") { + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(argv[5]); + addr.sin_family = AF_INET; + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv4_tcp_client_sustained_test(path, (char*)nwid.c_str(), &addr, port, n_seconds, n_times) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + + // IPv6 + if(protocol == "6") { + server = gethostbyname2(argv[1],AF_INET6); + memset((char *) &addr6, 0, sizeof(addr6)); + addr6.sin6_flowinfo = 0; + addr6.sin6_family = AF_INET6; + memmove((char *) &addr6.sin6_addr.s6_addr, (char *) server->h_addr, server->h_length); + addr6.sin6_port = htons(port); + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv6_tcp_client_sustained_test(path, (char*)nwid.c_str(), &addr6, port, n_seconds, n_times) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + } + + // SUSTAINED SERVER + if(mode == "server") + { + port = atoi(argv[5]); + printf("serving on port %s\n", argv[6]); + + // IPv4 + if(protocol == "4") { + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr("10.9.9.0"); + // addr.sin_addr.s_addr = htons(INADDR_ANY); + addr.sin_family = AF_INET; + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv4_tcp_server_sustained_test(path, (char*)nwid.c_str(), &addr, port, n_seconds, n_times) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + + // IPv6 + if(protocol == "6") { + server = gethostbyname2(argv[1],AF_INET6); + memset((char *) &addr6, 0, sizeof(addr6)); + addr6.sin6_flowinfo = 0; + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(port); + + addr6.sin6_addr = in6addr_any; + //memmove((char *) &addr6.sin6_addr.s6_addr, (char *) server->h_addr, server->h_length); + + printf(" running (%s) test as ipv=%s\n", mode.c_str(), protocol.c_str()); + if(ipv6_tcp_server_sustained_test(path, (char*)nwid.c_str(), &addr6, port, n_seconds, n_times) == PASSED) + printf("PASSED\n"); + else + printf("FAILED\n"); + return 0; + } + } + } + + + /****************************************************************************/ + /* COMPREHENSIVE */ + /****************************************************************************/ + + // COMPREHENSIVE + // Tests ALL API calls + if(type == "comprehensive") + { + + } + + + /****************************************************************************/ + /* RANDOM */ + /****************************************************************************/ + + // RANDOM + // performs random API calls with plausible (and random) arguments/data + if(type == "random") + { + + } + + printf("invalid configuration. exiting.\n"); return 0; } \ No newline at end of file diff --git a/test/unit/server.cpp b/test/unit/server.cpp deleted file mode 100644 index f8fe832..0000000 --- a/test/unit/server.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// Comprehensive stress test for socket-like API - -#include - -/****************************************************************************/ -/* Test Functions */ -/****************************************************************************/ - -int test_for_correctness() -{ - return 0; -} - -int ipv4_udp_server() -{ - return 0; -} - -int ipv6_udp_server() -{ - return 0; -} - -int ipv4_tcp_server() -{ - return 0; -} - -int ipv6_tcp_server() -{ - return 0; -} - -/****************************************************************************/ -/* main */ -/****************************************************************************/ - -int main() -{ - int test_all = 1; - - if(test_all) - { - printf("Testing API calls for correctness\n"); - test_for_correctness(); - - printf("Testing as IPv4 UDP Server\n"); - ipv4_udp_server(); - - printf("Testing as IPv6 UDP Server\n"); - ipv6_udp_server(); - - printf("Testing as IPv4 TCP Server\n"); - ipv4_udp_server(); - - printf("Testing as IPv6 TCP Server\n"); - ipv6_udp_server(); - - printf("Testing \n"); - printf("\n"); - printf("\n"); - } - return 0; -} \ No newline at end of file diff --git a/test/unit/simple.cpp b/test/unit/simple.cpp index 67a4d6e..0a89448 100644 --- a/test/unit/simple.cpp +++ b/test/unit/simple.cpp @@ -52,12 +52,11 @@ int main() printf("waiting for service to issue an address\n"); sleep(1); } - // Begin Socket API calls int err; int sockfd; - int port = 5555; + int port = 7878; struct sockaddr_in addr; // socket() @@ -69,7 +68,7 @@ int main() // connect() IPv6 if(false) { - struct hostent *server = gethostbyname2("fde5:cd7a:9e1c:fd2:7299:932e:e35a:9a03",AF_INET6); + struct hostent *server = gethostbyname2("fde5:cd7a:9e1c:0fd2:7299:9367:5993:3b86",AF_INET6); struct sockaddr_in6 serv_addr; memset((char *) &serv_addr, 0, sizeof(serv_addr)); serv_addr.sin6_flowinfo = 0; @@ -91,6 +90,10 @@ int main() printf("error connecting to remote host (%d)\n", err); return -1; } + + zts_write(sockfd, "hello", 5); + sleep(3); + zts_close(sockfd); } // bind() ipv4 if(false) @@ -129,12 +132,12 @@ int main() printf("peer_count = %lu\n", zts_get_peer_count()); -/* + while(1) { sleep(1); } -*/ + // --- diff --git a/zto/service/OneService.cpp b/zto/service/OneService.cpp index f28d966..191c1ae 100644 --- a/zto/service/OneService.cpp +++ b/zto/service/OneService.cpp @@ -1003,9 +1003,14 @@ public: Mutex::Lock _l(_nets_m); std::map::iterator it; for(it = _nets.begin(); it != _nets.end(); it++) { + printf("TEEEESTING....\n"); if(it->second.tap) { + printf("\tips = %d\n", it->second.tap->_ips.size()); for(int j=0; jsecond.tap->_ips.size(); j++) { + printf("\tTESTING: %s\n", it->second.tap->_ips[j].toString().c_str()); + printf("\t\tdoesn't contain: %s\n", addr.toString().c_str()); if(it->second.tap->_ips[j].containsAddress(addr)) { + return it->second.tap; } }