added compilation sanity checks, unit test script, updated docs

This commit is contained in:
Joseph Henry
2016-11-03 15:55:03 -07:00
parent c10120c19f
commit efa0fad664
29 changed files with 380 additions and 328 deletions

4
Jenkinsfile vendored
View File

@@ -10,6 +10,10 @@ parallel 'centos7': {
stage('Build centos7') { stage('Build centos7') {
sh 'make linux_service_and_intercept SDK_LWIP=1 SDK_IPV4=1' sh 'make linux_service_and_intercept SDK_LWIP=1 SDK_IPV4=1'
} }
stage('Run Basic Unit Test') {
sh 'make unit_test SDK_LWIP=1 SDK_IPV4=1'
sh './tests/unit/docker/start/sh'
}
} }
catch (err) { catch (err) {
currentBuild.result = "FAILURE" currentBuild.result = "FAILURE"

View File

@@ -4,6 +4,8 @@ FILE="$1"
if ([ -f "$FILE" ] && [ -s "$FILE" ]) || ([ -d "$FILE" ] && [ "$(ls -A "$FILE")" ]); if ([ -f "$FILE" ] && [ -s "$FILE" ]) || ([ -d "$FILE" ] && [ "$(ls -A "$FILE")" ]);
then then
echo "[OK ] $FILE" echo "[OK ] $FILE"
exit 0
else else
echo "[FAIL] $FILE" >&2 echo "[FAIL] $FILE" >&2
exit 1
fi fi

View File

@@ -14,10 +14,15 @@ For more support on these integrations, or if you'd like help creating a new int
*** ***
## Important Build flags ## Important Build flags
- `SDK_IPV4=1` - Enable IPv4 support in whatever stack you have selected
- `SDK_IPV6=1` - Enable IPv6 support in whatever stack you have selected
- `SDK_LWIP=1` - Enable the use of `lwIP`
- `SDK_PICOTCP=1` - Enable the use of `picoTCP`
- `SDK_DEBUG=1` - Turns on SDK activity/warning/error output. Levels of verbosity can be adjusted in `src/SDK_Debug.h` - `SDK_DEBUG=1` - Turns on SDK activity/warning/error output. Levels of verbosity can be adjusted in `src/SDK_Debug.h`
- `SDK_DEBUG_LOGFILE=1` - Used in conjunction with `SDK_DEBUG`, this will write all SDK debug chatter to a log file. To use this, set `make SDK_DEBUG_LOGFILE=1` then `export ZT_SDK_LOGFILE=debug.log`. - `SDK_LWIP_DEBUG=1` - Turns on debug output for the `lwIP` library.
- `SDK_LWIP_DEBUG=1` - Turns on debug output for the lwIP library. - `SDK_BUNDLED=1` - Builds the SDK as a single bundled target including a the RPC mechanism, the `lwIP` library, and the ZeroTier service.
- `SDK_BUNDLED=1` - Builds the SDK as a single bundled target including a the RPC mechanism, the lwIP library, and the ZeroTier service.
*** ***
## Current Integrations ## Current Integrations

View File

@@ -1,4 +1,4 @@
ZeroTier SDK (beta) ZeroTier SDK
====== ======
ZeroTier-enabled apps. Virtual network access embedded directly into applications and games. ZeroTier-enabled apps. Virtual network access embedded directly into applications and games.
@@ -23,7 +23,7 @@ Check out our [Integrations](integrations/) to learn how to integrate this into
## How does it work? ## How does it work?
We've built a special background service that pairs the ZeroTier protocol with a user-space [Lightweight IP (lwIP) stack](http://savannah.nongnu.org/projects/lwip/) to create a new way for you to bring your applications onto your virtual network. For a more in-depth explanation of our technology take a look at our [SDK Primer](docs/zt_sdk_primer.md) We've built a special background tap service that pairs the ZeroTier protocol with swappable user-space network stacks. For our initial release we've provided drivers for [Lightweight IP (lwIP)](http://savannah.nongnu.org/projects/lwip/) and [picoTCP](http://www.picotcp.com/). The aim is to provide a new way for you to bring your applications onto your virtual network. For a more in-depth explanation of our technology take a look at our [SDK Primer](docs/zt_sdk_primer.md)
## APIs ## APIs

View File

@@ -40,8 +40,8 @@ When your applcation attempts to establish a connection over a socket the follow
- our library's `zt_socket()` is executed instead - our library's `zt_socket()` is executed instead
- library establishes an `AF_LOCAL` socket connection with the service (this is used to orchestrate communication between the library and the ZeroTier service) - library establishes an `AF_LOCAL` socket connection with the service (this is used to orchestrate communication between the library and the ZeroTier service)
- an `RPC_SOCKET` message is sent to the ZeroTier tap service - an `RPC_SOCKET` message is sent to the ZeroTier tap service
- the tap service receives the `RPC_SOCKET` message and requests a new `tcp_pcb` representing the new "socket" from lwIP - the tap service receives the `RPC_SOCKET` message and requests the allocation of a new "connection" object representing the new "socket" from lwIP
- If the user-space network stack grants the tap service the new `tcp_pcb`, the tap service then repurposes the socket used for the RPC message and returns its file descriptor to your application for it to use as the new socket. - If the user-space network stack grants the tap service the new connection object, the tap service then repurposes the socket used for the RPC message and returns its file descriptor to your application for it to use as the new socket.
From your application's perspective nothing out of the ordinary has happened. It called `socket()`, and got a file descriptor back. From your application's perspective nothing out of the ordinary has happened. It called `socket()`, and got a file descriptor back.

View File

@@ -9,10 +9,17 @@ Currently our *lwIP* stack driver supports IPV4 and limited IPV6, whereas our *p
To enable specific protocol versions use `SDK_IPV4=1` and `SDK_IPV6=1` in conjunction with the above stack selection flags. To enable specific protocol versions use `SDK_IPV4=1` and `SDK_IPV6=1` in conjunction with the above stack selection flags.
Also, to enable debug for the SDK use `SDK_DEBUG=1`, to enable debug for the *lwIP* stack use `SDK_LWIP_DEBUG=1`.
## Integrating Your Own Custom Stack ## Integrating Your Own Custom Stack
If you don't know why this section exists, then I suggest turning back now. This is not for you. Now, let's get on with things, here's how you can integrate your own custom network stack if for some reason lwIP or picoTCP aren't cutting it for you. If you don't know why this section exists, then I suggest turning back now. This is not for you. Otherwise, let's get on with things, here's how you can integrate your own custom network stack if for some reason lwIP or picoTCP aren't cutting it for you:
The integration points are designated in the tap service code with the tags such as `SIP-0, SIP-1, ..., SIP-n`. Investigate the structure of `src/tap.cpp`, this file contains calls to functions implemented in the stack driver code (located in `src/stack_drivers`).
[More content to come] Each stack is different but generally you'll need to provide:
- An initialization function to configure and bring up the stack's `interface` (or similar).
- An I/O polling loop section where you'll execute your timer calls, and check for inbound and outbound frames.
- A low-level input and output function to handle feeding ethernet frames into and out of the stack in its own unique way.
- Calls to your stack's API which roughly correspond with `handleRead()`, `handleWrite()`, `handleSocket()`, `handleConnect()`, etc
- In those calls you'll need to handle the creation, management, and destruction of your stack's "connection" objects, whatever that may be.

View File

@@ -9,7 +9,7 @@
# Target output filenames # Target output filenames
SHARED_LIB_NAME = libztlinux.so SHARED_LIB_NAME = libztlinux.so
INTERCEPT_NAME = libztintercept.so SDK_INTERCEPT_NAME = libztintercept.so
SDK_SERVICE_NAME = zerotier-sdk-service SDK_SERVICE_NAME = zerotier-sdk-service
ONE_SERVICE_NAME = zerotier-one ONE_SERVICE_NAME = zerotier-one
ONE_CLI_NAME = zerotier-cli ONE_CLI_NAME = zerotier-cli
@@ -17,7 +17,7 @@ ONE_ID_TOOL_NAME = zerotier-idtool
LWIP_LIB_NAME = liblwip.so LWIP_LIB_NAME = liblwip.so
# #
SHARED_LIB = $(BUILD)/$(SHARED_LIB_NAME) SHARED_LIB = $(BUILD)/$(SHARED_LIB_NAME)
INTERCEPT = $(BUILD)/$(INTERCEPT_NAME) SDK_INTERCEPT = $(BUILD)/$(SDK_INTERCEPT_NAME)
SDK_SERVICE = $(BUILD)/$(SDK_SERVICE_NAME) SDK_SERVICE = $(BUILD)/$(SDK_SERVICE_NAME)
ONE_SERVICE = $(BUILD)/$(ONE_SERVICE_NAME) ONE_SERVICE = $(BUILD)/$(ONE_SERVICE_NAME)
ONE_CLI = $(BUILD)/$(ONE_CLI_NAME) ONE_CLI = $(BUILD)/$(ONE_CLI_NAME)
@@ -35,9 +35,9 @@ SDK_SERVICE_CPP_FILES:=src/tap.cpp \
$(ZT1)/one.cpp $(ZT1)/one.cpp
SDK_SERVICE_C_FILES = src/rpc.c SDK_SERVICE_C_FILES = src/rpc.c
SDK_INTERCEPT_C_FILES:=sockets.c \ SDK_INTERCEPT_C_FILES:=src/sockets.c \
intercept.c \ src/intercept.c \
rpc.c src/rpc.c
# Automagically pick clang or gcc, with preference for clang # Automagically pick clang or gcc, with preference for clang
@@ -190,7 +190,7 @@ one: $(OBJS) $(ZT1)/service/OneService.o $(ZT1)/one.o $(ZT1)/osdep/LinuxEthernet
# Build only the intercept library # Build only the intercept library
linux_intercept: linux_intercept:
# Use gcc not clang to build standalone intercept library since gcc is typically used for libc and we want to ensure maximal ABI compatibility # Use gcc not clang to build standalone intercept library since gcc is typically used for libc and we want to ensure maximal ABI compatibility
cd src ; gcc $(DEFS) $(INCLUDES) -g -O2 -Wall -std=c99 -fPIC -DVERBOSE -D_GNU_SOURCE -DSDK_INTERCEPT -nostdlib -nostdlib -shared -o ../$(INTERCEPT) $(SDK_INTERCEPT_C_FILES) -ldl gcc $(DEFS) $(INCLUDES) -g -O2 -Wall -std=c99 -fPIC -DVERBOSE -D_GNU_SOURCE -DSDK_INTERCEPT -nostdlib -nostdlib -shared -o $(SDK_INTERCEPT) $(SDK_INTERCEPT_C_FILES) -ldl
# Build only the SDK service # Build only the SDK service
ifeq ($(SDK_LWIP),1) ifeq ($(SDK_LWIP),1)
@@ -211,7 +211,7 @@ linux_service_and_intercept: linux_intercept linux_sdk_service
# Builds a single shared library which contains everything # Builds a single shared library which contains everything
linux_shared_lib: $(OBJS) linux_shared_lib: $(OBJS)
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(INCLUDES) -DSDK -DZT_ONE_NO_ROOT_CHECK -shared -o $(SHARED_LIB) $(OBJS) zerotierone/service/OneService.cpp src/service.cpp src/tap.cpp src/proxy.cpp zerotierone/one.cpp -x c src/sockets.c src/intercept.c src/rpc.c $(LDLIBS) -ldl $(CXX) $(CXXFLAGS) $(LDFLAGS) $(STACK_FLAGS) $(DEFS) $(INCLUDES) -DSDK_SERVICE -DSDK -DZT_ONE_NO_ROOT_CHECK -shared -o $(SHARED_LIB) $(OBJS) $(LWIP_DRIVER_FILES) $(SDK_SERVICE_CPP_FILES) $(SDK_SERVICE_C_FILES) $(SDK_INTERCEPT_C_FILES) $(LDLIBS) -ldl
@@ -231,11 +231,29 @@ android_jni_lib:
# -------- TESTING --------- # -------- TESTING ---------
# Build the docker demo images
docker_demo: one linux_shared_lib unit_test: one linux_service_and_intercept
mkdir -p $(BUILD) mkdir -p $(BUILD)
cp $(INTERCEPT) $(INT)/docker/docker_demo/$(INTERCEPT_NAME) cp $(SDK_INTERCEPT) tests/unit/docker/$(SDK_INTERCEPT_NAME)
cp $(SERVICE) $(INT)/docker/docker_demo/$(SERVICE_NAME) cp $(SDK_SERVICE) tests/unit/docker/$(SDK_SERVICE_NAME)
cp $(LWIP_LIB) tests/unit/docker/$(LWIP_LIB_NAME)
cp $(ONE_CLI) tests/unit/docker/$(ONE_CLI_NAME)
cp $(ONE_SERVICE) tests/unit/docker/$(ONE_SERVICE_NAME)
touch tests/unit/docker/docker_demo.name
# Server image
# This image will contain the server application and everything required to
# run the ZeroTier SDK service
cd tests/unit/docker; docker build --tag="docker_demo" -f sdk_dockerfile .
# Client image
# This image is merely a test image designed to interact with the server image
# in order to verify it's working properly
cd tests/unit/docker; docker build --tag="docker_demo_monitor" -f monitor_dockerfile .
# Build the docker demo images
docker_demo: one linux_service_and_intercept
mkdir -p $(BUILD)
cp $(SDK_INTERCEPT) $(INT)/docker/docker_demo/$(SDK_INTERCEPT_NAME)
cp $(SDK_SERVICE) $(INT)/docker/docker_demo/$(SDK_SERVICE_NAME)
cp $(LWIP_LIB) $(INT)/docker/docker_demo/$(LWIP_LIB_NAME) cp $(LWIP_LIB) $(INT)/docker/docker_demo/$(LWIP_LIB_NAME)
cp $(ONE_CLI) $(INT)/docker/docker_demo/$(ONE_CLI_NAME) cp $(ONE_CLI) $(INT)/docker/docker_demo/$(ONE_CLI_NAME)
touch $(INT)/docker/docker_demo/docker_demo.name touch $(INT)/docker/docker_demo/docker_demo.name
@@ -249,7 +267,7 @@ docker_demo: one linux_shared_lib
cd $(INT)/docker/docker_demo; docker build --tag="docker_demo_monitor" -f monitor_dockerfile . cd $(INT)/docker/docker_demo; docker build --tag="docker_demo_monitor" -f monitor_dockerfile .
# Builds all docker test images # Builds all docker test images
docker_images: one linux_shared_lib docker_images: one linux_service_and_intercept
./tests/docker/build_images.sh ./tests/docker/build_images.sh
# Runs docker container tests # Runs docker container tests
@@ -262,18 +280,18 @@ docker_check_test:
# Check for the presence of built frameworks/bundles/libaries # Check for the presence of built frameworks/bundles/libaries
check: check:
./check.sh $(LWIP_LIB) -./check.sh $(LWIP_LIB)
./check.sh $(INTERCEPT) -./check.sh $(SDK_INTERCEPT)
./check.sh $(ONE_SERVICE) -./check.sh $(ONE_SERVICE)
./check.sh $(SDK_SERVICE) -./check.sh $(SDK_SERVICE)
./check.sh $(SHARED_LIB) -./check.sh $(SHARED_LIB)
./check.sh $(BUILD)/android_jni_lib/arm64-v8a/libZeroTierOneJNI.so -./check.sh $(BUILD)/android_jni_lib/arm64-v8a/libZeroTierOneJNI.so
./check.sh $(BUILD)/android_jni_lib/armeabi/libZeroTierOneJNI.so -./check.sh $(BUILD)/android_jni_lib/armeabi/libZeroTierOneJNI.so
./check.sh $(BUILD)/android_jni_lib/armeabi-v7a/libZeroTierOneJNI.so -./check.sh $(BUILD)/android_jni_lib/armeabi-v7a/libZeroTierOneJNI.so
./check.sh $(BUILD)/android_jni_lib/mips/libZeroTierOneJNI.so -./check.sh $(BUILD)/android_jni_lib/mips/libZeroTierOneJNI.so
./check.sh $(BUILD)/android_jni_lib/mips64/libZeroTierOneJNI.so -./check.sh $(BUILD)/android_jni_lib/mips64/libZeroTierOneJNI.so
./check.sh $(BUILD)/android_jni_lib/x86/libZeroTierOneJNI.so -./check.sh $(BUILD)/android_jni_lib/x86/libZeroTierOneJNI.so
./check.sh $(BUILD)/android_jni_lib/x86_64/libZeroTierOneJNI.so -./check.sh $(BUILD)/android_jni_lib/x86_64/libZeroTierOneJNI.so
# Tests # Tests
OSTYPE=$(shell uname -s | tr '[A-Z]' '[a-z]') OSTYPE=$(shell uname -s | tr '[A-Z]' '[a-z]')
@@ -313,7 +331,7 @@ clean_basic:
-rm -rf $(BUILD)/* -rm -rf $(BUILD)/*
-rm -rf $(INT)/Unity3D/Assets/Plugins/* -rm -rf $(INT)/Unity3D/Assets/Plugins/*
-rm -rf zerotier-cli zerotier-idtool -rm -rf zerotier-cli zerotier-idtool
-find . -type f \( -name $(ONE_SERVICE_NAME) -o -name $(SDK_SERVICE_NAME) \) -delete -find . -type f \( -name $(ONE_SERVICE_NAME) -o -name $(SDK_SERVICE_NAME) -o -name $(ONE_CLI_NAME) \) -delete
-find . -type f \( -name '*.a' -o -name '*.o' -o -name '*.so' -o -name '*.o.d' -o -name '*.out' -o -name '*.log' -o -name '*.dSYM' \) -delete -find . -type f \( -name '*.a' -o -name '*.o' -o -name '*.so' -o -name '*.o.d' -o -name '*.out' -o -name '*.log' -o -name '*.dSYM' \) -delete
clean_thorough: clean_basic clean_thorough: clean_basic

View File

@@ -28,12 +28,27 @@
#ifndef _ZT_SDK_H #ifndef _ZT_SDK_H
#define _ZT_SDK_H 1 #define _ZT_SDK_H 1
#if defined(SDK_SERVICE)
// Sanity checks for compilation
#if !defined(SDK_LWIP) && !defined(SDK_PICOTCP)
#error "No network stack specified, use SDK_LWIP=1, SDK_PICOTCP=1, or similar"
#endif
#if defined(SDK_LWIP) && defined(SDK_PICOTCP)
#error "Dual stacks is not currently supported, try one or the other"
#endif
#if !defined(SDK_IPV4) && !defined(SDK_IPV6)
#error "No IP protocol version specified, use SDK_IPV4=1, or SDK_IPV6=1"
#endif
#if !defined(SDK_IPV4) && !defined(SDK_IPV6)
#error "Dual protocol versions is not currently supported. try one or the other"
#endif
#endif
#include <sys/socket.h> #include <sys/socket.h>
#include <stdbool.h> #include <stdbool.h>
#include "signatures.h" #include "signatures.h"
#if defined(__ANDROID__) #if defined(__ANDROID__)
// For defining the Android direct-call API
#include <jni.h> #include <jni.h>
#endif #endif

View File

@@ -7,7 +7,6 @@ rm _results/*.txt
# How long we shall wait for each test to conclude # How long we shall wait for each test to conclude
export sdk_test_wait_time=60s export sdk_test_wait_time=60s
export image_build_script=_build_single_image.sh export image_build_script=_build_single_image.sh
# Iterate over all depth=2 (relatively-speaking) directories and perform each test # Iterate over all depth=2 (relatively-speaking) directories and perform each test

View File

@@ -0,0 +1,16 @@
#!/bin/bash
# Runs test image and monitor image as daemons
test_name=${PWD##*/}
echo 'Starting containers for: ' "$test_name"
touch "$test_name".name
test_container=$(docker run -d -it -v $PWD/../../_results:/opt/results --device=/dev/net/tun "$test_name":latest)
monitor_container=$(docker run -d -it -v $PWD/../../_results:/opt/results --device=/dev/net/tun "$test_name"_monitor:latest)
echo "waiting $sdk_test_wait_time for test to complete."
sleep $sdk_test_wait_time
docker stop $(docker ps -a -q)
docker rm $test_container
docker rm $monitor_container
rm -f *.name

View File

@@ -1,49 +0,0 @@
// TCP Client test program
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int atoi(const char *str);
int close(int filedes);
int main(int argc , char *argv[])
{
if(argc < 3) {
printf("usage: client <addr> <port>\n");
return 1;
}
int sock, port = atoi(argv[2]);
struct sockaddr_in server;
char message[1000] , server_reply[2000];
sock = socket(AF_INET , SOCK_STREAM , 0);
if (sock == -1) {
printf("could not create socket");
}
server.sin_addr.s_addr = inet_addr(argv[1]);
server.sin_family = AF_INET;
server.sin_port = htons( port );
printf("connecting...\n");
if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0) {
perror("connect failed. Error");
return 1;
}
printf("connected\n");
char *msg = "welcome to the machine!";
// TX
if(send(sock, msg, strlen(msg), 0) < 0) {
printf("send failed");
return 1;
}
else {
printf("sent message: %s\n", msg);
printf("len = %ld\n", strlen(msg));
}
close(sock);
return 0;
}

