From 564531042d7172347edee72dbc2db432210a9562 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 1 Nov 2016 15:38:09 -0700 Subject: [PATCH] split stack driver sections --- build/README.md | 3 - make-linux.mk | 25 +- make-mac.mk | 81 +- src/debug.h | 22 +- src/rpc.c | 4 +- src/sdk.h | 2 - src/sdkutils.hpp | 28 +- src/stack_drivers/README.md | 2 +- src/stack_drivers/jip/README.md | 4 + src/stack_drivers/jip/jip.cpp | 52 + src/stack_drivers/{ => jip}/jip.hpp | 10 +- src/stack_drivers/lwip/README.md | 2 + src/stack_drivers/lwip/lwip.cpp | 1135 ++++++++++++ src/stack_drivers/{ => lwip}/lwip.hpp | 139 +- src/stack_drivers/picotcp/README.md | 2 + src/stack_drivers/picotcp/picotcp.cpp | 568 ++++++ src/stack_drivers/{ => picotcp}/picotcp.hpp | 21 + src/tap.cpp | 1810 +------------------ src/tap.hpp | 169 +- 19 files changed, 2142 insertions(+), 1937 deletions(-) delete mode 100644 build/README.md create mode 100644 src/stack_drivers/jip/README.md create mode 100644 src/stack_drivers/jip/jip.cpp rename src/stack_drivers/{ => jip}/jip.hpp (92%) create mode 100644 src/stack_drivers/lwip/README.md create mode 100644 src/stack_drivers/lwip/lwip.cpp rename src/stack_drivers/{ => lwip}/lwip.hpp (77%) create mode 100644 src/stack_drivers/picotcp/README.md create mode 100644 src/stack_drivers/picotcp/picotcp.cpp rename src/stack_drivers/{ => picotcp}/picotcp.hpp (92%) diff --git a/build/README.md b/build/README.md deleted file mode 100644 index ac6d8e7..0000000 --- a/build/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Build Directory -==== -Where all of your target binaries will be copies along with their required network stack libraries diff --git a/make-linux.mk b/make-linux.mk index 41f5441..bc13fad 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -26,6 +26,19 @@ LWIP_LIB = $(BUILD)/$(LWIP_LIB_NAME) # LWIP_DIR = ext/lwip PICOTCP_DIR = ext/picotcp +# +LWIP_DRIVER_FILES = src/stack_drivers/lwip/lwip.cpp +PICO_DRIVER_FILES = src/stack_drivers/picotcp/picotcp.cpp +SDK_SERVICE_CPP_FILES:=src/tap.cpp \ + src/proxy.cpp \ + $(ZT1)/service/OneService.cpp \ + $(ZT1)/one.cpp + +SDK_SERVICE_C_FILES = src/rpc.c +SDK_INTERCEPT_C_FILES:=sockets.c \ + intercept.c \ + rpc.c + # Automagically pick clang or gcc, with preference for clang # This is only done if we have not overridden these with an environment or CLI variable @@ -86,7 +99,10 @@ INCLUDES+= -Iext \ -I$(LWIP_DIR)/src/include/ipv6 \ -I$(PICOTCP_DIR)/include \ -I$(PICOTCP_DIR)/build/include \ - -Isrc/stack_drivers/lwip + -Isrc/stack_drivers/lwip \ + -Isrc/stack_drivers/picotcp \ + -Isrc/stack_drivers/jip + # Stack selection / parameters @@ -145,8 +161,6 @@ remove_only_intermediates: -find . -type f \( -name '*.o' -o -name '*.so' \) -delete - - # --- EXTERNAL LIBRARIES --- lwip: -make -f make-liblwip.mk $(LWIP_FLAGS) @@ -175,15 +189,16 @@ one: $(OBJS) $(ZT1)/service/OneService.o $(ZT1)/one.o $(ZT1)/osdep/LinuxEthernet # Build only the intercept library linux_intercept: # Use gcc not clang to build standalone intercept library since gcc is typically used for libc and we want to ensure maximal ABI compatibility - cd src ; gcc $(DEFS) $(INCLUDES) -g -O2 -Wall -std=c99 -fPIC -DVERBOSE -D_GNU_SOURCE -DSDK_INTERCEPT -nostdlib -nostdlib -shared -o ../$(INTERCEPT) sockets.c intercept.c rpc.c -ldl + cd src ; gcc $(DEFS) $(INCLUDES) -g -O2 -Wall -std=c99 -fPIC -DVERBOSE -D_GNU_SOURCE -DSDK_INTERCEPT -nostdlib -nostdlib -shared -o ../$(INTERCEPT) $(SDK_INTERCEPT_C_FILES) -ldl # Build only the SDK service ifeq ($(SDK_LWIP),1) linux_sdk_service: lwip $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(STACK_FLAGS) $(DEFS) $(INCLUDES) -DSDK_SERVICE -DSDK -DZT_ONE_NO_ROOT_CHECK -o $(SDK_SERVICE) $(OBJS) $(LWIP_DRIVER_FILES) $(SDK_SERVICE_CPP_FILES) $(SDK_SERVICE_C_FILES) $(LDLIBS) -ldl else linux_sdk_service: pico $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(STACK_FLAGS) $(DEFS) $(INCLUDES) -DSDK_SERVICE -DSDK -DZT_ONE_NO_ROOT_CHECK -o $(SDK_SERVICE) $(OBJS) $(PICO_DRIVER_FILES) $(SDK_SERVICE_CPP_FILES) $(SDK_SERVICE_C_FILES) $(LDLIBS) -ldl endif - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(STACK_FLAGS) $(DEFS) $(INCLUDES) -DSDK -DZT_ONE_NO_ROOT_CHECK -o $(SDK_SERVICE) $(OBJS) $(ZT1)/service/OneService.cpp src/tap.cpp src/proxy.cpp $(ZT1)/one.cpp src/rpc.c $(LDLIBS) -ldl ln -sf $(SDK_SERVICE_NAME) $(BUILD)/zerotier-cli ln -sf $(SDK_SERVICE_NAME) $(BUILD)/zerotier-idtool diff --git a/make-mac.mk b/make-mac.mk index 3b05994..9c06e8e 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -24,7 +24,21 @@ ONE_CLI = $(BUILD)/$(ONE_CLI_NAME) ONE_IDTOOL = $(BUILD)/$(ONE_IDTOOL_NAME) LWIP_LIB = $(BUILD)/$(LWIP_LIB_NAME) # -LWIP_BASE_DIR = ext/lwip +LWIP_DIR = ext/lwip +PICOTCP_DIR = ext/picotcp +# +LWIP_DRIVER_FILES = src/stack_drivers/lwip/lwip.cpp +PICO_DRIVER_FILES = src/stack_drivers/picotcp/picotcp.cpp +SDK_SERVICE_CPP_FILES:=src/tap.cpp \ + src/proxy.cpp \ + $(ZT1)/service/OneService.cpp \ + $(ZT1)/one.cpp + +SDK_SERVICE_C_FILES = src/rpc.c +SDK_INTERCEPT_C_FILES:=sockets.c \ + intercept.c \ + rpc.c + # Automagically pick clang or gcc, with preference for clang # This is only done if we have not overridden these with an environment or CLI variable @@ -79,23 +93,51 @@ INCLUDES+= -Iext \ -I../$(ZT1)/node \ -I../$(ZT1)/service \ -I. \ - -Isrc + -Isrc \ + -Isrc/stack_drivers \ + -I$(LWIP_DIR)/src/include \ + -I$(LWIP_DIR)/src/include/ipv4 \ + -I$(LWIP_DIR)/src/include/ipv6 \ + -I$(PICOTCP_DIR)/include \ + -I$(PICOTCP_DIR)/build/include \ + -Isrc/stack_drivers/lwip \ + -Isrc/stack_drivers/picotcp \ + -Isrc/stack_drivers/jip -# lwIP +# Stack selection / parameters + +# lwIP debug ifeq ($(SDK_LWIP_DEBUG),1) LWIP_FLAGS+=SDK_LWIP_DEBUG=1 endif -ifeq ($(LWIP_VERSION_2),1) - CXXFLAGS+=-DLWIP_VERSION_2 - INCLUDES+=-I$(LWIP_2_DIR)/src/include - INCLUDES+=-I$(LWIP_2_DIR)/src/include/ipv4 - INCLUDES+=-I$(LWIP_2_DIR)/src/include/ipv6 -else - CXXFLAGS+=-DLWIP_VERSION_1 - INCLUDES+=-I$(LWIP_1_DIR)/src/include - INCLUDES+=-I$(LWIP_1_DIR)/src/include/ipv4 - INCLUDES+=-I$(LWIP_1_DIR)/src/include/ipv6 + +# lwIP +ifeq ($(SDK_LWIP),1) + STACK_FLAGS+=-DSDK_LWIP +endif + +# picoTCP +ifeq ($(SDK_PICOTCP),1) + STACK_FLAGS+=-DSDK_PICOTCP +endif + +# jip +ifeq ($(SDK_JIP),1) + STACK_FLAGS+=-DSDK_JIP +endif + + + +# TCP protocol version +ifeq ($(SDK_IPV4),1) + LWIP_FLAGS+=SDK_IPV4=1 + STACK_FLAGS+=-DSDK_IPV4 +endif + +ifeq ($(SDK_IPV6),1) + LWIP_FLAGS+=SDK_IPV6=1 + STACK_FLAGS+=-DSDK_IPV6 endif @@ -174,13 +216,18 @@ osx_shared_lib: $(OBJS) osx_intercept: # Use gcc not clang to build standalone intercept library since gcc is typically used for libc and we want to ensure maximal ABI compatibility - cd src ; gcc $(DEFS) $(INCLUDES) -g -O2 -Wall -std=c99 -fPIC -DVERBOSE -D_GNU_SOURCE -DSDK_INTERCEPT -nostdlib -nostdlib -shared -o ../$(INTERCEPT) SDK_Sockets.c SDK_Intercept.c SDK_RPC.c -ldl + cd src ; gcc $(DEFS) $(INCLUDES) -g -O2 -Wall -std=c99 -fPIC -DVERBOSE -D_GNU_SOURCE -DSDK_INTERCEPT -nostdlib -nostdlib -shared -o ../$(INTERCEPT) $(SDK_INTERCEPT_C_FILES) -ldl # Build only the SDK service +ifeq ($(SDK_LWIP),1) osx_sdk_service: lwip $(OBJS) - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(DEFS) $(INCLUDES) -DSDK -DZT_ONE_NO_ROOT_CHECK -o $(SDK_SERVICE) $(OBJS) $(ZT1)/service/OneService.cpp src/SDK_EthernetTap.cpp src/SDK_Proxy.cpp $(ZT1)/one.cpp -x c src/SDK_RPC.c $(LDLIBS) -ldl - ln -sf $(SDK_SERVICE_NAME) zerotier-cli - ln -sf $(SDK_SERVICE_NAME) zerotier-idtool + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(STACK_FLAGS) $(DEFS) $(INCLUDES) -DSDK_SERVICE -DSDK -DZT_ONE_NO_ROOT_CHECK -o $(SDK_SERVICE) $(OBJS) $(LWIP_DRIVER_FILES) $(SDK_SERVICE_CPP_FILES) $(SDK_SERVICE_C_FILES) $(LDLIBS) -ldl +else +osx_sdk_service: pico $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(STACK_FLAGS) $(DEFS) $(INCLUDES) -DSDK_SERVICE -DSDK -DZT_ONE_NO_ROOT_CHECK -o $(SDK_SERVICE) $(OBJS) $(PICO_DRIVER_FILES) $(SDK_SERVICE_CPP_FILES) $(SDK_SERVICE_C_FILES) $(LDLIBS) -ldl +endif + ln -sf $(SDK_SERVICE_NAME) $(BUILD)/zerotier-cli + ln -sf $(SDK_SERVICE_NAME) $(BUILD)/zerotier-idtool # Build both intercept library and SDK service (separate) osx_service_and_intercept: osx_intercept osx_sdk_service diff --git a/src/debug.h b/src/debug.h index 9613238..0f4a37d 100644 --- a/src/debug.h +++ b/src/debug.h @@ -87,19 +87,19 @@ extern "C" { //#if defined(SDK_DEBUG) #if DEBUG_LEVEL >= MSG_ERROR - #define DEBUG_ERROR(fmt, args...) fprintf(stderr, RED "ZT_ERROR: %20s:%4d:%25s: " fmt "\n" RESET, __FILENAME__, __LINE__, __FUNCTION__, ##args) + #define DEBUG_ERROR(fmt, args...) fprintf(stderr, RED "ZT_ERROR: %14s:%4d:%25s: " fmt "\n" RESET, __FILENAME__, __LINE__, __FUNCTION__, ##args) #else #define DEBUG_ERROR(fmt, args...) #endif #if DEBUG_LEVEL >= MSG_INFO #if defined(__ANDROID__) - #define DEBUG_INFO(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, "ZT_INFO : %20s:%4d:%20s: " fmt "\n", __FILENAME__, __LINE__, __FUNCTION__, ##args)) - #define DEBUG_BLANK(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, "ZT_INFO : %20s:%4d:" fmt "\n", __FILENAME__, __LINE__, __FUNCTION__, ##args)) + #define DEBUG_INFO(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, "ZT_INFO : %14s:%4d:%20s: " fmt "\n", __FILENAME__, __LINE__, __FUNCTION__, ##args)) + #define DEBUG_BLANK(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, "ZT_INFO : %14s:%4d:" fmt "\n", __FILENAME__, __LINE__, __FUNCTION__, ##args)) #else - #define DEBUG_INFO(fmt, args...) fprintf(stderr, "ZT_INFO : %20s:%4d:%25s: " fmt "\n", __FILENAME__, __LINE__, __FUNCTION__, ##args) - #define DEBUG_ATTN(fmt, args...) fprintf(stderr, CYN "ZT_INFO : %20s:%4d:%25s: " fmt "\n" RESET, __FILENAME__, __LINE__, __FUNCTION__, ##args) - #define DEBUG_STACK(fmt, args...) fprintf(stderr, YEL "ZT_STACK: %20s:%4d:%25s: " fmt "\n" RESET, __FILENAME__, __LINE__, __FUNCTION__, ##args) - #define DEBUG_BLANK(fmt, args...) fprintf(stderr, "ZT_INFO : %20s:%4d:" fmt "\n", __FILENAME__, __LINE__, ##args) + #define DEBUG_INFO(fmt, args...) fprintf(stderr, "ZT_INFO : %14s:%4d:%25s: " fmt "\n", __FILENAME__, __LINE__, __FUNCTION__, ##args) + #define DEBUG_ATTN(fmt, args...) fprintf(stderr, CYN "ZT_INFO : %14s:%4d:%25s: " fmt "\n" RESET, __FILENAME__, __LINE__, __FUNCTION__, ##args) + #define DEBUG_STACK(fmt, args...) fprintf(stderr, YEL "ZT_STACK: %14s:%4d:%25s: " fmt "\n" RESET, __FILENAME__, __LINE__, __FUNCTION__, ##args) + #define DEBUG_BLANK(fmt, args...) fprintf(stderr, "ZT_INFO : %14s:%4d:" fmt "\n", __FILENAME__, __LINE__, ##args) #endif #else #define DEBUG_INFO(fmt, args...) @@ -107,18 +107,18 @@ extern "C" { #endif #if DEBUG_LEVEL >= MSG_TRANSFER #if defined(__ANDROID__) - #define DEBUG_TRANS(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, "ZT_TRANS : %20s:%4d:%25s: " fmt "\n", __FILENAME__, __LINE__, __FUNCTION__, ##args)) + #define DEBUG_TRANS(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, "ZT_TRANS : %14s:%4d:%25s: " fmt "\n", __FILENAME__, __LINE__, __FUNCTION__, ##args)) #else - #define DEBUG_TRANS(fmt, args...) fprintf(stderr, GRN "ZT_TRANS: %20s:%4d:%25s: " fmt "\n" RESET, __FILENAME__, __LINE__, __FUNCTION__, ##args) + #define DEBUG_TRANS(fmt, args...) fprintf(stderr, GRN "ZT_TRANS: %14s:%4d:%25s: " fmt "\n" RESET, __FILENAME__, __LINE__, __FUNCTION__, ##args) #endif #else #define DEBUG_TRANS(fmt, args...) #endif #if DEBUG_LEVEL >= MSG_EXTRA #if defined(__ANDROID__) - #define DEBUG_EXTRA(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, "ZT_EXTRA : %20s:%4d:%25s: " fmt "\n", __FILENAME__, __LINE__, __FUNCTION__, ##args)) + #define DEBUG_EXTRA(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, "ZT_EXTRA : %14s:%4d:%25s: " fmt "\n", __FILENAME__, __LINE__, __FUNCTION__, ##args)) #else - #define DEBUG_EXTRA(fmt, args...) fprintf(stderr, "ZT_EXTRA: %20s:%4d:%25s: " fmt "\n", __FILENAME__, __LINE__, __FUNCTION__, ##args) + #define DEBUG_EXTRA(fmt, args...) fprintf(stderr, "ZT_EXTRA: %14s:%4d:%25s: " fmt "\n", __FILENAME__, __LINE__, __FUNCTION__, ##args) #endif #else #define DEBUG_EXTRA(fmt, args...) diff --git a/src/rpc.c b/src/rpc.c index a968627..968c374 100644 --- a/src/rpc.c +++ b/src/rpc.c @@ -135,7 +135,7 @@ int rpc_join(char * sockname) #else if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){ #endif - DEBUG_ERROR("error while creating RPC socket"); + DEBUG_ERROR("error creating RPC socket"); return -1; } while((conn_err != 0) /* && (attempts < SERVICE_CONNECT_ATTEMPTS) */){ @@ -144,7 +144,7 @@ int rpc_join(char * sockname) #else if((conn_err = connect(sock, (struct sockaddr*)&addr, sizeof(addr))) != 0) { #endif - DEBUG_ERROR("error while connecting to RPC socket. Re-attempting..."); + DEBUG_ERROR("error connecting to RPC socket. Re-attempting..."); usleep(100000); } else diff --git a/src/sdk.h b/src/sdk.h index 6704f63..a0c1427 100644 --- a/src/sdk.h +++ b/src/sdk.h @@ -158,13 +158,11 @@ ssize_t zts_recvmsg(RECVMSG_SIG); JNIEXPORT jobject JNICALL Java_ZeroTier_ZTSDK_zt_1get_1ipv4_1address(JNIEnv *env, jobject thisObj, jstring nwid); JNIEXPORT jobject JNICALL Java_ZeroTier_ZTSDK_zt_1get_1ipv6_1address(JNIEnv *env, jobject thisObj, jstring nwid); JNIEXPORT jboolean JNICALL Java_ZeroTier_ZTSDK_zt_1is_1relayed(); - // SOCKS5 PROXY SERVER CONTROLS JNIEXPORT jint JNICALL Java_ZeroTier_ZTSDK_zt_1start_1proxy_1server(JNIEnv *env, jobject thisObj, jstring nwid, jobject zaddr); JNIEXPORT jint JNICALL Java_ZeroTier_ZTSDK_zt_1stop_1proxy_1server(JNIEnv *env, jobject thisObj, jstring nwid); JNIEXPORT jint JNICALL Java_ZeroTier_ZTSDK_zt_1get_1proxy_1server_1address(JNIEnv *env, jobject thisObj, jstring nwid, jobject zaddr); JNIEXPORT jboolean JNICALL Java_ZeroTier_ZTSDK_zt_1proxy_1is_1running(JNIEnv *env, jobject thisObj, jstring nwid); - // SOCKET API JNIEXPORT jint JNICALL Java_ZeroTier_ZTSDK_zt_1socket(JNIEnv *env, jobject thisObj, jint family, jint type, jint protocol); JNIEXPORT jint JNICALL Java_ZeroTier_ZTSDK_zt_1connect(JNIEnv *env, jobject thisObj, jint fd, jstring addrstr, jint port); diff --git a/src/sdkutils.hpp b/src/sdkutils.hpp index 45fa4c5..e764158 100644 --- a/src/sdkutils.hpp +++ b/src/sdkutils.hpp @@ -25,6 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ +#ifndef _SDK_UTILS_HPP_ +#define _SDK_UTILS_HPP_ #if defined(SDK_LWIP) && defined(SDK_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)); \ @@ -32,9 +34,8 @@ (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 - void in6_to_ip6(ip6_addr_t *ba, struct sockaddr_in6 *in6) + inline void in6_to_ip6(ip6_addr_t *ba, struct sockaddr_in6 *in6) { uint8_t *ip = &(in6->sin6_addr).s6_addr[0]; uint16_t ip16; @@ -49,4 +50,25 @@ (((ip[14] & 0xffff) << 8) | ((ip[15]) & 0xffff)) ); } -#endif \ No newline at end of file +#endif + +#if defined(SDK_LWIP) && defined(SDK_IPV4) + #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 ip_addr_t convert_ip(struct sockaddr_in * addr) + { + ip_addr_t conn_addr; + 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(&conn_addr, a,b,c,d); + return conn_addr; + } +#endif + +#endif // _SDK_UTILS_HPP_ \ No newline at end of file diff --git a/src/stack_drivers/README.md b/src/stack_drivers/README.md index ba713dc..81b0711 100644 --- a/src/stack_drivers/README.md +++ b/src/stack_drivers/README.md @@ -1,4 +1,4 @@ Stack Drivers ==== -These files contain code to load and interface with network stacks. +These files contain code to load, initialize, and interface network stacks to the ZeroTier ethernet tap service. diff --git a/src/stack_drivers/jip/README.md b/src/stack_drivers/jip/README.md new file mode 100644 index 0000000..479a48b --- /dev/null +++ b/src/stack_drivers/jip/README.md @@ -0,0 +1,4 @@ +jIP Network Stack Driver +==== + +This section only exists as minimal example of how a network stack would interact with the ZeroTier ethernet tap service. See the `lwIP` and `picoTCP` driver sections for full implementations of a stack driver. \ No newline at end of file diff --git a/src/stack_drivers/jip/jip.cpp b/src/stack_drivers/jip/jip.cpp new file mode 100644 index 0000000..d8145ab --- /dev/null +++ b/src/stack_drivers/jip/jip.cpp @@ -0,0 +1,52 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#if defined(SDK_JIP) + +namespace ZeroTier { + + void jip_init_interface(NetconEthernetTap *tap, const InetAddress &ip) + { + // initialize your stack here + } + + void jip_loop(NetconEthernetTap *tap) + { + while(_run) + { + // Tick stack timers here + // Perhaps do some polling? + } + } + + void jip_rx(NetconEthernetTap *tap, const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) + { + // RX packets here + } +} + +#endif // SDK_JIP \ No newline at end of file diff --git a/src/stack_drivers/jip.hpp b/src/stack_drivers/jip/jip.hpp similarity index 92% rename from src/stack_drivers/jip.hpp rename to src/stack_drivers/jip/jip.hpp index b0b796e..0991bab 100644 --- a/src/stack_drivers/jip.hpp +++ b/src/stack_drivers/jip/jip.hpp @@ -28,6 +28,8 @@ #ifndef SDK_JIPSTACK_H #define SDK_JIPSTACK_H +#if defined(SDK_JIP) + #include "Mutex.hpp" #include "OSUtils.hpp" #include "debug.h" @@ -41,6 +43,10 @@ namespace ZeroTier { + void jip_init_interface(NetconEthernetTap *tap, const InetAddress &ip); + void jip_loop(NetconEthernetTap *tap); + void jip_rx(NetconEthernetTap *tap, const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); + /** * Loads an instance of picoTCP stack library in a private memory arena * @@ -123,4 +129,6 @@ namespace ZeroTier { } // namespace ZeroTier -#endif +#endif // SDK_JIP + +#endif diff --git a/src/stack_drivers/lwip/README.md b/src/stack_drivers/lwip/README.md new file mode 100644 index 0000000..6116363 --- /dev/null +++ b/src/stack_drivers/lwip/README.md @@ -0,0 +1,2 @@ +lwIP Network Stack Driver +==== diff --git a/src/stack_drivers/lwip/lwip.cpp b/src/stack_drivers/lwip/lwip.cpp new file mode 100644 index 0000000..9e4e701 --- /dev/null +++ b/src/stack_drivers/lwip/lwip.cpp @@ -0,0 +1,1135 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +//#include "lwip.hpp" +#include "tap.hpp" +#include "sdkutils.hpp" +//#include "debug.h" + +namespace ZeroTier +{ + void lwip_init_interface(NetconEthernetTap *tap, const InetAddress &ip) + { + DEBUG_INFO(); + lwIP_stack *stack = tap->lwipstack; + Mutex::Lock _l(tap->_ips_m); + + if (std::find(tap->_ips.begin(),tap->_ips.end(),ip) == tap->_ips.end()) { + tap->_ips.push_back(ip); + std::sort(tap->_ips.begin(),tap->_ips.end()); + + #if defined(SDK_IPV4) + if (ip.isV4()) { + // Set IP + static ip_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()); + stack->__netif_add(&(tap->interface),&ipaddr, &netmask, &gw, NULL, tapif_init, stack->_ethernet_input); + tap->interface.state = tap; + tap->interface.output = stack->_etharp_output; + tap->_mac.copyTo(tap->interface.hwaddr, 6); + tap->interface.mtu = tap->_mtu; + tap->interface.name[0] = 'l'; + tap->interface.name[1] = '4'; + tap->interface.linkoutput = low_level_output; + tap->interface.hwaddr_len = 6; + tap->interface.flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP; + stack->__netif_set_default(&(tap->interface)); + stack->__netif_set_up(&(tap->interface)); + DEBUG_INFO("addr=%s, netmask=%s", ip.toString().c_str(), ip.netmask().toString().c_str()); + } + #endif + #if defined(SDK_IPV6) + if(ip.isV6()) { + DEBUG_INFO("local_addr=%s", ip.toString().c_str()); + static ip6_addr_t addr6; + struct sockaddr_in6 in6; + memcpy(in6.sin6_addr.s6_addr,ip.rawIpData(),16); + in6_to_ip6((ip6_addr *)&addr6, &in6); + tap->interface6.mtu = tap->_mtu; + tap->interface6.name[0] = 'l'; + tap->interface6.name[1] = '6'; + tap->interface6.hwaddr_len = 6; + tap->interface6.linkoutput = low_level_output; + tap->interface6.ip6_autoconfig_enabled = 1; + tap->_mac.copyTo(tap->interface6.hwaddr, tap->interface6.hwaddr_len); + stack->__netif_create_ip6_linklocal_address(&(tap->interface6), 1); + stack->__netif_add(&(tap->interface6), NULL, tapif_init, stack->_ethernet_input); + stack->__netif_set_default(&(tap->interface6)); + stack->__netif_set_up(&(tap->interface6)); + netif_ip6_addr_set_state(&(tap->interface6), 1, IP6_ADDR_TENTATIVE); + ip6_addr_copy(ip_2_ip6(tap->interface6.ip6_addr[1]), addr6); + tap->interface6.output_ip6 = stack->_ethip6_output; + tap->interface6.state = tap; + tap->interface6.flags = NETIF_FLAG_LINK_UP | NETIF_FLAG_UP; + DEBUG_INFO("addr=%s, netmask=%s", ip.toString().c_str(), ip.netmask().toString().c_str()); + } + #endif + } + } + + void lwip_loop(NetconEthernetTap *tap) + { + DEBUG_INFO(); + lwIP_stack *stack = tap->lwipstack; + uint64_t prev_tcp_time = 0, prev_status_time = 0, prev_discovery_time = 0; + // Main timer loop + while (tap->_run) { + uint64_t now = OSUtils::now(); + uint64_t since_tcp = now - prev_tcp_time; + uint64_t since_discovery = now - prev_discovery_time; + uint64_t since_status = now - prev_status_time; + uint64_t tcp_remaining = ZT_LWIP_TCP_TIMER_INTERVAL; + uint64_t discovery_remaining = 5000; + + #if defined(LWIP_IPV6) + #define DISCOVERY_INTERVAL 1000 + #elif + #define DISCOVERY_INTERVAL ARP_TMR_INTERVAL + #endif + + // Connection prunning + if (since_status >= STATUS_TMR_INTERVAL) { + prev_status_time = now; + for(size_t i=0;i_Connections.size();++i) { + if(!tap->_Connections[i]->sock || tap->_Connections[i]->type != SOCK_STREAM) + continue; + int fd = tap->_phy.getDescriptor(tap->_Connections[i]->sock); + // DEBUG_INFO(" tap_thread(): tcp\\jobs = {%d, %d}\n", _Connection.size(), jobmap.size()); + // If there's anything on the RX buf, set to notify in case we stalled + if(tap->_Connections[i]->rxsz > 0) + tap->_phy.setNotifyWritable(tap->_Connections[i]->sock, true); + fcntl(fd, F_SETFL, O_NONBLOCK); + unsigned char tmpbuf[BUF_SZ]; + + ssize_t n = read(fd,&tmpbuf,BUF_SZ); + if(tap->_Connections[i]->TCP_pcb->state == SYN_SENT) { + DEBUG_EXTRA(" should finish or be removed soon, sock=%p, state=SYN_SENT", + (void*)&(tap->_Connections[i]->sock)); + } + if((n < 0 && errno != EAGAIN) || (n == 0 && errno == EAGAIN)) { + //DEBUG_INFO(" closing sock (%x)", (void*)_Connections[i]->sock); + tap->closeConnection(tap->_Connections[i]->sock); + } else if (n > 0) { + DEBUG_INFO(" data read during connection check (%ld bytes)", n); + tap->phyOnUnixData(tap->_Connections[i]->sock,tap->_phy.getuptr(tap->_Connections[i]->sock),&tmpbuf,n); + } + } + } + // Main TCP/ETHARP timer section + if (since_tcp >= ZT_LWIP_TCP_TIMER_INTERVAL) { + prev_tcp_time = now; + stack->__tcp_tmr(); + // FIXME: could be removed or refactored? + // Makeshift poll + for(size_t i=0;i_Connections.size();++i) { + if(tap->_Connections[i]->txsz > 0){ + lwip_handleWrite(tap, tap->_Connections[i]); + } + } + } else { + tcp_remaining = ZT_LWIP_TCP_TIMER_INTERVAL - since_tcp; + } + if (since_discovery >= DISCOVERY_INTERVAL) { + prev_discovery_time = now; + #if defined(SDK_IPV4) + stack->__etharp_tmr(); + #endif + #if defined(SDK_IPV6) + stack->__nd6_tmr(); + #endif + } else { + discovery_remaining = DISCOVERY_INTERVAL - since_discovery; + } + tap->_phy.poll((unsigned long)std::min(tcp_remaining,discovery_remaining)); + } + stack->close(); + } + + void lwip_rx(NetconEthernetTap *tap, const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) + { + // DEBUG_EXTRA(); + lwIP_stack *stack = tap->lwipstack; + struct pbuf *p,*q; + if (!tap->_enabled) + return; + struct eth_hdr ethhdr; + from.copyTo(ethhdr.src.addr, 6); + to.copyTo(ethhdr.dest.addr, 6); + ethhdr.type = Utils::hton((uint16_t)etherType); + + p = stack->__pbuf_alloc(PBUF_RAW, len+sizeof(struct eth_hdr), PBUF_POOL); + if (p != NULL) { + const char *dataptr = reinterpret_cast(data); + // First pbuf gets ethernet header at start + q = p; + if (q->len < sizeof(ethhdr)) { + DEBUG_ERROR("dropped packet: first pbuf smaller than ethernet header"); + return; + } + memcpy(q->payload,ðhdr,sizeof(ethhdr)); + memcpy((char*)q->payload + sizeof(ethhdr),dataptr,q->len - sizeof(ethhdr)); + dataptr += q->len - sizeof(ethhdr); + + // Remaining pbufs (if any) get rest of data + while ((q = q->next)) { + memcpy(q->payload,dataptr,q->len); + dataptr += q->len; + } + } + else { + DEBUG_ERROR("dropped packet: no pbufs available"); + return; + } + { + #if defined(SDK_IPV6) + if(tap->interface6.input(p, &(tap->interface6)) != ERR_OK) { + DEBUG_ERROR("error while feeding frame into stack interface6"); + } + #endif + #if defined(SDK_IPV4) + if(tap->interface.input(p, &(tap->interface)) != ERR_OK) { + DEBUG_ERROR("error while feeding frame into stack interface"); + } + #endif + } + } + + // Create and set up a lwIP socket PCB and Connection object + Connection *lwip_handleSocket(NetconEthernetTap *tap, PhySocket *sock, void **uptr, struct socket_st* socket_rpc) + { + lwIP_stack *stack = tap->lwipstack; + struct udp_pcb *new_udp_PCB = NULL; + struct tcp_pcb *new_tcp_PCB = NULL; + + if(socket_rpc->socket_type == SOCK_DGRAM) { + DEBUG_EXTRA("SOCK_DGRAM"); + Mutex::Lock _l(tap->_tcpconns_m); + new_udp_PCB = stack->__udp_new(); + } + else if(socket_rpc->socket_type == SOCK_STREAM) { + DEBUG_EXTRA("SOCK_STREAM"); + Mutex::Lock _l(tap->_tcpconns_m); + new_tcp_PCB = stack->__tcp_new(); + } + else if(socket_rpc->socket_type == SOCK_RAW) { + DEBUG_ERROR("SOCK_RAW, not currently supported."); + } + if(new_udp_PCB || new_tcp_PCB) { + Connection * newConn = new Connection(); + *uptr = newConn; + newConn->type = socket_rpc->socket_type; + newConn->sock = sock; + newConn->local_addr = NULL; + newConn->peer_addr = NULL; + if(newConn->type == SOCK_DGRAM) newConn->UDP_pcb = new_udp_PCB; + if(newConn->type == SOCK_STREAM) newConn->TCP_pcb = new_tcp_PCB; + tap->_Connections.push_back(newConn); + return newConn; + } + DEBUG_ERROR(" memory not available for new PCB"); + tap->sendReturnValue(tap->_phy.getDescriptor(sock), -1, ENOMEM); + return NULL; + } + + // + Connection * lwip_handleSocketProxy(NetconEthernetTap *tap, PhySocket *sock, int socket_type) + { + /* + Connection *conn = getConnection(sock); + if(!conn){ + DEBUG_ERROR("unable to locate Connection object for this PhySocket sock=%p", (void*)&sock); + return NULL; + } + DEBUG_ATTN("sock=%p", (void*)&sock); + struct udp_pcb *new_udp_PCB = NULL; + struct tcp_pcb *new_tcp_PCB = NULL; + if(socket_type == SOCK_DGRAM) { + DEBUG_EXTRA("SOCK_DGRAM"); + Mutex::Lock _l(_tcpconns_m); + new_udp_PCB = lwipstack->__udp_new(); + } + else if(socket_type == SOCK_STREAM) { + DEBUG_EXTRA("SOCK_STREAM"); + Mutex::Lock _l(_tcpconns_m); + new_tcp_PCB = lwipstack->__tcp_new(); + } + if(new_udp_PCB || new_tcp_PCB) { + conn->sock = sock; + conn->type = socket_type; + conn->local_addr = NULL; + conn->peer_addr = NULL; + if(conn->type == SOCK_DGRAM) conn->UDP_pcb = new_udp_PCB; + if(conn->type == SOCK_STREAM) conn->TCP_pcb = new_tcp_PCB; + DEBUG_INFO(" updated sock=%p", (void*)&sock); + return conn; + } + DEBUG_ERROR(" memory not available for new PCB"); + */ + return NULL; + } + + // Connects an lwIP socket PCB to a remote host + void lwip_handleConnect(NetconEthernetTap *tap, PhySocket *sock, PhySocket *rpcSock, Connection *conn, struct connect_st* connect_rpc) + { + lwIP_stack *stack = tap->lwipstack; + ip_addr_t ba; + char addrstr[INET6_ADDRSTRLEN]; + struct sockaddr_in6 *rawAddr = (struct sockaddr_in6 *) &connect_rpc->addr; + struct sockaddr *addr = (struct sockaddr*)rawAddr; + int err, port = stack->__lwip_ntohs(rawAddr->sin6_port); + + // ipv4 + #if defined(SDK_IPV4) + if(addr->sa_family == AF_INET) { + struct sockaddr_in *connaddr = (struct sockaddr_in *)addr; + inet_ntop(AF_INET, &(connaddr->sin_addr), addrstr, INET_ADDRSTRLEN); + sprintf(addrstr, "%s:%d", addrstr, stack->__lwip_ntohs(connaddr->sin_port)); + } + struct sockaddr_in *rawAddr4 = (struct sockaddr_in *) &connect_rpc->addr; + ba = convert_ip(rawAddr4); + port = stack->__lwip_ntohs(rawAddr4->sin_port); + #endif + + // ipv6 + #if defined(SDK_IPV6) + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&connect_rpc->addr; + in6_to_ip6((ip6_addr *)&ba, in6); + + if(addr->sa_family == AF_INET6) { + struct sockaddr_in6 *connaddr6 = (struct sockaddr_in6 *)addr; + inet_ntop(AF_INET6, &(connaddr6->sin6_addr), addrstr, INET6_ADDRSTRLEN); + sprintf(addrstr, "%s:%d", addrstr, stack->__lwip_ntohs(connaddr6->sin6_port)); + } + #endif + DEBUG_INFO("addr=%s", addrstr); + + if(conn->type == SOCK_DGRAM) { + // Generates no network traffic + if((err = stack->__udp_connect(conn->UDP_pcb,(ip_addr_t *)&ba,port)) < 0) + DEBUG_ERROR("error while connecting to with UDP"); + stack->__udp_recv(conn->UDP_pcb, nc_udp_recved, new Larg(tap, conn)); + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), 0, ERR_OK); + return; + } + if(conn != NULL) { + stack->__tcp_sent(conn->TCP_pcb, nc_sent); + stack->__tcp_recv(conn->TCP_pcb, nc_recved); + stack->__tcp_err(conn->TCP_pcb, nc_err); + stack->__tcp_poll(conn->TCP_pcb, nc_poll, APPLICATION_POLL_FREQ); + stack->__tcp_arg(conn->TCP_pcb, new Larg(tap, conn)); + + DEBUG_EXTRA(" pcb->state=%x", conn->TCP_pcb->state); + if(conn->TCP_pcb->state != CLOSED) { + DEBUG_INFO(" cannot connect using this PCB, PCB!=CLOSED"); + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, EAGAIN); + return; + } + if((err = stack->__tcp_connect(conn->TCP_pcb,&ba,port,nc_connected)) < 0) + { + if(err == ERR_ISCONN) { + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, EISCONN); // Already in connected state + return; + } if(err == ERR_USE) { + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, EADDRINUSE); // Already in use + return; + } if(err == ERR_VAL) { + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, EINVAL); // Invalid ipaddress parameter + return; + } if(err == ERR_RTE) { + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, ENETUNREACH); // No route to host + return; + } if(err == ERR_BUF) { + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, EAGAIN); // No more ports available + return; + } + if(err == ERR_MEM) { + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, EAGAIN); // TODO: Doesn't describe the problem well, but closest match + return; + } + // We should only return a value if failure happens immediately + // Otherwise, we still need to wait for a callback from lwIP. + // - This is because an ERR_OK from tcp_connect() only verifies + // that the SYN packet was enqueued onto the stack properly, + // that's it! + // - Most instances of a retval for a connect() should happen + // in the nc_connect() and nc_err() callbacks! + DEBUG_ERROR(" unable to connect"); + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, EAGAIN); + } + // Everything seems to be ok, but we don't have enough info to retval + conn->listening=true; + conn->rpcSock=rpcSock; // used for return value from lwip CB + } + else { + DEBUG_ERROR(" could not locate PCB based on application-provided fd"); + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, EBADF); + } + } + + // Connect to a remote hose via the built-in SOCKS5 proxy server + int lwip_handleConnectProxy(NetconEthernetTap *tap, PhySocket *sock, struct sockaddr_in *rawAddr) + { + /* + DEBUG_ATTN("sock=%p", (void*)&sock); + Mutex::Lock _l(_tcpconns_m); + int port = rawAddr->sin_port; + ip_addr_t connAddr = convert_ip(rawAddr); + int err = 0; + + Connection *conn = getConnection(sock); + if(!conn) { + DEBUG_INFO(" unable to locate Connection object for sock=%p", (void*)&sock); + return -1; + } + if(conn->type == SOCK_DGRAM) { + // Generates no network traffic + if((err = lwipstack->__udp_connect(conn->UDP_pcb,&connAddr,port)) < 0) + DEBUG_INFO("error while connecting to with UDP (sock=%p)", (void*)&sock); + lwipstack->__udp_recv(conn->UDP_pcb, nc_udp_recved, new Larg(this, conn)); + errno = ERR_OK; + return 0; + } + if(conn != NULL) { + lwipstack->__tcp_sent(conn->TCP_pcb, nc_sent); + lwipstack->__tcp_recv(conn->TCP_pcb, nc_recved); + lwipstack->__tcp_err(conn->TCP_pcb, nc_err); + lwipstack->__tcp_poll(conn->TCP_pcb, nc_poll, APPLICATION_POLL_FREQ); + lwipstack->__tcp_arg(conn->TCP_pcb, new Larg(this, conn)); + + int ip = rawAddr->sin_addr.s_addr; + unsigned char d[4]; + d[0] = ip & 0xFF; + d[1] = (ip >> 8) & 0xFF; + d[2] = (ip >> 16) & 0xFF; + d[3] = (ip >> 24) & 0xFF; + DEBUG_INFO(" addr=%d.%d.%d.%d:%d", d[0],d[1],d[2],d[3], port); + DEBUG_INFO(" pcb->state=%x", conn->TCP_pcb->state); + if(conn->TCP_pcb->state != CLOSED) { + DEBUG_INFO(" cannot connect using this PCB, PCB!=CLOSED"); + errno = EAGAIN; + return -1; + } + if((err = lwipstack->__tcp_connect(conn->TCP_pcb,&connAddr,port,nc_connected_proxy)) < 0) + { + if(err == ERR_ISCONN) { + errno = EISCONN; // Already in connected state + return -1; + } if(err == ERR_USE) { + errno = EADDRINUSE; // Already in use + return -1; + } if(err == ERR_VAL) { + errno = EINVAL; // Invalid ipaddress parameter + return -1; + } if(err == ERR_RTE) { + errno = ENETUNREACH; // No route to host + return -1; + } if(err == ERR_BUF) { + errno = EAGAIN; // No more ports available + return -1; + } + if(err == ERR_MEM) { + // Can occur for the following reasons: tcp_enqueue_flags() + + // 1) tcp_enqueue_flags is always called with either SYN or FIN in flags. + // We need one available snd_buf byte to do that. + // This means we can't send FIN while snd_buf==0. A better fix would be to + // not include SYN and FIN sequence numbers in the snd_buf count. + + // 2) Cannot allocate new pbuf + // 3) Cannot allocate new TCP segment + + errno = EAGAIN; // TODO: Doesn't describe the problem well, but closest match + return -1; + } + // We should only return a value if failure happens immediately + // Otherwise, we still need to wait for a callback from lwIP. + // - This is because an ERR_OK from tcp_connect() only verifies + // that the SYN packet was enqueued onto the stack properly, + // that's it! + // - Most instances of a retval for a connect() should happen + // in the nc_connect() and nc_err() callbacks! + DEBUG_ERROR(" unable to connect"); + errno = EAGAIN; + return -1; + } + // Everything seems to be ok, but we don't have enough info to retval + conn->listening=true; + return 0; + } else { + DEBUG_ERROR(" could not locate PCB based on application-provided fd"); + errno = EBADF; + return -1; + } + */ + return -1; + } + + void lwip_handleBind(NetconEthernetTap *tap, PhySocket *sock, PhySocket *rpcSock, void **uptr, struct bind_st *bind_rpc) + { + lwIP_stack *stack = tap->lwipstack; + ip_addr_t ba; + char addrstr[INET6_ADDRSTRLEN]; + struct sockaddr_in6 *rawAddr = (struct sockaddr_in6 *) &bind_rpc->addr; + struct sockaddr *addr = (struct sockaddr*)rawAddr; + int err, port = stack->__lwip_ntohs(rawAddr->sin6_port); + + // ipv4 + #if defined(SDK_IPV4) + if(addr->sa_family == AF_INET) { + struct sockaddr_in *connaddr = (struct sockaddr_in *)addr; + inet_ntop(AF_INET, &(connaddr->sin_addr), addrstr, INET_ADDRSTRLEN); + sprintf(addrstr, "%s:%d", addrstr, stack->__lwip_ntohs(connaddr->sin_port)); + } + struct sockaddr_in *rawAddr4 = (struct sockaddr_in *) &bind_rpc->addr; + ba = convert_ip(rawAddr4); + port = stack->__lwip_ntohs(rawAddr4->sin_port); + #endif + + // ipv6 + #if defined(SDK_IPV6) + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&bind_rpc->addr; + in6_to_ip6((ip6_addr *)&ba, in6); + if(addr->sa_family == AF_INET6) { + struct sockaddr_in6 *connaddr6 = (struct sockaddr_in6 *)addr; + inet_ntop(AF_INET6, &(connaddr6->sin6_addr), addrstr, INET6_ADDRSTRLEN); + sprintf(addrstr, "%s:%d", addrstr, stack->__lwip_ntohs(connaddr6->sin6_port)); + } + #endif + + Connection *conn = tap->getConnection(sock); + DEBUG_ATTN(" addr=%s, sock=%p, fd=%d", addrstr, (void*)&sock, bind_rpc->fd); + if(conn) { + if(conn->type == SOCK_DGRAM) { + #if defined(__ANDROID__) + err = stack->__udp_bind(conn->UDP_pcb, NULL, port); + #else + err = stack->__udp_bind(conn->UDP_pcb, (const ip_addr_t *)&ba, port); + #endif + if(err == ERR_USE) // port in use + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, EADDRINUSE); + else { + stack->__udp_recv(conn->UDP_pcb, nc_udp_recved, new Larg(tap, conn)); + struct sockaddr_in addr_in; + memcpy(&addr_in, &bind_rpc->addr, sizeof(addr_in)); + addr_in.sin_port = Utils::ntoh(conn->UDP_pcb->local_port); // Newly assigned port + memcpy(&conn->local_addr, &addr_in, sizeof(addr_in)); + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), ERR_OK, ERR_OK); // Success + } + return; + } + else if (conn->type == SOCK_STREAM) { + if(conn->TCP_pcb->state == CLOSED){ + err = stack->__tcp_bind(conn->TCP_pcb, (const ip_addr_t *)&ba, port); + if(err != ERR_OK) { + DEBUG_ERROR("err=%d", err); + if(err == ERR_USE) + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, EADDRINUSE); + if(err == ERR_MEM) + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, ENOMEM); + if(err == ERR_BUF) + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, ENOMEM); + } else { + conn->local_addr = (struct sockaddr_storage *) &bind_rpc->addr; + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), ERR_OK, ERR_OK); // Success + } + } else { + DEBUG_ERROR(" ignoring BIND request, PCB (conn=%p, pcb=%p) not in CLOSED state. ", + (void*)&conn, (void*)&conn->TCP_pcb); + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, EINVAL); + } + } + } + else { + DEBUG_ERROR(" unable to locate Connection"); + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, EBADF); + } + } + + void lwip_handleListen(NetconEthernetTap *tap, PhySocket *sock, PhySocket *rpcSock, void **uptr, struct listen_st *listen_rpc) + { + DEBUG_ATTN("sock=%p", (void*)&sock); + lwIP_stack *stack = tap->lwipstack; + + Connection *conn = tap->getConnection(sock); + if(conn->type==SOCK_DGRAM) { + // FIX: Added sendReturnValue() call to fix listen() return bug on Android + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), ERR_OK, ERR_OK); + return; + } + if(!conn) { + DEBUG_ERROR(" unable to locate Connection"); + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, EBADF); + return; + } + if(conn->TCP_pcb->state == LISTEN) { + DEBUG_ERROR(" PCB is already in listening state"); + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), ERR_OK, ERR_OK); + return; + } + struct tcp_pcb* listeningPCB; + + #ifdef TCP_LISTEN_BACKLOG + listeningPCB = stack->__tcp_listen_with_backlog(conn->TCP_pcb, listen_rpc->backlog); + #else + listeningPCB = stack->__tcp_listen(conn->pcb); + #endif + if(listeningPCB != NULL) { + conn->TCP_pcb = listeningPCB; + stack->__tcp_accept(listeningPCB, nc_accept); + stack->__tcp_arg(listeningPCB, new Larg(tap, conn)); + fcntl(tap->_phy.getDescriptor(conn->sock), F_SETFL, O_NONBLOCK); + conn->listening = true; + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), ERR_OK, ERR_OK); + return; + } + tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, -1); + } + + // (RX packet) Read data from the RX buffer that lwIP just wrote to + void lwip_handleRead(NetconEthernetTap *tap, PhySocket *sock, void **uptr, bool lwip_invoked) + { + DEBUG_EXTRA(); + lwIP_stack *stack = tap->lwipstack; + if(!lwip_invoked) { + tap->_tcpconns_m.lock(); + tap->_rx_buf_m.lock(); + } + Connection *conn = tap->getConnection(sock); + if(conn && conn->rxsz) { + float max = conn->type == SOCK_STREAM ? (float)DEFAULT_TCP_RX_BUF_SZ : (float)DEFAULT_UDP_RX_BUF_SZ; + long n = tap->_phy.streamSend(conn->sock, conn->rxbuf, ZT_MAX_MTU); + int payload_sz, addr_sz_offset = sizeof(struct sockaddr_storage); + memcpy(&payload_sz, conn->rxbuf + addr_sz_offset, sizeof(int)); // OPT: + // extract address + struct sockaddr_storage addr; + memcpy(&addr, conn->rxbuf, addr_sz_offset); + + if(n == ZT_MAX_MTU) { + if(conn->rxsz-n > 0) // If more remains on buffer + memcpy(conn->rxbuf, conn->rxbuf+ZT_MAX_MTU, conn->rxsz - ZT_MAX_MTU); + conn->rxsz -= ZT_MAX_MTU; + // DGRAM + if(conn->type==SOCK_DGRAM){ + tap->_phy.setNotifyWritable(conn->sock, false); + + #if DEBUG_LEVEL >= MSG_TRANSFER + struct sockaddr_in * addr_in2 = (struct sockaddr_in *)&addr; + int port = stack->__lwip_ntohs(addr_in2->sin_port); + int ip = addr_in2->sin_addr.s_addr; + unsigned char d[4]; + d[0] = ip & 0xFF; + d[1] = (ip >> 8) & 0xFF; + d[2] = (ip >> 16) & 0xFF; + d[3] = (ip >> 24) & 0xFF; + DEBUG_TRANS("UDP RX <--- :: {TX: %.3f%%, RX: %d, sock=%p} :: payload = %d bytes (src_addr=%d.%d.%d.%d:%d)", + (float)conn->txsz / max, conn->rxsz/* / max*/, (void*)conn->sock, payload_sz, d[0],d[1],d[2],d[3], port); + #endif + } + // STREAM + //DEBUG_INFO("phyOnUnixWritable(): tid = %d\n", pthread_mach_thread_np(pthread_self())); + if(conn->type==SOCK_STREAM) { // Only acknolwedge receipt of TCP packets + stack->__tcp_recved(conn->TCP_pcb, n); + DEBUG_TRANS("TCP RX <--- :: {TX: %.3f%%, RX: %.3f%%, sock=%p} :: %ld bytes", + (float)conn->txsz / max, (float)conn->rxsz / max, (void*)conn->sock, n); + } + } else { + DEBUG_EXTRA(" errno = %d, rxsz = %d", errno, conn->rxsz); + tap->_phy.setNotifyWritable(conn->sock, false); + } + } + // If everything on the buffer has been written + if(conn->rxsz == 0) { + tap->_phy.setNotifyWritable(conn->sock, false); + } + if(!lwip_invoked) { + tap->_tcpconns_m.unlock(); + tap->_rx_buf_m.unlock(); + } + } + + // (TX packet) Write data from user app to network stack + void lwip_handleWrite(NetconEthernetTap *tap, Connection *conn) + { + DEBUG_EXTRA("conn=%p", (void*)&conn); + lwIP_stack *stack = tap->lwipstack; + if(!conn || (!conn->TCP_pcb && !conn->UDP_pcb)) { + DEBUG_ERROR(" invalid connection"); + return; + } + if(conn->type == SOCK_DGRAM) { + if(!conn->UDP_pcb) { + DEBUG_ERROR(" invalid UDP_pcb, type=SOCK_DGRAM"); + return; + } + // TODO: Packet re-assembly hasn't yet been tested with lwIP so UDP packets are limited to MTU-sized chunks + int udp_trans_len = conn->txsz < ZT_UDP_DEFAULT_PAYLOAD_MTU ? conn->txsz : ZT_UDP_DEFAULT_PAYLOAD_MTU; + + DEBUG_EXTRA(" allocating pbuf chain of size=%d for UDP packet, txsz=%d", udp_trans_len, conn->txsz); + struct pbuf * pb = stack->__pbuf_alloc(PBUF_TRANSPORT, udp_trans_len, PBUF_POOL); + if(!pb){ + DEBUG_ERROR(" unable to allocate new pbuf of size=%d", conn->txsz); + return; + } + memcpy(pb->payload, conn->txbuf, udp_trans_len); + int err = stack->__udp_send(conn->UDP_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); + } else { + // Success + int buf_remaining = (conn->txsz)-udp_trans_len; + if(buf_remaining) + memmove(&conn->txbuf, (conn->txbuf+udp_trans_len), buf_remaining); + conn->txsz -= udp_trans_len; + + #if DEBUG_LEVEL >= MSG_TRANSFER + struct sockaddr_in * addr_in2 = (struct sockaddr_in *)conn->peer_addr; + int port = stack->__lwip_ntohs(addr_in2->sin_port); + int ip = addr_in2->sin_addr.s_addr; + unsigned char d[4]; + d[0] = ip & 0xFF; + d[1] = (ip >> 8) & 0xFF; + d[2] = (ip >> 16) & 0xFF; + d[3] = (ip >> 24) & 0xFF; + DEBUG_TRANS("[UDP TX] ---> :: {TX: ------, RX: ------, sock=%p} :: %d bytes (dest_addr=%d.%d.%d.%d:%d)", + (void*)conn->sock, udp_trans_len, d[0], d[1], d[2], d[3], port); + #endif + } + stack->__pbuf_free(pb); + return; + } + else if(conn->type == SOCK_STREAM) { + if(!conn->TCP_pcb) { + DEBUG_ERROR(" invalid TCP_pcb, type=SOCK_STREAM"); + return; + } + // How much we are currently allowed to write to the connection + int sndbuf = conn->TCP_pcb->snd_buf; + int err, sz, r; + + if(!sndbuf) { + // PCB send buffer is full, turn off readability notifications for the + // corresponding PhySocket until nc_sent() is called and confirms that there is + // now space on the buffer + if(!conn->probation) { + DEBUG_ERROR(" LWIP stack is full, sndbuf == 0"); + tap->_phy.setNotifyReadable(conn->sock, false); + conn->probation = true; + } + return; + } + if(conn->txsz <= 0) + return; // Nothing to write + if(!conn->listening) + stack->__tcp_output(conn->TCP_pcb); + + if(conn->sock) { + r = conn->txsz < sndbuf ? conn->txsz : 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 = stack->__tcp_write(conn->TCP_pcb, &conn->txbuf, r, TCP_WRITE_FLAG_COPY); + stack->__tcp_output(conn->TCP_pcb); + if(err != ERR_OK) { + DEBUG_ERROR(" error while writing to PCB, err=%d", err); + if(err == -1) + DEBUG_ERROR("out of memory"); + return; + } else { + // adjust buffer + sz = (conn->txsz)-r; + if(sz) + memmove(&conn->txbuf, (conn->txbuf+r), sz); + conn->txsz -= r; + int max = conn->type == SOCK_STREAM ? DEFAULT_TCP_TX_BUF_SZ : DEFAULT_UDP_TX_BUF_SZ; + DEBUG_TRANS("[TCP TX] ---> :: {TX: %.3f%%, RX: %.3f%%, sock=%p} :: %d bytes", + (float)conn->txsz / (float)max, (float)conn->rxsz / max, (void*)&conn->sock, r); + return; + } + } + } + } + } + + void lwip_handleClose(NetconEthernetTap *tap, PhySocket *sock, Connection *conn) + { + DEBUG_ATTN(); + lwIP_stack *stack = tap->lwipstack; + + if(conn->type==SOCK_DGRAM) { + stack->__udp_remove(conn->UDP_pcb); + } + if(conn->TCP_pcb && conn->TCP_pcb->state != CLOSED) { + DEBUG_EXTRA("conn=%p, sock=%p, PCB->state = %d", + (void*)&conn, (void*)&sock, conn->TCP_pcb->state); + if(conn->TCP_pcb->state == SYN_SENT /*|| conn->TCP_pcb->state == CLOSE_WAIT*/) { + DEBUG_EXTRA("ignoring close request. invalid PCB state for this operation. sock=%p", (void*)&sock); + return; + } + // DEBUG_BLANK("__tcp_close(...)"); + if(stack->__tcp_close(conn->TCP_pcb) == ERR_OK) { + // Unregister callbacks for this PCB + stack->__tcp_arg(conn->TCP_pcb, NULL); + stack->__tcp_recv(conn->TCP_pcb, NULL); + stack->__tcp_err(conn->TCP_pcb, NULL); + stack->__tcp_sent(conn->TCP_pcb, NULL); + stack->__tcp_poll(conn->TCP_pcb, NULL, 1); + } + else { + DEBUG_EXTRA("error while calling tcp_close() sock=%p", (void*)&sock); + } + } + } + + /*------------------------------------------------------------------------------ + -------------------------------- lwIP Callbacks -------------------------------- + ------------------------------------------------------------------------------*/ + + err_t tapif_init(struct netif *netif) + { + // Actual init functionality is in addIp() of tap + return ERR_OK; + } + + /* + * Outputs data from the pbuf queue to the interface + */ + err_t low_level_output(struct netif *netif, struct pbuf *p) + { + struct pbuf *q; + char buf[ZT_MAX_MTU+32]; + char *bufptr; + int totalLength = 0; + + ZeroTier::NetconEthernetTap *tap = (ZeroTier::NetconEthernetTap*)netif->state; + bufptr = buf; + // Copy data from each pbuf, one at a time + for(q = p; q != NULL; q = q->next) { + memcpy(bufptr, q->payload, q->len); + bufptr += q->len; + totalLength += q->len; + } + // [Send packet to network] + // Split ethernet header and feed into handler + struct eth_hdr *ethhdr; + ethhdr = (struct eth_hdr *)buf; + + ZeroTier::MAC src_mac; + ZeroTier::MAC dest_mac; + src_mac.setTo(ethhdr->src.addr, 6); + dest_mac.setTo(ethhdr->dest.addr, 6); + + tap->_handler(tap->_arg,tap->_nwid,src_mac,dest_mac, + Utils::ntoh((uint16_t)ethhdr->type),0,buf + sizeof(struct eth_hdr),totalLength - sizeof(struct eth_hdr)); + return ERR_OK; + } + + // + err_t nc_recved(void *arg, struct tcp_pcb *PCB, struct pbuf *p, err_t err) + { + Larg *l = (Larg*)arg; + DEBUG_EXTRA("conn=%p, pcb=%p", (void*)&(l->conn), (void*)&PCB); + int tot = 0; + struct pbuf* q = p; + Mutex::Lock _l(l->tap->_tcpconns_m); + + if(!l->conn) { + DEBUG_ERROR("no connection"); + return ERR_OK; + } + if(p == NULL) { + if(l->conn->TCP_pcb->state == CLOSE_WAIT){ + l->tap->closeConnection(l->conn->sock); + return ERR_ABRT; + } + return err; + } + Mutex::Lock _l2(l->tap->_rx_buf_m); + // Cycle through pbufs and write them to the RX buffer + // The RX buffer will be emptied via phyOnUnixWritable() + while(p != NULL) { + if(p->len <= 0) + break; + int avail = DEFAULT_TCP_RX_BUF_SZ - l->conn->rxsz; + int len = p->len; + if(avail < len) + DEBUG_ERROR("not enough room (%d bytes) on RX buffer", avail); + memcpy(l->conn->rxbuf + (l->conn->rxsz), p->payload, len); + l->conn->rxsz += len; + p = p->next; + tot += len; + } + if(tot) { + //#if defined(USE_SOCKS_PROXY) + // l->tap->phyOnTcpWritable(l->conn->sock, NULL, true); + //#else + l->tap->phyOnUnixWritable(l->conn->sock, NULL, true); + //#endif + } + l->tap->lwipstack->__pbuf_free(q); + return ERR_OK; + } + + // + err_t nc_accept(void *arg, struct tcp_pcb *newPCB, err_t err) + { + DEBUG_ATTN("pcb=%p", (void*)&newPCB); + Larg *l = (Larg*)arg; + Mutex::Lock _l(l->tap->_tcpconns_m); + Connection *conn = l->conn; + NetconEthernetTap *tap = l->tap; + + if(!conn) + return -1; + if(conn->type==SOCK_DGRAM) + return -1; + if(!conn->sock) + return -1; + int fd = tap->_phy.getDescriptor(conn->sock); + + if(conn) { + // create new socketpair + ZT_PHY_SOCKFD_TYPE fds[2]; + if(socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) { + if(errno < 0) { + // sendReturnValue(conn, -1, errno); + DEBUG_ERROR("unable to create socketpair"); + return ERR_MEM; + } + } + // create and populate new Connection + Connection *newTcpConn = new Connection(); + l->tap->_Connections.push_back(newTcpConn); + newTcpConn->TCP_pcb = newPCB; + newTcpConn->type = SOCK_STREAM; + newTcpConn->sock = tap->_phy.wrapSocket(fds[0], newTcpConn); + + if(sock_fd_write(fd, fds[1]) < 0) + return -1; + tap->lwipstack->__tcp_arg(newPCB, new Larg(tap, newTcpConn)); + tap->lwipstack->__tcp_recv(newPCB, nc_recved); + tap->lwipstack->__tcp_err(newPCB, nc_err); + tap->lwipstack->__tcp_sent(newPCB, nc_sent); + tap->lwipstack->__tcp_poll(newPCB, nc_poll, 1); + if(conn->TCP_pcb->state == LISTEN) + return ERR_OK; + tcp_accepted(conn->TCP_pcb); // Let lwIP know that it can queue additional incoming connections + return ERR_OK; + } else + DEBUG_ERROR("can't locate Connection object for PCB"); + return -1; + } + + // + void nc_udp_recved(void * arg, struct udp_pcb * upcb, struct pbuf * p, ip_addr_t * addr, u16_t port) + { + Larg *l = (Larg*)arg; + DEBUG_EXTRA("nc_udp_recved(conn=%p,pcb=%p,port=%d)\n", (void*)&(l->conn), (void*)&upcb, port); + /* + int tot = 0; + unsigned char *addr_pos, *sz_pos, *payload_pos; + struct pbuf* q = p; + struct sockaddr_storage sockaddr_big; + + #if defined(LWIP_IPV6) + struct sockaddr_in6 addr_in; + addr_in.sin6_addr.s6_addr = addr->u_addr.ip6.addr; + addr_in.sin6_port = port; + #else // ipv4 + struct sockaddr_in *addr_in = (struct sockaddr_in *)&sockaddr_big; + addr_in->sin_addr.s_addr = addr->addr; + addr_in->sin_port = port; + #endif + + // TODO: Finish address treatment + + Mutex::Lock _l2(l->tap->_rx_buf_m); + // Cycle through pbufs and write them to the RX buffer + // The RX "buffer" will be emptied via phyOnUnixWritable() + if(p) { + // Intra-API "packetization" scheme: [addr_len|addr|payload_len|payload] + if(l->conn->rxsz == DEFAULT_UDP_RX_BUF_SZ) { // if UDP buffer full + DEBUG_INFO("UDP RX buffer full. Discarding oldest payload segment"); + memmove(l->conn->rxbuf, l->conn->rxbuf + ZT_MAX_MTU, DEFAULT_UDP_RX_BUF_SZ - ZT_MAX_MTU); + addr_pos = l->conn->rxbuf + (DEFAULT_UDP_RX_BUF_SZ - ZT_MAX_MTU); // TODO: + sz_pos = addr_pos + sizeof(struct sockaddr_storage); + l->conn->rxsz -= ZT_MAX_MTU; + } + else { + addr_pos = l->conn->rxbuf + l->conn->rxsz; // where we'll prepend the size of the address + sz_pos = addr_pos + sizeof(struct sockaddr_storage); + } + payload_pos = addr_pos + sizeof(struct sockaddr_storage) + sizeof(tot); // where we'll write the payload + // write remote host address + memcpy(addr_pos, &addr_in, sizeof(struct sockaddr_storage)); + } + while(p != NULL) { + if(p->len <= 0) + break; + int len = p->len; + memcpy(payload_pos, p->payload, len); + payload_pos = payload_pos + len; + p = p->next; + tot += len; + } + if(tot) { + l->conn->rxsz += ZT_MAX_MTU; + memcpy(sz_pos, &tot, sizeof(tot)); + //DEBUG_EXTRA(" nc_udp_recved(): data_len = %d, rxsz = %d, addr_info_len = %d\n", + // tot, l->conn->rxsz, sizeof(u32_t) + sizeof(u16_t)); + l->tap->phyOnUnixWritable(l->conn->sock, NULL, true); + l->tap->_phy.setNotifyWritable(l->conn->sock, true); + } + l->tap->lwipstack->__pbuf_free(q); + */ + } + + // + err_t nc_sent(void* arg, struct tcp_pcb *PCB, u16_t len) + { + DEBUG_EXTRA("pcb=%p", (void*)&PCB); + Larg *l = (Larg*)arg; + Mutex::Lock _l(l->tap->_tcpconns_m); + if(l->conn->probation && l->conn->txsz == 0){ + l->conn->probation = false; // TX buffer now empty, removing from probation + } + if(l && l->conn && len && !l->conn->probation) { + int softmax = l->conn->type == SOCK_STREAM ? DEFAULT_TCP_TX_BUF_SZ : DEFAULT_UDP_TX_BUF_SZ; + if(l->conn->txsz < softmax) { + l->tap->_phy.setNotifyReadable(l->conn->sock, true); + l->tap->_phy.whack(); + } + } + return ERR_OK; + } + + // + err_t nc_connected_proxy(void *arg, struct tcp_pcb *PCB, err_t err) + { + DEBUG_INFO("pcb=%p", (void*)&PCB); + return ERR_OK; + } + + // + err_t nc_connected(void *arg, struct tcp_pcb *PCB, err_t err) + { + DEBUG_ATTN("pcb=%p", (void*)&PCB); + Larg *l = (Larg*)arg; + if(l && l->conn) + l->tap->sendReturnValue(l->tap->_phy.getDescriptor(l->conn->rpcSock), ERR_OK, 0); + return ERR_OK; + } + + // + err_t nc_poll(void* arg, struct tcp_pcb *PCB) + { + return ERR_OK; + } + + // + void nc_err(void *arg, err_t err) + { + DEBUG_ERROR("err=%d", err); + Larg *l = (Larg*)arg; + Mutex::Lock _l(l->tap->_tcpconns_m); + + if(!l->conn) + DEBUG_ERROR("conn==NULL"); + int fd = l->tap->_phy.getDescriptor(l->conn->sock); + switch(err) + { + case ERR_MEM: + DEBUG_ERROR("ERR_MEM->ENOMEM"); + l->tap->sendReturnValue(fd, -1, ENOMEM); + break; + case ERR_BUF: + DEBUG_ERROR("ERR_BUF->ENOBUFS"); + l->tap->sendReturnValue(fd, -1, ENOBUFS); + break; + case ERR_TIMEOUT: + DEBUG_ERROR("ERR_TIMEOUT->ETIMEDOUT"); + l->tap->sendReturnValue(fd, -1, ETIMEDOUT); + break; + case ERR_RTE: + DEBUG_ERROR("ERR_RTE->ENETUNREACH"); + l->tap->sendReturnValue(fd, -1, ENETUNREACH); + break; + case ERR_INPROGRESS: + DEBUG_ERROR("ERR_INPROGRESS->EINPROGRESS"); + l->tap->sendReturnValue(fd, -1, EINPROGRESS); + break; + case ERR_VAL: + DEBUG_ERROR("ERR_VAL->EINVAL"); + l->tap->sendReturnValue(fd, -1, EINVAL); + break; + case ERR_WOULDBLOCK: + DEBUG_ERROR("ERR_WOULDBLOCK->EWOULDBLOCK"); + l->tap->sendReturnValue(fd, -1, EWOULDBLOCK); + break; + case ERR_USE: + DEBUG_ERROR("ERR_USE->EADDRINUSE"); + l->tap->sendReturnValue(fd, -1, EADDRINUSE); + break; + case ERR_ISCONN: + DEBUG_ERROR("ERR_ISCONN->EISCONN"); + l->tap->sendReturnValue(fd, -1, EISCONN); + break; + case ERR_ABRT: + DEBUG_ERROR("ERR_ABRT->ECONNREFUSED"); + l->tap->sendReturnValue(fd, -1, ECONNREFUSED); + break; + + // TODO: Below are errors which don't have a standard errno correlate + + case ERR_RST: + l->tap->sendReturnValue(fd, -1, -1); + break; + case ERR_CLSD: + l->tap->sendReturnValue(fd, -1, -1); + break; + case ERR_CONN: + l->tap->sendReturnValue(fd, -1, -1); + break; + case ERR_ARG: + l->tap->sendReturnValue(fd, -1, -1); + break; + case ERR_IF: + l->tap->sendReturnValue(fd, -1, -1); + break; + default: + break; + } + DEBUG_ERROR(" closing connection"); + l->tap->closeConnection(l->conn); + } +} \ No newline at end of file diff --git a/src/stack_drivers/lwip.hpp b/src/stack_drivers/lwip/lwip.hpp similarity index 77% rename from src/stack_drivers/lwip.hpp rename to src/stack_drivers/lwip/lwip.hpp index 411f468..01efc4e 100644 --- a/src/stack_drivers/lwip.hpp +++ b/src/stack_drivers/lwip/lwip.hpp @@ -39,7 +39,6 @@ #include "lwip/tcp.h" #include "lwip/priv/tcp_priv.h" - #include "Mutex.hpp" #include "OSUtils.hpp" #include "debug.h" @@ -47,6 +46,143 @@ #include #include +#include "tap.hpp" + +namespace ZeroTier { + class NetconEthernetTap; + struct Connection; + + void lwip_init_interface(NetconEthernetTap *tap, const InetAddress &ip); + void lwip_loop(NetconEthernetTap *tap); + void lwip_rx(NetconEthernetTap *tap, const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); + Connection *lwip_handleSocket(NetconEthernetTap *tap, PhySocket *sock, void **uptr, struct socket_st* socket_rpc); + Connection * lwip_handleSocketProxy(NetconEthernetTap *tap, PhySocket *sock, int socket_type); + void lwip_handleConnect(NetconEthernetTap *tap, PhySocket *sock, PhySocket *rpcSock, Connection *conn, struct connect_st* connect_rpc); + int lwip_handleConnectProxy(NetconEthernetTap *tap, PhySocket *sock, struct sockaddr_in *rawAddr); + void lwip_handleBind(NetconEthernetTap *tap, PhySocket *sock, PhySocket *rpcSock, void **uptr, struct bind_st *bind_rpc); + void lwip_handleListen(NetconEthernetTap *tap, PhySocket *sock, PhySocket *rpcSock, void **uptr, struct listen_st *listen_rpc); + void lwip_handleRead(NetconEthernetTap *tap, PhySocket *sock, void **uptr, bool lwip_invoked); + void lwip_handleWrite(NetconEthernetTap *tap, Connection *conn); + void lwip_handleClose(NetconEthernetTap *tap, PhySocket *sock, Connection *conn); + + + + err_t tapif_init(struct netif *netif); + err_t low_level_output(struct netif *netif, struct pbuf *p); + + /* + * Callback from LWIP for when data is available to be read from the network. + * + * Data is in the form of a linked list of struct pbufs, it is then recombined and + * send to the client over the associated unix socket. + * + * @param associated service state object + * @param allocated PCB + * @param chain of pbufs + * @param error code + * @return ERR_OK if everything is ok, -1 otherwise + * + */ + err_t nc_recved(void *arg, struct tcp_pcb *PCB, struct pbuf *p, err_t err); + + /* + * Callback from LWIP for when a connection has been accepted and the PCB has been + * put into an ACCEPT state. + * + * A socketpair is created, one end is kept and wrapped into a PhySocket object + * for use in the main ZT I/O loop, and one end is sent to the client. The client + * is then required to tell the service what new file descriptor it has allocated + * for this connection. After the mapping is complete, the accepted socket can be + * used. + * + * @param associated service state object + * @param newly allocated PCB + * @param error code + * @return ERR_OK if everything is ok, -1 otherwise + * + * i := should be implemented in intercept lib + * I := is implemented in intercept lib + * X := is implemented in service + * ? := required treatment Unknown + * - := Not needed + * + * [ ] EAGAIN or EWOULDBLOCK - The socket is marked nonblocking and no connections are present + * to be accepted. POSIX.1-2001 allows either error to be returned for + * this case, and does not require these constants to have the same value, + * so a portable application should check for both possibilities. + * [I] EBADF - The descriptor is invalid. + * [I] ECONNABORTED - A connection has been aborted. + * [i] EFAULT - The addr argument is not in a writable part of the user address space. + * [-] EINTR - The system call was interrupted by a signal that was caught before a valid connection arrived; see signal(7). + * [I] EINVAL - Socket is not listening for connections, or addrlen is invalid (e.g., is negative). + * [I] EINVAL - (accept4()) invalid value in flags. + * [I] EMFILE - The per-process limit of open file descriptors has been reached. + * [ ] ENFILE - The system limit on the total number of open files has been reached. + * [ ] ENOBUFS, ENOMEM - Not enough free memory. This often means that the memory allocation is + * limited by the socket buffer limits, not by the system memory. + * [I] ENOTSOCK - The descriptor references a file, not a socket. + * [I] EOPNOTSUPP - The referenced socket is not of type SOCK_STREAM. + * [ ] EPROTO - Protocol error. + * + */ + err_t nc_accept(void *arg, struct tcp_pcb *newPCB, err_t err); + + + err_t nc_recved_proxy(void *arg, struct tcp_pcb *PCB, struct pbuf *p, err_t err); + void nc_udp_recved(void * arg, struct udp_pcb * upcb, struct pbuf * p, ip_addr_t * addr, u16_t port); + + + /* + * Callback from LWIP when an internal error is associtated with the given (arg) + * + * Since the PCB related to this error might no longer exist, only its perviously + * associated (arg) is provided to us. + * + * @param associated service state object + * @param error code + * + */ + void nc_err(void *arg, err_t err); + + /* + * Callback from LWIP to do whatever work we might need to do. + * + * @param associated service state object + * @param PCB we're polling on + * @return ERR_OK if everything is ok, -1 otherwise + * + */ + err_t nc_poll(void* arg, struct tcp_pcb *PCB); + + /* + * Callback from LWIP to signal that 'len' bytes have successfully been sent. + * As a result, we should put our socket back into a notify-on-readability state + * since there is now room on the PCB buffer to write to. + * + * NOTE: This could be used to track the amount of data sent by a connection. + * + * @param associated service state object + * @param relevant PCB + * @param length of data sent + * @return ERR_OK if everything is ok, -1 otherwise + * + */ + err_t nc_sent(void *arg, struct tcp_pcb *PCB, u16_t len); + + /* + * Callback from LWIP which sends a return value to the client to signal that + * a connection was established for this PCB + * + * @param associated service state object + * @param relevant PCB + * @param error code + * @return ERR_OK if everything is ok, -1 otherwise + * + */ + err_t nc_connected(void *arg, struct tcp_pcb *PCB, err_t err); + err_t nc_connected_proxy(void *arg, struct tcp_pcb *PCB, err_t err); +} + #ifdef D_GNU_SOURCE #define _GNU_SOURCE #endif @@ -336,7 +472,6 @@ namespace ZeroTier { dlclose(_libref); } - #if defined(SDK_IPV4) inline struct netif * __netif_add(NETIF_ADD_SIG) throw() { Mutex::Lock _l(_lock); return _netif_add(netif,ipaddr,netmask,gw,state,init,input); } #endif diff --git a/src/stack_drivers/picotcp/README.md b/src/stack_drivers/picotcp/README.md new file mode 100644 index 0000000..f51c24a --- /dev/null +++ b/src/stack_drivers/picotcp/README.md @@ -0,0 +1,2 @@ +picoTCP Network Stack Driver +==== diff --git a/src/stack_drivers/picotcp/picotcp.cpp b/src/stack_drivers/picotcp/picotcp.cpp new file mode 100644 index 0000000..bf4e4ad --- /dev/null +++ b/src/stack_drivers/picotcp/picotcp.cpp @@ -0,0 +1,568 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#if defined(SDK_PICOTCP) + +#include "tap.hpp" + +#include "picotcp.hpp" +#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" + +namespace ZeroTier { + + // Reference to the tap interface + // This is needed due to the fact that there's a lot going on in the tap interface + // that needs to be updated on each of the network stack's callbacks and not every + // network stack provides a mechanism for storing a reference to the tap. + // + // In future releases this will be replaced with a new structure of static pointers that + // will make it easier to maintain multiple active tap interfaces + NetconEthernetTap *picotap; + struct pico_device picodev; + + int pico_eth_send(struct pico_device *dev, void *buf, int len); + int pico_eth_poll(struct pico_device *dev, int loop_score); + + // Initialize network stack's interfaces and assign addresses + void pico_init_interface(NetconEthernetTap *tap, const InetAddress &ip) + { + picoTCP_stack *stack = tap->picostack; + DEBUG_INFO(); + if (std::find(picotap->_ips.begin(),picotap->_ips.end(),ip) == picotap->_ips.end()) { + picotap->_ips.push_back(ip); + std::sort(picotap->_ips.begin(),picotap->_ips.end()); + #if defined(SDK_IPV4) + if(ip.isV4()) + { + struct pico_ip4 ipaddr, netmask; + ipaddr.addr = *((u32_t *)ip.rawIpData()); + netmask.addr = *((u32_t *)ip.netmask().rawIpData()); + uint8_t mac[PICO_SIZE_ETH]; + picotap->_mac.copyTo(mac, PICO_SIZE_ETH); + DEBUG_ATTN("mac = %s", picotap->_mac.toString().c_str()); + picodev.send = pico_eth_send; // tx + picodev.poll = pico_eth_poll; // rx + picodev.mtu = picotap->_mtu; + if( 0 != stack->__pico_device_init(&(picodev), "p0", mac)) { + DEBUG_ERROR("device init failed"); + return; + } + stack->__pico_ipv4_link_add(&(picodev), ipaddr, netmask); + // DEBUG_INFO("device initialized as ipv4_addr = %s", ipv4_str); + // picostack->__pico_icmp4_ping("10.8.8.1", 20, 1000, 10000, 64, cb_ping); + } + #elif defined(SDK_IPV6) + if(ip.isV6()) + { + struct pico_ip6 ipaddr, netmask; + 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); + stack->__pico_string_to_ipv6(ipv6_str, ipaddr.addr); + stack->__pico_string_to_ipv6(nm_str, netmask.addr); + stack->__pico_ipv6_link_add(&(picodev), ipaddr, netmask); + picodev.send = pico_eth_send; // tx + picodev.poll = pico_eth_poll; // rx + uint8_t mac[PICO_SIZE_ETH]; + picotap->_mac.copyTo(mac, PICO_SIZE_ETH); + DEBUG_ATTN("mac = %s", picotap->_mac.toString().c_str()); + if( 0 != stack->__pico_device_init(&(picodev), "p0", mac)) { + DEBUG_ERROR("device init failed"); + return; + } + DEBUG_ATTN("device initialized as ipv6_addr = %s", ipv6_str); + } + #endif + } + } + + // I/O thread loop + void pico_loop(NetconEthernetTap *tap) + { + DEBUG_INFO(); + while(tap->_run) + { + tap->_phy.poll((unsigned long)std::min(500,1000)); + usleep(1000); + tap->picostack->__pico_stack_tick(); + } + } + + // RX packets from network onto internal buffer + // Also notifies the tap service that data can be read, buffer will be emptied by pico_handleRead() + void pico_cb_tcp_read(NetconEthernetTap *tap, struct pico_socket *s) + { + // TODO: Verify + DEBUG_INFO(); + Connection *conn = tap->getConnection(s); + if(conn) { + int r; + do { + //int avail = DEFAULT_TCP_RX_BUF_SZ - conn->rxsz; + //if(avail) { + r = tap->picostack->__pico_socket_read(s, conn->rxbuf + (conn->rxsz), ZT_MAX_MTU); + tap->_phy.setNotifyWritable(conn->sock, true); + DEBUG_INFO("read=%d", r); + if (r > 0) { + conn->rxsz += r; + } + //} + if (r < 0) { + exit(5); + } + } + while(r > 0); + return; + } + DEBUG_ERROR("invalid connection"); + } + + // TX packets from internal buffer to network + void pico_cb_tcp_write(NetconEthernetTap *tap, struct pico_socket *s) + { + Connection *conn = tap->getConnection(s); + if(!conn) + DEBUG_ERROR("invalid connection"); + if(!conn->txsz) + return; + DEBUG_INFO("txsz=%d bytes ready to be written", conn->txsz); + + // Only called from a locked context, no need to lock anything + if(conn->txsz > 0) { + int r = conn->txsz < ZT_MAX_MTU ? conn->txsz : ZT_MAX_MTU; + if((r = tap->picostack->__pico_socket_write(s, &conn->txbuf, r)) < 0) { + DEBUG_ERROR("unable to write to pico_socket=%p", (void*)s); + return; + } + int sz = (conn->txsz)-r; + if(sz) + memmove(&conn->txbuf, (conn->txbuf+r), sz); + conn->txsz -= r; + int max = conn->type == SOCK_STREAM ? DEFAULT_TCP_TX_BUF_SZ : DEFAULT_UDP_TX_BUF_SZ; + DEBUG_TRANS("[TCP TX] ---> :: {TX: %.3f%%, RX: %.3f%%, sock=%p} :: %d bytes", + (float)conn->txsz / (float)max, (float)conn->rxsz / max, (void*)&conn->sock, r); + return; + } + } + + // Main callback for TCP connections + void pico_cb_tcp(uint16_t ev, struct pico_socket *s) + { + Mutex::Lock _l(picotap->_tcpconns_m); + Connection *conn = picotap->getConnection(s); + if(!conn) { + DEBUG_ERROR("invalid connection"); + } + if (ev & PICO_SOCK_EV_RD) { + pico_cb_tcp_read(picotap, s); + } + + // Accept connection (analogous to lwip_nc_accept) + if (ev & PICO_SOCK_EV_CONN) { + DEBUG_INFO("connection established with server, sock=%p", (void*)(conn->picosock)); + uint32_t peer; + uint16_t port; + struct pico_socket *client = picotap->picostack->__pico_socket_accept(s, &peer, &port); + if(!client) { + DEBUG_ERROR("there was an error accepting the connection, sock=%p", (void*)(conn->picosock)); + } + + ZT_PHY_SOCKFD_TYPE fds[2]; + if(socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) { + if(errno < 0) { + // FIXME: Return a value to the client + //picotap->sendReturnValue(conn, -1, errno); + DEBUG_ERROR("unable to create socketpair"); + return; + } + } + Connection *newTcpConn = new Connection(); + picotap->_Connections.push_back(newTcpConn); + newTcpConn->type = SOCK_STREAM; + newTcpConn->sock = picotap->_phy.wrapSocket(fds[0], newTcpConn); + newTcpConn->picosock = client; + int fd = picotap->_phy.getDescriptor(conn->sock); + if(sock_fd_write(fd, fds[1]) < 0) { + DEBUG_ERROR("error sending new fd to client application"); + } + } + if (ev & PICO_SOCK_EV_FIN) { + DEBUG_INFO("socket closed. Exit normally."); + //picotap->__pico_timer_add(2000, compare_results, NULL); + } + if (ev & PICO_SOCK_EV_ERR) { + DEBUG_INFO("socket error received" /*, strerror(pico_err)*/); + //exit(1); + } + if (ev & PICO_SOCK_EV_CLOSE) { + DEBUG_INFO("socket received close from peer - Wrong case if not all client data sent!"); + picotap->picostack->__pico_socket_close(s); + picotap->closeConnection(conn); + return; + } + if (ev & PICO_SOCK_EV_WR) { + pico_cb_tcp_write(picotap, s); + } + } + + // Called when an incoming ping is received + /* + static void pico_cb_ping(struct pico_icmp4_stats *s) + { + DEBUG_INFO(); + char host[30]; + picotap->picostack->__pico_ipv4_to_string(host, s->dst.addr); + if (s->err == 0) { + printf("%lu bytes from %s: icmp_req=%lu ttl=%lu time=%lu ms\n", s->size, + host, s->seq, s->ttl, (long unsigned int)s->time); + } else { + printf("PING %lu to %s: Error %d\n", s->seq, host, s->err); + } + } + */ + + // Sends data to the tap device (in our case, the ZeroTier service) + int pico_eth_send(struct pico_device *dev, void *buf, int len) + { + DEBUG_INFO("len=%d", len); + struct eth_hdr *ethhdr; + ethhdr = (struct eth_hdr *)buf; + + MAC src_mac; + MAC dest_mac; + src_mac.setTo(ethhdr->src.addr, 6); + dest_mac.setTo(ethhdr->dest.addr, 6); + + picotap->_handler(picotap->_arg,picotap->_nwid,src_mac,dest_mac, + Utils::ntoh((uint16_t)ethhdr->type),0, ((char*)buf) + sizeof(struct eth_hdr),len - sizeof(struct eth_hdr)); + return len; + } + + // Receives data from the tap device and encapsulates it into a ZeroTier ethernet frame and places it in a locked memory buffer + void pico_rx(NetconEthernetTap *tap, const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) + { + // DEBUG_INFO(); + // Since picoTCP only allows the reception of frames from within the polling function, we + // must enqueue each frame into a memory structure shared by both threads. This structure will + Mutex::Lock _l(tap->_pico_frame_rxbuf_m); + if(len > ((1024 * 1024) - tap->pico_frame_rxbuf_tot)) { + DEBUG_ERROR("dropping packet (len = %d) - not enough space left on RX frame buffer", len); + return; + } + //if(len != memcpy(pico_frame_rxbuf, data, len)) { + // DEBUG_ERROR("dropping packet (len = %d) - unable to copy contents of frame to RX frame buffer", len); + // return; + //} + + // assemble new eth header + struct eth_hdr ethhdr; + from.copyTo(ethhdr.src.addr, 6); + to.copyTo(ethhdr.dest.addr, 6); + ethhdr.type = Utils::hton((uint16_t)etherType); + int newlen = len+sizeof(struct eth_hdr); + // + memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot, &newlen, sizeof(newlen)); // size of frame + memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot + sizeof(newlen), ðhdr, sizeof(ethhdr)); // new eth header + memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot + sizeof(newlen) + sizeof(ethhdr), data, len); // frame data + tap->pico_frame_rxbuf_tot += len + sizeof(len) + sizeof(ethhdr); + // DEBUG_INFO("RX frame buffer %3f full", (float)pico_frame_rxbuf_tot / (float)(1024 * 1024)); + DEBUG_INFO("len=%d", len); + } + + // Is called periodically by the stack, this removes data from the locked memory buffer and feeds it into the stack. + // A maximum of 'loop_score' frames can be processed in each call + int pico_eth_poll(struct pico_device *dev, int loop_score) + { + // DEBUG_EXTRA(); + // OPTIMIZATION: The copy logic and/or buffer structure should be reworked for better performance after the BETA + // NetconEthernetTap *tap = (NetconEthernetTap*)netif->state; + Mutex::Lock _l(picotap->_pico_frame_rxbuf_m); + unsigned char frame[ZT_MAX_MTU]; + uint32_t len; + + while (picotap->pico_frame_rxbuf_tot > 0) { + memset(frame, 0, sizeof(frame)); + len = 0; + memcpy(&len, picotap->pico_frame_rxbuf, sizeof(len)); // get frame len + memcpy(frame, picotap->pico_frame_rxbuf + sizeof(len), len); // get frame data + memmove(picotap->pico_frame_rxbuf, picotap->pico_frame_rxbuf + sizeof(len) + len, ZT_MAX_MTU-(sizeof(len) + len)); + picotap->picostack->__pico_stack_recv(dev, (uint8_t*)frame, len); + picotap->pico_frame_rxbuf_tot-=(sizeof(len) + len); + // DEBUG_EXTRA("RX frame buffer %3f full", (float)(picotap->pico_frame_rxbuf_tot) / (float)(MAX_PICO_FRAME_RX_BUF_SZ)); + loop_score--; + } + return loop_score; + } + + // Creates a new pico_socket and Connection object to represent a new connection to be. + Connection *pico_handleSocket(PhySocket *sock, void **uptr, struct socket_st* socket_rpc) + { + DEBUG_INFO(); + struct pico_socket * psock; + #if defined(SDK_IPV4) + psock = picotap->picostack->__pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &pico_cb_tcp); + #elif defined(SDK_IPV6) + psock = picotap->picostack->__pico_socket_open(PICO_PROTO_IPV6, PICO_PROTO_TCP, &pico_cb_tcp); + #endif + if(psock) { + DEBUG_ATTN("psock = %p", (void*)psock); + Connection * newConn = new Connection(); + *uptr = newConn; + newConn->type = socket_rpc->socket_type; + newConn->sock = sock; + newConn->local_addr = NULL; + newConn->peer_addr = NULL; + newConn->picosock = psock; + picotap->_Connections.push_back(newConn); + return newConn; + } + else { + DEBUG_ERROR("failed to create pico_socket"); + } + return NULL; + } + + // Writes data from the I/O buffer to the network stack + void pico_handleWrite(Connection *conn) + { + DEBUG_INFO(); + if(!conn || !conn->picosock) { + DEBUG_ERROR(" invalid connection"); + return; + } + int r, max_write_len = conn->txsz < ZT_MAX_MTU ? conn->txsz : ZT_MAX_MTU; + if((r = picotap->picostack->__pico_socket_write(conn->picosock, &conn->txbuf, max_write_len)) < 0) { + DEBUG_ERROR("unable to write to pico_socket(%p)", (void*)&(conn->picosock)); + return; + } + /* + if(pico_err == PICO_ERR_EINVAL) + DEBUG_ERROR("PICO_ERR_EINVAL - invalid argument"); + if(pico_err == PICO_ERR_EIO) + DEBUG_ERROR("PICO_ERR_EIO - input/output error"); + if(pico_err == PICO_ERR_ENOTCONN) + DEBUG_ERROR("PICO_ERR_ENOTCONN - the socket is not connected"); + if(pico_err == PICO_ERR_ESHUTDOWN) + DEBUG_ERROR("PICO_ERR_ESHUTDOWN - cannot send after transport endpoint shutdown"); + if(pico_err == PICO_ERR_EADDRNOTAVAIL) + DEBUG_ERROR("PICO_ERR_EADDRNOTAVAIL - address not available"); + if(pico_err == PICO_ERR_EHOSTUNREACH) + DEBUG_ERROR("PICO_ERR_EHOSTUNREACH - host is unreachable"); + if(pico_err == PICO_ERR_ENOMEM) + DEBUG_ERROR("PICO_ERR_ENOMEM - not enough space"); + if(pico_err == PICO_ERR_EAGAIN) + DEBUG_ERROR("PICO_ERR_EAGAIN - resource temporarily unavailable"); + */ + // adjust buffer + int sz = (conn->txsz)-r; + if(sz) + memmove(&conn->txbuf, (conn->txbuf+r), sz); + conn->txsz -= r; + int max = conn->type == SOCK_STREAM ? DEFAULT_TCP_TX_BUF_SZ : DEFAULT_UDP_TX_BUF_SZ; + DEBUG_TRANS("[TCP TX] ---> :: {TX: %.3f%%, RX: %.3f%%, sock=%p} :: %d bytes", + (float)conn->txsz / (float)max, (float)conn->rxsz / max, (void*)&conn->sock, r); + } + + // Instructs the stack to connect to a remote host + void pico_handleConnect(PhySocket *sock, PhySocket *rpcSock, Connection *conn, struct connect_st* connect_rpc) + { + DEBUG_INFO(); + if(conn->picosock) { + struct sockaddr_in *addr = (struct sockaddr_in *) &connect_rpc->addr; + int ret; + // TODO: Rewrite this + #if defined(SDK_IPV4) + struct pico_ip4 zaddr; + struct sockaddr_in *in4 = (struct sockaddr_in*)&connect_rpc->addr; + char ipv4_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(in4->sin_addr), ipv4_str, INET_ADDRSTRLEN); + picotap->picostack->__pico_string_to_ipv4(ipv4_str, &(zaddr.addr)); + DEBUG_ATTN("addr=%s:%d", ipv4_str, addr->sin_port); + ret = picotap->picostack->__pico_socket_connect(conn->picosock, &zaddr, addr->sin_port); + #elif defined(SDK_IPV6) // "fd56:5799:d8f6:1238:8c99:9322:30ce:418a" + struct pico_ip6 zaddr; + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&connect_rpc->addr; + char ipv6_str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN); + picotap->picostack->__pico_string_to_ipv6(ipv6_str, zaddr.addr); + DEBUG_ATTN("addr=%s:%d", ipv6_str, addr->sin_port); + ret = picotap->picostack->__pico_socket_connect(conn->picosock, &zaddr, addr->sin_port); + #endif + + if(ret == PICO_ERR_EPROTONOSUPPORT) { + DEBUG_ERROR("PICO_ERR_EPROTONOSUPPORT"); + } + if(ret == PICO_ERR_EINVAL) { + DEBUG_ERROR("PICO_ERR_EINVAL"); + } + if(ret == PICO_ERR_EHOSTUNREACH) { + DEBUG_ERROR("PICO_ERR_EHOSTUNREACH"); + } + picotap->sendReturnValue(picotap->_phy.getDescriptor(rpcSock), 0, ERR_OK); + } + } + + // Instructs the stack to bind to a given address + void pico_handleBind(PhySocket *sock, PhySocket *rpcSock, void **uptr, struct bind_st *bind_rpc) + { + DEBUG_INFO(); + Connection *conn = picotap->getConnection(sock); + if(!sock) { + DEBUG_ERROR("invalid connection"); + return; + } + struct sockaddr_in *addr = (struct sockaddr_in *) &bind_rpc->addr; + int ret; + // TODO: Rewrite this + #if defined(SDK_IPV4) + struct pico_ip4 zaddr; + struct sockaddr_in *in4 = (struct sockaddr_in*)&bind_rpc->addr; + char ipv4_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(in4->sin_addr), ipv4_str, INET_ADDRSTRLEN); + picotap->picostack->__pico_string_to_ipv4(ipv4_str, &(zaddr.addr)); + DEBUG_ATTN("addr=%s", ipv4_str/*, ntohs((uint16_t*)&(addr->sin_port))*/); + ret = picotap->picostack->__pico_socket_bind(conn->picosock, &zaddr, (uint16_t*)&(addr->sin_port)); + #elif defined(SDK_IPV6) + struct pico_ip6 zaddr; + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&bind_rpc->addr; + char ipv6_str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN); + picotap->picostack->__pico_string_to_ipv6(ipv6_str, zaddr.addr); + DEBUG_ATTN("addr=%s", ipv6_str/*, ntohs((uint16_t*)&(addr->sin_port))*/); + ret = picotap->picostack->__pico_socket_bind(conn->picosock, &zaddr, (uint16_t*)&(addr->sin_port)); + #endif + if(ret < 0) { + DEBUG_ERROR("unable to bind pico_socket(%p)", (void*)(conn->picosock)); + if(ret == PICO_ERR_EINVAL) { + DEBUG_ERROR("PICO_ERR_EINVAL - invalid argument"); + picotap->sendReturnValue(picotap->_phy.getDescriptor(rpcSock), -1, EINVAL); + } + if(ret == PICO_ERR_ENOMEM) { + DEBUG_ERROR("PICO_ERR_ENOMEM - not enough space"); + picotap->sendReturnValue(picotap->_phy.getDescriptor(rpcSock), -1, ENOMEM); + } + if(ret == PICO_ERR_ENXIO) { + DEBUG_ERROR("PICO_ERR_ENXIO - no such device or address"); + picotap->sendReturnValue(picotap->_phy.getDescriptor(rpcSock), -1, ENXIO); + } + } + picotap->sendReturnValue(picotap->_phy.getDescriptor(rpcSock), ERR_OK, ERR_OK); // success + } + + // Puts a pico_socket into a listening state to receive incoming connection requests + void pico_handleListen(PhySocket *sock, PhySocket *rpcSock, void **uptr, struct listen_st *listen_rpc) + { + Connection *conn = picotap->getConnection(sock); + DEBUG_ATTN("conn = %p", (void*)conn); + if(!sock || !conn) { + DEBUG_ERROR("invalid connection"); + return; + } + int ret, backlog = 1; + if((ret = picotap->picostack->__pico_socket_listen(conn->picosock, backlog)) < 0) + { + if(ret == PICO_ERR_EINVAL) { + DEBUG_ERROR("PICO_ERR_EINVAL - invalid argument"); + picotap->sendReturnValue(picotap->_phy.getDescriptor(rpcSock), -1, EINVAL); + } + if(ret == PICO_ERR_EISCONN) { + DEBUG_ERROR("PICO_ERR_EISCONN - socket is connected"); + picotap->sendReturnValue(picotap->_phy.getDescriptor(rpcSock), -1, EISCONN); + } + } + picotap->sendReturnValue(picotap->_phy.getDescriptor(rpcSock), ERR_OK, ERR_OK); // success + } + + // Feeds data into the client socket from the I/O buffer associated with the connection + void pico_handleRead(PhySocket *sock,void **uptr,bool lwip_invoked) + { + // DEBUG_INFO(); + Connection *conn = picotap->getConnection(sock); + if(conn && conn->rxsz) { + float max = conn->type == SOCK_STREAM ? (float)DEFAULT_TCP_RX_BUF_SZ : (float)DEFAULT_UDP_RX_BUF_SZ; + long n = picotap->_phy.streamSend(conn->sock, conn->rxbuf, /* ZT_MAX_MTU */ conn->rxsz); + // extract address and payload size info + if(conn->type==SOCK_DGRAM) { + int payload_sz, addr_sz_offset = sizeof(struct sockaddr_storage); + memcpy(&payload_sz, conn->rxbuf + addr_sz_offset, sizeof(int)); + struct sockaddr_storage addr; + memcpy(&addr, conn->rxbuf, addr_sz_offset); + // adjust buffer + if(conn->rxsz-n > 0) // If more remains on buffer + memcpy(conn->rxbuf, conn->rxbuf+ZT_MAX_MTU, conn->rxsz - ZT_MAX_MTU); + conn->rxsz -= ZT_MAX_MTU; + } + if(conn->type==SOCK_STREAM) { + //int payload_sz, addr_sz_offset = sizeof(struct sockaddr_storage); + //memcpy(&payload_sz, conn->rxbuf + addr_sz_offset, sizeof(int)); + //struct sockaddr_storage addr; + //memcpy(&addr, conn->rxbuf, addr_sz_offset); + // adjust buffer + if(conn->rxsz-n > 0) // If more remains on buffer + memcpy(conn->rxbuf, conn->rxbuf+n, conn->rxsz - n); + conn->rxsz -= n; + DEBUG_INFO("rxsz=%d", conn->rxsz); + } + if(n) { + //DEBUG_INFO("wrote %d bytes to client application", n); + if(conn->type==SOCK_STREAM) { // Only acknolwedge receipt of TCP packets + DEBUG_TRANS("[TCP RX] <--- :: {TX: %.3f%%, RX: %.3f%%, sock=%p} :: %ld bytes", + (float)conn->txsz / max, (float)conn->rxsz / max, (void*)conn->sock, n); + } + picotap->_phy.setNotifyWritable(conn->sock, true); + } + if(!n || !(conn->rxsz)) { + picotap->_phy.setNotifyWritable(conn->sock, false); + } + } + } + + // Closes a pico_socket + /* + static void pico_handleClose(Connection *conn) + { + DEBUG_INFO(); + int ret; + if(conn && conn->picosock) { + if((ret = picotap->picostack->__pico_socket_close(conn->picosock)) < 0) { + DEBUG_ERROR("error closing pico_socket(%p)", (void*)(conn->picosock)); + // sendReturnValue() + } + return; + } + DEBUG_ERROR("invalid connection or pico_socket"); + } + */ + +} + +#endif // SDK_PICOTCP \ No newline at end of file diff --git a/src/stack_drivers/picotcp.hpp b/src/stack_drivers/picotcp/picotcp.hpp similarity index 92% rename from src/stack_drivers/picotcp.hpp rename to src/stack_drivers/picotcp/picotcp.hpp index de754f1..56225bd 100644 --- a/src/stack_drivers/picotcp.hpp +++ b/src/stack_drivers/picotcp/picotcp.hpp @@ -80,6 +80,27 @@ namespace ZeroTier { + class NetconEthernetTap; + struct Connection; + + // Driver function prototypes + int pico_eth_send(struct pico_device *dev, void *buf, int len); + int pico_eth_poll(struct pico_device *dev, int loop_score); + void pico_init_interface(NetconEthernetTap *tap, const InetAddress &ip); + void pico_loop(NetconEthernetTap *tap); + void pico_cb_tcp_read(NetconEthernetTap *tap, struct pico_socket *s); + void pico_cb_tcp_write(NetconEthernetTap *tap, struct pico_socket *s); + void pico_cb_tcp(uint16_t ev, struct pico_socket *s); + int pico_eth_send(struct pico_device *dev, void *buf, int len); + void pico_rx(NetconEthernetTap *tap, const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); + int pico_eth_poll(struct pico_device *dev, int loop_score); + Connection *pico_handleSocket(PhySocket *sock, void **uptr, struct socket_st* socket_rpc); + void pico_handleWrite(Connection *conn); + void pico_handleConnect(PhySocket *sock, PhySocket *rpcSock, Connection *conn, struct connect_st* connect_rpc); + void pico_handleBind(PhySocket *sock, PhySocket *rpcSock, void **uptr, struct bind_st *bind_rpc); + void pico_handleListen(PhySocket *sock, PhySocket *rpcSock, void **uptr, struct listen_st *listen_rpc); + void pico_handleRead(PhySocket *sock,void **uptr,bool lwip_invoked); + /** * Loads an instance of picoTCP stack library in a private memory arena * diff --git a/src/tap.cpp b/src/tap.cpp index c72ec87..313474a 100644 --- a/src/tap.cpp +++ b/src/tap.cpp @@ -40,17 +40,12 @@ #include "sdk.h" #include "defs.h" #include "debug.h" +#include "rpc.h" #if defined(SDK_LWIP) #include "lwip.hpp" #elif defined(SDK_PICOTCP) #include "picotcp.hpp" - #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" #elif defined(SDK_JIP) #include "jip.hpp" #endif @@ -60,610 +55,42 @@ #include "Constants.hpp" #include "Phy.hpp" -// LWIP -#include "lwip/priv/tcp_priv.h" -#include "lwip/nd6.h" -#include "lwip/api.h" -#include "lwip/ip.h" -#include "lwip/ip_addr.h" -#include "lwip/ip4_addr.h" - -#include "lwip/tcp.h" -#include "lwip/init.h" -#include "lwip/mem.h" -#include "lwip/pbuf.h" -#include "lwip/netif.h" -#include "lwip/udp.h" -#include "lwip/tcp.h" - //#if !defined(__IOS__) && !defined(__ANDROID__) && !defined(__UNITY_3D__) && !defined(__XCODE__) // const ip_addr_t ip_addr_any = { IPADDR_ANY }; //#endif namespace ZeroTier { - -/*------------------------------------------------------------------------------ -------------------------------- picoTCP callbacks ------------------------------ ----------- This section represents the "driver" for the picoTCP stack ---------- -------------------------------------------------------------------------------*/ - -#if defined(SDK_PICOTCP) - - static struct pico_device picodev; - - // Reference to the tap interface - // This is needed due to the fact that there's a lot going on in the tap interface - // that needs to be updated on each of the network stack's callbacks and not every - // network stack provides a mechanism for storing a reference to the tap. - // - // In future releases this will be replaced with a new structure of static pointers that - // will make it easier to maintain multiple active tap interfaces - static NetconEthernetTap *picotap; - - static int pico_eth_send(struct pico_device *dev, void *buf, int len); - static int pico_eth_poll(struct pico_device *dev, int loop_score); - - // Initialize network stack's interfaces and assign addresses - void pico_init_interface(NetconEthernetTap *tap, const InetAddress &ip) +int NetconEthernetTap::sendReturnValue(int fd, int retval, int _errno) +{ + //#if !defined(USE_SOCKS_PROXY) + DEBUG_EXTRA("fd=%d, retval=%d, errno=%d", fd, retval, _errno); + int sz = sizeof(char) + sizeof(retval) + sizeof(errno); + char retmsg[sz]; + memset(&retmsg, 0, sizeof(retmsg)); + retmsg[0]=RPC_RETVAL; + memcpy(&retmsg[1], &retval, sizeof(retval)); + memcpy(&retmsg[1]+sizeof(retval), &_errno, sizeof(_errno)); + return write(fd, &retmsg, sz); + //#else + // return 1; + //#endif +} +// Unpacks the buffer from an RPC command +void NetconEthernetTap::unloadRPC(void *data, pid_t &pid, pid_t &tid, + char (timestamp[RPC_TIMESTAMP_SZ]), char (CANARY[sizeof(uint64_t)]), char &cmd, void* &payload) { - picoTCP_stack *stack = tap->picostack; - DEBUG_INFO(); - if (std::find(picotap->_ips.begin(),picotap->_ips.end(),ip) == picotap->_ips.end()) { - picotap->_ips.push_back(ip); - std::sort(picotap->_ips.begin(),picotap->_ips.end()); - #if defined(SDK_IPV4) - if(ip.isV4()) - { - struct pico_ip4 ipaddr, netmask; - ipaddr.addr = *((u32_t *)ip.rawIpData()); - netmask.addr = *((u32_t *)ip.netmask().rawIpData()); - uint8_t mac[PICO_SIZE_ETH]; - picotap->_mac.copyTo(mac, PICO_SIZE_ETH); - DEBUG_ATTN("mac = %s", picotap->_mac.toString().c_str()); - picodev.send = pico_eth_send; // tx - picodev.poll = pico_eth_poll; // rx - picodev.mtu = picotap->_mtu; - if( 0 != stack->__pico_device_init(&(picodev), "p0", mac)) { - DEBUG_ERROR("device init failed"); - return; - } - stack->__pico_ipv4_link_add(&(picodev), ipaddr, netmask); - // DEBUG_INFO("device initialized as ipv4_addr = %s", ipv4_str); - // picostack->__pico_icmp4_ping("10.8.8.1", 20, 1000, 10000, 64, cb_ping); - } - #elif defined(SDK_IPV6) - if(ip.isV6()) - { - struct pico_ip6 ipaddr, netmask; - 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); - stack->__pico_string_to_ipv6(ipv6_str, ipaddr.addr); - stack->__pico_string_to_ipv6(nm_str, netmask.addr); - stack->__pico_ipv6_link_add(&(picodev), ipaddr, netmask); - picodev.send = pico_eth_send; // tx - picodev.poll = pico_eth_poll; // rx - uint8_t mac[PICO_SIZE_ETH]; - picotap->_mac.copyTo(mac, PICO_SIZE_ETH); - DEBUG_ATTN("mac = %s", picotap->_mac.toString().c_str()); - if( 0 != stack->__pico_device_init(&(picodev), "p0", mac)) { - DEBUG_ERROR("device init failed"); - return; - } - DEBUG_ATTN("device initialized as ipv6_addr = %s", ipv6_str); - } - #endif - } - } - - // I/O thread loop - void pico_loop(NetconEthernetTap *tap) - { - DEBUG_INFO(); - while(tap->_run) - { - tap->_phy.poll((unsigned long)std::min(500,1000)); - usleep(1000); - tap->picostack->__pico_stack_tick(); - } - } - - // RX packets from network onto internal buffer - // Also notifies the tap service that data can be read, buffer will be emptied by pico_handleRead() - static void pico_cb_tcp_read(NetconEthernetTap *tap, struct pico_socket *s) - { - // TODO: Verify - DEBUG_INFO(); - Connection *conn = tap->getConnection(s); - if(conn) { - int r; - do { - //int avail = DEFAULT_TCP_RX_BUF_SZ - conn->rxsz; - //if(avail) { - r = tap->picostack->__pico_socket_read(s, conn->rxbuf + (conn->rxsz), ZT_MAX_MTU); - tap->_phy.setNotifyWritable(conn->sock, true); - DEBUG_INFO("read=%d", r); - if (r > 0) { - conn->rxsz += r; - } - //} - if (r < 0) { - exit(5); - } - } - while(r > 0); - return; - } - DEBUG_ERROR("invalid connection"); - } - - // TX packets from internal buffer to network - static void pico_cb_tcp_write(NetconEthernetTap *tap, struct pico_socket *s) - { - Connection *conn = tap->getConnection(s); - if(!conn) - DEBUG_ERROR("invalid connection"); - if(!conn->txsz) - return; - DEBUG_INFO("txsz=%d bytes ready to be written", conn->txsz); - - // Only called from a locked context, no need to lock anything - if(conn->txsz > 0) { - int r = conn->txsz < ZT_MAX_MTU ? conn->txsz : ZT_MAX_MTU; - if((r = tap->picostack->__pico_socket_write(s, &conn->txbuf, r)) < 0) { - DEBUG_ERROR("unable to write to pico_socket=%p", (void*)s); - return; - } - int sz = (conn->txsz)-r; - if(sz) - memmove(&conn->txbuf, (conn->txbuf+r), sz); - conn->txsz -= r; - int max = conn->type == SOCK_STREAM ? DEFAULT_TCP_TX_BUF_SZ : DEFAULT_UDP_TX_BUF_SZ; - DEBUG_TRANS("[TCP TX] ---> :: {TX: %.3f%%, RX: %.3f%%, sock=%p} :: %d bytes", - (float)conn->txsz / (float)max, (float)conn->rxsz / max, (void*)&conn->sock, r); - return; - } - } - - // Main callback for TCP connections - static void pico_cb_tcp(uint16_t ev, struct pico_socket *s) - { - Mutex::Lock _l(picotap->_tcpconns_m); - Connection *conn = picotap->getConnection(s); - if(!conn) { - DEBUG_ERROR("invalid connection"); - } - if (ev & PICO_SOCK_EV_RD) { - pico_cb_tcp_read(picotap, s); - } - - // Accept connection (analogous to lwip_nc_accept) - if (ev & PICO_SOCK_EV_CONN) { - DEBUG_INFO("connection established with server, sock=%p", (void*)(conn->picosock)); - uint32_t peer; - uint16_t port; - struct pico_socket *client = picotap->picostack->__pico_socket_accept(s, &peer, &port); - if(!client) { - DEBUG_ERROR("there was an error accepting the connection, sock=%p", (void*)(conn->picosock)); - } - - ZT_PHY_SOCKFD_TYPE fds[2]; - if(socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) { - if(errno < 0) { - picotap->sendReturnValue(conn, -1, errno); - DEBUG_ERROR("unable to create socketpair"); - return; - } - } - Connection *newTcpConn = new Connection(); - picotap->_Connections.push_back(newTcpConn); - newTcpConn->type = SOCK_STREAM; - newTcpConn->sock = picotap->_phy.wrapSocket(fds[0], newTcpConn); - newTcpConn->picosock = client; - int fd = picotap->_phy.getDescriptor(conn->sock); - if(sock_fd_write(fd, fds[1]) < 0) { - DEBUG_ERROR("error sending new fd to client application"); - } - } - if (ev & PICO_SOCK_EV_FIN) { - DEBUG_INFO("socket closed. Exit normally."); - //picotap->__pico_timer_add(2000, compare_results, NULL); - } - if (ev & PICO_SOCK_EV_ERR) { - DEBUG_INFO("socket error received" /*, strerror(pico_err)*/); - //exit(1); - } - if (ev & PICO_SOCK_EV_CLOSE) { - DEBUG_INFO("socket received close from peer - Wrong case if not all client data sent!"); - picotap->picostack->__pico_socket_close(s); - picotap->closeConnection(conn); - return; - } - if (ev & PICO_SOCK_EV_WR) { - pico_cb_tcp_write(picotap, s); - } - } - - // Called when an incoming ping is received - /* - static void pico_cb_ping(struct pico_icmp4_stats *s) - { - DEBUG_INFO(); - char host[30]; - picotap->picostack->__pico_ipv4_to_string(host, s->dst.addr); - if (s->err == 0) { - printf("%lu bytes from %s: icmp_req=%lu ttl=%lu time=%lu ms\n", s->size, - host, s->seq, s->ttl, (long unsigned int)s->time); - } else { - printf("PING %lu to %s: Error %d\n", s->seq, host, s->err); - } - } - */ - - // Sends data to the tap device (in our case, the ZeroTier service) - static int pico_eth_send(struct pico_device *dev, void *buf, int len) - { - DEBUG_INFO("len=%d", len); - struct eth_hdr *ethhdr; - ethhdr = (struct eth_hdr *)buf; - - MAC src_mac; - MAC dest_mac; - src_mac.setTo(ethhdr->src.addr, 6); - dest_mac.setTo(ethhdr->dest.addr, 6); - - picotap->_handler(picotap->_arg,picotap->_nwid,src_mac,dest_mac, - Utils::ntoh((uint16_t)ethhdr->type),0, ((char*)buf) + sizeof(struct eth_hdr),len - sizeof(struct eth_hdr)); - return len; - } - - // Receives data from the tap device and encapsulates it into a ZeroTier ethernet frame and places it in a locked memory buffer - void pico_rx(NetconEthernetTap *tap, const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) - { - // DEBUG_INFO(); - // Since picoTCP only allows the reception of frames from within the polling function, we - // must enqueue each frame into a memory structure shared by both threads. This structure will - Mutex::Lock _l(tap->_pico_frame_rxbuf_m); - if(len > ((1024 * 1024) - tap->pico_frame_rxbuf_tot)) { - DEBUG_ERROR("dropping packet (len = %d) - not enough space left on RX frame buffer", len); - return; - } - //if(len != memcpy(pico_frame_rxbuf, data, len)) { - // DEBUG_ERROR("dropping packet (len = %d) - unable to copy contents of frame to RX frame buffer", len); - // return; - //} - - // assemble new eth header - struct eth_hdr ethhdr; - from.copyTo(ethhdr.src.addr, 6); - to.copyTo(ethhdr.dest.addr, 6); - ethhdr.type = Utils::hton((uint16_t)etherType); - int newlen = len+sizeof(struct eth_hdr); - - memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot, &newlen, sizeof(newlen)); // size of frame - memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot + sizeof(newlen), ðhdr, sizeof(ethhdr)); // new eth header - memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot + sizeof(newlen) + sizeof(ethhdr), data, len); // frame data - tap->pico_frame_rxbuf_tot += len + sizeof(len) + sizeof(ethhdr); - // DEBUG_INFO("RX frame buffer %3f full", (float)pico_frame_rxbuf_tot / (float)(1024 * 1024)); - DEBUG_INFO("len=%d", len); - } - - // Is called periodically by the stack, this removes data from the locked memory buffer and feeds it into the stack. - // A maximum of 'loop_score' frames can be processed in each call - static int pico_eth_poll(struct pico_device *dev, int loop_score) - { - // DEBUG_EXTRA(); - // OPTIMIZATION: The copy logic and/or buffer structure should be reworked for better performance after the BETA - // NetconEthernetTap *tap = (NetconEthernetTap*)netif->state; - Mutex::Lock _l(picotap->_pico_frame_rxbuf_m); - unsigned char frame[ZT_MAX_MTU]; - uint32_t len; - - while (picotap->pico_frame_rxbuf_tot > 0) { - memset(frame, 0, sizeof(frame)); - len = 0; - memcpy(&len, picotap->pico_frame_rxbuf, sizeof(len)); // get frame len - memcpy(frame, picotap->pico_frame_rxbuf + sizeof(len), len); // get frame data - memmove(picotap->pico_frame_rxbuf, picotap->pico_frame_rxbuf + sizeof(len) + len, ZT_MAX_MTU-(sizeof(len) + len)); - picotap->picostack->__pico_stack_recv(dev, (uint8_t*)frame, len); - picotap->pico_frame_rxbuf_tot-=(sizeof(len) + len); - // DEBUG_EXTRA("RX frame buffer %3f full", (float)(picotap->pico_frame_rxbuf_tot) / (float)(MAX_PICO_FRAME_RX_BUF_SZ)); - loop_score--; - } - return loop_score; - } - - // Creates a new pico_socket and Connection object to represent a new connection to be. - static Connection *pico_handleSocket(PhySocket *sock, void **uptr, struct socket_st* socket_rpc) - { - DEBUG_INFO(); - struct pico_socket * psock; - #if defined(SDK_IPV4) - psock = picotap->picostack->__pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &pico_cb_tcp); - #elif defined(SDK_IPV6) - psock = picotap->picostack->__pico_socket_open(PICO_PROTO_IPV6, PICO_PROTO_TCP, &pico_cb_tcp); - #endif - if(psock) { - DEBUG_ATTN("psock = %p", (void*)psock); - Connection * newConn = new Connection(); - *uptr = newConn; - newConn->type = socket_rpc->socket_type; - newConn->sock = sock; - newConn->local_addr = NULL; - newConn->peer_addr = NULL; - newConn->picosock = psock; - picotap->_Connections.push_back(newConn); - return newConn; - } - else { - DEBUG_ERROR("failed to create pico_socket"); - } - return NULL; - } - - // Writes data from the I/O buffer to the network stack - static void pico_handleWrite(Connection *conn) - { - DEBUG_INFO(); - if(!conn || !conn->picosock) { - DEBUG_ERROR(" invalid connection"); - return; - } - int r, max_write_len = conn->txsz < ZT_MAX_MTU ? conn->txsz : ZT_MAX_MTU; - if((r = picotap->picostack->__pico_socket_write(conn->picosock, &conn->txbuf, max_write_len)) < 0) { - DEBUG_ERROR("unable to write to pico_socket(%p)", (void*)&(conn->picosock)); - return; - } - /* - if(pico_err == PICO_ERR_EINVAL) - DEBUG_ERROR("PICO_ERR_EINVAL - invalid argument"); - if(pico_err == PICO_ERR_EIO) - DEBUG_ERROR("PICO_ERR_EIO - input/output error"); - if(pico_err == PICO_ERR_ENOTCONN) - DEBUG_ERROR("PICO_ERR_ENOTCONN - the socket is not connected"); - if(pico_err == PICO_ERR_ESHUTDOWN) - DEBUG_ERROR("PICO_ERR_ESHUTDOWN - cannot send after transport endpoint shutdown"); - if(pico_err == PICO_ERR_EADDRNOTAVAIL) - DEBUG_ERROR("PICO_ERR_EADDRNOTAVAIL - address not available"); - if(pico_err == PICO_ERR_EHOSTUNREACH) - DEBUG_ERROR("PICO_ERR_EHOSTUNREACH - host is unreachable"); - if(pico_err == PICO_ERR_ENOMEM) - DEBUG_ERROR("PICO_ERR_ENOMEM - not enough space"); - if(pico_err == PICO_ERR_EAGAIN) - DEBUG_ERROR("PICO_ERR_EAGAIN - resource temporarily unavailable"); - */ - // adjust buffer - int sz = (conn->txsz)-r; - if(sz) - memmove(&conn->txbuf, (conn->txbuf+r), sz); - conn->txsz -= r; - int max = conn->type == SOCK_STREAM ? DEFAULT_TCP_TX_BUF_SZ : DEFAULT_UDP_TX_BUF_SZ; - DEBUG_TRANS("[TCP TX] ---> :: {TX: %.3f%%, RX: %.3f%%, sock=%p} :: %d bytes", - (float)conn->txsz / (float)max, (float)conn->rxsz / max, (void*)&conn->sock, r); - } - - // Instructs the stack to connect to a remote host - static void pico_handleConnect(PhySocket *sock, PhySocket *rpcSock, Connection *conn, struct connect_st* connect_rpc) - { - DEBUG_INFO(); - if(conn->picosock) { - struct sockaddr_in *addr = (struct sockaddr_in *) &connect_rpc->addr; - int ret; - // TODO: Rewrite this - #if defined(SDK_IPV4) - struct pico_ip4 zaddr; - struct sockaddr_in *in4 = (struct sockaddr_in*)&connect_rpc->addr; - char ipv4_str[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &(in4->sin_addr), ipv4_str, INET_ADDRSTRLEN); - picotap->picostack->__pico_string_to_ipv4(ipv4_str, &(zaddr.addr)); - DEBUG_ATTN("addr=%s:%d", ipv4_str, addr->sin_port); - ret = picotap->picostack->__pico_socket_connect(conn->picosock, &zaddr, addr->sin_port); - #elif defined(SDK_IPV6) // "fd56:5799:d8f6:1238:8c99:9322:30ce:418a" - struct pico_ip6 zaddr; - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&connect_rpc->addr; - char ipv6_str[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN); - picotap->picostack->__pico_string_to_ipv6(ipv6_str, zaddr.addr); - DEBUG_ATTN("addr=%s:%d", ipv6_str, addr->sin_port); - ret = picotap->picostack->__pico_socket_connect(conn->picosock, &zaddr, addr->sin_port); - #endif - - if(ret == PICO_ERR_EPROTONOSUPPORT) { - DEBUG_ERROR("PICO_ERR_EPROTONOSUPPORT"); - } - if(ret == PICO_ERR_EINVAL) { - DEBUG_ERROR("PICO_ERR_EINVAL"); - } - if(ret == PICO_ERR_EHOSTUNREACH) { - DEBUG_ERROR("PICO_ERR_EHOSTUNREACH"); - } - picotap->sendReturnValue(rpcSock, 0, ERR_OK); - } - } - - // Instructs the stack to bind to a given address - static void pico_handleBind(PhySocket *sock, PhySocket *rpcSock, void **uptr, struct bind_st *bind_rpc) - { - DEBUG_INFO(); - Connection *conn = picotap->getConnection(sock); - if(!sock) { - DEBUG_ERROR("invalid connection"); - return; - } - struct sockaddr_in *addr = (struct sockaddr_in *) &bind_rpc->addr; - int ret; - // TODO: Rewrite this - #if defined(SDK_IPV4) - struct pico_ip4 zaddr; - struct sockaddr_in *in4 = (struct sockaddr_in*)&bind_rpc->addr; - char ipv4_str[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &(in4->sin_addr), ipv4_str, INET_ADDRSTRLEN); - picotap->picostack->__pico_string_to_ipv4(ipv4_str, &(zaddr.addr)); - DEBUG_ATTN("addr=%s", ipv4_str/*, ntohs((uint16_t*)&(addr->sin_port))*/); - ret = picotap->picostack->__pico_socket_bind(conn->picosock, &zaddr, (uint16_t*)&(addr->sin_port)); - #elif defined(SDK_IPV6) - struct pico_ip6 zaddr; - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&bind_rpc->addr; - char ipv6_str[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN); - picotap->picostack->__pico_string_to_ipv6(ipv6_str, zaddr.addr); - DEBUG_ATTN("addr=%s", ipv6_str/*, ntohs((uint16_t*)&(addr->sin_port))*/); - ret = picotap->picostack->__pico_socket_bind(conn->picosock, &zaddr, (uint16_t*)&(addr->sin_port)); - #endif - if(ret < 0) { - DEBUG_ERROR("unable to bind pico_socket(%p)", (void*)(conn->picosock)); - if(ret == PICO_ERR_EINVAL) { - DEBUG_ERROR("PICO_ERR_EINVAL - invalid argument"); - picotap->sendReturnValue(rpcSock, -1, EINVAL); - } - if(ret == PICO_ERR_ENOMEM) { - DEBUG_ERROR("PICO_ERR_ENOMEM - not enough space"); - picotap->sendReturnValue(rpcSock, -1, ENOMEM); - } - if(ret == PICO_ERR_ENXIO) { - DEBUG_ERROR("PICO_ERR_ENXIO - no such device or address"); - picotap->sendReturnValue(rpcSock, -1, ENXIO); - } - } - picotap->sendReturnValue(rpcSock, ERR_OK, ERR_OK); // success - } - - // Puts a pico_socket into a listening state to receive incoming connection requests - static void pico_handleListen(PhySocket *sock, PhySocket *rpcSock, void **uptr, struct listen_st *listen_rpc) - { - Connection *conn = picotap->getConnection(sock); - DEBUG_ATTN("conn = %p", (void*)conn); - if(!sock || !conn) { - DEBUG_ERROR("invalid connection"); - return; - } - int ret, backlog = 1; - if((ret = picotap->picostack->__pico_socket_listen(conn->picosock, backlog)) < 0) - { - if(ret == PICO_ERR_EINVAL) { - DEBUG_ERROR("PICO_ERR_EINVAL - invalid argument"); - picotap->sendReturnValue(rpcSock, -1, EINVAL); - } - if(ret == PICO_ERR_EISCONN) { - DEBUG_ERROR("PICO_ERR_EISCONN - socket is connected"); - picotap->sendReturnValue(rpcSock, -1, EISCONN); - } - } - picotap->sendReturnValue(rpcSock, ERR_OK, ERR_OK); // success - } - - // Feeds data into the client socket from the I/O buffer associated with the connection - static void pico_handleRead(PhySocket *sock,void **uptr,bool lwip_invoked) - { - // DEBUG_INFO(); - Connection *conn = picotap->getConnection(sock); - if(conn && conn->rxsz) { - float max = conn->type == SOCK_STREAM ? (float)DEFAULT_TCP_RX_BUF_SZ : (float)DEFAULT_UDP_RX_BUF_SZ; - long n = picotap->_phy.streamSend(conn->sock, conn->rxbuf, /* ZT_MAX_MTU */ conn->rxsz); - // extract address and payload size info - if(conn->type==SOCK_DGRAM) { - int payload_sz, addr_sz_offset = sizeof(struct sockaddr_storage); - memcpy(&payload_sz, conn->rxbuf + addr_sz_offset, sizeof(int)); - struct sockaddr_storage addr; - memcpy(&addr, conn->rxbuf, addr_sz_offset); - // adjust buffer - if(conn->rxsz-n > 0) // If more remains on buffer - memcpy(conn->rxbuf, conn->rxbuf+ZT_MAX_MTU, conn->rxsz - ZT_MAX_MTU); - conn->rxsz -= ZT_MAX_MTU; - } - if(conn->type==SOCK_STREAM) { - //int payload_sz, addr_sz_offset = sizeof(struct sockaddr_storage); - //memcpy(&payload_sz, conn->rxbuf + addr_sz_offset, sizeof(int)); - //struct sockaddr_storage addr; - //memcpy(&addr, conn->rxbuf, addr_sz_offset); - // adjust buffer - if(conn->rxsz-n > 0) // If more remains on buffer - memcpy(conn->rxbuf, conn->rxbuf+n, conn->rxsz - n); - conn->rxsz -= n; - DEBUG_INFO("rxsz=%d", conn->rxsz); - } - if(n) { - //DEBUG_INFO("wrote %d bytes to client application", n); - if(conn->type==SOCK_STREAM) { // Only acknolwedge receipt of TCP packets - DEBUG_TRANS("[TCP RX] <--- :: {TX: %.3f%%, RX: %.3f%%, sock=%p} :: %ld bytes", - (float)conn->txsz / max, (float)conn->rxsz / max, (void*)conn->sock, n); - } - picotap->_phy.setNotifyWritable(conn->sock, true); - } - if(!n || !(conn->rxsz)) { - picotap->_phy.setNotifyWritable(conn->sock, false); - } - } - } - - // Closes a pico_socket - /* - static void pico_handleClose(Connection *conn) - { - DEBUG_INFO(); - int ret; - if(conn && conn->picosock) { - if((ret = picotap->picostack->__pico_socket_close(conn->picosock)) < 0) { - DEBUG_ERROR("error closing pico_socket(%p)", (void*)(conn->picosock)); - // sendReturnValue() - } - return; - } - DEBUG_ERROR("invalid connection or pico_socket"); - } - */ - -#endif // SDK_PICOTCP - + unsigned char *buf = (unsigned char*)data; + memcpy(&pid, &buf[IDX_PID], sizeof(pid_t)); + memcpy(&tid, &buf[IDX_TID], sizeof(pid_t)); + memcpy(timestamp, &buf[IDX_TIME], RPC_TIMESTAMP_SZ); + memcpy(&cmd, &buf[IDX_PAYLOAD], sizeof(char)); + memcpy(CANARY, &buf[IDX_PAYLOAD+1], CANARY_SZ); +} /*------------------------------------------------------------------------------ -------------------------------- Tap Service ---------------------------------- ------------------------------------------------------------------------------*/ -#if SDK_LWIP - static err_t tapif_init(struct netif *netif) - { - // Actual init functionality is in addIp() of tap - return ERR_OK; - } - - /* - * Outputs data from the pbuf queue to the interface - */ - static err_t low_level_output(struct netif *netif, struct pbuf *p) - { - struct pbuf *q; - char buf[ZT_MAX_MTU+32]; - char *bufptr; - int totalLength = 0; - - ZeroTier::NetconEthernetTap *tap = (ZeroTier::NetconEthernetTap*)netif->state; - bufptr = buf; - // Copy data from each pbuf, one at a time - for(q = p; q != NULL; q = q->next) { - memcpy(bufptr, q->payload, q->len); - bufptr += q->len; - totalLength += q->len; - } - // [Send packet to network] - // Split ethernet header and feed into handler - struct eth_hdr *ethhdr; - ethhdr = (struct eth_hdr *)buf; - - ZeroTier::MAC src_mac; - ZeroTier::MAC dest_mac; - src_mac.setTo(ethhdr->src.addr, 6); - dest_mac.setTo(ethhdr->dest.addr, 6); - - tap->_handler(tap->_arg,tap->_nwid,src_mac,dest_mac, - Utils::ntoh((uint16_t)ethhdr->type),0,buf + sizeof(struct eth_hdr),totalLength - sizeof(struct eth_hdr)); - return ERR_OK; - } -#endif - - -// --------------------------------------------------------------------------- - NetconEthernetTap::NetconEthernetTap( const char *homePath, const MAC &mac, @@ -689,7 +116,6 @@ NetconEthernetTap::NetconEthernetTap( Utils::snprintf(sockPath,sizeof(sockPath),"%s%snc_%.16llx",homePath,ZT_PATH_SEPARATOR_S,_nwid,ZT_PATH_SEPARATOR_S,(unsigned long long)nwid); _dev = sockPath; // in SDK mode, set device to be just the network ID - // SIP-0 // Load and initialize network stack library #if defined(SDK_LWIP) Utils::snprintf(stackPath,sizeof(stackPath),"%s%sliblwip.so",homePath,ZT_PATH_SEPARATOR_S); @@ -749,79 +175,11 @@ bool NetconEthernetTap::enabled() const return _enabled; } -void NetconEthernetTap::lwIP_init_interface(const InetAddress &ip) -{ - #if defined(SDK_LWIP) - Mutex::Lock _l(_ips_m); - - if (std::find(_ips.begin(),_ips.end(),ip) == _ips.end()) { - _ips.push_back(ip); - std::sort(_ips.begin(),_ips.end()); - - #if defined(SDK_IPV4) - if (ip.isV4()) { - // Set IP - static ip_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()); - lwipstack->__netif_add(&interface,&ipaddr, &netmask, &gw, NULL, tapif_init, lwipstack->_ethernet_input); - interface.state = this; - interface.output = lwipstack->_etharp_output; - _mac.copyTo(interface.hwaddr, 6); - interface.mtu = _mtu; - interface.name[0] = 'l'; - interface.name[1] = '4'; - interface.linkoutput = low_level_output; - interface.hwaddr_len = 6; - interface.flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP; - lwipstack->__netif_set_default(&interface); - lwipstack->__netif_set_up(&interface); - DEBUG_INFO("addr=%s, netmask=%s", ip.toString().c_str(), ip.netmask().toString().c_str()); - } - #endif - - #if defined(SDK_IPV6) - if(ip.isV6()) { - DEBUG_INFO("local_addr=%s", ip.toString().c_str()); - static ip6_addr_t addr6; - struct sockaddr_in6 in6; - memcpy(in6.sin6_addr.s6_addr,ip.rawIpData(),16); - in6_to_ip6((ip6_addr *)&addr6, &in6); - interface6.mtu = _mtu; - interface6.name[0] = 'l'; - interface6.name[1] = '6'; - interface6.hwaddr_len = 6; - interface6.linkoutput = low_level_output; - interface6.ip6_autoconfig_enabled = 1; - _mac.copyTo(interface6.hwaddr, interface6.hwaddr_len); - lwipstack->__netif_create_ip6_linklocal_address(&interface6, 1); - lwipstack->__netif_add(&interface6, NULL, tapif_init, lwipstack->_ethernet_input); - lwipstack->__netif_set_default(&interface6); - lwipstack->__netif_set_up(&interface6); - netif_ip6_addr_set_state(&interface6, 1, IP6_ADDR_TENTATIVE); - ip6_addr_copy(ip_2_ip6(interface6.ip6_addr[1]), addr6); - interface6.output_ip6 = lwipstack->_ethip6_output; - interface6.state = this; - interface6.flags = NETIF_FLAG_LINK_UP | NETIF_FLAG_UP; - DEBUG_INFO("addr=%s, netmask=%s", ip.toString().c_str(), ip.netmask().toString().c_str()); - } - #endif - } - #endif -} - -void NetconEthernetTap::jip_init_interface(const InetAddress &ip) -{ - // will be similar to lwIP initialization process -} - bool NetconEthernetTap::addIp(const InetAddress &ip) { - // SIP-1 // Initialize network stack's interface, assign addresses #if defined(SDK_LWIP) - lwIP_init_interface(ip); + lwip_init_interface(this, ip); #elif defined(SDK_PICOTCP) picotap = this; pico_init_interface(this, ip); @@ -839,7 +197,7 @@ bool NetconEthernetTap::removeIp(const InetAddress &ip) return false; _ips.erase(i); if (ip.isV4()) { - // TODO: dealloc from LWIP + // TODO: De-register from network stacks } return true; } @@ -850,70 +208,12 @@ std::vector NetconEthernetTap::ips() const return _ips; } - -void NetconEthernetTap::lwIP_rx(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) -{ - #if defined(SDK_LWIP) - // DEBUG_EXTRA(); - struct pbuf *p,*q; - if (!_enabled) - return; - struct eth_hdr ethhdr; - from.copyTo(ethhdr.src.addr, 6); - to.copyTo(ethhdr.dest.addr, 6); - ethhdr.type = Utils::hton((uint16_t)etherType); - - p = lwipstack->__pbuf_alloc(PBUF_RAW, len+sizeof(struct eth_hdr), PBUF_POOL); - if (p != NULL) { - const char *dataptr = reinterpret_cast(data); - // First pbuf gets ethernet header at start - q = p; - if (q->len < sizeof(ethhdr)) { - DEBUG_ERROR("dropped packet: first pbuf smaller than ethernet header"); - return; - } - memcpy(q->payload,ðhdr,sizeof(ethhdr)); - memcpy((char*)q->payload + sizeof(ethhdr),dataptr,q->len - sizeof(ethhdr)); - dataptr += q->len - sizeof(ethhdr); - - // Remaining pbufs (if any) get rest of data - while ((q = q->next)) { - memcpy(q->payload,dataptr,q->len); - dataptr += q->len; - } - } - else { - DEBUG_ERROR("dropped packet: no pbufs available"); - return; - } - { - #if defined(SDK_IPV6) - if(interface6.input(p, &interface6) != ERR_OK) { - DEBUG_ERROR("error while feeding frame into stack interface6"); - } - #endif - #if defined(SDK_IPV4) - if(interface.input(p, &interface) != ERR_OK) { - DEBUG_ERROR("error while feeding frame into stack interface"); - } - #endif - } - #endif -} - - -void NetconEthernetTap::jip_rx(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) -{ - DEBUG_INFO(); -} - void NetconEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) { // DEBUG_EXTRA("RX packet: len=%d, etherType=%d", len, etherType); - // SIP- // RX packet #if defined(SDK_LWIP) - lwIP_rx(from,to,etherType,data,len); + lwip_rx(this, from,to,etherType,data,len); #elif defined(SDK_PICOTCP) pico_rx(this, from,to,etherType,data,len); #elif defined(SDK_JIP) @@ -951,106 +251,17 @@ void NetconEthernetTap::scanMulticastGroups(std::vector &added,s } _multicastGroups.swap(newGroups); } - -void NetconEthernetTap::lwIP_loop() -{ - #if defined(SDK_LWIP) - DEBUG_INFO(); - uint64_t prev_tcp_time = 0, prev_status_time = 0, prev_discovery_time = 0; - // Main timer loop - while (_run) { - uint64_t now = OSUtils::now(); - uint64_t since_tcp = now - prev_tcp_time; - uint64_t since_discovery = now - prev_discovery_time; - uint64_t since_status = now - prev_status_time; - uint64_t tcp_remaining = ZT_LWIP_TCP_TIMER_INTERVAL; - uint64_t discovery_remaining = 5000; - - #if defined(LWIP_IPV6) - #define DISCOVERY_INTERVAL 1000 // fuck you - #elif - #define DISCOVERY_INTERVAL ARP_TMR_INTERVAL - #endif - - // Connection prunning - if (since_status >= STATUS_TMR_INTERVAL) { - prev_status_time = now; - for(size_t i=0;i<_Connections.size();++i) { - if(!_Connections[i]->sock || _Connections[i]->type != SOCK_STREAM) - continue; - int fd = _phy.getDescriptor(_Connections[i]->sock); - // DEBUG_INFO(" tap_thread(): tcp\\jobs = {%d, %d}\n", _Connection.size(), jobmap.size()); - // If there's anything on the RX buf, set to notify in case we stalled - if(_Connections[i]->rxsz > 0) - _phy.setNotifyWritable(_Connections[i]->sock, true); - fcntl(fd, F_SETFL, O_NONBLOCK); - unsigned char tmpbuf[BUF_SZ]; - - ssize_t n = read(fd,&tmpbuf,BUF_SZ); - if(_Connections[i]->TCP_pcb->state == SYN_SENT) { - DEBUG_EXTRA(" should finish or be removed soon, sock=%p, state=SYN_SENT", - (void*)&(_Connections[i]->sock)); - } - if((n < 0 && errno != EAGAIN) || (n == 0 && errno == EAGAIN)) { - //DEBUG_INFO(" closing sock (%x)", (void*)_Connections[i]->sock); - closeConnection(_Connections[i]->sock); - } else if (n > 0) { - DEBUG_INFO(" data read during connection check (%ld bytes)", n); - phyOnUnixData(_Connections[i]->sock,_phy.getuptr(_Connections[i]->sock),&tmpbuf,n); - } - } - } - // Main TCP/ETHARP timer section - if (since_tcp >= ZT_LWIP_TCP_TIMER_INTERVAL) { - prev_tcp_time = now; - lwipstack->__tcp_tmr(); - // FIXME: could be removed or refactored? - // Makeshift poll - for(size_t i=0;i<_Connections.size();++i) { - if(_Connections[i]->txsz > 0){ - handleWrite(_Connections[i]); - } - } - } else { - tcp_remaining = ZT_LWIP_TCP_TIMER_INTERVAL - since_tcp; - } - if (since_discovery >= DISCOVERY_INTERVAL) { - prev_discovery_time = now; - #if defined(SDK_IPV4) - lwipstack->__etharp_tmr(); - #endif - #if defined(SDK_IPV6) - lwipstack->__nd6_tmr(); - #endif - } else { - discovery_remaining = DISCOVERY_INTERVAL - since_discovery; - } - _phy.poll((unsigned long)std::min(tcp_remaining,discovery_remaining)); - } - lwipstack->close(); - #endif -} - -void NetconEthernetTap::jip_loop() -{ - DEBUG_INFO(); - while(_run) - { - - } -} void NetconEthernetTap::threadMain() throw() { - // SIP-2 // Enter main thread loop for network stack #if defined(SDK_LWIP) - lwIP_loop(); + lwip_loop(this); #elif defined(SDK_PICOTCP) pico_loop(this); #elif defined(SDK_JIP) - jip_loop(); + jip_loop(this); #endif } @@ -1075,48 +286,22 @@ Connection *NetconEthernetTap::getConnection(struct pico_socket *sock) void NetconEthernetTap::closeConnection(PhySocket *sock) { DEBUG_EXTRA("sock=%p", (void*)sock); - //return; - Mutex::Lock _l(_close_m); // Here we assume _tcpconns_m is already locked by caller if(!sock) { DEBUG_EXTRA("invalid PhySocket"); return; } - Connection *conn = getConnection(sock); - if(!conn) - return; - // picoTCP #if defined(SDK_PICOTCP) // pico_handleClose(conn); #endif - + Connection *conn = getConnection(sock); + if(!conn) + return; // lwIP #if defined(SDK_LWIP) - if(conn->type==SOCK_DGRAM) { - lwipstack->__udp_remove(conn->UDP_pcb); - } - if(conn->TCP_pcb && conn->TCP_pcb->state != CLOSED) { - DEBUG_EXTRA("conn=%p, sock=%p, PCB->state = %d", - (void*)&conn, (void*)&sock, conn->TCP_pcb->state); - if(conn->TCP_pcb->state == SYN_SENT /*|| conn->TCP_pcb->state == CLOSE_WAIT*/) { - DEBUG_EXTRA("ignoring close request. invalid PCB state for this operation. sock=%p", (void*)&sock); - return; - } - // DEBUG_BLANK("__tcp_close(...)"); - if(lwipstack->__tcp_close(conn->TCP_pcb) == ERR_OK) { - // Unregister callbacks for this PCB - lwipstack->__tcp_arg(conn->TCP_pcb, NULL); - lwipstack->__tcp_recv(conn->TCP_pcb, NULL); - lwipstack->__tcp_err(conn->TCP_pcb, NULL); - lwipstack->__tcp_sent(conn->TCP_pcb, NULL); - lwipstack->__tcp_poll(conn->TCP_pcb, NULL, 1); - } - else { - DEBUG_EXTRA("error while calling tcp_close() sock=%p", (void*)&sock); - } - } + lwip_handleClose(this, sock, conn); #endif for(size_t i=0;i<_Connections.size();++i) { if(_Connections[i] == conn){ @@ -1137,73 +322,16 @@ void NetconEthernetTap::phyOnUnixClose(PhySocket *sock,void **uptr) { //closeConnection(sock); } - void NetconEthernetTap::handleRead(PhySocket *sock,void **uptr,bool lwip_invoked) { - //DEBUG_EXTRA("handleRead(sock=%p): lwip_invoked = %d\n", (void*)&sock, lwip_invoked); - + // DEBUG_EXTRA("handleRead(sock=%p): lwip_invoked = %d\n", (void*)&sock, lwip_invoked); // picoTCP #if defined(SDK_PICOTCP) pico_handleRead(sock, uptr, lwip_invoked); #endif - // lwIP #if defined(SDK_LWIP) - if(!lwip_invoked) { - _tcpconns_m.lock(); - _rx_buf_m.lock(); - } - Connection *conn = getConnection(sock); - if(conn && conn->rxsz) { - float max = conn->type == SOCK_STREAM ? (float)DEFAULT_TCP_RX_BUF_SZ : (float)DEFAULT_UDP_RX_BUF_SZ; - long n = _phy.streamSend(conn->sock, conn->rxbuf, ZT_MAX_MTU); - int payload_sz, addr_sz_offset = sizeof(struct sockaddr_storage); - memcpy(&payload_sz, conn->rxbuf + addr_sz_offset, sizeof(int)); // OPT: - // extract address - struct sockaddr_storage addr; - memcpy(&addr, conn->rxbuf, addr_sz_offset); - - if(n == ZT_MAX_MTU) { - if(conn->rxsz-n > 0) // If more remains on buffer - memcpy(conn->rxbuf, conn->rxbuf+ZT_MAX_MTU, conn->rxsz - ZT_MAX_MTU); - conn->rxsz -= ZT_MAX_MTU; - // DGRAM - if(conn->type==SOCK_DGRAM){ - _phy.setNotifyWritable(conn->sock, false); - - #if DEBUG_LEVEL >= MSG_TRANSFER - struct sockaddr_in * addr_in2 = (struct sockaddr_in *)&addr; - int port = lwipstack->__lwip_ntohs(addr_in2->sin_port); - int ip = addr_in2->sin_addr.s_addr; - unsigned char d[4]; - d[0] = ip & 0xFF; - d[1] = (ip >> 8) & 0xFF; - d[2] = (ip >> 16) & 0xFF; - d[3] = (ip >> 24) & 0xFF; - DEBUG_TRANS("UDP RX <--- :: {TX: %.3f%%, RX: %d, sock=%p} :: payload = %d bytes (src_addr=%d.%d.%d.%d:%d)", - (float)conn->txsz / max, conn->rxsz/* / max*/, (void*)conn->sock, payload_sz, d[0],d[1],d[2],d[3], port); - #endif - } - // STREAM - //DEBUG_INFO("phyOnUnixWritable(): tid = %d\n", pthread_mach_thread_np(pthread_self())); - if(conn->type==SOCK_STREAM) { // Only acknolwedge receipt of TCP packets - lwipstack->__tcp_recved(conn->TCP_pcb, n); - DEBUG_TRANS("TCP RX <--- :: {TX: %.3f%%, RX: %.3f%%, sock=%p} :: %ld bytes", - (float)conn->txsz / max, (float)conn->rxsz / max, (void*)conn->sock, n); - } - } else { - DEBUG_EXTRA(" errno = %d, rxsz = %d", errno, conn->rxsz); - _phy.setNotifyWritable(conn->sock, false); - } - } - // If everything on the buffer has been written - if(conn->rxsz == 0) { - _phy.setNotifyWritable(conn->sock, false); - } - if(!lwip_invoked) { - _tcpconns_m.unlock(); - _rx_buf_m.unlock(); - } + lwip_handleRead(this, sock, uptr, lwip_invoked); #endif } @@ -1329,8 +457,7 @@ void NetconEthernetTap::phyOnUnixData(PhySocket *sock, void **uptr, void *data, rpcSock = sockdata.first; buf = (unsigned char*)sockdata.second; unloadRPC(buf, pid, tid, timestamp, CANARY, cmd, payload); - //DEBUG_EXTRA(" RPC: sock=%p, (pid=%d, tid=%d, timestamp=%s, cmd=%d)", (void*)&sock, pid, tid, timestamp, cmd); - + // DEBUG_EXTRA(" RPC: sock=%p, (pid=%d, tid=%d, timestamp=%s, cmd=%d)", (void*)&sock, pid, tid, timestamp, cmd); switch(cmd) { case RPC_BIND: DEBUG_INFO("RPC_BIND, sock=%p", (void*)&sock); @@ -1373,318 +500,6 @@ void NetconEthernetTap::phyOnUnixData(PhySocket *sock, void **uptr, void *data, } } -int NetconEthernetTap::sendReturnValue(PhySocket *sock, int retval, int _errno = 0){ - DEBUG_EXTRA("sock=%p", (void*)&sock); - return sendReturnValue(_phy.getDescriptor(sock), retval, _errno); -} -int NetconEthernetTap::sendReturnValue(int fd, int retval, int _errno = 0) -{ -//#if !defined(USE_SOCKS_PROXY) - DEBUG_EXTRA("fd=%d, retval=%d, errno=%d", fd, retval, _errno); - int sz = sizeof(char) + sizeof(retval) + sizeof(errno); - char retmsg[sz]; - memset(&retmsg, 0, sizeof(retmsg)); - retmsg[0]=RPC_RETVAL; - memcpy(&retmsg[1], &retval, sizeof(retval)); - memcpy(&retmsg[1]+sizeof(retval), &_errno, sizeof(_errno)); - return write(fd, &retmsg, sz); -//#else -// return 1; -//#endif -} - -void NetconEthernetTap::unloadRPC(void *data, pid_t &pid, pid_t &tid, - char (timestamp[RPC_TIMESTAMP_SZ]), char (CANARY[sizeof(uint64_t)]), char &cmd, void* &payload) -{ - unsigned char *buf = (unsigned char*)data; - memcpy(&pid, &buf[IDX_PID], sizeof(pid_t)); - memcpy(&tid, &buf[IDX_TID], sizeof(pid_t)); - memcpy(timestamp, &buf[IDX_TIME], RPC_TIMESTAMP_SZ); - memcpy(&cmd, &buf[IDX_PAYLOAD], sizeof(char)); - memcpy(CANARY, &buf[IDX_PAYLOAD+1], CANARY_SZ); -} - -/*------------------------------------------------------------------------------ ---------------------------------- LWIP callbacks ------------------------------- -------------------------------------------------------------------------------*/ - -#if defined(SDK_LWIP) - -err_t NetconEthernetTap::nc_accept(void *arg, struct tcp_pcb *newPCB, err_t err) -{ - DEBUG_ATTN("pcb=%p", (void*)&newPCB); - Larg *l = (Larg*)arg; - Mutex::Lock _l(l->tap->_tcpconns_m); - Connection *conn = l->conn; - NetconEthernetTap *tap = l->tap; - - if(!conn) - return -1; - if(conn->type==SOCK_DGRAM) - return -1; - if(!conn->sock) - return -1; - int fd = tap->_phy.getDescriptor(conn->sock); - - if(conn) { - // create new socketpair - ZT_PHY_SOCKFD_TYPE fds[2]; - if(socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) { - if(errno < 0) { - l->tap->sendReturnValue(conn, -1, errno); - DEBUG_ERROR("unable to create socketpair"); - return ERR_MEM; - } - } - // create and populate new Connection - Connection *newTcpConn = new Connection(); - l->tap->_Connections.push_back(newTcpConn); - newTcpConn->TCP_pcb = newPCB; - newTcpConn->type = SOCK_STREAM; - newTcpConn->sock = tap->_phy.wrapSocket(fds[0], newTcpConn); - - if(sock_fd_write(fd, fds[1]) < 0) - return -1; - tap->lwipstack->__tcp_arg(newPCB, new Larg(tap, newTcpConn)); - tap->lwipstack->__tcp_recv(newPCB, nc_recved); - tap->lwipstack->__tcp_err(newPCB, nc_err); - tap->lwipstack->__tcp_sent(newPCB, nc_sent); - tap->lwipstack->__tcp_poll(newPCB, nc_poll, 1); - if(conn->TCP_pcb->state == LISTEN) - return ERR_OK; - tcp_accepted(conn->TCP_pcb); // Let lwIP know that it can queue additional incoming connections - return ERR_OK; - } else - DEBUG_ERROR("can't locate Connection object for PCB"); - return -1; -} - -void NetconEthernetTap::nc_udp_recved(void * arg, struct udp_pcb * upcb, struct pbuf * p, ip_addr_t * addr, u16_t port) -{ - Larg *l = (Larg*)arg; - DEBUG_EXTRA("nc_udp_recved(conn=%p,pcb=%p,port=%d)\n", (void*)&(l->conn), (void*)&upcb, port); - /* - int tot = 0; - unsigned char *addr_pos, *sz_pos, *payload_pos; - struct pbuf* q = p; - struct sockaddr_storage sockaddr_big; - -#if defined(LWIP_IPV6) - struct sockaddr_in6 addr_in; - addr_in.sin6_addr.s6_addr = addr->u_addr.ip6.addr; - addr_in.sin6_port = port; -#else // ipv4 - struct sockaddr_in *addr_in = (struct sockaddr_in *)&sockaddr_big; - addr_in->sin_addr.s_addr = addr->addr; - addr_in->sin_port = port; -#endif - - // TODO: Finish address treatment - - Mutex::Lock _l2(l->tap->_rx_buf_m); - // Cycle through pbufs and write them to the RX buffer - // The RX "buffer" will be emptied via phyOnUnixWritable() - if(p) { - // Intra-API "packetization" scheme: [addr_len|addr|payload_len|payload] - if(l->conn->rxsz == DEFAULT_UDP_RX_BUF_SZ) { // if UDP buffer full - DEBUG_INFO("UDP RX buffer full. Discarding oldest payload segment"); - memmove(l->conn->rxbuf, l->conn->rxbuf + ZT_MAX_MTU, DEFAULT_UDP_RX_BUF_SZ - ZT_MAX_MTU); - addr_pos = l->conn->rxbuf + (DEFAULT_UDP_RX_BUF_SZ - ZT_MAX_MTU); // TODO: - sz_pos = addr_pos + sizeof(struct sockaddr_storage); - l->conn->rxsz -= ZT_MAX_MTU; - } - else { - addr_pos = l->conn->rxbuf + l->conn->rxsz; // where we'll prepend the size of the address - sz_pos = addr_pos + sizeof(struct sockaddr_storage); - } - payload_pos = addr_pos + sizeof(struct sockaddr_storage) + sizeof(tot); // where we'll write the payload - // write remote host address - memcpy(addr_pos, &addr_in, sizeof(struct sockaddr_storage)); - } - while(p != NULL) { - if(p->len <= 0) - break; - int len = p->len; - memcpy(payload_pos, p->payload, len); - payload_pos = payload_pos + len; - p = p->next; - tot += len; - } - if(tot) { - l->conn->rxsz += ZT_MAX_MTU; - memcpy(sz_pos, &tot, sizeof(tot)); - //DEBUG_EXTRA(" nc_udp_recved(): data_len = %d, rxsz = %d, addr_info_len = %d\n", - // tot, l->conn->rxsz, sizeof(u32_t) + sizeof(u16_t)); - l->tap->phyOnUnixWritable(l->conn->sock, NULL, true); - l->tap->_phy.setNotifyWritable(l->conn->sock, true); - } - l->tap->lwipstack->__pbuf_free(q); - */ -} - - -err_t NetconEthernetTap::nc_recved(void *arg, struct tcp_pcb *PCB, struct pbuf *p, err_t err) -{ - Larg *l = (Larg*)arg; - DEBUG_EXTRA("conn=%p, pcb=%p", (void*)&(l->conn), (void*)&PCB); - int tot = 0; - struct pbuf* q = p; - Mutex::Lock _l(l->tap->_tcpconns_m); - - if(!l->conn) { - DEBUG_ERROR("no connection"); - return ERR_OK; - } - if(p == NULL) { - if(l->conn->TCP_pcb->state == CLOSE_WAIT){ - l->tap->closeConnection(l->conn->sock); - return ERR_ABRT; - } - return err; - } - Mutex::Lock _l2(l->tap->_rx_buf_m); - // Cycle through pbufs and write them to the RX buffer - // The RX buffer will be emptied via phyOnUnixWritable() - while(p != NULL) { - if(p->len <= 0) - break; - int avail = DEFAULT_TCP_RX_BUF_SZ - l->conn->rxsz; - int len = p->len; - if(avail < len) - DEBUG_ERROR("not enough room (%d bytes) on RX buffer", avail); - memcpy(l->conn->rxbuf + (l->conn->rxsz), p->payload, len); - l->conn->rxsz += len; - p = p->next; - tot += len; - } - if(tot) { - //#if defined(USE_SOCKS_PROXY) - // l->tap->phyOnTcpWritable(l->conn->sock, NULL, true); - //#else - l->tap->phyOnUnixWritable(l->conn->sock, NULL, true); - //#endif - } - l->tap->lwipstack->__pbuf_free(q); - return ERR_OK; -} - -err_t NetconEthernetTap::nc_sent(void* arg, struct tcp_pcb *PCB, u16_t len) -{ - DEBUG_EXTRA("pcb=%p", (void*)&PCB); - Larg *l = (Larg*)arg; - Mutex::Lock _l(l->tap->_tcpconns_m); - if(l->conn->probation && l->conn->txsz == 0){ - l->conn->probation = false; // TX buffer now empty, removing from probation - } - if(l && l->conn && len && !l->conn->probation) { - int softmax = l->conn->type == SOCK_STREAM ? DEFAULT_TCP_TX_BUF_SZ : DEFAULT_UDP_TX_BUF_SZ; - if(l->conn->txsz < softmax) { - l->tap->_phy.setNotifyReadable(l->conn->sock, true); - l->tap->_phy.whack(); - } - } - return ERR_OK; -} - -err_t NetconEthernetTap::nc_connected_proxy(void *arg, struct tcp_pcb *PCB, err_t err) -{ - DEBUG_INFO("pcb=%p", (void*)&PCB); - return ERR_OK; -} - -err_t NetconEthernetTap::nc_connected(void *arg, struct tcp_pcb *PCB, err_t err) -{ - DEBUG_ATTN("pcb=%p", (void*)&PCB); - Larg *l = (Larg*)arg; - if(l && l->conn) - l->tap->sendReturnValue(l->tap->_phy.getDescriptor(l->conn->rpcSock), ERR_OK); - return ERR_OK; -} - -err_t NetconEthernetTap::nc_poll(void* arg, struct tcp_pcb *PCB) -{ - return ERR_OK; -} - -void NetconEthernetTap::nc_err(void *arg, err_t err) -{ - DEBUG_ERROR("err=%d", err); - Larg *l = (Larg*)arg; - Mutex::Lock _l(l->tap->_tcpconns_m); - - if(!l->conn) - DEBUG_ERROR("conn==NULL"); - int fd = l->tap->_phy.getDescriptor(l->conn->sock); - - switch(err) - { - case ERR_MEM: - DEBUG_ERROR("ERR_MEM->ENOMEM"); - l->tap->sendReturnValue(fd, -1, ENOMEM); - break; - case ERR_BUF: - DEBUG_ERROR("ERR_BUF->ENOBUFS"); - l->tap->sendReturnValue(fd, -1, ENOBUFS); - break; - case ERR_TIMEOUT: - DEBUG_ERROR("ERR_TIMEOUT->ETIMEDOUT"); - l->tap->sendReturnValue(fd, -1, ETIMEDOUT); - break; - case ERR_RTE: - DEBUG_ERROR("ERR_RTE->ENETUNREACH"); - l->tap->sendReturnValue(fd, -1, ENETUNREACH); - break; - case ERR_INPROGRESS: - DEBUG_ERROR("ERR_INPROGRESS->EINPROGRESS"); - l->tap->sendReturnValue(fd, -1, EINPROGRESS); - break; - case ERR_VAL: - DEBUG_ERROR("ERR_VAL->EINVAL"); - l->tap->sendReturnValue(fd, -1, EINVAL); - break; - case ERR_WOULDBLOCK: - DEBUG_ERROR("ERR_WOULDBLOCK->EWOULDBLOCK"); - l->tap->sendReturnValue(fd, -1, EWOULDBLOCK); - break; - case ERR_USE: - DEBUG_ERROR("ERR_USE->EADDRINUSE"); - l->tap->sendReturnValue(fd, -1, EADDRINUSE); - break; - case ERR_ISCONN: - DEBUG_ERROR("ERR_ISCONN->EISCONN"); - l->tap->sendReturnValue(fd, -1, EISCONN); - break; - case ERR_ABRT: - DEBUG_ERROR("ERR_ABRT->ECONNREFUSED"); - l->tap->sendReturnValue(fd, -1, ECONNREFUSED); - break; - - // TODO: Below are errors which don't have a standard errno correlate - - case ERR_RST: - l->tap->sendReturnValue(fd, -1, -1); - break; - case ERR_CLSD: - l->tap->sendReturnValue(fd, -1, -1); - break; - case ERR_CONN: - l->tap->sendReturnValue(fd, -1, -1); - break; - case ERR_ARG: - l->tap->sendReturnValue(fd, -1, -1); - break; - case ERR_IF: - l->tap->sendReturnValue(fd, -1, -1); - break; - default: - break; - } - DEBUG_ERROR(" closing connection"); - l->tap->closeConnection(l->conn); -} - -#endif // SDK_LWIP - /*------------------------------------------------------------------------------ ----------------------------- RPC Handler functions ---------------------------- ------------------------------------------------------------------------------*/ @@ -1716,6 +531,44 @@ void NetconEthernetTap::handleGetpeername(PhySocket *sock, PhySocket *rpcSock, v } write(_phy.getDescriptor(rpcSock), conn->peer_addr, sizeof(struct sockaddr_storage)); } + +Connection * NetconEthernetTap::handleSocket(PhySocket *sock, void **uptr, struct socket_st* socket_rpc) +{ + DEBUG_ATTN("sock=%p, sock_type=%d", (void*)&sock, socket_rpc->socket_type); + // picoTCP + #if defined(SDK_PICOTCP) + return pico_handleSocket(sock, uptr, socket_rpc); + #endif + // lwIP + #if defined(SDK_LWIP) + return lwip_handleSocket(this, sock, uptr, socket_rpc); + #endif + return NULL; +} + +Connection * NetconEthernetTap::handleSocketProxy(PhySocket *sock, int socket_type) +{ + return NULL; +} +int NetconEthernetTap::handleConnectProxy(PhySocket *sock, struct sockaddr_in *rawAddr) +{ + return -1; +} + +// Connect a stack's PCB/socket/Connection object to a remote host +void NetconEthernetTap::handleConnect(PhySocket *sock, PhySocket *rpcSock, Connection *conn, struct connect_st* connect_rpc) +{ + DEBUG_ATTN("sock=%p", (void*)&sock); + Mutex::Lock _l(_tcpconns_m); + // picoTCP + #if defined(SDK_PICOTCP) + pico_handleConnect(sock, rpcSock, conn, connect_rpc); + #endif + // lwIP + #if defined(SDK_LWIP) + lwip_handleConnect(this, sock, rpcSock, conn, connect_rpc); + #endif +} void NetconEthernetTap::handleBind(PhySocket *sock, PhySocket *rpcSock, void **uptr, struct bind_st *bind_rpc) { @@ -1723,543 +576,42 @@ void NetconEthernetTap::handleBind(PhySocket *sock, PhySocket *rpcSock, void **u if(!_ips.size()) { // We haven't been given an address yet. Binding at this stage is premature DEBUG_ERROR("cannot bind yet. ZT address hasn't been provided"); - sendReturnValue(rpcSock, -1, ENOMEM); + sendReturnValue(_phy.getDescriptor(rpcSock), -1, ENOMEM); return; } - // picoTCP #if defined(SDK_PICOTCP) pico_handleBind(sock,rpcSock,uptr,bind_rpc); #endif - // lwIP #if defined(SDK_LWIP) - ip_addr_t ba; - char addrstr[INET6_ADDRSTRLEN]; - struct sockaddr_in6 *rawAddr = (struct sockaddr_in6 *) &bind_rpc->addr; - struct sockaddr *addr = (struct sockaddr*)rawAddr; - int err, port = lwipstack->__lwip_ntohs(rawAddr->sin6_port); - - // ipv4 - #if defined(SDK_IPV4) - if(addr->sa_family == AF_INET) { - struct sockaddr_in *connaddr = (struct sockaddr_in *)addr; - inet_ntop(AF_INET, &(connaddr->sin_addr), addrstr, INET_ADDRSTRLEN); - sprintf(addrstr, "%s:%d", addrstr, lwipstack->__lwip_ntohs(connaddr->sin_port)); - } - struct sockaddr_in *rawAddr4 = (struct sockaddr_in *) &bind_rpc->addr; - ba = convert_ip(rawAddr4); - port = lwipstack->__lwip_ntohs(rawAddr4->sin_port); - #endif - - // ipv6 - #if defined(SDK_IPV6) - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&bind_rpc->addr; - in6_to_ip6((ip6_addr *)&ba, in6); - if(addr->sa_family == AF_INET6) { - struct sockaddr_in6 *connaddr6 = (struct sockaddr_in6 *)addr; - inet_ntop(AF_INET6, &(connaddr6->sin6_addr), addrstr, INET6_ADDRSTRLEN); - sprintf(addrstr, "%s:%d", addrstr, lwipstack->__lwip_ntohs(connaddr6->sin6_port)); - } - #endif - - Connection *conn = getConnection(sock); - DEBUG_ATTN(" addr=%s, sock=%p, fd=%d", addrstr, (void*)&sock, bind_rpc->fd); - if(conn) { - if(conn->type == SOCK_DGRAM) { - #if defined(__ANDROID__) - err = lwipstack->__udp_bind(conn->UDP_pcb, NULL, port); - #else - err = lwipstack->__udp_bind(conn->UDP_pcb, (const ip_addr_t *)&ba, port); - #endif - if(err == ERR_USE) // port in use - sendReturnValue(rpcSock, -1, EADDRINUSE); - else { - lwipstack->__udp_recv(conn->UDP_pcb, nc_udp_recved, new Larg(this, conn)); - struct sockaddr_in addr_in; - memcpy(&addr_in, &bind_rpc->addr, sizeof(addr_in)); - addr_in.sin_port = Utils::ntoh(conn->UDP_pcb->local_port); // Newly assigned port - memcpy(&conn->local_addr, &addr_in, sizeof(addr_in)); - sendReturnValue(rpcSock, ERR_OK, ERR_OK); // Success - } - return; - } - else if (conn->type == SOCK_STREAM) { - if(conn->TCP_pcb->state == CLOSED){ - err = lwipstack->__tcp_bind(conn->TCP_pcb, (const ip_addr_t *)&ba, port); - if(err != ERR_OK) { - DEBUG_ERROR("err=%d", err); - if(err == ERR_USE) - sendReturnValue(rpcSock, -1, EADDRINUSE); - if(err == ERR_MEM) - sendReturnValue(rpcSock, -1, ENOMEM); - if(err == ERR_BUF) - sendReturnValue(rpcSock, -1, ENOMEM); - } else { - conn->local_addr = (struct sockaddr_storage *) &bind_rpc->addr; - sendReturnValue(rpcSock, ERR_OK, ERR_OK); // Success - } - } else { - DEBUG_ERROR(" ignoring BIND request, PCB (conn=%p, pcb=%p) not in CLOSED state. ", - (void*)&conn, (void*)&conn->TCP_pcb); - sendReturnValue(rpcSock, -1, EINVAL); - } - } - } else { - DEBUG_ERROR(" unable to locate Connection"); - sendReturnValue(rpcSock, -1, EBADF); - } + lwip_handleBind(this, sock, rpcSock, uptr, bind_rpc); #endif } void NetconEthernetTap::handleListen(PhySocket *sock, PhySocket *rpcSock, void **uptr, struct listen_st *listen_rpc) { - DEBUG_ATTN("sock=%p", (void*)&sock); Mutex::Lock _l(_tcpconns_m); - // picoTCP #if defined(SDK_PICOTCP) pico_handleListen(sock, rpcSock, uptr, listen_rpc); #endif - // lwIP #if defined(SDK_LWIP) - Connection *conn = getConnection(sock); - if(conn->type==SOCK_DGRAM) { - // FIX: Added sendReturnValue() call to fix listen() return bug on Android - sendReturnValue(rpcSock, ERR_OK, ERR_OK); - return; - } - if(!conn) { - DEBUG_ERROR(" unable to locate Connection"); - sendReturnValue(rpcSock, -1, EBADF); - return; - } - if(conn->TCP_pcb->state == LISTEN) { - DEBUG_ERROR(" PCB is already in listening state"); - sendReturnValue(rpcSock, ERR_OK, ERR_OK); - return; - } - struct tcp_pcb* listeningPCB; - - #ifdef TCP_LISTEN_BACKLOG - listeningPCB = lwipstack->__tcp_listen_with_backlog(conn->TCP_pcb, listen_rpc->backlog); - #else - listeningPCB = lwipstack->__tcp_listen(conn->pcb); - #endif - if(listeningPCB != NULL) { - conn->TCP_pcb = listeningPCB; - lwipstack->__tcp_accept(listeningPCB, nc_accept); - lwipstack->__tcp_arg(listeningPCB, new Larg(this, conn)); - fcntl(_phy.getDescriptor(conn->sock), F_SETFL, O_NONBLOCK); - conn->listening = true; - sendReturnValue(rpcSock, ERR_OK, ERR_OK); - return; - } - sendReturnValue(rpcSock, -1, -1); - #endif -} - -Connection * NetconEthernetTap::handleSocketProxy(PhySocket *sock, int socket_type) -{ - /* - Connection *conn = getConnection(sock); - if(!conn){ - DEBUG_ERROR("unable to locate Connection object for this PhySocket sock=%p", (void*)&sock); - return NULL; - } - DEBUG_ATTN("sock=%p", (void*)&sock); - struct udp_pcb *new_udp_PCB = NULL; - struct tcp_pcb *new_tcp_PCB = NULL; - if(socket_type == SOCK_DGRAM) { - DEBUG_EXTRA("SOCK_DGRAM"); - Mutex::Lock _l(_tcpconns_m); - new_udp_PCB = lwipstack->__udp_new(); - } - else if(socket_type == SOCK_STREAM) { - DEBUG_EXTRA("SOCK_STREAM"); - Mutex::Lock _l(_tcpconns_m); - new_tcp_PCB = lwipstack->__tcp_new(); - } - if(new_udp_PCB || new_tcp_PCB) { - conn->sock = sock; - conn->type = socket_type; - conn->local_addr = NULL; - conn->peer_addr = NULL; - if(conn->type == SOCK_DGRAM) conn->UDP_pcb = new_udp_PCB; - if(conn->type == SOCK_STREAM) conn->TCP_pcb = new_tcp_PCB; - DEBUG_INFO(" updated sock=%p", (void*)&sock); - return conn; - } - DEBUG_ERROR(" memory not available for new PCB"); - */ - return NULL; -} - -Connection * NetconEthernetTap::handleSocket(PhySocket *sock, void **uptr, struct socket_st* socket_rpc) -{ - DEBUG_ATTN("sock=%p, sock_type=%d", (void*)&sock, socket_rpc->socket_type); - - // picoTCP - #if defined(SDK_PICOTCP) - return pico_handleSocket(sock, uptr, socket_rpc); - #endif - - // lwIP - #if defined(SDK_LWIP) - struct udp_pcb *new_udp_PCB = NULL; - struct tcp_pcb *new_tcp_PCB = NULL; - - if(socket_rpc->socket_type == SOCK_DGRAM) { - DEBUG_EXTRA("SOCK_DGRAM"); - Mutex::Lock _l(_tcpconns_m); - new_udp_PCB = lwipstack->__udp_new(); - } - else if(socket_rpc->socket_type == SOCK_STREAM) { - DEBUG_EXTRA("SOCK_STREAM"); - Mutex::Lock _l(_tcpconns_m); - new_tcp_PCB = lwipstack->__tcp_new(); - } - else if(socket_rpc->socket_type == SOCK_RAW) { - DEBUG_ERROR("SOCK_RAW, not currently supported."); - } - if(new_udp_PCB || new_tcp_PCB) { - Connection * newConn = new Connection(); - *uptr = newConn; - newConn->type = socket_rpc->socket_type; - newConn->sock = sock; - newConn->local_addr = NULL; - newConn->peer_addr = NULL; - if(newConn->type == SOCK_DGRAM) newConn->UDP_pcb = new_udp_PCB; - if(newConn->type == SOCK_STREAM) newConn->TCP_pcb = new_tcp_PCB; - _Connections.push_back(newConn); - return newConn; - } - DEBUG_ERROR(" memory not available for new PCB"); - sendReturnValue(_phy.getDescriptor(sock), -1, ENOMEM); - #endif - - return NULL; -} - -int NetconEthernetTap::handleConnectProxy(PhySocket *sock, struct sockaddr_in *rawAddr) -{ - /* - DEBUG_ATTN("sock=%p", (void*)&sock); - Mutex::Lock _l(_tcpconns_m); - int port = rawAddr->sin_port; - ip_addr_t connAddr = convert_ip(rawAddr); - int err = 0; - - Connection *conn = getConnection(sock); - if(!conn) { - DEBUG_INFO(" unable to locate Connection object for sock=%p", (void*)&sock); - return -1; - } - if(conn->type == SOCK_DGRAM) { - // Generates no network traffic - if((err = lwipstack->__udp_connect(conn->UDP_pcb,&connAddr,port)) < 0) - DEBUG_INFO("error while connecting to with UDP (sock=%p)", (void*)&sock); - lwipstack->__udp_recv(conn->UDP_pcb, nc_udp_recved, new Larg(this, conn)); - errno = ERR_OK; - return 0; - } - if(conn != NULL) { - lwipstack->__tcp_sent(conn->TCP_pcb, nc_sent); - lwipstack->__tcp_recv(conn->TCP_pcb, nc_recved); - lwipstack->__tcp_err(conn->TCP_pcb, nc_err); - lwipstack->__tcp_poll(conn->TCP_pcb, nc_poll, APPLICATION_POLL_FREQ); - lwipstack->__tcp_arg(conn->TCP_pcb, new Larg(this, conn)); - - int ip = rawAddr->sin_addr.s_addr; - unsigned char d[4]; - d[0] = ip & 0xFF; - d[1] = (ip >> 8) & 0xFF; - d[2] = (ip >> 16) & 0xFF; - d[3] = (ip >> 24) & 0xFF; - DEBUG_INFO(" addr=%d.%d.%d.%d:%d", d[0],d[1],d[2],d[3], port); - DEBUG_INFO(" pcb->state=%x", conn->TCP_pcb->state); - if(conn->TCP_pcb->state != CLOSED) { - DEBUG_INFO(" cannot connect using this PCB, PCB!=CLOSED"); - errno = EAGAIN; - return -1; - } - if((err = lwipstack->__tcp_connect(conn->TCP_pcb,&connAddr,port,nc_connected_proxy)) < 0) - { - if(err == ERR_ISCONN) { - errno = EISCONN; // Already in connected state - return -1; - } if(err == ERR_USE) { - errno = EADDRINUSE; // Already in use - return -1; - } if(err == ERR_VAL) { - errno = EINVAL; // Invalid ipaddress parameter - return -1; - } if(err == ERR_RTE) { - errno = ENETUNREACH; // No route to host - return -1; - } if(err == ERR_BUF) { - errno = EAGAIN; // No more ports available - return -1; - } - if(err == ERR_MEM) { - // Can occur for the following reasons: tcp_enqueue_flags() - - // 1) tcp_enqueue_flags is always called with either SYN or FIN in flags. - // We need one available snd_buf byte to do that. - // This means we can't send FIN while snd_buf==0. A better fix would be to - // not include SYN and FIN sequence numbers in the snd_buf count. - - // 2) Cannot allocate new pbuf - // 3) Cannot allocate new TCP segment - - errno = EAGAIN; // TODO: Doesn't describe the problem well, but closest match - return -1; - } - // We should only return a value if failure happens immediately - // Otherwise, we still need to wait for a callback from lwIP. - // - This is because an ERR_OK from tcp_connect() only verifies - // that the SYN packet was enqueued onto the stack properly, - // that's it! - // - Most instances of a retval for a connect() should happen - // in the nc_connect() and nc_err() callbacks! - DEBUG_ERROR(" unable to connect"); - errno = EAGAIN; - return -1; - } - // Everything seems to be ok, but we don't have enough info to retval - conn->listening=true; - return 0; - } else { - DEBUG_ERROR(" could not locate PCB based on application-provided fd"); - errno = EBADF; - return -1; - } - */ - return -1; -} - -void NetconEthernetTap::handleConnect(PhySocket *sock, PhySocket *rpcSock, Connection *conn, struct connect_st* connect_rpc) -{ - DEBUG_ATTN("sock=%p", (void*)&sock); - Mutex::Lock _l(_tcpconns_m); - - // picoTCP - #if defined(SDK_PICOTCP) - pico_handleConnect(sock, rpcSock, conn, connect_rpc); - #endif - - // lwIP - #if defined(SDK_LWIP) - - ip_addr_t ba; - char addrstr[INET6_ADDRSTRLEN]; - struct sockaddr_in6 *rawAddr = (struct sockaddr_in6 *) &connect_rpc->addr; - struct sockaddr *addr = (struct sockaddr*)rawAddr; - int err, port = lwipstack->__lwip_ntohs(rawAddr->sin6_port); - - // ipv4 - #if defined(SDK_IPV4) - if(addr->sa_family == AF_INET) { - struct sockaddr_in *connaddr = (struct sockaddr_in *)addr; - inet_ntop(AF_INET, &(connaddr->sin_addr), addrstr, INET_ADDRSTRLEN); - sprintf(addrstr, "%s:%d", addrstr, lwipstack->__lwip_ntohs(connaddr->sin_port)); - } - struct sockaddr_in *rawAddr4 = (struct sockaddr_in *) &connect_rpc->addr; - ba = convert_ip(rawAddr4); - port = lwipstack->__lwip_ntohs(rawAddr4->sin_port); - #endif - - // ipv6 - #if defined(SDK_IPV6) - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&connect_rpc->addr; - in6_to_ip6((ip6_addr *)&ba, in6); - - if(addr->sa_family == AF_INET6) { - struct sockaddr_in6 *connaddr6 = (struct sockaddr_in6 *)addr; - inet_ntop(AF_INET6, &(connaddr6->sin6_addr), addrstr, INET6_ADDRSTRLEN); - sprintf(addrstr, "%s:%d", addrstr, lwipstack->__lwip_ntohs(connaddr6->sin6_port)); - } - #endif - DEBUG_INFO("addr=%s", addrstr); - - if(conn->type == SOCK_DGRAM) { - // Generates no network traffic - if((err = lwipstack->__udp_connect(conn->UDP_pcb,(ip_addr_t *)&ba,port)) < 0) - DEBUG_ERROR("error while connecting to with UDP"); - lwipstack->__udp_recv(conn->UDP_pcb, nc_udp_recved, new Larg(this, conn)); - sendReturnValue(rpcSock, 0, ERR_OK); - return; - } - if(conn != NULL) { - lwipstack->__tcp_sent(conn->TCP_pcb, nc_sent); - lwipstack->__tcp_recv(conn->TCP_pcb, nc_recved); - lwipstack->__tcp_err(conn->TCP_pcb, nc_err); - lwipstack->__tcp_poll(conn->TCP_pcb, nc_poll, APPLICATION_POLL_FREQ); - lwipstack->__tcp_arg(conn->TCP_pcb, new Larg(this, conn)); - - DEBUG_EXTRA(" pcb->state=%x", conn->TCP_pcb->state); - if(conn->TCP_pcb->state != CLOSED) { - DEBUG_INFO(" cannot connect using this PCB, PCB!=CLOSED"); - sendReturnValue(rpcSock, -1, EAGAIN); - return; - } - if((err = lwipstack->__tcp_connect(conn->TCP_pcb,&ba,port,nc_connected)) < 0) - { - if(err == ERR_ISCONN) { - sendReturnValue(rpcSock, -1, EISCONN); // Already in connected state - return; - } if(err == ERR_USE) { - sendReturnValue(rpcSock, -1, EADDRINUSE); // Already in use - return; - } if(err == ERR_VAL) { - sendReturnValue(rpcSock, -1, EINVAL); // Invalid ipaddress parameter - return; - } if(err == ERR_RTE) { - sendReturnValue(rpcSock, -1, ENETUNREACH); // No route to host - return; - } if(err == ERR_BUF) { - sendReturnValue(rpcSock, -1, EAGAIN); // No more ports available - return; - } - if(err == ERR_MEM) { - sendReturnValue(rpcSock, -1, EAGAIN); // TODO: Doesn't describe the problem well, but closest match - return; - } - - // We should only return a value if failure happens immediately - // Otherwise, we still need to wait for a callback from lwIP. - // - This is because an ERR_OK from tcp_connect() only verifies - // that the SYN packet was enqueued onto the stack properly, - // that's it! - // - Most instances of a retval for a connect() should happen - // in the nc_connect() and nc_err() callbacks! - DEBUG_ERROR(" unable to connect"); - sendReturnValue(rpcSock, -1, EAGAIN); - } - // Everything seems to be ok, but we don't have enough info to retval - conn->listening=true; - conn->rpcSock=rpcSock; // used for return value from lwip CB - } - else { - DEBUG_ERROR(" could not locate PCB based on application-provided fd"); - sendReturnValue(rpcSock, -1, EBADF); - } + lwip_handleListen(this, sock, rpcSock, uptr, listen_rpc); #endif } +// Write to the network stack (and thus out onto the network) void NetconEthernetTap::handleWrite(Connection *conn) { - DEBUG_EXTRA("conn=%p", (void*)&conn); - // picoTCP #if defined(SDK_PICOTCP) pico_handleWrite(conn); #endif - // lwIP #if defined(SDK_LWIP) - if(!conn || (!conn->TCP_pcb && !conn->UDP_pcb)) { - DEBUG_ERROR(" invalid connection"); - return; - } - if(conn->type == SOCK_DGRAM) { - if(!conn->UDP_pcb) { - DEBUG_ERROR(" invalid UDP_pcb, type=SOCK_DGRAM"); - return; - } - // TODO: Packet re-assembly hasn't yet been tested with lwIP so UDP packets are limited to MTU-sized chunks - int udp_trans_len = conn->txsz < ZT_UDP_DEFAULT_PAYLOAD_MTU ? conn->txsz : ZT_UDP_DEFAULT_PAYLOAD_MTU; - - DEBUG_EXTRA(" allocating pbuf chain of size=%d for UDP packet, txsz=%d", udp_trans_len, conn->txsz); - struct pbuf * pb = lwipstack->__pbuf_alloc(PBUF_TRANSPORT, udp_trans_len, PBUF_POOL); - if(!pb){ - DEBUG_ERROR(" unable to allocate new pbuf of size=%d", conn->txsz); - return; - } - memcpy(pb->payload, conn->txbuf, udp_trans_len); - int err = lwipstack->__udp_send(conn->UDP_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); - } else { - // Success - int buf_remaining = (conn->txsz)-udp_trans_len; - if(buf_remaining) - memmove(&conn->txbuf, (conn->txbuf+udp_trans_len), buf_remaining); - conn->txsz -= udp_trans_len; - - #if DEBUG_LEVEL >= MSG_TRANSFER - struct sockaddr_in * addr_in2 = (struct sockaddr_in *)conn->peer_addr; - int port = lwipstack->__lwip_ntohs(addr_in2->sin_port); - int ip = addr_in2->sin_addr.s_addr; - unsigned char d[4]; - d[0] = ip & 0xFF; - d[1] = (ip >> 8) & 0xFF; - d[2] = (ip >> 16) & 0xFF; - d[3] = (ip >> 24) & 0xFF; - DEBUG_TRANS("[UDP TX] ---> :: {TX: ------, RX: ------, sock=%p} :: %d bytes (dest_addr=%d.%d.%d.%d:%d)", - (void*)conn->sock, udp_trans_len, d[0], d[1], d[2], d[3], port); - #endif - } - lwipstack->__pbuf_free(pb); - return; - } - else if(conn->type == SOCK_STREAM) { - if(!conn->TCP_pcb) { - DEBUG_ERROR(" invalid TCP_pcb, type=SOCK_STREAM"); - return; - } - // How much we are currently allowed to write to the connection - int sndbuf = conn->TCP_pcb->snd_buf; - int err, sz, r; - - if(!sndbuf) { - // PCB send buffer is full, turn off readability notifications for the - // corresponding PhySocket until nc_sent() is called and confirms that there is - // now space on the buffer - if(!conn->probation) { - DEBUG_ERROR(" LWIP stack is full, sndbuf == 0"); - _phy.setNotifyReadable(conn->sock, false); - conn->probation = true; - } - return; - } - if(conn->txsz <= 0) - return; // Nothing to write - if(!conn->listening) - lwipstack->__tcp_output(conn->TCP_pcb); - - if(conn->sock) { - r = conn->txsz < sndbuf ? conn->txsz : 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 = lwipstack->__tcp_write(conn->TCP_pcb, &conn->txbuf, r, TCP_WRITE_FLAG_COPY); - lwipstack->__tcp_output(conn->TCP_pcb); - if(err != ERR_OK) { - DEBUG_ERROR(" error while writing to PCB, err=%d", err); - if(err == -1) - DEBUG_ERROR("out of memory"); - return; - } else { - // adjust buffer - sz = (conn->txsz)-r; - if(sz) - memmove(&conn->txbuf, (conn->txbuf+r), sz); - conn->txsz -= r; - int max = conn->type == SOCK_STREAM ? DEFAULT_TCP_TX_BUF_SZ : DEFAULT_UDP_TX_BUF_SZ; - DEBUG_TRANS("[TCP TX] ---> :: {TX: %.3f%%, RX: %.3f%%, sock=%p} :: %d bytes", - (float)conn->txsz / (float)max, (float)conn->rxsz / max, (void*)&conn->sock, r); - return; - } - } - } - } + lwip_handleWrite(this, conn); #endif } diff --git a/src/tap.hpp b/src/tap.hpp index 9f9aa8f..5e7def6 100644 --- a/src/tap.hpp +++ b/src/tap.hpp @@ -56,11 +56,6 @@ #include "pico_protocol.h" #endif -#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]) - // lwIP structs struct tcp_pcb; struct udp_pcb; @@ -75,6 +70,9 @@ struct accept_st; namespace ZeroTier { + extern struct pico_device picodev; + extern NetconEthernetTap *picotap; + class NetconEthernetTap; class LWIPStack; @@ -142,7 +140,8 @@ namespace ZeroTier { void setFriendlyName(const char *friendlyName); void scanMulticastGroups(std::vector &added,std::vector &removed); - // SIP- + int sendReturnValue(int fd, int retval, int _errno); + void unloadRPC(void *data, pid_t &pid, pid_t &tid, char (timestamp[RPC_TIMESTAMP_SZ]), char (CANARY[sizeof(uint64_t)]), char &cmd, void* &payload); void threadMain() throw(); @@ -166,19 +165,13 @@ namespace ZeroTier { std::string _homePath; // lwIP - void lwIP_loop(); - void lwIP_rx(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); - void lwIP_init_interface(const InetAddress &ip); #if defined(SDK_LWIP) lwIP_stack *lwipstack; #endif - // jip - void jip_loop(); - void jip_rx(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); - void jip_init_interface(const InetAddress &ip); - jip_stack *jipstack; - + #if defined(SDK_JIP) + jip_stack *jipstack; + #endif // picoTCP #if defined(SDK_PICOTCP) unsigned char pico_frame_rxbuf[MAX_PICO_FRAME_RX_BUF_SZ]; @@ -187,124 +180,6 @@ namespace ZeroTier { picoTCP_stack *picostack; #endif - // LWIP callbacks - // NOTE: these are called from within LWIP, meaning that lwipstack->_lock is ALREADY - // locked in this case! - - /* - * Callback from LWIP for when a connection has been accepted and the PCB has been - * put into an ACCEPT state. - * - * A socketpair is created, one end is kept and wrapped into a PhySocket object - * for use in the main ZT I/O loop, and one end is sent to the client. The client - * is then required to tell the service what new file descriptor it has allocated - * for this connection. After the mapping is complete, the accepted socket can be - * used. - * - * @param associated service state object - * @param newly allocated PCB - * @param error code - * @return ERR_OK if everything is ok, -1 otherwise - * - * i := should be implemented in intercept lib - * I := is implemented in intercept lib - * X := is implemented in service - * ? := required treatment Unknown - * - := Not needed - * - * [ ] EAGAIN or EWOULDBLOCK - The socket is marked nonblocking and no connections are present - * to be accepted. POSIX.1-2001 allows either error to be returned for - * this case, and does not require these constants to have the same value, - * so a portable application should check for both possibilities. - * [I] EBADF - The descriptor is invalid. - * [I] ECONNABORTED - A connection has been aborted. - * [i] EFAULT - The addr argument is not in a writable part of the user address space. - * [-] EINTR - The system call was interrupted by a signal that was caught before a valid connection arrived; see signal(7). - * [I] EINVAL - Socket is not listening for connections, or addrlen is invalid (e.g., is negative). - * [I] EINVAL - (accept4()) invalid value in flags. - * [I] EMFILE - The per-process limit of open file descriptors has been reached. - * [ ] ENFILE - The system limit on the total number of open files has been reached. - * [ ] ENOBUFS, ENOMEM - Not enough free memory. This often means that the memory allocation is - * limited by the socket buffer limits, not by the system memory. - * [I] ENOTSOCK - The descriptor references a file, not a socket. - * [I] EOPNOTSUPP - The referenced socket is not of type SOCK_STREAM. - * [ ] EPROTO - Protocol error. - * - */ - static err_t nc_accept(void *arg, struct tcp_pcb *newPCB, err_t err); - - /* - * Callback from LWIP for when data is available to be read from the network. - * - * Data is in the form of a linked list of struct pbufs, it is then recombined and - * send to the client over the associated unix socket. - * - * @param associated service state object - * @param allocated PCB - * @param chain of pbufs - * @param error code - * @return ERR_OK if everything is ok, -1 otherwise - * - */ - static err_t nc_recved(void *arg, struct tcp_pcb *PCB, struct pbuf *p, err_t err); - static err_t nc_recved_proxy(void *arg, struct tcp_pcb *PCB, struct pbuf *p, err_t err); - static void nc_udp_recved(void * arg, struct udp_pcb * upcb, struct pbuf * p, ip_addr_t * addr, u16_t port); - - - /* - * Callback from LWIP when an internal error is associtated with the given (arg) - * - * Since the PCB related to this error might no longer exist, only its perviously - * associated (arg) is provided to us. - * - * @param associated service state object - * @param error code - * - */ - static void nc_err(void *arg, err_t err); - - /* - * Callback from LWIP to do whatever work we might need to do. - * - * @param associated service state object - * @param PCB we're polling on - * @return ERR_OK if everything is ok, -1 otherwise - * - */ - static err_t nc_poll(void* arg, struct tcp_pcb *PCB); - - /* - * Callback from LWIP to signal that 'len' bytes have successfully been sent. - * As a result, we should put our socket back into a notify-on-readability state - * since there is now room on the PCB buffer to write to. - * - * NOTE: This could be used to track the amount of data sent by a connection. - * - * @param associated service state object - * @param relevant PCB - * @param length of data sent - * @return ERR_OK if everything is ok, -1 otherwise - * - */ - static err_t nc_sent(void *arg, struct tcp_pcb *PCB, u16_t len); - - /* - * Callback from LWIP which sends a return value to the client to signal that - * a connection was established for this PCB - * - * @param associated service state object - * @param relevant PCB - * @param error code - * @return ERR_OK if everything is ok, -1 otherwise - * - */ - static err_t nc_connected(void *arg, struct tcp_pcb *PCB, err_t err); - static err_t nc_connected_proxy(void *arg, struct tcp_pcb *PCB, err_t err); - - - //static void nc_close(struct tcp_pcb *PCB); - //static err_t nc_send(struct tcp_pcb *PCB); - /* * Handles an RPC to bind an LWIP PCB to a given address and port * @@ -443,18 +318,6 @@ namespace ZeroTier { */ void handleWrite(Connection *conn); - /* - * Sends a return value to the intercepted application - */ - int sendReturnValue(PhySocket *sock, int retval, int _errno); - int sendReturnValue(int fd, int retval, int _errno); - - /* - * Unpacks the buffer from an RPC command - */ - void unloadRPC(void *data, pid_t &pid, pid_t &tid, - char (timestamp[RPC_TIMESTAMP_SZ]), char (magic[sizeof(uint64_t)]), char &cmd, void* &payload); - // Unused -- no UDP or TCP from this thread/Phy<> void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *local_address, const struct sockaddr *from,void *data,unsigned long len); void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success); @@ -496,22 +359,6 @@ namespace ZeroTier { */ void closeConnection(PhySocket *sock); - - #if defined(SDK_IPV4) - ip_addr_t convert_ip(struct sockaddr_in * addr) - { - ip_addr_t conn_addr; - 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(&conn_addr, a,b,c,d); - - return conn_addr; - } - #endif - Phy _phy; PhySocket *_unixListenSocket;