Browse Source

lwip_tcp: защита от зацикливания tw_pcbs + next_owner диагностика + таймауты + фикс RTO

- 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 в лог
etcp-inflight-fix
Evgeny 4 months ago
parent
commit
11002bcb57
  1. 18
      src/lwip_tcp/lwip_tcp.c
  2. 12
      src/lwip_tcp/lwip_tcp.h
  3. 11
      src/lwip_tcp/lwip_tcp_in.c
  4. 8
      src/lwip_tcp/lwip_tcp_opts.h
  5. 5
      src/lwip_tcp/lwip_tcp_priv.h
  6. 2
      src/proxy/tcp_proxy_client.c

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

12
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;
};

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

8
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

5
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 <string.h>
// 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)

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

Loading…
Cancel
Save