From e0d4b84dd49a1a6eb0492c813855a96f426ffead Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 6 Nov 2017 13:50:20 -0800 Subject: [PATCH] Preparation for C--, removed classes, namespaces, advanced build options are now controlled via include/libztDefs.h --- Makefile | 124 +- README.md | 7 +- RELEASE-NOTES.md | 16 +- examples/apps/ztproxy/ztproxy.cpp | 23 +- examples/apps/ztproxy/ztproxy.hpp | 7 +- .../cpp/simple_client_server/client.cpp | 10 +- .../cpp/simple_client_server/server.cpp | 10 +- .../libztHelloWorld_csharp/libzt.cs | 32 +- examples/bindings/java/ExampleApp.java | 16 +- examples/bindings/java/README.md | 9 +- examples/bindings/java/zerotier/Address.java | 106 - examples/bindings/java/zerotier/ZeroTier.java | 4 +- ext/lwip/src/core/ipv4/etharp.c | 4 +- ext/lwip/src/include/lwip/arch.h | 2 +- ext/lwip/src/include/lwip/errno.h | 3 +- ext/pico_bsd/LICENSE | 339 +++ ext/pico_bsd/README.md | 4 + ext/pico_bsd/pico_bsd_sockets.c | 1585 +++++++++++++ ext/pico_bsd/pico_bsd_sockets.h | 331 +++ ext/pico_bsd/pico_bsd_syscalls.h | 336 +++ ext/pico_bsd/pico_osal.h | 45 + ext/pico_bsd/pico_osal_freertos.c | 176 ++ ext/pico_bsd/pico_osal_noos.c | 166 ++ ext/pico_bsd/pico_osal_pthread.c | 119 + ext/pico_bsd/pico_posix_wrapper.c | 429 ++++ ext/picotcp/modules/pico_dns_client.c | 10 +- ext/picotcp/modules/pico_dns_client.h | 8 + ext/picotcp/modules/pico_ipv4.c | 2 +- ext/picotcp/modules/pico_ipv6.c | 2 +- ext/picotcp/stack/pico_protocol.c | 2 +- ext/picotcp/stack/pico_socket.c | 2 +- ext/picotcp/stack/pico_stack.c | 2 +- include/README.md | 2 +- .../RingBuffer.h | 65 +- include/{Platform.h => SysUtils.h} | 45 +- include/Utilities.h | 36 +- include/VirtualBindingPair.h | 10 +- include/VirtualSocket.h | 112 +- include/VirtualSocketLayer.h | 443 ++++ include/VirtualTap.h | 331 +++ include/ZT1Service.h | 130 +- include/libzt.h | 85 +- include/{Debug.hpp => libztDebug.h} | 18 +- include/{Defs.h => libztDefs.h} | 92 +- include/lwIP.h | 250 +++ include/lwIP.hpp | 101 - include/lwipopts.h | 43 +- include/picoTCP.h | 222 ++ make-liblwip.mk | 2 +- src/RingBuffer.cpp | 143 ++ src/RingBuffer.hpp | 185 -- src/{Platform.cpp => SysUtils.cpp} | 30 +- src/Utilities.cpp | 80 +- src/VirtualSocket.cpp | 150 +- src/VirtualSocketLayer.cpp | 1250 +++++++++++ src/VirtualTap.cpp | 1061 +++++---- src/VirtualTap.hpp | 311 --- src/ZT1Service.cpp | 410 ++-- src/libzt.cpp | 1068 +++++---- src/libztJNI.cpp | 333 ++- src/lwIP.cpp | 1838 ++++++++++++++- src/picoTCP.cpp | 1979 +++++++++++++++++ test/dummy.cpp | 15 + test/selftest.cpp | 44 +- zto | 2 +- 65 files changed, 12208 insertions(+), 2609 deletions(-) delete mode 100644 examples/bindings/java/zerotier/Address.java create mode 100644 ext/pico_bsd/LICENSE create mode 100644 ext/pico_bsd/README.md create mode 100644 ext/pico_bsd/pico_bsd_sockets.c create mode 100644 ext/pico_bsd/pico_bsd_sockets.h create mode 100644 ext/pico_bsd/pico_bsd_syscalls.h create mode 100644 ext/pico_bsd/pico_osal.h create mode 100644 ext/pico_bsd/pico_osal_freertos.c create mode 100644 ext/pico_bsd/pico_osal_noos.c create mode 100644 ext/pico_bsd/pico_osal_pthread.c create mode 100644 ext/pico_bsd/pico_posix_wrapper.c rename src/VirtualBindingPair.cpp => include/RingBuffer.h (50%) rename include/{Platform.h => SysUtils.h} (63%) create mode 100644 include/VirtualSocketLayer.h create mode 100644 include/VirtualTap.h rename include/{Debug.hpp => libztDebug.h} (97%) rename include/{Defs.h => libztDefs.h} (82%) create mode 100644 include/lwIP.h delete mode 100644 include/lwIP.hpp create mode 100644 include/picoTCP.h create mode 100644 src/RingBuffer.cpp delete mode 100644 src/RingBuffer.hpp rename src/{Platform.cpp => SysUtils.cpp} (87%) create mode 100644 src/VirtualSocketLayer.cpp delete mode 100644 src/VirtualTap.hpp create mode 100644 src/picoTCP.cpp create mode 100644 test/dummy.cpp diff --git a/Makefile b/Makefile index 48a022c..5b02e42 100644 --- a/Makefile +++ b/Makefile @@ -159,6 +159,10 @@ endif ifeq ($(LIBZT_TRACE),1) LIBZT_DEFS+=-DLIBZT_DEBUG endif +# For using experimental stack drivers which interface via raw API's +ifeq ($(LIBZT_RAW),1) + LIBZT_DEFS+=-DLIBZT_RAW=1 +endif ifeq ($(NS_DEBUG),1) CFLAGS+=-Wall -g @@ -173,13 +177,10 @@ ifeq ($(LIBZT_SANITIZE),1) endif # JNI (Java Native Interface) -ifeq ($(SDK_JNI), 1) - # jni.h - LIBZT_INCLUDES+=-I$(shell /usr/libexec/java_home)/include - # jni_md.h - LIBZT_INCLUDES+=-I$(shell /usr/libexec/java_home)/include/$(OSTYPE) - LIBZT_DEFS+=-DSDK_JNI -endif +# jni.h +JNI_INCLUDES+=-I$(shell /usr/libexec/java_home)/include +# jni_md.h +JNI_INCLUDES+=-I$(shell /usr/libexec/java_home)/include/$(OSTYPE) CXXFLAGS+=$(CFLAGS) -Wno-format -fno-rtti -std=c++11 ZT_DEFS+=-DZT_SDK -DZT_SOFTWARE_UPDATE_DEFAULT="\"disable\"" @@ -190,48 +191,19 @@ STATIC_LIB=$(BUILD)/libzt.a ## Stack Configuration ## ############################################################################## -# default stack (picoTCP) -STACK_LWIP=1 -ifeq ($(NO_STACK)$(STACK_PICO),1) -STACK_LWIP=0 -endif +#ifeq ($(NO_STACK),1) +#STACK_DRIVER_DEFS+=-DNO_STACK +#endif -# picoTCP -ifeq ($(STACK_PICO),1) -# picoTCP default protocol versions -ifeq ($(NS_DEBUG),1) - STACK_DEFS+= -endif -ifeq ($(LIBZT_IPV4)$(LIBZT_IPV6),1) -ifeq ($(LIBZT_IPV4),1) -STACK_DRIVER_DEFS+=-DLIBZT_IPV4 -STACK_DEFS+=IPV4=1 -endif -ifeq ($(LIBZT_IPV6),1) -STACK_DRIVER_DEFS+=-DLIBZT_IPV6 -STACK_DEFS+=IPV6=1 -endif -else -STACK_DRIVER_DEFS+=-DLIBZT_IPV4 -DLIBZT_IPV6 -STACK_DEFS+=IPV6=1 IPV4=1 -endif -STACK_DRIVER_DEFS+=-DSTACK_PICO -STACK_LIB:=libpicotcp.a -STACK_DIR:=ext/picotcp -STACK_LIB:=$(STACK_DIR)/build/lib/$(STACK_LIB) -STACK_DRIVER_FILES:=src/picoTCP.cpp -STACK_INCLUDES+=-Iext/picotcp/include -Iext/picotcp/build/include -endif - -# lwIP -ifeq ($(STACK_LWIP),1) -# lwIP default protocol versions ifeq ($(NS_DEBUG),1) STACK_DEFS+=LWIP_DEBUG=1 endif -STACK_DRIVER_DEFS+=-DLWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS -STACK_DRIVER_DEFS+=-DSTACK_LWIP -STACK_DRIVER_FILES:=src/lwIP.cpp + +# picoTCP +STACK_INCLUDES+=-Iext/picotcp/include -Iext/picotcp/build/include +STACK_DRIVER_FILES:=src/picoTCP.cpp + +# lwIP LWIPDIR=ext/lwip/src STACK_INCLUDES+=-I$(LWIPARCHINCLUDE) -Iext/lwip/src/include/lwip \ -I$(LWIPDIR)/include \ @@ -239,14 +211,7 @@ STACK_INCLUDES+=-I$(LWIPARCHINCLUDE) -Iext/lwip/src/include/lwip \ -I$(LWIPDIR)/include/ipv4 \ -I$(LWIPDIR) \ -Iext -endif - -ifeq ($(NO_STACK),1) -STACK_DRIVER_DEFS+=-DNO_STACK -endif - -STACK_DRIVER_DEFS+=-DLIBZT_IPV4 -DLWIP_IPV4=1 -DLIBZT_IPV6 -DLWIP_IPV6=1 -STACK_DEFS+=LIBZT_IPV6=1 IPV6=1 LIBZT_IPV4=1 IPV4=1 +STACK_DRIVER_FILES:=src/lwIP.cpp ############################################################################## ## Targets ## @@ -254,7 +219,7 @@ STACK_DEFS+=LIBZT_IPV6=1 IPV6=1 LIBZT_IPV4=1 IPV4=1 %.o : %.cpp @mkdir -p $(BUILD) obj - $(CXX) $(CXXFLAGS) $(SANFLAGS) $(STACK_DRIVER_DEFS) $(ZT_DEFS) \ + $(CXX) $(CXXFLAGS) $(STACK_DRIVER_DEFS) $(ZT_DEFS) \ $(ZT_INCLUDES) $(LIBZT_INCLUDES) -c $^ -o obj/$(@F) %.o : %.c @@ -270,37 +235,38 @@ picotcp: lwip: echo $(STACK_DEFS) - make -f make-liblwip.mk liblwip.a $(STACK_DEFS) + make -f make-liblwip.mk liblwip.a LIBZT_IPV4=1 IPV4=1 lwip_driver: $(CXX) $(CXXFLAGS) -c src/lwIP.cpp \ - $(ZT_DEFS) $(ZT_INCLUDES) $(STACK_INCLUDES) $(LIBZT_DEFS) $(LIBZT_INCLUDES) $(STACK_DRIVER_DEFS) + $(ZT_DEFS) $(ZT_INCLUDES) $(STACK_INCLUDES) $(LIBZT_DEFS) $(LIBZT_INCLUDES) -DZT_DRIVER_MODULE picotcp_driver: $(CXX) $(CXXFLAGS) -c src/picoTCP.cpp \ - $(ZT_DEFS) $(ZT_INCLUDES) $(STACK_INCLUDES) $(LIBZT_DEFS) $(LIBZT_INCLUDES) $(STACK_DRIVER_DEFS) + $(ZT_DEFS) $(ZT_INCLUDES) $(STACK_INCLUDES) $(LIBZT_DEFS) $(LIBZT_INCLUDES) -DZT_DRIVER_MODULE libzt_socket_layer: $(CXX) $(CXXFLAGS) -c src/VirtualSocket.cpp \ - $(LIBZT_INCLUDES) - $(CXX) $(CXXFLAGS) -c src/VirtualBindingPair.cpp \ - $(ZT_INCLUDES) $(LIBZT_INCLUDES) + $(ZT_DEFS) $(ZT_INCLUDES) $(LIBZT_INCLUDES) $(LIBZT_DEFS) + $(CXX) $(CXXFLAGS) -c src/VirtualSocketLayer.cpp \ + $(ZT_DEFS) $(ZT_INCLUDES) $(STACK_INCLUDES) $(LIBZT_INCLUDES) $(LIBZT_DEFS) $(CXX) $(CXXFLAGS) -c src/VirtualTap.cpp \ - $(ZT_DEFS) $(ZT_INCLUDES) $(LIBZT_DEFS) $(LIBZT_INCLUDES) $(STACK_DRIVER_DEFS) + $(ZT_DEFS) $(ZT_INCLUDES) $(LIBZT_DEFS) $(LIBZT_INCLUDES) $(CXX) $(CXXFLAGS) -c src/ZT1Service.cpp \ - $(ZT_DEFS) $(ZT_INCLUDES) $(LIBZT_INCLUDES) $(LIBZT_DEFS) $(STACK_DRIVER_DEFS) + $(ZT_DEFS) $(ZT_INCLUDES) $(LIBZT_INCLUDES) $(LIBZT_DEFS) $(CXX) $(CXXFLAGS) -c src/libzt.cpp \ - $(ZT_DEFS) $(ZT_INCLUDES) $(STACK_INCLUDES) $(LIBZT_DEFS) $(LIBZT_INCLUDES) $(STACK_DRIVER_DEFS) + $(ZT_DEFS) $(ZT_INCLUDES) $(STACK_INCLUDES) $(LIBZT_DEFS) $(LIBZT_INCLUDES) + $(CXX) $(CXXFLAGS) -c src/RingBuffer.cpp $(LIBZT_INCLUDES) jni_socket_wrapper: $(CXX) $(CXXFLAGS) -DSDK_JNI -c src/libztJNI.cpp \ - $(ZT_DEFS) $(ZT_INCLUDES) $(STACK_INCLUDES) $(LIBZT_DEFS) $(LIBZT_INCLUDES) $(STACK_DRIVER_DEFS) + $(ZT_DEFS) $(ZT_INCLUDES) $(STACK_INCLUDES) $(JNI_INCLUDES) $(LIBZT_DEFS) $(LIBZT_INCLUDES) utilities: - $(CXX) $(CXXFLAGS) -c src/Platform.cpp \ + $(CXX) $(CXXFLAGS) -c src/SysUtils.cpp \ $(LIBZT_INCLUDES) $(CXX) $(CXXFLAGS) -c src/Utilities.cpp \ - $(ZT_DEFS) $(ZT_INCLUDES) $(LIBZT_INCLUDES) $(STACK_DRIVER_DEFS) + $(ZT_DEFS) $(ZT_INCLUDES) $(LIBZT_INCLUDES) $(STACK_INCLUDES) # windows DLL win_dll: lwip lwip_driver libzt_socket_layer utilities $(ZTO_OBJS) @@ -327,27 +293,12 @@ shared_jni_lib: lwip lwip_driver libzt_socket_layer jni_socket_wrapper utilities $(CXX) $(CXXFLAGS) -dynamiclib -o $(BUILD)/libzt.dylib obj/*.o # static library -ifeq ($(STACK_PICO),1) -static_lib: picotcp picotcp_driver libzt_socket_layer utilities $(ZTO_OBJS) +static_lib: picotcp picotcp_driver lwip lwip_driver libzt_socket_layer utilities $(ZTO_OBJS) @mkdir -p $(BUILD) obj mv *.o obj mv ext/picotcp/build/lib/*.o obj mv ext/picotcp/build/modules/*.o obj $(ARTOOL) $(ARFLAGS) -o $(STATIC_LIB) obj/*.o -endif -ifeq ($(STACK_LWIP),1) -static_lib: lwip lwip_driver libzt_socket_layer utilities $(ZTO_OBJS) - @mkdir -p $(BUILD) obj - mv *.o obj - $(ARTOOL) $(ARFLAGS) -o $(STATIC_LIB) obj/*.o -endif -# for layer-2 only (this will omit all userspace network stack code) -ifeq ($(NO_STACK),1) -static_lib: libzt_socket_layer utilities $(ZTO_OBJS) - @mkdir -p $(BUILD) obj - mv *.o obj - $(ARTOOL) $(ARFLAGS) -o $(STATIC_LIB) obj/*.o -endif ############################################################################## ## iOS/macOS App Frameworks ## @@ -377,7 +328,7 @@ python_module: ## Unit Tests ## ############################################################################## -tests: selftest nativetest ztproxy +tests: selftest nativetest ztproxy simple # intercept ZT_UTILS:=zto/node/Utils.cpp -Izto/node @@ -400,8 +351,11 @@ intercept: $(CXX) $(CXXFLAGS) $(SANFLAGS) $(STACK_DRIVER_DEFS) $(LIBZT_INCLUDES) \ $(ZT_INCLUDES) examples/intercept/intercept.cpp -D_GNU_SOURCE \ -shared -o $(BUILD)/intercept.so $< -ldl +simple: + $(CXX) $(CXXFLAGS) $(SANFLAGS) $(LIBZT_INCLUDES) $(LIBZT_DEFS) examples/bindings/cpp/simple_client_server/client.cpp -o $(BUILD)/client -L$(BUILD) -lzt + $(CXX) $(CXXFLAGS) $(SANFLAGS) $(LIBZT_INCLUDES) $(LIBZT_DEFS) examples/bindings/cpp/simple_client_server/server.cpp -o $(BUILD)/server -L$(BUILD) -lzt dlltest: - $(CXX) $(CXXFLAGS) + $(CXX) $(CXXFLAGS) ############################################################################## @@ -410,9 +364,7 @@ dlltest: standardize: vera++ --transform trim_right src/*.cpp - vera++ --transform trim_right src/*.hpp vera++ --transform trim_right include/*.h - vera++ --transform trim_right include/*.hpp clean: -rm -rf .depend diff --git a/README.md b/README.md index ce58fba..4682ba3 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,6 @@ A library version of [ZeroTier](https://github.com/zerotier/ZeroTierOne), **libz int main() { char *str = "welcome to the machine"; - char *nwid = "c7cd7c981b0f52a2"; // network - char *path = "config_path"; // where this instance's keys and configs are stored char *ip = "10.8.8.42"; // remote address int port = 8080; // remote port @@ -38,7 +36,7 @@ int main() addr.sin_addr.s_addr = inet_addr(ip); addr.sin_port = htons(port); - zts_startjoin(path, nwid); + zts_startjoin("path", 0xc7cd7c981b0f52a2); // config path, nwid int fd, err = 0; if ((fd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { @@ -85,4 +83,7 @@ Please make pull requests against the `dev` branch. The `master` branch is relea ### Commercial License - To be released from GPLv3, contact us directly via `contact@zerotier.com` to discuss commercial licensing. + +### Adding a custom network stack + - If you wish to use something other than lwIP or picoTCP, you can easily add your own API function calls in `src/libzt.cpp` or `src/VirtualSocket.cpp` depending on whether your stack's API exposes a POSIX-socket API or a raw API, respectively. \ No newline at end of file diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index c51b1c1..cabe8e9 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,5 +1,17 @@ -### 2017-XX-XX -- Version 1.1.6 - - In progress +Current version: 1.1.5 +*** + +### 2017-11-10 -- Version 1.1.6 + - [In progress] + - API simplification + - Removed protocol specific address getters, ) + - Changed how network IDs and node IDs are passed to the library (everything is now uint64_t) + - Initial steps for C-- efforts + - Removed classes for stack drivers + - Removed namespaces + - Names of key classes changed slightly to prevent collisions + - Re-classification of Virtual Socket Layer (and associated stack drivers) as experimental + - lwIP sequential socket API is now default ### 2017-10-19 -- Version 1.1.5 (Current) diff --git a/examples/apps/ztproxy/ztproxy.cpp b/examples/apps/ztproxy/ztproxy.cpp index 6aa5218..85c76be 100644 --- a/examples/apps/ztproxy/ztproxy.cpp +++ b/examples/apps/ztproxy/ztproxy.cpp @@ -24,27 +24,25 @@ * of your own application. */ -#include -#include - #if defined(__linux__) || defined(__APPLE__) #include #endif +#include +#include #include #include #include #include - #include #include #include -#include "RingBuffer.hpp" -#include "ztproxy.hpp" -#include "Utilities.h" #include "libzt.h" +#include "RingBuffer.h" +#include "ztproxy.hpp" + namespace ZeroTier { typedef void PhySocket; @@ -292,7 +290,7 @@ namespace ZeroTier { } // Write data coming from client TCP connection to its TX buffer, later emptied into libzt by threadMain I/O loop conn->tx_m.lock(); - if ((wr = conn->TXbuf->write((const unsigned char *)data, len)) < 0) { + if ((wr = conn->TXbuf->write((const char *)data, len)) < 0) { DEBUG_ERROR("there was an error while writing data from client to tx buffer, err=%d", wr); } else { @@ -368,7 +366,7 @@ int main(int argc, char **argv) } std::string path = argv[1]; int proxy_listen_port = atoi(argv[2]); - std::string nwid = argv[3]; + std::string nwidstr = argv[3]; std::string internal_addr = argv[4]; int internal_port = atoi(argv[5]); std::string dns_nameserver= "";//argv[6]; @@ -376,13 +374,14 @@ int main(int argc, char **argv) // Start ZeroTier Node // Join Network which contains resources we need to proxy DEBUG_INFO("waiting for libzt to come online"); - zts_startjoin(path.c_str(), nwid.c_str()); + uint64_t nwid = strtoll(nwidstr.c_str(),NULL,16); + zts_startjoin(path.c_str(), nwid); - ZeroTier::ZTProxy *proxy = new ZeroTier::ZTProxy(proxy_listen_port, nwid, path, internal_addr, internal_port, dns_nameserver); + ZeroTier::ZTProxy *proxy = new ZeroTier::ZTProxy(proxy_listen_port, nwidstr, path, internal_addr, internal_port, dns_nameserver); if (proxy) { printf("\nZTProxy started. Listening on %d\n", proxy_listen_port); - printf("Traffic will be proxied to and from %s:%d on network %s\n", internal_addr.c_str(), internal_port, nwid.c_str()); + printf("Traffic will be proxied to and from %s:%d on network %s\n", internal_addr.c_str(), internal_port, nwidstr.c_str()); printf("Proxy Node config files and key stored in: %s/\n\n", path.c_str()); while(1) { sleep(1); diff --git a/examples/apps/ztproxy/ztproxy.hpp b/examples/apps/ztproxy/ztproxy.hpp index 91f49ab..9bbfa21 100644 --- a/examples/apps/ztproxy/ztproxy.hpp +++ b/examples/apps/ztproxy/ztproxy.hpp @@ -54,14 +54,13 @@ namespace ZeroTier { public: int zfd; PhySocket *client_sock; - RingBuffer *TXbuf; - RingBuffer *RXbuf; + RingBuffer *TXbuf, *RXbuf; Mutex tx_m, rx_m; TcpConnection() { zfd = -1; - TXbuf = new RingBuffer(BUF_SZ); - RXbuf = new RingBuffer(BUF_SZ); + TXbuf = new RingBuffer(BUF_SZ); + RXbuf = new RingBuffer(BUF_SZ); } ~TcpConnection() { diff --git a/examples/bindings/cpp/simple_client_server/client.cpp b/examples/bindings/cpp/simple_client_server/client.cpp index b178bb9..c6b8069 100644 --- a/examples/bindings/cpp/simple_client_server/client.cpp +++ b/examples/bindings/cpp/simple_client_server/client.cpp @@ -20,7 +20,7 @@ int main(int argc, char **argv) exit(0); } std::string path = argv[1]; - std::string nwid = argv[2]; + std::string nwidstr = argv[2]; std::string remote_addr = argv[3]; int remote_port = atoi(argv[4]); int r=0, w=0, err=0, sockfd; @@ -36,10 +36,10 @@ int main(int argc, char **argv) // --- BEGIN DEBUG_TEST("Waiting for libzt to come online...\n"); - zts_startjoin(path.c_str(), nwid.c_str()); - char device_id[11]; - zts_get_id(device_id); - DEBUG_TEST("I am %s", device_id); + uint64_t nwid = strtoll(nwidstr.c_str(),NULL,16); + zts_startjoin(path.c_str(), nwid); + uint64_t nodeId = zts_get_node_id(); + DEBUG_TEST("I am %x", nodeId); sleep(2); // socket() diff --git a/examples/bindings/cpp/simple_client_server/server.cpp b/examples/bindings/cpp/simple_client_server/server.cpp index 9770c1a..f932918 100644 --- a/examples/bindings/cpp/simple_client_server/server.cpp +++ b/examples/bindings/cpp/simple_client_server/server.cpp @@ -18,7 +18,7 @@ int main(int argc, char **argv) exit(0); } std::string path = argv[1]; - std::string nwid = argv[2]; + std::string nwidstr = argv[2]; int bind_port = atoi(argv[3]); int w=0, r=0, err=0, sockfd, accfd; char rbuf[32]; @@ -33,10 +33,10 @@ int main(int argc, char **argv) DEBUG_TEST("Waiting for libzt to come online...\n"); - zts_startjoin(path.c_str(), nwid.c_str()); - char device_id[11]; - zts_get_id(device_id); - DEBUG_TEST("I am %s", device_id); + uint64_t nwid = strtoll(nwidstr.c_str(),NULL,16); + zts_startjoin(path.c_str(), nwid); + uint64_t nodeId = zts_get_node_id(); + DEBUG_TEST("I am %x", nodeId); sleep(2); // socket() diff --git a/examples/bindings/csharp/csproj_dll/libztHelloWorld_csharp/libzt.cs b/examples/bindings/csharp/csproj_dll/libztHelloWorld_csharp/libzt.cs index d894721..ab240f8 100644 --- a/examples/bindings/csharp/csproj_dll/libztHelloWorld_csharp/libzt.cs +++ b/examples/bindings/csharp/csproj_dll/libztHelloWorld_csharp/libzt.cs @@ -18,58 +18,46 @@ namespace ZeroTier public static extern int zts_start(string path); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] - public static extern int zts_startjoin(string path, string nwid); + public static extern int zts_startjoin(string path, ulong nwid); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] public static extern void zts_stop(); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] - public static extern void zts_join(string nwid); + public static extern void zts_join(ulong nwid); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] - public static extern void zts_join_soft(string filepath, string nwid); - - [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] - public static extern void zts_leave(string nwid); - - [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] - public static extern void zts_leave_soft(string filepath, string nwid); + public static extern void zts_leave(ulong nwid); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] public static extern void zts_get_homepath(string homePath, int len); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] - public static extern int zts_get_id(string devID); + public static extern int zts_get_id(string nodeId); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] public static extern int zts_running(); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] - public static extern int zts_has_ipv4_address(string nwid); + public static extern int zts_has_address(ulong nwid); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] - public static extern int zts_has_ipv6_address(string nwid); + public static extern int zts_has_address(ulong nwid); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] - public static extern int zts_has_address(string nwid); + public static extern void zts_get_address(ulong nwid, System.IntPtr addr, int addrlen); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] - public static extern void zts_get_ipv4_address(string nwid, string addrstr, int addrlen); + public static extern void zts_get_6plane_addr(System.IntPtr addr addr, string nwid, string nodeId); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] - public static extern void zts_get_ipv6_address(string nwid, string addrstr, int addrlen); - - [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] - public static extern void zts_get_6plane_addr(string addr, string nwid, string devID); - - [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] - public static extern void zts_get_rfc4193_addr(string addr, string nwid, string devID); + public static extern void zts_get_rfc4193_addr(System.IntPtr addr addr, string nwid, string nodeId); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] public static extern long zts_get_peer_count(); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] - public static extern int zts_get_peer_address(string peer, string devID); + public static extern int zts_get_peer_address(string peer, ulong nodeId); [DllImport("libzt.dll", CallingConvention = CallingConvention.StdCall)] public static extern void zts_enable_http_control_plane(); diff --git a/examples/bindings/java/ExampleApp.java b/examples/bindings/java/ExampleApp.java index 1f5c517..437a354 100644 --- a/examples/bindings/java/ExampleApp.java +++ b/examples/bindings/java/ExampleApp.java @@ -27,6 +27,7 @@ // Simple Java example for libzt using JNI import zerotier.ZeroTier; +import java.net.*; public class ExampleApp { @@ -39,25 +40,28 @@ public class ExampleApp { } public static void main(String[] args) { - + final ZeroTier libzt = new ZeroTier(); new Thread(new Runnable() { public void run() { System.out.println("starting libzt"); - libzt.startjoin("/Users/joseph/op/zt/libzt/ztjni", "1212121212121212"); + //libzt.startjoin("config_path", "123456789abcdeff"); + libzt.startjoin("/Users/joseph/op/zt/libzt/ztjni", "17d709436c2c5367"); System.out.println("started."); - // start(path) will not block - // startjoin(path, nwid) will block + int fd = 0, err = 0; if ((fd = libzt.socket(libzt.AF_INET, libzt.SOCK_STREAM, 0)) < 0) { System.out.println("error creating socket"); return; } - if ((err = libzt.bind(fd, "0.0.0.0", 3000)) < 0) { + + InetSocketAddress addr = new InetSocketAddress("0.0.0.0", 3434); + + if ((err = libzt.bind(fd, addr)) < 0) { System.out.println("error binding socket to virtual interface"); return; - } + } } }).start(); diff --git a/examples/bindings/java/README.md b/examples/bindings/java/README.md index 666cad4..965533d 100644 --- a/examples/bindings/java/README.md +++ b/examples/bindings/java/README.md @@ -11,4 +11,11 @@ To get this example project to work, do the following: Notes: -Upon execution, it will load the libzt dynamic library via the `loadLibrary` method and begin generating an identity. \ No newline at end of file +Upon execution, it will load the libzt dynamic library via the `loadLibrary` method and begin generating an identity. + + +*** + +More resources on JNI usage: + +http://journals.ecs.soton.ac.uk/java/tutorial/native1.1/implementing/index.html diff --git a/examples/bindings/java/zerotier/Address.java b/examples/bindings/java/zerotier/Address.java deleted file mode 100644 index afe9f0c..0000000 --- a/examples/bindings/java/zerotier/Address.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 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 . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -package zerotier; - -import java.math.BigInteger; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.regex.Pattern; - -/* - -The ZTAddress object is merely a convenience object for moving address information -across the JNI memory border. - -*/ - -public class Address -{ - // int -> byte array - static public byte[] toIPByteArray(long addr){ - return new byte[]{(byte)addr,(byte)(addr>>>8),(byte)(addr>>>16),(byte)(addr>>>24)}; - } - - // byte array -> int - long toIPInt(String _addr) { - long result = 0; - for(String part: _addr.split(Pattern.quote("."))) { - result = result << 8; - result |= Integer.parseInt(part); - } - return result; - } - - public int port; - public int Port() { - return port; - } - - public long _rawAddr; - public String Address() - { - try { - return InetAddress.getByAddress(toIPByteArray(_rawAddr)).getHostAddress(); - } catch (UnknownHostException e) { - // should never happen - return null; - } - } - - public String toString() { - return Address() + ":" + Port(); - } - - public Address() - { - port = -1; - _rawAddr = -1; - } - - public Address(String _addr, int _port) - { - _rawAddr = toIPInt(_addr); - port = _port; - } - - public void ZTAddress(InetSocketAddress ins) - { - port = ins.getPort(); - _rawAddr = toIPInt(ins.getAddress().getHostAddress()); - } - - public InetSocketAddress ToInetSocketAddress() throws IllegalArgumentException { - InetSocketAddress sock_addr = null; - try { - sock_addr = new InetSocketAddress(Address(), port); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } - return sock_addr; - } -} \ No newline at end of file diff --git a/examples/bindings/java/zerotier/ZeroTier.java b/examples/bindings/java/zerotier/ZeroTier.java index 351913b..2afbc30 100644 --- a/examples/bindings/java/zerotier/ZeroTier.java +++ b/examples/bindings/java/zerotier/ZeroTier.java @@ -26,6 +26,8 @@ package zerotier; +import java.net.*; + public class ZeroTier { // socket families @@ -58,7 +60,7 @@ public class ZeroTier { // socket API public native int socket(int family, int type, int protocol); public native int connect(int fd, String addr, int port); - public native int bind(int fd, String addr, int port); + public native int bind(int fd, InetSocketAddress addr); public native int listen(int fd, int backlog); public native int accept(int fd, Address addr); public native int accept4(int fd, String addr, int port); diff --git a/ext/lwip/src/core/ipv4/etharp.c b/ext/lwip/src/core/ipv4/etharp.c index a5df46b..cca8bde 100755 --- a/ext/lwip/src/core/ipv4/etharp.c +++ b/ext/lwip/src/core/ipv4/etharp.c @@ -340,8 +340,8 @@ etharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif* netif) if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) || /* or no empty entry found and not allowed to recycle? */ ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) { - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n")); - return (s8_t)ERR_MEM; + //LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n")); + //return (s8_t)ERR_MEM; } /* b) choose the least destructive entry to recycle: diff --git a/ext/lwip/src/include/lwip/arch.h b/ext/lwip/src/include/lwip/arch.h index 6bb6368..8c62577 100755 --- a/ext/lwip/src/include/lwip/arch.h +++ b/ext/lwip/src/include/lwip/arch.h @@ -75,7 +75,7 @@ * in turn pull in a lot of standard libary code. In resource-constrained * systems, this should be defined to something less resource-consuming. */ -#include "Debug.hpp" +#include "libztDebug.h" #ifndef LWIP_PLATFORM_DIAG #define LWIP_PLATFORM_DIAG(x) DEBUG_STACK x #include diff --git a/ext/lwip/src/include/lwip/errno.h b/ext/lwip/src/include/lwip/errno.h index 1c770df..610aaa4 100644 --- a/ext/lwip/src/include/lwip/errno.h +++ b/ext/lwip/src/include/lwip/errno.h @@ -174,7 +174,8 @@ extern "C" { #define EMEDIUMTYPE 124 /* Wrong medium type */ #ifndef errno -extern int errno; +//extern int errno; +//#include #endif #else /* LWIP_PROVIDE_ERRNO */ diff --git a/ext/pico_bsd/LICENSE b/ext/pico_bsd/LICENSE new file mode 100644 index 0000000..22fbe5d --- /dev/null +++ b/ext/pico_bsd/LICENSE @@ -0,0 +1,339 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. \ No newline at end of file diff --git a/ext/pico_bsd/README.md b/ext/pico_bsd/README.md new file mode 100644 index 0000000..9b896c0 --- /dev/null +++ b/ext/pico_bsd/README.md @@ -0,0 +1,4 @@ +picotcp-bsd +=========== + +BSD POSIX-compliant socket support for PicoTCP running on any OS. diff --git a/ext/pico_bsd/pico_bsd_sockets.c b/ext/pico_bsd/pico_bsd_sockets.c new file mode 100644 index 0000000..62a9058 --- /dev/null +++ b/ext/pico_bsd/pico_bsd_sockets.c @@ -0,0 +1,1585 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2013 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. +Do not redistribute without a written permission by the Copyright +holders. + +Author: Maxime Vincent, Daniele Lacamera +*********************************************************************/ + +#include "pico_defines.h" +#include "pico_config.h" /* for zalloc and free */ +#include "pico_bsd_sockets.h" +#include "pico_osal.h" +#ifdef PICO_SUPPORT_SNTP_CLIENT +#include "pico_sntp_client.h" +#endif + +#include /* should be there in C99 */ + +#define SOCK_OPEN 0 +#define SOCK_BOUND 1 +#define SOCK_LISTEN 2 +#define SOCK_CONNECTED 3 +#define SOCK_ERROR 4 +#define SOCK_RESET_BY_PEER 5 +#define SOCK_CLOSED 100 + +#define bsd_dbg(...) do {} while(0) +#define bsd_dbg_select(...) do {} while(0) + +/* Global signal sent on any event (for select) */ +void * picoLock = NULL; /* pico stack lock */ +void * pico_signal_select = NULL; /* pico global signal for select */ +void * pico_signal_tick = NULL; /* pico tick signal, e.g. coming from a driver ISR */ + +struct pico_bsd_endpoint { + struct pico_socket *s; + int socket_fd; + int posix_fd; /* TODO: ifdef... */ + uint8_t in_use; + uint8_t state; /* for pico_state */ + uint8_t nonblocking; /* The non-blocking flag, for non-blocking socket operations */ + uint16_t events; /* events that we filter for */ + uint16_t revents; /* received events */ + uint16_t proto; + void * mutex_lock; /* mutex for clearing revents */ + void * signal; /* signals new events */ + uint32_t timeout; /* this is used for timeout sockets */ + int error; /* used for SO_ERROR sockopt after connect() */ +}; + +/* MACRO's */ +#define VALIDATE_NULL(param) \ + if(!param) \ + { \ + return -1; \ + } + +#define VALIDATE_ONE(param,value) \ + if(param != value) { \ + pico_err = PICO_ERR_EINVAL; \ + errno = pico_err; \ + return -1; \ + } + +#define VALIDATE_TWO(param,value1,value2) \ + if(param != value1 && param != value2) { \ + pico_err = PICO_ERR_EINVAL; \ + errno = pico_err; \ + return -1; \ + } + + +/* Private function prototypes */ +static void pico_event_clear(struct pico_bsd_endpoint *ep, uint16_t events); +static uint16_t pico_bsd_wait(struct pico_bsd_endpoint * ep, int read, int write, int close); +static void pico_socket_event(uint16_t ev, struct pico_socket *s); + + +/************************/ +/* Public API functions */ +/************************/ +void pico_bsd_init(void) +{ + pico_signal_select = pico_signal_init(); + pico_signal_tick = pico_signal_init(); + picoLock = pico_mutex_init(); +} + +void pico_bsd_deinit(void) +{ + pico_mutex_deinit(picoLock); +} + +/* just ticks the stack twice */ +void pico_bsd_stack_tick(void) +{ + pico_mutex_lock(picoLock); + pico_stack_tick(); + pico_stack_tick(); + pico_mutex_unlock(picoLock); +} + +/* ticks the stack, but wait for a signal with a timeout (e.g. from the driver interrupt) */ +void pico_bsd_stack_tick_timeout(int timeout_ms) +{ + pico_signal_wait_timeout(pico_signal_tick, timeout_ms); + pico_bsd_stack_tick(); +} + +/** Declarations of helper functions **/ +static struct pico_bsd_endpoint *pico_bsd_create_socket(void); +static int get_free_sd(struct pico_bsd_endpoint *ep); +static int new_sd(struct pico_bsd_endpoint *ep); +static void free_up_ep(struct pico_bsd_endpoint *ep); +static struct pico_bsd_endpoint *get_endpoint(int sd, int set_err); +static int bsd_to_pico_addr(union pico_address *addr, struct sockaddr *_saddr, socklen_t socklen); +static uint16_t bsd_to_pico_port(struct sockaddr *_saddr, socklen_t socklen); +static int pico_addr_to_bsd(struct sockaddr *_saddr, socklen_t socklen, union pico_address *addr, uint16_t net); +static int pico_port_to_bsd(struct sockaddr *_saddr, socklen_t socklen, uint16_t port); + +/** Global Sockets descriptors array **/ +static struct pico_bsd_endpoint **PicoSockets = NULL; +static int PicoSocket_max = 0; + +/*** Public socket functions ***/ + +/* Socket interface. */ +int pico_newsocket(int domain, int type, int proto) +{ + struct pico_bsd_endpoint * ep = NULL; + (void)proto; + +#ifdef PICO_SUPPORT_IPV6 + VALIDATE_TWO(domain,AF_INET, AF_INET6); +#else + VALIDATE_ONE(domain, AF_INET); +#endif + VALIDATE_TWO(type,SOCK_STREAM,SOCK_DGRAM); + + if (AF_INET6 != PICO_PROTO_IPV6) { + if (domain == AF_INET6) + domain = PICO_PROTO_IPV6; + else + domain = PICO_PROTO_IPV4; + } + + if (SOCK_STREAM != PICO_PROTO_TCP) { + if (type == SOCK_STREAM) + type = PICO_PROTO_TCP; + else + type = PICO_PROTO_UDP; + } + + pico_mutex_lock(picoLock); + ep = pico_bsd_create_socket(); + VALIDATE_NULL(ep); + ep->error = PICO_ERR_NOERR; + + ep->proto = type; + + ep->s = pico_socket_open(domain, type,&pico_socket_event); + if (!ep->s) + { + PICO_FREE(ep); + pico_mutex_unlock(picoLock); + return -1; + } + + ep->s->priv = ep; /* let priv point to the endpoint struct */ + + /* open picotcp endpoint */ + ep->state = SOCK_OPEN; + ep->mutex_lock = pico_mutex_init(); + ep->signal = pico_signal_init(); + ep->error = pico_err; + pico_mutex_unlock(picoLock); + return ep->socket_fd; +} + + +int pico_bind(int sd, struct sockaddr * local_addr, socklen_t socklen) +{ + union pico_address addr = { .ip4 = { 0 } }; + uint16_t port; + struct pico_bsd_endpoint *ep = get_endpoint(sd, 1); + + VALIDATE_NULL(ep); + ep->error = PICO_ERR_NOERR; + VALIDATE_NULL(local_addr); + VALIDATE_TWO(socklen, SOCKSIZE, SOCKSIZE6); + + if (bsd_to_pico_addr(&addr, local_addr, socklen) < 0) + { + ep->error = PICO_ERR_EINVAL; + errno = pico_err; + return -1; + } + port = bsd_to_pico_port(local_addr, socklen); + + pico_mutex_lock(picoLock); + if(pico_socket_bind(ep->s, &addr, &port) < 0) + { + ep->error = pico_err; + errno = pico_err; + pico_mutex_unlock(picoLock); + return -1; + } + + ep->state = SOCK_BOUND; + pico_mutex_unlock(picoLock); + + return 0; +} + +int pico_getsockname(int sd, struct sockaddr * local_addr, socklen_t *socklen) +{ + union pico_address addr; + uint16_t port, proto; + struct pico_bsd_endpoint *ep = get_endpoint(sd, 1); + VALIDATE_NULL(ep); + ep->error = PICO_ERR_NOERR; + VALIDATE_NULL(local_addr); + VALIDATE_NULL(socklen); + pico_mutex_lock(picoLock); + if(pico_socket_getname(ep->s, &addr, &port, &proto) < 0) + { + ep->error = pico_err; + errno = pico_err; + pico_mutex_unlock(picoLock); + return -1; + } + + if (proto == PICO_PROTO_IPV6) + *socklen = SOCKSIZE6; + else + *socklen = SOCKSIZE; + + if (pico_addr_to_bsd(local_addr, *socklen, &addr, proto) < 0) { + ep->error = pico_err; + errno = pico_err; + pico_mutex_unlock(picoLock); + return -1; + } + pico_mutex_unlock(picoLock); + pico_port_to_bsd(local_addr, *socklen, port); + ep->error = pico_err; + return 0; +} + +int pico_getpeername(int sd, struct sockaddr * remote_addr, socklen_t *socklen) +{ + union pico_address addr; + uint16_t port, proto; + struct pico_bsd_endpoint *ep = get_endpoint(sd, 1); + VALIDATE_NULL(ep); + ep->error = PICO_ERR_NOERR; + VALIDATE_NULL(remote_addr); + VALIDATE_NULL(socklen); + pico_mutex_lock(picoLock); + if(pico_socket_getpeername(ep->s, &addr, &port, &proto) < 0) + { + pico_mutex_unlock(picoLock); + return -1; + } + + if (proto == PICO_PROTO_IPV6) + *socklen = SOCKSIZE6; + else + *socklen = SOCKSIZE; + + if (pico_addr_to_bsd(remote_addr, *socklen, &addr, proto) < 0) { + pico_mutex_unlock(picoLock); + return -1; + } + pico_mutex_unlock(picoLock); + pico_port_to_bsd(remote_addr, *socklen, port); + return 0; +} + + +int pico_listen(int sd, int backlog) +{ + struct pico_bsd_endpoint *ep = get_endpoint(sd, 1); + + VALIDATE_NULL(ep); + ep->error = PICO_ERR_NOERR; + VALIDATE_NULL(ep->s); + VALIDATE_ONE(ep->state, SOCK_BOUND); + + pico_mutex_lock(picoLock); + + if(pico_socket_listen(ep->s, backlog) < 0) + { + ep->error = pico_err; + errno = pico_err; + pico_mutex_unlock(picoLock); + return -1; + } + ep->state = SOCK_LISTEN; + + ep->error = pico_err; + pico_mutex_unlock(picoLock); + return 0; +} + +int pico_connect(int sd, struct sockaddr *_saddr, socklen_t socklen) +{ + struct pico_bsd_endpoint *ep = get_endpoint(sd, 1); + union pico_address addr; + uint16_t port; + uint16_t ev = 0; + int ret; + + VALIDATE_NULL(ep); + ep->error = PICO_ERR_NOERR; + VALIDATE_NULL(_saddr); + if (bsd_to_pico_addr(&addr, _saddr, socklen) < 0) + { + ep->error = PICO_ERR_EINVAL; + errno = pico_err; + return -1; + } + port = bsd_to_pico_port(_saddr, socklen); + pico_mutex_lock(picoLock); + ret = pico_socket_connect(ep->s, &addr, port); + pico_mutex_unlock(picoLock); + if (ret < 0) { + ep->error = pico_err; + return -1; + } + + if (ep->nonblocking) { + pico_err = PICO_ERR_EAGAIN; + ep->error = pico_err; + } else { + /* wait for event */ + ev = pico_bsd_wait(ep, 0, 0, 0); /* wait for ERR, FIN and CONN */ + } + + if(ev & PICO_SOCK_EV_CONN) + { + /* clear the EV_CONN event */ + pico_event_clear(ep, PICO_SOCK_EV_CONN); + ep->error = pico_err; + return 0; + } else { + if (!(ep->nonblocking)) + pico_socket_close(ep->s); + } + ep->error = pico_err; + errno = pico_err; + return -1; +} + +int pico_isconnected(int sd) { + struct pico_bsd_endpoint *ep = NULL; + int state = 0; + + ep = get_endpoint(sd, 1); + + VALIDATE_NULL(ep); + ep->error = PICO_ERR_NOERR; + + pico_mutex_lock(picoLock); + if(ep->state == SOCK_CONNECTED) { + state = 1; + } + pico_mutex_unlock(picoLock); + + return state; +} + +int pico_accept(int sd, struct sockaddr *_orig, socklen_t *socklen) +{ + struct pico_bsd_endpoint *ep, * client_ep = NULL; + uint16_t events; + union pico_address picoaddr; + uint16_t port; + + ep = get_endpoint(sd, 1); + + VALIDATE_NULL(ep); + ep->error = PICO_ERR_NOERR; + VALIDATE_ONE(ep->state, SOCK_LISTEN); + + if (ep->nonblocking) + events = PICO_SOCK_EV_CONN; + else + events = pico_bsd_wait(ep, 0, 0, 0); /* Wait for CONN, FIN and ERR */ + + if(events & PICO_SOCK_EV_CONN) + { + struct pico_socket *s; + pico_mutex_lock(picoLock); + s = pico_socket_accept(ep->s,&picoaddr,&port); + if (!s) + { + ep->error = pico_err; + errno = pico_err; + pico_mutex_unlock(picoLock); + return -1; + } + + /* Create a new client EP, only after the accept returned succesfully */ + client_ep = pico_bsd_create_socket(); + if (!client_ep) + { + ep->error = pico_err; + errno = pico_err; + pico_mutex_unlock(picoLock); + return -1; + } + client_ep->s = s; + client_ep->state = SOCK_OPEN; + client_ep->mutex_lock = pico_mutex_init(); + client_ep->signal = pico_signal_init(); + + client_ep->s->priv = client_ep; + pico_event_clear(ep, PICO_SOCK_EV_CONN); /* clear the CONN event the listening socket */ + if (client_ep->s->net->proto_number == PICO_PROTO_IPV4) + *socklen = SOCKSIZE; + else + *socklen = SOCKSIZE6; + client_ep->state = SOCK_CONNECTED; + if (pico_addr_to_bsd(_orig, *socklen, &picoaddr, client_ep->s->net->proto_number) < 0) { + client_ep->in_use = 0; + pico_mutex_unlock(picoLock); + return -1; + } + pico_port_to_bsd(_orig, *socklen, port); + + client_ep->in_use = 1; + pico_mutex_unlock(picoLock); + ep->error = pico_err; + return client_ep->socket_fd; + } + client_ep->in_use = 0; + ep->error = pico_err; + errno = pico_err; + return -1; +} + +int pico_sendto(int sd, void * buf, int len, int flags, struct sockaddr *_dst, socklen_t socklen) +{ + int retval = 0; + int tot_len = 0; + uint16_t port; + union pico_address picoaddr; + struct pico_bsd_endpoint *ep = get_endpoint(sd, 1); + + VALIDATE_NULL(ep); + ep->error = PICO_ERR_NOERR; + + if (!buf || (len <= 0)) { + pico_err = PICO_ERR_EINVAL; + errno = pico_err; + ep->error = pico_err; + return -1; + } + + while (tot_len < len) { + /* Write to the pico socket */ + pico_mutex_lock(picoLock); + if (_dst == NULL) { + retval = pico_socket_send(ep->s, ((uint8_t *)buf) + tot_len, len - tot_len); + } else { + if (bsd_to_pico_addr(&picoaddr, _dst, socklen) < 0) { + ep->error = PICO_ERR_EINVAL; + errno = pico_err; + pico_mutex_unlock(picoLock); + return -1; + } + port = bsd_to_pico_port(_dst, socklen); + retval = pico_socket_sendto(ep->s, ((uint8_t *)buf) + tot_len, len - tot_len, &picoaddr, port); + } + pico_event_clear(ep, PICO_SOCK_EV_WR); + pico_mutex_unlock(picoLock); + + /* If sending failed, return an error */ + if (retval < 0) + { + ep->error = pico_err; + errno = pico_err; + pico_event_clear(ep, PICO_SOCK_EV_WR); + return -1; + } + + if (retval > 0) + { + tot_len += retval; + break; + } + + if (ep->nonblocking) + break; + + /* If sent bytes (retval) < len-tot_len: socket full, we need to wait for a new WR event */ + if (retval < (len - tot_len)) + { + uint16_t ev = 0; + /* wait for a new WR or CLOSE event */ + ev = pico_bsd_wait(ep, 0, 1, 1); + + if (ev & (PICO_SOCK_EV_ERR | PICO_SOCK_EV_FIN | PICO_SOCK_EV_CLOSE)) + { + ep->error = pico_err; + errno = pico_err; + pico_event_clear(ep, PICO_SOCK_EV_WR); + /* closing and freeing the socket is done in the event handler */ + return -1; + } + } + tot_len += retval; + } + ep->error = pico_err; + return tot_len; +} + +int pico_fcntl(int sd, int cmd, int arg) +{ + struct pico_bsd_endpoint *ep = get_endpoint(sd, 1); + if (!ep) { + pico_err = PICO_ERR_EINVAL; + errno = pico_err; + return -1; + } + + if (cmd == F_SETFL) { + if ((arg & O_NONBLOCK) != 0) { + ep->nonblocking = 1; + } else { + ep->nonblocking = 0; + } + ep->error = PICO_ERR_NOERR; + return 0; + } + + if (cmd == F_GETFL) { + (void)arg; /* F_GETFL: arg is ignored */ + ep->error = PICO_ERR_NOERR; + if (ep->nonblocking) + return O_NONBLOCK; + else + return 0; + } + + if (cmd == F_SETFD) { + (void)arg; + ep->error = PICO_ERR_NOERR; + return 0; + } + + + pico_err = PICO_ERR_EINVAL; + errno = pico_err; + ep->error = pico_err; + return -1; +} + +/* + * RETURN VALUE + * Upon successful completion, recv_from() shall return the length of the + * message in bytes. If no messages are available to be received and the + * peer has performed an orderly shutdown, recv() shall return 0. Otherwise, + * −1 shall be returned and errno set to indicate the error. + */ +int pico_recvfrom(int sd, void * _buf, int len, int flags, struct sockaddr *_addr, socklen_t *socklen) +{ + int retval = 0; + int tot_len = 0; + struct pico_bsd_endpoint *ep = get_endpoint(sd, 1); + union pico_address picoaddr; + uint16_t port; + unsigned char *buf = (unsigned char *)_buf; + bsd_dbg("Recvfrom called \n"); + + VALIDATE_NULL(ep); + ep->error = PICO_ERR_NOERR; + + if (ep->state == SOCK_RESET_BY_PEER) { + /* not much to do here. Peer has nothing to say. */ + return 0; + } + + if (!buf || (len <= 0)) { + pico_err = PICO_ERR_EINVAL; + errno = pico_err; + ep->error = pico_err; + return -1; + } + + while (tot_len < len) { + pico_mutex_lock(picoLock); + retval = pico_socket_recvfrom(ep->s, buf + tot_len , len - tot_len, &picoaddr, &port); + pico_mutex_unlock(picoLock); + bsd_dbg("pico_socket_recvfrom returns %d, first bytes are %c-%c-%c-%c\n", retval, buf[0], buf[1], buf[2], buf[3]); + + /* pico_socket_recvfrom failed */ + if (retval < 0) { + /* data was received */ + if (tot_len > 0) + { + bsd_dbg("Recvfrom returning %d\n", tot_len); + ep->error = pico_err; + return tot_len; + } + /* no data was received yet */ + ep->error = pico_err; + if (pico_err == PICO_ERR_ESHUTDOWN) /* If no messages are available to be received and the peer has performed an orderly shutdown, recvfrom() shall return 0. */ + { + return 0; + } + else /* Otherwise, the function shall return −1 and set errno to indicate the error. */ + { + return -1; + } + } + + /* If received 0 bytes, return -1 or amount of bytes received */ + if (retval == 0) { + pico_event_clear(ep, PICO_SOCK_EV_RD); + if (tot_len > 0) { + bsd_dbg("Recvfrom returning %d\n", tot_len); + ep->error = pico_err; + return tot_len; + } + } + + if (retval > 0) { + if (ep->proto == PICO_PROTO_UDP) { + if (_addr && socklen > 0) + { + if (pico_addr_to_bsd(_addr, *socklen, &picoaddr, ep->s->net->proto_number) < 0) { + pico_err = PICO_ERR_EINVAL; + errno = pico_err; + ep->error = pico_err; + return -1; + } + pico_port_to_bsd(_addr, *socklen, port); + } + /* If in a recvfrom call, for UDP we should return immediately after the first dgram */ + ep->error = pico_err; + return retval + tot_len; + } else { + /* TCP: continue until recvfrom = 0, socket buffer empty */ + tot_len += retval; + continue; + } + } + + if (ep->nonblocking) + { + if (retval == 0) + { + pico_err = PICO_ERR_EAGAIN; /* or EWOULDBLOCK */ + errno = pico_err; + retval = -1; /* BSD-speak: -1 == 0 bytes received */ + } + break; + } + + /* If recv bytes (retval) < len-tot_len: socket empty, we need to wait for a new RD event */ + if (retval < (len - tot_len)) + { + uint16_t ev = 0; + /* wait for a new RD event -- also wait for CLOSE event */ + ev = pico_bsd_wait(ep, 1, 0, 1); + if (ev & (PICO_SOCK_EV_ERR | PICO_SOCK_EV_FIN | PICO_SOCK_EV_CLOSE)) + { + /* closing and freeing the socket is done in the event handler */ + pico_event_clear(ep, PICO_SOCK_EV_RD); + ep->error = pico_err; + return 0; /* return 0 on a properly closed socket */ + } + } + tot_len += retval; + } + pico_event_clear(ep, PICO_SOCK_EV_RD); // What if still space available and we clear it here?? + bsd_dbg("Recvfrom returning %d (full block)\n", tot_len); + ep->error = pico_err; + + if (tot_len == 0) + { + errno = pico_err; + tot_len = -1; /* BSD-speak: -1 == 0 bytes received */ + } + return tot_len; +} + +int pico_write(int sd, void * buf, int len) +{ + return pico_sendto(sd, buf, len, 0, NULL, 0); +} + +int pico_send(int sd, void * buf, int len, int flags) +{ + return pico_sendto(sd, buf, len, flags, NULL, 0); +} + +int pico_read(int sd, void * buf, int len) +{ + return pico_recvfrom(sd, buf, len, 0, NULL, 0); +} + +int pico_recv(int sd, void * buf, int len, int flags) +{ + return pico_recvfrom(sd, buf, len, flags, NULL, 0); +} + + + +int pico_close(int sd) +{ + struct pico_bsd_endpoint *ep = get_endpoint(sd, 1); + VALIDATE_NULL(ep); + ep->error = PICO_ERR_NOERR; + + if (ep->s && ep->in_use) /* valid socket, try to close it */ + { + pico_mutex_lock(picoLock); + pico_socket_close(ep->s); + ep->s->priv = NULL; + pico_mutex_unlock(picoLock); + } + ep->in_use = 0; + ep->error = pico_err; + return 0; +} + +int pico_shutdown(int sd, int how) +{ + struct pico_bsd_endpoint *ep = get_endpoint(sd, 1); + VALIDATE_NULL(ep); + ep->error = PICO_ERR_NOERR; + + if(ep->s) /* valid socket, try to close it */ + { + pico_mutex_lock(picoLock); + pico_socket_shutdown(ep->s, how); + ep->error = pico_err; + pico_mutex_unlock(picoLock); + } else { + ep->error = PICO_ERR_EINVAL; + errno = pico_err; + } + return 0; +} + +int pico_join_multicast_group(int sd, const char *address, const char *local) { + + int ret; + struct pico_ip_mreq mreq={}; + + pico_string_to_ipv4(address, &mreq.mcast_group_addr.ip4.addr); + pico_string_to_ipv4(local, &mreq.mcast_link_addr.ip4.addr); + ret = pico_setsockopt(sd, SOL_SOCKET, PICO_IP_ADD_MEMBERSHIP, &mreq, sizeof(struct pico_ip_mreq)); + + return ret; +} + +/*** Helper functions ***/ +static int bsd_to_pico_addr(union pico_address *addr, struct sockaddr *_saddr, socklen_t socklen) +{ + VALIDATE_TWO(socklen, SOCKSIZE, SOCKSIZE6); + if (socklen == SOCKSIZE6) { + struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)_saddr; + memcpy(&addr->ip6.addr, &saddr->sin6_addr.s6_addr, 16); + saddr->sin6_family = AF_INET6; + } else { + struct sockaddr_in *saddr = (struct sockaddr_in *)_saddr; + addr->ip4.addr = saddr->sin_addr.s_addr; + saddr->sin_family = AF_INET; + } + return 0; +} + +static uint16_t bsd_to_pico_port(struct sockaddr *_saddr, socklen_t socklen) +{ + VALIDATE_TWO(socklen, SOCKSIZE, SOCKSIZE6); + if (socklen == SOCKSIZE6) { + struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)_saddr; + return saddr->sin6_port; + } else { + struct sockaddr_in *saddr = (struct sockaddr_in *)_saddr; + return saddr->sin_port; + } +} + +static int pico_port_to_bsd(struct sockaddr *_saddr, socklen_t socklen, uint16_t port) +{ + if (socklen == SOCKSIZE6) { + struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)_saddr; + saddr->sin6_port = port; + return 0; + } else { + struct sockaddr_in *saddr = (struct sockaddr_in *)_saddr; + saddr->sin_port = port; + return 0; + } + pico_err = PICO_ERR_EINVAL; + errno = pico_err; + return -1; +} + +static int pico_addr_to_bsd(struct sockaddr *_saddr, socklen_t socklen, union pico_address *addr, uint16_t net) +{ + VALIDATE_TWO(socklen, SOCKSIZE, SOCKSIZE6); + if ((socklen == SOCKSIZE6) && (net == PICO_PROTO_IPV6)) { + struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)_saddr; + memcpy(&saddr->sin6_addr.s6_addr, &addr->ip6.addr, 16); + saddr->sin6_family = AF_INET6; + } else if ((socklen == SOCKSIZE) && (net == PICO_PROTO_IPV4)) { + struct sockaddr_in *saddr = (struct sockaddr_in *)_saddr; + saddr->sin_addr.s_addr = addr->ip4.addr; + saddr->sin_family = AF_INET; + } + return 0; +} + +static void free_up_ep(struct pico_bsd_endpoint *ep) +{ + if (ep->signal) + pico_signal_deinit(ep->signal); + if (ep->mutex_lock) + pico_mutex_deinit(ep->mutex_lock); + PICO_FREE(ep); +} + +static int get_free_sd(struct pico_bsd_endpoint *ep) +{ + int i; + for (i = 0; i < PicoSocket_max; i++) { + if (!PicoSockets[i]->in_use) { + free_up_ep(PicoSockets[i]); + PicoSockets[i] = ep; + return i; + } + } + return -1; +} + +/* DLA TODO: make a GC for freeing up the last socket descriptor periodically if not in use */ + +static int new_sd(struct pico_bsd_endpoint *ep) +{ + int sd = PicoSocket_max; + struct pico_bsd_endpoint **new; + new = PICO_ZALLOC(sizeof(void *) * ++PicoSocket_max); + if (!new) { + PicoSocket_max--; + pico_err = PICO_ERR_ENOMEM; + errno = pico_err; + return -1; + } + if (sd > 0) { + memcpy(new, PicoSockets, sd * sizeof(void *)); + PICO_FREE(PicoSockets); + } + PicoSockets = new; + new[sd] = ep; + return sd; +} + +/* picoLock must be taken already ! */ +static struct pico_bsd_endpoint *pico_bsd_create_socket(void) +{ + struct pico_bsd_endpoint *ep = PICO_ZALLOC(sizeof(struct pico_bsd_endpoint)); + if (!ep) { + pico_err = PICO_ERR_ENOMEM; + errno = pico_err; + } + ep->in_use = 1; + ep->socket_fd = get_free_sd(ep); + if (ep->socket_fd < 0) { + ep->socket_fd = new_sd(ep); + } + return ep; +} + + +#ifndef PICO_EBADFD +# define PICO_EBADFD 77 /* File descriptor in bad state */ +#endif + +static struct pico_bsd_endpoint *get_endpoint(int sd, int set_err) +{ + if ((sd > PicoSocket_max) || (sd < 0) || + (PicoSockets[sd]->in_use == 0)) { + if (set_err) + { + pico_err = PICO_EBADFD; + errno = pico_err; + } + return NULL; + } + return PicoSockets[sd]; +} + +/* wait for one of the selected events, return any of those that occurred */ +uint16_t pico_bsd_select(struct pico_bsd_endpoint *ep) +{ + uint16_t events = ep->events & ep->revents; /* maybe an event we are waiting for, was already queued ? */ + /* wait for one of the selected events... */ + while (!events) + { + pico_signal_wait(ep->signal); + events = (ep->revents & ep->events); /* filter for the events we were waiting for */ + } + /* the event we were waiting for happened, now report it */ + return events; /* return any event(s) that occurred, that we were waiting for */ +} + + +/****************************/ +/* Private helper functions */ +/****************************/ +static uint16_t pico_bsd_wait(struct pico_bsd_endpoint * ep, int read, int write, int close) +{ + pico_mutex_lock(ep->mutex_lock); + + ep->events = PICO_SOCK_EV_ERR; + ep->events |= PICO_SOCK_EV_FIN; + ep->events |= PICO_SOCK_EV_CONN; + if (close) + ep->events |= PICO_SOCK_EV_CLOSE; + if (read) + ep->events |= PICO_SOCK_EV_RD; + if (write) + ep->events |= PICO_SOCK_EV_WR; + + pico_mutex_unlock(ep->mutex_lock); + + return pico_bsd_select(ep); +} + + +static void pico_event_clear(struct pico_bsd_endpoint *ep, uint16_t events) +{ + pico_mutex_lock(ep->mutex_lock); + ep->revents &= ~events; /* clear those events */ + pico_mutex_unlock(ep->mutex_lock); +} + +/* NOTE: __NO__ picoLock'ing here !! */ +/* this is called from pico_stack_tick, so picoLock is already locked */ +static void pico_socket_event(uint16_t ev, struct pico_socket *s) +{ + struct pico_bsd_endpoint * ep = (struct pico_bsd_endpoint *)(s->priv); + if (!s) + return; + if(!ep || !ep->s || !ep->mutex_lock || !ep->signal ) + { + /* DLA: do not call close upon SOCK_CLOSE, we might still write. */ + if(ev & (PICO_SOCK_EV_FIN | PICO_SOCK_EV_ERR) ) + { + pico_signal_send(pico_signal_select); /* Signal this event globally (e.g. for select()) */ + pico_socket_close(s); + } + + if (ev & PICO_SOCK_EV_CLOSE) + pico_signal_send(pico_signal_select); + + /* endpoint not initialized yet! */ + return; + } + + if(ep->in_use != 1) + return; + + pico_mutex_lock(ep->mutex_lock); /* lock over the complete body is needed, + as the event might get cleared in another process.. */ + ep->revents |= ev; /* set those events */ + + if(ev & PICO_SOCK_EV_CONN) + { + if(ep->state != SOCK_LISTEN) + { + ep->state = SOCK_CONNECTED; + } + } + + if(ev & PICO_SOCK_EV_ERR) + { + ep->state = SOCK_RESET_BY_PEER; + } + + if (ev & PICO_SOCK_EV_CLOSE) { + ep->state = SOCK_RESET_BY_PEER; + /* DO NOT close: we might still write! */ + } + + if (ev & PICO_SOCK_EV_FIN) { + /* DO NOT set ep->s = NULL, we might still be transmitting stuff! */ + ep->state = SOCK_CLOSED; + } + + pico_signal_send(pico_signal_select); /* Signal this event globally (e.g. for select()) */ + pico_signal_send(ep->signal); /* Signal the endpoint that was blocking on this event */ + + pico_mutex_unlock(ep->mutex_lock); +} + + +#define DNSQUERY_OK 1 +#define DNSQUERY_FAIL 0xFF +struct dnsquery_cookie +{ + struct addrinfo **res; + void *signal; + uint8_t block; + uint8_t revents; +}; + +static struct dnsquery_cookie *dnsquery_cookie_create(struct addrinfo **res, uint8_t block) +{ + struct dnsquery_cookie *ck = PICO_ZALLOC(sizeof(struct dnsquery_cookie)); + if (!ck) { + pico_err = PICO_ERR_ENOMEM; + errno = pico_err; + return NULL; + } + ck->signal = pico_signal_init(); + ck->res = res; + ck->block = block; + return ck; +} + +static int dnsquery_cookie_delete(struct dnsquery_cookie *ck) +{ + if (!ck) { + pico_err = PICO_ERR_EINVAL; + errno = pico_err; + return -1; + } + if (ck->signal) + { + pico_signal_deinit(ck->signal); + ck->signal = NULL; + } + PICO_FREE(ck); + return 0; +} + +#ifdef PICO_SUPPORT_IPV6 +static void dns_ip6_cb(char *ip, void *arg) +{ + struct dnsquery_cookie *ck = (struct dnsquery_cookie *)arg; + struct addrinfo *new; + + if (ip) { + new = PICO_ZALLOC(sizeof(struct addrinfo)); + if (!new) { + ck->revents = DNSQUERY_FAIL; + if (ck->block) + pico_signal_send(ck->signal); + return; + } + new->ai_family = AF_INET6; + new->ai_addr = PICO_ZALLOC(sizeof(struct sockaddr_in6)); + if (!new->ai_addr) { + PICO_FREE(new); + ck->revents = DNSQUERY_FAIL; + if (ck->block) + pico_signal_send(ck->signal); + return; + } + new->ai_addrlen = sizeof(struct sockaddr_in6); + pico_string_to_ipv6(ip, (((struct sockaddr_in6*)(new->ai_addr))->sin6_addr.s6_addr)); + ((struct sockaddr_in6*)(new->ai_addr))->sin6_family = AF_INET6; + new->ai_next = *ck->res; + *ck->res = new; + ck->revents = DNSQUERY_OK; + } else { + /* No ip given, but still callback was called: timeout! */ + ck->revents = DNSQUERY_FAIL; + } + + if (ck->block) + pico_signal_send(ck->signal); +} +#endif + +static void dns_ip4_cb(char *ip, void *arg) +{ + struct dnsquery_cookie *ck = (struct dnsquery_cookie *)arg; + struct addrinfo *new; + if (ip) { + new = PICO_ZALLOC(sizeof(struct addrinfo)); + if (!new) { + ck->revents = DNSQUERY_FAIL; + if (ck->block) + pico_signal_send(ck->signal); + return; + } + new->ai_family = AF_INET; + new->ai_addr = PICO_ZALLOC(sizeof(struct sockaddr_in)); + if (!new->ai_addr) { + PICO_FREE(new); + ck->revents = DNSQUERY_FAIL; + if (ck->block) + pico_signal_send(ck->signal); + return; + } + new->ai_addrlen = sizeof(struct sockaddr_in); + pico_string_to_ipv4(ip, &(((struct sockaddr_in*)new->ai_addr)->sin_addr.s_addr)); + ((struct sockaddr_in*)(new->ai_addr))->sin_family = AF_INET; + new->ai_next = *ck->res; + *ck->res = new; + ck->revents = DNSQUERY_OK; + } else { + /* No ip given, but still callback was called: timeout! */ + ck->revents = DNSQUERY_FAIL; + } + if (ck->block) + pico_signal_send(ck->signal); +} + +#ifdef PICO_SUPPORT_DNS_CLIENT +int pico_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) +{ + struct dnsquery_cookie *ck4 = NULL; + struct dnsquery_cookie *ck6 = NULL; + struct sockaddr_in sa4; + *res = NULL; + (void)service; + bsd_dbg("Called pico_getaddrinfo, looking for %s\n", node); + +#ifdef PICO_SUPPORT_IPV6 + struct sockaddr_in6 sa6; + if (pico_string_to_ipv6(node, sa6.sin6_addr.s6_addr) == 0) { + ck6 = dnsquery_cookie_create(res, 0); + dns_ip6_cb((char *)node, ck6); + dnsquery_cookie_delete(ck6); + return 0; + } +#endif + + if (pico_string_to_ipv4(node, &sa4.sin_addr.s_addr) == 0) { + ck4 = dnsquery_cookie_create(res, 0); + dns_ip4_cb((char*)node, ck4); + dnsquery_cookie_delete(ck4); + return 0; + } + +#ifdef PICO_SUPPORT_IPV6 + { + if (!hints || (hints->ai_family == AF_INET6)) { + ck6 = dnsquery_cookie_create(res, 1); + if (!ck6) + return -1; + pico_mutex_lock(picoLock); + if (pico_dns_client_getaddr6(node, dns_ip6_cb, ck6) < 0) + { + bsd_dbg("Error resolving AAAA record %s\n", node); + dnsquery_cookie_delete(ck6); + pico_mutex_unlock(picoLock); + return -1; + } + bsd_dbg("Resolving AAAA record %s\n", node); + pico_mutex_unlock(picoLock); + } + } +#endif /* PICO_SUPPORT_IPV6 */ + + if (!hints || (hints->ai_family == AF_INET)) { + ck4 = dnsquery_cookie_create(res, 1); + pico_mutex_lock(picoLock); + if (pico_dns_client_getaddr(node, dns_ip4_cb, ck4) < 0) + { + bsd_dbg("Error resolving A record %s\n", node); + dnsquery_cookie_delete(ck4); + pico_mutex_unlock(picoLock); + return -1; + } + bsd_dbg("Resolving A record %s\n", node); + pico_mutex_unlock(picoLock); + } + +#ifdef PICO_SUPPORT_IPV6 + if (ck6) { + /* Signal is always sent; either dns resolved, or timeout/failure */ + pico_signal_wait(ck6->signal); + dnsquery_cookie_delete(ck6); + } +#endif /* PICO_SUPPORT_IPV6 */ + + if (ck4) { + /* Signal is always sent; either dns resolved, or timeout/failure */ + pico_signal_wait(ck4->signal); + dnsquery_cookie_delete(ck4); + } + + if (*res) + return 0; + + return -1; +} + +void pico_freeaddrinfo(struct addrinfo *res) +{ + struct addrinfo *cur = res; + struct addrinfo *nxt; + while(cur) { + if (cur->ai_addr) + PICO_FREE(cur->ai_addr); + nxt = cur->ai_next; + PICO_FREE(cur); + cur = nxt; + } +} + +/* Legacy gethostbyname call implementation */ +static struct hostent PRIV_HOSTENT = { }; +struct hostent *pico_gethostbyname(const char *name) +{ + struct addrinfo *res; + struct addrinfo hint = {.ai_family = AF_INET}; + int ret; + if (!PRIV_HOSTENT.h_addr_list) { + /* Done only once: reserve space for 2 entries */ + PRIV_HOSTENT.h_addr_list = PICO_ZALLOC(2 * sizeof(void*)); + PRIV_HOSTENT.h_addr_list[1] = NULL; + } + ret = pico_getaddrinfo(name, NULL, &hint, &res); + if (ret == 0) { + if (PRIV_HOSTENT.h_name != NULL) { + PICO_FREE(PRIV_HOSTENT.h_name); + PRIV_HOSTENT.h_name = NULL; + } + if (PRIV_HOSTENT.h_addr_list[0] != NULL) { + PICO_FREE(PRIV_HOSTENT.h_addr_list[0]); + PRIV_HOSTENT.h_addr_list[0] = NULL; + } + PRIV_HOSTENT.h_name = PICO_ZALLOC(strlen(name)); + if (!PRIV_HOSTENT.h_name) { + pico_freeaddrinfo(res); + return NULL; + } + strcpy(PRIV_HOSTENT.h_name, name); + PRIV_HOSTENT.h_addrtype = res->ai_addr->sa_family; + if (PRIV_HOSTENT.h_addrtype == AF_INET) { + PRIV_HOSTENT.h_length = 4; + PRIV_HOSTENT.h_addr_list[0] = PICO_ZALLOC(4); + if (!PRIV_HOSTENT.h_addr_list[0]) { + pico_freeaddrinfo(res); + return NULL; + } + memcpy (PRIV_HOSTENT.h_addr_list[0], &(((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr), 4); + } else { + /* Only IPv4 supported by this ancient call. */ + pico_freeaddrinfo(res); + return NULL; + } + pico_freeaddrinfo(res); + return &PRIV_HOSTENT; + } + return NULL; +} +#endif + +int pico_getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) +{ + struct pico_bsd_endpoint *ep = get_endpoint(sockfd, 1); + int ret; + bsd_dbg("called getsockopt\n"); + VALIDATE_NULL(ep); + if (level != SOL_SOCKET) { + pico_err = PICO_ERR_EPROTONOSUPPORT; + errno = pico_err; + return -1; + } + if (!ep) { + pico_err = PICO_ERR_EINVAL; + errno = pico_err; + return -1; + } + if (!optval) { + pico_err = PICO_ERR_EFAULT; + errno = pico_err; + return -1; + } + + if (optname == SO_ERROR) + { + *((int*)optval) = ep->error; + ep->error = 0; + return 0; + } + + pico_mutex_lock(ep->mutex_lock); + ret = pico_socket_getoption(ep->s, sockopt_get_name(optname), optval); + pico_mutex_unlock(ep->mutex_lock); + return ret; + +} + +int pico_setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) +{ + + struct pico_bsd_endpoint *ep = get_endpoint(sockfd, 1); + int ret; + VALIDATE_NULL(ep); + ep->error = PICO_ERR_NOERR; + bsd_dbg("called setsockopt\n"); + if (level != SOL_SOCKET) { + pico_err = PICO_ERR_EPROTONOSUPPORT; + errno = pico_err; + return -1; + } + if (!ep) { + pico_err = PICO_ERR_EINVAL; + errno = pico_err; + return -1; + } + if (!optval) { + pico_err = PICO_ERR_EFAULT; + errno = pico_err; + return -1; + } + if ((optname == SO_REUSEADDR) || (optname == SO_REUSEPORT)) + return 0; /* Pretend it was OK. */ + if (optname == SO_ERROR) + return PICO_ERR_ENOPROTOOPT; + + pico_mutex_lock(ep->mutex_lock); + ret = pico_socket_setoption(ep->s, sockopt_get_name(optname), (void *)optval); + pico_mutex_unlock(ep->mutex_lock); + return ret; +} +#ifdef PICO_SUPPORT_SNTP_CLIENT +int pico_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + int ret; + (void)tz; + struct pico_timeval ptv; + + ret= pico_sntp_gettimeofday(&ptv); + + tv->tv_sec = ptv.tv_sec; + tv->tv_usec= ptv.tv_msec * 1000; /* pico_timeval uses milliseconds instead of microseconds */ + return ret; +} + +/* dummy function */ +int pico_settimeofday(struct timeval *tv, struct timezone *tz) +{ + (void)tz; + (void)tv; + return 0; +} + +#else + +static struct pico_timeval ptv = {0u,0u}; + +int pico_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + int ret; + (void)tz; + + tv->tv_sec = ptv.tv_sec; + tv->tv_usec= ptv.tv_msec * 1000; /* pico_timeval uses milliseconds instead of microseconds */ + return 0; +} + +int pico_settimeofday(struct timeval *tv, struct timezone *tz) +{ + int ret; + (void)tz; + + ptv.tv_sec = tv->tv_sec; + ptv.tv_msec= tv->tv_usec / 1000; /* pico_timeval uses milliseconds instead of microseconds */ + return 0; +} +#endif + +long XTIME(void) { + struct timeval t; + pico_gettimeofday(&t, NULL); + return (long)t.tv_sec; +} + +const char *pico_inet_ntop(int af, const void *src, char *dst, socklen_t size) +{ + if ((!dst) || (!src)) + return NULL; + + switch (af) + { + case AF_INET: + if (size < INET_ADDRSTRLEN) + return NULL; + pico_ipv4_to_string(dst, *((const uint32_t *)src)); + break; +#ifdef PICO_SUPPORT_IPV6 + case AF_INET6: + if (size < INET6_ADDRSTRLEN) + return NULL; + pico_ipv6_to_string(dst, ((struct in6_addr *)src)->s6_addr); + break; +#endif + default: + dst = NULL; + break; + } + return dst; +} + +char *pico_inet_ntoa(struct in_addr in) +{ + static char ipbuf[INET_ADDRSTRLEN]; + pico_ipv4_to_string(ipbuf, (uint32_t)in.s_addr); + return ipbuf; +} + + + +int pico_pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask) +{ + /* + * EV_READ: sets the readfds + * EV_WRITE: sets the writefds + * EV_CONN: sets the readfds (a.k.a. someone connects to your listening socket) + * EV_CLOSE: sets the readfds, then next recv() returns 0; + * EV_FIN: sets the readfds, then next recv() returns 0; + * EV_ERR: sets the exceptfds + */ + + int i = 0; /* socket fds */ + int nfds_out = 0; /* amount of changed sockets */ + (void) sigmask; + + bsd_dbg_select("=== IN: PICO SELECT === readfds[0]: 0x%x -- writefds[0]: 0x%x\n", readfds?(*(uint8_t *)readfds):0, writefds?(*(uint8_t *)writefds):0); + + pico_fd_set readfds_out = {}; + pico_fd_set writefds_out = {}; + pico_fd_set exceptfds_out = {}; + /* First, loop over all possible file descriptors, check if one has an event pending that we're waiting for */ + while (nfds_out == 0) + { + for (i = 0; i < nfds; i++) + { + struct pico_bsd_endpoint *ep = get_endpoint(i, 0); + bsd_dbg_select("\t~~~ SELECT: fds %d - ep:%p ", i, ep); + if (ep) + { + /* Is this endpoint still valid? */ + if (!ep->in_use) + { + bsd_dbg_select(" ep->in_use = 0\n"); + break; + } + + /* READ event needed and available? */ + if (readfds && PICO_FD_ISSET(i,readfds) && (ep->revents & (PICO_SOCK_EV_CONN | PICO_SOCK_EV_CLOSE | PICO_SOCK_EV_RD))) + { + bsd_dbg_select("- READ_EV - "); + nfds_out++; + PICO_FD_SET(i, &readfds_out); + } + + /* Force write events on empty udp sockets */ + if ((ep->proto == PICO_PROTO_UDP) && (ep->s->q_out.size < ep->s->q_out.max_size)) + ep->revents |= PICO_SOCK_EV_WR; + + /* WRITE event needed? and available? */ + if (writefds && PICO_FD_ISSET(i,writefds) && (ep->revents & (PICO_SOCK_EV_WR))) + { + bsd_dbg_select("- WRITE_EV - "); + nfds_out++; + PICO_FD_SET(i, &writefds_out); + } + + /* EXCEPTION event needed and available? */ + if (exceptfds && PICO_FD_ISSET(i,exceptfds) && (ep->revents & (PICO_SOCK_EV_ERR))) + { + bsd_dbg_select("- EXCEPT_EV - "); + nfds_out++; + PICO_FD_SET(i, &exceptfds_out); + } + } + + if (ep) + bsd_dbg_select("- s:%p - ev:%x", ep->s, ep->revents); + bsd_dbg_select("\n"); + } + + /* If there was a hit, break out of the loop */ + if (nfds_out) + break; + + /* If not, wait for a semaphore signaling an event from the stack */ + if (pico_signal_wait_timeout(pico_signal_select, (timeout->tv_sec * 1000) + ((timeout->tv_nsec) / 1000000)) == -1) + { + /* On timeout, break out of the loop */ + bsd_dbg_select("\t~~~ SELECT: TIMEOUT\n"); + break; + } else { + /* Process the received event -> re-iterate */ + bsd_dbg_select("\t~~~ SELECT: Socket event, re-iterating fds\n"); + } + } + + /* Copy back result only if descriptor was valid */ + if (readfds) + memcpy(readfds, &readfds_out, sizeof(pico_fd_set)); + if (writefds) + memcpy(writefds, &writefds_out, sizeof(pico_fd_set)); + if (exceptfds) + memcpy(exceptfds, &exceptfds_out, sizeof(pico_fd_set)); + + bsd_dbg_select("=== OUT: PICO SELECT === fds changed: %d\n", nfds_out); + + return nfds_out; +} + +int pico_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) +{ + struct timespec ts; + if (timeout) { + ts.tv_sec = timeout->tv_sec; + ts.tv_nsec = timeout->tv_usec * 1000; + return pico_pselect(nfds, readfds, writefds, exceptfds, &ts, NULL); + } else + return pico_pselect(nfds, readfds, writefds, exceptfds, NULL, NULL); +} + +int pico_ppoll(struct pollfd *pfd, nfds_t npfd, const struct timespec *timeout, const sigset_t *sigmask) { + int i; + int ret = 0; + (void) sigmask; + + while (ret == 0) { + for (i = 0; i < npfd; i++) { + struct pico_bsd_endpoint *ep = get_endpoint(pfd[i].fd, 0); + pfd[i].revents = 0u; + + /* Always polled events */ + if (!ep) { + pfd[i].revents |= POLLNVAL; + } + if (!ep->in_use) { + pfd[i].revents |= POLLNVAL; + } + if (ep->revents & (PICO_SOCK_EV_FIN | PICO_SOCK_EV_ERR)) { + pfd[i].revents |= POLLERR; + ret++; + } + if (ep->revents & PICO_SOCK_EV_CLOSE) + pfd[i].revents |= POLLHUP; /* XXX: I am sure we mean POLLRDHUP ! see man 2 poll */ + + /* Checking POLLIN */ + if ((pfd[i].events & POLLIN) && (ep->revents & (PICO_SOCK_EV_RD | PICO_SOCK_EV_CONN))) { + pfd[i].revents |= POLLIN; + if (pfd[i].events & POLLRDNORM) + pfd[i].revents |= POLLRDNORM; + } + /* Checking POLLOUT */ + if ((pfd[i].events & POLLOUT) && (ep->revents & (PICO_SOCK_EV_WR))) { + pfd[i].revents |= POLLOUT; + if (pfd[i].events & POLLWRNORM) + pfd[i].revents |= POLLWRNORM; + } + + if (pfd[i].revents != 0) + ret++; + } /* End for loop */ + if ((ret == 0) && timeout && (pico_signal_wait_timeout(pico_signal_select, (timeout->tv_sec * 1000) + ((timeout->tv_nsec) / 1000000)) == -1)) + return 0; /* Timeout */ + } /* End while loop */ + return ret; +} + +int pico_poll(struct pollfd *pfd, nfds_t npfd, int timeout) +{ + struct timespec ts = {0U, 0U}; + if (timeout >= 0) { + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000; + return pico_ppoll(pfd, npfd, &ts, NULL); + } else { + return pico_ppoll(pfd, npfd, NULL, NULL); + } +} diff --git a/ext/pico_bsd/pico_bsd_sockets.h b/ext/pico_bsd/pico_bsd_sockets.h new file mode 100644 index 0000000..e8e2198 --- /dev/null +++ b/ext/pico_bsd/pico_bsd_sockets.h @@ -0,0 +1,331 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2013 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. +Do not redistribute without a written permission by the Copyright +holders. + +Author: Maxime Vincent, Daniele Lacamera +*********************************************************************/ +#ifndef PICO_BSD_SOCKETS_H_ +#define PICO_BSD_SOCKETS_H_ + +#include +#include +#include "pico_defines.h" +#include "pico_constants.h" +#include "pico_config.h" +#include "pico_stack.h" +#include "pico_icmp4.h" +#include "pico_stack.h" +#include "pico_ipv4.h" +#include "pico_ipv6.h" +#include "pico_dns_client.h" +#include "pico_socket.h" + +#define SOCKSIZE 16 +#define SOCKSIZE6 28 + +struct pico_bsd_endpoint; +extern void *picoLock; +extern void *pico_signal_tick; + +#if defined STDSOCKET || defined __socklen_t_defined + #include + #include + #include + #include + #include + #include + #include + #ifdef __linux__ + #include + #endif + + static inline int sockopt_get_name(int posix_name) + { + switch (posix_name) { + case IP_MULTICAST_LOOP: return PICO_IP_MULTICAST_LOOP; + case IP_MULTICAST_TTL: return PICO_IP_MULTICAST_TTL; + case IP_MULTICAST_IF: return PICO_IP_MULTICAST_IF; + case IP_ADD_MEMBERSHIP: return PICO_IP_ADD_MEMBERSHIP; + case IP_DROP_MEMBERSHIP: return PICO_IP_DROP_MEMBERSHIP; + case SO_RCVBUF : return PICO_SOCKET_OPT_RCVBUF; + case SO_SNDBUF : return PICO_SOCKET_OPT_SNDBUF; + case TCP_NODELAY : return PICO_TCP_NODELAY; + case TCP_KEEPCNT : return PICO_SOCKET_OPT_KEEPCNT; + case TCP_KEEPIDLE : return PICO_SOCKET_OPT_KEEPIDLE; + case TCP_KEEPINTVL : return PICO_SOCKET_OPT_KEEPINTVL; + } + return -1; + } + + #define pico_fd_set fd_set + #define PICO_FD_SET FD_SET + #define PICO_FD_CLR FD_CLR + #define PICO_FD_ISSET FD_ISSET + #define PICO_FD_ZERO FD_ZERO + + #undef fcntl + #define fcntl pico_fcntl + +#else + typedef int socklen_t; + #define AF_INET (PICO_PROTO_IPV4) + #define AF_INET6 (PICO_PROTO_IPV6) + #define SOCK_STREAM (PICO_PROTO_TCP) + #define SOCK_DGRAM (PICO_PROTO_UDP) + + #define SOL_SOCKET (0x80) + + #define IP_MULTICAST_LOOP (PICO_IP_MULTICAST_LOOP) + #define IP_MULTICAST_TTL (PICO_IP_MULTICAST_TTL) + #define IP_MULTICAST_IF (PICO_IP_MULTICAST_IF) + #define IP_ADD_MEMBERSHIP (PICO_IP_ADD_MEMBERSHIP) + #define IP_DROP_MEMBERSHIP (PICO_IP_DROP_MEMBERSHIP) + #define SO_RCVBUF (PICO_SOCKET_OPT_RCVBUF) + #define SO_SNDBUF (PICO_SOCKET_OPT_SNDBUF) + #define TCP_NODELAY (PICO_TCP_NODELAY) + #define TCP_KEEPCNT (PICO_SOCKET_OPT_KEEPCNT) + #define TCP_KEEPIDLE (PICO_SOCKET_OPT_KEEPIDLE) + #define TCP_KEEPINTVL (PICO_SOCKET_OPT_KEEPINTVL) + #define SO_ERROR (4103) + #define SO_REUSEADDR (2) + #define sockopt_get_name(x) ((x)) + + #define INET_ADDRSTRLEN (16) + #define INET6_ADDRSTRLEN (46) + + struct sockaddr { + uint16_t sa_family; + }; + + struct in_addr { + uint32_t s_addr; + }; + + #define INADDR_ANY ((uint32_t)0U) + + struct in6_addr { + uint8_t s6_addr[16]; + }; + + struct __attribute__((packed)) sockaddr_in { + uint16_t sin_family; + uint16_t sin_port; + struct in_addr sin_addr; + uint8_t _pad[SOCKSIZE - 8]; + }; + + + struct __attribute__((packed)) sockaddr_in6 { + uint16_t sin6_family; + uint16_t sin6_port; + uint32_t sin6_flowinfo; + struct in6_addr sin6_addr; + uint32_t sin6_scope_id; + }; + + struct __attribute__((packed)) sockaddr_storage { + uint16_t ss_family; + uint8_t _pad[(SOCKSIZE6 - sizeof(uint16_t))]; + }; + + /* getaddrinfo */ + struct addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + socklen_t ai_addrlen; + struct sockaddr *ai_addr; + char *ai_canonname; + struct addrinfo *ai_next; + }; + + + /* hostent */ + struct hostent { + char *h_name; /* official name of host */ + char **h_aliases; /* alias list */ + int h_addrtype; /* host address type */ + int h_length; /* length of address */ + char **h_addr_list; /* list of addresses */ + }; + #define h_addr h_addr_list[0] /* for backward compatibility */ + + /* fd_set */ + #ifndef FD_SETSIZE + #define FD_SETSIZE 64 /* 64 files max, 1 bit per file -> 64bits = 8 bytes */ + #endif + + struct pico_fd_set_s { + uint8_t fds_bits[FD_SETSIZE/8]; + }; + + typedef struct pico_fd_set_s pico_fd_set; + #ifndef fd_set + #define fd_set pico_fd_set + #endif + + //typedef void sigset_t; + + #define PICO_FD_SET(n, p) ((p)->fds_bits[(n)/8] |= (1u << ((n) % 8))) + #define PICO_FD_CLR(n, p) ((p)->fds_bits[(n)/8] &= ~(1u << ((n) % 8))) + #define PICO_FD_ISSET(n, p) ((p)->fds_bits[(n)/8] & (1u << ((n) % 8))) + #define PICO_FD_ZERO(p) do{memset((p)->fds_bits, 0, sizeof(struct pico_fd_set_s));}while(0) + + /* Not socket related */ + #ifndef __time_t_defined + typedef pico_time time_t; + #define __time_t_defined + #endif + + #if !defined _TIME_H && !defined _TIMEVAL_DEFINED && !defined _STRUCT_TIMEVAL + struct timeval { + time_t tv_sec; + time_t tv_usec; + }; + + #if !defined __timespec_defined && !defined _SYS__TIMESPEC_H_ + struct timespec { + long tv_sec; + long tv_nsec; + }; + #endif + + struct timezone { + int tz_minuteswest; /* minutes west of Greenwich */ + int tz_dsttime; /* type of DST correction */ + }; + #define _TIMEVAL_DEFINED + #endif +#endif /* STDSOCKET */ + +#ifndef SO_REUSEPORT + #define SO_REUSEPORT (15) +#endif + +#ifndef FD_CLOEXEC + #define FD_CLOEXEC 1 +#endif + +#ifndef F_DUPFD + #define F_DUPFD 0 +#endif + +#ifndef F_GETFD + #define F_GETFD 1 +#endif + +#ifndef F_SETFD + #define F_SETFD 2 +#endif + +#ifndef F_GETFL + #define F_GETFL 3 +#endif + +#ifndef F_SETFL + #define F_SETFL 4 +#endif + + +#ifndef O_NONBLOCK + #define O_NONBLOCK 0x4000 +#endif + +#ifndef _SYS_POLL_H + #define POLLIN 0x001 /* There is data to read. */ + #define POLLPRI 0x002 /* There is urgent data to read. */ + #define POLLOUT 0x004 /* Writing now will not block. */ + #define POLLRDNORM 0x040 /* Normal data may be read. */ + #define POLLRDBAND 0x080 /* Priority data may be read. */ + #define POLLWRNORM 0x100 /* Writing now will not block. */ + #define POLLWRBAND 0x200 /* Priority data may be written. */ + + #define POLLMSG 0x400 + #define POLLREMOVE 0x1000 + #define POLLRDHUP 0x2000 + + #define POLLERR 0x008 /* Error condition. */ + #define POLLHUP 0x010 /* Hung up. */ + #define POLLNVAL 0x020 /* Invalid polling request. */ + + typedef unsigned long int nfds_t; + + struct pollfd { + int fd; + uint16_t events; + uint16_t revents; + }; +#endif + +int pico_newsocket(int domain, int type, int proto); +int pico_bind(int sd, struct sockaddr * local_addr, socklen_t socklen); +int pico_listen(int sd, int backlog); +int pico_connect(int sd, struct sockaddr *_saddr, socklen_t socklen); +int pico_isconnected(int sd); +int pico_accept(int sd, struct sockaddr *_orig, socklen_t *socklen); +int pico_sendto(int sd, void * buf, int len, int flags, struct sockaddr *_dst, socklen_t socklen); +int pico_recvfrom(int sd, void * buf, int len, int flags, struct sockaddr *_addr, socklen_t *socklen); +int pico_write(int sd, void * buf, int len); +int pico_send(int sd, void * buf, int len, int flags); +int pico_read(int sd, void * buf, int len); +int pico_recv(int sd, void * buf, int len, int flags); +int pico_close(int sd); +int pico_shutdown(int sd, int how); +int pico_getsockname(int sd, struct sockaddr * local_addr, socklen_t *socklen); +int pico_getpeername(int sd, struct sockaddr * remote_addr, socklen_t *socklen); +int pico_fcntl(int sd, int cmd, int arg); +int pico_join_multicast_group(int sd, const char *address, const char *local); + +#ifdef PICO_SUPPORT_DNS_CLIENT + struct hostent *pico_gethostbyname(const char *name); + + /* getaddrinfo */ + int pico_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res); + + void pico_freeaddrinfo(struct addrinfo *res); +#endif + +int pico_setsockopt (int sockfd, int level, int optname, const void *optval, socklen_t optlen); +int pico_getsockopt (int sockfd, int level, int optname, void *optval, socklen_t *optlen); + +int pico_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); +int pico_pselect (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, + const sigset_t *sigmask); + +int pico_poll (struct pollfd *pfd, nfds_t npfd, int timeout); +int pico_ppoll (struct pollfd *pfd, nfds_t npfd, const struct timespec *timeout_ts, const sigset_t *sigmask); + +#ifndef PICO_SUPPORT_SNTP_CLIENT + struct pico_timeval + { + pico_time tv_sec; + pico_time tv_msec; + }; + int pico_settimeofday(struct timeval *tv, struct timezone *tz); +#endif + +int pico_gettimeofday(struct timeval *tv, struct timezone *tz); +long XTIME(void); +#define XGMTIME gmtime + +/* arpa/inet.h */ +const char *pico_inet_ntop (int af, const void *src, char *dst, socklen_t size); +char *pico_inet_ntoa (struct in_addr in); + +/* Non-POSIX */ +void pico_bsd_init(void); +void pico_bsd_deinit(void); +void pico_bsd_stack_tick(void); +void pico_bsd_stack_tick_timeout(int timeout_ms); +uint16_t pico_bsd_select(struct pico_bsd_endpoint *ep); + +#ifdef REPLACE_STDCALLS + #include "pico_bsd_syscalls.h" +#endif + +#endif /* PICO_BSD_SOCKETS_H_ */ diff --git a/ext/pico_bsd/pico_bsd_syscalls.h b/ext/pico_bsd/pico_bsd_syscalls.h new file mode 100644 index 0000000..177012c --- /dev/null +++ b/ext/pico_bsd/pico_bsd_syscalls.h @@ -0,0 +1,336 @@ +#ifndef PICO_BSD_SYSCALLS_H_ +#define PICO_BSD_SYSCALLS_H_ + +#include "pico_bsd_sockets.h" + +/* Cannot be included directly, expect from pico_bsd_sockets.h */ + +#if defined (__socklen_t_defined) && defined (REPLACE_STDCALLS) + + /* For systems that have the syscalls already defined */ + #ifdef socket + #undef socket + #endif + #define socket pico_newsocket + + #ifdef bind + #undef bind + #endif + #define bind pico_bind + + #ifdef listen + #undef listen + #endif + #define listen pico_listen + + #ifdef connect + #undef connect + #endif + #define connect pico_connect + + #ifdef accept + #undef accept + #endif + #define accept pico_accept + + #ifdef sendto + #undef sendto + #endif + #define sendto pico_sendto + + #ifdef recvfrom + #undef recvfrom + #endif + #define recvfrom pico_recvfrom + + #ifdef write + #undef write + #endif + #define write pico_write + + #ifdef read + #undef read + #endif + #define read pico_read + + #ifdef send + #undef send + #endif + #define send pico_send + + #ifdef recv + #undef recv + #endif + #define recv pico_recv + + #ifdef close + #undef close + #endif + #define close pico_close + + #ifdef shutdown + #undef shutdown + #endif + #define shutdown pico_shutdown + + #ifdef getsockname + #undef getsockname + #endif + #define getsockname pico_getsockname + + #ifdef getpeername + #undef getpeername + #endif + #define getpeername pico_getpeername + + #ifdef setsockopt + #undef setsockopt + #endif + #define setsockopt pico_setsockopt + + #ifdef getsockopt + #undef getsockopt + #endif + #define getsockopt pico_getsockopt + + #ifdef gettimeofday + #undef gettimeofday + #endif + #define gettimeofday pico_gettimeofday + + #ifdef gethostbyname + #undef gethostbyname + #endif + #define gethostbyname pico_gethostbyname + + #ifdef getaddrinfo + #undef getaddrinfo + #endif + #define getaddrinfo pico_getaddrinfo + + #ifdef freeaddrinfo + #undef freeaddrinfo + #endif + #define freeaddrinfo pico_freeaddrinfo + + #ifdef htons + #undef htons + #endif + #define htons short_be + + #ifdef htonl + #undef htonl + #endif + #define htonl long_be + + #ifdef ntohs + #undef ntohs + #endif + #define ntohs short_be + + #ifdef ntohl + #undef ntohl + #endif + #define ntohl long_be + + #ifdef inet_ntoa + #undef inet_ntoa + #endif + #define inet_ntoa pico_inet_ntoa + + #ifdef inet_ntop + #undef inet_ntop + #endif + #define inet_ntop pico_inet_ntop + + #ifdef select + #undef select + #endif + #define select pico_select + + #ifdef pselect + #undef pselect + #endif + #define pselect pico_pselect + + #ifdef poll + #undef poll + #endif + #define poll pico_poll + + #ifdef ppoll + #undef ppoll + #endif + #define ppoll pico_ppoll +#else + + static inline int socket(int domain, int type, int proto) + { + return pico_newsocket(domain, type, proto); + } + + static inline int bind(int sd, struct sockaddr * local_addr, socklen_t socklen) + { + return pico_bind(sd, local_addr, socklen); + } + + static inline int listen(int sd, int backlog) + { + return pico_listen(sd, backlog); + } + + static inline int connect(int sd, struct sockaddr *_saddr, socklen_t socklen) + { + return pico_connect(sd, _saddr, socklen); + } + + static inline int accept(int sd, struct sockaddr *_orig, socklen_t *socklen) + { + return pico_accept(sd, _orig, socklen); + } + + static inline int sendto(int sd, void * buf, int len, int flags, struct sockaddr *_dst, socklen_t socklen) + { + return pico_sendto(sd, buf, len, flags, _dst, socklen); + } + + static inline int recvfrom(int sd, void * buf, int len, int flags, struct sockaddr *_addr, socklen_t *socklen) + { + return pico_recvfrom(sd, buf, len, flags, _addr, socklen); + } + + static inline int write(int sd, void * buf, int len) + { + return pico_write(sd, buf, len); + } + + static inline int send(int sd, void * buf, int len, int flags) + { + return pico_send(sd, buf, len, flags); + } + + static inline int read(int sd, void * buf, int len) + { + return pico_read(sd, buf, len); + } + + static inline int recv(int sd, void * buf, int len, int flags) + { + return pico_recv(sd, buf, len, flags); + } + + static inline int close(int sd) + { + return pico_close(sd); + } + + static inline int shutdown(int sd, int how) + { + return pico_shutdown(sd, how); + } + + static inline int getsockname(int sd, struct sockaddr * local_addr, socklen_t *socklen) + { + return pico_getsockname(sd, local_addr, socklen); + } + + static inline int getpeername(int sd, struct sockaddr * remote_addr, socklen_t *socklen) + { + return pico_getpeername(sd, remote_addr, socklen); + } + + static inline int fcntl(int sd, int cmd, int arg) + { + return pico_fcntl(sd, cmd, arg); + } + + #ifdef PICO_SUPPORT_DNS_CLIENT + static inline struct hostent *gethostbyname(const char *name) + { + return pico_gethostbyname(name); + } + + static inline int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) + { + return pico_getaddrinfo(node, service, hints, res); + } + + static inline void freeaddrinfo(struct addrinfo *res) + { + return pico_freeaddrinfo(res); + } + #endif + + static inline int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) + { + return pico_setsockopt(sockfd, level, optname, optval, optlen); + } + + static inline int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) + { + return pico_getsockopt(sockfd, level, optname, optval, optlen); + } + + static inline int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) + { + return pico_select(nfds, readfds, writefds, exceptfds, timeout); + } + + static inline int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask) + { + return pico_pselect(nfds, readfds, writefds, exceptfds, timeout, sigmask); + } + + static inline int poll(struct pollfd *pfd, nfds_t npfd, int timeout) + { + return pico_poll(pfd, npfd, timeout); + } + + static inline int ppoll(struct pollfd *pfd, nfds_t npfd, const struct timespec *timeout_ts, const sigset_t *sigmask) + { + return pico_ppoll(pfd, npfd, timeout_ts, sigmask); + } + + static int gettimeofday(struct timeval *tv, struct timezone *tz) + { + return pico_gettimeofday(tv, tz); + } + + static int settimeofday(struct timeval *tv, struct timezone *tz) + { + return pico_settimeofday(tv, tz); + } + + static inline const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) + { + return pico_inet_ntop(af, src, dst, size); + } + + static inline char *inet_ntoa(struct in_addr in) + { + return pico_inet_ntoa(in); + } + + static inline uint32_t htonl(uint32_t le) + { + return long_be(le); + } + + static inline uint32_t ntohl(uint32_t le) + { + return long_be(le); + } + + static inline uint16_t htons(uint16_t le) + { + return short_be(le); + } + + static inline uint16_t ntohs(uint16_t le) + { + return short_be(le); + } + +#endif + +#endif /* PICO_BSD_SYSCALLS_H_ */ diff --git a/ext/pico_bsd/pico_osal.h b/ext/pico_bsd/pico_osal.h new file mode 100644 index 0000000..69dd61a --- /dev/null +++ b/ext/pico_bsd/pico_osal.h @@ -0,0 +1,45 @@ +/* + * pico_osal.h + * + * Created on: December 2013 + * Author: Maxime Vincent + * Description: OS Abstraction Layer between PicoTCP and FreeRTOS + * + */ + +#ifndef _PICO_OSAL_H_ +#define _PICO_OSAL_H_ + +/***************************************************************************** + * Public types/enumerations/variables + ****************************************************************************/ + +/* Queue implementation API is: */ + + +/* Posix version of thread */ +typedef void* pico_thread_t; +typedef void *(*pico_thread_fn)(void *); + +void * pico_mutex_init(void); +void pico_mutex_deinit(void * mutex); +void pico_mutex_lock(void * mutex); +int pico_mutex_lock_timeout(void * mutex, int timeout); +void pico_mutex_unlock(void * mutex); +void pico_mutex_unlock_ISR(void * mutex); + +void * pico_signal_init(void); +void pico_signal_deinit(void * signal); +void pico_signal_wait(void * signal); +int pico_signal_wait_timeout(void * signal, int timeout); +void pico_signal_send(void * signal); +void pico_signal_send_ISR(void * signal); + +pico_thread_t pico_thread_create(pico_thread_fn thread, void *arg, int stack_size, int prio); +void pico_thread_destroy(pico_thread_t t); +void pico_msleep(int ms); + +void pico_threads_schedule(void); + +#endif /* _PICO_OSAL_H_ */ + diff --git a/ext/pico_bsd/pico_osal_freertos.c b/ext/pico_bsd/pico_osal_freertos.c new file mode 100644 index 0000000..bdf41fa --- /dev/null +++ b/ext/pico_bsd/pico_osal_freertos.c @@ -0,0 +1,176 @@ +/* + * pico_osal.h + * + * Created on: December 2013 + * Author: Maxime Vincent + * Description: OS Abstraction Layer between PicoTCP and FreeRTOS + * + */ +/* FreeRTOS includes */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +#include "portmacro.h" + + +/* PicoTCP includes */ +#include "pico_defines.h" +#include "pico_config.h" +#include "pico_osal.h" + +#define osal_dbg(...) +//#define osal_dbg(...) printf(__VA_ARGS__) + +/***************************************************************************** + * Public functions + ****************************************************************************/ + +struct osal_mutex { + void * mutex; + uint8_t idx; /* only to keep track of the amount/idx, no real function .. */ +}; +static uint8_t mtx_number = 0; + + +/* ============= */ +/* == SIGNALS == */ +/* ============= */ + +void * pico_signal_init(void) +{ + struct osal_mutex *signal; + signal = pico_zalloc(sizeof(struct osal_mutex)); + osal_dbg("mi: %p for %p\n", signal, __builtin_return_address(0)); + if (!signal) + return NULL; + signal->mutex= xSemaphoreCreateBinary(); + signal->idx = mtx_number++; + return signal; +} + +void pico_signal_deinit(void * signal) +{ + struct osal_mutex * mtx = signal; + vSemaphoreDelete(mtx->mutex); + pico_free(signal); +} + +void pico_signal_wait(void * signal) +{ + pico_signal_wait_timeout(signal, (int)portMAX_DELAY); +} + +int pico_signal_wait_timeout(void * signal, int timeout) +{ + int retval = 0; + if(signal != NULL) + { + struct osal_mutex * mtx = signal; + if (timeout == portMAX_DELAY) { + while (xSemaphoreTake(mtx->mutex, portMAX_DELAY) == pdFALSE); + } else { + retval = xSemaphoreTake(mtx->mutex, timeout); + } + } + if (retval) { + return 0; /* Success */ + } else { + return -1; /* Timeout */ + } +} + +void pico_signal_send(void * signal) +{ + if(signal != NULL) + { + struct osal_mutex * mtx = signal; + xSemaphoreGive(mtx->mutex); + } +} + +void pico_signal_send_ISR(void * signal) +{ + if(signal != NULL) + { + struct osal_mutex * mtx = signal; + long task_switch_needed = 0; + xSemaphoreGiveFromISR(mtx->mutex, &task_switch_needed); + portYIELD_FROM_ISR(task_switch_needed); + } +} + +/* ============= */ +/* == MUTEXES == */ +/* ============= */ + + +void *pico_mutex_init(void) +{ + struct osal_mutex *mutex; + mutex = pico_zalloc(sizeof(struct osal_mutex)); + osal_dbg("mi: %p for %p\n", mutex, __builtin_return_address(0)); + if (!mutex) + return NULL; + mutex->mutex = xSemaphoreCreateMutex(); + mutex->idx = mtx_number++; + return mutex; +} + +void pico_mutex_deinit(void * mutex) +{ + pico_signal_deinit(mutex); +} + +int pico_mutex_lock_timeout(void * mutex, int timeout) +{ + return pico_signal_wait_timeout(mutex, timeout); +} + +void pico_mutex_lock(void * mutex) +{ + pico_signal_wait_timeout(mutex, (int)portMAX_DELAY); +} + +void pico_mutex_unlock(void * mutex) +{ + pico_signal_send(mutex); +} + +void pico_mutex_unlock_ISR(void * mutex) +{ + pico_signal_send_ISR(mutex); +} + + +/* ============= */ +/* == THREADS == */ +/* ============= */ +static char thread_name[4] = "T"; +static int thread_n = 0; + +pico_thread_t pico_thread_create(pico_thread_fn thread, void *arg, int stack_size, int prio) +{ + pico_thread_t t = PICO_ZALLOC(sizeof(TaskHandle_t)); + if (!t) + return NULL; + thread_name[2] = (thread_n++) % 10; + thread_name[3] = 0; + xTaskCreate((TaskFunction_t)thread, thread_name, stack_size, arg, prio, t); + return t; +} + +void pico_thread_destroy(pico_thread_t t) +{ + vTaskDelete((TaskHandle_t)t); + PICO_FREE(t); +} + +void pico_msleep(int ms) +{ + vTaskDelay(ms); +} + +void pico_threads_schedule(void) +{ + vTaskStartScheduler(); +} diff --git a/ext/pico_bsd/pico_osal_noos.c b/ext/pico_bsd/pico_osal_noos.c new file mode 100644 index 0000000..2681c9a --- /dev/null +++ b/ext/pico_bsd/pico_osal_noos.c @@ -0,0 +1,166 @@ +/* + * pico_osal.h + * + * Created on: December 2014 + * Author: Maxime Vincent + * Description: OS Abstraction Layer between PicoTCP and No Operating System + * + */ + +/* PicoTCP includes */ +#include "pico_defines.h" +#include "pico_config.h" +#include "pico_stack.h" +#include "pico_osal.h" + +#define osal_dbg(...) +//#define osal_dbg(...) printf(__VA_ARGS__) + +/***************************************************************************** + * Public functions + ****************************************************************************/ + +/* ============= */ +/* == MUTEXES == */ +/* ============= */ + +struct osal_mutex { + volatile int want_to_take; /* for ISR safety, basically a mutex for the mutex */ + volatile int mutex; + int idx; /* only to keep track of the amount/idx, no real function .. */ +}; + +static uint8_t mtx_number = 0; + +void * pico_mutex_init(void) +{ + struct osal_mutex * mutex; + mutex = pico_zalloc(sizeof(struct osal_mutex)); + osal_dbg("mi: %p for %p\n", mutex, __builtin_return_address(0)); + if (!mutex) + return NULL; + mutex->mutex = 1; + mutex->idx = mtx_number++; + return mutex; +} + +void pico_mutex_deinit(void * mutex) +{ + struct osal_mutex * mtx = mutex; + pico_free(mutex); +} + +int pico_mutex_lock_timeout(void * mutex, int timeout) +{ + int retval = 0; + if(mutex != NULL) + { + struct osal_mutex * mtx = mutex; + pico_time timestamp = PICO_TIME_MS(); + while (mtx->mutex == 0) + { + pico_stack_tick(); + #ifdef _POSIX_VERSION + usleep(500); + #endif + + /* break on timeout unless infinite timeout */ + if ((timeout != -1) && (PICO_TIME_MS() > (timestamp + timeout))) + break; + } + if (mtx->mutex == 1) + { + mtx->mutex = 0; /* take the mutex */ + } + else + { + retval = -1; /* timeout */ + } + } + return retval; +} + +void pico_mutex_lock(void * mutex) +{ + pico_mutex_lock_timeout(mutex, -1); +} + +void pico_mutex_unlock(void * mutex) +{ + if(mutex != NULL) + { + struct osal_mutex * mtx = mutex; + mtx->mutex = 1; + } +} + +void pico_mutex_unlock_ISR(void * mutex) +{ + if(mutex != NULL) + { + struct osal_mutex * mtx = mutex; + // tricky stuff needed or not? + mtx->mutex = 1; + } +} + +/* ============= */ +/* == SIGNALS == */ +/* ============= */ + +void * pico_signal_init(void) +{ + void * signal = pico_mutex_init(); + pico_mutex_lock(signal); + return signal; +} + +void pico_signal_deinit(void * signal) +{ + pico_mutex_deinit(signal); +} + +void pico_signal_wait(void * signal) +{ + pico_signal_wait_timeout(signal, -1); +} + +int pico_signal_wait_timeout(void * signal, int timeout) +{ + return pico_mutex_lock_timeout(signal, timeout); +} + +void pico_signal_send(void * signal) +{ + pico_mutex_unlock(signal); +} + +void pico_signal_send_ISR(void * signal) +{ + pico_mutex_unlock_ISR(signal); +} + + +/* ============= */ +/* == THREADS == */ +/* ============= */ + +pico_thread_t pico_thread_create(pico_thread_fn thread, void *arg, int stack_size, int prio) +{ + (void)thread; + (void)arg; + (void)stack_size; + (void)prio; + return NULL; +} + +void pico_thread_destroy(pico_thread_t t) +{ + return; +} + +void pico_msleep(int ms) +{ + pico_time now = PICO_TIME_MS(); + while ((pico_time)(now + ms) < PICO_TIME_MS()); +} diff --git a/ext/pico_bsd/pico_osal_pthread.c b/ext/pico_bsd/pico_osal_pthread.c new file mode 100644 index 0000000..e02c882 --- /dev/null +++ b/ext/pico_bsd/pico_osal_pthread.c @@ -0,0 +1,119 @@ +/* Pthread osal implementation, for testing purposes */ +#include +#include "pico_defines.h" +#include "pico_stack.h" +#include "pico_osal.h" +#include +#include + +#define BILLION 1000000000 + +void * pico_mutex_init(void) { + pthread_mutex_t *mutex = pico_zalloc(sizeof(pthread_mutex_t)); + if (!mutex) + return NULL; + if (pthread_mutex_init(mutex, NULL) == 0 ) + return mutex; + pico_free(mutex); + return NULL; +} +void pico_mutex_deinit(void * mutex) +{ + pthread_mutex_destroy((pthread_mutex_t *)mutex); + pico_free(mutex); +} + +void pico_mutex_lock(void * mutex) +{ + pthread_mutex_lock((pthread_mutex_t *)mutex); +} + +int pico_mutex_lock_timeout(void *mutex, int timeout) +{ + if (timeout < 0) { + return pthread_mutex_lock((pthread_mutex_t *)mutex); + + } else { + struct timespec ts = { timeout / 1000, (timeout % 1000) * 1000000 }; + return pthread_mutex_timedlock((pthread_mutex_t *)mutex, &ts); + } + +} + +void pico_mutex_unlock(void * mutex) +{ + pthread_mutex_unlock((pthread_mutex_t *)mutex); +} + + +void * pico_signal_init(void) +{ + sem_t *sem = pico_zalloc(sizeof(pthread_mutex_t)); + if (!sem) + return NULL; + if (sem_init(sem, 0, 0) == 0) + return sem; + pico_free(sem); + return NULL; +} + +void pico_signal_deinit(void * signal) +{ + sem_destroy((sem_t *) signal); +} + +void pico_signal_wait(void * signal) +{ + sem_wait((sem_t *) signal); +} + +int pico_signal_wait_timeout(void * signal, int timeout) +{ + if (timeout < 0) { + return sem_wait((sem_t *) signal); + } else { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += (timeout / 1000); + ts.tv_nsec += ((timeout % 1000) * 1000000); + if (ts.tv_nsec >= BILLION) { + ts.tv_nsec -= BILLION; + ts.tv_sec++; + } + return sem_timedwait((sem_t *) signal, &ts); + } +} + +void pico_signal_send(void * signal) +{ + sem_post((sem_t *) signal); +} + +pico_thread_t pico_thread_create(pico_thread_fn thread, void *arg, int stack_size, int prio) +{ + pico_thread_t t = PICO_ZALLOC(sizeof(pthread_t)); + if (!t) + return NULL; + (void)stack_size; + (void)prio; + pthread_create((pthread_t *)t, NULL, thread, arg); + pthread_detach(*((pthread_t *)t)); +} + +void pico_thread_destroy(pico_thread_t t) +{ + pthread_cancel(*((pthread_t *)t)); + PICO_FREE(t); +} + +void pico_msleep(int ms) +{ + struct timespec ts = { ms / 1000, (ms % 1000) * 1000000 }; + nanosleep(&ts, NULL); +} + +void pico_threads_schedule(void) +{ + while (1 < 2) + pico_msleep(1000); +} diff --git a/ext/pico_bsd/pico_posix_wrapper.c b/ext/pico_bsd/pico_posix_wrapper.c new file mode 100644 index 0000000..a376753 --- /dev/null +++ b/ext/pico_bsd/pico_posix_wrapper.c @@ -0,0 +1,429 @@ +#include "pico_bsd_sockets.h" +#define _GNU_SOURCE +#define __GNU_SOURCE +#define __USE_GNU +#include +#include +#include "pico_ipv4.h" +#include "pico_ipv6.h" +#include "pico_stack.h" +#include "pico_socket.h" +#include "pico_dev_vde.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static __thread int in_the_stack = 0; +static int initialized = 0; +#define ptsock_dbg printf + + + + +#define swap_socketcall(call, name) \ +{ \ + const char *msg; \ + if (host_##call == NULL) { \ + *(void **)(&host_##call) = dlsym(RTLD_NEXT, name); \ + if ((msg = dlerror()) != NULL) \ + fprintf (stderr, "%s: dlsym(%s): %s\n", "picotcp", name, msg); \ + } \ +} + + +#define conditional_steal_call(call, i, ...) \ + if(in_the_stack) { \ + return host_##call(i, ## __VA_ARGS__); \ + } else { \ + if (get_pico_fd(i) > -1) { \ + int __pico_retval = pico_##call(get_pico_fd(i), ## __VA_ARGS__); \ + if (__pico_retval != 0) \ + errno = pico_err; \ + return __pico_retval; \ + }else { \ + return host_##call(i, ## __VA_ARGS__); \ + } \ + } + +static int max_fd = 0; +static int *pico_fds = NULL; + +static int remap_fd(int pico_fd) +{ + int new_fd = open("/dev/zero", O_RDONLY); + int old_max = max_fd; + int i; + if (new_fd < 0) { + abort(); + } + if (max_fd < new_fd + 1) + max_fd = new_fd + 1; + if (pico_fds == NULL) { + pico_fds = malloc(sizeof(int) * max_fd); + for (i = 0; i < max_fd; i++) + pico_fds[i] = -1; + pico_fds[new_fd] = pico_fd; + return new_fd; + } + pico_fds = realloc(pico_fds, sizeof(int) * max_fd); + for (i = old_max; i < max_fd; i++) + pico_fds[i] = -1; + pico_fds[new_fd] = pico_fd; + return new_fd; +} + +static int get_pico_fd(int j) +{ + if (j >= max_fd) + return -1; + return pico_fds[j]; +} + + +static int (*host_socket ) (int domain, int type, int protocol) = NULL; +static int (*host_bind ) (int sockfd, const struct sockaddr *addr, socklen_t addrlen); +static int (*host_connect ) (int sockfd, const struct sockaddr *addr, socklen_t addrlen); +static int (*host_accept ) (int sockfd, struct sockaddr *addr, socklen_t *addrlen); +static int (*host_listen ) (int sockfd, int backlog); +static ssize_t (*host_recvfrom) (int sockfd, void *buf, size_t len, int flags, struct sockaddr *addr, int *addrlen); +static ssize_t (*host_recv ) (int sockfd, void *buf, size_t len, int flags); +static ssize_t (*host_read ) (int sockfd, void *buf, size_t len); +static ssize_t (*host_sendto ) (int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen); +static ssize_t (*host_send ) (int sockfd, void *buf, size_t len, int flags); +static ssize_t (*host_write ) (int sockfd, const void *buf, size_t len); +static int (*host_close ) (int sockfd); +static int (*host_shutdown) (int sockfd, int how); +static int (*host_setsockopt) (int sockfd, int level, int optname, const void *optval, socklen_t optlen); +static int (*host_getsockopt) (int sockfd, int level, int optname, void *optval, socklen_t *optlen); + + int getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res); + + void freeaddrinfo(struct addrinfo *res); + + +static int (*host_getaddrinfo) (const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); +static int (*host_freeaddrinfo) (struct addrinfo *res); +static int (*host_poll) (struct pollfd *pfd, nfds_t npfd, int timeout); +static int (*host_ppoll) (struct pollfd *pfd, nfds_t npfd, const struct timespec *timeout_ts, const sigset_t *sigmask); +static int (*host_select) (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); +static int (*host_pselect) (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, + const sigset_t *sigmask); + +int socket(int domain, int type, int protocol) +{ + int new_sd, posix_fd = -1; + ptsock_dbg ("Called Socket (pid=%d) in_the_stack=%d\n", getpid(), in_the_stack); + if (in_the_stack) + return host_socket(domain, type, protocol); + if ((domain != AF_INET) && (domain != AF_INET6)) { + return host_socket(domain, type, protocol); + } + new_sd = pico_newsocket(domain, type, protocol); + if (new_sd < 0) { + ptsock_dbg("socket() call failed.\n"); + abort(); + } + posix_fd = remap_fd(new_sd); + ptsock_dbg ("Socket stolen, sd=%d, fd = %d\n", new_sd, posix_fd); + return posix_fd; +} + +int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + conditional_steal_call(bind, sockfd, addr, addrlen); +} + +int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + conditional_steal_call(connect, sockfd, addr, addrlen); +} + +int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) +{ + if(in_the_stack) { + return host_accept(sockfd, addr, addrlen); + } else { + int posix_fd, new_sd, listen_sd = get_pico_fd(sockfd); + if (listen_sd < 0) { + return host_accept(sockfd, addr, addrlen); + } + + new_sd = pico_accept(listen_sd, addr, addrlen); + if (new_sd < 0) + return -1; + posix_fd = remap_fd(new_sd); + ptsock_dbg ("Socket accepted, sd=%d, fd = %d\n", new_sd, posix_fd); + return posix_fd; + } +} + +int listen(int sockfd, int backlog) +{ + conditional_steal_call(listen, sockfd, backlog); +} + +ssize_t recv(int sockfd, void *buf, size_t len, int flags) +{ + conditional_steal_call(recvfrom, sockfd, buf, len, flags, 0, 0); +} + +ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen) +{ + conditional_steal_call(recvfrom, sockfd, buf, len, flags, addr, addrlen); +} + +ssize_t read(int sockfd, void *buf, size_t len) +{ + conditional_steal_call(read, sockfd, buf, len); +} + +ssize_t send(int sockfd, const void *buf, size_t len, int flags) +{ + conditional_steal_call(sendto, sockfd, buf, len, flags, 0, 0); +} + +ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen) +{ + conditional_steal_call(sendto, sockfd, buf, len, flags, addr, addrlen); +} + +ssize_t write(int sockfd, const void *buf, size_t len) +{ + conditional_steal_call(write, sockfd, buf, len); +} + +int close(int sockfd) +{ + int pico_sd; + if (in_the_stack) + return host_close(sockfd); + pico_sd = get_pico_fd(sockfd); + if (pico_sd < 0) + return host_close(sockfd); + pico_close(pico_sd); + pico_fds[sockfd] = -1; + return 0; +} + +int shutdown(int sockfd, int how) +{ + int pico_sd; + if (in_the_stack) + return host_shutdown(sockfd, how); + pico_sd = get_pico_fd(sockfd); + if (pico_sd < 0) + return host_shutdown(sockfd, how); + + if (how != SHUT_WR) + pico_fds[sockfd] = -1; + pico_shutdown(pico_sd, how); + return 0; +} + +int setsockopt (int sockfd, int level, int optname, const void *optval, socklen_t optlen) +{ + conditional_steal_call(setsockopt, sockfd, level, optname, optval, optlen); +} + +int getsockopt (int sockfd, int level, int optname, void *optval, socklen_t *optlen) +{ + conditional_steal_call(getsockopt, sockfd, level, optname, optval, optlen); +} + +int poll(struct pollfd *pfd, nfds_t npfd, int timeout) +{ + if(in_the_stack) { + return host_poll(pfd, npfd, timeout); + } else { + int i, j = 0; + struct pollfd pico_pfd[npfd]; + for (i = 0; i < npfd; i++) { + pico_pfd[j].fd = get_pico_fd(pfd[i].fd); + if (pico_pfd[j].fd >= 0) { + j++; + pico_pfd[j].events = pfd[i].events; + } + } + if (j > 0) { + int pico_retval = pico_poll(pico_pfd, j, timeout); + if (pico_retval < 0) + errno = pico_err; + return pico_retval; + } else { + errno = EINVAL; + return -1; + } + } +} + + +int ppoll(struct pollfd *pfd, nfds_t npfd, const struct timespec *timeout_ts, const sigset_t *sigmask) +{ + if(in_the_stack) { + return host_ppoll(pfd, npfd, timeout_ts, sigmask); + } else { + int i, j = 0; + struct pollfd pico_pfd[npfd]; + for (i = 0; i < npfd; i++) { + pico_pfd[j].fd = get_pico_fd(pfd[i].fd); + if (pico_pfd[j].fd >= 0) { + j++; + pico_pfd[j].events = pfd[i].events; + } + } + if (j > 0) { + int pico_retval = pico_ppoll(pico_pfd, j, timeout_ts, NULL); + if (pico_retval < 0) + errno = pico_err; + return pico_retval; + } else { + errno = EINVAL; + return -1; + } + } +} + +int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask) +{ + pico_fd_set rs, ws, es; + int pico_retval = -1; + + PICO_FD_ZERO(&rs); + PICO_FD_ZERO(&ws); + PICO_FD_ZERO(&es); + + if(in_the_stack) { + return host_pselect(nfds, readfds, writefds, exceptfds, timeout, sigmask); + } else { + int i, max = -1; + for (i = 0; i < nfds; i++) { + int picofd = get_pico_fd(i); + if (picofd >= 0) { + if (FD_ISSET(i, readfds)) + PICO_FD_SET(picofd, &rs); + if (FD_ISSET(i, writefds)) + PICO_FD_SET(picofd, &ws); + if (FD_ISSET(i, exceptfds)) + PICO_FD_SET(picofd, &es); + } + if (picofd > max) + max = picofd; + } + if (max < 0) { + errno = EINVAL; + return -1; + } + max++; + pico_retval = pico_pselect(max, &rs, &ws, &es, timeout, NULL); + if (pico_retval < 0) + errno = pico_err; + else { + for(i = 0; i < nfds; i++) { + int picofd = get_pico_fd(i); + FD_CLR(i, readfds); + FD_CLR(i, writefds); + FD_CLR(i, exceptfds); + if (picofd >= 0) { + if (FD_ISSET(picofd, &rs)) + FD_SET(i, readfds); + if (FD_ISSET(picofd, &ws)) + FD_SET(i, writefds); + if (FD_ISSET(picofd, &es)) + FD_SET(i, exceptfds); + } + } + } + return pico_retval; + } +} + +int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) +{ + if(in_the_stack) { + return host_select(nfds, readfds, writefds, exceptfds, timeout); + } else { + if (timeout) { + const struct timespec ts = {timeout->tv_sec, timeout->tv_usec * 1000}; + return pselect(nfds, readfds, writefds, exceptfds, &ts, NULL); + } else { + return pselect(nfds, readfds, writefds, exceptfds, NULL, NULL); + } + } +} + +void *pico_tick_thread(void *arg) { + struct pico_ip4 addr, netmask, gateway, zero = {}; + struct pico_device *vde; + struct pico_ip4 v4_ip_host, v4_ip_route; + struct pico_ip4 v4_mask; + struct pico_ip4 v4_zero={}, v4_gateway; + struct pico_ip6 v6_public; + struct pico_ip6 v6_netmask = {0xff,0xff,}; + struct pico_device *tun; + in_the_stack = 1; + + pico_bsd_init(); + pico_stack_init(); + + tun = (struct pico_device *) pico_tun_create("psx0"); + if (!tun) + abort(); + + pico_string_to_ipv4("192.168.2.150",&v4_ip_host.addr); + pico_string_to_ipv4("192.168.2.1",&v4_ip_route.addr); + pico_string_to_ipv4("255.255.0.0",&v4_mask.addr); + pico_string_to_ipv4("192.168.2.1",&v4_gateway.addr); + pico_string_to_ipv6("7a55::150",v6_public.addr); + + pico_ipv4_link_add(tun, v4_ip_host, v4_mask); + pico_ipv4_route_add(v4_ip_route, v4_mask, v4_gateway, 1, NULL); + pico_ipv6_link_add(tun, v6_public, v6_netmask); + + for (;;) { + pico_bsd_stack_tick(); + usleep(1000); + } +} + + +int __attribute__((constructor)) pico_wrapper_start(void) +{ + pthread_t ticker; + if (initialized++) + return 0; + printf("Stealing all your system calls, please wait...\n"); + swap_socketcall(socket , "socket"); + swap_socketcall(bind , "bind"); + swap_socketcall(connect , "connect"); + swap_socketcall(accept , "accept"); + swap_socketcall(listen , "listen"); + swap_socketcall(recvfrom, "recvfrom"); + swap_socketcall(recv , "recv"); + swap_socketcall(read , "read"); + swap_socketcall(sendto , "sendto"); + swap_socketcall(send , "send"); + swap_socketcall(write , "write"); + swap_socketcall(close , "close"); + swap_socketcall(shutdown, "shutdown"); + swap_socketcall(setsockopt, "setsockopt"); + swap_socketcall(getaddrinfo, "getaddrinfo"); + swap_socketcall(freeaddrinfo, "freeaddrinfo"); + swap_socketcall(poll, "poll"); + swap_socketcall(ppoll, "ppoll"); + swap_socketcall(select, "select"); + swap_socketcall(pselect, "pselect"); + pthread_create(&ticker, NULL, pico_tick_thread, NULL); + sleep(1); + return 0; +} + diff --git a/ext/picotcp/modules/pico_dns_client.c b/ext/picotcp/modules/pico_dns_client.c index 91d9cc3..cf283cc 100644 --- a/ext/picotcp/modules/pico_dns_client.c +++ b/ext/picotcp/modules/pico_dns_client.c @@ -779,18 +779,18 @@ static int pico_dns_getname_univ(const char *ip, void (*callback)(char *, void * return 0; } -int pico_dns_client_getname(const char *ip, void (*callback)(char *, void *), void *arg) +extern int pico_dns_client_getname(const char *ip, void (*callback)(char *, void *), void *arg) { return pico_dns_getname_univ(ip, callback, arg, PICO_DNS_ARPA4); } -int pico_dns_client_getname6(const char *ip, void (*callback)(char *, void *), void *arg) +extern int pico_dns_client_getname6(const char *ip, void (*callback)(char *, void *), void *arg) { return pico_dns_getname_univ(ip, callback, arg, PICO_DNS_ARPA6); } -int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag) +extern int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag) { if (!ns) { pico_err = PICO_ERR_EINVAL; @@ -820,7 +820,7 @@ int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag) return 0; } -int pico_dns_client_init(void) +extern int pico_dns_client_init(void) { struct pico_ip4 default_ns = { 0 @@ -834,7 +834,7 @@ int pico_dns_client_init(void) #else -int pico_dns_client_init(void) +extern int pico_dns_client_init(void) { dbg("ERROR Trying to initialize DNS module: IPv4 not supported in this build.\n"); return -1; diff --git a/ext/picotcp/modules/pico_dns_client.h b/ext/picotcp/modules/pico_dns_client.h index da3f313..437d558 100644 --- a/ext/picotcp/modules/pico_dns_client.h +++ b/ext/picotcp/modules/pico_dns_client.h @@ -37,6 +37,10 @@ #define PICO_DNS_RR_A_RDLENGTH 4 #define PICO_DNS_RR_AAAA_RDLENGTH 16 +#ifdef __cplusplus +extern "C" { +#endif + int pico_dns_client_init(void); /* flag is PICO_DNS_NS_DEL or PICO_DNS_NS_ADD */ int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag); @@ -47,4 +51,8 @@ int pico_dns_client_getaddr6(const char *url, void (*callback)(char *, void *), int pico_dns_client_getname6(const char *url, void (*callback)(char *, void *), void *arg); #endif +#ifdef __cplusplus +} +#endif + #endif /* _INCLUDE_PICO_DNS_CLIENT */ diff --git a/ext/picotcp/modules/pico_ipv4.c b/ext/picotcp/modules/pico_ipv4.c index de870ff..74739a1 100644 --- a/ext/picotcp/modules/pico_ipv4.c +++ b/ext/picotcp/modules/pico_ipv4.c @@ -25,7 +25,7 @@ #include "pico_ethernet.h" #include "pico_mcast.h" -#include "../../../include/Debug.hpp" +#include "../../../include/libztDebug.h" #ifdef PICO_SUPPORT_IPV4 diff --git a/ext/picotcp/modules/pico_ipv6.c b/ext/picotcp/modules/pico_ipv6.c index 93ec7b6..2970b8b 100644 --- a/ext/picotcp/modules/pico_ipv6.c +++ b/ext/picotcp/modules/pico_ipv6.c @@ -23,7 +23,7 @@ #include "pico_mcast.h" #ifdef PICO_SUPPORT_IPV6 -#include "../../../include/Debug.hpp" +#include "../../../include/libztDebug.h" #define PICO_IPV6_EXTHDR_OPT_PAD1 0 #define PICO_IPV6_EXTHDR_OPT_PADN 1 diff --git a/ext/picotcp/stack/pico_protocol.c b/ext/picotcp/stack/pico_protocol.c index a4b16cf..c301c0a 100644 --- a/ext/picotcp/stack/pico_protocol.c +++ b/ext/picotcp/stack/pico_protocol.c @@ -10,7 +10,7 @@ #include "pico_protocol.h" #include "pico_tree.h" -#include "../../../include/Debug.hpp" +#include "../../../include/libztDebug.h" struct pico_proto_rr { diff --git a/ext/picotcp/stack/pico_socket.c b/ext/picotcp/stack/pico_socket.c index 5ed4d6a..145442f 100644 --- a/ext/picotcp/stack/pico_socket.c +++ b/ext/picotcp/stack/pico_socket.c @@ -23,7 +23,7 @@ #include "pico_socket_tcp.h" #include "pico_socket_udp.h" -#include "../../../include/Debug.hpp" +#include "../../../include/libztDebug.h" #if defined (PICO_SUPPORT_IPV4) || defined (PICO_SUPPORT_IPV6) #if defined (PICO_SUPPORT_TCP) || defined (PICO_SUPPORT_UDP) diff --git a/ext/picotcp/stack/pico_stack.c b/ext/picotcp/stack/pico_stack.c index 9df3dd2..3e21115 100644 --- a/ext/picotcp/stack/pico_stack.c +++ b/ext/picotcp/stack/pico_stack.c @@ -33,7 +33,7 @@ #include "pico_socket.h" #include "heap.h" -#include "../../../include/Debug.hpp" +#include "../../../include/libztDebug.h" /* Mockables */ #if defined UNIT_TEST diff --git a/include/README.md b/include/README.md index 40d8807..4c902f7 100644 --- a/include/README.md +++ b/include/README.md @@ -1 +1 @@ -For applications, see [libzt.h](libzt.h) for POSIX-like socket API. +See [libzt.h](libzt.h) for POSIX-like socket API exposed for applications. diff --git a/src/VirtualBindingPair.cpp b/include/RingBuffer.h similarity index 50% rename from src/VirtualBindingPair.cpp rename to include/RingBuffer.h index 7917e2f..7e5b74e 100644 --- a/src/VirtualBindingPair.cpp +++ b/include/RingBuffer.h @@ -27,7 +27,68 @@ /** * @file * - * + * Ring buffer implementation for network stack drivers */ -// Only available in experimental branch +#ifndef ZT_RINGBUFFER_H +#define ZT_RINGBUFFER_H + +typedef char bufElementType; + +class RingBuffer +{ +private: + bufElementType * buf; + size_t size; + size_t begin; + size_t end; + bool wrap; + +public: + /** + * create a RingBuffer with space for up to size elements. + */ + explicit RingBuffer(size_t size) + : size(size), + begin(0), + end(0), + wrap(false) + { + buf = new bufElementType[size]; + } +/* + RingBuffer(const RingBuffer & ring) + { + this(ring.size); + begin = ring.begin; + end = ring.end; + memcpy(buf, ring.buf, sizeof(T) * size); + } +*/ + ~RingBuffer() + { + delete[] buf; + } + + // get a reference to the underlying buffer + bufElementType* get_buf(); + + // adjust buffer index pointer as if we copied data in + size_t produce(size_t n); + + // merely reset the buffer pointer, doesn't erase contents + void reset(); + + // adjust buffer index pointer as if we copied data out + size_t consume(size_t n); + + size_t write(const bufElementType * data, size_t n); + + size_t read(bufElementType * dest, size_t n); + + size_t count(); + + size_t getFree(); +}; + +#endif // _H diff --git a/include/Platform.h b/include/SysUtils.h similarity index 63% rename from include/Platform.h rename to include/SysUtils.h index 9d24e0c..50c62ab 100644 --- a/include/Platform.h +++ b/include/SysUtils.h @@ -30,20 +30,11 @@ * Platform-specific implementations of common functions */ -#ifndef LIBZT_PLATFORM_H -#define LIBZT_PLATFORM_H +#ifndef LIBZT_SYSUTILS_H +#define LIBZT_SYSUTILS_H -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Used to perform a common action upon a failure in the VirtualSocket/VirtualTap layer. - * - * @usage For internal use only. - * @return - */ -void handle_general_failure(); +#include +#include /** * @brief Returns the thread-id. Used in debug traces. @@ -53,8 +44,32 @@ void handle_general_failure(); */ inline unsigned int gettid(); -#ifdef __cplusplus -} +/** + * @brief Current time in milliseconds since epoch, platform-aware convenience function. + * + * @usage For internal use only. + * @return Current time in integer form + */ +inline uint64_t time_now() +{ +#ifdef __WINDOWS__ + FILETIME ft; + SYSTEMTIME st; + ULARGE_INTEGER tmp; + GetSystemTime(&st); + SystemTimeToFileTime(&st,&ft); + tmp.LowPart = ft.dwLowDateTime; + tmp.HighPart = ft.dwHighDateTime; + return (uint64_t)( ((tmp.QuadPart - 116444736000000000LL) / 10000L) + st.wMilliseconds ); +#else + struct timeval tv; +#ifdef __LINUX__ + syscall(SYS_gettimeofday,&tv,0); /* fix for musl libc broken gettimeofday bug */ +#else + gettimeofday(&tv,(struct timezone *)0); #endif + return ( (1000LL * (uint64_t)tv.tv_sec) + (uint64_t)(tv.tv_usec / 1000) ); +#endif +} #endif // _H diff --git a/include/Utilities.h b/include/Utilities.h index ae1b6a1..868d4f6 100644 --- a/include/Utilities.h +++ b/include/Utilities.h @@ -33,10 +33,14 @@ #ifndef LIBZT_UTILITIES_H #define LIBZT_UTILITIES_H -#include "InetAddress.hpp" +#include + +namespace ZeroTier { + struct InetAddress; +} #if defined(__MINGW32__) - + #define NS_INADDRSZ 4 #define NS_IN6ADDRSZ 16 #define NS_INT16SZ 2 @@ -46,15 +50,6 @@ int inet_pton6(const char *src, void *dst); int inet_pton(int af, const char *src, void *dst); #endif -/** - * @brief Returns masked address for subnet comparisons - * - * @usage For internal use only. - * @param socket_type - * @return - */ -bool ipv6_in_subnet(ZeroTier::InetAddress *subnet, ZeroTier::InetAddress *addr); - /** * @brief Convert protocol numbers to human-readable strings * @@ -64,17 +59,6 @@ bool ipv6_in_subnet(ZeroTier::InetAddress *subnet, ZeroTier::InetAddress *addr); */ char *beautify_eth_proto_nums(int proto); -/** - * @brief Convert a struct sockaddr to a ZeroTier::InetAddress - * - * @usage For internal use only. - * @param socket_family - * @param addr - * @param inet - * @return - */ -//void sockaddr2inet(int socket_family, const struct sockaddr *addr, ZeroTier::InetAddress *inet); - /** * @brief Convert a raw MAC address byte array into a human-readable string * @@ -86,12 +70,4 @@ char *beautify_eth_proto_nums(int proto); */ void mac2str(char *macbuf, int len, unsigned char* addr); -#if defined(STACK_LWIP) && defined(LIBZT_IPV6) -#define IP6_ADDR2(ipaddr, a,b,c,d,e,f,g,h) do { (ipaddr)->addr[0] = ZeroTier::Utils::hton((u32_t)((a & 0xffff) << 16) | (b & 0xffff)); \ - (ipaddr)->addr[1] = ZeroTier::Utils::hton(((c & 0xffff) << 16) | (d & 0xffff)); \ - (ipaddr)->addr[2] = ZeroTier::Utils::hton(((e & 0xffff) << 16) | (f & 0xffff)); \ - (ipaddr)->addr[3] = ZeroTier::Utils::hton(((g & 0xffff) << 16) | (h & 0xffff)); } while(0) - -#endif - #endif // _H diff --git a/include/VirtualBindingPair.h b/include/VirtualBindingPair.h index 5064120..e136480 100644 --- a/include/VirtualBindingPair.h +++ b/include/VirtualBindingPair.h @@ -37,7 +37,15 @@ extern "C" { #endif -struct VirtualBindingPair; +/** + * A helper object for passing VirtualTap(s) and VirtualSocket(s) through the stack + */ +struct VirtualBindingPair +{ + VirtualTap *tap; + VirtualSocket *vs; + VirtualBindingPair(VirtualTap *_tap, VirtualSocket *_vs) : tap(_tap), vs(_vs) {} +}; #ifdef __cplusplus } diff --git a/include/VirtualSocket.h b/include/VirtualSocket.h index 804fc24..3bd967a 100644 --- a/include/VirtualSocket.h +++ b/include/VirtualSocket.h @@ -27,12 +27,60 @@ /** * @file * - * Platform-agnostic implementation of a socket-like object + * Platform- and stack-agnostic implementation of a socket-like object */ #ifndef LIBZT_VIRTUALSOCKET_H #define LIBZT_VIRTUALSOCKET_H +#include + +#include "RingBuffer.h" +#include "libztDefs.h" +#include "VirtualTap.h" +#include "Mutex.hpp" + +#define VS_STATE_INACTIVE 0x000000u // Default value for newly created VirtualSocket +#define VS_STATE_ACTIVE 0x000001u // VirtualSocket is RX'ing or TX'ing without issue +#define VS_STATE_SHOULD_SHUTDOWN 0x000002u // Application, stack driver, or stack marked this VirtualSocket for death +#define VS_STATE_SHUTDOWN 0x000004u // VirtualSocket and underlying protocol control structures will not RX/TX +#define VS_STATE_CLOSED 0x000008u // VirtualSocket and underlying protocol control structures are closed +#define VS_STATE_UNHANDLED_CONNECTED 0x000010u // stack callback has received a connection but we haven't dealt with it +#define VS_STATE_CONNECTED 0x000020u // stack driver has akwnowledged new connection +#define VS_STATE_LISTENING 0x000040u // virtual socket is listening for incoming connections + +#define VS_OPT_TCP_NODELAY 0x000000u // Nagle's algorithm +#define VS_OPT_SO_LINGER 0x000001u // VirtualSocket waits for data transmission before closure +/* +#define VS_RESERVED 0x000002u // +#define VS_RESERVED 0x000004u // +#define VS_RESERVED 0x000008u // +#define VS_RESERVED 0x000010u // +#define VS_RESERVED 0x000020u // +#define VS_RESERVED 0x000040u // +*/ +#define VS_OPT_FD_NONBLOCKING 0x000080u // Whether the VirtualSocket exhibits non-blocking behaviour +/* +#define VS_RESERVED 0x000100u // +#define VS_RESERVED 0x000200u // +#define VS_RESERVED 0x000400u // +#define VS_RESERVED 0x000800u // +#define VS_RESERVED 0x001000u // +#define VS_RESERVED 0x002000u // +#define VS_RESERVED 0x004000u // +#define VS_RESERVED 0x008000u // +#define VS_RESERVED 0x010000u // +#define VS_RESERVED 0x020000u // +#define VS_RESERVED 0x040000u // +#define VS_RESERVED 0x080000u // +#define VS_RESERVED 0x100000u // +#define VS_RESERVED 0x200000u // +#define VS_RESERVED 0x400000u // +#define VS_RESERVED 0x800000u // +*/ + +#define vs_is_nonblocking(vs) (((vs)->optflags & VS_OPT_FD_NONBLOCKING) != 0) + #ifdef __cplusplus extern "C" { #endif @@ -42,7 +90,67 @@ extern "C" { * and the network stack's representation of a protocol control structure. This object is used by * the POSIX socket emulation layer and stack drivers. */ -class VirtualSocket; +class VirtualSocket +{ + private: + int _state = VS_STATE_INACTIVE; + public: + RingBuffer *TXbuf, *RXbuf; + ZeroTier::Mutex _tx_m, _rx_m, _op_m; + ZeroTier::PhySocket *sock = NULL; + + void *pcb = NULL; // Protocol Control Block + +#if defined(STACK_LWIP) + int32_t optflags = 0; + int linger; + /* + - TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated + for the data to be copied into. If this flag is not given, no new memory + should be allocated and the data should only be referenced by pointer. This + also means that the memory behind dataptr must not change until the data is + ACKed by the remote host + - TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is omitted, + the PSH flag is set in the last segment created by this call to tcp_write. + If this flag is given, the PSH flag is not set. + */ + // copy as default, processed via pointer reference if set to 0. See notes in lwip_cb_sent() and lwip_Write() + int8_t copymode = 1; // TCP_WRITE_FLAG_COPY; +#endif + struct sockaddr_storage local_addr; // address we've bound to locally + struct sockaddr_storage peer_addr; // address of connection call to remote host + int socket_family = 0; + int socket_type = 0; + int protocol = 0; + int app_fd = 0; // used by app for I/O + int sdk_fd = 0; // used by lib for I/O + std::queue _AcceptedConnections; + VirtualTap *tap = NULL; + + /** + * Sets the VirtualSocket's state value + */ + void apply_state(int state); + + /** + * Sets the VirtualSocket's state value + */ + void set_state(int state); + /** + * Gets the VirtualSocket's state value + */ + int get_state(); + + /** + * default ctor + */ + VirtualSocket(); + + /** + * dtor + */ + ~VirtualSocket(); +}; #ifdef __cplusplus } diff --git a/include/VirtualSocketLayer.h b/include/VirtualSocketLayer.h new file mode 100644 index 0000000..e74aff4 --- /dev/null +++ b/include/VirtualSocketLayer.h @@ -0,0 +1,443 @@ +/* + * ZeroTier SDK - Network Virtualization Everywhere + * Copyright (C) 2011-2017 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 . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +/** + * @file + * + * VirtualSocket management layer + */ + +//#include "InetAddress.hpp" + +namespace ZeroTier { + class Mutex; + struct InetAddress; +} + +class VirtualSocket; +class VirtualTap; + +extern ZeroTier::Mutex _multiplexer_lock; + +VirtualSocket *get_virt_socket(int fd); +int del_virt_socket(int fd); +int add_unassigned_virt_socket(int fd, VirtualSocket *vs); +int del_unassigned_virt_socket(int fd); +int add_assigned_virt_socket(void *tap, VirtualSocket *vs, int fd); +int del_assigned_virt_socket(void *tap, VirtualSocket *vs, int fd); +//void *get_assigned_virtual_pair(int fd); + +/** + * @brief Stops all VirtualTap interfaces and associated I/O loops + * + * @usage For internal use only. + * @param + * @return + */ +void disableTaps(); + + +/** + * @brief Create a socket + * + * This function will return an integer which can be used in much the same way as a + * typical file descriptor, however it is only valid for use with libzt library calls + * as this is merely a facade which is associated with the internal socket representation + * of both the network stacks and drivers. + * + * @usage Call this after virt_start() has succeeded + * @param socket_family Address family (AF_INET, AF_INET6) + * @param socket_type Type of socket (SOCK_STREAM, SOCK_DGRAM, SOCK_RAW) + * @param protocol Protocols supported on this socket + * @return + */ +int virt_socket(int socket_family, int socket_type, int protocol); + +/** + * @brief Connect a socket to a remote host + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param addr Remote host address to connect to + * @param addrlen Length of address + * @return + */ +int virt_connect(int fd, const struct sockaddr *addr, socklen_t addrlen); + +/** + * @brief Bind a socket to a virtual interface + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param addr Local interface address to bind to + * @param addrlen Length of address + * @return + */ +int virt_bind(int fd, const struct sockaddr *addr, socklen_t addrlen); + +/** + * @brief Listen for incoming connections + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param backlog Number of backlogged connection allowed + * @return + */ +int virt_listen(int fd, int backlog); + +/** + * @brief Accept an incoming connection + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param addr Address of remote host for accepted connection + * @param addrlen Length of address + * @return + */ +int virt_accept(int fd, struct sockaddr *addr, socklen_t *addrlen); + +/** + * @brief Accept an incoming connection + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param addr Address of remote host for accepted connection + * @param addrlen Length of address + * @param flags + * @return + */ +#if defined(__linux__) + int virt_accept4(int fd, struct sockaddr *addr, socklen_t *addrlen, int flags); +#endif + +/** + * @brief Set socket options + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param level Protocol level to which option name should apply + * @param optname Option name to set + * @param optval Source of option value to set + * @param optlen Length of option value + * @return + */ +int virt_setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen); + +/** + * @brief Get socket options + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param level Protocol level to which option name should apply + * @param optname Option name to get + * @param optval Where option value will be stored + * @param optlen Length of value + * @return + */ +int virt_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen); + +/** + * @brief Get socket name + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param addr Name associated with this socket + * @param addrlen Length of name + * @return + */ +int virt_getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen); + +/** + * @brief Get the peer name for the remote end of a connected socket + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param addr Name associated with remote end of this socket + * @param addrlen Length of name + * @return + */ +int virt_getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen); + +/** + * @brief Gets current hostname + * + * @usage Call this after virt_start() has succeeded + * @param name + * @param len + * @return + */ +int virt_gethostname(char *name, size_t len); + +/** + * @brief Sets current hostname + * + * @usage Call this after virt_start() has succeeded + * @param name + * @param len + * @return + */ +int virt_sethostname(const char *name, size_t len); + +/** + * @brief Return a pointer to an object with the following structure describing an internet host referenced by name + * + * @usage Call this after virt_start() has succeeded + * @param name + * @return Returns pointer to hostent structure otherwise NULL if failure + */ +struct hostent *virt_gethostbyname(const char *name); + +/** + * @brief Close a socket + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @return + */ +int virt_close(int fd); + +/** + * @brief Waits for one of a set of file descriptors to become ready to perform I/O. + * + * @usage Call this after virt_start() has succeeded + * @param fds + * @param nfds + * @param timeout + * @return + */ +/* +#ifdef __linux__ +int virt_poll(struct pollfd *fds, nfds_t nfds, int timeout); +#endif +*/ +/** + * @brief Monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" + * + * @usage Call this after virt_start() has succeeded + * @param nfds + * @param readfds + * @param writefds + * @param exceptfds + * @param timeout + * @return + */ +int virt_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); + +/** + * @brief Issue file control commands on a socket + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param cmd + * @param flags + * @return + */ +int virt_fcntl(int fd, int cmd, int flags); + +/** + * @brief Control a device + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param request + * @param argp + * @return + */ +int virt_ioctl(int fd, unsigned long request, void *argp); + +/** + * @brief Send data to remote host + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param buf Pointer to data buffer + * @param len Length of data to write + * @param flags + * @return + */ +ssize_t virt_send(int fd, const void *buf, size_t len, int flags); + +/** + * @brief Send data to remote host + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param buf Pointer to data buffer + * @param len Length of data to write + * @param flags + * @param addr Destination address + * @param addrlen Length of destination address + * @return + */ +ssize_t virt_sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen); + +/** + * @brief Send message to remote host + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param msg + * @param flags + * @return + */ +ssize_t virt_sendmsg(int fd, const struct msghdr *msg, int flags); + +/** + * @brief Receive data from remote host + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param buf Pointer to data buffer + * @param len Length of data buffer + * @param flags + * @return + */ +ssize_t virt_recv(int fd, void *buf, size_t len, int flags); + +/** + * @brief Receive data from remote host + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param buf Pointer to data buffer + * @param len Length of data buffer + * @param flags + * @param addr + * @param addrlen + * @return + */ +ssize_t virt_recvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen); + +/** + * @brief Receive a message from remote host + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param msg + * @param flags + * @return + */ +ssize_t virt_recvmsg(int fd, struct msghdr *msg,int flags); + +/** + * @brief Read bytes from socket onto buffer + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param buf Pointer to data buffer + * @param len Length of data buffer to receive data + * @return + */ +int virt_read(int fd, void *buf, size_t len); + +/** + * @brief Write bytes from buffer to socket + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param buf Pointer to data buffer + * @param len Length of buffer to write + * @return + */ +int virt_write(int fd, const void *buf, size_t len); + +/** + * @brief Shut down some aspect of a socket (read, write, or both) + * + * @usage Call this after virt_start() has succeeded + * @param fd File descriptor (only valid for use with libzt calls) + * @param how Which aspects of the socket should be shut down + * @return + */ +int virt_shutdown(int fd, int how); + +/** + * @brief Adds a DNS nameserver for the network stack to use + * + * @usage Call this after virt_start() has succeeded + * @param addr Address for DNS nameserver + * @return + */ +int virt_add_dns_nameserver(struct sockaddr *addr); + +/** + * @brief Removes a DNS nameserver + * + * @usage Call this after virt_start() has succeeded + * @param addr Address for DNS nameserver + * @return + */ +int virt_del_dns_nameserver(struct sockaddr *addr); + +/** + * @brief Returns whether one can add a new socket or not. This depends on network stack in use. + * + * @usage Call this after zts_start() has succeeded + * @param socket_type + * @return + */ +bool virt_can_provision_new_socket(int socket_type); + +/** + * @brief Returns the number of VirtualSockets either already provisioned or waiting to be + * Some network stacks may have a limit on the number of sockets that they can + * safely handle due to timer construction, this is a way to check that we + * haven't passed that limit. Someday if multiple stacks are used simultaneously + * the logic for this function should change accordingly. + * + * @usage Call this after zts_start() has succeeded + * @return + */ +int virt_num_active_sockets(); + +/** + * @brief Return the maximum number of sockets allowable by platform/stack configuration + * + * @usage Call this after zts_start() has succeeded + * @param socket_type + * @return + */ +int virt_maxsockets(int socket_type); + +/** + * @brief Return the number of currently active picoTCP timers + * + * @usage Call this after zts_start() has succeeded + * @return + */ +//int pico_ntimers(); + +/** + * @brief Convert a struct sockaddr to a ZeroTier::InetAddress + * + * @usage For internal use only. + * @param socket_family + * @param addr + * @param inet + * @return + */ +void sockaddr2inet(int socket_family, const struct sockaddr *addr, ZeroTier::InetAddress *inet); + diff --git a/include/VirtualTap.h b/include/VirtualTap.h new file mode 100644 index 0000000..22fb0d9 --- /dev/null +++ b/include/VirtualTap.h @@ -0,0 +1,331 @@ +/* + * ZeroTier SDK - Network Virtualization Everywhere + * Copyright (C) 2011-2017 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 . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +/** + * @file + * + * Virtual Ethernet tap device + */ + +#ifndef ZT_VIRTUALTAP_HPP +#define ZT_VIRTUALTAP_HPP + +#include "Mutex.hpp" +#include "MulticastGroup.hpp" +#include "InetAddress.hpp" +#include "Thread.hpp" +#include "Phy.hpp" + +#include "libztDefs.h" + +#include +extern std::vector vtaps; +extern ZeroTier::Mutex _vtaps_lock; + +#if defined(__MINGW32__) || defined(__MINGW64__) +#include +#include +#include +#include +#endif + +using namespace ZeroTier; + +class VirtualSocket; + +/** + * emulates an Ethernet tap device + */ +class VirtualTap +{ + friend class Phy; + +public: + VirtualTap( + const char *homePath, + const ZeroTier::MAC &mac, + unsigned int mtu, + unsigned int metric, + uint64_t nwid, + const char *friendlyName, + void (*handler)(void *, void *, uint64_t, const ZeroTier::MAC &, + const ZeroTier::MAC &, unsigned int, unsigned int, const void *, unsigned int), + void *arg); + + ~VirtualTap(); + + void setEnabled(bool en); + bool enabled() const; + + /** + * Registers a device with the given address + */ + bool registerIpWithStack(const ZeroTier::InetAddress &ip); + + /** + * Adds an address to the userspace stack interface associated with this VirtualTap + * - Starts VirtualTap main thread ONLY if successful + */ + bool addIp(const ZeroTier::InetAddress &ip); + + /** + * Removes an address from the userspace stack interface associated with this VirtualTap + */ + bool removeIp(const ZeroTier::InetAddress &ip); + + /** + * Presents data to the userspace stack + */ + void put(const ZeroTier::MAC &from,const ZeroTier::MAC &to,unsigned int etherType,const void *data, + unsigned int len); + + /** + * Get VirtualTap device name (e.g. 'libzt4-17d72843bc2c5760') + */ + std::string deviceName() const; + + /** + * Get Node ID (ZT address) + */ + std::string nodeId() const; + + /** + * Set friendly name + */ + void setFriendlyName(const char *friendlyName); + + /** + * Scan multicast groups + */ + void scanMulticastGroups(std::vector &added, + std::vector &removed); + + /** + * Set MTU + */ + void setMtu(unsigned int mtu); + + /** + * Calls main network stack loops + */ + void threadMain() + throw(); + +#if defined(__MINGW32__) + /* The following is merely to make ZeroTier's OneService happy while building on Windows. + we won't use these in libzt */ + NET_LUID _deviceLuid; + std::string _deviceInstanceId; + + /** + * Returns whether the VirtualTap interface has been initialized + */ + bool isInitialized() const { return _initialized; }; + + inline const NET_LUID &luid() const { return _deviceLuid; } + inline const std::string &instanceId() const { return _deviceInstanceId; } +#endif + /** + * For moving data onto the ZeroTier virtual wire + */ + void (*_handler)(void *, void *, uint64_t, const ZeroTier::MAC &, const ZeroTier::MAC &, unsigned int, unsigned int, + const void *, unsigned int); + + /** + * Signals us to close the TcpVirtualSocket associated with this PhySocket + */ + void phyOnUnixClose(ZeroTier::PhySocket *sock, void **uptr); + + /** + * Notifies us that there is data to be read from an application's socket + */ + void phyOnUnixData(ZeroTier::PhySocket *sock, void **uptr, void *data, ssize_t len); + + /** + * Notifies us that we can write to an application's socket + */ + void phyOnUnixWritable(ZeroTier::PhySocket *sock, void **uptr, bool stack_invoked); + + /** + * Adds a route to the virtual tap + */ + bool routeAdd(const ZeroTier::InetAddress &ip, const ZeroTier::InetAddress &nm, const ZeroTier::InetAddress &gw); + + /** + * Deletes a route from the virtual tap + */ + bool routeDelete(const ZeroTier::InetAddress &ip, const ZeroTier::InetAddress &nm); + + /** + * Assign a VirtualSocket to the VirtualTap + */ + void addVirtualSocket(VirtualSocket *vs); + + /** + * Remove a VirtualSocket from the VirtualTap + */ + void removeVirtualSocket(); + + /****************************************************************************/ + /* DNS */ + /****************************************************************************/ + + /** + * Registers a DNS nameserver with the network stack + */ + int add_DNS_Nameserver(struct sockaddr *addr); + + /** + * Un-registers a DNS nameserver from the network stack + */ + int del_DNS_Nameserver(struct sockaddr *addr); + + /****************************************************************************/ + /* Vars */ + /****************************************************************************/ + +#if defined(STACK_PICO) + bool should_start_stack = false; + struct pico_device *picodev = NULL; + + /****************************************************************************/ + /* Guarded RX Frame Buffer for picoTCP */ + /****************************************************************************/ + + unsigned char pico_frame_rxbuf[MAX_PICO_FRAME_RX_BUF_SZ]; + int pico_frame_rxbuf_tot = 0; + Mutex _pico_frame_rxbuf_m; +#endif + + std::vector> routes; + void *zt1ServiceRef = NULL; + + char vtap_full_name[64]; + char vtap_abbr_name[16]; + + static int devno; + size_t ifindex = 0; + + std::vector ips() const; + std::vector _ips; + + std::string _homePath; + void *_arg; + volatile bool _initialized; + volatile bool _enabled; + volatile bool _run; + ZeroTier::MAC _mac; + unsigned int _mtu; + uint64_t _nwid; + ZeroTier::PhySocket *_unixListenSocket; + ZeroTier::Phy _phy; + + std::vector _VirtualSockets; + + Thread _thread; + std::string _dev; // path to Unix domain socket + + std::vector _multicastGroups; + Mutex _multicastGroups_m; + Mutex _ips_m, _tcpconns_m, _rx_buf_m, _close_m; + + /* + * Timestamp of last run of housekeeping + * SEE: ZT_HOUSEKEEPING_INTERVAL in libzt.h + */ + uint64_t last_housekeeping_ts = 0; + + /****************************************************************************/ + /* 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 VirtualTap + */ + int Connect(VirtualSocket *vs, const struct sockaddr *addr, socklen_t addrlen); + + /** + * Bind to the userspace stack interface associated with this VirtualTap + */ + int Bind(VirtualSocket *vs, const struct sockaddr *addr, socklen_t addrlen); + + /** + * Listen for a VirtualSocket + */ + int Listen(VirtualSocket *vs, int backlog); + + /** + * Accepts an incoming VirtualSocket + */ + VirtualSocket* Accept(VirtualSocket *vs); + + /** + * Move data from RX buffer to application's "socket" + */ + int Read(VirtualSocket *vs, PhySocket *sock, void **uptr, bool stack_invoked); + + /** + * Move data from application's "socket" into network stack + */ + int Write(VirtualSocket *vs, void *data, ssize_t len); + + /** + * Send data to specified host + */ + int SendTo(VirtualSocket *vs, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen); + + /** + * Closes a VirtualSocket + */ + int Close(VirtualSocket *vs); + + /** + * Shuts down some aspect of a VirtualSocket + */ + int Shutdown(VirtualSocket *vs, int how); + + /** + * Disposes of previously-closed VirtualSockets + */ + void Housekeeping(); + + /****************************************************************************/ + /* 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); +}; + + +#endif // _H diff --git a/include/ZT1Service.h b/include/ZT1Service.h index d9c4117..3f14a45 100644 --- a/include/ZT1Service.h +++ b/include/ZT1Service.h @@ -31,7 +31,8 @@ */ #include "ZeroTierOne.h" -#include "Defs.h" +#include "InetAddress.hpp" +#include "libztDefs.h" #include @@ -42,20 +43,13 @@ extern "C" { #endif -namespace ZeroTier -{ - extern std::vector vtaps; +class VirtualTap; +class VirtualSocket; - class picoTCP; - extern ZeroTier::picoTCP *picostack; - - class lwIP; - extern ZeroTier::lwIP *lwipstack; - - class VirtualTap; - class VirtualSocket; - struct InetAddress; -} +VirtualTap *getTapByAddr(ZeroTier::InetAddress *addr); +VirtualTap *getTapByName(char *ifname); +VirtualTap *getTapByIndex(size_t index); +VirtualTap *getAnyTap(); /** * @brief Returns a vector of network routes { target, via, metric, etc... } @@ -64,7 +58,7 @@ namespace ZeroTier * @param nwid 16-digit hexidecimal network identifier * @return */ -std::vector *zts_get_network_routes(char *nwid); +std::vector *zts_get_network_routes(const uint64_t nwid); /** * @brief @@ -74,7 +68,7 @@ std::vector *zts_get_network_routes(char *nwid); * @param devID buffer to which the device ID (nodeID, ztAddress) should be copied * @return */ -int zts_getid_from_file(const char *filepath, char *devID); +int zts_getid_from_file(const char *filepath, uint64_t nodeId); /** * @brief Starts a ZeroTier service in the background @@ -86,62 +80,24 @@ int zts_getid_from_file(const char *filepath, char *devID); void *zts_start_service(void *thread_id); /** - * @brief Stops all VirtualTap interfaces and associated I/O loops - * - * @usage For internal use only. - * @param - * @return - */ -void disableTaps(); - -/** - * @brief Gets the VirtualTap's (interface) IPv4 address + * @brief Gets the VirtualTap's (interface) IP address * * @usage For internal use only. * @param nwid - * @param addrstr + * @param addr * @param addrlen * @return */ -void zts_get_ipv4_address(const char *nwid, char *addrstr, const size_t addrlen); +void zts_get_address(const uint64_t nwid, struct sockaddr_storage *addr, const size_t addrlen); /** - * @brief Gets the VirtualTap's (interface) IPv6 address - * - * @usage For internal use only. - * @param nwid - * @param addrstr - * @param addrlen - * @return - */ -void zts_get_ipv6_address(const char *nwid, char *addrstr, const size_t addrlen); - -/** - * @brief Returns whether the VirtualTap has an assigned IPv4 address + * @brief Returns whether the VirtualTap has an assigned address (IPv4 or IPv6) * * @usage For internal use only. * @param nwid * @return */ -int zts_has_ipv4_address(const char *nwid); - -/** - * @brief Returns whether the VirtualTap has an assigned IPv6 address - * - * @usage For internal use only. - * @param nwid - * @return - */ -int zts_has_ipv6_address(const char *nwid); - -/** - * @brief Returns whether the VirtualTap has an assigned address of any protocol version - * - * @usage For internal use only. - * @param nwid - * @return - */ -int zts_has_address(const char *nwid); +int zts_has_address(const uint64_t nwid); /** * @brief Copies the 6PLANE IPv6 address for the VirtualTap into the provided buffer @@ -152,7 +108,7 @@ int zts_has_address(const char *nwid); * @param devID * @return */ -void zts_get_6plane_addr(char *addr, const char *nwid, const char *devID); +void zts_get_6plane_addr(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); /** * @brief Copies the RFC4193 IPv6 address for the VirtualTap into the provided buffer @@ -160,10 +116,10 @@ void zts_get_6plane_addr(char *addr, const char *nwid, const char *devID); * @usage * @param addr * @param nwid - * @param devID + * @param devID * @return */ -void zts_get_rfc4193_addr(char *addr, const char *nwid, const char *devID); +void zts_get_rfc4193_addr(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); /** * @brief Join a network @@ -172,7 +128,7 @@ void zts_get_rfc4193_addr(char *addr, const char *nwid, const char *devID); * @param nwid A 16-digit hexidecimal virtual network ID * @return */ -void zts_join(const char * nwid); +void zts_join(const uint64_t nwid); /** * @brief Leave a network @@ -181,7 +137,7 @@ void zts_join(const char * nwid); * @param nwid A 16-digit hexidecimal virtual network ID * @return */ -void zts_leave(const char * nwid); +void zts_leave(const uint64_t nwid); /** * @brief Return whether libzt (specifically the ZeroTier core service) is currently running @@ -213,7 +169,7 @@ int zts_start(const char *path, bool blocking); * @param nwid A 16-digit hexidecimal virtual network ID * @return Returns 0 on success, -1 on failure */ -int zts_startjoin(const char *path, const char *nwid); +int zts_startjoin(const char *path, const uint64_t nwid); /** * @brief Stops libzt (ZeroTier core services, stack drivers, stack threads, etc) @@ -226,7 +182,7 @@ void zts_stop(); /** * @brief Copies the configuration path used by ZeroTier into the provided buffer * - * @usage + * @usage * @param homePath * @param len * @return @@ -234,13 +190,12 @@ void zts_stop(); void zts_get_homepath(char *homePath, size_t len); /** - * @brief Copies the hexidecimal representation of this nodeID into the provided buffer + * @brief Returns the ztaddress/nodeId/device ID of this instance * * @usage Call this after zts_start() and/or when zts_running() returns true - * @param devID Buffer to which id string is copied * @return */ -int zts_get_id(char *devID); +uint64_t zts_get_node_id(); /** * @brief Return the number of peers @@ -258,7 +213,7 @@ unsigned long zts_get_peer_count(); * @param * @return */ -int zts_get_peer_address(char *peer, const char *devID); +int zts_get_peer_address(char *peer, const uint64_t nodeId); /** * @brief Allow or disallow this instance of libzt to be controlled via HTTP requests @@ -270,42 +225,13 @@ int zts_get_peer_address(char *peer, const char *devID); void zts_allow_http_control(bool allowed); /** - * @brief Returns whether one can add a new socket or not. This depends on network stack in use. + * @brief Returns masked address for subnet comparisons * - * @usage Call this after zts_start() has succeeded + * @usage For internal use only. * @param socket_type * @return */ -bool can_provision_new_socket(int socket_type); - -/** - * @brief Returns the number of VirtualSockets either already provisioned or waiting to be - * Some network stacks may have a limit on the number of sockets that they can - * safely handle due to timer construction, this is a way to check that we - * haven't passed that limit. Someday if multiple stacks are used simultaneously - * the logic for this function should change accordingly. - * - * @usage Call this after zts_start() has succeeded - * @return - */ -int zts_num_active_virt_sockets(); - -/** - * @brief Return the maximum number of sockets allowable by platform/stack configuration - * - * @usage Call this after zts_start() has succeeded - * @param socket_type - * @return - */ -int zts_maxsockets(int socket_type); - -/** - * @brief Return the number of currently active picoTCP timers - * - * @usage Call this after zts_start() has succeeded - * @return - */ -int pico_ntimers(); +bool _ipv6_in_subnet(ZeroTier::InetAddress *subnet, ZeroTier::InetAddress *addr); #ifdef __cplusplus } diff --git a/include/libzt.h b/include/libzt.h index 7a92248..7c0d290 100644 --- a/include/libzt.h +++ b/include/libzt.h @@ -33,16 +33,8 @@ #ifndef LIBZT_H #define LIBZT_H -#include -#include -#include - -#if defined(__linux__) - #include -#endif - -#include "Debug.hpp" -#include "Defs.h" +#include "libztDebug.h" +#include "sys/socket.h" /****************************************************************************/ /* DLL export for Windows (and other cruft) */ @@ -52,6 +44,9 @@ typedef int ssize_t; #endif +#include +#include + #if defined(__MING32__) || defined(__MING64__) #ifdef ADD_EXPORTS #define ZT_SOCKET_API __declspec(dllexport) @@ -73,7 +68,7 @@ extern "C" { #endif // forward declarations from ZT1Service.h -ZT_SOCKET_API int ZTCALL zts_get_id(char *devID); +ZT_SOCKET_API uint64_t ZTCALL zts_get_node_id(); ZT_SOCKET_API void ZTCALL init_network_stack(); /** @@ -104,7 +99,7 @@ ZT_SOCKET_API int ZTCALL zts_start(const char *path, bool blocking); * @param nwid A 16-digit hexidecimal network identifier (e.g. Earth: `8056c2e21c000001`) * @return 0 if successful; or 1 if failed */ -ZT_SOCKET_API int ZTCALL zts_startjoin(const char *path, const char *nwid); +ZT_SOCKET_API int ZTCALL zts_startjoin(const char *path, const uint64_t nwid); /** * @brief Stops the ZeroTier core service and disconnects from all virtual networks @@ -129,7 +124,7 @@ ZT_SOCKET_API int ZTCALL zts_running(); * @param nwid the 16-digit hexidecimal network identifier * @return */ -ZT_SOCKET_API void ZTCALL zts_join(const char * nwid); +ZT_SOCKET_API void ZTCALL zts_join(const uint64_t nwid); /** * @brief Joins a network (eventually), this will create the dir and conf file required, don't instruct the core @@ -140,7 +135,7 @@ ZT_SOCKET_API void ZTCALL zts_join(const char * nwid); * @param nwid * @return */ -ZT_SOCKET_API void ZTCALL zts_join_soft(const char * filepath, const char * nwid); +ZT_SOCKET_API void ZTCALL zts_join_soft(const char * filepath, const uint64_t nwid); /** * @brief Leaves a virtual network. @@ -149,7 +144,7 @@ ZT_SOCKET_API void ZTCALL zts_join_soft(const char * filepath, const char * nwid * @param nwid * @return */ -ZT_SOCKET_API void ZTCALL zts_leave(const char * nwid); +ZT_SOCKET_API void ZTCALL zts_leave(const uint64_t nwid); /** * @brief Leave a network - Only delete the .conf file, this will prevent the service from joining upon next startup @@ -159,7 +154,7 @@ ZT_SOCKET_API void ZTCALL zts_leave(const char * nwid); * @param nwid * @return */ -ZT_SOCKET_API void ZTCALL zts_leave_soft(const char * filepath, const char * nwid); +ZT_SOCKET_API void ZTCALL zts_leave_soft(const char * filepath, const uint64_t nwid); /** * @brief Returns path used by ZeroTier/libzt for storing identity and config files @@ -172,31 +167,13 @@ ZT_SOCKET_API void ZTCALL zts_leave_soft(const char * filepath, const char * nwi ZT_SOCKET_API void ZTCALL zts_get_homepath(char *homePath, const size_t len); /** - * @brief Get device ID (10-digit hex + NULL byte) + * @brief Get device ID (10-digit hex number) * * @usage - * @param devID + * @param nodeId * @return */ -ZT_SOCKET_API int ZTCALL zts_get_id(char *devID); - -/** - * @brief Returns whether any IPv6 address has been assigned to the SockTap for this network - * - * @usage This is used as an indicator of readiness for service for the ZeroTier core and stack - * @param nwid - * @return - */ -ZT_SOCKET_API int ZTCALL zts_has_ipv4_address(const char *nwid); - -/** - * @brief Returns whether any IPv4 address has been assigned to the SockTap for this network - * - * @usage This is used as an indicator of readiness for service for the ZeroTier core and stack - * @param nwid - * @return - */ -ZT_SOCKET_API int ZTCALL zts_has_ipv6_address(const char *nwid); +ZT_SOCKET_API int ZTCALL zts_get_id(uint64_t *nodeId); /** * @brief Returns whether any address has been assigned to the SockTap for this network @@ -205,29 +182,18 @@ ZT_SOCKET_API int ZTCALL zts_has_ipv6_address(const char *nwid); * @param nwid * @return */ -ZT_SOCKET_API int ZTCALL zts_has_address(const char *nwid); +ZT_SOCKET_API int ZTCALL zts_has_address(const uint64_t nwid); /** - * @brief Get IPV4 Address for this device on a given network + * @brief Get IP address for this device on a given network * - * @usage FIXME: Only returns first address found for given protocol and network (should be enough for now) + * @usage FIXME: Only returns first address found, good enough for most cases * @param nwid * @param addrstr * @param addrlen * @return */ -ZT_SOCKET_API void ZTCALL zts_get_ipv4_address(const char *nwid, char *addrstr, const size_t addrlen); - -/** - * @brief Get IPV6 Address for this device on a given network - * - * @usage FIXME: Only returns first address found for given protocol and network (should be enough for now) - * @param nwid - * @param addrstr - * @param addrlen - * @return - */ -ZT_SOCKET_API void ZTCALL zts_get_ipv6_address(const char *nwid, char *addrstr, const size_t addrlen); +ZT_SOCKET_API void ZTCALL zts_get_address(const uint64_t nwid, struct sockaddr_storage *addr, const size_t addrlen); /** * @brief Returns a 6PLANE IPv6 address given a network ID and zerotier ID @@ -235,10 +201,10 @@ ZT_SOCKET_API void ZTCALL zts_get_ipv6_address(const char *nwid, char *addrstr, * @usage * @param addr * @param nwid - * @param devID + * @param nodeId * @return */ -ZT_SOCKET_API void ZTCALL zts_get_6plane_addr(char *addr, const char *nwid, const char *devID); +ZT_SOCKET_API void ZTCALL zts_get_6plane_addr(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); /** * @brief Returns an RFC 4193 IPv6 address given a network ID and zerotier ID @@ -246,10 +212,10 @@ ZT_SOCKET_API void ZTCALL zts_get_6plane_addr(char *addr, const char *nwid, cons * @usage * @param addr * @param nwid - * @param devID + * @param nodeId * @return */ -ZT_SOCKET_API void ZTCALL zts_get_rfc4193_addr(char *addr, const char *nwid, const char *devID); +ZT_SOCKET_API void ZTCALL zts_get_rfc4193_addr(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); /** * @brief Return the number of peers on this network @@ -264,10 +230,10 @@ ZT_SOCKET_API unsigned long zts_get_peer_count(); * * @usage * @param peer - * @param devID + * @param nodeId * @return */ -ZT_SOCKET_API int ZTCALL zts_get_peer_address(char *peer, const char *devID); +ZT_SOCKET_API int ZTCALL zts_get_peer_address(char *peer, const uint64_t nodeId); /** * @brief Enable HTTP control plane (traditionally used by zerotier-cli) @@ -462,7 +428,10 @@ ZT_SOCKET_API int ZTCALL zts_close(int fd); * @return */ #if defined(__linux__) +/* +typedef unsigned int nfds_t; int zts_poll(struct pollfd *fds, nfds_t nfds, int timeout); +*/ #endif /** diff --git a/include/Debug.hpp b/include/libztDebug.h similarity index 97% rename from include/Debug.hpp rename to include/libztDebug.h index 901bf8d..84c2501 100644 --- a/include/Debug.hpp +++ b/include/libztDebug.h @@ -38,14 +38,11 @@ #include #endif -#include -#include - #if defined(__linux__) #include #endif -#include "Platform.h" +#include #define ZT_MSG_ERROR true // Errors #define ZT_MSG_INFO true // Information which is generally useful to any developer @@ -81,18 +78,13 @@ #define ZT_FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) // short -#ifdef __cplusplus -extern "C" { -#endif -extern unsigned int gettid(); // defined in libzt.cpp -#ifdef __cplusplus -} -#endif +extern unsigned int gettid(); #ifdef __linux__ #define ZT_THREAD_ID syscall(SYS_gettid) -#elif __APPLE__ - #define ZT_THREAD_ID (long)0//(long)gettid() +#endif +#ifdef __APPLE__ + #define ZT_THREAD_ID (long)0 //(long)gettid() #endif #ifdef __MINGW32__ #define ZT_THREAD_ID (long)0 diff --git a/include/Defs.h b/include/libztDefs.h similarity index 82% rename from include/Defs.h rename to include/libztDefs.h index 8c2776c..d5aa611 100644 --- a/include/Defs.h +++ b/include/libztDefs.h @@ -33,6 +33,85 @@ #ifndef LIBZT_DEFINES_H #define LIBZT_DEFINES_H +/** + * Use ZeroTier Virtual Socket layer to abstract network stack raw API + */ +#define ZT_VIRTUAL_SOCKET 0 +/** + * Use lwIP sockets API + */ +#define ZT_LWIP_SEQ_SOCKET 1 +/** + * Use pico BSD socket API + */ +#define ZT_PICO_BSD_SOCKET 0 + +#define STACK_LWIP 1 +#define STACK_PICO 0 +#define NO_STACK 0 // for layer-2 only (this will omit all userspace network stack code) + + +/* sanity checks for userspace network stack and socket API layer choices + + EX. + zts_socket() + 1. ) ZT_VIRTUAL_SOCKET? -> virt_socket() --- Choose this if the default socket layer isn't doing what you need + STACK_LWIP? -> raw lwip_ API + STACK_PICO? -> raw pico_ API + otherStack? -> raw API + + 2.) ZT_LWIP_SEQ_SOCKET? (default) -> lwip_socket() --- currently provides greatest safety and performance + 3.) ZT_PICO_BSD_SOCKET? -> pico_ socket API + otherStack? -> other_stack_socket() + + Default is: STACK_LWIP=1 ZT_LWIP_SEQ_SOCKET=1 + +*/ + +#if (STACK_LWIP+STACK_PICO) > 1 +#error "Multiple network stacks specified. Pick only one." +#endif +#if STACK_LWIP==0 && STACK_PICO==0 && NO_STACK==0 +#error "No network stacks specified and NO_STACK wasn't set. Pick one." +#endif +#if ZT_VIRTUAL_SOCKET==0 && ZT_LWIP_SEQ_SOCKET==0 && ZT_PICO_BSD_SOCKET==0 +#error "No socket handling layer specified. Pick one." +#endif +#if (ZT_VIRTUAL_SOCKET + ZT_LWIP_SEQ_SOCKET + ZT_PICO_BSD_SOCKET) > 1 +#error "Multiple socket handling layers specified. Pick only one." +#endif +#if ZT_LWIP_SEQ_SOCKET==1 && STACK_LWIP==0 +#error "ZT_LWIP_SEQ_SOCKET is selected as socket handling layer, but STACK_LWIP isn't set" +#endif +#if ZT_PICO_BSD_SOCKET==1 && STACK_PICO==0 +#error "ZT_PICO_BSD_SOCKET is selected as socket handling layer, but STACK_PICO isn't set" +#endif + +#if STACK_LWIP==1 + #undef STACK_PICO + #undef NO_STACK +#endif +#if STACK_PICO==1 + #undef STACK_LWIP + #undef NO_STACK +#endif +#if NO_STACK==1 + #undef STACK_LWIP + #undef STACK_PICO +#endif +#if ZT_VIRTUAL_SOCKET==1 + #undef ZT_LWIP_SEQ_SOCKET + #undef ZT_PICO_BSD_SOCKET +#endif +#if ZT_LWIP_SEQ_SOCKET==1 + #undef ZT_VIRTUAL_SOCKET + #undef ZT_PICO_BSD_SOCKET +#endif +#if ZT_PICO_BSD_SOCKET==1 + #undef ZT_VIRTUAL_SOCKET + #undef ZT_LWIP_SEQ_SOCKET +#endif + /** * Maximum MTU size for ZeroTier */ @@ -49,7 +128,7 @@ #define ZTO_ID_LEN 16 #if !defined(__MINGW32__) -typedef uint32_t socklen_t; +typedef unsigned int socklen_t; #endif /****************************************************************************/ @@ -137,6 +216,9 @@ struct sockaddr_ll { typedef signed char err_t; +#define ND6_DISCOVERY_INTERVAL 1000 +#define ARP_DISCOVERY_INTERVAL ARP_TMR_INTERVAL + /** Specifies the polling interval and the callback function that should be called to poll the application. The interval is specified in @@ -310,13 +392,7 @@ typedef signed char err_t; /** * Interval for performing background tasks (such as adding routes) on VirtualTap objects (in seconds) */ -#define ZT_HOUSEKEEPING_INTERVAL 3 - -/** - * Whether or not we want libzt to exit on internal failure - */ -#define ZT_EXIT_ON_GENERAL_FAIL false - +#define ZT_HOUSEKEEPING_INTERVAL 1 /****************************************************************************/ /* Socket API Signatures */ diff --git a/include/lwIP.h b/include/lwIP.h new file mode 100644 index 0000000..a17459f --- /dev/null +++ b/include/lwIP.h @@ -0,0 +1,250 @@ +/* + * ZeroTier SDK - Network Virtualization Everywhere + * Copyright (C) 2011-2017 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 . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +/** + * @file + * + * lwIP network stack driver + */ + +#ifndef ZT_LWIP_HPP +#define ZT_LWIP_HPP + +#include "libztDefs.h" + +#ifdef STACK_LWIP + +namespace ZeroTier { + class MAC; + class Mutex; + struct InetAddress; +} + +//#include "lwip/err.h" + +/** + * @brief Initialize network stack semaphores, threads, and timers. + * + * @usage This is called during the initial setup of each VirtualTap but is only allowed to execute once + * @return + */ +void lwip_driver_init(); + +/** + * @brief Initialize and start the DNS client + * + * @usage Called after lwip_driver_init() + * @return + */ +void lwip_dns_init(); + +/** + * @brief Starts DHCP timers + * + * @usage lwip_driver_init() + * @return + */ +void lwip_start_dhcp(void *netif); + +/** + * @brief Set up an interface in the network stack for the VirtualTap. + * + * @param + * @param tapref Reference to VirtualTap that will be responsible for sending and receiving data + * @param mac Virtual hardware address for this ZeroTier VirtualTap interface + * @param ip Virtual IP address for this ZeroTier VirtualTap interface + * @return + */ +void lwip_init_interface(void *tapref, const ZeroTier::MAC &mac, const ZeroTier::InetAddress &ip); + +/** + * @brief Called from the stack, outbound ethernet frames from the network stack enter the ZeroTier virtual wire here. + * + * @usage This shall only be called from the stack or the stack driver. Not the application thread. + * @param netif Transmits an outgoing Ethernet fram from the network stack onto the ZeroTier virtual wire + * @param p A pointer to the beginning of a chain pf struct pbufs + * @return + */ +err_t lwip_eth_tx(struct netif *netif, struct pbuf *p); + +/** + * @brief Receives incoming Ethernet frames from the ZeroTier virtual wire + * + * @usage This shall be called from the VirtualTap's I/O thread (via VirtualTap::put()) + * @param tap Pointer to VirtualTap from which this data comes + * @param from Origin address (virtual ZeroTier hardware address) + * @param to Intended destination address (virtual ZeroTier hardware address) + * @param etherType Protocol type + * @param data Pointer to Ethernet frame + * @param len Length of Ethernet frame + * @return + */ +void lwip_eth_rx(VirtualTap *tap, const ZeroTier::MAC &from, const ZeroTier::MAC &to, unsigned int etherType, + const void *data, unsigned int len); + +/****************************************************************************/ +/* Raw API driver */ +/****************************************************************************/ + +#ifdef ZT_VIRTUAL_SOCKET + +class VirtualSocket; + +/** + * Returns the number of TCP PCBs currently allocated + */ +int rd_lwip_num_current_tcp_pcbs(); + +/** + * Returns the number of UDP PCBs currently allocated + */ +int rd_lwip_num_current_udp_pcbs(); + +/** + * Returns the number of RAW PCBs currently allocated + */ +int rd_lwip_num_current_raw_pcbs(); + +/** + * Returns the total number of PCBs of any time or state + */ +int rd_lwip_num_total_pcbs(); + +/** + * Registers a DNS nameserver with the network stack + */ +int rd_lwip_add_dns_nameserver(struct sockaddr *addr); + +/** + * Un-registers a DNS nameserver from the network stack + */ +int rd_lwip_del_dns_nameserver(struct sockaddr *addr); + +/** + * Main stack loop + */ +void rd_lwip_loop(VirtualTap *tap); + +/** + * Creates a stack-specific "socket" or "VirtualSocket object" + */ +int rd_lwip_socket(void **pcb, int socket_family, int socket_type, int protocol); + +/** + * Connect to remote host via userspace network stack interface - Called from VirtualTap + */ +int rd_lwip_connect(VirtualSocket *vs, const struct sockaddr *addr, socklen_t addrlen); + +/** + * Bind to a userspace network stack interface - Called from VirtualTap + */ +int rd_lwip_bind(VirtualTap *tap, VirtualSocket *vs, const struct sockaddr *addr, socklen_t addrlen); + +/** + * Listen for incoming VirtualSockets - Called from VirtualTap + */ +int rd_lwip_listen(VirtualSocket *vs, int backlog); + +/** + * Accept an incoming VirtualSocket - Called from VirtualTap + */ +VirtualSocket* rd_lwip_accept(VirtualSocket *vs); + +/** + * Read from RX buffer to application - Called from VirtualTap + */ +int rd_lwip_read(VirtualSocket *vs, bool lwip_invoked); + +/** + * Write to userspace network stack - Called from VirtualTap + */ +int rd_lwip_write(VirtualSocket *vs, void *data, ssize_t len); + +/** + * Close a VirtualSocket - Called from VirtualTap + */ +int rd_lwip_close(VirtualSocket *vs); + +/** + * Shuts down some aspect of a VirtualSocket - Called from VirtualTap + */ +int rd_lwip_shutdown(VirtualSocket *vs, int how); + +/** + * Sets a property of a socket + */ +int rd_lwip_setsockopt(VirtualSocket *vs, int level, int optname, const void *optval, socklen_t optlen); + +/** + * Gets a property of a socket + */ +int rd_lwip_getsockopt(VirtualSocket *vs, int level, int optname, void *optval, socklen_t *optlen); + +// --- Callbacks from network stack --- + +#ifdef ZT_DRIVER_MODULE // only include these symbols if we're building the full driver + +/** + * Callback for handling received UDP packets (already processed by network stack) + */ +static err_t rd_lwip_cb_tcp_recved(void *arg, struct tcp_pcb *PCB, struct pbuf *p, err_t err); + +/** + * Callback for handling accepted connection + */ +static err_t rd_lwip_cb_accept(void *arg, struct tcp_pcb *newPCB, err_t err); + +/** + * Callback for handling received TCP packets (already processed by stack) + */ +static void rd_lwip_cb_udp_recved(void * arg, struct udp_pcb * upcb, struct pbuf * p, const ip_addr_t * addr, u16_t port); + +/** + * Callback for handling errors from within the network stack + */ +static void rd_lwip_cb_err(void *arg, err_t err); + +/** + * Callback for handling periodic background tasks + */ +static err_t rd_lwip_cb_poll(void* arg, struct tcp_pcb *PCB); + +/** + * Callback for handling confirmation of sent packets + */ +static err_t rd_lwip_cb_sent(void *arg, struct tcp_pcb *PCB, u16_t len); + +/** + * Callback for handling successful connections + */ +static err_t rd_lwip_cb_connected(void *arg, struct tcp_pcb *PCB, err_t err); + +#endif // ZT_DRIVER_MODULE + +#endif + +#endif // ZT_VIRTUAL_SOCKET +#endif // STACK_LWIP diff --git a/include/lwIP.hpp b/include/lwIP.hpp deleted file mode 100644 index 46d7a0b..0000000 --- a/include/lwIP.hpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 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 . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -/** - * @file - * - * lwIP network stack driver - */ - -#ifndef ZT_LWIP_HPP -#define ZT_LWIP_HPP - -#include "MAC.hpp" -#include "InetAddress.hpp" -#include "Defs.h" -#include "Mutex.hpp" - -/** - * @brief Initialize network stack semaphores, threads, and timers. - * - * @usage This is called during the initial setup of each VirtualTap but is only allowed to execute once - * @return - */ -void lwip_driver_init(); - -/** - * @brief Initialize and start the DNS client - * - * @usage Called after lwip_driver_init() - * @return - */ -void lwip_dns_init(); - -/** - * @brief Starts DHCP timers - * - * @usage lwip_driver_init() - * @return - */ -void lwip_start_dhcp(void *netif); - -/** - * @brief Set up an interface in the network stack for the VirtualTap. - * - * @param - * @param tapref Reference to VirtualTap that will be responsible for sending and receiving data - * @param mac Virtual hardware address for this ZeroTier VirtualTap interface - * @param ip Virtual IP address for this ZeroTier VirtualTap interface - * @return - */ -void lwip_init_interface(void *tapref, const ZeroTier::MAC &mac, const ZeroTier::InetAddress &ip); - -/** - * @brief Called from the stack, outbound ethernet frames from the network stack enter the ZeroTier virtual wire here. - * - * @usage This shall only be called from the stack or the stack driver. Not the application thread. - * @param netif Transmits an outgoing Ethernet fram from the network stack onto the ZeroTier virtual wire - * @param p A pointer to the beginning of a chain pf struct pbufs - * @return - */ -err_t lwip_eth_tx(struct netif *netif, struct pbuf *p); - -/** - * @brief Receives incoming Ethernet frames from the ZeroTier virtual wire - * - * @usage This shall be called from the VirtualTap's I/O thread (via VirtualTap::put()) - * @param tap Pointer to VirtualTap from which this data comes - * @param from Origin address (virtual ZeroTier hardware address) - * @param to Intended destination address (virtual ZeroTier hardware address) - * @param etherType Protocol type - * @param data Pointer to Ethernet frame - * @param len Length of Ethernet frame - * @return - */ -void lwip_eth_rx(ZeroTier::VirtualTap *tap, const ZeroTier::MAC &from, const ZeroTier::MAC &to, unsigned int etherType, - const void *data, unsigned int len); - -#endif diff --git a/include/lwipopts.h b/include/lwipopts.h index 2836bfa..174c2e9 100644 --- a/include/lwipopts.h +++ b/include/lwipopts.h @@ -33,7 +33,7 @@ /** * @file * - * lwIP Options Configuration + * lwIP options configuration */ #ifndef __LWIPOPTS_H__ @@ -108,47 +108,47 @@ #define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL #define LWIP_DBG_TYPES_ON LWIP_DBG_ON -#define ETHARP_DEBUG LWIP_DBG_OFF +#define ETHARP_DEBUG LWIP_DBG_ON // interfaces #define SLIP_DEBUG LWIP_DBG_OFF -#define NETIF_DEBUG LWIP_DBG_OFF +#define NETIF_DEBUG LWIP_DBG_ON // API (not used in libzt) -#define API_LIB_DEBUG LWIP_DBG_OFF -#define API_MSG_DEBUG LWIP_DBG_OFF -#define SOCKETS_DEBUG LWIP_DBG_OFF +#define API_LIB_DEBUG LWIP_DBG_ON +#define API_MSG_DEBUG LWIP_DBG_ON +#define SOCKETS_DEBUG LWIP_DBG_ON // other -#define ICMP_DEBUG LWIP_DBG_OFF -#define IGMP_DEBUG LWIP_DBG_OFF -#define INET_DEBUG LWIP_DBG_OFF -#define RAW_DEBUG LWIP_DBG_OFF +#define ICMP_DEBUG LWIP_DBG_ON +#define IGMP_DEBUG LWIP_DBG_ON +#define INET_DEBUG LWIP_DBG_ON +#define RAW_DEBUG LWIP_DBG_ON // memory -#define PBUF_DEBUG LWIP_DBG_OFF -#define MEM_DEBUG LWIP_DBG_OFF -#define MEMP_DEBUG LWIP_DBG_OFF +#define PBUF_DEBUG LWIP_DBG_ON +#define MEM_DEBUG LWIP_DBG_ON +#define MEMP_DEBUG LWIP_DBG_ON // system #define SYS_DEBUG LWIP_DBG_OFF #define TIMERS_DEBUG LWIP_DBG_OFF // TCP #define TCP_DEBUG_TMR LWIP_DBG_OFF // not standard in lwIP, added for debugging convenience -#define TCP_DEBUG LWIP_DBG_OFF -#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#define TCP_DEBUG LWIP_DBG_ON +#define TCP_INPUT_DEBUG LWIP_DBG_ON #define TCP_FR_DEBUG LWIP_DBG_OFF #define TCP_RTO_DEBUG LWIP_DBG_OFF #define TCP_CWND_DEBUG LWIP_DBG_OFF #define TCP_WND_DEBUG LWIP_DBG_OFF -#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#define TCP_OUTPUT_DEBUG LWIP_DBG_ON #define TCP_RST_DEBUG LWIP_DBG_OFF #define TCP_QLEN_DEBUG LWIP_DBG_OFF // IP #define AUTOIP_DEBUG LWIP_DBG_OFF -#define IP_DEBUG LWIP_DBG_OFF +#define IP_DEBUG LWIP_DBG_ON #define IP_REASS_DEBUG LWIP_DBG_OFF -#define IP6_DEBUG LWIP_DBG_OFF +#define IP6_DEBUG LWIP_DBG_ON // TCP/IP -#define TCPIP_DEBUG LWIP_DBG_OFF +#define TCPIP_DEBUG LWIP_DBG_ON // UDP -#define UDP_DEBUG LWIP_DBG_OFF +#define UDP_DEBUG LWIP_DBG_ON // services #define DHCP_DEBUG LWIP_DBG_OFF #define DNS_DEBUG LWIP_DBG_OFF @@ -381,7 +381,8 @@ happening sooner than they should. * LWIP_ARP==1: Enable ARP functionality. */ #define LWIP_ARP 1 - +#define ARP_TABLE_SIZE 64 +//#define ETHARP_FLAG_TRY_HARD 0 /*------------------------------------------------------------------------------ ------------------------------------ IP options--------------------------------- diff --git a/include/picoTCP.h b/include/picoTCP.h new file mode 100644 index 0000000..a934b5c --- /dev/null +++ b/include/picoTCP.h @@ -0,0 +1,222 @@ +/* + * ZeroTier SDK - Network Virtualization Everywhere + * Copyright (C) 2011-2017 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 . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +#ifndef ZT_PICOTCP_HPP +#define ZT_PICOTCP_HPP +/* +#include "pico_eth.h" +#include "pico_stack.h" +#include "pico_ipv4.h" +#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" +*/ + +struct pico_socket; +class VirtualSocket; +class VirtualTap; + +#include "VirtualTap.h" + +/****************************************************************************/ +/* PicoTCP API Signatures (See libzt.h for the application-facing API) */ +/****************************************************************************/ + +#define PICO_IPV4_TO_STRING_SIG char *ipbuf, const uint32_t ip +#define PICO_TAP_CREATE_SIG char *name +#define PICO_IPV4_LINK_ADD_SIG struct pico_device *dev, struct pico_ip4 address, struct pico_ip4 netmask +#define PICO_DEVICE_INIT_SIG struct pico_device *dev, const char *name, uint8_t *mac +#define PICO_STACK_RECV_SIG struct pico_device *dev, uint8_t *buffer, uint32_t len +#define PICO_ICMP4_PING_SIG char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp4_stats *) +#define PICO_TIMER_ADD_SIG pico_time expire, void (*timer)(pico_time, void *), void *arg +#define PICO_STRING_TO_IPV4_SIG const char *ipstr, uint32_t *ip +#define PICO_STRING_TO_IPV6_SIG const char *ipstr, uint8_t *ip +#define PICO_SOCKET_SETOPTION_SIG struct pico_socket *s, int option, void *value +#define PICO_SOCKET_SEND_SIG struct pico_socket *s, const void *buf, int len +#define PICO_SOCKET_SENDTO_SIG struct pico_socket *s, const void *buf, int len, void *dst, uint16_t remote_port +#define PICO_SOCKET_RECV_SIG struct pico_socket *s, void *buf, int len +#define PICO_SOCKET_RECVFROM_SIG struct pico_socket *s, void *buf, int len, void *orig, uint16_t *remote_port +#define PICO_SOCKET_OPEN_SIG uint16_t net, uint16_t proto, void (*wakeup)(uint16_t ev, struct pico_socket *s) +#define PICO_SOCKET_BIND_SIG struct pico_socket *s, void *local_addr, uint16_t *port +#define PICO_SOCKET_CONNECT_SIG struct pico_socket *s, const void *srv_addr, uint16_t remote_port +#define PICO_SOCKET_LISTEN_SIG struct pico_socket *s, const int backlog +#define PICO_SOCKET_READ_SIG struct pico_socket *s, void *buf, int len +#define PICO_SOCKET_WRITE_SIG struct pico_socket *s, const void *buf, int len +#define PICO_SOCKET_CLOSE_SIG struct pico_socket *s +#define PICO_SOCKET_SHUTDOWN_SIG struct pico_socket *s, int mode +#define PICO_SOCKET_ACCEPT_SIG struct pico_socket *s, void *orig, uint16_t *port +#define PICO_IPV6_LINK_ADD_SIG struct pico_device *dev, struct pico_ip6 address, struct pico_ip6 netmask +#define PICO_IPV4_ROUTE_ADD_SIG struct pico_ip4 address, struct pico_ip4 netmask, struct pico_ip4 gateway, int metric, struct pico_ipv4_link *link +#define PICO_IPV4_ROUTE_DEL_SIG struct pico_ip4 address, struct pico_ip4 netmask, int metric +#define PICO_IPV6_ROUTE_ADD_SIG struct pico_ip6 address, struct pico_ip6 netmask, struct pico_ip6 gateway, int metric, struct pico_ipv6_link *link +#define PICO_IPV6_ROUTE_DEL_SIG struct pico_ip6 address, struct pico_ip6 netmask, struct pico_ip6 gateway, int metric, struct pico_ipv6_link *link +#define PICO_DNS_CLIENT_NAMESERVER_SIG pico_ip4*, unsigned char + +/** + * Send raw frames from the stack to the ZeroTier virtual wire + */ +int rd_pico_eth_tx(struct pico_device *dev, void *buf, int len); + +/** + * Read raw frames from RX frame buffer into the stack + */ +int rd_pico_eth_poll(struct pico_device *dev, int loop_score); + +/** + * Set up an interface in the network stack for the VirtualTap + */ +bool pico_init_interface(VirtualTap *tap); + +/** + * Register an address with the stack + */ +bool pico_register_address(VirtualTap *tap, const InetAddress &ip); + +/** + * Adds a route to the picoTCP device + */ +bool rd_pico_route_add(VirtualTap *tap, const InetAddress &addr, const InetAddress &nm, const InetAddress &gw, int metric); + +/** + * Deletes a route from the picoTCP device + */ +bool rd_pico_route_del(VirtualTap *tap, const InetAddress &addr, const InetAddress &nm, int metric); + +/** + * Registers a DNS nameserver with the network stack + */ +int rd_pico_add_dns_nameserver(struct sockaddr *addr); + +/** + * Un-registers a DNS nameserver from the network stack + */ +int rd_pico_del_dns_nameserver(struct sockaddr *addr); + +/** + * Main stack loop + */ +void rd_pico_loop(VirtualTap *tap); + +/** + * Read bytes from the stack to the RX buffer (prepare to be read by app) + */ +void rd_pico_cb_tcp_read(VirtualTap *tap, struct pico_socket *s); + +/** + * Read bytes from the stack to the RX buffer (prepare to be read by app) + */ +void rd_pico_cb_udp_read(VirtualTap *tap, struct pico_socket *s); + + /** + * Write bytes from TX buffer to stack (prepare to be sent to ZT virtual wire) + */ +void rd_pico_cb_tcp_write(VirtualTap *tap, struct pico_socket *s); + +/** + * Write bytes from TX buffer to stack (prepare to be sent to ZT virtual wire) + */ +void rd_pico_cb_socket_ev(uint16_t ev, struct pico_socket *s); + +/** + * Packets from the ZeroTier virtual wire enter the stack here + */ +void rd_pico_eth_rx(VirtualTap *tap, const ZeroTier::MAC &from, const ZeroTier::MAC &to, + unsigned int etherType, const void *data, unsigned int len); + +/** + * Creates a stack-specific "socket" or "VirtualSocket object" + */ +int rd_pico_socket(struct pico_socket **p, int socket_family, int socket_type, int protocol); + +/** + * Connect to remote host via userspace network stack interface - Called from VirtualTap + */ +int rd_pico_connect(VirtualSocket *vs, const struct sockaddr *addr, socklen_t addrlen); + +/** + * Bind to a userspace network stack interface - Called from VirtualTap + */ +int rd_pico_bind(VirtualSocket *vs, const struct sockaddr *addr, socklen_t addrlen); + +/** + * Listen for incoming VirtualSockets - Called from VirtualTap + */ +int rd_pico_listen(VirtualSocket *vs, int backlog); + +/** + * Accept an incoming VirtualSocket - Called from VirtualTap + */ +VirtualSocket* rd_pico_accept(VirtualSocket *vs); + +/** + * Read from RX buffer to application - Called from VirtualTap + */ +int rd_pico_read(VirtualTap *tap, ZeroTier::PhySocket *sock, VirtualSocket *vs, bool stack_invoked); + +/** + * Write to userspace network stack - Called from VirtualTap + */ +int rd_pico_write(VirtualSocket *vs, void *data, ssize_t len); + +/** + * Close a VirtualSocket - Called from VirtualTap + */ +int rd_pico_close(VirtualSocket *vs); + +/** + * Shuts down some aspect of a VirtualSocket - Called from VirtualTap + */ +int rd_pico_shutdown(VirtualSocket *vs, int how); + +/** + * Sets a property of a socket + */ +int rd_pico_setsockopt(VirtualSocket *vs, int level, int optname, const void *optval, socklen_t optlen); + +/** + * Gets a property of a socket + */ +int rd_pico_getsockopt(VirtualSocket *vs, int level, int optname, void *optval, socklen_t *optlen); + +/** + * Converts a pico_err to its most closely-related errno, and sets errno + */ +int map_pico_err_to_errno(int err); + +/** + * Converts picoTCP error codes to pretty string + */ +char *beautify_pico_error(int err); + +/** + * Converts picoTCP socket states into pretty string + */ +char *beautify_pico_state(int state); + +#endif // _H diff --git a/make-liblwip.mk b/make-liblwip.mk index 18b8443..d9c9fbe 100644 --- a/make-liblwip.mk +++ b/make-liblwip.mk @@ -64,7 +64,7 @@ LWIPARCH=$(CONTRIBDIR)/ports/unix endif LWIPINCLUDES:=-I$(LWIPDIR)/include -I$(LWIPARCH) -I$(LWIPARCH)/include -I$(LWIPDIR) -I. -Iext -Iinclude -CFLAGS=$(WINDEFS) -Wno-format -Wno-deprecated -O3 -g -Wall -fPIC $(LWIPINCLUDES) +CFLAGS=$(WINDEFS) -Wno-format -Wno-missing-prototypes -Wno-deprecated -O3 -g -Wall -fPIC $(LWIPINCLUDES) ifeq ($(NS_DEBUG),1) CFLAGS+=-DLWIP_DEBUG=1 diff --git a/src/RingBuffer.cpp b/src/RingBuffer.cpp new file mode 100644 index 0000000..7f74061 --- /dev/null +++ b/src/RingBuffer.cpp @@ -0,0 +1,143 @@ +/* + * ZeroTier SDK - Network Virtualization Everywhere + * Copyright (C) 2011-2017 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 . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +/** + * @file + * + * Ring buffer implementation for network stack drivers + */ + +#include +#include + +#include "RingBuffer.h" + +bufElementType* RingBuffer::get_buf() +{ + return buf + begin; +} + +size_t RingBuffer::produce(size_t n) +{ + n = std::min(n, getFree()); + if (n == 0) { + return n; + } + const size_t first_chunk = std::min(n, size - end); + end = (end + first_chunk) % size; + if (first_chunk < n) { + const size_t second_chunk = n - first_chunk; + end = (end + second_chunk) % size; + } + if (begin == end) { + wrap = true; + } + return n; +} + +void RingBuffer::reset() +{ + consume(count()); +} + +size_t RingBuffer::consume(size_t n) +{ + n = std::min(n, count()); + if (n == 0) { + return n; + } + if (wrap) { + wrap = false; + } + const size_t first_chunk = std::min(n, size - begin); + begin = (begin + first_chunk) % size; + if (first_chunk < n) { + const size_t second_chunk = n - first_chunk; + begin = (begin + second_chunk) % size; + } + return n; +} + +size_t RingBuffer::write(const bufElementType * data, size_t n) +{ + n = std::min(n, getFree()); + + if (n == 0) { + return n; + } + const size_t first_chunk = std::min(n, size - end); + memcpy(buf + end, data, first_chunk * sizeof(bufElementType)); + end = (end + first_chunk) % size; + if (first_chunk < n) { + const size_t second_chunk = n - first_chunk; + memcpy(buf + end, data + first_chunk, second_chunk * sizeof(bufElementType)); + end = (end + second_chunk) % size; + } + if (begin == end) { + wrap = true; + } + return n; +} + +size_t RingBuffer::read(bufElementType * dest, size_t n) +{ + n = std::min(n, count()); + + if (n == 0) { + return n; + } + if (wrap) { + wrap = false; + } + const size_t first_chunk = std::min(n, size - begin); + memcpy(dest, buf + begin, first_chunk * sizeof(bufElementType)); + begin = (begin + first_chunk) % size; + if (first_chunk < n) { + const size_t second_chunk = n - first_chunk; + memcpy(dest + first_chunk, buf + begin, second_chunk * sizeof(bufElementType)); + begin = (begin + second_chunk) % size; + } + return n; +} + +size_t RingBuffer::count() +{ + if (end == begin) { + return wrap ? size : 0; + } + else if (end > begin) { + return end - begin; + } + else { + return size + end - begin; + } +} + +size_t RingBuffer::getFree() +{ + return size - count(); +} + diff --git a/src/RingBuffer.hpp b/src/RingBuffer.hpp deleted file mode 100644 index b274065..0000000 --- a/src/RingBuffer.hpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 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 . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -/** - * @file - * - * Ring buffer implementation for network stack drivers - */ - -#ifndef ZT_RINGBUFFER_HPP -#define ZT_RINGBUFFER_HPP - -#include -#include - -namespace ZeroTier { - - template class RingBuffer { - - private: - T * buf; - size_t size; - size_t begin; - size_t end; - bool wrap; - - public: - /** - * create a RingBuffer with space for up to size elements. - */ - explicit RingBuffer(size_t size) - : size(size), - begin(0), - end(0), - wrap(false) - { - buf = new T[size]; - } - - RingBuffer(const RingBuffer & ring) - { - this(ring.size); - begin = ring.begin; - end = ring.end; - memcpy(buf, ring.buf, sizeof(T) * size); - } - - ~RingBuffer() - { - delete[] buf; - } - - // get a reference to the underlying buffer - T* get_buf() - { - return buf + begin; - } - - // adjust buffer index pointer as if we copied data in - size_t produce(size_t n) - { - n = std::min(n, getFree()); - if (n == 0) { - return n; - } - const size_t first_chunk = std::min(n, size - end); - end = (end + first_chunk) % size; - if (first_chunk < n) { - const size_t second_chunk = n - first_chunk; - end = (end + second_chunk) % size; - } - if (begin == end) { - wrap = true; - } - return n; - } - - // merely reset the buffer pointer, doesn't erase contents - void reset() - { - consume(count()); - } - - // adjust buffer index pointer as if we copied data out - size_t consume(size_t n) - { - n = std::min(n, count()); - if (n == 0) { - return n; - } - if (wrap) { - wrap = false; - } - const size_t first_chunk = std::min(n, size - begin); - begin = (begin + first_chunk) % size; - if (first_chunk < n) { - const size_t second_chunk = n - first_chunk; - begin = (begin + second_chunk) % size; - } - return n; - } - - size_t write(const T * data, size_t n) - { - n = std::min(n, getFree()); - - if (n == 0) { - return n; - } - const size_t first_chunk = std::min(n, size - end); - memcpy(buf + end, data, first_chunk * sizeof(T)); - end = (end + first_chunk) % size; - if (first_chunk < n) { - const size_t second_chunk = n - first_chunk; - memcpy(buf + end, data + first_chunk, second_chunk * sizeof(T)); - end = (end + second_chunk) % size; - } - if (begin == end) { - wrap = true; - } - return n; - } - - size_t read(T * dest, size_t n) - { - n = std::min(n, count()); - - if (n == 0) { - return n; - } - if (wrap) { - wrap = false; - } - const size_t first_chunk = std::min(n, size - begin); - memcpy(dest, buf + begin, first_chunk * sizeof(T)); - begin = (begin + first_chunk) % size; - if (first_chunk < n) { - const size_t second_chunk = n - first_chunk; - memcpy(dest + first_chunk, buf + begin, second_chunk * sizeof(T)); - begin = (begin + second_chunk) % size; - } - return n; - } - - size_t count() { - if (end == begin) { - return wrap ? size : 0; - } - else if (end > begin) { - return end - begin; - } - else { - return size + end - begin; - } - } - - size_t getFree() { - return size - count(); - } - }; -} -#endif // ZT_RINGBUFFER_HPP diff --git a/src/Platform.cpp b/src/SysUtils.cpp similarity index 87% rename from src/Platform.cpp rename to src/SysUtils.cpp index 5cbc1ae..ffd3dcf 100644 --- a/src/Platform.cpp +++ b/src/SysUtils.cpp @@ -30,7 +30,20 @@ * Platform-specific implementations of common functions */ -#include "Platform.h" +#if defined(STACK_LWIP) +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include "lwip/ip_addr.h" +#include "lwip/netdb.h" +#include "dns.h" +#endif +#if defined(NO_STACK) +#include +#endif + +#include + +#include "SysUtils.h" #include #include @@ -39,17 +52,6 @@ #include #endif -#ifdef __cplusplus -extern "C" { -#endif - -void handle_general_failure() { -#ifdef ZT_EXIT_ON_GENERAL_FAIL - DEBUG_ERROR("exiting (ZT_EXIT_ON_GENERAL_FAIL==1)"); - exit(-1); -#endif -} - inline unsigned int gettid() { #ifdef _WIN32 @@ -63,7 +65,3 @@ inline unsigned int gettid() return static_cast(tid64); #endif } - -#ifdef __cplusplus -} -#endif diff --git a/src/Utilities.cpp b/src/Utilities.cpp index d0010e7..e0d95f1 100644 --- a/src/Utilities.cpp +++ b/src/Utilities.cpp @@ -30,8 +30,7 @@ * Misc utilities */ -#include "InetAddress.hpp" -#include "Debug.hpp" +//#include "libztDebug.h" #include "Utilities.h" #if defined(__MINGW32__) @@ -194,10 +193,6 @@ int inet_pton(int af, const char *src, void *dst) #endif - - - - char *beautify_eth_proto_nums(int proto) { if (proto == 0x0800) return (char*)"IPv4"; @@ -253,81 +248,8 @@ char *beautify_eth_proto_nums(int proto) return (char*)"UNKNOWN"; } -bool ipv6_in_subnet(ZeroTier::InetAddress *subnet, ZeroTier::InetAddress *addr) -{ - ZeroTier::InetAddress r(addr); - ZeroTier::InetAddress b(subnet); - const unsigned int bits = subnet->netmaskBits(); - switch(r.ss_family) { - case AF_INET: - reinterpret_cast(&r)->sin_addr.s_addr &= ZeroTier::Utils::hton((uint32_t)(0xffffffff << (32 - bits))); - break; - case AF_INET6: { - uint64_t nm[2]; - uint64_t nm2[2]; - memcpy(nm,reinterpret_cast(&r)->sin6_addr.s6_addr,16); - memcpy(nm2,reinterpret_cast(&b)->sin6_addr.s6_addr,16); - - nm[0] &= ZeroTier::Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits)))); - nm[1] &= ZeroTier::Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits)))); - - nm2[0] &= ZeroTier::Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits)))); - nm2[1] &= ZeroTier::Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits)))); - - memcpy(reinterpret_cast(&r)->sin6_addr.s6_addr,nm,16); - memcpy(reinterpret_cast(&b)->sin6_addr.s6_addr,nm2,16); - } - break; - } - char b0[64], b1[64]; - memset(b0, 0, 64); - memset(b1, 0, 64); - return !strcmp(r.toIpString(b0), b.toIpString(b1)); -} - -/* -void sockaddr2inet(int socket_family, const struct sockaddr *addr, ZeroTier::InetAddress *inet) -{ - char ipstr[INET6_ADDRSTRLEN]; - memset(ipstr, 0, INET6_ADDRSTRLEN); - if (socket_family == AF_INET) { - inet_ntop(AF_INET, - (const void *)&((struct sockaddr_in *)addr)->sin_addr.s_addr, ipstr, INET_ADDRSTRLEN); - inet->fromString(ipstr); - } - if (socket_family == AF_INET6) { - inet_ntop(AF_INET6, - (const void *)&((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, ipstr, INET6_ADDRSTRLEN); - char addrstr[64]; - sprintf(addrstr, "%s", ipstr); - inet->fromString(addrstr); - } -} -*/ - void mac2str(char *macbuf, int len, unsigned char* addr) { snprintf(macbuf, len, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); } - - -/** - * Convert from standard IPV6 address structure to an lwIP native structure - */ -/* -inline void in6_to_ip6(ip6_addr_t *ba, struct sockaddr_in6 *in6) -{ - uint8_t *ip = &(in6->sin6_addr).s6_addr[0]; - IP6_ADDR2(ba, - (((ip[ 0] & 0xffff) << 8) | ((ip[ 1]) & 0xffff)), - (((ip[ 2] & 0xffff) << 8) | ((ip[ 3]) & 0xffff)), - (((ip[ 4] & 0xffff) << 8) | ((ip[ 5]) & 0xffff)), - (((ip[ 6] & 0xffff) << 8) | ((ip[ 7]) & 0xffff)), - (((ip[ 8] & 0xffff) << 8) | ((ip[ 9]) & 0xffff)), - (((ip[10] & 0xffff) << 8) | ((ip[11]) & 0xffff)), - (((ip[12] & 0xffff) << 8) | ((ip[13]) & 0xffff)), - (((ip[14] & 0xffff) << 8) | ((ip[15]) & 0xffff)) - ); -} -*/ diff --git a/src/VirtualSocket.cpp b/src/VirtualSocket.cpp index 8d48e80..fe2deb3 100644 --- a/src/VirtualSocket.cpp +++ b/src/VirtualSocket.cpp @@ -27,97 +27,79 @@ /** * @file * - * Platform-agnostic implementation of a socket-like object + * Platform- and stack-agnostic implementation of a socket-like object */ -#ifndef ZT_VIRTUALSOCKET_HPP -#define ZT_VIRTUALSOCKET_HPP +#include "libztDefs.h" + +#ifdef ZT_VIRTUAL_SOCKET #include - -#if defined(__linux__) || defined(__APPLE__) #include -#endif +#include "Phy.hpp" + +#include "libzt.h" +#include "libztDebug.h" #include "VirtualSocket.h" -#include "VirtualBindingPair.h" -#include "RingBuffer.hpp" +#include "VirtualTap.h" +#include "RingBuffer.h" -#define VS_STATE_INACTIVE 0x000000u // Default value for newly created VirtualSocket -#define VS_STATE_ACTIVE 0x000001u // VirtualSocket is RX'ing or TX'ing without issue -#define VS_STATE_SHOULD_SHUTDOWN 0x000002u // Application, stack driver, or stack marked this VirtualSocket for death -#define VS_STATE_SHUTDOWN 0x000004u // VirtualSocket and underlying protocol control structures will not RX/TX -#define VS_STATE_CLOSED 0x000008u // VirtualSocket and underlying protocol control structures are closed -#define VS_STATE_UNHANDLED_CONNECTED 0x000010u // stack callback has received a connection but we haven't dealt with it -#define VS_STATE_CONNECTED 0x000020u // stack driver has akwnowledged new connection -#define VS_STATE_LISTENING 0x000040u // virtual socket is listening for incoming connections +class VirtualTap; -#define VS_OPT_TCP_NODELAY 0x000000u // Nagle's algorithm -#define VS_OPT_SO_LINGER 0x000001u // VirtualSocket waits for data transmission before closure -/* -#define VS_RESERVED 0x000002u // -#define VS_RESERVED 0x000004u // -#define VS_RESERVED 0x000008u // -#define VS_RESERVED 0x000010u // -#define VS_RESERVED 0x000020u // -#define VS_RESERVED 0x000040u // -*/ -#define VS_OPT_FD_NONBLOCKING 0x000080u // Whether the VirtualSocket exhibits non-blocking behaviour -/* -#define VS_RESERVED 0x000100u // -#define VS_RESERVED 0x000200u // -#define VS_RESERVED 0x000400u // -#define VS_RESERVED 0x000800u // -#define VS_RESERVED 0x001000u // -#define VS_RESERVED 0x002000u // -#define VS_RESERVED 0x004000u // -#define VS_RESERVED 0x008000u // -#define VS_RESERVED 0x010000u // -#define VS_RESERVED 0x020000u // -#define VS_RESERVED 0x040000u // -#define VS_RESERVED 0x080000u // -#define VS_RESERVED 0x100000u // -#define VS_RESERVED 0x200000u // -#define VS_RESERVED 0x400000u // -#define VS_RESERVED 0x800000u // -*/ - -#define vs_is_nonblocking(vs) (((vs)->optflags & VS_OPT_FD_NONBLOCKING) != 0) - -namespace ZeroTier { - - class VirtualTap; - - class VirtualSocket - { - private: - int _state = VS_STATE_INACTIVE; - public: - - /** - * Sets the VirtualSocket's state value - */ - void apply_state(int state) { - _state &= state; - } - /** - * Sets the VirtualSocket's state value - */ - void set_state(int state) { - _state = state; - } - /** - * Gets the VirtualSocket's state value - */ - int get_state() { - return _state; - } - - VirtualSocket() { - // Only available in experimental branch - } - ~VirtualSocket() { - } - }; +void VirtualSocket::apply_state(int state) { + // states may be set by application or by stack callbacks, thus this must be guarded + _op_m.lock(); + _state &= state; + _op_m.unlock(); } -#endif + +void VirtualSocket::set_state(int state) { + _op_m.lock(); + _state = state; + _op_m.unlock(); +} + +int VirtualSocket::get_state() { + return _state; +} + +VirtualSocket::VirtualSocket() { + DEBUG_EXTRA("this=%p",this); + memset(&local_addr, 0, sizeof(sockaddr_storage)); + memset(&peer_addr, 0, sizeof(sockaddr_storage)); + + // ringbuffer used for incoming and outgoing traffic between app, stack, stack drivers, and ZT + TXbuf = new RingBuffer(ZT_TCP_TX_BUF_SZ); + RXbuf = new RingBuffer(ZT_TCP_RX_BUF_SZ); + + // socketpair, I/O channel between app and stack drivers + ZT_PHY_SOCKFD_TYPE fdpair[2]; + if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fdpair) < 0) { + if (errno < 0) { + DEBUG_ERROR("unable to create socketpair, errno=%d", errno); + return; + } + } + sdk_fd = fdpair[0]; + app_fd = fdpair[1]; + + // set to non-blocking since these are used as the primary I/O channel + if (fcntl(sdk_fd, F_SETFL, O_NONBLOCK) < 0) { + DEBUG_ERROR("error while setting virtual socket to NONBLOCKING. exiting", errno); + exit(0); + } +} + +VirtualSocket::~VirtualSocket() { + DEBUG_EXTRA("this=%p",this); + close(app_fd); + close(sdk_fd); + delete TXbuf; + delete RXbuf; + TXbuf = RXbuf = NULL; + //picosock->priv = NULL; + pcb = NULL; +} + +#endif // ZT_VIRTUAL_SOCKET diff --git a/src/VirtualSocketLayer.cpp b/src/VirtualSocketLayer.cpp new file mode 100644 index 0000000..f814007 --- /dev/null +++ b/src/VirtualSocketLayer.cpp @@ -0,0 +1,1250 @@ +/* + * ZeroTier SDK - Network Virtualization Everywhere + * Copyright (C) 2011-2017 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 . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +/** + * @file + * + * VirtualSocket management layer + */ + +#include "libztDefs.h" + +#ifdef ZT_VIRTUAL_SOCKET + +#ifdef __linux__ +#include +#include +#include +#endif + +extern int errno; + +#include "libzt.h" +#include "VirtualTap.h" +#include "RingBuffer.h" +#include "VirtualSocket.h" +#include "VirtualSocketLayer.h" +#include "VirtualBindingPair.h" +#include "ZT1Service.h" +#include "Utilities.h" + +#ifdef STACK_LWIP +#include "lwIP.h" +//#include "lwip/sockets.h" +#endif + +#ifdef STACK_PICO +#include "picoTCP.h" +#include "pico_stack.h" +#include "pico_socket.h" +#endif + +#include +#include + +std::map*> fdmap; +std::map unmap; + +// externs from VirtualSocketLayer.h +//std::map unmap; +//std::map*> fdmap; + +// int socket_family, int socket_type, int protocol +int virt_socket(int socket_family, int socket_type, int protocol) { + DEBUG_EXTRA(); + int err = errno = 0; + if (socket_family < 0 || socket_type < 0 || protocol < 0) { + errno = EINVAL; + return -1; + } + if (socket_type == SOCK_SEQPACKET) { + DEBUG_ERROR("SOCK_SEQPACKET not yet supported."); + errno = EPROTONOSUPPORT; // seemingly closest match + return -1; + } + if (socket_type == SOCK_RAW) { + // VirtualSocket is only used to associate a socket with a VirtualTap, it has no other implication + VirtualSocket *vs = new VirtualSocket(); + vs->socket_family = socket_family; + vs->socket_type = socket_type; + vs->protocol = protocol; + add_unassigned_virt_socket(vs->app_fd, vs); + return vs->app_fd; + } +#if defined(STACK_PICO) + struct pico_socket *p; + err = rd_pico_socket(&p, socket_family, socket_type, protocol); + if (err == false && p) { + VirtualSocket *vs = new VirtualSocket(); + vs->socket_family = socket_family; + vs->socket_type = socket_type; + vs->pcb = p; + add_unassigned_virt_socket(vs->app_fd, vs); + err = vs->app_fd; // return one end of the socketpair + } + else { + DEBUG_ERROR("failed to create pico_socket"); + errno = ENOMEM; + err = -1; + } +#endif +#if defined(STACK_LWIP) + // TODO: check for max lwIP timers/sockets + void *pcb; + err = rd_lwip_socket(&pcb, socket_family, socket_type, protocol); + if (pcb) { + VirtualSocket *vs = new VirtualSocket(); + vs->socket_family = socket_family; + vs->socket_type = socket_type; + vs->pcb = pcb; + add_unassigned_virt_socket(vs->app_fd, vs); + // return one end of the socketpair for the app to use + err = vs->app_fd; + } + else { + DEBUG_ERROR("failed to create lwip pcb"); + errno = ENOMEM; + err = -1; + } +#endif + +//#if defined(DEFAULT_VS_LINGER) + /* + if (socket_type == SOCK_STREAM) { + linger lin; + unsigned int y=sizeof(lin); + lin.l_onoff=1; + lin.l_linger=10; + int fd = err; + if ((err = virt_setsockopt(fd, SOL_SOCKET, SO_LINGER, (void*)(&lin), y)) < 0) { + DEBUG_ERROR("error while setting default linger time on socket"); + errno = -1; // TODO + return -1; + } + err = fd; + } + */ +//#endif + return err; +} + +int virt_connect(int fd, const struct sockaddr *addr, socklen_t addrlen) { + DEBUG_INFO("fd=%d",fd); + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + DEBUG_ERROR("invalid socket, unable to locate VirtualSocket for fd=%d", fd); + errno = EBADF; + return -1; + } + struct pico_socket *ps = (struct pico_socket*)(vs->pcb); + if (addr == NULL) { + DEBUG_ERROR("invalid address for fd=%d", fd); + errno = EINVAL; + return -1; + } + if (addrlen <= 0) { + DEBUG_ERROR("invalid address length for fd=%d", fd); + errno = EINVAL; + return -1; + } + // TODO: Handle bad address lengths, right now this call will still + // succeed with a complete connect despite a bad address length. + // DEBUG_EXTRA("fd = %d, %s : %d", fd, ipstr, ntohs(port)); + ZeroTier::InetAddress inet; + sockaddr2inet(vs->socket_family, addr, &inet); + VirtualTap *tap = getTapByAddr(&inet); + if (tap == NULL) { + DEBUG_ERROR("no route to host, could not find appropriate VirtualTap for fd=%d", fd); + errno = ENETUNREACH; + return -1; + } + +#if defined(STACK_PICO) + // pointer to virtual tap we use in callbacks from the stack + + ps->priv = new VirtualBindingPair(tap, vs); +#endif +#if defined(STACK_LWIP) +#endif + + if ((err = tap->Connect(vs, addr, addrlen)) < 0) { + DEBUG_ERROR("error while connecting socket"); + // errno will be set by tap->Connect + return -1; + } + // assign this VirtualSocket to the tap we decided on + tap->_VirtualSockets.push_back(vs); + vs->tap = tap; + vs->sock = tap->_phy.wrapSocket(vs->sdk_fd, vs); + + // TODO: Consolidate these calls + del_unassigned_virt_socket(fd); + add_assigned_virt_socket(tap, vs, fd); + + // save peer addr, for calls like getpeername + memcpy(&(vs->peer_addr), addr, sizeof(vs->peer_addr)); + + // Below will simulate BLOCKING/NON-BLOCKING behaviour + + // 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 VirtualSocket in a VS_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 + + int f_err, blocking = 1; + if ((f_err = fcntl(fd, F_GETFL, 0)) < 0) { + DEBUG_ERROR("fcntl error, err=%s, errno=%d", f_err, errno); + // errno will be set by fcntl + return -1; + } + blocking = !(f_err & O_NONBLOCK); + if (blocking == false) { + errno = EINPROGRESS; // can't connect immediately + err = -1; + } + if (blocking == true) { + bool complete = false; + while (true) { + // FIXME: locking and unlocking so often might cause significant performance overhead while outgoing VirtualSockets + // are being established (also applies to accept()) + nanosleep((const struct timespec[]) {{0, (ZT_CONNECT_RECHECK_DELAY * 1000000)}}, NULL); + tap->_tcpconns_m.lock(); + for (int i=0; i_VirtualSockets.size(); i++) { +#if defined(STACK_PICO) + DEBUG_EXTRA("checking tap->_VirtualSockets[i]=%p", tap->_VirtualSockets[i]); + if (tap->_VirtualSockets[i]->get_state() == PICO_ERR_ECONNRESET) { + errno = ECONNRESET; + DEBUG_ERROR("ECONNRESET"); + err = -1; + } +#endif +#if defined(STACK_LWIP) +#endif + if (tap->_VirtualSockets[i]->get_state() == VS_STATE_UNHANDLED_CONNECTED) { + tap->_VirtualSockets[i]->set_state(VS_STATE_CONNECTED); + complete = true; + } + } + tap->_tcpconns_m.unlock(); + if (complete) { + break; + } + } + } + return err; +} + +int virt_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + DEBUG_ERROR("no VirtualSocket for fd=%d", fd); + errno = ENOTSOCK; + return -1; + } + struct pico_socket *ps = (struct pico_socket*)(vs->pcb); + // detect local interface binds + VirtualTap *tap = NULL; + if (vs->socket_family == AF_INET) { + struct sockaddr_in *in4 = (struct sockaddr_in *)addr; + if (in4->sin_addr.s_addr == INADDR_ANY) { + DEBUG_EXTRA("AF_INET, INADDR_ANY, binding to all interfaces"); + // grab first vtap + if (vtaps.size()) { + tap = (VirtualTap*)(vtaps[0]); // pick any vtap + } + } + if (in4->sin_addr.s_addr == 0x7f000001) { + DEBUG_EXTRA("127.0.0.1, will bind to appropriate vtap when connection is inbound"); + if (vtaps.size()) { + tap = (VirtualTap*)(vtaps[0]); // pick any vtap + } + } + } + if (vs->socket_family == AF_INET6) { + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr; + if (memcmp((void*)&(in6->sin6_addr), (void*)&(in6addr_any), sizeof(in6addr_any)) == 0) { + DEBUG_EXTRA("AF_INET6, in6addr_any, binding to all interfaces"); + if (vtaps.size()) { + tap = (VirtualTap*)(vtaps[0]); // pick any vtap + } + } + } + + ZeroTier::InetAddress inet; + sockaddr2inet(vs->socket_family, addr, &inet); + if (tap == NULL) { + tap = getTapByAddr(&inet); + } + if (tap == NULL) { + DEBUG_ERROR("no matching interface to bind to, could not find appropriate VirtualTap for fd=%d", fd); + errno = ENETUNREACH; + return -1; + } +#if defined(STACK_PICO) + // used in callbacks from network stack + ps->priv = new VirtualBindingPair(tap, vs); +#endif + tap->addVirtualSocket(vs); + err = tap->Bind(vs, addr, addrlen); + if (err == 0) { // success + vs->tap = tap; + del_unassigned_virt_socket(fd); + add_assigned_virt_socket(tap, vs, fd); + } + return err; +} + +int virt_listen(int fd, int backlog) { + DEBUG_EXTRA("fd=%d", fd); + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + DEBUG_ERROR("!vs"); + return -1; + } + //std::pair *p = get_assigned_virtual_pair(fd); + _multiplexer_lock.lock(); + VirtualTap *tap = vs->tap; + if (tap == NULL || vs == NULL) { + DEBUG_ERROR("unable to locate tap interface for file descriptor"); + errno = EBADF; + return -1; + } + backlog = backlog > 128 ? 128 : backlog; // See: /proc/sys/net/core/somaxconn + err = tap->Listen(vs, backlog); + vs->set_state(VS_STATE_LISTENING); + _multiplexer_lock.unlock(); + return err; +} + +int virt_accept(int fd, struct sockaddr *addr, socklen_t *addrlen) { + int err = errno = 0; + DEBUG_EXTRA("fd=%d", fd); + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + if (addr && *addrlen <= 0) { + DEBUG_ERROR("invalid address length given"); + errno = EINVAL; // TODO, not actually a valid error for this function + return -1; + } + if (virt_can_provision_new_socket(SOCK_STREAM) == false) { + DEBUG_ERROR("cannot provision additional socket due to limitation of network stack"); + errno = EMFILE; + return -1; + } + + //std::pair *p = get_assigned_virtual_pair(fd); + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + DEBUG_ERROR("!vs"); + return -1; + } + _multiplexer_lock.lock(); + //std::pair *p = get_assigned_virtual_pair(fd); + VirtualTap *tap = vs->tap; + // BLOCKING: loop and keep checking until we find a newly accepted VirtualSocket + int f_err, blocking = 1; + if ((f_err = fcntl(fd, F_GETFL, 0)) < 0) { + DEBUG_ERROR("fcntl error, err = %s, errno = %d", f_err, errno); + err = -1; + } + else { + blocking = !(f_err & O_NONBLOCK); + } + VirtualSocket *accepted_vs; + if (err == false) { + if (blocking == false) { // non-blocking + DEBUG_EXTRA("EWOULDBLOCK, assuming non-blocking mode"); + errno = EWOULDBLOCK; + err = -1; + accepted_vs = tap->Accept(vs); + } + else { // blocking + while (true) { + nanosleep((const struct timespec[]) {{0, (ZT_ACCEPT_RECHECK_DELAY * 1000000)}}, NULL); + accepted_vs = tap->Accept(vs); + if (accepted_vs) + break; // accepted fd = err + } + } + if (accepted_vs) { + add_assigned_virt_socket(tap, accepted_vs, accepted_vs->app_fd); + err = accepted_vs->app_fd; + } + } + if (err > 0) { + if (addr && *addrlen) { + *addrlen = *addrlen < sizeof(accepted_vs->peer_addr) ? *addrlen : sizeof(accepted_vs->peer_addr); + // copy address into provided address buffer and len buffer + memcpy(addr, &(accepted_vs->peer_addr), *addrlen); + } + } + return err; +} + +#if defined(__linux__) +int virt_accept4(int fd, struct sockaddr *addr, socklen_t *addrlen, int flags) +{ + errno = 0; + //DEBUG_INFO("fd=%d", fd); + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + if (flags & SOCK_NONBLOCK) { + fcntl(fd, F_SETFL, O_NONBLOCK); + } + if (flags & SOCK_CLOEXEC) { + fcntl(fd, F_SETFL, FD_CLOEXEC); + } + addrlen = !addr ? 0 : addrlen; + return virt_accept(fd, addr, addrlen); +} +#endif + +int virt_setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) +{ + DEBUG_EXTRA("fd=%d, level=%d, optname=%d", fd, level, optname); + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + DEBUG_ERROR("invalid fd=%d", fd); + errno = EBADF; + return -1; + } +#if defined(STACK_PICO) + err = rd_pico_setsockopt(vs, level, optname, optval, optlen); +#endif +#if defined(STACK_LWIP) + err = rd_lwip_setsockopt(vs, level, optname, optval, optlen); +#endif + return err; +} + +int virt_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen) +{ + DEBUG_EXTRA("fd=%d, level=%d, optname=%d", fd, level, optname); + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + DEBUG_ERROR("invalid fd=%d", fd); + errno = EBADF; + return -1; + } +#if defined(STACK_PICO) + err = rd_pico_getsockopt(vs, level, optname, optval, optlen); +#endif +#if defined(STACK_LWIP) + err = rd_lwip_getsockopt(vs, level, optname, optval, optlen); +#endif + return err; +} + +int virt_getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen) +{ + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + // TODO + return err; +} + +int virt_getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen) +{ + DEBUG_INFO("fd=%d", fd); + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + errno = ENOTCONN; + return -1; + } + memcpy(addr, &(vs->peer_addr), sizeof(struct sockaddr_storage)); + return err; +} + +int virt_gethostname(char *name, size_t len) +{ + errno = 0; + return gethostname(name, len); +} + +int virt_sethostname(const char *name, size_t len) +{ + errno = 0; + return sethostname(name, len); +} + +int virt_close(int fd) +{ + DEBUG_EXTRA("fd=%d", fd); + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + DEBUG_ERROR("no vs found for fd=%d", fd); + errno = EBADF; + return -1; + } + del_virt_socket(fd); + if (vs->tap) { + vs->tap->Close(vs); + } + delete vs; + vs = NULL; + return err; +} + +/* +int virt_poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + errno = 0; + return poll(fds, nfds, timeout); +} +*/ + +int virt_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) +{ + errno = 0; + return select(nfds, readfds, writefds, exceptfds, timeout); +} + +int virt_fcntl(int fd, int cmd, int flags) +{ + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + DEBUG_ERROR("invalid vs for fd=%d", fd); + errno = EBADF; + return -1; + } + err = fcntl(fd, cmd, flags); + return err; +} + +int virt_ioctl(int fd, unsigned long request, void *argp) +{ + int err = errno = -1; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + else { +#if defined(__linux__) + /* + if (argp) + { + struct ifreq *ifr = (struct ifreq *)argp; + VirtualTap *tap = getTapByName(ifr->ifr_name); + if (tap == NULL) { + DEBUG_ERROR("unable to locate tap interface with that name"); + err = -1; + errno = EINVAL; + } + // index of VirtualTap interface + if (request == SIOCGIFINDEX) { + ifr->ifr_ifindex = tap->ifindex; + err = 0; + } + // MAC addres or VirtualTap + if (request == SIOCGIFHWADDR) { + tap->_mac.copyTo(&(ifr->ifr_hwaddr.sa_data), sizeof(ifr->ifr_hwaddr.sa_data)); + err = 0; + } + // IP address of VirtualTap + if (request == SIOCGIFADDR) { + struct sockaddr_in *in4 = (struct sockaddr_in *)&(ifr->ifr_addr); + memcpy(&(in4->sin_addr.s_addr), tap->_ips[0].rawIpData(), sizeof(ifr->ifr_addr)); + err = 0; + } + } + else { + DEBUG_INFO("!argp"); + } + */ +#else + // err = ioctl(fd, request, argp); + err = -1; +#endif + } + return err; +} + +ssize_t virt_sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen) +{ + //DEBUG_TRANS("fd=%d", fd); + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + if (len == 0) { + return 0; + } + if (len > ZT_SOCKET_MSG_BUF_SZ) { + DEBUG_ERROR("msg is too long to be sent atomically (len=%d)", len); + errno = EMSGSIZE; + return -1; + } + if (buf == NULL) { + DEBUG_ERROR("msg buf is null"); + errno = EINVAL; + return -1; + } + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + DEBUG_ERROR("no vs found for fd=%x", fd); + errno = EBADF; + return -1; + } + ZeroTier::InetAddress iaddr; + VirtualTap *tap; + + if (vs->socket_type == SOCK_DGRAM) { + if (vs->socket_family == AF_INET) + { + char ipstr[INET_ADDRSTRLEN]; + memset(ipstr, 0, INET_ADDRSTRLEN); + inet_ntop(AF_INET, + (const void *)&((struct sockaddr_in *)addr)->sin_addr.s_addr, ipstr, INET_ADDRSTRLEN); + iaddr.fromString(ipstr); + } + if (vs->socket_family == AF_INET6) + { + char ipstr[INET6_ADDRSTRLEN]; + memset(ipstr, 0, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, + (const void *)&((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, ipstr, INET6_ADDRSTRLEN); + // TODO: This is a hack, determine a proper way to do this + char addrstr[INET6_ADDRSTRLEN]; + sprintf(addrstr, "%s%s", ipstr, std::string("/40").c_str()); + iaddr.fromString(addrstr); + } + // get tap + tap = getTapByAddr(&iaddr); + if (tap == NULL) { + DEBUG_INFO("SOCK_DGRAM, tap not found"); + errno = EDESTADDRREQ; // TODO: double check this is the best errno to report + return -1; + } + // write + if ((err = tap->SendTo(vs, buf, len, flags, addr, addrlen)) < 0) { + DEBUG_ERROR("error while attempting to sendto"); + errno = EINVAL; // TODO: Not correct, but what else could we use? + } + } + if (vs->socket_type == SOCK_RAW) + { + struct sockaddr_ll *socket_address = (struct sockaddr_ll *)addr; + VirtualTap *tap = getTapByIndex(socket_address->sll_ifindex); + if (tap) { + DEBUG_INFO("found interface of ifindex=%d", tap->ifindex); + err = tap->Write(vs, (void*)buf, len); + } + else { + DEBUG_ERROR("unable to locate tap of ifindex=%d", socket_address->sll_ifindex); + err = -1; + errno = EINVAL; + } + } + return err; +} + +ssize_t virt_send(int fd, const void *buf, size_t len, int flags) +{ + // DEBUG_TRANS("fd=%d", fd); + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + if (len == 0) { + return 0; + } + if (len > ZT_SOCKET_MSG_BUF_SZ) { + DEBUG_ERROR("msg is too long to be sent atomically (len=%d)", len); + errno = EMSGSIZE; + return -1; + } + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + DEBUG_ERROR("invalid vs for fd=%d", fd); + errno = EBADF; + return -1; + } + if (vs->socket_type != SOCK_STREAM) { + DEBUG_ERROR("the socket is not connection-mode, and no peer address is set for fd=%d", fd); + errno = EDESTADDRREQ; + return -1; + } + if (flags & MSG_DONTROUTE) { + DEBUG_INFO("MSG_DONTROUTE not implemented yet"); + errno = EINVAL; + return -1; + } + if (flags & MSG_DONTWAIT) { + // The stack drivers and stack are inherently non-blocking by design, but we + // still need to modify the unix pipe connecting them to the application: + fcntl(fd, F_SETFL, O_NONBLOCK); + } + if (flags & MSG_EOR) { + DEBUG_INFO("MSG_EOR not implemented yet"); + errno = EINVAL; + return -1; + } + if (flags & MSG_OOB) { + DEBUG_INFO("MSG_OOB not implemented yet"); + errno = EINVAL; + return -1; + } +#if defined(__linux__) + if (flags & MSG_CONFIRM) { + DEBUG_INFO("MSG_CONFIRM not implemented yet"); + errno = EINVAL; + return -1; + } + if (flags & MSG_MORE) { + DEBUG_INFO("MSG_MORE not implemented yet"); + errno = EINVAL; + return -1; + } + if (flags & MSG_NOSIGNAL) { + DEBUG_INFO("MSG_NOSIGNAL not implemented yet"); + errno = EINVAL; + return -1; + } +#endif + + err = write(fd, buf, len); + // restore "per-call" flags + + if (flags & MSG_DONTWAIT) { + int saved_flags = fcntl(fd, F_GETFL); + if (fcntl(fd, F_SETFL, saved_flags & ~O_NONBLOCK) < 0) { // mask out the blocking flag + return -1; + } + } + return err; +} + +// TODO +ssize_t virt_sendmsg(int fd, const struct msghdr *msg, int flags) +{ + DEBUG_TRANS("fd=%d", fd); + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + else { + err = sendmsg(fd, msg, flags); + } + return err; +} + +ssize_t virt_recv(int fd, void *buf, size_t len, int flags) +{ + DEBUG_TRANS("fd=%d", fd); + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + DEBUG_ERROR("invalid vs for fd=%d", fd); + errno = EBADF; + return -1; + } + if (vs->socket_type != SOCK_STREAM) { + DEBUG_ERROR("the socket is not connection-mode, and no peer address is set for fd=%d", fd); + errno = EDESTADDRREQ; + return -1; + } + if (vs->get_state() != VS_STATE_CONNECTED) { + DEBUG_ERROR("the socket is not in a connected state, fd=%d", fd); + errno = ENOTCONN; + return -1; + } + if (flags & MSG_DONTWAIT) { + // The stack drivers and stack are inherently non-blocking by design, but we + // still need to modify the unix pipe connecting them to the application: + fcntl(fd, F_SETFL, O_NONBLOCK); + } + if (flags & MSG_OOB) { + DEBUG_INFO("MSG_OOB not implemented yet"); + errno = EINVAL; + return -1; + } + if (flags & MSG_TRUNC) { + DEBUG_INFO("MSG_TRUNC not implemented yet"); + errno = EINVAL; + return -1; + } + if (flags & MSG_WAITALL) { + DEBUG_INFO("MSG_WAITALL not implemented yet"); + errno = EINVAL; + return -1; + } +#if defined(__linux__) + if (flags & MSG_ERRQUEUE) { + DEBUG_INFO("MSG_ERRQUEUE not implemented yet"); + errno = EINVAL; + return -1; + } +#endif + + if (flags & MSG_PEEK) { + // MSG_PEEK doesn't require any special stack-related machinery so we can just + // pass it to a regular recv() call with no issue + err = recv(fd, buf, len, MSG_PEEK); + } + + // restore "per-call" flags + + if (flags & MSG_DONTWAIT) { + int saved_flags = fcntl(fd, F_GETFL); + if (fcntl(fd, F_SETFL, saved_flags & ~O_NONBLOCK) < 0) { // mask out the blocking flag + return -1; + } + } + return err; +} + +ssize_t virt_recvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen) +{ + //DEBUG_TRANS("fd=%d", fd); + int32_t r = 0; + errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + if (len == 0) { + return 0; + } + if (buf == NULL) { + DEBUG_ERROR("buf is null"); + errno = EINVAL; + return -1; + } + char udp_msg_buf[ZT_SOCKET_MSG_BUF_SZ]; + char *msg_ptr = udp_msg_buf; + memset(msg_ptr, 0, sizeof(int32_t)); // zero only len portion + + int32_t udp_msg_len = 0; + + // PEEK at the buffer and see if we can read a length, if not, err out + r = recv(fd, msg_ptr, sizeof(int32_t), MSG_PEEK); + if (r != sizeof(int32_t)) { + errno = EIO; // TODO: test for this + return -1; + } + // read of sizeof(int32_t) for the length of the datagram (including address) + r = read(fd, msg_ptr, sizeof(int32_t)); + // copy to length variable + memcpy(&udp_msg_len, msg_ptr, sizeof(int32_t)); + msg_ptr+=sizeof(int32_t); + + if (udp_msg_len <= 0) { + DEBUG_ERROR("invalid datagram"); + errno = EIO; // TODO: test for this + return -1; + } + // there is a datagram to read, so let's read it + // zero remainder of buffer + memset(msg_ptr, 0, ZT_SOCKET_MSG_BUF_SZ- sizeof(int32_t)); + if ((r = read(fd, msg_ptr, udp_msg_len)) < 0) { + DEBUG_ERROR("invalid datagram"); + errno = EIO; // TODO: test for this + return -1; + } + // get address + if (addr) { + if (*addrlen < sizeof(struct sockaddr_storage)) { + DEBUG_ERROR("invalid address length provided"); + errno = EINVAL; + return -1; + } + *addrlen = sizeof(struct sockaddr_storage); + memcpy(addr, msg_ptr, *addrlen); + } + msg_ptr+=sizeof(struct sockaddr_storage); + // get payload + int32_t payload_sz = udp_msg_len - *addrlen; + int32_t write_sz = len < payload_sz ? len : payload_sz; + memcpy(buf, msg_ptr, write_sz); + return write_sz; +} + +// TODO +ssize_t virt_recvmsg(int fd, struct msghdr *msg,int flags) +{ + //DEBUG_TRANS("fd=%d", fd); + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + else { + err = recvmsg(fd, msg, flags); + } + return err; +} + +int virt_read(int fd, void *buf, size_t len) { + DEBUG_TRANS("fd=%d", fd); + errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + DEBUG_ERROR("invalid vs for fd=%d", fd); + errno = EBADF; + return -1; + } + return read(fd, buf, len); +} + +int virt_write(int fd, const void *buf, size_t len) { + DEBUG_TRANS("fd=%d", fd); + errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + DEBUG_ERROR("invalid vs for fd=%d", fd); + errno = EBADF; + return -1; + } + return write(fd, buf, len); +} + +int virt_shutdown(int fd, int how) +{ + int err = errno = 0; + if (fd < 0 || fd >= ZT_MAX_SOCKETS) { + errno = EBADF; + return -1; + } + if (how != SHUT_RD && how != SHUT_WR && how != SHUT_RDWR) { + errno = EINVAL; + return -1; + } + VirtualSocket *vs = get_virt_socket(fd); + if (vs == NULL) { + DEBUG_ERROR("invalid vs for fd=%d", fd); + errno = EBADF; + return -1; + } + if (vs->get_state() != VS_STATE_CONNECTED || vs->socket_type != SOCK_STREAM) { + DEBUG_ERROR("the socket is either not in a connected state, or isn't connection-based, fd=%d", fd); + errno = ENOTCONN; + return -1; + } + if (vs->tap) { + err = vs->tap->Shutdown(vs, how); + } + return err; +} + +/****************************************************************************/ +/* Socket API Helper functions --- DON'T CALL THESE DIRECTLY */ +/****************************************************************************/ + +bool virt_can_provision_new_socket(int socket_type) +{ + int can = false; +#if defined(STACK_PICO) + return !(pico_ntimers()+1 > PICO_MAX_TIMERS); +#endif +#if defined(STACK_LWIP) + if (socket_type == SOCK_STREAM) { + return !(rd_lwip_num_current_tcp_pcbs()+1 > MEMP_NUM_TCP_PCB); + } + if (socket_type == SOCK_DGRAM) { + return !(rd_lwip_num_current_udp_pcbs()+1 > MEMP_NUM_UDP_PCB); + } + if (socket_type == SOCK_RAW) { + return !(rd_lwip_num_current_raw_pcbs()+1 > MEMP_NUM_RAW_PCB); + } + can = true; +#endif +#if defined(NO_STACK) + // always true since there's no network stack timer/memory limitation + can = true; +#endif + return can; +} + +int virt_num_active_sockets() +{ + _multiplexer_lock.lock(); + int num = unmap.size() + fdmap.size(); + _multiplexer_lock.unlock(); + return num; +} + +int virt_maxsockets(int socket_type) +{ + int max = 0; +#if defined(STACK_PICO) + // TODO: This is only an approximation + // TODO: distinquish by type + max = PICO_MAX_TIMERS - 10; +#endif +#if defined(STACK_LWIP) + if (socket_type == SOCK_STREAM) { + max = MEMP_NUM_TCP_PCB; + } + if (socket_type == SOCK_DGRAM) { + max = MEMP_NUM_UDP_PCB; + } +#endif +#if defined(NO_STACK) + // arbitrary +#if defined(__linux__) + max = RLIMIT_NOFILE; +#endif +#if defined(__APPLE__) + max = 1024; +#endif +#endif + return max; +} + +/****************************************************************************/ +/* VirtualSocket / VirtualTap helper functions - DON'T CALL THESE DIRECTLY */ +/****************************************************************************/ + +VirtualSocket *get_virt_socket(int fd) +{ + DEBUG_EXTRA("fd=%d", fd); + _multiplexer_lock.lock(); + // try to locate in unmapped set + VirtualSocket *vs = unmap[fd]; + if (vs == NULL) { + // if not, try to find in mapped set (bind to vtap has been performed) + std::pair *p = fdmap[fd]; + if (p) { + vs = p->first; + } + else { + DEBUG_ERROR("unable to locate virtual socket"); + } + } + _multiplexer_lock.unlock(); + return vs; +} + +int del_virt_socket(int fd) +{ + DEBUG_EXTRA("fd=%d", fd); + int err = 0; + _multiplexer_lock.lock(); + try { + std::map::iterator un_iter = unmap.find(fd); + if (un_iter != unmap.end()) { + unmap.erase(un_iter); + } + std::map*>::iterator fd_iter = fdmap.find(fd); + if (fd_iter != fdmap.end()) { + fdmap.erase(fd_iter); + } + } + catch( ... ) { + DEBUG_ERROR("unable to remove virtual socket"); + err = -1; + } + _multiplexer_lock.unlock(); + return err; +} + +int add_unassigned_virt_socket(int fd, VirtualSocket *vs) +{ + DEBUG_EXTRA("fd=%d, vs=%p", fd, vs); + int err = 0; + _multiplexer_lock.lock(); + try { + std::map::iterator un_iter = unmap.find(fd); + if (un_iter == unmap.end()) { + unmap[fd] = vs; + } + else { + DEBUG_ERROR("fd=%d already contained in map", fd); + } + } + catch( ... ) { + DEBUG_ERROR("unable to add virtual socket"); + err = -1; + } + _multiplexer_lock.unlock(); + return err; +} + +int del_unassigned_virt_socket(int fd) +{ + DEBUG_EXTRA("fd=%d", fd); + int err = 0; + _multiplexer_lock.lock(); + try { + std::map::iterator un_iter = unmap.find(fd); + if (un_iter != unmap.end()) { + unmap.erase(un_iter); + } + } + catch( ... ) { + DEBUG_ERROR("unable to remove virtual socket"); + err = -1; + } + _multiplexer_lock.unlock(); + return err; +} + +int add_assigned_virt_socket(void *tap_ptr, VirtualSocket *vs, int fd) +{ + VirtualTap *tap = (VirtualTap*)tap_ptr; + DEBUG_EXTRA("tap=%p, vs=%p, fd=%d", tap, vs, fd); + int err = 0; + _multiplexer_lock.lock(); + try { + std::map*>::iterator fd_iter; + fd_iter = fdmap.find(fd); + if (fd_iter == fdmap.end()) { + fdmap[fd] = new std::pair(vs, tap); + } + else { + DEBUG_ERROR("fd=%d already contained in > map", fd); + } + } + catch( ... ) { + DEBUG_ERROR("unable to add virtual socket"); + err = -1; + } + _multiplexer_lock.unlock(); + return err; +} + +int del_assigned_virt_socket(VirtualTap *tap, VirtualSocket *vs, int fd) +{ + DEBUG_EXTRA("tap=%p, vs=%p, fd=%d", tap, vs, fd); + int err = 0; + _multiplexer_lock.lock(); + try { + std::map*>::iterator fd_iter; + fd_iter = fdmap.find(fd); + if (fd_iter != fdmap.end()) { + fdmap.erase(fd_iter); + } + } + catch( ... ) { + DEBUG_ERROR("unable to remove virtual socket"); + err = -1; + } + _multiplexer_lock.unlock(); + return err; +} + +/* +std::pair *get_assigned_virtual_pair(int fd) +{ + _multiplexer_lock.lock(); + std::pair *p = fdmap[fd]; + _multiplexer_lock.unlock(); + return p; +} +*/ + +void disableTaps() +{ + _vtaps_lock.lock(); + for (int i=0; i_enabled = false; + } + _vtaps_lock.unlock(); +} + +void sockaddr2inet(int socket_family, const struct sockaddr *addr, ZeroTier::InetAddress *inet) +{ + char ipstr[INET6_ADDRSTRLEN]; + memset(ipstr, 0, INET6_ADDRSTRLEN); + if (socket_family == AF_INET) { + inet_ntop(AF_INET, + (const void *)&((struct sockaddr_in *)addr)->sin_addr.s_addr, ipstr, INET_ADDRSTRLEN); + inet->fromString(ipstr); + } + if (socket_family == AF_INET6) { + inet_ntop(AF_INET6, + (const void *)&((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, ipstr, INET6_ADDRSTRLEN); + char addrstr[64]; + sprintf(addrstr, "%s", ipstr); + inet->fromString(addrstr); + } +} + +#endif // ZT_VIRTUAL_SOCKET diff --git a/src/VirtualTap.cpp b/src/VirtualTap.cpp index 1ac169b..a7f53a5 100644 --- a/src/VirtualTap.cpp +++ b/src/VirtualTap.cpp @@ -1,478 +1,679 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 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 . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ +* ZeroTier SDK - Network Virtualization Everywhere +* Copyright (C) 2011-2017 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 . +* +* -- +* +* You can be released from the requirements of the license by purchasing +* a commercial license. Buying such a license is mandatory as soon as you +* develop commercial closed-source software that incorporates or links +* directly against ZeroTier software without disclosing the source code +* of your own application. +*/ /** - * @file - * - * Virtual Ethernet tap device - */ +* @file +* +* Virtual Ethernet tap device +*/ -#include +// Used by raw stack driver implementation +struct ip_addr_t; +typedef unsigned short u16_t; #include "Phy.hpp" -#include "VirtualTap.hpp" - -#include -#include -#include - +#include "VirtualTap.h" #include "libzt.h" - -#if defined(STACK_LWIP) -#include "lwIP.hpp" -#endif - -#if defined(__APPLE__) -#include -#endif -#if defined(__linux__) -#include -#endif - +#include "libztDebug.h" +#include "lwIP.h" +#include "picoTCP.h" #include "ZT1Service.h" +#include "VirtualSocket.h" +#include "VirtualSocketLayer.h" +#include "SysUtils.h" -#include "Utils.hpp" #include "Mutex.hpp" -#include "Constants.hpp" #include "InetAddress.hpp" #include "OneService.hpp" -namespace ZeroTier { +//class VirtualSocket; - int VirtualTap::devno = 0; +int VirtualTap::devno = 0; - /****************************************************************************/ - /* VirtualTap Service */ - /* - For each joined network a VirtualTap will be created to administer I/O */ - /* calls to the stack and the ZT virtual wire */ - /****************************************************************************/ +VirtualTap::VirtualTap( + 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), + _initialized(false), + _enabled(true), + _run(true), + _mac(mac), + _mtu(mtu), + _nwid(nwid), + _unixListenSocket((PhySocket *)0), + _phy(this,false,true) +{ + vtaps.push_back((void*)this); - VirtualTap::VirtualTap( - 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), - _initialized(false), - _enabled(true), - _run(true), - _mac(mac), - _mtu(mtu), - _nwid(nwid), - _unixListenSocket((PhySocket *)0), - _phy(this,false,true) - { - ZeroTier::vtaps.push_back((void*)this); - - // set virtual tap interface name (full) - memset(vtap_full_name, 0, sizeof(vtap_full_name)); - ifindex = devno; - snprintf(vtap_full_name, sizeof(vtap_full_name), "libzt%d-%lx", devno++, _nwid); - _dev = vtap_full_name; - DEBUG_INFO("set VirtualTap interface name to: %s", _dev.c_str()); - // set virtual tap interface name (abbreviated) - memset(vtap_abbr_name, 0, sizeof(vtap_abbr_name)); - snprintf(vtap_abbr_name, sizeof(vtap_abbr_name), "libzt%d", devno); + // set virtual tap interface name (full) + memset(vtap_full_name, 0, sizeof(vtap_full_name)); + ifindex = devno; + snprintf(vtap_full_name, sizeof(vtap_full_name), "libzt%d-%lx", devno++, _nwid); + _dev = vtap_full_name; + DEBUG_INFO("set VirtualTap interface name to: %s", _dev.c_str()); + // set virtual tap interface name (abbreviated) + memset(vtap_abbr_name, 0, sizeof(vtap_abbr_name)); + snprintf(vtap_abbr_name, sizeof(vtap_abbr_name), "libzt%d", devno); #if defined(STACK_LWIP) - // initialize network stacks - lwip_driver_init(); + // initialize network stacks + lwip_driver_init(); #endif - // start vtap thread and stack I/O loops - _thread = Thread::start(this); - } + // start vtap thread and stack I/O loops + _thread = Thread::start(this); +} - VirtualTap::~VirtualTap() - { - _run = false; - _phy.whack(); - Thread::join(_thread); - _phy.close(_unixListenSocket,false); - } +VirtualTap::~VirtualTap() +{ + _run = false; + _phy.whack(); + Thread::join(_thread); + _phy.close(_unixListenSocket,false); +} - void VirtualTap::setEnabled(bool en) - { - _enabled = en; - } +void VirtualTap::setEnabled(bool en) +{ + _enabled = en; +} - bool VirtualTap::enabled() const - { - return _enabled; - } +bool VirtualTap::enabled() const +{ + return _enabled; +} - bool VirtualTap::registerIpWithStack(const InetAddress &ip) - { +bool VirtualTap::registerIpWithStack(const InetAddress &ip) +{ #if defined(STACK_LWIP) - lwip_init_interface((void*)this, this->_mac, ip); + lwip_init_interface((void*)this, this->_mac, ip); #endif +#if defined(STACK_PICO) + pico_register_address(this, ip); +#endif + return true; +} + +bool VirtualTap::addIp(const InetAddress &ip) +{ +#if defined(NO_STACK) + char ipbuf[INET6_ADDRSTRLEN]; + DEBUG_INFO("addIp (%s)", ip.toString(ipbuf)); + _ips.push_back(ip); + std::sort(_ips.begin(),_ips.end()); + return true; +#endif +#if defined(STACK_PICO) || defined(STACK_LWIP) + char ipbuf[INET6_ADDRSTRLEN]; + DEBUG_EXTRA("addr=%s", ip.toString(ipbuf)); + Mutex::Lock _l(_ips_m); + if (registerIpWithStack(ip)) { + if (std::find(_ips.begin(),_ips.end(),ip) == _ips.end()) { + _ips.push_back(ip); + std::sort(_ips.begin(),_ips.end()); + } return true; } + return false; +#endif +} - bool VirtualTap::addIp(const InetAddress &ip) - { - char ipbuf[INET6_ADDRSTRLEN]; - DEBUG_EXTRA("addr=%s", ip.toString(ipbuf)); - Mutex::Lock _l(_ips_m); - if (registerIpWithStack(ip)) { - if (std::find(_ips.begin(),_ips.end(),ip) == _ips.end()) { - _ips.push_back(ip); - std::sort(_ips.begin(),_ips.end()); +bool VirtualTap::removeIp(const InetAddress &ip) +{ + DEBUG_EXTRA(); + 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; +} + +std::vector VirtualTap::ips() const +{ + Mutex::Lock _l(_ips_m); + return _ips; +} + +void VirtualTap::put(const MAC &from,const MAC &to,unsigned int etherType, + const void *data,unsigned int len) +{ +#if defined(STACK_LWIP) + lwip_eth_rx(this, from, to, etherType, data, len); +#endif +#if defined(STACK_PICO) + rd_pico_eth_rx(this,from,to,etherType,data,len); +#endif +} + +std::string VirtualTap::deviceName() const +{ + return _dev; +} + +std::string VirtualTap::nodeId() const +{ + if (zt1ServiceRef) { + char id[ZTO_ID_LEN]; + memset(id, 0, sizeof(id)); + sprintf(id, "%lx",((ZeroTier::OneService *)zt1ServiceRef)->getNode()->address()); + return std::string(id); + } + else { + return std::string("----------"); + } +} + +void VirtualTap::setFriendlyName(const char *friendlyName) +{ + DEBUG_EXTRA("%s", friendlyName); +} + +void VirtualTap::scanMulticastGroups(std::vector &added, + std::vector &removed) +{ + std::vector newGroups; + Mutex::Lock _l(_multicastGroups_m); + // TODO: get multicast subscriptions + 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 VirtualTap::setMtu(unsigned int mtu) +{ + _mtu = mtu; +} + +void VirtualTap::threadMain() + throw() +{ +#if defined(STACK_LWIP) && !defined(LIBZT_RAW) + while (true) { + _phy.poll(ZT_PHY_POLL_INTERVAL); + Housekeeping(); + } +#endif +#if defined(STACK_LWIP) && defined(LIBZT_RAW) + rd_lwip_loop(this); // use driver loop +#endif +#if defined(STACK_PICO) + pico_init_interface(this); + if (this->should_start_stack) { + rd_pico_loop(this); // use driver loop + } +#endif +} + +void VirtualTap::phyOnUnixClose(PhySocket *sock, void **uptr) +{ + DEBUG_EXTRA(); +} + +void VirtualTap::phyOnUnixData(PhySocket *sock, void **uptr, void *data, ssize_t len) +{ + DEBUG_EXTRA(); +#if defined(LIBZT_RAW) + VirtualSocket *vs = (VirtualSocket*)*uptr; + if (vs == NULL) { + return; + } + if (len > 0) { + Write(vs, data, len); + } +#endif +} + +void VirtualTap::phyOnUnixWritable(PhySocket *sock, void **uptr, bool stack_invoked) +{ + DEBUG_EXTRA(); +#if defined(LIBZT_RAW) + if (sock) { + Read(sock,uptr,stack_invoked); + } else { + DEBUG_ERROR("!sock"); + } +#endif +} + +bool VirtualTap::routeAdd(const InetAddress &ip, const InetAddress &nm, const InetAddress &gw) +{ + bool err = false; + DEBUG_EXTRA(); +#if defined(STACK_LWIP) + // general_lwip_init_interface(this, NULL, "n1", _mac, ip, nm, gw); + // general_turn_on_interface(NULL); + return true; +#endif +#if defined(STACK_PICO) + return rd_pico_route_add(this, ip, nm, gw, 0); +#endif +#if defined(NO_STACK) + // nothing to do +#endif + return err; +} + +bool VirtualTap::routeDelete(const InetAddress &ip, const InetAddress &nm) +{ + bool err = false; + DEBUG_EXTRA(); +#if defined(STACK_LWIP) + // general_lwip_init_interface(this, NULL, "n1", _mac, ip, nm, gw); + // general_turn_on_interface(NULL); + return true; +#endif +#if defined(STACK_PICO) + return rd_pico_route_del(this, ip, nm, 0); +#endif +#if defined(NO_STACK) + // nothing to do +#endif + return err; +} + +void VirtualTap::addVirtualSocket(VirtualSocket *vs) +{ +#if defined(LIBZT_RAW) + DEBUG_EXTRA(); + Mutex::Lock _l(_tcpconns_m); + _VirtualSockets.push_back(vs); +#endif +} + +void VirtualTap::removeVirtualSocket() +{ +#if defined(LIBZT_RAW) + DEBUG_EXTRA(); + Mutex::Lock _l(_tcpconns_m); + for (int i=0; i<_VirtualSockets.size(); i++) { + if (vs == _VirtualSockets[i]) { + _VirtualSockets.erase(_VirtualSockets.begin() + i); + break; + } + } +#endif +} + +/****************************************************************************/ +/* DNS */ +/****************************************************************************/ + +int VirtualTap::add_DNS_Nameserver(struct sockaddr *addr) +{ + int err = -1; +#if defined(STACK_LWIP) && defined(LIBZT_RAW) + err = rd_lwip_add_dns_nameserver(addr); +#endif +#if defined(STACK_PICO) + rd_pico_add_dns_nameserver(addr); +#endif + return err; +} + +int VirtualTap::del_DNS_Nameserver(struct sockaddr *addr) +{ + int err = -1; +#if defined(STACK_LWIP) && defined(LIBZT_RAW) + err = rd_lwip_del_dns_nameserver(addr); +#endif +#if defined(STACK_PICO) + err = rd_pico_del_dns_nameserver(addr); +#endif + return err; +} + +/****************************************************************************/ +/* SDK Socket API */ +/****************************************************************************/ + +// Connect +int VirtualTap::Connect(VirtualSocket *vs, const struct sockaddr *addr, socklen_t addrlen) +{ +#if !defined(LIBZT_RAW) + return -1; +#endif +#if defined(NO_STACK) + return -1; +#endif + int err = -1; +#if defined(STACK_LWIP) && defined(LIBZT_RAW) + err = rd_lwip_connect(vs, addr, addrlen); +#endif +#if defined(STACK_PICO) + Mutex::Lock _l(_tcpconns_m); + err = rd_pico_connect(vs, addr, addrlen); +#endif + return err; +} + +// Bind VirtualSocket to a network stack's interface +int VirtualTap::Bind(VirtualSocket *vs, const struct sockaddr *addr, socklen_t addrlen) +{ +#if !defined(LIBZT_RAW) + return -1; +#endif +#if defined(NO_STACK) + return -1; +#endif +#if defined(STACK_LWIP) && defined(LIBZT_RAW) + Mutex::Lock _l(_tcpconns_m); + return rd_lwip_bind(this, vs, addr, addrlen); +#endif +#if defined(STACK_PICO) + Mutex::Lock _l(_tcpconns_m); + return rd_pico_bind(vs, addr, addrlen); +#endif + return -1; +} + +// Listen for an incoming VirtualSocket +int VirtualTap::Listen(VirtualSocket *vs, int backlog) +{ +#if !defined(LIBZT_RAW) + return -1; +#endif +#if defined(NO_STACK) + return -1; +#endif + int err = -1; +#if defined(STACK_LWIP) && defined(LIBZT_RAW) + Mutex::Lock _l(_tcpconns_m); + err = rd_lwip_listen(vs, backlog); +#endif +#if defined(STACK_PICO) + Mutex::Lock _l(_tcpconns_m); + return rd_pico_listen(vs, backlog); +#endif + return err; +} + +// Accept a VirtualSocket +VirtualSocket *VirtualTap::Accept(VirtualSocket *vs) +{ +#if !defined(LIBZT_RAW) + return NULL; +#endif + VirtualSocket *new_vs = NULL; +#if defined(NO_STACK) + new_vs = NULL; +#endif +#if defined(STACK_LWIP) && defined(LIBZT_RAW) + Mutex::Lock _l(_tcpconns_m); + new_vs = rd_lwip_accept(vs); +#endif +#if defined(STACK_PICO) + // TODO: separation of church and state + Mutex::Lock _l(_tcpconns_m); + new_vs = rd_pico_accept(vs); +#endif + return new_vs; +} + +// Read from stack/buffers into the app's socket +int VirtualTap::Read(VirtualSocket *vs, PhySocket *sock, void **uptr, bool stack_invoked) +{ +#if !defined(LIBZT_RAW) + return -1; +#endif +#if defined(NO_STACK) + return -1; +#endif + int err = -1; +#if defined(STACK_LWIP) && defined(LIBZT_RAW) + err = rd_lwip_read((VirtualSocket*)*(_phy.getuptr(sock)), stack_invoked); +#endif +#if defined(STACK_PICO) + err = rd_pico_read(this, sock, (VirtualSocket*)uptr, stack_invoked); +#endif + return err; +} + +// Write data from app socket to the virtual wire, either raw over VL2, or via network stack +int VirtualTap::Write(VirtualSocket *vs, void *data, ssize_t len) +{ +#if !defined(LIBZT_RAW) + return -1; +#endif +#if defined(NO_STACK) + return -1; +#endif + DEBUG_EXTRA("vs=%p, fd=%d, data=%p, len=%d", vs, vs->app_fd, data, len); + int err = -1; +#if defined(LIBZT_RAW) + // VL2, SOCK_RAW, no network stack + if (vs->socket_type == SOCK_RAW) { + struct ether_header *eh = (struct ether_header *) data; + MAC src_mac; + MAC dest_mac; + src_mac.setTo(eh->ether_shost, 6); + dest_mac.setTo(eh->ether_dhost, 6); + _handler(_arg,NULL,_nwid,src_mac,dest_mac, Utils::ntoh((uint16_t)eh->ether_type),0, ((char*)data) + sizeof(struct ether_header),len - sizeof(struct ether_header)); + return len; + } +#endif +#if defined(STACK_LWIP) && defined(LIBZT_RAW) + err = rd_lwip_write(vs, data, len); +#endif +#if defined(STACK_PICO) + err = rd_pico_write(vs, data, len); +#endif + return err; +} + +// Send data to a specified host +int VirtualTap::SendTo(VirtualSocket *vs, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen) +{ +#if !defined(ZT_VIRTUAL_SOCKET) + return -1; +#endif + int err = -1; +#if defined(STACK_LWIP) && defined(ZT_VIRTUAL_SOCKET) + if ((err = rd_lwip_connect(vs, addr, addrlen)) < 0) { // implicit + return err; + } + if ((err = rd_lwip_write(vs, (void*)buf, len)) < 0) { + return err; + } +#endif +#if defined(STACK_PICO) && defined(ZT_VIRTUAL_SOCKET) + if ((err = rd_pico_connect(vs, addr, addrlen)) < 0) { // implicit + errno = ENOTCONN; + return err; + } + if ((err = rd_pico_write(vs, (void*)buf, len)) < 0) { + errno = ENOBUFS; // TODO: translate pico err to something more useful + return err; + } +#endif + return err; +} + +// Remove VritualSocket from VirtualTap, and instruct network stacks to dismantle their +// respective protocol control structures +int VirtualTap::Close(VirtualSocket *vs) +{ +#if !defined(LIBZT_RAW) + return -1; +#endif + int err = 0; +#if defined(LIBZT_RAW) + if (vs == NULL) { + DEBUG_ERROR("invalid VirtualSocket"); + return -1; + } + if (vs->sock) { + DEBUG_EXTRA("calling _phy.close()"); + _phy.close(vs->sock, true); + } + removeVirtualSocket(vs); +#endif +#if defined(STACK_LWIP) && defined(LIBZT_RAW) + err = rd_lwip_close(vs); +#endif +#if defined(STACK_PICO) + /* + if (vs->get_state() != VS_STATE_CLOSED && vs->get_state() != VS_STATE_LISTENING) { + DEBUG_EXTRA("vs=%p, vs->get_state()=%d, vs->picosock->state=%d", vs, vs->get_state(), vs->picosock->state); + // doesn't make sense to shut down a listening socket, just close it + if ((err = vs->tap->Shutdown(vs, SHUT_RDWR)) < 0) { + DEBUG_ERROR("error while shutting down socket"); + return - 1; } - return true; } - return false; - } - - bool VirtualTap::removeIp(const InetAddress &ip) - { - DEBUG_EXTRA(); - 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 + rd_pico_Close(vs); + removeVirtualSocket(vs); + if (vs->socket_type == SOCK_STREAM) { + while (!(vs->picosock->state & PICO_SOCKET_STATE_CLOSED)) { + nanosleep((const struct timespec[]) {{0, (ZT_ACCEPT_RECHECK_DELAY * 1000000)}}, NULL); + DEBUG_EXTRA("virtual lingering on socket, ps=%p, buf remaining=%d",vs->picosock, vs->TXbuf->count()); + } } - if (ip.isV6()) { - // FIXME: De-register from network stacks - } - return true; - } - - std::vector VirtualTap::ips() const - { - Mutex::Lock _l(_ips_m); - return _ips; - } - - void VirtualTap::put(const MAC &from,const MAC &to,unsigned int etherType, - const void *data,unsigned int len) - { -#if defined(STACK_LWIP) - lwip_eth_rx(this, from, to, etherType, data, len); + */ #endif - } + return err; +} - std::string VirtualTap::deviceName() const - { - return _dev; - } - - std::string VirtualTap::nodeId() const - { - if (zt1ServiceRef) { - char id[ZTO_ID_LEN]; - memset(id, 0, sizeof(id)); - sprintf(id, "%lx",((ZeroTier::OneService *)zt1ServiceRef)->getNode()->address()); - return std::string(id); - } - else { - return std::string("----------"); - } - } - - void VirtualTap::setFriendlyName(const char *friendlyName) - { - DEBUG_EXTRA("%s", friendlyName); - } - - void VirtualTap::scanMulticastGroups(std::vector &added, - std::vector &removed) - { - std::vector newGroups; - Mutex::Lock _l(_multicastGroups_m); - // TODO: get multicast subscriptions - 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 VirtualTap::setMtu(unsigned int mtu) - { - _mtu = mtu; - } - - void VirtualTap::threadMain() - throw() - { - while (true) { - _phy.poll(ZT_PHY_POLL_INTERVAL); - Housekeeping(); - } - } - - void VirtualTap::phyOnUnixClose(PhySocket *sock, void **uptr) - { - DEBUG_EXTRA(); - } - - void VirtualTap::phyOnUnixData(PhySocket *sock, void **uptr, void *data, ssize_t len) - { - DEBUG_EXTRA(); - } - - void VirtualTap::phyOnUnixWritable(PhySocket *sock, void **uptr, bool stack_invoked) - { - DEBUG_EXTRA(); - } - - bool VirtualTap::routeAdd(const InetAddress &ip, const InetAddress &nm, const InetAddress &gw) - { - bool err = false; - DEBUG_EXTRA(); -#if defined(STACK_LWIP) - //general_lwip_init_interface(this, NULL, "n1", _mac, ip, nm, gw); - //general_turn_on_interface(NULL); +// Shuts down some aspect of a connection +int VirtualTap::Shutdown(VirtualSocket *vs, int how) +{ +#if !defined(LIBZT_RAW) + return -1; +#endif + int err = 0; +#if defined(STACK_LWIP) && defined(LIBZT_RAW) + err = rd_lwip_shutdown(vs, how); #endif #if defined(STACK_PICO) - if (picostack) { - return picostack->pico_route_add(this, ip, nm, gw, 0); - } else { - handle_general_failure(); - return false; - } + err = rd_pico_shutdown(vs, how); #endif -#if defined(NO_STACK) - // nothing to do -#endif - return err; - } + return err; +} - bool VirtualTap::routeDelete(const InetAddress &ip, const InetAddress &nm) - { - bool err = false; +void VirtualTap::Housekeeping() +{ + Mutex::Lock _l(_tcpconns_m); + uint64_t current_ts = time_now(); + if (current_ts > last_housekeeping_ts + ZT_HOUSEKEEPING_INTERVAL) { DEBUG_EXTRA(); -#if defined(STACK_LWIP) - //general_lwip_init_interface(this, NULL, "n1", _mac, ip, nm, gw); - //general_turn_on_interface(NULL); -#endif -#if defined(STACK_PICO) - if (picostack) { - return picostack->pico_route_del(this, ip, nm, 0); - } else { - handle_general_failure(); - return false; - } -#endif -#if defined(NO_STACK) - // nothing to do -#endif - return err; - } - - void VirtualTap::addVirtualSocket() - { - DEBUG_EXTRA(); - } - - void VirtualTap::removeVirtualSocket() - { - DEBUG_EXTRA(); - } - - /****************************************************************************/ - /* DNS */ - /****************************************************************************/ - - int VirtualTap::add_DNS_Nameserver(struct sockaddr *addr) - { - DEBUG_EXTRA(); - return -1; - } - - int VirtualTap::del_DNS_Nameserver(struct sockaddr *addr) - { - DEBUG_EXTRA(); - return -1; - } - - /****************************************************************************/ - /* SDK Socket API */ - /****************************************************************************/ - - // Connect - int VirtualTap::Connect(const struct sockaddr *addr, socklen_t addrlen) - { - DEBUG_EXTRA(); - return -1; - } - - // Bind VirtualSocket to a network stack's interface - int VirtualTap::Bind(const struct sockaddr *addr, socklen_t addrlen) - { - DEBUG_EXTRA(); - return -1; - } - - // Listen for an incoming VirtualSocket - int VirtualTap::Listen(int backlog) - { - DEBUG_EXTRA(); - return -1; - } - - // Accept a VirtualSocket - void VirtualTap::Accept() - { - DEBUG_EXTRA(); - } - - // Read from stack/buffers into the app's socket - int VirtualTap::Read(PhySocket *sock,void **uptr,bool stack_invoked) - { - DEBUG_EXTRA(); - return -1; - } - - // Write data from app socket to the virtual wire, either raw over VL2, or via network stack - int VirtualTap::Write(void *data, ssize_t len) - { - DEBUG_EXTRA("data=%p, len=%d", data, len); - return -1; - } - - // Send data to a specified host - int VirtualTap::SendTo(const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen) - { - DEBUG_EXTRA(); - return -1; - } - - // Remove VritualSocket from VirtualTap, and instruct network stacks to dismantle their - // respective protocol control structures - int VirtualTap::Close() - { - DEBUG_EXTRA(); - return -1; - } - - // Shuts down some aspect of a connection (Read/Write) - int VirtualTap::Shutdown(int how) - { - DEBUG_EXTRA(); - return -1; - } - - void VirtualTap::Housekeeping() - { - Mutex::Lock _l(_tcpconns_m); - std::time_t current_ts = std::time(nullptr); - if (current_ts > last_housekeeping_ts + ZT_HOUSEKEEPING_INTERVAL) { - // DEBUG_EXTRA(); - // update managed routes (add/del from network stacks) - ZeroTier::OneService *service = ((ZeroTier::OneService *)zt1ServiceRef); - if (service) { - std::unique_ptr> managed_routes(service->getRoutes(this->_nwid)); - ZeroTier::InetAddress target_addr; - ZeroTier::InetAddress via_addr; - ZeroTier::InetAddress null_addr; - ZeroTier::InetAddress nm; - null_addr.fromString(""); - bool found; - char ipbuf[INET6_ADDRSTRLEN], ipbuf2[INET6_ADDRSTRLEN], ipbuf3[INET6_ADDRSTRLEN]; - // TODO: Rework this when we have time - // check if pushed route exists in tap (add) - for (int i=0; iat(i).target; - via_addr = managed_routes->at(i).via; + // update managed routes (add/del from network stacks) + ZeroTier::OneService *service = ((ZeroTier::OneService *)zt1ServiceRef); + if (service) { + std::unique_ptr> managed_routes(service->getRoutes(this->_nwid)); + ZeroTier::InetAddress target_addr; + ZeroTier::InetAddress via_addr; + ZeroTier::InetAddress null_addr; + ZeroTier::InetAddress nm; + null_addr.fromString(""); + bool found; + char ipbuf[INET6_ADDRSTRLEN], ipbuf2[INET6_ADDRSTRLEN], ipbuf3[INET6_ADDRSTRLEN]; + // TODO: Rework this when we have time + // check if pushed route exists in tap (add) + for (int i=0; iat(i).target; + via_addr = managed_routes->at(i).via; + nm = target_addr.netmask(); + for (size_t j=0; j", target_addr.toString(ipbuf), nm.toString(ipbuf2), via_addr.toString(ipbuf3)); + routes.push_back(std::pair(target_addr, nm)); + routeAdd(target_addr, nm, via_addr); + } + } + } + // check if route exists in tap but not in pushed routes (remove) + for (size_t i=0; iat(j).target; + via_addr = managed_routes->at(j).via; nm = target_addr.netmask(); - for (size_t j=0; j", target_addr.toString(ipbuf), nm.toString(ipbuf2), via_addr.toString(ipbuf3)); - routes.push_back(std::pair(target_addr, nm)); - routeAdd(target_addr, nm, via_addr); - } + if (routes[i].first.ipsEqual(target_addr) && routes[i].second.ipsEqual(nm)) { + found=true; } } - // check if route exists in tap but not in pushed routes (remove) - for (size_t i=0; iat(j).target; - via_addr = managed_routes->at(j).via; - nm = target_addr.netmask(); - if (routes[i].first.ipsEqual(target_addr) && routes[i].second.ipsEqual(nm)) { - found=true; - } - } - if (found == false) { - DEBUG_INFO("removing route to ", routes[i].first.toString(ipbuf), routes[i].second.toString(ipbuf2)); - routes.erase(routes.begin() + i); - routeDelete(routes[i].first, routes[i].second); - } + if (found == false) { + DEBUG_INFO("removing route to ", routes[i].first.toString(ipbuf), routes[i].second.toString(ipbuf2)); + routes.erase(routes.begin() + i); + routeDelete(routes[i].first, routes[i].second); } } - // TODO: Clean up VirtualSocket objects - last_housekeeping_ts = std::time(nullptr); } + // TODO: Clean up VirtualSocket objects + last_housekeeping_ts = time_now(); } +} - /****************************************************************************/ - /* Not used in this implementation */ - /****************************************************************************/ +/****************************************************************************/ +/* Not used in this implementation */ +/****************************************************************************/ - void VirtualTap::phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *local_address, - const struct sockaddr *from,void *data,unsigned long len) {} - void VirtualTap::phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) {} - void VirtualTap::phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN, - const struct sockaddr *from) {} - void VirtualTap::phyOnTcpClose(PhySocket *sock,void **uptr) {} - void VirtualTap::phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) {} - void VirtualTap::phyOnTcpWritable(PhySocket *sock,void **uptr) {} +void VirtualTap::phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *local_address, + const struct sockaddr *from,void *data,unsigned long len) {} +void VirtualTap::phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) {} +void VirtualTap::phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN, + const struct sockaddr *from) {} +void VirtualTap::phyOnTcpClose(PhySocket *sock,void **uptr) {} +void VirtualTap::phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) {} +void VirtualTap::phyOnTcpWritable(PhySocket *sock,void **uptr) {} -} // namespace ZeroTier diff --git a/src/VirtualTap.hpp b/src/VirtualTap.hpp deleted file mode 100644 index dd61e5c..0000000 --- a/src/VirtualTap.hpp +++ /dev/null @@ -1,311 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 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 . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -/** - * @file - * - * Virtual Ethernet tap device - */ - -#ifndef ZT_VIRTUALTAP_HPP -#define ZT_VIRTUALTAP_HPP - -#include - -#include "Mutex.hpp" -#include "MulticastGroup.hpp" -#include "InetAddress.hpp" -#include "Thread.hpp" -#include "Phy.hpp" - -#if defined(__MINGW32__) || defined(__MINGW64__) -#include -#include -#include -#include -#endif - -namespace ZeroTier { - - /** - * emulates an Ethernet tap device - */ - class VirtualTap - { - friend class Phy; - - public: - VirtualTap( - 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); - - ~VirtualTap(); - - void setEnabled(bool en); - bool enabled() const; - - /** - * Registers a device with the given address - */ - bool registerIpWithStack(const InetAddress &ip); - - /** - * Adds an address to the userspace stack interface associated with this VirtualTap - * - Starts VirtualTap main thread ONLY if successful - */ - bool addIp(const InetAddress &ip); - - /** - * Removes an address from the userspace stack interface associated with this VirtualTap - */ - bool removeIp(const InetAddress &ip); - - /** - * Presents data to the userspace stack - */ - void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data, - unsigned int len); - - /** - * Get VirtualTap device name (e.g. 'libzt4-17d72843bc2c5760') - */ - std::string deviceName() const; - - /** - * Get Node ID (ZT address) - */ - std::string nodeId() const; - - /** - * Set friendly name - */ - void setFriendlyName(const char *friendlyName); - - /** - * Scan multicast groups - */ - void scanMulticastGroups(std::vector &added,std::vector &removed); - - /** - * Set MTU - */ - void setMtu(unsigned int mtu); - - /** - * Calls main network stack loops - */ - void threadMain() - throw(); - -#if defined(__MINGW32__) - /* The following is merely to make ZeroTier's OneService happy while building on Windows. - we won't use these in libzt */ - NET_LUID _deviceLuid; - std::string _deviceInstanceId; - - /** - * Returns whether the VirtualTap interface has been initialized - */ - bool isInitialized() const { return _initialized; }; - - inline const NET_LUID &luid() const { return _deviceLuid; } - inline const std::string &instanceId() const { return _deviceInstanceId; } -#endif - /** - * 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); - - /** - * Signals us to close the TcpVirtualSocket associated with this PhySocket - */ - void phyOnUnixClose(PhySocket *sock, void **uptr); - - /** - * Notifies us that there is data to be read from an application's socket - */ - void phyOnUnixData(PhySocket *sock, void **uptr, void *data, ssize_t len); - - /** - * Notifies us that we can write to an application's socket - */ - void phyOnUnixWritable(PhySocket *sock, void **uptr, bool stack_invoked); - - /** - * Adds a route to the virtual tap - */ - bool routeAdd(const InetAddress &ip, const InetAddress &nm, const InetAddress &gw); - - /** - * Deletes a route from the virtual tap - */ - bool routeDelete(const InetAddress &ip, const InetAddress &nm); - - /** - * Assign a VirtualSocket to the VirtualTap - */ - void addVirtualSocket(); - - /** - * Remove a VirtualSocket from the VirtualTap - */ - void removeVirtualSocket(); - - /****************************************************************************/ - /* DNS */ - /****************************************************************************/ - - /** - * Registers a DNS nameserver with the network stack - */ - int add_DNS_Nameserver(struct sockaddr *addr); - - /** - * Un-registers a DNS nameserver from the network stack - */ - int del_DNS_Nameserver(struct sockaddr *addr); - - /****************************************************************************/ - /* Vars */ - /****************************************************************************/ - - std::vector> routes; - void *zt1ServiceRef = NULL; - - char vtap_full_name[64]; - char vtap_abbr_name[16]; - - static int devno; - size_t ifindex = 0; - - std::vector ips() const; - std::vector _ips; - - std::string _homePath; - void *_arg; - volatile bool _initialized; - volatile bool _enabled; - volatile bool _run; - MAC _mac; - unsigned int _mtu; - uint64_t _nwid; - PhySocket *_unixListenSocket; - Phy _phy; - - //std::vector _VirtualSockets; - - Thread _thread; - std::string _dev; // path to Unix domain socket - - std::vector _multicastGroups; - Mutex _multicastGroups_m; - Mutex _ips_m, _tcpconns_m, _rx_buf_m, _close_m; - - /* - * Timestamp of last run of housekeeping - * SEE: ZT_HOUSEKEEPING_INTERVAL in libzt.h - */ - std::time_t last_housekeeping_ts = 0; - - /****************************************************************************/ - /* 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 VirtualTap - */ - int Connect( const struct sockaddr *addr, socklen_t addrlen); - - /** - * Bind to the userspace stack interface associated with this VirtualTap - */ - int Bind(const struct sockaddr *addr, socklen_t addrlen); - - /** - * Listen for a VirtualSocket - */ - int Listen(int backlog); - - /** - * Accepts an incoming VirtualSocket - */ - void Accept(); - - /** - * Move data from RX buffer to application's "socket" - */ - int Read(PhySocket *sock,void **uptr,bool stack_invoked); - - /** - * Move data from application's "socket" into network stack - */ - int Write(void *data, ssize_t len); - - /** - * Send data to specified host - */ - int SendTo(const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen); - - /** - * Closes a VirtualSocket - */ - int Close(); - - /** - * Shuts down some aspect of a VirtualSocket - */ - int Shutdown(int how); - - /** - * Disposes of previously-closed VirtualSockets - */ - void Housekeeping(); - - /****************************************************************************/ - /* 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); - }; - -} // namespace ZeroTier - -#endif // _H diff --git a/src/ZT1Service.cpp b/src/ZT1Service.cpp index 8fc33a4..9be7a52 100644 --- a/src/ZT1Service.cpp +++ b/src/ZT1Service.cpp @@ -31,29 +31,27 @@ */ #include "ZT1Service.h" - -#include "Debug.hpp" +#include "libztDebug.h" +#include "SysUtils.h" #include "Phy.hpp" #include "OneService.hpp" -#include "Utilities.h" +#include "InetAddress.hpp" #include "OSUtils.hpp" +std::vector vtaps; +ZeroTier::Mutex _vtaps_lock; + #ifdef __cplusplus extern "C" { #endif -namespace ZeroTier { - std::vector vtaps; +static ZeroTier::OneService *zt1Service; - static ZeroTier::OneService *zt1Service; +std::string homeDir; // Platform-specific dir we *must* use internally +std::string netDir; // Where network .conf files are to be written - std::string homeDir; // Platform-specific dir we *must* use internally - std::string netDir; // Where network .conf files are to be written - - ZeroTier::Mutex _vtaps_lock; - ZeroTier::Mutex _multiplexer_lock; -} +ZeroTier::Mutex _multiplexer_lock; #if defined(__MINGW32__) || defined(__MINGW64__) WSADATA wsaData; @@ -63,132 +61,152 @@ WSADATA wsaData; /* ZeroTier Core helper functions for libzt - DON'T CALL THESE DIRECTLY */ /****************************************************************************/ -std::vector *zts_get_network_routes(char *nwid) +std::vector *zts_get_network_routes(const uint64_t nwid) { - uint64_t nwid_int = strtoull(nwid, NULL, 16); - return ZeroTier::zt1Service->getRoutes(nwid_int); + return zt1Service->getRoutes(nwid); } -ZeroTier::VirtualTap *getTapByNWID(uint64_t nwid) +VirtualTap *getTapByNWID(uint64_t nwid) { - ZeroTier::_vtaps_lock.lock(); - ZeroTier::VirtualTap *s, *tap = nullptr; - for (size_t i=0; i_nwid == nwid) { tap = s; } } - ZeroTier::_vtaps_lock.unlock(); + _vtaps_lock.unlock(); return tap; } -ZeroTier::VirtualTap *getTapByAddr(ZeroTier::InetAddress *addr) +VirtualTap *getTapByAddr(ZeroTier::InetAddress *addr) { - ZeroTier::_vtaps_lock.lock(); - ZeroTier::VirtualTap *s, *tap = nullptr; + _vtaps_lock.lock(); + VirtualTap *s, *tap = nullptr; //char ipbuf[64], ipbuf2[64], ipbuf3[64]; - for (size_t i=0; i_ips.size(); j++) { if ((s->_ips[j].isV4() && addr->isV4()) || (s->_ips[j].isV6() && addr->isV6())) { - //DEBUG_EXTRA("looking at tap %s, --- for <%s>", s->_dev.c_str(), s->_ips[j].toString(ipbuf), addr->toIpString(ipbuf2)); + /* DEBUG_EXTRA("looking at tap %s, --- for <%s>", s->_dev.c_str(), + s->_ips[j].toString(ipbuf), addr->toIpString(ipbuf2)); */ if (s->_ips[j].isEqualPrefix(addr) || s->_ips[j].ipsEqual(addr) || s->_ips[j].containsAddress(addr) - || (addr->isV6() && ipv6_in_subnet(&s->_ips[j], addr)) + || (addr->isV6() && _ipv6_in_subnet(&s->_ips[j], addr)) ) { //DEBUG_EXTRA("selected tap %s, ", s->_dev.c_str(), s->_ips[j].toString(ipbuf)); - ZeroTier::_vtaps_lock.unlock(); + _vtaps_lock.unlock(); return s; } } } // check managed routes if (tap == NULL) { - std::vector *managed_routes = ZeroTier::zt1Service->getRoutes(s->_nwid); + std::vector *managed_routes = zt1Service->getRoutes(s->_nwid); ZeroTier::InetAddress target, nm, via; for (size_t i=0; isize(); i++) { target = managed_routes->at(i).target; nm = target.netmask(); via = managed_routes->at(i).via; if (target.containsAddress(addr)) { - //DEBUG_EXTRA("chose tap with route ", target.toString(ipbuf), nm.toString(ipbuf2), via.toString(ipbuf3)); - ZeroTier::_vtaps_lock.unlock(); + /* DEBUG_EXTRA("chose tap with route ", target.toString(ipbuf), + nm.toString(ipbuf2), via.toString(ipbuf3)); */ + _vtaps_lock.unlock(); return s; } } } } - ZeroTier::_vtaps_lock.unlock(); + _vtaps_lock.unlock(); return tap; } -ZeroTier::VirtualTap *getTapByName(char *ifname) +VirtualTap *getTapByName(char *ifname) { - ZeroTier::_vtaps_lock.lock(); - ZeroTier::VirtualTap *s, *tap = nullptr; - for (size_t i=0; i_dev.c_str(), ifname) == false) { tap = s; } } - ZeroTier::_vtaps_lock.unlock(); + _vtaps_lock.unlock(); return tap; } -ZeroTier::VirtualTap *getTapByIndex(size_t index) +VirtualTap *getTapByIndex(size_t index) { - ZeroTier::_vtaps_lock.lock(); - ZeroTier::VirtualTap *s, *tap = nullptr; - for (size_t i=0; iifindex == index) { tap = s; } } - ZeroTier::_vtaps_lock.unlock(); + _vtaps_lock.unlock(); return tap; } -ZeroTier::VirtualTap *getAnyTap() +VirtualTap *getAnyTap() { - ZeroTier::_vtaps_lock.lock(); - ZeroTier::VirtualTap *vtap = NULL; - if (ZeroTier::vtaps.size()) { - vtap = (ZeroTier::VirtualTap *)ZeroTier::vtaps[0]; + _vtaps_lock.lock(); + VirtualTap *vtap = NULL; + if (vtaps.size()) { + vtap = (VirtualTap *)vtaps[0]; } - ZeroTier::_vtaps_lock.unlock(); + _vtaps_lock.unlock(); return vtap; } -int zts_get_id_from_file(const char *filepath, char *devID) +/* + + else // Service isn't online, try to read ID from file + { + std::string fname("identity.public"); + std::string fpath(homeDir); + if (ZeroTier::OSUtils::fileExists((fpath + ZT_PATH_SEPARATOR_S + fname).c_str(),false)) { + std::string oldid; + ZeroTier::OSUtils::readFile((fpath + ZT_PATH_SEPARATOR_S + fname).c_str(),oldid); + memcpy(devID, oldid.c_str(), ZTO_ID_LEN); // first 10 bytes of file + return 0; + } + } +*/ + +int zts_get_id_from_file(const char *filepath, uint64_t *nodeId) { + /* DEBUG_EXTRA(); std::string fname("identity.public"); std::string fpath(filepath); if (ZeroTier::OSUtils::fileExists((fpath + ZT_PATH_SEPARATOR_S + fname).c_str(),false)) { std::string oldid; ZeroTier::OSUtils::readFile((fpath + ZT_PATH_SEPARATOR_S + fname).c_str(),oldid); - memcpy(devID, oldid.c_str(), 10); // first 10 bytes of file + uint64_t value = Utils::hexStrToU64(oldid); + memcpy(nodeId, value, sizeof(uint64_t)); // first 10 bytes of file + // TOmorrow return 0; } + */ return -1; } // Starts a ZeroTier service in the background void *zts_start_service(void *thread_id) { - DEBUG_INFO("zto-thread, path=%s", ZeroTier::homeDir.c_str()); + DEBUG_INFO("zto-thread, path=%s", homeDir.c_str()); // Where network .conf files will be stored - ZeroTier::netDir = ZeroTier::homeDir + "/networks.d"; - ZeroTier::zt1Service = (ZeroTier::OneService *)0; + netDir = homeDir + "/networks.d"; + zt1Service = (ZeroTier::OneService *)0; // Construct path for network config and supporting service files - if (ZeroTier::homeDir.length()) { - std::vector hpsp(ZeroTier::OSUtils::split(ZeroTier::homeDir.c_str(), ZT_PATH_SEPARATOR_S,"","")); + if (homeDir.length()) { + std::vector hpsp(ZeroTier::OSUtils::split(homeDir.c_str(), ZT_PATH_SEPARATOR_S,"","")); std::string ptmp; - if (ZeroTier::homeDir[0] == ZT_PATH_SEPARATOR) { + if (homeDir[0] == ZT_PATH_SEPARATOR) { ptmp.push_back(ZT_PATH_SEPARATOR); } for (std::vector::iterator pi(hpsp.begin());pi!=hpsp.end();++pi) { @@ -199,7 +217,6 @@ void *zts_start_service(void *thread_id) if ((*pi != ".")&&(*pi != "..")) { if (ZeroTier::OSUtils::mkdir(ptmp) == false) { DEBUG_ERROR("home path does not exist, and could not create"); - handle_general_failure(); perror("error\n"); } } @@ -207,7 +224,6 @@ void *zts_start_service(void *thread_id) } else { DEBUG_ERROR("homeDir is empty, could not construct path"); - handle_general_failure(); return NULL; } @@ -217,27 +233,27 @@ void *zts_start_service(void *thread_id) // TODO: Better port random range selection int servicePort = 9000 + (randp % 1000); for (;;) { - ZeroTier::zt1Service = ZeroTier::OneService::newInstance(ZeroTier::homeDir.c_str(),servicePort); - switch(ZeroTier::zt1Service->run()) { + zt1Service = ZeroTier::OneService::newInstance(homeDir.c_str(),servicePort); + switch(zt1Service->run()) { case ZeroTier::OneService::ONE_STILL_RUNNING: case ZeroTier::OneService::ONE_NORMAL_TERMINATION: break; case ZeroTier::OneService::ONE_UNRECOVERABLE_ERROR: DEBUG_ERROR("ZTO service port = %d", servicePort); - DEBUG_ERROR("fatal error: %s",ZeroTier::zt1Service->fatalErrorMessage().c_str()); + DEBUG_ERROR("fatal error: %s",zt1Service->fatalErrorMessage().c_str()); break; case ZeroTier::OneService::ONE_IDENTITY_COLLISION: { - delete ZeroTier::zt1Service; - ZeroTier::zt1Service = (ZeroTier::OneService *)0; + delete zt1Service; + zt1Service = (ZeroTier::OneService *)0; std::string oldid; - ZeroTier::OSUtils::readFile((ZeroTier::homeDir + ZT_PATH_SEPARATOR_S + ZeroTier::OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str(),oldid); if (oldid.length()) { - ZeroTier::OSUtils::writeFile((ZeroTier::homeDir + ZT_PATH_SEPARATOR_S + ZeroTier::OSUtils::writeFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret.saved_after_collision").c_str(),oldid); - ZeroTier::OSUtils::rm((ZeroTier::homeDir + ZT_PATH_SEPARATOR_S + ZeroTier::OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str()); - ZeroTier::OSUtils::rm((ZeroTier::homeDir + ZT_PATH_SEPARATOR_S + ZeroTier::OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.public").c_str()); } } @@ -245,179 +261,89 @@ void *zts_start_service(void *thread_id) } break; // terminate loop -- normally we don't keep restarting } - delete ZeroTier::zt1Service; - ZeroTier::zt1Service = (ZeroTier::OneService *)0; + delete zt1Service; + zt1Service = (ZeroTier::OneService *)0; return NULL; } -void disableTaps() +void zts_get_address(const uint64_t nwid, struct sockaddr_storage *addr, const size_t addrlen) { - DEBUG_EXTRA(); - ZeroTier::_vtaps_lock.lock(); - for (size_t i=0; i_enabled = false; + if(!zt1Service) { + return; } - ZeroTier::_vtaps_lock.unlock(); -} - -void zts_get_ipv4_address(const char *nwid, char *addrstr, const size_t addrlen) -{ - // DEBUG_EXTRA(); - if (ZeroTier::zt1Service) { - uint64_t nwid_int = strtoull(nwid, NULL, 16); - ZeroTier::VirtualTap *tap = getTapByNWID(nwid_int); - if (tap && tap->_ips.size()) { - for (size_t i=0; i_ips.size(); i++) { - if (tap->_ips[i].isV4()) { - char ipbuf[INET_ADDRSTRLEN]; - std::string addr = tap->_ips[i].toString(ipbuf); - int len = addrlen < addr.length() ? addrlen : addr.length(); - memset(addrstr, 0, len); - memcpy(addrstr, addr.c_str(), len); - return; - } + VirtualTap *tap = getTapByNWID(nwid); + if (tap && tap->_ips.size()) { + for (size_t i=0; i_ips.size(); i++) { + if (tap->_ips[i].isV4()) { + memcpy(addr, &(tap->_ips[i]), addrlen); + return; } } } - else - memcpy(addrstr, "\0", 1); } -void zts_get_ipv6_address(const char *nwid, char *addrstr, size_t addrlen) +int zts_has_address(const uint64_t nwid) { - // DEBUG_EXTRA(); - if (ZeroTier::zt1Service) { - uint64_t nwid_int = strtoull(nwid, NULL, 16); - ZeroTier::VirtualTap *tap = getTapByNWID(nwid_int); - if (tap && tap->_ips.size()) { - for (size_t i=0; i_ips.size(); i++) { - if (tap->_ips[i].isV6()) { - char ipbuf[INET6_ADDRSTRLEN]; - std::string addr = tap->_ips[i].toString(ipbuf); - int len = addrlen < addr.length() ? addrlen : addr.length(); - memset(addrstr, 0, len); - memcpy(addrstr, addr.c_str(), len); - return; - } - } - } - } - else - memcpy(addrstr, "\0", 1); + struct sockaddr_storage ss; + memset(&ss, 0, sizeof(ss)); + zts_get_address(nwid, &ss, sizeof(ss)); + return ss.ss_family == AF_INET || ss.ss_family == AF_INET6; } -int zts_has_ipv4_address(const char *nwid) +void zts_get_6plane_addr(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId) { - // DEBUG_EXTRA(); - char ipv4_addr[INET_ADDRSTRLEN]; - memset(ipv4_addr, 0, INET_ADDRSTRLEN); - zts_get_ipv4_address(nwid, ipv4_addr, INET_ADDRSTRLEN); - return strcmp(ipv4_addr, "\0"); + ZeroTier::InetAddress _6planeAddr = ZeroTier::InetAddress::makeIpv66plane(nwid,nodeId); + memcpy(addr, _6planeAddr.rawIpData(), sizeof(struct sockaddr_storage)); } -int zts_has_ipv6_address(const char *nwid) +void zts_get_rfc4193_addr(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId) { - // DEBUG_EXTRA(); - char ipv6_addr[INET6_ADDRSTRLEN]; - memset(ipv6_addr, 0, INET6_ADDRSTRLEN); - zts_get_ipv6_address(nwid, ipv6_addr, INET6_ADDRSTRLEN); - return strcmp(ipv6_addr, "\0"); + ZeroTier::InetAddress _rfc4193Addr = ZeroTier::InetAddress::makeIpv6rfc4193(nwid,nodeId); + memcpy(addr, _rfc4193Addr.rawIpData(), sizeof(struct sockaddr_storage)); } -int zts_has_address(const char *nwid) -{ - // DEBUG_EXTRA(); - return zts_has_ipv4_address(nwid) || zts_has_ipv6_address(nwid); -} - -void zts_get_6plane_addr(char *addr, const char *nwid, const char *devID) +void zts_join(const uint64_t nwid) { DEBUG_EXTRA(); - ZeroTier::InetAddress _6planeAddr = ZeroTier::InetAddress::makeIpv66plane( - ZeroTier::Utils::hexStrToU64(nwid),ZeroTier::Utils::hexStrToU64(devID)); - char ipbuf[INET6_ADDRSTRLEN]; - memcpy(addr, _6planeAddr.toIpString(ipbuf), 40); -} - -void zts_get_rfc4193_addr(char *addr, const char *nwid, const char *devID) -{ - DEBUG_EXTRA(); - ZeroTier::InetAddress _6planeAddr = ZeroTier::InetAddress::makeIpv6rfc4193( - ZeroTier::Utils::hexStrToU64(nwid),ZeroTier::Utils::hexStrToU64(devID)); - char ipbuf[INET6_ADDRSTRLEN]; - memcpy(addr, _6planeAddr.toIpString(ipbuf), 40); -} - -void zts_join(const char * nwid) -{ - DEBUG_EXTRA(); - if (ZeroTier::zt1Service) { - std::string confFile = ZeroTier::zt1Service->givenHomePath() + "/networks.d/" + nwid + ".conf"; - if (ZeroTier::OSUtils::mkdir(ZeroTier::netDir) == false) { - DEBUG_ERROR("unable to create: %s", ZeroTier::netDir.c_str()); - handle_general_failure(); + if (zt1Service) { + std::string confFile = zt1Service->givenHomePath() + "/networks.d/" + std::to_string(nwid) + ".conf"; + if (ZeroTier::OSUtils::mkdir(netDir) == false) { + DEBUG_ERROR("unable to create: %s", netDir.c_str()); } if (ZeroTier::OSUtils::writeFile(confFile.c_str(), "") == false) { DEBUG_ERROR("unable to write network conf file: %s", confFile.c_str()); - handle_general_failure(); } - ZeroTier::zt1Service->join(nwid); + zt1Service->join(nwid); } // provide ZTO service reference to virtual taps // TODO: This might prove to be unreliable, but it works for now - for (size_t i=0;izt1ServiceRef=(void*)ZeroTier::zt1Service; + for (size_t i=0;izt1ServiceRef=(void*)zt1Service; } } -void zts_join_soft(const char * filepath, const char * nwid) +void zts_leave(const uint64_t nwid) { DEBUG_EXTRA(); - std::string net_dir = std::string(filepath) + "/networks.d/"; - std::string confFile = net_dir + std::string(nwid) + ".conf"; - if (ZeroTier::OSUtils::mkdir(net_dir) == false) { - DEBUG_ERROR("unable to create: %s", net_dir.c_str()); - handle_general_failure(); - } - if (ZeroTier::OSUtils::fileExists(confFile.c_str(), false) == false) { - if (ZeroTier::OSUtils::writeFile(confFile.c_str(), "") == false) { - DEBUG_ERROR("unable to write network conf file: %s", confFile.c_str()); - handle_general_failure(); - } + if (zt1Service) { + zt1Service->leave(nwid); } } -void zts_leave(const char * nwid) +int zts_running() { - DEBUG_EXTRA(); - if (ZeroTier::zt1Service) { - ZeroTier::zt1Service->leave(nwid); - } -} - -void zts_leave_soft(const char * filepath, const char * nwid) -{ - DEBUG_EXTRA(); - std::string net_dir = std::string(filepath) + "/networks.d/"; - ZeroTier::OSUtils::rm((net_dir + nwid + ".conf").c_str()); -} - -int zts_running() -{ - DEBUG_EXTRA(); - return ZeroTier::zt1Service == NULL ? false : ZeroTier::zt1Service->isRunning(); + return zt1Service == NULL ? false : zt1Service->isRunning(); } int zts_start(const char *path, bool blocking = false) { DEBUG_EXTRA(); - if (ZeroTier::zt1Service) { + if (zt1Service) { return 0; // already initialized, ok } if (path) { - ZeroTier::homeDir = path; + homeDir = path; } #if defined(__MINGW32__) || defined(__MINGW64__) WSAStartup(MAKEWORD(2, 2), &wsaData); // initialize WinSock. Used in Phy for loopback pipe @@ -426,21 +352,21 @@ int zts_start(const char *path, bool blocking = false) int err = pthread_create(&service_thread, NULL, zts_start_service, NULL); if (blocking) { // block to prevent service calls before we're ready ZT_NodeStatus status; - while (zts_running() == false || ZeroTier::zt1Service->getNode() == NULL) { + while (zts_running() == false || zt1Service->getNode() == NULL) { nanosleep((const struct timespec[]) {{0, (ZTO_WRAPPER_CHECK_INTERVAL * 500000)}}, NULL); } - while (ZeroTier::zt1Service->getNode()->address() <= 0) { + while (zt1Service->getNode()->address() <= 0) { nanosleep((const struct timespec[]) {{0, (ZTO_WRAPPER_CHECK_INTERVAL * 500000)}}, NULL); } while (status.online <= 0) { nanosleep((const struct timespec[]) {{0, (ZTO_WRAPPER_CHECK_INTERVAL * 500000)}}, NULL); - ZeroTier::zt1Service->getNode()->status(&status); + zt1Service->getNode()->status(&status); } } return err; } -int zts_startjoin(const char *path, const char *nwid) +int zts_startjoin(const char *path, const uint64_t nwid) { DEBUG_EXTRA(); int err = zts_start(path, true); @@ -452,7 +378,6 @@ int zts_startjoin(const char *path, const char *nwid) } catch( ... ) { DEBUG_ERROR("there was a problem joining the virtual network %s", nwid); - handle_general_failure(); } } while (zts_has_address(nwid) == false) { @@ -461,67 +386,53 @@ int zts_startjoin(const char *path, const char *nwid) return err; } -void zts_stop() +void zts_stop() { DEBUG_EXTRA(); - if (ZeroTier::zt1Service) { - ZeroTier::zt1Service->terminate(); - disableTaps(); + if (zt1Service) { + zt1Service->terminate(); + // disableTaps(); } #if defined(__MINGW32__) || defined(__MINGW64__) WSACleanup(); // clean up WinSock #endif } -void zts_get_homepath(char *homePath, size_t len) +void zts_get_homepath(char *homePath, size_t len) { DEBUG_EXTRA(); - if (ZeroTier::homeDir.length()) { + if (homeDir.length()) { memset(homePath, 0, len); - size_t buf_len = len < ZeroTier::homeDir.length() ? len : ZeroTier::homeDir.length(); - memcpy(homePath, ZeroTier::homeDir.c_str(), buf_len); + size_t buf_len = len < homeDir.length() ? len : homeDir.length(); + memcpy(homePath, homeDir.c_str(), buf_len); } } -int zts_get_id(char *devID) +uint64_t zts_get_node_id() { DEBUG_EXTRA(); - if (ZeroTier::zt1Service) { - char id[ZTO_ID_LEN]; - sprintf(id, "%lx",ZeroTier::zt1Service->getNode()->address()); - memcpy(devID, id, ZTO_ID_LEN); - return 0; - } - else // Service isn't online, try to read ID from file - { - std::string fname("identity.public"); - std::string fpath(ZeroTier::homeDir); - if (ZeroTier::OSUtils::fileExists((fpath + ZT_PATH_SEPARATOR_S + fname).c_str(),false)) { - std::string oldid; - ZeroTier::OSUtils::readFile((fpath + ZT_PATH_SEPARATOR_S + fname).c_str(),oldid); - memcpy(devID, oldid.c_str(), ZTO_ID_LEN); // first 10 bytes of file - return 0; - } + if (zt1Service) { + return zt1Service->getNode()->address(); } return -1; } -unsigned long zts_get_peer_count() +unsigned long zts_get_peer_count() { DEBUG_EXTRA(); - if (ZeroTier::zt1Service) { - return ZeroTier::zt1Service->getNode()->peers()->peerCount; + if (zt1Service) { + return zt1Service->getNode()->peers()->peerCount; } else { return 0; } } -int zts_get_peer_address(char *peer, const char *devID) +int zts_get_peer_address(char *peer, const uint64_t nodeId) { DEBUG_EXTRA(); - if (ZeroTier::zt1Service) { - ZT_PeerList *pl = ZeroTier::zt1Service->getNode()->peers(); + if (zt1Service) { + ZT_PeerList *pl = zt1Service->getNode()->peers(); // uint64_t addr; for (size_t i=0; ipeerCount; i++) { // ZT_Peer *p = &(pl->peers[i]); @@ -539,6 +450,37 @@ void zts_allow_http_control(bool allowed) // TODO } +bool _ipv6_in_subnet(ZeroTier::InetAddress *subnet, ZeroTier::InetAddress *addr) +{ + ZeroTier::InetAddress r(addr); + ZeroTier::InetAddress b(subnet); + const unsigned int bits = subnet->netmaskBits(); + switch(r.ss_family) { + case AF_INET: + reinterpret_cast(&r)->sin_addr.s_addr &= ZeroTier::Utils::hton((uint32_t)(0xffffffff << (32 - bits))); + break; + case AF_INET6: { + uint64_t nm[2]; + uint64_t nm2[2]; + memcpy(nm,reinterpret_cast(&r)->sin6_addr.s6_addr,16); + memcpy(nm2,reinterpret_cast(&b)->sin6_addr.s6_addr,16); + + nm[0] &= ZeroTier::Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits)))); + nm[1] &= ZeroTier::Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits)))); + + nm2[0] &= ZeroTier::Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits)))); + nm2[1] &= ZeroTier::Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits)))); + + memcpy(reinterpret_cast(&r)->sin6_addr.s6_addr,nm,16); + memcpy(reinterpret_cast(&b)->sin6_addr.s6_addr,nm2,16); + } + break; + } + char b0[64], b1[64]; + memset(b0, 0, 64); + memset(b1, 0, 64); + return !strcmp(r.toIpString(b0), b.toIpString(b1)); +} #ifdef __cplusplus } #endif diff --git a/src/libzt.cpp b/src/libzt.cpp index 49407c3..cfe6a34 100644 --- a/src/libzt.cpp +++ b/src/libzt.cpp @@ -30,11 +30,10 @@ * Application-facing, partially-POSIX-compliant socket API */ -#include +#include "libztDefs.h" #if defined(STACK_LWIP) #include "lwip/sockets.h" -#include "lwip/sys.h" #include "lwip/ip_addr.h" #include "lwip/netdb.h" #include "dns.h" @@ -42,20 +41,619 @@ #if defined(NO_STACK) #include #endif +#if defined(STACK_PICO) +#include +#endif -#include "libzt.h" +#include "VirtualSocketLayer.h" +#include "libztDebug.h" + +#include +//void * memcpy(void * dst, const void * src, size_t n); #ifdef __cplusplus extern "C" { #endif +int platform_adjusted_socket_family(int family); +void fix_addr_socket_family(struct sockaddr *addr); +bool zts_running(); + +int zts_socket(int socket_family, int socket_type, int protocol) +{ + DEBUG_EXTRA("family=%d, type=%d, proto=%d", socket_family, socket_type, protocol); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + /* with this option, the VirtualSocket layer will abstract a stack's raw API + into something that resembles a POSIX socket API, this driver shall be implemented in + src/stack_name.cpp and include/stack_name.h */ + return virt_socket(socket_family, socket_type, protocol); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + /* use the lwIP community's own socket API, this provides thread safety and core + locking */ + int socket_family_adj = platform_adjusted_socket_family(socket_family); + return lwip_socket(socket_family_adj, socket_type, protocol); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + // return pico_bsd_socket(socket_family, socket_type, protocol); + return -1; +#endif +} + +int zts_connect(int fd, const struct sockaddr *addr, socklen_t addrlen) +{ + DEBUG_EXTRA("fd=%d",fd); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_connect(fd, addr, addrlen); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + struct sockaddr_storage ss; + memcpy(&ss, addr, addrlen); + fix_addr_socket_family((struct sockaddr*)&ss); + return lwip_connect(fd, (struct sockaddr*)&ss, addrlen); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +int zts_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) +{ + DEBUG_EXTRA("fd=%d", fd); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_bind(fd, addr, addrlen); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + struct sockaddr_storage ss; + memcpy(&ss, addr, addrlen); + fix_addr_socket_family((struct sockaddr*)&ss); + return lwip_bind(fd, (struct sockaddr*)&ss, addrlen); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +int zts_listen(int fd, int backlog) +{ + DEBUG_EXTRA("fd=%d", fd); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_listen(fd, backlog); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_listen(fd, backlog); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +int zts_accept(int fd, struct sockaddr *addr, socklen_t *addrlen) +{ + DEBUG_EXTRA("fd=%d", fd); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_accept(fd, addr, addrlen); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_accept(fd, addr, addrlen); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +#if defined(__linux__) +int zts_accept4(int fd, struct sockaddr *addr, socklen_t *addrlen, int flags) +{ + DEBUG_EXTRA("fd=%d", fd); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_accept4(fd, addr, addrlen, flags); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + // return lwip_accept4(fd, addr, addrlen, flags); + return -1; +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} +#endif + +int zts_setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) +{ + DEBUG_EXTRA("fd=%d, level=%d, optname=%d", fd, level, optname); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_setsockopt(fd, level, optname, optval, optlen); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_setsockopt(fd, level, optname, optval, optlen); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +int zts_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen) +{ + DEBUG_EXTRA("fd=%d, level=%d, optname=%d", fd, level, optname); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_getsockopt(fd, level, optname, optval, optlen); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_getsockopt(fd, level, optname, optval, optlen); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +int zts_getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen) +{ + DEBUG_EXTRA("fd=%p", fd); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_getsockname(fd, addr, addrlen); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_getsockname(fd, addr, addrlen); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +int zts_getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen) +{ + DEBUG_EXTRA("fd=%d", fd); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_getpeername(fd, addr, addrlen); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_getpeername(fd, addr, addrlen); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +int zts_gethostname(char *name, size_t len) +{ + DEBUG_EXTRA(); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return -1; +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return -1; +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +int zts_sethostname(const char *name, size_t len) +{ + DEBUG_EXTRA(); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return -1; +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return -1; +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +struct hostent *zts_gethostbyname(const char *name) +{ + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return NULL; + } +#if defined(ZT_VIRTUAL_SOCKET) + //return virt_gethostbyname(name); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + // TODO: Test thread safety + /* + char buf[256]; + int buflen = 256; + int h_err = 0; + struct hostent hret; + struct hostent **result = NULL; + int err = 0; + if ((err = lwip_gethostbyname_r(name, &hret, buf, buflen, result, &h_err)) != 0) { + DEBUG_ERROR("err = %d", err); + DEBUG_ERROR("h_err = %d", h_err); + errno = h_err; + return NULL; // failure + } + return *result; + */ + return lwip_gethostbyname(name); +#endif +#if defined(ZT_PICO_BSD_SOCKET) +#endif +#if defined(STACK_LWIP) +#endif + return NULL; +} + +int zts_close(int fd) +{ + DEBUG_EXTRA("fd=%d", fd); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_close(fd); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_close(fd); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +#if defined(__linux__) +/* +int zts_poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + DEBUG_ERROR("warning, this is not implemented"); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + //return poll(fds, nfds, timeout); + return -1; +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + //return poll(fds, nfds, timeout); + return -1; +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} +*/ +#endif + +int zts_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) +{ + //DEBUG_EXTRA(); + /* + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } + */ +#if defined(ZT_VIRTUAL_SOCKET) + return virt_select(nfds, readfds, writefds, exceptfds, timeout); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_select(nfds, readfds, writefds, exceptfds, timeout); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +int zts_fcntl(int fd, int cmd, int flags) +{ + DEBUG_EXTRA("fd=%d, cmd=%d, flags=%d", fd, cmd, flags); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_fcntl(fd, cmd, flags); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + // translation from platform flag values to stack flag values + int translated_flags = 0; +#if defined(__linux__) + if (flags == 2048) { + translated_flags = 1; + } +#endif +#if defined(__APPLE__) + if (flags == 4) { + translated_flags = 1; + } +#endif + return lwip_fcntl(fd, cmd, translated_flags); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +int zts_ioctl(int fd, unsigned long request, void *argp) +{ + DEBUG_EXTRA("fd=%d, req=%d", fd, request); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_ioctl(fd, request, argp); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_ioctl(fd, request, argp); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +ssize_t zts_sendto(int fd, const void *buf, size_t len, int flags, + const struct sockaddr *addr, socklen_t addrlen) +{ + DEBUG_TRANS("fd=%d, len=%d", fd, len); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + //return virt_sendto(); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + struct sockaddr_storage ss; + memcpy(&ss, addr, addrlen); + fix_addr_socket_family((struct sockaddr*)&ss); + return lwip_sendto(fd, buf, len, flags, (struct sockaddr*)&ss, addrlen); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif + return -1; +} + +ssize_t zts_send(int fd, const void *buf, size_t len, int flags) +{ + DEBUG_TRANS("fd=%d, len=%d", fd, len); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_send(fd, buf, len, flags); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_send(fd, buf, len, flags); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +ssize_t zts_sendmsg(int fd, const struct msghdr *msg, int flags) +{ + DEBUG_TRANS("fd=%d", fd); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_sendmsg(fd, msg, flags); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_sendmsg(fd, msg, flags); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +ssize_t zts_recv(int fd, void *buf, size_t len, int flags) +{ + DEBUG_TRANS("fd=%d", fd); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_recv(fd, buf, len, flags); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_recv(fd, buf, len, flags); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +ssize_t zts_recvfrom(int fd, void *buf, size_t len, int flags, + struct sockaddr *addr, socklen_t *addrlen) +{ + DEBUG_TRANS("fd=%d", fd); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_recvfrom(fd, buf, len, flags, addr, addrlen); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_recvfrom(fd, buf, len, flags, addr, addrlen); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +ssize_t zts_recvmsg(int fd, struct msghdr *msg,int flags) +{ + DEBUG_TRANS("fd=%d", fd); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_recvmsg(fd, msg, flags); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + //return lwip_recvmsg(fd, msg, flags); + return -1; +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +int zts_read(int fd, void *buf, size_t len) +{ + DEBUG_TRANS("fd=%d, len=%d", fd, len); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_read(fd, buf, len); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_read(fd, buf, len); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + // return pico_read(fd, buf, len); +#endif +} + +int zts_write(int fd, const void *buf, size_t len) +{ + DEBUG_EXTRA("fd=%d, len=%d", fd, len); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_write(fd, buf, len); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_write(fd, buf, len); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +int zts_shutdown(int fd, int how) +{ + DEBUG_EXTRA("fd=%d, how=%d", fd, how); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return virt_shutdown(fd, how); +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return lwip_shutdown(fd, how); +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +int zts_add_dns_nameserver(struct sockaddr *addr) +{ + DEBUG_EXTRA(); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return -1; +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + struct sockaddr_in *in4 = (struct sockaddr_in*)&addr; + static ip4_addr_t ipaddr; + ipaddr.addr = in4->sin_addr.s_addr; + // TODO: manage DNS server indices + dns_setserver(0, (const ip_addr_t*)&ipaddr); + return 0; +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + +int zts_del_dns_nameserver(struct sockaddr *addr) +{ + DEBUG_EXTRA(); + if (zts_running() == false) { + DEBUG_ERROR("service not started yet, call zts_startjoin()"); + return -1; + } +#if defined(ZT_VIRTUAL_SOCKET) + return -1; +#endif +#if defined(ZT_LWIP_SEQ_SOCKET) + return -1; +#endif +#if defined(ZT_PICO_BSD_SOCKET) + return -1; +#endif +} + /* The rationale for the following correctional methods is as follows: - Since we don't want the user of this library to worry about naming conflicts - with their native OS/platform's socket facilities we deliberately isolate what + Since we don't want the user of this library to worry about naming conflicts + with their native OS/platform's socket facilities we deliberately isolate what is used by the userspace network stack and stack drivers from the user's application. As a result of this, we must compensate for a few things on our - side. For instance, differing values for AF_INET6 on major operating systems, and + side. For instance, differing values for AF_INET6 on major operating systems, and differing structure definitions for sockaddr. */ @@ -77,13 +675,11 @@ int platform_adjusted_socket_family(int family) #endif } -void fix_addr_socket_family(struct sockaddr *addr) +void fix_addr_socket_family(struct sockaddr *addr) { #if defined(__linux__) /* linux's socket.h's sockaddr definition doesn't contain an sa_len field - so we must compensate here before feeding it into the stack. Due to this limitation - we must cast the pointer to the address into two different address - */ + so we must adjust it here before feeding it into the stack. */ #if defined(STACK_LWIP) if (addr->sa_len == 2) { if (addr->sa_family == 0) { @@ -99,461 +695,11 @@ void fix_addr_socket_family(struct sockaddr *addr) } #endif /* once we've moved the value to its anticipated location, convert it from - its platform-specific value to one that the network stack can work with */ + its platform-specific value to one that the network stack can work with */ #endif addr->sa_family = platform_adjusted_socket_family(addr->sa_family); } -int zts_socket(int socket_family, int socket_type, int protocol) -{ - int err = -1; - int socket_family_adj = platform_adjusted_socket_family(socket_family); - DEBUG_EXTRA("family=%d, type=%d, proto=%d", socket_family, socket_type, protocol); -#if defined(STACK_LWIP) - err = lwip_socket(socket_family_adj, socket_type, protocol); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_connect(int fd, const struct sockaddr *addr, socklen_t addrlen) -{ - int err = -1; - DEBUG_EXTRA("fd=%d",fd); -#if defined(STACK_LWIP) - struct sockaddr_storage ss; - memcpy(&ss, addr, addrlen); - fix_addr_socket_family((struct sockaddr*)&ss); - err = lwip_connect(fd, (struct sockaddr*)&ss, addrlen); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) -{ - int err = -1; - DEBUG_EXTRA("fd=%d", fd); -#if defined(STACK_LWIP) - struct sockaddr_storage ss; - memcpy(&ss, addr, addrlen); - fix_addr_socket_family((struct sockaddr*)&ss); - err = lwip_bind(fd, (struct sockaddr*)&ss, addrlen); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_listen(int fd, int backlog) -{ - int err = -1; - DEBUG_EXTRA("fd=%d", fd); -#if defined(STACK_LWIP) - err = lwip_listen(fd, backlog); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_accept(int fd, struct sockaddr *addr, socklen_t *addrlen) -{ - int err = -1; - DEBUG_EXTRA("fd=%d", fd); -#if defined(STACK_LWIP) - err = lwip_accept(fd, addr, addrlen); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -#if defined(__linux__) -int zts_accept4(int fd, struct sockaddr *addr, socklen_t *addrlen, int flags) -{ - int err = -1; - DEBUG_EXTRA("fd=%d", fd); -#if defined(STACK_LWIP) - err = zts_accept(fd, addr, addrlen); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} -#endif - -int zts_setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) -{ - int err = -1; - DEBUG_EXTRA("fd=%d, level=%d, optname=%d", fd, level, optname); -#if defined(STACK_LWIP) - err = lwip_setsockopt(fd, level, optname, optval, optlen); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen) -{ - int err = -1; - DEBUG_EXTRA("fd=%d, level=%d, optname=%d", fd, level, optname); -#if defined(STACK_LWIP) - err = lwip_getsockopt(fd, level, optname, optval, optlen); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen) -{ - int err = -1; - DEBUG_EXTRA("fd=%p", fd); -#if defined(STACK_LWIP) - err = lwip_getsockname(fd, addr, addrlen); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen) -{ - int err = -1; - DEBUG_EXTRA("fd=%d", fd); -#if defined(STACK_LWIP) - err = lwip_getpeername(fd, addr, addrlen); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_gethostname(char *name, size_t len) -{ - DEBUG_EXTRA(); - int err = -1; -#if defined(STACK_LWIP) -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_sethostname(const char *name, size_t len) -{ - DEBUG_EXTRA(); - int err = -1; -#if defined(STACK_LWIP) -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -struct hostent *zts_gethostbyname(const char *name) -{ -#if defined(STACK_LWIP) - // TODO: Test thread safety - /* - char buf[256]; - int buflen = 256; - int h_err = 0; - struct hostent hret; - struct hostent **result = NULL; - int err = 0; - if ((err = lwip_gethostbyname_r(name, &hret, buf, buflen, result, &h_err)) != 0) { - DEBUG_ERROR("err = %d", err); - DEBUG_ERROR("h_err = %d", h_err); - errno = h_err; - return NULL; // failure - } - return *result; - */ - return lwip_gethostbyname(name); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) - return NULL; -#endif -} - -int zts_close(int fd) -{ - int err = -1; - DEBUG_EXTRA("fd=%d", fd); -#if defined(STACK_LWIP) - err = lwip_close(fd); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -#if defined(__linux__) -int zts_poll(struct pollfd *fds, nfds_t nfds, int timeout) -{ - int err = -1; -#if defined(STACK_LWIP) - DEBUG_ERROR("warning, this is not implemented"); - return poll(fds, nfds, timeout); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} -#endif - -int zts_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, - struct timeval *timeout) -{ - int err = -1; - //DEBUG_EXTRA(); -#if defined(STACK_LWIP) - err = lwip_select(nfds, readfds, writefds, exceptfds, timeout); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_fcntl(int fd, int cmd, int flags) -{ - int err = -1; - DEBUG_EXTRA("fd=%d, cmd=%d, flags=%d", fd, cmd, flags); -#if defined(STACK_LWIP) - // translation from platform flag values to stack flag values - int translated_flags = 0; -#if defined(__linux__) - if (flags == 2048) { - translated_flags = 1; - } -#endif -#if defined(__APPLE__) - if (flags == 4) { - translated_flags = 1; - } -#endif - err = lwip_fcntl(fd, cmd, translated_flags); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_ioctl(int fd, unsigned long request, void *argp) -{ - int err = -1; - DEBUG_EXTRA("fd=%d, req=%d", fd, request); -#if defined(STACK_LWIP) - err = lwip_ioctl(fd, request, argp); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -ssize_t zts_sendto(int fd, const void *buf, size_t len, int flags, - const struct sockaddr *addr, socklen_t addrlen) -{ - int err = -1; - DEBUG_TRANS("fd=%d, len=%d", fd, len); -#if defined(STACK_LWIP) - struct sockaddr_storage ss; - memcpy(&ss, addr, addrlen); - fix_addr_socket_family((struct sockaddr*)&ss); - err = lwip_sendto(fd, buf, len, flags, (struct sockaddr*)&ss, addrlen); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -ssize_t zts_send(int fd, const void *buf, size_t len, int flags) -{ - int err = -1; - DEBUG_TRANS("fd=%d, len=%d", fd, len); -#if defined(STACK_LWIP) - err = lwip_send(fd, buf, len, flags); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -ssize_t zts_sendmsg(int fd, const struct msghdr *msg, int flags) -{ - int err = -1; - DEBUG_TRANS("fd=%d", fd); -#if defined(STACK_LWIP) - err = lwip_sendmsg(fd, msg, flags); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -ssize_t zts_recv(int fd, void *buf, size_t len, int flags) -{ - int err = -1; - DEBUG_TRANS("fd=%d", fd); -#if defined(STACK_LWIP) - err = lwip_recv(fd, buf, len, flags); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -ssize_t zts_recvfrom(int fd, void *buf, size_t len, int flags, - struct sockaddr *addr, socklen_t *addrlen) -{ - int err = -1; - DEBUG_TRANS("fd=%d", fd); -#if defined(STACK_LWIP) - err = lwip_recvfrom(fd, buf, len, flags, addr, addrlen); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -ssize_t zts_recvmsg(int fd, struct msghdr *msg,int flags) -{ - DEBUG_TRANS("fd=%d", fd); - int err = -1; -#if defined(STACK_LWIP) -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_read(int fd, void *buf, size_t len) -{ - int err = -1; - //DEBUG_TRANS("fd=%d, len=%d", fd, len); -#if defined(STACK_LWIP) - err = lwip_read(fd, buf, len); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_write(int fd, const void *buf, size_t len) -{ - //DEBUG_TRANS("fd=%d, len=%d", fd, len); - int err = -1; -#if defined(STACK_LWIP) - err = lwip_write(fd, buf, len); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_shutdown(int fd, int how) -{ - int err = -1; - DEBUG_EXTRA("fd=%d, how=%d", fd, how); -#if defined(STACK_LWIP) - err = lwip_shutdown(fd, how); -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_add_dns_nameserver(struct sockaddr *addr) -{ - DEBUG_EXTRA(); - int err = -1; -#if defined(STACK_LWIP) && defined(LIBZT_IPV4) - struct sockaddr_in *in4 = (struct sockaddr_in*)&addr; - static ip4_addr_t ipaddr; - ipaddr.addr = in4->sin_addr.s_addr; - // TODO: manage DNS server indices - dns_setserver(0, (const ip_addr_t*)&ipaddr); - err = 0; -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - -int zts_del_dns_nameserver(struct sockaddr *addr) -{ - DEBUG_EXTRA(); - int err = -1; -#if defined(STACK_LWIP) -#endif -#if defined(STCK_PICO) -#endif -#if defined(NO_STACK) -#endif - return err; -} - #ifdef __cplusplus } #endif diff --git a/src/libztJNI.cpp b/src/libztJNI.cpp index 2d3bcc6..b4b31ee 100644 --- a/src/libztJNI.cpp +++ b/src/libztJNI.cpp @@ -28,11 +28,9 @@ * @file * * Javs JNI wrapper for partially-POSIX-compliant socket API - * JNI naming convention: Java_PACKAGENAME_CLASSNAME_METHODNAME + * JNI naming convention: Java_PACKAGENAME_CLASSNAME_METHODNAME */ -#if defined(SDK_JNI) - #include #include "libzt.h" @@ -50,14 +48,14 @@ namespace ZeroTier { /* ZeroTier Socket API (for JNI wrapper) */ /****************************************************************************/ - JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_socket(JNIEnv *env, jobject thisObj, - jint family, jint type, jint protocol) + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_socket(JNIEnv *env, jobject thisObj, + jint family, jint type, jint protocol) { return zts_socket(family, type, protocol); } - JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_connect(JNIEnv *env, jobject thisObj, - jint fd, jstring addrstr, jint port) + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_connect(JNIEnv *env, jobject thisObj, + jint fd, jstring addrstr, jint port) { struct sockaddr_storage ss; socklen_t namelen = sizeof(ss); @@ -91,8 +89,41 @@ namespace ZeroTier { return zts_connect(fd, (struct sockaddr *)&ss, sizeof(in_addr)); } - JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_bind(JNIEnv *env, jobject thisObj, - jint fd, jstring addrstr, jint port) +/* + + JNIEXPORT jobject JNICALL Java_zerotier_ZeroTier_get_address( + JNIEnv *env, jobject thisObj, jstring nwid) + { + // get address + const char *nwid_str = env->GetStringUTFChars(nwid, NULL); + struct sockaddr_storage ss; + zts_get_address(nwid_str, &ss, INET_ADDRSTRLEN); + // map sockaddr_storage contents to java InetAddress + jclass c = (*env).FindClass("java/net/InetSocketAddress"); + if (c) { + + jobject addresses = (*env).NewObject(c, (*env).GetMethodID(c, "", "()V")); + env->CallBooleanMethod(addresses, env->GetMethodID(c, "getPort", "(Ljava/lang/Object;)Z"), _str); + + DEBUG_INFO("port from JNI layer = %d", port); + + fid = (*env).GetFieldID(c, "port", "I"); + (*env).SetIntField(ztaddr, fid, addr.sin_port); + fid = (*env).GetFieldID(c,"_rawAddr", "J"); + (*env).SetLongField(ztaddr, fid,addr.sin_addr.s_addr); + //jobject addresses = (*env).NewObject(c, (*env).GetMethodID(c, "", "()V")); + //env->CallBooleanMethod(addresses, env->GetMethodID(c, "add", "(Ljava/lang/Object;)Z"), _str); + //return addresses; + } + return NULL; + } + +*/ + + +/* + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_bind(JNIEnv *env, jobject thisObj, + jint fd, jstring addrstr, jint port) { struct sockaddr_storage ss; socklen_t namelen = sizeof(ss); @@ -125,10 +156,132 @@ namespace ZeroTier { DEBUG_INFO("fd=%d, addr=%s, port=%d", fd, str, port); return zts_bind(fd, (struct sockaddr *)&ss, sizeof(in_addr)); } +*/ + + + + +/* + jfieldID IPv4 = (*env).GetFieldID(inetClass, "IPv4", "I"); + if (IPv4) { + ipv4 = (*env).GetIntField(inetClass, IPv4); + } + else { + DEBUG_ERROR("No field IPv4"); + } + jfieldID IPv6 = (*env).GetFieldID(inetClass, "IPv6", "I"); + if (IPv6) { + ipv6 = (*env).GetIntField(inetClass, IPv6); + } + else { + DEBUG_ERROR("No field IPv6"); + } + ipv6 = (*env).GetIntField(inetClass, IPv6); +*/ + + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_bind(JNIEnv *env, jobject thisObj, jint fd, jobject addr) + { + struct sockaddr_storage ss; + memset(&ss, 0, sizeof(ss)); + struct sockaddr_in *in4 = (struct sockaddr_in*)&ss; + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&ss; + int err = -1; + int port = 0; + int socket_family = 0; + socklen_t addrlen; + + jclass c = (*env).GetObjectClass(addr); + if (!c) { + return -1; + } + // get port + jmethodID getPort = (*env).GetMethodID(c, "getPort", "()I"); + if (!getPort) { + return -1; + } + port = (*env).CallIntMethod(addr, getPort); + // get internal InetAddress + jobject inetaddr; + int family = 0; + jmethodID getAddress = (*env).GetMethodID(c, "getAddress", "()Ljava/net/InetAddress;"); + if (!getAddress) { + return -1; + } + inetaddr = (*env).CallObjectMethod(addr, getAddress); + if(!inetaddr) { + return -1; + } + jclass inetClass = (*env).GetObjectClass(inetaddr); + if(!inetClass) { + return -1; + } + // string representation of IP address + jmethodID getHostAddress = (*env).GetMethodID(inetClass, "getHostAddress", "()Ljava/lang/String;"); + jstring addrstr = (jstring)(*env).CallObjectMethod(inetaddr, getHostAddress); + const char *addr_str = (*env).GetStringUTFChars(addrstr, NULL); + DEBUG_INFO("addr_str=%s", addr_str); + for (int i=0; isin_family = AF_INET; + in4->sin_port = htons(port); + in4->sin_addr.s_addr = inet_addr(addr_str); + /* + if(!inet_pton(AF_INET, addr_str, &(in4->sin_addr))) { + DEBUG_ERROR("error converting address %s", addr_str); + } + */ + addrlen = sizeof(struct sockaddr_in); + break; + } + if(addr_str[i]==':') { + DEBUG_INFO("ipv6"); + socket_family = AF_INET6; + if(!inet_pton(AF_INET6, addr_str, &(in6->sin6_addr))) { + DEBUG_ERROR("error converting address %s", addr_str); + } + addrlen = sizeof(struct sockaddr_in6); + break; + } + } + (*env).ReleaseStringUTFChars(addrstr, addr_str); + + DEBUG_TEST("RESULT => %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); + + // + /* + jmethodID getRawAddress = (*env).GetMethodID(inetClass, "getAddress", "()[B"); // get raw IP bytes + if (getRawAddress) + { + jbyte rawIPbuf = (*env).CallByteMethod(inetaddr, getRawAddress); + if (socket_family == AF_INET) + { + DEBUG_INFO("copying buffer as AF_INET"); + in4->sin_family = socket_family; + in4->sin_port = port; + memcpy(in4->sin_addr, &rawIPbuf, sizeof(in4->sin_addr)); + } + if (socket_family == AF_INET6) + { + DEBUG_INFO("copying buffer as AF_INET"); + in6->sin6_family = socket_family; + } + } + else { + DEBUG_ERROR("!getRawAddress"); + } + */ + + //DEBUG_INFO("port = %d", port); + //DEBUG_INFO("inetaddr = %p", inetaddr); + err = zts_bind(fd, (struct sockaddr*)&ss, addrlen); + return err; + } #if defined(__linux__) - JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_accept4(JNIEnv *env, jobject thisObj, - jint fd, jstring addrstr, jint port, jint flags) + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_accept4(JNIEnv *env, jobject thisObj, + jint fd, jstring addrstr, jint port, jint flags) { struct sockaddr_in addr; char *str; @@ -141,8 +294,8 @@ namespace ZeroTier { } #endif - JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_accept(JNIEnv *env, jobject thisObj, - jint fd, jstring addrstr, jint port) + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_accept(JNIEnv *env, jobject thisObj, + jint fd, jstring addrstr, jint port) { struct sockaddr_in addr; // TODO: Send addr info back to Javaland @@ -152,14 +305,14 @@ namespace ZeroTier { return zts_accept(fd, (struct sockaddr *)&addr, (socklen_t *)sizeof(addr)); } - JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_listen(JNIEnv *env, jobject thisObj, - jint fd, int backlog) + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_listen(JNIEnv *env, jobject thisObj, + jint fd, int backlog) { return zts_listen(fd, backlog); } - JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_close(JNIEnv *env, jobject thisObj, - jint fd) + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_close(JNIEnv *env, jobject thisObj, + jint fd) { return zts_close(fd); } @@ -168,20 +321,23 @@ namespace ZeroTier { JNIEnv *env, jobject thisObj, jint fd, jarray buf, jint len, jint flags, jobject ztaddr) { struct sockaddr_in addr; - jclass cls = (*env).GetObjectClass( ztaddr); - jfieldID f = (*env).GetFieldID( cls, "port", "I"); - addr.sin_port = htons((*env).GetIntField( ztaddr, f)); - f = (*env).GetFieldID( cls, "_rawAddr", "J"); - addr.sin_addr.s_addr = (*env).GetLongField( ztaddr, f); - addr.sin_family = AF_INET; - //LOGV("zt_sendto(): fd = %d\naddr = %s\nport=%d", fd, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); - // TODO: Optimize this - jbyte *body = (*env).GetByteArrayElements((_jbyteArray *)buf, 0); - char * bufp = (char *)malloc(sizeof(char)*len); - memcpy(bufp, body, len); - (*env).ReleaseByteArrayElements((_jbyteArray *)buf, body, 0); - // "connect" and send buffer contents - int sent_bytes = zts_sendto(fd, body, len, flags, (struct sockaddr *)&addr, sizeof(addr)); + int sent_bytes = 0; + jclass c = (*env).GetObjectClass( ztaddr); + if (c) { + jfieldID f = (*env).GetFieldID(c, "port", "I"); + addr.sin_port = htons((*env).GetIntField( ztaddr, f)); + f = (*env).GetFieldID(c, "_rawAddr", "J"); + addr.sin_addr.s_addr = (*env).GetLongField( ztaddr, f); + addr.sin_family = AF_INET; + //LOGV("zt_sendto(): fd = %d\naddr = %s\nport=%d", fd, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + // TODO: Optimize this + jbyte *body = (*env).GetByteArrayElements((_jbyteArray *)buf, 0); + char * bufp = (char *)malloc(sizeof(char)*len); + memcpy(bufp, body, len); + (*env).ReleaseByteArrayElements((_jbyteArray *)buf, body, 0); + // "connect" and send buffer contents + sent_bytes = zts_sendto(fd, body, len, flags, (struct sockaddr *)&addr, sizeof(addr)); + } return sent_bytes; } @@ -193,16 +349,19 @@ namespace ZeroTier { unsigned char buffer[ZT_SDK_MTU]; int payload_offset = sizeof(int32_t) + sizeof(struct sockaddr_storage); int rxbytes = zts_recvfrom(fd, &buffer, len, flags, (struct sockaddr *)&addr, (socklen_t *)sizeof(struct sockaddr_storage)); - if (rxbytes > 0) + if (rxbytes > 0) { memcpy(body, (jbyte*)buffer + payload_offset, rxbytes); + } (*env).ReleaseByteArrayElements( buf, body, 0); // Update fields of Java ZTAddress object jfieldID fid; - jclass cls = (*env).GetObjectClass( ztaddr); - fid = (*env).GetFieldID( cls, "port", "I"); - (*env).SetIntField( ztaddr, fid, addr.sin_port); - fid = (*env).GetFieldID( cls,"_rawAddr", "J"); - (*env).SetLongField( ztaddr, fid,addr.sin_addr.s_addr); + jclass c = (*env).GetObjectClass( ztaddr); + if (c) { + fid = (*env).GetFieldID(c, "port", "I"); + (*env).SetIntField(ztaddr, fid, addr.sin_port); + fid = (*env).GetFieldID(c,"_rawAddr", "J"); + (*env).SetLongField(ztaddr, fid,addr.sin_addr.s_addr); + } return rxbytes; } @@ -216,7 +375,7 @@ namespace ZeroTier { return written_bytes; } - JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_write(JNIEnv *env, jobject thisObj, + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_write(JNIEnv *env, jobject thisObj, jint fd, jarray buf, jint len) { jbyte *body = (*env).GetByteArrayElements((_jbyteArray *)buf, 0); @@ -227,7 +386,7 @@ namespace ZeroTier { return written_bytes; } - JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_read(JNIEnv *env, jobject thisObj, + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_read(JNIEnv *env, jobject thisObj, jint fd, jarray buf, jint len) { jbyte *body = (*env).GetByteArrayElements((_jbyteArray *)buf, 0); @@ -237,48 +396,52 @@ namespace ZeroTier { } JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_setsockopt( - JNIEnv *env, jobject thisObj, - jint fd, jint level, jint optname, jint optval, jint optlen) + JNIEnv *env, jobject thisObj, + jint fd, jint level, jint optname, jint optval, jint optlen) { return zts_setsockopt(fd, level, optname, (const void*)optval, optlen); } - JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_getsockopt(JNIEnv *env, jobject thisObj, - jint fd, jint level, jint optname, jint optval, jint optlen) + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_getsockopt(JNIEnv *env, jobject thisObj, + jint fd, jint level, jint optname, jint optval, jint optlen) { return zts_getsockopt(fd, level, optname, (void*)optval, (socklen_t *)optlen); } - JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_getsockname(JNIEnv *env, jobject thisObj, - jint fd, jobject ztaddr) + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_getsockname(JNIEnv *env, jobject thisObj, + jint fd, jobject ztaddr) { struct sockaddr_in addr; int err = zts_getsockname(fd, (struct sockaddr *)&addr, (socklen_t *)sizeof(struct sockaddr)); jfieldID fid; - jclass cls = (*env).GetObjectClass(ztaddr); - fid = (*env).GetFieldID( cls, "port", "I"); - (*env).SetIntField( ztaddr, fid, addr.sin_port); - fid = (*env).GetFieldID( cls,"_rawAddr", "J"); - (*env).SetLongField( ztaddr, fid,addr.sin_addr.s_addr); + jclass c = (*env).GetObjectClass(ztaddr); + if (c) { + fid = (*env).GetFieldID(c, "port", "I"); + (*env).SetIntField(ztaddr, fid, addr.sin_port); + fid = (*env).GetFieldID(c,"_rawAddr", "J"); + (*env).SetLongField(ztaddr, fid,addr.sin_addr.s_addr); + } return err; } - JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_getpeername(JNIEnv *env, jobject thisObj, - jint fd, jobject ztaddr) + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_getpeername(JNIEnv *env, jobject thisObj, + jint fd, jobject ztaddr) { struct sockaddr_in addr; int err = zts_getpeername(fd, (struct sockaddr *)&addr, (socklen_t *)sizeof(struct sockaddr)); jfieldID fid; - jclass cls = (*env).GetObjectClass( ztaddr); - fid = (*env).GetFieldID( cls, "port", "I"); - (*env).SetIntField( ztaddr, fid, addr.sin_port); - fid = (*env).GetFieldID( cls,"_rawAddr", "J"); - (*env).SetLongField( ztaddr, fid,addr.sin_addr.s_addr); + jclass c = (*env).GetObjectClass( ztaddr); + if (c) { + fid = (*env).GetFieldID(c, "port", "I"); + (*env).SetIntField(ztaddr, fid, addr.sin_port); + fid = (*env).GetFieldID(c,"_rawAddr", "J"); + (*env).SetLongField(ztaddr, fid,addr.sin_addr.s_addr); + } return err; } - JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_fcntl(JNIEnv *env, jobject thisObj, - jint fd, jint cmd, jint flags) + JNIEXPORT jint JNICALL Java_zerotier_ZeroTier_fcntl(JNIEnv *env, jobject thisObj, + jint fd, jint cmd, jint flags) { return zts_fcntl(fd,cmd,flags); } @@ -287,21 +450,21 @@ namespace ZeroTier { /* ZeroTier service controls (for JNI wrapper) */ /****************************************************************************/ - JNIEXPORT void JNICALL Java_zerotier_ZeroTier_start(JNIEnv *env, jobject thisObj, jstring path, jboolean blocking) + JNIEXPORT void JNICALL Java_zerotier_ZeroTier_start(JNIEnv *env, jobject thisObj, jstring path, jboolean blocking) { if (path) { zts_start(env->GetStringUTFChars(path, NULL), blocking); } } - JNIEXPORT void JNICALL Java_zerotier_ZeroTier_startjoin(JNIEnv *env, jobject thisObj, jstring path, jstring nwid) + JNIEXPORT void JNICALL Java_zerotier_ZeroTier_startjoin(JNIEnv *env, jobject thisObj, jstring path, jstring nwid) { if (path && nwid) { zts_startjoin(env->GetStringUTFChars(path, NULL), env->GetStringUTFChars(nwid, NULL)); } } - JNIEXPORT void JNICALL Java_zerotier_ZeroTier_stop(JNIEnv *env, jobject thisObj) + JNIEXPORT void JNICALL Java_zerotier_ZeroTier_stop(JNIEnv *env, jobject thisObj) { zts_stop(); } @@ -309,17 +472,17 @@ namespace ZeroTier { JNIEXPORT jboolean JNICALL Java_zerotier_ZeroTier_running( JNIEnv *env, jobject thisObj) { - return zts_running(); + return zts_running(); } - JNIEXPORT void JNICALL Java_zerotier_ZeroTier_join(JNIEnv *env, jobject thisObj, jstring nwid) + JNIEXPORT void JNICALL Java_zerotier_ZeroTier_join(JNIEnv *env, jobject thisObj, jstring nwid) { if (nwid) { zts_join(env->GetStringUTFChars(nwid, NULL)); } } - JNIEXPORT void JNICALL Java_zerotier_ZeroTier_leave(JNIEnv *env, jobject thisObj, jstring nwid) + JNIEXPORT void JNICALL Java_zerotier_ZeroTier_leave(JNIEnv *env, jobject thisObj, jstring nwid) { if (nwid) { zts_leave(env->GetStringUTFChars(nwid, NULL)); @@ -334,32 +497,22 @@ namespace ZeroTier { return (*env).NewStringUTF(""); } - JNIEXPORT jobject JNICALL Java_zerotier_ZeroTier_get_ipv4_address( + JNIEXPORT jobject JNICALL Java_zerotier_ZeroTier_get_address( JNIEnv *env, jobject thisObj, jstring nwid) { + // get address const char *nwid_str = env->GetStringUTFChars(nwid, NULL); - char address_string[INET_ADDRSTRLEN]; - memset(address_string, 0, INET_ADDRSTRLEN); - zts_get_ipv4_address(nwid_str, address_string, INET_ADDRSTRLEN); - jclass clazz = (*env).FindClass("java/util/ArrayList"); - jobject addresses = (*env).NewObject(clazz, (*env).GetMethodID(clazz, "", "()V")); - jstring _str = (*env).NewStringUTF(address_string); - env->CallBooleanMethod(addresses, env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z"), _str); - return addresses; - } - - JNIEXPORT jobject JNICALL Java_zerotier_ZeroTier_get_ipv6_address( - JNIEnv *env, jobject thisObj, jstring nwid) - { - const char *nwid_str = env->GetStringUTFChars(nwid, NULL); - char address_string[INET6_ADDRSTRLEN]; - memset(address_string, 0, INET6_ADDRSTRLEN); - zts_get_ipv6_address(nwid_str, address_string, INET6_ADDRSTRLEN); - jclass clazz = (*env).FindClass("java/util/ArrayList"); - jobject addresses = (*env).NewObject(clazz, (*env).GetMethodID(clazz, "", "()V")); - jstring _str = (*env).NewStringUTF(address_string); - env->CallBooleanMethod(addresses, env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z"), _str); - return addresses; + struct sockaddr_storage ss; + zts_get_address(nwid_str, &ss, INET_ADDRSTRLEN); + // map sockaddr_storage contents to java InetAddress + /* + jclass c = (*env).FindClass("java/net/InetAddress"); + if (c) { + jobject addresses = (*env).NewObject(c, (*env).GetMethodID(c, "", "()V")); + env->CallBooleanMethod(addresses, env->GetMethodID(c, "add", "(Ljava/lang/Object;)Z"), _str); + return addresses; + }*/ + return NULL; } JNIEXPORT jint Java_zerotier_ZeroTier_get_id() @@ -368,8 +521,6 @@ namespace ZeroTier { } } -#endif // SDK_JNI - #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/src/lwIP.cpp b/src/lwIP.cpp index 5ac40e7..31801df 100644 --- a/src/lwIP.cpp +++ b/src/lwIP.cpp @@ -27,17 +27,36 @@ /** * @file * - * lwIP network stack driver + * lwIP network stack driver. + * + * Calls made in this network stack driver may never block since all packet + * processing (input and output) as well as timer processing (TCP mainly) is done + * in a single execution context. + * */ -#include "VirtualTap.hpp" +#include "libztDefs.h" -#include "ZeroTierOne.h" +#ifdef STACK_LWIP + +int errno; + +// forward declarations +#include "VirtualTap.h" +#include "VirtualSocket.h" + +class VirtualTap; +class VirtualSocket; +bool virt_can_provision_new_socket(int socket_type); + +#include "Mutex.hpp" #include "MAC.hpp" +#include "ZeroTierOne.h" #include "libzt.h" +#include "SysUtils.h" #include "Utilities.h" -#include "Debug.hpp" +#include "libztDebug.h" #include "netif/ethernet.h" #include "lwip/netif.h" @@ -51,21 +70,17 @@ #include "lwip/timeouts.h" #include "lwip/stats.h" #include "lwip/ethip6.h" +#include "lwip/ip_addr.h" +#include "lwip/nd6.h" +#include "lwip/dns.h" +#include "lwip/netifapi.h" -#include "dns.h" -#include "netifapi.h" - -#include "lwIP.hpp" +#include "lwIP.h" // lwIP netif interfaces used by virtual taps -netif lwipdev, lwipdev6, n1; struct netif lwip_interfaces[10]; static int num_lwip_interfaces = 0; - - - - bool lwip_driver_initialized = false; ZeroTier::Mutex driver_m; @@ -93,7 +108,7 @@ static void tcpip_init_done(void *arg) DEBUG_EXTRA("tcpip-thread"); sys_sem_t *sem; sem = (sys_sem_t *)arg; - netif_set_up(&lwipdev); + //netif_set_up(&lwipdev); lwip_driver_initialized = true; driver_m.unlock(); // sys_timeout(5000, tcp_timeout, NULL); @@ -136,7 +151,7 @@ err_t lwip_eth_tx(struct netif *netif, struct pbuf *p) char *bufptr; int totalLength = 0; - ZeroTier::VirtualTap *tap = (ZeroTier::VirtualTap*)netif->state; + VirtualTap *tap = (VirtualTap*)netif->state; bufptr = buf; for (q = p; q != NULL; q = q->next) { memcpy(bufptr, q->payload, q->len); @@ -185,67 +200,71 @@ void lwip_start_dhcp(void *netif) void lwip_init_interface(void *tapref, const ZeroTier::MAC &mac, const ZeroTier::InetAddress &ip) { char ipbuf[INET6_ADDRSTRLEN], nmbuf[INET6_ADDRSTRLEN]; -#if defined(LIBZT_IPV4) + char macbuf[ZT_MAC_ADDRSTRLEN]; + + DEBUG_INFO("num_lwip_interfaces=%d", num_lwip_interfaces); + struct netif *lwipdev = &lwip_interfaces[num_lwip_interfaces]; + DEBUG_INFO("lwipdev=%p", lwipdev); + if (ip.isV4()) { static ip4_addr_t ipaddr, netmask, gw; IP4_ADDR(&gw,127,0,0,1); ipaddr.addr = *((u32_t *)ip.rawIpData()); netmask.addr = *((u32_t *)ip.netmask().rawIpData()); - netif_add(&(lwipdev),&ipaddr, &netmask, &gw, NULL, tapif_init, tcpip_input); - lwipdev.state = tapref; - lwipdev.output = etharp_output; - lwipdev.mtu = ZT_MAX_MTU; - lwipdev.name[0] = 'l'; - lwipdev.name[1] = '4'; - lwipdev.linkoutput = lwip_eth_tx; - lwipdev.hwaddr_len = 6; - mac.copyTo(lwipdev.hwaddr, lwipdev.hwaddr_len); - lwipdev.flags = NETIF_FLAG_BROADCAST + netif_add(lwipdev, &ipaddr, &netmask, &gw, NULL, tapif_init, tcpip_input); + lwipdev->state = tapref; + lwipdev->output = etharp_output; + lwipdev->mtu = ZT_MAX_MTU; + lwipdev->name[0] = 'l'; + lwipdev->name[1] = (char)('A' + num_lwip_interfaces); //'4'; + lwipdev->linkoutput = lwip_eth_tx; + lwipdev->hwaddr_len = 6; + mac.copyTo(lwipdev->hwaddr, lwipdev->hwaddr_len); + lwipdev->flags = 0; + lwipdev->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP; - netif_set_default(&lwipdev); - netif_set_link_up(&lwipdev); - netif_set_up(&lwipdev); - char macbuf[ZT_MAC_ADDRSTRLEN]; - mac2str(macbuf, ZT_MAC_ADDRSTRLEN, lwipdev.hwaddr); + netif_set_default(lwipdev); + netif_set_link_up(lwipdev); + netif_set_up(lwipdev); + mac2str(macbuf, ZT_MAC_ADDRSTRLEN, lwipdev->hwaddr); DEBUG_INFO("initialized netif as [mac=%s, addr=%s, nm=%s]", macbuf, ip.toString(ipbuf), ip.netmask().toString(nmbuf)); + num_lwip_interfaces++; } -#endif -#if defined(LIBZT_IPV6) if (ip.isV6()) { + /* static ip6_addr_t ipaddr; memcpy(&(ipaddr.addr), ip.rawIpData(), sizeof(ipaddr.addr)); - - lwipdev6.mtu = ZT_MAX_MTU; - lwipdev6.name[0] = 'l'; - lwipdev6.name[1] = '6'; - lwipdev6.hwaddr_len = 6; - lwipdev6.linkoutput = lwip_eth_tx; - lwipdev6.ip6_autoconfig_enabled = 1; - mac.copyTo(lwipdev6.hwaddr, lwipdev6.hwaddr_len); - netif_add(&lwipdev6, NULL, NULL, NULL, NULL, tapif_init, ethernet_input); - lwipdev6.output_ip6 = ethip6_output; - lwipdev6.state = tapref; + lwipdev->mtu = ZT_MAX_MTU; + lwipdev->name[0] = 'l'; + lwipdev->name[1] = '6'; + lwipdev->hwaddr_len = 6; + lwipdev->linkoutput = lwip_eth_tx; + lwipdev->ip6_autoconfig_enabled = 1; - netif_create_ip6_linklocal_address(&lwipdev6, 1); + mac.copyTo(lwipdev->hwaddr, lwipdev->hwaddr_len); + netif_add(lwipdev, NULL, NULL, NULL, NULL, tapif_init, ethernet_input); + lwipdev->output_ip6 = ethip6_output; + lwipdev->state = tapref; + + netif_create_ip6_linklocal_address(lwipdev, 1); s8_t idx = 1; - netif_add_ip6_address(&lwipdev6, &ipaddr, &idx); - netif_set_default(&lwipdev6); - netif_set_up(&lwipdev6); - netif_set_link_up(&lwipdev6); - netif_ip6_addr_set_state(&lwipdev6, 1, IP6_ADDR_TENTATIVE); - - char macbuf[ZT_MAC_ADDRSTRLEN]; - mac2str(macbuf, ZT_MAC_ADDRSTRLEN, lwipdev6.hwaddr); - DEBUG_INFO("initialized netif as [mac=%s, addr=%s]", macbuf, ip.toString(ipbuf)); + netif_add_ip6_address(lwipdev, &ipaddr, &idx); + netif_set_default(lwipdev); + netif_set_up(lwipdev); + netif_set_link_up(lwipdev); + netif_ip6_addr_set_state(lwipdev, 1, IP6_ADDR_TENTATIVE); + mac2str(macbuf, ZT_MAC_ADDRSTRLEN, lwipdev->hwaddr); + DEBUG_INFO("initialized netif as [mac=%s, addr=%s]", macbuf, ip.toString(ipbuf)); + */ } -#endif + } -void lwip_eth_rx(ZeroTier::VirtualTap *tap, const ZeroTier::MAC &from, const ZeroTier::MAC &to, unsigned int etherType, +void lwip_eth_rx(VirtualTap *tap, const ZeroTier::MAC &from, const ZeroTier::MAC &to, unsigned int etherType, const void *data, unsigned int len) { struct pbuf *p,*q; @@ -288,25 +307,1702 @@ void lwip_eth_rx(ZeroTier::VirtualTap *tap, const ZeroTier::MAC &from, const Zer ZeroTier::Utils::ntoh(ethhdr.type), beautify_eth_proto_nums(ZeroTier::Utils::ntoh(ethhdr.type)), flagbuf); } { - // TODO: Routing logic -#if defined(LIBZT_IPV4) - // feed in IPV4 and ARP - if (ZeroTier::Utils::ntoh(ethhdr.type) == 0x800 || ZeroTier::Utils::ntoh(ethhdr.type) == 0x0806) { - DEBUG_INFO("Inputting to lwipdev"); - if (lwipdev.input(p, &lwipdev) != ERR_OK) { - DEBUG_ERROR("error while feeding frame into stack interface (ipv4)"); + switch (((struct eth_hdr *)p->payload)->type) + { + case PP_HTONS(ETHTYPE_IP): + { + DEBUG_INFO("ETHTYPE_IP"); + struct ip_hdr *iphdr; + ip_addr_t iphdr_dest; + + iphdr = (struct ip_hdr *)((char *)p->payload + SIZEOF_ETH_HDR); + + //ip_addr_copy(iphdr_dest, iphdr->dest); + + for (int i=0; iip_addr.u_addr.ip4.addr=%ld", lwipdev->ip_addr.u_addr.ip4.addr); + DEBUG_INFO("iphdr->dest.addr =%ld", iphdr->dest.addr); + if (lwipdev->ip_addr.u_addr.ip4.addr == iphdr->dest.addr || ip4_addr_isbroadcast_u32(iphdr->dest.addr, lwipdev)) + { + DEBUG_INFO("USING=%p", lwipdev); + if (lwipdev->input(p, lwipdev) != ERR_OK) { + DEBUG_ERROR("error while feeding frame into stack interface (ipv4)"); + } + break; // netif found, use current value of lwipdev + } + } } + case PP_HTONS(ETHTYPE_ARP): + { + DEBUG_INFO("ETHTYPE_ARP"); + for (int i=0; ioutput == etharp_output) { // ipv4/arp + DEBUG_INFO("IPV4/ARP"); + if (ethhdr, ethhdr.dest.addr, 6) == 1) { // not destined for this address + continue; + } } - } -#endif + if (lwipdev->output == etharp6_output) { // ipv6 + DEBUG_INFO("IPV6"); + + //if (memcmp(lwipdev->hwaddr, ethhdr.dest.addr, 6) == 1) { // not destined for this address + // continue; + //} + + } + */ + // by this point we have a packet destined for this interface, send it in + //if (ZeroTier::Utils::ntoh(ethhdr.type) == 0x800 || ZeroTier::Utils::ntoh(ethhdr.type) == 0x0806) { + // DEBUG_INFO("Inputting ipv4/arp into lwipdev=%p", lwipdev); + // if (lwipdev->input(p, lwipdev) != ERR_OK) { + // DEBUG_ERROR("error while feeding frame into stack interface (ipv4)"); + // } + //} + //} } } + +/****************************************************************************/ +/* Raw API driver */ +/****************************************************************************/ + +#ifdef ZT_VIRTUAL_SOCKET + +void nd6_tmr(void); + + +int rd_lwip_num_current_tcp_pcbs() +{ + // TODO: These will likely need some sort of locking protection + int count = 0; + struct tcp_pcb *pcb_ptr = tcp_active_pcbs; // PCBs that can RX/TX data + while (pcb_ptr) { + pcb_ptr = pcb_ptr->next; + count++; + } + pcb_ptr = tcp_tw_pcbs; // PCBs in TIME-WAIT state + while (pcb_ptr) { + pcb_ptr = pcb_ptr->next; + count++; + } + /* TODO + pcb_ptr = tcp_listen_pcbs; + while (pcb_ptr) { + pcb_ptr = pcb_ptr->next; + count++; + DEBUG_ERROR("FOUND --- tcp_listen_pcbs PCB COUNT = %d", count); + }*/ + pcb_ptr = tcp_bound_pcbs; // PCBs in a bound state + while (pcb_ptr) { + pcb_ptr = pcb_ptr->next; + count++; + } + return count; +} + +int rd_lwip_num_current_udp_pcbs() +{ + // TODO: These will likely need some sort of locking protection + int count = 0; + struct udp_pcb *pcb_ptr = udp_pcbs; + while (pcb_ptr) { + pcb_ptr = pcb_ptr->next; + count++; + } + return count; +} + +int rd_lwip_num_current_raw_pcbs() +{ + // TODO: These will likely need some sort of locking protection + /* + int count = 0; + struct raw_pcb *pcb_ptr = raw_pcbs; + while (pcb_ptr) { + pcb_ptr = pcb_ptr->next; + count++; + DEBUG_ERROR("FOUND --- raw_pcbs PCB COUNT = %d", count); + } + return count; + */ + return 0; +} + +int rd_lwip_num_total_pcbs() +{ + return rd_lwip_num_current_raw_pcbs() + rd_lwip_num_current_udp_pcbs() + rd_lwip_num_current_tcp_pcbs(); +} + +int rd_lwip_add_dns_nameserver(struct sockaddr *addr) +{ + return -1; +} + +int rd_lwip_del_dns_nameserver(struct sockaddr *addr) +{ + return -1; +} + +void rd_lwip_loop(VirtualTap *tap) +{ + uint64_t prev_tcp_time = 0, prev_discovery_time = 0; + while (tap->_run) + { + uint64_t now = time_now(); + uint64_t since_tcp = now - prev_tcp_time; + uint64_t since_discovery = now - prev_discovery_time; + uint64_t tcp_remaining = LWIP_TCP_TIMER_INTERVAL; + uint64_t discovery_remaining = 5000; + + // Main TCP/ETHARP timer section + if (since_tcp >= LWIP_TCP_TIMER_INTERVAL) { + prev_tcp_time = now; + tcp_tmr(); + } + else { + tcp_remaining = LWIP_TCP_TIMER_INTERVAL - since_tcp; + } + +#if defined(LIBZT_IPV4) + if (since_discovery >= DISCOVERY_INTERVAL) { + prev_discovery_time = now; + etharp_tmr(); + } else { + discovery_remaining = DISCOVERY_INTERVAL - since_discovery; + } +#endif + +#if defined(LIBZT_IPV6) + if (since_discovery >= ND6_DISCOVERY_INTERVAL) { + prev_discovery_time = now; + nd6_tmr(); + } else { + discovery_remaining = ND6_DISCOVERY_INTERVAL - since_discovery; + } + } +#endif + + tap->_phy.poll((unsigned long)std::min(tcp_remaining,discovery_remaining)); + tap->Housekeeping(); + } +} + +int rd_lwip_socket(void **pcb, int socket_family, int socket_type, int protocol) +{ + if (virt_can_provision_new_socket(socket_type) == false) { + DEBUG_ERROR("unable to create socket due to limitation of network stack, PCBs=%d", rd_lwip_num_total_pcbs()); + errno = ENOMEM; + return -1; + } + if (socket_type == SOCK_STREAM) { + struct tcp_pcb *new_tcp_PCB = tcp_new(); + *pcb = new_tcp_PCB; + tcp_nagle_disable(new_tcp_PCB); + return ERR_OK; + } + if (socket_type == SOCK_DGRAM) { + struct udp_pcb *new_udp_PCB = udp_new(); + *pcb = new_udp_PCB; + return ERR_OK; + } + errno = ENOMEM; + return -1; +} + +int rd_lwip_connect(VirtualSocket *vs, const struct sockaddr *addr, socklen_t addrlen) +{ + ip_addr_t ba; + char addrstr[INET6_ADDRSTRLEN]; + int port = 0, err = 0; +/* +#if defined(LIBZT_IPV4) + struct sockaddr_in *in4 = (struct sockaddr_in *)addr; + if (addr->sa_family == AF_INET && vs->socket_type == SOCK_STREAM) { + inet_ntop(AF_INET, &(in4->sin_addr), addrstr, INET_ADDRSTRLEN); + DEBUG_EXTRA("connecting to %s : %d", addrstr, lwip_ntohs(in4->sin_port)); + } + convert_ip(in4, &ba); + port = lwip_ntohs(in4->sin_port); +#endif +#if defined(LIBZT_IPV6) + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&addr; + in6_to_ip6((ip6_addr *)&ba, in6); + if (addr->sa_family == AF_INET6 && vs->socket_type == SOCK_STREAM) { + inet_ntop(AF_INET6, &(in6->sin6_addr), addrstr, INET6_ADDRSTRLEN); + DEBUG_EXTRA("connecting to %s : %d", addrstr, lwip_ntohs(in6->sin6_port)); + } +#endif +*/ + if (vs->socket_type == SOCK_DGRAM) { + // generates no network traffic + if ((err = udp_connect((struct udp_pcb*)vs->pcb,(ip_addr_t *)&ba,port)) < 0) { + DEBUG_ERROR("error while connecting to with UDP"); + } + udp_recv((struct udp_pcb*)vs->pcb, rd_lwip_cb_udp_recved, vs); + return ERR_OK; + } + if (vs->socket_type == SOCK_STREAM) { + struct tcp_pcb *tpcb = (struct tcp_pcb*)vs->pcb; + tcp_sent(tpcb, rd_lwip_cb_sent); + tcp_recv(tpcb, rd_lwip_cb_tcp_recved); + tcp_err(tpcb, rd_lwip_cb_err); + tcp_poll(tpcb, rd_lwip_cb_poll, LWIP_APPLICATION_POLL_FREQ); + tcp_arg(tpcb, vs); + if ((err = tcp_connect(tpcb, &ba, port, rd_lwip_cb_connected)) < 0) { + errno = err_to_errno(err); + // We should only return a value if failure happens immediately + // Otherwise, we still need to wait for a callback from lwIP. + // - This is because an ERR_OK from tcp_connect() only verifies + // that the SYN packet was enqueued onto the stack properly, + // that's it! + DEBUG_ERROR("unable to connect"); + err = -1; + } + } + return err; +} + +int rd_lwip_bind(VirtualTap *tap, VirtualSocket *vs, const struct sockaddr *addr, socklen_t addrlen) +{ + // TODO: Check case for IP_ADDR_ANY + ip_addr_t ba; + char addrstr[INET6_ADDRSTRLEN]; + memset(addrstr, 0, INET6_ADDRSTRLEN); + int port = 0, err = 0; + /* +#if defined(LIBZT_IPV4) + struct sockaddr_in *in4 = (struct sockaddr_in *)addr; + if (addr->sa_family == AF_INET) { + inet_ntop(AF_INET, &(in4->sin_addr), addrstr, INET_ADDRSTRLEN); + DEBUG_EXTRA("binding to %s : %d", addrstr, lwip_ntohs(in4->sin_port)); + } + convert_ip(in4, &ba); + port = lwip_ntohs(in4->sin_port); +#endif +#if defined(LIBZT_IPV6) + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr; + in6_to_ip6((ip6_addr *)&ba, in6); + if (addr->sa_family == AF_INET6) { + inet_ntop(AF_INET6, &(in6->sin6_addr), addrstr, INET6_ADDRSTRLEN); + DEBUG_EXTRA("binding to %s : %d", addrstr, lwip_ntohs(in6->sin6_port)); + } +#endif + if (vs->socket_type == SOCK_DGRAM) { + if ((err = udp_bind((struct udp_pcb*)vs->pcb, (const ip_addr_t *)&ba, port)) < 0) { + errno = err_to_errno(err); + err = -1; + } + else { + // set callback + udp_recv((struct udp_pcb*)vs->pcb, rd_lwip_cb_udp_recved, vs); + err = ERR_OK; + } + } + else if (vs->socket_type == SOCK_STREAM) { + if ((err = tcp_bind((struct tcp_pcb*)vs->pcb, (const ip_addr_t *)&ba, port)) < 0) { + errno = err_to_errno(err); + err = -1; + } + else { + err = ERR_OK; + } + } + */ + return err; +} + +int rd_lwip_listen(VirtualSocket *vs, int backlog) +{ + int err = 0; + struct tcp_pcb* listeningPCB; +#ifdef TCP_LISTEN_BACKLOG + listeningPCB = tcp_listen_with_backlog((struct tcp_pcb*)vs->pcb, backlog); +#else + listeningPCB = tcp_listen((struct tcp_pcb*)vs->pcb); +#endif + if (listeningPCB) { + vs->pcb = listeningPCB; + // set callback + tcp_accept(listeningPCB, rd_lwip_cb_accept); + tcp_arg(listeningPCB, vs); + err = ERR_OK; + } + else { + errno = ENOMEM; + err = -1; + } + return err; +} + +VirtualSocket* rd_lwip_accept(VirtualSocket *vs) +{ + if (vs == NULL) { + DEBUG_ERROR("invalid virtual socket"); + return NULL; + } + // Retreive first of queued VirtualSockets from parent VirtualSocket + // TODO: check multithreaded behaviour + VirtualSocket *new_vs = NULL; + if (vs->_AcceptedConnections.size()) { + new_vs = vs->_AcceptedConnections.front(); + vs->_AcceptedConnections.pop(); + } + return new_vs; +} + +int rd_lwip_read(VirtualSocket *vs, bool lwip_invoked) +{ + DEBUG_EXTRA("vs=%p", vs); + int err = 0; + if (vs == NULL) { + DEBUG_ERROR("no virtual socket"); + return -1; + } + if (lwip_invoked == false) { + DEBUG_INFO("!lwip_invoked"); + vs->tap->_tcpconns_m.lock(); + vs->_rx_m.lock(); + } + if (vs->socket_type == SOCK_STREAM && vs->RXbuf->count()) { + int wr = std::min((ssize_t)ZT_STACK_TCP_SOCKET_RX_SZ, (ssize_t)vs->RXbuf->count()); + int n = vs->tap->_phy.streamSend(vs->sock, vs->RXbuf->get_buf(), wr); + if (n > 0) { + vs->RXbuf->consume(n); + tcp_recved((struct tcp_pcb*)vs->pcb, n); + DEBUG_TRANS("TCP RX %d bytes", n); + } + } + if (vs->RXbuf->count() == 0) { + vs->tap->_phy.setNotifyWritable(vs->sock, false); // nothing else to send to the app + } + if (lwip_invoked == false) { + vs->tap->_tcpconns_m.unlock(); + vs->_rx_m.unlock(); + } + return err; +} + +int rd_lwip_write(VirtualSocket *vs, void *data, ssize_t len) +{ + int err = 0; + if (vs == NULL) { + DEBUG_ERROR("no virtual socket"); + return -1; + } + DEBUG_EXTRA("fd=%d, vs=%p, pcb=%p, pcb->state=%d, len=%d", + vs->app_fd, vs, (struct tcp_pcb*)(vs->pcb), ((struct tcp_pcb*)(vs->pcb))->state, len); + if (vs->socket_type == SOCK_DGRAM) { + // TODO: Packet re-assembly hasn't yet been tested with lwIP so UDP packets are limited to MTU-sized chunks + int udp_trans_len = std::min(len, (ssize_t)ZT_MAX_MTU); + struct pbuf * pb = pbuf_alloc(PBUF_TRANSPORT, udp_trans_len, PBUF_POOL); + if (pb == NULL) { + DEBUG_ERROR("unable to allocate new pbuf of len=%d", udp_trans_len); + return -1; + } + memcpy(pb->payload, data, udp_trans_len); + int err = udp_send((struct udp_pcb*)vs->pcb, pb); + + if (err == ERR_MEM) { + DEBUG_ERROR("error sending packet. out of memory"); + } else if (err == ERR_RTE) { + DEBUG_ERROR("could not find route to destinations address"); + } else if (err != ERR_OK) { + DEBUG_ERROR("error sending packet - %d", err); + } + pbuf_free(pb); + if (err == ERR_OK) { + return udp_trans_len; + } + } + if (vs->socket_type == SOCK_STREAM) { + // How much we are currently allowed to write to the VirtualSocket + ssize_t sndbuf = ((struct tcp_pcb*)vs->pcb)->snd_buf; + if (sndbuf == 0) { + // PCB send buffer is full, turn off readability notifications for the + // corresponding PhySocket until lwip_cb_sent() is called and confirms that there is + // now space on the buffer + DEBUG_ERROR("lwIP stack is full, sndbuf==0"); + //vs->tap->_phy.setNotifyReadable(vs->sock, false); + err = -1; + } + vs->_tx_m.lock(); + int buf_w = vs->TXbuf->write((const char*)data, len); + if (buf_w != len) { + DEBUG_ERROR("only wrote len=%d but expected to write len=%d to TX buffer", buf_w, len); + err = ZT_ERR_GENERAL_FAILURE; + } + if (vs->TXbuf->count() <= 0) { + err = -1; // nothing to write + } + if (err == ERR_OK) { + int r = std::min((ssize_t)vs->TXbuf->count(), sndbuf); + // Writes data pulled from the client's socket buffer to LWIP. This merely sends the + // data to LWIP to be enqueued and eventually sent to the network. + if (r > 0) { + err = tcp_write((struct tcp_pcb*)vs->pcb, vs->TXbuf->get_buf(), r, vs->copymode); + tcp_output((struct tcp_pcb*)vs->pcb); + if (err != ERR_OK) { + DEBUG_ERROR("error while writing to lwIP tcp_pcb, err=%d", err); + if (err == ERR_MEM) { + DEBUG_ERROR("lwIP out of memory"); + } + err = -1; + } else { + if (vs->copymode & TCP_WRITE_FLAG_COPY) { + // since we copied the data (allocated pbufs), we can consume the buffer + vs->TXbuf->consume(r); // success + DEBUG_TRANS("len=%5d tx_buf_len=%10d [VSTXBF --> NSLWIP]", err, vs->TXbuf->count()); + } + else { + // since we only processed the data by pointer reference we + // want to preserve it until it has been ACKed by the remote host + // (DO NOTHING) + } + err = ERR_OK; + } + } + } + vs->_tx_m.unlock(); + } + return err; +} + +int rd_lwip_close(VirtualSocket *vs) +{ + // requests to close non-LISTEN PCBs are handled lwip_cb_poll() + int err = -1; + if (vs == NULL) { + DEBUG_ERROR("invalid vs"); + return -1; + } + if (vs->socket_type == SOCK_STREAM) { + struct tcp_pcb *tpcb = (struct tcp_pcb*)(vs->pcb); + if (tpcb == NULL) { + DEBUG_ERROR("invalid pcb"); + return -1; + } + // should be safe to tcp_close() from application thread IF PCB is in LISTENING state (I think) + if (tpcb->state == LISTEN) { + DEBUG_EXTRA("PCB is in LISTEN, calling tcp_close() from application thread."); + tcp_accept(tpcb, NULL); + if ((err = tcp_close(tpcb)) < 0) { + DEBUG_ERROR("error while calling tcp_close, fd=%d, vs=%p, pcb=%p", vs->app_fd, vs, vs->pcb); + errno = err_to_errno(err); + err = -1; + } + return ERR_OK; + } + // handle junk state values + if (tpcb->state > TIME_WAIT) { + DEBUG_EXTRA("invalid TCP pcb state, already closed, report ERR_OK"); + return ERR_OK; + } + else { + // place a request for the stack to close this VirtualSocket's PCB + vs->set_state(VS_STATE_SHOULD_SHUTDOWN); + // wait for indication of success, this will block if the PCB can't close + while (true) { + sleep(1); + nanosleep((const struct timespec[]) {{0, (ZT_API_CHECK_INTERVAL * 1000000)}}, NULL); + DEBUG_EXTRA("checking closure state... pcb->state=%d", tpcb->state); + if (vs->get_state() == VS_STATE_CLOSED || tpcb->state == CLOSED) { + return ERR_OK; + } + } + } + } + if (vs->socket_type == SOCK_DGRAM) { + // place a request for the stack to close this VirtualSocket's PCB + vs->set_state(VS_STATE_SHOULD_SHUTDOWN); + } + return err; +} + +int rd_lwip_shutdown(VirtualSocket *vs, int how) +{ + int err=0, shut_rx=0, shut_tx=0; + if (how == SHUT_RD) { + shut_rx = 1; + } + if (how == SHUT_WR) { + shut_tx = 1; + } + if (how == SHUT_RDWR) { + shut_rx = 1; + shut_tx = 1; + } + if ((err = tcp_shutdown((tcp_pcb*)(vs->pcb), shut_rx, shut_tx) < 0)) { + DEBUG_ERROR("error while shutting down socket, fd=%d", vs->app_fd); + } + return err; +} + +/****************************************************************************/ +/* Callbacks from lwIP stack */ +/****************************************************************************/ + +// write data from processed packets from the stack to the client app +/* + With the raw API, tcp_recv() sets up to receive data via a callback function. Your callback + is delivered chains of pbufs as they become available. You have to manage extracting data + from the pbuf chain, and don't forget to watch out for multiple pbufs in a single callback: + the 'tot_len' field indicates the total length of data in the pbuf chain. You must call + tcp_recved() to tell LWIP when you have processed the received data. As with the netconn API, + you may receive more or less data than you want, and will have to either wait for further + callbacks, or hold onto excess data for later processing. + + http://lwip.wikia.com/wiki/Receiving_data_with_LWIP +*/ +err_t rd_lwip_cb_tcp_recved(void *arg, struct tcp_pcb *PCB, struct pbuf *p, err_t err) +{ + //DEBUG_INFO(); + VirtualSocket *vs = (VirtualSocket *)arg; + int tot = 0; + if (vs == NULL) { + DEBUG_ERROR("no virtual socket"); + return ERR_OK; + } + struct pbuf* q = p; + if (p == NULL) { + DEBUG_INFO("p=0x0 for pcb=%p, vs->pcb=%p, this indicates a closure. No need to call tcp_close()", PCB, vs->pcb); + vs->set_state(VS_STATE_SHOULD_SHUTDOWN); + return ERR_ABRT; + } + vs->tap->_tcpconns_m.lock(); + vs->_rx_m.lock(); + // cycle through pbufs and write them to the RX buffer + while (p != NULL) { + if (p->len <= 0) { + break; + } + int avail = ZT_TCP_RX_BUF_SZ - vs->RXbuf->count(); + int len = p->len; + if (avail < len) { + DEBUG_ERROR("not enough room (%d bytes) on RX buffer", avail); + } + // place new incoming data on ringbuffer before we try to send it to the app + memcpy(vs->RXbuf->get_buf(), p->payload, len); + vs->RXbuf->produce(len); + p = p->next; + tot += len; + } + if (tot) { + tcp_recved(PCB, tot); + DEBUG_TRANS("len=%5d rx_buf_len=%10d [NSLWIP --> VSRXBF]", tot, vs->RXbuf->count()); + int w, write_attempt_sz = vs->RXbuf->count() < ZT_MAX_MTU ? vs->RXbuf->count() : ZT_MAX_MTU; + if ((w = write(vs->sdk_fd, vs->RXbuf->get_buf(), write_attempt_sz)) < 0) { + DEBUG_ERROR("write(fd=%d)=%d, errno=%d", vs->sdk_fd, w, errno); + } + if (w > 0) { + vs->RXbuf->consume(w); + if (w < write_attempt_sz) { + DEBUG_TRANS("len=%5d rx_buf_len=%10d [VSRXBF --> APPFDS]", w, vs->RXbuf->count()); + DEBUG_EXTRA("intended to write len=%d, only wrote len=%d", write_attempt_sz, w); + } + else { + DEBUG_TRANS("len=%5d rx_buf_len=%10d [VSRXBF --> APPFDS]", w, vs->RXbuf->count()); + } + } + } + else { + DEBUG_EXTRA("warning, wrote 0 bytes"); + } + vs->tap->_tcpconns_m.unlock(); + vs->_rx_m.unlock(); + pbuf_free(q); + return ERR_OK; +} + +// callback from stack to notify driver of the successful acceptance of a connection +err_t rd_lwip_cb_accept(void *arg, struct tcp_pcb *newPCB, err_t err) +{ + VirtualSocket *vs = (VirtualSocket*)arg; + struct sockaddr_storage ss; +/* +#if defined(LIBZT_IPV4) + struct sockaddr_in *in4 = (struct sockaddr_in *)&ss; + in4->sin_addr.s_addr = newPCB->remote_ip.addr; + in4->sin_port = newPCB->remote_port; +#endif +#if defined(LIBZT_IPV6) + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&ss; + // TODO: check this + memcpy(&(in6->sin6_addr.s6_addr), &(newPCB->remote_ip), sizeof(int32_t)*4); + in6->sin6_port = newPCB->remote_port; +#endif + VirtualSocket *new_vs = new VirtualSocket(); + new_vs->socket_type = SOCK_STREAM; + new_vs->pcb = newPCB; + new_vs->tap = vs->tap; + new_vs->sock = vs->tap->_phy.wrapSocket(new_vs->sdk_fd, new_vs); + memcpy(&(new_vs->peer_addr), &ss, sizeof(new_vs->peer_addr)); + // add new VirtualSocket object to parent VirtualSocket so that we can find it via lwip_Accept() + vs->_AcceptedConnections.push(new_vs); + // set callbacks + tcp_arg(newPCB, new_vs); + tcp_recv(newPCB, rd_lwip_cb_tcp_recved); + tcp_err(newPCB, rd_lwip_cb_err); + tcp_sent(newPCB, rd_lwip_cb_sent); + tcp_poll(newPCB, rd_lwip_cb_poll, 1); + // let lwIP know that it can queue additional incoming PCBs + tcp_accepted((struct tcp_pcb*)vs->pcb); +*/ + return 0; +} + +// copy processed datagram to app socket +void rd_lwip_cb_udp_recved(void * arg, struct udp_pcb * upcb, struct pbuf * p, const ip_addr_t * addr, u16_t port) +{ + //DEBUG_EXTRA("arg(vs)=%p, pcb=%p, port=%d)", arg, upcb, port); + /* + VirtualSocket *vs = (VirtualSocket *)arg; + if (vs == NULL) { + DEBUG_ERROR("invalid virtual socket"); + return; + } + if (p == NULL) { + DEBUG_ERROR("p == NULL"); + return; + } + struct pbuf* q = p; + struct sockaddr_storage ss; + +#if defined(LIBZT_IPV4) + struct sockaddr_in *in4 = (struct sockaddr_in *)&ss; + in4->sin_addr.s_addr = addr->addr; + in4->sin_port = port; +#endif +#if defined(LIBZT_IPV6) + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&ss; + memcpy(&(in6->sin6_addr.s6_addr), &(addr->addr), sizeof(int32_t)*4); + in6->sin6_port = port; +#endif + + char udp_payload_buf[ZT_SOCKET_MSG_BUF_SZ]; + memset(udp_payload_buf, 0, sizeof(ZT_SOCKET_MSG_BUF_SZ)); + char *msg_ptr = udp_payload_buf; + int tot_len = 0; + while (p != NULL) { + if (p->len <= 0) { + break; + } + memcpy(msg_ptr, p->payload, p->len); + msg_ptr += p->len; + tot_len += p->len; + p = p->next; + } + if (tot_len > 0) { + int w = 0; + // [sz : addr : payload] + char udp_msg_buf[ZT_SOCKET_MSG_BUF_SZ]; + memset(udp_msg_buf, 0, sizeof(ZT_SOCKET_MSG_BUF_SZ)); + int32_t len = sizeof(struct sockaddr_storage) + tot_len; + int32_t msg_tot_len = sizeof(int32_t) + len; + // len: sockaddr+payload + memcpy(udp_msg_buf, &len, sizeof(int32_t)); + // sockaddr + memcpy(udp_msg_buf + sizeof(int32_t), &ss, sizeof(struct sockaddr_storage)); + // payload + memcpy(udp_msg_buf + sizeof(int32_t) + sizeof(struct sockaddr_storage), &udp_payload_buf, tot_len); + if ((w = write(vs->sdk_fd, udp_msg_buf, msg_tot_len)) < 0) { + DEBUG_ERROR("write(fd=%d)=%d, errno=%d", vs->sdk_fd, w, errno); + } + } + pbuf_free(q); + */ +} + +// callback from stack to notify driver that data was sent +err_t rd_lwip_cb_sent(void* arg, struct tcp_pcb *PCB, u16_t len) +{ + //DEBUG_EXTRA("pcb=%p", PCB); + VirtualSocket *vs = (VirtualSocket *)arg; + if (vs == NULL) { + DEBUG_ERROR("invalid vs for PCB=%p, len=%d", PCB, len); + } + if ((vs->copymode & TCP_WRITE_FLAG_COPY) == false) { + /* + From lwIP docs: + + To achieve zero-copy on transmit, the data passed to the raw API must + remain unchanged until sent. Because the send- (or write-)functions return + when the packets have been enqueued for sending, data must be kept stable + after that, too. + + This implies that PBUF_RAM/PBUF_POOL pbufs passed to raw-API send functions + must *not* be reused by the application unless their ref-count is 1. + + For no-copy pbufs (PBUF_ROM/PBUF_REF), data must be kept unchanged, too, + but the stack/driver will/must copy PBUF_REF'ed data when enqueueing, while + PBUF_ROM-pbufs are just enqueued (as ROM-data is expected to never change). + + Also, data passed to tcp_write without the copy-flag must not be changed! + + Therefore, be careful which type of PBUF you use and if you copy TCP data + or not! + */ + + // since we decided in lwip_Write() not to consume the buffere data, as it + // was not copied and was only used by pointer reference, we can now consume + // the data on the buffer since we've got an ACK back from the remote host + vs->_tx_m.lock(); + vs->TXbuf->consume(len); + vs->_tx_m.unlock(); + } + return ERR_OK; +} + +err_t rd_lwip_cb_connected(void *arg, struct tcp_pcb *PCB, err_t err) +{ + DEBUG_EXTRA("pcb=%p", PCB); + VirtualSocket *vs = (VirtualSocket *)arg; + if (vs == NULL) { + DEBUG_ERROR("invalid virtual socket"); + return -1; + } + // add to unhandled connection set for zts_connect to pick up on + vs->tap->_tcpconns_m.lock(); + vs->set_state(VS_STATE_UNHANDLED_CONNECTED); + vs->tap->_VirtualSockets.push_back(vs); + vs->tap->_tcpconns_m.unlock(); + return ERR_OK; +} + +err_t rd_lwip_cb_poll(void* arg, struct tcp_pcb *PCB) +{ + VirtualSocket *vs = (VirtualSocket *)arg; + if (vs == NULL) { + DEBUG_ERROR("invalid vs"); + return ERR_OK; // TODO: determine appropriate error value, if any + } + if (vs->socket_type == SOCK_DGRAM) { + DEBUG_INFO("fd=%d, vs=%p, pcb=%p", vs->app_fd, vs, PCB, vs->pcb); + } + + // Handle PCB closure requests (set in lwip_Close()) + if (vs->get_state() == VS_STATE_SHOULD_SHUTDOWN) { + DEBUG_EXTRA("closing pcb=%p, fd=%d, vs=%p", PCB, vs->app_fd, vs); + int err = 0; + errno = 0; + if (vs->socket_type == SOCK_DGRAM) { + udp_remove((struct udp_pcb*)vs->pcb); + } + if (vs->socket_type == SOCK_STREAM) { + if (vs->pcb) { + struct tcp_pcb* tpcb = (struct tcp_pcb*)vs->pcb; + if (tpcb->state == CLOSED) { + DEBUG_EXTRA("pcb is in CLOSED state"); + // calling tcp_close() here would be redundant + return 0; + } + //if (tpcb->state == CLOSE_WAIT) { + // DEBUG_EXTRA("pcb is in CLOSE_WAIT state"); + // // calling tcp_close() here would be redundant + //} + if (tpcb->state > TIME_WAIT) { + DEBUG_ERROR("warning, pcb=%p is in an invalid state=%d", vs->pcb, tpcb->state); + err = -1; + } + // unregister callbacks for this PCB + tcp_arg(tpcb, NULL); + if (tpcb->state == LISTEN) { + tcp_accept(tpcb, NULL); + } + else { + tcp_recv(tpcb, NULL); + tcp_sent(tpcb, NULL); + tcp_poll(tpcb, NULL, 0); + tcp_err(tpcb, NULL); + } + if ((err = tcp_close(tpcb)) < 0) { + DEBUG_ERROR("error while calling tcp_close, fd=%d, vs=%p, pcb=%p", vs->app_fd, vs, vs->pcb); + errno = err_to_errno(err); + err = -1; + } + else { + vs->set_state(VS_STATE_CLOSED); // success + } + } + } + } + + + // Handle transmission and reception of data + if (vs->socket_type == SOCK_STREAM) { + DEBUG_INFO("fd=%d, vs=%p, PCB=%p, vs->pcb=%p, vs->pcb->state=%d", vs->app_fd, vs, PCB, (struct tcp_pcb*)(vs->pcb), ((struct tcp_pcb*)(vs->pcb))->state); + if (((struct tcp_pcb*)(vs->pcb))->state == CLOSE_WAIT) { + DEBUG_EXTRA("pcb->state=CLOSE_WAIT. do nothing"); + return ERR_OK; + } + if (((struct tcp_pcb*)(vs->pcb))->state == CLOSED) { + DEBUG_EXTRA("pcb->state=CLOSED. do nothing"); + return ERR_OK; + } + // --- Check buffers to see if we need to finish reading/writing anything --- + + // TODO: Make a more generic form of each of these RX/TX blocks that can be shared + // between all polling callbacks and read write methods + + // RX + vs->_rx_m.lock(); + if (vs->RXbuf->count()) { + // this data has already been acknowledged via tcp_recved(), we merely need to + // move it off of the ringbuffer and into the client app + int w, write_attempt_sz = vs->RXbuf->count() < ZT_MAX_MTU ? vs->RXbuf->count() : ZT_MAX_MTU; + if ((w = write(vs->sdk_fd, vs->RXbuf->get_buf(), write_attempt_sz)) < 0) { + DEBUG_ERROR("write(fd=%d)=%d, errno=%d", vs->sdk_fd, w, errno); + } + if (w > 0) { + vs->RXbuf->consume(w); + if (w < write_attempt_sz) { + DEBUG_TRANS("len=%5d rx_buf_len=%10d [VSRXBF --> APPFDS]", w, vs->RXbuf->count()); + DEBUG_EXTRA("intended to write len=%d, only wrote len=%d", write_attempt_sz, w); + } + else { + DEBUG_TRANS("len=%5d rx_buf_len=%10d [VSRXBF --> APPFDS]", w, vs->RXbuf->count()); + } + } + } + vs->_rx_m.unlock(); + // No need to lock the TX buffer since lwip_Write() will lock it for us + // TX + if (vs->TXbuf->count()) { + // we previously attempted to tcp_write(), but something went wrong, this + // is where we retry + rd_lwip_write(vs, vs->TXbuf->get_buf(), vs->TXbuf->count()); + } + } + return ERR_OK; +} + +int rd_lwip_setsockopt(VirtualSocket *vs, int level, int optname, const void *optval, socklen_t optlen) +{ + int err = -1; + errno = 0; + if (vs == NULL) { + DEBUG_ERROR("invalid vs"); + return -1; + } else { + DEBUG_EXTRA("fd=%d, level=%d, optname=%d", vs->app_fd, level, optname); + } + if (level == SOL_SOCKET) + { + /* Turns on recording of debugging information. This option enables or disables debugging in the underlying + protocol modules. This option takes an int value. This is a Boolean option.*/ + if (optname == SO_DEBUG) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Specifies that the rules used in validating addresses supplied to bind() should allow reuse of local + addresses, if this is supported by the protocol. This option takes an int value. This is a Boolean option.*/ + if (optname == SO_REUSEADDR) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Keeps connections active by enabling the periodic transmission of messages, if this is supported by the + protocol. This option takes an int value. */ + if (optname == SO_KEEPALIVE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Requests that outgoing messages bypass the standard routing facilities. */ + if (optname == SO_DONTROUTE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Lingers on a close() if data is present. */ + if (optname == SO_LINGER) + { + // we do this at the VirtualSocket layer since lwIP's raw API doesn't currently have a way to do this + vs->optflags &= VS_OPT_SO_LINGER; + return 0; + } + /* Permits sending of broadcast messages, if this is supported by the protocol. This option takes an int + value. This is a Boolean option. */ + if (optname == SO_BROADCAST) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Leaves received out-of-band data (data marked urgent) inline. This option takes an int value. This is a + Boolean option. */ + if (optname == SO_OOBINLINE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Sets send buffer size. This option takes an int value. */ + if (optname == SO_SNDBUF) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Sets receive buffer size. This option takes an int value. */ + if (optname == SO_RCVBUF) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* */ + if (optname == SO_STYLE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* */ + if (optname == SO_TYPE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Get error status and clear */ + if (optname == SO_ERROR) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + } + if (level == IPPROTO_IP) + { + if (optname == IP_ADD_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_ADD_SOURCE_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_BIND_ADDRESS_NO_PORT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_BLOCK_SOURCE) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_DROP_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_DROP_SOURCE_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_FREEBIND) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_HDRINCL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MSFILTER) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MTU) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MTU_DISCOVER) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MULTICAST_ALL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MULTICAST_IF) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MULTICAST_LOOP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MULTICAST_TTL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_NODEFRAG) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_OPTIONS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_PKTINFO) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVOPTS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVORIGDSTADDR) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVTOS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVTTL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RETOPTS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_ROUTER_ALERT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_TOS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_TRANSPARENT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_TTL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_UNBLOCK_SOURCE) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + // TODO + return -1; + } + if (level == IPPROTO_TCP) + { + struct tcp_pcb *pcb = (struct tcp_pcb*)(vs->pcb); + if (pcb == NULL) { + return -1; + } + /* If set, don't send out partial frames. */ + if (optname == TCP_CORK) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Allow a listener to be awakened only when data arrives on the socket. */ + if (optname == TCP_DEFER_ACCEPT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Used to collect information about this socket. The kernel returns a struct tcp_info as defined in the + file /usr/include/linux/tcp.h.*/ + if (optname == TCP_INFO) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* The maximum number of keepalive probes TCP should send before dropping the connection.*/ + if (optname == TCP_KEEPCNT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* The time (in seconds) the connection needs to remain idle before TCP starts sending keepalive probes, + if the socket option SO_KEEPALIVE has been set on this socket. */ + if (optname == TCP_KEEPIDLE) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* The time (in seconds) between individual keepalive probes.*/ + if (optname == TCP_KEEPINTVL) { + // TODO + return -1; + } + /* The lifetime of orphaned FIN_WAIT2 state sockets. */ + if (optname == TCP_LINGER2) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* The maximum segment size for outgoing TCP packets. */ + if (optname == TCP_MAXSEG) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* If set, disable the Nagle algorithm. */ + if (optname == TCP_NODELAY) { + int enable_nagle = *((const int*)optval); + if (enable_nagle == true) { + tcp_nagle_enable(pcb); + } else { + tcp_nagle_disable(pcb); + } + return 0; + } + /* Enable quickack mode if set or disable quickack mode if cleared. */ + if (optname == TCP_QUICKACK) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Set the number of SYN retransmits that TCP should send before aborting the attempt to connect. It + cannot exceed 255. */ + if (optname == TCP_SYNCNT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Bound the size of the advertised window to this value. The kernel imposes a minimum size of + SOCK_MIN_RCVBUF/2. */ + if (optname == TCP_WINDOW_CLAMP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + } + if (level == IPPROTO_UDP) + { + /*If this option is enabled, then all data output on this socket is accumulated into a single + datagram that is transmitted when the option is disabled. */ + if (optname == UDP_CORK) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + } + return err; +} + +int rd_lwip_getsockopt(VirtualSocket *vs, int level, int optname, void *optval, socklen_t *optlen) +{ + int err = -1, optval_tmp = 0; + errno = 0; + if (vs == NULL) { + DEBUG_ERROR("invalid vs"); + return -1; + } else { + DEBUG_EXTRA("fd=%d, level=%d, optname=%d", vs->app_fd, level, optname); + } + if (level == SOL_SOCKET) + { + /* Turns on recording of debugging information. This option enables or disables debugging in the underlying + protocol modules. This option takes an int value. This is a Boolean option.*/ + if (optname == SO_DEBUG) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Specifies that the rules used in validating addresses supplied to bind() should allow reuse of local + addresses, if this is supported by the protocol. This option takes an int value. This is a Boolean option.*/ + if (optname == SO_REUSEADDR) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Keeps connections active by enabling the periodic transmission of messages, if this is supported by the + protocol. This option takes an int value. */ + if (optname == SO_KEEPALIVE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Requests that outgoing messages bypass the standard routing facilities. */ + if (optname == SO_DONTROUTE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Get SO_LINGER flag value */ + if (optname == SO_LINGER) + { + // we do this at the VirtualSocket layer since lwIP's raw API doesn't currently have a way to do this + optval_tmp = (vs->optflags & VS_OPT_SO_LINGER); + memcpy(optval, &optval_tmp, *optlen); + return 0; + } + /* Permits sending of broadcast messages, if this is supported by the protocol. This option takes an int + value. This is a Boolean option. */ + if (optname == SO_BROADCAST) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Leaves received out-of-band data (data marked urgent) inline. This option takes an int value. This is a + Boolean option. */ + if (optname == SO_OOBINLINE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Sets send buffer size. This option takes an int value. */ + if (optname == SO_SNDBUF) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Sets receive buffer size. This option takes an int value. */ + if (optname == SO_RCVBUF) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* */ + if (optname == SO_STYLE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* */ + if (optname == SO_TYPE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Get error status and clear */ + if (optname == SO_ERROR) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + } + if (level == IPPROTO_IP) + { + if (optname == IP_ADD_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_ADD_SOURCE_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_BIND_ADDRESS_NO_PORT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_BLOCK_SOURCE) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_DROP_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_DROP_SOURCE_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_FREEBIND) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_HDRINCL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MSFILTER) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MTU) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MTU_DISCOVER) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MULTICAST_ALL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MULTICAST_IF) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MULTICAST_LOOP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MULTICAST_TTL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_NODEFRAG) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_OPTIONS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_PKTINFO) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVOPTS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVORIGDSTADDR) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVTOS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVTTL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RETOPTS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_ROUTER_ALERT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_TOS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_TRANSPARENT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_TTL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_UNBLOCK_SOURCE) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + // TODO + return -1; + } + if (level == IPPROTO_TCP) + { + struct tcp_pcb *pcb = (struct tcp_pcb*)(vs->pcb); + if (pcb == NULL) { + return -1; + } + /* If set, don't send out partial frames. */ + if (optname == TCP_CORK) { + // TODO + errno = ENOPROTOOPT; + err = -1; + } + /* Allow a listener to be awakened only when data arrives on the socket. */ + if (optname == TCP_DEFER_ACCEPT) { + // TODO + errno = ENOPROTOOPT; + err = -1; + } + /* Used to collect information about this socket. */ + if (optname == TCP_INFO) { + // TODO + errno = ENOPROTOOPT; + err = -1; + } + /* The maximum number of keepalive probes TCP should send before dropping the connection.*/ + if (optname == TCP_KEEPCNT) { + // TODO + errno = ENOPROTOOPT; + err = -1; + } + /* The time (in seconds) the connection needs to remain idle before TCP starts sending keepalive probes, + if the socket option SO_KEEPALIVE has been set on this socket. */ + if (optname == TCP_KEEPIDLE) { + // TODO + errno = ENOPROTOOPT; + err = -1; + } + /* The time (in seconds) between individual keepalive probes.*/ + if (optname == TCP_KEEPINTVL) { + // TODO + err = -1; + } + /* The lifetime of orphaned FIN_WAIT2 state sockets. */ + if (optname == TCP_LINGER2) { + // TODO + errno = ENOPROTOOPT; + err = -1; + } + /* The maximum segment size for outgoing TCP packets. */ + if (optname == TCP_MAXSEG) { + // TODO + errno = ENOPROTOOPT; + err = -1; + } + /* Get value of Nagle algorithm flag */ + if (optname == TCP_NODELAY) { + optval_tmp = tcp_nagle_disabled(pcb); + memcpy(optval, &optval_tmp, *optlen); + err = 0; + } + /* Enable quickack mode if set or disable quickack mode if cleared. */ + if (optname == TCP_QUICKACK) { + // TODO + errno = ENOPROTOOPT; + err = -1; + } + /* Set the number of SYN retransmits that TCP should send before aborting the attempt to connect. It + cannot exceed 255. */ + if (optname == TCP_SYNCNT) { + // TODO + errno = ENOPROTOOPT; + err = -1; + } + /* Bound the size of the advertised window to this value. The kernel imposes a minimum size of + SOCK_MIN_RCVBUF/2. */ + if (optname == TCP_WINDOW_CLAMP) { + // TODO + errno = ENOPROTOOPT; + err = -1; + } + } + if (level == IPPROTO_UDP) + { + /* If this option is enabled, then all data output on this socket is accumulated into a single + datagram that is transmitted when the option is disabled. */ + if (optname == UDP_CORK) { + // TODO + errno = ENOPROTOOPT; + err = -1; + } + } + return err; +} + +void rd_lwip_cb_err(void *arg, err_t err) +{ + VirtualSocket *vs = (VirtualSocket *)arg; + if (vs == NULL) { + DEBUG_ERROR("err=%d, invalid virtual socket", err); + errno = -1; + } + if (vs->socket_type == SOCK_STREAM) { + DEBUG_ERROR("vs=%p, pcb=%p, pcb->state=%d, fd=%d, err=%d", vs, vs->pcb, ((struct tcp_pcb*)(vs->pcb))->state, vs->app_fd, err); + } + if (vs->socket_type == SOCK_DGRAM) { + DEBUG_ERROR("vs=%p, pcb=%p, fd=%d, err=%d", vs, vs->pcb, vs->app_fd, err); + } + switch(err) + { + case ERR_MEM: // -1 + DEBUG_ERROR("ERR_MEM->ENOMEM, Out of memory error."); + break; + case ERR_BUF: // -2 + DEBUG_ERROR("ERR_BUF->ENOBUFS, Buffer error."); + break; + case ERR_TIMEOUT: // -3 + DEBUG_ERROR("ERR_TIMEOUT->ETIMEDOUT, Timeout."); + break; + case ERR_RTE: // -4 + DEBUG_ERROR("ERR_RTE->ENETUNREACH, Routing problem."); + break; + case ERR_INPROGRESS: // -5 + DEBUG_ERROR("ERR_INPROGRESS->EINPROGRESS, Operation in progress."); + break; + case ERR_VAL: // -6 + DEBUG_ERROR("ERR_VAL->EINVAL, Illegal value."); + break; + case ERR_WOULDBLOCK: // -7 + DEBUG_ERROR("ERR_WOULDBLOCK->EWOULDBLOCK, Operation would block."); + break; + case ERR_USE: // -8 + DEBUG_ERROR("ERR_USE->EADDRINUSE, Address in use."); + break; + case ERR_ALREADY: // -9 ? + DEBUG_ERROR("ERR_ALREADY->EISCONN, Already connecting."); + break; + case ERR_ISCONN: // -10 + DEBUG_ERROR("ERR_ISCONN->EISCONN, Already connected"); + break; + case ERR_CONN: // -11 ? + DEBUG_ERROR("ERR_CONN->EISCONN, Not connected"); + break; + case ERR_IF: // -12 + DEBUG_ERROR("ERR_IF, Low-level netif error."); + break; + case ERR_ABRT: // -13 + DEBUG_ERROR("ERR_ABRT, Connection aborted."); + break; + case ERR_RST: // -14 + DEBUG_ERROR("ERR_RST, Connection reset."); + break; + case ERR_CLSD: // -15 + DEBUG_ERROR("ERR_CLSD, Connection closed."); + break; + case ERR_ARG: // -16 + DEBUG_ERROR("ERR_ARG, Illegal argument."); + break; + default: + break; + } + errno = err_to_errno(err); +} + +#include "lwip/ip_addr.h" +#include + +#include "lwip/ip_addr.h" +#define ip4_addr1b(ipaddr) (((u8_t*)(ipaddr))[0]) +#define ip4_addr2b(ipaddr) (((u8_t*)(ipaddr))[1]) +#define ip4_addr3b(ipaddr) (((u8_t*)(ipaddr))[2]) +#define ip4_addr4b(ipaddr) (((u8_t*)(ipaddr))[3]) +inline void convert_ip(struct sockaddr_in * addr, struct ip4_addr *addr4); + +/** + * Convert from standard sockaddr address to ip4_addr + */ +inline void convert_ip(struct sockaddr_in *addr, struct ip4_addr *addr4) +{ + struct sockaddr_in *ipv4 = addr; + short a = ip4_addr1b(&(ipv4->sin_addr)); + short b = ip4_addr2b(&(ipv4->sin_addr)); + short c = ip4_addr3b(&(ipv4->sin_addr)); + short d = ip4_addr4b(&(ipv4->sin_addr)); + IP4_ADDR(addr4, a,b,c,d); +} + +#define IP6_ADDR2(ipaddr, a,b,c,d,e,f,g,h) do { (ipaddr)->addr[0] = ZeroTier::Utils::hton((u32_t)((a & 0xffff) << 16) | (b & 0xffff)); \ + (ipaddr)->addr[1] = ZeroTier::Utils::hton(((c & 0xffff) << 16) | (d & 0xffff)); \ + (ipaddr)->addr[2] = ZeroTier::Utils::hton(((e & 0xffff) << 16) | (f & 0xffff)); \ + (ipaddr)->addr[3] = ZeroTier::Utils::hton(((g & 0xffff) << 16) | (h & 0xffff)); } while(0) + +/** + * Convert from standard IPV6 address structure to an lwIP native structure + */ +inline void in6_to_ip6(ip6_addr *ba, struct sockaddr_in6 *in6) +{ + uint8_t *ip = &(in6->sin6_addr).s6_addr[0]; + IP6_ADDR2(ba, + (((ip[ 0] & 0xffff) << 8) | ((ip[ 1]) & 0xffff)), + (((ip[ 2] & 0xffff) << 8) | ((ip[ 3]) & 0xffff)), + (((ip[ 4] & 0xffff) << 8) | ((ip[ 5]) & 0xffff)), + (((ip[ 6] & 0xffff) << 8) | ((ip[ 7]) & 0xffff)), + (((ip[ 8] & 0xffff) << 8) | ((ip[ 9]) & 0xffff)), + (((ip[10] & 0xffff) << 8) | ((ip[11]) & 0xffff)), + (((ip[12] & 0xffff) << 8) | ((ip[13]) & 0xffff)), + (((ip[14] & 0xffff) << 8) | ((ip[15]) & 0xffff)) + ); +} + +#endif // ZT_VIRTUAL_SOCKET +#endif // STACK_LWIP diff --git a/src/picoTCP.cpp b/src/picoTCP.cpp new file mode 100644 index 0000000..fc715c2 --- /dev/null +++ b/src/picoTCP.cpp @@ -0,0 +1,1979 @@ +/* + * ZeroTier SDK - Network Virtualization Everywhere + * Copyright (C) 2011-2017 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 . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +/** + * @file + * + * picoTCP stack driver + */ + +#include "libztDefs.h" + +#ifdef STACK_PICO + +#include +#include + +#include "pico_eth.h" +#include "pico_stack.h" +#include "pico_ipv4.h" +#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" +#include "pico_tcp.h" +#include "pico_dns_client.h" + +int errno; + +#include "libzt.h" +#include "SysUtils.h" +#include "Utilities.h" +#include "VirtualTap.h" +#include "picoTCP.h" +#include "RingBuffer.h" +#include "VirtualSocket.h" +#include "VirtualBindingPair.h" +#include "VirtualSocketLayer.h" +#include "ZT1Service.h" + +#include "Utils.hpp" +#include "Mutex.hpp" +#include "Constants.hpp" +#include "Phy.hpp" +#include "InetAddress.hpp" + +using namespace ZeroTier; + +int pico_ipv4_to_string(PICO_IPV4_TO_STRING_SIG); +extern "C" int pico_stack_init(void); +extern "C" void pico_stack_tick(void); +extern "C" int pico_ipv4_link_add(PICO_IPV4_LINK_ADD_SIG); +extern "C" int pico_ipv4_route_add(PICO_IPV4_ROUTE_ADD_SIG); +extern "C" int pico_ipv4_route_del(PICO_IPV4_ROUTE_DEL_SIG); +extern "C" int pico_device_init(PICO_DEVICE_INIT_SIG); +extern "C" int pico_string_to_ipv4(PICO_STRING_TO_IPV4_SIG); +extern "C" int pico_string_to_ipv6(PICO_STRING_TO_IPV6_SIG); +extern "C" int pico_socket_recvfrom(PICO_SOCKET_RECVFROM_SIG); +extern "C" struct pico_socket * pico_socket_open(PICO_SOCKET_OPEN_SIG); +extern "C" int pico_socket_connect(PICO_SOCKET_CONNECT_SIG); +extern "C" int pico_socket_listen(PICO_SOCKET_LISTEN_SIG); +extern "C" int pico_socket_write(PICO_SOCKET_WRITE_SIG); +extern "C" int pico_socket_close(PICO_SOCKET_CLOSE_SIG); +extern "C" struct pico_ipv6_link * pico_ipv6_link_add(PICO_IPV6_LINK_ADD_SIG); +extern "C" int pico_dns_client_nameserver(PICO_DNS_CLIENT_NAMESERVER_SIG); + +/* +int pico_stack_recv(PICO_STACK_RECV_SIG); +int pico_icmp4_ping(PICO_ICMP4_PING_SIG); +int pico_socket_setoption(PICO_SOCKET_SETOPTION_SIG); +uint32_t pico_timer_add(PICO_TIMER_ADD_SIG); +int pico_socket_send(PICO_SOCKET_SEND_SIG); +int pico_socket_sendto(PICO_SOCKET_SENDTO_SIG); +int pico_socket_recv(PICO_SOCKET_RECV_SIG); +int pico_socket_bind(PICO_SOCKET_BIND_SIG); +int pico_socket_read(PICO_SOCKET_READ_SIG); +int pico_socket_shutdown(PICO_SOCKET_SHUTDOWN_SIG); +struct pico_socket * pico_socket_accept(PICO_SOCKET_ACCEPT_SIG); +*/ + +extern std::vector vtaps; + +/* + * Whether our picoTCP device has been initialized + */ +static bool picodev_initialized; + +struct pico_device picodev; +ZeroTier::Mutex _picostack_driver_lock; + +bool pico_init_interface(VirtualTap *tap) +{ + bool err = false; + _picostack_driver_lock.lock(); + // give right to vtap to start the stack + // only one stack loop is permitted + if (picodev_initialized == false) { + tap->should_start_stack = true; + picodev.send = rd_pico_eth_tx; // tx + picodev.poll = rd_pico_eth_poll; // calls pico_eth_rx + picodev.mtu = tap->_mtu; + picodev.tap = tap; + uint8_t mac[PICO_SIZE_ETH]; + tap->_mac.copyTo(mac, PICO_SIZE_ETH); + if (pico_device_init(&picodev, tap->vtap_abbr_name, mac) != 0) { + DEBUG_ERROR("dev init failed"); + err = false; + } + picodev_initialized = true; + err = true; + } + _picostack_driver_lock.unlock(); + return err; +} + +bool pico_register_address(VirtualTap *tap, const InetAddress &ip) +{ + _picostack_driver_lock.lock(); + bool err = false; + char ipbuf[INET6_ADDRSTRLEN]; + uint8_t hwaddr[6]; + // register addresses + if (ip.isV4()) { + struct pico_ip4 ipaddr, netmask; + ipaddr.addr = *((uint32_t *)ip.rawIpData()); + netmask.addr = *((uint32_t *)ip.netmask().rawIpData()); + pico_ipv4_link_add(&picodev, ipaddr, netmask); + DEBUG_INFO("addr=%s", ip.toString(ipbuf)); + tap->_mac.copyTo(hwaddr, 6); + char macbuf[ZT_MAC_ADDRSTRLEN]; + mac2str(macbuf, ZT_MAC_ADDRSTRLEN, hwaddr); + DEBUG_INFO("mac=%s", macbuf); + err = true; + } + if (ip.isV6()) { + char ipv6_str[INET6_ADDRSTRLEN], nm_str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, ip.rawIpData(), ipv6_str, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, ip.netmask().rawIpData(), nm_str, INET6_ADDRSTRLEN); + struct pico_ip6 ipaddr, netmask; + pico_string_to_ipv6(ipv6_str, ipaddr.addr); + pico_string_to_ipv6(nm_str, netmask.addr); + pico_ipv6_link_add(&picodev, ipaddr, netmask); + DEBUG_INFO("addr=%s", ipv6_str); + tap->_mac.copyTo(hwaddr, 6); + char macbuf[ZT_MAC_ADDRSTRLEN]; + mac2str(macbuf, ZT_MAC_ADDRSTRLEN, hwaddr); + DEBUG_INFO("mac=%s", macbuf); + err = true; + } + _picostack_driver_lock.unlock(); + return err; +} + +// TODO: +// pico_ipv6_route_add +// pico_ipv6_route_del + +bool rd_pico_route_add(VirtualTap *tap, const InetAddress &addr, const InetAddress &nm, const InetAddress &gw, int metric) +{ + struct pico_ipv4_link *link = NULL; + struct pico_ip4 address; + address.addr = *((uint32_t *)addr.rawIpData()); + struct pico_ip4 netmask; + netmask.addr = *((uint32_t *)nm.rawIpData()); + struct pico_ip4 gateway; + gateway.addr = *((uint32_t *)gw.rawIpData()); + int err = pico_ipv4_route_add(address, netmask, gateway, metric, link); + if (err) { + DEBUG_ERROR("err=%d, %s", err, beautify_pico_error(pico_err)); + } + return err; +} + +bool rd_pico_route_del(VirtualTap *tap, const InetAddress &addr, const InetAddress &nm, int metric) +{ + struct pico_ip4 address; + address.addr = *((uint32_t *)addr.rawIpData()); + struct pico_ip4 netmask; + netmask.addr = *((uint32_t *)nm.rawIpData()); + int err = pico_ipv4_route_del(address, netmask, metric); + if (err) { + DEBUG_ERROR("err=%d, %s", err, beautify_pico_error(pico_err)); + } + return err; +} + +int rd_pico_add_dns_nameserver(struct sockaddr *addr) +{ + int err = errno = 0; + // TODO: De-complexify this + struct pico_ip4 ns; + memset(&ns, 0, sizeof (struct pico_ip4)); + struct sockaddr_in *in4 = (struct sockaddr_in*)addr; + char ipv4_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, (const void *)&in4->sin_addr.s_addr, ipv4_str, INET_ADDRSTRLEN); + uint32_t ipval = 0; + pico_string_to_ipv4(ipv4_str, &ipval); + ns.addr = ipval; + if ((err = pico_dns_client_nameserver(&ns, PICO_DNS_NS_ADD)) < 0) { + DEBUG_ERROR("error while adding DNS nameserver, err=%d, pico_err=%d, %s", + err, pico_err, beautify_pico_error(pico_err)); + map_pico_err_to_errno(pico_err); + } + return err; +} + +int rd_pico_del_dns_nameserver(struct sockaddr *addr) +{ + int err = errno = 0; + // TODO: De-complexify this + struct pico_ip4 ns; + memset(&ns, 0, sizeof (struct pico_ip4)); + struct sockaddr_in *in4 = (struct sockaddr_in*)addr; + char ipv4_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, (const void *)&in4->sin_addr.s_addr, ipv4_str, INET_ADDRSTRLEN); + uint32_t ipval = 0; + pico_string_to_ipv4(ipv4_str, &ipval); + ns.addr = ipval; + if ((err = pico_dns_client_nameserver(&ns, PICO_DNS_NS_DEL)) < 0) { + DEBUG_ERROR("error while removing DNS nameserver, err=%d, pico_err=%d, %s", + err, pico_err, beautify_pico_error(pico_err)); + } + return err; +} + +void rd_pico_loop(VirtualTap *tap) +{ + while (tap->_run) + { + tap->_phy.poll(ZT_PHY_POLL_INTERVAL); + pico_stack_tick(); + tap->Housekeeping(); + } +} + +// from stack socket to app socket +void rd_pico_cb_tcp_read(VirtualTap *tap, struct pico_socket *s) +{ + VirtualSocket *vs = (VirtualSocket*)(((VirtualBindingPair*)s->priv)->vs); + if (vs == NULL) { + DEBUG_ERROR("s->priv yielded no valid vs"); + return; + } + Mutex::Lock _l(vs->_rx_m); + if (tap == NULL) { + DEBUG_ERROR("invalid tap"); + return; + } + if (vs == NULL) { + DEBUG_ERROR("invalid vs"); + return; + } + int r; + uint16_t port = 0; + union { + struct pico_ip4 ip4; + struct pico_ip6 ip6; + } peer; + + do { + int n = 0; + int avail = ZT_TCP_RX_BUF_SZ - vs->RXbuf->count(); + if (avail) { + r = pico_socket_recvfrom(s, vs->RXbuf->get_buf(), ZT_STACK_SOCKET_RD_MAX, + (void *)&peer.ip4.addr, &port); + if (r > 0) + { + vs->RXbuf->produce(r); + n = tap->_phy.streamSend(vs->sock, vs->RXbuf->get_buf(), r); + + if (n>0) + vs->RXbuf->consume(n); + } + if (vs->RXbuf->count() == 0) { + tap->_phy.setNotifyWritable(vs->sock, false); + } + else { + tap->_phy.setNotifyWritable(vs->sock, true); + } + } + else { + //tap->_phy.setNotifyWritable(vs->sock, false); + DEBUG_ERROR("not enough space left on I/O RX buffer for pico_socket(%p)", s); + } + } + while (r > 0); +} + +// from stack socket to app socket +void rd_pico_cb_udp_read(VirtualTap *tap, struct pico_socket *s) +{ + // DEBUG_INFO(); + VirtualSocket *vs = (VirtualSocket*)(((VirtualBindingPair*)s->priv)->vs); + if (vs == NULL) { + DEBUG_ERROR("s->priv yielded no valid vs"); + return; + } + Mutex::Lock _l(vs->_rx_m); + if (tap == NULL) { + DEBUG_ERROR("invalid tap"); + return; + } + if (vs == NULL) { + DEBUG_ERROR("invalid vs"); + return; + } + + uint16_t port = 0; + union { + struct pico_ip4 ip4; + struct pico_ip6 ip6; + } peer; + int r = 0, w = 0; + // TODO: Consolidate this + if (vs->socket_family == AF_INET) { + struct sockaddr_in in4; + char udp_payload_buf[ZT_MAX_MTU]; + if ((r = pico_socket_recvfrom(s, udp_payload_buf, ZT_SDK_MTU, (void *)&peer.ip4.addr, &port)) < 0) { + DEBUG_ERROR("err=%d, %s", r, beautify_pico_error(pico_err)); + } + in4.sin_addr.s_addr = peer.ip4.addr; + in4.sin_port = port; + // immediately attempt to write addr and payload to app socket. The idea is that the zts_recvfrom() has + // been called and will pick this up and correctly handle it + char udp_msg_buf[ZT_SOCKET_MSG_BUF_SZ]; // [sz : addr : payload] + int32_t len = sizeof(struct sockaddr_storage) + r; + int32_t tot_len = sizeof(int32_t) + len; + memcpy(udp_msg_buf, &len, sizeof(int32_t)); // len: sockaddr+payload + memcpy(udp_msg_buf + sizeof(int32_t), &in4, sizeof(struct sockaddr_storage)); // sockaddr + memcpy(udp_msg_buf + sizeof(int32_t) + sizeof(struct sockaddr_storage), &udp_payload_buf, r); // payload + if ((w = write(vs->sdk_fd, udp_msg_buf, tot_len)) < 0) { + DEBUG_ERROR("write()=%d, errno=%d", w, errno); + } + } + if (vs->socket_family == AF_INET6) { + struct sockaddr_in6 in6; + char udp_payload_buf[ZT_MAX_MTU]; + if ((r = pico_socket_recvfrom(s, udp_payload_buf, ZT_SDK_MTU, (void *)&peer.ip6.addr, &port)) < 0) { + DEBUG_ERROR("err=%d, %s", r, beautify_pico_error(pico_err)); + } + memcpy(&(in6.sin6_addr.s6_addr), &(peer.ip6.addr), sizeof(peer.ip6.addr)); + in6.sin6_port = port; + // immediately attempt to write addr and payload to app socket. The idea is that the zts_recvfrom() has + // been called and will pick this up and correctly handle it + char udp_msg_buf[ZT_SOCKET_MSG_BUF_SZ]; // [sz : addr : payload] + int32_t len = sizeof(struct sockaddr_storage) + r; + int32_t tot_len = sizeof(int32_t) + len; + memcpy(udp_msg_buf, &len, sizeof(int32_t)); // len: sockaddr+payload + memcpy(udp_msg_buf + sizeof(int32_t), &in6, sizeof(struct sockaddr_storage)); // sockaddr + memcpy(udp_msg_buf + sizeof(int32_t) + sizeof(struct sockaddr_storage), &udp_payload_buf, r); // payload + if ((w = write(vs->sdk_fd, udp_msg_buf, tot_len)) < 0) { + DEBUG_ERROR("write()=%d, errno=%d", w, errno); + } + } +} + +void rd_pico_cb_tcp_write(VirtualTap *tap, struct pico_socket *s) +{ + VirtualSocket *vs = (VirtualSocket*)(((VirtualBindingPair*)s->priv)->vs); + if (vs == NULL) { + DEBUG_EXTRA("vs == NULL"); + return; + } + struct pico_socket *ps = (struct pico_socket*)(vs->pcb); + if (ps != s) { + DEBUG_ERROR("ps != s, bad callback"); + return; + } + // we will get the vs->TXBuf->get_buf() reference from within pico_Write + rd_pico_write(vs, NULL, vs->TXbuf->count()); +} + +void rd_pico_cb_socket_ev(uint16_t ev, struct pico_socket *s) +{ + int err = 0; + //DEBUG_EXTRA("s=%p, s->state=%d %s", s, s->state, beautify_pico_state(s->state)); + + // --- handle error events --- + + // PICO_SOCK_EV_FIN - triggered when the socket is closed. No further communication is + // possible from this point on the socket. + if (ev & PICO_SOCK_EV_FIN) { + DEBUG_EXTRA("PICO_SOCK_EV_FIN (socket closed), picosock=%p", s); + //DEBUG_EXTRA("PICO_SOCK_EV_FIN (socket closed), picosock=%p, vs=%p, app_fd=%d, sdk_fd=%d", s, vs, vs->app_fd, vs->sdk_fd); + } + + // PICO_SOCK_EV_ERR - triggered when an error occurs. + if (ev & PICO_SOCK_EV_ERR) { + if (pico_err == PICO_ERR_ECONNRESET) { + DEBUG_ERROR("PICO_ERR_ECONNRESET"); + } + //DEBUG_ERROR("PICO_SOCK_EV_ERR, err=%s, picosock=%p, app_fd=%d, sdk_fd=%d", + // beautify_pico_error(pico_err), s, vs->app_fd, vs->sdk_fd); + } + // PICO_SOCK_EV_CLOSE - triggered when a FIN segment is received (TCP only). This event + // indicates that the oher endpont has closed the VirtualSocket, so the local TCP layer is only + // allowed to send new data until a local shutdown or close is initiated. PicoTCP is able to + // keep the VirtualSocket half-open (only for sending) after the FIN packet has been received, + // allowing new data to be sent in the TCP CLOSE WAIT state. + + VirtualBindingPair *vbp = (VirtualBindingPair*)(s->priv); + if (vbp == NULL) { + DEBUG_ERROR("s->priv yielded no valid vbp"); + return; + } + VirtualTap *tap = static_cast(vbp->tap); + VirtualSocket *vs = static_cast(vbp->vs); + + if (ev & PICO_SOCK_EV_CLOSE) { + vs->set_state(VS_STATE_CLOSED); + if ((err = pico_socket_shutdown(s, PICO_SHUT_RDWR)) < 0) { + DEBUG_ERROR("error while shutting down socket"); + } + if ((err = pico_socket_close(s)) < 0) { + DEBUG_ERROR("pico_socket_close()=%d, pico_err=%d, %s", err, pico_err, beautify_pico_error(pico_err)); + } + DEBUG_EXTRA("PICO_SOCK_EV_CLOSE (socket closure) err=%d (%s), picosock=%p", pico_err, beautify_pico_error(pico_err), s); + return; + } + + // --- handle non-error events --- + + if (vs == NULL) { + DEBUG_ERROR("invalid VirtualSocket"); + return; + } + // PICO_SOCK_EV - triggered when VirtualSocket is established (TCP only). This event is + // received either after a successful call to pico socket vsect to indicate that the VirtualSocket + // has been established, or on a listening socket, indicating that a call to pico socket accept + // may now be issued in order to accept the incoming VirtualSocket from a remote host. + if (ev & PICO_SOCK_EV_CONN) { + DEBUG_EXTRA("PICO_SOCK_EV_CONN"); + if (vs->get_state() == VS_STATE_LISTENING) { + uint16_t port; + struct pico_socket *client_psock = nullptr; + struct pico_ip4 orig4; + struct pico_ip6 orig6; + if (vs->socket_family == AF_INET) { // NOTE: p->net->proto_number == PICO_PROTO_IPV4 + client_psock = pico_socket_accept(s, &orig4, &port); + } + if (vs->socket_family == AF_INET6) { // NOTE: p->net->proto_number == PICO_PROTO_IPV4 + client_psock = pico_socket_accept(s, &orig6, &port); + } + if (client_psock == NULL) { + DEBUG_ERROR("pico_socket_accept(): pico_socket=%p, pico_err=%d, %s", s, pico_err, beautify_pico_error(pico_err)); + return; + } + // Create a new VirtualSocket and add it to the queue, + // some time in the future a call to zts_multiplex_accept() will pick up + // this new VirtualSocket, add it to the VirtualSocket list and return its + // VirtualSocket->sock to the application + VirtualSocket *new_vs = new VirtualSocket(); + new_vs->socket_type = SOCK_STREAM; + struct pico_socket *new_ps = (struct pico_socket*)(new_vs->pcb); + new_ps = client_psock; + // TODO: Condense this + if (vs->socket_family == AF_INET) { + char addrstr[INET_ADDRSTRLEN]; + struct sockaddr_storage ss4; + struct sockaddr_in *in4 = (struct sockaddr_in *)&ss4; + in4->sin_addr.s_addr = orig4.addr; + in4->sin_port = Utils::hton(port); + memcpy(&(new_vs->peer_addr), in4, sizeof(new_vs->peer_addr)); + inet_ntop(AF_INET, &(in4->sin_addr), addrstr, INET_ADDRSTRLEN); + DEBUG_EXTRA("accepted connection from: %s : %d", addrstr, port); + ZeroTier::InetAddress inet; + inet.fromString(addrstr); + new_vs->tap = getTapByAddr(&inet); // assign to tap based on incoming address + } + if (vs->socket_family == AF_INET6) { + char addrstr[INET6_ADDRSTRLEN]; + struct sockaddr_in6 in6; + memcpy(&(in6.sin6_addr.s6_addr), &orig6, sizeof(in6.sin6_addr.s6_addr)); + in6.sin6_port = Utils::hton(port); + memcpy(&(new_vs->peer_addr), &in6, sizeof(new_vs->peer_addr)); + inet_ntop(AF_INET6, &(in6.sin6_addr), addrstr, INET6_ADDRSTRLEN); + DEBUG_EXTRA("accepted connection from: %s : %d", addrstr, port); + ZeroTier::InetAddress inet; + inet.fromString(addrstr); + new_vs->tap = getTapByAddr(&inet); // assign to tap based on incoming address + } + if (new_vs->tap == NULL) { + DEBUG_ERROR("no valid VirtualTap could be found"); + return; + } + // Assign this VirtualSocket to the appropriate VirtualTap + new_ps->priv = new VirtualBindingPair(new_vs->tap,new_vs); + new_vs->tap->addVirtualSocket(new_vs); + vs->_AcceptedConnections.push(new_vs); + new_vs->sock = new_vs->tap->_phy.wrapSocket(new_vs->sdk_fd, new_vs); + } + if (vs->get_state() != VS_STATE_LISTENING) { + // set state so socket multiplexer logic will pick this up + vs->set_state(VS_STATE_UNHANDLED_CONNECTED); + } + } + // PICO_SOCK_EV_RD - triggered when new data arrives on the socket. A new receive action + // can be taken by the socket owner because this event indicates there is new data to receive. + if (ev & PICO_SOCK_EV_RD) { + if (vs->socket_type==SOCK_STREAM) + rd_pico_cb_tcp_read(tap, s); + if (vs->socket_type==SOCK_DGRAM) + rd_pico_cb_udp_read(tap, s); + } + // PICO_SOCK_EV_WR - triggered when ready to write to the socket. Issuing a write/send call + // will now succeed if the buffer has enough space to allocate new outstanding data + if (ev & PICO_SOCK_EV_WR) { + rd_pico_cb_tcp_write(tap, s); + } +} + +int rd_pico_eth_tx(struct pico_device *dev, void *buf, int len) +{ + //DEBUG_TRANS(); + //_picostack_driver_lock.lock(); + VirtualTap *tap = static_cast(dev->tap); + if (tap == NULL) { + DEBUG_ERROR("invalid dev->tap"); + return ZT_ERR_GENERAL_FAILURE; + } + 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); + if (ZT_MSG_TRANSFER == true) { + char macBuf[ZT_MAC_ADDRSTRLEN], nodeBuf[ZTO_ID_LEN]; + mac2str(macBuf, ZT_MAC_ADDRSTRLEN, ethhdr->daddr); + ZeroTier::MAC mac; + mac.setTo(ethhdr->daddr, 6); + mac.toAddress(tap->_nwid).toString(nodeBuf); + + char flagbuf[32]; + memset(&flagbuf, 0, 32); +/* + struct pico_tcp_hdr *hdr; + void * tcp_hdr_ptr; + if (Utils::ntoh(ethhdr->proto) == 0x86dd) { // tcp, ipv6 + tcp_hdr_ptr = ðhdr + PICO_SIZE_ETHHDR + PICO_SIZE_IP4HDR; + } + + if (Utils::ntoh(ethhdr->proto) == 0x0800) // tcp + { + tcp_hdr_ptr = &buf + PICO_SIZE_ETHHDR + PICO_SIZE_IP4HDR; + hdr = (struct pico_tcp_hdr *)tcp_hdr_ptr; + + if (hdr) { + char *flag_ptr = flagbuf; + + if (hdr->flags & PICO_TCP_PSH) { + sprintf(flag_ptr, "PSH "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_SYN) { + sprintf(flag_ptr, "SYN "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_ACK) { + sprintf(flag_ptr, "ACK "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_FIN) { + sprintf(flag_ptr, "FIN "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_RST) { + sprintf(flag_ptr, "RST "); + flag_ptr+=4; + } + } + } +*/ + DEBUG_TRANS("len=%5d dst=%s [%s TX <-- %s] proto=0x%04x %s %s", len, macBuf, nodeBuf, tap->nodeId().c_str(), + Utils::ntoh(ethhdr->proto), beautify_eth_proto_nums(Utils::ntoh(ethhdr->proto)), flagbuf); + } + 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)); + //_picostack_driver_lock.unlock(); + return len; +} + +// receive frames from zerotier virtual wire and copy them to a guarded buffer awaiting placement into network stack +void rd_pico_eth_rx(VirtualTap *tap, const MAC &from,const MAC &to,unsigned int etherType, + const void *data,unsigned int len) +{ + //DEBUG_TRANS(); + //_picostack_driver_lock.lock(); + if (tap == NULL) { + DEBUG_ERROR("invalid tap"); + return; + } + // 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); + // assemble new eth header + struct pico_eth_hdr ethhdr; + from.copyTo(ethhdr.saddr, 6); + to.copyTo(ethhdr.daddr, 6); + ethhdr.proto = Utils::hton((uint16_t)etherType); + int32_t msg_len = len + sizeof(int32_t) + sizeof(struct pico_eth_hdr); + + if (ZT_MSG_TRANSFER == true) { + char macBuf[ZT_MAC_ADDRSTRLEN], nodeBuf[ZTO_ID_LEN]; + mac2str(macBuf, sizeof(macBuf), ethhdr.saddr); + ZeroTier::MAC mac; + mac.setTo(ethhdr.saddr, 6); + mac.toAddress(tap->_nwid).toString(nodeBuf); + + char flagbuf[64]; + memset(&flagbuf, 0, 64); +/* + struct pico_tcp_hdr *hdr; + void * tcp_hdr_ptr; + if (etherType == 0x86dd) { // tcp, ipv6 + tcp_hdr_ptr = ðhdr + PICO_SIZE_ETHHDR + PICO_SIZE_IP4HDR; + } + + if (etherType == 0x0800) // tcp, ipv4 + { + tcp_hdr_ptr = ðhdr + PICO_SIZE_ETHHDR + PICO_SIZE_IP4HDR; + hdr = (struct pico_tcp_hdr *)tcp_hdr_ptr; + if (hdr) { + char *flag_ptr = flagbuf; + + if (hdr->flags & PICO_TCP_PSH) { + sprintf(flag_ptr, "PSH "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_SYN) { + sprintf(flag_ptr, "SYN "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_ACK) { + sprintf(flag_ptr, "ACK "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_FIN) { + sprintf(flag_ptr, "FIN "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_RST) { + sprintf(flag_ptr, "RST "); + flag_ptr+=4; + } + } + } +*/ + DEBUG_TRANS("len=%5d src=%s [%s RX --> %s] proto=0x%04x %s %s", len, macBuf, nodeBuf, tap->nodeId().c_str(), + etherType, beautify_eth_proto_nums(etherType), flagbuf); + } + // write virtual ethernet frame to guarded buffer (emptied by pico_eth_poll()) + memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot, &msg_len, sizeof(int32_t)); // size of frame + meta + memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot + sizeof(int32_t), ðhdr, sizeof(ethhdr)); // new eth header + memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot + sizeof(int32_t) + sizeof(ethhdr), data, len); // frame data + tap->pico_frame_rxbuf_tot += msg_len; + //_picostack_driver_lock.unlock(); +} + +// feed frames on the guarded RX buffer (from zerotier virtual wire) into the network stack +int rd_pico_eth_poll(struct pico_device *dev, int loop_score) +{ + VirtualTap *tap = static_cast(dev->tap); + if (tap == NULL) { + DEBUG_ERROR("invalid dev->tap"); + return ZT_ERR_GENERAL_FAILURE; + } + // TODO: Optimize (use Ringbuffer) + Mutex::Lock _l(tap->_pico_frame_rxbuf_m); + unsigned char frame[ZT_SDK_MTU]; + int32_t len, err = 0; + while (tap->pico_frame_rxbuf_tot > 0 && loop_score > 0) { + memset(frame, 0, sizeof(frame)); + len = 0; + // get frame len + memcpy(&len, tap->pico_frame_rxbuf, sizeof(int32_t)); + if (len > sizeof(int32_t)) { // meaning, since we package the len in the msg, we don't want to recv a 0-(sizeof(int32_t)) sized frame + memcpy(frame, tap->pico_frame_rxbuf + sizeof(int32_t), len-(sizeof(int32_t)) ); // get frame data + memmove(tap->pico_frame_rxbuf, tap->pico_frame_rxbuf + len, MAX_PICO_FRAME_RX_BUF_SZ-len); // shift buffer + if ((err = pico_stack_recv(dev, (uint8_t*)frame, (len-sizeof(int32_t)))) < 0) { + DEBUG_ERROR("pico_stack_recv(), err=%d, pico_err=%d, %s", err, pico_err, picostack->beautify_pico_error(pico_err)); + } + tap->pico_frame_rxbuf_tot-=len; + } + else { + DEBUG_ERROR("invalid frame size (%d)",len); + } + loop_score--; + } + return loop_score; +} + +int rd_pico_socket(struct pico_socket **p, int socket_family, int socket_type, int protocol) +{ + int err = 0; + if (virt_can_provision_new_socket(socket_type) == false) { + DEBUG_ERROR("cannot create additional socket, see PICO_MAX_TIMERS. current=%d", pico_ntimers()); + errno = EMFILE; + err = -1; + } + else { + int protocol_version = 0; + struct pico_socket *psock; + if (socket_family == AF_INET) { + protocol_version = PICO_PROTO_IPV4; + } + if (socket_family == AF_INET6) { + protocol_version = PICO_PROTO_IPV6; + } + if (socket_type == SOCK_DGRAM) { + psock = pico_socket_open(protocol_version, PICO_PROTO_UDP, &rd_pico_cb_socket_ev); + if (psock) { + // configure size of UDP SND/RCV buffers + // TODO + } + } + if (socket_type == SOCK_STREAM) { + psock = pico_socket_open(protocol_version, PICO_PROTO_TCP, &rd_pico_cb_socket_ev); + if (psock) { + // configure size of TCP SND/RCV buffers + int tx_buf_sz = ZT_STACK_TCP_SOCKET_TX_SZ; + int rx_buf_sz = ZT_STACK_TCP_SOCKET_RX_SZ; + int t_err = 0; + if ((t_err = pico_socket_setoption(psock, PICO_SOCKET_OPT_SNDBUF, &tx_buf_sz)) < 0) { + DEBUG_ERROR("unable to set SNDBUF size, err=%d, pico_err=%d, %s", + t_err, pico_err, beautify_pico_error(pico_err)); + } + if ((t_err = pico_socket_setoption(psock, PICO_SOCKET_OPT_RCVBUF, &rx_buf_sz)) < 0) { + DEBUG_ERROR("unable to set RCVBUF size, err=%d, pico_err=%d, %s", + t_err, pico_err, beautify_pico_error(pico_err)); + } + } + } + *p = psock; + } + return err; +} + +int rd_pico_connect(VirtualSocket *vs, const struct sockaddr *addr, socklen_t addrlen) +{ + if (vs == NULL || vs->pcb == NULL) { + DEBUG_ERROR("invalid vs or ps"); + return ZT_ERR_GENERAL_FAILURE; + } + struct pico_socket *ps = (struct pico_socket*)(vs->pcb); + int err = 0; + if (vs->socket_family == AF_INET) { + struct pico_ip4 zaddr; + memset(&zaddr, 0, sizeof (struct pico_ip4)); + struct sockaddr_in *in4 = (struct sockaddr_in*)addr; + char ipv4_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, (const void *)&in4->sin_addr.s_addr, ipv4_str, INET_ADDRSTRLEN); + uint32_t ipval = 0; + pico_string_to_ipv4(ipv4_str, &ipval); + zaddr.addr = ipval; + if (vs->socket_type == SOCK_STREAM) { // connect is an implicit call for non-connection-based VirtualSockets + DEBUG_EXTRA("connecting to addr=%s port=%d", ipv4_str, Utils::ntoh(in4->sin_port)); + } + err = pico_socket_connect(ps, &zaddr, in4->sin_port); + } + if (vs->socket_family == AF_INET6) { + struct pico_ip6 zaddr; + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr; + char ipv6_str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN); + pico_string_to_ipv6(ipv6_str, zaddr.addr); + if (vs->socket_type == SOCK_STREAM) { + DEBUG_EXTRA("connecting to addr=%s port=%d", ipv6_str, Utils::ntoh(in6->sin6_port)); + } + err = pico_socket_connect(ps, &zaddr, in6->sin6_port); + } + if (err) { + DEBUG_ERROR("error connecting pico_socket=%p, err=%d, pico_err=%d, %s", + ps, err, pico_err, beautify_pico_error(pico_err)); + return map_pico_err_to_errno(pico_err); + } + memcpy(&(vs->peer_addr), &addr, sizeof(struct sockaddr_storage)); + return err; +} + +int rd_pico_bind(VirtualSocket *vs, const struct sockaddr *addr, socklen_t addrlen) +{ + if (vs == NULL || vs->pcb == NULL) { + DEBUG_ERROR("invalid vs or ps"); + return ZT_ERR_GENERAL_FAILURE; + } + struct pico_socket *ps = (struct pico_socket*)(vs->pcb); + int err = 0; + if (vs->socket_family == AF_INET) { + struct pico_ip4 zaddr; + uint32_t tempaddr; + memset(&zaddr, 0, sizeof (struct pico_ip4)); + struct sockaddr_in *in4 = (struct sockaddr_in*)addr; + char ipv4_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, (const void *)&in4->sin_addr.s_addr, ipv4_str, INET_ADDRSTRLEN); + pico_string_to_ipv4(ipv4_str, &tempaddr); + zaddr.addr = tempaddr; + DEBUG_EXTRA("binding to addr=%s port=%d", ipv4_str, Utils::ntoh(in4->sin_port)); + err = pico_socket_bind(ps, &zaddr, (uint16_t *)&(in4->sin_port)); + } + if (vs->socket_family == AF_INET6) { + struct pico_ip6 pip6; + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr; + char ipv6_str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN); + // TODO: This isn't proper + pico_string_to_ipv6("::", pip6.addr); + DEBUG_EXTRA("binding to addr=%s port=%d", ipv6_str, Utils::ntoh(in6->sin6_port)); + err = pico_socket_bind(ps, &pip6, (uint16_t *)&(in6->sin6_port)); + } + if (err < 0) { + DEBUG_ERROR("unable to bind pico_socket=%p, err=%d, pico_err=%d, %s", + (ps), err, pico_err, beautify_pico_error(pico_err)); + return map_pico_err_to_errno(pico_err); + } + return err; +} + +int rd_pico_listen(VirtualSocket *vs, int backlog) +{ + if (vs == NULL || vs->pcb == NULL) { + DEBUG_ERROR("invalid vs or ps"); + return ZT_ERR_GENERAL_FAILURE; + } + struct pico_socket *ps = (struct pico_socket*)(vs->pcb); + int err = 0; + if ((err = pico_socket_listen(ps, backlog)) < 0) { + DEBUG_ERROR("error putting pico_socket=%p into listening state. err=%d, pico_err=%d, %s", + ps, err, pico_err, beautify_pico_error(pico_err)); + return map_pico_err_to_errno(pico_err); + } + vs->set_state(VS_STATE_LISTENING); + return ZT_ERR_OK; +} + +VirtualSocket* rd_pico_accept(VirtualSocket *vs) +{ + if (vs == NULL) { + DEBUG_ERROR("invalid vs"); + return NULL; + } + // Retreive first of queued VirtualSockets from parent VirtualSocket + VirtualSocket *new_vs = NULL; + if (vs->_AcceptedConnections.size()) { + new_vs = vs->_AcceptedConnections.front(); + vs->_AcceptedConnections.pop(); + } + return new_vs; +} + +int rd_pico_read(VirtualTap *tap, PhySocket *sock, VirtualSocket* vs, bool stack_invoked) +{ + // DEBUG_INFO(); + // Vestigial + return 0; +} + +int rd_pico_write(VirtualSocket *vs, void *data, ssize_t len) +{ + int err = 0; + void *src_buf = NULL; + // TODO: Add RingBuffer overflow checks + DEBUG_EXTRA("vs=%p, fd=%d, data=%p, len=%d", vs, vs->app_fd, data, len); + if (vs == NULL) { + DEBUG_ERROR("invalid vs"); + return ZT_ERR_GENERAL_FAILURE; + } + struct pico_socket *ps = (struct pico_socket*)(vs->pcb); + Mutex::Lock _l(vs->_tx_m); + if (ps == NULL) { + DEBUG_ERROR("ps == NULL"); + return -1; + } + if (vs->app_fd <= 0) { + DEBUG_EXTRA("invalid fd"); + return -1; + } + if (ps->state & PICO_SOCKET_STATE_CLOSED) { + DEBUG_ERROR("socket is PICO_SOCKET_STATE_CLOSED, this pico_tcp_write() will fail"); + return -1; + } + if (vs == NULL) { + DEBUG_ERROR("invalid VirtualSocket (len=%d)", len); + return -1; + } + if (vs->socket_type == SOCK_DGRAM) { + if (data == NULL) { + DEBUG_ERROR("data == NULL"); + return -1; + } + if (len <= 0) { + DEBUG_ERROR("invalid write len=%d for SOCK_DGRAM", len); + return -1; + } + int r; + if ((r = pico_socket_write(ps, data, len)) < 0) { + DEBUG_ERROR("unable to write to picosock=%p, err=%d, pico_err=%d, %s", + ps, r, pico_err, beautify_pico_error(pico_err)); + err = -1; + } + else { + err = r; // successful write + } + if (vs->socket_type == SOCK_DGRAM) { + DEBUG_TRANS("len=%5d buf_len=N/A [APPFDS --> NSPICO] proto=0x%04x (UDP)", r, PICO_PROTO_TCP); + } + } + if (vs->socket_type == SOCK_STREAM) { + if (len > 0 && data != NULL) { + + src_buf = data; // --- Data source: poll loop I/O buffer --- + + // in this case, we've recieved data on the 'data' buffer, add it to TX ringbuffer, then try to handle it from there + int original_txsz = vs->TXbuf->count(); + if (original_txsz + len >= ZT_TCP_TX_BUF_SZ) { + DEBUG_ERROR("txsz=%d, len=%d", original_txsz, len); + DEBUG_ERROR("TX buffer is too small, try increasing ZT_TCP_TX_BUF_SZ in libzt.h"); + return ZT_ERR_GENERAL_FAILURE; + } + int buf_w = vs->TXbuf->write((const char*)data, len); + if (buf_w != len) { + // because we checked ZT_TCP_TX_BUF_SZ above, this should not happen + DEBUG_ERROR("wrote only len=%d but expected to write len=%d", buf_w, len); + return ZT_ERR_GENERAL_FAILURE; + } + } else if (len == 0 && data == NULL) { + DEBUG_EXTRA("len=0 => write request from poll loop or callback"); + + src_buf = vs->TXbuf->get_buf(); // --- Data source: TX ringbuffer --- + + // do nothing, all the data we need is already on the TX ringbuffer + } else if (len < 0) { + DEBUG_ERROR("invalid write len=%d for SOCK_STREAM", len); + } + + int txsz = vs->TXbuf->count(); + int r, max_write_len = std::min(std::min(txsz, ZT_SDK_MTU),ZT_STACK_SOCKET_WR_MAX); + if ((r = pico_socket_write(ps, src_buf, max_write_len)) < 0) { + DEBUG_ERROR("unable to write to picosock=%p, err=%d, pico_err=%d, %s", + ps, r, pico_err, beautify_pico_error(pico_err)); + err = -1; + } + else { + err = r; // successful write + } + if (r>0) { + vs->TXbuf->consume(r); + if (vs->socket_type == SOCK_STREAM) { + DEBUG_TRANS("len=%5d buf_len=%13d [VSTXBF --> NSPICO] proto=0x%04x (TCP)", r, vs->TXbuf->count(), PICO_PROTO_TCP); + } + } + } + return err; +} + +int rd_pico_close(VirtualSocket *vs) +{ + DEBUG_EXTRA(); + if (vs == NULL) { + DEBUG_ERROR("invalid vs"); + return ZT_ERR_GENERAL_FAILURE; + } + struct pico_socket *ps = (struct pico_socket*)(vs->pcb); + if (vs->get_state() == VS_STATE_CLOSED) { + DEBUG_EXTRA("socket already in VS_STATE_CLOSED state"); + return 0; + } + if (ps == NULL) { + DEBUG_EXTRA("ps == NULL"); + return 0; + } + if (ps->state & PICO_SOCKET_STATE_CLOSED) { + DEBUG_EXTRA("ps already closed, ps=%p", ps); + return 0; + } + DEBUG_EXTRA("vs=%p, picosock=%p, fd=%d", vs, ps, vs->app_fd); + if (vs == NULL || ps == NULL) + return ZT_ERR_GENERAL_FAILURE; + int err = 0; + Mutex::Lock _l(vs->tap->_tcpconns_m); + if ((err = pico_socket_close(ps)) < 0) { + errno = pico_err; + DEBUG_ERROR("error closing pico_socket, err=%d, pico_err=%d, %s", + err, pico_err, beautify_pico_error(pico_err)); + } + return err; +} + +int rd_pico_shutdown(VirtualSocket *vs, int how) +{ + DEBUG_EXTRA("vs=%p, how=%d", vs, how); + struct pico_socket *ps = (struct pico_socket*)(vs->pcb); + int err = 0, mode = 0; + if (how == SHUT_RD) { + mode = PICO_SHUT_RD; + } + if (how == SHUT_WR) { + mode = PICO_SHUT_WR; + } + if (how == SHUT_RDWR) { + mode = PICO_SHUT_RDWR; + } + if ((err = pico_socket_shutdown(ps, mode)) < 0) { + DEBUG_ERROR("error while shutting down socket, fd=%d, pico_err=%d, %s", vs->app_fd, pico_err, beautify_pico_error(pico_err)); + } + return err; +} + +int rd_pico_setsockopt(VirtualSocket *vs, int level, int optname, const void *optval, socklen_t optlen) +{ + int err = -1; + errno = 0; + if (vs == NULL) { + DEBUG_ERROR("invalid vs"); + return -1; + } else { + DEBUG_EXTRA("fd=%d, level=%d, optname=%d", vs->app_fd, level, optname); + } + struct pico_socket *ps = (struct pico_socket*)(vs->pcb); + if (level == SOL_SOCKET) + { + /* Turns on recording of debugging information. This option enables or disables debugging in the underlying + protocol modules. This option takes an int value. This is a Boolean option.*/ + if (optname == SO_DEBUG) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Specifies that the rules used in validating addresses supplied to bind() should allow reuse of local + addresses, if this is supported by the protocol. This option takes an int value. This is a Boolean option.*/ + if (optname == SO_REUSEADDR) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Keeps connections active by enabling the periodic transmission of messages, if this is supported by the + protocol. This option takes an int value. */ + if (optname == SO_KEEPALIVE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Requests that outgoing messages bypass the standard routing facilities. */ + if (optname == SO_DONTROUTE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Lingers on a close() if data is present. */ + if (optname == SO_LINGER) + { + int linger_time_ms = *((const int*)optval); + if ((err = pico_socket_setoption(ps, PICO_SOCKET_OPT_LINGER, &linger_time_ms)) < 0) { + DEBUG_ERROR("unable to set LINGER, err=%d, pico_err=%d, %s", + err, pico_err, beautify_pico_error(pico_err)); + } + return err; + } + /* Permits sending of broadcast messages, if this is supported by the protocol. This option takes an int + value. This is a Boolean option. */ + if (optname == SO_BROADCAST) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Leaves received out-of-band data (data marked urgent) inline. This option takes an int value. This is a + Boolean option. */ + if (optname == SO_OOBINLINE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Sets send buffer size. This option takes an int value. */ + if (optname == SO_SNDBUF) + { + int no_delay = *((const int*)optval); + if ((err = pico_socket_setoption(ps, PICO_SOCKET_OPT_SNDBUF, &no_delay) < 0)) { + if (err == PICO_ERR_EINVAL) { + DEBUG_ERROR("error while setting PICO_SOCKET_OPT_SNDBUF"); + errno = EINVAL; + err = -1; + } + } + return err; + } + /* Sets receive buffer size. This option takes an int value. */ + if (optname == SO_RCVBUF) + { + int no_delay = *((const int*)optval); + if ((err = pico_socket_setoption(ps, PICO_SOCKET_OPT_RCVBUF, &no_delay) < 0)) { + if (err == PICO_ERR_EINVAL) { + DEBUG_ERROR("error while setting PICO_SOCKET_OPT_RCVBUF"); + errno = EINVAL; + err = -1; + } + } + return err; + } + /* */ + if (optname == SO_STYLE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* */ + if (optname == SO_TYPE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Get error status and clear */ + if (optname == SO_ERROR) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + } + if (level == IPPROTO_IP) + { + if (optname == IP_ADD_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_ADD_SOURCE_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_BIND_ADDRESS_NO_PORT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_BLOCK_SOURCE) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_DROP_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_DROP_SOURCE_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_FREEBIND) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_HDRINCL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MSFILTER) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MTU) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MTU_DISCOVER) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MULTICAST_ALL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MULTICAST_IF) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MULTICAST_LOOP) { + int loop = *((const int*)optval); + if ((err = pico_socket_setoption(ps, PICO_IP_MULTICAST_LOOP, &loop) < 0)) { + if (err == PICO_ERR_EINVAL) { + DEBUG_ERROR("error while setting PICO_IP_MULTICAST_TTL"); + errno = EINVAL; + err = -1; + } + } + return err; + } + if (optname == IP_MULTICAST_TTL) { + int ttl = *((const int*)optval); + if ((err = pico_socket_setoption(ps, PICO_IP_MULTICAST_TTL, &ttl) < 0)) { + if (err == PICO_ERR_EINVAL) { + DEBUG_ERROR("error while setting PICO_IP_MULTICAST_TTL"); + errno = EINVAL; + err = -1; + } + } + return err; + } + if (optname == IP_NODEFRAG) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_OPTIONS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_PKTINFO) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVOPTS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVORIGDSTADDR) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVTOS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVTTL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RETOPTS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_ROUTER_ALERT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_TOS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_TRANSPARENT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_TTL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_UNBLOCK_SOURCE) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + // TODO + return -1; + } + if (level == IPPROTO_TCP) + { + struct pico_socket *p = ps; + if (p == NULL) { + return -1; + } + /* If set, don't send out partial frames. */ + if (optname == TCP_CORK) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Allow a listener to be awakened only when data arrives on the socket. */ + if (optname == TCP_DEFER_ACCEPT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Used to collect information about this socket. The kernel returns a struct tcp_info as defined in the + file /usr/include/linux/tcp.h.*/ + if (optname == TCP_INFO) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* The maximum number of keepalive probes TCP should send before dropping the connection.*/ + if (optname == TCP_KEEPCNT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* The time (in seconds) the connection needs to remain idle before TCP starts sending keepalive probes, + if the socket option SO_KEEPALIVE has been set on this socket. */ + if (optname == TCP_KEEPIDLE) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* The time (in seconds) between individual keepalive probes.*/ + if (optname == TCP_KEEPINTVL) { + // TODO + return -1; + } + /* The lifetime of orphaned FIN_WAIT2 state sockets. */ + if (optname == TCP_LINGER2) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* The maximum segment size for outgoing TCP packets. */ + if (optname == TCP_MAXSEG) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* If set, disable the Nagle algorithm. */ + if (optname == TCP_NODELAY) { + int no_delay = *((const int*)optval); + if ((err = pico_socket_setoption(ps, PICO_TCP_NODELAY, &no_delay) < 0)) { + if (err == PICO_ERR_EINVAL) { + DEBUG_ERROR("error while disabling Nagle's algorithm"); + errno = EINVAL; + err = -1; + } + } + return err; + } + /* Enable quickack mode if set or disable quickack mode if cleared. */ + if (optname == TCP_QUICKACK) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Set the number of SYN retransmits that TCP should send before aborting the attempt to connect. It + cannot exceed 255. */ + if (optname == TCP_SYNCNT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Bound the size of the advertised window to this value. The kernel imposes a minimum size of + SOCK_MIN_RCVBUF/2. */ + if (optname == TCP_WINDOW_CLAMP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + } + if (level == IPPROTO_UDP) + { + /*If this option is enabled, then all data output on this socket is accumulated into a single + datagram that is transmitted when the option is disabled. */ + if (optname == UDP_CORK) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + } + return err; +} + +int rd_pico_getsockopt(VirtualSocket *vs, int level, int optname, void *optval, socklen_t *optlen) +{ + int err = -1, optval_tmp = 0; + errno = 0; + if (vs == NULL) { + DEBUG_ERROR("invalid vs"); + return -1; + } else { + DEBUG_EXTRA("fd=%d, level=%d, optname=%d", vs->app_fd, level, optname); + } + struct pico_socket *ps = (struct pico_socket*)(vs->pcb); + if (level == SOL_SOCKET) + { + /* Turns on recording of debugging information. This option enables or disables debugging in the underlying + protocol modules. This option takes an int value. This is a Boolean option.*/ + if (optname == SO_DEBUG) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Specifies that the rules used in validating addresses supplied to bind() should allow reuse of local + addresses, if this is supported by the protocol. This option takes an int value. This is a Boolean option.*/ + if (optname == SO_REUSEADDR) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Keeps connections active by enabling the periodic transmission of messages, if this is supported by the + protocol. This option takes an int value. */ + if (optname == SO_KEEPALIVE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Requests that outgoing messages bypass the standard routing facilities. */ + if (optname == SO_DONTROUTE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Lingers on a close() if data is present. */ + if (optname == SO_LINGER) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Permits sending of broadcast messages, if this is supported by the protocol. This option takes an int + value. This is a Boolean option. */ + if (optname == SO_BROADCAST) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Leaves received out-of-band data (data marked urgent) inline. This option takes an int value. This is a + Boolean option. */ + if (optname == SO_OOBINLINE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Sets send buffer size. This option takes an int value. */ + if (optname == SO_SNDBUF) + { + if ((err = pico_socket_getoption(ps, PICO_SOCKET_OPT_SNDBUF, &optval_tmp)) < 0) { + if (err == PICO_ERR_EINVAL) { + DEBUG_ERROR("error while getting PICO_SOCKET_OPT_SNDBUF"); + errno = ENOPROTOOPT; + err = -1; + } + } + memcpy(optval, &optval_tmp, *optlen); + } + /* Sets receive buffer size. This option takes an int value. */ + if (optname == SO_RCVBUF) + { + if ((err = pico_socket_getoption(ps, PICO_SOCKET_OPT_SNDBUF, &optval_tmp)) < 0) { + if (err == PICO_ERR_EINVAL) { + DEBUG_ERROR("error while getting PICO_SOCKET_OPT_RCVBUF"); + errno = ENOPROTOOPT; + err = -1; + } + } + memcpy(optval, &optval_tmp, *optlen); + } + /* */ + if (optname == SO_STYLE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* */ + if (optname == SO_TYPE) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Get error status and clear */ + if (optname == SO_ERROR) + { + // TODO + errno = ENOPROTOOPT; + return -1; + } + } + if (level == IPPROTO_IP) + { + if (optname == IP_ADD_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_ADD_SOURCE_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_BIND_ADDRESS_NO_PORT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_BLOCK_SOURCE) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_DROP_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_DROP_SOURCE_MEMBERSHIP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_FREEBIND) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_HDRINCL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MSFILTER) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MTU) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MTU_DISCOVER) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MULTICAST_ALL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MULTICAST_IF) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_MULTICAST_LOOP) { + if ((err = pico_socket_getoption(ps, PICO_IP_MULTICAST_LOOP, &optval_tmp)) < 0) { + if (err == PICO_ERR_EINVAL) { + DEBUG_ERROR("error while getting PICO_IP_MULTICAST_TTL"); + errno = ENOPROTOOPT; + err = -1; + } + } + memcpy(optval, &optval_tmp, *optlen); + } + if (optname == IP_MULTICAST_TTL) { + if ((err = pico_socket_getoption(ps, PICO_IP_MULTICAST_TTL, &optval_tmp)) < 0) { + if (err == PICO_ERR_EINVAL) { + DEBUG_ERROR("error while getting PICO_IP_MULTICAST_TTL"); + errno = ENOPROTOOPT; + err = -1; + } + } + memcpy(optval, &optval_tmp, *optlen); + } + if (optname == IP_NODEFRAG) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_OPTIONS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_PKTINFO) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVOPTS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVORIGDSTADDR) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVTOS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RECVTTL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_RETOPTS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_ROUTER_ALERT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_TOS) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_TRANSPARENT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_TTL) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + if (optname == IP_UNBLOCK_SOURCE) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + // TODO + return -1; + } + if (level == IPPROTO_TCP) + { + struct pico_socket *p = ps; + if (p == NULL) { + return -1; + } + /* If set, don't send out partial frames. */ + if (optname == TCP_CORK) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Allow a listener to be awakened only when data arrives on the socket. */ + if (optname == TCP_DEFER_ACCEPT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Used to collect information about this socket. */ + if (optname == TCP_INFO) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* The maximum number of keepalive probes TCP should send before dropping the connection.*/ + if (optname == TCP_KEEPCNT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* The time (in seconds) the connection needs to remain idle before TCP starts sending keepalive probes, + if the socket option SO_KEEPALIVE has been set on this socket. */ + if (optname == TCP_KEEPIDLE) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* The time (in seconds) between individual keepalive probes.*/ + if (optname == TCP_KEEPINTVL) { + // TODO + return -1; + } + /* The lifetime of orphaned FIN_WAIT2 state sockets. */ + if (optname == TCP_LINGER2) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* The maximum segment size for outgoing TCP packets. */ + if (optname == TCP_MAXSEG) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* If set, disable the Nagle algorithm. */ + if (optname == TCP_NODELAY) { + if ((err = pico_socket_getoption(ps, PICO_TCP_NODELAY, &optval_tmp)) < 0) { + if (err == PICO_ERR_EINVAL) { + DEBUG_ERROR("error while disabling Nagle's algorithm"); + errno = ENOPROTOOPT; + err = -1; + } + } + memcpy(optval, &optval_tmp, *optlen); + return err; + } + /* Enable quickack mode if set or disable quickack mode if cleared. */ + if (optname == TCP_QUICKACK) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Set the number of SYN retransmits that TCP should send before aborting the attempt to connect. It + cannot exceed 255. */ + if (optname == TCP_SYNCNT) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + /* Bound the size of the advertised window to this value. The kernel imposes a minimum size of + SOCK_MIN_RCVBUF/2. */ + if (optname == TCP_WINDOW_CLAMP) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + } + if (level == IPPROTO_UDP) + { + /*If this option is enabled, then all data output on this socket is accumulated into a single + datagram that is transmitted when the option is disabled. */ + if (optname == UDP_CORK) { + // TODO + errno = ENOPROTOOPT; + return -1; + } + } + return err; +} + +int map_pico_err_to_errno(int err) +{ + if (err == PICO_ERR_NOERR) { errno = 0; return 0; } // + if (err == PICO_ERR_EPERM) { errno = ENXIO; } + if (err == PICO_ERR_ENOENT) { errno = ENXIO; } + if (err == PICO_ERR_EINTR) { errno = ENXIO; } + if (err == PICO_ERR_EIO) { errno = ENXIO; } + if (err == PICO_ERR_ENXIO) { errno = ENXIO; } // + if (err == PICO_ERR_EAGAIN) { errno = ENXIO; } + if (err == PICO_ERR_ENOMEM) { errno = ENOMEM; } // + if (err == PICO_ERR_EACCESS) { errno = ENXIO; } + if (err == PICO_ERR_EFAULT) { errno = ENXIO; } + if (err == PICO_ERR_EBUSY) { errno = ENXIO; } + if (err == PICO_ERR_EEXIST) { errno = ENXIO; } + if (err == PICO_ERR_EINVAL) { errno = EINVAL; } // + if (err == PICO_ERR_ENONET) { errno = ENXIO; } + if (err == PICO_ERR_EPROTO) { errno = ENXIO; } + if (err == PICO_ERR_ENOPROTOOPT) { errno = ENXIO; } + if (err == PICO_ERR_EPROTONOSUPPORT) { errno = ENXIO; } + if (err == PICO_ERR_EOPNOTSUPP) { errno = ENXIO; } + if (err == PICO_ERR_EADDRINUSE) { errno = ENXIO; } + if (err == PICO_ERR_EADDRNOTAVAIL) { errno = ENXIO; } + if (err == PICO_ERR_ENETDOWN) { errno = ENXIO; } + if (err == PICO_ERR_ENETUNREACH) { errno = ENXIO; } + if (err == PICO_ERR_ECONNRESET) { errno = ENXIO; } + if (err == PICO_ERR_EISCONN) { errno = ENXIO; } + if (err == PICO_ERR_ENOTCONN) { errno = ENXIO; } + if (err == PICO_ERR_ESHUTDOWN) { errno = ENXIO; } + if (err == PICO_ERR_ETIMEDOUT) { errno = ENXIO; } + if (err == PICO_ERR_ECONNREFUSED) { errno = ENXIO; } + if (err == PICO_ERR_EHOSTDOWN) { errno = ENXIO; } + if (err == PICO_ERR_EHOSTUNREACH) { errno = ENXIO; } + return -1; +} + +char *beautify_pico_error(int err) +{ + if (err== 0) return (char*)"PICO_ERR_NOERR"; + if (err== 1) return (char*)"PICO_ERR_EPERM"; + if (err== 2) return (char*)"PICO_ERR_ENOENT"; + // ... + if (err== 4) return (char*)"PICO_ERR_EINTR"; + if (err== 5) return (char*)"PICO_ERR_EIO"; + if (err== 6) return (char*)"PICO_ERR_ENXIO (no such device or address)"; + // ... + if (err== 11) return (char*)"PICO_ERR_EAGAIN"; + if (err== 12) return (char*)"PICO_ERR_ENOMEM (not enough space)"; + if (err== 13) return (char*)"PICO_ERR_EACCESS"; + if (err== 14) return (char*)"PICO_ERR_EFAULT"; + // ... + if (err== 16) return (char*)"PICO_ERR_EBUSY"; + if (err== 17) return (char*)"PICO_ERR_EEXIST"; + // ... + if (err== 22) return (char*)"PICO_ERR_EINVAL (invalid argument)"; + // ... + if (err== 64) return (char*)"PICO_ERR_ENONET"; + // ... + if (err== 71) return (char*)"PICO_ERR_EPROTO"; + // ... + if (err== 92) return (char*)"PICO_ERR_ENOPROTOOPT"; + if (err== 93) return (char*)"PICO_ERR_EPROTONOSUPPORT"; + // ... + if (err== 95) return (char*)"PICO_ERR_EOPNOTSUPP"; + if (err== 98) return (char*)"PICO_ERR_EADDRINUSE"; + if (err== 99) return (char*)"PICO_ERR_EADDRNOTAVAIL"; + if (err==100) return (char*)"PICO_ERR_ENETDOWN"; + if (err==101) return (char*)"PICO_ERR_ENETUNREACH"; + // ... + if (err==104) return (char*)"PICO_ERR_ECONNRESET"; + // ... + if (err==106) return (char*)"PICO_ERR_EISCONN"; + if (err==107) return (char*)"PICO_ERR_ENOTCONN"; + if (err==108) return (char*)"PICO_ERR_ESHUTDOWN"; + // ... + if (err==110) return (char*)"PICO_ERR_ETIMEDOUT"; + if (err==111) return (char*)"PICO_ERR_ECONNREFUSED"; + if (err==112) return (char*)"PICO_ERR_EHOSTDOWN"; + if (err==113) return (char*)"PICO_ERR_EHOSTUNREACH"; + return (char*)"UNKNOWN_ERROR"; +} + +/* + +#define PICO_SOCKET_STATE_UNDEFINED 0x0000u +#define PICO_SOCKET_STATE_SHUT_LOCAL 0x0001u +#define PICO_SOCKET_STATE_SHUT_REMOTE 0x0002u +#define PICO_SOCKET_STATE_BOUND 0x0004u +#define PICO_SOCKET_STATE_CONNECTED 0x0008u +#define PICO_SOCKET_STATE_CLOSING 0x0010u +#define PICO_SOCKET_STATE_CLOSED 0x0020u + +# define PICO_SOCKET_STATE_TCP 0xFF00u +# define PICO_SOCKET_STATE_TCP_UNDEF 0x00FFu +# define PICO_SOCKET_STATE_TCP_CLOSED 0x0100u +# define PICO_SOCKET_STATE_TCP_LISTEN 0x0200u +# define PICO_SOCKET_STATE_TCP_SYN_SENT 0x0300u +# define PICO_SOCKET_STATE_TCP_SYN_RECV 0x0400u +# define PICO_SOCKET_STATE_TCP_ESTABLISHED 0x0500u +# define PICO_SOCKET_STATE_TCP_CLOSE_WAIT 0x0600u +# define PICO_SOCKET_STATE_TCP_LAST_ACK 0x0700u +# define PICO_SOCKET_STATE_TCP_FIN_WAIT1 0x0800u +# define PICO_SOCKET_STATE_TCP_FIN_WAIT2 0x0900u +# define PICO_SOCKET_STATE_TCP_CLOSING 0x0a00u +# define PICO_SOCKET_STATE_TCP_TIME_WAIT 0x0b00u +# define PICO_SOCKET_STATE_TCP_ARRAYSIZ 0x0cu + +*/ +char *beautify_pico_state(int state) +{ + static char state_str[512]; + char *str_ptr = state_str; + + if (state & PICO_SOCKET_STATE_UNDEFINED) { + sprintf(str_ptr, "UNDEFINED "); + str_ptr += strlen("UNDEFINED "); + } + if (state & PICO_SOCKET_STATE_SHUT_LOCAL) { + sprintf(str_ptr, "SHUT_LOCAL "); + str_ptr += strlen("SHUT_LOCAL "); + } + if (state & PICO_SOCKET_STATE_SHUT_REMOTE) { + sprintf(str_ptr, "SHUT_REMOTE "); + str_ptr += strlen("SHUT_REMOTE "); + } + if (state & PICO_SOCKET_STATE_BOUND) { + sprintf(str_ptr, "BOUND "); + str_ptr += strlen("BOUND "); + } + if (state & PICO_SOCKET_STATE_CONNECTED) { + sprintf(str_ptr, "CONNECTED "); + str_ptr += strlen("CONNECTED "); + } + if (state & PICO_SOCKET_STATE_CLOSING) { + sprintf(str_ptr, "CLOSING "); + str_ptr += strlen("CLOSING "); + } + if (state & PICO_SOCKET_STATE_CLOSED) { + sprintf(str_ptr, "CLOSED "); + str_ptr += strlen("CLOSED "); + } + + + if (state & PICO_SOCKET_STATE_TCP) { + sprintf(str_ptr, "TCP "); + str_ptr += strlen("TCP "); + } + if (state & PICO_SOCKET_STATE_TCP_UNDEF) { + sprintf(str_ptr, "TCP_UNDEF "); + str_ptr += strlen("TCP_UNDEF "); + } + if (state & PICO_SOCKET_STATE_TCP_CLOSED) { + sprintf(str_ptr, "TCP_CLOSED "); + str_ptr += strlen("TCP_CLOSED "); + } + if (state & PICO_SOCKET_STATE_TCP_LISTEN) { + sprintf(str_ptr, "TCP_LISTEN "); + str_ptr += strlen("TCP_LISTEN "); + } + if (state & PICO_SOCKET_STATE_TCP_SYN_SENT) { + sprintf(str_ptr, "TCP_SYN_SENT "); + str_ptr += strlen("TCP_SYN_SENT "); + } + if (state & PICO_SOCKET_STATE_TCP_SYN_RECV) { + sprintf(str_ptr, "TCP_SYN_RECV "); + str_ptr += strlen("TCP_SYN_RECV "); + } + if (state & PICO_SOCKET_STATE_TCP_ESTABLISHED) { + sprintf(str_ptr, "TCP_ESTABLISHED "); + str_ptr += strlen("TCP_ESTABLISHED "); + } + if (state & PICO_SOCKET_STATE_TCP_CLOSE_WAIT) { + sprintf(str_ptr, "TCP_CLOSE_WAIT "); + str_ptr += strlen("TCP_CLOSE_WAIT "); + } + if (state & PICO_SOCKET_STATE_TCP_LAST_ACK) { + sprintf(str_ptr, "TCP_LAST_ACK "); + str_ptr += strlen("TCP_LAST_ACK "); + } + if (state & PICO_SOCKET_STATE_TCP_FIN_WAIT1) { + sprintf(str_ptr, "TCP_FIN_WAIT1 "); + str_ptr += strlen("TCP_FIN_WAIT1 "); + } + if (state & PICO_SOCKET_STATE_TCP_FIN_WAIT2) { + sprintf(str_ptr, "TCP_FIN_WAIT2 "); + str_ptr += strlen("TCP_FIN_WAIT2 "); + } + if (state & PICO_SOCKET_STATE_TCP_CLOSING) { + sprintf(str_ptr, "TCP_CLOSING "); + str_ptr += strlen("TCP_CLOSING "); + } + if (state & PICO_SOCKET_STATE_TCP_TIME_WAIT) { + sprintf(str_ptr, "TCP_TIME_WAIT "); + str_ptr += strlen("TCP_TIME_WAIT "); + } + if (state & PICO_SOCKET_STATE_TCP_ARRAYSIZ) { + sprintf(str_ptr, "TCP_ARRAYSIZ "); + str_ptr += strlen("TCP_ARRAYSIZ "); + } + return (char*)state_str; +} + +#endif // STACK_PICO diff --git a/test/dummy.cpp b/test/dummy.cpp new file mode 100644 index 0000000..4dbb53f --- /dev/null +++ b/test/dummy.cpp @@ -0,0 +1,15 @@ +#include +#include + +#include "libzt.h" + +int main() +{ + printf("Starting ZT service"); + zts_startjoin("ztp","17d709436c2c5367"); + + printf("Dummy. Going into infinite loop. Ping me or something\n"); + while(1) { + sleep(1); + } +} diff --git a/test/selftest.cpp b/test/selftest.cpp index 48cb707..c3274fa 100644 --- a/test/selftest.cpp +++ b/test/selftest.cpp @@ -1938,15 +1938,12 @@ int ZT_control_semantics_test(bool *passed) int zts_get_id_from_file(const char *filepath, char *devID); void *zts_start_service(void *thread_id); void disableTaps(); - void zts_get_ipv4_address(const char *nwid, char *addrstr, const size_t addrlen); - void zts_get_ipv6_address(const char *nwid, char *addrstr, const size_t addrlen); - int zts_has_ipv4_address(const char *nwid); - int zts_has_ipv6_address(const char *nwid); + void zts_get_address(const char *nwid, struct sockaddr_storage *addr, const size_t addrlen); int zts_has_address(const char *nwid); - void zts_get_6plane_addr(char *addr, const char *nwid, const char *devID); - void zts_get_rfc4193_addr(char *addr, const char *nwid, const char *devID); - void zts_join(const char * nwid); - void zts_leave(const char * nwid); + void zts_get_6plane_addr(struct sockaddr_storage *addr, const char *nwid, const char *devID); + void zts_get_rfc4193_addr(struct sockaddr_storage *addr, const char *nwid, const char *devID); + void zts_join(const uin64_t nwid); + void zts_leave(const uint64_t nwid); int zts_running(); int zts_start(const char *path); int zts_start(const char *path, const char *nwid); @@ -1958,8 +1955,8 @@ int ZT_control_semantics_test(bool *passed) */ int n_times = 5; - char *nwid = "17d709436c2c5367"; - char *path = "fake_path"; + char *nwid = (char*)"17d709436c2c5367"; + char *path = (char*)"fake_path"; /* // Perform operations on ZeroTier before calling zts_start(). Doing this makes absolutely no sense but could happen zts_stop(); @@ -1980,8 +1977,8 @@ int ZT_control_semantics_test(bool *passed) sleep(1); */ zts_start(path, false); - zts_join(nwid); - zts_leave(nwid); + zts_join(strtoll(nwid,NULL,16)); + zts_leave(strtoll(nwid,NULL,16)); zts_stop(); DEBUG_TEST("---\n"); @@ -2000,12 +1997,8 @@ int ZT_control_semantics_test(bool *passed) for (int i=0; i results; std::string remote_echo_ipv4, smode; - std::string nwid, stype; + std::string nwidstr, stype; std::string ipstr, ipstr6, local_ipstr, local_ipstr6, remote_ipstr, remote_ipstr6; int err = 0; @@ -2723,7 +2719,7 @@ int main(int argc , char *argv[]) // get origin details local_ipstr = testConf[me + ".ipv4"]; local_ipstr6 = testConf[me + ".ipv6"]; - nwid = testConf[me + ".nwid"]; + nwidstr = testConf[me + ".nwid"]; path = testConf[me + ".path"]; stype = testConf[me + ".test"]; smode = testConf[me + ".mode"]; @@ -2742,7 +2738,7 @@ int main(int argc , char *argv[]) fprintf(stderr, "\tlocal_ipstr6 = %s\n", local_ipstr6.c_str()); fprintf(stderr, "\tstart_port = %d\n", start_port); fprintf(stderr, "\tpath = %s\n", path.c_str()); - fprintf(stderr, "\tnwid = %s\n", nwid.c_str()); + fprintf(stderr, "\tnwid = %s\n", nwidstr.c_str()); fprintf(stderr, "\ttype = %s\n\n", stype.c_str()); fprintf(stderr, "DESTINATION:\n\n"); fprintf(stderr, "\tremote_ipstr = %s\n", remote_ipstr.c_str()); @@ -2753,10 +2749,10 @@ int main(int argc , char *argv[]) if (me != "dummy") { // used for testing ZT service wrapper API (before, during, and after coming online) // set start time here since we need to wait for both libzt instances to be online DEBUG_TEST("app-thread, waiting for libzt to come online...\n"); - zts_startjoin(path.c_str(), nwid.c_str()); - char device_id[ZTO_ID_LEN]; - zts_get_id(device_id); - DEBUG_TEST("I am %s, %s", device_id, me.c_str()); + uint64_t nwid = strtoll(nwidstr.c_str(),NULL,16); + zts_startjoin(path.c_str(), nwid); + uint64_t nodeId = zts_get_node_id(); + DEBUG_TEST("I am %x, %s", nodeId, me.c_str()); if (mode == TEST_MODE_SERVER) { DEBUG_TEST("Ready. You should start selftest program on second host now...\n\n"); } diff --git a/zto b/zto index ee4783e..e5d1243 160000 --- a/zto +++ b/zto @@ -1 +1 @@ -Subproject commit ee4783e3fb975d73aabeaa433c346aea043908a9 +Subproject commit e5d1243dd225526b044d36b15dd28a74c49a8816