From 11002bcb57e20853eec987f1f78887357450508c Mon Sep 17 00:00:00 2001 From: Evgeny Date: Fri, 5 Jun 2026 17:19:50 +0300 Subject: [PATCH] =?UTF-8?q?lwip=5Ftcp:=20=D0=B7=D0=B0=D1=89=D0=B8=D1=82?= =?UTF-8?q?=D0=B0=20=D0=BE=D1=82=20=D0=B7=D0=B0=D1=86=D0=B8=D0=BA=D0=BB?= =?UTF-8?q?=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20tw=5Fpcbs=20+=20next=5Fo?= =?UTF-8?q?wner=20=D0=B4=D0=B8=D0=B0=D0=B3=D0=BD=D0=BE=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0=20+=20=D1=82=D0=B0=D0=B9=D0=BC=D0=B0=D1=83=D1=82?= =?UTF-8?q?=D1=8B=20+=20=D1=84=D0=B8=D0=BA=D1=81=20RTO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TCP_TW_MAX=256: циклозащита в for/while циклах tw_pcbs (input, slowtmr, kill_timewait) + TCP_RMV макрос тоже защищён от бесконечного поиска - next_owner в struct tcp_pcb/tcp_pcb_listen: отслеживает кто правил pcb->next (REG=1, RMV=2, SLOWTMR=3, INPUT=4). memset при alloc обнуляет - таймауты: FIN_WAIT 20s→6s, SYN_RCVD 20s→6s, MSL 60s→25s (TIME_WAIT=50s) - tcp_alloc: RTO из ctx->rto_min_ms вместо хардкода 3000 - handle_fin: pcb_state в лог --- src/lwip_tcp/lwip_tcp.c | 18 ++++++++++++++++-- src/lwip_tcp/lwip_tcp.h | 12 ++++++++++++ src/lwip_tcp/lwip_tcp_in.c | 11 +++++++++++ src/lwip_tcp/lwip_tcp_opts.h | 8 +++++--- src/lwip_tcp/lwip_tcp_priv.h | 5 ++++- src/proxy/tcp_proxy_client.c | 2 +- 6 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/lwip_tcp/lwip_tcp.c b/src/lwip_tcp/lwip_tcp.c index e5583b03..8f9bbca0 100644 --- a/src/lwip_tcp/lwip_tcp.c +++ b/src/lwip_tcp/lwip_tcp.c @@ -749,6 +749,7 @@ void tcp_slowtmr(struct lwip_tcp_ctx *ctx) tcp_pcb_purge(pcb); if (prev != NULL) { prev->next = pcb->next; + prev->next_owner = PCB_NEXT_SLOWTMR; } else { ctx->active_pcbs = pcb->next; } @@ -787,7 +788,13 @@ void tcp_slowtmr(struct lwip_tcp_ctx *ctx) // ---- process TIME-WAIT PCBs ---- prev = NULL; pcb = ctx->tw_pcbs; + int tw_iter = 0; while (pcb != NULL) { + if (++tw_iter > TCP_TW_MAX) { + DEBUG_ERROR(DEBUG_CATEGORY_ALL, "TW_PCBS LOOP in tcp_slowtmr: %d iterations, aborting pcb=%p state=%d port=%u next_owner=%d", tw_iter, (void*)pcb, pcb->state, pcb->local_port, pcb->next_owner); + tcp_abort(pcb); + break; + } pcb_remove = 0; if ((uint32_t)(ctx->ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { @@ -799,6 +806,7 @@ void tcp_slowtmr(struct lwip_tcp_ctx *ctx) tcp_pcb_purge(pcb); if (prev != NULL) { prev->next = pcb->next; + prev->next_owner = PCB_NEXT_SLOWTMR; } else { ctx->tw_pcbs = pcb->next; } @@ -1016,7 +1024,13 @@ static void tcp_kill_timewait(struct lwip_tcp_ctx *ctx) if (!ctx) return; inactivity = 0; inactive = NULL; + int tw_iter = 0; for (pcb = ctx->tw_pcbs; pcb != NULL; pcb = pcb->next) { + if (++tw_iter > TCP_TW_MAX) { + DEBUG_ERROR(DEBUG_CATEGORY_ALL, "TW_PCBS LOOP in tcp_kill_timewait: %d iterations, aborting pcb=%p state=%d port=%u next_owner=%d", tw_iter, (void*)pcb, pcb->state, pcb->local_port, pcb->next_owner); + tcp_abort(pcb); + return; + } if ((uint32_t)(ctx->ticks - pcb->tmr) >= inactivity) { inactivity = ctx->ticks - pcb->tmr; inactive = pcb; @@ -1080,8 +1094,8 @@ struct tcp_pcb *tcp_alloc(struct lwip_tcp_ctx *ctx, uint8_t prio) pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND16(TCP_WND); pcb->ttl = 64; pcb->mss = INITIAL_MSS; - pcb->rto = 3000 / TCP_SLOW_INTERVAL; - pcb->sv = 3000 / TCP_SLOW_INTERVAL; + pcb->rto = (int16_t)(ctx->rto_min_ms / TCP_SLOW_INTERVAL); + pcb->sv = (int16_t)(ctx->rto_min_ms / TCP_SLOW_INTERVAL); pcb->rtime = -1; pcb->cwnd = 1; DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "tcp_alloc: cwnd=%u snd_buf=%u mss=%u snd_wnd=%u", pcb->cwnd, pcb->snd_buf, pcb->mss, pcb->snd_wnd); diff --git a/src/lwip_tcp/lwip_tcp.h b/src/lwip_tcp/lwip_tcp.h index 9300d549..0fa4ccae 100644 --- a/src/lwip_tcp/lwip_tcp.h +++ b/src/lwip_tcp/lwip_tcp.h @@ -67,6 +67,15 @@ enum tcp_err_enum { LERR_CLSD = -15, LERR_ARG = -16 }; + +// кто последним записал pcb->next (диагностика зацикливания tw_pcbs) +enum pcb_next_owner { + PCB_NEXT_NONE = 0, + PCB_NEXT_REG = 1, + PCB_NEXT_RMV = 2, + PCB_NEXT_SLOWTMR = 3, + PCB_NEXT_INPUT = 4, +}; typedef int err_t; // Forward declaration for callbacks @@ -167,6 +176,8 @@ struct tcp_pcb { tcp_poll_fn poll; tcp_err_fn errf; + uint8_t next_owner; // who last wrote pcb->next (enum pcb_next_owner) + uint32_t keep_idle; uint8_t persist_cnt; uint8_t persist_backoff; @@ -182,6 +193,7 @@ struct tcp_pcb_listen { uint8_t prio; uint16_t local_port; uint32_t local_ip; + uint8_t next_owner; tcp_accept_fn accept; }; diff --git a/src/lwip_tcp/lwip_tcp_in.c b/src/lwip_tcp/lwip_tcp_in.c index 88bb446d..044254b9 100644 --- a/src/lwip_tcp/lwip_tcp_in.c +++ b/src/lwip_tcp/lwip_tcp_in.c @@ -198,7 +198,9 @@ void lwip_tcp_input(struct lwip_tcp_ctx *ctx, struct pbuf *p, pcb->local_ip == dst_ip) { if (prev != NULL) { prev->next = pcb->next; + prev->next_owner = PCB_NEXT_INPUT; pcb->next = ctx->active_pcbs; + pcb->next_owner = PCB_NEXT_INPUT; ctx->active_pcbs = pcb; } break; @@ -207,7 +209,14 @@ void lwip_tcp_input(struct lwip_tcp_ctx *ctx, struct pbuf *p, } if (pcb == NULL) { + int tw_iter = 0; for (pcb = ctx->tw_pcbs; pcb != NULL; pcb = pcb->next) { + if (++tw_iter > TCP_TW_MAX) { + DEBUG_ERROR(DEBUG_CATEGORY_ALL, "TW_PCBS LOOP in lwip_tcp_input: %d iterations, aborting pcb=%p sport=%u dport=%u next_owner=%d", tw_iter, (void*)pcb, sport, dport, pcb->next_owner); + tcp_abort(pcb); + pbuf_free(p); + return; + } if (pcb->remote_port == sport && pcb->local_port == dport && pcb->remote_ip == src_ip && @@ -231,7 +240,9 @@ void lwip_tcp_input(struct lwip_tcp_ctx *ctx, struct pbuf *p, if (lpcb != NULL) { if (prev != NULL) { ((struct tcp_pcb_listen *)prev)->next = lpcb->next; + ((struct tcp_pcb_listen *)prev)->next_owner = PCB_NEXT_INPUT; lpcb->next = ctx->listen_pcbs; + lpcb->next_owner = PCB_NEXT_INPUT; ctx->listen_pcbs = (struct tcp_pcb *)lpcb; } tcp_listen_input(lpcb); diff --git a/src/lwip_tcp/lwip_tcp_opts.h b/src/lwip_tcp/lwip_tcp_opts.h index 2057072b..37d972fe 100644 --- a/src/lwip_tcp/lwip_tcp_opts.h +++ b/src/lwip_tcp/lwip_tcp_opts.h @@ -14,15 +14,17 @@ #define TCP_FAST_INTERVAL TCP_TMR_INTERVAL #define TCP_SLOW_INTERVAL (2 * TCP_TMR_INTERVAL) -#define TCP_FIN_WAIT_TIMEOUT 20000 // ms -#define TCP_SYN_RCVD_TIMEOUT 20000 // ms -#define TCP_MSL 60000 // ms +#define TCP_FIN_WAIT_TIMEOUT 6000 // ms +#define TCP_SYN_RCVD_TIMEOUT 6000 // ms +#define TCP_MSL 25000 // ms (2*MSL = 50s) #define TCP_OOSEQ_TIMEOUT 6 // x RTO #define TCP_KEEPIDLE_DEFAULT 7200000 // ms (unused, no keepalive) #define TCP_KEEPINTVL_DEFAULT 75000 #define TCP_KEEPCNT_DEFAULT 9 +#define TCP_TW_MAX 256 // max tw_pcbs before cycle detection triggers + #define TCP_WND_SCALE 0 // disabled #define TCP_TIMESTAMPS 0 // disabled #define TCP_SACK_OUT 0 // disabled diff --git a/src/lwip_tcp/lwip_tcp_priv.h b/src/lwip_tcp/lwip_tcp_priv.h index eafcb3c1..482727a0 100644 --- a/src/lwip_tcp/lwip_tcp_priv.h +++ b/src/lwip_tcp/lwip_tcp_priv.h @@ -3,6 +3,7 @@ #define LWIP_TCP_PRIV_H #include "lwip_tcp.h" +#include "lwip_tcp_opts.h" #include // Packed struct support @@ -235,13 +236,15 @@ void lwip_tcp_stats_clear(struct lwip_tcp_ctx *ctx); // PCB list management #define TCP_REG(pcbs, npcb) do { \ + (npcb)->next_owner = PCB_NEXT_REG; \ (npcb)->next = *(pcbs); *(pcbs) = (npcb); \ } while(0) #define TCP_RMV(pcbs, npcb) do { \ if(*(pcbs) == (npcb)) { *(pcbs) = (*pcbs)->next; } \ - else { struct tcp_pcb *tmp; for(tmp = *(pcbs); tmp != NULL; tmp = tmp->next) { if(tmp->next == (npcb)) { tmp->next = (npcb)->next; break; } } } \ + else { struct tcp_pcb *_tmp; int _rmv_i = 0; for(_tmp = *(pcbs); _tmp != NULL && _rmv_i < TCP_TW_MAX; _tmp = _tmp->next) { if(++_rmv_i >= TCP_TW_MAX) break; if(_tmp->next == (npcb)) { _tmp->next_owner = PCB_NEXT_SLOWTMR; _tmp->next = (npcb)->next; break; } } } \ (npcb)->next = NULL; \ + (npcb)->next_owner = PCB_NEXT_RMV; \ } while(0) #define TCP_REG_ACTIVE(ctx, npcb) TCP_REG(&(ctx)->active_pcbs, npcb) diff --git a/src/proxy/tcp_proxy_client.c b/src/proxy/tcp_proxy_client.c index 9501f069..7f167b10 100644 --- a/src/proxy/tcp_proxy_client.c +++ b/src/proxy/tcp_proxy_client.c @@ -496,7 +496,7 @@ static void tcp_proxy_client_handle_error(struct tcp_proxy_client* p, uint32_t s static void tcp_proxy_client_handle_fin(struct tcp_proxy_client* p, uint32_t stream_id) { struct tcp_proxy_client_conn* pc = tcp_proxy_client_find_conn(p, stream_id); if (!pc || !pc->pcb) return; - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY FIN FROM exit sid=%08x — shutdown write (send FIN to local)", stream_id); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY FIN FROM exit sid=%08x pcb_state=%u — shutdown write (send FIN to local)", stream_id, pc->pcb->state); pc->fin_remote = 1; if (pc->pcb->state != TIME_WAIT && pc->pcb->state != CLOSED) tcp_shutdown(pc->pcb, 0, 1);