Files
dpdk-ws-client/src/tcp_core.c
T
wxjandClaude d57a0260e8 stable: revert multi-core, restore working single-core optimizations
Keep P0-P4 optimizations:
- Fragment mempool (frag_alloc/frag_free)
- WS frame alloca() dynamic stack
- Fragment read_offset (eliminate memmove)
- nclose busy-poll (replace cond_timedwait)
- Remove dead ng_tcp_out()

Revert experimental multi-core changes (moved to feature/multi-core):
- Worker arrays, TLS context switching
- RX hash distribution, TX multi-ring collection

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-30 16:38:55 +00:00

893 lines
33 KiB
C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* tcp_core.c — Production-grade DPDK TCP/IP stack core
*
* Contains:
* - Hash-table-based TCB management (O(1) lookup)
* - Enhanced TCP packet encoding (with option bytes)
* - ARP / ICMP helpers
* - pkt_process loop with per-TCB timer maintenance
* - Port initialization
*/
#include "tcp_core.h"
#include <sched.h>
/* ══════════════════════════════════════════════════════════════════
* Globals
* ══════════════════════════════════════════════════════════════════ */
int gDpdkPortId = 0;
uint32_t gLocalIp = MAKE_IPV4_ADDR(192, 168, 0, 115);
uint8_t gSrcMac[RTE_ETHER_ADDR_LEN];
uint8_t gDefaultArpMac[RTE_ETHER_ADDR_LEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
/* Latency trace buffer */
struct lat_trace g_lat_trace[LAT_TRACE_SIZE];
int g_lat_trace_idx = 0;
static struct ng_tcp_table *tInst = NULL;
static struct inout_ring *rInst = NULL;
/* Fragment mempools (replaces rte_malloc for hot-path allocations) */
struct rte_mempool *g_frag_pool = NULL;
struct rte_mempool *g_ofo_pool = NULL;
/* fd → TCB index array (O(1) lookup from socket fd) */
static struct ng_tcp_stream *fd_to_tcb[MAX_FD_ARRAY];
/* ══════════════════════════════════════════════════════════════════
* TCP table singleton
* ══════════════════════════════════════════════════════════════════ */
struct ng_tcp_table *tcpInstance(void)
{
if (tInst == NULL)
{
tInst = rte_malloc("ng_tcp_table", sizeof(struct ng_tcp_table), 0);
if (tInst == NULL)
rte_exit(EXIT_FAILURE, "tcpInstance malloc failed\n");
memset(tInst, 0, sizeof(struct ng_tcp_table));
}
return tInst;
}
struct inout_ring *ringInstance(void)
{
if (rInst == NULL)
{
rInst = rte_malloc("inout_ring", sizeof(struct inout_ring), 0);
memset(rInst, 0, sizeof(struct inout_ring));
}
return rInst;
}
/* ══════════════════════════════════════════════════════════════════
* FD ↔ TCB index (O(1))
* ══════════════════════════════════════════════════════════════════ */
void ng_tcp_fd_set(int fd, struct ng_tcp_stream *s)
{
if (fd >= 0 && fd < MAX_FD_ARRAY)
fd_to_tcb[fd] = s;
}
void ng_tcp_fd_clear(int fd)
{
if (fd >= 0 && fd < MAX_FD_ARRAY)
fd_to_tcb[fd] = NULL;
}
struct ng_tcp_stream *ng_tcp_fd_get(int fd)
{
if (fd >= 0 && fd < MAX_FD_ARRAY)
return fd_to_tcb[fd];
return NULL;
}
/* ══════════════════════════════════════════════════════════════════
* Hash table operations
* ══════════════════════════════════════════════════════════════════ */
struct ng_tcp_stream *ng_tcp_stream_search(uint32_t sip, uint32_t dip,
uint16_t sport, uint16_t dport)
{
uint32_t h_fwd = tcp_hash4(sip, dip, sport, dport);
uint32_t h_rev = tcp_hash4(dip, sip, dport, sport);
struct ng_tcp_table *table = tcpInstance();
struct ng_tcp_stream *s;
/* Search forward hash bucket */
for (s = table->htable[h_fwd]; s != NULL; s = s->ht_next)
{
if ((s->sip == sip && s->dip == dip &&
s->sport == sport && s->dport == dport) ||
(s->sip == dip && s->dip == sip &&
s->sport == dport && s->dport == sport))
return s;
}
/* If reverse bucket is different, search it too */
if (h_rev != h_fwd)
{
for (s = table->htable[h_rev]; s != NULL; s = s->ht_next)
{
if ((s->sip == sip && s->dip == dip &&
s->sport == sport && s->dport == dport) ||
(s->sip == dip && s->dip == sip &&
s->sport == dport && s->dport == sport))
return s;
}
}
return NULL;
}
struct ng_tcp_stream *ng_tcp_stream_search_listener(uint16_t dport)
{
struct ng_tcp_table *table = tcpInstance();
struct ng_tcp_stream *s;
for (s = table->listeners; s != NULL; s = s->ht_next)
{
if (s->dport == dport && s->status == NG_TCP_STATUS_LISTEN)
return s;
}
return NULL;
}
void ng_tcp_hash_insert(struct ng_tcp_stream *stream)
{
uint32_t h = tcp_hash4(stream->sip, stream->dip,
stream->sport, stream->dport);
struct ng_tcp_table *table = tcpInstance();
stream->ht_next = table->htable[h];
table->htable[h] = stream;
table->count++;
}
void ng_tcp_hash_remove(struct ng_tcp_stream *stream)
{
uint32_t h = tcp_hash4(stream->sip, stream->dip,
stream->sport, stream->dport);
struct ng_tcp_table *table = tcpInstance();
struct ng_tcp_stream **pp;
for (pp = &table->htable[h]; *pp != NULL; pp = &(*pp)->ht_next)
{
if (*pp == stream)
{
*pp = stream->ht_next;
table->count--;
return;
}
}
}
/* ══════════════════════════════════════════════════════════════════
* Stream create / destroy
* ══════════════════════════════════════════════════════════════════ */
struct ng_tcp_stream *ng_tcp_stream_create(uint32_t sip, uint32_t dip,
uint16_t sport, uint16_t dport)
{
struct ng_tcp_stream *s = rte_malloc("ng_tcp_stream",
sizeof(struct ng_tcp_stream), 0);
if (s == NULL)
return NULL;
memset(s, 0, sizeof(struct ng_tcp_stream));
s->sip = sip;
s->dip = dip;
s->sport = sport;
s->dport = dport;
s->protocol = IPPROTO_TCP;
s->fd = -1;
s->status = NG_TCP_STATUS_CLOSED;
/* Sequence numbers (random ISN) */
uint32_t seed = time(NULL);
s->iss = rand_r(&seed) % TCP_MAX_SEQ;
s->snd_una = s->iss;
s->snd_nxt = s->iss;
s->rcv_nxt = 0;
/* Congestion control defaults */
s->cwnd = TCP_INITIAL_CWND;
s->ssthresh = TCP_INITIAL_WINDOW * 128; /* effectively unbounded */
s->mss = TCP_MSS_DEFAULT;
s->rcv_mss = TCP_MSS_DEFAULT;
/* Timers */
s->rto = TCP_RTO_INITIAL;
s->srtt = 0;
s->rttvar = 0;
/* Send buffer */
s->snd_buf = rte_malloc("snd_buf", TCP_SENDBUF_SIZE, 0);
if (s->snd_buf == NULL)
{
rte_free(s);
return NULL;
}
memset(s->snd_buf, 0, TCP_SENDBUF_SIZE);
/* Receive buffer accounting */
s->rcv_buf_max = TCP_RECVBUF_SIZE;
s->rcv_buf_avail = TCP_RECVBUF_SIZE;
s->rcv_wnd = TCP_INITIAL_WINDOW;
/* Rings */
s->sndbuf = rte_ring_create("tcp_snd", RING_SIZE,
rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);
s->rcvbuf = rte_ring_create("tcp_rcv", RING_SIZE,
rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);
s->retransmit_ring = rte_ring_create("tcp_rtx", RING_SIZE,
rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);
/* Condition variable */
pthread_condattr_t cattr;
pthread_condattr_init(&cattr);
pthread_cond_init(&s->cond, &cattr);
pthread_condattr_destroy(&cattr);
pthread_mutexattr_t mattr;
pthread_mutexattr_init(&mattr);
pthread_mutex_init(&s->mutex, &mattr);
pthread_mutexattr_destroy(&mattr);
rte_memcpy(s->localmac, gSrcMac, RTE_ETHER_ADDR_LEN);
/* Insert into hash table */
ng_tcp_hash_insert(s);
/* Defer active-list add until state is set (caller does ng_tcp_stream_activate) */
return s;
}
/* forward declarations for active list helpers */
static void ng_tcp_active_add(struct ng_tcp_stream *s);
static void ng_tcp_active_remove(struct ng_tcp_stream *s);
static int ng_tcp_is_active(struct ng_tcp_stream *s);
void ng_tcp_stream_destroy(struct ng_tcp_stream *stream)
{
if (stream == NULL)
return;
/* Remove from active list */
ng_tcp_active_remove(stream);
/* Remove from hash table */
ng_tcp_hash_remove(stream);
/* Clear fd mapping */
if (stream->fd >= 0)
ng_tcp_fd_clear(stream->fd);
/* Free out-of-order segments */
struct ofo_segment *ofo = stream->ofo_head;
while (ofo)
{
struct ofo_segment *next = ofo->next;
ofo_free(ofo);
ofo = next;
}
/* Free rings */
if (stream->sndbuf)
rte_ring_free(stream->sndbuf);
if (stream->rcvbuf)
rte_ring_free(stream->rcvbuf);
if (stream->retransmit_ring)
rte_ring_free(stream->retransmit_ring);
/* Free send buffer */
if (stream->snd_buf)
rte_free(stream->snd_buf);
/* Destroy synchronization primitives */
pthread_mutex_destroy(&stream->mutex);
pthread_cond_destroy(&stream->cond);
rte_free(stream);
}
void ng_tcp_stream_set_status(struct ng_tcp_stream *stream, NG_TCP_STATUS s)
{
stream->status = s;
}
/* Active list: track connections that need timer check + output processing */
static void ng_tcp_active_add(struct ng_tcp_stream *s)
{
struct ng_tcp_table *t = tcpInstance();
if (s->active_next || s->active_prev || t->active_head == s) return;
s->active_next = t->active_head;
s->active_prev = NULL;
if (t->active_head) t->active_head->active_prev = s;
t->active_head = s;
}
static void ng_tcp_active_remove(struct ng_tcp_stream *s)
{
struct ng_tcp_table *t = tcpInstance();
if (s->active_prev) s->active_prev->active_next = s->active_next;
if (s->active_next) s->active_next->active_prev = s->active_prev;
if (t->active_head == s) t->active_head = s->active_next;
s->active_next = s->active_prev = NULL;
}
static int ng_tcp_is_active(struct ng_tcp_stream *s)
{
switch (s->status) {
case NG_TCP_STATUS_SYN_SENT:
case NG_TCP_STATUS_SYN_RCVD:
case NG_TCP_STATUS_ESTABLISHED:
case NG_TCP_STATUS_CLOSE_WAIT:
case NG_TCP_STATUS_FIN_WAIT_1:
case NG_TCP_STATUS_FIN_WAIT_2:
case NG_TCP_STATUS_LAST_ACK:
case NG_TCP_STATUS_TIME_WAIT: /* needed for timer-based cleanup */
return 1;
default:
return 0;
}
}
/* Call when TCB enters active state (after nconnect or accept) */
void ng_tcp_stream_activate(struct ng_tcp_stream *s)
{
if (ng_tcp_is_active(s))
ng_tcp_active_add(s);
}
/* ══════════════════════════════════════════════════════════════════
* TCP ACK helper (pure ACK — no data, no options for now)
* ══════════════════════════════════════════════════════════════════ */
void ng_tcp_send_ack(struct ng_tcp_stream *stream)
{
struct ng_tcp_fragment *f = frag_alloc(0);
if (f == NULL)
return;
f->sport = stream->sport;
f->dport = stream->dport;
f->seqnum = stream->snd_nxt;
f->acknum = stream->rcv_nxt;
f->tcp_flags = RTE_TCP_ACK_FLAG;
f->windows = (stream->rcv_buf_avail > 65535) ? 65535
: (uint16_t)stream->rcv_buf_avail;
f->hdrlen_off = 0x50;
rte_ring_mp_enqueue(stream->sndbuf, f);
}
void ng_tcp_send_rst(struct ng_tcp_stream *stream, uint32_t seq, uint32_t ack)
{
struct ng_tcp_fragment *f = frag_alloc(0);
if (f == NULL)
return;
f->sport = stream->sport;
f->dport = stream->dport;
f->seqnum = seq;
f->acknum = 0;
f->tcp_flags = RTE_TCP_RST_FLAG;
f->windows = 0;
f->hdrlen_off = 0x50;
rte_ring_mp_enqueue(stream->sndbuf, f);
stream->stat_rst_sent++;
}
void ng_tcp_send_fin(struct ng_tcp_stream *stream)
{
struct ng_tcp_fragment *f = frag_alloc(0);
if (f == NULL)
return;
f->sport = stream->sport;
f->dport = stream->dport;
f->seqnum = stream->snd_nxt;
f->acknum = stream->rcv_nxt;
f->tcp_flags = RTE_TCP_FIN_FLAG | RTE_TCP_ACK_FLAG;
f->windows = (stream->rcv_buf_avail > 65535) ? 65535
: (uint16_t)stream->rcv_buf_avail;
f->hdrlen_off = 0x50;
rte_ring_mp_enqueue(stream->sndbuf, f);
}
/* ══════════════════════════════════════════════════════════════════
* TCP packet encoding (with option bytes in fragment->option[])
* ══════════════════════════════════════════════════════════════════ */
static int ng_encode_tcp_pkt(uint8_t *msg, uint32_t sip, uint32_t dip,
uint8_t *srcmac, uint8_t *dstmac,
struct ng_tcp_fragment *fragment)
{
int opt_bytes = fragment->optlen * sizeof(uint32_t);
unsigned total_len = fragment->length +
sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) +
sizeof(struct rte_tcp_hdr) + opt_bytes;
/* Ethernet */
struct rte_ether_hdr *eth = (struct rte_ether_hdr *)msg;
rte_memcpy(eth->src_addr.addr_bytes, srcmac, RTE_ETHER_ADDR_LEN);
rte_memcpy(eth->dst_addr.addr_bytes, dstmac, RTE_ETHER_ADDR_LEN);
eth->ether_type = htons(RTE_ETHER_TYPE_IPV4);
/* IP */
struct rte_ipv4_hdr *ip = (struct rte_ipv4_hdr *)(eth + 1);
ip->version_ihl = 0x45;
ip->type_of_service = 0;
ip->total_length = htons(total_len - sizeof(struct rte_ether_hdr));
ip->packet_id = 0;
ip->fragment_offset = 0;
ip->time_to_live = 64;
ip->next_proto_id = IPPROTO_TCP;
ip->src_addr = sip;
ip->dst_addr = dip;
ip->hdr_checksum = 0;
ip->hdr_checksum = rte_ipv4_cksum(ip);
/* TCP */
struct rte_tcp_hdr *tcp = (struct rte_tcp_hdr *)(ip + 1);
tcp->src_port = fragment->sport;
tcp->dst_port = fragment->dport;
tcp->sent_seq = htonl(fragment->seqnum);
tcp->recv_ack = htonl(fragment->acknum);
tcp->data_off = fragment->hdrlen_off; /* already includes options */
tcp->rx_win = fragment->windows;
tcp->tcp_urp = fragment->tcp_urp;
tcp->tcp_flags = fragment->tcp_flags;
/* TCP options */
if (opt_bytes > 0)
{
uint8_t *optptr = (uint8_t *)(tcp + 1);
uint8_t *raw = (uint8_t *)fragment->option;
rte_memcpy(optptr, raw, opt_bytes);
}
/* Payload */
if (fragment->length > 0)
{
uint8_t *payload = (uint8_t *)(tcp + 1) + opt_bytes;
rte_memcpy(payload, fragment->data, fragment->length);
}
/* TCP checksum */
tcp->cksum = 0;
tcp->cksum = rte_ipv4_udptcp_cksum(ip, tcp);
return 0;
}
struct rte_mbuf *ng_tcp_pkt(struct rte_mempool *mbuf_pool,
uint32_t sip, uint32_t dip,
uint8_t *srcmac, uint8_t *dstmac,
struct ng_tcp_fragment *fragment)
{
int opt_bytes = fragment->optlen * sizeof(uint32_t);
unsigned total_len = fragment->length +
sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) +
sizeof(struct rte_tcp_hdr) + opt_bytes;
struct rte_mbuf *mbuf = rte_pktmbuf_alloc(mbuf_pool);
if (!mbuf)
return NULL;
mbuf->pkt_len = total_len;
mbuf->data_len = total_len;
uint8_t *pktdata = rte_pktmbuf_mtod(mbuf, uint8_t *);
ng_encode_tcp_pkt(pktdata, sip, dip, srcmac, dstmac, fragment);
return mbuf;
}
/* ══════════════════════════════════════════════════════════════════
* Note: ng_tcp_out() removed. Output is handled in pkt_process()
* via active_head linked list (Phase 2+3), avoiding O(256) scan.
* ══════════════════════════════════════════════════════════════════ */
/* ══════════════════════════════════════════════════════════════════
* ARP / ICMP packet helpers
* ══════════════════════════════════════════════════════════════════ */
static int ng_encode_arp_pkt(uint8_t *msg, uint16_t opcode,
uint8_t *dst_mac, uint32_t sip, uint32_t dip)
{
struct rte_ether_hdr *eth = (struct rte_ether_hdr *)msg;
rte_memcpy(eth->src_addr.addr_bytes, gSrcMac, RTE_ETHER_ADDR_LEN);
rte_memcpy(eth->dst_addr.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN);
eth->ether_type = htons(RTE_ETHER_TYPE_ARP);
struct rte_arp_hdr *arp = (struct rte_arp_hdr *)(eth + 1);
arp->arp_hardware = htons(1);
arp->arp_protocol = htons(RTE_ETHER_TYPE_IPV4);
arp->arp_hlen = RTE_ETHER_ADDR_LEN;
arp->arp_plen = sizeof(uint32_t);
arp->arp_opcode = htons(opcode);
rte_memcpy(arp->arp_data.arp_sha.addr_bytes, gSrcMac, RTE_ETHER_ADDR_LEN);
rte_memcpy(arp->arp_data.arp_tha.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN);
arp->arp_data.arp_sip = sip;
arp->arp_data.arp_tip = dip;
return 0;
}
struct rte_mbuf *ng_send_arp(struct rte_mempool *mbuf_pool,
uint16_t opcode, uint8_t *dst_mac,
uint32_t sip, uint32_t dip)
{
const unsigned total_len = sizeof(struct rte_ether_hdr) +
sizeof(struct rte_arp_hdr);
struct rte_mbuf *mbuf = rte_pktmbuf_alloc(mbuf_pool);
if (!mbuf)
return NULL;
mbuf->pkt_len = total_len;
mbuf->data_len = total_len;
uint8_t *pktdata = rte_pktmbuf_mtod(mbuf, uint8_t *);
ng_encode_arp_pkt(pktdata, opcode, dst_mac, sip, dip);
return mbuf;
}
static struct rte_mbuf *ng_send_icmp(struct rte_mempool *mbuf_pool,
uint8_t *dst_mac, uint32_t sip, uint32_t dip,
uint16_t id, uint16_t seq)
{
const unsigned total_len = sizeof(struct rte_ether_hdr) +
sizeof(struct rte_ipv4_hdr) +
sizeof(struct rte_icmp_hdr);
struct rte_mbuf *mbuf = rte_pktmbuf_alloc(mbuf_pool);
if (!mbuf)
return NULL;
mbuf->pkt_len = total_len;
mbuf->data_len = total_len;
uint8_t *msg = rte_pktmbuf_mtod(mbuf, uint8_t *);
struct rte_ether_hdr *eth = (struct rte_ether_hdr *)msg;
rte_memcpy(eth->src_addr.addr_bytes, gSrcMac, RTE_ETHER_ADDR_LEN);
rte_memcpy(eth->dst_addr.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN);
eth->ether_type = htons(RTE_ETHER_TYPE_IPV4);
struct rte_ipv4_hdr *ip = (struct rte_ipv4_hdr *)(eth + 1);
ip->version_ihl = 0x45;
ip->type_of_service = 0;
ip->total_length = htons(sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_icmp_hdr));
ip->packet_id = 0;
ip->fragment_offset = 0;
ip->time_to_live = 64;
ip->next_proto_id = IPPROTO_ICMP;
ip->src_addr = sip;
ip->dst_addr = dip;
ip->hdr_checksum = 0;
ip->hdr_checksum = rte_ipv4_cksum(ip);
struct rte_icmp_hdr *icmp = (struct rte_icmp_hdr *)(ip + 1);
icmp->icmp_type = RTE_ICMP_TYPE_ECHO_REPLY;
icmp->icmp_code = 0;
icmp->icmp_ident = id;
icmp->icmp_seq_nb = seq;
icmp->icmp_cksum = 0;
icmp->icmp_cksum = rte_ipv4_udptcp_cksum(ip, (const void *)icmp);
return mbuf;
}
/* ══════════════════════════════════════════════════════════════════
* ng_tcp_process — main incoming packet dispatch
* ══════════════════════════════════════════════════════════════════ */
int ng_tcp_process(struct rte_mbuf *tcpmbuf)
{
struct rte_ipv4_hdr *iphdr = rte_pktmbuf_mtod_offset(tcpmbuf,
struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));
struct rte_tcp_hdr *tcphdr = (struct rte_tcp_hdr *)(iphdr + 1);
int tcplen = ntohs(iphdr->total_length) - sizeof(struct rte_ipv4_hdr);
if (tcplen < (int)sizeof(struct rte_tcp_hdr))
return 0; /* truncated */
/* Checksum validation */
uint16_t cksum = rte_ipv4_udptcp_cksum(iphdr, (const void *)tcphdr);
if (cksum != 0 && cksum != 0xFFFF)
return 0; /* bad checksum — drop silently */
uint32_t sip = iphdr->src_addr;
uint32_t dip = iphdr->dst_addr;
uint16_t sport = tcphdr->src_port;
uint16_t dport = tcphdr->dst_port;
uint8_t flags = tcphdr->tcp_flags;
/* Look up existing stream */
struct ng_tcp_stream *stream = ng_tcp_stream_search(sip, dip, sport, dport);
if (stream == NULL)
{
/* Try listener on SYN */
if (flags & RTE_TCP_SYN_FLAG)
{
struct ng_tcp_stream *listener = ng_tcp_stream_search_listener(dport);
if (listener)
{
ng_tcp_handle_listen(listener, tcphdr, iphdr, tcplen);
}
}
/* If RST and no stream, ignore */
return 0;
}
/* Update last receive timestamp */
stream->last_rcv_tsc = rte_get_timer_cycles();
/* Parse TCP options */
uint8_t hdrlen = (tcphdr->data_off & 0xF0) >> 2;
int optlen = hdrlen - sizeof(struct rte_tcp_hdr);
struct tcp_options opts;
memset(&opts, 0, sizeof(opts));
if (optlen > 0)
{
uint8_t *optptr = (uint8_t *)(tcphdr + 1);
ng_tcp_parse_options(optptr, optlen, &opts);
}
/* Dispatch by state */
switch (stream->status)
{
case NG_TCP_STATUS_LISTEN:
ng_tcp_handle_listen(stream, tcphdr, iphdr, tcplen);
break;
case NG_TCP_STATUS_SYN_SENT:
ng_tcp_handle_synsent(stream, tcphdr, iphdr, tcplen);
break;
case NG_TCP_STATUS_SYN_RCVD:
ng_tcp_handle_synrcvd(stream, tcphdr, iphdr, tcplen);
break;
case NG_TCP_STATUS_ESTABLISHED:
case NG_TCP_STATUS_FIN_WAIT_1:
case NG_TCP_STATUS_FIN_WAIT_2:
case NG_TCP_STATUS_CLOSING:
case NG_TCP_STATUS_TIME_WAIT:
case NG_TCP_STATUS_CLOSE_WAIT:
case NG_TCP_STATUS_LAST_ACK:
ng_tcp_input(stream, tcphdr, iphdr, tcplen);
break;
default:
break;
}
stream->stat_seg_rcvd++;
return 0;
}
/* ══════════════════════════════════════════════════════════════════
* pkt_process — protocol processing loop (lcore 1..N)
* ══════════════════════════════════════════════════════════════════ */
int pkt_process(void *arg)
{
struct sched_param sp = { .sched_priority = 90 };
sched_setscheduler(0, SCHED_FIFO, &sp);
struct rte_mempool *mbuf_pool = (struct rte_mempool *)arg;
struct inout_ring *ring = ringInstance();
while (1)
{
/* ── Phase 1: RX processing ── */
struct rte_mbuf *mbufs[BURST_SIZE];
unsigned num_recvd = rte_ring_mc_dequeue_burst(ring->in,
(void **)mbufs, BURST_SIZE, NULL);
for (unsigned i = 0; i < num_recvd; i++)
{
struct rte_ether_hdr *ehdr = rte_pktmbuf_mtod(mbufs[i],
struct rte_ether_hdr *);
/* ── ARP ── */
if (ehdr->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_ARP))
{
struct rte_arp_hdr *ahdr = rte_pktmbuf_mtod_offset(mbufs[i],
struct rte_arp_hdr *, sizeof(struct rte_ether_hdr));
if (ahdr->arp_data.arp_tip == gLocalIp)
{
if (ahdr->arp_opcode == rte_cpu_to_be_16(RTE_ARP_OP_REQUEST))
{
struct rte_mbuf *ab = ng_send_arp(mbuf_pool,
RTE_ARP_OP_REPLY, ahdr->arp_data.arp_sha.addr_bytes,
ahdr->arp_data.arp_tip, ahdr->arp_data.arp_sip);
if (ab)
rte_ring_mp_enqueue_burst(ring->out, (void **)&ab, 1, NULL);
}
else if (ahdr->arp_opcode == rte_cpu_to_be_16(RTE_ARP_OP_REPLY))
{
uint8_t *hw = ng_get_dst_macaddr(ahdr->arp_data.arp_sip);
if (hw == NULL)
ng_arp_table_insert(ahdr->arp_data.arp_sip,
ahdr->arp_data.arp_sha.addr_bytes,
ARP_ENTRY_STATUS_DYNAMIC);
}
}
rte_pktmbuf_free(mbufs[i]);
continue;
}
if (ehdr->ether_type != rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
continue;
struct rte_ipv4_hdr *iphdr = rte_pktmbuf_mtod_offset(mbufs[i],
struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));
/* TCP */
if (iphdr->next_proto_id == IPPROTO_TCP)
{
lat_trace_add(2); /* pkt_process picked up TCP packet */
ng_tcp_process(mbufs[i]);
}
/* ICMP echo */
if (iphdr->next_proto_id == IPPROTO_ICMP)
{
struct rte_icmp_hdr *icmphdr = (struct rte_icmp_hdr *)(iphdr + 1);
if (icmphdr->icmp_type == RTE_ICMP_TYPE_ECHO_REQUEST)
{
struct rte_mbuf *txbuf = ng_send_icmp(mbuf_pool,
ehdr->src_addr.addr_bytes,
iphdr->dst_addr, iphdr->src_addr,
icmphdr->icmp_ident, icmphdr->icmp_seq_nb);
if (txbuf)
rte_ring_mp_enqueue_burst(ring->out, (void **)&txbuf, 1, NULL);
rte_pktmbuf_free(mbufs[i]);
}
}
}
/* ── Phase 2+3: Timer + Output (only iterate active connections) ── */
{
uint64_t now = rte_get_timer_cycles();
struct ng_tcp_table *table = tcpInstance();
struct ng_tcp_stream *s = table->active_head;
while (s)
{
struct ng_tcp_stream *next = s->active_next;
/* Timer check */
ng_tcp_check_timers(s);
if (s->status == NG_TCP_STATUS_TIME_WAIT)
{
uint64_t elapsed_us = (now - s->timewait_start) * 1000000 / rte_get_timer_hz();
if (elapsed_us >= TCP_TIME_WAIT_USEC)
{
ng_tcp_stream_destroy(s);
s = next;
continue;
}
}
/* Output: send pending fragments for this connection */
struct ng_tcp_fragment *f = NULL;
if (rte_ring_mc_dequeue(s->sndbuf, (void **)&f) >= 0 && f)
{
uint8_t *dstmac = s->dst_mac_valid ? s->dst_mac
: ng_get_dst_macaddr(s->dip);
if (dstmac == NULL)
{
uint64_t ts = rte_get_timer_cycles();
uint64_t arp_int = rte_get_timer_hz() / 10;
if (s->last_arp_tsc == 0 || ts - s->last_arp_tsc > arp_int) {
s->last_arp_tsc = ts;
struct rte_mbuf *ab = ng_send_arp(mbuf_pool,
RTE_ARP_OP_REQUEST, gDefaultArpMac, s->sip, s->dip);
if (ab) rte_ring_mp_enqueue_burst(ring->out, (void **)&ab, 1, NULL);
}
rte_ring_mp_enqueue(s->sndbuf, f);
}
else
{
struct rte_mbuf *mbuf = ng_tcp_pkt(mbuf_pool,
s->sip, s->dip, s->localmac, dstmac, f);
if (mbuf) {
rte_ring_mp_enqueue_burst(ring->out, (void **)&mbuf, 1, NULL);
s->stat_seg_sent++;
}
if (f->length > 0)
rte_ring_mp_enqueue(s->retransmit_ring, f);
else
frag_free(f);
}
}
/* Remove from active list if no longer active */
if (!ng_tcp_is_active(s))
ng_tcp_active_remove(s);
s = next;
}
}
}
return 0;
}
/* ══════════════════════════════════════════════════════════════════
* Fragment mempool initialization
* ══════════════════════════════════════════════════════════════════ */
void ng_frag_pool_init(void)
{
unsigned socket_id = rte_socket_id();
/* Fragment pool: struct + max MSS payload (MP/MC safe, lock-free) */
char name_frag[64];
snprintf(name_frag, sizeof(name_frag), "frag_pool_%u", socket_id);
g_frag_pool = rte_mempool_create(name_frag,
FRAG_POOL_SIZE, FRAG_ELT_SIZE, 256, 0,
NULL, NULL, NULL, NULL, socket_id, 0);
if (g_frag_pool == NULL)
rte_exit(EXIT_FAILURE, "frag_pool create failed\n");
/* OFO segment pool */
char name_ofo[64];
snprintf(name_ofo, sizeof(name_ofo), "ofo_pool_%u", socket_id);
g_ofo_pool = rte_mempool_create(name_ofo,
OFO_POOL_SIZE, OFO_ELT_SIZE, 64, 0,
NULL, NULL, NULL, NULL, socket_id, 0);
if (g_ofo_pool == NULL)
rte_exit(EXIT_FAILURE, "ofo_pool create failed\n");
printf("[POOL] Fragment pool: %u elts × %u bytes, OFO pool: %u elts × %u bytes\n",
FRAG_POOL_SIZE, FRAG_ELT_SIZE, OFO_POOL_SIZE, OFO_ELT_SIZE);
}
/* ══════════════════════════════════════════════════════════════════
* Port initialization
* ══════════════════════════════════════════════════════════════════ */
void ng_init_port(struct rte_mempool *mbuf_pool)
{
const int num_rx_queues = 1;
const int num_tx_queues = 1;
struct rte_eth_conf port_conf = {
.rxmode = {.mtu = RTE_ETHER_MTU},
};
if (rte_eth_dev_configure(gDpdkPortId, num_rx_queues, num_tx_queues,
&port_conf) < 0)
rte_exit(EXIT_FAILURE, "Cannot configure port=%d\n", gDpdkPortId);
if (rte_eth_rx_queue_setup(gDpdkPortId, 0, 128,
rte_eth_dev_socket_id(gDpdkPortId),
NULL, mbuf_pool) < 0)
rte_exit(EXIT_FAILURE, "rx_queue_setup failed\n");
if (rte_eth_tx_queue_setup(gDpdkPortId, 0, 512,
rte_eth_dev_socket_id(gDpdkPortId),
NULL) < 0)
rte_exit(EXIT_FAILURE, "tx_queue_setup failed\n");
if (rte_eth_dev_start(gDpdkPortId) < 0)
rte_exit(EXIT_FAILURE, "dev_start failed\n");
rte_eth_promiscuous_enable(gDpdkPortId);
printf("[DPDK] Port %d initialized\n", gDpdkPortId);
}