diff options
Diffstat (limited to 'tests/tcpflood.c')
-rw-r--r-- | tests/tcpflood.c | 2108 |
1 files changed, 2108 insertions, 0 deletions
diff --git a/tests/tcpflood.c b/tests/tcpflood.c new file mode 100644 index 0000000..bd8edaf --- /dev/null +++ b/tests/tcpflood.c @@ -0,0 +1,2108 @@ +/* Opens a large number of tcp connections and sends + * messages over them. This is used for stress-testing. + * + * NOTE: the following part is actually the SPEC (or call it man page). + * It's not random comments. So if the code behavior does not match what + * is written here, it should be considered a bug. + * + * Params + * -t target address (default 127.0.0.1) + * -p target port(s) (default 13514), multiple via port1:port2:port3... + * -n number of target ports (all target ports must be given in -p!) + * Note -c must also be set to at LEAST the number of -n! + * -c number of connections (default 1), use negative number + * to set a "soft limit": if tcpflood cannot open the + * requested number of connections, gracefully degrade to + * whatever number could be opened. This is useful in environments + * where system config constraints cannot be overriden (e.g. + * vservers, non-admin users, ...) + * -m number of messages to send (connection is random) + * -i initial message number (optional) + * -P PRI to be used for generated messages (default is 167). + * Specify the plain number without leading zeros + * -d amount of extra data to add to message. If present, the + * number itself will be added as third field, and the data + * bytes as forth. Add -r to randomize the amount of extra + * data included in the range 1..(value of -d). + * -r randomize amount of extra data added (-d must be > 0) + * -s (silent) do not show progress indicator (never done on non-tty) + * -f support for testing dynafiles. If given, include a dynafile ID + * in the range 0..(f-1) as the SECOND field, shifting all field values + * one field to the right. Zero (default) disables this functionality. + * -M the message to be sent. Disables all message format options, as + * only that exact same message is sent. + * -I read specified input file, do NOT generate own test data. The test + * completes when eof is reached. + * -B The specified file (-I) is binary. No data processing is done by + * tcpflood. If multiple connections are specified, data is read in + * chunks and spread across the connections without taking any record + * delimiters into account. + * -C when input from a file is read, this file is transmitted -C times + * (C like cycle, running out of meaningful option switches ;)) + * -D randomly drop and re-establish connections. Useful for stress-testing + * the TCP receiver. + * -F USASCII value for frame delimiter (in octet-stuffing mode), default LF + * -R number of times the test shall be run (very useful for gathering performance + * data and other repetitive things). Default: 1 + * -S number of seconds to sleep between different runs (-R) Default: 30 + * -X generate sTats data records. Default: off + * -e encode output in CSV (not yet everywhere supported) + * for performance data: + * each inidividual line has the runtime of one test + * the last line has 0 in field 1, followed by numberRuns,TotalRuntime, + * Average,min,max + * -T transport to use. Currently supported: "udp", "tcp" (default), "tls" (tcp+tls), relp-plain, relp-tls + * Note: UDP supports a single target port, only + * -u Set RELP TLS Library to gnutls or openssl + * -W wait time between sending batches of messages, in microseconds (Default: 0) + * -b number of messages within a batch (default: 100,000,000 millions) + * -Y use multiple threads, one per connection (which means 1 if one only connection + * is configured!) + * -y use RFC5424 style test message + * -x CA Cert File for verification (TLS Mode / OpenSSL only) + * -z private key file for TLS mode + * -Z cert (public key) file for TLS mode + * -a Authentication Mode for relp-tls + * -A do NOT abort if an error occured during sending messages + * -E Permitted Peer for relp-tls + * -L loglevel to use for GnuTLS troubleshooting (0-off to 10-all, 0 default) + * -j format message in json, parameter is JSON cookie + * -O Use octate-count framing + * -v verbose output, possibly useful for troubleshooting. Most importantly, + * this gives insight into librelp actions (if relp is selected as protocol). + * -k Custom Configuration string passwed through the TLS library. + * Currently only OpenSSL is supported, possible configuration commands and values can be found here: + * https://www.openssl.org/docs/man1.0.2/man3/SSL_CONF_cmd.html + * Sample: -k"Protocol=ALL,-SSLv2,-SSLv3,-TLSv1,-TLSv1.1" + * Works for LIBRELP now as well! + * + * Part of the testbench for rsyslog. + * + * Copyright 2009-2019 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Rsyslog is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Rsyslog 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 Rsyslog. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + */ +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <assert.h> +#include <unistd.h> +#include <string.h> +#include <strings.h> +#include <netinet/in.h> +#include <pthread.h> +#ifdef ENABLE_RELP +#include <librelp.h> +#endif +#include <sys/resource.h> +#include <sys/time.h> +#include <errno.h> +#ifdef ENABLE_GNUTLS +# include <gnutls/gnutls.h> +# if GNUTLS_VERSION_NUMBER <= 0x020b00 +# include <gcrypt.h> + GCRY_THREAD_OPTION_PTHREAD_IMPL; +# endif +#endif +#ifdef ENABLE_OPENSSL + #include <openssl/ssl.h> + #include <openssl/x509v3.h> + #include <openssl/err.h> + #include <openssl/engine.h> + + /* OpenSSL API differences */ + #if OPENSSL_VERSION_NUMBER >= 0x10100000L + #define RSYSLOG_X509_NAME_oneline(X509CERT) X509_get_subject_name(X509CERT) + #define RSYSLOG_BIO_method_name(SSLBIO) BIO_method_name(SSLBIO) + #define RSYSLOG_BIO_number_read(SSLBIO) BIO_number_read(SSLBIO) + #define RSYSLOG_BIO_number_written(SSLBIO) BIO_number_written(SSLBIO) + #else + #define RSYSLOG_X509_NAME_oneline(X509CERT) (X509CERT != NULL ? X509CERT->cert_info->subject : NULL) + #define RSYSLOG_BIO_method_name(SSLBIO) SSLBIO->method->name + #define RSYSLOG_BIO_number_read(SSLBIO) SSLBIO->num + #define RSYSLOG_BIO_number_written(SSLBIO) SSLBIO->num + #endif + +#endif + +char *test_rs_strerror_r(int errnum, char *buf, size_t buflen) { +#ifndef HAVE_STRERROR_R + char *pszErr; + pszErr = strerror(errnum); + snprintf(buf, buflen, "%s", pszErr); +#else +# ifdef STRERROR_R_CHAR_P + char *p = strerror_r(errnum, buf, buflen); + if (p != buf) { + strncpy(buf, p, buflen); + buf[buflen - 1] = '\0'; + } +# else + strerror_r(errnum, buf, buflen); +# endif +#endif /* #ifdef __hpux */ + return buf; +} + +#define INVALID_SOCKET -1 +/* Name of input file, must match $IncludeConfig in test suite .conf files */ +#define NETTEST_INPUT_CONF_FILE "nettest.input.conf" +/* name of input file, must match $IncludeConfig in .conf files */ + +#define MAX_EXTRADATA_LEN 512*1024 +#define MAX_SENDBUF 2 * MAX_EXTRADATA_LEN +#define MAX_RCVBUF 16 * 1024 + 1/* TLS RFC 8449: max size of buffer for message reception */ + +static char *targetIP = "127.0.0.1"; +static char *msgPRI = "167"; +static int targetPort[5] = {13514}; +static int numTargetPorts = 1; +static int verbose = 0; +static int dynFileIDs = 0; +static int extraDataLen = 0; /* amount of extra data to add to message */ +static int useRFC5424Format = 0; /* should the test message be in RFC5424 format? */ +static int bRandomizeExtraData = 0; /* randomize amount of extra data added */ +static int numMsgsToSend = 1; /* number of messages to send */ +static int numConnections = 1; /* number of connections to create */ +static int softLimitConnections = 0; /* soft connection limit, see -c option description */ +static int *sockArray; /* array of sockets to use */ +#ifdef ENABLE_RELP +static relpClt_t **relpCltArray; /* array of sockets to use */ +#endif +static int msgNum = 0; /* initial message number to start with */ +static int bShowProgress = 1; /* show progress messages */ +static int bSilent = 0; /* completely silent operation */ +static int bRandConnDrop = 0; /* randomly drop connections? */ +static double dbRandConnDrop = 0.95; /* random drop probability */ +static char *MsgToSend = NULL; /* if non-null, this is the actual message to send */ +static int bBinaryFile = 0; /* is -I file binary */ +static char *dataFile = NULL; /* name of data file, if NULL, generate own data */ +static int numFileIterations = 1;/* how often is file data to be sent? */ +static char frameDelim = '\n'; /* default frame delimiter */ +FILE *dataFP = NULL; /* file pointer for data file, if used */ +static long nConnDrops = 0; /* counter: number of time connection was dropped (-D option) */ +static int numRuns = 1; /* number of times the test shall be run */ +static int sleepBetweenRuns = 30; /* number of seconds to sleep between runs */ +static int bStatsRecords = 0; /* generate stats records */ +static int bCSVoutput = 0; /* generate output in CSV (where applicable) */ +static long long batchsize = 100000000ll; +static int waittime = 0; +static int runMultithreaded = 0; /* run tests in multithreaded mode */ +static int numThrds = 1; /* number of threads to use */ +static int abortOnSendFail = 1; /* abort run if sending fails? */ +static char *tlsCAFile = NULL; +static char *tlsCertFile = NULL; +static char *tlsKeyFile = NULL; +static char *relpAuthMode = NULL; +static char *relpPermittedPeer = NULL; +#if defined(HAVE_RELPENGINESETTLSLIBBYNAME) +static char *relpTlsLib = NULL; +#endif + +static int tlsLogLevel = 0; +static char *jsonCookie = NULL; /* if non-NULL, use JSON format with this cookie */ +static int octateCountFramed = 0; +static char *customConfig = NULL; /* Stores a string with custom configuration passed through the TLS driver */ + +#ifdef ENABLE_GNUTLS +static gnutls_session_t *sessArray; /* array of TLS sessions to use */ +static gnutls_certificate_credentials_t tlscred; +#endif + +#ifdef ENABLE_OPENSSL +/* Main OpenSSL CTX pointer */ +static SSL_CTX *ctx; +static SSL **sslArray; +static struct sockaddr_in dtls_client_addr; /* socket address sender for receiving DTLS data */ +static int udpsockin; /* socket for receiving messages in DTLS mode */ +#endif + +/* variables for managing multi-threaded operations */ +int runningThreads; /* number of threads currently running */ +int doRun; /* shall sender thread begin to run? */ +pthread_mutex_t thrdMgmt; /* mutex for controling startup/shutdown */ +pthread_cond_t condStarted; +pthread_cond_t condDoRun; + +/* the following struct provides information for a generator instance (thread) */ +struct instdata { + /* lower and upper bounds for the thread in question */ + unsigned long long lower; + unsigned long long numMsgs; /* number of messages to send */ + unsigned long long numSent; /* number of messages already sent */ + unsigned idx; /**< index of fd to be used for sending */ + pthread_t thread; /**< thread processing this instance */ +} *instarray = NULL; + +/* the following structure is used to gather performance data */ +struct runstats { + unsigned long long totalRuntime; + unsigned long minRuntime; + unsigned long maxRuntime; + int numRuns; +}; + +static int udpsockout; /* socket for sending in UDP mode */ +static struct sockaddr_in udpRcvr; /* remote receiver in UDP mode */ + +static enum { TP_UDP, TP_TCP, TP_TLS, TP_RELP_PLAIN, TP_RELP_TLS, TP_DTLS } transport = TP_TCP; + +/* forward definitions */ +static void initTLSSess(int); +static int sendTLS(int i, char *buf, size_t lenBuf); +static void closeTLSSess(int __attribute__((unused)) i); + +static void initDTLSSess(void); +static int sendDTLS(char *buf, size_t lenBuf); +static void closeDTLSSess(void); + +#ifdef ENABLE_RELP +/* RELP subsystem */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-security" +static void relp_dbgprintf(char __attribute__((unused)) *fmt, ...) { + printf(fmt); +} +#pragma GCC diagnostic pop + +static relpEngine_t *pRelpEngine; +#define CHKRELP(f) if(f != RELP_RET_OK) { fprintf(stderr, "%s\n", #f); exit(1); } + +static void +onErr(void *pUsr, char *objinfo, char* errmesg, __attribute__((unused)) relpRetVal errcode) +{ + fprintf(stderr, "tcpflood: onErr '%s'\n", errmesg); +} + +static void +onGenericErr(char *objinfo, char* errmesg, __attribute__((unused)) relpRetVal errcode) +{ + fprintf(stderr, "tcpflood: onGenericErr '%s'\n", errmesg); +} + +static void +onAuthErr(void *pUsr, char *authinfo, char* errmesg, __attribute__((unused)) relpRetVal errcode) +{ + fprintf(stderr, "tcpflood: onAuthErr '%s' peer '%s'\n", errmesg, authinfo); +} + +static void +initRELP_PLAIN(void) +{ + CHKRELP(relpEngineConstruct(&pRelpEngine)); + CHKRELP(relpEngineSetDbgprint(pRelpEngine, + verbose ? relp_dbgprintf : NULL)); + CHKRELP(relpEngineSetEnableCmd(pRelpEngine, (unsigned char*)"syslog", + eRelpCmdState_Required)); + /* Error output support */ + CHKRELP(relpEngineSetOnErr(pRelpEngine, onErr)); + CHKRELP(relpEngineSetOnGenericErr(pRelpEngine, onGenericErr)); + CHKRELP(relpEngineSetOnAuthErr(pRelpEngine, onAuthErr)); + +} +#endif /* #ifdef ENABLE_RELP */ + +/* prepare send subsystem for UDP send */ +static int +setupUDP(void) +{ + if((udpsockout = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + return 1; + + memset((char *) &udpRcvr, 0, sizeof(udpRcvr)); + udpRcvr.sin_family = AF_INET; + udpRcvr.sin_port = htons(targetPort[0]); + if(inet_aton(targetIP, &udpRcvr.sin_addr)==0) { + fprintf(stderr, "inet_aton() failed\n"); + return(1); + } + + return 0; +} + +#if defined(ENABLE_OPENSSL) +static int +setupDTLS(void) +{ + // Setup receiving Socket for DTLS + if((udpsockin = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + return 1; + + memset(&dtls_client_addr, 0, sizeof(dtls_client_addr)); + dtls_client_addr.sin_family = AF_INET; + dtls_client_addr.sin_port = htons(0); + dtls_client_addr.sin_addr.s_addr = INADDR_ANY; + if (bind(udpsockin, (struct sockaddr*)&dtls_client_addr, sizeof(dtls_client_addr)) < 0) { + perror("bind()"); + fprintf(stderr, "Unable to bind DTLS CLient socket\n"); + return(1); + } + + memset((char *) &udpRcvr, 0, sizeof(udpRcvr)); + udpRcvr.sin_family = AF_INET; + udpRcvr.sin_port = htons(targetPort[0]); + if(inet_aton(targetIP, &udpRcvr.sin_addr)==0) { + fprintf(stderr, "inet_aton() failed\n"); + return(1); + } + + // Init Socket Connection (Which technically does not connect but prepares socket for DTLS) + printf("[DEBUG] Init Session to %s:%d ...\n", targetIP, targetPort[0]); + udpsockout = socket(AF_INET, SOCK_DGRAM, 0); + // Connect the UDP socket to the server's address + if (connect(udpsockout, (const struct sockaddr *) &udpRcvr, sizeof(udpRcvr)) < 0) { + perror("connect()"); + fprintf(stderr, "connect to %s:%d failed\n", targetIP, targetPort[0]); + return(1); + } + sockArray[0] = -1; + + return 0; +} +#endif + +/* open a single tcp connection + */ +int openConn(int *fd, const int connIdx) +{ + int sock; + struct sockaddr_in addr; + int port; + int retries = 0; + int rnd; + + /* randomize port if required */ + if(numTargetPorts > 1) { + rnd = rand(); /* easier if we need value for debug messages ;) */ + port = targetPort[(rnd % numTargetPorts)]; + } else { + port = targetPort[0]; + } + if(transport == TP_RELP_PLAIN || transport == TP_RELP_TLS) { + #ifdef ENABLE_RELP + relpRetVal relp_r; + relpClt_t *relpClt; + char relpPort[16]; + snprintf(relpPort, sizeof(relpPort), "%d", port); + CHKRELP(relpEngineCltConstruct(pRelpEngine, &relpClt)); + if(transport == TP_RELP_TLS) { + #if defined(HAVE_RELPENGINESETTLSLIBBYNAME) + if(relpTlsLib != NULL && relpEngineSetTLSLibByName(pRelpEngine, relpTlsLib) != RELP_RET_OK) { + fprintf(stderr, "relpTlsLib not accepted by librelp, using default\n"); + } + #endif + if(relpCltEnableTLS(relpClt) != RELP_RET_OK) { + fprintf(stderr, "error while enabling TLS for relp\n"); + exit(1); + } + if(relpAuthMode != NULL && relpCltSetAuthMode(relpClt, relpAuthMode) != RELP_RET_OK) { + fprintf(stderr, "could not set Relp Authentication mode: %s\n", relpAuthMode); + exit(1); + } + if(tlsCAFile != NULL && relpCltSetCACert(relpClt, tlsCAFile) != RELP_RET_OK) { + fprintf(stderr, "could not set CA File: %s\n", tlsCAFile); + exit(1); + } + if(tlsCertFile != NULL && relpCltSetOwnCert(relpClt, tlsCertFile) != RELP_RET_OK) { + fprintf(stderr, "could not set Cert File: %s\n", tlsCertFile); + exit(1); + } + if(tlsKeyFile != NULL && relpCltSetPrivKey(relpClt, tlsKeyFile) != RELP_RET_OK) { + fprintf(stderr, "could not set Key File: %s\n", tlsKeyFile); + exit(1); + } + if(relpPermittedPeer != NULL && relpCltAddPermittedPeer(relpClt, relpPermittedPeer) + != RELP_RET_OK) { + fprintf(stderr, "could not set Permitted Peer: %s\n", relpPermittedPeer); + exit(1); + } +#if defined(HAVE_RELPENGINESETTLSCFGCMD) + /* Check for Custom Config string */ + if(customConfig != NULL && relpCltSetTlsConfigCmd(relpClt, customConfig) + != RELP_RET_OK) { + fprintf(stderr, "could not set custom tls command: %s\n", customConfig); + exit(1); + } +#endif + } + relpCltArray[connIdx] = relpClt; + relp_r = relpCltConnect(relpCltArray[connIdx], 2, + (unsigned char*)relpPort, (unsigned char*)targetIP); + if(relp_r != RELP_RET_OK) { + fprintf(stderr, "relp connect failed with return %d\n", relp_r); + return(1); + } + *fd = 1; /* mimic "all ok" state */ + #endif + } else { /* TCP, with or without TLS */ + if((sock=socket(AF_INET, SOCK_STREAM, 0))==-1) { + perror("\nsocket()"); + return(1); + } + memset((char *) &addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if(inet_aton(targetIP, &addr.sin_addr)==0) { + fprintf(stderr, "inet_aton() failed\n"); + return(1); + } + while(1) { /* loop broken inside */ + if(connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0) { + break; + } else { + if(retries++ == 50) { + perror("connect()"); + fprintf(stderr, "connect(%d) failed\n", port); + return(1); + } else { + usleep(100000); /* ms = 1000 us! */ + } + } + } + + *fd = sock; + } + return 0; +} + + +/* open all requested tcp connections + * this includes allocating the connection array + */ +int openConnections(void) +{ + int i; + char msgBuf[128]; + size_t lenMsg; + + if(transport == TP_UDP) + return setupUDP(); + + if(bShowProgress) + if(write(1, " open connections", sizeof(" open connections")-1)){} +# if defined(ENABLE_OPENSSL) + sslArray = calloc(numConnections, sizeof(SSL *)); +# elif defined(ENABLE_GNUTLS) + sessArray = calloc(numConnections, sizeof(gnutls_session_t)); +# endif + sockArray = calloc(numConnections, sizeof(int)); + +# if defined(ENABLE_OPENSSL) + // Use setupDTLS on DTLS + if(transport == TP_DTLS) + return setupDTLS(); +# endif + + #ifdef ENABLE_RELP + if(transport == TP_RELP_PLAIN || transport == TP_RELP_TLS) + relpCltArray = calloc(numConnections, sizeof(relpClt_t*)); + #endif + for(i = 0 ; i < numConnections ; ++i) { + if(i % 10 == 0) { + if(bShowProgress) + printf("\r%5.5d", i); + } + if(openConn(&(sockArray[i]), i) != 0) { + printf("error in trying to open connection i=%d\n", i); + if(softLimitConnections) { + printf("Connection limit is soft, continuing with fewer connections\n"); + numConnections = i - 1; + int close_conn = 10; + for(i -= 1 ; close_conn > 0 && i > 1 ; --i, --close_conn) { + printf("closing connection %d to make some room\n", i); + /* close at least some connections so that + * other functionality has a chance to do + * at least something. + */ + if(transport == TP_RELP_PLAIN || transport == TP_RELP_TLS) { + #ifdef ENABLE_RELP + CHKRELP(relpEngineCltDestruct(pRelpEngine, + relpCltArray+i)); + #endif + } else { /* TCP and TLS modes */ + if(transport == TP_TLS) + closeTLSSess(i); + close(sockArray[i]); + } + sockArray[i] = -1; + } + numConnections = i; + printf("continuing with %d connections.\n", numConnections); + if(numConnections < 1) { + fprintf(stderr, "tcpflood could not open at least one " + "connection, error-terminating\n"); + exit(1); + } + break; + } + return 1; + } + if(transport == TP_TLS) { + initTLSSess(i); + } + } + if(bShowProgress) { + lenMsg = sprintf(msgBuf, "\r%5.5d open connections\n", i); + if(write(1, msgBuf, lenMsg)) {} + } + + return 0; +} + + +/* we also close all connections because otherwise we may get very bad + * timing for the syslogd - it may not be able to process all incoming + * messages fast enough if we immediately shut down. + * TODO: it may be an interesting excercise to handle that situation + * at the syslogd level, too + * rgerhards, 2009-04-14 + */ +void closeConnections(void) +{ + int i; + size_t lenMsg; + struct linger ling; + char msgBuf[128]; + + if(transport == TP_UDP) { + return; + } +# if defined(ENABLE_OPENSSL) + else if(transport == TP_DTLS) { + closeDTLSSess(); + return; + } +# endif + + if(bShowProgress) + if(write(1, " close connections", sizeof(" close connections")-1)){} + for(i = 0 ; i < numConnections ; ++i) { + if(i % 10 == 0 && bShowProgress) { + lenMsg = sprintf(msgBuf, "\r%5.5d", i); + if(write(1, msgBuf, lenMsg)){} + } + if(transport == TP_RELP_PLAIN || transport == TP_RELP_TLS) { + #ifdef ENABLE_RELP + relpRetVal relpr; + if(sockArray[i] != -1) { + relpr = relpEngineCltDestruct(pRelpEngine, relpCltArray+i); + if(relpr != RELP_RET_OK) { + fprintf(stderr, "relp error %d on close\n", relpr); + } + sockArray[i] = -1; + } + #endif + } else { /* TCP and TLS modes */ + if(sockArray[i] != -1) { + /* we try to not overrun the receiver by trying to flush buffers + * *during* close(). -- rgerhards, 2010-08-10 + */ + ling.l_onoff = 1; + ling.l_linger = 1; + setsockopt(sockArray[i], SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)); + if(transport == TP_TLS) { + closeTLSSess(i); + } + close(sockArray[i]); + } + } + } + if(bShowProgress) { + lenMsg = sprintf(msgBuf, "\r%5.5d close connections\n", i); + if(write(1, msgBuf, lenMsg)){} + } + +} + + +/* generate the message to be sent according to program command line parameters. + * this has been moved to its own function as we now have various different ways + * of constructing test messages. -- rgerhards, 2010-03-31 + */ +static void +genMsg(char *buf, size_t maxBuf, size_t *pLenBuf, struct instdata *inst) +{ + int edLen; /* actual extra data length to use */ + char extraData[MAX_EXTRADATA_LEN + 1]; + char dynFileIDBuf[128] = ""; + int done; + char payloadLen[32]; + int payloadStringLen; + + if(dataFP != NULL) { + /* get message from file */ + do { + done = 1; + *pLenBuf = fread(buf, 1, MAX_EXTRADATA_LEN + 1024, dataFP); + if(*pLenBuf == 0) { + if(--numFileIterations > 0) { + rewind(dataFP); + done = 0; /* need new iteration */ + } else { + *pLenBuf = 0; + goto finalize_it; + } + } + } while(!done); /* Attention: do..while()! */ + } else if(jsonCookie != NULL) { + if(useRFC5424Format) { + *pLenBuf = snprintf(buf, maxBuf, "<%s>1 2003-03-01T01:00:00.000Z mymachine.example.com " + "tcpflood - tag [tcpflood@32473 MSGNUM" + "=\"%8.8d\"] %s{\"msgnum\":%d}%c", msgPRI, msgNum, + jsonCookie, msgNum, frameDelim); + } else { + *pLenBuf = snprintf(buf, maxBuf, "<%s>Mar 1 01:00:00 172.20.245.8 tag %s{\"msgnum\":%d}%c", + msgPRI, jsonCookie, msgNum, frameDelim); + } + } else if(MsgToSend == NULL) { + if(dynFileIDs > 0) { + snprintf(dynFileIDBuf, sizeof(dynFileIDBuf), "%d:", rand() % dynFileIDs); + } + if(extraDataLen == 0) { + if(useRFC5424Format) { + *pLenBuf = snprintf(buf, maxBuf, "<%s>1 2003-03-01T01:00:00.000Z " + "mymachine.example.com tcpflood - tag [tcpflood@32473 " + "MSGNUM=\"%8.8d\"] msgnum:%s%8.8d:%c", + msgPRI, msgNum, dynFileIDBuf, msgNum, frameDelim); + } else { + *pLenBuf = snprintf(buf, maxBuf, "<%s>Mar 1 01:00:00 172.20.245.8 tag " + "msgnum:%s%8.8d:%c", msgPRI, dynFileIDBuf, msgNum, frameDelim); + } + } else { + if(bRandomizeExtraData) + edLen = ((unsigned long) rand() + extraDataLen) % extraDataLen + 1; + else + edLen = extraDataLen; + memset(extraData, 'X', edLen); + extraData[edLen] = '\0'; + if(useRFC5424Format) { + *pLenBuf = snprintf(buf, maxBuf, "<%s>1 2003-03-01T01:00:00.000Z " + "mymachine.example.com tcpflood - tag [tcpflood@32473 " + "MSGNUM=\"%8.8d\"] msgnum:%s%8.8d:%c", + msgPRI, msgNum, dynFileIDBuf, msgNum, frameDelim); + } else { + *pLenBuf = snprintf(buf, maxBuf, "<%s>Mar 1 01:00:00 172.20.245.8 tag msgnum" + ":%s%8.8d:%d:%s%c", msgPRI, dynFileIDBuf, msgNum, edLen, + extraData, frameDelim); + } + } + } else { + /* use fixed message format from command line */ + *pLenBuf = snprintf(buf, maxBuf, "%s%c", MsgToSend, frameDelim); + } + if (octateCountFramed == 1) { + snprintf(payloadLen, sizeof(payloadLen), "%zd ", *pLenBuf); + payloadStringLen = strlen(payloadLen); + memmove(buf + payloadStringLen, buf, *pLenBuf); + memcpy(buf, payloadLen, payloadStringLen); + *pLenBuf += payloadStringLen; + } + ++inst->numSent; + +finalize_it: /*EMPTY to keep the compiler happy */; +} + + +static int +sendPlainTCP(const int socknum, const char *const buf, const size_t lenBuf, int *const ret_errno) +{ + size_t lenSent; + int r; + + lenSent = 0; + while(lenSent != lenBuf) { + r = send(sockArray[socknum], buf + lenSent, lenBuf - lenSent, 0); + if(r > 0) { + lenSent += r; + } else { + *ret_errno = errno; + goto finalize_it; + } + } + +finalize_it: + return lenSent; +} + + +/* send messages to the tcp connections we keep open. We use + * a very basic format that helps identify the message + * (via msgnum:<number>: e.g. msgnum:00000001:). This format is suitable + * for extracton to field-based properties. + * The first numConnection messages are sent sequentially, as are the + * last. All messages in between are sent over random connections. + * Note that message numbers start at 0. + */ +int sendMessages(struct instdata *inst) +{ + unsigned i = 0; + int socknum; + size_t lenBuf; + size_t lenSend = 0; + char *statusText = ""; + char buf[MAX_EXTRADATA_LEN + 1024]; + char sendBuf[MAX_SENDBUF]; + int offsSendBuf = 0; + char errStr[1024]; + int error_number = 0; + unsigned show_progress_interval = 100; + + if(!bSilent) { + if(dataFile == NULL) { + printf("Sending %llu messages.\n", inst->numMsgs); + statusText = "messages"; + if ((inst->numMsgs / 100) > show_progress_interval) { + show_progress_interval = inst->numMsgs / 100; + } + } else { + printf("Sending file '%s' %d times.\n", dataFile, + numFileIterations); + statusText = "kb"; + } + } + if(bShowProgress) + printf("\r%8.8d %s sent", 0, statusText); + while(i < inst->numMsgs) { + if(runMultithreaded) { + socknum = inst->idx; + } else { + if((int) i < numConnections) + socknum = i; + else if(i >= inst->numMsgs - numConnections) { + socknum = i - (inst->numMsgs - numConnections); + } else { + int rnd = rand(); + socknum = rnd % numConnections; + } + } + genMsg(buf, sizeof(buf), &lenBuf, inst); /* generate the message to send according to params */ + if(lenBuf == 0) + break; /* terminate when no message could be generated */ + if(transport == TP_TCP) { + if(sockArray[socknum] == -1) { + /* connection was dropped, need to re-establish */ + if(openConn(&(sockArray[socknum]), socknum) != 0) { + printf("error in trying to re-open connection %d\n", socknum); + exit(1); + } + } + lenSend = sendPlainTCP(socknum, buf, lenBuf, &error_number); + } else if(transport == TP_UDP) { + lenSend = sendto(udpsockout, buf, lenBuf, 0, &udpRcvr, sizeof(udpRcvr)); + error_number = errno; + } else if(transport == TP_TLS) { + if(sockArray[socknum] == -1) { + /* connection was dropped, need to re-establish */ + if(openConn(&(sockArray[socknum]), socknum) != 0) { + printf("error in trying to re-open connection %d\n", socknum); + exit(1); + } + initTLSSess(socknum); + } + if(offsSendBuf + lenBuf < MAX_SENDBUF) { + memcpy(sendBuf+offsSendBuf, buf, lenBuf); + offsSendBuf += lenBuf; + lenSend = lenBuf; /* simulate "good" call */ + } else { + lenSend = sendTLS(socknum, sendBuf, offsSendBuf); + lenSend = (lenSend == offsSendBuf) ? lenBuf : -1; + memcpy(sendBuf, buf, lenBuf); + offsSendBuf = lenBuf; + } +# if defined(ENABLE_OPENSSL) + } else if(transport == TP_DTLS) { + if(sockArray[0] == -1) { + // Init DTLS Session (Bind local listener) + initDTLSSess(); + } + lenSend = sendDTLS(buf, lenBuf); +# endif + } else if(transport == TP_RELP_PLAIN || transport == TP_RELP_TLS) { + #ifdef ENABLE_RELP + relpRetVal relp_ret; + if(sockArray[socknum] == -1) { + /* connection was dropped, need to re-establish */ + if(openConn(&(sockArray[socknum]), socknum) != 0) { + printf("error in trying to re-open connection %d\n", socknum); + exit(1); + } + } + relp_ret = relpCltSendSyslog(relpCltArray[socknum], + (unsigned char*)buf, lenBuf); + if (relp_ret == RELP_RET_OK) { + lenSend = lenBuf; /* mimic ok */ + } else { + lenSend = 0; /* mimic fail */ + printf("\nrelpCltSendSyslog() failed with relp error code %d\n", + relp_ret); + } + #endif + } + if(lenSend != lenBuf) { + printf("\r%5.5u\n", i); + fflush(stdout); + test_rs_strerror_r(error_number, errStr, sizeof(errStr)); + if(lenSend == 0) { + printf("tcpflood: socket %d, index %u, msgNum %lld CLOSED REMOTELY\n", + sockArray[socknum], i, inst->numSent); + } else { + printf("tcpflood: send() failed \"%s\" at socket %d, index %u, " + "msgNum %lld, lenSend %zd, lenBuf %zd\n", + errStr, sockArray[socknum], i, inst->numSent, lenSend, + lenBuf); + } + fflush(stderr); + + if(abortOnSendFail) { + printf("tcpflood terminates due to send failure\n"); + return(1); + } + } + if(i % show_progress_interval == 0) { + if(bShowProgress) + printf("\r%8.8u", i); + } + if(!runMultithreaded && bRandConnDrop) { + /* if we need to randomly drop connections, see if we + * are a victim + */ + if(rand() > (int) (RAND_MAX * dbRandConnDrop)) { +#if 1 + if(transport == TP_TLS && offsSendBuf != 0) { + /* send remaining buffer */ + lenSend = sendTLS(socknum, sendBuf, offsSendBuf); + if(lenSend != offsSendBuf) { + fprintf(stderr, "tcpflood: error in send function causes potential " + "data loss lenSend %zd, offsSendBuf %d\n", + lenSend, offsSendBuf); + } + offsSendBuf = 0; + } +#endif + ++nConnDrops; + close(sockArray[socknum]); + sockArray[socknum] = -1; + } + } + if(inst->numSent % batchsize == 0) { + usleep(waittime); + } + ++msgNum; + ++i; + } + if(transport == TP_TLS && offsSendBuf != 0) { + /* send remaining buffer */ + lenSend = sendTLS(socknum, sendBuf, offsSendBuf); + } + if(!bSilent) + printf("\r%8.8u %s sent\n", i, statusText); + + return 0; +} + + +/* this is the thread that starts a generator + */ +static void * +thrdStarter(void *arg) +{ + struct instdata *inst = (struct instdata*) arg; + pthread_mutex_lock(&thrdMgmt); + runningThreads++; + pthread_cond_signal(&condStarted); + while(doRun == 0) { + pthread_cond_wait(&condDoRun, &thrdMgmt); + } + pthread_mutex_unlock(&thrdMgmt); + if(sendMessages(inst) != 0) { + printf("error sending messages\n"); + } + return NULL; +} + + +/* This function initializes the actual traffic generators. The function sets up all required + * parameter blocks and starts threads. It returns when all threads are ready to run + * and the main task must just enable them. + */ +static void +prepareGenerators() +{ + int i; + long long msgsThrd; + long long starting = 0; + pthread_attr_t thrd_attr; + + if(runMultithreaded) { + bSilent = 1; + numThrds = numConnections; + } else { + numThrds = 1; + } + + pthread_attr_init(&thrd_attr); + pthread_attr_setstacksize(&thrd_attr, 4096*1024); + + runningThreads = 0; + doRun = 0; + pthread_mutex_init(&thrdMgmt, NULL); + pthread_cond_init(&condStarted, NULL); + pthread_cond_init(&condDoRun, NULL); + + if(instarray != NULL) { + free(instarray); + } + instarray = calloc(numThrds, sizeof(struct instdata)); + msgsThrd = numMsgsToSend / numThrds; + + for(i = 0 ; i < numThrds ; ++i) { + instarray[i].lower = starting; + instarray[i].numMsgs = msgsThrd; + instarray[i].numSent = 0; + instarray[i].idx = i; + pthread_create(&(instarray[i].thread), &thrd_attr, thrdStarter, instarray + i); + /*printf("started thread %x\n", (unsigned) instarray[i].thread);*/ + starting += msgsThrd; + } +} + +/* Let all generators run. Threads must have been started. Here we wait until + * all threads are initialized and then broadcast that they can begin to run. + */ +static void +runGenerators() +{ + pthread_mutex_lock(&thrdMgmt); + while(runningThreads != numThrds){ + pthread_cond_wait(&condStarted, &thrdMgmt); + } + doRun = 1; + pthread_cond_broadcast(&condDoRun); + pthread_mutex_unlock(&thrdMgmt); +} + + +/* Wait for all traffic generators to stop. + */ +static void +waitGenerators() +{ + int i; + for(i = 0 ; i < numThrds ; ++i) { + pthread_join(instarray[i].thread, NULL); + /*printf("thread %x stopped\n", (unsigned) instarray[i].thread);*/ + } + pthread_mutex_destroy(&thrdMgmt); + pthread_cond_destroy(&condStarted); + pthread_cond_destroy(&condDoRun); +} + +/* functions related to computing statistics on the runtime of a test. This is + * a separate function primarily not to mess up the test driver. + * rgerhards, 2010-12-08 + */ +static void +endTiming(struct timeval *tvStart, struct runstats *stats) +{ + long sec, usec; + unsigned long runtime; + struct timeval tvEnd; + + gettimeofday(&tvEnd, NULL); + if(tvStart->tv_usec > tvEnd.tv_usec) { + tvEnd.tv_sec--; + tvEnd.tv_usec += 1000000; + } + + sec = tvEnd.tv_sec - tvStart->tv_sec; + usec = tvEnd.tv_usec - tvStart->tv_usec; + + runtime = sec * 1000 + (usec / 1000); + stats->totalRuntime += runtime; + if(runtime < stats->minRuntime) + stats->minRuntime = runtime; + if(runtime > stats->maxRuntime) + stats->maxRuntime = runtime; + + if(!bSilent || bStatsRecords) { + if(bCSVoutput) { + printf("%lu.%3.3ld\n", runtime / 1000, runtime % 1000); + } else { + printf("runtime: %lu.%3.3ld\n", runtime / 1000, runtime % 1000); + } + } +} + + +/* generate stats summary record at end of run + */ +static void +genStats(struct runstats *stats) +{ + long unsigned avg; + avg = stats->totalRuntime / stats->numRuns; + + if(bCSVoutput) { + printf("#numRuns,TotalRuntime,AvgRuntime,MinRuntime,MaxRuntime\n"); + printf("%d,%llu.%3.3d,%lu.%3.3lu,%lu.%3.3lu,%lu.%3.3lu\n", + stats->numRuns, + stats->totalRuntime / 1000, (int) stats->totalRuntime % 1000, + avg / 1000, avg % 1000, + stats->minRuntime / 1000, stats->minRuntime % 1000, + stats->maxRuntime / 1000, stats->maxRuntime % 1000); + } else { + printf("Runs: %d\n", stats->numRuns); + printf("Runtime:\n"); + printf(" total: %llu.%3.3d\n", stats->totalRuntime / 1000, + (int) stats->totalRuntime % 1000); + printf(" avg: %lu.%3.3lu\n", avg / 1000, avg % 1000); + printf(" min: %lu.%3.3lu\n", stats->minRuntime / 1000, stats->minRuntime % 1000); + printf(" max: %lu.%3.3lu\n", stats->maxRuntime / 1000, stats->maxRuntime % 1000); + printf("All times are wallclock time.\n"); + } +} + + +/* Run the actual test. This function handles various meta-parameters, like + * a specified number of iterations, performance measurement and so on... + * rgerhards, 2010-12-08 + */ +static int +runTests(void) +{ + struct timeval tvStart; + struct runstats stats; + int run; + + stats.totalRuntime = 0; + stats.minRuntime = 0xffffffffllu; + stats.maxRuntime = 0; + stats.numRuns = numRuns; + run = 1; + while(1) { /* loop broken inside */ + if(!bSilent) + printf("starting run %d\n", run); + prepareGenerators(); + gettimeofday(&tvStart, NULL); + runGenerators(); + waitGenerators(); + endTiming(&tvStart, &stats); + if(run == numRuns) + break; + if(!bSilent) + printf("sleeping %d seconds before next run\n", sleepBetweenRuns); + sleep(sleepBetweenRuns); + ++run; + } + + if(bStatsRecords) { + genStats(&stats); + } + + return 0; +} + +# if defined(ENABLE_OPENSSL) +/* OpenSSL implementation of TLS funtions. + * alorbach, 2018-06-11 + */ + +long BIO_debug_callback(BIO *bio, int cmd, const char __attribute__((unused)) *argp, + int argi, long __attribute__((unused)) argl, long ret) +{ + long r = 1; + + if (BIO_CB_RETURN & cmd) + r = ret; + + printf("tcpflood: openssl debugmsg: BIO[%p]: ", (void *)bio); + + switch (cmd) { + case BIO_CB_FREE: + printf("Free - %s\n", RSYSLOG_BIO_method_name(bio)); + break; +/* Disabled due API changes for OpenSSL 1.1.0+ */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + case BIO_CB_READ: + if (bio->method->type & BIO_TYPE_DESCRIPTOR) + printf("read(%d,%lu) - %s fd=%d\n", + RSYSLOG_BIO_number_read(bio), (unsigned long)argi, + RSYSLOG_BIO_method_name(bio), RSYSLOG_BIO_number_read(bio)); + else + printf("read(%d,%lu) - %s\n", + RSYSLOG_BIO_number_read(bio), (unsigned long)argi, RSYSLOG_BIO_method_name(bio)); + break; + case BIO_CB_WRITE: + if (bio->method->type & BIO_TYPE_DESCRIPTOR) + printf("write(%d,%lu) - %s fd=%d\n", + RSYSLOG_BIO_number_written(bio), (unsigned long)argi, + RSYSLOG_BIO_method_name(bio), RSYSLOG_BIO_number_written(bio)); + else + printf("write(%d,%lu) - %s\n", + RSYSLOG_BIO_number_written(bio), (unsigned long)argi, RSYSLOG_BIO_method_name(bio)); + break; +#else + case BIO_CB_READ: + printf("read %s\n", RSYSLOG_BIO_method_name(bio)); + break; + case BIO_CB_WRITE: + printf("write %s\n", RSYSLOG_BIO_method_name(bio)); + break; +#endif + case BIO_CB_PUTS: + printf("puts() - %s\n", RSYSLOG_BIO_method_name(bio)); + break; + case BIO_CB_GETS: + printf("gets(%lu) - %s\n", (unsigned long)argi, + RSYSLOG_BIO_method_name(bio)); + break; + case BIO_CB_CTRL: + printf("ctrl(%lu) - %s\n", (unsigned long)argi, + RSYSLOG_BIO_method_name(bio)); + break; + case BIO_CB_RETURN | BIO_CB_READ: + printf("read return %ld\n", ret); + break; + case BIO_CB_RETURN | BIO_CB_WRITE: + printf("write return %ld\n", ret); + break; + case BIO_CB_RETURN | BIO_CB_GETS: + printf("gets return %ld\n", ret); + break; + case BIO_CB_RETURN | BIO_CB_PUTS: + printf("puts return %ld\n", ret); + break; + case BIO_CB_RETURN | BIO_CB_CTRL: + printf("ctrl return %ld\n", ret); + break; + default: + printf("bio callback - unknown type (%d)\n", cmd); + break; + } + + return (r); +} + +void osslLastSSLErrorMsg(int ret, SSL *ssl, const char* pszCallSource) +{ + unsigned long un_error = 0; + char psz[256]; + + if (ssl == NULL) { + /* Output Error Info*/ + printf("tcpflood: Error in '%s' with ret=%d\n", pszCallSource, ret); + } else { + long iMyRet = SSL_get_error(ssl, ret); + + /* Check which kind of error we have */ + printf("tcpflood: openssl error '%s' with error code=%ld\n", pszCallSource, iMyRet); + if(iMyRet == SSL_ERROR_SYSCALL){ + iMyRet = ERR_get_error(); + if(ret == 0) { + iMyRet = SSL_get_error(ssl, iMyRet); + if(iMyRet == 0) { + *psz = '\0'; + } else { + ERR_error_string_n(iMyRet, psz, 256); + } + printf("tcpflood: Errno %d, SysErr: %s\n", errno, psz); + } + } else { + printf("tcpflood: Unknown SSL Error in '%s' (%d), SSL_get_error: %ld\n", + pszCallSource, ret, iMyRet); + } + } + + /* Loop through errors */ + while ((un_error = ERR_get_error()) > 0){ + ERR_error_string_n(un_error, psz, 256); + printf("tcpflood: %s Errorstack: %s\n", pszCallSource, psz); + } +} + +int verify_callback(int status, X509_STORE_CTX *store) +{ + char szdbgdata1[256]; + char szdbgdata2[256]; + + if(status == 0) { + printf("tcpflood: verify_callback certificate validation failed!\n"); + + X509 *cert = X509_STORE_CTX_get_current_cert(store); + int depth = X509_STORE_CTX_get_error_depth(store); + int err = X509_STORE_CTX_get_error(store); + X509_NAME_oneline(X509_get_issuer_name(cert), szdbgdata1, sizeof(szdbgdata1)); + X509_NAME_oneline(RSYSLOG_X509_NAME_oneline(cert), szdbgdata2, sizeof(szdbgdata2)); + + /* Log Warning only on EXPIRED */ + if (err == X509_V_OK || err == X509_V_ERR_CERT_HAS_EXPIRED) { + printf( + "tcpflood: Certificate warning at depth: %d \n\t" + "issuer = %s\n\t" + "subject = %s\n\t" + "err %d:%s\n", + depth, szdbgdata1, szdbgdata2, err, X509_verify_cert_error_string(err)); + + /* Set Status to OK*/ + status = 1; + } else { + printf( + "tcpflood: Certificate error at depth: %d \n\t" + "issuer = %s\n\t" + "subject = %s\n\t" + "err %d:%s\n", + depth, szdbgdata1, szdbgdata2, err, X509_verify_cert_error_string(err)); + exit(1); + } + } + return status; +} + + +/* global init OpenSSL + */ +static void +initTLS(const SSL_METHOD *method) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + /* Setup OpenSSL library < 1.1.0 */ + if( !SSL_library_init()) { +#else + /* Setup OpenSSL library >= 1.1.0 with system default settings */ + if( OPENSSL_init_ssl(0, NULL) == 0) { +#endif + printf("tcpflood: error openSSL initialization failed!\n"); + exit(1); + } + + /* Load readable error strings */ + SSL_load_error_strings(); + OpenSSL_add_ssl_algorithms(); + ERR_load_BIO_strings(); + ERR_load_crypto_strings(); + + // Create OpenSSL Context + ctx = SSL_CTX_new(method); + + if(tlsCAFile != NULL && SSL_CTX_load_verify_locations(ctx, tlsCAFile, NULL) != 1) { + printf("tcpflood: Error, Failed loading CA certificate" + " Is the file at the right path? And do we have the permissions?"); + exit(1); + } + SSL_CTX_set_ecdh_auto(ctx, 1); + if(SSL_CTX_use_certificate_chain_file(ctx, tlsCertFile) != 1) { + printf("tcpflood: error cert file could not be accessed -- have you mixed up key and certificate?\n"); + printf("If in doubt, try swapping the files in -z/-Z\n"); + printf("Certifcate is: '%s'\n", tlsCertFile); + printf("Key is: '%s'\n", tlsKeyFile); + exit(1); + } + if(SSL_CTX_use_PrivateKey_file(ctx, tlsKeyFile, SSL_FILETYPE_PEM) != 1) { + printf("tcpflood: error key file could not be accessed -- have you mixed up key and certificate?\n"); + printf("If in doubt, try swapping the files in -z/-Z\n"); + printf("Certifcate is: '%s'\n", tlsCertFile); + printf("Key is: '%s'\n", tlsKeyFile); + exit(1); + } + + /* Set CTX Options */ + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2); /* Disable insecure SSLv2 Protocol */ + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3); /* Disable insecure SSLv3 Protocol */ + SSL_CTX_sess_set_cache_size(ctx,1024); + + /* Check for Custom Config string */ + if (customConfig != NULL){ +#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER) + char *pCurrentPos; + char *pNextPos; + char *pszCmd; + char *pszValue; + int iConfErr; + + printf("tcpflood: custom config set to '%s'\n", customConfig); + + /* Set working pointer */ + pCurrentPos = (char*) customConfig; + if (strlen(pCurrentPos) > 0) { + pNextPos = index(pCurrentPos, '='); + if (pNextPos != NULL) { + pszCmd = strndup(pCurrentPos, pNextPos-pCurrentPos); + pszValue = strdup(++pNextPos); + + // Create CTX Config Helper + SSL_CONF_CTX *cctx; + cctx = SSL_CONF_CTX_new(); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SHOW_ERRORS); + SSL_CONF_CTX_set_ssl_ctx(cctx, ctx); + + /* Add SSL Conf Command */ + iConfErr = SSL_CONF_cmd(cctx, pszCmd, pszValue); + if (iConfErr > 0) { + printf("tcpflood: Successfully added Command %s:%s\n", + pszCmd, pszValue); + } + else { + printf("tcpflood: error, adding Command: %s:%s " + "in SSL_CONF_cmd with error '%d'\n", + pszCmd, pszValue, iConfErr); + osslLastSSLErrorMsg(0, NULL, "initTLS"); + } + + /* Finalize SSL Conf */ + iConfErr = SSL_CONF_CTX_finish(cctx); + if (!iConfErr) { + printf("tcpflood: error, setting openssl command parameters: %s\n", + customConfig); + } + + free(pszCmd); + free(pszValue); + } else { + printf("tcpflood: error, invalid value for -k: %s\n", customConfig); + } + } else { + printf("tcpflood: error, invalid value for -k: %s\n", customConfig); + } +#else + printf("tcpflood: TLS library does not support SSL_CONF_cmd API (maybe it is too old?)."); +#endif + } + + + /* DO ONLY SUPPORT DEFAULT CIPHERS YET + * SSL_CTX_set_cipher_list(ctx,"ALL"); Support all ciphers */ + +/* // Create Extra Length DH! + pDH = DH_new(); + if ( !DH_generate_parameters_ex(pDH, 768, DH_GENERATOR_2, NULL) ) + { + if(pDH) + DH_free(pDH); + + fprintf(stderr, "Failed to generated dynamic DH\n"); + exit(1); + } + else + { + int iErrCheck = 0; + if ( !DH_check( pDH, &iErrCheck) ) + { + fprintf(stderr, "Failed to generated dynamic DH - iErrCheck=%d\n", iErrCheck); + exit(1); + } + } +*/ + /* Set default VERIFY Options for OpenSSL CTX - and CALLBACK */ + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_callback); + + SSL_CTX_set_timeout(ctx, 30); /* Default Session Timeout, TODO: Make configureable */ + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); +} + +static void +exitTLS(void) +{ + SSL_CTX_free(ctx); + ENGINE_cleanup(); + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" +static void +initTLSSess(int i) +{ + int res; + BIO *bio_client; + SSL* pNewSsl = SSL_new(ctx); + + sslArray[i] = pNewSsl; + + if(!sslArray[i]) { + osslLastSSLErrorMsg(0, sslArray[i], "initTLSSess1"); + } + + SSL_set_verify(sslArray[i], SSL_VERIFY_NONE, verify_callback); + + /* Create BIO from socket array! */ + bio_client = BIO_new_socket(sockArray[i], BIO_CLOSE /*BIO_NOCLOSE*/); + if (bio_client == NULL) { + osslLastSSLErrorMsg(0, sslArray[i], "initTLSSess2"); + exit(1); + } else { + // printf("initTLSSess: Init client BIO[%p] done\n", (void *)bio_client); + } + + if(tlsLogLevel > 0) { + /* Set debug Callback for client BIO as well! */ + BIO_set_callback(bio_client, BIO_debug_callback); + } + + /* Blocking socket */ + BIO_set_nbio( bio_client, 0 ); + SSL_set_bio(sslArray[i], bio_client, bio_client); + SSL_set_connect_state(sslArray[i]); /*sets ssl to work in client mode.*/ + + /* Perform the TLS handshake */ + if((res = SSL_do_handshake(sslArray[i])) <= 0) { + osslLastSSLErrorMsg(res, sslArray[i], "initTLSSess3"); + exit(1); + } +} +#pragma GCC diagnostic pop + + +static int +sendTLS(int i, char *buf, size_t lenBuf) +{ + size_t lenSent; + int r, err; + + lenSent = 0; + while(lenSent != lenBuf) { + r = SSL_write(sslArray[i], buf + lenSent, lenBuf - lenSent); + if(r > 0) { + lenSent += r; + } else { + err = SSL_get_error(sslArray[i], r); + if(err != SSL_ERROR_ZERO_RETURN && err != SSL_ERROR_WANT_READ && + err != SSL_ERROR_WANT_WRITE) { + /*SSL_ERROR_ZERO_RETURN: TLS connection has been closed. This + * result code is returned only if a closure alert has occurred + * in the protocol, i.e. if the connection has been closed cleanly. + *SSL_ERROR_WANT_READ/WRITE: The operation did not complete, try + * again later. */ + printf("Error while sending data: [%d] %s", err, ERR_error_string(err, NULL)); + printf("Error is: %s", ERR_reason_error_string(err)); + } else { + /* Check for SSL Shutdown */ + if (SSL_get_shutdown(sslArray[i]) == SSL_RECEIVED_SHUTDOWN) { + printf("received SSL_RECEIVED_SHUTDOWN!\n"); + } else { + printf("[ERROR] while sending data: [%d] %s", err, ERR_error_string(err, NULL)); + printf("[ERROR] Reason: %s", ERR_reason_error_string(err)); + } + } + exit(1); + } + } + + return lenSent; +} + +static void +closeTLSSess(int i) +{ + int r; + r = SSL_shutdown(sslArray[i]); + if (r <= 0){ + /* Shutdown not finished, call SSL_read to do a bidirectional shutdown, see doc for more: + * https://www.openssl.org/docs/man1.1.1/man3/SSL_shutdown.html + */ + char rcvBuf[MAX_RCVBUF]; + SSL_read(sslArray[i], rcvBuf, MAX_RCVBUF); + + } + SSL_free(sslArray[i]); +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" +static void +initDTLSSess() +{ + int res; + BIO *bio_client; + + // Create new SSL + SSL* pNewSsl = SSL_new(ctx); + + // set to array variables + sslArray[0] = pNewSsl; + sockArray[0] = udpsockout; + + if(!sslArray[0]) { + fprintf(stderr, "Unable to create SSL\n"); + osslLastSSLErrorMsg(0, sslArray[0], "initDTLSSess1"); + exit(1); + } + + SSL_set_verify(sslArray[0], SSL_VERIFY_NONE, verify_callback); + + /* Create BIO from socket array! */ + bio_client = BIO_new_dgram(udpsockout, BIO_NOCLOSE); + if (!bio_client) { + fprintf(stderr, "Unable to create BIO\n"); + osslLastSSLErrorMsg(0, sslArray[0], "initDTLSSess2"); + exit(1); + } + BIO_ctrl(bio_client, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &dtls_client_addr); + SSL_set_bio(sslArray[0], bio_client, bio_client); + + + if(tlsLogLevel > 0) { + /* Set debug Callback for client BIO as well! */ + BIO_set_callback(bio_client, BIO_debug_callback); + } + + /* Blocking socket */ +// BIO_set_nbio( bio_client, 0 ); +// SSL_set_bio(sslArray[0], bio_client, bio_client); +// SSL_set_connect_state(sslArray[0]); /*sets ssl to work in client mode.*/ + + printf("[DEBUG] Starting DTLS session ...\n"); + /* Perform handshake */ + if (SSL_connect(sslArray[0]) <= 0) { + fprintf(stderr, "SSL_connect failed\n"); + osslLastSSLErrorMsg(0, sslArray[0], "initDTLSSess3"); + exit(1); + } + + // Print Cipher info + const SSL_CIPHER *cipher = SSL_get_current_cipher(sslArray[0]); + if(tlsLogLevel > 0) { + printf("[DEBUG] Cipher used: %s\n", SSL_CIPHER_get_name(cipher)); + } + + // Print Peer Certificate info + if(tlsLogLevel > 0) { + X509 *cert = SSL_get_peer_certificate(sslArray[0]); + if (cert != NULL) { + char *line; + line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); + printf("[DEBUG] Subject: %s\n", line); + OPENSSL_free(line); + + line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); + printf("[DEBUG] Issuer: %s\n", line); + OPENSSL_free(line); + X509_free(cert); + } else { + printf("[DEBUG] No certificates.\n"); + } + } + + /* Set and activate timeouts */ + struct timeval timeout; + timeout.tv_sec = 3; + timeout.tv_usec = 0; + BIO_ctrl(bio_client, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); +} +#pragma GCC diagnostic pop + + +static int +sendDTLS(char *buf, size_t lenBuf) +{ + size_t lenSent; + int r, err; + + lenSent = 0; + r = SSL_write(sslArray[0], buf + lenSent, lenBuf - lenSent); + if(r > 0) { + lenSent += r; + } else { + err = SSL_get_error(sslArray[0], r); + int err = SSL_get_error(sslArray[0], r); + switch(err) { + case SSL_ERROR_SYSCALL: + printf("[ERROR] SSL_write (SSL_ERROR_SYSCALL): %s\n", strerror(errno)); + break; + default: + printf("[ERROR] while sending data: [%d] %s", err, ERR_error_string(err, NULL)); + printf("[ERROR] Reason: %s", ERR_reason_error_string(err)); + } + exit(1); + } + return lenSent; +} + +static void +closeDTLSSess() +{ + printf("closeDTLSSess ENTER\n"); + + int r; + r = SSL_shutdown(sslArray[0]); + if (r <= 0){ + /* Shutdown not finished, call SSL_read to do a bidirectional shutdown, see doc for more: + * https://www.openssl.org/docs/man1.1.1/man3/SSL_shutdown.html + */ + char rcvBuf[MAX_RCVBUF]; + SSL_read(sslArray[0], rcvBuf, MAX_RCVBUF); + + } + SSL_free(sslArray[0]); + close(udpsockout); + close(udpsockin); + + printf("closeDTLSSess EXIT\n"); +} + +# elif defined(ENABLE_GNUTLS) +/* This defines a log function to be provided to GnuTLS. It hopefully + * helps us track down hard to find problems. + * rgerhards, 2008-06-20 + */ +static void tlsLogFunction(int level, const char *msg) +{ + printf("GnuTLS (level %d): %s", level, msg); +} + +static void +exitTLS(void) +{ +} + +/* global init GnuTLS + */ +static void +initTLS(void) +{ + int r; + + /* order of gcry_control and gnutls_global_init matters! */ + #if GNUTLS_VERSION_NUMBER <= 0x020b00 + gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); + #endif + gnutls_global_init(); + /* set debug mode, if so required by the options */ + if(tlsLogLevel > 0) { + gnutls_global_set_log_function(tlsLogFunction); + gnutls_global_set_log_level(tlsLogLevel); + } + + r = gnutls_certificate_allocate_credentials(&tlscred); + if(r != GNUTLS_E_SUCCESS) { + printf("error allocating credentials\n"); + gnutls_perror(r); + exit(1); + } + r = gnutls_certificate_set_x509_key_file(tlscred, tlsCertFile, tlsKeyFile, GNUTLS_X509_FMT_PEM); + if(r != GNUTLS_E_SUCCESS) { + printf("error setting certificate files -- have you mixed up key and certificate?\n"); + printf("If in doubt, try swapping the files in -z/-Z\n"); + printf("Certifcate is: '%s'\n", tlsCertFile); + printf("Key is: '%s'\n", tlsKeyFile); + gnutls_perror(r); + r = gnutls_certificate_set_x509_key_file(tlscred, tlsKeyFile, tlsCertFile, + GNUTLS_X509_FMT_PEM); + if(r == GNUTLS_E_SUCCESS) { + printf("Tried swapping files, this seems to work " + "(but results may be unpredictable!)\n"); + } else { + exit(1); + } + } +} + + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" +static void +initTLSSess(int i) +{ + int r; + gnutls_init(sessArray + i, GNUTLS_CLIENT); + + /* Use default priorities */ + gnutls_set_default_priority(sessArray[i]); + + /* put our credentials to the current session */ + r = gnutls_credentials_set(sessArray[i], GNUTLS_CRD_CERTIFICATE, tlscred); + if(r != GNUTLS_E_SUCCESS) { + fprintf (stderr, "Setting credentials failed\n"); + gnutls_perror(r); + exit(1); + } + + /* NOTE: the following statement generates a cast warning, but there seems to + * be no way around it with current GnuTLS. Do NOT try to "fix" the situation! + */ + gnutls_transport_set_ptr(sessArray[i], (gnutls_transport_ptr_t) sockArray[i]); + + /* Perform the TLS handshake */ + r = gnutls_handshake(sessArray[i]); + if(r < 0) { + fprintf (stderr, "TLS Handshake failed\n"); + gnutls_perror(r); + exit(1); + } +} +#pragma GCC diagnostic pop + + +static int +sendTLS(int i, char *buf, size_t lenBuf) +{ + int lenSent; + int r; + + lenSent = 0; + while(lenSent != lenBuf) { + r = gnutls_record_send(sessArray[i], buf + lenSent, lenBuf - lenSent); + if(r < 0) + break; + lenSent += r; + } + + return lenSent; +} + +static void +closeTLSSess(int i) +{ + gnutls_bye(sessArray[i], GNUTLS_SHUT_RDWR); + gnutls_deinit(sessArray[i]); +} +# else /* NO TLS available */ +static void initTLS(void) {} +static void exitTLS(void) {} +static void initTLSSess(int __attribute__((unused)) i) {} +static int sendTLS(int __attribute__((unused)) i, char __attribute__((unused)) *buf, + size_t __attribute__((unused)) lenBuf) { return 0; } +static void closeTLSSess(int __attribute__((unused)) i) {} + +static void initDTLSSess(void) {} +static int sendDTLS(char *buf, size_t lenBuf) {} +static void closeDTLSSess(void) {} +# endif + +static void +setTargetPorts(const char *const port_arg) +{ + int i = 0; + + char *saveptr; + char *ports = strdup(port_arg); + char *port = strtok_r(ports, ":", &saveptr); + while(port != NULL) { + if(i == sizeof(targetPort)/sizeof(int)) { + fprintf(stderr, "too many ports specified, max %d\n", + (int) (sizeof(targetPort)/sizeof(int))); + exit(1); + } + targetPort[i] = atoi(port); + i++; + port = strtok_r(NULL, ":", &saveptr); + } + free(ports); +} + + +/* Run the test. + * rgerhards, 2009-04-03 + */ +int main(int argc, char *argv[]) +{ + int ret = 0; + int opt; + struct sigaction sigAct; + struct rlimit maxFiles; + static char buf[1024]; + + srand(time(NULL)); /* seed is good enough for our needs */ + + /* on Solaris, we do not HAVE MSG_NOSIGNAL, so for this reason + * we block SIGPIPE (not an issue for this program) + */ + memset(&sigAct, 0, sizeof(sigAct)); + sigemptyset(&sigAct.sa_mask); + sigAct.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sigAct, NULL); + + setvbuf(stdout, buf, _IONBF, 48); + + while((opt = getopt(argc, argv, "a:ABb:c:C:d:DeE:f:F:i:I:j:k:l:L:m:M:n:OP:p:rR:sS:t:T:u:vW:x:XyYz:Z:")) != -1) { + switch (opt) { + case 'b': batchsize = atoll(optarg); + break; + case 't': targetIP = optarg; + break; + case 'p': setTargetPorts(optarg); + break; + case 'n': numTargetPorts = atoi(optarg); + break; + case 'c': numConnections = atoi(optarg); + if(numConnections < 0) { + numConnections *= -1; + softLimitConnections = 1; + } + break; + case 'C': numFileIterations = atoi(optarg); + break; + case 'm': numMsgsToSend = atoi(optarg); + break; + case 'i': msgNum = atoi(optarg); + break; + case 'P': msgPRI = optarg; + break; + case 'j': jsonCookie = optarg; + break; + case 'd': extraDataLen = atoi(optarg); + if(extraDataLen > MAX_EXTRADATA_LEN) { + fprintf(stderr, "-d max is %d!\n", + MAX_EXTRADATA_LEN); + exit(1); + } + break; + case 'D': bRandConnDrop = 1; + break; + case 'l': + dbRandConnDrop = atof(optarg); + printf("RandConnDrop Level: '%lf' \n", dbRandConnDrop); + break; + case 'r': bRandomizeExtraData = 1; + break; + case 'f': dynFileIDs = atoi(optarg); + break; + case 'F': frameDelim = atoi(optarg); + break; + case 'L': tlsLogLevel = atoi(optarg); + break; + case 'M': MsgToSend = optarg; + break; + case 'I': dataFile = optarg; + /* in this mode, we do not know the num messages to send, so + * we set a (high) number to keep the code happy. + */ + numMsgsToSend = 1000000; + break; + case 's': bSilent = 1; + break; + case 'B': bBinaryFile = 1; + break; + case 'R': numRuns = atoi(optarg); + break; + case 'S': sleepBetweenRuns = atoi(optarg); + break; + case 'X': bStatsRecords = 1; + break; + case 'e': bCSVoutput = 1; + break; + case 'T': if(!strcmp(optarg, "udp")) { + transport = TP_UDP; + } else if(!strcmp(optarg, "tcp")) { + transport = TP_TCP; + } else if(!strcmp(optarg, "tls")) { +# if defined(ENABLE_OPENSSL) + transport = TP_TLS; +# elif defined(ENABLE_GNUTLS) + transport = TP_TLS; +# else + fprintf(stderr, "compiled without gnutls/openssl TLS support: " + "\"-Ttls\" not supported!\n"); + exit(1); +# endif + } else if(!strcmp(optarg, "relp-plain")) { +# if defined(ENABLE_RELP) + transport = TP_RELP_PLAIN; +# else + fprintf(stderr, "compiled without RELP support: " + "\"-Trelp-plain\" not supported!\n" + "(add --enable-relp to ./configure options " + "if desired)\n"); + exit(1); +# endif + } else if(!strcmp(optarg, "relp-tls")) { +# if defined(ENABLE_RELP) + transport = TP_RELP_TLS; +# else + fprintf(stderr, "compiled without RELP support: " + "\"-Trelp-tls\" not supported!\n" + "(add --enable-relp to ./configure options " + "if desired)\n"); + exit(1); +# endif + } else if(!strcmp(optarg, "dtls")) { +# if defined(ENABLE_OPENSSL) + transport = TP_DTLS; +# else + fprintf(stderr, "compiled without openssl TLS support: " + "\"-Tdtls\" not supported!\n"); + exit(1); +# endif + } else { + fprintf(stderr, "unknown transport '%s'\n", optarg); + exit(1); + } + break; + case 'a': relpAuthMode = optarg; + break; + case 'A': abortOnSendFail = 0; + break; + case 'E': relpPermittedPeer = optarg; + break; + case 'u': +#if defined(HAVE_RELPENGINESETTLSLIBBYNAME) + relpTlsLib = optarg; +#endif + break; + case 'W': waittime = atoi(optarg); + break; + case 'Y': runMultithreaded = 1; + break; + case 'y': useRFC5424Format = 1; + break; + case 'x': tlsCAFile = optarg; + break; + case 'z': tlsKeyFile = optarg; + break; + case 'Z': tlsCertFile = optarg; + break; + case 'O': octateCountFramed = 1; + break; + case 'v': verbose = 1; + break; + case 'k': customConfig = optarg; + break; + default: printf("invalid option '%c' or value missing - terminating...\n", opt); + exit (1); + break; + } + } + + const char *const ci_env = getenv("CI"); + if(ci_env != NULL && !strcmp(ci_env, "true")) { + bSilent = 1; /* auto-apply silent option during CI runs */ + } + + if(tlsCAFile != NULL && transport != TP_RELP_TLS) { + #if !defined(ENABLE_OPENSSL) + fprintf(stderr, "-x CAFile not supported in GnuTLS mode - ignored.\n" + "Note: we do NOT VERIFY the remote peer when compiled for GnuTLS.\n" + "When compiled for OpenSSL, we do.\n"); + #endif + } + + if(bStatsRecords && waittime) { + fprintf(stderr, "warning: generating performance stats and using a waittime " + "is somewhat contradictory!\n"); + } + + if(!isatty(1) || bSilent) + bShowProgress = 0; + + if(numConnections > 20) { + /* if we use many (whatever this means, 20 is randomly picked) + * connections, we need to make sure we have a high enough + * limit. -- rgerhards, 2010-03-25 + */ + maxFiles.rlim_cur = numConnections + 20; + maxFiles.rlim_max = numConnections + 20; + if(setrlimit(RLIMIT_NOFILE, &maxFiles) < 0) { + perror("setrlimit to increase file handles failed"); + fprintf(stderr, + "could not set sufficiently large number of " + "open files for required connection count!\n"); + if(!softLimitConnections) { + exit(1); + } + } + } + + if(dataFile != NULL) { + if((dataFP = fopen(dataFile, "r")) == NULL) { + perror(dataFile); + exit(1); + } + } + + if(tlsKeyFile != NULL || tlsCertFile != NULL) { + if( transport != TP_TLS && + transport != TP_DTLS && + transport != TP_RELP_TLS) { + printf("error: TLS certificates were specified, but TLS is NOT enabled: " + "To enable TLS use parameter -Ttls\n"); + exit(1); + } + } + + if(transport == TP_TLS) { + if(tlsKeyFile == NULL || tlsCertFile == NULL) { + printf("error: transport TLS is specified (-Ttls), -z and -Z must also " + "be specified\n"); + exit(1); + } + /* Create main CTX Object. Use SSLv23_method for < Openssl 1.1.0 and TLS_method for newer versions! */ +#if defined(ENABLE_OPENSSL) +# if OPENSSL_VERSION_NUMBER < 0x10100000L + initTLS(SSLv23_method()); +# else + initTLS(TLS_method()); +# endif +#else + initTLS(); +#endif + } else if(transport == TP_DTLS) { + if(tlsKeyFile == NULL || tlsCertFile == NULL) { + printf("error: transport DTLS is specified (-Tdtls), -z and -Z must also " + "be specified\n"); + exit(1); + } +#if defined(ENABLE_OPENSSL) + initTLS(DTLS_client_method()); +#else + printf("error: transport DTLS is specified (-Tdtls) but not supported in GnuTLS driver\n"); + exit(1); +#endif + } else if(transport == TP_RELP_PLAIN || transport == TP_RELP_TLS) { + #ifdef ENABLE_RELP + initRELP_PLAIN(); + #endif + } + + if(openConnections() != 0) { + printf("error opening connections\n"); + exit(1); + } + + if(runTests() != 0) { + printf("error running tests\n"); + exit(1); + } + + closeConnections(); /* this is important so that we do not finish too early! */ + + #ifdef ENABLE_RELP + if(transport == TP_RELP_PLAIN || transport == TP_RELP_TLS) { + CHKRELP(relpEngineDestruct(&pRelpEngine)); + } + #endif + + if(nConnDrops > 0 && !bSilent) + printf("-D option initiated %ld connection closures\n", nConnDrops); + + if(!bSilent) + printf("End of tcpflood Run\n"); + + if(transport == TP_TLS) { + exitTLS(); + } + + exit(ret); +} |