ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
server.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 server.h
6 * @brief Layer 4 (Transport) - the passive OPEN: bound ports and their accept-time gates.
7 *
8 * RFC 9293 sec 3.9.1.1: "If the active/passive flag is set to passive, then this is a call to
9 * LISTEN for an incoming connection." This is that side of OPEN. The dialing side is client.h.
10 *
11 * Each active listener owns one listening control block and one event queue.
12 * When a new client connects, `listener_accept_cb` claims a slot from the shared
13 * `conn_pool`, wires the standard per-connection callbacks, and posts
14 * `EVT_CONNECT` to the owning listener's queue.
15 *
16 * The session layer drains all active listener queues each `Session.tick()`,
17 * routing events to the correct protocol handler via `TcpConn::proto`.
18 *
19 * **Single accept callback**
20 * `protocore_net_arg(listen_pcb, (void*)(uintptr_t)idx)` embeds the listener index
21 * in the control block's user data so a single static `listener_accept_cb`
22 * handles all ports.
23 *
24 * **Event posting**
25 * The queue belongs to the listener, so the connection callbacks in protocol/protocol.c post to it
26 * through ::TcpListener's enqueue rather than writing a queue they do not own.
27 *
28 * @author Douglas Quigg (dstroy0)
29 * @date 2026
30 */
31
32#ifndef PROTOCORE_TCP_SERVER_H
33#define PROTOCORE_TCP_SERVER_H
34
35#include "config/platform/platform.h" // the target's queues and TCP, under our names
36#include "network_drivers/transport/tcp/evt/evt.h" // TcpEvt: the event an enqueue posts. The listener rows themselves are common.h's.
37#include "shared/ip/ip.h" // protocore_ip: the peer address an allowlist matches
38
39#include "protocore_config.h"
40
42
43/**
44 * @brief Accept callback from the lower-level module - single handler for all listener ports.
45 *
46 * Non-static so the host unit tests can call it directly with a fabricated control block, the same
47 * convention protocol.c uses for lowlevel_recv_cb / lowlevel_sent_cb / lowlevel_err_cb - production
48 * code never calls this directly, the add binds it to the listening block.
49 */
50protocore_net_err listener_accept_cb(void *arg, protocore_pcb *newpcb, protocore_net_err err);
51
52/** @brief RFC 9293 sec 3.9.1.1 passive OPEN: what a bound port is. */
53typedef struct
54{
55 uint16_t port; ///< the port it binds, or the one a lookup names
56 ProtoConn proto; ///< the application protocol its connections take
57 proto_bool tls; ///< connections accepted here begin a handshake
58 uint8_t dscp; ///< the code point a port marks with
60
61/** @brief The accept-time gates: what a throttle or an allowlist judges. Nothing a bind reads. */
62typedef struct
63{
64 uint32_t now_ms; ///< the clock a throttle window measures against
65 const protocore_ip *addr; ///< the source address a gate judges
66 uint8_t prefix_len; ///< its CIDR prefix length (RFC 4632)
67 const char *cidr; ///< the same rule as text
69
70/** @brief Event routing: whose queue an event goes to, and what it names. */
71typedef struct
72{
73 int worker_id; ///< whose queue is being named
74 const TcpEvt *evt; ///< the event an enqueue posts
75 uint8_t conn_slot; ///< the slot that event names
77
78/**
79 * @brief The accepting side of TCP: bound ports, their worker queues, and the accept-time gates.
80 *
81 * RFC 9293 sec 3.9.1.1: a passive OPEN is a call to LISTEN for an incoming connection. A caller
82 * sets the members a call takes, invokes it through ::TcpListener, and reads the outcome off the
83 * same handle.
84 *
85 * @var TcpListenerNs::idx the listener row a call acts on
86 * @var TcpListenerNs::port the port it binds, or the one a lookup names
87 * @var TcpListenerNs::proto the application protocol its connections take
88 * @var TcpListenerNs::tls connections accepted here begin a handshake
89 * @var TcpListenerNs::dscp the code point a port marks with
90 * @var TcpListenerNs::now_ms the clock a throttle window measures against
91 * @var TcpListenerNs::addr the source address a gate judges
92 * @var TcpListenerNs::prefix_len its CIDR prefix length (RFC 4632)
93 * @var TcpListenerNs::cidr the same rule as text
94 * @var TcpListenerNs::worker_id whose queue is being named
95 * @var TcpListenerNs::evt the event an enqueue posts
96 * @var TcpListenerNs::conn_slot the slot that event names
97 * @var TcpListenerNs::ok a call's true/false outcome
98 * @var TcpListenerNs::i32 a call's signed outcome
99 * @var TcpListenerNs::queue the queue a lookup reports
100 * @var TcpListenerNs::add bind a port and start accepting on it
101 * @var TcpListenerNs::add_dynamic the same from a running task, for ssh -R
102 * @var TcpListenerNs::stop tear one listener down
103 * @var TcpListenerNs::stop_all tear every listener down
104 * @var TcpListenerNs::stop_dynamic tear down only the dynamically started listeners
105 * @var TcpListenerNs::enqueue post an event to the owning worker's queue
106 * @var TcpListenerNs::set_dscp the mark every connection accepted on a port takes
107 * @var TcpListenerNs::worker_queues_init create the per-worker queues
108 * @var TcpListenerNs::worker_queue the queue a worker drains
109 * @var TcpListenerNs::accept_allowed the global fixed-window accept throttle
110 * @var TcpListenerNs::accept_throttle_reset clear the global window
111 * @var TcpListenerNs::accept_allowed_ip the per-source-address throttle bucket
112 * @var TcpListenerNs::per_ip_throttle_reset clear every per-address bucket
113 * @var TcpListenerNs::ip_allow_add add one address to the allowlist
114 * @var TcpListenerNs::ip_allow_add_cidr add one CIDR rule to the allowlist
115 * @var TcpListenerNs::ip_allowed the allowlist verdict for an address
116 * @var TcpListenerNs::ip_allowlist_reset clear every allowlist rule
117 */
118typedef struct
119{
120 uint8_t idx; ///< the listener row every call names
121 TcpBindArgs bind; ///< what a passive OPEN binds (RFC 9293 sec 3.9.1.1)
122 TcpGateArgs gate; ///< what an accept-time gate judges
123 TcpQueueArgs q; ///< where an event goes
125 int32_t i32;
126 protocore_platform_queue queue;
127#if PROTOCORE_WORKER_COUNT > 1
128 // One worker owns every slot at N=1, so there are no per-worker queues to name.
129#else
130 // Above one worker an event routes to its slot owner's queue, so a listener row has none.
131#endif
133
134/** @brief The operands and the outcome. */
136
137/** @brief The entries. */
138typedef struct
139{
140 void (*const stop)(uint8_t *work);
141 void (*const stop_all)(uint8_t *work);
142 void (*const stop_dynamic)(uint8_t *work);
143 void (*const add)(uint8_t *work);
144 void (*const add_dynamic)(uint8_t *work);
145 void (*const enqueue)(uint8_t *work);
146 void (*const set_dscp)(uint8_t *work);
147 void (*const worker_queues_init)(uint8_t *work);
148 void (*const worker_queue)(uint8_t *work);
149 void (*const listener_queue)(uint8_t *work);
150 void (*const accept_allowed)(uint8_t *work);
151 void (*const accept_throttle_reset)(uint8_t *work);
152 void (*const accept_allowed_ip)(uint8_t *work);
153 void (*const per_ip_throttle_reset)(uint8_t *work);
154 void (*const ip_allow_add)(uint8_t *work);
155 void (*const ip_allow_add_cidr)(uint8_t *work);
156 void (*const ip_allowed)(uint8_t *work);
157 void (*const ip_allowlist_reset)(uint8_t *work);
159
160// What the table binds, defined once in the .c and taking one parameter each: everything
161// else an entry needs is an operand in TcpListenerV or a region of the borrow at a fixed offset.
162void protocore_tcp_listener_stop(uint8_t *work);
165void protocore_tcp_listener_add(uint8_t *work);
169#if PROTOCORE_WORKER_COUNT > 1
170void protocore_tcp_listener_worker_queues_init(uint8_t *work);
171void protocore_tcp_listener_worker_queue(uint8_t *work);
172#else
174#endif
183
184// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
185// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
186// `TcpListener.stop(work)` resolves to a named function and becomes a DIRECT call. An extern table
187// leaves the call indirect and the symbol live at every level, -O2 -flto included.
188static const TcpListenerNs TcpListener __attribute__((unused)) = {
196#if PROTOCORE_WORKER_COUNT > 1
197 .worker_queues_init = protocore_tcp_listener_worker_queues_init,
198 .worker_queue = protocore_tcp_listener_worker_queue,
199#else
201#endif
203 .accept_throttle_reset = protocore_tcp_listener_accept_throttle_reset,
204 .accept_allowed_ip = protocore_tcp_listener_accept_allowed_ip,
205 .per_ip_throttle_reset = protocore_tcp_listener_per_ip_throttle_reset,
207 .ip_allow_add_cidr = protocore_tcp_listener_ip_allow_add_cidr,
209 .ip_allowlist_reset = protocore_tcp_listener_ip_allowlist_reset,
210};
211
212/**
213 * @brief The PROTOCORE_TCP_LISTENER_BORROW bytes this module's state lives in.
214 *
215 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
216 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
217 * walks, so the state lasts the life of the program.
218 *
219 * @return the span.
220 */
222
224
225#endif
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.
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.
RFC 9293 sec 3.9.1.1 passive OPEN: what a bound port is.
Definition server.h:54
uint16_t port
the port it binds, or the one a lookup names
Definition server.h:55
proto_bool tls
connections accepted here begin a handshake
Definition server.h:57
uint8_t dscp
the code point a port marks with
Definition server.h:58
ProtoConn proto
the application protocol its connections take
Definition server.h:56
Event record posted from the stack callbacks to the session layer.
Definition evt.h:79
The accept-time gates: what a throttle or an allowlist judges. Nothing a bind reads.
Definition server.h:63
const char * cidr
the same rule as text
Definition server.h:67
uint8_t prefix_len
its CIDR prefix length (RFC 4632)
Definition server.h:66
uint32_t now_ms
the clock a throttle window measures against
Definition server.h:64
const protocore_ip * addr
the source address a gate judges
Definition server.h:65
The entries.
Definition server.h:139
void(*const stop)(uint8_t *work)
Definition server.h:140
TcpBindArgs bind
what a passive OPEN binds (RFC 9293 sec 3.9.1.1)
Definition server.h:121
TcpGateArgs gate
what an accept-time gate judges
Definition server.h:122
uint8_t idx
the listener row every call names
Definition server.h:120
int32_t i32
Definition server.h:125
TcpQueueArgs q
where an event goes
Definition server.h:123
protocore_platform_queue queue
Definition server.h:126
proto_bool ok
Definition server.h:124
Event routing: whose queue an event goes to, and what it names.
Definition server.h:72
uint8_t conn_slot
the slot that event names
Definition server.h:75
int worker_id
whose queue is being named
Definition server.h:73
const TcpEvt * evt
the event an enqueue posts
Definition server.h:74
A v4 or v6 address in network (big-endian) byte order.
Definition ip.h:56
void protocore_tcp_listener_listener_queue(uint8_t *work)
void protocore_tcp_listener_ip_allowlist_reset(uint8_t *work)
void protocore_tcp_listener_ip_allowed(uint8_t *work)
void protocore_tcp_listener_ip_allow_add(uint8_t *work)
void protocore_tcp_listener_set_dscp(uint8_t *work)
uint8_t * protocore_tcp_listener_span(void)
The PROTOCORE_TCP_LISTENER_BORROW bytes this module's state lives in.
void protocore_tcp_listener_add(uint8_t *work)
void protocore_tcp_listener_accept_allowed(uint8_t *work)
void protocore_tcp_listener_ip_allow_add_cidr(uint8_t *work)
TcpListenerVars TcpListenerV
The operands and the outcome.
void protocore_tcp_listener_accept_throttle_reset(uint8_t *work)
void protocore_tcp_listener_add_dynamic(uint8_t *work)
void protocore_tcp_listener_stop_all(uint8_t *work)
void protocore_tcp_listener_per_ip_throttle_reset(uint8_t *work)
void protocore_tcp_listener_stop_dynamic(uint8_t *work)
PROTOCORE_BEGIN_DECLS protocore_net_err listener_accept_cb(void *arg, protocore_pcb *newpcb, protocore_net_err err)
Accept callback from the lower-level module - single handler for all listener ports.
void protocore_tcp_listener_accept_allowed_ip(uint8_t *work)
void protocore_tcp_listener_enqueue(uint8_t *work)
void protocore_tcp_listener_stop(uint8_t *work)
#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