DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
flow_export.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file flow_export.h
6 * @brief Flow-record export codec (DETWS_ENABLE_FLOW_EXPORT) - zero-heap exporter-side
7 * builders for NetFlow v5, NetFlow v9 (RFC 3954), and IPFIX (RFC 7011), so a device
8 * can ship on-device flow accounting to a collector over the existing UDP transport.
9 *
10 * Three formats, all big-endian on the wire:
11 * - NetFlow v5: a fixed legacy layout - a 24-octet header then N fixed 48-octet records.
12 * - NetFlow v9 / IPFIX: the template-then-data model - the exporter sends Template records
13 * describing a record's field layout, then Data records that match a template by id.
14 *
15 * The v9 / IPFIX side is a small cursor (@ref FlowWriter): begin the message, emit a
16 * template, open a data set, append records, then finish (which patches the message
17 * length for IPFIX or the record count for v9). Field offsets verified against RFC 7011
18 * (IPFIX), RFC 3954 (NetFlow v9), and the published v5 record layout.
19 *
20 * This is the wire codec only; the flow cache (the 5-tuple + counters) is the app's, and
21 * the datagram send is `det_udp_sendto`.
22 *
23 * @author Douglas Quigg (dstroy0)
24 * @date 2026
25 */
26
27#ifndef DETERMINISTICESPASYNCWEBSERVER_FLOW_EXPORT_H
28#define DETERMINISTICESPASYNCWEBSERVER_FLOW_EXPORT_H
29
30#include "ServerConfig.h"
31
32#if DETWS_ENABLE_FLOW_EXPORT
33
34#include <stddef.h>
35#include <stdint.h>
36
37// ---- NetFlow v5 (fixed legacy format) ----
38
39#define FLOW_V5_HEADER_SIZE 24
40#define FLOW_V5_RECORD_SIZE 48
41
42/** @brief NetFlow v5 packet header (the builder fills version=5). */
43struct FlowV5Header
44{
45 uint16_t count; ///< number of records that follow
46 uint32_t sys_uptime; ///< ms since the device booted
47 uint32_t unix_secs; ///< seconds since the epoch
48 uint32_t unix_nsecs; ///< residual nanoseconds
49 uint32_t flow_sequence; ///< running count of exported flows
50 uint8_t engine_type; ///< flow-switching engine type
51 uint8_t engine_id; ///< flow-switching engine id
52 uint16_t sampling_interval; ///< sampling mode (top 2 bits) + interval
53};
54
55/** @brief One NetFlow v5 flow record (pad1 / pad2 are zero-filled by the builder). */
56struct FlowV5Record
57{
58 uint32_t src_addr; ///< source IPv4 (host order; written big-endian)
59 uint32_t dst_addr; ///< destination IPv4
60 uint32_t next_hop; ///< next-hop router IPv4
61 uint16_t input; ///< ingress interface SNMP index
62 uint16_t output; ///< egress interface SNMP index
63 uint32_t d_pkts; ///< packets in the flow
64 uint32_t d_octets; ///< bytes in the flow
65 uint32_t first; ///< sys_uptime at flow start
66 uint32_t last; ///< sys_uptime at last packet
67 uint16_t src_port;
68 uint16_t dst_port;
69 uint8_t tcp_flags; ///< cumulative OR of TCP flags
70 uint8_t prot; ///< IP protocol
71 uint8_t tos; ///< IP type of service
72 uint16_t src_as;
73 uint16_t dst_as;
74 uint8_t src_mask; ///< source prefix length
75 uint8_t dst_mask; ///< destination prefix length
76};
77
78/** @brief Write the 24-octet v5 header; returns FLOW_V5_HEADER_SIZE, or 0 on overflow. */
79size_t flow_v5_write_header(uint8_t *buf, size_t cap, const FlowV5Header *h);
80
81/** @brief Write one 48-octet v5 record; returns FLOW_V5_RECORD_SIZE, or 0 on overflow. */
82size_t flow_v5_write_record(uint8_t *buf, size_t cap, const FlowV5Record *r);
83
84// ---- NetFlow v9 / IPFIX (template-then-data) ----
85
86/** @brief A template field specifier: an Information Element id + its on-wire octet length. */
87struct FlowField
88{
89 uint16_t type;
90 uint16_t length;
91};
92
93/** @brief Cursor for building one v9 / IPFIX message. Treat the fields as opaque. */
94struct FlowWriter
95{
96 uint8_t *buf;
97 size_t cap;
98 size_t pos;
99 size_t set_start; ///< offset of the open Set/FlowSet header, or 0 when none is open
100 uint16_t records; ///< running record count (the v9 'count' field)
101 uint8_t version; ///< 9 (NetFlow v9) or 10 (IPFIX)
102 bool error; ///< sticky overflow / misuse flag
103};
104
105/** @brief Begin an IPFIX (version 10) message: writes the 16-octet header (length patched on finish). */
106bool flow_ipfix_begin(FlowWriter *w, uint8_t *buf, size_t cap, uint32_t export_time, uint32_t seq, uint32_t domain_id);
107
108/** @brief Begin a NetFlow v9 message: writes the 20-octet header (count patched on finish). */
109bool flow_v9_begin(FlowWriter *w, uint8_t *buf, size_t cap, uint32_t sys_uptime, uint32_t unix_secs, uint32_t seq,
110 uint32_t source_id);
111
112/**
113 * @brief Emit a Template (Set ID 2 for IPFIX, FlowSet ID 0 for v9) describing @p fields.
114 * @param template_id the id data records will reference (>= 256).
115 */
116bool flow_export_template(FlowWriter *w, uint16_t template_id, const FlowField *fields, size_t field_count);
117
118/** @brief Open a Data Set/FlowSet for @p template_id (must match a previously emitted template). */
119bool flow_export_data_begin(FlowWriter *w, uint16_t template_id);
120
121/** @brief Append one already-encoded data record (its fields must match the template, big-endian). */
122bool flow_export_data_record(FlowWriter *w, const uint8_t *record, size_t len);
123
124/** @brief Close the open Data Set (patches its length; pads to a 4-octet boundary for v9). */
125bool flow_export_data_end(FlowWriter *w);
126
127/** @brief Finish the message (auto-closes an open set); returns total bytes, or 0 on error. */
128size_t flow_export_finish(FlowWriter *w);
129
130#endif // DETWS_ENABLE_FLOW_EXPORT
131
132#endif // DETERMINISTICESPASYNCWEBSERVER_FLOW_EXPORT_H
User-facing configuration for DeterministicESPAsyncWebServer.