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);