add lwIP TCP stack test with fixes: rcv_wnd tracking, FIN compensation, tmr_phase dual-counter, trace ring buffer, stats counters
This commit is contained in:
1 parent
8994d7a834
commit
7c71ef683c
9 files changed
+961
-16
No files matched your search
+127
-6
@@ -12,6 +12,8 @@
|
||||
// protocol constants
|
||||
#define NUM_TCP_PCB_LISTS 4
|
||||
|
||||
tcp_trace_fn g_tcp_trace = NULL;
|
||||
|
||||
// window update threshold
|
||||
#ifndef TCP_WND_UPDATE_THRESHOLD
|
||||
#define TCP_WND_UPDATE_THRESHOLD (TCP_WND / 4)
|
||||
@@ -101,13 +103,31 @@ struct lwip_tcp_ctx *lwip_tcp_init(struct UASYNC *ua, tcp_output_fn output, void
|
||||
return NULL;
|
||||
}
|
||||
ctx->iss_seed = (uint16_t)(get_time_tb() & 0xFFFF);
|
||||
ctx->timer = uasync_set_timeout(ua, TCP_TMR_INTERVAL * 10, ctx, tcp_tmr_cb, "lwip_tcp_tmr");
|
||||
ctx->tmr_interval_ms = TCP_TMR_INTERVAL;
|
||||
ctx->rto_min_ms = 3000;
|
||||
ctx->rto_max_ms = 0;
|
||||
ctx->timer = uasync_set_timeout(ua, ctx->tmr_interval_ms * 10, ctx, tcp_tmr_cb, "lwip_tcp_tmr");
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
// ============================================
|
||||
// 2. lwip_tcp_destroy
|
||||
// 2. lwip_tcp_set_timer
|
||||
// ============================================
|
||||
|
||||
void lwip_tcp_set_timer(struct lwip_tcp_ctx *ctx,
|
||||
uint16_t interval_ms, uint16_t rto_min_ms, uint16_t rto_max_ms)
|
||||
{
|
||||
if (!ctx || interval_ms < 1) return;
|
||||
ctx->tmr_interval_ms = interval_ms;
|
||||
ctx->rto_min_ms = rto_min_ms;
|
||||
ctx->rto_max_ms = rto_max_ms;
|
||||
if (ctx->timer) uasync_cancel_timeout(ctx->ua, ctx->timer);
|
||||
ctx->timer = uasync_set_timeout(ctx->ua, interval_ms * 10, ctx, tcp_tmr_cb, "lwip_tcp_tmr");
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 3. lwip_tcp_destroy
|
||||
// ============================================
|
||||
|
||||
void lwip_tcp_destroy(struct lwip_tcp_ctx *ctx)
|
||||
@@ -144,15 +164,17 @@ static void tcp_tmr_cb(void *arg)
|
||||
struct lwip_tcp_ctx *ctx = (struct lwip_tcp_ctx *)arg;
|
||||
if (!ctx) return;
|
||||
|
||||
if (g_tcp_trace) g_tcp_trace(ctx, 'M', 0, 0, 0, "tmr");
|
||||
|
||||
tcp_fasttmr(ctx);
|
||||
|
||||
ctx->slowtmr_ctr++;
|
||||
if (ctx->slowtmr_ctr & 1) {
|
||||
ctx->tmr_phase++;
|
||||
if (ctx->tmr_phase & 1) {
|
||||
tcp_slowtmr(ctx);
|
||||
}
|
||||
|
||||
// re-arm — runs every TCP_TMR_INTERVAL ms
|
||||
ctx->timer = uasync_set_timeout(ctx->ua, TCP_TMR_INTERVAL * 10, ctx, tcp_tmr_cb, "lwip_tcp_tmr");
|
||||
// re-arm
|
||||
ctx->timer = uasync_set_timeout(ctx->ua, ctx->tmr_interval_ms * 10, ctx, tcp_tmr_cb, "lwip_tcp_tmr");
|
||||
}
|
||||
|
||||
|
||||
@@ -435,6 +457,7 @@ struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb)
|
||||
|
||||
lpcb = (struct tcp_pcb_listen *)memory_pool_alloc(pcb->ctx->pcb_listen_pool);
|
||||
if (lpcb == NULL) {
|
||||
pcb->ctx->stats.pcb_listen_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_listen: out of memory");
|
||||
return NULL;
|
||||
}
|
||||
@@ -663,6 +686,8 @@ void tcp_slowtmr(struct lwip_tcp_ctx *ctx)
|
||||
if ((pcb->rtime >= 0) && (pcb->rtime < 0x7FFF)) ++pcb->rtime;
|
||||
|
||||
if (pcb->rtime >= pcb->rto) {
|
||||
lwip_tcp_trace_record(pcb->ctx, 'T', pcb->snd_nxt, 0, pcb->cwnd,
|
||||
(uint16_t)pcb->rto, pcb->rtime, (uint8_t)pcb->state);
|
||||
if ((tcp_rexmit_rto_prepare(pcb) == LERR_OK) || ((pcb->unacked == NULL) && (pcb->unsent != NULL))) {
|
||||
if (pcb->state != SYN_SENT) {
|
||||
uint8_t backoff_idx = LWIP_MIN(pcb->nrtx, sizeof(tcp_backoff) - 1);
|
||||
@@ -735,6 +760,9 @@ void tcp_slowtmr(struct lwip_tcp_ctx *ctx)
|
||||
last_state = pcb->state;
|
||||
pcb2 = pcb;
|
||||
pcb = pcb->next;
|
||||
lwip_tcp_trace_record(ctx, 'K', pcb2->snd_nxt, pcb2->nrtx,
|
||||
pcb2->state, (uint16_t)pcb2->rto, pcb2->rtime,
|
||||
(uint8_t)pcb2->local_port);
|
||||
tcp_free(pcb2);
|
||||
|
||||
TCP_EVENT_ERR(last_state, err_fn, err_arg, LERR_ABRT);
|
||||
@@ -1033,6 +1061,10 @@ struct tcp_pcb *tcp_alloc(struct lwip_tcp_ctx *ctx, uint8_t prio)
|
||||
if (pcb == NULL) {
|
||||
tcp_kill_prio(ctx, prio);
|
||||
pcb = (struct tcp_pcb *)memory_pool_alloc(ctx->pcb_pool);
|
||||
if (pcb == NULL) {
|
||||
ctx->stats.pcb_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_alloc: out of memory after all retries");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1199,3 +1231,92 @@ err_t tcp_tcp_get_tcp_addrinfo(struct tcp_pcb *pcb, int local, uint32_t *addr, u
|
||||
}
|
||||
return LERR_OK;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 29. Trace ring buffer
|
||||
// ============================================
|
||||
|
||||
void lwip_tcp_trace_record(struct lwip_tcp_ctx *ctx, char event, uint32_t seq,
|
||||
uint16_t len, uint16_t wnd, uint16_t rto, int16_t rtime, uint8_t state)
|
||||
{
|
||||
if (!ctx) return;
|
||||
struct tcp_trace_buf *b = &ctx->trace;
|
||||
struct tcp_trace_entry *e = &b->e[b->pos];
|
||||
e->ticks = ctx->ticks;
|
||||
e->event = event;
|
||||
e->seq = seq;
|
||||
e->len = len;
|
||||
e->wnd = wnd;
|
||||
e->rto = rto;
|
||||
e->rtime = rtime;
|
||||
e->ctx_id = (uint8_t)ctx->trace_id;
|
||||
e->state = state;
|
||||
b->pos = (b->pos + 1) % TCP_TRACE_SIZE;
|
||||
if (b->count < TCP_TRACE_SIZE) b->count++;
|
||||
}
|
||||
|
||||
void lwip_tcp_trace_dump(struct lwip_tcp_ctx *ctx)
|
||||
{
|
||||
if (!ctx) return;
|
||||
struct tcp_trace_buf *b = &ctx->trace;
|
||||
int n = b->count;
|
||||
if (n == 0) return;
|
||||
int start = (b->count < TCP_TRACE_SIZE) ? 0 : b->pos;
|
||||
int show = n > 50 ? 50 : n;
|
||||
int skip = n - show;
|
||||
printf("=== Trace dump ctx=%c (%d events, showing last %d) ===\n",
|
||||
ctx->trace_id ? ctx->trace_id : '?', n, show);
|
||||
if (skip > 0) printf(" (skipped %d events)\n", skip);
|
||||
for (int i = 0; i < show; i++) {
|
||||
int idx = (start + skip + i) % TCP_TRACE_SIZE;
|
||||
struct tcp_trace_entry *e = &b->e[idx];
|
||||
char buf[64];
|
||||
int di = snprintf(buf, sizeof(buf), "t=%05u %c %c", e->ticks, e->ctx_id, e->event);
|
||||
switch (e->event) {
|
||||
case 'S': di += snprintf(buf + di, sizeof(buf) - di, " seq=%u len=%u wnd=%u", e->seq, e->len, e->wnd); break;
|
||||
case 'X': di += snprintf(buf + di, sizeof(buf) - di, " seq=%u len=%u cwnd=%u", e->seq, e->len, e->wnd); break;
|
||||
case 'A': di += snprintf(buf + di, sizeof(buf) - di, " ack=%u acked=%u cwnd=%u", e->seq, e->len, e->wnd); break;
|
||||
case 'D': di += snprintf(buf + di, sizeof(buf) - di, " seq=%u len=%u", e->seq, e->len); break;
|
||||
case 'r': di += snprintf(buf + di, sizeof(buf) - di, " rtime=%d rto=%u st=%u", e->rtime, e->rto, e->state); break;
|
||||
case 'T': di += snprintf(buf + di, sizeof(buf) - di, " rto=%u rtime=%d", e->rto, e->rtime); break;
|
||||
case 'R': di += snprintf(buf + di, sizeof(buf) - di, " seq=%u len=%u rto=%u", e->seq, e->len, e->rto); break;
|
||||
case 'F': di += snprintf(buf + di, sizeof(buf) - di, " seq=%u len=%u st=%u", e->seq, e->len, e->wnd); break;
|
||||
case 'C': di += snprintf(buf + di, sizeof(buf) - di, " st=%u", e->state); break;
|
||||
case 'L': di += snprintf(buf + di, sizeof(buf) - di, " drop len=%u", e->len); break;
|
||||
case 'M': break;
|
||||
case 'K': di += snprintf(buf + di, sizeof(buf) - di, " st=%u nrtx=%u port=%u",
|
||||
e->wnd, e->len, e->state); break;
|
||||
case 'Y': di += snprintf(buf + di, sizeof(buf) - di, " seq=%u nrtx=%u",
|
||||
e->seq, e->wnd); break;
|
||||
case 'Z': di += snprintf(buf + di, sizeof(buf) - di, " st=%u reset=%u port=%u",
|
||||
e->wnd, e->len, e->state); break;
|
||||
case 'E': di += snprintf(buf + di, sizeof(buf) - di, " rc=%d", (int)e->seq); break;
|
||||
case 'P': di += snprintf(buf + di, sizeof(buf) - di, " probe rto=%u", e->rto); break;
|
||||
default: break;
|
||||
}
|
||||
printf(" %s\n", buf);
|
||||
}
|
||||
}
|
||||
|
||||
void lwip_tcp_trace_clear(struct lwip_tcp_ctx *ctx)
|
||||
{
|
||||
if (!ctx) return;
|
||||
memset(&ctx->trace, 0, sizeof(ctx->trace));
|
||||
}
|
||||
|
||||
void lwip_tcp_stats_dump(struct lwip_tcp_ctx *ctx)
|
||||
{
|
||||
if (!ctx) return;
|
||||
printf("=== Stats ctx=%c: pbuf=%u seg=%u pcb=%u listen=%u write=%u enq=%u rst=%u ack=%u probe=%u split=%u ===\n",
|
||||
ctx->trace_id ? ctx->trace_id : '?',
|
||||
ctx->stats.pbuf_fails, ctx->stats.seg_fails, ctx->stats.pcb_fails,
|
||||
ctx->stats.pcb_listen_fails, ctx->stats.write_fails, ctx->stats.enq_fails,
|
||||
ctx->stats.rst_fails, ctx->stats.ack_fails, ctx->stats.probe_fails,
|
||||
ctx->stats.split_fails);
|
||||
}
|
||||
|
||||
void lwip_tcp_stats_clear(struct lwip_tcp_ctx *ctx)
|
||||
{
|
||||
if (!ctx) return;
|
||||
memset(&ctx->stats, 0, sizeof(ctx->stats));
|
||||
}
|
||||
+44
-1
@@ -83,6 +83,26 @@ typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *pcb, err_t err);
|
||||
// Output callback — called when TCP wants to send an IP packet
|
||||
typedef err_t (*tcp_output_fn)(void *arg, struct pbuf *p, uint32_t src_ip, uint32_t dst_ip);
|
||||
|
||||
#define TCP_TRACE_SIZE 384
|
||||
|
||||
struct tcp_trace_entry {
|
||||
uint32_t ticks;
|
||||
char event;
|
||||
uint32_t seq;
|
||||
uint16_t len;
|
||||
uint16_t wnd;
|
||||
uint16_t rto;
|
||||
int16_t rtime;
|
||||
uint8_t ctx_id;
|
||||
uint8_t state;
|
||||
};
|
||||
|
||||
struct tcp_trace_buf {
|
||||
struct tcp_trace_entry e[TCP_TRACE_SIZE];
|
||||
int pos;
|
||||
int count;
|
||||
};
|
||||
|
||||
// PCB structures
|
||||
struct tcp_pcb {
|
||||
struct tcp_pcb *next;
|
||||
@@ -181,16 +201,39 @@ struct lwip_tcp_ctx {
|
||||
struct memory_pool *pcb_listen_pool;
|
||||
struct memory_pool *seg_pool;
|
||||
|
||||
uint16_t tmr_interval_ms; // default 250 (was TCP_TMR_INTERVAL)
|
||||
uint16_t rto_min_ms; // default 3000 (initial RTO in ms)
|
||||
uint16_t rto_max_ms; // default 0 (0=unlimited, cap in ms)
|
||||
|
||||
uint32_t ticks;
|
||||
uint8_t slowtmr_ctr;
|
||||
uint8_t slowtmr_ctr; // for last_timer check (incremented by fasttmr and slowtmr)
|
||||
uint8_t tmr_phase; // for even/odd slowtmr decision (incremented in tcp_tmr_cb)
|
||||
uint16_t iss_seed;
|
||||
uint16_t ip_id;
|
||||
uint16_t port_seed;
|
||||
|
||||
char trace_id; // 'A' or 'B' for trace dump identification
|
||||
struct tcp_trace_buf trace;
|
||||
|
||||
struct {
|
||||
uint32_t pbuf_fails; // pbuf_alloc → NULL
|
||||
uint32_t seg_fails; // tcp_create_segment → NULL (seg pool)
|
||||
uint32_t pcb_fails; // tcp_alloc → NULL (pcb pool)
|
||||
uint32_t pcb_listen_fails; // tcp_listen pool alloc fail
|
||||
uint32_t write_fails; // tcp_write → ERR_MEM
|
||||
uint32_t enq_fails; // tcp_enqueue_flags → ERR_MEM
|
||||
uint32_t rst_fails; // tcp_rst pbuf alloc fail
|
||||
uint32_t ack_fails; // tcp_send_empty_ack pbuf fail
|
||||
uint32_t probe_fails; // tcp_zero_window_probe pbuf fail
|
||||
uint32_t split_fails; // tcp_split_unsent_seg fail
|
||||
} stats;
|
||||
};
|
||||
|
||||
// Lifecycle
|
||||
struct lwip_tcp_ctx* lwip_tcp_init(struct UASYNC *ua, tcp_output_fn output, void *output_arg);
|
||||
void lwip_tcp_destroy(struct lwip_tcp_ctx *ctx);
|
||||
void lwip_tcp_set_timer(struct lwip_tcp_ctx *ctx,
|
||||
uint16_t interval_ms, uint16_t rto_min_ms, uint16_t rto_max_ms);
|
||||
|
||||
// Input: feed an IP packet payload (after IP header) to TCP
|
||||
void lwip_tcp_input(struct lwip_tcp_ctx *ctx, struct pbuf *p,
|
||||
|
||||
@@ -67,7 +67,7 @@ static struct tcp_seg *tcp_seg_copy_local(struct tcp_pcb *pcb, struct tcp_seg *s
|
||||
struct tcp_seg *cseg;
|
||||
if (!seg || !pcb || !pcb->ctx) return NULL;
|
||||
cseg = (struct tcp_seg *)memory_pool_alloc(pcb->ctx->seg_pool);
|
||||
if (!cseg) return NULL;
|
||||
if (!cseg) { pcb->ctx->stats.seg_fails++; DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_seg_copy_local: no memory"); return NULL; }
|
||||
memcpy(cseg, seg, sizeof(struct tcp_seg));
|
||||
pbuf_ref(cseg->p);
|
||||
return cseg;
|
||||
@@ -266,6 +266,13 @@ void lwip_tcp_input(struct lwip_tcp_ctx *ctx, struct pbuf *p,
|
||||
|
||||
tcp_input_pcb = pcb;
|
||||
err = tcp_process(pcb);
|
||||
|
||||
if (recv_flags & TF_GOT_FIN && pcb->refused_data == NULL) {
|
||||
if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) {
|
||||
pcb->rcv_wnd++;
|
||||
}
|
||||
}
|
||||
|
||||
if (err != LERR_ABRT) {
|
||||
if (recv_flags & TF_RESET) {
|
||||
TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, LERR_RST);
|
||||
@@ -299,7 +306,6 @@ void lwip_tcp_input(struct lwip_tcp_ctx *ctx, struct pbuf *p,
|
||||
if (pcb->refused_data != NULL) {
|
||||
pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN;
|
||||
} else {
|
||||
if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) pcb->rcv_wnd++;
|
||||
TCP_EVENT_CLOSED(pcb, err);
|
||||
if (err == LERR_ABRT) goto aborted;
|
||||
}
|
||||
@@ -539,6 +545,8 @@ static err_t tcp_process(struct tcp_pcb *pcb)
|
||||
dport, sport);
|
||||
}
|
||||
} else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
|
||||
lwip_tcp_trace_record(pcb->ctx, 'Y', seqno, 0, pcb->nrtx,
|
||||
(uint16_t)pcb->rto, pcb->rtime, (uint8_t)pcb->state);
|
||||
tcp_rexmit(pcb);
|
||||
}
|
||||
break;
|
||||
@@ -728,6 +736,9 @@ static void tcp_receive(struct tcp_pcb *pcb)
|
||||
|
||||
pcb->dupacks = 0;
|
||||
pcb->lastack = ackno;
|
||||
lwip_tcp_trace_record(pcb->ctx, 'A', ackno, (uint16_t)acked, pcb->cwnd,
|
||||
(uint16_t)pcb->rto, pcb->rtime, (uint8_t)pcb->state);
|
||||
if (g_tcp_trace) g_tcp_trace(pcb->ctx, 'A', ackno, (uint16_t)acked, pcb->cwnd, "ack");
|
||||
|
||||
if (pcb->state >= ESTABLISHED) {
|
||||
if (pcb->cwnd < pcb->ssthresh) {
|
||||
@@ -855,6 +866,11 @@ static void tcp_receive(struct tcp_pcb *pcb)
|
||||
pcb->rcv_wnd = (tcpwnd_size_t)(pcb->rcv_wnd - tcplen);
|
||||
|
||||
tcp_update_rcv_ann_wnd(pcb);
|
||||
if (inseg.p->tot_len > 0) {
|
||||
lwip_tcp_trace_record(pcb->ctx, 'D', seqno, inseg.p->tot_len, pcb->cwnd,
|
||||
(uint16_t)pcb->rto, pcb->rtime, (uint8_t)pcb->state);
|
||||
if (g_tcp_trace) g_tcp_trace(pcb->ctx, 'D', seqno, inseg.p->tot_len, pcb->cwnd, "data");
|
||||
}
|
||||
|
||||
if (inseg.p->tot_len > 0) {
|
||||
recv_data = inseg.p;
|
||||
|
||||
@@ -74,6 +74,17 @@ static void tcp_segs_free_local(struct tcp_pcb *pcb, struct tcp_seg *seg)
|
||||
// tcp_pbuf_prealloc — simplified (no oversize)
|
||||
#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length))
|
||||
|
||||
// pbuf alloc wrapper with error counting
|
||||
static struct pbuf *tcp_pbuf_alloc(struct tcp_pcb *pcb, uint16_t layer, uint16_t length)
|
||||
{
|
||||
struct pbuf *p = pbuf_alloc(layer, length);
|
||||
if (!p && pcb && pcb->ctx) {
|
||||
pcb->ctx->stats.pbuf_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_pbuf_alloc: out of memory (layer=%u len=%u)", layer, length);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// Forward
|
||||
static err_t tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
|
||||
|
||||
@@ -89,7 +100,8 @@ tcp_create_segment(const struct tcp_pcb *pcb, struct pbuf *p,
|
||||
|
||||
seg = (struct tcp_seg *)memory_pool_alloc(pcb->ctx->seg_pool);
|
||||
if (seg == NULL) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_create_segment: no memory");
|
||||
pcb->ctx->stats.seg_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_create_segment: no memory for seg");
|
||||
pbuf_free(p);
|
||||
return NULL;
|
||||
}
|
||||
@@ -127,12 +139,16 @@ tcp_write_checks(struct tcp_pcb *pcb, uint16_t len)
|
||||
}
|
||||
if (len == 0) return LERR_OK;
|
||||
if (len > pcb->snd_buf) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_write: too much data");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_write: too much data (len=%u > snd_buf=%u)", len, pcb->snd_buf);
|
||||
pcb->ctx->stats.write_fails++;
|
||||
tcp_set_flags(pcb, TF_NAGLEMEMERR);
|
||||
tcp_set_flags(pcb, TF_NAGLEMEMERR);
|
||||
return LERR_MEM;
|
||||
}
|
||||
if (pcb->snd_queuelen >= TCP_SND_QUEUELEN) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_write: too long queue");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_write: too long queue (queuelen=%u max=%u)", pcb->snd_queuelen, TCP_SND_QUEUELEN);
|
||||
pcb->ctx->stats.write_fails++;
|
||||
tcp_set_flags(pcb, TF_NAGLEMEMERR);
|
||||
tcp_set_flags(pcb, TF_NAGLEMEMERR);
|
||||
return LERR_MEM;
|
||||
}
|
||||
@@ -307,6 +323,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, uint16_t len, uint8_t apiflags)
|
||||
return LERR_OK;
|
||||
|
||||
memerr:
|
||||
pcb->ctx->stats.write_fails++;
|
||||
tcp_set_flags(pcb, TF_NAGLEMEMERR);
|
||||
if (concat_p != NULL) pbuf_free(concat_p);
|
||||
if (queue != NULL) tcp_segs_free_local(pcb, queue);
|
||||
@@ -340,6 +357,7 @@ tcp_split_unsent_seg(struct tcp_pcb *pcb, uint16_t split)
|
||||
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, (uint16_t)(remainder + optlen));
|
||||
if (p == NULL) {
|
||||
pcb->ctx->stats.split_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_split_unsent_seg: could not allocate pbuf");
|
||||
goto memerr;
|
||||
}
|
||||
@@ -420,14 +438,18 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, uint8_t flags)
|
||||
}
|
||||
optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(optflags, pcb);
|
||||
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, optlen);
|
||||
p = tcp_pbuf_alloc(pcb, PBUF_TRANSPORT, optlen);
|
||||
if (p == NULL) {
|
||||
pcb->ctx->stats.enq_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_enqueue_flags: pbuf_alloc failed (optlen=%u)", optlen);
|
||||
tcp_set_flags(pcb, TF_NAGLEMEMERR);
|
||||
return LERR_MEM;
|
||||
}
|
||||
|
||||
seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags);
|
||||
if (seg == NULL) {
|
||||
pcb->ctx->stats.enq_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_enqueue_flags: tcp_create_segment failed");
|
||||
tcp_set_flags(pcb, TF_NAGLEMEMERR);
|
||||
return LERR_MEM;
|
||||
}
|
||||
@@ -479,6 +501,9 @@ tcp_output(struct tcp_pcb *pcb)
|
||||
if (pcb->local_ip == 0) return LERR_RTE;
|
||||
|
||||
if ((ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len) > wnd) {
|
||||
lwip_tcp_trace_record(pcb->ctx, 'X', ntohl(seg->tcphdr->seqno), seg->len, wnd,
|
||||
(uint16_t)pcb->rto, pcb->rtime, (uint8_t)pcb->state);
|
||||
if (g_tcp_trace) g_tcp_trace(pcb->ctx, 'X', ntohl(seg->tcphdr->seqno), seg->len, wnd, "cwd");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_TRAFFIC, "TCP_OUTPUT cwnd limited: seqno_diff=%u seg_len=%u wnd=%u cwnd=%u snd_wnd=%u",
|
||||
ntohl(seg->tcphdr->seqno) - pcb->lastack, seg->len, wnd, pcb->cwnd, pcb->snd_wnd);
|
||||
if (wnd == pcb->snd_wnd && pcb->unacked == NULL && pcb->persist_backoff == 0) {
|
||||
@@ -506,6 +531,9 @@ tcp_output(struct tcp_pcb *pcb)
|
||||
}
|
||||
|
||||
err = tcp_output_segment(seg, pcb);
|
||||
lwip_tcp_trace_record(pcb->ctx, 'S', ntohl(seg->tcphdr->seqno), seg->len, wnd,
|
||||
(uint16_t)pcb->rto, pcb->rtime, (uint8_t)pcb->state);
|
||||
if (g_tcp_trace) g_tcp_trace(pcb->ctx, 'S', ntohl(seg->tcphdr->seqno), seg->len, wnd, "snt");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_TRAFFIC, "TCP_OUTPUT seg seqno=%u len=%u wnd=%u cwnd=%u ret=%d",
|
||||
ntohl(seg->tcphdr->seqno), seg->len, wnd, pcb->cwnd, err);
|
||||
if (err != LERR_OK) {
|
||||
@@ -656,7 +684,12 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
|
||||
|
||||
seg->tcphdr->chksum = 0;
|
||||
seg->tcphdr->chksum = htons(tcp_pseudo_checksum(pcb->local_ip, pcb->remote_ip,
|
||||
IP_PROTO_TCP, seg->p->tot_len, seg->p));
|
||||
IP_PROTO_TCP, seg->p->tot_len, seg->p));
|
||||
|
||||
if (TCPH_FLAGS(seg->tcphdr) & TCP_FIN) {
|
||||
lwip_tcp_trace_record(pcb->ctx, 'C', ntohl(seg->tcphdr->seqno), seg->len,
|
||||
pcb->snd_wnd, (uint16_t)pcb->rto, pcb->rtime, (uint8_t)pcb->state);
|
||||
}
|
||||
|
||||
if (pbuf_header(seg->p, (int16_t)sizeof(struct ip_hdr)) == 0) {
|
||||
iph = (struct ip_hdr *)seg->p->payload;
|
||||
@@ -686,7 +719,6 @@ err_t
|
||||
tcp_rexmit_rto_prepare(struct tcp_pcb *pcb)
|
||||
{
|
||||
struct tcp_seg *seg;
|
||||
|
||||
if (pcb == NULL) return LERR_VAL;
|
||||
if (pcb->unacked == NULL) return LERR_VAL;
|
||||
|
||||
@@ -713,6 +745,8 @@ tcp_rexmit_rto_commit(struct tcp_pcb *pcb)
|
||||
{
|
||||
if (pcb == NULL) return;
|
||||
if (pcb->nrtx < 0xFF) pcb->nrtx++;
|
||||
lwip_tcp_trace_record(pcb->ctx, 'R', pcb->lastack + 1, 0, pcb->cwnd,
|
||||
(uint16_t)pcb->rto, pcb->rtime, (uint8_t)pcb->state);
|
||||
tcp_output(pcb);
|
||||
}
|
||||
|
||||
@@ -763,6 +797,8 @@ tcp_rexmit_fast(struct tcp_pcb *pcb)
|
||||
if (pcb == NULL) return;
|
||||
if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) {
|
||||
if (tcp_rexmit(pcb) == LERR_OK) {
|
||||
lwip_tcp_trace_record(pcb->ctx, 'F', ntohl(pcb->unacked->tcphdr->seqno),
|
||||
pcb->unacked->len, pcb->ssthresh, (uint16_t)pcb->rto, pcb->rtime, (uint8_t)pcb->state);
|
||||
pcb->ssthresh = pcb->cwnd;
|
||||
if ((uint32_t)pcb->snd_wnd < pcb->cwnd) pcb->ssthresh = pcb->snd_wnd;
|
||||
pcb->ssthresh = (tcpwnd_size_t)(pcb->ssthresh / 2);
|
||||
@@ -909,6 +945,7 @@ tcp_rst(struct tcp_pcb *pcb, uint32_t seqno, uint32_t ackno,
|
||||
p = tcp_output_alloc_header_common(ackno, optlen, 0, htonl(seqno),
|
||||
local_port, remote_port, TCP_RST | TCP_ACK, wnd);
|
||||
if (p == NULL) {
|
||||
if (pcb) pcb->ctx->stats.rst_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_rst: could not allocate memory for pbuf");
|
||||
return;
|
||||
}
|
||||
@@ -932,6 +969,7 @@ tcp_send_empty_ack(struct tcp_pcb *pcb)
|
||||
|
||||
p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt));
|
||||
if (p == NULL) {
|
||||
pcb->ctx->stats.ack_fails++;
|
||||
tcp_set_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_output: (ACK) could not allocate pbuf");
|
||||
return LERR_BUF;
|
||||
@@ -978,12 +1016,15 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
|
||||
if (seg == NULL) return LERR_OK;
|
||||
|
||||
if (pcb->persist_probe < 0xFF) pcb->persist_probe++;
|
||||
lwip_tcp_trace_record(pcb->ctx, 'P', ntohl(seg->tcphdr->seqno), seg->len,
|
||||
0, (uint16_t)pcb->rto, pcb->rtime, (uint8_t)pcb->state);
|
||||
|
||||
is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
|
||||
len = is_fin ? 0 : 1;
|
||||
|
||||
p = tcp_output_alloc_header(pcb, optlen, len, seg->tcphdr->seqno);
|
||||
if (p == NULL) {
|
||||
pcb->ctx->stats.probe_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_zero_window_probe: no memory for pbuf");
|
||||
return LERR_MEM;
|
||||
}
|
||||
|
||||
@@ -222,6 +222,17 @@ uint32_t tcp_next_iss(struct tcp_pcb *pcb);
|
||||
uint16_t tcp_checksum(const void *data, uint16_t len);
|
||||
uint16_t tcp_ip_checksum(const struct ip_hdr *iph);
|
||||
|
||||
// Trace hook — set by test to capture internal TCP events (ring buffer)
|
||||
typedef void (*tcp_trace_fn)(struct lwip_tcp_ctx *ctx, char event, uint32_t seq, uint16_t len, uint16_t wnd, const char *msg);
|
||||
extern tcp_trace_fn g_tcp_trace;
|
||||
|
||||
void lwip_tcp_trace_record(struct lwip_tcp_ctx *ctx, char event, uint32_t seq,
|
||||
uint16_t len, uint16_t wnd, uint16_t rto, int16_t rtime, uint8_t state);
|
||||
void lwip_tcp_trace_dump(struct lwip_tcp_ctx *ctx);
|
||||
void lwip_tcp_trace_clear(struct lwip_tcp_ctx *ctx);
|
||||
void lwip_tcp_stats_dump(struct lwip_tcp_ctx *ctx);
|
||||
void lwip_tcp_stats_clear(struct lwip_tcp_ctx *ctx);
|
||||
|
||||
// PCB list management
|
||||
#define TCP_REG(pcbs, npcb) do { \
|
||||
(npcb)->next = *(pcbs); *(pcbs) = (npcb); \
|
||||
|
||||
Reference in new issue
Block a user