View File

@@ -1,67 +0,0 @@
// TCP Server test program
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
int atoi(const char *str);
int main(int argc , char *argv[])
{
if(argc < 2) {
printf("usage: tcp_server <port>\n");
return 0;
}
int socket_desc, client_sock, c, read_size, port = atoi(argv[1]);
struct sockaddr_in server , client;
char client_message[2000];
socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if (socket_desc == -1) {
printf("could not create socket");
return 0;
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(port);
printf("binding on port %d\n", port);
if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0) {
perror("bind failed. Error");
return 0;
}
printf("listening\n");
listen(socket_desc , 3);
printf("waiting to accept\n");
c = sizeof(struct sockaddr_in);
client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
if (client_sock < 0) {
perror("accept failed");
return 0;
}
printf("connection accepted\n reading...\n");
// RX
int msglen = 1024;
unsigned long count = 0;
while(1)
{
count++;
int bytes_read = read(client_sock, client_message, msglen);
printf("[%lu] RX = (%d): ", count, bytes_read);
for(int i=0; i<bytes_read; i++) {
printf("%c", client_message[i]);
}
// TX
int bytes_written = write(client_sock, "Server here!", 12);
printf("\t\nTX = %d\n", bytes_written);
}
return 0;
}

View File

@@ -1,92 +0,0 @@
/*
* udpclient.c - A simple UDP client
* usage: udpclient <host> <port>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#define BUFSIZE 1024
/*
* error - wrapper for perror
*/
void error(char *msg) {
perror(msg);
exit(0);
}
int main(int argc, char **argv) {
int sockfd, portno, n;
int serverlen;
struct sockaddr_in serveraddr;
struct hostent *server;
char *hostname;
char buf[BUFSIZE];
/* check command line arguments */
if (argc != 3) {
fprintf(stderr,"usage: %s <hostname> <port>\n", argv[0]);
exit(0);
}
hostname = argv[1];
portno = atoi(argv[2]);
/* socket: create the socket */
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
/* gethostbyname: get the server's DNS entry */
server = gethostbyname(hostname);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host as %s\n", hostname);
exit(0);
}
/* build the server's Internet address */
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serveraddr.sin_addr.s_addr, server->h_length);
serveraddr.sin_port = htons(portno);
/* get a message from the user */
char *msg = "A message to the server!\0";
fcntl(sockfd, F_SETFL, O_NONBLOCK);
long count = 0;
while(1)
{
count++;
printf("\nTX(%lu)...\n", count);
usleep(10000);
//bzero(buf, BUFSIZE);
//printf("\nPlease enter msg: ");
//fgets(buf, BUFSIZE, stdin);
/* send the message to the server */
serverlen = sizeof(serveraddr);
printf("A\n");
n = sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr *)&serveraddr, serverlen);
printf("B\n");
//if (n < 0)
// error("ERROR in sendto");
/* print the server's reply */
printf("C\n");
memset(buf, 0, sizeof(buf));
printf("D\n");
n = recvfrom(sockfd, buf, BUFSIZE, 0, (struct sockaddr *)&serveraddr, (socklen_t *)&serverlen);
printf("E\n");
//if (n < 0)
// printf("ERROR in recvfrom: %d", n);
printf("Echo from server: %s", buf);
}
return 0;
}

