ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
proxy_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 proxy_protocol.h
6 * @brief HAProxy PROXY protocol codec (PROTOCORE_ENABLE_PROXY_PROTOCOL) - zero-heap parser +
7 * builder for the v1 (text) and v2 (binary) headers a load balancer / proxy prepends,
8 * so the server can recover the real client IPv4 when it sits behind one.
9 *
10 * The header is sent once, before the proxied stream:
11 * - v1 (text): `PROXY TCP4 <src-ip> <dst-ip> <src-port> <dst-port>\r\n` (space-separated,
12 * CRLF-terminated; also `PROXY TCP6 ...` and `PROXY UNKNOWN\r\n`).
13 * - v2 (binary): a 12-octet signature, then ver_cmd (high nibble version 2, low nibble
14 * command - 0x1 PROXY / 0x0 LOCAL), fam (high nibble address family - 0x1 AF_INET, low
15 * nibble transport - 0x1 STREAM), a 2-octet big-endian address-block length, then the
16 * address block (for TCP/IPv4: src(4) dst(4) src-port(2) dst-port(2), network order).
17 *
18 * This codec handles TCP/IPv4 (the library's address family); IPv6 / UNIX / LOCAL headers
19 * parse to their length but yield no addresses. Format per the HAProxy PROXY protocol spec.
20 *
21 * @author Douglas Quigg (dstroy0)
22 * @date 2026
23 */
24
25#ifndef PROTOCORE_PROXY_PROTOCOL_H
26#define PROTOCORE_PROXY_PROTOCOL_H
27
28#include "protocore_config.h" // the entry point: protocore_types.h for the widths
29
30#if PROTOCORE_ENABLE_PROXY_PROTOCOL
31
33
34// This module holds nothing between calls, so it carves no borrow and states none. An entry
35// takes one all the same, and never reads it, so every namespace in the tree is invoked the
36// same way.
37
38#define PROXY_V2_SIG_LEN 12 ///< v2 signature length
39#define PROXY_V2_VER_CMD_PROXY 0x21 ///< version 2 | PROXY command
40#define PROXY_V2_VER_CMD_LOCAL 0x20 ///< version 2 | LOCAL command
41#define PROXY_V2_FAM_TCP4 0x11 ///< AF_INET | STREAM (TCP over IPv4)
42
43/** @brief The decoded proxied connection endpoints (IPv4, host byte order). */
44typedef struct
45{
46 uint8_t version; ///< 1 or 2
47 proto_bool has_addr; ///< true when TCP/IPv4 addresses were decoded
48 uint32_t src_addr; ///< real client IPv4 (host order)
49 uint32_t dst_addr; ///< proxied destination IPv4
50 uint16_t src_port;
51 uint16_t dst_port;
52} ProxyInfo;
53
54/** @brief What parse takes: buf, len, out, consumed. */
55typedef struct
56{
57 const uint8_t *buf;
58 size_t len;
59 ProxyInfo *out;
60 size_t *consumed; ///< receives the header length so the caller can skip it before the stream
61} ProxyProtocolParseArgs;
62
63/** @brief What v1_build takes: buf, cap, src_addr, dst_addr, ... */
64typedef struct
65{
66 char *buf;
67 size_t cap;
68 uint32_t src_addr;
69 uint32_t dst_addr;
70 uint16_t src_port;
71 uint16_t dst_port;
72} ProxyProtocolV1BuildArgs;
73
74/** @brief What v2_build takes: buf, cap, src_addr, dst_addr, ... */
75typedef struct
76{
77 uint8_t *buf;
78 size_t cap;
79 uint32_t src_addr;
80 uint32_t dst_addr;
81 uint16_t src_port;
82 uint16_t dst_port;
83} ProxyProtocolV2BuildArgs;
84
85/**
86 * @brief HAProxy PROXY protocol codec (PROTOCORE_ENABLE_PROXY_PROTOCOL) - zero-heap parser + builder for the v1 (text)
87 * and v2 (binary) headers a load balancer / proxy prepends, so the server can recover the real client IPv4 when it sits
88 * behind one.
89 *
90 * A caller sets the members a call takes, invokes it through ::ProxyProtocol with the bytes it runs
91 * out of, and reads the outcome off the same handle.
92 *
93 * ProxyProtocol.parse_args.buf = ...;
94 * ProxyProtocol.parse_args.len = ...;
95 * ProxyProtocol.parse_args.out = ...;
96 * ProxyProtocol.parse_args.consumed = ...;
97 * ProxyProtocol.parse(work);
98 * // ProxyProtocol.ok is what the call reports
99 *
100 * @var ProxyProtocolNs::parse_args what parse takes: buf, len, out, consumed
101 * @var ProxyProtocolNs::v1_build_args what v1_build takes: buf, cap, src_addr, dst_addr,
102 * @var ProxyProtocolNs::v2_build_args what v2_build takes: buf, cap, src_addr, dst_addr,
103 * @var ProxyProtocolNs::ok true if a complete v1/v2 header was parsed; false if absent or not ...
104 * @var ProxyProtocolNs::n the count a call reports
105 * @var ProxyProtocolNs::parse detect + parse a PROXY header (v1 or v2) at the head of [buf, ...
106 * @var ProxyProtocolNs::v1_build build a v1 (text) TCP4 header. Returns bytes written (excluding ...
107 * @var ProxyProtocolNs::v2_build build a v2 (binary) TCP/IPv4 PROXY header. Returns 28, or 0 on ...
108 *
109 * @c work is bytes the CALLER holds. This module reads none of them: it carries nothing
110 * between calls, so there is no state to keep and nothing to wipe. The parameter is there so
111 * a caller drives every namespace the same way.
112 */
113typedef struct
114{
115 ProxyProtocolParseArgs parse_args;
116 ProxyProtocolV1BuildArgs v1_build_args;
117 ProxyProtocolV2BuildArgs v2_build_args;
118 proto_bool ok;
119 size_t n;
120} ProxyProtocolVars;
121
122/** @brief The operands and the outcome. */
123extern ProxyProtocolVars ProxyProtocolV;
124
125/** @brief The entries. */
126typedef struct
127{
128 void (*const parse)(uint8_t *work);
129 void (*const v1_build)(uint8_t *work);
130 void (*const v2_build)(uint8_t *work);
131} ProxyProtocolNs;
132
133// What the table binds, defined once in the .c and taking one parameter each: everything
134// else an entry needs is an operand in ProxyProtocolV or a region of the borrow at a fixed offset.
135void protocore_proxy_protocol_parse(uint8_t *work);
136void protocore_proxy_protocol_v1_build(uint8_t *work);
137void protocore_proxy_protocol_v2_build(uint8_t *work);
138
139// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
140// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
141// `ProxyProtocol.parse(work)` resolves to a named function and becomes a DIRECT call. An extern table
142// leaves the call indirect and the symbol live at every level, -O2 -flto included.
143static const ProxyProtocolNs ProxyProtocol __attribute__((unused)) = {
144 .parse = protocore_proxy_protocol_parse,
145 .v1_build = protocore_proxy_protocol_v1_build,
146 .v2_build = protocore_proxy_protocol_v2_build,
147};
148
150
151#endif // PROTOCORE_ENABLE_PROXY_PROTOCOL
152
153#endif // PROTOCORE_PROXY_PROTOCOL_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