BadVPN – Blame information for rev 1

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