View File

@@ -1,83 +0,0 @@
// UDP Server test program
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define MAXBUF 1024*1024
void echo( int sd ) {
char bufin[MAXBUF];
struct sockaddr_in remote;
int n;
socklen_t len = sizeof(remote);
long count = 0;
while (1) {
usleep(50);
count++;
// read a datagram from the socket (put result in bufin)
n=recvfrom(sd,bufin,MAXBUF,0,(struct sockaddr *)&remote,&len);
// print out the address of the sender
printf("DGRAM from %s:%d\n", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port));
if (n<0) {
perror("Error receiving data");
} else {
printf("GOT %d BYTES (count = %ld)\n", n, count);
// Got something, just send it back
// sendto(sd,bufin,n,0,(struct sockaddr *)&remote,len);
}
}
}
int main(int argc, char *argv[]) {
if(argc < 2) {
printf("usage: udp_server <port>\n");
return 0;
}
int ld, port = atoi(argv[1]);
socklen_t len;
struct sockaddr_in skaddr;
struct sockaddr_in skaddr2;
// Create socket
if ((ld = socket( PF_INET, SOCK_DGRAM, 0)) < 0) {
printf("error creating socket\n");
return 0;
}
// Create address
skaddr.sin_family = AF_INET;
skaddr.sin_addr.s_addr = htonl(INADDR_ANY);
skaddr.sin_port = htons(port);
// Bind to address
if (bind(ld, (struct sockaddr *) &skaddr, sizeof(skaddr))<0) {
printf("error binding\n");
return 0;
}
// find out what port we were assigned
len = sizeof( skaddr2 );
if (getsockname(ld, (struct sockaddr *) &skaddr2, &len)<0) {
printf("error getsockname\n");
return 0;
}
// Display address:port to verify it was sent over RPC correctly
port = ntohs(skaddr2.sin_port);
int ip = skaddr2.sin_addr.s_addr;
unsigned char d[4];
d[0] = ip & 0xFF;
d[1] = (ip >> 8) & 0xFF;
d[2] = (ip >> 16) & 0xFF;
d[3] = (ip >> 24) & 0xFF;
printf("bound to address: %d.%d.%d.%d : %d\n", d[0],d[1],d[2],d[3], port);
// RX
echo(ld);
return(0);
}

