DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
snmp_agent.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 snmp_agent.h
6 * @brief Zero-heap SNMP v1/v2c agent: PDU processing + a fixed MIB table.
7 *
8 * The agent is split into a pure, host-testable core and an ESP32-only UDP
9 * transport (mirroring how the provisioning service splits its form parser from
10 * its lwIP DNS responder):
11 *
12 * - snmp_agent_process() takes a complete request datagram and produces a
13 * complete response datagram in a caller buffer - no sockets, no heap. It is
14 * unit-tested on the host (env:native_snmp).
15 * - snmp_agent_begin_udp() binds the agent on :161 via the det_udp_* transport
16 * API (Arduino only) and feeds received datagrams through snmp_agent_process().
17 *
18 * The MIB is a fixed BSS table of SNMP_MAX_MIB_ENTRIES objects. Register objects
19 * with snmp_agent_add_*; values are either static (stored in the entry) or
20 * fetched through a getter callback (e.g. sysUpTime). All string/OID values are
21 * referenced by pointer and must outlive the agent (point them at flash/static
22 * data, exactly like the rest of the library's strings).
23 *
24 * Get / GetNext / GetBulk / Set are supported. GetBulk and per-varbind
25 * exceptions (noSuchObject / noSuchInstance / endOfMibView) apply to v2c; v1 uses
26 * the classic error-status / error-index reporting. SNMPv3 (USM) is a separate,
27 * gated layer.
28 */
29
30#ifndef DETERMINISTICESPASYNCWEBSERVER_SNMP_AGENT_H
31#define DETERMINISTICESPASYNCWEBSERVER_SNMP_AGENT_H
32
33#include "ServerConfig.h"
35#include <stddef.h>
36#include <stdint.h>
37
38#if DETWS_ENABLE_SNMP
39
40// SNMP message versions (the on-wire INTEGER value).
41enum class SnmpVersion : uint8_t
42{
43 SNMP_V1 = 0,
44 SNMP_V2C = 1,
45 SNMP_V3 = 3,
46};
47
48// PDU error-status values (RFC 1157 / RFC 3416).
49enum class SnmpErr : uint8_t
50{
51 SNMP_ERR_NO_ERROR = 0,
52 SNMP_ERR_TOO_BIG = 1,
53 SNMP_ERR_NO_SUCH_NAME = 2,
54 SNMP_ERR_BAD_VALUE = 3,
55 SNMP_ERR_READ_ONLY = 4,
56 SNMP_ERR_GEN_ERR = 5,
57 SNMP_ERR_NO_ACCESS = 6,
58 SNMP_ERR_WRONG_TYPE = 7,
59 SNMP_ERR_NOT_WRITABLE = 17,
60};
61
62/**
63 * @brief A typed SNMP value (a varbind's value, or a MIB object's value).
64 *
65 * Only the field matching @ref type is meaningful. String and OID values are
66 * referenced by pointer and are not copied - they must remain valid.
67 */
68struct SnmpValue
69{
70 uint8_t type; ///< BER tag: SnmpTag::BER_INTEGER / SnmpTag::BER_OCTET_STRING / SnmpTag::BER_OID /
71 ///< SnmpTag::SNMP_TIMETICKS / SnmpTag::SNMP_COUNTER32 / SnmpTag::SNMP_GAUGE32 /
72 ///< SnmpTag::SNMP_IPADDRESS, or an exception tag.
73 long ival; ///< value for SnmpTag::BER_INTEGER
74 uint32_t uval; ///< value for SnmpTag::SNMP_TIMETICKS / SnmpTag::SNMP_COUNTER32 / SnmpTag::SNMP_GAUGE32 /
75 ///< SnmpTag::SNMP_IPADDRESS
76 const char *str; ///< bytes for SnmpTag::BER_OCTET_STRING (not owned)
77 size_t str_len; ///< length of @ref str
78 const uint32_t *oid; ///< arcs for SnmpTag::BER_OID (not owned)
79 size_t oid_len; ///< number of arcs in @ref oid
80};
81
82/** @brief Dynamic value getter; fill @p out and return true, or return false for noSuchInstance. */
83typedef bool (*SnmpGetFn)(SnmpValue *out);
84/** @brief Value setter for a writable object; return true on success, false to reject (wrongType/badValue). */
85typedef bool (*SnmpSetFn)(const SnmpValue *in);
86
87// ---------------------------------------------------------------------------
88// Agent configuration / MIB registration
89// ---------------------------------------------------------------------------
90
91/**
92 * @brief Reset the agent and set the read-only community (default "public").
93 *
94 * Clears the MIB table. Call before registering objects. Pass nullptr to keep
95 * the default community.
96 */
97void snmp_agent_init(const char *ro_community = "public");
98
99/** @brief Set the read-write community used to authorize Set requests (default: none -> Sets refused). */
100void snmp_agent_set_rw_community(const char *rw_community);
101
102/**
103 * @brief Populate the standard MIB-II system group (1.3.6.1.2.1.1).
104 *
105 * Adds sysDescr.0, sysObjectID.0, sysUpTime.0 (dynamic, hundredths of a second
106 * since boot), sysContact.0, sysName.0, sysLocation.0 and sysServices.0. The
107 * string arguments are referenced by pointer (not copied). @p services is the
108 * sysServices bitmask (commonly 72 = application + internet layers).
109 */
110void snmp_agent_set_system(const char *descr, const char *contact, const char *name, const char *location,
111 long services = 72);
112
113/** @brief Register a static OCTET STRING object. @return false if the table is full. */
114bool snmp_agent_add_string(const uint32_t *oid, size_t oid_len, const char *value, SnmpSetFn setter = nullptr);
115/** @brief Register a static INTEGER object. @return false if the table is full. */
116bool snmp_agent_add_integer(const uint32_t *oid, size_t oid_len, long value, SnmpSetFn setter = nullptr);
117/** @brief Register a dynamic object served by @p getter (@p type names the value's BER tag). */
118bool snmp_agent_add_dynamic(const uint32_t *oid, size_t oid_len, uint8_t type, SnmpGetFn getter);
119
120// ---------------------------------------------------------------------------
121// Core processing (host-testable; no sockets, no heap)
122// ---------------------------------------------------------------------------
123
124/**
125 * @brief Process one SNMP request datagram and build the response datagram.
126 *
127 * Decodes the v1/v2c message, dispatches the PDU against the MIB, and encodes a
128 * Response PDU into @p resp. A request with an unknown community, a malformed
129 * message, or a v3 message (when SNMPv3 is not enabled) yields no response.
130 *
131 * @param req request datagram bytes.
132 * @param req_len number of bytes in @p req.
133 * @param resp destination buffer for the response datagram.
134 * @param resp_cap capacity of @p resp.
135 * @return number of response bytes written, or 0 to send nothing.
136 */
137size_t snmp_agent_process(const uint8_t *req, size_t req_len, uint8_t *resp, size_t resp_cap);
138
139/**
140 * @brief Process one request PDU against the MIB and emit a GetResponse PDU.
141 *
142 * The shared dispatch core used by both the v1/v2c community framing and the v3
143 * USM layer: it decodes a Get / GetNext / GetBulk / Set PDU TLV, runs it against
144 * the registered MIB, and encodes a single GetResponse PDU TLV.
145 *
146 * @param pdu a complete request-PDU TLV.
147 * @param pdu_len length of @p pdu.
148 * @param allow_write authorize Set (true for the read-write community / a v3 user with write access).
149 * @param v2c use v2c-style per-varbind exceptions (true for v2c and v3); false = v1 error-status.
150 * @param out destination for the response PDU TLV.
151 * @param out_cap capacity of @p out.
152 * @return number of response-PDU bytes written, or 0 on a malformed/unsupported PDU.
153 */
154size_t snmp_dispatch_pdu(const uint8_t *pdu, size_t pdu_len, bool allow_write, bool v2c, uint8_t *out, size_t out_cap);
155
156// ---------------------------------------------------------------------------
157// UDP transport (ESP32 only; no-op stub elsewhere)
158// ---------------------------------------------------------------------------
159
160/**
161 * @brief Bind the agent to UDP @p port (default 161) via the transport-layer UDP service.
162 *
163 * Callback-driven (no per-loop servicing). Call after WiFi is up. On non-Arduino
164 * builds this is a no-op so the core remains host-testable.
165 */
166void snmp_agent_begin_udp(uint16_t port = 161);
167
168#endif // DETWS_ENABLE_SNMP
169
170#endif // DETERMINISTICESPASYNCWEBSERVER_SNMP_AGENT_H
User-facing configuration for DeterministicESPAsyncWebServer.
Zero-heap ASN.1 BER encoder/decoder for the SNMP agent (DETWS_ENABLE_SNMP).