166 lines
5.9 KiB
C
Executable File
166 lines
5.9 KiB
C
Executable File
/**
|
|
* 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;
|
|
}
|