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&lt;OID2  1: OID1 &gt;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