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