added compilation sanity checks, unit test script, updated docs
This commit is contained in:
4
Jenkinsfile
vendored
4
Jenkinsfile
vendored
@@ -10,6 +10,10 @@ parallel 'centos7': {
|
||||
stage('Build centos7') {
|
||||
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) {
|
||||
currentBuild.result = "FAILURE"
|
||||
|
||||
2
check.sh
2
check.sh
@@ -4,6 +4,8 @@ FILE="$1"
|
||||
if ([ -f "$FILE" ] && [ -s "$FILE" ]) || ([ -d "$FILE" ] && [ "$(ls -A "$FILE")" ]);
|
||||
then
|
||||
echo "[OK ] $FILE"
|
||||
exit 0
|
||||
else
|
||||
echo "[FAIL] $FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -14,10 +14,15 @@ For more support on these integrations, or if you'd like help creating a new int
|
||||
***
|
||||
## 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_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_BUNDLED=1` - Builds the SDK as a single bundled target including a the RPC mechanism, the lwIP library, and the ZeroTier service.
|
||||
- `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.
|
||||
|
||||
***
|
||||
## Current Integrations
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ZeroTier SDK (beta)
|
||||
ZeroTier SDK
|
||||
======
|
||||
|
||||
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?
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -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
|
||||
- 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
|
||||
- the tap service receives the `RPC_SOCKET` message and requests a new `tcp_pcb` 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.
|
||||
- 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 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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
# Target output filenames
|
||||
SHARED_LIB_NAME = libztlinux.so
|
||||
INTERCEPT_NAME = libztintercept.so
|
||||
SDK_INTERCEPT_NAME = libztintercept.so
|
||||
SDK_SERVICE_NAME = zerotier-sdk-service
|
||||
ONE_SERVICE_NAME = zerotier-one
|
||||
ONE_CLI_NAME = zerotier-cli
|
||||
@@ -17,7 +17,7 @@ ONE_ID_TOOL_NAME = zerotier-idtool
|
||||
LWIP_LIB_NAME = liblwip.so
|
||||
#
|
||||
SHARED_LIB = $(BUILD)/$(SHARED_LIB_NAME)
|
||||
INTERCEPT = $(BUILD)/$(INTERCEPT_NAME)
|
||||
SDK_INTERCEPT = $(BUILD)/$(SDK_INTERCEPT_NAME)
|
||||
SDK_SERVICE = $(BUILD)/$(SDK_SERVICE_NAME)
|
||||
ONE_SERVICE = $(BUILD)/$(ONE_SERVICE_NAME)
|
||||
ONE_CLI = $(BUILD)/$(ONE_CLI_NAME)
|
||||
@@ -35,9 +35,9 @@ SDK_SERVICE_CPP_FILES:=src/tap.cpp \
|
||||
$(ZT1)/one.cpp
|
||||
|
||||
SDK_SERVICE_C_FILES = src/rpc.c
|
||||
SDK_INTERCEPT_C_FILES:=sockets.c \
|
||||
intercept.c \
|
||||
rpc.c
|
||||
SDK_INTERCEPT_C_FILES:=src/sockets.c \
|
||||
src/intercept.c \
|
||||
src/rpc.c
|
||||
|
||||
|
||||
# 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
|
||||
linux_intercept:
|
||||
# Use gcc not clang to build standalone intercept library since gcc is typically used for libc and we want to ensure maximal ABI compatibility
|
||||
cd src ; gcc $(DEFS) $(INCLUDES) -g -O2 -Wall -std=c99 -fPIC -DVERBOSE -D_GNU_SOURCE -DSDK_INTERCEPT -nostdlib -nostdlib -shared -o ../$(INTERCEPT) $(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
|
||||
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
|
||||
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 ---------
|
||||
# Build the docker demo images
|
||||
docker_demo: one linux_shared_lib
|
||||
|
||||
unit_test: one linux_service_and_intercept
|
||||
mkdir -p $(BUILD)
|
||||
cp $(INTERCEPT) $(INT)/docker/docker_demo/$(INTERCEPT_NAME)
|
||||
cp $(SERVICE) $(INT)/docker/docker_demo/$(SERVICE_NAME)
|
||||
cp $(SDK_INTERCEPT) tests/unit/docker/$(SDK_INTERCEPT_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 $(ONE_CLI) $(INT)/docker/docker_demo/$(ONE_CLI_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 .
|
||||
|
||||
# Builds all docker test images
|
||||
docker_images: one linux_shared_lib
|
||||
docker_images: one linux_service_and_intercept
|
||||
./tests/docker/build_images.sh
|
||||
|
||||
# Runs docker container tests
|
||||
@@ -262,18 +280,18 @@ docker_check_test:
|
||||
|
||||
# Check for the presence of built frameworks/bundles/libaries
|
||||
check:
|
||||
./check.sh $(LWIP_LIB)
|
||||
./check.sh $(INTERCEPT)
|
||||
./check.sh $(ONE_SERVICE)
|
||||
./check.sh $(SDK_SERVICE)
|
||||
./check.sh $(SHARED_LIB)
|
||||
./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-v7a/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/x86/libZeroTierOneJNI.so
|
||||
./check.sh $(BUILD)/android_jni_lib/x86_64/libZeroTierOneJNI.so
|
||||
-./check.sh $(LWIP_LIB)
|
||||
-./check.sh $(SDK_INTERCEPT)
|
||||
-./check.sh $(ONE_SERVICE)
|
||||
-./check.sh $(SDK_SERVICE)
|
||||
-./check.sh $(SHARED_LIB)
|
||||
-./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-v7a/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/x86/libZeroTierOneJNI.so
|
||||
-./check.sh $(BUILD)/android_jni_lib/x86_64/libZeroTierOneJNI.so
|
||||
|
||||
# Tests
|
||||
OSTYPE=$(shell uname -s | tr '[A-Z]' '[a-z]')
|
||||
@@ -313,7 +331,7 @@ clean_basic:
|
||||
-rm -rf $(BUILD)/*
|
||||
-rm -rf $(INT)/Unity3D/Assets/Plugins/*
|
||||
-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
|
||||
|
||||
clean_thorough: clean_basic
|
||||
|
||||
17
src/sdk.h
17
src/sdk.h
@@ -28,12 +28,27 @@
|
||||
#ifndef _ZT_SDK_H
|
||||
#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 <stdbool.h>
|
||||
#include "signatures.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
// For defining the Android direct-call API
|
||||
#include <jni.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ rm _results/*.txt
|
||||
|
||||
# How long we shall wait for each test to conclude
|
||||
export sdk_test_wait_time=60s
|
||||
|
||||
export image_build_script=_build_single_image.sh
|
||||
|
||||
# Iterate over all depth=2 (relatively-speaking) directories and perform each test
|
||||
|
||||
16
tests/docker/darkhttpd/darkhttpd-1.11.x86_64/_two_party_test.sh
Executable file
16
tests/docker/darkhttpd/darkhttpd-1.11.x86_64/_two_party_test.sh
Executable 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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
94
tests/unit/README.md
Normal 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 ZeroTier’s Network Container service bundled right in so that it’s 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
5
tests/unit/_remove_all.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Delete all containers
|
||||
docker rm $(docker ps -a -q)
|
||||
# Delete all images
|
||||
docker rmi $(docker images -q)
|
||||
0
tests/unit/docker/565799d8f658068e.conf
Normal file
0
tests/unit/docker/565799d8f658068e.conf
Normal file
7
tests/unit/docker/README.md
Normal file
7
tests/unit/docker/README.md
Normal 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`
|
||||
0
tests/unit/docker/docker_demo.name
Normal file
0
tests/unit/docker/docker_demo.name
Normal file
3
tests/unit/docker/hello.lua
Normal file
3
tests/unit/docker/hello.lua
Normal file
@@ -0,0 +1,3 @@
|
||||
local msg = "welcome to the machine!"
|
||||
redis.call("SET", "msg", msg)
|
||||
return redis.call("GET", "msg")
|
||||
28
tests/unit/docker/monitor_dockerfile
Normal file
28
tests/unit/docker/monitor_dockerfile
Normal 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"]
|
||||
54
tests/unit/docker/monitor_entrypoint.sh
Normal file
54
tests/unit/docker/monitor_entrypoint.sh
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
36
tests/unit/docker/sdk_dockerfile
Normal file
36
tests/unit/docker/sdk_dockerfile
Normal 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"]
|
||||
36
tests/unit/docker/sdk_entrypoint.sh
Normal file
36
tests/unit/docker/sdk_entrypoint.sh
Normal 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
11
tests/unit/docker/start.sh
Executable 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
3
tests/unit/docker/stop.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
docker stop $(docker ps -a -q)
|
||||
docker rm $test_container
|
||||
docker rm $monitor_container
|
||||
Reference in New Issue
Block a user