/* * ZeroTier SDK - Network Virtualization Everywhere * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * -- * * You can be released from the requirements of the license by purchasing * a commercial license. Buying such a license is mandatory as soon as you * develop commercial closed-source software that incorporates or links * directly against ZeroTier software without disclosing the source code * of your own application. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libzt.h" #if defined(__SELFTEST__) #include "Utils.hpp" #endif #define EXIT_ON_FAIL false #define PASSED 1 #define FAILED 0 #define ECHO_INTERVAL 1000000 // microseconds #define SLAM_INTERVAL 500000 // microseconds #define WAIT_FOR_TEST_TO_CONCLUDE 0 #define ARTIFICIAL_SOCKET_LINGER 1 #define STR_SIZE 32 #define TEST_OP_N_BYTES 10 #define TEST_OP_N_SECONDS 11 #define TEST_OP_N_TIMES 12 #define TEST_MODE_CLIENT 20 #define TEST_MODE_SERVER 21 #define TEST_TYPE_SIMPLE 30 #define TEST_TYPE_SUSTAINED 31 #define TEST_TYPE_PERF 32 #define TEST_TYPE_PERF_TO_ECHO 33 #define MIN_PORT 5000 #define MAX_PORT 50000 #define TCP_UNIT_TEST_SIG_4 struct sockaddr_in *addr, int op, int cnt, char *details, \ bool *passed #define UDP_UNIT_TEST_SIG_4 struct sockaddr_in *local_addr, struct sockaddr_in *remote_addr, \ int op, int cnt, char *details, bool *passed #define TCP_UNIT_TEST_SIG_6 struct sockaddr_in6 *addr, int op, int cnt, char *details, \ bool *passed #define UDP_UNIT_TEST_SIG_6 struct sockaddr_in6 *local_addr, struct sockaddr_in6 *remote_addr, \ int op, int cnt, char *details, bool *passed #define ECHOTEST_MODE_RX 333 #define ECHOTEST_MODE_TX 666 #define DATA_BUF_SZ 1024*32 #define MAX_RX_BUF_SZ 2048 #define MAX_TX_BUF_SZ 2048 #define ONE_MEGABYTE 1024 * 1024 #define DETAILS_STR_LEN 128 // If running a self test, use libzt calls #if defined(__SELFTEST__) #define SOCKET zts_socket #define BIND zts_bind #define LISTEN zts_listen #define ACCEPT zts_accept #define CONNECT zts_connect #define READ zts_read #define WRITE zts_write #define RECV zts_recvmsg #define SEND zts_send #define RECVFROM zts_recvfrom #define SENDTO zts_sendto #define RECVMSG zts_recvmsg #define SENDMSG zts_sendmsg #define SETSOCKOPT zts_setsockopt #define GETSOCKOPT zts_getsockopt #define IOCTL zts_ioctl #define FCNTL zts_fcntl #define SELECT zts_select #define CLOSE zts_close #define GETPEERNAME zts_getpeername #endif // If running a native instance to test against, use system calls #if defined(__NATIVETEST__) inline unsigned int gettid() { #ifdef _WIN32 return GetCurrentThreadId(); #elif defined(__unix__) return static_cast(::syscall(__NR_gettid)); #elif defined(__APPLE__) uint64_t tid64; pthread_threadid_np(NULL, &tid64); return static_cast(tid64); #endif } #define SOCKET socket #define BIND bind #define LISTEN listen #define ACCEPT accept #define CONNECT connect #define READ read #define WRITE write #define RECV recvmsg #define SEND send #define RECVFROM recvfrom #define SENDTO sendto #define RECVMSG recvmsg #define SENDMSG sendmsg #define SETSOCKOPT setsockopt #define GETSOCKOPT getsockopt #define IOCTL ioctl #define FCNTL fcntl #define SELECT select #define CLOSE close #define GETPEERNAME getpeername #endif std::map testConf; /* Tests in this file: Basic RX/TX connect()/accept() Functionality: [ ?] slam - perform thousands of the same call per second [ ] random - act like a monkey, press all the buttons [OK] simple client ipv4 - connect, send one message and wait for an echo [OK] simple server ipv4 - accept, read one message and echo it back [OK] simple client ipv6 - connect, send one message and wait for an echo [OK] simple server ipv6 - accept, read one message and echo it back [OK] sustained client ipv4 - connect and rx/tx many messages, VERIFIES data integrity [OK] sustained server ipv4 - accept and echo messages, VERIFIES data integrity [OK] sustained client ipv6 - connect and rx/tx many messages, VERIFIES data integrity [OK] sustained server ipv6 - accept and echo messages, VERIFIES data integrity [OK] comprehensive client ipv4 - test all ipv4/6 client simple/sustained modes [OK] comprehensive server ipv6 - test all ipv4/6 server simple/sustained modes [ ?] SOCK_RAW (VL2) ipv4 - See test/layer2.cpp [ ?] SOCK_RAW (VL2) ipv6 - See test/layer2.cpp Performance: (See libzt.h, compile libzt with appropriate ZT_TCP_TX_BUF_SZ, ZT_TCP_RX_BUF_SZ, ZT_UDP_TX_BUF_SZ, and ZT_UDO_RX_BUF_SZ for your test) [OK] Throughput - Test maximum RX/TX speeds [ ] Memory Usage - Test memory consumption profile [ ] CPU Usage - Test processor usage [ ] Correctness: [ ] Block/Non-block - Test that blocking and non-blocking behaviour is consistent [ ] Release of resources - Test that all destructor methods/blocks function properly [OK] Multi-network handling - Test internal Tap multiplexing works for multiple networks [ ] Address handling - Test that addresses are copied/parsed/returned properly */ /****************************************************************************/ /* Helper Functions */ /****************************************************************************/ void displayResults(int *results, int size) { int success = 0, failure = 0; for (int i=0; i get_now_ts()) { sleep(1); } } void wait_until_tplus_s(long int original_time, int tplus_s) { int current_time_offset = (get_now_ts() - original_time) / 1000; fprintf(stderr, "\n\n--- WAITING FOR T+%d --- (current: T+%d)\n\n", tplus_s, current_time_offset); if (current_time_offset > tplus_s) { DEBUG_ERROR("--- ABORTING TEST: Tests are out of sync and might not yield valid results. ---"); //exit(0); } if (current_time_offset == tplus_s) { DEBUG_ERROR("--- WARNING: Tests might be out of sync and might not yield valid results. ---"); } wait_until_tplus(original_time, tplus_s * 1000); } int rand_in_range(int min, int max) { #if defined(__SELFTEST__) unsigned int seed; ZeroTier::Utils::getSecureRandom((void*)&seed,sizeof(seed)); srand(seed); #else srand((unsigned int)time(NULL)); #endif return min + rand() % static_cast(max - min + 1); } void generate_random_data(void *buf, size_t n, int min, int max) { char *b = (char*)buf; for (int i=0; isin_port = htons(port); in4->sin_addr.s_addr = inet_addr(ipstr.c_str()); in4->sin_family = AF_INET; } if (ipv == 6) { struct sockaddr_in6 *in6 = (struct sockaddr_in6*)saddr; inet_pton(AF_INET6, ipstr.c_str(), &(in6->sin6_addr)); in6->sin6_flowinfo = 0; in6->sin6_family = AF_INET6; in6->sin6_port = htons(port); } } void RECORD_RESULTS(bool passed, char *details, std::vector *results) { char *ok_str = (char*)"[ OK ]"; char *fail_str = (char*)"[ FAIL ]"; if (passed == PASSED) { DEBUG_TEST("%s", ok_str); results->push_back(std::string(ok_str) + " " + std::string(details)); } else { DEBUG_ERROR("%s", fail_str); results->push_back(std::string(fail_str) + " " + std::string(details)); } if (EXIT_ON_FAIL && !passed) { fprintf(stderr, "%s\n", results->at(results->size()-1).c_str()); exit(0); } memset(details, 0, DETAILS_STR_LEN); } /****************************************************************************/ /* POLL/SELECT */ /****************************************************************************/ void tcp_select_server(TCP_UNIT_TEST_SIG_4) { std::string testname = "tcp_select_server"; std::string msg = "tcp_select"; fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts); fprintf(stderr, "accept connection, create poll/select loop, read and write strings back and forth\n"); int w=0, r=0, fd, client_fd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if ((fd = SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { DEBUG_ERROR("error creating ZeroTier socket"); perror("socket"); *passed = false; return; } if ((err = BIND(fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) < 0)) { DEBUG_ERROR("error binding to interface (%d)", err); perror("bind"); *passed = false; return; } if ((err = LISTEN(fd, 100)) < 0) { printf("error placing socket in LISTENING state (%d)", err); perror("listen"); *passed = false; return; } struct sockaddr_in client; socklen_t client_addrlen = sizeof(sockaddr_in); if ((client_fd = ACCEPT(fd, (struct sockaddr *)&client, &client_addrlen)) < 0) { perror("accept"); *passed = false; return; } DEBUG_TEST("accepted connection fd=%d", client_fd); fd_set read_set, write_set; uint32_t msecs = 5; struct timeval tv; tv.tv_sec = msecs / 1000; tv.tv_usec = (msecs % 1000) * 1000; int ret = 0; FD_SET(client_fd, &read_set); FD_SET(client_fd, &write_set); int tot = 1000, rx_num = 0, tx_num = 0; while(rx_num < tot && tx_num < tot) { FD_ZERO(&read_set); FD_ZERO(&write_set); FD_SET(client_fd, &read_set); FD_SET(client_fd, &write_set); ret = SELECT(client_fd + 1, &read_set, &write_set, NULL, &tv); if (ret > 0) { for (int fd_i=0; fd_i 0) { DEBUG_TEST("socket activity"); for (int fd_i=0; fd_i %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); w = WRITE(fd, msg.c_str(), len); r = READ(fd, rbuf, len); DEBUG_TEST("Sent : %s", msg.c_str()); DEBUG_TEST("Received : %s", rbuf); sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); } // TEST-2 void tcp_server_4(TCP_UNIT_TEST_SIG_4) { std::string testname = "tcp_server_4"; std::string msg = "tcp_cs_4"; fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts); fprintf(stderr, "accept connection with IPv4 address, read string, write string, compare.\n"); int w=0, r=0, fd, client_fd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if ((fd = SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { DEBUG_ERROR("error creating ZeroTier socket"); perror("socket"); *passed = false; return; } if ((err = BIND(fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) < 0)) { DEBUG_ERROR("error binding to interface (%d)", err); perror("bind"); *passed = false; return; } if ((err = LISTEN(fd, 100)) < 0) { printf("error placing socket in LISTENING state (%d)", err); perror("listen"); *passed = false; return; } struct sockaddr_in client; socklen_t client_addrlen = sizeof(sockaddr_in); if ((client_fd = ACCEPT(fd, (struct sockaddr *)&client, &client_addrlen)) < 0) { perror("accept"); *passed = false; return; } DEBUG_TEST("accepted connection from %s, on port %d", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); // TODO: Put this test in the general API section struct sockaddr_storage peer_addr; struct sockaddr_in *in4 = (struct sockaddr_in*)&peer_addr; socklen_t peer_addrlen = sizeof(peer_addr); if ((err = GETPEERNAME(client_fd, (struct sockaddr*)&peer_addr, &peer_addrlen)) < 0) { perror("getpeername"); *passed = false; return; } DEBUG_TEST("getpeername() => %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); r = READ(client_fd, rbuf, len); w = WRITE(client_fd, rbuf, len); DEBUG_TEST("Received : %s, r=%d, w=%d", rbuf, r, w); sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); err = CLOSE(client_fd); sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); } // TEST-3 void tcp_client_6(TCP_UNIT_TEST_SIG_6) { std::string testname = "tcp_client_6"; std::string msg = "tcp_cs_6"; fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts); fprintf(stderr, "connect to remote host with IPv6 address, write string, read string, compare.\n"); int r, w, fd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if ((fd = SOCKET(AF_INET6, SOCK_STREAM, 0)) < 0) { DEBUG_ERROR("error creating ZeroTier socket"); perror("socket"); *passed = false; return; } if ((err = CONNECT(fd, (const struct sockaddr *)addr, sizeof(*addr))) < 0) { DEBUG_ERROR("error connecting to remote host (%d)", err); perror("connect"); *passed = false; return; } // TODO: Put this test in the general API section struct sockaddr_storage peer_addr; struct sockaddr_in6 *p6 = (struct sockaddr_in6*)&peer_addr; socklen_t peer_addrlen = sizeof(peer_addr); if ((err = GETPEERNAME(fd, (struct sockaddr*)&peer_addr, &peer_addrlen)) < 0) { perror("getpeername"); *passed = false; return; } char peer_addrstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &(p6->sin6_addr), peer_addrstr, INET6_ADDRSTRLEN); DEBUG_TEST("getpeername() => %s : %d", peer_addrstr, ntohs(p6->sin6_port)); w = WRITE(fd, msg.c_str(), len); r = READ(fd, rbuf, len); sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); DEBUG_TEST("Sent : %s", msg.c_str()); DEBUG_TEST("Received : %s", rbuf); *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); } // TEST-4 void tcp_server_6(TCP_UNIT_TEST_SIG_6) { std::string testname = "tcp_server_6"; std::string msg = "tcp_cs_6"; fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts); fprintf(stderr, "accept connection with IPv6 address, read string, write string, compare.\n"); int w=0, r=0, fd, client_fd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if ((fd = SOCKET(AF_INET6, SOCK_STREAM, 0)) < 0) { DEBUG_ERROR("error creating ZeroTier socket"); perror("socket"); *passed = false; return; } if ((err = BIND(fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in6)) < 0)) { DEBUG_ERROR("error binding to interface (%d)", err); perror("bind"); *passed = false; return; } if ((err = LISTEN(fd, 100)) < 0) { DEBUG_ERROR("error placing socket in LISTENING state (%d)", err); perror("listen"); *passed = false; return; } struct sockaddr_in6 client; socklen_t client_addrlen = sizeof(sockaddr_in6); if ((client_fd = ACCEPT(fd, (struct sockaddr *)&client, &client_addrlen)) < 0) { perror("accept"); *passed = false; return; } char ipstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &client.sin6_addr, ipstr, sizeof ipstr); DEBUG_TEST("accepted connection from %s, on port %d", ipstr, ntohs(client.sin6_port)); // TODO: Put this test in the general API section struct sockaddr_storage peer_addr; struct sockaddr_in6 *p6 = (struct sockaddr_in6*)&peer_addr; socklen_t peer_addrlen = sizeof(peer_addr); if ((err = GETPEERNAME(client_fd, (struct sockaddr*)&peer_addr, &peer_addrlen)) < 0) { perror("getpeername"); *passed = false; return; } char peer_addrstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &(p6->sin6_addr), peer_addrstr, INET6_ADDRSTRLEN); DEBUG_TEST("getpeername() => %s : %d", peer_addrstr, ntohs(p6->sin6_port)); r = READ(client_fd, rbuf, sizeof rbuf); w = WRITE(client_fd, rbuf, len); DEBUG_TEST("Received : %s", rbuf); sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); err = CLOSE(client_fd); sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); } // UDP // TEST-5 void udp_client_4(UDP_UNIT_TEST_SIG_4) { std::string testname = "udp_client_4"; std::string msg = "udp_cs_4"; fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts); fprintf(stderr, "bind to interface with IPv4 address, send string until response is seen. compare.\n"); int r, w, fd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if ((fd = SOCKET(AF_INET, SOCK_DGRAM, 0)) < 0) { DEBUG_ERROR("error creating ZeroTier socket"); perror("socket"); *passed = false; return; } if ((err = FCNTL(fd, F_SETFL, O_NONBLOCK) < 0)) { fprintf(stderr, "error setting O_NONBLOCK (errno=%d)", errno); perror("fcntl"); *passed = false; return; } DEBUG_TEST("sending UDP packets until I get a single response..."); if ((err = BIND(fd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in)) < 0)) { DEBUG_ERROR("error binding to interface (%d)", err); perror("bind"); *passed = false; return; } struct sockaddr_storage saddr; while (true) { sleep(1); // tx if ((w = SENDTO(fd, msg.c_str(), strlen(msg.c_str()), 0, (struct sockaddr *)remote_addr, sizeof(*remote_addr))) < 0) { DEBUG_ERROR("error sending packet, err=%d", errno); } memset(rbuf, 0, sizeof(rbuf)); int serverlen = sizeof(struct sockaddr_storage); // rx r = RECVFROM(fd, rbuf, STR_SIZE, 0, (struct sockaddr *)&saddr, (socklen_t *)&serverlen); if (r == strlen(msg.c_str())) { sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); DEBUG_TEST("%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); DEBUG_TEST("Sent : %s", msg.c_str()); DEBUG_TEST("Received : %s", rbuf); *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); return; } } } // TEST-6 void udp_server_4(UDP_UNIT_TEST_SIG_4) { std::string testname = "udp_server_4"; std::string msg = "udp_cs_4"; fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts); fprintf(stderr, "bind to interface with IPv4 address, read single string, send many responses. compare.\n"); int r, w, fd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if ((fd = SOCKET(AF_INET, SOCK_DGRAM, 0)) < 0) { DEBUG_ERROR("error creating ZeroTier socket"); perror("socket"); *passed = false; return; } if ((err = BIND(fd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in)) < 0)) { DEBUG_ERROR("error binding to interface (%d)", err); perror("bind"); *passed = false; return; } // rx DEBUG_TEST("waiting for UDP packet..."); struct sockaddr_storage saddr; struct sockaddr_in *in4 = (struct sockaddr_in*)&saddr; int serverlen = sizeof(saddr); memset(&saddr, 0, sizeof(saddr)); if ((r = RECVFROM(fd, rbuf, STR_SIZE, 0, (struct sockaddr *)in4, (socklen_t *)&serverlen)) < 0) { perror("recvfrom"); *passed = false; return; } char addrstr[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(in4->sin_addr), addrstr, INET_ADDRSTRLEN); // once we receive a UDP packet, spend 10 seconds sending responses in the hopes that the client will see DEBUG_TEST("received DGRAM from %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); DEBUG_TEST("sending DGRAM(s) to %s : %d", inet_ntoa(remote_addr->sin_addr), ntohs(remote_addr->sin_port)); // tx long int tx_ti = get_now_ts(); while (true) { sleep(1); if ((w = SENDTO(fd, msg.c_str(), len, 0, (struct sockaddr *)remote_addr, sizeof(*remote_addr))) < 0) { DEBUG_ERROR("error sending packet, err=%d", errno); } if (get_now_ts() >= tx_ti + 10000) { break; } } sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); DEBUG_TEST("%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); DEBUG_TEST("Sent : %s", msg.c_str()); DEBUG_TEST("Received : %s", rbuf); *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); } // TEST-7 void udp_client_6(UDP_UNIT_TEST_SIG_6) { std::string testname = "udp_client_6"; std::string msg = "udp_cs_6"; fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts); fprintf(stderr, "bind to interface with IPv6 address, send string until response is seen. compare.\n"); int r, w, fd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if ((fd = SOCKET(AF_INET6, SOCK_DGRAM, 0)) < 0) { DEBUG_ERROR("error creating ZeroTier socket"); perror("socket"); *passed = false; return; } if ((err = FCNTL(fd, F_SETFL, O_NONBLOCK) < 0)) { std::cout << "error setting O_NONBLOCK (errno=" << strerror(errno) << ")" << std::endl; perror("fcntl"); *passed = false; return; } DEBUG_TEST("[1] binding and sending UDP packets until I get a single response..."); if ((err = BIND(fd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in6)) < 0)) { DEBUG_ERROR("error binding to interface (%d)", err); perror("bind"); *passed = false; return; } // start sending UDP packets in the hopes that at least one will be picked up by the server struct sockaddr_storage saddr; while (true) { // tx if ((w = SENDTO(fd, msg.c_str(), len, 0, (struct sockaddr *)remote_addr, sizeof(*remote_addr))) < 0) { DEBUG_ERROR("error sending packet, err=%d", errno); } usleep(100000); memset(rbuf, 0, sizeof(rbuf)); int serverlen = sizeof(struct sockaddr_storage); // rx r = RECVFROM(fd, rbuf, len, 0, (struct sockaddr *)&saddr, (socklen_t *)&serverlen); if (r == len) { DEBUG_TEST("[2] complete"); sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); DEBUG_TEST("%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); DEBUG_TEST("Sent : %s", msg.c_str()); DEBUG_TEST("Received : %s", rbuf); *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); return; } } } // TEST-8 void udp_server_6(UDP_UNIT_TEST_SIG_6) { std::string testname = "udp_server_6"; std::string msg = "udp_cs_6"; fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts); fprintf(stderr, "bind to interface with IPv6 address, read single string, send many responses. compare.\n"); int r, w, fd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if ((fd = SOCKET(AF_INET6, SOCK_DGRAM, 0)) < 0) { DEBUG_ERROR("error creating socket"); perror("socket"); *passed = false; return; } if ((err = BIND(fd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in6)) < 0)) { DEBUG_ERROR("error binding to interface (%d)", err); perror("bind"); *passed = false; return; } // rx DEBUG_TEST("[1/3] waiting for UDP packet to start test..."); struct sockaddr_storage saddr; struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&saddr; int serverlen = sizeof(saddr); memset(&saddr, 0, sizeof(saddr)); if ((r = RECVFROM(fd, rbuf, len, 0, (struct sockaddr *)&saddr, (socklen_t *)&serverlen)) < 0) { perror("recvfrom"); *passed = false; return; } char addrstr[INET6_ADDRSTRLEN], remote_addrstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &(in6->sin6_addr), addrstr, INET6_ADDRSTRLEN); inet_ntop(AF_INET6, &(remote_addr->sin6_addr), remote_addrstr, INET6_ADDRSTRLEN); DEBUG_TEST("[2/3] received DGRAM from %s : %d", addrstr, ntohs(in6->sin6_port)); DEBUG_TEST("[2/3] 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 (true) { usleep(100000); //DEBUG_TEST("sending UDP packet"); if ((w = SENDTO(fd, msg.c_str(), len, 0, (struct sockaddr *)remote_addr, sizeof(*remote_addr))) < 0) { DEBUG_ERROR("error sending packet, err=%d", errno); } if (get_now_ts() >= tx_ti + 10000) { // DEBUG_TEST("[3/4] get_now_ts()-tx_ti=%d", get_now_ts()-tx_ti); break; } } sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); DEBUG_TEST("[3/3] complete, %s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); DEBUG_TEST("Sent : %s", msg.c_str()); DEBUG_TEST("Received : %s", rbuf); *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); } /****************************************************************************/ /* SUSTAINED */ /****************************************************************************/ void tcp_client_sustained_4(TCP_UNIT_TEST_SIG_4) { std::string testname = "tcp_client_sustained_4"; std::string msg = "tcp_sustained_4"; fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts); fprintf(stderr, "connect to remote host with IPv4 address, exchange a sequence of packets, check order.\n"); int n=0, w=0, r=0, fd, err; char *rxbuf = (char*)malloc(cnt*sizeof(char)); char *txbuf = (char*)malloc(cnt*sizeof(char)); generate_random_data(txbuf, cnt, 0, 9); if ((fd = SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { DEBUG_ERROR("error creating ZeroTier socket"); perror("socket"); *passed = false; return; } if ((err = CONNECT(fd, (const struct sockaddr *)addr, sizeof(*addr))) < 0) { DEBUG_ERROR("error connecting to remote host (%d)", err); perror("connect"); *passed = false; return; } if (op == TEST_OP_N_BYTES) { int wrem = cnt, rrem = cnt; // TX long int tx_ti = get_now_ts(); while (wrem) { int next_write = std::min(4096, wrem); signal(SIGPIPE, SIG_IGN); DEBUG_TEST("writing..."); n = WRITE(fd, &txbuf[w], next_write); DEBUG_TEST("wrote=%d", n); if (n > 0) { w += n; wrem -= n; err = n; DEBUG_TEST("wrote=%d, w=%d, wrem=%d", n, w, wrem); } } long int tx_tf = get_now_ts(); DEBUG_TEST("wrote=%d, reading next...", w); // RX long int rx_ti = 0; while (rrem) { n = READ(fd, &rxbuf[r], rrem); if (rx_ti == 0) { // wait for first message rx_ti = get_now_ts(); } if (n > 0) { r += n; rrem -= n; err = n; } } long int rx_tf = get_now_ts(); DEBUG_TEST("read=%d", r); sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); // Compare RX and TX buffer and detect mismatches bool match = true; for (int i=0; i=0); } free(rxbuf); free(txbuf); } void tcp_client_sustained_6(TCP_UNIT_TEST_SIG_6) { std::string testname = "tcp_client_sustained_6"; std::string msg = "tcp_sustained_6"; fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts); fprintf(stderr, "connect to remote host with IPv6 address, exchange a sequence of packets, check order.\n"); int n=0, w=0, r=0, fd, err; char *rxbuf = (char*)malloc(cnt*sizeof(char)); char *txbuf = (char*)malloc(cnt*sizeof(char)); generate_random_data(txbuf, cnt, 0, 9); if ((fd = SOCKET(AF_INET6, SOCK_STREAM, 0)) < 0){ DEBUG_ERROR("error creating ZeroTier socket"); perror("socket"); *passed = false; return; } if ((err = CONNECT(fd, (const struct sockaddr *)addr, sizeof(*addr))) < 0) { DEBUG_ERROR("error connecting to remote host (%d)", err); perror("connect"); *passed = false; return; } if (op == TEST_OP_N_BYTES) { int wrem = cnt, rrem = cnt; // TX long int tx_ti = get_now_ts(); while (wrem) { int next_write = std::min(4096, wrem); n = WRITE(fd, &txbuf[w], next_write); if (n > 0) { w += n; wrem -= n; err = n; } } long int tx_tf = get_now_ts(); DEBUG_TEST("wrote=%d", w); // RX long int rx_ti = 0; while (rrem) { n = READ(fd, &rxbuf[r], rrem); if (rx_ti == 0) { // wait for first message rx_ti = get_now_ts(); } if (n > 0) { r += n; rrem -= n; err = n; } } long int rx_tf = get_now_ts(); DEBUG_TEST("read=%d", r); sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); // Compare RX and TX buffer and detect mismatches bool match = true; for (int i=0; i=0); } free(rxbuf); free(txbuf); } void tcp_server_sustained_4(TCP_UNIT_TEST_SIG_4) { std::string testname = "tcp_server_sustained_4"; std::string msg = "tcp_sustained_4"; fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts); fprintf(stderr, "accept connection from host with IPv4 address, exchange a sequence of packets, check order.\n"); int n=0, w=0, r=0, fd, client_fd, err; char *rxbuf = (char*)malloc(cnt*sizeof(char)); memset(rxbuf, 0, cnt); if ((fd = SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { DEBUG_ERROR("error creating ZeroTier socket"); perror("socket"); *passed = false; return; } if ((err = BIND(fd, (struct sockaddr *)addr, (socklen_t)sizeof(*addr)) < 0)) { DEBUG_ERROR("error binding to interface (%d)", err); perror("bind"); *passed = false; return; } if ((err = LISTEN(fd, 1)) < 0) { DEBUG_ERROR("error placing socket in LISTENING state (%d)", err); perror("listen"); *passed = false; return; } struct sockaddr_storage client; struct sockaddr_in *in4 = (struct sockaddr_in*)&client; socklen_t client_addrlen = sizeof(sockaddr_storage); if ((client_fd = ACCEPT(fd, (struct sockaddr *)in4, &client_addrlen)) < 0) { fprintf(stderr,"error accepting connection (%d)\n", err); perror("accept"); } DEBUG_TEST("accepted connection from %s, on port %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); if (op == TEST_OP_N_BYTES) { int wrem = cnt, rrem = cnt; long int rx_ti = 0; while (rrem) { n = READ(client_fd, &rxbuf[r], rrem); if (n > 0) { if (rx_ti == 0) { // wait for first message rx_ti = get_now_ts(); } r += n; rrem -= n; err = n; DEBUG_TEST("read=%d, r=%d, rrem=%d", n, r, rrem); } } long int rx_tf = get_now_ts(); DEBUG_TEST("read=%d, writing next...", r); long int tx_ti = get_now_ts(); while (wrem) { int next_write = std::min(1024, wrem); n = WRITE(client_fd, &rxbuf[w], next_write); if (n > 0) { w += n; wrem -= n; err = n; } } long int tx_tf = get_now_ts(); DEBUG_TEST("wrote=%d", w); sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); err = CLOSE(client_fd); // Compute time deltas and transfer rates float tx_dt = (tx_tf - tx_ti) / (float)1000; float rx_dt = (rx_tf - rx_ti) / (float)1000; float tx_rate = (float)cnt / (float)tx_dt; float rx_rate = (float)cnt / (float)rx_dt; 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", testname.c_str(), cnt, tx_dt, rx_dt, r, w, (tx_rate / float(ONE_MEGABYTE) ), (rx_rate / float(ONE_MEGABYTE) )); *passed = (r == cnt && w == cnt && err>=0); } free(rxbuf); } void tcp_server_sustained_6(TCP_UNIT_TEST_SIG_6) { std::string testname = "tcp_server_sustained_6"; std::string msg = "tcp_sustained_6"; fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts); fprintf(stderr, "accept connection from host with IPv6 address, exchange a sequence of packets, check order.\n"); int n=0, w=0, r=0, fd, client_fd, err; char *rxbuf = (char*)malloc(cnt*sizeof(char)); memset(rxbuf, 0, cnt); if ((fd = SOCKET(AF_INET6, SOCK_STREAM, 0)) < 0) { DEBUG_ERROR("error creating ZeroTier socket"); perror("socket"); *passed = false; return; } if ((err = BIND(fd, (struct sockaddr *)addr, (socklen_t)sizeof(struct sockaddr_in6)) < 0)) { DEBUG_ERROR("error binding to interface (%d)", err); perror("bind"); *passed = false; return; } if ((err = LISTEN(fd, 1)) < 0) { DEBUG_ERROR("error placing socket in LISTENING state (%d)", err); perror("listen"); *passed = false; return; } struct sockaddr_in6 client; socklen_t client_addrlen = sizeof(sockaddr_in6); if ((client_fd = ACCEPT(fd, (struct sockaddr *)&client, &client_addrlen)) < 0) { fprintf(stderr,"error accepting connection (%d)\n", err); perror("accept"); *passed = false; return; } char ipstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &client.sin6_addr, ipstr, sizeof ipstr); DEBUG_TEST("accepted connection from %s, on port %d", ipstr, ntohs(client.sin6_port)); if (op == TEST_OP_N_BYTES) { int wrem = cnt, rrem = cnt; long int rx_ti = 0; while (rrem) { n = READ(client_fd, &rxbuf[r], rrem); if (n > 0) { if (rx_ti == 0) { // wait for first message rx_ti = get_now_ts(); } r += n; rrem -= n; err = n; } } long int rx_tf = get_now_ts(); DEBUG_TEST("read=%d", r); long int tx_ti = get_now_ts(); while (wrem) { int next_write = std::min(1024, wrem); n = WRITE(client_fd, &rxbuf[w], next_write); if (n > 0) { w += n; wrem -= n; err = n; } } long int tx_tf = get_now_ts(); DEBUG_TEST("wrote=%d", w); sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); err = CLOSE(client_fd); // Compute time deltas and transfer rates float tx_dt = (tx_tf - tx_ti) / (float)1000; float rx_dt = (rx_tf - rx_ti) / (float)1000; float tx_rate = (float)cnt / (float)tx_dt; float rx_rate = (float)cnt / (float)rx_dt; 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", testname.c_str(), cnt, tx_dt, rx_dt, r, w, (tx_rate / float(ONE_MEGABYTE) ), (rx_rate / float(ONE_MEGABYTE) )); *passed = (r == cnt && w == cnt && err>=0); } free(rxbuf); } void udp_client_sustained_4(UDP_UNIT_TEST_SIG_4) { std::string testname = "udp_client_sustained_4"; std::string msg = "udp_sustained_4"; fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts); fprintf(stderr, "bind to interface with IPv4 address, TX n-datagrams\n"); int w, fd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if ((fd = SOCKET(AF_INET, SOCK_DGRAM, 0)) < 0) { DEBUG_ERROR("error creating ZeroTier socket"); perror("socket"); *passed = false; return; } if ((err = FCNTL(fd, F_SETFL, O_NONBLOCK) < 0)) { fprintf(stderr, "error setting O_NONBLOCK (errno=%d)", errno); perror("fcntl"); *passed = false; return; } DEBUG_TEST("sending UDP packets until I get a single response..."); if ((err = BIND(fd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in)) < 0)) { DEBUG_ERROR("error binding to interface (%d)", err); perror("bind"); *passed = false; return; } int num_to_send = 10; for (int i=0; isin_addr), addrstr, INET_ADDRSTRLEN); // once we receive a UDP packet, spend 10 seconds sending responses in the hopes that the client will see DEBUG_TEST("received DGRAM from %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); DEBUG_TEST("sending DGRAM(s) to %s : %d", inet_ntoa(remote_addr->sin_addr), ntohs(remote_addr->sin_port)); } sleep(ARTIFICIAL_SOCKET_LINGER); //err = CLOSE(fd); DEBUG_TEST("%s, n=%d, err=%d, r=%d", testname.c_str(), cnt, err, r); sprintf(details, "%s, n=%d, err=%d, r=%d", testname.c_str(), cnt, err, r); DEBUG_TEST("Received : %s", rbuf); *passed = (r == len && !err) && !strcmp(rbuf, msg.c_str()); } void udp_client_sustained_6(UDP_UNIT_TEST_SIG_6) { std::string testname = "udp_client_sustained_6"; std::string msg = "udp_sustained_6"; fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts); fprintf(stderr, "bind to interface with IPv6 address, TX n-datagrams\n"); int w, fd, err, len = strlen(msg.c_str()); char rbuf[STR_SIZE]; memset(rbuf, 0, sizeof rbuf); if ((fd = SOCKET(AF_INET6, SOCK_DGRAM, 0)) < 0) { DEBUG_ERROR("error creating ZeroTier socket"); perror("socket"); *passed = false; return; } if ((err = FCNTL(fd, F_SETFL, O_NONBLOCK) < 0)) { fprintf(stderr, "error setting O_NONBLOCK (errno=%d)", errno); perror("fcntl"); *passed = false; return; } DEBUG_TEST("sending UDP packets until I get a single response..."); if ((err = BIND(fd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in6)) < 0)) { DEBUG_ERROR("error binding to interface (%d)", err); perror("bind"); *passed = false; return; } int num_to_send = 10; for (int i=0; isin6_addr), addrstr, INET6_ADDRSTRLEN); // once we receive a UDP packet, spend 10 seconds sending responses in the hopes that the client will see //DEBUG_TEST("received DGRAM from %s : %d", inet_ntoa(in6->sin6_addr), ntohs(in6->sin6_port)); //DEBUG_TEST("sending DGRAM(s) to %s : %d", inet_ntoa(remote_addr->sin6_addr), ntohs(remote_addr->sin6_port)); } sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); DEBUG_TEST("%s, n=%d, err=%d, r=%d", testname.c_str(), cnt, err, r); sprintf(details, "%s, n=%d, err=%d, r=%d", testname.c_str(), cnt, err, r); DEBUG_TEST("Received : %s", rbuf); *passed = (r == len && !err) && !strcmp(rbuf, msg.c_str()); } /****************************************************************************/ /* PERFORMANCE (between library instances) */ /****************************************************************************/ // Maintain transfer for cnt OR cnt void tcp_client_perf_4(TCP_UNIT_TEST_SIG_4) { fprintf(stderr, "\n\n\ntcp_client_perf_4\n"); /* int w=0, fd, err; int total_test_sz = cnt; int arbitrary_chunk_sz_max = MAX_RX_BUF_SZ; int arbitrary_chunk_sz_min = 512; char rbuf[arbitrary_chunk_sz_max]; for (int i=arbitrary_chunk_sz_min; (i*2) < arbitrary_chunk_sz_max; i*=2) { if ((fd = SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) DEBUG_ERROR("error creating ZeroTier socket"); if ((err = CONNECT(fd, (const struct sockaddr *)addr, sizeof(addr))) < 0) DEBUG_ERROR("error connecting to remote host (%d)", err); DEBUG_TEST("[TX] Testing (%d) byte chunks: ", i); int chunk_sz = i; long int start_time = get_now_ts(); w = 0; // TX while (w < total_test_sz) w += WRITE(fd, rbuf, chunk_sz); long int end_time = get_now_ts(); float ts_delta = (end_time - start_time) / (float)1000; float rate = (float)total_test_sz / (float)ts_delta; sprintf(details, "tot=%d, dt=%.2f, rate=%.2f MB/s", w, ts_delta, (rate / float(ONE_MEGABYTE) )); CLOSE(fd); } *passed = (w == total_test_sz && !err) ? PASSED : FAILED; */ } // Maintain transfer for cnt OR cnt void tcp_server_perf_4(TCP_UNIT_TEST_SIG_4) { fprintf(stderr, "\n\n\ntcp_server_perf_4\n"); /* int r=0, fd, client_fd, err; int total_test_sz = cnt; int arbitrary_chunk_sz_max = MAX_RX_BUF_SZ; int arbitrary_chunk_sz_min = 512; char rbuf[arbitrary_chunk_sz_max]; for (int i=arbitrary_chunk_sz_min; (i*2) < arbitrary_chunk_sz_max; i*=2) { DEBUG_ERROR("TESTING chunk size = %d", i); if ((fd = SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) DEBUG_ERROR("error creating ZeroTier socket"); if ((err = BIND(fd, (struct sockaddr *)addr, (socklen_t)sizeof(struct sockaddr_in)) < 0)) DEBUG_ERROR("error binding to interface (%d)", err); if ((err = LISTEN(fd, 1)) < 0) DEBUG_ERROR("error placing socket in LISTENING state (%d)", err); if ((client_fd = ACCEPT(fd, (struct sockaddr *)&addr, (socklen_t *)sizeof(addr))) < 0) DEBUG_ERROR("error accepting connection (%d)", err); DEBUG_TEST("[RX] Testing (%d) byte chunks: ", i); int chunk_sz = i; long int start_time = get_now_ts(); r = 0; // RX while (r < total_test_sz) r += READ(client_fd, rbuf, chunk_sz); long int end_time = get_now_ts(); float ts_delta = (end_time - start_time) / (float)1000; float rate = (float)total_test_sz / (float)ts_delta; sprintf(details, "tot=%d, dt=%.2f, rate=%.2f MB/s", r, ts_delta, (rate / float(ONE_MEGABYTE) )); CLOSE(fd); CLOSE(client_fd); } *passed = (r == total_test_sz && !err) ? PASSED : FAILED; */ } /****************************************************************************/ /* PERFORMANCE (between library and native) */ /****************************************************************************/ void tcp_perf_tx_echo_4(TCP_UNIT_TEST_SIG_4) { std::string msg = "tcp_perf_tx_echo_4"; fprintf(stderr, "\n\n%s\n\n", msg.c_str()); int err = 0; int tot = 0; int w = 0; int fd, mode; char pbuf[64]; // test parameter buffer char tbuf[MAX_TX_BUF_SZ]; mode = ECHOTEST_MODE_TX; // connect to remote echotest host if ((fd = SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { DEBUG_ERROR("error creating ZeroTier socket"); return; } if ((err = CONNECT(fd, (const struct sockaddr *)addr, sizeof(*addr))) < 0) { DEBUG_ERROR("error connecting to remote host (%d)", err); return; } DEBUG_TEST("copying test parameters to buffer"); memset(pbuf, 0, sizeof pbuf); memcpy(pbuf, &mode, sizeof mode); memcpy(pbuf + sizeof mode, &cnt, sizeof cnt); DEBUG_TEST("sending test parameters to echotest"); if ((w = WRITE(fd, pbuf, sizeof pbuf)) < 0) { DEBUG_ERROR("error while sending test parameters to echotest (err=%d)", w); return; } // begin DEBUG_TEST("beginning test, sending test byte stream..."); while (tot < cnt) { if ((w = WRITE(fd, tbuf, sizeof tbuf)) < 0) { DEBUG_ERROR("error while sending test byte stream to echotest (err=%d)", w); return; } tot += w; DEBUG_TEST("tot=%d, sent=%d", tot, w); } // read results memset(pbuf, 0, sizeof pbuf); DEBUG_TEST("reading test results from echotest"); if ((w = READ(fd, pbuf, sizeof tbuf)) < 0) { DEBUG_ERROR("error while reading results from echotest (err=%d)", w); return; } DEBUG_TEST("reading test results"); long int start_time = 0, end_time = 0; memcpy(&start_time, pbuf, sizeof start_time); memcpy(&end_time, pbuf + sizeof start_time, sizeof end_time); float ts_delta = (end_time - start_time) / (float)1000; float rate = (float)tot / (float)ts_delta; sprintf(details, "%s, tot=%d, dt=%.2f, rate=%.2f MB/s", msg.c_str(), tot, ts_delta, (rate / float(ONE_MEGABYTE) )); sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); *passed = (tot == cnt && !err) ? PASSED : FAILED; } void tcp_perf_rx_echo_4(TCP_UNIT_TEST_SIG_4) { std::string msg = "tcp_perf_rx_echo_4"; fprintf(stderr, "\n\n%s\n\n", msg.c_str()); int err = 0; int mode = 0; int tot = 0; int r = 0; char pbuf[64]; // test parameter buffer char tbuf[MAX_TX_BUF_SZ]; int fd; mode = ECHOTEST_MODE_RX; // connect to remote echotest host if ((fd = SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { DEBUG_ERROR("error creating ZeroTier socket"); return; } if ((err = CONNECT(fd, (const struct sockaddr *)addr, sizeof(*addr))) < 0) { DEBUG_ERROR("error connecting to remote host (%d)", err); return; } DEBUG_TEST("copying test parameters to buffer"); memset(pbuf, 0, sizeof pbuf); memcpy(pbuf, &mode, sizeof mode); memcpy(pbuf + sizeof mode, &cnt, sizeof cnt); DEBUG_TEST("sending test parameters to echotest"); if ((r = WRITE(fd, pbuf, sizeof pbuf)) < 0) { DEBUG_ERROR("error while sending test parameters to echotest (err=%d)", r); return; } // begin DEBUG_TEST("beginning test, as soon as bytes are read we will start keeping time..."); if ((r = read(fd, tbuf, sizeof tbuf)) < 0) { DEBUG_ERROR("there was an error reading the test stream. aborting (err=%d, errno=%s)", r, strerror(errno)); return; } tot += r; long int start_time = get_now_ts(); DEBUG_TEST("Received first set of bytes in test stream. now keeping time"); while (tot < cnt) { if ((r = read(fd, tbuf, sizeof tbuf)) < 0) { DEBUG_ERROR("there was an error reading the test stream. aborting (err=%d)", r); return; } tot += r; DEBUG_TEST("r=%d, tot=%d", r, tot); } 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, "%s, tot=%d, dt=%.2f, rate=%.2f MB/s", msg.c_str(), tot, ts_delta, (rate / float(ONE_MEGABYTE) )); sleep(ARTIFICIAL_SOCKET_LINGER); err = CLOSE(fd); *passed = (tot == cnt && !err) ? PASSED : FAILED; } /****************************************************************************/ /* OBSCURE API CALL TESTS */ /****************************************************************************/ int obscure_api_test(bool *passed) { int err = -1; fprintf(stderr, "\n\nobscure API test\n\n"); /* // --- // getpeername() int fd, client_fd; // after accept() if ((fd = SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) DEBUG_ERROR("error creating ZeroTier socket"); if ((err = BIND(fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) < 0)) DEBUG_ERROR("error binding to interface (%d)", err); if ((err = LISTEN(fd, 100)) < 0) printf("error placing socket in LISTENING state (%d)", err); // accept struct sockaddr_in client; socklen_t client_addrlen = sizeof(sockaddr_in); if ((client_fd = accept(fd, (struct sockaddr *)&client, &client_addrlen)) < 0) fprintf(stderr,"error accepting connection (%d)\n", err); fprintf(stderr, "accepted connection from %s, on port %d", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); // getpeername struct sockaddr_storage peer_addr; struct sockaddr_in *in4 = (struct sockaddr_in*)&peer_addr; socklen_t peer_addrlen = sizeof(peer_addr); GETPEERNAME(fd, (struct sockaddr*)&peer_addr, &peer_addrlen); DEBUG_TEST("getpeername() => %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); // compate getpeername() result to address returned by accept() // after connect if ((fd = SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) DEBUG_ERROR("error creating ZeroTier socket"); if ((err = CONNECT(fd, (const struct sockaddr *)addr, sizeof(*addr))) < 0) DEBUG_ERROR("error connecting to remote host (%d)", err); // TODO: Put this test in the general API section struct sockaddr_storage peer_addr; struct sockaddr_in *in4 = (struct sockaddr_in*)&peer_addr; socklen_t peer_addrlen = sizeof(peer_addr); GETPEERNAME(fd, (struct sockaddr*)&peer_addr, &peer_addrlen); DEBUG_TEST("getpeername() => %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); // compare result of getpeername to remote address // TODO: write an ipv6 version of the above ^^^ */ /* int levels[] = { IPPROTO_TCP, IPPROTO_UDP, IPPROTO_IP }; int num_levels = sizeof(levels) / sizeof(int); int optnames[] = { TCP_NODELAY, SO_LINGER }; int num_optnames = sizeof(optnames) / sizeof(int); for (int i=0; i 0) { // TODO: what should be expected for each platform? Should this mirror them? optval = 0; DEBUG_TEST("setting level=%d, optname=%d, optval=%d...", level, optname, optval); if ((err = SETSOCKOPT(fd, level, optname, (char *) &optval, (socklen_t)sizeof(int))) < 0) { DEBUG_ERROR("error while setting on socket"); *passed = false; err = -1; } else { DEBUG_TEST("success"); *passed = true; } } else { DEBUG_ERROR("the optval wasn't set correctly"); *passed = false; err = -1; } } } */ return err; } /****************************************************************************/ /* SLAM API (multiple of each api call and/or plausible call sequence) */ /****************************************************************************/ #if defined(__SELFTEST__) int ZT_control_semantics_test(bool *passed) { // TODO: Each discrete operation should be tested in random order among every other discrete operation for a sustained period /* std::vector *zts_get_network_routes(char *nwid); int zts_get_device_id_from_file(const char *filepath, char *devID); void *zts_start_service(void *thread_id); void disableTaps(); void zts_get_ipv4_address(const char *nwid, char *addrstr, const size_t addrlen); void zts_get_ipv6_address(const char *nwid, char *addrstr, const size_t addrlen); int zts_has_ipv4_address(const char *nwid); int zts_has_ipv6_address(const char *nwid); int zts_has_address(const char *nwid); void zts_get_6plane_addr(char *addr, const char *nwid, const char *devID); void zts_get_rfc4193_addr(char *addr, const char *nwid, const char *devID); void zts_join(const char * nwid); void zts_leave(const char * nwid); int zts_running(); int zts_start(const char *path); int zts_start(const char *path, const char *nwid); void zts_stop(); void zts_get_homepath(char *homePath, size_t len); int zts_get_device_id(char *devID); unsigned long zts_get_peer_count(); int zts_get_peer_address(char *peer, const char *devID); */ int n_times = 5; char *nwid = "17d709436c2c5367"; char *path = "fake_path"; /* // Perform operations on ZeroTier before calling zts_start(). Doing this makes absolutely no sense but could happen zts_stop(); zts_join(nwid); zts_leave(nwid); DEBUG_TEST("---\n"); sleep(1); // Perform operations on ZeroTier immediately upon startup, try to catch it with its pants down // Ideally, the service wrapper should perform necessary checks to prevent any sort of issue zts_start(path); zts_join(nwid); zts_leave(nwid); zts_stop(); DEBUG_TEST("---\n"); sleep(1); */ zts_start(path); zts_join(nwid); zts_leave(nwid); zts_stop(); DEBUG_TEST("---\n"); sleep(1); /* // start the ZeroTier service many times for (int i=0; i used_ports; for (int j=0; j 0) { if ((err = CLOSE(sock)) < 0) { std::cout << "error closing socket (errno = " << strerror(errno) << ")" << std::endl; //return -1; } } } } used_ports.clear(); //if (zts_num_active_virt_sockets() == 0) // std::cout << "PASSED [slam open, bind, listen, accept, close]" << std::endl; //else // std::cout << "FAILED [slam open, bind, listen, accept, close]" << std::endl; } // TESTS: // (1) socket() // (2) connect() // (3) close() int num_times = 3;//zts_maxsockets(SOCK_STREAM); std::cout << "socket/connect/close - " << num_times << " times" << std::endl; for (int i=0;i<(SLAM_NUMBER*SLAM_REPEAT); i++) { results[i] = 0; } if (true) { int port = 4545; // open, bind, listen, accept, close for (int j=0; j *routes = zts_get_network_routes(nwid); for (int i=0; isize(); i++) { struct sockaddr_in *target = (struct sockaddr_in*)&(routes->at(i).target); struct sockaddr_in *via = (struct sockaddr_in*)&(routes->at(i).via); char target_str[INET6_ADDRSTRLEN]; memset(target_str, 0, INET6_ADDRSTRLEN); inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)target)->sin_addr.s_addr, target_str, INET_ADDRSTRLEN); char via_str[INET6_ADDRSTRLEN]; memset(via_str, 0, INET6_ADDRSTRLEN); inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)via)->sin_addr.s_addr, via_str, INET_ADDRSTRLEN); DEBUG_TEST("", target_str, via_str, routes->at(i).flags); } } */ /****************************************************************************/ /* RANDOMIZED API TEST */ /****************************************************************************/ int random_api_test() { // PASSED implies we didn't segfault or hang anywhere // variables which will be populated with random values /* int socket_family; int socket_type; int protocol; int fd; int len; int addrlen; int flags; struct sockaddr_storage; struct sockaddr_in addr; struct sockaddr_in6 addr6; */ /* int num_operations = 100; char *opbuf = (char*)malloc(num_operations*sizeof(char)); generate_random_data(opbuf, num_operations, 0, 9); for (int i=0; ifd; struct sockaddr_in *remote_addr = fdp->remote_addr; //fprintf(stderr, "fd=%d\n", fd); int w = 0; for (int i=0; isin_port++; } } void bind_to_localhost_test(int port) { fprintf(stderr, "\n\nbind_to_localhost_test\n\n"); int fd, err = 0; // ipv4, 0.0.0.0 struct sockaddr_storage bind_addr; DEBUG_TEST("binding to 0.0.0.0"); str2addr("0.0.0.0", port, 4, (struct sockaddr *)&bind_addr); if ((fd = SOCKET(AF_INET, SOCK_STREAM, 0)) > 0) { if ((err = BIND(fd, (struct sockaddr *)&bind_addr, sizeof(struct sockaddr_in))) == 0) { usleep(100000); if ((err = 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 (%d)", err); } port++; /* // ipv4, 127.0.0.1 DEBUG_TEST("binding to 127.0.0.1"); str2addr("127.0.0.1", port, 4, (struct sockaddr *)&bind_addr); if ((fd = SOCKET(AF_INET, SOCK_STREAM, 0)) > 0) { if ((err = BIND(fd, (struct sockaddr *)&bind_addr, sizeof(struct sockaddr_in))) == 0) { usleep(100000); if ((err = 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_TEST("binding to [::]"); str2addr("::", port, 6, (struct sockaddr *)&bind_addr); if ((fd = SOCKET(AF_INET6, SOCK_STREAM, 0)) > 0) { if ((err = BIND(fd, (struct sockaddr *)&bind_addr, sizeof(struct sockaddr_in))) == 0) { usleep(100000); if ((err = 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 (%d)", err); } } #endif // __SELFTEST__ /****************************************************************************/ /* main(), calls test_driver(...) */ /****************************************************************************/ int main(int argc , char *argv[]) { if (argc < 6) { fprintf(stderr, "usage: selftest to \n"); fprintf(stderr, "e.g. : selftest 3 test/test.conf alice to bob\n"); return 1; } int num_repeats = atoi(argv[1]); std::string path = argv[2]; std::string from = argv[3]; std::string to = argv[5]; std::string me = from; std::vector results; std::string remote_echo_ipv4, smode; std::string nwid, stype; std::string ipstr, ipstr6, local_ipstr, local_ipstr6, remote_ipstr, remote_ipstr6; int err = 0; int mode = 0; int port = 0; int op = 0; int start_port = 0; int cnt = 0; int ipv; // for timing // how long we expect the specific test to take int subtest_expected_duration; // (T+X), when we plan to start this test int subtest_start_time_offset = 0; char details[128]; memset(&details, 0, sizeof details); bool passed = 0; struct sockaddr_storage local_addr; struct sockaddr_storage remote_addr; // load config file if (path.find(".conf") == std::string::npos) { fprintf(stderr, "Possibly invalid conf file. Exiting...\n"); exit(0); } loadTestConfigFile(path); // get origin details local_ipstr = testConf[me + ".ipv4"]; local_ipstr6 = testConf[me + ".ipv6"]; nwid = testConf[me + ".nwid"]; path = testConf[me + ".path"]; stype = testConf[me + ".test"]; smode = testConf[me + ".mode"]; start_port = atoi(testConf[me + ".port"].c_str()); remote_echo_ipv4 = testConf[to + ".echo_ipv4"]; remote_ipstr = testConf[to + ".ipv4"]; remote_ipstr6 = testConf[to + ".ipv6"]; if (strcmp(smode.c_str(), "server") == 0) mode = TEST_MODE_SERVER; else mode = TEST_MODE_CLIENT; fprintf(stderr, "\n\nORIGIN:\n\n"); fprintf(stderr, "\tlocal_ipstr = %s\n", local_ipstr.c_str()); fprintf(stderr, "\tlocal_ipstr6 = %s\n", local_ipstr6.c_str()); fprintf(stderr, "\tstart_port = %d\n", start_port); fprintf(stderr, "\tpath = %s\n", path.c_str()); fprintf(stderr, "\tnwid = %s\n", nwid.c_str()); fprintf(stderr, "\ttype = %s\n\n", stype.c_str()); fprintf(stderr, "DESTINATION:\n\n"); fprintf(stderr, "\tremote_ipstr = %s\n", remote_ipstr.c_str()); fprintf(stderr, "\tremote_ipstr6 = %s\n", remote_ipstr6.c_str()); fprintf(stderr, "\tremote_echo_ipv4 = %s\n", remote_echo_ipv4.c_str()); #if defined(__SELFTEST__) long int selftest_start_time = get_now_ts(); subtest_expected_duration = 5; if (me != "dummy") { // used for testing ZT service wrapper API (before, during, and after coming online) // set start time here since we need to wait for both libzt instances to be online DEBUG_TEST("Waiting for libzt to come online...\n"); zts_startjoin(path.c_str(), nwid.c_str()); char device_id[ZTO_ID_LEN]; zts_get_device_id(device_id); DEBUG_TEST("I am %s, %s", device_id, me.c_str()); if (mode == TEST_MODE_SERVER) { DEBUG_TEST("Ready. You should start selftest program on second host now...\n\n"); } if (mode == TEST_MODE_CLIENT) { DEBUG_TEST("Ready. Contacting selftest program on first host.\n\n"); } // What follows is a long-form of zts_start(): /* zts_start(path.c_str()); printf("waiting for service to start...\n"); while (zts_running() == false) sleep(1); printf("joining network...\n"); zts_join(nwid.c_str()); printf("waiting for address assignment...\n"); while (zts_has_address(nwid.c_str()) == false) sleep(1); */ } #endif // __SELFTEST__ for (int i=0; i