94
tests/unit/README.md Normal file
View File

@@ -0,0 +1,94 @@
Docker + ZeroTier SDK
====
Welcome!
Imagine a flat, encrypted, no-configuration LAN for all of your Docker containers.
This short tutorial will show you how to enable ZeroTier functionality for your Docker software container with little to no configuration. In this example we aim to build a Docker container with ZeroTiers Network Container service bundled right in so that its effortless to hook any number of your services in the container up to your virtual network. Alternatively, you can check out a docker project directory [here](docker_demo).
**Step 1: Build ZeroTier shared library**
`make linux_shared_lib`
- For debug output from the SDK, use `make linux_shared_lib SDK_DEBUG=1`, or `make linux_shared_lib ZT_DEBUG=1` to see debug output from the ZeroTier service.
**Step 2: Build your Docker image**
`docker build --tag=redis_test .`
The example dockerfile below incorperates a few important elements:
1) The ZeroTier service binaries
2) Whatever ZeroTier identity keys you plan on using (if you don't already have keys you wish to use, fret not! A new identity will be generated automatically).
3) The service we've chosen to use. In this case, redis.
```
FROM fedora:23
# Install apps
RUN yum -y update
RUN yum -y install redis-3.0.4-1.fc23.x86_64
RUN yum clean all
# Add ZT files
RUN mkdir -p /var/lib/zerotier-one/networks.d
ADD sdk_identity.public /var/lib/zerotier-one/identity.public
ADD sdk_identity.secret /var/lib/zerotier-one/identity.secret
ADD *.conf /var/lib/zerotier-one/networks.d/
ADD *.conf /
ADD *.name /
EXPOSE 9993/udp 6379/udp
# Install LWIP library used by service
ADD liblwip.so /var/lib/zerotier-one/liblwip.so
# Install syscall intercept library
ADD libztintercept.so /
RUN cp libztintercept.so lib/libztintercept.so
RUN ln -sf /lib/libztintercept.so /lib/libztintercept
ADD zerotier-cli /
Add zerotier-sdk-service /
# Install test scripts
ADD sdk_entrypoint.sh /sdk_entrypoint.sh
RUN chmod -v +x /sdk_entrypoint.sh
# Start ZeroTier-One
CMD ["./sdk_entrypoint.sh"]
```
**Step 3: Start container**
`docker run -d -it redis_test /bin/bash`
**Step 4: From container, set up environment variables**
Set our application pre-load with `export LD_PRELOAD=./libztintercept.so`. This dynamically loads our intercept library into your application which allows us to re-direct its network calls to our virtual network.
Tell the ZeroTier Network Containers service which network to connect to with `export ZT_NC_NETWORK=/var/lib/zerotier-one/nc_XXXXXXXXXXXXXXXX`.
**Step 5: Run your new ZeroTier-enabled service**
At this point, simply run your application as you normally would. It will be automatically intercepted and linked to the ZeroTier service (and hence your virtual networks!)
`/usr/bin/redis-server --port 6379`
***
**Additional info**
If you'd like to know the IP address your service can be reached at on this particular virtual network, use the following:
`zerotier-cli -D/var/lib/zerotier-one/nc_XXXXXXXXXXXXXXXX listnetworks`
## Installing in a Docker container (or any other container engine)
If it's not immediately obvious, installation into a Docker container is easy. Just install `zerotier-sdk-service`, `libztintercept.so`, and `liblwip.so` into the container at an appropriate locations. We suggest putting it all in `/var/lib/zerotier-one` since this is the default ZeroTier home and will eliminate the need to supply a path to any of ZeroTier's services or utilities. Then, in your Docker container entry point script launch the service with *-d* to run it in the background, set the appropriate environment variables as described above, and launch your container's main application.
The only bit of complexity is configuring which virtual network to join. ZeroTier's service automatically joins networks that have `.conf` files in `ZTHOME/networks.d` even if the `.conf` file is empty. So one way of doing this very easily is to add the following commands to your Dockerfile or container entry point script:
mkdir -p /var/lib/zerotier-one/networks.d
touch /var/lib/zerotier-one/networks.d/8056c2e21c000001.conf
Replace 8056c2e21c000001 with the network ID of the network you want your container to automatically join. It's also a good idea in your container's entry point script to add a small loop to wait until the container's instance of ZeroTier generates an identity and comes online. This could be something like:
/var/lib/zerotier-one/zerotier-sdk-service -d
while [ ! -f /var/lib/zerotier-one/identity.secret ]; do
sleep 0.1
done
# zerotier-sdk-service is now running and has generated an identity
(Be sure you don't bundle the identity into the container, otherwise every container will try to be the same device and they will "fight" over the device's address.)
Now each new instance of your container will automatically join the specified network on startup. Authorizing the container on a private network still requires a manual authorization step either via the ZeroTier Central web UI or the API. We're working on some ideas to automate this via bearer token auth or similar since doing this manually or with scripts for large deployments is tedious.

