diff --git a/tls/Makefile b/tls/Makefile index ec749593a..0cbce682c 100644 --- a/tls/Makefile +++ b/tls/Makefile @@ -23,6 +23,7 @@ LIBS+=$(DYN_LIB) SRC=$(wildcard *.c) TARGETS=$(patsubst %.c, %, $(SRC)) LINUX_SPECIFIC=client-tls-perf \ + server-tls-poll-perf \ server-tls-epoll-perf \ server-tls-epoll-threaded diff --git a/tls/server-tls-poll-perf.c b/tls/server-tls-poll-perf.c new file mode 100644 index 000000000..3cd702372 --- /dev/null +++ b/tls/server-tls-poll-perf.c @@ -0,0 +1,1089 @@ +/* server-tls-poll-perf.c + * + * Copyright (C) 2006-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 + *============================================================================= + * + * This is an example of a TCP Server that uses non-blocking input and output to + * handle a large number of connections. Reports performance figures. +*/ + +#include + +#include +#include + +#include + + +/* Default port to listen on. */ +#define DEFAULT_PORT 11111 +/* The number of concurrent connections to support. */ +#define SSL_NUM_CONN 15 +/* The number of bytes to read from client. */ +#define NUM_READ_BYTES 16384 +/* The number of bytes to write to client. */ +#define NUM_WRITE_BYTES 16384 +/* The maximum number of bytes to send in a run. */ +#define MAX_BYTES -1 +/* The maximum number of connections to perform in this run. */ +#define MAX_CONNECTIONS 100 +/* The maximum length of the queue of pending connections. */ +#define NUM_CLIENTS 100 + +/* The command line options. */ +#define OPTIONS "?p:v:al:c:k:A:n:N:R:W:B:" + +/* The default server certificate. */ +#define SVR_CERT "../certs/server-cert.pem" +/* The default server private key. */ +#define SVR_KEY "../certs/server-key.pem" +/* The default certificate/CA file for the client. */ +#define CLI_CERT "../certs/client-cert.pem" + +/* The file descriptor of an inactive event slot. */ +#define DISABLED_EVENT_SLOT_FD -1 +/* The number of maximum connections, including the listener. */ +#define MAX_EVENT_SLOTS_WITH_LISTENER(maxConns) (maxConns + 1) +/* The static event slot identifier of the listener, + * which is the last slot in the POLL events list. */ +#define LISTENER_EVENT_SLOT_ID(maxConns) maxConns + +/* The states of the SSL connection. */ +typedef enum SSLState { ACCEPT, READ, WRITE, CLOSED } SSLState; + +/* Type for the SSL connection data. */ +typedef struct SSLConn SSLConn; + +/* Data for each active connection. */ +struct SSLConn { + /* The socket listening on, reading from and writing to. */ + int sockfd; + /* The wolfSSL object to perform TLS communications. */ + WOLFSSL* ssl; + /* The current state of the SSL/TLS connection. */ + SSLState state; + /* Previous SSL connection data object. */ + SSLConn* prev; + /* Next SSL connection data object. */ + SSLConn* next; +}; + +/* The information about SSL/TLS connections. */ +typedef struct SSLConn_CTX { + /* An array of active connections. */ + SSLConn* sslConn; + /* Free list. */ + SSLConn* freeSSLConn; + /* Maximum number of active connections. */ + int numConns; + /* Count of currently active connections. */ + int cnt; + /* Accepting new connections. */ + int accepting; + + /* Size of the client data buffer. */ + int bufferLen; + /* Number of bytes to write. */ + int replyLen; + + /* Number of connections handled. */ + int numConnections; + /* Number of resumed connections handled. */ + int numResumed; + /* Maximum number of connections to perform. */ + int maxConnections; + + /* Total number of bytes read. */ + int totalReadBytes; + /* Total number of bytes written. */ + int totalWriteBytes; + /* Maximum number of bytes to read/write. */ + int maxBytes; + + /* Total time handling accept. */ + double acceptTime; + /* Total time handling accept - resumed connections. */ + double resumeTime; + /* Total time handling reading. */ + double readTime; + /* Total time handling writing. */ + double writeTime; + /* Total time handling connections. */ + double totalTime; +} SSLConn_CTX; + + +static void SSLConn_Free(SSLConn_CTX* ctx); +static void SSLConn_Close(SSLConn_CTX* ctx, SSLConn* sslConn); +static void SSLConn_FreeSSLConn(SSLConn_CTX* ctx); + + +/* The index of the command line option. */ +int myoptind = 0; +/* The current command line option. */ +char* myoptarg = NULL; +/* The data to reply with. */ +static char reply[NUM_WRITE_BYTES]; + + +/* Get the wolfSSL server method function for the specified version. + * + * version Protocol version to use. + * returns The server method function or NULL when version not supported. + */ +static wolfSSL_method_func SSL_GetMethod(int version, int allowDowngrade) +{ + wolfSSL_method_func method = NULL; + + switch (version) { +#ifndef NO_OLD_TLS + #ifdef WOLFSSL_ALLOW_SSLV3 + case 0: + method = wolfSSLv3_server_method_ex; + break; + #endif + + #ifndef NO_TLS + #ifdef WOLFSSL_ALLOW_TLSV10 + case 1: + method = wolfTLSv1_server_method_ex; + break; + #endif + + #ifndef NO_OLD_TLS + case 2: + method = wolfTLSv1_1_server_method_ex; + break; + #endif + #endif +#endif + +#ifndef NO_TLS + case 3: + method = allowDowngrade ? wolfSSLv23_server_method_ex : wolfTLSv1_2_server_method_ex; + break; +#endif + } + + return method; +} + + +/* Write data to a client. + * + * ssl The wolfSSL object. + * reply The data to send to the client. + * replyLen The length of the data to send to the client. + * totalBytes The total number of bytes sent to clients. + * writeTime The amount of time spent writing data to client. + * returns 0 on failure, 1 on success, 2 on want read and 3 on want write. + */ +static int SSL_Write(WOLFSSL* ssl, char* reply, int replyLen, int* totalBytes, + double* writeTime) +{ + int rwret = 0; + int error; + double start; + + start = current_time(1); + rwret = wolfSSL_write(ssl, reply, replyLen); + *writeTime += current_time(0) - start; + if (rwret == 0) { + fprintf(stderr, "The client has closed the connection - write!\n"); + return 0; + } + + if (rwret > 0) + *totalBytes += rwret; + if (rwret == replyLen) + return 1; + + error = wolfSSL_get_error(ssl, 0); + if (error == SSL_ERROR_WANT_READ) + return 2; + if (error == SSL_ERROR_WANT_WRITE) + return 3; + if (error == WC_PENDING_E) + return 4; + if (error == 0) + return 1; + + /* Cannot do anything about other errors. */ + fprintf(stderr, "wolfSSL_write error = %d\n", error); + return 0; +} + +/* Reads data from a client. + * + * ssl The wolfSSL object. + * buffer The buffer to place client data into. + * len The length of the buffer. + * totalBytes The total number of bytes read from clients. + * readTime The amount of time spent reading data from client. + * returns 0 on failure, 1 on success, 2 on want read and 3 on want write. + */ +static int SSL_Read(WOLFSSL* ssl, char* buffer, int len, int* totalBytes, + double* readTime) +{ + int rwret = 0; + int error; + double start; + + start = current_time(1); + rwret = wolfSSL_read(ssl, buffer, len); + *readTime += current_time(0) - start; + if (rwret == 0) { + return 0; + } + + if (rwret > 0) + *totalBytes += rwret; + + error = wolfSSL_get_error(ssl, 0); + if (error == SSL_ERROR_WANT_READ) + return 2; + if (error == SSL_ERROR_WANT_WRITE) + return 3; + if (error == WC_PENDING_E) + return 4; + if (error == 0) + return 1; + + /* Cannot do anything about other errors. */ + fprintf(stderr, "wolfSSL_read error = %d\n", error); + return 0; +} + +/* Accept/negotiate a secure connection. + * + * ssl The wolfSSL object. + * acceptTime The amount of time spent accepting a client. + * resumeTime The amount of time spent resuming a connection with a client. + * returns 0 on failure, 1 on success, 2 on want read and 3 on want write. + */ +static int SSL_Accept(WOLFSSL* ssl, double* acceptTime, double* resumeTime) +{ + int ret; + int error; + double start; + + /* Accept the connection. */ + start = current_time(1); + ret = wolfSSL_accept(ssl); + if (!wolfSSL_session_reused(ssl)) + *acceptTime += current_time(0) - start; + else + *resumeTime += current_time(0) - start; + if (ret == 0) { + fprintf(stderr, "The client has closed the connection - accept!\n"); + return 0; + } + + if (ret == SSL_SUCCESS) + return 1; + + error = wolfSSL_get_error(ssl, 0); + if (error == SSL_ERROR_WANT_READ) + return 2; + if (error == SSL_ERROR_WANT_WRITE) + return 3; + if (error == WC_PENDING_E) + return 4; + + /* Cannot do anything about other errors. */ + fprintf(stderr, "wolfSSL_accept error = %d (%p)\n", error, ssl); + return 0; +} + +/* Create a new SSL/TLS connection data object. + * + * max The maximum number of concurrent connections. + * bufferLen The number of data bytes to read from client. + * replyLen The number of data bytes to write to client. + * maxConns The number of connections to process this run. + * -1 indicates no maximum. + * maxBytes The number of bytes to send this run. + * -1 indicates no maximum. + * returns an allocated and initialized connection data object or NULL on error. + */ +static SSLConn_CTX* SSLConn_New(int numConns, int bufferLen, int replyLen, + int maxConns, int maxBytes) +{ + SSLConn_CTX* ctx; + + ctx = (SSLConn_CTX*)malloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + memset(ctx, 0, sizeof(*ctx)); + + ctx->numConns = numConns; + ctx->bufferLen = bufferLen; + ctx->replyLen = replyLen; + ctx->maxConnections = maxConns; + ctx->maxBytes = maxBytes; + ctx->sslConn = NULL; + + + + return ctx; +} + +/* Free the SSL/TLS connection data. + * + * ctx The connection data. + */ +static void SSLConn_Free(SSLConn_CTX* ctx) +{ + if (ctx == NULL) + return; + + while (ctx->sslConn != NULL) + SSLConn_Close(ctx, ctx->sslConn); + SSLConn_FreeSSLConn(ctx); + + free(ctx); +} + +/* Close an active connection. + * + * ctx The SSL/TLS connection data. + * sslConn The SSL connection data object. + */ +static void SSLConn_Close(SSLConn_CTX* ctx, SSLConn* sslConn) +{ + if (sslConn->state == CLOSED) + return; + + /* Display cipher suite all connection will use. */ + if (ctx->numConnections == 0) { + WOLFSSL_CIPHER* cipher; + cipher = wolfSSL_get_current_cipher(sslConn->ssl); + printf("SSL cipher suite is %s\n", + wolfSSL_CIPHER_get_name(cipher)); + } + + if (wolfSSL_session_reused(sslConn->ssl)) + ctx->numResumed++; + ctx->numConnections++; + + sslConn->state = CLOSED; + + /* Take it out of the double-linked list. */ + if (ctx->sslConn == sslConn) + ctx->sslConn = sslConn->next; + if (sslConn->next != NULL) + sslConn->next->prev = sslConn->prev; + if (sslConn->prev != NULL) + sslConn->prev->next = sslConn->next; + + /* Put object at head of free list */ + sslConn->next = ctx->freeSSLConn; + sslConn->prev = NULL; + ctx->freeSSLConn = sslConn; + + ctx->cnt--; +} + +/* Free the SSL/TLS connections that are closed. + * + * ctx The connection data. + */ +static void SSLConn_FreeSSLConn(SSLConn_CTX* ctx) +{ + SSLConn* sslConn = ctx->freeSSLConn; + + ctx->freeSSLConn = NULL; + + while (sslConn != NULL) { + SSLConn* next = sslConn->next; + + wolfSSL_free(sslConn->ssl); + close(sslConn->sockfd); + free(sslConn); + + sslConn = next; + } +} + +/* Checks whether this run is done i.e. maximum number of connections or bytes + * have been server. + * + * ctx The SSL/TLS connection data. + * returns 1 if the run is done or 0 otherwise. + */ +static int SSLConn_Done(SSLConn_CTX* ctx) { + if (ctx->maxConnections > 0) + return (ctx->numConnections >= ctx->maxConnections); + return (ctx->totalWriteBytes >= ctx->maxBytes) && + (ctx->totalReadBytes >= ctx->maxBytes); +} + +/* Accepts a new connection. + * + * ctx The SSL/TLS connection data. + * sslCtx The SSL/TLS context. + * sockfd The socket file descriptor to accept on. + * sslConn The newly create SSL connection data object. + * returns EXIT_SUCCESS if a new connection was accepted or EXIT_FAILURE + * otherwise. + */ +static int SSLConn_Accept(SSLConn_CTX* ctx, WOLFSSL_CTX* sslCtx, + socklen_t sockfd, SSLConn** sslConn) +{ + struct sockaddr_in clientAddr = {0}; + socklen_t size = sizeof(clientAddr); + SSLConn* conn; + + if (ctx->cnt == ctx->numConns) { + fprintf(stderr, "ERROR: Too many connections!\n"); + return EXIT_FAILURE; + } + + conn = malloc(sizeof(*conn)); + if (conn == NULL) + return EXIT_FAILURE; + + /* Accept the client connection. */ + conn->sockfd = accept(sockfd, (struct sockaddr *)&clientAddr, &size); + if (conn->sockfd == -1) { + free(conn); + fprintf(stderr, "ERROR: failed to accept\n"); + return EXIT_FAILURE; + } + /* Set the new socket to be non-blocking. */ + fcntl(conn->sockfd, F_SETFL, O_NONBLOCK); + + /* Setup SSL/TLS connection. */ + if ((conn->ssl = wolfSSL_new(sslCtx)) == NULL) { + free(conn); + fprintf(stderr, "wolfSSL_new error.\n"); + return EXIT_FAILURE; + } + /* Set the socket to communicate over into the wolfSSL object. */ + wolfSSL_set_fd(conn->ssl, conn->sockfd); + + conn->state = ACCEPT; + conn->next = ctx->sslConn; + conn->prev = NULL; + if (ctx->sslConn != NULL) + ctx->sslConn->prev = conn; + + ctx->sslConn = conn; + ctx->cnt++; + + *sslConn = conn; + + return EXIT_SUCCESS; +} + +/* Read/write from/to client at the specified socket. + * + * ctx The SSL/TLS connection data. + * sslConn The SSL connection data object. + * returns EXIT_FAILURE on failure and EXIT_SUCCESS otherwise. + */ +static int SSLConn_ReadWrite(SSLConn_CTX* ctx, SSLConn* sslConn) +{ + int ret; + int len; + + switch (sslConn->state) { + case ACCEPT: + /* Perform TLS handshake. */ + ret = SSL_Accept(sslConn->ssl, &ctx->acceptTime, &ctx->resumeTime); + if (ret == 0) { + printf("ERROR: Accept failed\n"); + SSLConn_Close(ctx, sslConn); + return EXIT_FAILURE; + } + + if (ret == 1) { + sslConn->state = READ; + } + break; + + case READ: + { + char buffer[NUM_READ_BYTES]; + + len = ctx->bufferLen; + if (ctx->maxBytes > 0) { + len = min(len, ctx->maxBytes - ctx->totalReadBytes); + } + if (len == 0) + break; + + /* Read application data. */ + ret = SSL_Read(sslConn->ssl, buffer, len, &ctx->totalReadBytes, + &ctx->readTime); + if (ret == 0) { + SSLConn_Close(ctx, sslConn); + return EXIT_FAILURE; + } + + if (ret != 1) + break; + sslConn->state = WRITE; + } + + case WRITE: + len = ctx->replyLen; + if (ctx->maxBytes > 0) { + len = min(len, ctx->maxBytes - ctx->totalWriteBytes); + } + if (len == 0) + break; + + /* Write application data. */ + ret = SSL_Write(sslConn->ssl, reply, len, &ctx->totalWriteBytes, + &ctx->writeTime); + if (ret == 0) { + printf("ERROR: Write failed\n"); + SSLConn_Close(ctx, sslConn); + return EXIT_FAILURE; + } + + if (ret == 1) + sslConn->state = READ; + break; + + case CLOSED: + break; + } + + return EXIT_SUCCESS; +} + +/* Print the connection statistics. + * + * ctx The SSL/TLS connection data. + */ +static void SSLConn_PrintStats(SSLConn_CTX* ctx) +{ + fprintf(stderr, "wolfSSL Server Benchmark %d bytes\n" + "\tNum Conns : %9d\n" + "\tTotal : %9.3f ms\n" + "\tTotal Avg : %9.3f ms\n" + "\tt/s : %9.3f\n" + "\tAccept : %9.3f ms\n" + "\tAccept Avg : %9.3f ms\n", + ctx->replyLen, + ctx->numConnections - ctx->numResumed, + ctx->totalTime * 1000, + ctx->totalTime * 1000 / ctx->numConnections, + ctx->numConnections / ctx->totalTime, + ctx->acceptTime * 1000, + ctx->acceptTime * 1000 / (ctx->numConnections - ctx->numResumed)); + if (ctx->numResumed > 0) { + fprintf(stderr, + "\tResumed Conns : %9d\n" + "\tResume : %9.3f ms\n" + "\tResume Avg : %9.3f ms\n", + ctx->numResumed, + ctx->resumeTime * 1000, + ctx->resumeTime * 1000 / ctx->numResumed); + } + fprintf(stderr, + "\tTotal Read bytes : %9d bytes\n" + "\tTotal Write bytes : %9d bytes\n" + "\tRead : %9.3f ms (%9.3f MBps)\n" + "\tWrite : %9.3f ms (%9.3f MBps)\n", + ctx->totalReadBytes, + ctx->totalWriteBytes, + ctx->readTime * 1000, + ctx->totalReadBytes / ctx->readTime / 1024 / 1024, + ctx->writeTime * 1000, + ctx->totalWriteBytes / ctx->writeTime / 1024 / 1024 ); +} + + +/* Initialize the wolfSSL library and create a wolfSSL context. + * + * version The protocol version. + * cert The server's certificate. + * key The server's private key matching the certificate. + * verifyCert The certificate for client authentication. + * cipherList The list of negotiable ciphers. + * wolfsslCtx The new wolfSSL context object. + * returns EXIT_SUCCESS when a wolfSSL context object is created and + * EXIT_FAILURE otherwise. + */ +static int WolfSSLCtx_Init(int version, int allowDowngrade, char* cert, + char* key, char* verifyCert, char* cipherList, WOLFSSL_CTX** wolfsslCtx) +{ + WOLFSSL_CTX* ctx; + wolfSSL_method_func method = NULL; + + method = SSL_GetMethod(version, allowDowngrade); + if (method == NULL) + return(EXIT_FAILURE); + + /* Create and initialize WOLFSSL_CTX structure */ + if ((ctx = wolfSSL_CTX_new(method(NULL))) == NULL) { + fprintf(stderr, "wolfSSL_CTX_new error.\n"); + return(EXIT_FAILURE); + } + + /* Load server certificate into WOLFSSL_CTX */ + if (wolfSSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM) + != SSL_SUCCESS) { + fprintf(stderr, "Error loading %s, please check the file.\n", cert); + wolfSSL_CTX_free(ctx); + return(EXIT_FAILURE); + } + + /* Load server key into WOLFSSL_CTX */ + if (wolfSSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) + != SSL_SUCCESS) { + fprintf(stderr, "Error loading %s, please check the file.\n", key); + wolfSSL_CTX_free(ctx); + return(EXIT_FAILURE); + } + + /* Setup client authentication. */ + wolfSSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, 0); + if (wolfSSL_CTX_load_verify_locations(ctx, verifyCert, 0) != SSL_SUCCESS) { + fprintf(stderr, "Error loading %s, please check the file.\n", + verifyCert); + wolfSSL_CTX_free(ctx); + return(EXIT_FAILURE); + } + + if (cipherList != NULL) { + if (wolfSSL_CTX_set_cipher_list(ctx, cipherList) != SSL_SUCCESS) { + fprintf(stderr, "Server can't set cipher list.\n"); + wolfSSL_CTX_free(ctx); + return(EXIT_FAILURE); + } + } + +#ifndef NO_DH + SetDHCtx(ctx); +#endif + + *wolfsslCtx = ctx; + return EXIT_SUCCESS; +} + +/* Cleanup the wolfSSL context and wolfSSL library. + * + * ctx The wolfSSL context object. + */ +static void WolfSSLCtx_Final(WOLFSSL_CTX* ctx) +{ + wolfSSL_CTX_free(ctx); +} + +/* Create a random reply. + * + * reply The buffer to put the random data into. + * replyLen The amount of data to generate. + */ +static void RandomReply(char* reply, int replyLen) +{ + int ret; + WC_RNG rng; + + ret = wc_InitRng(&rng); + if (ret != 0) { + fprintf(stderr, "Error: initialize random\n"); + exit(EXIT_FAILURE); + } + + ret = wc_RNG_GenerateBlock(&rng, (byte*)reply, replyLen); + wc_FreeRng(&rng); + if (ret != 0) { + fprintf(stderr, "Error: initialize random\n"); + exit(EXIT_FAILURE); + } +} + + +/* Create a socket to listen on and wait for first client. + * + * port The port to listen on. + * numClients The number of clients for listen to support. + * socketfd The socket file descriptor to accept on. + * returns EXIT_SUCCESS on success and EXIT_FAILURE otherwise. + */ +static int CreateSocketListen(int port, int numClients, socklen_t* socketfd) { + int ret; + socklen_t sockfd; + struct sockaddr_in serverAddr = {0}; + int on = 1; + socklen_t len = sizeof(on); + + /* Set the server's address. */ + memset((char *)&serverAddr, 0, sizeof(serverAddr)); + serverAddr.sin_family = AF_INET; + serverAddr.sin_addr.s_addr = INADDR_ANY; + serverAddr.sin_port = htons(port); + + /* Create a non-blocking socket to listen on for new connections. */ + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd == (socklen_t)-1) { + fprintf(stderr, "ERROR: failed to create the socket\n"); + return(EXIT_FAILURE); + } + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, len) < 0) + fprintf(stderr, "setsockopt SO_REUSEADDR failed\n"); + if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &on, len) < 0) + fprintf(stderr, "setsockopt TCP_NODELAY failed\n"); + + if (bind(sockfd, (struct sockaddr *)&serverAddr, + sizeof(serverAddr)) < 0) { + fprintf(stderr, "ERROR: failed to bind\n"); + return(EXIT_FAILURE); + } + + printf("Waiting for a connection...\n"); + + /* Listen for a client to connect. */ + ret = listen(sockfd, numClients); + if (ret == -1) { + fprintf(stderr, "ERROR: failed to listen\n"); + return(EXIT_FAILURE); + } + + *socketfd = sockfd; + return EXIT_SUCCESS; +} + +/* Display the usage for the program. + */ +static void Usage(void) +{ + printf("perf " LIBWOLFSSL_VERSION_STRING + " (NOTE: All files relative to wolfSSL home dir)\n"); + printf("-? Help, print this usage\n"); + printf("-p Port to listen on, not 0, default %d\n", wolfSSLPort); + printf("-v SSL version [0-3], SSLv3(0) - TLS1.2(3)), default %d\n", + SERVER_DEFAULT_VERSION); + printf("-a Allow TLS version downgrade\n"); + printf("-l Cipher suite list (: delimited)\n"); + printf("-c Certificate file, default %s\n", SVR_CERT); + printf("-k Key file, default %s\n", SVR_KEY); + printf("-A Certificate Authority file, default %s\n", CLI_CERT); + printf("-n Benchmark connections\n"); + printf("-N concurrent connections\n"); + printf("-R bytes read from client\n"); + printf("-W bytes written to client\n"); + printf("-B Benchmark written bytes\n"); +} + +/* + * Allocates an event slot in a given POLL events list. + * The complexity of this operation is O(events). For a reduced complexity, + * prefer the example leveraging EPOLL. + * + * events The POLL event list. + * number_of_events The number of events in the list. + * returns the event slot id (>= 0) on success, DISABLED_EVENT_SLOT_FD otherwise. + * + */ +static int allocate_event_slot(struct pollfd* events, int number_of_events) +{ + int i; + + /* Walk through the POLL events list to find the first free slot. */ + for (i = 0; i < number_of_events; i++) { + if (events[i].fd == DISABLED_EVENT_SLOT_FD) { + return i; + } + } + + return -1; +} + +/* + * Releases an event slot previously allocated in the list of POLL events. + * The complexity of this operation is O(1). + * + * events The POLL event list. + * event_slot_id The event slot identifier to release. + */ +static void release_event_slot(struct pollfd* events, int event_slot_id) +{ + events[event_slot_id].fd = DISABLED_EVENT_SLOT_FD; + events[event_slot_id].events = 0; + events[event_slot_id].revents = 0; +} + +/* Main entry point for the program. + * + * argc The count of command line arguments. + * argv The command line arguments. + * returns 0 on success and 1 otherwise. + */ +int main(int argc, char* argv[]) +{ + int ret = 0; + socklen_t socketfd = -1; + struct pollfd* events = NULL; + SSLConn** events_data = NULL; + int ch; + int i; + WOLFSSL_CTX* ctx = NULL; + SSLConn_CTX* sslConnCtx; + word16 port = wolfSSLPort; + char* cipherList = NULL; + char* ourCert = SVR_CERT; + char* ourKey = SVR_KEY; + char* verifyCert = CLI_CERT; + int version = SERVER_DEFAULT_VERSION; + int allowDowngrade= 0; + int numConns = SSL_NUM_CONN; + int numBytesRead = NUM_READ_BYTES; + int numBytesWrite = NUM_WRITE_BYTES; + int maxBytes = MAX_BYTES; + int maxConns = MAX_CONNECTIONS; + int numClients = NUM_CLIENTS; + + /* Parse the command line arguments. */ + while ((ch = mygetopt(argc, argv, OPTIONS)) != -1) { + switch (ch) { + /* Help with command line options. */ + case '?': + Usage(); + exit(EXIT_SUCCESS); + + /* Port number to listen on. */ + case 'p': + port = (word16)atoi(myoptarg); + break; + + /* Version of SSL/TLS to use. */ + case 'v': + version = atoi(myoptarg); + if (version < 0 || version > 3) { + Usage(); + exit(MY_EX_USAGE); + } + break; + case 'a': + allowDowngrade = 1; + break; + + /* List of cipher suites to use. */ + case 'l': + cipherList = myoptarg; + break; + + /* File name of server certificate for authentication. */ + case 'c': + ourCert = myoptarg; + break; + + /* File name of server private key for authentication. */ + case 'k': + ourKey = myoptarg; + break; + + /* File name of client certificate/CA for peer verification. */ + case 'A': + verifyCert = myoptarg; + break; + + /* Number of connections to make. */ + case 'n': + maxConns = atoi(myoptarg); + if (maxConns < 0 || maxConns > 1000000) { + Usage(); + exit(MY_EX_USAGE); + } + maxBytes = 0; + break; + + /* Number of conncurrent connections to use. */ + case 'N': + numConns = atoi(myoptarg); + if (numConns < 0 || numConns > 1000000) { + Usage(); + exit(MY_EX_USAGE); + } + break; + + /* Number of bytes to read each call. */ + case 'R': + numBytesRead = atoi(myoptarg); + if (numBytesRead <= 0) { + Usage(); + exit(MY_EX_USAGE); + } + break; + + /* Number of bytes to write each call. */ + case 'W': + numBytesWrite = atoi(myoptarg); + if (numBytesWrite <= 0) { + Usage(); + exit(MY_EX_USAGE); + } + break; + + /* Maximum number of read and write bytes (separate counts). */ + case 'B': + maxBytes = atoi(myoptarg); + if (maxBytes <= 0) { + Usage(); + exit(MY_EX_USAGE); + } + maxConns = 0; + break; + + /* Unrecognized command line argument. */ + default: + Usage(); + exit(MY_EX_USAGE); + } + } + + /* Allocate space for POLL events to be stored. */ + events = (struct pollfd*)malloc(MAX_EVENT_SLOTS_WITH_LISTENER(maxConns) * sizeof(struct pollfd)); + if (events == NULL) + exit(EXIT_FAILURE); + + /* Allocate space for the POLL events data to be stored. */ + events_data = (SSLConn**)malloc(MAX_EVENT_SLOTS_WITH_LISTENER(maxConns) * sizeof(SSLConn*)); + if (events_data == NULL) + exit(EXIT_FAILURE); + +#ifdef DEBUG_WOLFSSL + wolfSSL_Debugging_ON(); +#endif + + /* Initialize wolfSSL */ + wolfSSL_Init(); + + /* Initialize wolfSSL and create a context object. */ + if (WolfSSLCtx_Init(version, allowDowngrade, ourCert, ourKey, verifyCert, cipherList, &ctx) + == -1) + exit(EXIT_FAILURE); + + RandomReply(reply, sizeof(reply)); + + /* Create SSL/TLS connection data object. */ + sslConnCtx = SSLConn_New(numConns, numBytesRead, numBytesWrite, + maxConns, maxBytes); + if (sslConnCtx == NULL) + exit(EXIT_FAILURE); + + /* Create a socket and listen for a client. */ + if (CreateSocketListen(port, numClients, &socketfd) == EXIT_FAILURE) + exit(EXIT_FAILURE); + + /* Initialize the POLL events data as empty */ + memset(events_data, 0, MAX_EVENT_SLOTS_WITH_LISTENER(maxConns) * sizeof(SSLConn*)); + + /* Initialize the POLL events as disabled */ + memset(events, 0, MAX_EVENT_SLOTS_WITH_LISTENER(maxConns) * sizeof(struct pollfd)); + for (i = 0; i < MAX_EVENT_SLOTS_WITH_LISTENER(maxConns); i++) { + events[i].fd = DISABLED_EVENT_SLOT_FD; + } + + /* Add the event for communications on listening socket. */ + events[LISTENER_EVENT_SLOT_ID(maxConns)].events = POLLIN; + events[LISTENER_EVENT_SLOT_ID(maxConns)].fd = socketfd; + sslConnCtx->accepting = 1; + + /* Keep handling clients until done. */ + while (!SSLConn_Done(sslConnCtx)) { + + /* Wait for events. */ + poll(events, MAX_EVENT_SLOTS_WITH_LISTENER(maxConns), -1); + + /* Process all returned events. */ + for (i = 0; i < MAX_EVENT_SLOTS_WITH_LISTENER(maxConns); i++) { + /* If no data is ready to be processed. */ + if (events[i].revents == 0) continue; + + /* Error event on socket. */ + if (!(events[i].revents & POLLIN)) { + if (events_data[i] == NULL) { + /* Not a client, therefore the listening connection. */ + close(socketfd); + socketfd = -1; + events[LISTENER_EVENT_SLOT_ID(maxConns)].fd = DISABLED_EVENT_SLOT_FD; + } + else { + /* Client connection. */ + events_data[i] = NULL; + release_event_slot(events, i); + events[LISTENER_EVENT_SLOT_ID(maxConns)].fd = socketfd; + } + } + else if (events_data[i] == NULL) { + SSLConn* sslConn; + + /* Accept a new client on the listener. */ + ret = SSLConn_Accept(sslConnCtx, ctx, socketfd, &sslConn); + if (ret == EXIT_SUCCESS) { + int event_slot_id; + + /* Set POLL to check for events on the new socket. */ + event_slot_id = allocate_event_slot(events, maxConns); + if (event_slot_id == -1) { + fprintf(stderr, "ERROR: failed add event to poll because the list is full.\n"); + exit(EXIT_FAILURE); + } + events[event_slot_id].events = POLLIN; + events[event_slot_id].fd = sslConn->sockfd; + events_data[event_slot_id] = sslConn; + } + + if (sslConnCtx->cnt == sslConnCtx->numConns) { + /* Don't accept any more TCP connections. */ + events[LISTENER_EVENT_SLOT_ID(maxConns)].fd = DISABLED_EVENT_SLOT_FD; + sslConnCtx->accepting = 0; + } + } + else { + if (sslConnCtx->totalTime == 0) + sslConnCtx->totalTime = current_time(1); + SSLConn_ReadWrite(sslConnCtx, events_data[i]); + } + } + + SSLConn_FreeSSLConn(sslConnCtx); + + /* Accept more connections again up to the maximum concurrent. */ + if (!sslConnCtx->accepting && + sslConnCtx->cnt < sslConnCtx->numConns) { + events[LISTENER_EVENT_SLOT_ID(maxConns)].fd = socketfd; + sslConnCtx->accepting = 1; + } + } + + sslConnCtx->totalTime = current_time(0) - sslConnCtx->totalTime; + + if (socketfd != -1) + close(socketfd); + free(events); + free(events_data); + + SSLConn_PrintStats(sslConnCtx); + SSLConn_Free(sslConnCtx); + + WolfSSLCtx_Final(ctx); + + wolfSSL_Cleanup(); + + exit(EXIT_SUCCESS); +} +