Lee Kai Xuan / mbed-os

Fork of mbed-os by erkin yucel

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