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