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