From a198cfb9e3cdff3d8001acbfe319b4a6b2dc0437 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 24 Oct 2026 17:33:32 +0200 Subject: [PATCH] Bind inline dummynet filters to UDP link lifetime and report failures --- src/transport_layer/dummynet.c | 80 ++++++++++++++++++++++++---------- src/transport_layer/dummynet.h | 8 ++-- 2 files changed, 60 insertions(+), 28 deletions(-) diff --git a/src/transport_layer/dummynet.c b/src/transport_layer/dummynet.c index 09929a83..4cf6af91 100644 --- a/src/transport_layer/dummynet.c +++ b/src/transport_layer/dummynet.c @@ -11,6 +11,7 @@ #include "../lib/debug_config.h" #include "../lib/platform_compat.h" #include "etcp_connections.h" +#include "etcp.h" #include #include @@ -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) { diff --git a/src/transport_layer/dummynet.h b/src/transport_layer/dummynet.h index 87dcf380..b769595b 100644 --- a/src/transport_layer/dummynet.h +++ b/src/transport_layer/dummynet.h @@ -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