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