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