5
tests/unit/_remove_all.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Delete all containers
docker rm $(docker ps -a -q)
# Delete all images
docker rmi $(docker images -q)

View File

View File

@@ -0,0 +1,7 @@
Simple Docker Demo
====
- Type `make docker_demo` to build an image with the SDK pre-installed and a test image to serve as a monitor
- From this local directory, start both containers with `./start.sh`
Results of the test can be found in `_results`

View File

View File

@@ -0,0 +1,3 @@
local msg = "welcome to the machine!"
redis.call("SET", "msg", msg)
return redis.call("GET", "msg")

View File

@@ -0,0 +1,28 @@
# ZT SDK Test Monitor
FROM fedora:23
MAINTAINER https://www.zerotier.com/
RUN yum -y install redis-3.0.4-1.fc23.x86_64
EXPOSE 9993/udp
# Add ZT files
RUN mkdir -p /var/lib/zerotier-one/networks.d
ADD monitor_identity.public /var/lib/zerotier-one/identity.public
ADD monitor_identity.secret /var/lib/zerotier-one/identity.secret
ADD *.conf /var/lib/zerotier-one/networks.d/
ADD *.conf /
ADD *.name /
# Install LWIP library used by service
ADD liblwip.so /var/lib/zerotier-one/liblwip.so
ADD hello.lua /
ADD zerotier-one /
ADD zerotier-cli /
# Start ZeroTier-One
ADD monitor_entrypoint.sh /monitor_entrypoint.sh
RUN chmod -v +x /monitor_entrypoint.sh
CMD ["./monitor_entrypoint.sh"]

