Add Linux-only recvmmsg receive path for DTLS/UDP listener (#1852)

## Summary

This change lets the listener batch incoming UDP datagrams when
`--udp-recvmmsg` is enabled, reducing per-packet overhead on busy
listeners while preserving the existing behavior as the default and
fallback path.

## What changed

- add a new `--udp-recvmmsg` runtime flag
- implement a batched UDP receive path in the DTLS listener using
`recvmmsg()`
- reuse packet classification and datagram processing logic across
batched and non-batched receive paths
- reduce buffer/metadata churn by reusing listener-side scratch state
and network buffers
- keep compatibility safeguards by falling back when `recvmmsg()` is
unavailable or unsupported
- expose the setting in admin/CLI configuration output
- update the example test runner to enable the flag on Linux

## Why

The current listener processes UDP datagrams one at a time. On Linux,
`recvmmsg()` allows the server to receive multiple packets per syscall,
which should improve throughput and lower CPU overhead under load for
UDP-heavy traffic.

## Notes

- the feature is opt-in and defaults to disabled
- the implementation is Linux-specific and leaves the existing path
unchanged on other platforms
- the listener still falls back to the legacy receive path if batched
receive is unavailable at runtime

## Testing

- updated `examples/run_tests.sh` to pass `--udp-recvmmsg` on Linux
- validated behavior through the existing listener flow and fallback
handling
This commit is contained in:
Pavel Punsky
2026-04-10 08:56:28 -07:00
committed by GitHub
parent 318239ae2d
commit 3294c714aa
7 changed files with 360 additions and 59 deletions
+7 -1
View File
@@ -11,8 +11,14 @@ if [ ! -f $BINDIR/turnserver ]; then
BINDIR="../build/bin"
fi
TURNSERVER_EXTRA_ARGS=""
if [ "$(uname -s)" = "Linux" ]; then
TURNSERVER_EXTRA_ARGS="--udp-recvmmsg"
echo 'Using TURNSERVER_EXTRA_ARGS="--udp-recvmmsg"'
fi
echo 'Running turnserver'
$BINDIR/turnserver --use-auth-secret --sock-buf-size=1048576 --static-auth-secret=secret --realm=north.gov --allow-loopback-peers --cert ../examples/ca/turn_server_cert.pem --pkey ../examples/ca/turn_server_pkey.pem > /dev/null &
$BINDIR/turnserver --use-auth-secret --sock-buf-size=1048576 --static-auth-secret=secret --realm=north.gov --allow-loopback-peers $TURNSERVER_EXTRA_ARGS --cert ../examples/ca/turn_server_cert.pem --pkey ../examples/ca/turn_server_pkey.pem > /dev/null &
turnserver_pid="$!"
echo 'Running peer client'
+333 -58
View File
@@ -64,6 +64,34 @@ typedef uint16_t in_port_t;
#define COOKIE_SECRET_LENGTH (32)
#define MAX_SINGLE_UDP_BATCH (16)
#define MAX_RECVMMSG_BATCH MAX_SINGLE_UDP_BATCH
#if defined(__linux__) && defined(CMSG_SPACE)
#if defined(IP_RECVTTL) || defined(IP_TTL)
#define RECVMMSG_IPV4_TTL_CMSG_SZ CMSG_SPACE(sizeof(int))
#else
#define RECVMMSG_IPV4_TTL_CMSG_SZ 0
#endif
#if defined(IP_RECVTOS) || defined(IP_TOS)
#define RECVMMSG_IPV4_TOS_CMSG_SZ CMSG_SPACE(sizeof(int))
#else
#define RECVMMSG_IPV4_TOS_CMSG_SZ 0
#endif
#if defined(IPV6_RECVHOPLIMIT) || defined(IPV6_HOPLIMIT)
#define RECVMMSG_IPV6_TTL_CMSG_SZ CMSG_SPACE(sizeof(int))
#else
#define RECVMMSG_IPV6_TTL_CMSG_SZ 0
#endif
#if defined(IPV6_RECVTCLASS) || defined(IPV6_TCLASS)
#define RECVMMSG_IPV6_TOS_CMSG_SZ CMSG_SPACE(sizeof(int))
#else
#define RECVMMSG_IPV6_TOS_CMSG_SZ 0
#endif
#define RECVMMSG_IPV4_CMSG_SZ (RECVMMSG_IPV4_TTL_CMSG_SZ + RECVMMSG_IPV4_TOS_CMSG_SZ)
#define RECVMMSG_IPV6_CMSG_SZ (RECVMMSG_IPV6_TTL_CMSG_SZ + RECVMMSG_IPV6_TOS_CMSG_SZ)
#define RECVMMSG_CMSG_SZ \
((RECVMMSG_IPV4_CMSG_SZ > RECVMMSG_IPV6_CMSG_SZ) ? RECVMMSG_IPV4_CMSG_SZ : RECVMMSG_IPV6_CMSG_SZ)
#endif
#if !defined(WINDOWS)
_Thread_local uint32_t packetcounter = 0;
@@ -71,6 +99,14 @@ _Thread_local uint32_t packetcounter = 0;
static uint32_t packetcounter = 0;
#endif
typedef enum {
UDP_PACKET_CLASS_INVALID = 0,
UDP_PACKET_CLASS_STUN_OR_CHANNEL,
UDP_PACKET_CLASS_DTLS_HANDSHAKE,
UDP_PACKET_CLASS_DTLS_OTHER,
UDP_PACKET_CLASS_OLD_STUN
} udp_packet_classification_t;
struct dtls_listener_relay_server_info {
char ifname[1025];
ioa_addr addr;
@@ -83,13 +119,30 @@ struct dtls_listener_relay_server_info {
struct message_to_relay sm;
size_t slen0;
ioa_engine_new_connection_event_handler connect_cb;
#if defined(__linux__)
struct dtls_listener_recvmmsg_state *recvmmsg_state;
#endif
};
#if defined(__linux__)
struct dtls_listener_recvmmsg_state {
struct mmsghdr msgs[MAX_RECVMMSG_BATCH];
struct iovec iovecs[MAX_RECVMMSG_BATCH];
char cmsgs[MAX_RECVMMSG_BATCH][RECVMMSG_CMSG_SZ];
ioa_addr src_addrs[MAX_RECVMMSG_BATCH];
int ttls[MAX_RECVMMSG_BATCH];
int toss[MAX_RECVMMSG_BATCH];
udp_packet_classification_t packet_types[MAX_RECVMMSG_BATCH];
ioa_network_buffer_handle elems[MAX_RECVMMSG_BATCH];
};
#endif
///////////// forward declarations ////////
static int create_server_socket(dtls_listener_relay_server_type *server, int report_creation, int sock_buf_size);
static int clean_server(dtls_listener_relay_server_type *server);
static int reopen_server_socket(dtls_listener_relay_server_type *server, evutil_socket_t fd);
static int create_new_connected_udp_socket(dtls_listener_relay_server_type *server, ioa_socket_handle s);
///////////// dtls message types //////////
@@ -340,7 +393,7 @@ static ioa_socket_handle dtls_server_input_handler(dtls_listener_relay_server_ty
#endif
static int handle_udp_packet(dtls_listener_relay_server_type *server, struct message_to_relay *sm,
ioa_engine_handle ioa_eng, turn_turnserver *ts) {
ioa_engine_handle ioa_eng, turn_turnserver *ts, udp_packet_classification_t packet_type) {
const int verbose = ioa_eng->verbose;
ioa_socket_handle s = sm->m.sm.s;
@@ -453,8 +506,7 @@ static int handle_udp_packet(dtls_listener_relay_server_type *server, struct mes
chs = NULL;
#if DTLS_SUPPORTED
if (!turn_params.no_dtls && is_dtls_handshake_message(ioa_network_buffer_data(sm->m.sm.nd.nbh),
(int)ioa_network_buffer_get_size(sm->m.sm.nd.nbh))) {
if (!turn_params.no_dtls && (packet_type == UDP_PACKET_CLASS_DTLS_HANDSHAKE)) {
chs = dtls_server_input_handler(server, s, sm->m.sm.nd.nbh);
ioa_network_buffer_delete(server->e, sm->m.sm.nd.nbh);
sm->m.sm.nd.nbh = NULL;
@@ -494,6 +546,221 @@ static int handle_udp_packet(dtls_listener_relay_server_type *server, struct mes
return 0;
}
static int process_udp_datagram(dtls_listener_relay_server_type *server, ioa_socket_handle s,
ioa_network_buffer_handle elem, const ioa_addr *src_addr, ssize_t bsize, int recv_ttl,
int recv_tos, int packet_type, uint32_t *packets_processed, uint32_t *packets_dropped) {
int rc = 0;
server->sm.m.sm.s = s;
server->sm.m.sm.nd.nbh = elem;
server->sm.m.sm.nd.recv_ttl = recv_ttl;
server->sm.m.sm.nd.recv_tos = recv_tos;
server->sm.m.sm.can_resume = 1;
addr_cpy(&(server->sm.m.sm.nd.src_addr), src_addr);
ioa_network_buffer_set_size(elem, (size_t)bsize);
uint8_t *data = ioa_network_buffer_data(elem);
const bool is_valid_packet = (packet_type != UDP_PACKET_CLASS_INVALID);
if (turn_params.drop_invalid_packets && !is_valid_packet) {
packetcounter++;
if (turn_params.drop_invalid_packets_log && (packetcounter % 1000 == 0)) {
uint8_t txt2pcap[1000]; // 1000 is enough to print ~300B packet (3 chars per byte) with extras
print_packet_txt2pcap(packetcounter, data, (size_t)bsize, txt2pcap, sizeof(txt2pcap));
TURN_LOG_FUNC(TURN_LOG_LEVEL_DEBUG, "TXT2PCAP: %s\n", txt2pcap);
}
++(*packets_dropped);
} else {
++(*packets_processed);
if (server->connect_cb) {
rc = create_new_connected_udp_socket(server, s);
if (rc < 0) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot handle UDP packet, size %d\n", (int)bsize);
}
} else {
rc = handle_udp_packet(server, &(server->sm), server->e, server->ts, (udp_packet_classification_t)packet_type);
}
if (rc < 0 && eve(server->e->verbose)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot handle UDP event\n");
}
}
if (server->sm.m.sm.nd.nbh != NULL) {
/* buffer was not consumed downstream, return ownership to the caller */
server->sm.m.sm.nd.nbh = NULL;
return 1;
}
return 0;
}
static udp_packet_classification_t classify_udp_packet(const uint8_t *data, size_t blen) {
size_t candidate_len = blen;
uint16_t chnum = 0;
uint32_t old_stun_cookie = 0;
if (stun_is_channel_message_str(data, &candidate_len, &chnum, false) ||
stun_is_command_message_str(data, candidate_len)) {
return UDP_PACKET_CLASS_STUN_OR_CHANNEL;
}
#if DTLS_SUPPORTED
if (!turn_params.no_dtls && is_dtls_handshake_message(data, (int)blen)) {
return UDP_PACKET_CLASS_DTLS_HANDSHAKE;
}
if (!turn_params.no_dtls && is_dtls_message(data, (int)blen)) {
return UDP_PACKET_CLASS_DTLS_OTHER;
}
#endif
if (turn_params.stun_backward_compatibility && old_stun_is_command_message_str(data, blen, &old_stun_cookie)) {
return UDP_PACKET_CLASS_OLD_STUN;
}
return UDP_PACKET_CLASS_INVALID;
}
#if defined(__linux__)
typedef unsigned char listener_recv_ttl_t;
typedef unsigned char listener_recv_tos_t;
static void parse_udp_cmsg(struct msghdr *msg, int *ttl, int *tos) {
listener_recv_ttl_t recv_ttl = TTL_DEFAULT;
listener_recv_tos_t recv_tos = TOS_DEFAULT;
struct cmsghdr *cmsgh = NULL;
for (cmsgh = CMSG_FIRSTHDR(msg); cmsgh != NULL; cmsgh = CMSG_NXTHDR(msg, cmsgh)) {
const int l = cmsgh->cmsg_level;
const int t = cmsgh->cmsg_type;
switch (l) {
case IPPROTO_IP:
switch (t) {
#if defined(IP_RECVTTL) && !defined(__sparc_v9__)
case IP_RECVTTL:
case IP_TTL:
recv_ttl = *((listener_recv_ttl_t *)CMSG_DATA(cmsgh));
break;
#endif
#if defined(IP_RECVTOS)
case IP_RECVTOS:
case IP_TOS:
recv_tos = *((listener_recv_tos_t *)CMSG_DATA(cmsgh));
break;
#endif
default:;
};
break;
case IPPROTO_IPV6:
switch (t) {
#if defined(IPV6_RECVHOPLIMIT) && !defined(__sparc_v9__)
case IPV6_RECVHOPLIMIT:
case IPV6_HOPLIMIT:
recv_ttl = *((listener_recv_ttl_t *)CMSG_DATA(cmsgh));
break;
#endif
#if defined(IPV6_RECVTCLASS)
case IPV6_RECVTCLASS:
case IPV6_TCLASS:
recv_tos = *((listener_recv_tos_t *)CMSG_DATA(cmsgh));
break;
#endif
default:;
};
break;
default:;
};
}
*ttl = recv_ttl;
CORRECT_RAW_TTL(*ttl);
*tos = recv_tos;
CORRECT_RAW_TOS(*tos);
}
static int ensure_recvmmsg_state(dtls_listener_relay_server_type *server) {
if (!server) {
return -1;
}
if (server->recvmmsg_state) {
return 0;
}
server->recvmmsg_state =
(struct dtls_listener_recvmmsg_state *)calloc(1, sizeof(struct dtls_listener_recvmmsg_state));
if (!server->recvmmsg_state) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Cannot allocate recvmmsg scratch state\n", __FUNCTION__);
return -1;
}
for (unsigned int i = 0; i < MAX_RECVMMSG_BATCH; ++i) {
server->recvmmsg_state->msgs[i].msg_hdr.msg_name = &(server->recvmmsg_state->src_addrs[i]);
server->recvmmsg_state->msgs[i].msg_hdr.msg_namelen = (socklen_t)server->slen0;
server->recvmmsg_state->msgs[i].msg_hdr.msg_iov = &(server->recvmmsg_state->iovecs[i]);
server->recvmmsg_state->msgs[i].msg_hdr.msg_iovlen = 1;
server->recvmmsg_state->msgs[i].msg_hdr.msg_control = server->recvmmsg_state->cmsgs[i];
server->recvmmsg_state->msgs[i].msg_hdr.msg_controllen = RECVMMSG_CMSG_SZ;
server->recvmmsg_state->elems[i] = ioa_network_buffer_allocate(server->e);
if (!server->recvmmsg_state->elems[i]) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: Cannot allocate recvmmsg batch buffer\n", __FUNCTION__);
return -1;
}
}
return 0;
}
static int receive_udp_batch_recvmmsg(dtls_listener_relay_server_type *server, evutil_socket_t fd) {
unsigned int i = 0;
if (ensure_recvmmsg_state(server) < 0) {
return -1;
}
struct dtls_listener_recvmmsg_state *state = server->recvmmsg_state;
for (i = 0; i < MAX_RECVMMSG_BATCH; ++i) {
if (!state->elems[i]) {
state->elems[i] = ioa_network_buffer_allocate(server->e);
if (!state->elems[i]) {
break;
}
}
ioa_network_buffer_reset(state->elems[i]);
addr_set_any(&(state->src_addrs[i]));
state->ttls[i] = TTL_IGNORE;
state->toss[i] = TOS_IGNORE;
state->packet_types[i] = UDP_PACKET_CLASS_INVALID;
state->iovecs[i].iov_base = ioa_network_buffer_data(state->elems[i]);
state->iovecs[i].iov_len = ioa_network_buffer_get_capacity_udp();
state->msgs[i].msg_hdr.msg_namelen = (socklen_t)server->slen0;
state->msgs[i].msg_hdr.msg_controllen = RECVMMSG_CMSG_SZ;
state->msgs[i].msg_len = 0;
}
if (i == 0) {
return -1;
}
const int rc = recvmmsg(fd, state->msgs, i, MSG_DONTWAIT, NULL);
if (rc <= 0) {
return rc;
}
for (int j = 0; j < rc; ++j) {
ioa_network_buffer_set_size(state->elems[j], state->msgs[j].msg_len);
parse_udp_cmsg(&(state->msgs[j].msg_hdr), &(state->ttls[j]), &(state->toss[j]));
state->packet_types[j] =
classify_udp_packet(ioa_network_buffer_data(state->elems[j]), ioa_network_buffer_get_size(state->elems[j]));
}
return rc;
}
#endif
static int create_new_connected_udp_socket(dtls_listener_relay_server_type *server, ioa_socket_handle s) {
evutil_socket_t udp_fd = socket(s->local_addr.ss.sa_family, CLIENT_DGRAM_SOCKET_TYPE, CLIENT_DGRAM_SOCKET_PROTOCOL);
@@ -633,14 +900,61 @@ static void udp_server_input_handler(evutil_socket_t fd, short what, void *arg)
// printf_server_socket(server, fd);
ioa_network_buffer_handle *elem = NULL;
ioa_network_buffer_handle elem = NULL;
uint32_t packets_processed = 0;
uint32_t packets_dropped = 0;
#if defined(__linux__)
if (turn_params.udp_recvmmsg) {
const int batch_rc = receive_udp_batch_recvmmsg(server, fd);
if (batch_rc > 0) {
struct dtls_listener_recvmmsg_state *state = server->recvmmsg_state;
for (int i = 0; i < batch_rc; ++i) {
if (!state->elems[i]) {
continue;
}
const int keep_elem = process_udp_datagram(
server, s, state->elems[i], &(state->src_addrs[i]), (ssize_t)ioa_network_buffer_get_size(state->elems[i]),
state->ttls[i], state->toss[i], state->packet_types[i], &packets_processed, &packets_dropped);
if (keep_elem) {
ioa_network_buffer_reset(state->elems[i]);
} else {
state->elems[i] = NULL;
}
}
prom_inc_packet_dropped(packets_dropped);
prom_inc_packet_processed(packets_processed);
FUNCEND;
return;
}
if (batch_rc < 0) {
if (errno == ENOSYS || errno == EINVAL || errno == EOPNOTSUPP) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING,
"%s: recvmmsg() is unavailable on this system, disabling udp-recvmmsg fast path\n", __FUNCTION__);
turn_params.udp_recvmmsg = false;
} else if (would_block()) {
prom_inc_packet_dropped(packets_dropped);
prom_inc_packet_processed(packets_processed);
FUNCEND;
return;
} else if (is_connreset()) {
reopen_server_socket(server, fd);
prom_inc_packet_dropped(packets_dropped);
prom_inc_packet_processed(packets_processed);
FUNCEND;
return;
}
}
}
#endif
start_udp_cycle:
if (!elem) {
elem = (ioa_network_buffer_handle *)ioa_network_buffer_allocate(server->e);
elem = ioa_network_buffer_allocate(server->e);
}
server->sm.m.sm.nd.nbh = elem;
@@ -733,59 +1047,10 @@ start_udp_cycle:
}
if (bsize > 0) {
int rc = 0;
ioa_network_buffer_set_size(elem, (size_t)bsize);
// Do minimal validation on the received UDP packet
// stun_is_channel_message_str and stun_is_command_message_str
size_t blen = bsize;
uint16_t chnum = 0;
uint32_t old_stun_cookie = 0;
uint8_t *data = ioa_network_buffer_data(elem);
bool is_valid_packet = false;
if (stun_is_channel_message_str(data, &blen, &chnum, false) || stun_is_command_message_str(data, blen)) {
is_valid_packet = true;
}
#if DTLS_SUPPORTED
else if (!turn_params.no_dtls && is_dtls_message(data, blen)) {
is_valid_packet = true;
}
#endif
else if (turn_params.stun_backward_compatibility && old_stun_is_command_message_str(data, blen, &old_stun_cookie)) {
is_valid_packet = true;
}
if (turn_params.drop_invalid_packets && !is_valid_packet) {
packetcounter++;
if (turn_params.drop_invalid_packets_log && (packetcounter % 1000 == 0)) {
uint8_t txt2pcap[1000]; // 1000 is enough to print ~300B packet (3 chars per byte) with extras
print_packet_txt2pcap(packetcounter, data, blen, txt2pcap, sizeof(txt2pcap));
TURN_LOG_FUNC(TURN_LOG_LEVEL_DEBUG, "TXT2PCAP: %s\n", txt2pcap);
}
++packets_dropped;
} else {
++packets_processed;
if (server->connect_cb) {
rc = create_new_connected_udp_socket(server, s);
if (rc < 0) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot handle UDP packet, size %d\n", (int)bsize);
}
} else {
server->sm.m.sm.s = s;
rc = handle_udp_packet(server, &(server->sm), server->e, server->ts);
}
if (rc < 0) {
if (eve(server->e->verbose)) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot handle UDP event\n");
}
}
}
process_udp_datagram(server, s, elem, &(server->sm.m.sm.nd.src_addr), bsize, server->sm.m.sm.nd.recv_ttl,
server->sm.m.sm.nd.recv_tos,
(int)classify_udp_packet(ioa_network_buffer_data(elem), (size_t)bsize), &packets_processed,
&packets_dropped);
}
if (server->sm.m.sm.nd.nbh != NULL) {
@@ -1001,6 +1266,16 @@ static int clean_server(dtls_listener_relay_server_type *server) {
EVENT_DEL(server->udp_listen_ev);
close_ioa_socket(server->udp_listen_s);
server->udp_listen_s = NULL;
#if defined(__linux__)
if (server->recvmmsg_state) {
for (unsigned int i = 0; i < MAX_RECVMMSG_BATCH; ++i) {
ioa_network_buffer_delete(server->e, server->recvmmsg_state->elems[i]);
server->recvmmsg_state->elems[i] = NULL;
}
free(server->recvmmsg_state);
server->recvmmsg_state = NULL;
}
#endif
}
return 0;
}
+8
View File
@@ -241,6 +241,7 @@ turn_params_t turn_params = {
false, /* respond_http_unsupported */
true, /* drop_invalid_packets */
false, /* drop_invalid_packets_log */
false, /* udp_recvmmsg */
false /* include_reason_string */
};
@@ -1363,6 +1364,8 @@ static char Usage[] =
"packets.\n"
" --drop-invalid-packets-log Log invalid packets. The default behaviour is to not log "
"invalid packets.\n"
" --udp-recvmmsg Enable Linux-only batched UDP receive via recvmmsg() on listener "
"sockets.\n"
" --include-reason-string Include descriptive reason strings in STUN/TURN error responses.\n"
" By default, only the standard reason phrase for the error code is\n"
" sent. Enabling this option adds detailed error descriptions which\n"
@@ -1528,6 +1531,7 @@ enum EXTRA_OPTS {
RESPOND_HTTP_UNSUPPORTED_OPT,
DROP_INVALID_PACKETS_OPT,
DROP_INVALID_PACKETS_LOG_OPT,
UDP_RECVMMSG_OPT,
VERSION_OPT,
CPUS_OPT,
INCLUDE_REASON_STRING_OPT
@@ -1677,6 +1681,7 @@ static const struct myoption long_options[] = {
{"respond-http-unsupported", optional_argument, NULL, RESPOND_HTTP_UNSUPPORTED_OPT},
{"drop-invalid-packets", optional_argument, NULL, DROP_INVALID_PACKETS_OPT},
{"drop-invalid-packets-log", optional_argument, NULL, DROP_INVALID_PACKETS_LOG_OPT},
{"udp-recvmmsg", optional_argument, NULL, UDP_RECVMMSG_OPT},
{"include-reason-string", optional_argument, NULL, INCLUDE_REASON_STRING_OPT},
{"version", optional_argument, NULL, VERSION_OPT},
{"syslog-facility", required_argument, NULL, SYSLOG_FACILITY_OPT},
@@ -2458,6 +2463,9 @@ static void set_option(int c, char *value) {
case DROP_INVALID_PACKETS_LOG_OPT:
turn_params.drop_invalid_packets_log = get_bool_value(value);
break;
case UDP_RECVMMSG_OPT:
turn_params.udp_recvmmsg = get_bool_value(value);
break;
case INCLUDE_REASON_STRING_OPT:
turn_params.include_reason_string = get_bool_value(value);
break;
+1
View File
@@ -364,6 +364,7 @@ typedef struct _turn_params_ {
bool respond_http_unsupported;
bool drop_invalid_packets;
bool drop_invalid_packets_log;
bool udp_recvmmsg;
bool include_reason_string;
} turn_params_t;
+9
View File
@@ -3542,6 +3542,15 @@ ioa_network_buffer_handle ioa_network_buffer_allocate(ioa_engine_handle e) {
/* We do not use special header in this simple implementation */
void ioa_network_buffer_header_init(ioa_network_buffer_handle nbh) { UNUSED_ARG(nbh); }
void ioa_network_buffer_reset(ioa_network_buffer_handle nbh) {
if (nbh) {
stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh;
buf_elem->buf.len = 0;
buf_elem->buf.offset = 0;
buf_elem->buf.coffset = 0;
}
}
uint8_t *ioa_network_buffer_data(ioa_network_buffer_handle nbh) {
stun_buffer_list_elem *buf_elem = (stun_buffer_list_elem *)nbh;
return buf_elem->buf.buf + buf_elem->buf.offset - buf_elem->buf.coffset;
+1
View File
@@ -697,6 +697,7 @@ static void cli_print_configuration(struct cli_session *cs) {
cli_print_flag(cs, turn_params.fingerprint, "enforce fingerprints", 0);
cli_print_flag(cs, turn_params.mobility, "mobility", 1);
cli_print_flag(cs, turn_params.udp_self_balance, "udp-self-balance", 0);
cli_print_flag(cs, turn_params.udp_recvmmsg, "udp-recvmmsg", 0);
cli_print_str(cs, turn_params.pidfile, "pidfile", 0);
#if defined(WINDOWS)
// TODO: implement it!!!
+1
View File
@@ -199,6 +199,7 @@ const ip_range_list_t *ioa_get_blacklist(ioa_engine_handle e);
*/
ioa_network_buffer_handle ioa_network_buffer_allocate(ioa_engine_handle e);
void ioa_network_buffer_header_init(ioa_network_buffer_handle nbh);
void ioa_network_buffer_reset(ioa_network_buffer_handle nbh);
uint8_t *ioa_network_buffer_data(ioa_network_buffer_handle nbh);
size_t ioa_network_buffer_get_size(ioa_network_buffer_handle nbh);
size_t ioa_network_buffer_get_capacity(ioa_network_buffer_handle nbh);