/** * tcp_output.c — TCP output engine + congestion control * * - Congestion control (RFC 5681): slow start, CA, fast retransmit, fast recovery * - RTO-based retransmission * - Nagle algorithm * - Window management */ #include "tcp_core.h" /* ══════════════════════════════════════════════════════════════════ * Build TCP options for outgoing segments * ══════════════════════════════════════════════════════════════════ */ int ng_tcp_build_options(uint8_t *buf, struct ng_tcp_stream *stream, int want_syn, int want_syn_ack) { int pos = 0; (void)want_syn_ack; if (want_syn || want_syn_ack) { /* MSS */ buf[pos++] = TCPOPT_MSS; buf[pos++] = TCPOLEN_MSS; buf[pos++] = (TCP_MSS_DEFAULT >> 8) & 0xFF; buf[pos++] = TCP_MSS_DEFAULT & 0xFF; /* Window Scale */ buf[pos++] = TCPOPT_NOP; buf[pos++] = TCPOPT_WSCALE; buf[pos++] = TCPOLEN_WSCALE; buf[pos++] = TCP_WSCALE_DEFAULT; /* SACK-permitted */ buf[pos++] = TCPOPT_NOP; buf[pos++] = TCPOPT_NOP; buf[pos++] = TCPOPT_SACKP; buf[pos++] = TCPOLEN_SACKP; /* 2 */ /* Timestamps */ buf[pos++] = TCPOPT_NOP; buf[pos++] = TCPOPT_NOP; buf[pos++] = TCPOPT_TIMESTAMP; buf[pos++] = TCPOLEN_TIMESTAMP; uint32_t ts = (uint32_t)(rte_get_timer_cycles() / (rte_get_timer_hz() / 1000000)); buf[pos++] = (ts >> 24) & 0xFF; buf[pos++] = (ts >> 16) & 0xFF; buf[pos++] = (ts >> 8) & 0xFF; buf[pos++] = ts & 0xFF; buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = 0; /* TS Echo = 0 */ } /* Pad to 4-byte boundary */ while (pos & 0x3) buf[pos++] = TCPOPT_NOP; return pos; } /* ══════════════════════════════════════════════════════════════════ * Congestion control * ══════════════════════════════════════════════════════════════════ */ void ng_tcp_congestion_ack(struct ng_tcp_stream *s) { if (s->in_fast_recovery) { s->cwnd = s->ssthresh; s->in_fast_recovery = 0; return; } if (s->cwnd < s->ssthresh) { /* Slow start: exponential */ s->cwnd += s->rcv_mss; } else { /* Congestion avoidance: linear */ s->cwnd += (s->rcv_mss * s->rcv_mss) / s->cwnd; if (s->cwnd < TCP_INITIAL_CWND) s->cwnd = TCP_INITIAL_CWND; } } void ng_tcp_congestion_loss(struct ng_tcp_stream *s) { /* Fast retransmit + fast recovery */ uint32_t inflight = (s->snd_nxt > s->snd_una) ? (s->snd_nxt - s->snd_una) : 0; s->ssthresh = inflight / 2; if (s->ssthresh < 2 * s->rcv_mss) s->ssthresh = 2 * s->rcv_mss; s->cwnd = s->ssthresh + 3 * s->rcv_mss; s->in_fast_recovery = 1; s->recover = s->snd_nxt; /* snd_max in practice */ printf("[TCP] Fast retransmit: cwnd=%u ssthresh=%u\n", s->cwnd, s->ssthresh); } void ng_tcp_congestion_rto(struct ng_tcp_stream *s) { s->ssthresh = (s->snd_nxt - s->snd_una) / 2; if (s->ssthresh < 2 * s->rcv_mss) s->ssthresh = 2 * s->rcv_mss; s->cwnd = s->rcv_mss; /* reset to 1 MSS */ s->in_fast_recovery = 0; printf("[TCP] RTO: cwnd reset to %u\n", s->cwnd); } /* ══════════════════════════════════════════════════════════════════ * Process incoming ACK with SACK * ══════════════════════════════════════════════════════════════════ */ void ng_tcp_process_ack(struct ng_tcp_stream *stream, uint32_t ack, uint32_t ack_end) { (void)ack_end; /* Free retransmit ring entries that are now acknowledged */ int count = rte_ring_count(stream->retransmit_ring); for (int i = 0; i < count; i++) { struct ng_tcp_fragment *f = NULL; rte_ring_mc_dequeue(stream->retransmit_ring, (void **)&f); if (f == NULL) continue; if (SEQ_LEQ(f->seqnum + f->length, ack)) { /* Fully acknowledged — return to pool */ frag_free(f); } else { /* Not yet acked — re-enqueue */ rte_ring_mp_enqueue(stream->retransmit_ring, f); } } } /* ══════════════════════════════════════════════════════════════════ * ng_tcp_output — per-connection send processing * (Called from ng_tcp_out in tcp_core.c which handles per-stream dequeue) * Here we handle the scheduling logic that ng_tcp_out doesn't cover: * zero-window probes, pending retransmits, etc. * ══════════════════════════════════════════════════════════════════ */ int ng_tcp_output(struct rte_mempool *mbuf_pool) { (void)mbuf_pool; /* The main output logic is in ng_tcp_out (tcp_core.c). * This function is a placeholder for output scheduling: * - Zero-window probing * - Batch send across all connections * Currently handled inline in pkt_process timer loop. */ return 0; }