ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
diffserv.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 diffserv.h
6 * @brief Layer 4 (Transport) - DiffServ QoS marking (RFC 2474) for outbound traffic.
7 *
8 * Stamps the 6-bit DSCP into the DS field (the high 6 bits of the IPv4 TOS / IPv6 Traffic-Class byte) of
9 * outbound TCP connections and UDP datagrams so a QoS-aware network - and the Wi-Fi driver's 802.11e WMM
10 * access-category mapping - can prioritize real-time / safety packets over best-effort. The marking is applied
11 * on the stack's own thread where the pcb is created (accept / connect / udp create), so nothing is added to
12 * the send hot path. This module owns the two server-wide DSCP defaults; the per-listener override lives with
13 * its control block (tcp/server/server.c) but reads the defaults through here.
14 *
15 * Two levels of control, coarse to fine, each set through the object it marks:
16 * - DiffServ.set_default: every outbound TCP connection, accepted and client, starts here.
17 * - TcpListener.set_dscp: every connection accepted on one port, overriding that default.
18 * A live connection has no third level. RFC 9293 sec 3.9.2 SHLD-23: an application should not change
19 * the Diffserv field during a connection, so the port is the finest granularity.
20 * DiffServ.set_udp is the same default for outbound datagrams. A DSCP of 0 is best-effort: no
21 * marking, TOS left 0.
22 *
23 * @author Douglas Quigg (dstroy0)
24 * @date 2026
25 */
26
27#ifndef PROTOCORE_DIFFSERV_H
28#define PROTOCORE_DIFFSERV_H
29
30#include "protocore_config.h" // the entry point: the enable gate below, and the widths
31
32#if PROTOCORE_ENABLE_DIFFSERV
33
35
36// Common RFC 2474 / RFC 4594 code points for convenience; any 0-63 value is accepted.
37#define PROTOCORE_DSCP_CS0 0 ///< default / best effort
38#define PROTOCORE_DSCP_CS6 48 ///< network control
39#define PROTOCORE_DSCP_EF 46 ///< expedited forwarding (low-latency real-time)
40#define PROTOCORE_DSCP_AF41 34 ///< assured forwarding, class 4 low drop (interactive)
41#define PROTOCORE_DSCP_AF31 26 ///< assured forwarding, class 3 low drop (multimedia streaming)
42// PROTOCORE_DSCP_UNSET, the per-listener "no code point" sentinel, is a value of Listener::dscp and
43// is stated with that field in network_drivers/transport/tcp/common.h.
44
45/** @brief DSCP (0-63) -> the 8-bit DS field. The low 2 bits are ECN (left 0); TOS = DSCP << 2. */
46static inline uint8_t protocore_dscp_to_tos(uint8_t dscp)
47{
48 return (uint8_t)((dscp & 0x3F) << 2);
49}
50
51/**
52 * @brief The DSCP marks this server sets on what it sends.
53 *
54 * A caller sets the mark it wants, invokes the call through ::DiffServ, and reads a mark back off
55 * the same handle.
56 *
57 * @var DiffServNs::dscp the code point a setter installs
58 * @var DiffServNs::u8 the code point a getter reports
59 * @var DiffServNs::set_default the mark new TCP connections take
60 * @var DiffServNs::default_dscp that mark
61 * @var DiffServNs::set_udp the mark UDP sends take
62 * @var DiffServNs::udp_dscp that mark
63 *
64 * Per-connection and per-listener marks are set through the connection and the listener, which own
65 * those objects; this table holds only the defaults they start from.
66 */
67typedef struct
68{
69 uint8_t dscp;
70 uint8_t u8;
71} DiffServVars;
72
73/** @brief The operands and the outcome. */
74extern DiffServVars DiffServV;
75
76/** @brief The entries. */
77typedef struct
78{
79 void (*const set_default)(uint8_t *work);
80 void (*const default_dscp)(uint8_t *work);
81 void (*const set_udp)(uint8_t *work);
82 void (*const udp_dscp)(uint8_t *work);
83} DiffServNs;
84
85// What the table binds, defined once in the .c and taking one parameter each: everything
86// else an entry needs is an operand in DiffServV or a region of the borrow at a fixed offset.
87void protocore_diff_serv_set_default(uint8_t *work);
88void protocore_diff_serv_default_dscp(uint8_t *work);
89void protocore_diff_serv_set_udp(uint8_t *work);
90void protocore_diff_serv_udp_dscp(uint8_t *work);
91
92// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
93// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
94// `DiffServ.set_default(work)` resolves to a named function and becomes a DIRECT call. An extern table
95// leaves the call indirect and the symbol live at every level, -O2 -flto included.
96static const DiffServNs DiffServ __attribute__((unused)) = {
97 .set_default = protocore_diff_serv_set_default,
98 .default_dscp = protocore_diff_serv_default_dscp,
99 .set_udp = protocore_diff_serv_set_udp,
100 .udp_dscp = protocore_diff_serv_udp_dscp,
101};
102
103/**
104 * @brief The PROTOCORE_DIFFSERV_BORROW bytes this module's state lives in.
105 *
106 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
107 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
108 * walks, so the state lasts the life of the program.
109 *
110 * @return the span.
111 */
112uint8_t *protocore_diffserv_span(void);
113
114/** @brief The server-wide default DSCP outbound TCP connections start from. */
115uint8_t protocore_diffserv_default_dscp(void);
116
117/** @brief The default DSCP outbound UDP datagrams are stamped with. */
118uint8_t protocore_diffserv_udp_dscp(void);
119
121
122#endif // PROTOCORE_ENABLE_DIFFSERV
123
124#endif // PROTOCORE_DIFFSERV_H
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
#define PROTOCORE_END_DECLS
Definition types.h:97