mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-09 05:40:50 +02:00
Merge pull request #10551 from julek-wolfssl/dtls-perf-benchmark
Add DTLS throughput benchmark tool and optimize send path
This commit is contained in:
@@ -61,6 +61,7 @@ wolfcrypt/test/testwolfcrypt
|
||||
examples/async/async_client
|
||||
examples/async/async_server
|
||||
examples/benchmark/tls_bench
|
||||
examples/benchmark/dtls_bench
|
||||
examples/client/client
|
||||
examples/echoclient/echoclient
|
||||
examples/echoserver/echoserver
|
||||
|
||||
@@ -546,6 +546,7 @@ SL_SE_PRF_HMAC_SHA1
|
||||
SNIFFER_SINGLE_SESSION_CACHE
|
||||
SOCK_CLOEXEC
|
||||
SOFTDEVICE_PRESENT
|
||||
SO_BINDTODEVICE
|
||||
SO_NOSIGPIPE
|
||||
SO_REUSEPORT
|
||||
SP_INT_NO_ASM
|
||||
|
||||
@@ -0,0 +1,822 @@
|
||||
/* dtls_bench.c
|
||||
*
|
||||
* Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* 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 3 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-1335, USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Single-binary DTLS throughput benchmark over real UDP loopback (or any
|
||||
* picked interface).
|
||||
*
|
||||
* Server: ./dtls_bench -s
|
||||
* Client: ./dtls_bench
|
||||
*
|
||||
* Defaults are tuned so the client's crypto path is the only bottleneck:
|
||||
* - regular Ethernet MTU (1400 B, matching wolfSSL's WOLFSSL_MAX_MTU)
|
||||
* - record write of 1300 B per wolfSSL_write (one app write = one record
|
||||
* = one UDP datagram)
|
||||
* - 8 MiB SO_SNDBUF/SO_RCVBUF
|
||||
* - pre-filled buffer; no allocation/RNG/logging in the timed loop
|
||||
* - server completes the DTLS handshake, then bypasses wolfSSL and just
|
||||
* recv()s the UDP socket and drops the bytes. This keeps the receiver
|
||||
* arbitrarily faster than the sender so we can never saturate kernel
|
||||
* buffers, and the reported client throughput is the true ceiling
|
||||
* of wolfSSL's encrypt-and-send path on this CPU.
|
||||
*
|
||||
* Use -n for a plain-UDP baseline (no DTLS) at the same record size to see
|
||||
* the pure transport ceiling. Compare different ciphers with -c to confirm
|
||||
* crypto is on the critical path.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#ifndef WOLFSSL_USER_SETTINGS
|
||||
#include <wolfssl/options.h>
|
||||
#endif
|
||||
|
||||
#include <wolfssl/wolfcrypt/settings.h>
|
||||
#include <wolfssl/ssl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Needs DTLS and POSIX BSD sockets; build a stub main() elsewhere
|
||||
* (e.g. Windows/mingw, which lacks <sys/socket.h> et al.). */
|
||||
#if defined(WOLFSSL_DTLS) && !defined(USE_WINDOWS_API) && \
|
||||
!defined(WOLFSSL_NO_SOCK)
|
||||
#define DTLS_BENCH_ENABLED
|
||||
#endif
|
||||
|
||||
#ifdef DTLS_BENCH_ENABLED
|
||||
|
||||
#include <wolfssl/error-ssl.h>
|
||||
|
||||
#define USE_CERT_BUFFERS_2048
|
||||
#include <wolfssl/certs_test.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h> /* struct timeval (SO_RCVTIMEO) */
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#define DEFAULT_HOST "127.0.0.1"
|
||||
#define DEFAULT_PORT 11111
|
||||
#define DEFAULT_DURATION 10
|
||||
#define DEFAULT_MTU 1400
|
||||
#define DEFAULT_RECORD 1300
|
||||
#define DEFAULT_VERSION 13
|
||||
#define DEFAULT_SOCKBUF (8 * 1024 * 1024)
|
||||
|
||||
/* Upper bound on per-record overhead the user must leave under MTU.
|
||||
* DTLS 1.2 worst case: 13 (hdr) + 8 (explicit nonce) + 16 (AEAD tag) = 37.
|
||||
* DTLS 1.3 typical: ~7 (hdr) + 16 (AEAD tag) = 23. Round up to 40. */
|
||||
#define DTLS_OVERHEAD 40
|
||||
|
||||
typedef struct cfg {
|
||||
int isServer;
|
||||
int plainUdp;
|
||||
const char* host;
|
||||
int port;
|
||||
const char* iface;
|
||||
int duration;
|
||||
int recordSz;
|
||||
int mtu;
|
||||
int version;
|
||||
const char* cipherList;
|
||||
int sockBuf;
|
||||
int listCipher;
|
||||
int sinkSend;
|
||||
} cfg_t;
|
||||
|
||||
static double now_sec(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
|
||||
perror("clock_gettime");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
|
||||
}
|
||||
|
||||
/* Post-handshake send sink. Pretends every byte was transmitted but does
|
||||
* nothing; the kernel's UDP/IP path never runs. Used via -z to measure the
|
||||
* pure wolfSSL encrypt-and-frame ceiling, free of any I/O cost. */
|
||||
static int dtls_bench_sink_send(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
||||
{
|
||||
(void)ssl; (void)buf; (void)ctx;
|
||||
return sz;
|
||||
}
|
||||
|
||||
static void print_stats(const char* dir, long long bytes, double sec)
|
||||
{
|
||||
double mib = (double)bytes / (1024.0 * 1024.0);
|
||||
double mbps = sec > 0 ? mib / sec : 0;
|
||||
double gbps = sec > 0 ? ((double)bytes * 8.0) / sec / 1e9 : 0;
|
||||
printf(" %s bytes : %lld\n", dir, bytes);
|
||||
printf(" duration : %.3f s\n", sec);
|
||||
printf(" throughput: %.1f MiB/s (%.3f Gbps)\n", mbps, gbps);
|
||||
}
|
||||
|
||||
static void usage(FILE* out, const char* prog)
|
||||
{
|
||||
fprintf(out,
|
||||
"Usage: %s [options]\n"
|
||||
" -s run as DTLS server (default: client)\n"
|
||||
" -h <host> server address (client only, default %s)\n"
|
||||
" -p <port> UDP port (default %d)\n"
|
||||
" -i <ifname> bind to network interface (Linux SO_BINDTODEVICE)\n"
|
||||
" -d <seconds> test duration (client only, default %d)\n"
|
||||
" -r <bytes> bytes per wolfSSL_write/send (default %d)\n"
|
||||
" -m <mtu> DTLS MTU (default %d)\n"
|
||||
" -v <12|13> DTLS version (default %d)\n"
|
||||
" -c <list|help> cipher list, or 'help' to print compiled ciphers\n"
|
||||
" -b <bytes> SO_SNDBUF/SO_RCVBUF (default %d)\n"
|
||||
" -n plain UDP baseline (no DTLS)\n"
|
||||
" -z client-only: install a no-op IOSend after the\n"
|
||||
" handshake (measure pure wolfSSL encrypt path,\n"
|
||||
" no kernel send).\n"
|
||||
" -? show this help\n",
|
||||
prog, DEFAULT_HOST, DEFAULT_PORT,
|
||||
DEFAULT_DURATION, DEFAULT_RECORD, DEFAULT_MTU,
|
||||
DEFAULT_VERSION, DEFAULT_SOCKBUF);
|
||||
}
|
||||
|
||||
static void list_ciphers(void)
|
||||
{
|
||||
const char* name;
|
||||
int idx = 0;
|
||||
printf("Compiled-in ciphers:\n");
|
||||
while ((name = wolfSSL_get_cipher_list(idx)) != NULL) {
|
||||
printf(" %s\n", name);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_args(int argc, char** argv, cfg_t* c)
|
||||
{
|
||||
int opt;
|
||||
|
||||
c->isServer = 0;
|
||||
c->plainUdp = 0;
|
||||
c->host = DEFAULT_HOST;
|
||||
c->port = DEFAULT_PORT;
|
||||
c->iface = NULL;
|
||||
c->duration = DEFAULT_DURATION;
|
||||
c->recordSz = DEFAULT_RECORD;
|
||||
c->mtu = DEFAULT_MTU;
|
||||
c->version = DEFAULT_VERSION;
|
||||
c->cipherList = NULL;
|
||||
c->sockBuf = DEFAULT_SOCKBUF;
|
||||
c->listCipher = 0;
|
||||
c->sinkSend = 0;
|
||||
|
||||
/* getopt sets optopt only on errors, not for an explicit -?; clear it
|
||||
* so the two cases can be told apart. */
|
||||
optopt = 0;
|
||||
while ((opt = getopt(argc, argv, "sh:p:i:d:r:m:v:c:b:nz?")) != -1) {
|
||||
switch (opt) {
|
||||
case 's': c->isServer = 1; break;
|
||||
case 'h': c->host = optarg; break;
|
||||
case 'p': c->port = atoi(optarg); break;
|
||||
case 'i': c->iface = optarg; break;
|
||||
case 'd': c->duration = atoi(optarg); break;
|
||||
case 'r': c->recordSz = atoi(optarg); break;
|
||||
case 'm': c->mtu = atoi(optarg); break;
|
||||
case 'v': c->version = atoi(optarg); break;
|
||||
case 'c':
|
||||
if (strcmp(optarg, "help") == 0 ||
|
||||
strcmp(optarg, "list") == 0) {
|
||||
c->listCipher = 1;
|
||||
}
|
||||
else {
|
||||
c->cipherList = optarg;
|
||||
}
|
||||
break;
|
||||
case 'b': c->sockBuf = atoi(optarg); break;
|
||||
case 'n': c->plainUdp = 1; break;
|
||||
case 'z': c->sinkSend = 1; break;
|
||||
case '?':
|
||||
if (optopt == 0) {
|
||||
/* explicit -? help request */
|
||||
usage(stdout, argv[0]);
|
||||
return -2;
|
||||
}
|
||||
usage(stderr, argv[0]);
|
||||
return -1;
|
||||
default:
|
||||
usage(stderr, argv[0]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (c->listCipher) {
|
||||
wolfSSL_Init();
|
||||
list_ciphers();
|
||||
wolfSSL_Cleanup();
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (c->version != 12 && c->version != 13) {
|
||||
fprintf(stderr, "DTLS version must be 12 or 13\n");
|
||||
return -1;
|
||||
}
|
||||
if (c->port < 1 || c->port > 65535) {
|
||||
fprintf(stderr, "port must be between 1 and 65535\n");
|
||||
return -1;
|
||||
}
|
||||
/* cap so set_sockbuf()'s 2 * requested can't overflow */
|
||||
if (c->sockBuf < 1 || c->sockBuf > INT_MAX / 2) {
|
||||
fprintf(stderr, "socket buffer size must be between 1 and %d\n",
|
||||
INT_MAX / 2);
|
||||
return -1;
|
||||
}
|
||||
if (c->mtu < 64 || c->mtu > 16384) {
|
||||
fprintf(stderr, "MTU must be between 64 and 16384\n");
|
||||
return -1;
|
||||
}
|
||||
if (c->isServer && c->sinkSend) {
|
||||
fprintf(stderr, "warning: -z only applies to the client (the send "
|
||||
"side); ignoring it in server mode\n");
|
||||
c->sinkSend = 0;
|
||||
}
|
||||
if (c->recordSz < 1) {
|
||||
fprintf(stderr, "record size must be > 0\n");
|
||||
return -1;
|
||||
}
|
||||
if (!c->plainUdp && c->recordSz > c->mtu - DTLS_OVERHEAD) {
|
||||
fprintf(stderr,
|
||||
"record size (%d) exceeds MTU (%d) - %d B DTLS overhead.\n"
|
||||
"Pick -r <= %d, or raise -m.\n",
|
||||
c->recordSz, c->mtu, DTLS_OVERHEAD, c->mtu - DTLS_OVERHEAD);
|
||||
return -1;
|
||||
}
|
||||
if (c->plainUdp && c->recordSz > c->mtu) {
|
||||
fprintf(stderr, "plain-UDP record size (%d) > mtu (%d)\n",
|
||||
c->recordSz, c->mtu);
|
||||
return -1;
|
||||
}
|
||||
if (c->duration < 1) {
|
||||
fprintf(stderr, "duration must be >= 1 second\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_sockbuf(int fd, int which, int requested, const char* label)
|
||||
{
|
||||
int got = 0;
|
||||
socklen_t glen = sizeof(got);
|
||||
if (setsockopt(fd, SOL_SOCKET, which,
|
||||
&requested, sizeof(requested)) < 0) {
|
||||
perror(label);
|
||||
return -1;
|
||||
}
|
||||
if (getsockopt(fd, SOL_SOCKET, which, &got, &glen) == 0) {
|
||||
/* Linux returns 2x what was set, on success.
|
||||
* If half the reported value is below what we asked, kernel clamped. */
|
||||
if (got < 2 * requested) {
|
||||
fprintf(stderr,
|
||||
"warning: %s clamped to %d (asked %d). "
|
||||
"Try: sudo sysctl -w net.core.%cmem_max=%d\n",
|
||||
label, got / 2, requested,
|
||||
which == SO_SNDBUF ? 'w' : 'r', requested);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bind_to_iface(int fd, const char* ifname)
|
||||
{
|
||||
#ifdef SO_BINDTODEVICE
|
||||
/* Linux SO_BINDTODEVICE expects the NUL-terminated ifname; pass strlen+1
|
||||
* so the kernel sees the terminator. Passing strlen() alone yields
|
||||
* EINVAL on some kernels. */
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
|
||||
ifname, (socklen_t)(strlen(ifname) + 1)) < 0) {
|
||||
if (errno == EPERM) {
|
||||
fprintf(stderr,
|
||||
"SO_BINDTODEVICE requires CAP_NET_RAW.\n"
|
||||
"Run with sudo, or:\n"
|
||||
" sudo setcap cap_net_raw+ep ./dtls_bench\n");
|
||||
}
|
||||
else {
|
||||
perror("SO_BINDTODEVICE");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
(void)fd; (void)ifname;
|
||||
fprintf(stderr, "SO_BINDTODEVICE not available on this platform\n");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static WOLFSSL_METHOD* pick_method(int version, int isServer)
|
||||
{
|
||||
if (version == 13) {
|
||||
#ifdef WOLFSSL_DTLS13
|
||||
if (isServer) {
|
||||
#ifndef NO_WOLFSSL_SERVER
|
||||
return wolfDTLSv1_3_server_method();
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#ifndef NO_WOLFSSL_CLIENT
|
||||
return wolfDTLSv1_3_client_method();
|
||||
#endif
|
||||
}
|
||||
#if defined(NO_WOLFSSL_SERVER) || defined(NO_WOLFSSL_CLIENT)
|
||||
fprintf(stderr, "DTLS 1.3 %s side not compiled in\n",
|
||||
isServer ? "server" : "client");
|
||||
return NULL;
|
||||
#endif
|
||||
#else
|
||||
fprintf(stderr,
|
||||
"DTLS 1.3 not compiled in (rebuild with --enable-dtls13)\n");
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
#ifndef WOLFSSL_NO_TLS12
|
||||
if (isServer) {
|
||||
#ifndef NO_WOLFSSL_SERVER
|
||||
return wolfDTLSv1_2_server_method();
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#ifndef NO_WOLFSSL_CLIENT
|
||||
return wolfDTLSv1_2_client_method();
|
||||
#endif
|
||||
}
|
||||
#if defined(NO_WOLFSSL_SERVER) || defined(NO_WOLFSSL_CLIENT)
|
||||
fprintf(stderr, "DTLS 1.2 %s side not compiled in\n",
|
||||
isServer ? "server" : "client");
|
||||
return NULL;
|
||||
#endif
|
||||
#else
|
||||
fprintf(stderr, "DTLS 1.2 not compiled in\n");
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int set_mtu(WOLFSSL* ssl, int mtu)
|
||||
{
|
||||
#ifdef WOLFSSL_DTLS_MTU
|
||||
if (wolfSSL_dtls_set_mtu(ssl, (unsigned short)mtu) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "wolfSSL_dtls_set_mtu failed\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
(void)ssl;
|
||||
if (mtu != DEFAULT_MTU) {
|
||||
fprintf(stderr,
|
||||
"warning: -m %d ignored (built without --enable-dtls-mtu)\n",
|
||||
mtu);
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ----- Plain-UDP baseline (-n) ----- */
|
||||
|
||||
static int udp_server(const cfg_t* c)
|
||||
{
|
||||
int fd = -1;
|
||||
unsigned char* buf = NULL;
|
||||
int ret = 1;
|
||||
int one = 1;
|
||||
int recvSz;
|
||||
long long total = 0;
|
||||
double start = 0;
|
||||
double last = 0;
|
||||
struct sockaddr_in addr;
|
||||
struct timeval tv = { .tv_sec = 2, .tv_usec = 0 };
|
||||
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0) { perror("socket"); goto out; }
|
||||
(void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
|
||||
set_sockbuf(fd, SO_RCVBUF, c->sockBuf, "SO_RCVBUF");
|
||||
if (c->iface && bind_to_iface(fd, c->iface) != 0) goto out;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_port = htons((uint16_t)c->port);
|
||||
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
perror("bind"); goto out;
|
||||
}
|
||||
printf("dtls_bench server: plain UDP on port %d\n", c->port);
|
||||
|
||||
/* Exit after 2s of silence following first packet. */
|
||||
(void)setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||
|
||||
recvSz = c->mtu > 65507 ? 65507 : c->mtu;
|
||||
buf = (unsigned char*)XMALLOC((size_t)recvSz, NULL,
|
||||
DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (!buf) goto out;
|
||||
|
||||
for (;;) {
|
||||
ssize_t n = recvfrom(fd, buf, (size_t)recvSz, 0, NULL, NULL);
|
||||
if (n <= 0) {
|
||||
if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) break;
|
||||
if (n < 0) perror("recvfrom");
|
||||
break;
|
||||
}
|
||||
if (total == 0) start = now_sec();
|
||||
total += n;
|
||||
last = now_sec();
|
||||
}
|
||||
printf("dtls_bench server: plain UDP results\n");
|
||||
print_stats("recv", total, last - start);
|
||||
ret = 0;
|
||||
out:
|
||||
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (fd >= 0) close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int udp_client(const cfg_t* c)
|
||||
{
|
||||
int fd = -1;
|
||||
unsigned char* buf = NULL;
|
||||
int ret = 1;
|
||||
long long total = 0;
|
||||
double start;
|
||||
double end;
|
||||
double elapsed;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0) { perror("socket"); goto out; }
|
||||
set_sockbuf(fd, SO_SNDBUF, c->sockBuf, "SO_SNDBUF");
|
||||
if (c->iface && bind_to_iface(fd, c->iface) != 0) goto out;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons((uint16_t)c->port);
|
||||
if (inet_pton(AF_INET, c->host, &addr.sin_addr) != 1) {
|
||||
fprintf(stderr, "invalid host %s\n", c->host); goto out;
|
||||
}
|
||||
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
perror("connect"); goto out;
|
||||
}
|
||||
|
||||
buf = (unsigned char*)XMALLOC((size_t)c->recordSz, NULL,
|
||||
DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (!buf) goto out;
|
||||
memset(buf, 0xA5, (size_t)c->recordSz);
|
||||
|
||||
start = now_sec();
|
||||
end = start + (double)c->duration;
|
||||
while (now_sec() < end) {
|
||||
ssize_t n = send(fd, buf, (size_t)c->recordSz, 0);
|
||||
if (n != c->recordSz) {
|
||||
if (n < 0 && (errno == ENOBUFS || errno == EAGAIN
|
||||
|| errno == EWOULDBLOCK)) {
|
||||
continue; /* tx queue full, retry */
|
||||
}
|
||||
if (n < 0) perror("send");
|
||||
break;
|
||||
}
|
||||
total += n;
|
||||
}
|
||||
elapsed = now_sec() - start;
|
||||
printf("dtls_bench client: plain UDP results (peer %s:%d)\n",
|
||||
c->host, c->port);
|
||||
printf(" iface : %s\n", c->iface ? c->iface : "(kernel default)");
|
||||
printf(" mtu : %d\n", c->mtu);
|
||||
printf(" record : %d B per send\n", c->recordSz);
|
||||
print_stats("sent", total, elapsed);
|
||||
ret = 0;
|
||||
out:
|
||||
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (fd >= 0) close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ----- DTLS server ----- */
|
||||
|
||||
static int dtls_server(const cfg_t* c)
|
||||
{
|
||||
int ret = 1;
|
||||
int listenFd = -1;
|
||||
WOLFSSL_CTX* ctx = NULL;
|
||||
WOLFSSL* ssl = NULL;
|
||||
unsigned char* buf = NULL;
|
||||
WOLFSSL_METHOD* method;
|
||||
int one = 1;
|
||||
int rbufSz = 16384;
|
||||
long long total = 0;
|
||||
double start = 0;
|
||||
double last = 0;
|
||||
ssize_t pn;
|
||||
socklen_t plen;
|
||||
unsigned char peekbuf[256];
|
||||
struct sockaddr_in servAddr;
|
||||
struct sockaddr_in peer;
|
||||
struct timeval tv = { .tv_sec = 2, .tv_usec = 0 };
|
||||
|
||||
if (wolfSSL_Init() != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "wolfSSL_Init failed\n"); return 1;
|
||||
}
|
||||
|
||||
method = pick_method(c->version, 1);
|
||||
if (!method) goto cleanup;
|
||||
ctx = wolfSSL_CTX_new(method);
|
||||
if (!ctx) {
|
||||
fprintf(stderr, "wolfSSL_CTX_new failed\n"); goto cleanup;
|
||||
}
|
||||
|
||||
if (wolfSSL_CTX_use_PrivateKey_buffer(ctx,
|
||||
server_key_der_2048, sizeof_server_key_der_2048,
|
||||
WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "use_PrivateKey_buffer failed\n"); goto cleanup;
|
||||
}
|
||||
if (wolfSSL_CTX_use_certificate_buffer(ctx,
|
||||
server_cert_der_2048, sizeof_server_cert_der_2048,
|
||||
WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "use_certificate_buffer failed\n"); goto cleanup;
|
||||
}
|
||||
if (c->cipherList && wolfSSL_CTX_set_cipher_list(ctx, c->cipherList)
|
||||
!= WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "set_cipher_list failed for '%s'\n",
|
||||
c->cipherList);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
listenFd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (listenFd < 0) { perror("socket"); goto cleanup; }
|
||||
(void)setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
|
||||
set_sockbuf(listenFd, SO_RCVBUF, c->sockBuf, "SO_RCVBUF");
|
||||
if (c->iface && bind_to_iface(listenFd, c->iface) != 0) goto cleanup;
|
||||
|
||||
memset(&servAddr, 0, sizeof(servAddr));
|
||||
servAddr.sin_family = AF_INET;
|
||||
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
servAddr.sin_port = htons((uint16_t)c->port);
|
||||
if (bind(listenFd, (struct sockaddr*)&servAddr,
|
||||
sizeof(servAddr)) < 0) {
|
||||
perror("bind"); goto cleanup;
|
||||
}
|
||||
printf("dtls_bench server: DTLS %s on UDP/%d\n",
|
||||
c->version == 13 ? "1.3" : "1.2", c->port);
|
||||
|
||||
/* Peek at first datagram to learn peer, then connect to pin. */
|
||||
plen = sizeof(peer);
|
||||
pn = recvfrom(listenFd, peekbuf, sizeof(peekbuf), MSG_PEEK,
|
||||
(struct sockaddr*)&peer, &plen);
|
||||
if (pn < 0) { perror("recvfrom"); goto cleanup; }
|
||||
if (connect(listenFd, (struct sockaddr*)&peer, plen) < 0) {
|
||||
perror("connect"); goto cleanup;
|
||||
}
|
||||
|
||||
ssl = wolfSSL_new(ctx);
|
||||
if (!ssl) { fprintf(stderr, "wolfSSL_new failed\n"); goto cleanup; }
|
||||
if (wolfSSL_dtls_set_peer(ssl, &peer, plen) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "dtls_set_peer failed\n"); goto cleanup;
|
||||
}
|
||||
/* listenFd was connect()-ed to the peer above; mark it as such so
|
||||
* EmbedSendTo skips its per-send SO_TYPE probe. */
|
||||
if (wolfSSL_set_dtls_fd_connected(ssl, listenFd) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "set_dtls_fd_connected failed\n"); goto cleanup;
|
||||
}
|
||||
if (set_mtu(ssl, c->mtu) != 0) goto cleanup;
|
||||
|
||||
if (wolfSSL_accept(ssl) != WOLFSSL_SUCCESS) {
|
||||
int err = wolfSSL_get_error(ssl, 0);
|
||||
fprintf(stderr, "wolfSSL_accept err=%d %s\n",
|
||||
err, wolfSSL_ERR_reason_error_string(err));
|
||||
goto cleanup;
|
||||
}
|
||||
printf("dtls_bench server: handshake OK (cipher=%s)\n",
|
||||
wolfSSL_get_cipher(ssl));
|
||||
|
||||
/* Once the handshake is up, stop using wolfSSL on this side and just
|
||||
* drain the UDP socket as fast as the kernel will deliver datagrams.
|
||||
* The server then runs arbitrarily faster than the client, so the
|
||||
* client's crypto path is the only thing under measurement and there
|
||||
* is no way for the receiver to backpressure or drop. */
|
||||
buf = (unsigned char*)XMALLOC((size_t)rbufSz, NULL,
|
||||
DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (!buf) goto cleanup;
|
||||
|
||||
/* Exit after 2s of idle following the last datagram. */
|
||||
(void)setsockopt(listenFd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||
|
||||
for (;;) {
|
||||
ssize_t n = recv(listenFd, buf, (size_t)rbufSz, 0);
|
||||
if (n <= 0) {
|
||||
if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) break;
|
||||
if (n < 0) perror("recv");
|
||||
break;
|
||||
}
|
||||
if (total == 0) start = now_sec();
|
||||
total += n;
|
||||
last = now_sec();
|
||||
}
|
||||
|
||||
printf("dtls_bench server: results (raw recv-and-drop after handshake)\n");
|
||||
printf(" cipher : %s\n", wolfSSL_get_cipher(ssl));
|
||||
printf(" mtu : %d\n", c->mtu);
|
||||
print_stats("wire recv", total, last - start);
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (ssl) wolfSSL_free(ssl);
|
||||
if (ctx) wolfSSL_CTX_free(ctx);
|
||||
if (listenFd >= 0) close(listenFd);
|
||||
wolfSSL_Cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ----- DTLS client ----- */
|
||||
|
||||
static int dtls_client(const cfg_t* c)
|
||||
{
|
||||
int ret = 1;
|
||||
int fd = -1;
|
||||
WOLFSSL_CTX* ctx = NULL;
|
||||
WOLFSSL* ssl = NULL;
|
||||
unsigned char* buf = NULL;
|
||||
WOLFSSL_METHOD* method;
|
||||
long long total = 0;
|
||||
double start;
|
||||
double end;
|
||||
double elapsed;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
if (wolfSSL_Init() != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "wolfSSL_Init failed\n"); return 1;
|
||||
}
|
||||
|
||||
method = pick_method(c->version, 0);
|
||||
if (!method) goto cleanup;
|
||||
ctx = wolfSSL_CTX_new(method);
|
||||
if (!ctx) {
|
||||
fprintf(stderr, "wolfSSL_CTX_new failed\n"); goto cleanup;
|
||||
}
|
||||
|
||||
if (wolfSSL_CTX_load_verify_buffer(ctx,
|
||||
ca_cert_der_2048, sizeof_ca_cert_der_2048,
|
||||
WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "load_verify_buffer failed\n"); goto cleanup;
|
||||
}
|
||||
if (c->cipherList && wolfSSL_CTX_set_cipher_list(ctx, c->cipherList)
|
||||
!= WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "set_cipher_list failed for '%s'\n",
|
||||
c->cipherList);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0) { perror("socket"); goto cleanup; }
|
||||
set_sockbuf(fd, SO_SNDBUF, c->sockBuf, "SO_SNDBUF");
|
||||
set_sockbuf(fd, SO_RCVBUF, c->sockBuf, "SO_RCVBUF");
|
||||
if (c->iface && bind_to_iface(fd, c->iface) != 0) goto cleanup;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons((uint16_t)c->port);
|
||||
if (inet_pton(AF_INET, c->host, &addr.sin_addr) != 1) {
|
||||
fprintf(stderr, "invalid host %s\n", c->host); goto cleanup;
|
||||
}
|
||||
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
perror("connect"); goto cleanup;
|
||||
}
|
||||
|
||||
ssl = wolfSSL_new(ctx);
|
||||
if (!ssl) { fprintf(stderr, "wolfSSL_new failed\n"); goto cleanup; }
|
||||
if (wolfSSL_dtls_set_peer(ssl, &addr, sizeof(addr)) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "dtls_set_peer failed\n"); goto cleanup;
|
||||
}
|
||||
/* Socket was connect()-ed above; tell wolfSSL so EmbedSendTo can skip
|
||||
* its per-send getsockopt(SO_TYPE) probe. */
|
||||
if (wolfSSL_set_dtls_fd_connected(ssl, fd) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "set_dtls_fd_connected failed\n"); goto cleanup;
|
||||
}
|
||||
if (set_mtu(ssl, c->mtu) != 0) goto cleanup;
|
||||
|
||||
if (wolfSSL_connect(ssl) != WOLFSSL_SUCCESS) {
|
||||
int err = wolfSSL_get_error(ssl, 0);
|
||||
fprintf(stderr, "wolfSSL_connect err=%d %s\n",
|
||||
err, wolfSSL_ERR_reason_error_string(err));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Handshake bytes had to go on the wire so the server could complete
|
||||
* its half. From this point on, swap in a no-op IOSend so wolfSSL_write
|
||||
* encrypts and frames as usual but never enters the kernel -- the
|
||||
* resulting throughput is the pure wolfSSL encrypt-and-frame ceiling
|
||||
* on this CPU. */
|
||||
if (c->sinkSend) {
|
||||
wolfSSL_SSLSetIOSend(ssl, dtls_bench_sink_send);
|
||||
}
|
||||
|
||||
printf("dtls_bench client: handshake OK to %s:%d\n", c->host, c->port);
|
||||
printf(" version : %s\n", wolfSSL_get_version(ssl));
|
||||
printf(" cipher : %s\n", wolfSSL_get_cipher(ssl));
|
||||
printf(" iface : %s\n", c->iface ? c->iface : "(kernel default)");
|
||||
printf(" mtu : %d\n", c->mtu);
|
||||
printf(" record : %d B per write\n", c->recordSz);
|
||||
if (c->sinkSend) {
|
||||
printf(" send mode : sink (no-op IOSend, kernel never sees data)\n");
|
||||
}
|
||||
|
||||
buf = (unsigned char*)XMALLOC((size_t)c->recordSz, NULL,
|
||||
DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (!buf) goto cleanup;
|
||||
memset(buf, 0xA5, (size_t)c->recordSz);
|
||||
|
||||
start = now_sec();
|
||||
end = start + (double)c->duration;
|
||||
while (now_sec() < end) {
|
||||
int n = wolfSSL_write(ssl, buf, c->recordSz);
|
||||
if (n != c->recordSz) {
|
||||
int err = wolfSSL_get_error(ssl, n);
|
||||
/* Retry the same errors udp_client retries on: EAGAIN and
|
||||
* EWOULDBLOCK come back as WANT_WRITE, ENOBUFS as
|
||||
* SOCKET_ERROR_E with errno set. The record stays buffered in
|
||||
* wolfSSL and the next call flushes it. */
|
||||
if (err == WOLFSSL_ERROR_WANT_WRITE ||
|
||||
(err == WC_NO_ERR_TRACE(SOCKET_ERROR_E) &&
|
||||
errno == ENOBUFS)) {
|
||||
continue; /* tx queue full, retry */
|
||||
}
|
||||
fprintf(stderr, "wolfSSL_write n=%d err=%d %s\n",
|
||||
n, err, wolfSSL_ERR_reason_error_string(err));
|
||||
goto cleanup;
|
||||
}
|
||||
total += n;
|
||||
}
|
||||
elapsed = now_sec() - start;
|
||||
|
||||
print_stats("sent", total, elapsed);
|
||||
|
||||
/* Send a one-way close_notify and exit. Don't loop on
|
||||
* WOLFSSL_SHUTDOWN_NOT_DONE: the server dropped its SSL state right
|
||||
* after the handshake and will never send back a close_notify, so a
|
||||
* bidirectional shutdown would block forever. */
|
||||
wolfSSL_shutdown(ssl);
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (ssl) wolfSSL_free(ssl);
|
||||
if (ctx) wolfSSL_CTX_free(ctx);
|
||||
if (fd >= 0) close(fd);
|
||||
wolfSSL_Cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
cfg_t c;
|
||||
int rc = parse_args(argc, argv, &c);
|
||||
if (rc == -2) return 0; /* -? usage or -c help printed */
|
||||
if (rc < 0) return 1;
|
||||
|
||||
if (c.plainUdp) {
|
||||
return c.isServer ? udp_server(&c) : udp_client(&c);
|
||||
}
|
||||
return c.isServer ? dtls_server(&c) : dtls_client(&c);
|
||||
}
|
||||
|
||||
#else /* DTLS_BENCH_ENABLED */
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("dtls_bench not supported in this build "
|
||||
"(requires DTLS and POSIX BSD sockets)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* DTLS_BENCH_ENABLED */
|
||||
@@ -11,5 +11,16 @@ examples_benchmark_tls_bench_LDADD = src/libwolfssl@LIBSUFFIX@.la $(LIB_S
|
||||
examples_benchmark_tls_bench_DEPENDENCIES = src/libwolfssl@LIBSUFFIX@.la
|
||||
endif
|
||||
|
||||
if BUILD_DTLS
|
||||
if BUILD_EXAMPLE_SERVERS
|
||||
noinst_PROGRAMS += examples/benchmark/dtls_bench
|
||||
examples_benchmark_dtls_bench_SOURCES = examples/benchmark/dtls_bench.c
|
||||
examples_benchmark_dtls_bench_LDADD = src/libwolfssl@LIBSUFFIX@.la $(LIB_STATIC_ADD)
|
||||
examples_benchmark_dtls_bench_DEPENDENCIES = src/libwolfssl@LIBSUFFIX@.la
|
||||
endif
|
||||
endif
|
||||
|
||||
dist_example_DATA+= examples/benchmark/tls_bench.c
|
||||
dist_example_DATA+= examples/benchmark/dtls_bench.c
|
||||
DISTCLEANFILES+= examples/benchmark/.libs/tls_bench
|
||||
DISTCLEANFILES+= examples/benchmark/.libs/dtls_bench
|
||||
|
||||
+64
-3
@@ -8104,6 +8104,8 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
|
||||
|
||||
ssl->buffers.dtlsCtx.rfd = -1;
|
||||
ssl->buffers.dtlsCtx.wfd = -1;
|
||||
ssl->buffers.dtlsCtx.rfdIsDGram = 0;
|
||||
ssl->buffers.dtlsCtx.wfdIsDGram = 0;
|
||||
|
||||
#ifdef WOLFSSL_RW_THREADED
|
||||
if (wc_InitRwLock(&ssl->buffers.dtlsCtx.peerLock) != 0)
|
||||
@@ -9805,6 +9807,37 @@ void WriteSEQ(WOLFSSL* ssl, int verifyOrder, byte* out)
|
||||
c32toa(seq[0], out);
|
||||
c32toa(seq[1], out + OPAQUE32_LEN);
|
||||
}
|
||||
|
||||
/* Same as WriteSEQ() but does not advance the per-direction TLS sequence
|
||||
* counter. Lets a caller share the 64-bit record sequence between fields
|
||||
* that are written more than once per record (e.g. AES-GCM nonce_explicit
|
||||
* and AAD seq_num): peek here, then a later WriteSEQ() inside
|
||||
* writeAeadAuthData() does the single mandated increment. For DTLS the
|
||||
* underlying GetSEQ is already read-only, so this is identical to
|
||||
* WriteSEQ() in that path. */
|
||||
static WC_INLINE void PeekSEQ(WOLFSSL* ssl, int verifyOrder, byte* out)
|
||||
{
|
||||
word32 seq[2] = {0, 0};
|
||||
|
||||
if (!ssl->options.dtls) {
|
||||
if (verifyOrder) {
|
||||
seq[0] = ssl->keys.peer_sequence_number_hi;
|
||||
seq[1] = ssl->keys.peer_sequence_number_lo;
|
||||
}
|
||||
else {
|
||||
seq[0] = ssl->keys.sequence_number_hi;
|
||||
seq[1] = ssl->keys.sequence_number_lo;
|
||||
}
|
||||
}
|
||||
else {
|
||||
#ifdef WOLFSSL_DTLS
|
||||
DtlsGetSEQ(ssl, verifyOrder, seq);
|
||||
#endif
|
||||
}
|
||||
|
||||
c32toa(seq[0], out);
|
||||
c32toa(seq[1], out + OPAQUE32_LEN);
|
||||
}
|
||||
#endif /* WOLFSSL_DTLS || !WOLFSSL_NO_TLS12 */
|
||||
#endif /* !NO_OLD_TLS || WOLFSSL_DTLS || !WOLFSSL_NO_TLS12 ||
|
||||
* ((HAVE_CHACHA || HAVE_AESCCM || HAVE_AESGCM || WOLFSSL_SM4_GCM ||
|
||||
@@ -25457,9 +25490,37 @@ int BuildMessage(WOLFSSL* ssl, byte* output, int outSz, const byte* input,
|
||||
args->iv = args->staticIvBuffer;
|
||||
}
|
||||
|
||||
ret = wc_RNG_GenerateBlock(ssl->rng, args->iv, args->ivSz);
|
||||
if (ret != 0)
|
||||
goto exit_buildmsg;
|
||||
#ifdef HAVE_AEAD
|
||||
/* When the explicit nonce is the 8-byte record sequence
|
||||
* number, write it directly rather than drawing a random
|
||||
* value: RFC 5288 sec 3 (AES-GCM), RFC 6655 sec 3 (AES-CCM),
|
||||
* RFC 8998 sec 3 + NIST SP 800-38D sec 8.2.1 (SM4-GCM/CCM);
|
||||
* Camellia-GCM (RFC 6367) and ARIA-GCM (RFC 6209) inherit the
|
||||
* RFC 5288 construction. The ivSz == AESGCM_EXP_IV_SZ test
|
||||
* selects exactly these suites: ChaCha20 has an implicit nonce
|
||||
* (ivSz 0, so it never enters this ivSz > 0 block), while the
|
||||
* cipher_type == aead test excludes 3DES-CBC, whose explicit
|
||||
* block IV is also 8 bytes.
|
||||
*
|
||||
* These bytes normally never reach the wire: the Encrypt()
|
||||
* paths overwrite the explicit nonce with the cipher's own,
|
||||
* and FIPS<2 builds overwrite args->iv with keys.aead_exp_IV
|
||||
* just below. The win is skipping a per-record RNG draw.
|
||||
* The exception is an ATOMIC_USER MacEncryptCb, which sends
|
||||
* the bytes as-is; the sequence number is a valid explicit
|
||||
* nonce per the above RFCs. */
|
||||
if (ssl->specs.cipher_type == aead &&
|
||||
args->ivSz == AESGCM_EXP_IV_SZ) {
|
||||
PeekSEQ(ssl, epochOrder, args->iv);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ret = wc_RNG_GenerateBlock(ssl->rng, args->iv,
|
||||
args->ivSz);
|
||||
if (ret != 0)
|
||||
goto exit_buildmsg;
|
||||
}
|
||||
}
|
||||
#if !defined(NO_PUBLIC_GCM_SET_IV) && \
|
||||
((defined(HAVE_FIPS) || defined(HAVE_SELFTEST)) && \
|
||||
|
||||
@@ -1134,6 +1134,10 @@ int wolfSSL_set_read_fd(WOLFSSL* ssl, int fd)
|
||||
if (ssl->options.dtls) {
|
||||
ssl->IOCB_ReadCtx = &ssl->buffers.dtlsCtx;
|
||||
ssl->buffers.dtlsCtx.rfd = fd;
|
||||
#ifdef USE_WOLFSSL_IO
|
||||
ssl->buffers.dtlsCtx.rfdIsDGram =
|
||||
(byte)(wolfIO_SockIsDGram(fd) != 0);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1158,6 +1162,10 @@ int wolfSSL_set_write_fd(WOLFSSL* ssl, int fd)
|
||||
if (ssl->options.dtls) {
|
||||
ssl->IOCB_WriteCtx = &ssl->buffers.dtlsCtx;
|
||||
ssl->buffers.dtlsCtx.wfd = fd;
|
||||
#ifdef USE_WOLFSSL_IO
|
||||
ssl->buffers.dtlsCtx.wfdIsDGram =
|
||||
(byte)(wolfIO_SockIsDGram(fd) != 0);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -6822,6 +6830,10 @@ int wolfSSL_set_compression(WOLFSSL* ssl)
|
||||
if (ssl->options.dtls) {
|
||||
ssl->IOCB_ReadCtx = &ssl->buffers.dtlsCtx;
|
||||
ssl->buffers.dtlsCtx.rfd = rfd;
|
||||
#ifdef USE_WOLFSSL_IO
|
||||
ssl->buffers.dtlsCtx.rfdIsDGram =
|
||||
(byte)(wolfIO_SockIsDGram(rfd) != 0);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+7
-3
@@ -642,7 +642,11 @@ static int PeerIsIpv6(const SOCKADDR_S *peer, XSOCKLENT len)
|
||||
}
|
||||
#endif /* !WOLFSSL_IPV6 */
|
||||
|
||||
static int isDGramSock(int sfd)
|
||||
/* Return non-zero iff sfd is a SOCK_DGRAM socket. A descriptor's type is
|
||||
* fixed for its lifetime, so the probe runs where dtlsCtx.rfd/wfd is
|
||||
* assigned and the result is stored alongside the descriptor, keeping the
|
||||
* getsockopt() syscall out of the I/O callbacks. */
|
||||
int wolfIO_SockIsDGram(int sfd)
|
||||
{
|
||||
int type = 0;
|
||||
/* optvalue 'type' is of size int */
|
||||
@@ -844,7 +848,7 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx)
|
||||
return recvd;
|
||||
}
|
||||
else if (recvd == 0) {
|
||||
if (!isDGramSock(sd)) {
|
||||
if (!dtlsCtx->rfdIsDGram) {
|
||||
/* Closed TCP connection */
|
||||
recvd = WOLFSSL_CBIO_ERR_CONN_CLOSE;
|
||||
}
|
||||
@@ -942,7 +946,7 @@ int EmbedSendTo(WOLFSSL* ssl, char *buf, int sz, void *ctx)
|
||||
if (sz < 0)
|
||||
return WOLFSSL_CBIO_ERR_GENERAL;
|
||||
|
||||
if (!isDGramSock(sd)) {
|
||||
if (!dtlsCtx->wfdIsDGram) {
|
||||
/* Probably a TCP socket. peer and peerSz MUST be NULL and 0 */
|
||||
}
|
||||
else if (!dtlsCtx->connected) {
|
||||
|
||||
@@ -2797,6 +2797,11 @@ typedef struct WOLFSSL_DTLS_CTX {
|
||||
* connected (connect() and bind() both called).
|
||||
* This means that sendto and recvfrom do not need to
|
||||
* specify and store the peer address. */
|
||||
byte rfdIsDGram:1; /* whether rfd is a SOCK_DGRAM socket; probed with
|
||||
* getsockopt(SO_TYPE) where rfd is assigned to keep
|
||||
* the syscall out of the I/O callbacks. */
|
||||
byte wfdIsDGram:1; /* as rfdIsDGram, for wfd; rfd and wfd may be
|
||||
* different sockets of different types. */
|
||||
#ifdef WOLFSSL_DTLS_CID
|
||||
byte processingPendingRecord:1;
|
||||
#endif
|
||||
|
||||
@@ -703,6 +703,7 @@ WOLFSSL_LOCAL int SslBioReceive(WOLFSSL* ssl, char* buf, int sz, void* ctx);
|
||||
WOLFSSL_API int EmbedSendTo(WOLFSSL* ssl, char *buf, int sz, void *ctx);
|
||||
WOLFSSL_API int EmbedGenerateCookie(WOLFSSL* ssl, byte *buf, int sz,
|
||||
void *ctx);
|
||||
WOLFSSL_LOCAL int wolfIO_SockIsDGram(int sfd);
|
||||
#ifdef WOLFSSL_MULTICAST
|
||||
WOLFSSL_API int EmbedReceiveFromMcast(WOLFSSL *ssl, char *buf,
|
||||
int sz, void *ctx);
|
||||
|
||||
Reference in New Issue
Block a user