From 8992bb0244ac4112ab4685f362f6155538df2519 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 21 Sep 2020 22:12:28 +0200 Subject: [PATCH] Added picotcp-server example --- picotcp/Makefile | 88 ++++++++++++++ picotcp/README.md | 39 ++++++ picotcp/picotcp-server.c | 254 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 381 insertions(+) create mode 100644 picotcp/Makefile create mode 100644 picotcp/README.md create mode 100644 picotcp/picotcp-server.c diff --git a/picotcp/Makefile b/picotcp/Makefile new file mode 100644 index 00000000..dac5b7d4 --- /dev/null +++ b/picotcp/Makefile @@ -0,0 +1,88 @@ +# TLS Examples Makefile +CC = gcc +LIB_PATH = /usr/ +PICOTCP_PATH = ../../picotcp +CFLAGS = -Wall -I$(LIB_PATH)/include -I$(PICOTCP_PATH)/build/include +LIBS = -lm -pthread + +# option variables +DYN_LIB = -lwolfssl +STATIC_LIB = $(PICOTCP_PATH)/build/lib/libpicotcp.a +DEBUG_FLAGS = -g -DDEBUG +DEBUG_INC_PATHS = -MD +SILENCE_FLAGS = -Wno-address-of-packed-member -Wno-discarded-qualifiers +OPTIMIZE = -Os + +# Options +CFLAGS+=$(DEBUG_FLAGS) +CFLAGS+=$(OPTIMIZE) +CFLAGS+=$(SILENCE_FLAGS) +LIBS+=$(STATIC_LIB) $(DYN_LIB) + +# build targets +SRC=$(wildcard *.c) +TARGETS=$(patsubst %.c, %, $(SRC)) +LINUX_SPECIFIC=client-tls-perf \ + server-tls-epoll-perf \ + server-tls-epoll-threaded + +# OS / CPU Detection +OS_DET=UNKNOWN +CPU_DET=UNKNOWN +ifeq ($(OS),Windows_NT) + OS_DET=WIN32 + ifeq ($(PROCESSOR_ARCHITEW6432),AMD64) + CPU_DET=AMD64 + else + ifeq ($(PROCESSOR_ARCHITECTURE),AMD64) + CPU_DET=AMD64 + endif + ifeq ($(PROCESSOR_ARCHITECTURE),x86) + CPU_DET=IA32 + endif + endif +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Linux) + OS_DET=LINUX + endif + ifeq ($(UNAME_S),Darwin) + OS_DET=OSX + endif + UNAME_P := $(shell uname -p) + ifeq ($(UNAME_P),x86_64) + CPU_DET=AMD64 + endif + ifneq ($(filter %86,$(UNAME_P)),) + CPU_DET=IA32 + endif + ifneq ($(filter arm%,$(UNAME_P)),) + CPU_DET=ARM + endif +endif + +.PHONY: clean all + +ifneq ($(OS_DET),LINUX) +all: $(filter-out $(LINUX_SPECIFIC), $(TARGETS)) +else +all: $(TARGETS) +endif + +debug: CFLAGS+=$(DEBUG_FLAGS) +debug: all + +# add the -pthread flag to any threaded examples +%-threaded: CFLAGS+=-pthread +%-writedup: CFLAGS+=-pthread +memory-tls: CFLAGS+=-pthread + +# compile tcp examples without the LIBS variable +%-tcp: LIBS= + +# build template +%: %.c + $(CC) -o $@ $< $(CFLAGS) $(LIBS) + +clean: + rm -f $(TARGETS) diff --git a/picotcp/README.md b/picotcp/README.md new file mode 100644 index 00000000..ad06d3be --- /dev/null +++ b/picotcp/README.md @@ -0,0 +1,39 @@ +## TLS server using picoTCP via wolfSSL custom callbacks + +This TLS server runs in userspace, using picoTCP as compiled-in TCP/IP stack. + +`picotcp-server` has been tested on Linux using latest picoTCP master, and wolfSSL 4.5.0. + + +### Requirements + +- PicoTCP v.1.7 or later +- wolfSSL +- Access to `/dev/net/tun` on the host system (typically root privileges) + +### How to compile picotcp-server + +- clone or download picoTCP +- compile picoTCP with `make ARCH=shared TAP=1 WOLFSSL=1` +- modify `PICOTCP_PATH` at the top of Makefile, pointing to the picoTCP root directory +- run `make` + +### Testing picotcp-server + +Run `picotcp-server` with no arguments. If the executable was run with the right permissions (i.e. accessing `/dev/net/tun`), a `tap0` device will be created, and the picoTCP endpoint address is set to `10.0.0.1/24`. + +The TLS server listens to port 11111 by default, and can be reached using the [TLS client example](../tls/client-tls.c). + +Example: + +```(bash) +# Server side: +./picotcp-server + +# On client side: +ip addr add dev tap0 10.0.0.2 +ip link set tap0 up + +./client-tls 10.0.0.1 + +``` diff --git a/picotcp/picotcp-server.c b/picotcp/picotcp-server.c new file mode 100644 index 00000000..e775099c --- /dev/null +++ b/picotcp/picotcp-server.c @@ -0,0 +1,254 @@ +/* picotcp-server.c + * + * Copyright (C) 2020 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL 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 2 of the License, or + * (at your option) any later version. + * + * wolfSSL 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* the usual suspects */ +#include +#include +#include + +/* wolfSSL */ +#include +#include + +/* PicoTCP */ +#include +#include +#include + +/* Pthreads */ +#include + +/* Semaphore */ +#include + +#define DEFAULT_PORT 11111 + +#define CERT_FILE "../certs/server-cert.pem" +#define KEY_FILE "../certs/server-key.pem" + +/* Global picotcp client socket object */ +static volatile struct pico_socket *conn = NULL; +sem_t picotcp_rx_data; + +/* PicoTCP send/receive callbacks */ +int pico_send(struct WOLFSSL *ssl, char *buf, int len, void *ctx) +{ + struct pico_socket *s = (struct pico_socket *)ctx; + int r; + r = pico_socket_write(s, buf, len); + if (r > 0) + return r; + else + return WOLFSSL_CBIO_ERR_WANT_WRITE; +} + +int pico_recv(struct WOLFSSL *ssl, char *buf, int len, void *ctx) +{ + struct pico_socket *s = (struct pico_socket *)ctx; + int r; + r = pico_socket_read(s, buf, len); + if (r > 0) + return r; + else + return WOLFSSL_CBIO_ERR_WANT_READ; +} + +/* PicoTCP: socket callback */ +static void socket_cb(uint16_t ev, struct pico_socket *s) +{ + struct pico_ip4 client_addr; + uint16_t client_port; + if (ev & PICO_SOCK_EV_CONN) { + conn = pico_socket_accept(s, &client_addr, &client_port); + return; + } + if (ev & PICO_SOCK_EV_RD) { + sem_post(&picotcp_rx_data); + } +} + +/* PicoTCP: tick thread */ +static volatile int picotick_terminated = 0; +static void *picotick_thread (void *arg) { + printf("PicoTCP thread: started.\n"); + while(1) { + pico_stack_tick(); + if ((usleep(2000) > 0) || picotick_terminated) { + fprintf(stderr, "picotick_thread: Goodbye!\n"); + pthread_exit(NULL); + } + } +} + + +int main() +{ + char buff[256]; + size_t len; + int shutdown = 0; + int ret; + const char* reply = "I hear ya fa shizzle!\n"; + const char* tap_ipaddr = "10.0.0.1"; + const char* tap_netmask = "255.255.255.0"; + struct pico_device *dev; + struct pico_ip4 dev_tap_ipaddr, dev_tap_netmask, in_addr_any, client_addr; + uint16_t listen_port = short_be(DEFAULT_PORT), client_port; + struct pico_socket *pico_s; + pthread_t pico_tick_thread; + WOLFSSL_CTX* ctx; + WOLFSSL* ssl; + + /* Initialize picoTCP */ + pico_stack_init(); + + dev = pico_tap_create("tap0"); + if (!dev) { + fprintf(stderr, "ERROR: Failed to create picoTCP tap device: %s\n", strerror(errno)); + return -1; + } + + /* Convert IP address + netmask from text to ip4 */ + pico_string_to_ipv4(tap_ipaddr, &dev_tap_ipaddr.addr); + pico_string_to_ipv4(tap_netmask, &dev_tap_netmask.addr); + memset(&in_addr_any, 0, sizeof(struct pico_ip4)); + + /* Add ipv4 link to tap interface */ + pico_ipv4_link_add(dev, dev_tap_ipaddr, dev_tap_netmask); + + /* Initialize semaphore */ + sem_init(&picotcp_rx_data, 1, 0); + + /* Start picoTCP ticking thread */ + pthread_create(&pico_tick_thread, NULL, picotick_thread, NULL); + + /* Initialize wolfSSL */ + wolfSSL_Init(); + + /* Create a socket that uses an internet IPv4 address, + * Set the socket to be stream based (TCP), + * associate to picoTCP socket callback */ + pico_s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &socket_cb); + if (pico_s == NULL) { + fprintf(stderr, "ERROR: failed to create the socket\n"); + return -1; + } + + /* Create and initialize WOLFSSL_CTX */ + if ((ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())) == NULL) { + fprintf(stderr, "ERROR: failed to create WOLFSSL_CTX\n"); + return -1; + } + + /* Load server certificates into WOLFSSL_CTX */ + if (wolfSSL_CTX_use_certificate_file(ctx, CERT_FILE, SSL_FILETYPE_PEM) + != SSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to load %s, please check the file.\n", + CERT_FILE); + return -1; + } + + /* Load server key into WOLFSSL_CTX */ + if (wolfSSL_CTX_use_PrivateKey_file(ctx, KEY_FILE, SSL_FILETYPE_PEM) + != SSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to load %s, please check the file.\n", + KEY_FILE); + return -1; + } + + /* Associate picoTCP callbacks to wolfSSL context */ + wolfSSL_CTX_SetIORecv(ctx, pico_recv); + wolfSSL_CTX_SetIOSend(ctx, pico_send); + + /* Bind the server socket to our port */ + if (pico_socket_bind(pico_s, &in_addr_any, &listen_port) != 0) { + fprintf(stderr, "ERROR: failed to bind\n"); + return -1; + } + + /* Listen for a new connection, allow 5 pending connections */ + if (pico_socket_listen(pico_s, 5) != 0) { + fprintf(stderr, "ERROR: failed to listen\n"); + return -1; + } + + /* Continue to accept clients until shutdown is issued */ + while (!shutdown) { + conn = pico_socket_accept(pico_s, &client_addr, &client_port); + printf("Waiting for a connection...\n"); + while(!conn) { + usleep(10000); + } + /* Create a WOLFSSL object */ + if ((ssl = wolfSSL_new(ctx)) == NULL) { + fprintf(stderr, "ERROR: failed to create WOLFSSL object\n"); + return -1; + } + /* Attach wolfSSL to the socket */ + + /* Establish TLS connection */ + do { + wolfSSL_SetIOReadCtx(ssl, conn); + wolfSSL_SetIOWriteCtx(ssl, conn); + ret = wolfSSL_accept(ssl); + sem_wait(&picotcp_rx_data); + } while ((ret != SSL_SUCCESS) && (wolfSSL_get_error(ssl, ret) == SSL_ERROR_WANT_READ)); + + printf("Client connected successfully\n"); + /* Read the client data into our buff array */ + memset(buff, 0, sizeof(buff)); + sem_wait(&picotcp_rx_data); + if (wolfSSL_read(ssl, buff, sizeof(buff)-1) == -1) { + fprintf(stderr, "ERROR: failed to read\n"); + return -1; + } + /* Print to stdout any data the client sends */ + printf("Client: %s\n", buff); + + /* Check for server shutdown command */ + if (strncmp(buff, "shutdown", 8) == 0) { + printf("Shutdown command issued!\n"); + shutdown = 1; + } + /* Write our reply into buff */ + memset(buff, 0, sizeof(buff)); + memcpy(buff, reply, strlen(reply)); + len = strnlen(buff, sizeof(buff)); + + /* Reply back to the client */ + if (wolfSSL_write(ssl, buff, len) != len) { + fprintf(stderr, "ERROR: failed to write\n"); + return -1; + } + /* Cleanup after this connection */ + wolfSSL_free(ssl); /* Free the wolfSSL object */ + pico_socket_close(conn); /* Close the connection to the client */ + conn = NULL; + } + printf("Shutdown complete\n"); + + /* Cleanup and return */ + wolfSSL_CTX_free(ctx); /* Free the wolfSSL context object */ + wolfSSL_Cleanup(); /* Cleanup the wolfSSL environment */ + pico_socket_close(pico_s); /* Close the socket listening for clients */ + picotick_terminated = 1; /* Send termination to picotick thread */ + pthread_join(pico_tick_thread, NULL); /* Join tick thread */ + return 0; /* Return reporting a success */ +}