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