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