ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
common.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file common.h
6 * @brief Layer 4 (Transport) - the connection state every half of TCP reads.
7 *
8 * RFC 9293 sec 3.3.1 calls these the key connection state variables. Here that is the pool slot:
9 * its lifecycle state, its receive ring, and the identity fields the layers above filter on. The
10 * server (sec 3.9.1.1 passive OPEN), the client (active OPEN), the protocol engine (sec 3.10) and
11 * the lower-level seam (sec 3.9.2) all read this header and none of them declares its own copy.
12 *
13 * The event record itself is in evt.h, which this includes: the slot state below is `_Atomic`,
14 * which is C11 and not C++, so the header that reaches the sketches has to be the smaller one.
15 *
16 * **Concurrency model**
17 * | Context | Reads | Writes |
18 * |------------------|------------------------------|--------------------------|
19 * | stack callbacks | anularis.vacant (to check) | anularis.put |
20 * | main loop | anularis.available/read/peek | anularis.read/consume |
21 *
22 * The receive ring is a memoria_anularis ring over the slot's own aligned rx_buffer: one producer
23 * (the stack callbacks) and one consumer (the owning worker), with the ordering inside the ring.
24 * `state` is `_Atomic`, read and written through PROTO_ATOMIC_LOAD / PROTO_ATOMIC_STORE.
25 *
26 * **Backpressure (lossless)**
27 * When a whole inbound segment will not fit the free ring space, the recv callback refuses it
28 * without taking ownership of the segment; the stack holds it and redelivers once the main loop has
29 * drained the ring, so no received byte is dropped. Requires RX_BUF_SIZE > one TCP segment
30 * (TCP_MSS).
31 *
32 * @author Douglas Quigg (dstroy0)
33 * @date 2026
34 */
35
36#ifndef PROTOCORE_TCP_COMMON_H
37#define PROTOCORE_TCP_COMMON_H
38
40#include "memoria_anularis/memoria_anularis.h" // mmgr_ring: the receive ring, and the loculi the slots are
41#include "network_drivers/transport/tcp/evt/evt.h" // EvtType, TcpEvt: what this layer posts to a listener queue
42#include "shared/ip/ip.h" // protocore_ip (family-tagged peer address)
43#include <stdatomic.h> // atomic_load_explicit / atomic_store_explicit on the slot state
44
45#include "protocore_config.h"
46
48
49// ConnState and the CONN_* names live in evt.h, which this header includes: the signaling layer
50// reads a slot's state and reaches this layer through protocore.h.
51
52/**
53 * @brief A single TCP connection context.
54 *
55 * Sized so that `MAX_CONNS` instances fit in a static array without
56 * fragmentation. All fields except the ring and the state may
57 * only be accessed from the main-loop task.
58 */
59typedef struct TcpConn
60{
61 uint8_t id; ///< Fixed slot index (0 … MAX_CONNS-1).
62 _Atomic ConnState state; ///< Lifecycle state; acquire/release for inter-task visibility.
63 protocore_pcb *pcb; ///< Stack control block; null when slot is free.
64 uint32_t last_activity_ms; ///< `protocore_millis()` timestamp of last TX/RX event.
65
66 EMBED_ALIGN(MMGR_ALIGN_BYTES) uint8_t rx_buffer[RX_BUF_SIZE]; ///< The ring's bytes, owned here.
67 mmgr_ring rx; ///< The receive ring over rx_buffer.
68 size_t rx_unacked; ///< Bytes drained since the last ACK to the stack. Worker-only: the window is
69 ///< reopened by exactly these, so it tracks ring occupancy (ack-on-consume)
70 ///< rather than copy.
71
72 uint8_t listener_id; ///< Index into listener_pool[]; set at accept time.
73 uint8_t owner; ///< Worker that owns this slot (round-robin at accept). Always 0 at N=1.
74 ProtoConn proto; ///< Application protocol for this connection.
75 uint8_t
76 proto_slot; ///< Per-protocol session/pool index (0xFF = none): the SSH session, an MQTT/Modbus session, etc.
77 protocore_if_kind iface; ///< Interface this connection arrived on; set at accept time.
78 uint8_t tls; ///< Non-zero when this connection is TLS (set at accept time).
79 // The response sink and the HTTP/2 and HTTP/3 per-connection fields are HTTP semantics and live
80 // with HTTP, keyed on the same slot index, as http_req_count already does.
82
83/** @brief Sentinel for TcpConn.proto_slot meaning "no per-protocol session bound". */
84#define PROTOCORE_PROTO_SLOT_NONE 0xFFu
85
86// ---------------------------------------------------------------------------
87// Slot state, as loculi
88// ---------------------------------------------------------------------------
89//
90// A slot's availability is one loculus of the pool's slot ring (protocol/protocol.c), rather than a
91// field to load and compare. Loculus i is held while conn_pool[i] is anything but CONN_FREE, keeping
92// out that slot's rx_buffer, and dropped when it returns to CONN_FREE. Written through
93// protocore_conn_set_state() only, so it stays in lock-step with the state.
94//
95// A slot is allocatable only when its loculus is free AND not held: anularis.loculus_ready is
96// `free & ~held`, and anularis.loculus_next picks the lowest. Holding is what makes reuse safe.
97// Without it a slot reads free while its bytes are still in use, and the index is handed to a new
98// connection on top of the old one's data - the collision RFC 9293 sec 3.6.1 keeps a connection
99// identifier out of circulation to avoid, expressed as a bit rather than a timer, because a pool
100// index is not a socket and has no quiet period to wait out.
101
102_Static_assert(MAX_CONNS <= MMGR_RING_LOCULI,
103 "every connection slot is a loculus; raise MMGR_RING_LOCULI (and MMGR_RING_WORDS) with MAX_CONNS");
104
105/**
106 * @brief Access-point IPv4 address (network byte order) for STA/AP interface tagging.
107 *
108 * Zero when no access point is configured. Set via set_ap_ip(); the
109 * accept callback tags each connection PROTOCORE_IF_WIFI_AP when its local IP equals
110 * this, else PROTOCORE_IF_WIFI_STA. Used by per-route interface filters.
111 */
112extern uint32_t protocore_ap_ip;
113
114/** @brief Static pool of connection contexts. Defined in protocol/protocol.c.
115 * Sized CONN_POOL_SLOTS: MAX_CONNS TCP slots plus any reserved internal dispatch slot(s)
116 * (HTTP/3); the TCP accept path only ever uses [0, MAX_CONNS). */
118
119// The receive ring is drained through ::ConnPool - available, read_byte, peek, consume and read -
120// and a slot is asked about through active, iface, listener_id, tls, owner, proto_of and pcb_of.
121// Transport owns the ring: nothing above this layer reaches rx_buffer or the ring itself.
122
123// ---------------------------------------------------------------------------
124// Listener pool entry
125// ---------------------------------------------------------------------------
126
127/**
128 * @brief State for one TCP listening port.
129 *
130 * All queue storage is embedded in this struct so the entire listener pool
131 * lives in BSS - no heap allocation anywhere in the listener layer.
132 *
133 * A single `Listener` instance consumes:
134 * sizeof(protocore_pcb*) + sizeof(protocore_platform_queue_ctrl) + EVT_QUEUE_DEPTH*sizeof(TcpEvt)
135 * + sizeof(protocore_platform_queue) + 3 bytes overhead (port, proto, active).
136 */
137typedef struct
138{
139 uint16_t port; ///< TCP port this listener binds.
140 ProtoConn proto; ///< Application protocol for all connections accepted here.
141 protocore_pcb *listen_pcb; ///< the listening control block; NULL when inactive.
142#if PROTOCORE_WORKER_COUNT == 1
143 // One worker owns every slot, so the listener's own queue is the only path an event takes. Above
144 // one, an event routes to its slot owner's queue instead and this one is never sent to.
145 protocore_platform_queue_ctrl _queue_struct; ///< Static queue descriptor.
146 uint8_t _queue_storage[EVT_QUEUE_DEPTH * sizeof(TcpEvt)]; ///< Queue backing store.
147 protocore_platform_queue queue; ///< Handle returned by protocore_platform_queue_create().
148#endif
149 proto_bool active; ///< True after listener_add(), false after listener_stop().
150 proto_bool tls; ///< True when connections accepted here begin a TLS handshake.
151 uint8_t dscp; ///< Per-listener DiffServ DSCP for accepted connections; PROTOCORE_DSCP_UNSET = use the default.
152} Listener;
153
154/**
155 * @brief The row carries no code point of its own; accept() takes the server-wide default.
156 *
157 * Stated here rather than in diffserv.h because it is a value of ::Listener::dscp, which every
158 * build has: a port either names a code point or does not, and only whether the accept callback
159 * stamps one into the DS field depends on ::PROTOCORE_ENABLE_DIFFSERV.
160 */
161#define PROTOCORE_DSCP_UNSET 0xFF
162
163/** @brief Static pool of listener contexts. Defined in server.c. */
165
167
168#endif // PROTOCORE_TCP_COMMON_H
#define CONN_POOL_SLOTS
enum PROTO_ENUM_PACKED protocore_if_kind
What an interface is, and the filter that selects one.
#define MAX_LISTENERS
Maximum number of simultaneously active listener ports.
#define RX_BUF_SIZE
Ring-buffer capacity in bytes per connection slot (feature floors enforced last, in derived_sizing....
#define EVT_QUEUE_DEPTH
Depth of the FreeRTOS event queue shared between lwIP callbacks and the main-loop task.
#define MAX_CONNS
Maximum simultaneous TCP connections (fixed static pool; ~3.95 KB of internal RAM per slot).
enum PROTO_ENUM_PACKED ProtoConn
Application protocol spoken on a listener port or connection slot.
What the stack callbacks post to a listener's queue: the event type and the record.
enum PROTO_ENUM_PACKED ConnState
Lifecycle state of a connection pool slot.
Layer 3 (Network) - a family-tagged IP address (IPv4 or IPv6) with RFC-faithful text parsing,...
The platform contract: what the library asks of a target, in the library's own words.
State for one TCP listening port.
Definition common.h:138
ProtoConn proto
Application protocol for all connections accepted here.
Definition common.h:140
uint8_t dscp
Per-listener DiffServ DSCP for accepted connections; PROTOCORE_DSCP_UNSET = use the default.
Definition common.h:151
proto_bool active
True after listener_add(), false after listener_stop().
Definition common.h:149
proto_bool tls
True when connections accepted here begin a TLS handshake.
Definition common.h:150
uint16_t port
TCP port this listener binds.
Definition common.h:139
protocore_pcb * listen_pcb
the listening control block; NULL when inactive.
Definition common.h:141
A single TCP connection context.
Definition common.h:60
size_t rx_unacked
Definition common.h:68
mmgr_ring rx
The receive ring over rx_buffer.
Definition common.h:67
protocore_if_kind iface
Interface this connection arrived on; set at accept time.
Definition common.h:77
uint32_t last_activity_ms
protocore_millis() timestamp of last TX/RX event.
Definition common.h:64
uint8_t id
Fixed slot index (0 … MAX_CONNS-1).
Definition common.h:61
uint8_t owner
Worker that owns this slot (round-robin at accept). Always 0 at N=1.
Definition common.h:73
uint8_t listener_id
Index into listener_pool[]; set at accept time.
Definition common.h:72
ProtoConn proto
Application protocol for this connection.
Definition common.h:74
uint8_t tls
Non-zero when this connection is TLS (set at accept time).
Definition common.h:78
uint8_t proto_slot
Per-protocol session/pool index (0xFF = none): the SSH session, an MQTT/Modbus session,...
Definition common.h:76
_Atomic ConnState state
Lifecycle state; acquire/release for inter-task visibility.
Definition common.h:62
protocore_pcb * pcb
Stack control block; null when slot is free.
Definition common.h:63
EMBED_ALIGN(MMGR_ALIGN_BYTES) uint8_t rx_buffer[RX_BUF_SIZE]
The ring's bytes, owned here.
Event record posted from the stack callbacks to the session layer.
Definition evt.h:79
TcpConn conn_pool[CONN_POOL_SLOTS]
Static pool of connection contexts. Defined in protocol/protocol.c. Sized CONN_POOL_SLOTS: MAX_CONNS ...
uint32_t protocore_ap_ip
Access-point IPv4 address (network byte order) for STA/AP interface tagging.
Listener listener_pool[MAX_LISTENERS]
Static pool of listener contexts. Defined in server.c.
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97