Bind inline dummynet filters to UDP link lifetime and report failures

This commit is contained in:
evgeny committed 2026-10-24 17:33:59 +02:00
1 parent 83bf76dd41
commit a198cfb9e3
2 files changed
+60 -28

No files matched your search

+57 -23
View File
@@ -11,6 +11,7 @@
#include "../lib/debug_config.h"
#include "../lib/platform_compat.h"
#include "etcp_connections.h"
#include "etcp.h"
#include <stdlib.h>
#include <string.h>
@@ -612,8 +613,6 @@ void dummynet_get_debug_counters(struct dummynet *dn, int direction,
struct dummynet_filter {
struct UASYNC* ua;
uint32_t loss_permille;
uint32_t delay_ms;
uint32_t jitter_ms;
struct ETCP_LINK* link;
struct dummynet_stats stats;
struct {
@@ -629,12 +628,13 @@ static ssize_t dummynet_filter_send_hook(socket_t fd, const void* buf, size_t le
struct dummynet_filter* df = (struct dummynet_filter*)ctx;
(void)link;
df->stats.recv++;
df->stats.recv_bytes += len;
if (addr && addr->sa_family == AF_INET && df->block_count > 0) {
const struct sockaddr_in* sin = (const struct sockaddr_in*)addr;
for (uint8_t i = 0; i < df->block_count; i++) {
if (sin->sin_addr.s_addr == df->block_addrs[i].ip &&
sin->sin_port == df->block_addrs[i].port) {
df->stats.lost++;
df->stats.lost++; df->stats.blocked++;
return (ssize_t)len;
}
}
@@ -647,68 +647,102 @@ static ssize_t dummynet_filter_send_hook(socket_t fd, const void* buf, size_t le
}
}
ssize_t sent = socket_sendto(fd, buf, len, addr, addr_len);
if (sent > 0) df->stats.sent++;
if (sent == (ssize_t)len) { df->stats.sent++; df->stats.sent_bytes += len; }
else {
df->stats.dropped++;
DEBUG_ERROR(DEBUG_CATEGORY_DUMMYNET, "Inline UDP send failed bytes=%zd expected=%zu error=%s",
sent, len, socket_strerror(socket_get_error()));
}
return sent;
}
/* Линк освобождается независимо от фильтра; после этого detach/destroy не обращаются к его памяти. */
static void dummynet_filter_link_closed(struct ETCP_LINK* link, void* ctx) {
struct dummynet_filter* df = ctx;
if (df->link == link) df->link = NULL;
DEBUG_DEBUG(DEBUG_CATEGORY_DUMMYNET, "Inline filter detached by link close path=%u", link->local_link_id);
}
struct dummynet_filter* dummynet_filter_create(struct UASYNC* ua) {
if (!ua) { DEBUG_ERROR(DEBUG_CATEGORY_DUMMYNET, "Inline filter requires uasync"); return NULL; }
struct dummynet_filter* df = u_calloc(1, sizeof(*df));
if (!df) return NULL;
if (!df) { DEBUG_ERROR(DEBUG_CATEGORY_DUMMYNET, "Inline filter allocation failed"); return NULL; }
df->ua = ua;
DEBUG_INFO(DEBUG_CATEGORY_DUMMYNET, "Inline UDP loss filter created");
return df;
}
void dummynet_filter_set_loss(struct dummynet_filter* df, uint32_t loss_permille) {
if (!df) return;
if (loss_permille > 1000) loss_permille = 1000;
if (!df) { DEBUG_WARN(DEBUG_CATEGORY_DUMMYNET, "NULL inline filter loss update"); return; }
if (loss_permille > DUMMYNET_MAX_LOSS_PERMILLE) {
DEBUG_WARN(DEBUG_CATEGORY_DUMMYNET, "Inline loss=%u clamped to %d", loss_permille, DUMMYNET_MAX_LOSS_PERMILLE);
loss_permille = DUMMYNET_MAX_LOSS_PERMILLE;
}
df->loss_permille = loss_permille;
DEBUG_DEBUG(DEBUG_CATEGORY_DUMMYNET, "Inline loss updated permille=%u", loss_permille);
}
void dummynet_filter_set_delay(struct dummynet_filter* df, uint32_t delay_ms, uint32_t jitter_ms) {
if (!df) return;
df->delay_ms = delay_ms;
df->jitter_ms = jitter_ms;
}
void dummynet_filter_attach(struct dummynet_filter* df, struct ETCP_LINK* link) {
if (!df || !link) return;
int dummynet_filter_attach(struct dummynet_filter* df, struct ETCP_LINK* link) {
if (!df || !link || !link->etcp || link->is_tcp || link->closing || link->etcp->close_requested ||
link->etcp->instance->ua != df->ua || (link->conn && link->conn->socks_udp)) {
DEBUG_ERROR(DEBUG_CATEGORY_DUMMYNET, "Inline filter requires live native UDP link on its uasync link=%p", (void*)link); return -1;
}
if (df->link == link && link->send_hook == dummynet_filter_send_hook && link->send_hook_ctx == df) return 0;
if (link->send_hook || link->send_hook_ctx || link->send_hook_release) {
DEBUG_ERROR(DEBUG_CATEGORY_DUMMYNET, "Inline filter target already has send hook path=%u", link->local_link_id); return -1;
}
dummynet_filter_detach(df);
df->link = link;
link->send_hook = dummynet_filter_send_hook;
link->send_hook_ctx = df;
link->send_hook_release = dummynet_filter_link_closed;
DEBUG_INFO(DEBUG_CATEGORY_DUMMYNET, "Inline filter attached path=%u", link->local_link_id);
return 0;
}
void dummynet_filter_detach(struct dummynet_filter* df) {
if (!df || !df->link) return;
df->link->send_hook = NULL;
df->link->send_hook_ctx = NULL;
struct ETCP_LINK* link = df->link;
if (link->send_hook == dummynet_filter_send_hook && link->send_hook_ctx == df) {
link->send_hook = NULL; link->send_hook_ctx = NULL;
} else DEBUG_WARN(DEBUG_CATEGORY_DUMMYNET, "Inline filter hook replaced externally path=%u; preserving replacement", link->local_link_id);
if (link->send_hook_release == dummynet_filter_link_closed) link->send_hook_release = NULL;
df->link = NULL;
DEBUG_DEBUG(DEBUG_CATEGORY_DUMMYNET, "Inline filter detached path=%u", link->local_link_id);
}
void dummynet_filter_destroy(struct dummynet_filter* df) {
if (!df) return;
dummynet_filter_detach(df);
DEBUG_INFO(DEBUG_CATEGORY_DUMMYNET, "Inline filter destroyed recv=%llu sent=%llu lost=%llu dropped=%llu",
(unsigned long long)df->stats.recv, (unsigned long long)df->stats.sent,
(unsigned long long)df->stats.lost, (unsigned long long)df->stats.dropped);
u_free(df);
}
void dummynet_filter_block_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port) {
if (!df || df->block_count >= 4) return;
int dummynet_filter_block_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port) {
if (!df || !ip || !port) { DEBUG_WARN(DEBUG_CATEGORY_DUMMYNET, "Invalid inline block address"); return -1; }
for (uint8_t i = 0; i < df->block_count; i++)
if (df->block_addrs[i].ip == ip && df->block_addrs[i].port == port) return;
if (df->block_addrs[i].ip == ip && df->block_addrs[i].port == port) return 0;
if (df->block_count == 4) { DEBUG_ERROR(DEBUG_CATEGORY_DUMMYNET, "Inline block list full (4 endpoints)"); return -1; }
df->block_addrs[df->block_count].ip = ip;
df->block_addrs[df->block_count].port = port;
df->block_count++;
DEBUG_DEBUG(DEBUG_CATEGORY_DUMMYNET, "Inline block added ip=%08x port=%u count=%u", ntohl(ip), ntohs(port), df->block_count);
return 0;
}
void dummynet_filter_unblock_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port) {
if (!df) return;
int dummynet_filter_unblock_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port) {
if (!df || !ip || !port) { DEBUG_WARN(DEBUG_CATEGORY_DUMMYNET, "Invalid inline unblock address"); return -1; }
for (uint8_t i = 0; i < df->block_count; i++) {
if (df->block_addrs[i].ip == ip && df->block_addrs[i].port == port) {
df->block_count--;
if (i < df->block_count) memmove(&df->block_addrs[i], &df->block_addrs[i+1], (df->block_count - i) * sizeof(df->block_addrs[0]));
return;
DEBUG_DEBUG(DEBUG_CATEGORY_DUMMYNET, "Inline block removed ip=%08x port=%u count=%u", ntohl(ip), ntohs(port), df->block_count);
return 0;
}
}
return 0;
}
const struct dummynet_stats* dummynet_filter_get_stats(struct dummynet_filter* df) {
+3 -5
View File
@@ -193,14 +193,12 @@ struct dummynet_filter;
struct dummynet_filter* dummynet_filter_create(struct UASYNC* ua);
void dummynet_filter_set_loss(struct dummynet_filter* df, uint32_t loss_permille);
// Пока только сохраняет параметры; inline-фильтр задержку не применяет. Для RTT использовать UDP dummynet.
void dummynet_filter_set_delay(struct dummynet_filter* df, uint32_t delay_ms, uint32_t jitter_ms);
void dummynet_filter_attach(struct dummynet_filter* df, struct ETCP_LINK* link);
int dummynet_filter_attach(struct dummynet_filter* df, struct ETCP_LINK* link);
void dummynet_filter_detach(struct dummynet_filter* df);
void dummynet_filter_destroy(struct dummynet_filter* df);
const struct dummynet_stats* dummynet_filter_get_stats(struct dummynet_filter* df);
void dummynet_filter_block_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port);
void dummynet_filter_unblock_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port);
int dummynet_filter_block_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port);
int dummynet_filter_unblock_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port);
#ifdef __cplusplus