mirror of
https://git.savannah.nongnu.org/git/lwip.git
synced 2025-12-12 17:56:39 +08:00
1350 lines
40 KiB
C
1350 lines
40 KiB
C
/**
|
|
* @file
|
|
* MIB tree access/construction functions.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
|
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
|
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
|
* OF SUCH DAMAGE.
|
|
*
|
|
* Author: Christiaan Simons <christiaan.simons@axon.tv>
|
|
* Martin Hentschel <info@cl-soft.de>
|
|
*/
|
|
|
|
/**
|
|
* @defgroup snmp SNMPv2c agent
|
|
* @ingroup apps
|
|
* SNMPv2c compatible agent\n
|
|
* There is also a MIB compiler and a MIB viewer in lwIP contrib repository
|
|
* (lwip-contrib/apps/LwipMibCompiler).\n
|
|
* The agent implements the most important MIB2 MIBs including IPv6 support
|
|
* (interfaces, UDP, TCP, SNMP, ICMP, SYSTEM). IP MIB is an older version
|
|
* whithout IPv6 statistics (TODO).\n
|
|
* Rewritten by Martin Hentschel <info@cl-soft.de> and
|
|
* Dirk Ziegelmeier <dziegel@gmx.de>\n
|
|
* Work on SNMPv3 has started, but is not finished.\n
|
|
*
|
|
* 0 Agent Capabilities
|
|
* ====================
|
|
*
|
|
* Features:
|
|
* ---------
|
|
* - SNMPv2c support.
|
|
* - Low RAM usage - no memory pools, stack only.
|
|
* - MIB2 implementation is separated from SNMP stack.
|
|
* - Support for multiple MIBs (snmp_set_mibs() call) - e.g. for private MIB.
|
|
* - Simple and generic API for MIB implementation.
|
|
* - Comfortable node types and helper functions for scalar arrays and tables.
|
|
* - Counter64, bit and truthvalue datatype support.
|
|
* - Callbacks for SNMP writes e.g. to implement persistency.
|
|
* - Runs on two APIs: RAW and netconn.
|
|
* - Async API is gone - the stack now supports netconn API instead,
|
|
* so blocking operations can be done in MIB calls.
|
|
* SNMP runs in a worker thread when netconn API is used.
|
|
* - Simplified thread sync support for MIBs - useful when MIBs
|
|
* need to access variables shared with other threads where no locking is
|
|
* possible. Used in MIB2 to access lwIP stats from lwIP thread.
|
|
*
|
|
* MIB compiler (code generator):
|
|
* ------------------------------
|
|
* - Provided in lwIP contrib repository.
|
|
* - Written in C#. MIB viewer used Windows Forms.
|
|
* - Developed on Windows with Visual Studio 2010.
|
|
* - Can be compiled and used on all platforms with http://www.monodevelop.com/.
|
|
* - Based on a heavily modified version of of SharpSnmpLib (a4bd05c6afb4)
|
|
* (https://sharpsnmplib.codeplex.com/SourceControl/network/forks/Nemo157/MIBParserUpdate).
|
|
* - MIB parser, C file generation framework and LWIP code generation are cleanly
|
|
* separated, which means the code may be useful as a base for code generation
|
|
* of other SNMP agents.
|
|
*
|
|
* Notes:
|
|
* ------
|
|
* - Stack and MIB compiler were used to implement a Profinet device.
|
|
* Compiled/implemented MIBs: LLDP-MIB, LLDP-EXT-DOT3-MIB, LLDP-EXT-PNO-MIB.
|
|
*
|
|
* SNMPv1 per RFC1157 and SNMPv2c per RFC 3416
|
|
* -------------------------------------------
|
|
* Note the S in SNMP stands for "Simple". Note that "Simple" is
|
|
* relative. SNMP is simple compared to the complex ISO network
|
|
* management protocols CMIP (Common Management Information Protocol)
|
|
* and CMOT (CMip Over Tcp).
|
|
*
|
|
* MIB II
|
|
* ------
|
|
* The standard lwIP stack management information base.
|
|
* This is a required MIB, so this is always enabled.
|
|
* The groups EGP, CMOT and transmission are disabled by default.
|
|
*
|
|
* Most mib-2 objects are not writable except:
|
|
* sysName, sysLocation, sysContact, snmpEnableAuthenTraps.
|
|
* Writing to or changing the ARP and IP address and route
|
|
* tables is not possible.
|
|
*
|
|
* Note lwIP has a very limited notion of IP routing. It currently
|
|
* doen't have a route table and doesn't have a notion of the U,G,H flags.
|
|
* Instead lwIP uses the interface list with only one default interface
|
|
* acting as a single gateway interface (G) for the default route.
|
|
*
|
|
* The agent returns a "virtual table" with the default route 0.0.0.0
|
|
* for the default interface and network routes (no H) for each
|
|
* network interface in the netif_list.
|
|
* All routes are considered to be up (U).
|
|
*
|
|
* Loading additional MIBs
|
|
* -----------------------
|
|
* MIBs can only be added in compile-time, not in run-time.
|
|
*
|
|
*
|
|
* 1 Building the Agent
|
|
* ====================
|
|
* First of all you'll need to add the following define
|
|
* to your local lwipopts.h:
|
|
* \#define LWIP_SNMP 1
|
|
*
|
|
* and add the source files your makefile.
|
|
*
|
|
* Note you'll might need to adapt you network driver to update
|
|
* the mib2 variables for your interface.
|
|
*
|
|
* 2 Running the Agent
|
|
* ===================
|
|
* The following function calls must be made in your program to
|
|
* actually get the SNMP agent running.
|
|
*
|
|
* Before starting the agent you should supply pointers
|
|
* for sysContact, sysLocation, and snmpEnableAuthenTraps.
|
|
* You can do this by calling
|
|
*
|
|
* - snmp_mib2_set_syscontact()
|
|
* - snmp_mib2_set_syslocation()
|
|
* - snmp_set_auth_traps_enabled()
|
|
*
|
|
* You can register a callback which is called on successful write access:
|
|
* snmp_set_write_callback().
|
|
*
|
|
* Additionally you may want to set
|
|
*
|
|
* - snmp_mib2_set_sysdescr()
|
|
* - snmp_set_device_enterprise_oid()
|
|
* - snmp_mib2_set_sysname()
|
|
*
|
|
* Also before starting the agent you need to setup
|
|
* one or more trap destinations using these calls:
|
|
*
|
|
* - snmp_trap_dst_enable()
|
|
* - snmp_trap_dst_ip_set()
|
|
*
|
|
* If you need more than MIB2, set the MIBs you want to use
|
|
* by snmp_set_mibs().
|
|
*
|
|
* Finally, enable the agent by calling snmp_init()
|
|
*
|
|
* @defgroup snmp_core Core
|
|
* @ingroup snmp
|
|
*
|
|
* @defgroup snmp_traps Traps
|
|
* @ingroup snmp
|
|
*/
|
|
|
|
#include "lwip/apps/snmp_opts.h"
|
|
|
|
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
|
|
|
|
#include "lwip/apps/snmp.h"
|
|
#include "lwip/apps/snmp_core.h"
|
|
#include "snmp_core_priv.h"
|
|
#include "lwip/netif.h"
|
|
#include <string.h>
|
|
|
|
|
|
#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
|
|
#error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
|
|
#endif
|
|
#if (!LWIP_UDP && LWIP_SNMP)
|
|
#error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
|
|
#endif
|
|
|
|
struct snmp_statistics snmp_stats;
|
|
static const struct snmp_obj_id snmp_device_enterprise_oid_default = {SNMP_DEVICE_ENTERPRISE_OID_LEN, SNMP_DEVICE_ENTERPRISE_OID};
|
|
static const struct snmp_obj_id* snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default;
|
|
|
|
const u32_t snmp_zero_dot_zero_values[] = { 0, 0 };
|
|
const struct snmp_obj_id_const_ref snmp_zero_dot_zero = { LWIP_ARRAYSIZE(snmp_zero_dot_zero_values), snmp_zero_dot_zero_values };
|
|
|
|
|
|
#if SNMP_LWIP_MIB2
|
|
#include "lwip/apps/snmp_mib2.h"
|
|
static const struct snmp_mib* const default_mibs[] = { &mib2 };
|
|
static u8_t snmp_num_mibs = 1;
|
|
#else
|
|
static const struct snmp_mib* const default_mibs[] = { NULL };
|
|
static u8_t snmp_num_mibs = 0;
|
|
#endif
|
|
|
|
/* List of known mibs */
|
|
static struct snmp_mib const * const *snmp_mibs = default_mibs;
|
|
|
|
/**
|
|
* @ingroup snmp_core
|
|
* Sets the MIBs to use.
|
|
* Example: call snmp_set_mibs() as follows:
|
|
* static const struct snmp_mib *my_snmp_mibs[] = {
|
|
* &mib2,
|
|
* &private_mib
|
|
* };
|
|
* snmp_set_mibs(my_snmp_mibs, LWIP_ARRAYSIZE(my_snmp_mibs));
|
|
*/
|
|
void
|
|
snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs)
|
|
{
|
|
LWIP_ASSERT("mibs pointer must be != NULL", (mibs != NULL));
|
|
LWIP_ASSERT("num_mibs pointer must be != 0", (num_mibs != 0));
|
|
snmp_mibs = mibs;
|
|
snmp_num_mibs = num_mibs;
|
|
}
|
|
|
|
/**
|
|
* @ingroup snmp_core
|
|
* 'device enterprise oid' is used for 'device OID' field in trap PDU's (for identification of generating device)
|
|
* as well as for value returned by MIB-2 'sysObjectID' field (if internal MIB2 implementation is used).
|
|
* The 'device enterprise oid' shall point to an OID located under 'private-enterprises' branch (1.3.6.1.4.1.XXX). If a vendor
|
|
* wants to provide a custom object there, he has to get its own enterprise oid from IANA (http://www.iana.org). It
|
|
* is not allowed to use LWIP enterprise ID!
|
|
* In order to identify a specific device it is recommended to create a dedicated OID for each device type under its own
|
|
* enterprise oid.
|
|
* e.g.
|
|
* device a > 1.3.6.1.4.1.XXX(ent-oid).1(devices).1(device a)
|
|
* device b > 1.3.6.1.4.1.XXX(ent-oid).1(devices).2(device b)
|
|
* for more details see description of 'sysObjectID' field in RFC1213-MIB
|
|
*/
|
|
void snmp_set_device_enterprise_oid(const struct snmp_obj_id* device_enterprise_oid)
|
|
{
|
|
if (device_enterprise_oid == NULL) {
|
|
snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default;
|
|
} else {
|
|
snmp_device_enterprise_oid = device_enterprise_oid;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @ingroup snmp_core
|
|
* Get 'device enterprise oid'
|
|
*/
|
|
const struct snmp_obj_id* snmp_get_device_enterprise_oid(void)
|
|
{
|
|
return snmp_device_enterprise_oid;
|
|
}
|
|
|
|
#if LWIP_IPV4
|
|
/**
|
|
* Conversion from InetAddressIPv4 oid to lwIP ip4_addr
|
|
* @param oid points to u32_t ident[4] input
|
|
* @param ip points to output struct
|
|
*/
|
|
u8_t
|
|
snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip)
|
|
{
|
|
if ((oid[0] > 0xFF) ||
|
|
(oid[1] > 0xFF) ||
|
|
(oid[2] > 0xFF) ||
|
|
(oid[3] > 0xFF)) {
|
|
ip4_addr_copy(*ip, *IP4_ADDR_ANY4);
|
|
return 0;
|
|
}
|
|
|
|
IP4_ADDR(ip, oid[0], oid[1], oid[2], oid[3]);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Convert ip4_addr to InetAddressIPv4 (no InetAddressType)
|
|
* @param ip points to input struct
|
|
* @param oid points to u32_t ident[4] output
|
|
*/
|
|
void
|
|
snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid)
|
|
{
|
|
oid[0] = ip4_addr1(ip);
|
|
oid[1] = ip4_addr2(ip);
|
|
oid[2] = ip4_addr3(ip);
|
|
oid[3] = ip4_addr4(ip);
|
|
}
|
|
#endif /* LWIP_IPV4 */
|
|
|
|
#if LWIP_IPV6
|
|
/**
|
|
* Conversion from InetAddressIPv6 oid to lwIP ip6_addr
|
|
* @param oid points to u32_t oid[16] input
|
|
* @param ip points to output struct
|
|
*/
|
|
u8_t
|
|
snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip)
|
|
{
|
|
if ((oid[0] > 0xFF) ||
|
|
(oid[1] > 0xFF) ||
|
|
(oid[2] > 0xFF) ||
|
|
(oid[3] > 0xFF) ||
|
|
(oid[4] > 0xFF) ||
|
|
(oid[5] > 0xFF) ||
|
|
(oid[6] > 0xFF) ||
|
|
(oid[7] > 0xFF) ||
|
|
(oid[8] > 0xFF) ||
|
|
(oid[9] > 0xFF) ||
|
|
(oid[10] > 0xFF) ||
|
|
(oid[11] > 0xFF) ||
|
|
(oid[12] > 0xFF) ||
|
|
(oid[13] > 0xFF) ||
|
|
(oid[14] > 0xFF) ||
|
|
(oid[15] > 0xFF)) {
|
|
ip6_addr_set_any(ip);
|
|
return 0;
|
|
}
|
|
|
|
ip->addr[0] = (oid[0] << 24) | (oid[1] << 16) | (oid[2] << 8) | (oid[3] << 0);
|
|
ip->addr[1] = (oid[4] << 24) | (oid[5] << 16) | (oid[6] << 8) | (oid[7] << 0);
|
|
ip->addr[2] = (oid[8] << 24) | (oid[9] << 16) | (oid[10] << 8) | (oid[11] << 0);
|
|
ip->addr[3] = (oid[12] << 24) | (oid[13] << 16) | (oid[14] << 8) | (oid[15] << 0);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Convert ip6_addr to InetAddressIPv6 (no InetAddressType)
|
|
* @param ip points to input struct
|
|
* @param oid points to u32_t ident[16] output
|
|
*/
|
|
void
|
|
snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid)
|
|
{
|
|
oid[0] = (ip->addr[0] & 0xFF000000) >> 24;
|
|
oid[1] = (ip->addr[0] & 0x00FF0000) >> 16;
|
|
oid[2] = (ip->addr[0] & 0x0000FF00) >> 8;
|
|
oid[3] = (ip->addr[0] & 0x000000FF) >> 0;
|
|
oid[4] = (ip->addr[1] & 0xFF000000) >> 24;
|
|
oid[5] = (ip->addr[1] & 0x00FF0000) >> 16;
|
|
oid[6] = (ip->addr[1] & 0x0000FF00) >> 8;
|
|
oid[7] = (ip->addr[1] & 0x000000FF) >> 0;
|
|
oid[8] = (ip->addr[2] & 0xFF000000) >> 24;
|
|
oid[9] = (ip->addr[2] & 0x00FF0000) >> 16;
|
|
oid[10] = (ip->addr[2] & 0x0000FF00) >> 8;
|
|
oid[11] = (ip->addr[2] & 0x000000FF) >> 0;
|
|
oid[12] = (ip->addr[3] & 0xFF000000) >> 24;
|
|
oid[13] = (ip->addr[3] & 0x00FF0000) >> 16;
|
|
oid[14] = (ip->addr[3] & 0x0000FF00) >> 8;
|
|
oid[15] = (ip->addr[3] & 0x000000FF) >> 0;
|
|
}
|
|
#endif /* LWIP_IPV6 */
|
|
|
|
#if LWIP_IPV4 || LWIP_IPV6
|
|
/**
|
|
* Convert to InetAddressType+InetAddress+InetPortNumber
|
|
* @param ip IP address
|
|
* @param port Port
|
|
* @param oid OID
|
|
* @return OID length
|
|
*/
|
|
u8_t
|
|
snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid)
|
|
{
|
|
u8_t idx;
|
|
|
|
idx = snmp_ip_to_oid(ip, oid);
|
|
oid[idx] = port;
|
|
idx++;
|
|
|
|
return idx;
|
|
}
|
|
|
|
/**
|
|
* Convert to InetAddressType+InetAddress
|
|
* @param ip IP address
|
|
* @param oid OID
|
|
* @return OID length
|
|
*/
|
|
u8_t
|
|
snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid)
|
|
{
|
|
if (IP_IS_ANY_TYPE_VAL(*ip)) {
|
|
oid[0] = 0; /* any */
|
|
oid[1] = 0; /* no IP OIDs follow */
|
|
return 2;
|
|
} else if (IP_IS_V6(ip)) {
|
|
#if LWIP_IPV6
|
|
oid[0] = 2; /* ipv6 */
|
|
oid[1] = 16; /* 16 InetAddressIPv6 OIDs follow */
|
|
snmp_ip6_to_oid(ip_2_ip6(ip), &oid[2]);
|
|
return 18;
|
|
#else /* LWIP_IPV6 */
|
|
return 0;
|
|
#endif /* LWIP_IPV6 */
|
|
} else {
|
|
#if LWIP_IPV4
|
|
oid[0] = 1; /* ipv4 */
|
|
oid[1] = 4; /* 4 InetAddressIPv4 OIDs follow */
|
|
snmp_ip4_to_oid(ip_2_ip4(ip), &oid[2]);
|
|
return 6;
|
|
#else /* LWIP_IPV4 */
|
|
return 0;
|
|
#endif /* LWIP_IPV4 */
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert from InetAddressType+InetAddress to ip_addr_t
|
|
* @param oid OID
|
|
* @param oid_len OID length
|
|
* @param ip IP address
|
|
* @return Parsed OID length
|
|
*/
|
|
u8_t
|
|
snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip)
|
|
{
|
|
/* InetAddressType */
|
|
if (oid_len < 1) {
|
|
return 0;
|
|
}
|
|
|
|
if (oid[0] == 0) { /* any */
|
|
/* 1x InetAddressType, 1x OID len */
|
|
if (oid_len < 2) {
|
|
return 0;
|
|
}
|
|
if (oid[1] != 0) {
|
|
return 0;
|
|
}
|
|
|
|
memset(ip, 0, sizeof(*ip));
|
|
IP_SET_TYPE(ip, IPADDR_TYPE_ANY);
|
|
|
|
return 2;
|
|
} else if (oid[0] == 1) { /* ipv4 */
|
|
#if LWIP_IPV4
|
|
/* 1x InetAddressType, 1x OID len, 4x InetAddressIPv4 */
|
|
if (oid_len < 6) {
|
|
return 0;
|
|
}
|
|
|
|
/* 4x ipv4 OID */
|
|
if (oid[1] != 4) {
|
|
return 0;
|
|
}
|
|
|
|
IP_SET_TYPE(ip, IPADDR_TYPE_V4);
|
|
if (!snmp_oid_to_ip4(&oid[2], ip_2_ip4(ip))) {
|
|
return 0;
|
|
}
|
|
|
|
return 6;
|
|
#else /* LWIP_IPV4 */
|
|
return 0;
|
|
#endif /* LWIP_IPV4 */
|
|
} else if (oid[0] == 2) { /* ipv6 */
|
|
#if LWIP_IPV6
|
|
/* 1x InetAddressType, 1x OID len, 16x InetAddressIPv6 */
|
|
if (oid_len < 18) {
|
|
return 0;
|
|
}
|
|
|
|
/* 16x ipv6 OID */
|
|
if (oid[1] != 16) {
|
|
return 0;
|
|
}
|
|
|
|
IP_SET_TYPE(ip, IPADDR_TYPE_V6);
|
|
if (!snmp_oid_to_ip6(&oid[2], ip_2_ip6(ip))) {
|
|
return 0;
|
|
}
|
|
|
|
return 18;
|
|
#else /* LWIP_IPV6 */
|
|
return 0;
|
|
#endif /* LWIP_IPV6 */
|
|
} else { /* unsupported InetAddressType */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert from InetAddressType+InetAddress+InetPortNumber to ip_addr_t and u16_t
|
|
* @param oid OID
|
|
* @param oid_len OID length
|
|
* @param ip IP address
|
|
* @param port Port
|
|
* @return Parsed OID length
|
|
*/
|
|
u8_t
|
|
snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port)
|
|
{
|
|
u8_t idx = 0;
|
|
|
|
/* InetAddressType + InetAddress */
|
|
idx += snmp_oid_to_ip(&oid[idx], oid_len-idx, ip);
|
|
if (idx == 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* InetPortNumber */
|
|
if (oid_len < (idx+1)) {
|
|
return 0;
|
|
}
|
|
if (oid[idx] > 0xffff) {
|
|
return 0;
|
|
}
|
|
*port = (u16_t)oid[idx];
|
|
idx++;
|
|
|
|
return idx;
|
|
}
|
|
|
|
#endif /* LWIP_IPV4 || LWIP_IPV6 */
|
|
|
|
/**
|
|
* Assign an OID to struct snmp_obj_id
|
|
* @param target Assignment target
|
|
* @param oid OID
|
|
* @param oid_len OID length
|
|
*/
|
|
void
|
|
snmp_oid_assign(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len)
|
|
{
|
|
LWIP_ASSERT("oid_len <= LWIP_SNMP_OBJ_ID_LEN", oid_len <= SNMP_MAX_OBJ_ID_LEN);
|
|
|
|
target->len = oid_len;
|
|
|
|
if (oid_len > 0) {
|
|
MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prefix an OID to OID in struct snmp_obj_id
|
|
* @param target Assignment target to prefix
|
|
* @param oid OID
|
|
* @param oid_len OID length
|
|
*/
|
|
void
|
|
snmp_oid_prefix(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len)
|
|
{
|
|
LWIP_ASSERT("target->len + oid_len <= LWIP_SNMP_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
|
|
|
|
if (oid_len > 0) {
|
|
/* move existing OID to make room at the beginning for OID to insert */
|
|
int i;
|
|
for (i = target->len-1; i>=0; i--) {
|
|
target->id[i + oid_len] = target->id[i];
|
|
}
|
|
|
|
/* paste oid at the beginning */
|
|
MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Combine two OIDs into struct snmp_obj_id
|
|
* @param target Assignmet target
|
|
* @param oid1 OID 1
|
|
* @param oid1_len OID 1 length
|
|
* @param oid2 OID 2
|
|
* @param oid2_len OID 2 length
|
|
*/
|
|
void
|
|
snmp_oid_combine(struct snmp_obj_id* target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
|
|
{
|
|
snmp_oid_assign(target, oid1, oid1_len);
|
|
snmp_oid_append(target, oid2, oid2_len);
|
|
}
|
|
|
|
/**
|
|
* Append OIDs to struct snmp_obj_id
|
|
* @param target Assignment target to append to
|
|
* @param oid OID
|
|
* @param oid_len OID length
|
|
*/
|
|
void
|
|
snmp_oid_append(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len)
|
|
{
|
|
LWIP_ASSERT("offset + oid_len <= LWIP_SNMP_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
|
|
|
|
if (oid_len > 0) {
|
|
MEMCPY(&target->id[target->len], oid, oid_len * sizeof(u32_t));
|
|
target->len += oid_len;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compare two OIDs
|
|
* @param oid1 OID 1
|
|
* @param oid1_len OID 1 length
|
|
* @param oid2 OID 2
|
|
* @param oid2_len OID 2 length
|
|
* @return -1: OID1<OID2 1: OID1 >OID2 0: equal
|
|
*/
|
|
s8_t
|
|
snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
|
|
{
|
|
u8_t level = 0;
|
|
LWIP_ASSERT("'oid1' param must not be NULL or 'oid1_len' param be 0!", (oid1 != NULL) || (oid1_len == 0));
|
|
LWIP_ASSERT("'oid2' param must not be NULL or 'oid2_len' param be 0!", (oid2 != NULL) || (oid2_len == 0));
|
|
|
|
while ((level < oid1_len) && (level < oid2_len)) {
|
|
if (*oid1 < *oid2) {
|
|
return -1;
|
|
}
|
|
if (*oid1 > *oid2) {
|
|
return 1;
|
|
}
|
|
|
|
level++;
|
|
oid1++;
|
|
oid2++;
|
|
}
|
|
|
|
/* common part of both OID's is equal, compare length */
|
|
if (oid1_len < oid2_len) {
|
|
return -1;
|
|
}
|
|
if (oid1_len > oid2_len) {
|
|
return 1;
|
|
}
|
|
|
|
/* they are equal */
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check of two OIDs are equal
|
|
* @param oid1 OID 1
|
|
* @param oid1_len OID 1 length
|
|
* @param oid2 OID 2
|
|
* @param oid2_len OID 2 length
|
|
* @return 1: equal 0: non-equal
|
|
*/
|
|
u8_t
|
|
snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
|
|
{
|
|
return (snmp_oid_compare(oid1, oid1_len, oid2, oid2_len) == 0)? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* Convert netif to interface index
|
|
* @param netif netif
|
|
* @return index
|
|
*/
|
|
u8_t
|
|
netif_to_num(const struct netif *netif)
|
|
{
|
|
u8_t result = 0;
|
|
struct netif *netif_iterator = netif_list;
|
|
|
|
while (netif_iterator != NULL) {
|
|
result++;
|
|
|
|
if (netif_iterator == netif) {
|
|
return result;
|
|
}
|
|
|
|
netif_iterator = netif_iterator->next;
|
|
}
|
|
|
|
LWIP_ASSERT("netif not found in netif_list", 0);
|
|
return 0;
|
|
}
|
|
|
|
static const struct snmp_mib*
|
|
snmp_get_mib_from_oid(const u32_t *oid, u8_t oid_len)
|
|
{
|
|
const u32_t* list_oid;
|
|
const u32_t* searched_oid;
|
|
u8_t i, l;
|
|
|
|
u8_t max_match_len = 0;
|
|
const struct snmp_mib* matched_mib = NULL;
|
|
|
|
LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
|
|
|
|
if (oid_len == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < snmp_num_mibs; i++) {
|
|
LWIP_ASSERT("MIB array not initialized correctly", (snmp_mibs[i] != NULL));
|
|
LWIP_ASSERT("MIB array not initialized correctly - base OID is NULL", (snmp_mibs[i]->base_oid != NULL));
|
|
|
|
if (oid_len >= snmp_mibs[i]->base_oid_len) {
|
|
l = snmp_mibs[i]->base_oid_len;
|
|
list_oid = snmp_mibs[i]->base_oid;
|
|
searched_oid = oid;
|
|
|
|
while (l > 0) {
|
|
if (*list_oid != *searched_oid) {
|
|
break;
|
|
}
|
|
|
|
l--;
|
|
list_oid++;
|
|
searched_oid++;
|
|
}
|
|
|
|
if ((l == 0) && (snmp_mibs[i]->base_oid_len > max_match_len)) {
|
|
max_match_len = snmp_mibs[i]->base_oid_len;
|
|
matched_mib = snmp_mibs[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
return matched_mib;
|
|
}
|
|
|
|
static const struct snmp_mib*
|
|
snmp_get_next_mib(const u32_t *oid, u8_t oid_len)
|
|
{
|
|
u8_t i;
|
|
const struct snmp_mib* next_mib = NULL;
|
|
|
|
LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
|
|
|
|
if (oid_len == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < snmp_num_mibs; i++) {
|
|
if (snmp_mibs[i]->base_oid != NULL) {
|
|
/* check if mib is located behind starting point */
|
|
if (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len, oid, oid_len) > 0) {
|
|
if ((next_mib == NULL) ||
|
|
(snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len,
|
|
next_mib->base_oid, next_mib->base_oid_len) < 0)) {
|
|
next_mib = snmp_mibs[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return next_mib;
|
|
}
|
|
|
|
static const struct snmp_mib*
|
|
snmp_get_mib_between(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
|
|
{
|
|
const struct snmp_mib* next_mib = snmp_get_next_mib(oid1, oid1_len);
|
|
|
|
LWIP_ASSERT("'oid2' param must not be NULL!", (oid2 != NULL));
|
|
LWIP_ASSERT("'oid2_len' param must be greater than 0!", (oid2_len > 0));
|
|
|
|
if (next_mib != NULL) {
|
|
if (snmp_oid_compare(next_mib->base_oid, next_mib->base_oid_len, oid2, oid2_len) < 0) {
|
|
return next_mib;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
u8_t
|
|
snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance* node_instance)
|
|
{
|
|
u8_t result = SNMP_ERR_NOSUCHOBJECT;
|
|
const struct snmp_mib *mib;
|
|
const struct snmp_node *mn = NULL;
|
|
|
|
mib = snmp_get_mib_from_oid(oid, oid_len);
|
|
if (mib != NULL) {
|
|
u8_t oid_instance_len;
|
|
|
|
mn = snmp_mib_tree_resolve_exact(mib, oid, oid_len, &oid_instance_len);
|
|
if ((mn != NULL) && (mn->node_type != SNMP_NODE_TREE)) {
|
|
/* get instance */
|
|
const struct snmp_leaf_node* leaf_node = (const struct snmp_leaf_node*)(const void*)mn;
|
|
|
|
node_instance->node = mn;
|
|
snmp_oid_assign(&node_instance->instance_oid, oid + (oid_len - oid_instance_len), oid_instance_len);
|
|
|
|
result = leaf_node->get_instance(
|
|
oid,
|
|
oid_len - oid_instance_len,
|
|
node_instance);
|
|
|
|
#ifdef LWIP_DEBUG
|
|
if (result == SNMP_ERR_NOERROR) {
|
|
if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
|
|
LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
|
|
}
|
|
if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
|
|
LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value and/or set_test function is specified\n"));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
u8_t
|
|
snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void* validate_node_instance_arg, struct snmp_obj_id* node_oid, struct snmp_node_instance* node_instance)
|
|
{
|
|
const struct snmp_mib *mib;
|
|
const struct snmp_node *mn = NULL;
|
|
const u32_t* start_oid = NULL;
|
|
u8_t start_oid_len = 0;
|
|
|
|
/* resolve target MIB from passed OID */
|
|
mib = snmp_get_mib_from_oid(oid, oid_len);
|
|
if (mib == NULL) {
|
|
/* passed OID does not reference any known MIB, start at the next closest MIB */
|
|
mib = snmp_get_next_mib(oid, oid_len);
|
|
|
|
if (mib != NULL) {
|
|
start_oid = mib->base_oid;
|
|
start_oid_len = mib->base_oid_len;
|
|
}
|
|
} else {
|
|
start_oid = oid;
|
|
start_oid_len = oid_len;
|
|
}
|
|
|
|
/* resolve target node from MIB, skip to next MIB if no suitable node is found in current MIB */
|
|
while ((mib != NULL) && (mn == NULL)) {
|
|
u8_t oid_instance_len;
|
|
|
|
/* check if OID directly references a node inside current MIB, in this case we have to ask this node for the next instance */
|
|
mn = snmp_mib_tree_resolve_exact(mib, start_oid, start_oid_len, &oid_instance_len);
|
|
if (mn != NULL) {
|
|
snmp_oid_assign(node_oid, start_oid, start_oid_len - oid_instance_len); /* set oid to node */
|
|
snmp_oid_assign(&node_instance->instance_oid, start_oid + (start_oid_len - oid_instance_len), oid_instance_len); /* set (relative) instance oid */
|
|
} else {
|
|
/* 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 */
|
|
mn = snmp_mib_tree_resolve_next(mib, start_oid, start_oid_len, node_oid);
|
|
node_instance->instance_oid.len = 0;
|
|
}
|
|
|
|
/* 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 */
|
|
node_instance->node = mn;
|
|
while (mn != NULL) {
|
|
u8_t result;
|
|
|
|
/* clear fields which may have values from previous loops */
|
|
node_instance->asn1_type = 0;
|
|
node_instance->access = SNMP_NODE_INSTANCE_NOT_ACCESSIBLE;
|
|
node_instance->get_value = NULL;
|
|
node_instance->set_test = NULL;
|
|
node_instance->set_value = NULL;
|
|
node_instance->release_instance = NULL;
|
|
node_instance->reference.ptr = NULL;
|
|
node_instance->reference_len = 0;
|
|
|
|
result = ((const struct snmp_leaf_node*)(const void*)mn)->get_next_instance(
|
|
node_oid->id,
|
|
node_oid->len,
|
|
node_instance);
|
|
|
|
if (result == SNMP_ERR_NOERROR) {
|
|
#ifdef LWIP_DEBUG
|
|
if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
|
|
LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
|
|
}
|
|
if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
|
|
LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value function is specified\n"));
|
|
}
|
|
#endif
|
|
|
|
/* validate node because the node may be not accessible for example (but let the caller decide what is valid */
|
|
if ((validate_node_instance_method == NULL) ||
|
|
(validate_node_instance_method(node_instance, validate_node_instance_arg) == SNMP_ERR_NOERROR)) {
|
|
/* node_oid "returns" the full result OID (including the instance part) */
|
|
snmp_oid_append(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
|
|
break;
|
|
}
|
|
|
|
if (node_instance->release_instance != NULL) {
|
|
node_instance->release_instance(node_instance);
|
|
}
|
|
/*
|
|
the instance itself is not valid, ask for next instance from same node.
|
|
we don't have to change any variables because node_instance->instance_oid is used as input (starting point)
|
|
as well as output (resulting next OID), so we have to simply call get_next_instance method again
|
|
*/
|
|
} else {
|
|
if (node_instance->release_instance != NULL) {
|
|
node_instance->release_instance(node_instance);
|
|
}
|
|
|
|
/* the node has no further instance, skip to next node */
|
|
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 */
|
|
if (mn != NULL) {
|
|
/* prepare for next loop */
|
|
snmp_oid_assign(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
|
|
node_instance->instance_oid.len = 0;
|
|
node_instance->node = mn;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mn != NULL) {
|
|
/*
|
|
we found a suitable next node,
|
|
now we have to check if a inner MIB is located between the searched OID and the resulting OID.
|
|
this is possible because MIB's may be located anywhere in the global tree, that means also in
|
|
the subtree of another MIB (e.g. if searched OID is .2 and resulting OID is .4, then another
|
|
MIB having .3 as root node may exist)
|
|
*/
|
|
const struct snmp_mib *intermediate_mib;
|
|
intermediate_mib = snmp_get_mib_between(start_oid, start_oid_len, node_oid->id, node_oid->len);
|
|
|
|
if (intermediate_mib != NULL) {
|
|
/* search for first node inside intermediate mib in next loop */
|
|
if (node_instance->release_instance != NULL) {
|
|
node_instance->release_instance(node_instance);
|
|
}
|
|
|
|
mn = NULL;
|
|
mib = intermediate_mib;
|
|
start_oid = mib->base_oid;
|
|
start_oid_len = mib->base_oid_len;
|
|
}
|
|
/* else { we found out target node } */
|
|
} else {
|
|
/*
|
|
there is no further (suitable) node inside this MIB, search for the next MIB with following priority
|
|
1. search for inner MIB's (whose root is located inside tree of current MIB)
|
|
2. search for surrouding MIB's (where the current MIB is the inner MIB) and continue there if any
|
|
3. take the next closest MIB (not being related to the current MIB)
|
|
*/
|
|
const struct snmp_mib *next_mib;
|
|
next_mib = snmp_get_next_mib(start_oid, start_oid_len); /* returns MIB's related to point 1 and 3 */
|
|
|
|
/* is the found MIB an inner MIB? (point 1) */
|
|
if ((next_mib != NULL) && (next_mib->base_oid_len > mib->base_oid_len) &&
|
|
(snmp_oid_compare(next_mib->base_oid, mib->base_oid_len, mib->base_oid, mib->base_oid_len) == 0)) {
|
|
/* yes it is -> continue at inner MIB */
|
|
mib = next_mib;
|
|
start_oid = mib->base_oid;
|
|
start_oid_len = mib->base_oid_len;
|
|
} else {
|
|
/* check if there is a surrounding mib where to continue (point 2) (only possible if OID length > 1) */
|
|
if (mib->base_oid_len > 1) {
|
|
mib = snmp_get_mib_from_oid(mib->base_oid, mib->base_oid_len - 1);
|
|
|
|
if (mib == NULL) {
|
|
/* no surrounding mib, use next mib encountered above (point 3) */
|
|
mib = next_mib;
|
|
|
|
if (mib != NULL) {
|
|
start_oid = mib->base_oid;
|
|
start_oid_len = mib->base_oid_len;
|
|
}
|
|
}
|
|
/* else { start_oid stays the same because we want to continue from current offset in surrounding mib (point 2) } */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mib == NULL) {
|
|
/* loop is only left when mib == null (error) or mib_node != NULL (success) */
|
|
return SNMP_ERR_ENDOFMIBVIEW;
|
|
}
|
|
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
|
|
/**
|
|
* Searches tree for the supplied object identifier.
|
|
*
|
|
*/
|
|
const struct snmp_node *
|
|
snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t* oid_instance_len)
|
|
{
|
|
const struct snmp_node* const* node = &mib->root_node;
|
|
u8_t oid_offset = mib->base_oid_len;
|
|
|
|
while ((oid_offset < oid_len) && ((*node)->node_type == SNMP_NODE_TREE)) {
|
|
/* search for matching sub node */
|
|
u32_t subnode_oid = *(oid + oid_offset);
|
|
|
|
u32_t i = (*(const struct snmp_tree_node* const*)node)->subnode_count;
|
|
node = (*(const struct snmp_tree_node* const*)node)->subnodes;
|
|
while ((i > 0) && ((*node)->oid != subnode_oid)) {
|
|
node++;
|
|
i--;
|
|
}
|
|
|
|
if (i == 0) {
|
|
/* no matching subnode found */
|
|
return NULL;
|
|
}
|
|
|
|
oid_offset++;
|
|
}
|
|
|
|
if ((*node)->node_type != SNMP_NODE_TREE) {
|
|
/* we found a leaf node */
|
|
*oid_instance_len = oid_len - oid_offset;
|
|
return (*node);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const struct snmp_node*
|
|
snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id* oidret)
|
|
{
|
|
u8_t oid_offset = mib->base_oid_len;
|
|
const struct snmp_node* const* node;
|
|
const struct snmp_tree_node* node_stack[SNMP_MAX_OBJ_ID_LEN];
|
|
s32_t nsi = 0; /* NodeStackIndex */
|
|
u32_t subnode_oid;
|
|
|
|
if (mib->root_node->node_type != SNMP_NODE_TREE) {
|
|
/* a next operation on a mib with only a leaf node will always return NULL because there is no other node */
|
|
return NULL;
|
|
}
|
|
|
|
/* first build node stack related to passed oid (as far as possible), then go backwards to determine the next node */
|
|
node_stack[nsi] = (const struct snmp_tree_node*)(const void*)mib->root_node;
|
|
while (oid_offset < oid_len) {
|
|
/* search for matching sub node */
|
|
u32_t i = node_stack[nsi]->subnode_count;
|
|
node = node_stack[nsi]->subnodes;
|
|
|
|
subnode_oid = *(oid + oid_offset);
|
|
|
|
while ((i > 0) && ((*node)->oid != subnode_oid)) {
|
|
node++;
|
|
i--;
|
|
}
|
|
|
|
if ((i == 0) || ((*node)->node_type != SNMP_NODE_TREE)) {
|
|
/* no (matching) tree-subnode found */
|
|
break;
|
|
}
|
|
nsi++;
|
|
node_stack[nsi] = (const struct snmp_tree_node*)(const void*)(*node);
|
|
|
|
oid_offset++;
|
|
}
|
|
|
|
|
|
if (oid_offset >= oid_len) {
|
|
/* passed oid references a tree node -> return first useable sub node of it */
|
|
subnode_oid = 0;
|
|
} else {
|
|
subnode_oid = *(oid + oid_offset) + 1;
|
|
}
|
|
|
|
while (nsi >= 0) {
|
|
const struct snmp_node* subnode = NULL;
|
|
|
|
/* find next node on current level */
|
|
s32_t i = node_stack[nsi]->subnode_count;
|
|
node = node_stack[nsi]->subnodes;
|
|
while (i > 0) {
|
|
if ((*node)->oid == subnode_oid) {
|
|
subnode = *node;
|
|
break;
|
|
} else if (((*node)->oid > subnode_oid) && ((subnode == NULL) || ((*node)->oid < subnode->oid))) {
|
|
subnode = *node;
|
|
}
|
|
|
|
node++;
|
|
i--;
|
|
}
|
|
|
|
if (subnode == NULL) {
|
|
/* no further node found on this level, go one level up and start searching with index of current node*/
|
|
subnode_oid = node_stack[nsi]->node.oid + 1;
|
|
nsi--;
|
|
} else {
|
|
if (subnode->node_type == SNMP_NODE_TREE) {
|
|
/* next is a tree node, go into it and start searching */
|
|
nsi++;
|
|
node_stack[nsi] = (const struct snmp_tree_node*)(const void*)subnode;
|
|
subnode_oid = 0;
|
|
} else {
|
|
/* we found a leaf node -> fill oidret and return it */
|
|
snmp_oid_assign(oidret, mib->base_oid, mib->base_oid_len);
|
|
i = 1;
|
|
while (i <= nsi) {
|
|
oidret->id[oidret->len] = node_stack[i]->node.oid;
|
|
oidret->len++;
|
|
i++;
|
|
}
|
|
|
|
oidret->id[oidret->len] = subnode->oid;
|
|
oidret->len++;
|
|
|
|
return subnode;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/** initialize struct next_oid_state using this function before passing it to next_oid_check */
|
|
void
|
|
snmp_next_oid_init(struct snmp_next_oid_state *state,
|
|
const u32_t *start_oid, u8_t start_oid_len,
|
|
u32_t *next_oid_buf, u8_t next_oid_max_len)
|
|
{
|
|
state->start_oid = start_oid;
|
|
state->start_oid_len = start_oid_len;
|
|
state->next_oid = next_oid_buf;
|
|
state->next_oid_len = 0;
|
|
state->next_oid_max_len = next_oid_max_len;
|
|
state->status = SNMP_NEXT_OID_STATUS_NO_MATCH;
|
|
}
|
|
|
|
/** checks if the passed incomplete OID may be a possible candidate for snmp_next_oid_check();
|
|
this methid is intended if the complete OID is not yet known but it is very expensive to build it up,
|
|
so it is possible to test the starting part before building up the complete oid and pass it to snmp_next_oid_check()*/
|
|
u8_t
|
|
snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len)
|
|
{
|
|
if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
|
|
u8_t start_oid_len = (oid_len < state->start_oid_len) ? oid_len : state->start_oid_len;
|
|
|
|
/* check passed OID is located behind start offset */
|
|
if (snmp_oid_compare(oid, oid_len, state->start_oid, start_oid_len) >= 0) {
|
|
/* check if new oid is located closer to start oid than current closest oid */
|
|
if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
|
|
(snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** 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 */
|
|
u8_t
|
|
snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len, void* reference)
|
|
{
|
|
/* do not overwrite a fail result */
|
|
if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
|
|
/* check passed OID is located behind start offset */
|
|
if (snmp_oid_compare(oid, oid_len, state->start_oid, state->start_oid_len) > 0) {
|
|
/* check if new oid is located closer to start oid than current closest oid */
|
|
if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
|
|
(snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
|
|
if (oid_len <= state->next_oid_max_len) {
|
|
MEMCPY(state->next_oid, oid, oid_len * sizeof(u32_t));
|
|
state->next_oid_len = oid_len;
|
|
state->status = SNMP_NEXT_OID_STATUS_SUCCESS;
|
|
state->reference = reference;
|
|
return 1;
|
|
} else {
|
|
state->status = SNMP_NEXT_OID_STATUS_BUF_TO_SMALL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
u8_t
|
|
snmp_oid_in_range(const u32_t *oid_in, u8_t oid_len, const struct snmp_oid_range *oid_ranges, u8_t oid_ranges_len)
|
|
{
|
|
u8_t i;
|
|
|
|
if (oid_len != oid_ranges_len) {
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < oid_ranges_len; i++) {
|
|
if ((oid_in[i] < oid_ranges[i].min) || (oid_in[i] > oid_ranges[i].max)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
snmp_err_t
|
|
snmp_set_test_ok(struct snmp_node_instance* instance, u16_t value_len, void* value)
|
|
{
|
|
LWIP_UNUSED_ARG(instance);
|
|
LWIP_UNUSED_ARG(value_len);
|
|
LWIP_UNUSED_ARG(value);
|
|
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
|
|
/**
|
|
* Decodes BITS pseudotype value from ASN.1 OctetString.
|
|
*
|
|
* @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
|
|
* be encoded/decoded by the agent. Instead call this function as required from
|
|
* get/test/set methods.
|
|
*
|
|
* @param buf points to a buffer holding the ASN1 octet string
|
|
* @param buf_len length of octet string
|
|
* @param bit_value decoded Bit value with Bit0 == LSB
|
|
* @return ERR_OK if successful, ERR_ARG if bit value contains more than 32 bit
|
|
*/
|
|
err_t
|
|
snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value)
|
|
{
|
|
u8_t b;
|
|
u8_t bits_processed = 0;
|
|
*bit_value = 0;
|
|
|
|
while (buf_len > 0) {
|
|
/* any bit set in this byte? */
|
|
if (*buf != 0x00) {
|
|
if (bits_processed >= 32) {
|
|
/* accept more than 4 bytes, but only when no bits are set */
|
|
return ERR_VAL;
|
|
}
|
|
|
|
b = *buf;
|
|
do {
|
|
if (b & 0x80) {
|
|
*bit_value |= (1 << bits_processed);
|
|
}
|
|
bits_processed++;
|
|
b <<= 1;
|
|
}
|
|
while ((bits_processed & 0x07) != 0); /* &0x07 -> % 8 */
|
|
} else {
|
|
bits_processed += 8;
|
|
}
|
|
|
|
buf_len--;
|
|
buf++;
|
|
}
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
err_t
|
|
snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value)
|
|
{
|
|
/* defined by RFC1443:
|
|
TruthValue ::= TEXTUAL-CONVENTION
|
|
STATUS current
|
|
DESCRIPTION
|
|
"Represents a boolean value."
|
|
SYNTAX INTEGER { true(1), false(2) }
|
|
*/
|
|
|
|
if ((asn1_value == NULL) || (bool_value == NULL)) {
|
|
return ERR_ARG;
|
|
}
|
|
|
|
if (*asn1_value == 1) {
|
|
*bool_value = 1;
|
|
} else if (*asn1_value == 2) {
|
|
*bool_value = 0;
|
|
} else {
|
|
return ERR_VAL;
|
|
}
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
/**
|
|
* Encodes BITS pseudotype value into ASN.1 OctetString.
|
|
*
|
|
* @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
|
|
* be encoded/decoded by the agent. Instead call this function as required from
|
|
* get/test/set methods.
|
|
*
|
|
* @param buf points to a buffer where the resulting ASN1 octet string is stored to
|
|
* @param buf_len max length of the bufffer
|
|
* @param bit_value Bit value to encode with Bit0 == LSB
|
|
* @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)
|
|
* @return number of bytes used from buffer to store the resulting OctetString
|
|
*/
|
|
u8_t
|
|
snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count)
|
|
{
|
|
u8_t len = 0;
|
|
u8_t min_bytes = (bit_count + 7) >> 3; /* >>3 -> / 8 */
|
|
|
|
while ((buf_len > 0) && (bit_value != 0x00)) {
|
|
s8_t i = 7;
|
|
*buf = 0x00;
|
|
while (i >= 0) {
|
|
if (bit_value & 0x01) {
|
|
*buf |= 0x01;
|
|
}
|
|
|
|
if (i > 0) {
|
|
*buf <<= 1;
|
|
}
|
|
|
|
bit_value >>= 1;
|
|
i--;
|
|
}
|
|
|
|
buf++;
|
|
buf_len--;
|
|
len++;
|
|
}
|
|
|
|
if (len < min_bytes) {
|
|
buf += len;
|
|
buf_len -= len;
|
|
|
|
while ((len < min_bytes) && (buf_len > 0)) {
|
|
*buf = 0x00;
|
|
buf++;
|
|
buf_len--;
|
|
len++;
|
|
}
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
u8_t
|
|
snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value)
|
|
{
|
|
/* defined by RFC1443:
|
|
TruthValue ::= TEXTUAL-CONVENTION
|
|
STATUS current
|
|
DESCRIPTION
|
|
"Represents a boolean value."
|
|
SYNTAX INTEGER { true(1), false(2) }
|
|
*/
|
|
|
|
if (asn1_value == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (bool_value) {
|
|
*asn1_value = 1; /* defined by RFC1443 */
|
|
} else {
|
|
*asn1_value = 2; /* defined by RFC1443 */
|
|
}
|
|
|
|
return sizeof(s32_t);
|
|
}
|
|
|
|
#endif /* LWIP_SNMP */
|