1 /**
2 * @file
3 * MIB tree access/construction functions.
4 */
5
6 /*
7 * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without modification,
11 * are permitted provided that the following conditions are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * 3. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
22 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
24 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
30 * OF SUCH DAMAGE.
31 *
32 * Author: Christiaan Simons <christiaan.simons@axon.tv>
33 * Martin Hentschel <info@cl-soft.de>
34 */
35
36 /**
37 * @defgroup snmp SNMPv2c/v3 agent
38 * @ingroup apps
39 * SNMPv2c and SNMPv3 compatible agent<br>
40 * There is also a MIB compiler and a MIB viewer in lwIP/contrib subdir
41 * (lwip/contrib/apps/LwipMibCompiler).<br>
42 * The agent implements the most important MIB2 MIBs including IPv6 support
43 * (interfaces, UDP, TCP, SNMP, ICMP, SYSTEM). IP MIB is an older version
44 * without IPv6 statistics (TODO).<br>
45 * Rewritten by Martin Hentschel <info@cl-soft.de> and
46 * Dirk Ziegelmeier <dziegel@gmx.de>
47 *
48 * 0 Agent Capabilities
49 * ====================
50 *
51 * Features:
52 * ---------
53 * - SNMPv2c support.
54 * - SNMPv3 support (a port to ARM mbedtls is provided, LWIP_SNMP_V3_MBEDTLS option).
55 * - Low RAM usage - no memory pools, stack only.
56 * - MIB2 implementation is separated from SNMP stack.
57 * - Support for multiple MIBs (snmp_set_mibs() call) - e.g. for private MIB.
58 * - Simple and generic API for MIB implementation.
59 * - Comfortable node types and helper functions for scalar arrays and tables.
60 * - Counter64, bit and truthvalue datatype support.
61 * - Callbacks for SNMP writes e.g. to implement persistency.
62 * - Runs on two APIs: RAW and netconn.
63 * - Async API is gone - the stack now supports netconn API instead,
64 * so blocking operations can be done in MIB calls.
65 * SNMP runs in a worker thread when netconn API is used.
66 * - Simplified thread sync support for MIBs - useful when MIBs
67 * need to access variables shared with other threads where no locking is
68 * possible. Used in MIB2 to access lwIP stats from lwIP thread.
69 *
70 * MIB compiler (code generator):
71 * ------------------------------
72 * - Provided in contrib dir.
73 * - Written in C#. MIB viewer using Windows Forms.
74 * - Developed on Windows with Visual Studio 2010.
75 * - Can be compiled and used on all platforms with http://www.monodevelop.com/.
76 * - Based on a heavily modified version of of SharpSnmpLib (a4bd05c6afb4)
77 * (https://sharpsnmplib.codeplex.com/SourceControl/network/forks/Nemo157/MIBParserUpdate).
78 * This has been the last known revision of that code before being converted to
79 * closed source. The new code on github has completely changed, so we can not
80 * just update :-(
81 * - MIB parser, C file generation framework and LWIP code generation are cleanly
82 * separated, which means the code may be useful as a base for code generation
83 * of other SNMP agents.
84 *
85 * Notes:
86 * ------
87 * - Stack and MIB compiler were used to implement a Profinet device.
88 * Compiled/implemented MIBs: LLDP-MIB, LLDP-EXT-DOT3-MIB, LLDP-EXT-PNO-MIB.
89 *
90 * SNMPv1 per RFC1157 and SNMPv2c per RFC 3416
91 * -------------------------------------------
92 * Note the S in SNMP stands for "Simple". Note that "Simple" is
93 * relative. SNMP is simple compared to the complex ISO network
94 * management protocols CMIP (Common Management Information Protocol)
95 * and CMOT (CMip Over Tcp).
96 *
97 * SNMPv3
98 * ------
99 * When SNMPv3 is used, several functions from snmpv3.h must be implemented
100 * by the user. This is mainly user management and persistence handling.
101 * The sample provided in lwip-contrib is insecure, don't use it in production
102 * systems, especially the missing persistence for engine boots variable
103 * simplifies replay attacks.
104 *
105 * MIB II
106 * ------
107 * The standard lwIP stack management information base.
108 * This is a required MIB, so this is always enabled.
109 * The groups EGP, CMOT and transmission are disabled by default.
110 *
111 * Most mib-2 objects are not writable except:
112 * sysName, sysLocation, sysContact, snmpEnableAuthenTraps.
113 * Writing to or changing the ARP and IP address and route
114 * tables is not possible.
115 *
116 * Note lwIP has a very limited notion of IP routing. It currently
117 * doesn't have a route table and doesn't have a notion of the U,G,H flags.
118 * Instead lwIP uses the interface list with only one default interface
119 * acting as a single gateway interface (G) for the default route.
120 *
121 * The agent returns a "virtual table" with the default route 0.0.0.0
122 * for the default interface and network routes (no H) for each
123 * network interface in the netif_list.
124 * All routes are considered to be up (U).
125 *
126 * Loading additional MIBs
127 * -----------------------
128 * MIBs can only be added in compile-time, not in run-time.
129 *
130 *
131 * 1 Building the Agent
132 * ====================
133 * First of all you'll need to add the following define
134 * to your local lwipopts.h:
135 * \#define LWIP_SNMP 1
136 *
137 * and add the source files your makefile.
138 *
139 * Note you'll might need to adapt you network driver to update
140 * the mib2 variables for your interface.
141 *
142 * 2 Running the Agent
143 * ===================
144 * The following function calls must be made in your program to
145 * actually get the SNMP agent running.
146 *
147 * Before starting the agent you should supply pointers
148 * for sysContact, sysLocation, and snmpEnableAuthenTraps.
149 * You can do this by calling
150 *
151 * - snmp_mib2_set_syscontact()
152 * - snmp_mib2_set_syslocation()
153 * - snmp_set_auth_traps_enabled()
154 *
155 * You can register a callback which is called on successful write access:
156 * snmp_set_write_callback().
157 *
158 * Additionally you may want to set
159 *
160 * - snmp_mib2_set_sysdescr()
161 * - snmp_set_device_enterprise_oid()
162 * - snmp_mib2_set_sysname()
163 *
164 * Also before starting the agent you need to setup
165 * one or more trap destinations using these calls:
166 *
167 * - snmp_trap_dst_enable()
168 * - snmp_trap_dst_ip_set()
169 *
170 * If you need more than MIB2, set the MIBs you want to use
171 * by snmp_set_mibs().
172 *
173 * Finally, enable the agent by calling snmp_init()
174 *
175 * @defgroup snmp_core Core
176 * @ingroup snmp
177 *
178 * @defgroup snmp_traps Traps
179 * @ingroup snmp
180 */
181
182 #include "lwip/apps/snmp_opts.h"
183
184 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
185
186 #include "lwip/apps/snmp.h"
187 #include "lwip/apps/snmp_core.h"
188 #include "snmp_core_priv.h"
189 #include "lwip/netif.h"
190 #include <string.h>
191
192
193 #if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
194 #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
195 #endif
196 #if (!LWIP_UDP && LWIP_SNMP)
197 #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
198 #endif
199 #if SNMP_MAX_OBJ_ID_LEN > 255
200 #error "SNMP_MAX_OBJ_ID_LEN must fit into an u8_t"
201 #endif
202
203 struct snmp_statistics snmp_stats;
204 static const struct snmp_obj_id snmp_device_enterprise_oid_default = {SNMP_DEVICE_ENTERPRISE_OID_LEN, SNMP_DEVICE_ENTERPRISE_OID};
205 static const struct snmp_obj_id *snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default;
206
207 const u32_t snmp_zero_dot_zero_values[] = { 0, 0 };
208 const struct snmp_obj_id_const_ref snmp_zero_dot_zero = { LWIP_ARRAYSIZE(snmp_zero_dot_zero_values), snmp_zero_dot_zero_values };
209
210 #if SNMP_LWIP_MIB2 && LWIP_SNMP_V3
211 #include "lwip/apps/snmp_mib2.h"
212 #include "lwip/apps/snmp_snmpv2_framework.h"
213 #include "lwip/apps/snmp_snmpv2_usm.h"
214 static const struct snmp_mib *const default_mibs[] = { &mib2, &snmpframeworkmib, &snmpusmmib };
215 static u8_t snmp_num_mibs = LWIP_ARRAYSIZE(default_mibs);
216 #elif SNMP_LWIP_MIB2
217 #include "lwip/apps/snmp_mib2.h"
218 static const struct snmp_mib *const default_mibs[] = { &mib2 };
219 static u8_t snmp_num_mibs = LWIP_ARRAYSIZE(default_mibs);
220 #else
221 static const struct snmp_mib *const default_mibs[] = { NULL };
222 static u8_t snmp_num_mibs = 0;
223 #endif
224
225 /* List of known mibs */
226 static struct snmp_mib const *const *snmp_mibs = default_mibs;
227
228 /**
229 * @ingroup snmp_core
230 * Sets the MIBs to use.
231 * Example: call snmp_set_mibs() as follows:
232 * static const struct snmp_mib *my_snmp_mibs[] = {
233 * &mib2,
234 * &private_mib
235 * };
236 * snmp_set_mibs(my_snmp_mibs, LWIP_ARRAYSIZE(my_snmp_mibs));
237 */
238 void
snmp_set_mibs(const struct snmp_mib ** mibs,u8_t num_mibs)239 snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs)
240 {
241 LWIP_ASSERT_SNMP_LOCKED();
242 LWIP_ASSERT("mibs pointer must be != NULL", (mibs != NULL));
243 LWIP_ASSERT("num_mibs pointer must be != 0", (num_mibs != 0));
244 snmp_mibs = mibs;
245 snmp_num_mibs = num_mibs;
246 }
247
248 /**
249 * @ingroup snmp_core
250 * 'device enterprise oid' is used for 'device OID' field in trap PDU's (for identification of generating device)
251 * as well as for value returned by MIB-2 'sysObjectID' field (if internal MIB2 implementation is used).
252 * The 'device enterprise oid' shall point to an OID located under 'private-enterprises' branch (1.3.6.1.4.1.XXX). If a vendor
253 * wants to provide a custom object there, he has to get its own enterprise oid from IANA (http://www.iana.org). It
254 * is not allowed to use LWIP enterprise ID!
255 * In order to identify a specific device it is recommended to create a dedicated OID for each device type under its own
256 * enterprise oid.
257 * e.g.
258 * device a > 1.3.6.1.4.1.XXX(ent-oid).1(devices).1(device a)
259 * device b > 1.3.6.1.4.1.XXX(ent-oid).1(devices).2(device b)
260 * for more details see description of 'sysObjectID' field in RFC1213-MIB
261 */
snmp_set_device_enterprise_oid(const struct snmp_obj_id * device_enterprise_oid)262 void snmp_set_device_enterprise_oid(const struct snmp_obj_id *device_enterprise_oid)
263 {
264 LWIP_ASSERT_SNMP_LOCKED();
265 if (device_enterprise_oid == NULL) {
266 snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default;
267 } else {
268 snmp_device_enterprise_oid = device_enterprise_oid;
269 }
270 }
271
272 /**
273 * @ingroup snmp_core
274 * Get 'device enterprise oid'
275 */
snmp_get_device_enterprise_oid(void)276 const struct snmp_obj_id *snmp_get_device_enterprise_oid(void)
277 {
278 LWIP_ASSERT_SNMP_LOCKED();
279 return snmp_device_enterprise_oid;
280 }
281
282 #if LWIP_IPV4
283 /**
284 * Conversion from InetAddressIPv4 oid to lwIP ip4_addr
285 * @param oid points to u32_t ident[4] input
286 * @param ip points to output struct
287 */
288 u8_t
snmp_oid_to_ip4(const u32_t * oid,ip4_addr_t * ip)289 snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip)
290 {
291 if ((oid[0] > 0xFF) ||
292 (oid[1] > 0xFF) ||
293 (oid[2] > 0xFF) ||
294 (oid[3] > 0xFF)) {
295 ip4_addr_copy(*ip, *IP4_ADDR_ANY4);
296 return 0;
297 }
298
299 IP4_ADDR(ip, oid[0], oid[1], oid[2], oid[3]);
300 return 1;
301 }
302
303 /**
304 * Convert ip4_addr to InetAddressIPv4 (no InetAddressType)
305 * @param ip points to input struct
306 * @param oid points to u32_t ident[4] output
307 */
308 void
snmp_ip4_to_oid(const ip4_addr_t * ip,u32_t * oid)309 snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid)
310 {
311 oid[0] = ip4_addr1(ip);
312 oid[1] = ip4_addr2(ip);
313 oid[2] = ip4_addr3(ip);
314 oid[3] = ip4_addr4(ip);
315 }
316 #endif /* LWIP_IPV4 */
317
318 #if LWIP_IPV6
319 /**
320 * Conversion from InetAddressIPv6 oid to lwIP ip6_addr
321 * @param oid points to u32_t oid[16] input
322 * @param ip points to output struct
323 */
324 u8_t
snmp_oid_to_ip6(const u32_t * oid,ip6_addr_t * ip)325 snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip)
326 {
327 if ((oid[0] > 0xFF) ||
328 (oid[1] > 0xFF) ||
329 (oid[2] > 0xFF) ||
330 (oid[3] > 0xFF) ||
331 (oid[4] > 0xFF) ||
332 (oid[5] > 0xFF) ||
333 (oid[6] > 0xFF) ||
334 (oid[7] > 0xFF) ||
335 (oid[8] > 0xFF) ||
336 (oid[9] > 0xFF) ||
337 (oid[10] > 0xFF) ||
338 (oid[11] > 0xFF) ||
339 (oid[12] > 0xFF) ||
340 (oid[13] > 0xFF) ||
341 (oid[14] > 0xFF) ||
342 (oid[15] > 0xFF)) {
343 ip6_addr_set_any(ip);
344 return 0;
345 }
346
347 ip->addr[0] = (oid[0] << 24) | (oid[1] << 16) | (oid[2] << 8) | (oid[3] << 0);
348 ip->addr[1] = (oid[4] << 24) | (oid[5] << 16) | (oid[6] << 8) | (oid[7] << 0);
349 ip->addr[2] = (oid[8] << 24) | (oid[9] << 16) | (oid[10] << 8) | (oid[11] << 0);
350 ip->addr[3] = (oid[12] << 24) | (oid[13] << 16) | (oid[14] << 8) | (oid[15] << 0);
351 return 1;
352 }
353
354 /**
355 * Convert ip6_addr to InetAddressIPv6 (no InetAddressType)
356 * @param ip points to input struct
357 * @param oid points to u32_t ident[16] output
358 */
359 void
snmp_ip6_to_oid(const ip6_addr_t * ip,u32_t * oid)360 snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid)
361 {
362 oid[0] = (ip->addr[0] & 0xFF000000) >> 24;
363 oid[1] = (ip->addr[0] & 0x00FF0000) >> 16;
364 oid[2] = (ip->addr[0] & 0x0000FF00) >> 8;
365 oid[3] = (ip->addr[0] & 0x000000FF) >> 0;
366 oid[4] = (ip->addr[1] & 0xFF000000) >> 24;
367 oid[5] = (ip->addr[1] & 0x00FF0000) >> 16;
368 oid[6] = (ip->addr[1] & 0x0000FF00) >> 8;
369 oid[7] = (ip->addr[1] & 0x000000FF) >> 0;
370 oid[8] = (ip->addr[2] & 0xFF000000) >> 24;
371 oid[9] = (ip->addr[2] & 0x00FF0000) >> 16;
372 oid[10] = (ip->addr[2] & 0x0000FF00) >> 8;
373 oid[11] = (ip->addr[2] & 0x000000FF) >> 0;
374 oid[12] = (ip->addr[3] & 0xFF000000) >> 24;
375 oid[13] = (ip->addr[3] & 0x00FF0000) >> 16;
376 oid[14] = (ip->addr[3] & 0x0000FF00) >> 8;
377 oid[15] = (ip->addr[3] & 0x000000FF) >> 0;
378 }
379 #endif /* LWIP_IPV6 */
380
381 #if LWIP_IPV4 || LWIP_IPV6
382 /**
383 * Convert to InetAddressType+InetAddress+InetPortNumber
384 * @param ip IP address
385 * @param port Port
386 * @param oid OID
387 * @return OID length
388 */
389 u8_t
snmp_ip_port_to_oid(const ip_addr_t * ip,u16_t port,u32_t * oid)390 snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid)
391 {
392 u8_t idx;
393
394 idx = snmp_ip_to_oid(ip, oid);
395 oid[idx] = port;
396 idx++;
397
398 return idx;
399 }
400
401 /**
402 * Convert to InetAddressType+InetAddress
403 * @param ip IP address
404 * @param oid OID
405 * @return OID length
406 */
407 u8_t
snmp_ip_to_oid(const ip_addr_t * ip,u32_t * oid)408 snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid)
409 {
410 if (IP_IS_ANY_TYPE_VAL(*ip)) {
411 oid[0] = 0; /* any */
412 oid[1] = 0; /* no IP OIDs follow */
413 return 2;
414 } else if (IP_IS_V6(ip)) {
415 #if LWIP_IPV6
416 oid[0] = 2; /* ipv6 */
417 oid[1] = 16; /* 16 InetAddressIPv6 OIDs follow */
418 snmp_ip6_to_oid(ip_2_ip6(ip), &oid[2]);
419 return 18;
420 #else /* LWIP_IPV6 */
421 return 0;
422 #endif /* LWIP_IPV6 */
423 } else {
424 #if LWIP_IPV4
425 oid[0] = 1; /* ipv4 */
426 oid[1] = 4; /* 4 InetAddressIPv4 OIDs follow */
427 snmp_ip4_to_oid(ip_2_ip4(ip), &oid[2]);
428 return 6;
429 #else /* LWIP_IPV4 */
430 return 0;
431 #endif /* LWIP_IPV4 */
432 }
433 }
434
435 /**
436 * Convert from InetAddressType+InetAddress to ip_addr_t
437 * @param oid OID
438 * @param oid_len OID length
439 * @param ip IP address
440 * @return Parsed OID length
441 */
442 u8_t
snmp_oid_to_ip(const u32_t * oid,u8_t oid_len,ip_addr_t * ip)443 snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip)
444 {
445 /* InetAddressType */
446 if (oid_len < 1) {
447 return 0;
448 }
449
450 if (oid[0] == 0) { /* any */
451 /* 1x InetAddressType, 1x OID len */
452 if (oid_len < 2) {
453 return 0;
454 }
455 if (oid[1] != 0) {
456 return 0;
457 }
458
459 memset(ip, 0, sizeof(*ip));
460 IP_SET_TYPE(ip, IPADDR_TYPE_ANY);
461
462 return 2;
463 } else if (oid[0] == 1) { /* ipv4 */
464 #if LWIP_IPV4
465 /* 1x InetAddressType, 1x OID len, 4x InetAddressIPv4 */
466 if (oid_len < 6) {
467 return 0;
468 }
469
470 /* 4x ipv4 OID */
471 if (oid[1] != 4) {
472 return 0;
473 }
474
475 IP_SET_TYPE(ip, IPADDR_TYPE_V4);
476 if (!snmp_oid_to_ip4(&oid[2], ip_2_ip4(ip))) {
477 return 0;
478 }
479
480 return 6;
481 #else /* LWIP_IPV4 */
482 return 0;
483 #endif /* LWIP_IPV4 */
484 } else if (oid[0] == 2) { /* ipv6 */
485 #if LWIP_IPV6
486 /* 1x InetAddressType, 1x OID len, 16x InetAddressIPv6 */
487 if (oid_len < 18) {
488 return 0;
489 }
490
491 /* 16x ipv6 OID */
492 if (oid[1] != 16) {
493 return 0;
494 }
495
496 IP_SET_TYPE(ip, IPADDR_TYPE_V6);
497 if (!snmp_oid_to_ip6(&oid[2], ip_2_ip6(ip))) {
498 return 0;
499 }
500
501 return 18;
502 #else /* LWIP_IPV6 */
503 return 0;
504 #endif /* LWIP_IPV6 */
505 } else { /* unsupported InetAddressType */
506 return 0;
507 }
508 }
509
510 /**
511 * Convert from InetAddressType+InetAddress+InetPortNumber to ip_addr_t and u16_t
512 * @param oid OID
513 * @param oid_len OID length
514 * @param ip IP address
515 * @param port Port
516 * @return Parsed OID length
517 */
518 u8_t
snmp_oid_to_ip_port(const u32_t * oid,u8_t oid_len,ip_addr_t * ip,u16_t * port)519 snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port)
520 {
521 u8_t idx;
522
523 /* InetAddressType + InetAddress */
524 idx = snmp_oid_to_ip(&oid[0], oid_len, ip);
525 if (idx == 0) {
526 return 0;
527 }
528
529 /* InetPortNumber */
530 if (oid_len < (idx + 1)) {
531 return 0;
532 }
533 if (oid[idx] > 0xffff) {
534 return 0;
535 }
536 *port = (u16_t)oid[idx];
537 idx++;
538
539 return idx;
540 }
541
542 #endif /* LWIP_IPV4 || LWIP_IPV6 */
543
544 /**
545 * Assign an OID to struct snmp_obj_id
546 * @param target Assignment target
547 * @param oid OID
548 * @param oid_len OID length
549 */
550 void
snmp_oid_assign(struct snmp_obj_id * target,const u32_t * oid,u8_t oid_len)551 snmp_oid_assign(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len)
552 {
553 LWIP_ASSERT("oid_len <= SNMP_MAX_OBJ_ID_LEN", oid_len <= SNMP_MAX_OBJ_ID_LEN);
554
555 target->len = oid_len;
556
557 if (oid_len > 0) {
558 MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
559 }
560 }
561
562 /**
563 * Prefix an OID to OID in struct snmp_obj_id
564 * @param target Assignment target to prefix
565 * @param oid OID
566 * @param oid_len OID length
567 */
568 void
snmp_oid_prefix(struct snmp_obj_id * target,const u32_t * oid,u8_t oid_len)569 snmp_oid_prefix(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len)
570 {
571 LWIP_ASSERT("target->len + oid_len <= SNMP_MAX_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
572
573 if (oid_len > 0) {
574 /* move existing OID to make room at the beginning for OID to insert */
575 int i;
576 for (i = target->len - 1; i >= 0; i--) {
577 target->id[i + oid_len] = target->id[i];
578 }
579
580 /* paste oid at the beginning */
581 MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
582 }
583 }
584
585 /**
586 * Combine two OIDs into struct snmp_obj_id
587 * @param target Assignmet target
588 * @param oid1 OID 1
589 * @param oid1_len OID 1 length
590 * @param oid2 OID 2
591 * @param oid2_len OID 2 length
592 */
593 void
snmp_oid_combine(struct snmp_obj_id * target,const u32_t * oid1,u8_t oid1_len,const u32_t * oid2,u8_t oid2_len)594 snmp_oid_combine(struct snmp_obj_id *target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
595 {
596 snmp_oid_assign(target, oid1, oid1_len);
597 snmp_oid_append(target, oid2, oid2_len);
598 }
599
600 /**
601 * Append OIDs to struct snmp_obj_id
602 * @param target Assignment target to append to
603 * @param oid OID
604 * @param oid_len OID length
605 */
606 void
snmp_oid_append(struct snmp_obj_id * target,const u32_t * oid,u8_t oid_len)607 snmp_oid_append(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len)
608 {
609 LWIP_ASSERT("offset + oid_len <= SNMP_MAX_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
610
611 if (oid_len > 0) {
612 MEMCPY(&target->id[target->len], oid, oid_len * sizeof(u32_t));
613 target->len = (u8_t)(target->len + oid_len);
614 }
615 }
616
617 /**
618 * Compare two OIDs
619 * @param oid1 OID 1
620 * @param oid1_len OID 1 length
621 * @param oid2 OID 2
622 * @param oid2_len OID 2 length
623 * @return -1: OID1<OID2 1: OID1 >OID2 0: equal
624 */
625 s8_t
snmp_oid_compare(const u32_t * oid1,u8_t oid1_len,const u32_t * oid2,u8_t oid2_len)626 snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
627 {
628 u8_t level = 0;
629 LWIP_ASSERT("'oid1' param must not be NULL or 'oid1_len' param be 0!", (oid1 != NULL) || (oid1_len == 0));
630 LWIP_ASSERT("'oid2' param must not be NULL or 'oid2_len' param be 0!", (oid2 != NULL) || (oid2_len == 0));
631
632 while ((level < oid1_len) && (level < oid2_len)) {
633 if (*oid1 < *oid2) {
634 return -1;
635 }
636 if (*oid1 > *oid2) {
637 return 1;
638 }
639
640 level++;
641 oid1++;
642 oid2++;
643 }
644
645 /* common part of both OID's is equal, compare length */
646 if (oid1_len < oid2_len) {
647 return -1;
648 }
649 if (oid1_len > oid2_len) {
650 return 1;
651 }
652
653 /* they are equal */
654 return 0;
655 }
656
657
658 /**
659 * Check of two OIDs are equal
660 * @param oid1 OID 1
661 * @param oid1_len OID 1 length
662 * @param oid2 OID 2
663 * @param oid2_len OID 2 length
664 * @return 1: equal 0: non-equal
665 */
666 u8_t
snmp_oid_equal(const u32_t * oid1,u8_t oid1_len,const u32_t * oid2,u8_t oid2_len)667 snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
668 {
669 return (snmp_oid_compare(oid1, oid1_len, oid2, oid2_len) == 0) ? 1 : 0;
670 }
671
672 /**
673 * Convert netif to interface index
674 * @param netif netif
675 * @return index
676 */
677 u8_t
netif_to_num(const struct netif * netif)678 netif_to_num(const struct netif *netif)
679 {
680 return netif_get_index(netif);
681 }
682
683 static const struct snmp_mib *
snmp_get_mib_from_oid(const u32_t * oid,u8_t oid_len)684 snmp_get_mib_from_oid(const u32_t *oid, u8_t oid_len)
685 {
686 const u32_t *list_oid;
687 const u32_t *searched_oid;
688 u8_t i, l;
689
690 u8_t max_match_len = 0;
691 const struct snmp_mib *matched_mib = NULL;
692
693 LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
694
695 if (oid_len == 0) {
696 return NULL;
697 }
698
699 for (i = 0; i < snmp_num_mibs; i++) {
700 LWIP_ASSERT("MIB array not initialized correctly", (snmp_mibs[i] != NULL));
701 LWIP_ASSERT("MIB array not initialized correctly - base OID is NULL", (snmp_mibs[i]->base_oid != NULL));
702
703 if (oid_len >= snmp_mibs[i]->base_oid_len) {
704 l = snmp_mibs[i]->base_oid_len;
705 list_oid = snmp_mibs[i]->base_oid;
706 searched_oid = oid;
707
708 while (l > 0) {
709 if (*list_oid != *searched_oid) {
710 break;
711 }
712
713 l--;
714 list_oid++;
715 searched_oid++;
716 }
717
718 if ((l == 0) && (snmp_mibs[i]->base_oid_len > max_match_len)) {
719 max_match_len = snmp_mibs[i]->base_oid_len;
720 matched_mib = snmp_mibs[i];
721 }
722 }
723 }
724
725 return matched_mib;
726 }
727
728 static const struct snmp_mib *
snmp_get_next_mib(const u32_t * oid,u8_t oid_len)729 snmp_get_next_mib(const u32_t *oid, u8_t oid_len)
730 {
731 u8_t i;
732 const struct snmp_mib *next_mib = NULL;
733
734 LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
735
736 if (oid_len == 0) {
737 return NULL;
738 }
739
740 for (i = 0; i < snmp_num_mibs; i++) {
741 if (snmp_mibs[i]->base_oid != NULL) {
742 /* check if mib is located behind starting point */
743 if (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len, oid, oid_len) > 0) {
744 if ((next_mib == NULL) ||
745 (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len,
746 next_mib->base_oid, next_mib->base_oid_len) < 0)) {
747 next_mib = snmp_mibs[i];
748 }
749 }
750 }
751 }
752
753 return next_mib;
754 }
755
756 static const struct snmp_mib *
snmp_get_mib_between(const u32_t * oid1,u8_t oid1_len,const u32_t * oid2,u8_t oid2_len)757 snmp_get_mib_between(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
758 {
759 const struct snmp_mib *next_mib = snmp_get_next_mib(oid1, oid1_len);
760
761 LWIP_ASSERT("'oid2' param must not be NULL!", (oid2 != NULL));
762 LWIP_ASSERT("'oid2_len' param must be greater than 0!", (oid2_len > 0));
763
764 if (next_mib != NULL) {
765 if (snmp_oid_compare(next_mib->base_oid, next_mib->base_oid_len, oid2, oid2_len) < 0) {
766 return next_mib;
767 }
768 }
769
770 return NULL;
771 }
772
773 u8_t
snmp_get_node_instance_from_oid(const u32_t * oid,u8_t oid_len,struct snmp_node_instance * node_instance)774 snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance *node_instance)
775 {
776 u8_t result = SNMP_ERR_NOSUCHOBJECT;
777 const struct snmp_mib *mib;
778 const struct snmp_node *mn = NULL;
779
780 mib = snmp_get_mib_from_oid(oid, oid_len);
781 if (mib != NULL) {
782 u8_t oid_instance_len;
783
784 mn = snmp_mib_tree_resolve_exact(mib, oid, oid_len, &oid_instance_len);
785 if ((mn != NULL) && (mn->node_type != SNMP_NODE_TREE)) {
786 /* get instance */
787 const struct snmp_leaf_node *leaf_node = (const struct snmp_leaf_node *)(const void *)mn;
788
789 node_instance->node = mn;
790 snmp_oid_assign(&node_instance->instance_oid, oid + (oid_len - oid_instance_len), oid_instance_len);
791
792 result = leaf_node->get_instance(
793 oid,
794 oid_len - oid_instance_len,
795 node_instance);
796
797 #ifdef LWIP_DEBUG
798 if (result == SNMP_ERR_NOERROR) {
799 if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
800 LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
801 }
802 if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
803 LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value and/or set_test function is specified\n"));
804 }
805 }
806 #endif
807 }
808 }
809
810 return result;
811 }
812
813 u8_t
snmp_get_next_node_instance_from_oid(const u32_t * oid,u8_t oid_len,snmp_validate_node_instance_method validate_node_instance_method,void * validate_node_instance_arg,struct snmp_obj_id * node_oid,struct snmp_node_instance * node_instance)814 snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void *validate_node_instance_arg, struct snmp_obj_id *node_oid, struct snmp_node_instance *node_instance)
815 {
816 const struct snmp_mib *mib;
817 const struct snmp_node *mn = NULL;
818 const u32_t *start_oid = NULL;
819 u8_t start_oid_len = 0;
820
821 /* resolve target MIB from passed OID */
822 mib = snmp_get_mib_from_oid(oid, oid_len);
823 if (mib == NULL) {
824 /* passed OID does not reference any known MIB, start at the next closest MIB */
825 mib = snmp_get_next_mib(oid, oid_len);
826
827 if (mib != NULL) {
828 start_oid = mib->base_oid;
829 start_oid_len = mib->base_oid_len;
830 }
831 } else {
832 start_oid = oid;
833 start_oid_len = oid_len;
834 }
835
836 /* resolve target node from MIB, skip to next MIB if no suitable node is found in current MIB */
837 while ((mib != NULL) && (mn == NULL)) {
838 u8_t oid_instance_len;
839
840 /* check if OID directly references a node inside current MIB, in this case we have to ask this node for the next instance */
841 mn = snmp_mib_tree_resolve_exact(mib, start_oid, start_oid_len, &oid_instance_len);
842 if (mn != NULL) {
843 snmp_oid_assign(node_oid, start_oid, start_oid_len - oid_instance_len); /* set oid to node */
844 snmp_oid_assign(&node_instance->instance_oid, start_oid + (start_oid_len - oid_instance_len), oid_instance_len); /* set (relative) instance oid */
845 } else {
846 /* OID does not reference a node, search for the next closest node inside MIB; set instance_oid.len to zero because we want the first instance of this node */
847 mn = snmp_mib_tree_resolve_next(mib, start_oid, start_oid_len, node_oid);
848 node_instance->instance_oid.len = 0;
849 }
850
851 /* validate the node; if the node has no further instance or the returned instance is invalid, search for the next in MIB and validate again */
852 node_instance->node = mn;
853 while (mn != NULL) {
854 u8_t result;
855
856 /* clear fields which may have values from previous loops */
857 node_instance->asn1_type = 0;
858 node_instance->access = SNMP_NODE_INSTANCE_NOT_ACCESSIBLE;
859 node_instance->get_value = NULL;
860 node_instance->set_test = NULL;
861 node_instance->set_value = NULL;
862 node_instance->release_instance = NULL;
863 node_instance->reference.ptr = NULL;
864 node_instance->reference_len = 0;
865
866 result = ((const struct snmp_leaf_node *)(const void *)mn)->get_next_instance(
867 node_oid->id,
868 node_oid->len,
869 node_instance);
870
871 if (result == SNMP_ERR_NOERROR) {
872 #ifdef LWIP_DEBUG
873 if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
874 LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
875 }
876 if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
877 LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value function is specified\n"));
878 }
879 #endif
880
881 /* validate node because the node may be not accessible for example (but let the caller decide what is valid */
882 if ((validate_node_instance_method == NULL) ||
883 (validate_node_instance_method(node_instance, validate_node_instance_arg) == SNMP_ERR_NOERROR)) {
884 /* node_oid "returns" the full result OID (including the instance part) */
885 snmp_oid_append(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
886 break;
887 }
888
889 if (node_instance->release_instance != NULL) {
890 node_instance->release_instance(node_instance);
891 }
892 /*
893 the instance itself is not valid, ask for next instance from same node.
894 we don't have to change any variables because node_instance->instance_oid is used as input (starting point)
895 as well as output (resulting next OID), so we have to simply call get_next_instance method again
896 */
897 } else {
898 if (node_instance->release_instance != NULL) {
899 node_instance->release_instance(node_instance);
900 }
901
902 /* the node has no further instance, skip to next node */
903 mn = snmp_mib_tree_resolve_next(mib, node_oid->id, node_oid->len, &node_instance->instance_oid); /* misuse node_instance->instance_oid as tmp buffer */
904 if (mn != NULL) {
905 /* prepare for next loop */
906 snmp_oid_assign(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
907 node_instance->instance_oid.len = 0;
908 node_instance->node = mn;
909 }
910 }
911 }
912
913 if (mn != NULL) {
914 /*
915 we found a suitable next node,
916 now we have to check if a inner MIB is located between the searched OID and the resulting OID.
917 this is possible because MIB's may be located anywhere in the global tree, that means also in
918 the subtree of another MIB (e.g. if searched OID is .2 and resulting OID is .4, then another
919 MIB having .3 as root node may exist)
920 */
921 const struct snmp_mib *intermediate_mib;
922 intermediate_mib = snmp_get_mib_between(start_oid, start_oid_len, node_oid->id, node_oid->len);
923
924 if (intermediate_mib != NULL) {
925 /* search for first node inside intermediate mib in next loop */
926 if (node_instance->release_instance != NULL) {
927 node_instance->release_instance(node_instance);
928 }
929
930 mn = NULL;
931 mib = intermediate_mib;
932 start_oid = mib->base_oid;
933 start_oid_len = mib->base_oid_len;
934 }
935 /* else { we found out target node } */
936 } else {
937 /*
938 there is no further (suitable) node inside this MIB, search for the next MIB with following priority
939 1. search for inner MIB's (whose root is located inside tree of current MIB)
940 2. search for surrounding MIB's (where the current MIB is the inner MIB) and continue there if any
941 3. take the next closest MIB (not being related to the current MIB)
942 */
943 const struct snmp_mib *next_mib;
944 next_mib = snmp_get_next_mib(start_oid, start_oid_len); /* returns MIB's related to point 1 and 3 */
945
946 /* is the found MIB an inner MIB? (point 1) */
947 if ((next_mib != NULL) && (next_mib->base_oid_len > mib->base_oid_len) &&
948 (snmp_oid_compare(next_mib->base_oid, mib->base_oid_len, mib->base_oid, mib->base_oid_len) == 0)) {
949 /* yes it is -> continue at inner MIB */
950 mib = next_mib;
951 start_oid = mib->base_oid;
952 start_oid_len = mib->base_oid_len;
953 } else {
954 /* check if there is a surrounding mib where to continue (point 2) (only possible if OID length > 1) */
955 if (mib->base_oid_len > 1) {
956 mib = snmp_get_mib_from_oid(mib->base_oid, mib->base_oid_len - 1);
957
958 if (mib == NULL) {
959 /* no surrounding mib, use next mib encountered above (point 3) */
960 mib = next_mib;
961
962 if (mib != NULL) {
963 start_oid = mib->base_oid;
964 start_oid_len = mib->base_oid_len;
965 }
966 }
967 /* else { start_oid stays the same because we want to continue from current offset in surrounding mib (point 2) } */
968 }
969 }
970 }
971 }
972
973 if (mib == NULL) {
974 /* loop is only left when mib == null (error) or mib_node != NULL (success) */
975 return SNMP_ERR_ENDOFMIBVIEW;
976 }
977
978 return SNMP_ERR_NOERROR;
979 }
980
981 /**
982 * Searches tree for the supplied object identifier.
983 *
984 */
985 const struct snmp_node *
snmp_mib_tree_resolve_exact(const struct snmp_mib * mib,const u32_t * oid,u8_t oid_len,u8_t * oid_instance_len)986 snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t *oid_instance_len)
987 {
988 const struct snmp_node *const *node = &mib->root_node;
989 u8_t oid_offset = mib->base_oid_len;
990
991 while ((oid_offset < oid_len) && ((*node)->node_type == SNMP_NODE_TREE)) {
992 /* search for matching sub node */
993 u32_t subnode_oid = *(oid + oid_offset);
994
995 u32_t i = (*(const struct snmp_tree_node * const *)node)->subnode_count;
996 node = (*(const struct snmp_tree_node * const *)node)->subnodes;
997 while ((i > 0) && ((*node)->oid != subnode_oid)) {
998 node++;
999 i--;
1000 }
1001
1002 if (i == 0) {
1003 /* no matching subnode found */
1004 return NULL;
1005 }
1006
1007 oid_offset++;
1008 }
1009
1010 if ((*node)->node_type != SNMP_NODE_TREE) {
1011 /* we found a leaf node */
1012 *oid_instance_len = oid_len - oid_offset;
1013 return (*node);
1014 }
1015
1016 return NULL;
1017 }
1018
1019 const struct snmp_node *
snmp_mib_tree_resolve_next(const struct snmp_mib * mib,const u32_t * oid,u8_t oid_len,struct snmp_obj_id * oidret)1020 snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id *oidret)
1021 {
1022 u8_t oid_offset = mib->base_oid_len;
1023 const struct snmp_node *const *node;
1024 const struct snmp_tree_node *node_stack[SNMP_MAX_OBJ_ID_LEN];
1025 s32_t nsi = 0; /* NodeStackIndex */
1026 u32_t subnode_oid;
1027
1028 if (mib->root_node->node_type != SNMP_NODE_TREE) {
1029 /* a next operation on a mib with only a leaf node will always return NULL because there is no other node */
1030 return NULL;
1031 }
1032
1033 /* first build node stack related to passed oid (as far as possible), then go backwards to determine the next node */
1034 node_stack[nsi] = (const struct snmp_tree_node *)(const void *)mib->root_node;
1035 while (oid_offset < oid_len) {
1036 /* search for matching sub node */
1037 u32_t i = node_stack[nsi]->subnode_count;
1038 node = node_stack[nsi]->subnodes;
1039
1040 subnode_oid = *(oid + oid_offset);
1041
1042 while ((i > 0) && ((*node)->oid != subnode_oid)) {
1043 node++;
1044 i--;
1045 }
1046
1047 if ((i == 0) || ((*node)->node_type != SNMP_NODE_TREE)) {
1048 /* no (matching) tree-subnode found */
1049 break;
1050 }
1051 nsi++;
1052 node_stack[nsi] = (const struct snmp_tree_node *)(const void *)(*node);
1053
1054 oid_offset++;
1055 }
1056
1057
1058 if (oid_offset >= oid_len) {
1059 /* passed oid references a tree node -> return first usable sub node of it */
1060 subnode_oid = 0;
1061 } else {
1062 subnode_oid = *(oid + oid_offset) + 1;
1063 }
1064
1065 while (nsi >= 0) {
1066 const struct snmp_node *subnode = NULL;
1067
1068 /* find next node on current level */
1069 s32_t i = node_stack[nsi]->subnode_count;
1070 node = node_stack[nsi]->subnodes;
1071 while (i > 0) {
1072 if ((*node)->oid == subnode_oid) {
1073 subnode = *node;
1074 break;
1075 } else if (((*node)->oid > subnode_oid) && ((subnode == NULL) || ((*node)->oid < subnode->oid))) {
1076 subnode = *node;
1077 }
1078
1079 node++;
1080 i--;
1081 }
1082
1083 if (subnode == NULL) {
1084 /* no further node found on this level, go one level up and start searching with index of current node*/
1085 subnode_oid = node_stack[nsi]->node.oid + 1;
1086 nsi--;
1087 } else {
1088 if (subnode->node_type == SNMP_NODE_TREE) {
1089 /* next is a tree node, go into it and start searching */
1090 nsi++;
1091 node_stack[nsi] = (const struct snmp_tree_node *)(const void *)subnode;
1092 subnode_oid = 0;
1093 } else {
1094 /* we found a leaf node -> fill oidret and return it */
1095 snmp_oid_assign(oidret, mib->base_oid, mib->base_oid_len);
1096 i = 1;
1097 while (i <= nsi) {
1098 oidret->id[oidret->len] = node_stack[i]->node.oid;
1099 oidret->len++;
1100 i++;
1101 }
1102
1103 oidret->id[oidret->len] = subnode->oid;
1104 oidret->len++;
1105
1106 return subnode;
1107 }
1108 }
1109 }
1110
1111 return NULL;
1112 }
1113
1114 /** initialize struct next_oid_state using this function before passing it to next_oid_check */
1115 void
snmp_next_oid_init(struct snmp_next_oid_state * state,const u32_t * start_oid,u8_t start_oid_len,u32_t * next_oid_buf,u8_t next_oid_max_len)1116 snmp_next_oid_init(struct snmp_next_oid_state *state,
1117 const u32_t *start_oid, u8_t start_oid_len,
1118 u32_t *next_oid_buf, u8_t next_oid_max_len)
1119 {
1120 state->start_oid = start_oid;
1121 state->start_oid_len = start_oid_len;
1122 state->next_oid = next_oid_buf;
1123 state->next_oid_len = 0;
1124 state->next_oid_max_len = next_oid_max_len;
1125 state->status = SNMP_NEXT_OID_STATUS_NO_MATCH;
1126 }
1127
1128 /** checks if the passed incomplete OID may be a possible candidate for snmp_next_oid_check();
1129 this method is intended if the complete OID is not yet known but it is very expensive to build it up,
1130 so it is possible to test the starting part before building up the complete oid and pass it to snmp_next_oid_check()*/
1131 u8_t
snmp_next_oid_precheck(struct snmp_next_oid_state * state,const u32_t * oid,u8_t oid_len)1132 snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, u8_t oid_len)
1133 {
1134 if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
1135 u8_t start_oid_len = (oid_len < state->start_oid_len) ? oid_len : state->start_oid_len;
1136
1137 /* check passed OID is located behind start offset */
1138 if (snmp_oid_compare(oid, oid_len, state->start_oid, start_oid_len) >= 0) {
1139 /* check if new oid is located closer to start oid than current closest oid */
1140 if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
1141 (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
1142 return 1;
1143 }
1144 }
1145 }
1146
1147 return 0;
1148 }
1149
1150 /** checks the passed OID if it is a candidate to be the next one (get_next); returns !=0 if passed oid is currently closest, otherwise 0 */
1151 u8_t
snmp_next_oid_check(struct snmp_next_oid_state * state,const u32_t * oid,u8_t oid_len,void * reference)1152 snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, u8_t oid_len, void *reference)
1153 {
1154 /* do not overwrite a fail result */
1155 if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
1156 /* check passed OID is located behind start offset */
1157 if (snmp_oid_compare(oid, oid_len, state->start_oid, state->start_oid_len) > 0) {
1158 /* check if new oid is located closer to start oid than current closest oid */
1159 if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
1160 (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
1161 if (oid_len <= state->next_oid_max_len) {
1162 MEMCPY(state->next_oid, oid, oid_len * sizeof(u32_t));
1163 state->next_oid_len = oid_len;
1164 state->status = SNMP_NEXT_OID_STATUS_SUCCESS;
1165 state->reference = reference;
1166 return 1;
1167 } else {
1168 state->status = SNMP_NEXT_OID_STATUS_BUF_TO_SMALL;
1169 }
1170 }
1171 }
1172 }
1173
1174 return 0;
1175 }
1176
1177 u8_t
snmp_oid_in_range(const u32_t * oid_in,u8_t oid_len,const struct snmp_oid_range * oid_ranges,u8_t oid_ranges_len)1178 snmp_oid_in_range(const u32_t *oid_in, u8_t oid_len, const struct snmp_oid_range *oid_ranges, u8_t oid_ranges_len)
1179 {
1180 u8_t i;
1181
1182 if (oid_len != oid_ranges_len) {
1183 return 0;
1184 }
1185
1186 for (i = 0; i < oid_ranges_len; i++) {
1187 if ((oid_in[i] < oid_ranges[i].min) || (oid_in[i] > oid_ranges[i].max)) {
1188 return 0;
1189 }
1190 }
1191
1192 return 1;
1193 }
1194
1195 snmp_err_t
snmp_set_test_ok(struct snmp_node_instance * instance,u16_t value_len,void * value)1196 snmp_set_test_ok(struct snmp_node_instance *instance, u16_t value_len, void *value)
1197 {
1198 LWIP_UNUSED_ARG(instance);
1199 LWIP_UNUSED_ARG(value_len);
1200 LWIP_UNUSED_ARG(value);
1201
1202 return SNMP_ERR_NOERROR;
1203 }
1204
1205 /**
1206 * Decodes BITS pseudotype value from ASN.1 OctetString.
1207 *
1208 * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
1209 * be encoded/decoded by the agent. Instead call this function as required from
1210 * get/test/set methods.
1211 *
1212 * @param buf points to a buffer holding the ASN1 octet string
1213 * @param buf_len length of octet string
1214 * @param bit_value decoded Bit value with Bit0 == LSB
1215 * @return ERR_OK if successful, ERR_ARG if bit value contains more than 32 bit
1216 */
1217 err_t
snmp_decode_bits(const u8_t * buf,u32_t buf_len,u32_t * bit_value)1218 snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value)
1219 {
1220 u8_t b;
1221 u8_t bits_processed = 0;
1222 *bit_value = 0;
1223
1224 while (buf_len > 0) {
1225 /* any bit set in this byte? */
1226 if (*buf != 0x00) {
1227 if (bits_processed >= 32) {
1228 /* accept more than 4 bytes, but only when no bits are set */
1229 return ERR_VAL;
1230 }
1231
1232 b = *buf;
1233 do {
1234 if (b & 0x80) {
1235 *bit_value |= (1 << bits_processed);
1236 }
1237 bits_processed++;
1238 b <<= 1;
1239 } while ((bits_processed & 0x07) != 0); /* &0x07 -> % 8 */
1240 } else {
1241 bits_processed += 8;
1242 }
1243
1244 buf_len--;
1245 buf++;
1246 }
1247
1248 return ERR_OK;
1249 }
1250
1251 err_t
snmp_decode_truthvalue(const s32_t * asn1_value,u8_t * bool_value)1252 snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value)
1253 {
1254 /* defined by RFC1443:
1255 TruthValue ::= TEXTUAL-CONVENTION
1256 STATUS current
1257 DESCRIPTION
1258 "Represents a boolean value."
1259 SYNTAX INTEGER { true(1), false(2) }
1260 */
1261
1262 if ((asn1_value == NULL) || (bool_value == NULL)) {
1263 return ERR_ARG;
1264 }
1265
1266 if (*asn1_value == 1) {
1267 *bool_value = 1;
1268 } else if (*asn1_value == 2) {
1269 *bool_value = 0;
1270 } else {
1271 return ERR_VAL;
1272 }
1273
1274 return ERR_OK;
1275 }
1276
1277 /**
1278 * Encodes BITS pseudotype value into ASN.1 OctetString.
1279 *
1280 * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
1281 * be encoded/decoded by the agent. Instead call this function as required from
1282 * get/test/set methods.
1283 *
1284 * @param buf points to a buffer where the resulting ASN1 octet string is stored to
1285 * @param buf_len max length of the buffer
1286 * @param bit_value Bit value to encode with Bit0 == LSB
1287 * @param bit_count Number of possible bits for the bit value (according to rfc we have to send all bits independent from their truth value)
1288 * @return number of bytes used from buffer to store the resulting OctetString
1289 */
1290 u8_t
snmp_encode_bits(u8_t * buf,u32_t buf_len,u32_t bit_value,u8_t bit_count)1291 snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count)
1292 {
1293 u8_t len = 0;
1294 u8_t min_bytes = (bit_count + 7) >> 3; /* >>3 -> / 8 */
1295
1296 while ((buf_len > 0) && (bit_value != 0x00)) {
1297 s8_t i = 7;
1298 *buf = 0x00;
1299 while (i >= 0) {
1300 if (bit_value & 0x01) {
1301 *buf |= 0x01;
1302 }
1303
1304 if (i > 0) {
1305 *buf <<= 1;
1306 }
1307
1308 bit_value >>= 1;
1309 i--;
1310 }
1311
1312 buf++;
1313 buf_len--;
1314 len++;
1315 }
1316
1317 if (len < min_bytes) {
1318 buf += len;
1319 buf_len -= len;
1320
1321 while ((len < min_bytes) && (buf_len > 0)) {
1322 *buf = 0x00;
1323 buf++;
1324 buf_len--;
1325 len++;
1326 }
1327 }
1328
1329 return len;
1330 }
1331
1332 u8_t
snmp_encode_truthvalue(s32_t * asn1_value,u32_t bool_value)1333 snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value)
1334 {
1335 /* defined by RFC1443:
1336 TruthValue ::= TEXTUAL-CONVENTION
1337 STATUS current
1338 DESCRIPTION
1339 "Represents a boolean value."
1340 SYNTAX INTEGER { true(1), false(2) }
1341 */
1342
1343 if (asn1_value == NULL) {
1344 return 0;
1345 }
1346
1347 if (bool_value) {
1348 *asn1_value = 1; /* defined by RFC1443 */
1349 } else {
1350 *asn1_value = 2; /* defined by RFC1443 */
1351 }
1352
1353 return sizeof(s32_t);
1354 }
1355
1356 #endif /* LWIP_SNMP */
1357