From 51faa97c5422dd2ad3e159739b74af4bc6b78606 Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Tue, 25 Nov 2025 14:04:10 -0700 Subject: [PATCH] add some DTLS benchmarking examples --- dtls/benchmark-client-dtls13.c | 399 +++++++++++++++++++++++++++++++++ dtls/benchmark-client-udp.c | 240 ++++++++++++++++++++ dtls/benchmark-server-dtls13.c | 295 ++++++++++++++++++++++++ dtls/benchmark-server-udp.c | 154 +++++++++++++ 4 files changed, 1088 insertions(+) create mode 100644 dtls/benchmark-client-dtls13.c create mode 100644 dtls/benchmark-client-udp.c create mode 100644 dtls/benchmark-server-dtls13.c create mode 100644 dtls/benchmark-server-udp.c diff --git a/dtls/benchmark-client-dtls13.c b/dtls/benchmark-client-dtls13.c new file mode 100644 index 00000000..25975b40 --- /dev/null +++ b/dtls/benchmark-client-dtls13.c @@ -0,0 +1,399 @@ +/* + * benchmark-client-dtls13.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define USE_CERT_BUFFERS_256 +#include + +#define SERV_PORT 11111 +#define PACKET_SIZE 15360 /* 15KB = 15 * 1024 */ +#define DEFAULT_MAX_TRIES 100 +#define SEND_INTERVAL_US 2000 /* 2ms = 2000 microseconds */ +#define DTLS_MAX_RECORD_SIZE 1300 /* Very small size to avoid DTLS datagram limit */ + +/* Get current time in microseconds */ +static long long get_time_us(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (long long)tv.tv_sec * 1000000 + tv.tv_usec; +} + +/* Write data in chunks to fit DTLS record size limits */ +/* Loop over wolfSSL_write to send the 15KB packet in chunks */ +static int dtls_write_chunked(WOLFSSL* ssl, const unsigned char* buf, int len) +{ + int total_written = 0; + int remaining = len; + const unsigned char* ptr = buf; + + while (remaining > 0) { + int chunk_size = (remaining > DTLS_MAX_RECORD_SIZE) ? DTLS_MAX_RECORD_SIZE : remaining; + int written = wolfSSL_write(ssl, ptr, chunk_size); + + if (written < 0) { + return written; /* Error */ + } + + if (written == 0) { + /* Should not happen with blocking socket, but handle it */ + break; + } + + total_written += written; + remaining -= written; + ptr += written; + } + + return total_written; +} + +/* Read data in chunks to handle DTLS record size limits */ +static int dtls_read_chunked(WOLFSSL* ssl, unsigned char* buf, int len) +{ + int total_read = 0; + int remaining = len; + unsigned char* ptr = buf; + + while (remaining > 0) { + int chunk_size = (remaining > DTLS_MAX_RECORD_SIZE) ? DTLS_MAX_RECORD_SIZE : remaining; + int read = wolfSSL_read(ssl, ptr, chunk_size); + + if (read <= 0) { + return read; /* Error or EOF */ + } + + total_read += read; + remaining -= read; + ptr += read; + } + + return total_read; +} + +/* Run a single benchmark with a specific cipher suite */ +static int run_benchmark(const char* server_ip, int max_tries, + const char* cipher_suite, const char* cipher_name, + int curve_id, int use_ecdsa_cert) +{ + (void)use_ecdsa_cert; /* Reserved for future ECDSA certificate support */ + int sockfd = INVALID_SOCKET; + int err; + int ret; + int exitVal = 1; + struct sockaddr_in servAddr; + WOLFSSL* ssl = NULL; + WOLFSSL_CTX* ctx = NULL; + unsigned char sendBuf[PACKET_SIZE]; + unsigned char recvBuf[PACKET_SIZE]; + long long total_send_time = 0; + long long total_recv_time = 0; + long long connection_time = 0; + int successful_sends = 0; + int successful_recvs = 0; + int send_errors = 0; + int recv_errors = 0; + int size_mismatches = 0; + long long send_start, send_end, recv_start, recv_end; + long long conn_start, conn_end; + int recvlen; + int first_send_error = 0; + int first_recv_error = 0; + + if ((ctx = wolfSSL_CTX_new( +#ifdef WOLFSSL_DTLS13 + wolfDTLSv1_3_client_method() +#else + wolfDTLSv1_2_client_method() +#endif + )) == NULL) { + fprintf(stderr, "wolfSSL_CTX_new error.\n"); + goto cleanup; + } + + /* Set cipher suite */ + if (wolfSSL_CTX_set_cipher_list(ctx, cipher_suite) != WOLFSSL_SUCCESS) { + fprintf(stderr, "Error setting cipher suite: %s\n", cipher_suite); + goto cleanup; + } + + /* Set supported groups (key exchange) */ + if (wolfSSL_CTX_UseSupportedCurve(ctx, curve_id) != WOLFSSL_SUCCESS) { + fprintf(stderr, "Error setting supported curve\n"); + goto cleanup; + } + + /* Load CA certificate from memory buffer */ + if (wolfSSL_CTX_load_verify_buffer(ctx, ca_ecc_cert_der_256, sizeof_ca_ecc_cert_der_256, + WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) { + fprintf(stderr, "Error loading CA certificate from buffer.\n"); + goto cleanup; + } + + /* Assign ssl variable */ + ssl = wolfSSL_new(ctx); + if (ssl == NULL) { + fprintf(stderr, "unable to get ssl object\n"); + goto cleanup; + } + + /* servAddr setup */ + memset(&servAddr, 0, sizeof(servAddr)); + servAddr.sin_family = AF_INET; + servAddr.sin_port = htons(SERV_PORT); + if (inet_pton(AF_INET, server_ip, &servAddr.sin_addr) < 1) { + perror("inet_pton()"); + goto cleanup; + } + + if (wolfSSL_dtls_set_peer(ssl, &servAddr, sizeof(servAddr)) + != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_dtls_set_peer failed\n"); + goto cleanup; + } + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + perror("socket()"); + goto cleanup; + } + + /* Increase socket buffer sizes to handle 15KB packets */ + int buffer_size = PACKET_SIZE * 10; /* 10x packet size for buffer */ + if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &buffer_size, sizeof(buffer_size)) < 0) { + perror("setsockopt SO_SNDBUF failed"); + } + if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &buffer_size, sizeof(buffer_size)) < 0) { + perror("setsockopt SO_RCVBUF failed"); + } + + /* Set the file descriptor for ssl */ + if (wolfSSL_set_fd(ssl, sockfd) != WOLFSSL_SUCCESS) { + fprintf(stderr, "cannot set socket file descriptor\n"); + goto cleanup; + } + + /* Measure connection establishment time (handshake) */ + conn_start = get_time_us(); + if (wolfSSL_connect(ssl) != WOLFSSL_SUCCESS) { + err = wolfSSL_get_error(ssl, 0); + fprintf(stderr, "err = %d, %s\n", err, wolfSSL_ERR_reason_error_string(err)); + fprintf(stderr, "wolfSSL_connect failed\n"); + goto cleanup; + } + conn_end = get_time_us(); + connection_time = conn_end - conn_start; + + /* Initialize send buffer with some data */ + memset(sendBuf, 0xAA, PACKET_SIZE); + + printf("\n=== Testing Cipher: %s ===\n", cipher_name); + printf("Keyshare: %s\n", + curve_id == WOLFSSL_ECC_SECP256R1 ? "secp256r1" : + curve_id == WOLFSSL_ECC_SECP384R1 ? "secp384r1" : + curve_id == WOLFSSL_ECC_SECP521R1 ? "secp521r1" : + curve_id == WOLFSSL_ECC_X25519 ? "x25519" : + curve_id == WOLFSSL_ECC_X448 ? "x448" : "unknown"); + + /* Send and receive packets */ + for (int i = 0; i < max_tries; i++) { + /* Measure send time - no printf/perror in this section */ + send_start = get_time_us(); + ret = dtls_write_chunked(ssl, sendBuf, PACKET_SIZE); + send_end = get_time_us(); + + if (ret != PACKET_SIZE) { + if (ret < 0) { + send_errors++; + if (first_send_error == 0) { + first_send_error = wolfSSL_get_error(ssl, 0); + } + } else { + size_mismatches++; + } + continue; + } + total_send_time += (send_end - send_start); + successful_sends++; + + /* Measure receive time - no printf/perror in this section */ + recv_start = get_time_us(); + recvlen = dtls_read_chunked(ssl, recvBuf, PACKET_SIZE); + recv_end = get_time_us(); + + if (recvlen < 0) { + recv_errors++; + if (first_recv_error == 0) { + first_recv_error = wolfSSL_get_error(ssl, 0); + } + } else if (recvlen == PACKET_SIZE) { + total_recv_time += (recv_end - recv_start); + successful_recvs++; + } else { + size_mismatches++; + } + + /* Wait 2ms before next send (except for the last iteration) */ + if (i < max_tries - 1) { + usleep(SEND_INTERVAL_US); + } + } + + /* Calculate and print averages - all output after benchmark */ + printf("Connection establishment time: %.3f microseconds (%.3f ms)\n", + (double)connection_time, (double)connection_time / 1000.0); + printf("Total packets sent: %d\n", successful_sends); + printf("Total packets received: %d\n", successful_recvs); + + if (send_errors > 0) { + printf("Send errors: %d", send_errors); + if (first_send_error != 0) { + printf(" (first error: %d - %s)", first_send_error, + wolfSSL_ERR_reason_error_string(first_send_error)); + } + printf("\n"); + } + if (recv_errors > 0) { + printf("Receive errors: %d", recv_errors); + if (first_recv_error != 0) { + printf(" (first error: %d - %s)", first_recv_error, + wolfSSL_ERR_reason_error_string(first_recv_error)); + } + printf("\n"); + } + if (size_mismatches > 0) { + printf("Size mismatches: %d\n", size_mismatches); + } + + if (successful_sends > 0) { + double avg_send_us = (double)total_send_time / successful_sends; + printf("Average send time: %.3f microseconds (%.3f ms)\n", + avg_send_us, avg_send_us / 1000.0); + } else { + printf("No successful sends to calculate average\n"); + } + + if (successful_recvs > 0) { + double avg_recv_us = (double)total_recv_time / successful_recvs; + printf("Average receive time: %.3f microseconds (%.3f ms)\n", + avg_recv_us, avg_recv_us / 1000.0); + } else { + printf("No successful receives to calculate average\n"); + } + + exitVal = 0; +cleanup: + if (ssl != NULL) { + /* Attempt a full shutdown */ + ret = wolfSSL_shutdown(ssl); + if (ret == WOLFSSL_SHUTDOWN_NOT_DONE) + ret = wolfSSL_shutdown(ssl); + if (ret != WOLFSSL_SUCCESS) { + err = wolfSSL_get_error(ssl, 0); + fprintf(stderr, "err = %d, %s\n", err, + wolfSSL_ERR_reason_error_string(err)); + fprintf(stderr, "wolfSSL_shutdown failed\n"); + } + wolfSSL_free(ssl); + } + if (sockfd != INVALID_SOCKET) + close(sockfd); + if (ctx != NULL) + wolfSSL_CTX_free(ctx); + + return exitVal; +} + +int main(int argc, char** argv) +{ + int exitVal = 1; + int max_tries = DEFAULT_MAX_TRIES; + int curve_id = WOLFSSL_ECC_SECP256R1; /* Default ECC curve */ + + /* Define all cipher suites to test */ + const char* ciphers[] = { + "TLS13-AES128-GCM-SHA256", + "TLS13-AES256-GCM-SHA384", + "TLS13-CHACHA20-POLY1305-SHA256" + }; + const char* cipher_names[] = { + "AES128-GCM", + "AES256-GCM", + "ChaCha20-Poly1305" + }; + int num_ciphers = 3; + + /* Program argument checking */ + if (argc < 2 || argc > 3) { + fprintf(stderr, "usage: %s [max_tries]\n", argv[0]); + fprintf(stderr, " max_tries defaults to %d\n", DEFAULT_MAX_TRIES); + fprintf(stderr, "\nNote: Will automatically test all three ciphers: AES128, AES256, ChaCha20\n"); + return exitVal; + } + + if (argc >= 3) { + max_tries = atoi(argv[2]); + if (max_tries <= 0) { + fprintf(stderr, "Error: max_tries must be positive\n"); + return exitVal; + } + } + + + /* Initialize wolfSSL */ + if (wolfSSL_Init() != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_Init error.\n"); + return exitVal; + } + + printf("Starting DTLS 1.3 benchmark: %d packets of %d bytes, %d us interval\n", + max_tries, PACKET_SIZE, SEND_INTERVAL_US); + printf("Server: %s:%d\n", argv[1], SERV_PORT); + printf("Testing all three cipher suites automatically...\n\n"); + + /* Run benchmark for each cipher suite */ + for (int i = 0; i < num_ciphers; i++) { + if (run_benchmark(argv[1], max_tries, ciphers[i], cipher_names[i], + curve_id, 1) != 0) { + fprintf(stderr, "Benchmark failed for %s\n", cipher_names[i]); + exitVal = 1; + } + } + + printf("\n=== All benchmarks completed ===\n"); + wolfSSL_Cleanup(); + return 0; +} diff --git a/dtls/benchmark-client-udp.c b/dtls/benchmark-client-udp.c new file mode 100644 index 00000000..590add78 --- /dev/null +++ b/dtls/benchmark-client-udp.c @@ -0,0 +1,240 @@ +/* + * benchmark-client-udp.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PACKET_SIZE 15360 /* 15KB = 15 * 1024 */ +#define SERV_PORT 11111 +#define DEFAULT_MAX_TRIES 100 +#define SEND_INTERVAL_US 2000 /* 2ms = 2000 microseconds */ +#define MAX_CHUNK_SIZE 1300 /* Match DTLS chunk size for fair comparison */ + +/* Get current time in microseconds */ +static long long get_time_us(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (long long)tv.tv_sec * 1000000 + tv.tv_usec; +} + +/* Send data in chunks to match DTLS behavior */ +static int udp_send_chunked(int sockfd, const unsigned char* buf, int len, + const struct sockaddr* dest_addr, socklen_t addrlen) +{ + int total_sent = 0; + int remaining = len; + const unsigned char* ptr = buf; + + while (remaining > 0) { + int chunk_size; + int sent; + + chunk_size = (remaining > MAX_CHUNK_SIZE) ? MAX_CHUNK_SIZE : + remaining; + sent = sendto(sockfd, ptr, chunk_size, 0, dest_addr, addrlen); + + if (sent < 0) { + return sent; + } + + total_sent += sent; + remaining -= sent; + ptr += sent; + } + + return total_sent; +} + +/* Receive data in chunks to match DTLS behavior */ +static int udp_recv_chunked(int sockfd, unsigned char* buf, int len) +{ + int total_received = 0; + int remaining = len; + unsigned char* ptr = buf; + + while (remaining > 0) { + int chunk_size; + int received; + + chunk_size = (remaining > MAX_CHUNK_SIZE) ? MAX_CHUNK_SIZE : remaining; + received = recvfrom(sockfd, ptr, chunk_size, 0, NULL, NULL); + + if (received < 0) { + return received; + } + + /* Connection closed */ + if (received == 0) { + break; + } + + total_received += received; + remaining -= received; + ptr += received; + } + + return total_received; +} + +int main(int argc, char** argv) +{ + int sockfd; + int recvlen; + int ret; + struct sockaddr_in servAddr; + const struct sockaddr* servAddr_in; + socklen_t servLen; + unsigned char sendBuf[PACKET_SIZE]; + unsigned char recvBuf[PACKET_SIZE]; + int max_tries = DEFAULT_MAX_TRIES; + long long total_send_time = 0; + long long total_recv_time = 0; + int successful_sends = 0; + int successful_recvs = 0; + int send_errors = 0; + int recv_errors = 0; + int size_mismatches = 0; + long long send_start, send_end, recv_start, recv_end; + + if (argc < 2 || argc > 3) { + printf("usage: %s [max_tries]\n", argv[0]); + printf(" max_tries defaults to %d\n", DEFAULT_MAX_TRIES); + return 1; + } + + if (argc == 3) { + max_tries = atoi(argv[2]); + if (max_tries <= 0) { + printf("Error: max_tries must be positive\n"); + return 1; + } + } + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("cannot create socket"); + return 1; + } + + memset(&servAddr, 0, sizeof(servAddr)); + servAddr.sin_family = AF_INET; + servAddr.sin_port = htons(SERV_PORT); + if (inet_pton(AF_INET, argv[1], &servAddr.sin_addr) <= 0) { + printf("Error: invalid IP address\n"); + return 1; + } + + servAddr_in = (struct sockaddr*) &servAddr; + servLen = sizeof(servAddr); + + /* Initialize send buffer with some data */ + memset(sendBuf, 0xAA, PACKET_SIZE); + + printf("Starting UDP benchmark: %d packets of %d bytes, %d us interval\n", + max_tries, PACKET_SIZE, SEND_INTERVAL_US); + printf("Server: %s:%d\n\n", argv[1], SERV_PORT); + + /* Send and receive packets */ + for (int i = 0; i < max_tries; i++) { + /* Measure send time - no printf/perror in this section */ + send_start = get_time_us(); + ret = udp_send_chunked(sockfd, sendBuf, PACKET_SIZE, servAddr_in, + servLen); + send_end = get_time_us(); + + if (ret != PACKET_SIZE) { + if (ret < 0) { + send_errors++; + } else { + size_mismatches++; + } + continue; + } + total_send_time += (send_end - send_start); + successful_sends++; + + /* Measure receive time - no printf/perror in this section */ + recv_start = get_time_us(); + recvlen = udp_recv_chunked(sockfd, recvBuf, PACKET_SIZE); + recv_end = get_time_us(); + + if (recvlen < 0) { + recv_errors++; + } + else if (recvlen == PACKET_SIZE) { + total_recv_time += (recv_end - recv_start); + successful_recvs++; + } + else { + size_mismatches++; + } + + /* Wait 2ms before next send (except for the last iteration) */ + if (i < max_tries - 1) { + usleep(SEND_INTERVAL_US); + } + } + + close(sockfd); + + /* Calculate and print averages - all output after benchmark */ + printf("\n=== Benchmark Results without DTLS 1.3===\n"); + printf("Total packets sent: %d\n", successful_sends); + printf("Total packets received: %d\n", successful_recvs); + + if (send_errors > 0) { + printf("Send errors: %d\n", send_errors); + } + if (recv_errors > 0) { + printf("Receive errors: %d\n", recv_errors); + } + if (size_mismatches > 0) { + printf("Size mismatches: %d\n", size_mismatches); + } + + if (successful_sends > 0) { + double avg_send_us = (double)total_send_time / successful_sends; + printf("Average send time: %.3f microseconds (%.3f ms)\n", + avg_send_us, avg_send_us / 1000.0); + } + else { + printf("No successful sends to calculate average\n"); + } + + if (successful_recvs > 0) { + double avg_recv_us = (double)total_recv_time / successful_recvs; + printf("Average receive time: %.3f microseconds (%.3f ms)\n", + avg_recv_us, avg_recv_us / 1000.0); + } else { + printf("No successful receives to calculate average\n"); + } + + return 0; +} diff --git a/dtls/benchmark-server-dtls13.c b/dtls/benchmark-server-dtls13.c new file mode 100644 index 00000000..447b31bd --- /dev/null +++ b/dtls/benchmark-server-dtls13.c @@ -0,0 +1,295 @@ +/* + * benchmark-server-dtls13.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define USE_CERT_BUFFERS_256 +#include + +#define SERV_PORT 11111 +#define PACKET_SIZE 15360 /* 15KB = 15 * 1024 */ +#define DTLS_MAX_RECORD_SIZE 1300 /* Very small size to avoid DTLS datagram limit */ + +/* Write data in chunks to fit DTLS record size limits */ +/* Loop over wolfSSL_write to send the 15KB packet in chunks */ +static int dtls_write_chunked(WOLFSSL* ssl, const unsigned char* buf, int len) +{ + int total_written = 0; + int remaining = len; + const unsigned char* ptr = buf; + + while (remaining > 0) { + int chunk_size = (remaining > DTLS_MAX_RECORD_SIZE) ? DTLS_MAX_RECORD_SIZE : remaining; + int written = wolfSSL_write(ssl, ptr, chunk_size); + + if (written < 0) { + return written; /* Error */ + } + + if (written == 0) { + /* Should not happen with blocking socket, but handle it */ + break; + } + + total_written += written; + remaining -= written; + ptr += written; + } + + return total_written; +} + +/* Read data in chunks to handle DTLS record size limits */ +static int dtls_read_chunked(WOLFSSL* ssl, unsigned char* buf, int len) +{ + int total_read = 0; + int remaining = len; + unsigned char* ptr = buf; + + while (remaining > 0) { + int chunk_size = (remaining > DTLS_MAX_RECORD_SIZE) ? DTLS_MAX_RECORD_SIZE : remaining; + int read = wolfSSL_read(ssl, ptr, chunk_size); + + if (read <= 0) { + return read; /* Error or EOF */ + } + + total_read += read; + remaining -= read; + ptr += read; + } + + return total_read; +} + +WOLFSSL_CTX* ctx = NULL; +WOLFSSL* ssl = NULL; +int listenfd = INVALID_SOCKET; + +static void sig_handler(const int sig); +static void free_resources(void); + +int main(int argc, char** argv) +{ + int exitVal = 1; + struct sockaddr_in servAddr; + struct sockaddr_in cliaddr; + int ret; + int err; + int recvLen = 0; + socklen_t cliLen; + unsigned char buff[PACKET_SIZE]; + int msgNum = 0; + + /* Initialize wolfSSL before assigning ctx */ + if (wolfSSL_Init() != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_Init error.\n"); + return exitVal; + } + + /* Set ctx to DTLS 1.3 */ + if ((ctx = wolfSSL_CTX_new( +#ifdef WOLFSSL_DTLS13 + wolfDTLSv1_3_server_method() +#else + wolfDTLSv1_2_server_method() +#endif + )) == NULL) { + fprintf(stderr, "wolfSSL_CTX_new error.\n"); + goto cleanup; + } + + /* Load certificates from memory buffer */ + if (wolfSSL_CTX_load_verify_buffer(ctx, ca_ecc_cert_der_256, sizeof_ca_ecc_cert_der_256, + WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) { + fprintf(stderr, "Error loading CA certificate from buffer.\n"); + goto cleanup; + } + if (wolfSSL_CTX_use_certificate_buffer(ctx, serv_ecc_der_256, + sizeof_serv_ecc_der_256, WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) { + fprintf(stderr, "Error loading server certificate from buffer.\n"); + goto cleanup; + } + if (wolfSSL_CTX_use_PrivateKey_buffer(ctx, ecc_key_der_256, + sizeof_ecc_key_der_256, WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) { + fprintf(stderr, "Error loading server private key from buffer.\n"); + goto cleanup; + } + + /* Create a UDP/IP socket */ + if ((listenfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket()"); + goto cleanup; + } + + memset((char *)&servAddr, 0, sizeof(servAddr)); + servAddr.sin_family = AF_INET; + servAddr.sin_addr.s_addr = htonl(INADDR_ANY); + servAddr.sin_port = htons(SERV_PORT); + + /* Bind Socket */ + if (bind(listenfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) < 0) { + perror("bind()"); + goto cleanup; + } + + signal(SIGINT, sig_handler); + + printf("DTLS 1.3 server listening on port %d\n", SERV_PORT); + printf("Ready to receive %d byte packets\n", PACKET_SIZE); + printf("Certificates: %s\n", "ECDSA"); + printf("Press Ctrl+C to stop\n\n"); + + while (1) { + printf("Awaiting client connection on port %d\n", SERV_PORT); + + cliLen = sizeof(cliaddr); + ret = (int)recvfrom(listenfd, (char *)buff, sizeof(buff), MSG_PEEK, + (struct sockaddr*)&cliaddr, &cliLen); + + if (ret < 0) { + perror("recvfrom()"); + goto cleanup; + } + else if (ret == 0) { + fprintf(stderr, "recvfrom zero return\n"); + goto cleanup; + } + + /* Create the WOLFSSL Object */ + if ((ssl = wolfSSL_new(ctx)) == NULL) { + fprintf(stderr, "wolfSSL_new error.\n"); + goto cleanup; + } + + if (wolfSSL_dtls_set_peer(ssl, &cliaddr, cliLen) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_dtls_set_peer error.\n"); + goto cleanup; + } + + if (wolfSSL_set_fd(ssl, listenfd) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_set_fd error.\n"); + break; + } + + if (wolfSSL_accept(ssl) != WOLFSSL_SUCCESS) { + err = wolfSSL_get_error(ssl, 0); + fprintf(stderr, "error = %d, %s\n", err, + wolfSSL_ERR_reason_error_string(err)); + fprintf(stderr, "SSL_accept failed.\n"); + goto cleanup; + } + + /* No printf/perror in the receive/send loop to avoid affecting timing */ + while (1) { + recvLen = dtls_read_chunked(ssl, buff, PACKET_SIZE); + if (recvLen > 0) { + msgNum++; + + /* Echo back the received data - do this immediately, no printf in between */ + /* Loop over wolfSSL_write to send the 15KB packet in chunks */ + if (dtls_write_chunked(ssl, buff, PACKET_SIZE) < 0) { + err = wolfSSL_get_error(ssl, 0); + /* Only log errors occasionally */ + if (msgNum % 100 == 0) { + fprintf(stderr, "error = %d, %s\n", err, + wolfSSL_ERR_reason_error_string(err)); + fprintf(stderr, "wolfSSL_write failed.\n"); + } + /* Continue to next receive, don't exit */ + } + } + else if (recvLen <= 0) { + err = wolfSSL_get_error(ssl, 0); + if (err == WOLFSSL_ERROR_ZERO_RETURN) /* Received shutdown */ + break; + /* Only log errors occasionally */ + if (msgNum == 0 || (msgNum % 100 == 0)) { + fprintf(stderr, "error = %d, %s\n", err, + wolfSSL_ERR_reason_error_string(err)); + fprintf(stderr, "SSL_read failed.\n"); + } + /* Continue to next receive */ + } + } + + /* Attempt a full shutdown */ + ret = wolfSSL_shutdown(ssl); + if (ret == WOLFSSL_SHUTDOWN_NOT_DONE) + ret = wolfSSL_shutdown(ssl); + if (ret != WOLFSSL_SUCCESS) { + err = wolfSSL_get_error(ssl, 0); + fprintf(stderr, "err = %d, %s\n", err, + wolfSSL_ERR_reason_error_string(err)); + fprintf(stderr, "wolfSSL_shutdown failed\n"); + } + wolfSSL_free(ssl); + ssl = NULL; + + printf("Connection closed. Awaiting new connection\n"); + } + + exitVal = 0; +cleanup: + free_resources(); + wolfSSL_Cleanup(); + + return exitVal; +} + +static void sig_handler(const int sig) +{ + (void)sig; + free_resources(); + wolfSSL_Cleanup(); + exit(0); +} + +static void free_resources(void) +{ + if (ssl != NULL) { + wolfSSL_shutdown(ssl); + wolfSSL_free(ssl); + ssl = NULL; + } + if (ctx != NULL) { + wolfSSL_CTX_free(ctx); + ctx = NULL; + } + if (listenfd != INVALID_SOCKET) { + close(listenfd); + listenfd = INVALID_SOCKET; + } +} diff --git a/dtls/benchmark-server-udp.c b/dtls/benchmark-server-udp.c new file mode 100644 index 00000000..3391acae --- /dev/null +++ b/dtls/benchmark-server-udp.c @@ -0,0 +1,154 @@ +/* + * benchmark-server-udp.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include +#include +#include +#include +#include +#include + +#define SERV_PORT 11111 +#define PACKET_SIZE 15360 /* 15KB = 15 * 1024 */ +#define MAX_CHUNK_SIZE 1300 /* Match DTLS chunk size for fair comparison */ + +/* Send data in chunks to match DTLS behavior */ +static int udp_send_chunked(int sockfd, const unsigned char* buf, int len, + const struct sockaddr* dest_addr, socklen_t addrlen) +{ + int total_sent = 0; + int remaining = len; + const unsigned char* ptr = buf; + + while (remaining > 0) { + int chunk_size = (remaining > MAX_CHUNK_SIZE) ? MAX_CHUNK_SIZE : remaining; + int sent = sendto(sockfd, ptr, chunk_size, 0, dest_addr, addrlen); + + if (sent < 0) { + return sent; /* Error */ + } + + total_sent += sent; + remaining -= sent; + ptr += sent; + } + + return total_sent; +} + +/* Receive data in chunks to match DTLS behavior */ +static int udp_recv_chunked(int sockfd, unsigned char* buf, int len, + struct sockaddr* src_addr, socklen_t* addrlen) +{ + int total_received = 0; + int remaining = len; + unsigned char* ptr = buf; + + while (remaining > 0) { + int chunk_size = (remaining > MAX_CHUNK_SIZE) ? MAX_CHUNK_SIZE : remaining; + int received = recvfrom(sockfd, ptr, chunk_size, 0, src_addr, addrlen); + + if (received < 0) { + return received; + } + + if (received == 0) { + break; /* Connection closed */ + } + + total_received += received; + remaining -= received; + ptr += received; + } + + return total_received; +} + +int main(void) +{ + int sockfd; + int recvLen; + int msgNum = 0; + unsigned char buf[PACKET_SIZE]; + struct sockaddr_in servAddr; + struct sockaddr_in cliAddr; + socklen_t cliAddrLen = sizeof(cliAddr); + + /* create a UDP/IP socket */ + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("cannot create socket"); + return 1; + } + + memset((char *)&servAddr, 0, sizeof(servAddr)); + servAddr.sin_family = AF_INET; + servAddr.sin_addr.s_addr = htonl(INADDR_ANY); + servAddr.sin_port = htons(SERV_PORT); + + if (bind(sockfd, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0) { + perror("bind failed"); + return 1; + } + + printf("UDP server listening on port %d\n", SERV_PORT); + printf("Ready to receive %d byte packets\n", PACKET_SIZE); + printf("Press Ctrl+C to stop\n\n"); + + /* loop, listen for client, echo back to client */ + /* No printf/perror in the receive/send loop to avoid affecting timing */ + for (;;) { + memset(buf, 0, sizeof(buf)); + + /* Receive data in chunks to match DTLS behavior */ + recvLen = udp_recv_chunked(sockfd, buf, PACKET_SIZE, + (struct sockaddr *)&cliAddr, &cliAddrLen); + + if (recvLen < 0) { + /* Only log errors occasionally, not every packet */ + if (msgNum == 0 || (msgNum % 100 == 0)) { + perror("recvfrom failed"); + } + continue; + } + + if (recvLen != PACKET_SIZE) { + continue; /* Incomplete packet */ + } + + msgNum++; + + /* Echo back the received data - do this immediately, no printf in between */ + /* Send data in chunks to match DTLS behavior */ + if (udp_send_chunked(sockfd, buf, PACKET_SIZE, + (struct sockaddr *)&cliAddr, cliAddrLen) < 0) { + /* Only log errors occasionally */ + if (msgNum % 100 == 0) { + perror("sendto failed"); + } + /* Continue to next receive, don't exit */ + } + } + + return 0; +} +