ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
protocol.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 protocol.h
6 * @brief Layer 4 (Transport) - the user/TCP interface and the event processing behind it.
7 *
8 * RFC 9293 sec 3.9.1 names the calls a TCP implementation owes its user: Open, Send, Receive,
9 * Close, Status, Abort, Flush, Asynchronous Reports, Set Differentiated Services Field. ::ConnPoolNs
10 * is that set for an accepted connection - the pool holds what the server accepted, and every layer
11 * above reaches a connection through this table rather than through the stack.
12 *
13 * Behind the calls is sec 3.10 Event Processing: the three stack callbacks (sec 3.10.7 SEGMENT
14 * ARRIVES), the close sequence and its drain dwell (sec 3.6), the window management that reopens on
15 * consume (sec 3.8.6), and the timeouts (sec 3.10.8).
16 *
17 * Calls INTO the stack are not here - those are lower.h (sec 3.9.2). This module decides what to
18 * do; that one performs it in the context where it is safe.
19 *
20 * @author Douglas Quigg (dstroy0)
21 * @date 2026
22 */
23
24#ifndef PROTOCORE_TCP_PROTOCOL_H
25#define PROTOCORE_TCP_PROTOCOL_H
26
27#include "config/platform/platform.h" // protocore_pcb, protocore_net_err: the types a call names
28#include "network_drivers/transport/tcp/evt/evt.h" // ConnState, TcpEvt, and the observability hook
29#include "shared/ip/ip.h" // protocore_ip: where a peer address is written
30
31#include "protocore_config.h"
32
34
35// ---------------------------------------------------------------------------
36// Event processing - the stack's callbacks (RFC 9293 sec 3.10.7 SEGMENT ARRIVES)
37// ---------------------------------------------------------------------------
38// The stack calls these directly, so their shapes are its and not this module's. Non-static so the
39// server's accept path can take their address, and so a host test can drive them: on native there
40// is no real stack event to fire them.
41
42protocore_net_err lowlevel_recv_cb(void *arg, protocore_pcb *tpcb, protocore_pbuf *p, protocore_net_err err);
43protocore_net_err lowlevel_sent_cb(void *arg, protocore_pcb *tpcb, proto_u16 len);
44void lowlevel_err_cb(void *arg, protocore_net_err err);
45
46/** @brief RFC 9293 sec 3.9.1 SEND / RECEIVE: the bytes a call moves, and the room they move into. */
47typedef struct
48{
49 const void *data; ///< bytes for a send
50 proto_u16 len; ///< how many
51 uint8_t *buf; ///< where a read or a peek lands
52 size_t cap; ///< how much room it has
53 size_t off; ///< a peek's offset from the tail
54 size_t count; ///< bytes a peek copies, or a consume drops
56
57/** @brief What a pool lifecycle call reads: the idle deadline it sweeps against, and whose slots. */
58typedef struct
59{
60 proto_u32 conn_timeout_ms; ///< milliseconds of inactivity before the sweep closes a slot
61 int worker_id; ///< whose slots the sweep reaps
63
64#if PROTOCORE_ENABLE_OBSERVABILITY
65/** @brief What an observability call records; nothing on the byte path reads these. */
66typedef struct
67{
68 protocore_conn_event_cb event_cb_in; ///< the observer on_event installs
69 protocore_conn_reason reason; ///< why a transition or notice fired
70 ConnState olds; ///< the state a transition left
71 ConnState news; ///< the state it entered
72 protocore_conn_counters counters; ///< where counters_get reports
73} ConnObsArgs;
74#endif
75
76/**
77 * @brief The pool of accepted connections: the user/TCP interface for one connection.
78 *
79 * Named for the pool rather than the slot: a slot is named by its index, and its layout is the
80 * transport's own.
81 *
82 * A caller sets the members a call takes, invokes it through ::ConnPool, and reads the result off
83 * the same handle. The pool's own state - the idle bound, the counters, the template a slot is
84 * reset from, and the calls themselves - is behind @ref internal and is not describable here.
85 *
86 * @var ConnPoolNs::slot the connection a call acts on
87 * @var ConnPoolNs::st the state a write installs
88 * @var ConnPoolNs::data bytes for a send
89 * @var ConnPoolNs::len how many
90 * @var ConnPoolNs::pcb the control block a raw call acts on
91 * @var ConnPoolNs::conn_timeout_ms the idle deadline init loads
92 * @var ConnPoolNs::worker_id whose slots the sweep reaps
93 * @var ConnPoolNs::out where the peer address is written
94 * @var ConnPoolNs::evt the event an enqueue posts
95 * @var ConnPoolNs::buf where a read or a peek lands
96 * @var ConnPoolNs::cap how much room it has
97 * @var ConnPoolNs::off a peek's offset from the tail
98 * @var ConnPoolNs::count bytes a peek copies, or a consume drops
99 * @var ConnPoolNs::ok a call's true/false outcome
100 * @var ConnPoolNs::u16 a call's 16-bit outcome
101 * @var ConnPoolNs::u32 a call's 32-bit outcome
102 * @var ConnPoolNs::u8 a call's 8-bit outcome
103 * @var ConnPoolNs::i32 a call's signed outcome
104 * @var ConnPoolNs::n a byte count a call reports
105 * @var ConnPoolNs::if_kind the interface a slot's connection arrived on
106 * @var ConnPoolNs::proto the application protocol a slot carries
107 * @var ConnPoolNs::set_state install the state in st on the slot
108 * @var ConnPoolNs::alloc_free claim the lowest free slot
109 * @var ConnPoolNs::timeout_ms the idle deadline the sweep measures against
110 * @var ConnPoolNs::send queue bytes for transmission (RFC 9293 sec 3.9.1 SEND)
111 * @var ConnPoolNs::send_flush the same, pushed on the way out
112 * @var ConnPoolNs::sndbuf room the send buffer has left
113 * @var ConnPoolNs::flush push what is queued (RFC 9293 sec 3.9.1 PUSH)
114 * @var ConnPoolNs::ack_consumed reopen the receive window by what was drained
115 * @var ConnPoolNs::raw_send write already-encrypted bytes, no TLS re-entry
116 * @var ConnPoolNs::close tear the connection down (RFC 9293 sec 3.9.1 CLOSE)
117 * @var ConnPoolNs::abort_slot reset it (RFC 9293 sec 3.9.1 ABORT)
118 * @var ConnPoolNs::closing_finalize finish a slot whose TX has drained
119 * @var ConnPoolNs::closing_check finalize it if it has
120 * @var ConnPoolNs::begin_close enter the dwell that precedes the close
121 * @var ConnPoolNs::enqueue post an event to the owning listener's queue
122 * @var ConnPoolNs::init bring the pool up on the idle deadline it was given
123 * @var ConnPoolNs::stop take every slot down
124 * @var ConnPoolNs::active_count how many slots are live
125 * @var ConnPoolNs::remote_ip the peer address of a slot
126 * @var ConnPoolNs::remote_addr the same, formatted
127 * @var ConnPoolNs::touch_active restart a slot's idle timer
128 * @var ConnPoolNs::check_timeouts reap the slots whose deadline passed
129 * @var ConnPoolNs::on_event install the observer in event_cb_in
130 * @var ConnPoolNs::counters_get read the counters out
131 * @var ConnPoolNs::counters_reset zero them
132 * @var ConnPoolNs::obs_bump count one reason
133 * @var ConnPoolNs::obs_transition record a state change
134 * @var ConnPoolNs::obs_notice record a notice against the current state
135 * @var ConnPoolNs::available bytes the slot's receive ring holds
136 * @var ConnPoolNs::read_byte pop one byte into u8
137 * @var ConnPoolNs::peek copy count bytes at off into buf without consuming
138 * @var ConnPoolNs::consume drop count bytes from the tail
139 * @var ConnPoolNs::read pop up to cap bytes into buf
140 * @var ConnPoolNs::active the slot holds a live connection that can take a send
141 * @var ConnPoolNs::iface the interface it arrived on
142 * @var ConnPoolNs::listener_id the listener it was accepted on
143 * @var ConnPoolNs::tls the connection began a TLS handshake
144 * @var ConnPoolNs::owner the worker that owns the slot
145 * @var ConnPoolNs::proto_of the application protocol it carries
146 * @var ConnPoolNs::pcb_of its control block
147 */
148typedef struct
149{
150 uint8_t slot; ///< the connection every call names
151 ConnState st; ///< the state a write installs
152 protocore_pcb *pcb; ///< the control block a raw call acts on, or the one pcb_of reports
153 const TcpEvt *evt; ///< the event an enqueue posts
154 ConnIoArgs io; ///< the bytes a send or a receive moves (RFC 9293 sec 3.9.1)
155 ConnLifeArgs life; ///< what a pool lifecycle call reads
156#if PROTOCORE_ENABLE_OBSERVABILITY
157 ConnObsArgs obs; ///< what an observability call records
158#endif
161 uint32_t u32;
162 uint8_t u8;
163 int32_t i32;
164 size_t n;
167 protocore_ip *out; ///< where a peer address is written
168#if PROTOCORE_ENABLE_OBSERVABILITY
169#endif
170 // The receive ring, and what a slot is. Transport owns the ring: a layer above drains it only
171 // through these, and never indexes the buffer or advances the tail itself.
173
174/** @brief The operands and the outcome. */
176
177/** @brief The entries. */
178typedef struct
179{
180 void (*const set_state)(uint8_t *work);
181 void (*const alloc_free)(uint8_t *work);
182 void (*const timeout_ms)(uint8_t *work);
183 void (*const send)(uint8_t *work);
184 void (*const send_flush)(uint8_t *work);
185 void (*const sndbuf)(uint8_t *work);
186 void (*const flush)(uint8_t *work);
187 void (*const ack_consumed)(uint8_t *work);
188 void (*const raw_send)(uint8_t *work);
189 void (*const close)(uint8_t *work);
190 void (*const abort_slot)(uint8_t *work);
191 void (*const closing_finalize)(uint8_t *work);
192 void (*const closing_check)(uint8_t *work);
193 void (*const begin_close)(uint8_t *work);
194 void (*const enqueue)(uint8_t *work);
195 void (*const init)(uint8_t *work);
196 void (*const stop)(uint8_t *work);
197 void (*const active_count)(uint8_t *work);
198 void (*const remote_ip)(uint8_t *work);
199 void (*const remote_addr)(uint8_t *work);
200 void (*const touch_active)(uint8_t *work);
201 void (*const check_timeouts)(uint8_t *work);
202 void (*const on_event)(uint8_t *work);
203 void (*const counters_get)(uint8_t *work);
204 void (*const counters_reset)(uint8_t *work);
205 void (*const obs_bump)(uint8_t *work);
206 void (*const obs_transition)(uint8_t *work);
207 void (*const obs_notice)(uint8_t *work);
208 void (*const available)(uint8_t *work);
209 void (*const read_byte)(uint8_t *work);
210 void (*const peek)(uint8_t *work);
211 void (*const consume)(uint8_t *work);
212 void (*const read)(uint8_t *work);
213 void (*const active)(uint8_t *work);
214 void (*const iface)(uint8_t *work);
215 void (*const listener_id)(uint8_t *work);
216 void (*const tls)(uint8_t *work);
217 void (*const owner)(uint8_t *work);
218 void (*const proto_of)(uint8_t *work);
219 void (*const pcb_of)(uint8_t *work);
220} ConnPoolNs;
221
222// What the table binds, defined once in the .c and taking one parameter each: everything
223// else an entry needs is an operand in ConnPoolV or a region of the borrow at a fixed offset.
227void protocore_conn_pool_send(uint8_t *work);
229void protocore_conn_pool_sndbuf(uint8_t *work);
230void protocore_conn_pool_flush(uint8_t *work);
233void protocore_conn_pool_close(uint8_t *work);
238void protocore_conn_pool_enqueue(uint8_t *work);
239void protocore_conn_pool_init(uint8_t *work);
240void protocore_conn_pool_stop(uint8_t *work);
246#if PROTOCORE_ENABLE_OBSERVABILITY
247void protocore_conn_pool_on_event(uint8_t *work);
248void protocore_conn_pool_counters_get(uint8_t *work);
249void protocore_conn_pool_counters_reset(uint8_t *work);
250void protocore_conn_pool_obs_bump(uint8_t *work);
251void protocore_conn_pool_obs_transition(uint8_t *work);
252void protocore_conn_pool_obs_notice(uint8_t *work);
253#endif
256void protocore_conn_pool_peek(uint8_t *work);
257void protocore_conn_pool_consume(uint8_t *work);
258void protocore_conn_pool_read(uint8_t *work);
259void protocore_conn_pool_active(uint8_t *work);
260void protocore_conn_pool_iface(uint8_t *work);
262void protocore_conn_pool_tls(uint8_t *work);
263void protocore_conn_pool_owner(uint8_t *work);
265void protocore_conn_pool_pcb_of(uint8_t *work);
266
267// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
268// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
269// `ConnPool.set_state(work)` resolves to a named function and becomes a DIRECT call. An extern table
270// leaves the call indirect and the symbol live at every level, -O2 -flto included.
271static const ConnPoolNs ConnPool __attribute__((unused)) = {
273 .alloc_free = protocore_conn_pool_alloc_free,
274 .timeout_ms = protocore_conn_pool_timeout_ms,
276 .send_flush = protocore_conn_pool_send_flush,
279 .ack_consumed = protocore_conn_pool_ack_consumed,
282 .abort_slot = protocore_conn_pool_abort_slot,
283 .closing_finalize = protocore_conn_pool_closing_finalize,
284 .closing_check = protocore_conn_pool_closing_check,
285 .begin_close = protocore_conn_pool_begin_close,
289 .active_count = protocore_conn_pool_active_count,
291 .remote_addr = protocore_conn_pool_remote_addr,
292 .touch_active = protocore_conn_pool_touch_active,
293 .check_timeouts = protocore_conn_pool_check_timeouts,
294#if PROTOCORE_ENABLE_OBSERVABILITY
295 .on_event = protocore_conn_pool_on_event,
296 .counters_get = protocore_conn_pool_counters_get,
297 .counters_reset = protocore_conn_pool_counters_reset,
298 .obs_bump = protocore_conn_pool_obs_bump,
299 .obs_transition = protocore_conn_pool_obs_transition,
300 .obs_notice = protocore_conn_pool_obs_notice,
301#endif
309 .listener_id = protocore_conn_pool_listener_id,
314};
315
316/**
317 * @brief The PROTOCORE_CONN_POOL_BORROW bytes this module's state lives in.
318 *
319 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
320 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
321 * walks, so the state lasts the life of the program.
322 *
323 * @return the span.
324 */
326
328
329#endif // PROTOCORE_TCP_PROTOCOL_H
enum PROTO_ENUM_PACKED protocore_if_kind
What an interface is, and the filter that selects one.
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.
protocore_net_err lowlevel_sent_cb(void *arg, protocore_pcb *tpcb, proto_u16 len)
void protocore_conn_pool_sndbuf(uint8_t *work)
void lowlevel_err_cb(void *arg, protocore_net_err err)
void protocore_conn_pool_alloc_free(uint8_t *work)
void protocore_conn_pool_begin_close(uint8_t *work)
void protocore_conn_pool_close(uint8_t *work)
void protocore_conn_pool_active(uint8_t *work)
void protocore_conn_pool_iface(uint8_t *work)
void protocore_conn_pool_peek(uint8_t *work)
void protocore_conn_pool_consume(uint8_t *work)
void protocore_conn_pool_owner(uint8_t *work)
void protocore_conn_pool_flush(uint8_t *work)
void protocore_conn_pool_enqueue(uint8_t *work)
void protocore_conn_pool_read_byte(uint8_t *work)
void protocore_conn_pool_pcb_of(uint8_t *work)
PROTOCORE_BEGIN_DECLS protocore_net_err lowlevel_recv_cb(void *arg, protocore_pcb *tpcb, protocore_pbuf *p, protocore_net_err err)
void protocore_conn_pool_remote_ip(uint8_t *work)
void protocore_conn_pool_ack_consumed(uint8_t *work)
void protocore_conn_pool_raw_send(uint8_t *work)
void protocore_conn_pool_touch_active(uint8_t *work)
void protocore_conn_pool_active_count(uint8_t *work)
void protocore_conn_pool_tls(uint8_t *work)
void protocore_conn_pool_send_flush(uint8_t *work)
void protocore_conn_pool_stop(uint8_t *work)
uint8_t * protocore_conn_pool_span(void)
The PROTOCORE_CONN_POOL_BORROW bytes this module's state lives in.
void protocore_conn_pool_listener_id(uint8_t *work)
void protocore_conn_pool_available(uint8_t *work)
void protocore_conn_pool_timeout_ms(uint8_t *work)
void protocore_conn_pool_init(uint8_t *work)
void protocore_conn_pool_remote_addr(uint8_t *work)
ConnPoolVars ConnPoolV
The operands and the outcome.
void protocore_conn_pool_read(uint8_t *work)
void protocore_conn_pool_send(uint8_t *work)
void protocore_conn_pool_closing_finalize(uint8_t *work)
void protocore_conn_pool_abort_slot(uint8_t *work)
void protocore_conn_pool_check_timeouts(uint8_t *work)
void protocore_conn_pool_closing_check(uint8_t *work)
void protocore_conn_pool_proto_of(uint8_t *work)
void protocore_conn_pool_set_state(uint8_t *work)
RFC 9293 sec 3.9.1 SEND / RECEIVE: the bytes a call moves, and the room they move into.
Definition protocol.h:48
size_t count
bytes a peek copies, or a consume drops
Definition protocol.h:54
uint8_t * buf
where a read or a peek lands
Definition protocol.h:51
proto_u16 len
how many
Definition protocol.h:50
size_t off
a peek's offset from the tail
Definition protocol.h:53
const void * data
bytes for a send
Definition protocol.h:49
size_t cap
how much room it has
Definition protocol.h:52
What a pool lifecycle call reads: the idle deadline it sweeps against, and whose slots.
Definition protocol.h:59
proto_u32 conn_timeout_ms
milliseconds of inactivity before the sweep closes a slot
Definition protocol.h:60
int worker_id
whose slots the sweep reaps
Definition protocol.h:61
The entries.
Definition protocol.h:179
void(*const set_state)(uint8_t *work)
Definition protocol.h:180
proto_u16 u16
Definition protocol.h:160
protocore_ip * out
where a peer address is written
Definition protocol.h:167
uint8_t slot
the connection every call names
Definition protocol.h:150
protocore_if_kind if_kind
Definition protocol.h:165
ConnState st
the state a write installs
Definition protocol.h:151
ConnIoArgs io
the bytes a send or a receive moves (RFC 9293 sec 3.9.1)
Definition protocol.h:154
int32_t i32
Definition protocol.h:163
uint32_t u32
Definition protocol.h:161
uint8_t u8
Definition protocol.h:162
const TcpEvt * evt
the event an enqueue posts
Definition protocol.h:153
proto_bool ok
Definition protocol.h:159
ConnLifeArgs life
what a pool lifecycle call reads
Definition protocol.h:155
protocore_pcb * pcb
the control block a raw call acts on, or the one pcb_of reports
Definition protocol.h:152
ProtoConn proto
Definition protocol.h:166
Event record posted from the stack callbacks to the session layer.
Definition evt.h:79
A v4 or v6 address in network (big-endian) byte order.
Definition ip.h:56
#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
uint16_t proto_u16
Definition types.h:41
#define PROTOCORE_END_DECLS
Definition types.h:97
uint32_t proto_u32
Definition types.h:42