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