View File

@@ -0,0 +1,54 @@
#!/bin/bash
export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
# --- Test Parameters ---
test_namefile=$(ls *.name)
test_name="${test_namefile%.*}" # test network id
nwconf=$(ls *.conf) # blank test network config file
nwid="${nwconf%.*}" # test network id
sdk_wait_time=60 # wait for test container to come online
app_timeout_time=15 # app-specific timeout
file_path=/opt/results/ # test result output file path (fs shared between host and containers)
file_base="$test_name".txt # test result output file
fail=FAIL. # appended to result file in event of failure
ok=OK. # appended to result file in event of success
tmp_ext=.tmp # temporary filetype used for sharing test data between containers
address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
# --- Network Config ---
echo '*** ZeroTier SDK Test Monitor'
chown -R daemon /var/lib/zerotier-one
chgrp -R daemon /var/lib/zerotier-one
su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
virtip4=""
while [ -z "$virtip4" ]; do
sleep 0.2
virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
done
echo '*** Starting Test...'
echo '*** Up and running at' $virtip4 ' on network: ' $nwid
echo '*** Sleeping for (' "$sdk_wait_time" 's ) while we wait for the container to come online...'
sleep "$sdk_wait_time"s
ncvirtip=$(<$address_file)
# --- Test section ---
echo '*** Running lua script against redis host at' $ncvirtip
redis-cli -h $ncvirtip EVAL "$(cat hello.lua)" 0 > redis_response.txt
response_string=$(<redis_response.txt)
if [[ $response_string == *"welcome to the machine!"* ]]
then
echo 'REDIS RESPONSE OK'
touch "$file_path$ok$test_name.txt"
printf 'Test: redis-server responded!\n' >> "$file_path$ok$test_name.txt"
else
echo 'REDIS RESPONSE FAIL'
touch "$file_path$fail$test_name.txt"
printf 'Test: redis server did NOT respond!\n' >> "$file_path$fail$test_name.txt"
fi

