• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
snmp_set_mibs(const struct snmp_mib ** mibs,u8_t num_mibs)236 snmp_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  */
snmp_set_device_enterprise_oid(const struct snmp_obj_id * device_enterprise_oid)259 void 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  */
snmp_get_device_enterprise_oid(void)273 const 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  */
285 u8_t
snmp_oid_to_ip4(const u32_t * oid,ip4_addr_t * ip)286 snmp_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  */
305 void
snmp_ip4_to_oid(const ip4_addr_t * ip,u32_t * oid)306 snmp_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  */
321 u8_t
snmp_oid_to_ip6(const u32_t * oid,ip6_addr_t * ip)322 snmp_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  */
356 void
snmp_ip6_to_oid(const ip6_addr_t * ip,u32_t * oid)357 snmp_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  */
386 u8_t
snmp_ip_port_to_oid(const ip_addr_t * ip,u16_t port,u32_t * oid)387 snmp_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  */
404 u8_t
snmp_ip_to_oid(const ip_addr_t * ip,u32_t * oid)405 snmp_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  */
439 u8_t
snmp_oid_to_ip(const u32_t * oid,u8_t oid_len,ip_addr_t * ip)440 snmp_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  */
515 u8_t
snmp_oid_to_ip_port(const u32_t * oid,u8_t oid_len,ip_addr_t * ip,u16_t * port)516 snmp_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  */
547 void
snmp_oid_assign(struct snmp_obj_id * target,const u32_t * oid,u8_t oid_len)548 snmp_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  */
565 void
snmp_oid_prefix(struct snmp_obj_id * target,const u32_t * oid,u8_t oid_len)566 snmp_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  */
590 void
snmp_oid_combine(struct snmp_obj_id * target,const u32_t * oid1,u8_t oid1_len,const u32_t * oid2,u8_t oid2_len)591 snmp_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  */
603 void
snmp_oid_append(struct snmp_obj_id * target,const u32_t * oid,u8_t oid_len)604 snmp_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  */
622 s8_t
snmp_oid_compare(const u32_t * oid1,u8_t oid1_len,const u32_t * oid2,u8_t oid2_len)623 snmp_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  */
663 u8_t
snmp_oid_equal(const u32_t * oid1,u8_t oid1_len,const u32_t * oid2,u8_t oid2_len)664 snmp_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  */
674 u8_t
netif_to_num(const struct netif * netif)675 netif_to_num(const struct netif *netif)
676 {
677   return netif_get_index(netif);
678 }
679 
680 static const struct snmp_mib *
snmp_get_mib_from_oid(const u32_t * oid,u8_t oid_len)681 snmp_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 
725 static const struct snmp_mib *
snmp_get_next_mib(const u32_t * oid,u8_t oid_len)726 snmp_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 
753 static const struct snmp_mib *
snmp_get_mib_between(const u32_t * oid1,u8_t oid1_len,const u32_t * oid2,u8_t oid2_len)754 snmp_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 
770 u8_t
snmp_get_node_instance_from_oid(const u32_t * oid,u8_t oid_len,struct snmp_node_instance * node_instance)771 snmp_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 
810 u8_t
snmp_get_next_node_instance_from_oid(const u32_t * oid,u8_t oid_len,snmp_validate_node_instance_method validate_node_instance_method,void * validate_node_instance_arg,struct snmp_obj_id * node_oid,struct snmp_node_instance * node_instance)811 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)
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  */
982 const struct snmp_node *
snmp_mib_tree_resolve_exact(const struct snmp_mib * mib,const u32_t * oid,u8_t oid_len,u8_t * oid_instance_len)983 snmp_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 
1016 const struct snmp_node *
snmp_mib_tree_resolve_next(const struct snmp_mib * mib,const u32_t * oid,u8_t oid_len,struct snmp_obj_id * oidret)1017 snmp_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 */
1112 void
snmp_next_oid_init(struct snmp_next_oid_state * state,const u32_t * start_oid,u8_t start_oid_len,u32_t * next_oid_buf,u8_t next_oid_max_len)1113 snmp_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();
1126 this methid is intended if the complete OID is not yet known but it is very expensive to build it up,
1127 so it is possible to test the starting part before building up the complete oid and pass it to snmp_next_oid_check()*/
1128 u8_t
snmp_next_oid_precheck(struct snmp_next_oid_state * state,const u32_t * oid,u8_t oid_len)1129 snmp_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 */
1148 u8_t
snmp_next_oid_check(struct snmp_next_oid_state * state,const u32_t * oid,u8_t oid_len,void * reference)1149 snmp_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 
1174 u8_t
snmp_oid_in_range(const u32_t * oid_in,u8_t oid_len,const struct snmp_oid_range * oid_ranges,u8_t oid_ranges_len)1175 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)
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 
1192 snmp_err_t
snmp_set_test_ok(struct snmp_node_instance * instance,u16_t value_len,void * value)1193 snmp_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  */
1214 err_t
snmp_decode_bits(const u8_t * buf,u32_t buf_len,u32_t * bit_value)1215 snmp_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 
1248 err_t
snmp_decode_truthvalue(const s32_t * asn1_value,u8_t * bool_value)1249 snmp_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  */
1287 u8_t
snmp_encode_bits(u8_t * buf,u32_t buf_len,u32_t bit_value,u8_t bit_count)1288 snmp_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 
1329 u8_t
snmp_encode_truthvalue(s32_t * asn1_value,u32_t bool_value)1330 snmp_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