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