DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
snmp_notify.cpp
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_notify.cpp
6 * @brief Outbound SNMP Trap / Inform PDU builder (host-testable) + the UDP send
7 * (ESP32 only). SNMPv3 USM notifications live in snmp_v3.cpp.
8 */
9
11
12#if DETWS_ENABLE_SNMP_TRAP
13
15#include <string.h>
16
17// The two mandatory bindings of any v2c/v3 notification (RFC 3416 4.2.6).
18static const uint32_t OID_SYSUPTIME_0[] = {1, 3, 6, 1, 2, 1, 1, 3, 0};
19static const uint32_t OID_SNMPTRAPOID_0[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0};
20
21// Encode one caller varbind: SEQUENCE { OID, typed-value }.
22static void put_varbind(BerEnc *e, const SnmpVarbind *vb)
23{
24 size_t t = ber_seq_begin(e, (uint8_t)SnmpTag::BER_SEQUENCE);
25 ber_put_oid(e, vb->oid, vb->oid_len);
26 switch (vb->type)
27 {
28 case (uint8_t)SnmpVbType::SNMP_VB_INT:
29 ber_put_integer(e, vb->ival);
30 break;
31 case (uint8_t)SnmpVbType::SNMP_VB_STRING:
32 ber_put_octet_string(e, (uint8_t)SnmpTag::BER_OCTET_STRING, vb->bytes, vb->blen);
33 break;
34 case (uint8_t)SnmpVbType::SNMP_VB_OID:
35 ber_put_oid(e, vb->oid_val, vb->oid_val_len);
36 break;
37 case (uint8_t)SnmpVbType::SNMP_VB_COUNTER32:
38 ber_put_uint(e, (uint8_t)SnmpTag::SNMP_COUNTER32, (uint32_t)vb->ival);
39 break;
40 case (uint8_t)SnmpVbType::SNMP_VB_GAUGE32:
41 ber_put_uint(e, (uint8_t)SnmpTag::SNMP_GAUGE32, (uint32_t)vb->ival);
42 break;
43 case (uint8_t)SnmpVbType::SNMP_VB_TIMETICKS:
44 ber_put_uint(e, (uint8_t)SnmpTag::SNMP_TIMETICKS, (uint32_t)vb->ival);
45 break;
46 case (uint8_t)SnmpVbType::SNMP_VB_IPADDR:
47 ber_put_octet_string(e, (uint8_t)SnmpTag::SNMP_IPADDRESS, vb->bytes, vb->blen);
48 break;
49 default:
50 e->ok = false;
51 break;
52 }
53 ber_seq_end(e, t);
54}
55
56// Build the notification PDU (request-id, 0, 0, varbinds) under @p pdu_tag. The
57// varbinds begin with sysUpTime.0 + snmpTrapOID.0. Shared with the v3 builder
58// (which wraps this PDU in a scopedPDU); see snmp_notify_build_pdu() in the header.
59size_t snmp_notify_build_pdu(BerEnc *e, uint8_t pdu_tag, uint32_t request_id, const uint32_t *trap_oid,
60 size_t trap_oid_len, uint32_t uptime_ticks, const SnmpVarbind *vbs, size_t n)
61{
62 size_t pdu = ber_seq_begin(e, pdu_tag);
63 ber_put_integer(e, (long)request_id); // request-id
64 ber_put_integer(e, 0); // error-status
65 ber_put_integer(e, 0); // error-index
66 size_t vbl = ber_seq_begin(e, (uint8_t)SnmpTag::BER_SEQUENCE);
67 // sysUpTime.0 = TimeTicks
68 {
69 size_t t = ber_seq_begin(e, (uint8_t)SnmpTag::BER_SEQUENCE);
70 ber_put_oid(e, OID_SYSUPTIME_0, sizeof(OID_SYSUPTIME_0) / sizeof(uint32_t));
71 ber_put_uint(e, (uint8_t)SnmpTag::SNMP_TIMETICKS, uptime_ticks);
72 ber_seq_end(e, t);
73 }
74 // snmpTrapOID.0 = OID
75 {
76 size_t t = ber_seq_begin(e, (uint8_t)SnmpTag::BER_SEQUENCE);
77 ber_put_oid(e, OID_SNMPTRAPOID_0, sizeof(OID_SNMPTRAPOID_0) / sizeof(uint32_t));
78 ber_put_oid(e, trap_oid, trap_oid_len);
79 ber_seq_end(e, t);
80 }
81 for (size_t i = 0; i < n; i++)
82 put_varbind(e, &vbs[i]);
83 ber_seq_end(e, vbl);
84 ber_seq_end(e, pdu);
85 return e->len;
86}
87
88size_t snmp_notify_build_v2c(uint8_t *out, size_t cap, const char *community, uint8_t pdu_tag, uint32_t request_id,
89 const uint32_t *trap_oid, size_t trap_oid_len, uint32_t uptime_ticks,
90 const SnmpVarbind *vbs, size_t n)
91{
92 if (!out || !community || !trap_oid)
93 return 0;
94 BerEnc e;
95 ber_enc_init(&e, out, cap);
96 size_t msg = ber_seq_begin(&e, (uint8_t)SnmpTag::BER_SEQUENCE);
97 ber_put_integer(&e, 1); // version: SNMPv2c
98 ber_put_octet_string(&e, (uint8_t)SnmpTag::BER_OCTET_STRING, (const uint8_t *)community,
99 strnlen(community, SNMP_COMMUNITY_MAX + 1));
100 snmp_notify_build_pdu(&e, pdu_tag, request_id, trap_oid, trap_oid_len, uptime_ticks, vbs, n);
101 ber_seq_end(&e, msg);
102 return e.ok ? e.len : 0;
103}
104
105// ---------------------------------------------------------------------------
106// Transport (ESP32 only)
107// ---------------------------------------------------------------------------
108#if defined(ARDUINO)
109
111#include <Arduino.h>
112
113// All SNMP-notify transport state, owned by one instance (internal linkage): the trap
114// request-id counter, so it is one named owner, unreachable from any other translation unit.
115namespace
116{
117struct SnmpNotifyCtx
118{
119 uint32_t trap_reqid = 1;
120};
121SnmpNotifyCtx s_notify;
122} // namespace
123
124bool snmp_trap_v2c(const char *dst_ip, uint16_t port, const char *community, const uint32_t *trap_oid,
125 size_t trap_oid_len, const SnmpVarbind *vbs, size_t n)
126{
127 uint8_t buf[DETWS_SNMP_TRAP_BUF_SIZE];
128 uint32_t up = (uint32_t)(millis() / 10); // TimeTicks = hundredths of a second
129 size_t len = snmp_notify_build_v2c(buf, sizeof(buf), community, (uint8_t)SnmpTag::SNMP_PDU_TRAPV2,
130 s_notify.trap_reqid++, trap_oid, trap_oid_len, up, vbs, n);
131 return len && det_udp_sendto(dst_ip, port, buf, len);
132}
133
134bool snmp_inform_v2c(const char *dst_ip, uint16_t port, const char *community, uint32_t request_id,
135 const uint32_t *trap_oid, size_t trap_oid_len, const SnmpVarbind *vbs, size_t n)
136{
137 uint8_t buf[DETWS_SNMP_TRAP_BUF_SIZE];
138 uint32_t up = (uint32_t)(millis() / 10);
139 size_t len = snmp_notify_build_v2c(buf, sizeof(buf), community, 0xA6 /* InformRequest */, request_id, trap_oid,
140 trap_oid_len, up, vbs, n);
141 return len && det_udp_sendto(dst_ip, port, buf, len);
142}
143
144#else // host build: transport is a stub
145
146bool snmp_trap_v2c(const char *, uint16_t, const char *, const uint32_t *, size_t, const SnmpVarbind *, size_t)
147{
148 return false;
149}
150bool snmp_inform_v2c(const char *, uint16_t, const char *, uint32_t, const uint32_t *, size_t, const SnmpVarbind *,
151 size_t)
152{
153 return false;
154}
155
156#endif // ARDUINO
157
158#endif // DETWS_ENABLE_SNMP_TRAP
#define SNMP_COMMUNITY_MAX
Maximum SNMP community-string length (including null terminator).
#define DETWS_SNMP_TRAP_BUF_SIZE
Static datagram buffer for an outbound SNMP notification, bytes.
Zero-heap ASN.1 BER encoder/decoder for the SNMP agent (DETWS_ENABLE_SNMP).
Outbound SNMP notifications - Trap and InformRequest (DETWS_ENABLE_SNMP_TRAP).
bool det_udp_sendto(const char *dst_ip, uint16_t dst_port, const uint8_t *data, size_t len)
Send a UDP datagram to an arbitrary destination (outbound client).
Definition udp.cpp:196
Layer 4 (Transport) - connectionless UDP datagram service.