diff --git a/examples/cpp/simple.cpp b/examples/cpp/simple.cpp deleted file mode 100644 index 82148b8..0000000 --- a/examples/cpp/simple.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// Comprehensive stress test for socket-like API - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "libzt.h" - -int main() -{ - char *nwid = (char *)"e5cd7a9e1c0fd272"; - - // Get ZeroTier core version - char ver[ZT_VER_STR_LEN]; - zts_core_version(ver); - printf("zts_core_version = %s\n", ver); - - // Get SDK version - zts_sdk_version(ver); - printf("zts_sdk_version = %s\n", ver); - - // Spawns a couple threads to support ZeroTier core, userspace network stack, and generates ID in ./zt - zts_start("./zt"); - - // Print the device/app ID (this is also the ID you'd see in ZeroTier Central) - char id[ZT_ID_LEN + 1]; - zts_get_device_id(id); - printf("id = %s\n", id); - - // Get the home path of this ZeroTier instance, where we store identity keys, conf files, etc - char homePath[ZT_HOME_PATH_MAX_LEN+1]; - zts_get_homepath(homePath, ZT_HOME_PATH_MAX_LEN); - printf("homePath = %s\n", homePath); - - // Wait for ZeroTier service to start - while(!zts_running()) { - printf("wating for service to start\n"); - sleep(1); - } - - // Join a network - zts_join(nwid); - - // Wait for ZeroTier service to issue an address to the device on the given network - while(!zts_has_address("e5cd7a9e1c0fd272")) { - printf("waiting for service to issue an address\n"); - sleep(1); - } - // Begin Socket API calls - - int err; - int sockfd; - int port = 7878; - struct sockaddr_in addr; - - // socket() - if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) - printf("error creating ZeroTier socket"); - else - printf("sockfd = %d\n", sockfd); - - // connect() IPv6 - if(false) - { - struct hostent *server = gethostbyname2("fde5:cd7a:9e1c:0fd2:7299:9367:5993:3b86",AF_INET6); - struct sockaddr_in6 serv_addr; - memset((char *) &serv_addr, 0, sizeof(serv_addr)); - serv_addr.sin6_flowinfo = 0; - serv_addr.sin6_family = AF_INET6; - memmove((char *) &serv_addr.sin6_addr.s6_addr, (char *) server->h_addr, server->h_length); - serv_addr.sin6_port = htons( port ); - if((err = zts_connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))) < 0) { - printf("error connecting to remote host (%d)\n", err); - return -1; - } - } - // connect() IPv4 - if(true) - { - addr.sin_addr.s_addr = inet_addr("10.9.9.20"); - addr.sin_family = AF_INET; - addr.sin_port = htons( port ); - if((err = zts_connect(sockfd, (const struct sockaddr *)&addr, sizeof(addr))) < 0) { - printf("error connecting to remote host (%d)\n", err); - return -1; - } - - zts_write(sockfd, "hello", 5); - sleep(3); - zts_close(sockfd); - } - // bind() ipv4 - if(false) - { - //addr.sin_addr.s_addr = INADDR_ANY; // TODO: Requires significant socket multiplexer work - addr.sin_addr.s_addr = inet_addr("10.9.9.40"); - addr.sin_family = AF_INET; - addr.sin_port = htons( port ); - if((err = zts_bind(sockfd, (const struct sockaddr *)&addr, sizeof(addr))) < 0) { - printf("error binding to interface (%d)\n", err); - return -1; - } - zts_listen(sockfd, 1); - struct sockaddr_in client; - int c = sizeof(struct sockaddr_in); - - int accept_fd = zts_accept(sockfd, (struct sockaddr *)&client, (socklen_t*)&c); - - printf("reading from buffer\n"); - char newbuf[32]; - memset(newbuf, 0, 32); - read(accept_fd, newbuf, 20); - printf("newbuf = %s\n", newbuf); - } - - // End Socket API calls - - // Get the ipv4 address assigned for this network - char addr_str[ZT_MAX_IPADDR_LEN]; - zts_get_ipv4_address(nwid, addr_str, ZT_MAX_IPADDR_LEN); - printf("ipv4 = %s\n", addr_str); - - zts_get_ipv6_address(nwid, addr_str, ZT_MAX_IPADDR_LEN); - printf("ipv6 = %s\n", addr_str); - - printf("peer_count = %lu\n", zts_get_peer_count()); - - while(1) { - sleep(1); - } - - // Stop service, delete tap interfaces, and network stack - zts_stop(); - return 0; -} \ No newline at end of file diff --git a/include/Debug.hpp b/include/Debug.hpp index e512bc0..ce7e68c 100644 --- a/include/Debug.hpp +++ b/include/Debug.hpp @@ -75,7 +75,7 @@ #endif */ -#define ZT_THREAD_ID 0 +// #define ZT_THREAD_ID 0 #if defined(__JNI_LIB__) #include @@ -86,15 +86,15 @@ #endif #if ZT_DEBUG_LEVEL >= ZT_MSG_TEST - #define DEBUG_TEST(fmt, args...) fprintf(stderr, ZT_CYN "TEST [%d] : %17s:%5d:%25s: " fmt \ - "\n" ZT_RESET, ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, ##args) + #define DEBUG_TEST(fmt, args...) fprintf(stderr, ZT_CYN "TEST : %17s:%5d:%25s: " fmt \ + "\n" ZT_RESET, ZT_FILENAME, __LINE__, __FUNCTION__, ##args) #else #define DEBUG_ERROR(fmt, args...) #endif #if ZT_DEBUG_LEVEL >= ZT_MSG_ERROR - #define DEBUG_ERROR(fmt, args...) fprintf(stderr, ZT_RED "ERROR[%d] : %17s:%5d:%25s: " fmt \ - "\n" ZT_RESET, ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, ##args) + #define DEBUG_ERROR(fmt, args...) fprintf(stderr, ZT_RED "ERROR: %17s:%5d:%25s: " fmt \ + "\n" ZT_RESET, ZT_FILENAME, __LINE__, __FUNCTION__, ##args) #else #define DEBUG_ERROR(fmt, args...) #endif @@ -110,14 +110,14 @@ #define DEBUG_STACK(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, ZT_LOG_TAG, \ "STACK: %17s:%5d:%25s: " fmt "\n", ZT_FILENAME, __LINE__, __FUNCTION__, ##args)) #else - #define DEBUG_INFO(fmt, args...) fprintf(stderr, \ - "INFO [%d] : %17s:%5d:%25s: " fmt "\n", ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, ##args) - #define DEBUG_ATTN(fmt, args...) fprintf(stderr, ZT_CYN \ - "ATTN [%d] : %17s:%5d:%25s: " fmt "\n" ZT_RESET, ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, ##args) - #define DEBUG_STACK(fmt, args...) fprintf(stderr, ZT_YEL \ - "STACK[%d] : %17s:%5d:%25s: " fmt "\n" ZT_RESET, ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, ##args) - #define DEBUG_BLANK(fmt, args...) fprintf(stderr, \ - "INFO [%d] : %17s:%5d:" fmt "\n", ZT_THREAD_ID, ZT_FILENAME, __LINE__, ##args) + #define DEBUG_INFO(fmt, args...) fprintf(stderr, \ + "INFO : %17s:%5d:%25s: " fmt "\n", ZT_FILENAME, __LINE__, __FUNCTION__, ##args) + #define DEBUG_ATTN(fmt, args...) fprintf(stderr, ZT_CYN \ + "ATTN : %17s:%5d:%25s: " fmt "\n" ZT_RESET, ZT_FILENAME, __LINE__, __FUNCTION__, ##args) + #define DEBUG_STACK(fmt, args...) fprintf(stderr, ZT_YEL \ + "STACK: %17s:%5d:%25s: " fmt "\n" ZT_RESET, ZT_FILENAME, __LINE__, __FUNCTION__, ##args) + #define DEBUG_BLANK(fmt, args...) fprintf(stderr, \ + "INFO : %17s:%5d:" fmt "\n", ZT_FILENAME, __LINE__, ##args) #endif #else #define DEBUG_INFO(fmt, args...) @@ -129,10 +129,10 @@ #if ZT_DEBUG_LEVEL >= ZT_MSG_TRANSFER #if defined(__ANDROID__) #define DEBUG_TRANS(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, ZT_LOG_TAG, \ - "TRANS : %17s:%5d:%25s: " fmt "\n", ZT_FILENAME, __LINE__, __FUNCTION__, ##args)) + "TRANS: %17s:%5d:%25s: " fmt "\n", ZT_FILENAME, __LINE__, __FUNCTION__, ##args)) #else - #define DEBUG_TRANS(fmt, args...) fprintf(stderr, ZT_GRN "TRANS[%ld] : %17s:%5d:%25s: " fmt \ - "\n" ZT_RESET, ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, ##args) + #define DEBUG_TRANS(fmt, args...) fprintf(stderr, ZT_GRN "TRANS: %17s:%5d:%25s: " fmt \ + "\n" ZT_RESET, ZT_FILENAME, __LINE__, __FUNCTION__, ##args) #endif #else #define DEBUG_TRANS(fmt, args...) @@ -141,10 +141,10 @@ #if ZT_DEBUG_LEVEL >= ZT_MSG_EXTRA #if defined(__ANDROID__) #define DEBUG_EXTRA(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, ZT_LOG_TAG, \ - "EXTRA : %17s:%5d:%25s: " fmt "\n", ZT_FILENAME, __LINE__, __FUNCTION__, ##args)) + "EXTRA: %17s:%5d:%25s: " fmt "\n", ZT_FILENAME, __LINE__, __FUNCTION__, ##args)) #else #define DEBUG_EXTRA(fmt, args...) fprintf(stderr, \ - "EXTRA[%d] : %17s:%5d:%25s: " fmt "\n", ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, ##args) + "EXTRA: %17s:%5d:%25s: " fmt "\n", ZT_FILENAME, __LINE__, __FUNCTION__, ##args) #endif #else #define DEBUG_EXTRA(fmt, args...) @@ -155,8 +155,8 @@ #define DEBUG_FLOW(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, ZT_LOG_TAG, \ "FLOW : %17s:%5d:%25s: " fmt "\n", ZT_FILENAME, __LINE__, __FUNCTION__, ##args)) #else - #define DEBUG_FLOW(fmt, args...) fprintf(stderr, "FLOW [%ld] : %17s:%5d:%25s: " fmt "\n", \ - ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, ##args) + #define DEBUG_FLOW(fmt, args...) fprintf(stderr, "FLOW : %17s:%5d:%25s: " fmt "\n", \ + ZT_FILENAME, __LINE__, __FUNCTION__, ##args) #endif #else #define DEBUG_FLOW(fmt, args...) diff --git a/include/libzt.h b/include/libzt.h index 1d2902b..576e1eb 100644 --- a/include/libzt.h +++ b/include/libzt.h @@ -495,6 +495,16 @@ int zts_shutdown(ZT_SHUTDOWN_SIG); */ std::vector *zts_get_network_routes(char *nwid); +/* + * Adds a DNS nameserver for the network stack to use + */ +int zts_add_dns_nameserver(struct sockaddr *addr); + +/* + * Removes a DNS nameserver + */ +int zts_remove_dns_nameserver(struct sockaddr *addr); + /****************************************************************************/ /* SDK Socket API Helper functions/objects --- DONT CALL THESE DIRECTLY */ /****************************************************************************/ @@ -545,7 +555,7 @@ int pico_ntimers(); /****************************************************************************/ ZeroTier::VirtualTap *getTapByNWID(uint64_t nwid); -ZeroTier::VirtualTap *getTapByAddr(ZeroTier::InetAddress &addr); +ZeroTier::VirtualTap *getTapByAddr(ZeroTier::InetAddress *addr); ZeroTier::VirtualTap *getTapByName(char *ifname); ZeroTier::VirtualTap *getTapByIndex(int index); @@ -554,6 +564,30 @@ ZeroTier::VirtualTap *getTapByIndex(int index); */ ZeroTier::VirtualSocket *get_virtual_socket(int fd); +/* + * Removes a VirtualSocket + */ +void del_virtual_socket(int fd); + +/* + * Adds a virtualSocket + */ +void add_unassigned_virtual_socket(int fd, ZeroTier::VirtualSocket *vs); +/* + * Removes unassigned VirtualSocket + */ +void del_unassigned_virtual_socket(int fd); + +/* + * Adds an assigned VirtualSocket + */ +void add_assigned_virtual_socket(ZeroTier::VirtualTap *tap, ZeroTier::VirtualSocket *vs, int fd); + +/* + * Removes an assigned VirtualSocket + */ +void del_assigned_virtual_socket(ZeroTier::VirtualTap *tap, ZeroTier::VirtualSocket *vs, int fd); + /* * Destroys all virtual tap devices */ diff --git a/src/Utilities.cpp b/src/Utilities.cpp index fcef79f..256645b 100644 --- a/src/Utilities.cpp +++ b/src/Utilities.cpp @@ -24,4 +24,137 @@ * of your own application. */ -// Intentionally left blank \ No newline at end of file +#include "InetAddress.hpp" +#include "Debug.hpp" + +char *beautify_eth_proto_nums(int proto) +{ + if(proto == 0x0800) return (char*)"IPv4"; + if(proto == 0x0806) return (char*)"ARP"; + if(proto == 0x0842) return (char*)"Wake-on-LAN"; + if(proto == 0x22F3) return (char*)"IETF TRILL Protocol"; + if(proto == 0x22EA) return (char*)"Stream Reservation Protocol"; + if(proto == 0x6003) return (char*)"DECnet Phase IV"; + if(proto == 0x8035) return (char*)"Reverse Address Resolution Protocol"; + if(proto == 0x809B) return (char*)"AppleTalk (Ethertalk)"; + if(proto == 0x80F3) return (char*)"AppleTalk Address Resolution Protocol (AARP)"; + if(proto == 0x8100) return (char*)"VLAN-tagged frame (IEEE 802.1Q) and Shortest Path Bridging IEEE 802.1aq with NNI compatibility"; + if(proto == 0x8137) return (char*)"IPX"; + if(proto == 0x8204) return (char*)"QNX Qnet"; + if(proto == 0x86DD) return (char*)"IPv6"; + if(proto == 0x8808) return (char*)"Ethernet flow control"; + if(proto == 0x8809) return (char*)"Ethernet Slow Protocols"; + if(proto == 0x8819) return (char*)"CobraNet"; + if(proto == 0x8847) return (char*)"MPLS unicast"; + if(proto == 0x8848) return (char*)"MPLS multicast"; + if(proto == 0x8863) return (char*)"PPPoE Discovery Stage"; + if(proto == 0x8864) return (char*)"PPPoE Session Stage"; + if(proto == 0x886D) return (char*)"Intel Advanced Networking Services"; + if(proto == 0x8870) return (char*)"Jumbo Frames (Obsoleted draft-ietf-isis-ext-eth-01)"; + if(proto == 0x887B) return (char*)"HomePlug 1.0 MME"; + if(proto == 0x888E) return (char*)"EAP over LAN (IEEE 802.1X)"; + if(proto == 0x8892) return (char*)"PROFINET Protocol"; + if(proto == 0x889A) return (char*)"HyperSCSI (SCSI over Ethernet)"; + if(proto == 0x88A2) return (char*)"ATA over Ethernet"; + if(proto == 0x88A4) return (char*)"EtherCAT Protocol"; + if(proto == 0x88A8) return (char*)"Provider Bridging (IEEE 802.1ad) & Shortest Path Bridging IEEE 802.1aq"; + if(proto == 0x88AB) return (char*)"Ethernet Powerlink[citation needed]"; + if(proto == 0x88B8) return (char*)"GOOSE (Generic Object Oriented Substation event)"; + if(proto == 0x88B9) return (char*)"GSE (Generic Substation Events) Management Services"; + if(proto == 0x88BA) return (char*)"SV (Sampled Value Transmission)"; + if(proto == 0x88CC) return (char*)"Link Layer Discovery Protocol (LLDP)"; + if(proto == 0x88CD) return (char*)"SERCOS III"; + if(proto == 0x88DC) return (char*)"WSMP, WAVE Short Message Protocol"; + if(proto == 0x88E1) return (char*)"HomePlug AV MME[citation needed]"; + if(proto == 0x88E3) return (char*)"Media Redundancy Protocol (IEC62439-2)"; + if(proto == 0x88E5) return (char*)"MAC security (IEEE 802.1AE)"; + if(proto == 0x88E7) return (char*)"Provider Backbone Bridges (PBB) (IEEE 802.1ah)"; + if(proto == 0x88F7) return (char*)"Precision Time Protocol (PTP) over Ethernet (IEEE 1588)"; + if(proto == 0x88FB) return (char*)"Parallel Redundancy Protocol (PRP)"; + if(proto == 0x8902) return (char*)"IEEE 802.1ag Connectivity Fault Management (CFM) Protocol / ITU-T Recommendation Y.1731 (OAM)"; + if(proto == 0x8906) return (char*)"Fibre Channel over Ethernet (FCoE)"; + if(proto == 0x8914) return (char*)"FCoE Initialization Protocol"; + if(proto == 0x8915) return (char*)"RDMA over Converged Ethernet (RoCE)"; + if(proto == 0x891D) return (char*)"TTEthernet Protocol Control Frame (TTE)"; + if(proto == 0x892F) return (char*)"High-availability Seamless Redundancy (HSR)"; + if(proto == 0x9000) return (char*)"Ethernet Configuration Testing Protocol"; + if(proto == 0x9100) return (char*)"VLAN-tagged (IEEE 802.1Q) frame with double tagging"; + return (char*)"UNKNOWN"; +} + +/* +ZeroTier::InetAddress *ztipv6_mask(ZeroTier::InetAddress *addr, unsigned int bits) +{ + ZeroTier::InetAddress r(addr); + switch(r.ss_family) { + case AF_INET: + reinterpret_cast(&r)->sin_addr.s_addr &= ZeroTier::Utils::hton((uint32_t)(0xffffffff << (32 - bits))); + break; + case AF_INET6: { + uint64_t nm[2]; + memcpy(nm,reinterpret_cast(&r)->sin6_addr.s6_addr,16); + nm[0] &= ZeroTier::Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits)))); + nm[1] &= ZeroTier::Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits)))); + memcpy(reinterpret_cast(&r)->sin6_addr.s6_addr,nm,16); + } break; + } + return &r; +} +*/ + +bool ipv6_in_subnet(ZeroTier::InetAddress *subnet, ZeroTier::InetAddress *addr) +{ + ZeroTier::InetAddress r(addr); + ZeroTier::InetAddress b(subnet); + const unsigned int bits = subnet->netmaskBits(); + switch(r.ss_family) { + case AF_INET: + reinterpret_cast(&r)->sin_addr.s_addr &= ZeroTier::Utils::hton((uint32_t)(0xffffffff << (32 - bits))); + break; + case AF_INET6: { + uint64_t nm[2]; + uint64_t nm2[2]; + memcpy(nm,reinterpret_cast(&r)->sin6_addr.s6_addr,16); + memcpy(nm2,reinterpret_cast(&b)->sin6_addr.s6_addr,16); + + nm[0] &= ZeroTier::Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits)))); + nm[1] &= ZeroTier::Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits)))); + + nm2[0] &= ZeroTier::Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits)))); + nm2[1] &= ZeroTier::Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits)))); + + memcpy(reinterpret_cast(&r)->sin6_addr.s6_addr,nm,16); + memcpy(reinterpret_cast(&b)->sin6_addr.s6_addr,nm2,16); + } + break; + } + char b0[64], b1[64]; + memset(b0, 0, 64); + memset(b1, 0, 64); + return !strcmp(r.toIpString(b0), b.toIpString(b1)); +} + + +void sockaddr2inet(int socket_family, const struct sockaddr *addr, ZeroTier::InetAddress *inet) +{ + char ipstr[INET6_ADDRSTRLEN]; + memset(ipstr, 0, INET6_ADDRSTRLEN); + if(socket_family == AF_INET) { + inet_ntop(AF_INET, + (const void *)&((struct sockaddr_in *)addr)->sin_addr.s_addr, ipstr, INET_ADDRSTRLEN); + inet->fromString(ipstr); + } + if(socket_family == AF_INET6) { + inet_ntop(AF_INET6, + (const void *)&((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, ipstr, INET6_ADDRSTRLEN); + char addrstr[64]; + sprintf(addrstr, "%s", ipstr); + inet->fromString(addrstr); + } +} + +void mac2str(char *macbuf, int len, unsigned char* addr) +{ + snprintf(macbuf, len, "%02x:%02x:%02x:%02x:%02x:%02x", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); +} \ No newline at end of file diff --git a/src/Utilities.hpp b/src/Utilities.hpp index 07d69de..adc484e 100644 --- a/src/Utilities.hpp +++ b/src/Utilities.hpp @@ -27,66 +27,26 @@ #ifndef UTILITIES_HPP #define UTILITIES_HPP -char *beautify_eth_proto_nums(int proto) -{ - if(proto == 0x0800) return (char*)"IPv4"; - if(proto == 0x0806) return (char*)"ARP"; - if(proto == 0x0842) return (char*)"Wake-on-LAN"; - if(proto == 0x22F3) return (char*)"IETF TRILL Protocol"; - if(proto == 0x22EA) return (char*)"Stream Reservation Protocol"; - if(proto == 0x6003) return (char*)"DECnet Phase IV"; - if(proto == 0x8035) return (char*)"Reverse Address Resolution Protocol"; - if(proto == 0x809B) return (char*)"AppleTalk (Ethertalk)"; - if(proto == 0x80F3) return (char*)"AppleTalk Address Resolution Protocol (AARP)"; - if(proto == 0x8100) return (char*)"VLAN-tagged frame (IEEE 802.1Q) and Shortest Path Bridging IEEE 802.1aq with NNI compatibility"; - if(proto == 0x8137) return (char*)"IPX"; - if(proto == 0x8204) return (char*)"QNX Qnet"; - if(proto == 0x86DD) return (char*)"IPv6"; - if(proto == 0x8808) return (char*)"Ethernet flow control"; - if(proto == 0x8809) return (char*)"Ethernet Slow Protocols"; - if(proto == 0x8819) return (char*)"CobraNet"; - if(proto == 0x8847) return (char*)"MPLS unicast"; - if(proto == 0x8848) return (char*)"MPLS multicast"; - if(proto == 0x8863) return (char*)"PPPoE Discovery Stage"; - if(proto == 0x8864) return (char*)"PPPoE Session Stage"; - if(proto == 0x886D) return (char*)"Intel Advanced Networking Services"; - if(proto == 0x8870) return (char*)"Jumbo Frames (Obsoleted draft-ietf-isis-ext-eth-01)"; - if(proto == 0x887B) return (char*)"HomePlug 1.0 MME"; - if(proto == 0x888E) return (char*)"EAP over LAN (IEEE 802.1X)"; - if(proto == 0x8892) return (char*)"PROFINET Protocol"; - if(proto == 0x889A) return (char*)"HyperSCSI (SCSI over Ethernet)"; - if(proto == 0x88A2) return (char*)"ATA over Ethernet"; - if(proto == 0x88A4) return (char*)"EtherCAT Protocol"; - if(proto == 0x88A8) return (char*)"Provider Bridging (IEEE 802.1ad) & Shortest Path Bridging IEEE 802.1aq"; - if(proto == 0x88AB) return (char*)"Ethernet Powerlink[citation needed]"; - if(proto == 0x88B8) return (char*)"GOOSE (Generic Object Oriented Substation event)"; - if(proto == 0x88B9) return (char*)"GSE (Generic Substation Events) Management Services"; - if(proto == 0x88BA) return (char*)"SV (Sampled Value Transmission)"; - if(proto == 0x88CC) return (char*)"Link Layer Discovery Protocol (LLDP)"; - if(proto == 0x88CD) return (char*)"SERCOS III"; - if(proto == 0x88DC) return (char*)"WSMP, WAVE Short Message Protocol"; - if(proto == 0x88E1) return (char*)"HomePlug AV MME[citation needed]"; - if(proto == 0x88E3) return (char*)"Media Redundancy Protocol (IEC62439-2)"; - if(proto == 0x88E5) return (char*)"MAC security (IEEE 802.1AE)"; - if(proto == 0x88E7) return (char*)"Provider Backbone Bridges (PBB) (IEEE 802.1ah)"; - if(proto == 0x88F7) return (char*)"Precision Time Protocol (PTP) over Ethernet (IEEE 1588)"; - if(proto == 0x88FB) return (char*)"Parallel Redundancy Protocol (PRP)"; - if(proto == 0x8902) return (char*)"IEEE 802.1ag Connectivity Fault Management (CFM) Protocol / ITU-T Recommendation Y.1731 (OAM)"; - if(proto == 0x8906) return (char*)"Fibre Channel over Ethernet (FCoE)"; - if(proto == 0x8914) return (char*)"FCoE Initialization Protocol"; - if(proto == 0x8915) return (char*)"RDMA over Converged Ethernet (RoCE)"; - if(proto == 0x891D) return (char*)"TTEthernet Protocol Control Frame (TTE)"; - if(proto == 0x892F) return (char*)"High-availability Seamless Redundancy (HSR)"; - if(proto == 0x9000) return (char*)"Ethernet Configuration Testing Protocol"; - if(proto == 0x9100) return (char*)"VLAN-tagged (IEEE 802.1Q) frame with double tagging"; - return (char*)"UNKNOWN"; -} +/* + * Returns masked address for subnet comparisons + */ +//ZeroTier::InetAddress *ztipv6_mask(ZeroTier::InetAddress *addr, unsigned int bits); +bool ipv6_in_subnet(ZeroTier::InetAddress *subnet, ZeroTier::InetAddress *addr); -void mac2str(char *macbuf, int len, unsigned char* addr) -{ - snprintf(macbuf, len, "%02x:%02x:%02x:%02x:%02x:%02x", - addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); -} +/* + * Convert protocol numbers to human-readable strings + */ +char *beautify_eth_proto_nums(int proto); + +/* + * Convert a struct sockaddr to a ZeroTier::InetAddress + */ +void sockaddr2inet(int socket_family, const struct sockaddr *addr, ZeroTier::InetAddress *inet); + +/* + * Convert a raw MAC address byte array into a human-readable string + */ +void mac2str(char *macbuf, int len, unsigned char* addr); #if defined(STACK_LWIP) && defined(LIBZT_IPV6) #define IP6_ADDR2(ipaddr, a,b,c,d,e,f,g,h) do { (ipaddr)->addr[0] = ZeroTier::Utils::hton((u32_t)((a & 0xffff) << 16) | (b & 0xffff)); \ @@ -94,7 +54,9 @@ void mac2str(char *macbuf, int len, unsigned char* addr) (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 +/* + * Convert from standard IPV6 address structure to an lwIP native structure + */ inline void in6_to_ip6(ip6_addr_t *ba, struct sockaddr_in6 *in6) { uint8_t *ip = &(in6->sin6_addr).s6_addr[0]; diff --git a/src/VirtualSocket.hpp b/src/VirtualSocket.hpp index 3fbdcd8..e447bd8 100644 --- a/src/VirtualSocket.hpp +++ b/src/VirtualSocket.hpp @@ -82,7 +82,6 @@ namespace ZeroTier { std::time_t closure_ts = 0; VirtualSocket() { - DEBUG_EXTRA("this=0x%x, socket_family=%d, socket_type=%d", this, socket_family, socket_type); TXbuf = new RingBuffer(ZT_TCP_TX_BUF_SZ); RXbuf = new RingBuffer(ZT_TCP_RX_BUF_SZ); @@ -98,7 +97,11 @@ namespace ZeroTier { app_fd = fdpair[1]; } ~VirtualSocket() { - DEBUG_EXTRA("this=0x%x", this); + close(app_fd); + close(sdk_fd); + delete TXbuf; + delete RXbuf; + TXbuf = RXbuf = NULL; } }; diff --git a/src/VirtualTap.cpp b/src/VirtualTap.cpp index 48db29b..e38f277 100644 --- a/src/VirtualTap.cpp +++ b/src/VirtualTap.cpp @@ -62,6 +62,8 @@ class VirtualTap; extern std::vector vtaps; +static bool picodev_initialized; + namespace ZeroTier { int VirtualTap::devno = 0; @@ -94,14 +96,12 @@ namespace ZeroTier { _phy(this,false,true) { vtaps.push_back((void*)this); - // set interface name char tmp3[17]; ifindex = devno; - sprintf(tmp3, "libzt%d", devno++); + sprintf(tmp3, "libzt%d-%lx", devno++, _nwid); _dev = tmp3; - DEBUG_INFO("set device name to: %s", _dev.c_str()); - + DEBUG_INFO("set VirtualTap interface name to: %s", _dev.c_str()); _thread = Thread::start(this); } @@ -111,10 +111,6 @@ namespace ZeroTier { _phy.whack(); Thread::join(_thread); _phy.close(_unixListenSocket,false); - for(int i=0; i<_VirtualSockets.size(); i++) { - delete _VirtualSockets[i]; - _VirtualSockets[i] = NULL; - } } void VirtualTap::setEnabled(bool en) @@ -131,7 +127,7 @@ namespace ZeroTier { { #if defined(STACK_PICO) if(picostack){ - picostack->pico_init_interface(this, ip); + picostack->pico_register_address(this, ip); return true; } #endif @@ -153,15 +149,18 @@ namespace ZeroTier { std::sort(_ips.begin(),_ips.end()); return true; #endif - if(registerIpWithStack(ip)) - { - // only start the stack if we successfully registered and initialized a device to - // the given address - _ips.push_back(ip); - std::sort(_ips.begin(),_ips.end()); +#if defined(STACK_PICO) || defined(STACK_LWIP) + char ipbuf[64]; + DEBUG_INFO("addIp (%s)", ip.toString(ipbuf)); + if(registerIpWithStack(ip)) { + if (std::find(_ips.begin(),_ips.end(),ip) == _ips.end()) { + _ips.push_back(ip); + std::sort(_ips.begin(),_ips.end()); + } return true; } return false; +#endif } bool VirtualTap::removeIp(const InetAddress &ip) @@ -204,6 +203,19 @@ namespace ZeroTier { return _dev; } + std::string VirtualTap::nodeId() const + { + // TODO: This is inefficient and awkward, should be replaced with something more elegant + if(zt1ServiceRef) { + char id[ZT_ID_LEN+1]; + sprintf(id, "%lx",((ZeroTier::OneService *)zt1ServiceRef)->getNode()->address()); + return std::string(id); + } + else { + return std::string("----------"); + } + } + void VirtualTap::setFriendlyName(const char *friendlyName) { DEBUG_INFO("%s", friendlyName); @@ -215,14 +227,14 @@ namespace ZeroTier { { std::vector newGroups; Mutex::Lock _l(_multicastGroups_m); - // TODO: get multicast subscriptions from network stack + // TODO: get multicast subscriptions std::vector allIps(ips()); for(std::vector::iterator ip(allIps.begin());ip!=allIps.end();++ip) newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip)); std::sort(newGroups.begin(),newGroups.end()); std::unique(newGroups.begin(),newGroups.end()); - + for(std::vector::iterator m(newGroups.begin());m!=newGroups.end();++m) { if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m)) added.push_back(*m); @@ -245,8 +257,12 @@ namespace ZeroTier { throw() { #if defined(STACK_PICO) - if(picostack) - picostack->pico_loop(this); + if(picostack){ + picostack->pico_init_interface(this); + if(should_start_stack) { + picostack->pico_loop(this); + } + } #endif #if defined(STACK_LWIP) if(lwipstack) @@ -284,12 +300,18 @@ namespace ZeroTier { // Adds a route to the virtual tap bool VirtualTap::routeAdd(const InetAddress &addr, const InetAddress &nm, const InetAddress &gw) { +#if defined(NO_STACK) + return false; +#endif #if defined(STACK_PICO) - if(picostack) + if(picostack) { return picostack->pico_route_add(this, addr, nm, gw, 0); + } #endif #if defined(STACK_LWIP) - return true; + if(lwipstack) { + return true; + } #endif return false; } @@ -297,16 +319,40 @@ namespace ZeroTier { // Deletes a route from the virtual tap bool VirtualTap::routeDelete(const InetAddress &addr, const InetAddress &nm) { +#if defined(NO_STACK) + return false; +#endif #if defined(STACK_PICO) - if(picostack) + if(picostack) { return picostack->pico_route_del(this, addr, nm, 0); + } #endif #if defined(STACK_LWIP) - return true; + if(lwipstack) { + return true; + } #endif return false; } + + void VirtualTap::addVirtualSocket(VirtualSocket *vs) + { + Mutex::Lock _l(_tcpconns_m); + _VirtualSockets.push_back(vs); + } + void VirtualTap::removeVirtualSocket(VirtualSocket *vs) + { + Mutex::Lock _l(_tcpconns_m); + for(int i=0; i<_VirtualSockets.size(); i++) { + if(vs == _VirtualSockets[i]) { + _VirtualSockets.erase(_VirtualSockets.begin() + i); + DEBUG_INFO("Removed vs=%p from vt=%p", vs, this); + break; + } + } + } + /****************************************************************************/ /* SDK Socket API */ /****************************************************************************/ @@ -317,15 +363,17 @@ namespace ZeroTier { return -1; #endif #if defined(STACK_PICO) - if(picostack) + if(picostack) { Mutex::Lock _l(_tcpconns_m); return picostack->pico_Connect(vs, addr, addrlen); + } #endif #if defined(STACK_LWIP) - if(lwipstack) + if(lwipstack) { return lwipstack->lwip_Connect(vs, addr, addrlen); + } #endif - return ZT_ERR_GENERAL_FAILURE; + return -1; } // Bind VirtualSocket to a network stack's interface @@ -333,16 +381,19 @@ namespace ZeroTier { #if defined(NO_STACK) return -1; #endif - Mutex::Lock _l(_tcpconns_m); #if defined(STACK_PICO) - if(picostack) + if(picostack) { + Mutex::Lock _l(_tcpconns_m); return picostack->pico_Bind(vs, addr, addrlen); + } #endif #if defined(STACK_LWIP) - if(lwipstack) + if(lwipstack) { + Mutex::Lock _l(_tcpconns_m); return lwipstack->lwip_Bind(this, vs, addr, addrlen); + } #endif - return ZT_ERR_GENERAL_FAILURE; + return -1; } // Listen for an incoming VirtualSocket @@ -350,18 +401,24 @@ namespace ZeroTier { #if defined(NO_STACK) return -1; #endif - Mutex::Lock _l(_tcpconns_m); #if defined(STACK_PICO) - if(picostack) + if(picostack) { + Mutex::Lock _l(_tcpconns_m); return picostack->pico_Listen(vs, backlog); - return ZT_ERR_GENERAL_FAILURE; + } + else { + return ZT_ERR_GENERAL_FAILURE; + } #endif #if defined(STACK_LWIP) - if(lwipstack) + if(lwipstack) { + Mutex::Lock _l(_tcpconns_m); return lwipstack->lwip_Listen(vs, backlog); - return ZT_ERR_GENERAL_FAILURE; + } + else { + return ZT_ERR_GENERAL_FAILURE; + } #endif - return ZT_ERR_GENERAL_FAILURE; } // Accept a VirtualSocket @@ -370,28 +427,37 @@ namespace ZeroTier { return NULL; #endif #if defined(STACK_PICO) - if(picostack) + // TODO: separation of church and state + if(picostack) { Mutex::Lock _l(_tcpconns_m); return picostack->pico_Accept(vs); - return NULL; + } + else { + return NULL; + } #endif #if defined(STACK_LWIP) - if(lwipstack) + if(lwipstack) { + Mutex::Lock _l(_tcpconns_m); return lwipstack->lwip_Accept(vs); - return NULL; + } + else { + return NULL; + } #endif - return NULL; } // Read from stack/buffers into the app's socket int VirtualTap::Read(PhySocket *sock,void **uptr,bool stack_invoked) { #if defined(STACK_PICO) - if(picostack) + if(picostack) { return picostack->pico_Read(this, sock, (VirtualSocket*)uptr, stack_invoked); + } #endif #if defined(STACK_LWIP) - if(lwipstack) + if(lwipstack) { return lwipstack->lwip_Read((VirtualSocket*)*(_phy.getuptr(sock)), stack_invoked); + } #endif return -1; } @@ -409,12 +475,14 @@ namespace ZeroTier { return len; } #if defined(STACK_PICO) - if(picostack) + if(picostack) { return picostack->pico_Write(vs, data, len); + } #endif #if defined(STACK_LWIP) - if(lwipstack) + if(lwipstack) { return lwipstack->lwip_Write(vs, data, len); + } #endif return -1; } @@ -426,8 +494,14 @@ namespace ZeroTier { int err = 0; #if defined(STACK_PICO) if(picostack) { - err = picostack->pico_Connect(vs, addr, addrlen); // implicit - err = picostack->pico_Write(vs, (void*)buf, len); + if((err = picostack->pico_Connect(vs, addr, addrlen)) < 0) { // implicit + errno = ENOTCONN; + return err; + } + if((err = picostack->pico_Write(vs, (void*)buf, len)) < 0) { + errno = ENOBUFS; // TODO: translate pico err to something more useful + return err; + } } #endif #if defined(STACK_LWIP) @@ -439,36 +513,22 @@ namespace ZeroTier { } int VirtualTap::Close(VirtualSocket *vs) { -#if defined(STACK_PICO) + int err = 0; if(!vs) { DEBUG_ERROR("invalid VirtualSocket"); return -1; } - picostack->pico_Close(vs); - if(!vs->sock) { - // DEBUG_EXTRA("invalid PhySocket"); - return -1; - } - // Here we assume _tcpconns_m is already locked by caller - // FIXME: is this assumption still valid - if(vs->state==ZT_SOCK_STATE_LISTENING) - { - // since we never wrapped this socket - DEBUG_INFO("in LISTENING state, no need to close in PhyIO"); - return -1; - } - else - { - if(vs->sock) - _phy.close(vs->sock, false); - } - //close(_phy.getDescriptor(vs->sock)); +#if defined(STACK_PICO) + err = picostack->pico_Close(vs); #endif #if defined(STACK_LWIP) if(lwipstack) lwipstack->lwip_Close(vs); #endif - return 0; // TODO + if(vs->sock) { + _phy.close(vs->sock, false); + } + return err; } void VirtualTap::Housekeeping() diff --git a/src/VirtualTap.hpp b/src/VirtualTap.hpp index a703c10..9bbcbd8 100644 --- a/src/VirtualTap.hpp +++ b/src/VirtualTap.hpp @@ -109,10 +109,15 @@ namespace ZeroTier { unsigned int len); /* - * Get device name + * Get VirtualTap device name (e.g. 'libzt4-17d72843bc2c5760') */ std::string deviceName() const; + /* + * Get Node ID (ZT address) + */ + std::string nodeId() const; + /* * Set friendly name */ @@ -153,7 +158,7 @@ namespace ZeroTier { /* * Notifies us that we can write to an application's socket */ - void phyOnUnixWritable(PhySocket *sock, void **uptr, bool lwip_invoked); + void phyOnUnixWritable(PhySocket *sock, void **uptr, bool stack_invoked); /* * Adds a route to the virtual tap @@ -165,19 +170,24 @@ namespace ZeroTier { */ bool routeDelete(const InetAddress &addr, const InetAddress &nm); + /* + * Assign a VirtualSocket to the VirtualTap + */ + void addVirtualSocket(VirtualSocket *vs); + + /* + * Remove a VirtualSocket from the VirtualTap + */ + void removeVirtualSocket(VirtualSocket *vs); + /****************************************************************************/ /* Vars */ /****************************************************************************/ #if defined(STACK_PICO) - /* - * Whether our picoTCP device has been initialized - */ - bool picodev_initialized = false; - + bool should_start_stack = false; struct pico_device *picodev = NULL; - struct pico_device *picodev6 = NULL; /****************************************************************************/ /* Guarded RX Frame Buffer for picoTCP */ diff --git a/src/libzt.cpp b/src/libzt.cpp index 904975a..8be027e 100644 --- a/src/libzt.cpp +++ b/src/libzt.cpp @@ -34,6 +34,7 @@ for applications to use. See also: include/libzt.h */ #include #include #include +#include #if defined(__APPLE__) #include @@ -67,6 +68,7 @@ for applications to use. See also: include/libzt.h */ #include "InetAddress.hpp" #include "ZeroTierOne.h" +#include "Utilities.hpp" #include "VirtualTap.hpp" #include "libzt.h" @@ -89,7 +91,7 @@ namespace ZeroTier { #endif /* - * "sockets" that have been created but not bound to a VirtualTap interface yet + * VirtualSockets that have been created but not bound to a VirtualTap interface yet */ std::map unmap; @@ -105,7 +107,6 @@ namespace ZeroTier { ZeroTier::Mutex _vtaps_lock; ZeroTier::Mutex _multiplexer_lock; - ZeroTier::Mutex _accepted_VirtualSocket_lock; } /****************************************************************************/ @@ -135,11 +136,15 @@ void zts_start(const char *path) void zts_simple_start(const char *path, const char *nwid) { zts_start(path); - while(!zts_running()) - usleep(ZT_API_CHECK_INTERVAL * 1000); + while(!zts_running()) { + nanosleep((const struct timespec[]){{0, (ZT_API_CHECK_INTERVAL * 1000000)}}, NULL); + //usleep(ZT_API_CHECK_INTERVAL * 1000); + } zts_join(nwid); - while(!zts_has_address(nwid)) - usleep(ZT_API_CHECK_INTERVAL * 1000); + while(!zts_has_address(nwid)) { + //usleep(ZT_API_CHECK_INTERVAL * 1000); + nanosleep((const struct timespec[]){{0, (ZT_API_CHECK_INTERVAL * 1000000)}}, NULL); + } } void zts_stop() { @@ -375,12 +380,11 @@ Darwin: // int socket_family, int socket_type, int protocol int zts_socket(ZT_SOCKET_SIG) { - errno = 0; + int err = errno = 0; if(socket_family < 0 || socket_type < 0 || protocol < 0) { errno = EINVAL; return -1; } - int err = 0; if(!ZeroTier::zt1Service) { DEBUG_ERROR("cannot create socket, no service running. call zts_start() first."); errno = EMFILE; // could also be ENFILE @@ -392,8 +396,6 @@ int zts_socket(ZT_SOCKET_SIG) { return -1; } - ZeroTier::_multiplexer_lock.lock(); - if(socket_type == SOCK_RAW) { // VirtualSocket is only used to associate a socket with a VirtualTap, it has no other implication @@ -401,7 +403,7 @@ int zts_socket(ZT_SOCKET_SIG) { vs->socket_family = socket_family; vs->socket_type = socket_type; vs->protocol = protocol; - ZeroTier::unmap[vs->app_fd] = vs; + add_unassigned_virtual_socket(vs->app_fd, vs); return vs->app_fd; } @@ -413,7 +415,7 @@ int zts_socket(ZT_SOCKET_SIG) { vs->socket_family = socket_family; vs->socket_type = socket_type; vs->picosock = p; - ZeroTier::unmap[vs->app_fd] = vs; + add_unassigned_virtual_socket(vs->app_fd, vs); err = vs->app_fd; // return one end of the socketpair } else { @@ -431,7 +433,7 @@ int zts_socket(ZT_SOCKET_SIG) { vs->socket_family = socket_family; vs->socket_type = socket_type; vs->pcb = pcb; - ZeroTier::unmap[vs->app_fd] = vs; + add_unassigned_virtual_socket(vs->app_fd, vs); err = vs->app_fd; // return one end of the socketpair } else { @@ -439,8 +441,6 @@ int zts_socket(ZT_SOCKET_SIG) { err = -1; } #endif - - ZeroTier::_multiplexer_lock.unlock(); return err; } @@ -498,137 +498,108 @@ Linux: IP sockets the timeout may be very long when syncookies are enabled on the server. */ + int zts_connect(ZT_CONNECT_SIG) { - DEBUG_INFO("fd = %d", fd); - int err = 0; + int err = errno = 0; if(fd < 0) { errno = EBADF; DEBUG_ERROR("EBADF"); return -1; } if(!ZeroTier::zt1Service) { - DEBUG_ERROR("Service not started. Call zts_start(path) first"); + DEBUG_ERROR("service not started. call zts_start(path) first"); errno = EBADF; return -1; } - ZeroTier::_multiplexer_lock.lock(); - ZeroTier::VirtualSocket *vs = ZeroTier::unmap[fd]; - ZeroTier::VirtualTap *tap; + ZeroTier::VirtualSocket *vs = get_virtual_socket(fd); + if(!vs) { + DEBUG_ERROR("invalid socket, unable to locate VirtualSocket for fd=%d", fd); + errno = EBADF; + return -1; + } + // DEBUG_EXTRA("fd = %d, %s : %d", fd, ipstr, ntohs(port)); + ZeroTier::InetAddress inet; + sockaddr2inet(vs->socket_family, addr, &inet); + ZeroTier::VirtualTap *tap = getTapByAddr(&inet); + if(!tap) { + DEBUG_ERROR("no route to host, could not find appropriate VirtualTap for fd=%d", fd); + errno = ENETUNREACH; + return -1; + } - if(vs) { - char ipstr[INET6_ADDRSTRLEN]; - memset(ipstr, 0, INET6_ADDRSTRLEN); - ZeroTier::InetAddress iaddr; - int port = 0; - - if(vs->socket_family == AF_INET) { - inet_ntop(AF_INET, - (const void *)&((struct sockaddr_in *)addr)->sin_addr.s_addr, ipstr, INET_ADDRSTRLEN); - iaddr.fromString(ipstr); - port = ((struct sockaddr_in*)addr)->sin_port; - } - if(vs->socket_family == AF_INET6) { - inet_ntop(AF_INET6, - (const void *)&((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, ipstr, INET6_ADDRSTRLEN); - // TODO: This is a hack, determine a proper way to do this - char addrstr[64]; - sprintf(addrstr, "%s%s", ipstr, std::string("/88").c_str()); - iaddr.fromString(addrstr); - port = ((struct sockaddr_in6*)addr)->sin6_port; - } - DEBUG_EXTRA("fd = %d, %s : %d", fd, ipstr, ntohs(port)); - tap = getTapByAddr(iaddr); - if(!tap) { - DEBUG_ERROR("no route to host"); - errno = ENETUNREACH; - err = -1; - } - else { #if defined(STACK_PICO) - // pointer to tap we use in callbacks from the stack - vs->picosock->priv = new ZeroTier::VirtualBindingPair(tap, vs); + // pointer to virtual tap we use in callbacks from the stack + vs->picosock->priv = new ZeroTier::VirtualBindingPair(tap, vs); #endif #if defined(STACK_LWIP) -#endif - err = tap->Connect(vs, addr, addrlen); - if(err == 0) { - tap->_VirtualSockets.push_back(vs); // Give this VirtualSocket to the tap we decided on - vs->tap = tap; - } - // Wrap the socketpair we created earlier - // For I/O loop participation and referencing the PhySocket's parent VirtualSocket in callbacks - vs->sock = tap->_phy.wrapSocket(vs->sdk_fd, vs); - } +#endif + + if((err = tap->Connect(vs, addr, addrlen)) < 0) { + DEBUG_ERROR("error while connecting socket"); + // errno will be set by tap->Connect + return -1; } - else { - DEBUG_ERROR("unable to locate VirtualSocket"); - errno = EBADF; - err = -1; - } - ZeroTier::unmap.erase(fd); - ZeroTier::fdmap[fd] = new std::pair(vs, tap); - ZeroTier::_multiplexer_lock.unlock(); + // assign this VirtualSocket to the tap we decided on + tap->_VirtualSockets.push_back(vs); + vs->tap = tap; + vs->sock = tap->_phy.wrapSocket(vs->sdk_fd, vs); + + // TODO: Consolidate these calls + del_unassigned_virtual_socket(fd); + add_assigned_virtual_socket(tap, vs, fd); + + // save peer addr, for calls like getpeername + memcpy(&(vs->peer_addr), addr, sizeof(vs->peer_addr)); + + // Below will simulate BLOCKING/NON-BLOCKING behaviour // NOTE: pico_socket_connect() will return 0 if no error happens immediately, but that doesn't indicate - // the VirtualSocket was completed, for that we must wait for a callback from the stack. During that + // the connection was completed, for that we must wait for a callback from the stack. During that // callback we will place the VirtualSocket in a ZT_SOCK_STATE_UNHANDLED_CONNECTED state to signal - // to the multiplexer logic that this VirtualSocket is complete and a success value can be sent to the + // to the multiplexer logic that this connection is complete and a success value can be sent to the // user application int f_err, blocking = 1; if ((f_err = fcntl(fd, F_GETFL, 0)) < 0) { - DEBUG_ERROR("fcntl error, err = %s, errno = %d", f_err, errno); - err = -1; + DEBUG_ERROR("fcntl error, err=%s, errno=%d", f_err, errno); + // errno will be set by fcntl + return -1; } - else { - blocking = !(f_err & O_NONBLOCK); - } - // non-blocking - if(err == 0 && !blocking) { - DEBUG_EXTRA("EINPROGRESS, not a real error, assuming non-blocking mode"); - errno = EINPROGRESS; + blocking = !(f_err & O_NONBLOCK); + if(!blocking) { + errno = EINPROGRESS; // can't connect immediately err = -1; } - else // blocking - { - // FIXME: Double check that accept/connect queues in multithreaded apps don't get mixed up - if(err == 0 && blocking) { - bool complete = false; - while(true) + if(blocking) { + bool complete = false; + while(true) + { + // FIXME: locking and unlocking so often might cause a performance bottleneck while outgoing VirtualSockets + // are being established (also applies to accept()) + //usleep(ZT_CONNECT_RECHECK_DELAY * 1000); + nanosleep((const struct timespec[]){{0, (ZT_CONNECT_RECHECK_DELAY * 1000000)}}, NULL); + tap->_tcpconns_m.lock(); + for(int i=0; i_VirtualSockets.size(); i++) { - // FIXME: locking and unlocking so often might cause a performance bottleneck while outgoing VirtualSockets - // are being established (also applies to accept()) - usleep(ZT_CONNECT_RECHECK_DELAY * 1000); - //DEBUG_ERROR("waiting to connect...\n"); - tap->_tcpconns_m.lock(); - for(int i=0; i_VirtualSockets.size(); i++) - { #if defined(STACK_PICO) - if(tap->_VirtualSockets[i]->state == PICO_ERR_ECONNRESET) { - errno = ECONNRESET; - DEBUG_ERROR("ECONNRESET"); - err = -1; - } + if(tap->_VirtualSockets[i]->state == PICO_ERR_ECONNRESET) { + errno = ECONNRESET; + DEBUG_ERROR("ECONNRESET"); + err = -1; + } #endif #if defined(STACK_LWIP) #endif - if(tap->_VirtualSockets[i]->state == ZT_SOCK_STATE_UNHANDLED_CONNECTED) { - tap->_VirtualSockets[i]->state = ZT_SOCK_STATE_CONNECTED; - errno = 0; - err = 0; // complete - complete = true; - } + if(tap->_VirtualSockets[i]->state == ZT_SOCK_STATE_UNHANDLED_CONNECTED) { + tap->_VirtualSockets[i]->state = ZT_SOCK_STATE_CONNECTED; + complete = true; } - tap->_tcpconns_m.unlock(); - if(complete) - break; } + tap->_tcpconns_m.unlock(); + if(complete) + break; } } - if(!err) { - // for calls like getpeername() - memcpy(&(vs->peer_addr), addr, sizeof(vs->peer_addr)); - } return err; } @@ -637,7 +608,7 @@ int zts_connect(ZT_CONNECT_SIG) { Darwin: [--] [EBADF] S is not a valid descriptor. - [ ] [ENOTSOCK] S is not a socket. + [--] [ENOTSOCK] S is not a socket. [--] [EADDRNOTAVAIL] The specified address is not available from the local machine. [ ] [EADDRINUSE] The specified address is already in use. @@ -648,74 +619,69 @@ Darwin: address space. */ int zts_bind(ZT_BIND_SIG) { - int err = 0; + DEBUG_INFO(); + int err = errno = 0; if(fd < 0) { errno = EBADF; return -1; } if(!ZeroTier::zt1Service) { - DEBUG_ERROR("Service not started. Call zts_start(path) first"); + DEBUG_ERROR("service not started. call zts_start(path) first"); errno = EBADF; return -1; } - ZeroTier::_multiplexer_lock.lock(); - ZeroTier::VirtualSocket *vs = ZeroTier::unmap[fd]; + ZeroTier::VirtualSocket *vs = get_virtual_socket(fd); + if(!vs) { + DEBUG_ERROR("no VirtualSocket for fd=%d", fd); + errno = ENOTSOCK; + return -1; + } + + // detect local interface binds ZeroTier::VirtualTap *tap; - if(vs) { - char ipstr[INET6_ADDRSTRLEN]; - memset(ipstr, 0, INET6_ADDRSTRLEN); - ZeroTier::InetAddress iaddr; - int port = 0; - if(vs->socket_family == AF_INET) { - inet_ntop(AF_INET, - (const void *)&((struct sockaddr_in *)addr)->sin_addr.s_addr, ipstr, INET_ADDRSTRLEN); - port = ((struct sockaddr_in*)addr)->sin_port; + if(vs->socket_family == AF_INET) { + struct sockaddr_in *in4 = (struct sockaddr_in *)addr; + if(in4->sin_addr.s_addr == INADDR_ANY) { + DEBUG_INFO("AF_INET, INADDR_ANY, binding to all interfaces"); + // grab first vtap + if(ZeroTier::vtaps.size()) { + tap = (ZeroTier::VirtualTap*)(ZeroTier::vtaps[0]); + } } - if(vs->socket_family == AF_INET6) { - inet_ntop(AF_INET6, - (const void *)&((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, ipstr, INET6_ADDRSTRLEN); - port = ((struct sockaddr_in6*)addr)->sin6_port; + } + if(vs->socket_family == AF_INET6) { + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr; + if(memcmp((void*)&(in6->sin6_addr), (void*)&(in6addr_any), sizeof(in6addr_any)) == 0) { + DEBUG_INFO("AF_INET6, in6addr_any, binding to all interfaces"); + if(ZeroTier::vtaps.size()) { + tap = (ZeroTier::VirtualTap*)(ZeroTier::vtaps[0]); + } } - DEBUG_EXTRA("fd = %d, %s : %d", fd, ipstr, ntohs(port)); - iaddr.fromString(ipstr); - tap = getTapByAddr(iaddr); + } - if(!tap) { - DEBUG_ERROR("no matching interface to bind to"); - errno = EADDRNOTAVAIL; - err = -1; - } + ZeroTier::InetAddress inet; + sockaddr2inet(vs->socket_family, addr, &inet); + char buf3[64]; + if(!tap) + tap = getTapByAddr(&inet); + + if(!tap) { + DEBUG_ERROR("no matching interface to bind to, could not find appropriate VirtualTap for fd=%d", fd); + errno = ENETUNREACH; + return -1; + } #if defined(STACK_PICO) - else { - vs->picosock->priv = new ZeroTier::VirtualBindingPair(tap, vs); - tap->_VirtualSockets.push_back(vs); // Give this VirtualSocket to the tap we decided on - err = tap->Bind(vs, addr, addrlen); - vs->tap = tap; - if(err == 0) { // success - ZeroTier::unmap.erase(fd); - ZeroTier::fdmap[fd] = new std::pair(vs, tap); - } - } -#endif -#if defined(STACK_LWIP) - else { - tap->_VirtualSockets.push_back(vs); - err = tap->Bind(vs, addr, addrlen); - vs->tap = tap; - if(err == 0) { // success - ZeroTier::unmap.erase(fd); - ZeroTier::fdmap[fd] = new std::pair(vs, tap); - } - } + // used in callbacks from network stack + vs->picosock->priv = new ZeroTier::VirtualBindingPair(tap, vs); #endif + tap->addVirtualSocket(vs); + err = tap->Bind(vs, addr, addrlen); + vs->tap = tap; + if(err == 0) { // success + del_unassigned_virtual_socket(fd); + add_assigned_virtual_socket(tap, vs, fd); } - else { - DEBUG_ERROR("unable to locate VirtualSocket"); - errno = EBADF; - err = -1; - } - ZeroTier::_multiplexer_lock.unlock(); return err; } @@ -738,8 +704,7 @@ Linux: [ ] [EOPNOTSUPP] The socket is not of a type that supports the listen() operation. */ int zts_listen(ZT_LISTEN_SIG) { - DEBUG_EXTRA("fd = %d", fd); - int err = 0; + int err = errno = 0; if(fd < 0) { errno = EBADF; return -1; @@ -763,12 +728,10 @@ int zts_listen(ZT_LISTEN_SIG) { errno = EBADF; return -1; } - if(!err) { - backlog = backlog > 128 ? 128 : backlog; // See: /proc/sys/net/core/somaxconn - err = tap->Listen(vs, backlog); - vs->state = ZT_SOCK_STATE_LISTENING; - ZeroTier::_multiplexer_lock.unlock(); - } + backlog = backlog > 128 ? 128 : backlog; // See: /proc/sys/net/core/somaxconn + err = tap->Listen(vs, backlog); + vs->state = ZT_SOCK_STATE_LISTENING; + ZeroTier::_multiplexer_lock.unlock(); return err; } @@ -787,8 +750,8 @@ Darwin: [ ] [ENFILE] The system file table is full. */ int zts_accept(ZT_ACCEPT_SIG) { - DEBUG_EXTRA("fd = %d", fd); - int err = 0; + int err = errno = 0; + //DEBUG_EXTRA("fd=%d", fd); if(fd < 0) { errno = EBADF; return -1; @@ -830,7 +793,8 @@ int zts_accept(ZT_ACCEPT_SIG) { } else { // blocking while(true) { - usleep(ZT_ACCEPT_RECHECK_DELAY * 1000); + //usleep(ZT_ACCEPT_RECHECK_DELAY * 1000); + nanosleep((const struct timespec[]){{0, (ZT_ACCEPT_RECHECK_DELAY * 1000000)}}, NULL); accepted_vs = tap->Accept(vs); if(accepted_vs) break; // accepted fd = err @@ -873,17 +837,37 @@ Errors In addition, Linux accept() may fail if: EPERM Firewall rules forbid VirtualSocket. + + + + SOCK_NONBLOCK Set the O_NONBLOCK file status flag on the new open + file description. Using this flag saves extra calls + to fcntl(2) to achieve the same result. + + SOCK_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the new + file descriptor. See the description of the + O_CLOEXEC flag in open(2) for reasons why this may be + useful. + + int fd, struct sockaddr *addr, socklen_t *addrlen, int flags */ #if defined(__linux__) int zts_accept4(ZT_ACCEPT4_SIG) { - DEBUG_INFO("fd = %d", fd); - int err = 0; + errno = 0; + //DEBUG_INFO("fd=%d", fd); if(fd < 0) { errno = EBADF; - err = -1; + return -1; } - return 0; + if(flags & SOCK_NONBLOCK) { + fcntl(fd, F_SETFL, O_NONBLOCK); + } + if(flags & SOCK_CLOEXEC) { + fcntl(fd, F_SETFL, FD_CLOEXEC); + } + addrlen = !addr ? 0 : addrlen; + return zts_accept(fd, addr, addrlen); } #endif @@ -900,14 +884,13 @@ EPERM Firewall rules forbid VirtualSocket. */ int zts_setsockopt(ZT_SETSOCKOPT_SIG) { + int err = errno = 0; #if defined(STACK_PICO) - DEBUG_INFO("fd = %d", fd); - int err = 0; + //DEBUG_INFO("fd=%d", fd); if(fd < 0) { errno = EBADF; err = -1; } - // Disable Nagle's algorithm struct pico_socket *p = NULL; err = zts_get_pico_socket(fd, &p); @@ -941,8 +924,7 @@ int zts_setsockopt(ZT_SETSOCKOPT_SIG) */ int zts_getsockopt(ZT_GETSOCKOPT_SIG) { - //DEBUG_INFO("fd = %d", fd); - int err = 0; + int err = errno = 0; if(fd < 0) { errno = EBADF; err = -1; @@ -961,8 +943,7 @@ int zts_getsockopt(ZT_GETSOCKOPT_SIG) */ int zts_getsockname(ZT_GETSOCKNAME_SIG) { - DEBUG_INFO("fd = %d", fd); - int err = 0; + int err = errno = 0; if(fd < 0) { errno = EBADF; err = -1; @@ -985,7 +966,7 @@ Linux: */ int zts_getpeername(ZT_GETPEERNAME_SIG) { - int err = 0; + int err = errno = 0; if(fd < 0) { errno = EBADF; err = -1; @@ -1015,6 +996,7 @@ Linux: int zts_gethostname(ZT_GETHOSTNAME_SIG) { + errno = 0; return gethostname(name, len); } @@ -1034,6 +1016,7 @@ Linux: int zts_sethostname(ZT_SETHOSTNAME_SIG) { + errno = 0; return sethostname(name, len); } @@ -1052,126 +1035,66 @@ Linux / Darwin: int zts_close(ZT_CLOSE_SIG) { -#if defined(STACK_PICO) - DEBUG_EXTRA("fd = %d", fd); - int err = 0; + int err = errno = 0; + DEBUG_EXTRA("fd=%d", fd); if(fd < 0) { errno = EBADF; - err = -1; + return -1; } - else - { - if(!ZeroTier::zt1Service) { - DEBUG_ERROR("cannot close socket. service not started. call zts_start(path) first"); - errno = EBADF; - err = -1; - } - else - { - ZeroTier::_multiplexer_lock.lock(); - //DEBUG_INFO("unmap=%d, fdmap=%d", ZeroTier::unmap.size(), ZeroTier::fdmap.size()); - // First, look for for unassigned VirtualSockets - ZeroTier::VirtualSocket *vs = ZeroTier::unmap[fd]; - - // Since we found an unassigned VirtualSocket, we don't need to consult the stack or tap - // during closure - it isn't yet stitched into the clockwork - if(vs) // unassigned - { - DEBUG_ERROR("unassigned closure"); - if((err = pico_socket_close(vs->picosock)) < 0) - DEBUG_ERROR("error calling pico_socket_close()"); - if((err = close(vs->app_fd)) < 0) - DEBUG_ERROR("error closing app_fd"); - if((err = close(vs->sdk_fd)) < 0) - DEBUG_ERROR("error closing sdk_fd"); - delete vs; - vs = NULL; - ZeroTier::unmap.erase(fd); - } - else // assigned - { - std::pair *p = ZeroTier::fdmap[fd]; - if(!p) - { - DEBUG_ERROR("unable to locate VirtualSocket pair."); - errno = EBADF; - err = -1; - } - else // found everything, begin closure - { - vs = p->first; - ZeroTier::VirtualTap *tap = p->second; - - // check if socket is blocking - int f_err, blocking = 1; - if ((f_err = fcntl(fd, F_GETFL, 0)) < 0) { - DEBUG_ERROR("fcntl error, err = %s, errno = %d", f_err, errno); - err = -1; - } - else { - blocking = !(f_err & O_NONBLOCK); - } - - if(blocking) { - DEBUG_INFO("blocking, waiting for write operations before closure..."); - for(int i=0; iTXbuf->count() == 0) - break; - usleep(ZT_API_CHECK_INTERVAL * 1000); - } - } - - // For cases where data might still need to pass through the library - // before socket closure - - /* - if(ZT_SOCK_BEHAVIOR_LINGER) { - socklen_t optlen; - struct linger so_linger; - so_linger.l_linger = 0; - zts_getsockopt(fd, SOL_SOCKET, SO_LINGER, &so_linger, &optlen); - //DEBUG_ERROR("fd = %d, value = %d", fd, so_linger.l_linger); - // if (so_linger.l_linger != 0) { - DEBUG_EXTRA("lingering before closure for (%d) seconds...", so_linger.l_linger); - sleep(3); // do the linger! - // } - } - else - { - DEBUG_ERROR("LINGER NOT enabled"); - } - */ - - //DEBUG_INFO("s->state = %s", ZeroTier::picoTCP::beautify_pico_state(vs->picosock->state)); - tap->Close(vs); - delete vs; - vs = NULL; - ZeroTier::fdmap.erase(fd); - err = 0; - } - } - //DEBUG_INFO(" unmap=%d, fdmap=%d", ZeroTier::unmap.size(), ZeroTier::fdmap.size()); - ZeroTier::_multiplexer_lock.unlock(); - } + if(!ZeroTier::zt1Service) { + DEBUG_ERROR("cannot close socket. service not started. call zts_start(path) first"); + errno = EBADF; + return -1; } + ZeroTier::VirtualSocket *vs = get_virtual_socket(fd); + if(!vs) { + DEBUG_ERROR("no vs found for fd=%d", fd); + errno = EBADF; + return -1; + } + if(vs->tap) { + vs->tap->Close(vs); + } + delete vs; + vs = NULL; + del_virtual_socket(fd); return err; -#endif - return 0; + + /* + // check if socket is blocking + int f_err, blocking = 1; + if ((f_err = fcntl(fd, F_GETFL, 0)) < 0) { + DEBUG_ERROR("fcntl error, err = %s, errno = %d", f_err, errno); + err = -1; + } + else { + blocking = !(f_err & O_NONBLOCK); + } + if(blocking) { + for(int i=0; iTXbuf->count() == 0) + break; + usleep(ZT_API_CHECK_INTERVAL * 1000); + } + } + */ } int zts_poll(ZT_POLL_SIG) { + errno = 0; return poll(fds, nfds, timeout); } int zts_select(ZT_SELECT_SIG) { + errno = 0; return select(nfds, readfds, writefds, exceptfds, timeout); } int zts_fcntl(ZT_FCNTL_SIG) { - int err = 0; + int err = errno = 0; if(fd < 0) { errno = EBADF; err = -1; @@ -1182,27 +1105,6 @@ int zts_fcntl(ZT_FCNTL_SIG) return err; } -/* - struct ifreq { - char ifr_name[IFNAMSIZ]; - union { - struct sockaddr ifr_addr; - struct sockaddr ifr_dstaddr; - struct sockaddr ifr_broadaddr; - struct sockaddr ifr_netmask; - struct sockaddr ifr_hwaddr; - short ifr_flags; - int ifr_ifindex; - int ifr_metric; - int ifr_mtu; - struct ifmap ifr_map; - char ifr_slave[IFNAMSIZ]; - char ifr_newname[IFNAMSIZ]; - char *ifr_data; - }; - }; -*/ - /* [ ] [BADF] fd is not a valid file descriptor. [ ] [EFAULT] argp references an inaccessible memory area. @@ -1212,7 +1114,7 @@ int zts_fcntl(ZT_FCNTL_SIG) int zts_ioctl(ZT_IOCTL_SIG) { - int err = 0; + int err = errno = 0; if(fd < 0) { errno = EBADF; err = -1; @@ -1290,15 +1192,15 @@ Linux: */ ssize_t zts_sendto(ZT_SENDTO_SIG) { - //DEBUG_INFO("fd = %d", fd); - int err = 0; + //DEBUG_INFO(); + int err = errno = 0; if(fd < 0) { errno = EBADF; return -1; } ZeroTier::VirtualSocket *vs = get_virtual_socket(fd); if(!vs) { - DEBUG_ERROR("invalid vs"); + DEBUG_ERROR("no vs found for fd=%x", fd); handle_general_failure(); errno = EBADF; return -1; @@ -1307,7 +1209,7 @@ ssize_t zts_sendto(ZT_SENDTO_SIG) ZeroTier::InetAddress iaddr; ZeroTier::VirtualTap *tap; char ipstr[INET6_ADDRSTRLEN]; - int port; + // int port; memset(ipstr, 0, INET6_ADDRSTRLEN); if(vs->socket_type == SOCK_DGRAM) { @@ -1316,7 +1218,7 @@ ssize_t zts_sendto(ZT_SENDTO_SIG) inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)addr)->sin_addr.s_addr, ipstr, INET_ADDRSTRLEN); iaddr.fromString(ipstr); - port = ((struct sockaddr_in*)addr)->sin_port; + // port = ((struct sockaddr_in*)addr)->sin_port; } if(vs->socket_family == AF_INET6) { inet_ntop(AF_INET6, @@ -1325,10 +1227,10 @@ ssize_t zts_sendto(ZT_SENDTO_SIG) char addrstr[64]; sprintf(addrstr, "%s%s", ipstr, std::string("/40").c_str()); iaddr.fromString(addrstr); - port = ((struct sockaddr_in6*)addr)->sin6_port; + // port = ((struct sockaddr_in6*)addr)->sin6_port; } // get tap - tap = getTapByAddr(iaddr); + tap = getTapByAddr(&iaddr); if(!tap) { DEBUG_INFO("SOCK_DGRAM, tap not found"); errno = EDESTADDRREQ; // TODO: double check this is the best errno to report @@ -1343,21 +1245,11 @@ ssize_t zts_sendto(ZT_SENDTO_SIG) if(vs->socket_type == SOCK_RAW) { struct sockaddr_ll *socket_address = (struct sockaddr_ll *)addr; ZeroTier::VirtualTap *tap = getTapByIndex(socket_address->sll_ifindex); - if(tap) - { + if(tap) { DEBUG_INFO("found interface of ifindex=%d", tap->ifindex); - if(vs) { - DEBUG_INFO("located VirtualSocket object for fd=%d", fd); - err = tap->Write(vs, (void*)buf, len); - } - else { - DEBUG_ERROR("unable to locate VirtualSocket object for fd=%d", fd); - err = -1; - errno = EINVAL; - } + err = tap->Write(vs, (void*)buf, len); } - else - { + else { DEBUG_ERROR("unable to locate tap of ifindex=%d", socket_address->sll_ifindex); err = -1; errno = EINVAL; @@ -1371,39 +1263,39 @@ ssize_t zts_sendto(ZT_SENDTO_SIG) Linux: [ ] EACCES (For UNIX domain sockets, which are identified by pathname) - Write permission is denied on the destination socket file, or - search permission is denied for one of the directories the - path prefix. (See path_resolution(7).) + Write permission is denied on the destination socket file, or + search permission is denied for one of the directories the + path prefix. (See path_resolution(7).) - (For UDP sockets) An attempt was made to send to a - network/broadcast address as though it was a unicast address. + (For UDP sockets) An attempt was made to send to a + network/broadcast address as though it was a unicast address. [ ] EAGAIN or EWOULDBLOCK The socket is marked nonblocking and the requested operation - would block. 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. + would block. 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. [ ] EAGAIN (Internet domain datagram sockets) The socket referred to by - sockfd had not previously been bound to an address and, upon - attempting to bind it to an ephemeral port, it was determined - that all port numbers in the ephemeral port range are - currently in use. See the discussion of - /proc/sys/net/ipv4/ip_local_port_range in ip(7). - [ ] EBADF sockfd is not a valid open file descriptor. + sockfd had not previously been bound to an address and, upon + attempting to bind it to an ephemeral port, it was determined + that all port numbers in the ephemeral port range are + currently in use. See the discussion of + /proc/sys/net/ipv4/ip_local_port_range in ip(7). + [--] EBADF sockfd is not a valid open file descriptor. [ ] ECONNRESET Connection reset by peer. - [ ] EDESTADDRREQ The socket is not connection-mode, and no peer address is set. + [--] EDESTADDRREQ The socket is not connection-mode, and no peer address is set. [ ] EFAULT An invalid user space address was specified for an argument. [ ] EINTR A signal occurred before any data was transmitted - [ ] EINVAL Invalid argument passed. + [--] EINVAL Invalid argument passed. [ ] EISCONN The connection-mode socket was connected already but a recipient was specified. (Now either this error is returned, or the recipient specification is ignored.) [ ] EMSGSIZE The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible. [ ] ENOBUFS The output queue for a network interface was full. This - generally indicates that the interface has stopped sending, - but may be caused by transient congestion. (Normally, this - does not occur in Linux. Packets are just silently dropped - when a device queue overflows.) + generally indicates that the interface has stopped sending, + but may be caused by transient congestion. (Normally, this + does not occur in Linux. Packets are just silently dropped + when a device queue overflows.) [ ] ENOMEM No memory available. [ ] ENOTCONN The socket is not connected, and no target has been given. [ ] ENOTSOCK The file descriptor sockfd does not refer to a socket. @@ -1416,14 +1308,75 @@ ssize_t zts_sendto(ZT_SENDTO_SIG) */ ssize_t zts_send(ZT_SEND_SIG) { - return send(fd, buf, len, flags); + int err = errno = 0; + ZeroTier::VirtualSocket *vs = get_virtual_socket(fd); + if(!vs) { + DEBUG_ERROR("invalid vs for fd=%d", fd); + errno = EBADF; + return -1; + } + if(vs->socket_type != SOCK_STREAM) { + DEBUG_ERROR("the socket is not connection-mode, and no peer address is set for fd=%d", fd); + errno = EDESTADDRREQ; + return -1; + } + if(flags & MSG_DONTROUTE) { + DEBUG_INFO("MSG_DONTROUTE not implemented yet"); + errno = EINVAL; + return -1; + } + if(flags & MSG_DONTWAIT) { + // The stack drivers and stack are inherently non-blocking by design, but we + // still need to modify the unix pipe connecting them to the application: + fcntl(fd, F_SETFL, O_NONBLOCK); + } + if(flags & MSG_EOR) { + DEBUG_INFO("MSG_EOR not implemented yet"); + errno = EINVAL; + return -1; + } + if(flags & MSG_OOB) { + DEBUG_INFO("MSG_OOB not implemented yet"); + errno = EINVAL; + return -1; + } +#if defined(__linux__) + if(flags & MSG_CONFIRM) { + DEBUG_INFO("MSG_CONFIRM not implemented yet"); + errno = EINVAL; + return -1; + } + if(flags & MSG_MORE) { + DEBUG_INFO("MSG_MORE not implemented yet"); + errno = EINVAL; + return -1; + } + if(flags & MSG_NOSIGNAL) { + DEBUG_INFO("MSG_NOSIGNAL not implemented yet"); + errno = EINVAL; + return -1; + } +#endif + + err = write(fd, buf, len); + + // restore "per-call" flags + + if(flags & MSG_DONTWAIT) { + int saved_flags = fcntl(fd, F_GETFL); + if(fcntl(fd, F_SETFL, saved_flags & ~O_NONBLOCK) < 0) { // mask out the blocking flag + handle_general_failure(); + return -1; + } + } + return err; } // TODO ssize_t zts_sendmsg(ZT_SENDMSG_SIG) { DEBUG_INFO("fd = %d", fd); - int err = 0; + int err = errno = 0; if(fd < 0) { errno = EBADF; err = -1; @@ -1445,7 +1398,7 @@ ssize_t zts_sendmsg(ZT_SENDMSG_SIG) [ ] EAGAIN or EWOULDBLOCK The socket is marked nonblocking and the receive operation would block, or a receive timeout had been set and the timeout expired before data was received. - [ ] EBADF The argument sockfd is an invalid file descriptor. + [--] EBADF The argument sockfd is an invalid file descriptor. [ ] ECONNREFUSED A remote host refused to allow the network connection (typically because it is not running the requested service). @@ -1453,14 +1406,74 @@ ssize_t zts_sendmsg(ZT_SENDMSG_SIG) address space. [ ] EINTR The receive was interrupted by delivery of a signal before any data were available; see signal(7). - [ ] EINVAL Invalid argument passed. + [--] EINVAL Invalid argument passed. [ ] ENOMEM Could not allocate memory for recvmsg(). [ ] ENOTCONN The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and accept(2)). [ ] ENOTSOCK The file descriptor sockfd does not refer to a socket. */ ssize_t zts_recv(ZT_RECV_SIG) { - return recv(fd, buf, len, flags); + int err = errno = 0; + ZeroTier::VirtualSocket *vs = get_virtual_socket(fd); + if(!vs) { + DEBUG_ERROR("invalid vs for fd=%d", fd); + errno = EBADF; + return -1; + } + if(vs->socket_type != SOCK_STREAM) { + DEBUG_ERROR("the socket is not connection-mode, and no peer address is set for fd=%d", fd); + errno = EDESTADDRREQ; + return -1; + } + if(vs->state != ZT_SOCK_STATE_CONNECTED) { + DEBUG_ERROR("the socket is not in a connected state, fd=%d", fd); + errno = ENOTCONN; + return -1; + } + if(flags & MSG_DONTWAIT) { + // The stack drivers and stack are inherently non-blocking by design, but we + // still need to modify the unix pipe connecting them to the application: + fcntl(fd, F_SETFL, O_NONBLOCK); + } + if(flags & MSG_OOB) { + DEBUG_INFO("MSG_OOB not implemented yet"); + errno = EINVAL; + return -1; + } + if(flags & MSG_TRUNC) { + DEBUG_INFO("MSG_TRUNC not implemented yet"); + errno = EINVAL; + return -1; + } + if(flags & MSG_WAITALL) { + DEBUG_INFO("MSG_WAITALL not implemented yet"); + errno = EINVAL; + return -1; + } +#if defined(__linux__) + if(flags & MSG_ERRQUEUE) { + DEBUG_INFO("MSG_ERRQUEUE not implemented yet"); + errno = EINVAL; + return -1; + } +#endif + + if(flags & MSG_PEEK) { + // MSG_PEEK doesn't require any special stack-related machinery so we can just + // pass it to a regular recv() call with no issue + err = recv(fd, buf, len, MSG_PEEK); + } + + // restore "per-call" flags + + if(flags & MSG_DONTWAIT) { + int saved_flags = fcntl(fd, F_GETFL); + if(fcntl(fd, F_SETFL, saved_flags & ~O_NONBLOCK) < 0) { // mask out the blocking flag + handle_general_failure(); + return -1; + } + } + return err; } /* @@ -1487,8 +1500,8 @@ ssize_t zts_recv(ZT_RECV_SIG) */ ssize_t zts_recvfrom(ZT_RECVFROM_SIG) { - //DEBUG_INFO("fd = %d", fd); - int r = 0; + //DEBUG_INFO(); + int r = 0, err = errno = 0; if(fd < 0) { errno = EBADF; return -1; @@ -1510,8 +1523,8 @@ ssize_t zts_recvfrom(ZT_RECVFROM_SIG) // TODO ssize_t zts_recvmsg(ZT_RECVMSG_SIG) { - DEBUG_INFO("fd = %d", fd); - int err = 0; + DEBUG_INFO("fd=%d", fd); + int err = errno = 0; if(fd < 0) { errno = EBADF; err = -1; @@ -1528,16 +1541,18 @@ int zts_read(ZT_READ_SIG) { } int zts_write(ZT_WRITE_SIG) { - //DEBUG_INFO("fd = %d", fd); + // DEBUG_INFO("fd = %d", fd); return write(fd, buf, len); } int zts_shutdown(ZT_SHUTDOWN_SIG) { + /* + int err = errno = 0; #if defined(STACK_PICO) DEBUG_INFO("fd = %d", fd); - int err = 0, mode = 0; + int mode = 0; if(how == SHUT_RD) mode = PICO_SHUT_RD; if(how == SHUT_WR) mode = PICO_SHUT_WR; if(how == SHUT_RDWR) mode = PICO_SHUT_RDWR; @@ -1563,13 +1578,14 @@ int zts_shutdown(ZT_SHUTDOWN_SIG) if(vs) // unassigned { DEBUG_ERROR("unassigned shutdown"); - /* - PICO_SHUT_RD - PICO_SHUT_WR - PICO_SHUT_RDWR - */ + + // PICO_SHUT_RD + // PICO_SHUT_WR + // PICO_SHUT_RDWR + if((err = pico_socket_shutdown(vs->picosock, mode)) < 0) DEBUG_ERROR("error calling pico_socket_shutdown()"); + DEBUG_ERROR("vs=%p", vs); delete vs; vs = NULL; ZeroTier::unmap.erase(fd); @@ -1600,7 +1616,9 @@ int zts_shutdown(ZT_SHUTDOWN_SIG) for(int i=0; iTXbuf->count() == 0) break; - usleep(ZT_API_CHECK_INTERVAL * 1000); + //usleep(ZT_API_CHECK_INTERVAL * 1000); + nanosleep((const struct timespec[]){{0, (ZT_API_CHECK_INTERVAL * 1000000)}}, NULL); + } } @@ -1613,12 +1631,25 @@ int zts_shutdown(ZT_SHUTDOWN_SIG) } return err; #endif +*/ + return 0; +} + + +int zts_add_dns_nameserver(struct sockaddr *addr) +{ + errno = 0; + return 0; +} + +int zts_remove_dns_nameserver(struct sockaddr *addr) +{ + errno = 0; return 0; } /****************************************************************************/ -/* SDK Socket API (Java Native Interface JNI) */ -/* JNI naming convention: Java_PACKAGENAME_CLASSNAME_METHODNAME */ +/* SDK Socket API (Java Native Interface JNI) /* JNI naming convention: Java_PACKAGENAME_CLASSNAME_METHODNAME */ /****************************************************************************/ @@ -1878,6 +1909,18 @@ namespace ZeroTier { /* SDK Socket API Helper functions --- DON'T CALL THESE DIRECTLY */ /****************************************************************************/ +void zts_start_dns_client() +{ + // Coming soon + + /* + struct pico_ip4 *ns; + uint8_t flag = PICO_DNS_NS_ADD; // PICO_DNS_NS_DEL, PICO_DNS_NS_ADD + pico_dns_client_nameserver(ns, flag); + */ +} + + #if defined(STACK_PICO) int zts_get_pico_socket(int fd, struct pico_socket **s) { @@ -1887,8 +1930,7 @@ int zts_get_pico_socket(int fd, struct pico_socket **s) errno = EBADF; err = -1; } - else - { + else { ZeroTier::_multiplexer_lock.lock(); // First, look for for unassigned VirtualSockets ZeroTier::VirtualSocket *vs = ZeroTier::unmap[fd]; @@ -1930,7 +1972,9 @@ bool can_provision_new_socket() // TODO: Add check here (see lwipopts.h) return true; #endif - return false; +#if defined(NO_STACK) + return true; // always true since there's no network stack timer limitation +#endif } int zts_nsockets() @@ -1947,7 +1991,12 @@ int zts_maxsockets() // TODO: This is only an approximation return PICO_MAX_TIMERS - 10; #endif +#if defined(STACK_LWIP) return 32; +#endif +#if defined(NO_STACK) + return 1024; // arbitrary +#endif } /****************************************************************************/ @@ -1972,7 +2021,7 @@ ZeroTier::VirtualTap *getTapByNWID(uint64_t nwid) return tap; } -ZeroTier::VirtualTap *getTapByAddr(ZeroTier::InetAddress &addr) +ZeroTier::VirtualTap *getTapByAddr(ZeroTier::InetAddress *addr) { ZeroTier::_vtaps_lock.lock(); ZeroTier::VirtualTap *s, *tap = nullptr; @@ -1981,14 +2030,18 @@ ZeroTier::VirtualTap *getTapByAddr(ZeroTier::InetAddress &addr) s = (ZeroTier::VirtualTap*)ZeroTier::vtaps[i]; // check address schemes for(int j=0; j_ips.size(); j++) { - DEBUG_INFO("looking at tap for <%s>", s->_ips[j].toString(ipbuf), addr.toString(ipbuf2)); - if(s->_ips[j].isEqualPrefix(addr) - || s->_ips[j].ipsEqual(addr) - || s->_ips[j].containsAddress(addr)) - { - DEBUG_INFO("chose tap ", s->_ips[j].toString(ipbuf)); - tap = s; - break; + if((s->_ips[j].isV4() && addr->isV4()) || (s->_ips[j].isV6() && addr->isV6())) { + //DEBUG_INFO("looking at tap %s, --- for <%s>", s->_dev.c_str(), s->_ips[j].toString(ipbuf), addr->toIpString(ipbuf2)); + if(s->_ips[j].isEqualPrefix(addr) + || s->_ips[j].ipsEqual(addr) + || s->_ips[j].containsAddress(addr) + || (addr->isV6() && ipv6_in_subnet(&s->_ips[j], addr)) + ) + { + //DEBUG_INFO("selected tap %s, ", s->_dev.c_str(), s->_ips[j].toString(ipbuf)); + ZeroTier::_vtaps_lock.unlock(); + return s; + } } } // check managed routes @@ -2001,8 +2054,8 @@ ZeroTier::VirtualTap *getTapByAddr(ZeroTier::InetAddress &addr) via = managed_routes->at(i).via; if(target.containsAddress(addr)) { // DEBUG_INFO("chose tap with route ", target.toString(ipbuf), nm.toString(ipbuf2), via.toString(ipbuf3)); - tap = s; - break; + ZeroTier::_vtaps_lock.unlock(); + return s; } } } @@ -2039,6 +2092,10 @@ ZeroTier::VirtualTap *getTapByIndex(int index) return tap; } +/****************************************************************************/ +/* VirtualSocket / VirtualTap helper functions - DON'T CALL THESE DIRECTLY */ +/****************************************************************************/ + ZeroTier::VirtualSocket *get_virtual_socket(int fd) { ZeroTier::_multiplexer_lock.lock(); @@ -2055,10 +2112,47 @@ ZeroTier::VirtualSocket *get_virtual_socket(int fd) return vs; } +void del_virtual_socket(int fd) +{ + ZeroTier::_multiplexer_lock.lock(); + ZeroTier::unmap.erase(fd); + ZeroTier::fdmap.erase(fd); + ZeroTier::_multiplexer_lock.unlock(); +} + +void add_unassigned_virtual_socket(int fd, ZeroTier::VirtualSocket *vs) +{ + ZeroTier::_multiplexer_lock.lock(); + ZeroTier::unmap[fd] = vs; + ZeroTier::_multiplexer_lock.unlock(); +} + +void del_unassigned_virtual_socket(int fd) +{ + ZeroTier::_multiplexer_lock.lock(); + ZeroTier::unmap.erase(fd); + ZeroTier::_multiplexer_lock.unlock(); +} + +void add_assigned_virtual_socket(ZeroTier::VirtualTap *tap, ZeroTier::VirtualSocket *vs, int fd) +{ + ZeroTier::_multiplexer_lock.lock(); + ZeroTier::fdmap[fd] = new std::pair(vs, tap); + ZeroTier::_multiplexer_lock.unlock(); +} + +void del_assigned_virtual_socket(ZeroTier::VirtualTap *tap, ZeroTier::VirtualSocket *vs, int fd) +{ + ZeroTier::_multiplexer_lock.lock(); + ZeroTier::fdmap.erase(fd); + ZeroTier::_multiplexer_lock.unlock(); +} + void dismantleTaps() { ZeroTier::_vtaps_lock.lock(); for(int i=0; irun()) { @@ -2126,6 +2219,7 @@ void *zts_start_service(void *thread_id) { case ZeroTier::OneService::ONE_NORMAL_TERMINATION: break; case ZeroTier::OneService::ONE_UNRECOVERABLE_ERROR: + DEBUG_ERROR("ZTO service port = %d", servicePort); DEBUG_ERROR("fatal error: %s",ZeroTier::zt1Service->fatalErrorMessage().c_str()); break; case ZeroTier::OneService::ONE_IDENTITY_COLLISION: { diff --git a/src/picoTCP.cpp b/src/picoTCP.cpp index 291e80b..364de0e 100644 --- a/src/picoTCP.cpp +++ b/src/picoTCP.cpp @@ -35,6 +35,7 @@ #include "pico_socket.h" #include "pico_device.h" #include "pico_ipv6.h" +#include "pico_tcp.h" #include "libzt.h" #include "Utilities.hpp" @@ -79,64 +80,80 @@ int pico_socket_shutdown(PICO_SOCKET_SHUTDOWN_SIG); struct pico_socket * pico_socket_accept(PICO_SOCKET_ACCEPT_SIG); */ +extern std::vector vtaps; + +/* + * Whether our picoTCP device has been initialized + */ +static bool picodev_initialized; + namespace ZeroTier { struct pico_device picodev; + ZeroTier::Mutex _picostack_driver_lock; - bool picoTCP::pico_init_interface(VirtualTap *tap, const InetAddress &ip) + bool picoTCP::pico_init_interface(VirtualTap *tap) { - char ipbuf[64]; - uint8_t hwaddr[6]; - 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(!tap->picodev_initialized) - { - picodev.send = pico_eth_tx; // tx - picodev.poll = pico_eth_poll; // calls pico_eth_rx - picodev.mtu = tap->_mtu; - picodev.tap = tap; - uint8_t mac[PICO_SIZE_ETH]; - tap->_mac.copyTo(mac, PICO_SIZE_ETH); - if(pico_device_init(&picodev, tap->_dev.c_str(), mac) != 0) { - DEBUG_ERROR("dev init failed"); - handle_general_failure(); - return false; - } - tap->picodev_initialized = true; - } - if(ip.isV4()) - { - struct pico_ip4 ipaddr, netmask; - ipaddr.addr = *((uint32_t *)ip.rawIpData()); - netmask.addr = *((uint32_t *)ip.netmask().rawIpData()); - pico_ipv4_link_add(&picodev, ipaddr, netmask); - DEBUG_INFO("addr=%s", ip.toString(ipbuf)); - tap->_mac.copyTo(hwaddr, 6); - char macbuf[18]; - mac2str(macbuf, sizeof(macbuf), hwaddr); - DEBUG_INFO("mac=%s", macbuf); - return true; - } - if(ip.isV6()) - { - char ipv6_str[INET6_ADDRSTRLEN], nm_str[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, ip.rawIpData(), ipv6_str, INET6_ADDRSTRLEN); - inet_ntop(AF_INET6, ip.netmask().rawIpData(), nm_str, INET6_ADDRSTRLEN); - struct pico_ip6 ipaddr, netmask; - pico_string_to_ipv6(ipv6_str, ipaddr.addr); - pico_string_to_ipv6(nm_str, netmask.addr); - pico_ipv6_link_add(&picodev, ipaddr, netmask); - DEBUG_INFO("addr=%s", ipv6_str); - tap->_mac.copyTo(hwaddr, 6); - char macbuf[18]; - mac2str(macbuf, sizeof(macbuf), hwaddr); - DEBUG_INFO("mac=%s", macbuf); - return true; + bool err = false; + _picostack_driver_lock.lock(); + // give right to vtap to start the stack + // only one stack loop is permitted + if(!picodev_initialized) { + tap->should_start_stack = true; + picodev.send = pico_eth_tx; // tx + picodev.poll = pico_eth_poll; // calls pico_eth_rx + picodev.mtu = tap->_mtu; + picodev.tap = tap; + uint8_t mac[PICO_SIZE_ETH]; + tap->_mac.copyTo(mac, PICO_SIZE_ETH); + if(pico_device_init(&picodev, tap->_dev.c_str(), mac) != 0) { + DEBUG_ERROR("dev init failed"); + handle_general_failure(); + err = false; } + picodev_initialized = true; + err = true; } - return false; + _picostack_driver_lock.unlock(); + return err; + } + + bool picoTCP::pico_register_address(VirtualTap *tap, const InetAddress &ip) + { + _picostack_driver_lock.lock(); + bool err = false; + char ipbuf[64]; + uint8_t hwaddr[6]; + // register addresses + if(ip.isV4()) { + struct pico_ip4 ipaddr, netmask; + ipaddr.addr = *((uint32_t *)ip.rawIpData()); + netmask.addr = *((uint32_t *)ip.netmask().rawIpData()); + pico_ipv4_link_add(&picodev, ipaddr, netmask); + DEBUG_INFO("addr=%s", ip.toString(ipbuf)); + tap->_mac.copyTo(hwaddr, 6); + char macbuf[18]; + mac2str(macbuf, sizeof(macbuf), hwaddr); + DEBUG_INFO("mac=%s", macbuf); + err = true; + } + if(ip.isV6()) { + char ipv6_str[INET6_ADDRSTRLEN], nm_str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, ip.rawIpData(), ipv6_str, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, ip.netmask().rawIpData(), nm_str, INET6_ADDRSTRLEN); + struct pico_ip6 ipaddr, netmask; + pico_string_to_ipv6(ipv6_str, ipaddr.addr); + pico_string_to_ipv6(nm_str, netmask.addr); + pico_ipv6_link_add(&picodev, ipaddr, netmask); + DEBUG_INFO("addr=%s", ipv6_str); + tap->_mac.copyTo(hwaddr, 6); + char macbuf[18]; + mac2str(macbuf, sizeof(macbuf), hwaddr); + DEBUG_INFO("mac=%s", macbuf); + err = true; + } + _picostack_driver_lock.unlock(); + return err; } // TODO: @@ -177,7 +194,9 @@ namespace ZeroTier { while(tap->_run) { tap->_phy.poll(ZT_PHY_POLL_INTERVAL); + //_picostack_driver_lock.lock(); pico_stack_tick(); + //_picostack_driver_lock.unlock(); tap->Housekeeping(); } } @@ -185,7 +204,12 @@ namespace ZeroTier { // from stack socket to app socket void picoTCP::pico_cb_tcp_read(ZeroTier::VirtualTap *tap, struct pico_socket *s) { - VirtualSocket *vs = (VirtualSocket*)((VirtualBindingPair*)(s->priv))->vs; + VirtualSocket *vs = (VirtualSocket*)(((VirtualBindingPair*)s->priv)->vs); + if(!vs) { + DEBUG_ERROR("s->priv yielded no valid vs"); + handle_general_failure(); + return; + } Mutex::Lock _l(vs->_rx_m); if(!tap) { @@ -211,6 +235,12 @@ namespace ZeroTier { //DEBUG_INFO("RXbuf->count() = %d", vs->RXbuf->count()); int avail = ZT_TCP_RX_BUF_SZ - vs->RXbuf->count(); if(avail) { + DEBUG_INFO("vs->RXbuf->get_buf()= %p", vs->RXbuf->get_buf()); + DEBUG_INFO("vs->RXbuf->count() = %d", vs->RXbuf->count()); + DEBUG_INFO("s = %p", s); + DEBUG_INFO("avail = %d", avail); + DEBUG_INFO("tap = %p", tap); + DEBUG_INFO("peer.ip4.addr = %p", peer.ip4.addr); r = pico_socket_recvfrom(s, vs->RXbuf->get_buf(), ZT_STACK_SOCKET_RD_MAX, (void *)&peer.ip4.addr, &port); if (r > 0) @@ -245,7 +275,12 @@ namespace ZeroTier { void picoTCP::pico_cb_udp_read(VirtualTap *tap, struct pico_socket *s) { // DEBUG_INFO(); - VirtualSocket *vs = (VirtualSocket*)((VirtualBindingPair*)(s->priv))->vs; + VirtualSocket *vs = (VirtualSocket*)(((VirtualBindingPair*)s->priv)->vs); + if(!vs) { + DEBUG_ERROR("s->priv yielded no valid vs"); + handle_general_failure(); + return; + } Mutex::Lock _l(vs->_rx_m); if(!tap) { @@ -264,8 +299,8 @@ namespace ZeroTier { struct pico_ip4 ip4; struct pico_ip6 ip6; } peer; - int r = 0; - + int r = 0, w = 0; + // TODO: Consolidate this if(vs->socket_family == AF_INET) { struct sockaddr_in in4; char udp_payload_buf[ZT_MAX_MTU]; @@ -282,7 +317,9 @@ namespace ZeroTier { memcpy(udp_msg_buf, &len, sizeof(len)); // len: sockaddr+payload memcpy(udp_msg_buf + sizeof(len), &in4, sizeof(in4)); // sockaddr memcpy(udp_msg_buf + sizeof(len) + sizeof(in4), &udp_payload_buf, r); // payload - int w = write(vs->sdk_fd, udp_msg_buf, tot_len); + if((w = write(vs->sdk_fd, udp_msg_buf, tot_len)) < 0) { + DEBUG_ERROR("write()=%d, errno=%d", w, errno); + } } if(vs->socket_family == AF_INET6) { struct sockaddr_in6 in6; @@ -300,13 +337,20 @@ namespace ZeroTier { memcpy(udp_msg_buf, &len, sizeof(len)); // len: sockaddr+payload memcpy(udp_msg_buf + sizeof(len), &in6, sizeof(in6)); // sockaddr memcpy(udp_msg_buf + sizeof(len) + sizeof(in6), &udp_payload_buf, r); // payload - int w = write(vs->sdk_fd, udp_msg_buf, tot_len); + if((w = write(vs->sdk_fd, udp_msg_buf, tot_len)) < 0) { + DEBUG_ERROR("write()=%d, errno=%d", w, errno); + } } } void picoTCP::pico_cb_tcp_write(VirtualTap *tap, struct pico_socket *s) { - VirtualSocket *vs = (VirtualSocket*)((VirtualBindingPair*)(s->priv))->vs; + VirtualSocket *vs = (VirtualSocket*)(((VirtualBindingPair*)s->priv)->vs); + if(!vs) { + DEBUG_ERROR("s->priv yielded no valid vs"); + handle_general_failure(); + return; + } Mutex::Lock _l(vs->_tx_m); if(!vs) { DEBUG_ERROR("invalid VirtualSocket"); @@ -340,32 +384,31 @@ namespace ZeroTier { vs->TXbuf->consume(r); } - void picoTCP::pico_cb_socket_activity(uint16_t ev, struct pico_socket *s) + void picoTCP::pico_cb_socket_ev(uint16_t ev, struct pico_socket *s) { - if(!(VirtualTap*)((VirtualBindingPair*)(s->priv))) - return; - VirtualTap *tap = (VirtualTap*)((VirtualBindingPair*)(s->priv))->tap; - VirtualSocket *vs = (VirtualSocket*)((VirtualBindingPair*)(s->priv))->vs; - if(!tap || !vs) { - DEBUG_ERROR("invalid tap or vs"); + //DEBUG_EXTRA("s=%p, s->state=%d %s", s, s->state, beautify_pico_state(s->state)); + VirtualBindingPair *vbp = (VirtualBindingPair*)(s->priv); + if(!vbp) { + DEBUG_ERROR("s->priv yielded no valid VirtualBindingPair"); handle_general_failure(); return; } + VirtualTap *tap = static_cast(vbp->tap); + VirtualSocket *vs = static_cast(vbp->vs); int err = 0; if(!vs) { DEBUG_ERROR("invalid VirtualSocket"); handle_general_failure(); return; } - // PICO_SOCK_EV_vs - triggered when VirtualSocket is established (TCP only). This event is + // PICO_SOCK_EV - triggered when VirtualSocket is established (TCP only). This event is // received either after a successful call to pico socket vsect to indicate that the VirtualSocket // has been established, or on a listening socket, indicating that a call to pico socket accept // may now be issued in order to accept the incoming VirtualSocket from a remote host. if (ev & PICO_SOCK_EV_CONN) { + //DEBUG_EXTRA("PICO_SOCK_EV_CONN"); if(vs->state == ZT_SOCK_STATE_LISTENING) { - Mutex::Lock _l(tap->_tcpconns_m); - uint16_t port; struct pico_socket *client_psock = nullptr; struct pico_ip4 orig4; @@ -381,7 +424,8 @@ namespace ZeroTier { if(!client_psock) { DEBUG_ERROR("pico_err=%s, picosock=%p", beautify_pico_error(pico_err), s); return; - } + } + // Create a new VirtualSocket and add it to the queue, // some time in the future a call to zts_multiplex_accept() will pick up // this new VirtualSocket, add it to the VirtualSocket list and return its @@ -389,42 +433,41 @@ namespace ZeroTier { VirtualSocket *new_vs = new VirtualSocket(); new_vs->socket_type = SOCK_STREAM; new_vs->picosock = client_psock; - new_vs->tap = tap; - new_vs->picosock->priv = new VirtualBindingPair(tap,new_vs); - tap->_VirtualSockets.push_back(new_vs); - vs->_AcceptedConnections.push(new_vs); + // TODO: Condense this - if(vs->socket_family == AF_INET) - { + char addrstr[INET6_ADDRSTRLEN]; + if(vs->socket_family == AF_INET) { struct sockaddr_in in4; in4.sin_addr.s_addr = orig4.addr; in4.sin_port = Utils::hton(port); memcpy(&(new_vs->peer_addr), &in4, sizeof(new_vs->peer_addr)); - char addrstr[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &(in4.sin_addr), addrstr, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &(in4.sin_addr), addrstr, INET6_ADDRSTRLEN); DEBUG_EXTRA("accepted connection from: %s : %d", addrstr, port); + ZeroTier::InetAddress inet; + inet.fromString(addrstr); + new_vs->tap = getTapByAddr(&inet); // assign to tap based on incoming address } - if(vs->socket_family == AF_INET6) - { + if(vs->socket_family == AF_INET6) { struct sockaddr_in6 in6; memcpy(&(in6.sin6_addr.s6_addr), &orig6, sizeof(in6.sin6_addr.s6_addr)); in6.sin6_port = Utils::hton(port); memcpy(&(new_vs->peer_addr), &in6, sizeof(new_vs->peer_addr)); - char addrstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &(in6.sin6_addr), addrstr, INET6_ADDRSTRLEN); DEBUG_EXTRA("accepted connection from: %s : %d", addrstr, port); + ZeroTier::InetAddress inet; + inet.fromString(addrstr); + new_vs->tap = getTapByAddr(&inet); // assign to tap based on incoming address } - - // int value = 1; - // pico_socket_setoption(new_vs->picosock, PICO_TCP_NODELAY, &value); - - if(ZT_SOCK_BEHAVIOR_LINGER) { - int linger_time_ms = ZT_SOCK_BEHAVIOR_LINGER_TIME; - int t_err = 0; - if((t_err = pico_socket_setoption(new_vs->picosock, PICO_SOCKET_OPT_LINGER, &linger_time_ms)) < 0) - DEBUG_ERROR("unable to set LINGER size, err=%d, pico_err=%d, app_fd=%d, sdk_fd=%d", t_err, pico_err, vs->app_fd, vs->sdk_fd); + if(!new_vs->tap) { + DEBUG_ERROR("no valid VirtualTap could be found for this incoming connect address <%s>", addrstr); + handle_general_failure(); + return; } - new_vs->sock = tap->_phy.wrapSocket(new_vs->sdk_fd, new_vs); + // Assign this VirtualSocket to the appropriate VirtualTap + new_vs->picosock->priv = new VirtualBindingPair(new_vs->tap,new_vs); + new_vs->tap->addVirtualSocket(new_vs); + vs->_AcceptedConnections.push(new_vs); + new_vs->sock = new_vs->tap->_phy.wrapSocket(new_vs->sdk_fd, new_vs); } if(vs->state != ZT_SOCK_STATE_LISTENING) { // set state so socket multiplexer logic will pick this up @@ -454,7 +497,9 @@ namespace ZeroTier { // keep the VirtualSocket half-open (only for sending) after the FIN packet has been received, // allowing new data to be sent in the TCP CLOSE WAIT state. if (ev & PICO_SOCK_EV_CLOSE) { - err = pico_socket_close(s); + if((err = pico_socket_close(s)) < 0) { + DEBUG_ERROR("pico_socket_close()=%d, %s", err, beautify_pico_error(pico_err)); + } //DEBUG_INFO("PICO_SOCK_EV_CLOSE (socket closure) err = %d, picosock=%p, vs=%p, app_fd=%d, sdk_fd=%d", err, s, vs, vs->app_fd, vs->sdk_fd); vs->closure_ts = std::time(nullptr); return; @@ -476,8 +521,9 @@ namespace ZeroTier { int pico_eth_tx(struct pico_device *dev, void *buf, int len) { + //_picostack_driver_lock.lock(); //DEBUG_INFO("len = %d", len); - VirtualTap *tap = (VirtualTap*)(dev->tap); + VirtualTap *tap = static_cast(dev->tap); if(!tap) { DEBUG_ERROR("invalid dev->tap"); handle_general_failure(); @@ -489,19 +535,65 @@ namespace ZeroTier { MAC dest_mac; src_mac.setTo(ethhdr->saddr, 6); dest_mac.setTo(ethhdr->daddr, 6); - if(ZT_DEBUG_LEVEL >= ZT_MSG_TRANSFER) { char macBuf[18], nodeBuf[11]; mac2str(macBuf, sizeof(macBuf), ethhdr->daddr); ZeroTier::MAC mac; mac.setTo(ethhdr->daddr, 6); mac.toAddress(tap->_nwid).toString(nodeBuf); - DEBUG_TRANS("len=%5d, dest=%s, node=%s, proto=0x%04x (%s)", len, macBuf, nodeBuf, Utils::ntoh(ethhdr->proto), beautify_eth_proto_nums(Utils::ntoh(ethhdr->proto))); - } + char flagbuf[32]; + memset(&flagbuf, 0, 32); + struct pico_tcp_hdr *hdr; + void * tcp_hdr_ptr; + + if(Utils::ntoh(ethhdr->proto) == 0x86dd) { // tcp, ipv6 + tcp_hdr_ptr = ðhdr + PICO_SIZE_ETHHDR + PICO_SIZE_IP4HDR; + } + + if(Utils::ntoh(ethhdr->proto) == 0x0800) // tcp + { + tcp_hdr_ptr = &buf + PICO_SIZE_ETHHDR + PICO_SIZE_IP4HDR; + hdr = (struct pico_tcp_hdr *)tcp_hdr_ptr; + + /* + ext/picotcp/build/include/pico_tcp.h:#define PICO_TCP_SYNACK (PICO_TCP_SYN | PICO_TCP_ACK) + ext/picotcp/build/include/pico_tcp.h:#define PICO_TCP_PSHACK (PICO_TCP_PSH | PICO_TCP_ACK) + ext/picotcp/build/include/pico_tcp.h:#define PICO_TCP_FINACK (PICO_TCP_FIN | PICO_TCP_ACK) + ext/picotcp/build/include/pico_tcp.h:#define PICO_TCP_FINPSHACK (PICO_TCP_FIN | PICO_TCP_PSH | PICO_TCP_ACK) + ext/picotcp/build/include/pico_tcp.h:#define PICO_TCP_RSTACK (PICO_TCP_RST | PICO_TCP_ACK) + */ + + char *flag_ptr = flagbuf; + + if (hdr->flags & PICO_TCP_PSH) { + sprintf(flag_ptr, "PSH "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_SYN) { + sprintf(flag_ptr, "SYN "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_ACK) { + sprintf(flag_ptr, "ACK "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_FIN) { + sprintf(flag_ptr, "FIN "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_RST) { + sprintf(flag_ptr, "RST "); + flag_ptr+=4; + } + } + + //DEBUG_TRANS("len=%5d dst=%s [%s TX <-- %s] proto=0x%04x %s %s", len, macBuf, nodeBuf, tap->nodeId().c_str(), Utils::ntoh(ethhdr->proto), beautify_eth_proto_nums(Utils::ntoh(ethhdr->proto)), flagbuf); + } tap->_handler(tap->_arg,NULL,tap->_nwid,src_mac,dest_mac, Utils::ntoh((uint16_t)ethhdr->proto),0, ((char*)buf) + sizeof(struct pico_eth_hdr),len - sizeof(struct pico_eth_hdr)); + //_picostack_driver_lock.unlock(); return len; } @@ -509,6 +601,7 @@ namespace ZeroTier { void picoTCP::pico_eth_rx(VirtualTap *tap, const MAC &from,const MAC &to,unsigned int etherType, const void *data,unsigned int len) { + //_picostack_driver_lock.lock(); if(!tap) { DEBUG_ERROR("invalid tap"); handle_general_failure(); @@ -530,45 +623,85 @@ namespace ZeroTier { ZeroTier::MAC mac; mac.setTo(ethhdr.saddr, 6); mac.toAddress(tap->_nwid).toString(nodeBuf); - DEBUG_TRANS("len=%5d, src=%s, node=%s, proto=0x%04x (%s)", len, macBuf, nodeBuf, etherType, beautify_eth_proto_nums(etherType)); + + char flagbuf[32]; + memset(&flagbuf, 0, 32); + struct pico_tcp_hdr *hdr; + void * tcp_hdr_ptr; + + if(etherType == 0x86dd) { // tcp, ipv6 + tcp_hdr_ptr = ðhdr + PICO_SIZE_ETHHDR + PICO_SIZE_IP4HDR; + } + + if(etherType == 0x0800) // tcp, ipv4 + { + tcp_hdr_ptr = ðhdr + PICO_SIZE_ETHHDR + PICO_SIZE_IP4HDR; + hdr = (struct pico_tcp_hdr *)tcp_hdr_ptr; + + char *flag_ptr = flagbuf; + + if (hdr->flags & PICO_TCP_PSH) { + sprintf(flag_ptr, "PSH "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_SYN) { + sprintf(flag_ptr, "SYN "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_ACK) { + sprintf(flag_ptr, "ACK "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_FIN) { + sprintf(flag_ptr, "FIN "); + flag_ptr+=4; + } + if (hdr->flags & PICO_TCP_RST) { + sprintf(flag_ptr, "RST "); + flag_ptr+=4; + } + } + //DEBUG_TRANS("len=%5d src=%s [%s RX --> %s] proto=0x%04x %s %s", len, macBuf, nodeBuf, tap->nodeId().c_str(), etherType, beautify_eth_proto_nums(etherType), flagbuf); } // write virtual ethernet frame to guarded buffer (emptied by pico_eth_poll()) memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot, &newlen, sizeof(newlen)); // size of frame + meta memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot + sizeof(newlen), ðhdr, sizeof(ethhdr)); // new eth header memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot + sizeof(newlen) + sizeof(ethhdr), data, len); // frame data tap->pico_frame_rxbuf_tot += newlen; + //_picostack_driver_lock.unlock(); } // feed frames on the guarded RX buffer (from zerotier virtual wire) into the network stack int pico_eth_poll(struct pico_device *dev, int loop_score) { - VirtualTap *tap = (VirtualTap*)(dev->tap); + VirtualTap *tap = static_cast(dev->tap); if(!tap) { DEBUG_ERROR("invalid dev->tap"); handle_general_failure(); return ZT_ERR_GENERAL_FAILURE; } - // FIXME: The copy logic and/or buffer structure should be reworked for better performance after the BETA - // VirtualTap *tap = (VirtualTap*)netif->state; + // TODO: Optimize Mutex::Lock _l(tap->_pico_frame_rxbuf_m); unsigned char frame[ZT_SDK_MTU]; - int len; - int err = 0; + int len, err = 0; while (tap->pico_frame_rxbuf_tot > 0 && loop_score > 0) { //DEBUG_FLOW(" [ FBUF -> STACK] Frame buffer SZ=%d", tap->pico_frame_rxbuf_tot); memset(frame, 0, sizeof(frame)); len = 0; memcpy(&len, tap->pico_frame_rxbuf, sizeof(len)); // get frame len - if(len >= 0) { + if(len > sizeof(len)) { // meaning, since we package the len in the msg, we don't want to recv a 0-(sizeof(int)) sized frame //DEBUG_FLOW(" [ FBUF -> STACK] Moving FRAME of size (%d) from FBUF(sz=%d) into stack",len, tap->pico_frame_rxbuf_tot-len); memcpy(frame, tap->pico_frame_rxbuf + sizeof(len), len-(sizeof(len)) ); // get frame data memmove(tap->pico_frame_rxbuf, tap->pico_frame_rxbuf + len, MAX_PICO_FRAME_RX_BUF_SZ-len); // shift buffer - err = pico_stack_recv(dev, (uint8_t*)frame, (len-sizeof(len))); - // DEBUG_INFO("pico_stack_recv() = %d", err); + if((err = pico_stack_recv(dev, (uint8_t*)frame, (len-sizeof(len)))) < 0) { + if(picostack) { + DEBUG_ERROR("pico_stack_recv()=%d, %s", err, picostack->beautify_pico_error(pico_err)); + } + } tap->pico_frame_rxbuf_tot-=len; } else { - DEBUG_ERROR("Invalid frame size (%d). Exiting.",len); + DEBUG_ERROR("invalid frame size (%d)",len); handle_general_failure(); } loop_score--; @@ -580,7 +713,7 @@ namespace ZeroTier { { int err = 0; if(!can_provision_new_socket()) { - DEBUG_ERROR("cannot create additional socket, see PICO_MAX_TIMERS. current = %d", pico_ntimers()); + DEBUG_ERROR("cannot create additional socket, see PICO_MAX_TIMERS. current=%d", pico_ntimers()); errno = EMFILE; err = -1; } @@ -596,14 +729,14 @@ namespace ZeroTier { if(socket_type == SOCK_DGRAM) { DEBUG_INFO("SOCK_DGRAM"); psock = pico_socket_open( - protocol_version, PICO_PROTO_UDP, &ZeroTier::picoTCP::pico_cb_socket_activity); + protocol_version, PICO_PROTO_UDP, &ZeroTier::picoTCP::pico_cb_socket_ev); if(psock) { // configure size of UDP SND/RCV buffers // TODO } } if(socket_type == SOCK_STREAM) { psock = pico_socket_open( - protocol_version, PICO_PROTO_TCP, &ZeroTier::picoTCP::pico_cb_socket_activity); + protocol_version, PICO_PROTO_TCP, &ZeroTier::picoTCP::pico_cb_socket_ev); if(psock) { // configure size of TCP SND/RCV buffers int tx_buf_sz = ZT_STACK_TCP_SOCKET_TX_SZ; int rx_buf_sz = ZT_STACK_TCP_SOCKET_RX_SZ; @@ -613,14 +746,14 @@ namespace ZeroTier { // pico_socket_setoption(psock, PICO_TCP_NODELAY, &value); if((t_err = pico_socket_setoption(psock, PICO_SOCKET_OPT_SNDBUF, &tx_buf_sz)) < 0) - DEBUG_ERROR("unable to set SNDBUF size, err = %d, pico_err = %d", t_err, pico_err); + DEBUG_ERROR("unable to set SNDBUF size, err=%d, pico_err=%d", t_err, pico_err); if((t_err = pico_socket_setoption(psock, PICO_SOCKET_OPT_RCVBUF, &rx_buf_sz)) < 0) - DEBUG_ERROR("unable to set RCVBUF size, err = %d, pico_err = %d", t_err, pico_err); + DEBUG_ERROR("unable to set RCVBUF size, err=%d, pico_err=%d", t_err, pico_err); if(ZT_SOCK_BEHAVIOR_LINGER) { int linger_time_ms = ZT_SOCK_BEHAVIOR_LINGER_TIME; if((t_err = pico_socket_setoption(psock, PICO_SOCKET_OPT_LINGER, &linger_time_ms)) < 0) - DEBUG_ERROR("unable to set LINGER, err = %d, pico_err = %d", t_err, pico_err); + DEBUG_ERROR("unable to set LINGER, err=%d, pico_err=%d", t_err, pico_err); } } } @@ -646,6 +779,7 @@ namespace ZeroTier { uint32_t ipval = 0; pico_string_to_ipv4(ipv4_str, &ipval); zaddr.addr = ipval; + //DEBUG_EXTRA("connecting to addr=%s port=%d", ipv4_str, Utils::ntoh(in4->sin_port)); err = pico_socket_connect(vs->picosock, &zaddr, in4->sin_port); } if(vs->socket_family == AF_INET6) { @@ -654,6 +788,7 @@ namespace ZeroTier { char ipv6_str[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN); pico_string_to_ipv6(ipv6_str, zaddr.addr); + //DEBUG_EXTRA("connecting to addr=%s port=%d", ipv6_str, Utils::ntoh(in6->sin6_port)); err = pico_socket_connect(vs->picosock, &zaddr, in6->sin6_port); } if(err) { @@ -688,7 +823,7 @@ namespace ZeroTier { inet_ntop(AF_INET, (const void *)&in4->sin_addr.s_addr, ipv4_str, INET_ADDRSTRLEN); pico_string_to_ipv4(ipv4_str, &tempaddr); zaddr.addr = tempaddr; - //DEBUG_EXTRA("addr=%s:%d", ipv4_str, Utils::ntoh(in4->sin_port)); + DEBUG_EXTRA("binding to addr=%s port=%d", ipv4_str, Utils::ntoh(in4->sin_port)); err = pico_socket_bind(vs->picosock, &zaddr, (uint16_t *)&(in4->sin_port)); } if(vs->socket_family == AF_INET6) { @@ -698,7 +833,7 @@ namespace ZeroTier { inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN); // TODO: This isn't proper pico_string_to_ipv6("::", pip6.addr); - //DEBUG_EXTRA("addr=%s:%d", ipv6_str, Utils::ntoh(in6->sin6_port)); + DEBUG_EXTRA("binding to addr=%s port=%d", ipv6_str, Utils::ntoh(in6->sin6_port)); err = pico_socket_bind(vs->picosock, &pip6, (uint16_t *)&(in6->sin6_port)); } if(err < 0) { @@ -778,6 +913,11 @@ namespace ZeroTier { int err = 0; // TODO: Add RingBuffer overflow checks // DEBUG_INFO("vs=%p, len=%d", vs, len); + if(!vs) { + DEBUG_ERROR("invalid vs"); + handle_general_failure(); + return ZT_ERR_GENERAL_FAILURE; + } Mutex::Lock _l(vs->_tx_m); if(len <= 0) { DEBUG_ERROR("invalid write length (len=%d)", len); @@ -793,9 +933,7 @@ namespace ZeroTier { handle_general_failure(); return -1; } - - if(vs->socket_type == SOCK_DGRAM) - { + if(vs->socket_type == SOCK_DGRAM) { int r; if((r = pico_socket_write(vs->picosock, data, len)) < 0) { DEBUG_ERROR("unable to write to picosock=%p, err=%d (%s)", @@ -805,28 +943,24 @@ namespace ZeroTier { else { err = r; // successful write } - // DEBUG_TRANS("[ UDP TX -> STACK] :: vs=%p, len=%d", vs, r); } - if(vs->socket_type == SOCK_STREAM) - { + if(vs->socket_type == SOCK_STREAM) { int original_txsz = vs->TXbuf->count(); if(original_txsz + len >= ZT_TCP_TX_BUF_SZ) { DEBUG_ERROR("txsz=%d, len=%d", original_txsz, len); DEBUG_ERROR("TX buffer is too small, try increasing ZT_TCP_TX_BUF_SZ in libzt.h"); - exit(0); + handle_general_failure(); + return ZT_ERR_GENERAL_FAILURE; } int buf_w = vs->TXbuf->write((const unsigned char*)data, len); if (buf_w != len) { // because we checked ZT_TCP_TX_BUF_SZ above, this should not happen DEBUG_ERROR("TX wrote only %d but expected to write %d", buf_w, len); - exit(0); + handle_general_failure(); + return ZT_ERR_GENERAL_FAILURE; } - //DEBUG_INFO("TXbuf->count() = %d", vs->TXbuf->count()); int txsz = vs->TXbuf->count(); - - int r, max_write_len = std::min(std::min(txsz, ZT_SDK_MTU),ZT_STACK_SOCKET_WR_MAX); - //int buf_r = vs->TXbuf->read(vs->tmptxbuf, max_write_len); - + int r, max_write_len = std::min(std::min(txsz, ZT_SDK_MTU),ZT_STACK_SOCKET_WR_MAX); if((r = pico_socket_write(vs->picosock, vs->TXbuf->get_buf(), max_write_len)) < 0) { DEBUG_ERROR("unable to write to picosock=%p, r=%d", vs->picosock, r); err = -1; @@ -844,6 +978,11 @@ namespace ZeroTier { int picoTCP::pico_Close(VirtualSocket *vs) { + if(!vs) { + DEBUG_ERROR("invalid vs"); + handle_general_failure(); + return ZT_ERR_GENERAL_FAILURE; + } DEBUG_INFO("vs=%p, picosock=%p, fd=%d", vs, vs->picosock, vs->app_fd); if(!vs || !vs->picosock) return ZT_ERR_GENERAL_FAILURE; diff --git a/src/picoTCP.hpp b/src/picoTCP.hpp index 92622f3..846fa31 100644 --- a/src/picoTCP.hpp +++ b/src/picoTCP.hpp @@ -40,7 +40,7 @@ #include "VirtualTap.hpp" /****************************************************************************/ -/* PicoTCP API Signatures (See libzt.h for the API an app should use) */ +/* PicoTCP API Signatures (See libzt.h for the application-facing API) */ /****************************************************************************/ #define PICO_IPV4_TO_STRING_SIG char *ipbuf, const uint32_t ip @@ -94,7 +94,12 @@ namespace ZeroTier /* * Set up an interface in the network stack for the VirtualTap */ - bool pico_init_interface(ZeroTier::VirtualTap *tap, const ZeroTier::InetAddress &ip); + bool pico_init_interface(ZeroTier::VirtualTap *tap); + + /* + * Register an address with the stack + */ + bool pico_register_address(VirtualTap *tap, const InetAddress &ip); /* * Adds a route to the picoTCP device @@ -129,7 +134,7 @@ namespace ZeroTier /* * Write bytes from TX buffer to stack (prepare to be sent to ZT virtual wire) */ - static void pico_cb_socket_activity(uint16_t ev, struct pico_socket *s); + static void pico_cb_socket_ev(uint16_t ev, struct pico_socket *s); /* * Packets from the ZeroTier virtual wire enter the stack here diff --git a/test/selftest.cpp b/test/selftest.cpp index ee5b531..e1e7e0b 100644 --- a/test/selftest.cpp +++ b/test/selftest.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include "libzt.h" @@ -98,8 +99,6 @@ #define DETAILS_STR_LEN 128 -char str[STR_SIZE]; - std::map testConf; /* Tests in this file: @@ -213,7 +212,6 @@ void RECORD_RESULTS(int *test_number, bool passed, char *details, std::vectorpush_back(std::string(ok_str) + " " + std::string(details)); @@ -242,8 +240,9 @@ void RECORD_RESULTS(int *test_number, bool passed, char *details, std::vector %s : %d", inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port)); + DEBUG_INFO("getpeername() => %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); - w = zts_write(sockfd, str, len); + w = zts_write(sockfd, msg.c_str(), len); r = zts_read(sockfd, rbuf, len); - DEBUG_TEST("Sent : %s", str); + DEBUG_TEST("Sent : %s", msg.c_str()); DEBUG_TEST("Received : %s", rbuf); sleep(WAIT_FOR_TRANSMISSION_TO_COMPLETE); err = zts_close(sockfd); - sprintf(details, "tcp_client_4, n=%d, err=%d, r=%d, w=%d", count, err, r, w); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, str); + sprintf(details, "%s, n=%d, err=%d, r=%d, w=%d", msg.c_str(), count, err, r, w); + *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); } // void tcp_server_4(TCP_UNIT_TEST_SIG_4) { - fprintf(stderr, "\n\n\ntcp_server_4\n"); - int w=0, r=0, sockfd, accfd, err, len = strlen(str); + std::string msg = "tcp_server_4"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); + int w=0, r=0, sockfd, accfd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) @@ -284,10 +285,11 @@ void tcp_server_4(TCP_UNIT_TEST_SIG_4) DEBUG_ERROR("error accepting connection (%d)", err); // TODO: Put this test in the general API section - struct sockaddr_in peer_addr; + struct sockaddr_storage peer_addr; + struct sockaddr_in *in4 = (struct sockaddr_in*)&peer_addr; socklen_t peer_addrlen; zts_getpeername(accfd, (struct sockaddr*)&peer_addr, &peer_addrlen); - DEBUG_INFO("getpeername() => %s : %d", inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port)); + DEBUG_INFO("getpeername() => %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); r = zts_read(accfd, rbuf, sizeof rbuf); w = zts_write(accfd, rbuf, len); @@ -295,15 +297,16 @@ void tcp_server_4(TCP_UNIT_TEST_SIG_4) sleep(WAIT_FOR_TRANSMISSION_TO_COMPLETE); err = zts_close(sockfd); err = zts_close(accfd); - sprintf(details, "tcp_server_4, n=%d, err=%d, r=%d, w=%d", count, err, r, w); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, str); + sprintf(details, "%s, n=%d, err=%d, r=%d, w=%d", msg.c_str(), count, err, r, w); + *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); } // void tcp_client_6(TCP_UNIT_TEST_SIG_6) { - fprintf(stderr, "\n\n\ntcp_client_6\n"); - int r, w, sockfd, err, len = strlen(str); + std::string msg = "tcp_client_6"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); + int r, w, sockfd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if((sockfd = zts_socket(AF_INET6, SOCK_STREAM, 0)) < 0) @@ -320,21 +323,22 @@ void tcp_client_6(TCP_UNIT_TEST_SIG_6) inet_ntop(AF_INET6, &(p6->sin6_addr), peer_addrstr, INET6_ADDRSTRLEN); DEBUG_INFO("getpeername() => %s : %d", peer_addrstr, ntohs(p6->sin6_port)); - w = zts_write(sockfd, str, len); + w = zts_write(sockfd, msg.c_str(), len); r = zts_read(sockfd, rbuf, len); sleep(WAIT_FOR_TRANSMISSION_TO_COMPLETE); err = zts_close(sockfd); - sprintf(details, "tcp_client_6, n=%d, err=%d, r=%d, w=%d", count, err, r, w); - DEBUG_TEST("Sent : %s", str); + sprintf(details, "%s, n=%d, err=%d, r=%d, w=%d", msg.c_str(), count, err, r, w); + DEBUG_TEST("Sent : %s", msg.c_str()); DEBUG_TEST("Received : %s", rbuf); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, str); + *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); } // void tcp_server_6(TCP_UNIT_TEST_SIG_6) { - fprintf(stderr, "\n\n\ntcp_server_6\n"); - int w=0, r=0, sockfd, accfd, err, len = strlen(str); + std::string msg = "tcp_sever_6"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); + int w=0, r=0, sockfd, accfd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if((sockfd = zts_socket(AF_INET6, SOCK_STREAM, 0)) < 0) @@ -361,8 +365,8 @@ void tcp_server_6(TCP_UNIT_TEST_SIG_6) sleep(WAIT_FOR_TRANSMISSION_TO_COMPLETE); err = zts_close(sockfd); err = zts_close(accfd); - sprintf(details, "tcp_server_6, n=%d, err=%d, r=%d, w=%d", count, err, r, w); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, str); + sprintf(details, "%s, n=%d, err=%d, r=%d, w=%d", msg.c_str(), count, err, r, w); + *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); } // UDP @@ -370,8 +374,9 @@ void tcp_server_6(TCP_UNIT_TEST_SIG_6) // void udp_client_4(UDP_UNIT_TEST_SIG_4) { - fprintf(stderr, "\n\n\nudp_client_4\n"); - int r, w, sockfd, err, len = strlen(str); + std::string msg = "udp_client_4"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); + int r, w, sockfd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); @@ -386,7 +391,7 @@ void udp_client_4(UDP_UNIT_TEST_SIG_4) struct sockaddr_in saddr; while(1) { // tx - if((w = zts_sendto(sockfd, str, strlen(str), 0, (struct sockaddr *)remote_addr, sizeof(remote_addr))) < 0) { + if((w = zts_sendto(sockfd, msg.c_str(), strlen(msg.c_str()), 0, (struct sockaddr *)remote_addr, sizeof(remote_addr))) < 0) { DEBUG_ERROR("error sending packet, err=%d", errno); } sleep(1); @@ -394,14 +399,14 @@ void udp_client_4(UDP_UNIT_TEST_SIG_4) int serverlen = sizeof(remote_addr); // rx r = zts_recvfrom(sockfd, rbuf, STR_SIZE, 0, (struct sockaddr *)&saddr, (socklen_t *)&serverlen); - if(r == strlen(str)) { + if(r == strlen(msg.c_str())) { sleep(WAIT_FOR_TRANSMISSION_TO_COMPLETE); err = zts_close(sockfd); - DEBUG_INFO("udp_client_4, n=%d, err=%d, r=%d, w=%d\n", count, err, r, w); - sprintf(details, "udp_client_4, n=%d, err=%d, r=%d, w=%d", count, err, r, w); - DEBUG_TEST("Sent : %s", str); + DEBUG_INFO("%s, n=%d, err=%d, r=%d, w=%d\n", msg.c_str(), count, err, r, w); + sprintf(details, "%s, n=%d, err=%d, r=%d, w=%d", msg.c_str(), count, err, r, w); + DEBUG_TEST("Sent : %s", msg.c_str()); DEBUG_TEST("Received : %s", rbuf); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, str); + *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); return; } } @@ -409,8 +414,9 @@ void udp_client_4(UDP_UNIT_TEST_SIG_4) void udp_server_4(UDP_UNIT_TEST_SIG_4) { - fprintf(stderr, "\n\n\nudp_server_4\n"); - int r, w, sockfd, err, len = strlen(str); + std::string msg = "udp_server_4"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); + int r, w, sockfd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); @@ -419,7 +425,7 @@ void udp_server_4(UDP_UNIT_TEST_SIG_4) if((err = zts_bind(sockfd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in)) < 0)) DEBUG_ERROR("error binding to interface (%d)", err); // rx - fprintf(stderr, "waiting for UDP packet...\n"); + DEBUG_INFO("waiting for UDP packet..."); struct sockaddr_in saddr; int serverlen = sizeof(saddr); memset(&saddr, 0, sizeof(saddr)); @@ -433,29 +439,30 @@ void udp_server_4(UDP_UNIT_TEST_SIG_4) long int tx_ti = get_now_ts(); while(1) { sleep(1); - DEBUG_INFO("sending UDP packet"); - if((w = zts_sendto(sockfd, str, strlen(str), 0, (struct sockaddr *)remote_addr, sizeof(remote_addr))) < 0) { + //DEBUG_INFO("sending UDP packet"); + if((w = zts_sendto(sockfd, msg.c_str(), strlen(msg.c_str()), 0, (struct sockaddr *)remote_addr, sizeof(remote_addr))) < 0) { DEBUG_ERROR("error sending packet, err=%d", errno); } if(get_now_ts() >= tx_ti + 20000) { - //fprintf(stderr, "tx_ti=%d\n", tx_ti); + DEBUG_INFO("get_now_ts()-tx_ti=%d\n", get_now_ts()-tx_ti); break; } } sleep(WAIT_FOR_TRANSMISSION_TO_COMPLETE); //err = zts_close(sockfd); - DEBUG_INFO("udp_server_4, n=%d, err=%d, r=%d, w=%d\n", count, err, r, w); - sprintf(details, "udp_server_4, n=%d, err=%d, r=%d, w=%d", count, err, r, w); - DEBUG_TEST("Sent : %s", str); + DEBUG_INFO("%s, n=%d, err=%d, r=%d, w=%d\n", msg.c_str(), count, err, r, w); + sprintf(details, "%s, n=%d, err=%d, r=%d, w=%d", msg.c_str(), count, err, r, w); + DEBUG_TEST("Sent : %s", msg.c_str()); DEBUG_TEST("Received : %s", rbuf); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, str); + *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); } // void udp_client_6(UDP_UNIT_TEST_SIG_6) { - fprintf(stderr, "\n\n\nudp_client_6\n"); - int r, w, sockfd, err, len = strlen(str); + std::string msg = "udp_client_6"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); + int r, w, sockfd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); @@ -463,29 +470,31 @@ void udp_client_6(UDP_UNIT_TEST_SIG_6) DEBUG_ERROR("error creating ZeroTier socket"); if((err = zts_fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0)) std::cout << "error setting O_NONBLOCK (errno=" << strerror(errno) << ")" << std::endl; - fprintf(stderr, "sending UDP packets until I get a single response...\n"); + DEBUG_INFO("[1] sending UDP packets until I get a single response...\n"); if((err = zts_bind(sockfd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in6)) < 0)) DEBUG_ERROR("error binding to interface (%d)", err); + // start sending UDP packets in the hopes that at least one will be picked up by the server struct sockaddr_in saddr; while(1) { // tx - if((w = zts_sendto(sockfd, str, strlen(str), 0, (struct sockaddr *)remote_addr, sizeof(remote_addr))) < 0) { + if((w = zts_sendto(sockfd, msg.c_str(), strlen(msg.c_str()), 0, (struct sockaddr *)remote_addr, sizeof(remote_addr))) < 0) { DEBUG_ERROR("error sending packet, err=%d", errno); } - sleep(1); + usleep(100000); memset(rbuf, 0, sizeof(rbuf)); int serverlen = sizeof(remote_addr); // rx r = zts_recvfrom(sockfd, rbuf, STR_SIZE, 0, (struct sockaddr *)&saddr, (socklen_t *)&serverlen); - if(r == strlen(str)) { + if(r == strlen(msg.c_str())) { + DEBUG_INFO("[2] complete"); sleep(WAIT_FOR_TRANSMISSION_TO_COMPLETE); err = zts_close(sockfd); - DEBUG_INFO("udp_client_6, n=%d, err=%d, r=%d, w=%d\n", count, err, r, w); - sprintf(details, "udp_client_6, n=%d, err=%d, r=%d, w=%d", count, err, r, w); - DEBUG_TEST("Sent : %s", str); + DEBUG_INFO("%s, n=%d, err=%d, r=%d, w=%d\n", msg.c_str(), count, err, r, w); + sprintf(details, "%s, n=%d, err=%d, r=%d, w=%d", msg.c_str(), count, err, r, w); + DEBUG_TEST("Sent : %s", msg.c_str()); DEBUG_TEST("Received : %s", rbuf); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, str); + *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); return; } } @@ -493,8 +502,9 @@ void udp_client_6(UDP_UNIT_TEST_SIG_6) void udp_server_6(UDP_UNIT_TEST_SIG_6) { - fprintf(stderr, "\n\n\nudp_server_6\n"); - int r, w, sockfd, err, len = strlen(str); + std::string msg = "udp_server_6"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); + int r, w, sockfd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); @@ -503,7 +513,7 @@ void udp_server_6(UDP_UNIT_TEST_SIG_6) if((err = zts_bind(sockfd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in6)) < 0)) DEBUG_ERROR("error binding to interface (%d)", err); // rx - fprintf(stderr, "waiting for UDP packet...\n"); + DEBUG_INFO("[1/4] waiting for UDP packet...\n"); struct sockaddr_in6 saddr; int serverlen = sizeof(saddr); memset(&saddr, 0, sizeof(saddr)); @@ -512,29 +522,30 @@ void udp_server_6(UDP_UNIT_TEST_SIG_6) char addrstr[INET6_ADDRSTRLEN], remote_addrstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &(saddr.sin6_addr), addrstr, INET6_ADDRSTRLEN); inet_ntop(AF_INET6, &(remote_addr->sin6_addr), remote_addrstr, INET6_ADDRSTRLEN); - DEBUG_INFO("received DGRAM from %s : %d", addrstr, ntohs(saddr.sin6_port)); - DEBUG_INFO("sending DGRAM(s) to %s : %d", remote_addrstr, ntohs(remote_addr->sin6_port)); + DEBUG_INFO("[2/4] received DGRAM from %s : %d", addrstr, ntohs(saddr.sin6_port)); + DEBUG_INFO("[2/4] sending DGRAM(s) to %s : %d", remote_addrstr, ntohs(remote_addr->sin6_port)); // once we receive a UDP packet, spend 10 seconds sending responses in the hopes that the client will see // tx long int tx_ti = get_now_ts(); while(1) { - sleep(1); - DEBUG_INFO("sending UDP packet"); - if((w = zts_sendto(sockfd, str, strlen(str), 0, (struct sockaddr *)remote_addr, sizeof(remote_addr))) < 0) { + usleep(100000); + //DEBUG_INFO("sending UDP packet"); + if((w = zts_sendto(sockfd, msg.c_str(), strlen(msg.c_str()), 0, (struct sockaddr *)remote_addr, sizeof(remote_addr))) < 0) { DEBUG_ERROR("error sending packet, err=%d", errno); } if(get_now_ts() >= tx_ti + 20000) { - //fprintf(stderr, "tx_ti=%d\n", tx_ti); + DEBUG_INFO("[3/4] get_now_ts()-tx_ti=%d\n", get_now_ts()-tx_ti); break; } } + DEBUG_INFO("[4/4] complete"); sleep(WAIT_FOR_TRANSMISSION_TO_COMPLETE); //err = zts_close(sockfd); - DEBUG_INFO("udp_server_6, n=%d, err=%d, r=%d, w=%d\n", count, err, r, w); - sprintf(details, "udp_server_6, n=%d, err=%d, r=%d, w=%d", count, err, r, w); - DEBUG_TEST("Sent : %s", str); + DEBUG_INFO("%s, n=%d, err=%d, r=%d, w=%d\n", msg.c_str(), count, err, r, w); + sprintf(details, "%s, n=%d, err=%d, r=%d, w=%d", msg.c_str(), count, err, r, w); + DEBUG_TEST("Sent : %s", msg.c_str()); DEBUG_TEST("Received : %s", rbuf); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, str); + *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); } @@ -546,7 +557,8 @@ void udp_server_6(UDP_UNIT_TEST_SIG_6) // Maintain transfer for count OR count void tcp_client_sustained_4(TCP_UNIT_TEST_SIG_4) { - fprintf(stderr, "\n\n\ntcp_client_sustained_4\n"); + std::string msg = "tcp_client_sustained_4"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); int n=0, w=0, r=0, sockfd, err; char *rxbuf = (char*)malloc(count*sizeof(char)); char *txbuf = (char*)malloc(count*sizeof(char)); @@ -608,8 +620,8 @@ void tcp_client_sustained_4(TCP_UNIT_TEST_SIG_4) float tx_rate = (float)count / (float)tx_dt; float rx_rate = (float)count / (float)rx_dt; - sprintf(details, "tcp_client_sustained_4, match=%d, n=%d, tx_dt=%.2f, rx_dt=%.2f, r=%d, w=%d, tx_rate=%.2f MB/s, rx_rate=%.2f MB/s", - match, count, tx_dt, rx_dt, r, w, (tx_rate / float(ONE_MEGABYTE) ), (rx_rate / float(ONE_MEGABYTE) )); + sprintf(details, "%s, match=%d, n=%d, tx_dt=%.2f, rx_dt=%.2f, r=%d, w=%d, tx_rate=%.2f MB/s, rx_rate=%.2f MB/s", + msg.c_str(), match, count, tx_dt, rx_dt, r, w, (tx_rate / float(ONE_MEGABYTE) ), (rx_rate / float(ONE_MEGABYTE) )); *passed = (r == count && w == count && match && err>=0); } @@ -622,7 +634,8 @@ void tcp_client_sustained_4(TCP_UNIT_TEST_SIG_4) // Maintain transfer for count OR count void tcp_client_sustained_6(TCP_UNIT_TEST_SIG_6) { - fprintf(stderr, "\n\n\ntcp_client_sustained_6\n"); + std::string msg = "tcp_server_sustained_6"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); int n=0, w=0, r=0, sockfd, err; char *rxbuf = (char*)malloc(count*sizeof(char)); char *txbuf = (char*)malloc(count*sizeof(char)); @@ -685,8 +698,8 @@ void tcp_client_sustained_6(TCP_UNIT_TEST_SIG_6) float tx_rate = (float)count / (float)tx_dt; float rx_rate = (float)count / (float)rx_dt; - sprintf(details, "tcp_client_sustained_6, match=%d, n=%d, tx_dt=%.2f, rx_dt=%.2f, r=%d, w=%d, tx_rate=%.2f MB/s, rx_rate=%.2f MB/s", - match, count, tx_dt, rx_dt, r, w, (tx_rate / float(ONE_MEGABYTE) ), (rx_rate / float(ONE_MEGABYTE) )); + sprintf(details, "%s, match=%d, n=%d, tx_dt=%.2f, rx_dt=%.2f, r=%d, w=%d, tx_rate=%.2f MB/s, rx_rate=%.2f MB/s", + msg.c_str(), msg.c_str(), match, count, tx_dt, rx_dt, r, w, (tx_rate / float(ONE_MEGABYTE) ), (rx_rate / float(ONE_MEGABYTE) )); *passed = (r == count && w == count && match && err>=0); } @@ -698,7 +711,8 @@ void tcp_client_sustained_6(TCP_UNIT_TEST_SIG_6) // Maintain transfer for count OR count void tcp_server_sustained_4(TCP_UNIT_TEST_SIG_4) { - fprintf(stderr, "\n\n\ntcp_server_sustained_4\n"); + std::string msg = "tcp_server_sustained_4"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); int n=0, w=0, r=0, sockfd, accfd, err; char *rxbuf = (char*)malloc(count*sizeof(char)); memset(rxbuf, 0, count); @@ -752,8 +766,8 @@ void tcp_server_sustained_4(TCP_UNIT_TEST_SIG_4) float tx_rate = (float)count / (float)tx_dt; float rx_rate = (float)count / (float)rx_dt; - sprintf(details, "tcp_server_sustained_4, n=%d, tx_dt=%.2f, rx_dt=%.2f, r=%d, w=%d, tx_rate=%.2f MB/s, rx_rate=%.2f MB/s", - count, tx_dt, rx_dt, r, w, (tx_rate / float(ONE_MEGABYTE) ), (rx_rate / float(ONE_MEGABYTE) )); + sprintf(details, "%s, n=%d, tx_dt=%.2f, rx_dt=%.2f, r=%d, w=%d, tx_rate=%.2f MB/s, rx_rate=%.2f MB/s", + msg.c_str(), count, tx_dt, rx_dt, r, w, (tx_rate / float(ONE_MEGABYTE) ), (rx_rate / float(ONE_MEGABYTE) )); *passed = (r == count && w == count && err>=0); } @@ -764,7 +778,8 @@ void tcp_server_sustained_4(TCP_UNIT_TEST_SIG_4) // Maintain transfer for count OR count void tcp_server_sustained_6(TCP_UNIT_TEST_SIG_6) { - fprintf(stderr, "\n\n\ntcp_server_sustained_6\n"); + std::string msg = "tcp_server_sustained_6"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); int n=0, w=0, r=0, sockfd, accfd, err; char *rxbuf = (char*)malloc(count*sizeof(char)); memset(rxbuf, 0, count); @@ -816,8 +831,8 @@ void tcp_server_sustained_6(TCP_UNIT_TEST_SIG_6) float tx_rate = (float)count / (float)tx_dt; float rx_rate = (float)count / (float)rx_dt; - sprintf(details, "tcp_server_sustained_6, n=%d, tx_dt=%.2f, rx_dt=%.2f, r=%d, w=%d, tx_rate=%.2f MB/s, rx_rate=%.2f MB/s", - count, tx_dt, rx_dt, r, w, (tx_rate / float(ONE_MEGABYTE) ), (rx_rate / float(ONE_MEGABYTE) )); + sprintf(details, "%s, n=%d, tx_dt=%.2f, rx_dt=%.2f, r=%d, w=%d, tx_rate=%.2f MB/s, rx_rate=%.2f MB/s", + msg.c_str(), count, tx_dt, rx_dt, r, w, (tx_rate / float(ONE_MEGABYTE) ), (rx_rate / float(ONE_MEGABYTE) )); *passed = (r == count && w == count && err>=0); } @@ -826,15 +841,16 @@ void tcp_server_sustained_6(TCP_UNIT_TEST_SIG_6) void udp_client_sustained_4(TCP_UNIT_TEST_SIG_4) { - fprintf(stderr, "\n\n\nudp_client_sustained_4\n"); - int r, w, sockfd, err, len = strlen(str); + std::string msg = "udp_client_sustained_4"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); + int r, w, sockfd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if((sockfd = zts_socket(AF_INET, SOCK_DGRAM, 0)) < 0) DEBUG_ERROR("error creating ZeroTier socket"); for(int i=0; i<1000; i++) { - w = zts_sendto(sockfd, str, strlen(str), 0, (struct sockaddr *)addr, sizeof(addr)); + w = zts_sendto(sockfd, msg.c_str(), strlen(msg.c_str()), 0, (struct sockaddr *)addr, sizeof(addr)); } memset(rbuf, 0, sizeof(rbuf)); int serverlen = sizeof(addr); @@ -842,16 +858,17 @@ void udp_client_sustained_4(TCP_UNIT_TEST_SIG_4) sleep(WAIT_FOR_TRANSMISSION_TO_COMPLETE); err = zts_close(sockfd); - sprintf(details, "udp_client_4, n=%d, err=%d, r=%d, w=%d", count, err, r, w); - DEBUG_TEST("Sent : %s", str); + sprintf(details, "%s, n=%d, err=%d, r=%d, w=%d", msg.c_str(), count, err, r, w); + DEBUG_TEST("Sent : %s", msg.c_str()); DEBUG_TEST("Received : %s", rbuf); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, str); + *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); } void udp_server_sustained_4(TCP_UNIT_TEST_SIG_4) { - fprintf(stderr, "\n\n\nudp_server_sustained_4\n"); - int r, w, sockfd, err, len = strlen(str); + std::string msg = "udp_server_sustained_4"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); + int r, w, sockfd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if((sockfd = zts_socket(AF_INET, SOCK_DGRAM, 0)) < 0) @@ -867,14 +884,14 @@ void udp_server_sustained_4(TCP_UNIT_TEST_SIG_4) DEBUG_TEST("Received : %s", rbuf); } //memset(rbuf, 0, sizeof(rbuf)); - //w = zts_sendto(sockfd, str, strlen(str), 0, (struct sockaddr *)addr, sizeof(addr)); + //w = zts_sendto(sockfd, msg.c_str()), strlen(msg.c_str())), 0, (struct sockaddr *)addr, sizeof(addr)); w = r; //sleep(WAIT_FOR_TRANSMISSION_TO_COMPLETE); //err = zts_close(sockfd); - sprintf(details, "udp_server_4, n=%d, err=%d, r=%d, w=%d", count, err, r, w); - DEBUG_TEST("Sent : %s", str); + sprintf(details, "%s, n=%d, err=%d, r=%d, w=%d", msg.c_str(), count, err, r, w); + DEBUG_TEST("Sent : %s", msg.c_str()); DEBUG_TEST("Received : %s", rbuf); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, str); + *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); } /****************************************************************************/ @@ -977,7 +994,8 @@ void tcp_server_perf_4(TCP_UNIT_TEST_SIG_4) void tcp_perf_tx_echo_4(TCP_UNIT_TEST_SIG_4) { - fprintf(stderr, "\n\n\ntcp_perf_tx_echo_4\n"); + std::string msg = "tcp_perf_tx_echo_4"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); int err = 0; int tot = 0; @@ -1035,7 +1053,7 @@ void tcp_perf_tx_echo_4(TCP_UNIT_TEST_SIG_4) float ts_delta = (end_time - start_time) / (float)1000; float rate = (float)tot / (float)ts_delta; - sprintf(details, "tcp_perf_tx_echo_4, tot=%d, dt=%.2f, rate=%.2f MB/s", tot, ts_delta, (rate / float(ONE_MEGABYTE) )); + sprintf(details, "%s, tot=%d, dt=%.2f, rate=%.2f MB/s", msg.c_str(), tot, ts_delta, (rate / float(ONE_MEGABYTE) )); sleep(WAIT_FOR_TRANSMISSION_TO_COMPLETE); err = zts_close(sockfd); @@ -1045,7 +1063,8 @@ void tcp_perf_tx_echo_4(TCP_UNIT_TEST_SIG_4) void tcp_perf_rx_echo_4(TCP_UNIT_TEST_SIG_4) { - fprintf(stderr, "\n\n\ntcp_perf_rx_echo_4\n"); + std::string msg = "tcp_perf_rx_echo_4"; + fprintf(stderr, "\n\n%s\n\n", msg.c_str()); int err = 0; int mode = 0; @@ -1102,7 +1121,7 @@ void tcp_perf_rx_echo_4(TCP_UNIT_TEST_SIG_4) long int end_time = get_now_ts(); float ts_delta = (end_time - start_time) / (float)1000; float rate = (float)tot / (float)ts_delta; - sprintf(details, "tcp_perf_rx_echo_4, tot=%d, dt=%.2f, rate=%.2f MB/s", tot, ts_delta, (rate / float(ONE_MEGABYTE) )); + sprintf(details, "%s, tot=%d, dt=%.2f, rate=%.2f MB/s", msg.c_str(), tot, ts_delta, (rate / float(ONE_MEGABYTE) )); sleep(WAIT_FOR_TRANSMISSION_TO_COMPLETE); err = zts_close(sockfd); @@ -1505,24 +1524,145 @@ void test_bad_args() DEBUG_TEST("SOCK_DGRAM = %d", SOCK_DGRAM); } +void close_while_writing_test() +{ + // TODO: Close a socket while another thread is writing to it or reading from it +} + + +void* create_socket(void *arg) +{ + /* + unsigned long i = 0; + pthread_t id = pthread_self(); + + if(pthread_equal(id,tid[0])) + { + printf("\n First thread processing\n"); + } + else + { + printf("\n Second thread processing\n"); + } + + for(i=0; i<(0xFFFFFFFF);i++); + + return NULL; + */ +} + + +void multithread_socket_creation() +{ + /* + pthread_t tid[2]; + + err = pthread_create(&(tid[i]), NULL, &create_socket, NULL); + if (err != 0) + printf("\ncan't create thread :[%s]", strerror(err)); + else + printf("\n Thread created successfully\n"); + */ + // TODO: +} + +void multithread_rw() +{ + // TODO: Test read/writes from multiple threads +} + // Tests rapid opening and closure of sockets void close_test(struct sockaddr *bind_addr) { + // BUG: While running an extended test of unassigned closures, the + // stack may crash at: `pico_check_timers at pico_stack.c:608, this appears + // to be a bad pointer to a timer within the stack. + bool extended = false; + int tries = !extended ? 32 : 1024; int err = 0; - for(int i=0; i<64; i++) + for(int i=0; isin_port++; } } +void bind_to_localhost_test(int port) +{ + int fd, err = 0; + + // ipv4, 0.0.0.0 + struct sockaddr bind_addr; + DEBUG_INFO("binding to 0.0.0.0"); + create_addr("0.0.0.0", port, 4, (struct sockaddr *)&bind_addr); + if((fd = zts_socket(AF_INET, SOCK_STREAM, 0)) > 0) { + if((err = zts_bind(fd, (struct sockaddr *)&bind_addr, sizeof(struct sockaddr_in))) == 0) { + usleep(100000); + if((err = zts_close(fd)) < 0) { + DEBUG_ERROR("error closing socket (%d)", err); + } + } + else{ + DEBUG_ERROR("error binding to interface (%d)", err); + } + } + else { + DEBUG_ERROR("error creating socket", err); + } + + port++; + + // ipv4, 127.0.0.1 + DEBUG_INFO("binding to 127.0.0.1"); + create_addr("127.0.0.1", port, 4, (struct sockaddr *)&bind_addr); + if((fd = zts_socket(AF_INET, SOCK_STREAM, 0)) > 0) { + if((err = zts_bind(fd, (struct sockaddr *)&bind_addr, sizeof(struct sockaddr_in))) == 0) { + usleep(100000); + if((err = zts_close(fd)) < 0) { + DEBUG_ERROR("error closing socket (%d)", err); + } + } + else{ + DEBUG_ERROR("error binding to interface (%d)", err); + } + } + else { + DEBUG_ERROR("error creating socket", err); + } + + port++; + + // ipv6, [::] + DEBUG_INFO("binding to [::]"); + create_addr("::", port, 6, (struct sockaddr *)&bind_addr); + if((fd = zts_socket(AF_INET6, SOCK_STREAM, 0)) > 0) { + if((err = zts_bind(fd, (struct sockaddr *)&bind_addr, sizeof(struct sockaddr_in))) == 0) { + usleep(100000); + if((err = zts_close(fd)) < 0) { + DEBUG_ERROR("error closing socket (%d)", err); + } + } + else{ + DEBUG_ERROR("error binding to interface (%d)", err); + } + } + else { + DEBUG_ERROR("error creating socket", err); + } +} + /****************************************************************************/ /* main(), calls test_driver(...) */ /****************************************************************************/ @@ -1553,7 +1693,6 @@ int main(int argc , char *argv[]) std::string remote_echo_ipv4, smode; std::string nwid, stype, path = argv[1]; std::string ipstr, ipstr6, local_ipstr, local_ipstr6, remote_ipstr, remote_ipstr6; - memcpy(str, "welcome to the machine", 22); // loaf config file if(path.find(".conf") == std::string::npos) { @@ -1626,15 +1765,25 @@ int main(int argc , char *argv[]) memset(&details, 0, sizeof details); bool passed = 0; - struct sockaddr local_addr; - struct sockaddr remote_addr; + struct sockaddr_storage local_addr; + struct sockaddr_storage remote_addr; // closure test - port = 1000; +/* + port = 1000; struct sockaddr_in in4; + DEBUG_INFO("testing closures by binding to: %s", local_ipstr.c_str()); create_addr(local_ipstr, port, 4, (struct sockaddr *)&in4); close_test((struct sockaddr*)&in4); +*/ + + close_while_writing_test(); + +// localhost bind test + + //bind_to_localhost_test(1000); + // Transmission Tests @@ -1644,6 +1793,8 @@ int main(int argc , char *argv[]) operation = TEST_OP_N_BYTES; // ipv4 client/server (UDP) + +/* ipv = 4; if(mode == TEST_MODE_SERVER) { create_addr(local_ipstr, port, ipv, (struct sockaddr *)&local_addr); @@ -1672,12 +1823,14 @@ int main(int argc , char *argv[]) } RECORD_RESULTS(&test_number, passed, details, &results); port++; +*/ + // ipv6 client/server (UDP) ipv = 6; if(mode == TEST_MODE_SERVER) { - create_addr(local_ipstr6, port, ipv, (struct sockaddr *)&local_addr); - create_addr(remote_ipstr6, port, ipv, (struct sockaddr *)&remote_addr); + create_addr(local_ipstr6, port, ipv, (struct sockaddr*)&local_addr); + create_addr(remote_ipstr6, port, ipv, (struct sockaddr*)&remote_addr); udp_server_6((struct sockaddr_in6 *)&local_addr, (struct sockaddr_in6 *)&remote_addr, operation, count, delay, details, &passed); } else if(mode == TEST_MODE_CLIENT) { @@ -1703,6 +1856,8 @@ int main(int argc , char *argv[]) RECORD_RESULTS(&test_number, passed, details, &results); port++; +exit(0); + // ipv4 sustained transfer (UDP) ipv = 4; if(mode == TEST_MODE_SERVER) {