/* 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 . * * A copy of the GPL can be found in the file "COPYING" in this distribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_RELP #include #endif #include #include #include #ifdef ENABLE_GNUTLS # include # if GNUTLS_VERSION_NUMBER <= 0x020b00 # include GCRY_THREAD_OPTION_PTHREAD_IMPL; # endif #endif #ifdef ENABLE_OPENSSL #include #include #include #include /* 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:: 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); }