ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
rawl2.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 rawl2.h
6 * @brief Raw Layer-2 Ethernet frame codec (PROTOCORE_ENABLE_RAWL2).
7 *
8 * The host-testable core of raw-L2 frame TX/RX: build and parse Ethernet II frames (and 802.1Q
9 * VLAN-tagged frames) so the app can inject/receive arbitrary L2 frames - the basis for the raw-L2
10 * industrial protocols (PROFINET DCP, IEC 61850 GOOSE, POWERLINK, SERCOS) and for custom management /
11 * proprietary MAC framing. On device the bytes go out through the vendor L2 transmit path
12 * (wired or Wi-Fi); the MAC normally appends the FCS, so the builder emits the frame
13 * without it and `Rawl2.fcs` is provided for the cases that need it.
14 *
15 * Ethernet II: [dst MAC 6][src MAC 6][ethertype 2][payload]
16 * 802.1Q: [dst 6][src 6][0x8100][TCI 2][ethertype 2][payload]
17 *
18 * Pure, zero heap, no stdlib, host-testable.
19 */
20
21#ifndef PROTOCORE_RAWL2_H
22#define PROTOCORE_RAWL2_H
23
24#include "protocore_config.h" // the entry point: protocore_types.h for the widths
25
26#if PROTOCORE_ENABLE_RAWL2
27
29
30// This module holds nothing between calls, so it carves no borrow and states none. An entry
31// takes one all the same, and never reads it, so every namespace in the tree is invoked the
32// same way.
33
34// Ethernet II framing sizes + ethertypes.
35#define ETH_ALEN 6 ///< MAC address length.
36#define ETH_HDR_LEN 14 ///< dst + src + ethertype.
37#define ETH_VLAN_HDR_LEN 18 ///< with the 4-octet 802.1Q tag.
38#define ETH_TPID_8021Q 0x8100 ///< 802.1Q tag protocol id.
39#define ETHERTYPE_IPV4 0x0800
40#define ETHERTYPE_ARP 0x0806
41#define ETHERTYPE_PROFINET 0x8892 ///< PROFINET RT / DCP.
42#define ETHERTYPE_GOOSE 0x88B8 ///< IEC 61850 GOOSE.
43#define ETHERTYPE_POWERLINK 0x88AB
44
45/** @brief A parsed Ethernet frame (pointers into the input). */
46typedef struct
47{
48 const uint8_t *dst;
49 const uint8_t *src;
50 proto_bool vlan;
51 uint8_t pcp;
52 uint16_t vid;
53 uint16_t ethertype;
54 const uint8_t *payload;
55 size_t payload_len;
56} EthFrame;
57
58/** @brief What build takes: dst, src, ethertype, payload, ... */
59typedef struct
60{
61 const uint8_t *dst;
62 const uint8_t *src;
63 uint16_t ethertype;
64 const uint8_t *payload;
65 size_t payload_len;
66 uint8_t *out;
67 size_t cap;
68} Rawl2BuildArgs;
69
70/** @brief What build_vlan takes: dst, src, pcp, dei, vid, ethertype, ... */
71typedef struct
72{
73 const uint8_t *dst;
74 const uint8_t *src;
75 uint8_t pcp; ///< priority code point (0..7)
76 proto_bool dei; ///< drop-eligible indicator
77 uint16_t vid; ///< VLAN id (0..4095)
78 uint16_t ethertype;
79 const uint8_t *payload;
80 size_t payload_len;
81 uint8_t *out;
82 size_t cap;
83} Rawl2BuildVlanArgs;
84
85/** @brief What parse takes: frame, len, out. */
86typedef struct
87{
88 const uint8_t *frame;
89 size_t len;
90 EthFrame *out;
91} Rawl2ParseArgs;
92
93/** @brief What fcs takes: bytes, len. */
94typedef struct
95{
96 const uint8_t *bytes;
97 size_t len;
98} Rawl2FcsArgs;
99
100/**
101 * @brief Raw Layer-2 Ethernet frame codec (PROTOCORE_ENABLE_RAWL2).
102 *
103 * A caller sets the members a call takes, invokes it through ::Rawl2 with the bytes it runs
104 * out of, and reads the outcome off the same handle.
105 *
106 * Rawl2.build_args.dst = ...;
107 * Rawl2.build_args.src = ...;
108 * Rawl2.build_args.ethertype = ...;
109 * Rawl2.build_args.payload = ...;
110 * Rawl2.build_args.payload_len = ...;
111 * Rawl2.build_args.out = ...;
112 * Rawl2.build_args.cap = ...;
113 * Rawl2.build(work);
114 * // Rawl2.n is what the call reports
115 *
116 * @var Rawl2Ns::build_args what build takes: dst, src, ethertype, payload,
117 * @var Rawl2Ns::build_vlan_args what build_vlan takes: dst, src, pcp, dei, vid, ethertype,
118 * @var Rawl2Ns::parse_args what parse takes: frame, len, out
119 * @var Rawl2Ns::fcs_args what fcs takes: bytes, len
120 * @var Rawl2Ns::ok a call's true/false outcome
121 * @var Rawl2Ns::n the frame length (14 + payload_len), or 0 if it won't fit or a ...
122 * @var Rawl2Ns::u32 what a call reports
123 * @var Rawl2Ns::build build an Ethernet II frame (no FCS)
124 * @var Rawl2Ns::build_vlan build an 802.1Q VLAN-tagged Ethernet frame (no FCS)
125 * @var Rawl2Ns::parse parse an Ethernet II / 802.1Q frame (FCS not expected). true if ...
126 * @var Rawl2Ns::fcs IEEE 802.3 frame check sequence (CRC-32, reflected, init ...
127 *
128 * @c work is bytes the CALLER holds. This module reads none of them: it carries nothing
129 * between calls, so there is no state to keep and nothing to wipe. The parameter is there so
130 * a caller drives every namespace the same way.
131 */
132typedef struct
133{
134 Rawl2BuildArgs build_args;
135 Rawl2BuildVlanArgs build_vlan_args;
136 Rawl2ParseArgs parse_args;
137 Rawl2FcsArgs fcs_args;
138 proto_bool ok;
139 size_t n;
140 uint32_t u32;
141} Rawl2Vars;
142
143/** @brief The operands and the outcome. */
144extern Rawl2Vars Rawl2V;
145
146/** @brief The entries. */
147typedef struct
148{
149 void (*const build)(uint8_t *work);
150 void (*const build_vlan)(uint8_t *work);
151 void (*const parse)(uint8_t *work);
152 void (*const fcs)(uint8_t *work);
153} Rawl2Ns;
154
155// What the table binds, defined once in the .c and taking one parameter each: everything
156// else an entry needs is an operand in Rawl2V or a region of the borrow at a fixed offset.
157void protocore_rawl2_build(uint8_t *work);
158void protocore_rawl2_build_vlan(uint8_t *work);
159void protocore_rawl2_parse(uint8_t *work);
160void protocore_rawl2_fcs(uint8_t *work);
161
162// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
163// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
164// `Rawl2.build(work)` resolves to a named function and becomes a DIRECT call. An extern table
165// leaves the call indirect and the symbol live at every level, -O2 -flto included.
166static const Rawl2Ns Rawl2 __attribute__((unused)) = {
167 .build = protocore_rawl2_build,
168 .build_vlan = protocore_rawl2_build_vlan,
169 .parse = protocore_rawl2_parse,
170 .fcs = protocore_rawl2_fcs,
171};
172
174
175#endif // PROTOCORE_ENABLE_RAWL2
176
177#endif // PROTOCORE_RAWL2_H
#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