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 UDP, the receiving side: bound ports, their receive rings, and the drain.
7 *
8 * One slot per bound port, and one receive ring per slot. The stack's receive trampoline is its
9 * sole producer; poll() is its sole consumer and calls the handler once per datagram, so the
10 * handler runs in the task that calls poll(), not in the stack's thread.
11 *
12 * The ring carries framed entries: address, port, and length ahead of the payload. A frame is
13 * published once, whole, so the consumer never observes a partial one, and a datagram that does not
14 * fit the free space is dropped at the trampoline.
15 *
16 * Sending is not queued. reply() and sendto() carry the caller's buffer to the wire inside one
17 * marshaled call and report what the stack did with it.
18 *
19 * Reached through ::UdpListener.
20 *
21 * @author Douglas Quigg (dstroy0)
22 * @date 2026
23 */
24
25#ifndef PROTOCORE_UDP_SERVER_H
26#define PROTOCORE_UDP_SERVER_H
27
28#include "shared/ip/ip.h" // protocore_ip: the destination, already an address
29
30#include "protocore_config.h"
31
33
34/**
35 * @brief The sender of a received datagram.
36 *
37 * Address and port by value, copied out of the ring frame by poll(), plus the slot the datagram
38 * arrived on so a reply leaves from the same endpoint. Valid for the duration of the handler call.
39 * The layout lives in server.c so no stack type escapes the transport.
40 */
41struct protocore_udp_peer;
42
43/**
44 * @brief Datagram handler, invoked once per received datagram by poll().
45 *
46 * @param data contiguous payload, staged out of the slot's receive ring.
47 * @param len payload length in bytes.
48 * @param peer reply token, valid only during this call.
49 * @param ctx the opaque context passed to listen().
50 */
51typedef void (*protocore_udp_handler)(const uint8_t *data, size_t len, const struct protocore_udp_peer *peer,
52 void *ctx);
53
54/** @brief RFC 768 "the creation of new receive ports": what binding one takes. */
55typedef struct
56{
57 protocore_udp_handler handler; ///< what a received datagram is delivered to
58 void *handler_ctx; ///< the opaque context that handler is given back
59 const char *group_ip; ///< the IPv4 group to join, as text
61
62/** @brief RFC 768 "an operation that allows a datagram to be sent": where it goes and what it carries. */
63typedef struct
64{
65 const protocore_ip *dst; ///< where a send goes
66 uint16_t dst_port; ///< its port
67 const uint8_t *data; ///< the octets to send
68 size_t len; ///< how many
70
71/** @brief RFC 768 "an indication of source port and source address": the sender a reply answers. */
72typedef struct
73{
74 const struct protocore_udp_peer *peer; ///< the sender a reply answers
75 char *ip_out; ///< where its address is formatted
76 size_t ip_cap; ///< how much room that has
77 uint16_t *port_out; ///< where its port is written
79
80/**
81 * @brief The receiving side of UDP.
82 *
83 * RFC 768 "User Interface": the creation of new receive ports, and receive operations on them that
84 * return the data octets and an indication of source port and source address. A caller sets the
85 * members a call takes, invokes it through ::UdpListener, and reads the outcome off the same handle.
86 *
87 * @var UdpListenerNs::port the receive port a call acts on
88 * @var UdpListenerNs::bind what creating a receive port takes
89 * @var UdpListenerNs::send_args what sending a datagram takes
90 * @var UdpListenerNs::peer_args what a sender lookup reads and writes
91 * @var UdpListenerNs::ok a call's true/false outcome
92 * @var UdpListenerNs::text the group a lookup reports, or NULL
93 * @var UdpListenerNs::listen bind a port and route its datagrams to a handler
94 * @var UdpListenerNs::listen_multicast bind a port and join an IPv4 group on every interface
95 * @var UdpListenerNs::leave_multicast leave the group bound on a port and free its slot
96 * @var UdpListenerNs::poll deliver received datagrams to their handlers
97 * @var UdpListenerNs::reply answer the peer a handler was given
98 * @var UdpListenerNs::peer_addr copy a peer's address and port out
99 * @var UdpListenerNs::sendto send from a bound port to an arbitrary destination
100 * @var UdpListenerNs::close unbind a port and free its slot, leaving any group first
101 * @var UdpListenerNs::joined_group the group a port joined, formatted, or NULL
102 *
103 * reply() and sendto() send the caller's bytes from where they already are, and report whether the
104 * stack took them. There is nothing between the caller and the wire: a refusal means the datagram
105 * did not leave, the caller's buffer is untouched, and the caller sends it again. A datagram longer
106 * than ::PROTOCORE_UDP_RX_BUF_SIZE is refused.
107 */
108typedef struct
109{
110 uint16_t port; ///< the receive port every call names
111 UdpBindArgs bind; ///< what creating a receive port takes (RFC 768 User Interface)
112 UdpSendArgs send_args; ///< what sending a datagram takes
113 UdpPeerArgs peer_args; ///< what a sender lookup reads and writes
115 const char *text;
117
118/** @brief The operands and the outcome. */
120
121/** @brief The entries. */
122typedef struct
123{
124 void (*const listen)(uint8_t *work);
125 void (*const listen_multicast)(uint8_t *work);
126 void (*const leave_multicast)(uint8_t *work);
127 void (*const poll)(uint8_t *work);
128 void (*const reply)(uint8_t *work);
129 void (*const peer_addr)(uint8_t *work);
130 void (*const sendto)(uint8_t *work);
131 void (*const close)(uint8_t *work);
132 void (*const joined_group)(uint8_t *work);
134
135// What the table binds, defined once in the .c and taking one parameter each: everything
136// else an entry needs is an operand in UdpListenerV or a region of the borrow at a fixed offset.
140void protocore_udp_listener_poll(uint8_t *work);
146
147// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
148// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
149// `UdpListener.listen(work)` resolves to a named function and becomes a DIRECT call. An extern table
150// leaves the call indirect and the symbol live at every level, -O2 -flto included.
151static const UdpListenerNs UdpListener __attribute__((unused)) = {
153 .listen_multicast = protocore_udp_listener_listen_multicast,
161};
162
163/**
164 * @brief The PROTOCORE_UDP_LISTENER_BORROW bytes this module's state lives in.
165 *
166 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
167 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
168 * walks, so the state lasts the life of the program.
169 *
170 * @return the span.
171 */
173
175
176#endif // PROTOCORE_UDP_SERVER_H
Layer 3 (Network) - a family-tagged IP address (IPv4 or IPv6) with RFC-faithful text parsing,...
RFC 768 "the creation of new receive ports": what binding one takes.
Definition server.h:56
protocore_udp_handler handler
what a received datagram is delivered to
Definition server.h:57
void * handler_ctx
the opaque context that handler is given back
Definition server.h:58
const char * group_ip
the IPv4 group to join, as text
Definition server.h:59
The entries.
Definition server.h:123
void(*const listen)(uint8_t *work)
Definition server.h:124
const char * text
Definition server.h:115
UdpSendArgs send_args
what sending a datagram takes
Definition server.h:112
UdpBindArgs bind
what creating a receive port takes (RFC 768 User Interface)
Definition server.h:111
proto_bool ok
Definition server.h:114
uint16_t port
the receive port every call names
Definition server.h:110
UdpPeerArgs peer_args
what a sender lookup reads and writes
Definition server.h:113
RFC 768 "an indication of source port and source address": the sender a reply answers.
Definition server.h:73
size_t ip_cap
how much room that has
Definition server.h:76
uint16_t * port_out
where its port is written
Definition server.h:77
char * ip_out
where its address is formatted
Definition server.h:75
const struct protocore_udp_peer * peer
the sender a reply answers
Definition server.h:74
RFC 768 "an operation that allows a datagram to be sent": where it goes and what it carries.
Definition server.h:64
const protocore_ip * dst
where a send goes
Definition server.h:65
uint16_t dst_port
its port
Definition server.h:66
const uint8_t * data
the octets to send
Definition server.h:67
size_t len
how many
Definition server.h:68
A v4 or v6 address in network (big-endian) byte order.
Definition ip.h:56
void protocore_udp_listener_listen_multicast(uint8_t *work)
void protocore_udp_listener_leave_multicast(uint8_t *work)
void protocore_udp_listener_joined_group(uint8_t *work)
void protocore_udp_listener_sendto(uint8_t *work)
UdpListenerVars UdpListenerV
The operands and the outcome.
void protocore_udp_listener_listen(uint8_t *work)
void protocore_udp_listener_close(uint8_t *work)
void protocore_udp_listener_peer_addr(uint8_t *work)
void(* protocore_udp_handler)(const uint8_t *data, size_t len, const struct protocore_udp_peer *peer, void *ctx)
Datagram handler, invoked once per received datagram by poll().
Definition server.h:51
uint8_t * protocore_udp_listener_span(void)
The PROTOCORE_UDP_LISTENER_BORROW bytes this module's state lives in.
void protocore_udp_listener_reply(uint8_t *work)
void protocore_udp_listener_poll(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