View File

@@ -0,0 +1,36 @@
# ZT SDK Test
FROM fedora:23
MAINTAINER https://www.zerotier.com/
# Install apps
RUN yum -y update
RUN yum -y install redis-3.0.4-1.fc23.x86_64
RUN yum clean all
# Add ZT files
RUN mkdir -p /var/lib/zerotier-one/networks.d
ADD sdk_identity.public /var/lib/zerotier-one/identity.public
ADD sdk_identity.secret /var/lib/zerotier-one/identity.secret
ADD *.conf /var/lib/zerotier-one/networks.d/
ADD *.conf /
ADD *.name /
EXPOSE 9993/udp 6379/udp
# Install LWIP library used by service
ADD liblwip.so /var/lib/zerotier-one/liblwip.so
# Install syscall intercept library
ADD libztintercept.so /
RUN cp libztintercept.so lib/libztintercept.so
RUN ln -sf /lib/libztintercept.so /lib/libzerotierintercept
ADD zerotier-cli /
Add zerotier-sdk-service /
# Install test scripts
ADD sdk_entrypoint.sh /sdk_entrypoint.sh
RUN chmod -v +x /sdk_entrypoint.sh
# Start ZeroTier-One
CMD ["./sdk_entrypoint.sh"]

View File

@@ -0,0 +1,36 @@
#!/bin/bash
export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/
# --- Test Parameters ---
test_namefile=$(ls *.name)
test_name="${test_namefile%.*}" # test network id
nwconf=$(ls *.conf) # blank test network config file
nwid="${nwconf%.*}" # test network id
file_path=/opt/results/ # test result output file path (fs shared between host and containers)
file_base="$test_name".txt # test result output file
tmp_ext=.tmp # temporary filetype used for sharing test data between containers
address_file="$file_path$test_name"_addr"$tmp_ext" # file shared between host and containers for sharing address (optional)
# --- Network Config ---
echo '*** ZeroTier SDK Test: ' "$test_name"
chown -R daemon /var/lib/zerotier-one
chgrp -R daemon /var/lib/zerotier-one
su daemon -s /bin/bash -c '/zerotier-sdk-service -d -U -p9993 >>/tmp/zerotier-sdk-service.out 2>&1'
virtip4=""
while [ -z "$virtip4" ]; do
sleep 0.2
virtip4=`/zerotier-cli listnetworks | grep -F $nwid | cut -d ' ' -f 9 | sed 's/,/\n/g' | grep -F '.' | cut -d / -f 1`
dev=`/zerotier-cli listnetworks | grep -F "" | cut -d ' ' -f 8 | cut -d "_" -f 2 | sed "s/^<dev>//" | tr '\n' '\0'`
done
echo '*** Up and running at' $virtip4 ' on network: ' $nwid
echo '*** Writing address to ' "$address_file"
echo $virtip4 > "$address_file"
# --- Test section ---
echo '*** Starting application...'
sleep 0.5
export ZT_NC_NETWORK=/var/lib/zerotier-one/nc_"$dev"
export LD_PRELOAD=./libztintercept.so
/usr/bin/redis-server --port 6379

11
tests/unit/docker/start.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
# Runs test image and monitor image as daemons
test_name="docker_demo"
echo 'Starting containers for: ' "$test_name"
touch "$test_name".name
test_container=$(docker run -d -it -v $PWD/_results:/opt/results --privileged --device=/dev/net/tun "$test_name":latest)
monitor_container=$(docker run -d -it -v $PWD/_results:/opt/results --privileged --device=/dev/net/tun "$test_name"_monitor:latest)
sleep 90
./check.sh _results/OK.docker_demo.txt

3
tests/unit/docker/stop.sh Executable file
View File

@@ -0,0 +1,3 @@
docker stop $(docker ps -a -q)
docker rm $test_container
docker rm $monitor_container