Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_snmp_core.c Source File

lwip_snmp_core.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  * MIB tree access/construction functions.
00004  */
00005 
00006 /*
00007  * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
00008  * All rights reserved.
00009  *
00010  * Redistribution and use in source and binary forms, with or without modification,
00011  * are permitted provided that the following conditions are met:
00012  *
00013  * 1. Redistributions of source code must retain the above copyright notice,
00014  *    this list of conditions and the following disclaimer.
00015  * 2. Redistributions in binary form must reproduce the above copyright notice,
00016  *    this list of conditions and the following disclaimer in the documentation
00017  *    and/or other materials provided with the distribution.
00018  * 3. The name of the author may not be used to endorse or promote products
00019  *    derived from this software without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00022  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00023  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
00024  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00025  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00026  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
00029  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
00030  * OF SUCH DAMAGE.
00031  *
00032  * Author: Christiaan Simons <christiaan.simons@axon.tv>
00033  *         Martin Hentschel <info@cl-soft.de>
00034 */
00035 
00036 /**
00037  * @defgroup snmp SNMPv2c agent
00038  * @ingroup apps
00039  * SNMPv2c compatible agent\n
00040  * There is also a MIB compiler and a MIB viewer in lwIP contrib repository
00041  * (lwip-contrib/apps/LwipMibCompiler).\n
00042  * The agent implements the most important MIB2 MIBs including IPv6 support
00043  * (interfaces, UDP, TCP, SNMP, ICMP, SYSTEM). IP MIB is an older version
00044  * whithout IPv6 statistics (TODO).\n
00045  * Rewritten by Martin Hentschel <info@cl-soft.de> and
00046  * Dirk Ziegelmeier <dziegel@gmx.de>\n
00047  * Work on SNMPv3 has started, but is not finished.\n
00048  *
00049  * 0 Agent Capabilities
00050  * ====================
00051  * 
00052  * Features:
00053  * ---------
00054  * - SNMPv2c support.
00055  * - Low RAM usage - no memory pools, stack only.
00056  * - MIB2 implementation is separated from SNMP stack.
00057  * - Support for multiple MIBs (snmp_set_mibs() call) - e.g. for private MIB.
00058  * - Simple and generic API for MIB implementation.
00059  * - Comfortable node types and helper functions for scalar arrays and tables.
00060  * - Counter64, bit and truthvalue datatype support.
00061  * - Callbacks for SNMP writes e.g. to implement persistency.
00062  * - Runs on two APIs: RAW and netconn.
00063  * - Async API is gone - the stack now supports netconn API instead,
00064  *   so blocking operations can be done in MIB calls.
00065  *   SNMP runs in a worker thread when netconn API is used.
00066  * - Simplified thread sync support for MIBs - useful when MIBs
00067  *   need to access variables shared with other threads where no locking is
00068  *   possible. Used in MIB2 to access lwIP stats from lwIP thread.
00069  * 
00070  * MIB compiler (code generator):
00071  * ------------------------------
00072  * - Provided in lwIP contrib repository.
00073  * - Written in C#. MIB viewer used Windows Forms.
00074  * - Developed on Windows with Visual Studio 2010.
00075  * - Can be compiled and used on all platforms with http://www.monodevelop.com/.
00076  * - Based on a heavily modified version of of SharpSnmpLib (a4bd05c6afb4)
00077  *   (https://sharpsnmplib.codeplex.com/SourceControl/network/forks/Nemo157/MIBParserUpdate).
00078  * - MIB parser, C file generation framework and LWIP code generation are cleanly
00079  *   separated, which means the code may be useful as a base for code generation
00080  *   of other SNMP agents.
00081  * 
00082  * Notes:
00083  * ------
00084  * - Stack and MIB compiler were used to implement a Profinet device.
00085  *   Compiled/implemented MIBs: LLDP-MIB, LLDP-EXT-DOT3-MIB, LLDP-EXT-PNO-MIB.
00086  * 
00087  * SNMPv1 per RFC1157 and SNMPv2c per RFC 3416
00088  * -------------------------------------------
00089  *   Note the S in SNMP stands for "Simple". Note that "Simple" is
00090  *   relative. SNMP is simple compared to the complex ISO network
00091  *   management protocols CMIP (Common Management Information Protocol)
00092  *   and CMOT (CMip Over Tcp).
00093  * 
00094  * MIB II
00095  * ------
00096  *   The standard lwIP stack management information base.
00097  *   This is a required MIB, so this is always enabled.
00098  *   The groups EGP, CMOT and transmission are disabled by default.
00099  * 
00100  *   Most mib-2 objects are not writable except:
00101  *   sysName, sysLocation, sysContact, snmpEnableAuthenTraps.
00102  *   Writing to or changing the ARP and IP address and route
00103  *   tables is not possible.
00104  * 
00105  *   Note lwIP has a very limited notion of IP routing. It currently
00106  *   doen't have a route table and doesn't have a notion of the U,G,H flags.
00107  *   Instead lwIP uses the interface list with only one default interface
00108  *   acting as a single gateway interface (G) for the default route.
00109  * 
00110  *   The agent returns a "virtual table" with the default route 0.0.0.0
00111  *   for the default interface and network routes (no H) for each
00112  *   network interface in the netif_list.
00113  *   All routes are considered to be up (U).
00114  * 
00115  * Loading additional MIBs
00116  * -----------------------
00117  *   MIBs can only be added in compile-time, not in run-time.
00118  *  
00119  * 
00120  * 1 Building the Agent
00121  * ====================
00122  * First of all you'll need to add the following define
00123  * to your local lwipopts.h:
00124  * \#define LWIP_SNMP               1
00125  * 
00126  * and add the source files your makefile.
00127  * 
00128  * Note you'll might need to adapt you network driver to update
00129  * the mib2 variables for your interface.
00130  * 
00131  * 2 Running the Agent
00132  * ===================
00133  * The following function calls must be made in your program to
00134  * actually get the SNMP agent running.
00135  * 
00136  * Before starting the agent you should supply pointers
00137  * for sysContact, sysLocation, and snmpEnableAuthenTraps.
00138  * You can do this by calling
00139  * 
00140  * - snmp_mib2_set_syscontact()
00141  * - snmp_mib2_set_syslocation()
00142  * - snmp_set_auth_traps_enabled()
00143  * 
00144  * You can register a callback which is called on successful write access: 
00145  * snmp_set_write_callback().
00146  * 
00147  * Additionally you may want to set
00148  * 
00149  * - snmp_mib2_set_sysdescr()
00150  * - snmp_set_device_enterprise_oid()
00151  * - snmp_mib2_set_sysname()
00152  * 
00153  * Also before starting the agent you need to setup
00154  * one or more trap destinations using these calls:
00155  * 
00156  * - snmp_trap_dst_enable()
00157  * - snmp_trap_dst_ip_set()
00158  * 
00159  * If you need more than MIB2, set the MIBs you want to use
00160  * by snmp_set_mibs().
00161  * 
00162  * Finally, enable the agent by calling snmp_init()
00163  *
00164  * @defgroup snmp_core Core
00165  * @ingroup snmp
00166  * 
00167  * @defgroup snmp_traps Traps
00168  * @ingroup snmp
00169  */
00170 
00171 #include "lwip/apps/snmp_opts.h"
00172 
00173 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
00174 
00175 #include "lwip/apps/snmp.h"
00176 #include "lwip/apps/snmp_core.h"
00177 #include "snmp_core_priv.h"
00178 #include "lwip/netif.h"
00179 #include <string.h>
00180 
00181 
00182 #if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
00183   #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
00184 #endif
00185 #if (!LWIP_UDP && LWIP_SNMP)
00186   #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
00187 #endif
00188 
00189 struct snmp_statistics snmp_stats;
00190 static const struct snmp_obj_id  snmp_device_enterprise_oid_default = {SNMP_DEVICE_ENTERPRISE_OID_LEN, SNMP_DEVICE_ENTERPRISE_OID};
00191 static const struct snmp_obj_id* snmp_device_enterprise_oid         = &snmp_device_enterprise_oid_default;
00192 
00193 const u32_t snmp_zero_dot_zero_values[] = { 0, 0 };
00194 const struct snmp_obj_id_const_ref snmp_zero_dot_zero = { LWIP_ARRAYSIZE(snmp_zero_dot_zero_values), snmp_zero_dot_zero_values };
00195 
00196 
00197 #if SNMP_LWIP_MIB2
00198 #include "lwip/apps/snmp_mib2.h"
00199 static const struct snmp_mib* const default_mibs[] = { &mib2 };
00200 static u8_t snmp_num_mibs                          = 1;
00201 #else
00202 static const struct snmp_mib* const default_mibs[] = { NULL };
00203 static u8_t snmp_num_mibs                          = 0;
00204 #endif
00205 
00206 /* List of known mibs */
00207 static struct snmp_mib const * const *snmp_mibs = default_mibs;
00208 
00209 /**
00210  * @ingroup snmp_core
00211  * Sets the MIBs to use.
00212  * Example: call snmp_set_mibs() as follows:
00213  * static const struct snmp_mib *my_snmp_mibs[] = {
00214  *   &mib2,
00215  *   &private_mib
00216  * };
00217  * snmp_set_mibs(my_snmp_mibs, LWIP_ARRAYSIZE(my_snmp_mibs));
00218  */
00219 void
00220 snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs)
00221 {
00222   LWIP_ASSERT("mibs pointer must be != NULL", (mibs != NULL));
00223   LWIP_ASSERT("num_mibs pointer must be != 0", (num_mibs != 0));
00224   snmp_mibs     = mibs;
00225   snmp_num_mibs = num_mibs;
00226 }
00227 
00228 /**
00229  * @ingroup snmp_core
00230  * 'device enterprise oid' is used for 'device OID' field in trap PDU's (for identification of generating device)
00231  * as well as for value returned by MIB-2 'sysObjectID' field (if internal MIB2 implementation is used).
00232  * The 'device enterprise oid' shall point to an OID located under 'private-enterprises' branch (1.3.6.1.4.1.XXX). If a vendor
00233  * wants to provide a custom object there, he has to get its own enterprise oid from IANA (http://www.iana.org). It
00234  * is not allowed to use LWIP enterprise ID!
00235  * In order to identify a specific device it is recommended to create a dedicated OID for each device type under its own 
00236  * enterprise oid.
00237  * e.g.
00238  * device a > 1.3.6.1.4.1.XXX(ent-oid).1(devices).1(device a)
00239  * device b > 1.3.6.1.4.1.XXX(ent-oid).1(devices).2(device b)
00240  * for more details see description of 'sysObjectID' field in RFC1213-MIB
00241  */
00242 void snmp_set_device_enterprise_oid(const struct snmp_obj_id* device_enterprise_oid)
00243 {
00244   if (device_enterprise_oid == NULL) {
00245     snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default;
00246   } else {
00247     snmp_device_enterprise_oid = device_enterprise_oid;
00248   }
00249 }
00250 
00251 /**
00252  * @ingroup snmp_core
00253  * Get 'device enterprise oid' 
00254  */
00255 const struct snmp_obj_id* snmp_get_device_enterprise_oid(void)
00256 {
00257   return snmp_device_enterprise_oid;
00258 }
00259 
00260 #if LWIP_IPV4
00261 /**
00262  * Conversion from InetAddressIPv4 oid to lwIP ip4_addr
00263  * @param oid points to u32_t ident[4] input
00264  * @param ip points to output struct
00265  */
00266 u8_t
00267 snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip)
00268 {
00269   if ((oid[0] > 0xFF) ||
00270       (oid[1] > 0xFF) ||
00271       (oid[2] > 0xFF) ||
00272       (oid[3] > 0xFF)) {
00273     ip4_addr_copy(*ip, *IP4_ADDR_ANY4);
00274     return 0;
00275   }
00276 
00277   IP4_ADDR(ip, oid[0], oid[1], oid[2], oid[3]);
00278   return 1;
00279 }
00280 
00281 /**
00282  * Convert ip4_addr to InetAddressIPv4 (no InetAddressType)
00283  * @param ip points to input struct
00284  * @param oid points to u32_t ident[4] output
00285  */
00286 void
00287 snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid)
00288 {
00289   oid[0] = ip4_addr1(ip);
00290   oid[1] = ip4_addr2(ip);
00291   oid[2] = ip4_addr3(ip);
00292   oid[3] = ip4_addr4(ip);
00293 }
00294 #endif /* LWIP_IPV4 */
00295 
00296 #if LWIP_IPV6
00297 /**
00298  * Conversion from InetAddressIPv6 oid to lwIP ip6_addr
00299  * @param oid points to u32_t oid[16] input
00300  * @param ip points to output struct
00301  */
00302 u8_t
00303 snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip)
00304 {
00305   if ((oid[0]  > 0xFF) ||
00306       (oid[1]  > 0xFF) ||
00307       (oid[2]  > 0xFF) ||
00308       (oid[3]  > 0xFF) ||
00309       (oid[4]  > 0xFF) ||
00310       (oid[5]  > 0xFF) ||
00311       (oid[6]  > 0xFF) ||
00312       (oid[7]  > 0xFF) ||
00313       (oid[8]  > 0xFF) ||
00314       (oid[9]  > 0xFF) ||
00315       (oid[10] > 0xFF) ||
00316       (oid[11] > 0xFF) ||
00317       (oid[12] > 0xFF) ||
00318       (oid[13] > 0xFF) ||
00319       (oid[14] > 0xFF) ||
00320       (oid[15] > 0xFF)) {
00321     ip6_addr_set_any(ip);
00322     return 0;
00323   }
00324 
00325   ip->addr[0] = (oid[0]  << 24) | (oid[1]  << 16) | (oid[2]  << 8) | (oid[3]  << 0);
00326   ip->addr[1] = (oid[4]  << 24) | (oid[5]  << 16) | (oid[6]  << 8) | (oid[7]  << 0);
00327   ip->addr[2] = (oid[8]  << 24) | (oid[9]  << 16) | (oid[10] << 8) | (oid[11] << 0);
00328   ip->addr[3] = (oid[12] << 24) | (oid[13] << 16) | (oid[14] << 8) | (oid[15] << 0);
00329   return 1;
00330 }
00331 
00332 /**
00333  * Convert ip6_addr to InetAddressIPv6 (no InetAddressType)
00334  * @param ip points to input struct
00335  * @param oid points to u32_t ident[16] output
00336  */
00337 void
00338 snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid)
00339 {
00340   oid[0]  = (ip->addr[0] & 0xFF000000) >> 24;
00341   oid[1]  = (ip->addr[0] & 0x00FF0000) >> 16;
00342   oid[2]  = (ip->addr[0] & 0x0000FF00) >>  8;
00343   oid[3]  = (ip->addr[0] & 0x000000FF) >>  0;
00344   oid[4]  = (ip->addr[1] & 0xFF000000) >> 24;
00345   oid[5]  = (ip->addr[1] & 0x00FF0000) >> 16;
00346   oid[6]  = (ip->addr[1] & 0x0000FF00) >>  8;
00347   oid[7]  = (ip->addr[1] & 0x000000FF) >>  0;
00348   oid[8]  = (ip->addr[2] & 0xFF000000) >> 24;
00349   oid[9]  = (ip->addr[2] & 0x00FF0000) >> 16;
00350   oid[10] = (ip->addr[2] & 0x0000FF00) >>  8;
00351   oid[11] = (ip->addr[2] & 0x000000FF) >>  0;
00352   oid[12] = (ip->addr[3] & 0xFF000000) >> 24;
00353   oid[13] = (ip->addr[3] & 0x00FF0000) >> 16;
00354   oid[14] = (ip->addr[3] & 0x0000FF00) >>  8;
00355   oid[15] = (ip->addr[3] & 0x000000FF) >>  0;
00356 }
00357 #endif /* LWIP_IPV6 */
00358 
00359 #if LWIP_IPV4 || LWIP_IPV6
00360 /**
00361  * Convert to InetAddressType+InetAddress+InetPortNumber
00362  * @param ip IP address
00363  * @param port Port
00364  * @param oid OID
00365  * @return OID length
00366  */
00367 u8_t
00368 snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid)
00369 {
00370   u8_t idx;
00371 
00372   idx = snmp_ip_to_oid(ip, oid);
00373   oid[idx] = port;
00374   idx++;
00375 
00376   return idx;
00377 }
00378 
00379 /**
00380  * Convert to InetAddressType+InetAddress
00381  * @param ip IP address
00382  * @param oid OID
00383  * @return OID length
00384  */
00385 u8_t
00386 snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid)
00387 {
00388   if (IP_IS_ANY_TYPE_VAL(*ip)) {
00389     oid[0] = 0; /* any */
00390     oid[1] = 0; /* no IP OIDs follow */
00391     return 2;
00392   } else if (IP_IS_V6(ip)) {
00393 #if LWIP_IPV6
00394     oid[0] = 2; /* ipv6 */
00395     oid[1] = 16; /* 16 InetAddressIPv6 OIDs follow */
00396     snmp_ip6_to_oid(ip_2_ip6(ip), &oid[2]);
00397     return 18;
00398 #else /* LWIP_IPV6 */
00399     return 0;
00400 #endif /* LWIP_IPV6 */
00401   } else {
00402 #if LWIP_IPV4
00403     oid[0] = 1; /* ipv4 */
00404     oid[1] = 4; /* 4 InetAddressIPv4 OIDs follow */
00405     snmp_ip4_to_oid(ip_2_ip4(ip), &oid[2]);
00406     return 6;
00407 #else /* LWIP_IPV4 */
00408     return 0;
00409 #endif /* LWIP_IPV4 */
00410   }
00411 }
00412 
00413 /**
00414  * Convert from InetAddressType+InetAddress to ip_addr_t
00415  * @param oid OID
00416  * @param oid_len OID length
00417  * @param ip IP address
00418  * @return Parsed OID length
00419  */
00420 u8_t
00421 snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip)
00422 {
00423   /* InetAddressType */
00424   if (oid_len < 1) {
00425     return 0;
00426   }
00427 
00428   if (oid[0] == 0) { /* any */
00429     /* 1x InetAddressType, 1x OID len */
00430     if (oid_len < 2) {
00431       return 0;
00432     }
00433     if (oid[1] != 0) {
00434       return 0;
00435     }
00436 
00437     memset(ip, 0, sizeof(*ip));
00438     IP_SET_TYPE(ip, IPADDR_TYPE_ANY);
00439 
00440     return 2;
00441   } else if (oid[0] == 1) { /* ipv4 */
00442 #if LWIP_IPV4
00443     /* 1x InetAddressType, 1x OID len, 4x InetAddressIPv4 */
00444     if (oid_len < 6) {
00445       return 0;
00446     }
00447 
00448     /* 4x ipv4 OID */
00449     if (oid[1] != 4) {
00450       return 0;
00451     }
00452 
00453     IP_SET_TYPE(ip, IPADDR_TYPE_V4);
00454     if (!snmp_oid_to_ip4(&oid[2], ip_2_ip4(ip))) {
00455       return 0;
00456     }
00457 
00458     return 6;
00459 #else /* LWIP_IPV4 */
00460     return 0;
00461 #endif /* LWIP_IPV4 */
00462   } else if (oid[0] == 2) { /* ipv6 */
00463 #if LWIP_IPV6
00464     /* 1x InetAddressType, 1x OID len, 16x InetAddressIPv6 */
00465     if (oid_len < 18) {
00466       return 0;
00467     }
00468 
00469     /* 16x ipv6 OID */
00470     if (oid[1] != 16) {
00471       return 0;
00472     }
00473 
00474     IP_SET_TYPE(ip, IPADDR_TYPE_V6);
00475     if (!snmp_oid_to_ip6(&oid[2], ip_2_ip6(ip))) {
00476       return 0;
00477     }
00478 
00479     return 18;
00480 #else /* LWIP_IPV6 */
00481     return 0;
00482 #endif /* LWIP_IPV6 */
00483   } else { /* unsupported InetAddressType */
00484     return 0;
00485   }
00486 }
00487 
00488 /**
00489  * Convert from InetAddressType+InetAddress+InetPortNumber to ip_addr_t and u16_t
00490  * @param oid OID
00491  * @param oid_len OID length
00492  * @param ip IP address
00493  * @param port Port
00494  * @return Parsed OID length
00495  */
00496 u8_t
00497 snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port)
00498 {
00499   u8_t idx = 0;
00500 
00501   /* InetAddressType + InetAddress */
00502   idx += snmp_oid_to_ip(&oid[idx], oid_len-idx, ip);
00503   if (idx == 0) {
00504     return 0;
00505   }
00506 
00507   /* InetPortNumber */
00508   if (oid_len < (idx+1)) {
00509     return 0;
00510   }
00511   if (oid[idx] > 0xffff) {
00512     return 0;
00513   }
00514   *port = (u16_t)oid[idx];
00515   idx++;
00516 
00517   return idx;
00518 }
00519 
00520 #endif /* LWIP_IPV4 || LWIP_IPV6 */
00521 
00522 /**
00523  * Assign an OID to struct snmp_obj_id
00524  * @param target Assignment target 
00525  * @param oid OID
00526  * @param oid_len OID length
00527  */
00528 void
00529 snmp_oid_assign(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len)
00530 {
00531   LWIP_ASSERT("oid_len <= LWIP_SNMP_OBJ_ID_LEN", oid_len <= SNMP_MAX_OBJ_ID_LEN);
00532 
00533   target->len = oid_len;
00534 
00535   if (oid_len > 0) {
00536     MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
00537   }
00538 }
00539 
00540 /**
00541  * Prefix an OID to OID in struct snmp_obj_id
00542  * @param target Assignment target to prefix
00543  * @param oid OID
00544  * @param oid_len OID length
00545  */
00546 void
00547 snmp_oid_prefix(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len)
00548 {
00549   LWIP_ASSERT("target->len + oid_len <= LWIP_SNMP_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
00550 
00551   if (oid_len > 0) {
00552     /* move existing OID to make room at the beginning for OID to insert */
00553     int i;
00554     for (i = target->len-1; i>=0; i--) {
00555       target->id[i + oid_len] = target->id[i];
00556     }
00557 
00558     /* paste oid at the beginning */
00559     MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
00560   }
00561 }
00562 
00563 /**
00564  * Combine two OIDs into struct snmp_obj_id
00565  * @param target Assignmet target
00566  * @param oid1 OID 1
00567  * @param oid1_len OID 1 length
00568  * @param oid2 OID 2
00569  * @param oid2_len OID 2 length
00570  */
00571 void
00572 snmp_oid_combine(struct snmp_obj_id* target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
00573 {
00574   snmp_oid_assign(target, oid1, oid1_len);
00575   snmp_oid_append(target, oid2, oid2_len);
00576 }
00577 
00578 /**
00579  * Append OIDs to struct snmp_obj_id
00580  * @param target Assignment target to append to
00581  * @param oid OID
00582  * @param oid_len OID length
00583  */
00584 void
00585 snmp_oid_append(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len)
00586 {
00587   LWIP_ASSERT("offset + oid_len <= LWIP_SNMP_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
00588 
00589   if (oid_len > 0) {
00590     MEMCPY(&target->id[target->len], oid, oid_len * sizeof(u32_t));
00591     target->len += oid_len;
00592   }
00593 }
00594 
00595 /**
00596  * Compare two OIDs
00597  * @param oid1 OID 1
00598  * @param oid1_len OID 1 length
00599  * @param oid2 OID 2
00600  * @param oid2_len OID 2 length
00601  * @return -1: OID1&lt;OID2  1: OID1 &gt;OID2 0: equal
00602  */
00603 s8_t
00604 snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
00605 {
00606   u8_t level = 0;
00607   LWIP_ASSERT("'oid1' param must not be NULL or 'oid1_len' param be 0!", (oid1 != NULL) || (oid1_len == 0));
00608   LWIP_ASSERT("'oid2' param must not be NULL or 'oid2_len' param be 0!", (oid2 != NULL) || (oid2_len == 0));
00609 
00610   while ((level < oid1_len) && (level < oid2_len)) {
00611     if (*oid1 < *oid2) {
00612       return -1;
00613     }
00614     if (*oid1 > *oid2) {
00615       return 1;
00616     }
00617 
00618     level++;
00619     oid1++;
00620     oid2++;
00621   }
00622 
00623   /* common part of both OID's is equal, compare length */
00624   if (oid1_len < oid2_len) {
00625     return -1;
00626   }
00627   if (oid1_len > oid2_len) {
00628     return 1;
00629   }
00630 
00631   /* they are equal */
00632   return 0;
00633 }
00634 
00635 
00636 /**
00637  * Check of two OIDs are equal
00638  * @param oid1 OID 1
00639  * @param oid1_len OID 1 length
00640  * @param oid2 OID 2
00641  * @param oid2_len OID 2 length
00642  * @return 1: equal 0: non-equal
00643  */
00644 u8_t
00645 snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
00646 {
00647   return (snmp_oid_compare(oid1, oid1_len, oid2, oid2_len) == 0)? 1 : 0;
00648 }
00649 
00650 /**
00651  * Convert netif to interface index
00652  * @param netif netif
00653  * @return index
00654  */
00655 u8_t
00656 netif_to_num(const struct netif *netif)
00657 {
00658   u8_t result = 0;
00659   struct netif *netif_iterator = netif_list;
00660 
00661   while (netif_iterator != NULL) {
00662     result++;
00663 
00664     if (netif_iterator == netif) {
00665       return result;
00666     }
00667 
00668     netif_iterator = netif_iterator->next;
00669   }
00670 
00671   LWIP_ASSERT("netif not found in netif_list", 0);
00672   return 0;
00673 }
00674 
00675 static const struct snmp_mib*
00676 snmp_get_mib_from_oid(const u32_t *oid, u8_t oid_len)
00677 {
00678   const u32_t* list_oid;
00679   const u32_t* searched_oid;
00680   u8_t i, l;
00681 
00682   u8_t max_match_len = 0;
00683   const struct snmp_mib* matched_mib = NULL;
00684 
00685   LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
00686 
00687   if (oid_len == 0) {
00688     return NULL;
00689   }
00690 
00691   for (i = 0; i < snmp_num_mibs; i++) {
00692     LWIP_ASSERT("MIB array not initialized correctly", (snmp_mibs[i] != NULL));
00693     LWIP_ASSERT("MIB array not initialized correctly - base OID is NULL", (snmp_mibs[i]->base_oid != NULL));
00694 
00695     if (oid_len >= snmp_mibs[i]->base_oid_len) {
00696       l            = snmp_mibs[i]->base_oid_len;
00697       list_oid     = snmp_mibs[i]->base_oid;
00698       searched_oid = oid;
00699 
00700       while (l > 0) {
00701         if (*list_oid != *searched_oid) {
00702           break;
00703         }
00704 
00705         l--;
00706         list_oid++;
00707         searched_oid++;
00708       }
00709 
00710       if ((l == 0) && (snmp_mibs[i]->base_oid_len > max_match_len)) {
00711         max_match_len = snmp_mibs[i]->base_oid_len;
00712         matched_mib = snmp_mibs[i];
00713       }
00714     }
00715   }
00716 
00717   return matched_mib;
00718 }
00719 
00720 static const struct snmp_mib*
00721 snmp_get_next_mib(const u32_t *oid, u8_t oid_len)
00722 {
00723   u8_t i;
00724   const struct snmp_mib* next_mib = NULL;
00725 
00726   LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
00727 
00728   if (oid_len == 0) {
00729     return NULL;
00730   }
00731 
00732   for (i = 0; i < snmp_num_mibs; i++) {
00733     if (snmp_mibs[i]->base_oid != NULL) {
00734       /* check if mib is located behind starting point */
00735       if (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len, oid, oid_len) > 0) {
00736         if ((next_mib == NULL) ||
00737             (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len,
00738                               next_mib->base_oid, next_mib->base_oid_len) < 0)) {
00739           next_mib = snmp_mibs[i];
00740         }
00741       }
00742     }
00743   }
00744 
00745   return next_mib;
00746 }
00747 
00748 static const struct snmp_mib*
00749 snmp_get_mib_between(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
00750 {
00751   const struct snmp_mib* next_mib = snmp_get_next_mib(oid1, oid1_len);
00752 
00753   LWIP_ASSERT("'oid2' param must not be NULL!", (oid2 != NULL));
00754   LWIP_ASSERT("'oid2_len' param must be greater than 0!", (oid2_len > 0));
00755 
00756   if (next_mib != NULL) {
00757     if (snmp_oid_compare(next_mib->base_oid, next_mib->base_oid_len, oid2, oid2_len) < 0) {
00758       return next_mib;
00759     }
00760   }
00761 
00762   return NULL;
00763 }
00764 
00765 u8_t
00766 snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance* node_instance)
00767 {
00768   u8_t result = SNMP_ERR_NOSUCHOBJECT;
00769   const struct snmp_mib *mib;
00770   const struct snmp_node *mn = NULL;
00771 
00772   mib = snmp_get_mib_from_oid(oid, oid_len);
00773   if (mib != NULL) {
00774     u8_t oid_instance_len;
00775 
00776     mn = snmp_mib_tree_resolve_exact(mib, oid, oid_len, &oid_instance_len);
00777     if ((mn != NULL) && (mn->node_type != SNMP_NODE_TREE)) {
00778       /* get instance */
00779       const struct snmp_leaf_node* leaf_node = (const struct snmp_leaf_node*)(const void*)mn;
00780 
00781       node_instance->node = mn;
00782       snmp_oid_assign(&node_instance->instance_oid, oid + (oid_len - oid_instance_len), oid_instance_len);
00783 
00784       result = leaf_node->get_instance(
00785         oid,
00786         oid_len - oid_instance_len,
00787         node_instance);
00788 
00789 #ifdef LWIP_DEBUG
00790       if (result == SNMP_ERR_NOERROR) {
00791         if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
00792           LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
00793         }
00794         if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
00795           LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value and/or set_test function is specified\n"));
00796         }
00797       }
00798 #endif
00799     }
00800   }
00801 
00802   return result;
00803 }
00804 
00805 u8_t
00806 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)
00807 {
00808   const struct snmp_mib      *mib;
00809   const struct snmp_node *mn = NULL;
00810   const u32_t* start_oid     = NULL;
00811   u8_t         start_oid_len = 0;
00812 
00813   /* resolve target MIB from passed OID */
00814   mib = snmp_get_mib_from_oid(oid, oid_len);
00815   if (mib == NULL) {
00816     /* passed OID does not reference any known MIB, start at the next closest MIB */
00817     mib = snmp_get_next_mib(oid, oid_len);
00818 
00819     if (mib != NULL) {
00820       start_oid     = mib->base_oid;
00821       start_oid_len = mib->base_oid_len;
00822     }
00823   } else {
00824     start_oid     = oid;
00825     start_oid_len = oid_len;
00826   }
00827 
00828   /* resolve target node from MIB, skip to next MIB if no suitable node is found in current MIB */
00829   while ((mib != NULL) && (mn == NULL)) {
00830     u8_t oid_instance_len;
00831 
00832     /* check if OID directly references a node inside current MIB, in this case we have to ask this node for the next instance */
00833     mn = snmp_mib_tree_resolve_exact(mib, start_oid, start_oid_len, &oid_instance_len);
00834     if (mn != NULL) {
00835       snmp_oid_assign(node_oid, start_oid, start_oid_len - oid_instance_len); /* set oid to node */
00836       snmp_oid_assign(&node_instance->instance_oid, start_oid + (start_oid_len - oid_instance_len), oid_instance_len); /* set (relative) instance oid */
00837     } else {
00838       /* 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 */
00839       mn = snmp_mib_tree_resolve_next(mib, start_oid, start_oid_len, node_oid);
00840       node_instance->instance_oid.len = 0;
00841     }
00842 
00843     /* 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 */
00844     node_instance->node = mn;
00845     while (mn != NULL) {
00846        u8_t result;
00847 
00848       /* clear fields which may have values from previous loops */
00849       node_instance->asn1_type        = 0;
00850       node_instance->access           = SNMP_NODE_INSTANCE_NOT_ACCESSIBLE;
00851       node_instance->get_value        = NULL;
00852       node_instance->set_test         = NULL;
00853       node_instance->set_value        = NULL;
00854       node_instance->release_instance = NULL;
00855       node_instance->reference.ptr    = NULL;
00856       node_instance->reference_len    = 0;
00857 
00858       result = ((const struct snmp_leaf_node*)(const void*)mn)->get_next_instance(
00859         node_oid->id,
00860         node_oid->len,
00861         node_instance);
00862 
00863       if (result == SNMP_ERR_NOERROR) {
00864 #ifdef LWIP_DEBUG
00865         if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
00866           LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
00867         }
00868         if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
00869           LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value function is specified\n"));
00870         }
00871 #endif
00872 
00873         /* validate node because the node may be not accessible for example (but let the caller decide what is valid */
00874         if ((validate_node_instance_method == NULL) ||
00875             (validate_node_instance_method(node_instance, validate_node_instance_arg) == SNMP_ERR_NOERROR)) {
00876           /* node_oid "returns" the full result OID (including the instance part) */
00877           snmp_oid_append(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
00878           break;
00879         }
00880 
00881         if (node_instance->release_instance != NULL) {
00882           node_instance->release_instance(node_instance);
00883         }
00884         /*
00885         the instance itself is not valid, ask for next instance from same node.
00886         we don't have to change any variables because node_instance->instance_oid is used as input (starting point)
00887         as well as output (resulting next OID), so we have to simply call get_next_instance method again
00888         */
00889       } else {
00890         if (node_instance->release_instance != NULL) {
00891           node_instance->release_instance(node_instance);
00892         }
00893 
00894         /* the node has no further instance, skip to next node */
00895         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 */
00896         if (mn != NULL) {
00897           /* prepare for next loop */
00898           snmp_oid_assign(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
00899           node_instance->instance_oid.len = 0;
00900           node_instance->node = mn;
00901         }
00902       }
00903     }
00904 
00905     if (mn != NULL) {
00906       /*
00907       we found a suitable next node,
00908       now we have to check if a inner MIB is located between the searched OID and the resulting OID.
00909       this is possible because MIB's may be located anywhere in the global tree, that means also in 
00910       the subtree of another MIB (e.g. if searched OID is .2 and resulting OID is .4, then another
00911       MIB having .3 as root node may exist)
00912       */
00913       const struct snmp_mib *intermediate_mib;
00914       intermediate_mib = snmp_get_mib_between(start_oid, start_oid_len, node_oid->id, node_oid->len);
00915 
00916       if (intermediate_mib != NULL) {
00917         /* search for first node inside intermediate mib in next loop */
00918         if (node_instance->release_instance != NULL) {
00919           node_instance->release_instance(node_instance);
00920         }
00921 
00922         mn            = NULL;
00923         mib           = intermediate_mib;
00924         start_oid     = mib->base_oid;
00925         start_oid_len = mib->base_oid_len;
00926       }
00927       /* else { we found out target node } */
00928     } else {
00929       /*
00930       there is no further (suitable) node inside this MIB, search for the next MIB with following priority
00931       1. search for inner MIB's (whose root is located inside tree of current MIB)
00932       2. search for surrouding MIB's (where the current MIB is the inner MIB) and continue there if any
00933       3. take the next closest MIB (not being related to the current MIB)
00934       */
00935       const struct snmp_mib *next_mib;
00936       next_mib = snmp_get_next_mib(start_oid, start_oid_len); /* returns MIB's related to point 1 and 3 */
00937 
00938       /* is the found MIB an inner MIB? (point 1) */
00939       if ((next_mib != NULL) && (next_mib->base_oid_len > mib->base_oid_len) &&
00940           (snmp_oid_compare(next_mib->base_oid, mib->base_oid_len, mib->base_oid, mib->base_oid_len) == 0)) {
00941         /* yes it is -> continue at inner MIB */
00942         mib = next_mib;
00943         start_oid     = mib->base_oid;
00944         start_oid_len = mib->base_oid_len;
00945       } else {
00946         /* check if there is a surrounding mib where to continue (point 2) (only possible if OID length > 1) */
00947         if (mib->base_oid_len > 1) {
00948           mib = snmp_get_mib_from_oid(mib->base_oid, mib->base_oid_len - 1);
00949 
00950           if (mib == NULL) {
00951             /* no surrounding mib, use next mib encountered above (point 3) */
00952             mib = next_mib;
00953 
00954             if (mib != NULL) {
00955               start_oid     = mib->base_oid;
00956               start_oid_len = mib->base_oid_len;
00957             }
00958           }
00959           /* else { start_oid stays the same because we want to continue from current offset in surrounding mib (point 2) } */
00960         }
00961       }
00962     }
00963   }
00964 
00965   if (mib == NULL) {
00966     /* loop is only left when mib == null (error) or mib_node != NULL (success) */
00967     return SNMP_ERR_ENDOFMIBVIEW;
00968   }
00969 
00970   return SNMP_ERR_NOERROR;
00971 }
00972 
00973 /**
00974  * Searches tree for the supplied object identifier.
00975  *
00976  */
00977 const struct snmp_node *
00978 snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t* oid_instance_len)
00979 {
00980   const struct snmp_node* const* node = &mib->root_node;
00981   u8_t oid_offset = mib->base_oid_len;
00982 
00983   while ((oid_offset < oid_len) && ((*node)->node_type == SNMP_NODE_TREE)) {
00984     /* search for matching sub node */
00985     u32_t subnode_oid = *(oid + oid_offset);
00986 
00987     u32_t i = (*(const struct snmp_tree_node* const*)node)->subnode_count;
00988     node    = (*(const struct snmp_tree_node* const*)node)->subnodes;
00989     while ((i > 0) && ((*node)->oid != subnode_oid)) {
00990       node++;
00991       i--;
00992     }
00993 
00994     if (i == 0) {
00995       /* no matching subnode found */
00996       return NULL;
00997     }
00998 
00999     oid_offset++;
01000   }
01001 
01002   if ((*node)->node_type != SNMP_NODE_TREE) {
01003     /* we found a leaf node */
01004     *oid_instance_len = oid_len - oid_offset;
01005     return (*node);
01006   }
01007 
01008   return NULL;
01009 }
01010 
01011 const struct snmp_node*
01012 snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id* oidret)
01013 {
01014   u8_t  oid_offset = mib->base_oid_len;
01015   const struct snmp_node* const* node;
01016   const struct snmp_tree_node* node_stack[SNMP_MAX_OBJ_ID_LEN];
01017   s32_t nsi = 0; /* NodeStackIndex */
01018   u32_t subnode_oid;
01019 
01020   if (mib->root_node->node_type != SNMP_NODE_TREE) {
01021     /* a next operation on a mib with only a leaf node will always return NULL because there is no other node */
01022     return NULL;
01023   }
01024 
01025   /* first build node stack related to passed oid (as far as possible), then go backwards to determine the next node */
01026   node_stack[nsi] = (const struct snmp_tree_node*)(const void*)mib->root_node;
01027   while (oid_offset < oid_len) {
01028     /* search for matching sub node */
01029     u32_t i = node_stack[nsi]->subnode_count;
01030     node    = node_stack[nsi]->subnodes;
01031 
01032     subnode_oid = *(oid + oid_offset);
01033 
01034     while ((i > 0) && ((*node)->oid != subnode_oid)) {
01035       node++;
01036       i--;
01037     }
01038 
01039     if ((i == 0) || ((*node)->node_type != SNMP_NODE_TREE)) {
01040       /* no (matching) tree-subnode found */
01041       break;
01042     }
01043     nsi++;
01044     node_stack[nsi] = (const struct snmp_tree_node*)(const void*)(*node);
01045 
01046     oid_offset++;
01047   }
01048 
01049 
01050   if (oid_offset >= oid_len) {
01051     /* passed oid references a tree node -> return first useable sub node of it */
01052     subnode_oid = 0;
01053   } else {
01054     subnode_oid = *(oid + oid_offset) + 1;
01055   }
01056 
01057   while (nsi >= 0) {
01058     const struct snmp_node* subnode = NULL;
01059 
01060     /* find next node on current level */
01061     s32_t i        = node_stack[nsi]->subnode_count;
01062     node           = node_stack[nsi]->subnodes;
01063     while (i > 0) {
01064       if ((*node)->oid == subnode_oid) {
01065         subnode = *node;
01066         break;
01067       } else if (((*node)->oid > subnode_oid) && ((subnode == NULL) || ((*node)->oid < subnode->oid))) {
01068         subnode = *node;
01069       }
01070 
01071       node++;
01072       i--;
01073     }
01074 
01075     if (subnode == NULL) {
01076       /* no further node found on this level, go one level up and start searching with index of current node*/
01077       subnode_oid = node_stack[nsi]->node.oid + 1;
01078       nsi--;
01079     } else {
01080       if (subnode->node_type == SNMP_NODE_TREE) {
01081         /* next is a tree node, go into it and start searching */
01082         nsi++;
01083         node_stack[nsi] = (const struct snmp_tree_node*)(const void*)subnode;
01084         subnode_oid = 0;
01085       } else {
01086         /* we found a leaf node -> fill oidret and return it */
01087         snmp_oid_assign(oidret, mib->base_oid, mib->base_oid_len);
01088         i = 1;
01089         while (i <= nsi) {
01090           oidret->id[oidret->len] = node_stack[i]->node.oid;
01091           oidret->len++;
01092           i++;
01093         }
01094 
01095         oidret->id[oidret->len] = subnode->oid;
01096         oidret->len++;
01097 
01098         return subnode;
01099       }
01100     }
01101   }
01102 
01103   return NULL;
01104 }
01105 
01106 /** initialize struct next_oid_state using this function before passing it to next_oid_check */
01107 void
01108 snmp_next_oid_init(struct snmp_next_oid_state *state,
01109   const u32_t *start_oid, u8_t start_oid_len,
01110   u32_t *next_oid_buf, u8_t next_oid_max_len)
01111 {
01112   state->start_oid        = start_oid;
01113   state->start_oid_len    = start_oid_len;
01114   state->next_oid         = next_oid_buf;
01115   state->next_oid_len     = 0;
01116   state->next_oid_max_len = next_oid_max_len;
01117   state->status           = SNMP_NEXT_OID_STATUS_NO_MATCH;
01118 }
01119 
01120 /** checks if the passed incomplete OID may be a possible candidate for snmp_next_oid_check();
01121 this methid is intended if the complete OID is not yet known but it is very expensive to build it up,
01122 so it is possible to test the starting part before building up the complete oid and pass it to snmp_next_oid_check()*/
01123 u8_t
01124 snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len)
01125 {
01126   if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
01127     u8_t start_oid_len = (oid_len < state->start_oid_len) ? oid_len : state->start_oid_len;
01128 
01129     /* check passed OID is located behind start offset */
01130     if (snmp_oid_compare(oid, oid_len, state->start_oid, start_oid_len) >= 0) {
01131       /* check if new oid is located closer to start oid than current closest oid */
01132       if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
01133         (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
01134         return 1;
01135       }
01136     }
01137   }
01138 
01139   return 0;
01140 }
01141 
01142 /** 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 */
01143 u8_t
01144 snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len, void* reference)
01145 {
01146   /* do not overwrite a fail result */
01147   if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
01148     /* check passed OID is located behind start offset */
01149     if (snmp_oid_compare(oid, oid_len, state->start_oid, state->start_oid_len) > 0) {
01150       /* check if new oid is located closer to start oid than current closest oid */
01151       if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
01152         (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
01153         if (oid_len <= state->next_oid_max_len) {
01154           MEMCPY(state->next_oid, oid, oid_len * sizeof(u32_t));
01155           state->next_oid_len = oid_len;
01156           state->status       = SNMP_NEXT_OID_STATUS_SUCCESS;
01157           state->reference    = reference;
01158           return 1;
01159         } else {
01160           state->status = SNMP_NEXT_OID_STATUS_BUF_TO_SMALL;
01161         }
01162       }
01163     }
01164   }
01165 
01166   return 0;
01167 }
01168 
01169 u8_t
01170 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)
01171 {
01172   u8_t i;
01173 
01174   if (oid_len != oid_ranges_len) {
01175     return 0;
01176   }
01177 
01178   for (i = 0; i < oid_ranges_len; i++) {
01179     if ((oid_in[i] < oid_ranges[i].min) || (oid_in[i] > oid_ranges[i].max)) {
01180       return 0;
01181     }
01182   }
01183 
01184   return 1;
01185 }
01186 
01187 snmp_err_t
01188 snmp_set_test_ok(struct snmp_node_instance* instance, u16_t value_len, void* value)
01189 {
01190   LWIP_UNUSED_ARG(instance);
01191   LWIP_UNUSED_ARG(value_len);
01192   LWIP_UNUSED_ARG(value);
01193 
01194   return SNMP_ERR_NOERROR;
01195 }
01196 
01197 /**
01198  * Decodes BITS pseudotype value from ASN.1 OctetString.
01199  *
01200  * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
01201  * be encoded/decoded by the agent. Instead call this function as required from
01202  * get/test/set methods.
01203  *
01204  * @param buf points to a buffer holding the ASN1 octet string
01205  * @param buf_len length of octet string
01206  * @param bit_value decoded Bit value with Bit0 == LSB
01207  * @return ERR_OK if successful, ERR_ARG if bit value contains more than 32 bit
01208  */
01209 err_t
01210 snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value)
01211 {
01212   u8_t b;
01213   u8_t bits_processed = 0;
01214   *bit_value = 0;
01215 
01216   while (buf_len > 0) {
01217     /* any bit set in this byte? */
01218     if (*buf != 0x00) {
01219       if (bits_processed >= 32) {
01220         /* accept more than 4 bytes, but only when no bits are set */
01221         return ERR_VAL;
01222       }
01223 
01224       b = *buf;
01225       do {
01226         if (b & 0x80) {
01227           *bit_value |= (1 << bits_processed);
01228         }
01229         bits_processed++;
01230         b <<= 1;
01231       }
01232       while ((bits_processed & 0x07) != 0); /* &0x07 -> % 8 */
01233     } else {
01234       bits_processed += 8;
01235     }
01236 
01237     buf_len--;
01238     buf++;
01239   }
01240 
01241   return ERR_OK;
01242 }
01243 
01244 err_t
01245 snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value)
01246 {
01247   /* defined by RFC1443:
01248    TruthValue ::= TEXTUAL-CONVENTION
01249     STATUS       current
01250     DESCRIPTION
01251      "Represents a boolean value."
01252     SYNTAX       INTEGER { true(1), false(2) }
01253   */
01254 
01255   if ((asn1_value == NULL) || (bool_value == NULL)) {
01256     return ERR_ARG;
01257   }
01258 
01259   if (*asn1_value == 1) {
01260     *bool_value = 1;
01261   } else if (*asn1_value == 2) {
01262     *bool_value = 0;
01263   } else {
01264     return ERR_VAL;
01265   }
01266 
01267   return ERR_OK;
01268 }
01269 
01270 /**
01271  * Encodes BITS pseudotype value into ASN.1 OctetString.
01272  *
01273  * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
01274  * be encoded/decoded by the agent. Instead call this function as required from
01275  * get/test/set methods.
01276  *
01277  * @param buf points to a buffer where the resulting ASN1 octet string is stored to
01278  * @param buf_len max length of the bufffer
01279  * @param bit_value Bit value to encode with Bit0 == LSB
01280  * @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)
01281  * @return number of bytes used from buffer to store the resulting OctetString
01282  */
01283 u8_t
01284 snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count)
01285 {
01286   u8_t len = 0;
01287   u8_t min_bytes = (bit_count + 7) >> 3; /* >>3 -> / 8 */
01288 
01289   while ((buf_len > 0) && (bit_value != 0x00)) {
01290     s8_t i = 7;
01291     *buf = 0x00;
01292     while (i >= 0) {
01293       if (bit_value & 0x01) {
01294         *buf |= 0x01;
01295       }
01296 
01297       if (i > 0) {
01298         *buf <<= 1;
01299       }
01300 
01301       bit_value >>= 1;
01302       i--;
01303     }
01304 
01305     buf++;
01306     buf_len--;
01307     len++;
01308   }
01309 
01310   if (len < min_bytes) {
01311     buf     += len;
01312     buf_len -= len;
01313 
01314     while ((len < min_bytes) && (buf_len > 0)) {
01315       *buf = 0x00;
01316       buf++;
01317       buf_len--;
01318       len++;
01319     }
01320   }
01321 
01322   return len;
01323 }
01324 
01325 u8_t
01326 snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value)
01327 {
01328   /* defined by RFC1443:
01329    TruthValue ::= TEXTUAL-CONVENTION
01330     STATUS       current
01331     DESCRIPTION
01332      "Represents a boolean value."
01333     SYNTAX       INTEGER { true(1), false(2) }
01334   */
01335 
01336   if (asn1_value == NULL) {
01337     return 0;
01338   }
01339 
01340   if (bool_value) {
01341     *asn1_value = 1; /* defined by RFC1443 */
01342   } else {
01343     *asn1_value = 2; /* defined by RFC1443 */
01344   }
01345 
01346   return sizeof(s32_t);
01347 }
01348 
01349 #endif /* LWIP_SNMP */