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<OID2 1: OID1 >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