mirror of
https://github.com/coturn/coturn.git
synced 2026-05-12 09:40:35 +00:00
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:
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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!!!
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user