11#if DETWS_ENABLE_SNMP_V3
21static uint32_t snmp_v3_uptime_s()
23 return (uint32_t)(millis() / 1000ULL);
26static uint32_t snmp_v3_uptime_s()
33static const uint32_t kUsmStatsBase[] = {1, 3, 6, 1, 6, 3, 15, 1, 1};
34enum class UsmStat : uint8_t
36 USM_STAT_NOT_IN_TIME = 2,
37 USM_STAT_UNKNOWN_USER = 3,
38 USM_STAT_UNKNOWN_ENGINE = 4,
39 USM_STAT_WRONG_DIGEST = 5,
53 uint8_t engine_id[
SNMP_V3_ENGINEID_MAX] = {0x80, 0x00, 0xC0, 0xDE, 0x05, 0x01, 0x02, 0x03, 0x04};
54 size_t engine_id_len = 9;
58 uint8_t auth_key[SNMP_USM_KEY_LEN];
59 uint8_t priv_key[SNMP_USM_KEY_LEN];
60 bool auth_set =
false;
61 bool priv_set =
false;
62 uint32_t salt_ctr = 0;
65 uint32_t stat_unknown_engine = 0;
66 uint32_t stat_unknown_user = 0;
67 uint32_t stat_wrong_digest = 0;
68 uint32_t stat_not_in_time = 0;
69 uint32_t stat_decrypt = 0;
84void snmp_v3_init(
const uint8_t *engine_id,
size_t engine_id_len)
88 memcpy(s_v3.engine_id, engine_id, engine_id_len);
89 s_v3.engine_id_len = engine_id_len;
92 s_v3.auth_set =
false;
93 s_v3.priv_set =
false;
96void snmp_v3_set_user(
const char *user,
const char *auth_pass,
const char *priv_pass)
98 strncpy(s_v3.user, user ? user :
"", sizeof(s_v3.user) - 1);
99 s_v3.user[
sizeof(s_v3.user) - 1] =
'\0';
100 s_v3.auth_set = auth_pass && auth_pass[0];
101 s_v3.priv_set = priv_pass && priv_pass[0];
103 snmp_usm_localize_key(auth_pass, s_v3.engine_id, s_v3.engine_id_len, s_v3.auth_key);
105 snmp_usm_localize_key(priv_pass, s_v3.engine_id, s_v3.engine_id_len, s_v3.priv_key);
108void snmp_v3_set_boots(uint32_t boots)
112uint32_t snmp_v3_get_boots()
121static bool ct_eq(
const uint8_t *a,
const uint8_t *b,
size_t n)
124 for (
size_t i = 0; i < n; i++)
125 r |= (uint8_t)(a[i] ^ b[i]);
129static void put_be32(uint8_t *p, uint32_t v)
131 p[0] = (uint8_t)(v >> 24);
132 p[1] = (uint8_t)(v >> 16);
133 p[2] = (uint8_t)(v >> 8);
138static bool parse_scoped(
const uint8_t *buf,
size_t len,
const uint8_t **ctxname,
size_t *ctxname_len,
139 const uint8_t **pdu,
size_t *pdu_len)
142 ber_dec_init(&d, buf, len);
145 if (!ber_read_header(&d, &tag, &l) || tag != (uint8_t)SnmpTag::BER_SEQUENCE)
147 if (!ber_read_header(&d, &tag, &l) || tag != (uint8_t)SnmpTag::BER_OCTET_STRING)
150 if (!ber_read_header(&d, &tag, &l) || tag != (uint8_t)SnmpTag::BER_OCTET_STRING)
152 *ctxname = d.buf + d.pos;
155 size_t pdu_start = d.pos;
156 if (!ber_read_header(&d, &tag, &l))
158 *pdu = buf + pdu_start;
159 *pdu_len = (size_t)(d.pos - pdu_start) + l;
164static long inner_request_id(
const uint8_t *mdata,
size_t mdata_len,
bool priv)
168 const uint8_t *ctxname;
172 if (!parse_scoped(mdata, mdata_len, &ctxname, &ctxname_len, &pdu, &pdu_len))
175 ber_dec_init(&d, pdu, pdu_len);
179 if (!ber_read_header(&d, &tag, &l) || !ber_read_integer(&d, &rid))
185static size_t build_message(
long msg_id,
bool auth,
bool priv,
const uint8_t *scoped,
size_t scoped_len, uint8_t *resp,
188 uint32_t now = snmp_v3_uptime_s();
189 const uint8_t *data_ptr = scoped;
190 size_t data_len = scoped_len;
191 uint8_t salt[SNMP_V3_PRIV_PARAM_LEN];
196 put_be32(salt + 4, ++s_v3.salt_ctr);
200 memcpy(iv + 8, salt, SNMP_V3_PRIV_PARAM_LEN);
201 if (scoped_len >
sizeof(s_v3.v3_d))
203 snmp_aes128_cfb(s_v3.priv_key, iv, scoped, s_v3.v3_d, scoped_len,
true);
204 data_ptr = s_v3.v3_d;
209 ber_enc_init(&se, s_v3.v3_sec,
sizeof(s_v3.v3_sec));
210 size_t ss = ber_seq_begin(&se, (uint8_t)SnmpTag::BER_SEQUENCE);
211 ber_put_octet_string(&se, (uint8_t)SnmpTag::BER_OCTET_STRING, s_v3.engine_id, s_v3.engine_id_len);
212 ber_put_integer(&se, (
long)s_v3.boots);
213 ber_put_integer(&se, (
long)now);
214 ber_put_octet_string(&se, (uint8_t)SnmpTag::BER_OCTET_STRING, (
const uint8_t *)s_v3.user,
219 auth_off = se.len + 2;
220 uint8_t zeros[SNMP_V3_AUTH_PARAM_LEN];
221 memset(zeros, 0,
sizeof(zeros));
222 ber_put_octet_string(&se, (uint8_t)SnmpTag::BER_OCTET_STRING, zeros, SNMP_V3_AUTH_PARAM_LEN);
226 ber_put_octet_string(&se, (uint8_t)SnmpTag::BER_OCTET_STRING,
nullptr, 0);
229 ber_put_octet_string(&se, (uint8_t)SnmpTag::BER_OCTET_STRING, salt, SNMP_V3_PRIV_PARAM_LEN);
231 ber_put_octet_string(&se, (uint8_t)SnmpTag::BER_OCTET_STRING,
nullptr, 0);
232 ber_seq_end(&se, ss);
236 size_t sec_len = se.len;
240 ber_enc_init(&e, resp, resp_cap);
241 size_t msg = ber_seq_begin(&e, (uint8_t)SnmpTag::BER_SEQUENCE);
242 ber_put_integer(&e, (
int)SnmpVersion::SNMP_V3);
243 size_t hdr = ber_seq_begin(&e, (uint8_t)SnmpTag::BER_SEQUENCE);
244 ber_put_integer(&e, msg_id);
245 ber_put_integer(&e, 65507);
246 uint8_t fl = (uint8_t)((auth ? 0x01 : 0) | (priv ? 0x02 : 0));
247 ber_put_octet_string(&e, (uint8_t)SnmpTag::BER_OCTET_STRING, &fl, 1);
248 ber_put_integer(&e, 3);
249 ber_seq_end(&e, hdr);
250 ber_put_octet_string(&e, (uint8_t)SnmpTag::BER_OCTET_STRING, s_v3.v3_sec, sec_len);
251 size_t sec_value_pos = e.len - sec_len;
253 ber_put_octet_string(&e, (uint8_t)SnmpTag::BER_OCTET_STRING, data_ptr, data_len);
255 ber_put_raw(&e, data_ptr, data_len);
256 ber_seq_end(&e, msg);
259 size_t total = e.len;
265 memcpy(resp + sec_value_pos + auth_off, mac, SNMP_V3_AUTH_PARAM_LEN);
271static size_t build_report(
long msg_id,
bool auth, uint32_t stat, uint32_t count,
long request_id, uint8_t *resp,
275 for (
int i = 0; i < 9; i++)
276 oid[i] = kUsmStatsBase[i];
281 ber_enc_init(&e, s_v3.v3_b,
sizeof(s_v3.v3_b));
282 size_t pdu = ber_seq_begin(&e, (uint8_t)SnmpTag::SNMP_PDU_REPORT);
283 ber_put_integer(&e, request_id);
284 ber_put_integer(&e, 0);
285 ber_put_integer(&e, 0);
286 size_t vbl = ber_seq_begin(&e, (uint8_t)SnmpTag::BER_SEQUENCE);
287 size_t vb = ber_seq_begin(&e, (uint8_t)SnmpTag::BER_SEQUENCE);
288 ber_put_oid(&e, oid, 11);
289 ber_put_uint(&e, (uint8_t)SnmpTag::SNMP_COUNTER32, count);
291 ber_seq_end(&e, vbl);
292 ber_seq_end(&e, pdu);
297 ber_enc_init(&sc, s_v3.v3_c,
sizeof(s_v3.v3_c));
298 size_t s = ber_seq_begin(&sc, (uint8_t)SnmpTag::BER_SEQUENCE);
299 ber_put_octet_string(&sc, (uint8_t)SnmpTag::BER_OCTET_STRING, s_v3.engine_id, s_v3.engine_id_len);
300 ber_put_octet_string(&sc, (uint8_t)SnmpTag::BER_OCTET_STRING,
nullptr, 0);
301 ber_put_raw(&sc, s_v3.v3_b, e.len);
306 return build_message(msg_id, auth,
false, s_v3.v3_c, sc.len, resp, resp_cap);
313size_t snmp_v3_process(
const uint8_t *req,
size_t req_len, uint8_t *resp,
size_t resp_cap)
316 ber_dec_init(&d, req, req_len);
319 if (!ber_read_header(&d, &tag, &l) || tag != (uint8_t)SnmpTag::BER_SEQUENCE)
322 if (!ber_read_integer(&d, &version) || version != (
int)SnmpVersion::SNMP_V3)
326 if (!ber_read_header(&d, &tag, &l) || tag != (uint8_t)SnmpTag::BER_SEQUENCE)
331 if (!ber_read_integer(&d, &msg_id) || !ber_read_integer(&d, &msg_max))
335 if (!ber_read_header(&d, &tag, &flags_len) || tag != (uint8_t)SnmpTag::BER_OCTET_STRING || flags_len < 1)
337 uint8_t flags = d.buf[d.pos];
339 if (!ber_read_integer(&d, &sec_model) || sec_model != 3)
341 bool req_auth = flags & 0x01;
342 bool req_priv = flags & 0x02;
343 if (req_priv && !req_auth)
348 if (!ber_read_header(&d, &tag, &sec_len) || tag != (uint8_t)SnmpTag::BER_OCTET_STRING)
350 const uint8_t *sec = d.buf + d.pos;
354 ber_dec_init(&sd, sec, sec_len);
355 if (!ber_read_header(&sd, &tag, &l) || tag != (uint8_t)SnmpTag::BER_SEQUENCE)
358 if (!ber_read_header(&sd, &tag, &eid_len) || tag != (uint8_t)SnmpTag::BER_OCTET_STRING)
360 const uint8_t *eid = sd.buf + sd.pos;
364 if (!ber_read_integer(&sd, &req_boots) || !ber_read_integer(&sd, &req_time))
367 if (!ber_read_header(&sd, &tag, &uname_len) || tag != (uint8_t)SnmpTag::BER_OCTET_STRING)
369 const uint8_t *uname = sd.buf + sd.pos;
372 if (!ber_read_header(&sd, &tag, &aparm_len) || tag != (uint8_t)SnmpTag::BER_OCTET_STRING)
374 const uint8_t *aparm = sd.buf + sd.pos;
375 size_t aparm_off = (size_t)(aparm - req);
378 if (!ber_read_header(&sd, &tag, &pparm_len) || tag != (uint8_t)SnmpTag::BER_OCTET_STRING)
380 const uint8_t *pparm = sd.buf + sd.pos;
386 const uint8_t *mdata = d.buf + d.pos;
387 size_t mdata_len = req_len - d.pos;
390 bool engine_match = (eid_len == s_v3.engine_id_len) && (memcmp(eid, s_v3.engine_id, eid_len) == 0);
393 s_v3.stat_unknown_engine++;
394 return build_report(msg_id,
false, (
int)UsmStat::USM_STAT_UNKNOWN_ENGINE, s_v3.stat_unknown_engine,
395 inner_request_id(mdata, mdata_len, req_priv), resp, resp_cap);
402 if (!s_v3.auth_set || !(uname_len == strnlen(s_v3.user,
SNMP_V3_USER_MAX) && s_v3.user[0] &&
403 memcmp(uname, s_v3.user, uname_len) == 0))
405 s_v3.stat_unknown_user++;
406 return build_report(msg_id,
false, (
int)UsmStat::USM_STAT_UNKNOWN_USER, s_v3.stat_unknown_user, 0, resp,
411 if (aparm_len != SNMP_V3_AUTH_PARAM_LEN || req_len >
sizeof(s_v3.v3_a))
413 s_v3.stat_wrong_digest++;
414 return build_report(msg_id,
false, (
int)UsmStat::USM_STAT_WRONG_DIGEST, s_v3.stat_wrong_digest, 0, resp,
417 memcpy(s_v3.v3_a, req, req_len);
418 memset(s_v3.v3_a + aparm_off, 0, SNMP_V3_AUTH_PARAM_LEN);
420 ssh_hmac_sha256(s_v3.auth_key, SNMP_USM_KEY_LEN, s_v3.v3_a, req_len, mac);
421 if (!ct_eq(mac, aparm, SNMP_V3_AUTH_PARAM_LEN))
423 s_v3.stat_wrong_digest++;
424 return build_report(msg_id,
false, (
int)UsmStat::USM_STAT_WRONG_DIGEST, s_v3.stat_wrong_digest, 0, resp,
429 uint32_t now = snmp_v3_uptime_s();
430 long dt = (long)now - req_time;
433 if ((uint32_t)req_boots != s_v3.boots || dt > 150)
435 s_v3.stat_not_in_time++;
436 return build_report(msg_id,
true, (
int)UsmStat::USM_STAT_NOT_IN_TIME, s_v3.stat_not_in_time, 0, resp, resp_cap);
440 const uint8_t *scoped;
444 if (!s_v3.priv_set || pparm_len != SNMP_V3_PRIV_PARAM_LEN)
447 return build_report(msg_id,
true, (
int)UsmStat::USM_STAT_DECRYPT, s_v3.stat_decrypt, 0, resp, resp_cap);
450 ber_dec_init(&md, mdata, mdata_len);
451 if (!ber_read_header(&md, &tag, &l) || tag != (uint8_t)SnmpTag::BER_OCTET_STRING)
453 const uint8_t *ct = md.buf + md.pos;
455 if (ct_len >
sizeof(s_v3.v3_a))
460 put_be32(iv + 4, (uint32_t)req_time);
461 memcpy(iv + 8, pparm, SNMP_V3_PRIV_PARAM_LEN);
462 snmp_aes128_cfb(s_v3.priv_key, iv, ct, s_v3.v3_a, ct_len,
false);
469 scoped_len = mdata_len;
473 const uint8_t *ctxname;
477 if (!parse_scoped(scoped, scoped_len, &ctxname, &ctxname_len, &pdu, &pdu_len))
479 size_t rpdu = snmp_dispatch_pdu(pdu, pdu_len,
true,
true, s_v3.v3_b,
sizeof(s_v3.v3_b));
485 ber_enc_init(&sc, s_v3.v3_c,
sizeof(s_v3.v3_c));
486 size_t s = ber_seq_begin(&sc, (uint8_t)SnmpTag::BER_SEQUENCE);
487 ber_put_octet_string(&sc, (uint8_t)SnmpTag::BER_OCTET_STRING, s_v3.engine_id, s_v3.engine_id_len);
488 ber_put_octet_string(&sc, (uint8_t)SnmpTag::BER_OCTET_STRING, ctxname, ctxname_len);
489 ber_put_raw(&sc, s_v3.v3_b, rpdu);
494 return build_message(msg_id,
true, req_priv, s_v3.v3_c, sc.len, resp, resp_cap);
497#if DETWS_ENABLE_SNMP_TRAP
506static bool send_v3_notify(
const char *dst_ip, uint16_t port, uint8_t pdu_tag, uint32_t request_id,
507 const uint32_t *trap_oid,
size_t trap_oid_len,
const SnmpVarbind *vbs,
size_t n)
514 ber_enc_init(&e, s_v3.v3_b,
sizeof(s_v3.v3_b));
515 snmp_notify_build_pdu(&e, pdu_tag, request_id, trap_oid, trap_oid_len, snmp_v3_uptime_s() * 100, vbs, n);
521 ber_enc_init(&sc, s_v3.v3_c,
sizeof(s_v3.v3_c));
522 size_t s = ber_seq_begin(&sc, (uint8_t)SnmpTag::BER_SEQUENCE);
523 ber_put_octet_string(&sc, (uint8_t)SnmpTag::BER_OCTET_STRING, s_v3.engine_id, s_v3.engine_id_len);
524 ber_put_octet_string(&sc, (uint8_t)SnmpTag::BER_OCTET_STRING,
nullptr, 0);
525 ber_put_raw(&sc, s_v3.v3_b, e.len);
531 size_t len = build_message((
long)request_id, s_v3.auth_set, s_v3.priv_set, s_v3.v3_c, sc.len, out,
sizeof(out));
535bool snmp_trap_v3(
const char *dst_ip, uint16_t port,
const uint32_t *trap_oid,
size_t trap_oid_len,
536 const SnmpVarbind *vbs,
size_t n)
538 static uint32_t s_v3_trap_id = 1;
539 return send_v3_notify(dst_ip, port, (uint8_t)SnmpTag::SNMP_PDU_TRAPV2, s_v3_trap_id++, trap_oid, trap_oid_len, vbs,
547bool snmp_inform_v3(
const char *dst_ip, uint16_t port, uint32_t request_id,
const uint32_t *trap_oid,
548 size_t trap_oid_len,
const SnmpVarbind *vbs,
size_t n)
550 return send_v3_notify(dst_ip, port, 0xA6 , request_id, trap_oid, trap_oid_len, vbs, n);
#define SNMP_V3_USER_MAX
Maximum SNMPv3 USM user-name length (including null terminator).
#define SNMP_MSG_BUF_SIZE
Static request/response datagram buffers for the SNMP UDP agent.
#define SNMP_V3_ENGINEID_MAX
Maximum SNMPv3 authoritative engine-ID length in bytes (RFC 3411 allows 5..32).
void put_be32(uint8_t *p, uint32_t v)
Zero-heap SNMP v1/v2c agent: PDU processing + a fixed MIB table.
Zero-heap ASN.1 BER encoder/decoder for the SNMP agent (DETWS_ENABLE_SNMP).
USM cryptographic primitives for SNMPv3 (DETWS_ENABLE_SNMP_V3).
Outbound SNMP notifications - Trap and InformRequest (DETWS_ENABLE_SNMP_TRAP).
SNMPv3 User-based Security Model (USM) layer (DETWS_ENABLE_SNMP_V3).
void ssh_hmac_sha256(const uint8_t *key, size_t key_len, const uint8_t *data, size_t len, uint8_t mac[SSH_HMAC_SHA256_LEN])
Compute HMAC-SHA2-256 over a single contiguous buffer.
HMAC-SHA2-256 (RFC 2104 + FIPS 198-1).
#define SSH_HMAC_SHA256_LEN
HMAC-SHA2-256 output length in bytes.
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).
Layer 4 (Transport) - connectionless UDP datagram service.