SNMP agent rewrite

- SNMPv2c support
  - Greatly reduced RAM usage, no memory pools any more
  - API cleanup
  - MIB2 is separated from SNMP stack
  - Support for multiple MIBs (snmp_set_mibs call) - e.g. for private MIB
  - Improved MIB2 implementation (tcpConnTable etc.)
  - Redesigned simple and generic API for MIB implementation
  - Comfortable node types for scalar arrays and tables
  - Counter64, bit and truthvalue datatype support
  - Callbacks for SNMP writes
  - 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 without locking
    (used in MIB2 to access lwIP stats from lwIP thread)

Currently in work:
  - Traps rewrite
  - MIB compiler
This commit is contained in:
Dirk Ziegelmeier
2015-12-26 11:43:29 +01:00
parent 0178d1d2ee
commit 7b946fa9b1
32 changed files with 8034 additions and 8176 deletions

30
src/apps/snmp/README Normal file
View File

@@ -0,0 +1,30 @@
lwIP SNMPv2c agent
==================
Based on SNMP stack written by Christiaan Simons <christiaan.simons@axon.tv>
Rewritten by Martin Hentschel <info@cl-soft.de> and
Dirk Ziegelmeier <dziegel@gmx.de>
Changes:
- SNMPv2c support
- Greatly reduced RAM usage, no memory pools any more
- API cleanup
- MIB2 is separated from SNMP stack
- Support for multiple MIBs (snmp_set_mibs call) - e.g. for private MIB
- Improved MIB2 implementation (tcpConnTable etc.)
- Redesigned simple and generic API for MIB implementation
- Comfortable node types for scalar arrays and tables
- Counter64, bit and truthvalue datatype support
- Callbacks for SNMP writes
- 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 without locking
(used in MIB2 to access lwIP stats from lwIP thread)
Currently in work:
- Traps rewrite
- MIB compiler

View File

@@ -1,614 +0,0 @@
/**
* @file
* Abstract Syntax Notation One (ISO 8824, 8825) decoding
*
* @todo not optimised (yet), favor correctness over speed, favor speed over size
*/
/*
* 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>
*/
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
#include "lwip/apps/snmp_asn1.h"
/**
* Retrieves type field from incoming pbuf chain.
*
* @param p points to a pbuf holding an ASN1 coded type field
* @param ofs points to the offset within the pbuf chain of the ASN1 coded type field
* @param type return ASN1 type
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
*/
err_t
snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type)
{
u16_t plen, base;
u8_t *msg_ptr;
plen = 0;
while (p != NULL) {
base = plen;
plen += p->len;
if (ofs < plen) {
msg_ptr = (u8_t*)p->payload;
msg_ptr += ofs - base;
*type = *msg_ptr;
return ERR_OK;
}
p = p->next;
}
/* p == NULL, ofs >= plen */
return ERR_ARG;
}
/**
* Decodes length field from incoming pbuf chain into host length.
*
* @param p points to a pbuf holding an ASN1 coded length
* @param ofs points to the offset within the pbuf chain of the ASN1 coded length
* @param octets_used returns number of octets used by the length code
* @param length return host order length, up to 64k
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
*/
err_t
snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length)
{
u16_t plen, base;
u8_t *msg_ptr;
plen = 0;
while (p != NULL) {
base = plen;
plen += p->len;
if (ofs < plen) {
msg_ptr = (u8_t*)p->payload;
msg_ptr += ofs - base;
if (*msg_ptr < 0x80) {
/* primitive definite length format */
*octets_used = 1;
*length = *msg_ptr;
return ERR_OK;
} else if (*msg_ptr == 0x80) {
/* constructed indefinite length format, termination with two zero octets */
u8_t zeros;
u8_t i;
*length = 0;
zeros = 0;
while (zeros != 2) {
i = 2;
while (i > 0) {
i--;
(*length) += 1;
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
if (*msg_ptr == 0) {
zeros++;
if (zeros == 2) {
/* stop while (i > 0) */
i = 0;
}
} else {
zeros = 0;
}
}
}
*octets_used = 1;
return ERR_OK;
} else if (*msg_ptr == 0x81) {
/* constructed definite length format, one octet */
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
*length = *msg_ptr;
*octets_used = 2;
return ERR_OK;
} else if (*msg_ptr == 0x82) {
u8_t i;
/* constructed definite length format, two octets */
i = 2;
while (i > 0) {
i--;
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
if (i == 0) {
/* least significant length octet */
*length |= *msg_ptr;
} else {
/* most significant length octet */
*length = (*msg_ptr) << 8;
}
}
*octets_used = 3;
return ERR_OK;
} else {
/* constructed definite length format 3..127 octets, this is too big (>64k) */
/** @todo: do we need to accept inefficient codings with many leading zero's? */
*octets_used = 1 + ((*msg_ptr) & 0x7f);
return ERR_ARG;
}
}
p = p->next;
}
/* p == NULL, ofs >= plen */
return ERR_ARG;
}
/**
* Decodes positive integer (counter, gauge, timeticks) into u32_t.
*
* @param p points to a pbuf holding an ASN1 coded integer
* @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
* @param len length of the coded integer field
* @param value return host order integer
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
*
* @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
* as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
* of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
*/
err_t
snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value)
{
u16_t plen, base;
u8_t *msg_ptr;
plen = 0;
while (p != NULL) {
base = plen;
plen += p->len;
if (ofs < plen) {
msg_ptr = (u8_t*)p->payload;
msg_ptr += ofs - base;
if ((len > 0) && (len < 6)) {
/* start from zero */
*value = 0;
if (*msg_ptr & 0x80) {
/* negative, expecting zero sign bit! */
return ERR_ARG;
} else {
/* positive */
if ((len > 1) && (*msg_ptr == 0)) {
/* skip leading "sign byte" octet 0x00 */
len--;
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
}
}
/* OR octets with value */
while (len > 1) {
len--;
*value |= *msg_ptr;
*value <<= 8;
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
}
*value |= *msg_ptr;
return ERR_OK;
} else {
return ERR_ARG;
}
}
p = p->next;
}
/* p == NULL, ofs >= plen */
return ERR_ARG;
}
/**
* Decodes integer into s32_t.
*
* @param p points to a pbuf holding an ASN1 coded integer
* @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
* @param len length of the coded integer field
* @param value return host order integer
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
*
* @note ASN coded integers are _always_ signed!
*/
err_t
snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value)
{
u16_t plen, base;
u8_t *msg_ptr;
#if BYTE_ORDER == LITTLE_ENDIAN
u8_t *lsb_ptr = (u8_t*)value;
#endif
#if BYTE_ORDER == BIG_ENDIAN
u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
#endif
u8_t sign;
plen = 0;
while (p != NULL) {
base = plen;
plen += p->len;
if (ofs < plen) {
msg_ptr = (u8_t*)p->payload;
msg_ptr += ofs - base;
if ((len > 0) && (len < 5)) {
if (*msg_ptr & 0x80) {
/* negative, start from -1 */
*value = -1;
sign = 1;
} else {
/* positive, start from 0 */
*value = 0;
sign = 0;
}
/* OR/AND octets with value */
while (len > 1) {
len--;
if (sign) {
*lsb_ptr &= *msg_ptr;
*value <<= 8;
*lsb_ptr |= 255;
} else {
*lsb_ptr |= *msg_ptr;
*value <<= 8;
}
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
}
if (sign) {
*lsb_ptr &= *msg_ptr;
} else {
*lsb_ptr |= *msg_ptr;
}
return ERR_OK;
} else {
return ERR_ARG;
}
}
p = p->next;
}
/* p == NULL, ofs >= plen */
return ERR_ARG;
}
/**
* Decodes object identifier from incoming message into array of s32_t.
*
* @param p points to a pbuf holding an ASN1 coded object identifier
* @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier
* @param len length of the coded object identifier
* @param oid return object identifier struct
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
*/
err_t
snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid)
{
u16_t plen, base;
u8_t *msg_ptr;
s32_t *oid_ptr;
plen = 0;
while (p != NULL) {
base = plen;
plen += p->len;
if (ofs < plen) {
msg_ptr = (u8_t*)p->payload;
msg_ptr += ofs - base;
oid->len = 0;
oid_ptr = &oid->id[0];
if (len > 0) {
/* first compressed octet */
if (*msg_ptr == 0x2B) {
/* (most) common case 1.3 (iso.org) */
*oid_ptr = 1;
oid_ptr++;
*oid_ptr = 3;
oid_ptr++;
} else if (*msg_ptr < 40) {
*oid_ptr = 0;
oid_ptr++;
*oid_ptr = *msg_ptr;
oid_ptr++;
} else if (*msg_ptr < 80) {
*oid_ptr = 1;
oid_ptr++;
*oid_ptr = (*msg_ptr) - 40;
oid_ptr++;
} else {
*oid_ptr = 2;
oid_ptr++;
*oid_ptr = (*msg_ptr) - 80;
oid_ptr++;
}
oid->len = 2;
} else {
/* accepting zero length identifiers e.g. for
getnext operation. uncommon but valid */
return ERR_OK;
}
len--;
if (len > 0) {
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
}
while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN)) {
/* sub-identifier uses multiple octets */
if (*msg_ptr & 0x80) {
s32_t sub_id = 0;
while ((*msg_ptr & 0x80) && (len > 1)) {
len--;
sub_id = (sub_id << 7) + (*msg_ptr & ~0x80);
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
}
if (!(*msg_ptr & 0x80) && (len > 0)) {
/* last octet sub-identifier */
len--;
sub_id = (sub_id << 7) + *msg_ptr;
*oid_ptr = sub_id;
}
} else {
/* !(*msg_ptr & 0x80) sub-identifier uses single octet */
len--;
*oid_ptr = *msg_ptr;
}
if (len > 0) {
/* remaining oid bytes available ... */
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
}
oid_ptr++;
oid->len++;
}
if (len == 0) {
/* len == 0, end of oid */
return ERR_OK;
} else {
/* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */
return ERR_ARG;
}
}
p = p->next;
}
/* p == NULL, ofs >= plen */
return ERR_ARG;
}
/**
* Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
* from incoming message into array.
*
* @param p points to a pbuf holding an ASN1 coded raw data
* @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data
* @param len length of the coded raw data (zero is valid, e.g. empty string!)
* @param raw_len length of the raw return value
* @param raw return raw bytes
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
*/
err_t
snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw)
{
u16_t plen, base;
u8_t *msg_ptr;
if (len > 0) {
plen = 0;
while (p != NULL) {
base = plen;
plen += p->len;
if (ofs < plen) {
msg_ptr = (u8_t*)p->payload;
msg_ptr += ofs - base;
if (raw_len >= len) {
while (len > 1) {
/* copy len - 1 octets */
len--;
*raw = *msg_ptr;
raw++;
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
}
/* copy last octet */
*raw = *msg_ptr;
return ERR_OK;
} else {
/* raw_len < len, not enough dst space */
return ERR_ARG;
}
}
p = p->next;
}
/* p == NULL, ofs >= plen */
return ERR_ARG;
} else {
/* len == 0, empty string */
return ERR_OK;
}
}
/**
* 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_asn1_dec_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_ARG;
}
b = *buf;
do {
if (b & 0x80) {
*bit_value |= (1 << bits_processed);
}
bits_processed++;
b <<= 1;
}
while ((bits_processed % 8) != 0);
} else {
bits_processed += 8;
}
buf_len--;
buf++;
}
return ERR_OK;
}
#endif /* LWIP_SNMP */

View File

@@ -1,580 +0,0 @@
/**
* @file
* Abstract Syntax Notation One (ISO 8824, 8825) encoding
*
* @todo not optimised (yet), favor correctness over speed, favor speed over size
*/
/*
* 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>
*/
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
#include "lwip/apps/snmp_asn1.h"
/**
* Returns octet count for length.
*
* @param length
* @param octets_needed points to the return value
*/
void
snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
{
if (length < 0x80U) {
*octets_needed = 1;
} else if (length < 0x100U) {
*octets_needed = 2;
} else {
*octets_needed = 3;
}
}
/**
* Returns octet count for an u32_t.
*
* @param value
* @param octets_needed points to the return value
*
* @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
* as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
* of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
*/
void
snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
{
if (value < 0x80UL) {
*octets_needed = 1;
} else if (value < 0x8000UL) {
*octets_needed = 2;
} else if (value < 0x800000UL) {
*octets_needed = 3;
} else if (value < 0x80000000UL) {
*octets_needed = 4;
} else {
*octets_needed = 5;
}
}
/**
* Returns octet count for an s32_t.
*
* @param value
* @param octets_needed points to the return value
*
* @note ASN coded integers are _always_ signed.
*/
void
snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
{
if (value < 0) {
value = ~value;
} if (value < 0x80L) {
*octets_needed = 1;
} else if (value < 0x8000L) {
*octets_needed = 2;
} else if (value < 0x800000L) {
*octets_needed = 3;
} else {
*octets_needed = 4;
}
}
/**
* Returns octet count for an object identifier.
*
* @param ident_len object identifier array length
* @param ident points to object identifier array
* @param octets_needed points to the return value
*/
void
snmp_asn1_enc_oid_cnt(u16_t ident_len, const s32_t *ident, u16_t *octets_needed)
{
s32_t sub_id;
u8_t cnt;
cnt = 0;
if (ident_len > 1) {
/* compressed prefix in one octet */
cnt++;
ident_len -= 2;
ident += 2;
}
while (ident_len > 0) {
ident_len--;
sub_id = *ident;
sub_id >>= 7;
cnt++;
while (sub_id > 0) {
sub_id >>= 7;
cnt++;
}
ident++;
}
*octets_needed = cnt;
}
/**
* Encodes ASN type field into a pbuf chained ASN1 msg.
*
* @param p points to output pbuf to encode value into
* @param ofs points to the offset within the pbuf chain
* @param type input ASN1 type
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
*/
err_t
snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type)
{
u16_t plen, base;
u8_t *msg_ptr;
plen = 0;
while (p != NULL) {
base = plen;
plen += p->len;
if (ofs < plen) {
msg_ptr = (u8_t*)p->payload;
msg_ptr += ofs - base;
*msg_ptr = type;
return ERR_OK;
}
p = p->next;
}
/* p == NULL, ofs >= plen */
return ERR_ARG;
}
/**
* Encodes host order length field into a pbuf chained ASN1 msg.
*
* @param p points to output pbuf to encode length into
* @param ofs points to the offset within the pbuf chain
* @param length is the host order length to be encoded
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
*/
err_t
snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length)
{
u16_t plen, base;
u8_t *msg_ptr;
plen = 0;
while (p != NULL) {
base = plen;
plen += p->len;
if (ofs < plen) {
msg_ptr = (u8_t*)p->payload;
msg_ptr += ofs - base;
if (length < 0x80) {
*msg_ptr = (u8_t)length;
return ERR_OK;
} else if (length < 0x100) {
*msg_ptr = 0x81;
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
*msg_ptr = (u8_t)length;
return ERR_OK;
} else {
u8_t i;
/* length >= 0x100 && length <= 0xFFFF */
*msg_ptr = 0x82;
i = 2;
while (i > 0) {
i--;
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
if (i == 0) {
/* least significant length octet */
*msg_ptr = (u8_t)length;
} else {
/* most significant length octet */
*msg_ptr = (u8_t)(length >> 8);
}
}
return ERR_OK;
}
}
p = p->next;
}
/* p == NULL, ofs >= plen */
return ERR_ARG;
}
/**
* Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
*
* @param p points to output pbuf to encode value into
* @param ofs points to the offset within the pbuf chain
* @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
* @param value is the host order u32_t value to be encoded
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
*
* @see snmp_asn1_enc_u32t_cnt()
*/
err_t
snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value)
{
u16_t plen, base;
u8_t *msg_ptr;
plen = 0;
while (p != NULL) {
base = plen;
plen += p->len;
if (ofs < plen) {
msg_ptr = (u8_t*)p->payload;
msg_ptr += ofs - base;
if (octets_needed == 5) {
/* not enough bits in 'value' add leading 0x00 */
octets_needed--;
*msg_ptr = 0x00;
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
}
while (octets_needed > 1) {
octets_needed--;
*msg_ptr = (u8_t)(value >> (octets_needed << 3));
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
}
/* (only) one least significant octet */
*msg_ptr = (u8_t)value;
return ERR_OK;
}
p = p->next;
}
/* p == NULL, ofs >= plen */
return ERR_ARG;
}
/**
* Encodes s32_t integer into a pbuf chained ASN1 msg.
*
* @param p points to output pbuf to encode value into
* @param ofs points to the offset within the pbuf chain
* @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
* @param value is the host order s32_t value to be encoded
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
*
* @see snmp_asn1_enc_s32t_cnt()
*/
err_t
snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value)
{
u16_t plen, base;
u8_t *msg_ptr;
plen = 0;
while (p != NULL) {
base = plen;
plen += p->len;
if (ofs < plen) {
msg_ptr = (u8_t*)p->payload;
msg_ptr += ofs - base;
while (octets_needed > 1) {
octets_needed--;
*msg_ptr = (u8_t)(value >> (octets_needed << 3));
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
}
/* (only) one least significant octet */
*msg_ptr = (u8_t)value;
return ERR_OK;
}
p = p->next;
}
/* p == NULL, ofs >= plen */
return ERR_ARG;
}
/**
* Encodes object identifier into a pbuf chained ASN1 msg.
*
* @param p points to output pbuf to encode oid into
* @param ofs points to the offset within the pbuf chain
* @param ident_len object identifier array length
* @param ident points to object identifier array
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
*/
err_t
snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u16_t ident_len, const s32_t *ident)
{
u16_t plen, base;
u8_t *msg_ptr;
plen = 0;
while (p != NULL) {
base = plen;
plen += p->len;
if (ofs < plen) {
msg_ptr = (u8_t*)p->payload;
msg_ptr += ofs - base;
if (ident_len > 1) {
if ((ident[0] == 1) && (ident[1] == 3)) {
/* compressed (most common) prefix .iso.org */
*msg_ptr = 0x2b;
} else {
/* calculate prefix */
*msg_ptr = (u8_t)((ident[0] * 40) + ident[1]);
}
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
ident_len -= 2;
ident += 2;
} else {
/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
/* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
return ERR_ARG;
}
while (ident_len > 0) {
s32_t sub_id;
u8_t shift, tail;
ident_len--;
sub_id = *ident;
tail = 0;
shift = 28;
while (shift > 0) {
u8_t code;
code = (u8_t)(sub_id >> shift);
if ((code != 0) || (tail != 0)) {
tail = 1;
*msg_ptr = code | 0x80;
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
}
shift -= 7;
}
*msg_ptr = (u8_t)sub_id & 0x7F;
if (ident_len > 0) {
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
}
/* proceed to next sub-identifier */
ident++;
}
return ERR_OK;
}
p = p->next;
}
/* p == NULL, ofs >= plen */
return ERR_ARG;
}
/**
* Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
*
* @param p points to output pbuf to encode raw data into
* @param ofs points to the offset within the pbuf chain
* @param raw_len raw data length
* @param raw points raw data
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
*/
err_t
snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, const u8_t *raw)
{
u16_t plen, base;
u8_t *msg_ptr;
plen = 0;
while (p != NULL) {
base = plen;
plen += p->len;
if (ofs < plen) {
msg_ptr = (u8_t*)p->payload;
msg_ptr += ofs - base;
while (raw_len > 1) {
/* copy raw_len - 1 octets */
raw_len--;
*msg_ptr = *raw;
raw++;
ofs += 1;
if (ofs >= plen) {
/* next octet in next pbuf */
p = p->next;
if (p == NULL) {
return ERR_ARG;
}
msg_ptr = (u8_t*)p->payload;
plen += p->len;
} else {
/* next octet in same pbuf */
msg_ptr++;
}
}
if (raw_len > 0) {
/* copy last or single octet */
*msg_ptr = *raw;
}
return ERR_OK;
}
p = p->next;
}
/* p == NULL, ofs >= plen */
return ERR_ARG;
}
/**
* 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
* @return number of bytes used from buffer to store the resulting OctetString
*/
u8_t
snmp_asn1_enc_bits(u8_t *buf, u32_t buf_len, u32_t bit_value)
{
int i = 0;
u8_t len = 0;
u8_t *buf_ptr = (u8_t *)buf;
while ((buf_len > 0) && (bit_value != 0x00)) {
*buf_ptr = 0x00;
i = 8;
while (i > 0) {
if (bit_value & 0x01) {
*buf_ptr |= 0x01;
}
bit_value >>= 1;
*buf_ptr <<= 1;
i--;
}
buf_ptr++;
buf_len--;
len++;
}
return len;
}
#endif /* LWIP_SNMP */

File diff suppressed because it is too large Load Diff

View File

@@ -1,994 +0,0 @@
/**
* @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>
*/
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
#include "lwip/apps/snmp_structs.h"
#include "lwip/memp.h"
#include "lwip/netif.h"
LWIP_MEMPOOL_DECLARE(SNMP_ROOTNODE, SNMP_NUM_ROOTNODE, sizeof(struct mib_list_rootnode), "SNMP_ROOTNODE")
LWIP_MEMPOOL_DECLARE(SNMP_NODE, SNMP_NUM_NODE, sizeof(struct mib_list_node), "SNMP_NODE")
/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */
const s32_t prefix[4] = {1, 3, 6, 1};
#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN)
/** node stack entry (old news?) */
struct nse
{
/** right child */
const struct mib_node* r_ptr;
/** right child identifier */
s32_t r_id;
/** right child next level */
u8_t r_nl;
};
static u8_t node_stack_cnt;
static struct nse node_stack[NODE_STACK_SIZE];
const struct nse node_null = {NULL, 0, 0};
/**
* Pushes nse struct onto stack.
*/
static void
push_node(const struct nse* node)
{
LWIP_ASSERT("node_stack_cnt < NODE_STACK_SIZE",node_stack_cnt < NODE_STACK_SIZE);
LWIP_DEBUGF(SNMP_MIB_DEBUG,("push_node() node=%p id=%"S32_F"\n",(const void*)(node->r_ptr),node->r_id));
if (node_stack_cnt < NODE_STACK_SIZE) {
node_stack[node_stack_cnt] = *node;
node_stack_cnt++;
}
}
/**
* Pops nse struct from stack.
*/
static void
pop_node(struct nse* node)
{
if (node_stack_cnt > 0) {
node_stack_cnt--;
*node = node_stack[node_stack_cnt];
}
LWIP_DEBUGF(SNMP_MIB_DEBUG,("pop_node() node=%p id=%"S32_F"\n",(const void *)(node->r_ptr),node->r_id));
}
/**
* Conversion from ifIndex to lwIP netif
* @param ifindex is a s32_t object sub-identifier
* @param netif points to returned netif struct pointer
*/
void
snmp_ifindextonetif(s32_t ifindex, struct netif **netif)
{
struct netif *nif = netif_list;
s32_t i, ifidx;
ifidx = ifindex - 1;
i = 0;
while ((nif != NULL) && (i < ifidx)) {
nif = nif->next;
i++;
}
*netif = nif;
}
/**
* Conversion from lwIP netif to ifIndex
* @param netif points to a netif struct
* @param ifidx points to s32_t object sub-identifier
*/
void
snmp_netiftoifindex(struct netif *netif, s32_t *ifidx)
{
struct netif *nif = netif_list;
u16_t i;
i = 0;
while ((nif != NULL) && (nif != netif)) {
nif = nif->next;
i++;
}
*ifidx = i+1;
}
/**
* Conversion from oid to lwIP ip_addr
* @param ident points to s32_t ident[4] input
* @param ip points to output struct
*/
void
snmp_oidtoip(s32_t *ident, ip4_addr_t *ip)
{
IP4_ADDR(ip, ident[0], ident[1], ident[2], ident[3]);
}
/**
* Conversion from lwIP ip_addr to oid
* @param ip points to input struct
* @param ident points to s32_t ident[4] output
*/
void
snmp_iptooid(const ip4_addr_t *ip, s32_t *ident)
{
ident[0] = ip4_addr1(ip);
ident[1] = ip4_addr2(ip);
ident[2] = ip4_addr3(ip);
ident[3] = ip4_addr4(ip);
}
struct mib_list_node *
snmp_mib_ln_alloc(s32_t id)
{
struct mib_list_node *ln;
ln = (struct mib_list_node *)LWIP_MEMPOOL_ALLOC(SNMP_NODE);
if (ln != NULL) {
ln->prev = NULL;
ln->next = NULL;
ln->objid = id;
ln->nptr = NULL;
}
return ln;
}
void
snmp_mib_ln_free(struct mib_list_node *ln)
{
LWIP_MEMPOOL_FREE(SNMP_NODE, ln);
}
struct mib_list_rootnode *
snmp_mib_lrn_alloc(void)
{
struct mib_list_rootnode *lrn;
lrn = (struct mib_list_rootnode*)LWIP_MEMPOOL_ALLOC(SNMP_ROOTNODE);
if (lrn != NULL) {
lrn->scalar.get_object_def = noleafs_get_object_def;
lrn->scalar.get_value = noleafs_get_value;
lrn->scalar.set_test = noleafs_set_test;
lrn->scalar.set_value = noleafs_set_value;
lrn->scalar.node.node_type = MIB_NODE_LR;
lrn->head = NULL;
lrn->tail = NULL;
lrn->count = 0;
}
return lrn;
}
void
snmp_mib_lrn_free(struct mib_list_rootnode *lrn)
{
LWIP_MEMPOOL_FREE(SNMP_ROOTNODE, lrn);
}
/**
* Inserts node in idx list in a sorted
* (ascending order) fashion and
* allocates the node if needed.
*
* @param rn points to the root node
* @param objid is the object sub identifier
* @param insn points to a pointer to the inserted node
* used for constructing the tree.
* @return -1 if failed, 1 if inserted, 2 if present.
*/
s8_t
snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn)
{
struct mib_list_node *nn;
s8_t insert;
LWIP_ASSERT("rn != NULL",rn != NULL);
/* -1 = malloc failure, 0 = not inserted, 1 = inserted, 2 = was present */
insert = 0;
if (rn->head == NULL) {
/* empty list, add first node */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc empty list objid==%"S32_F"\n",objid));
nn = snmp_mib_ln_alloc(objid);
if (nn != NULL) {
rn->head = nn;
rn->tail = nn;
*insn = nn;
insert = 1;
} else {
insert = -1;
}
} else {
struct mib_list_node *n;
/* at least one node is present */
n = rn->head;
while ((n != NULL) && (insert == 0)) {
if (n->objid == objid) {
/* node is already there */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("node already there objid==%"S32_F"\n",objid));
*insn = n;
insert = 2;
} else if (n->objid < objid) {
if (n->next == NULL) {
/* alloc and insert at the tail */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins tail objid==%"S32_F"\n",objid));
nn = snmp_mib_ln_alloc(objid);
if (nn != NULL) {
nn->next = NULL;
nn->prev = n;
n->next = nn;
rn->tail = nn;
*insn = nn;
insert = 1;
} else {
/* insertion failure */
insert = -1;
}
} else {
/* there's more to explore: traverse list */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("traverse list\n"));
n = n->next;
}
} else {
/* n->objid > objid */
/* alloc and insert between n->prev and n */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins n->prev, objid==%"S32_F", n\n",objid));
nn = snmp_mib_ln_alloc(objid);
if (nn != NULL) {
if (n->prev == NULL) {
/* insert at the head */
nn->next = n;
nn->prev = NULL;
rn->head = nn;
n->prev = nn;
} else {
/* insert in the middle */
nn->next = n;
nn->prev = n->prev;
n->prev->next = nn;
n->prev = nn;
}
*insn = nn;
insert = 1;
} else {
/* insertion failure */
insert = -1;
}
}
}
}
if (insert == 1) {
rn->count += 1;
}
LWIP_ASSERT("insert != 0",insert != 0);
return insert;
}
/**
* Finds node in idx list and returns deletion mark.
*
* @param rn points to the root node
* @param objid is the object sub identifier
* @param fn returns pointer to found node
* @return 0 if not found, 1 if deletable,
* 2 can't delete (2 or more children), 3 not a list_node
*/
s8_t
snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn)
{
s8_t fc;
struct mib_list_node *n;
LWIP_ASSERT("rn != NULL",rn != NULL);
n = rn->head;
while ((n != NULL) && (n->objid != objid)) {
n = n->next;
}
if (n == NULL) {
fc = 0;
} else if (n->nptr == NULL) {
/* leaf, can delete node */
fc = 1;
} else {
struct mib_list_rootnode *r;
if (n->nptr->node_type == MIB_NODE_LR) {
r = (struct mib_list_rootnode*)(void*)n->nptr;
if (r->count > 1) {
/* can't delete node */
fc = 2;
} else {
/* count <= 1, can delete node */
fc = 1;
}
} else {
/* other node type */
fc = 3;
}
}
*fn = n;
return fc;
}
/**
* Removes node from idx list
* if it has a single child left.
*
* @param rn points to the root node
* @param n points to the node to delete
* @return the nptr to be freed by caller
*/
struct mib_list_rootnode *
snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n)
{
struct mib_list_rootnode *next;
LWIP_ASSERT("rn != NULL",rn != NULL);
LWIP_ASSERT("n != NULL",n != NULL);
/* caller must remove this sub-tree */
next = (struct mib_list_rootnode*)(void*)n->nptr;
rn->count -= 1;
if (n == rn->head) {
rn->head = n->next;
if (n->next != NULL) {
/* not last node, new list begin */
n->next->prev = NULL;
}
} else if (n == rn->tail) {
rn->tail = n->prev;
if (n->prev != NULL) {
/* not last node, new list end */
n->prev->next = NULL;
}
} else {
/* node must be in the middle */
n->prev->next = n->next;
n->next->prev = n->prev;
}
LWIP_DEBUGF(SNMP_MIB_DEBUG,("free list objid==%"S32_F"\n",n->objid));
snmp_mib_ln_free(n);
if (rn->count == 0) {
rn->head = NULL;
rn->tail = NULL;
}
return next;
}
/**
* Searches tree for the supplied (scalar?) object identifier.
*
* @param node points to the root of the tree ('.internet')
* @param ident_len the length of the supplied object identifier
* @param ident points to the array of sub identifiers
* @param np points to the found object instance (return)
* @return pointer to the requested parent (!) node if success, NULL otherwise
*/
const struct mib_node *
snmp_search_tree(const struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np)
{
u8_t node_type, ext_level;
ext_level = 0;
LWIP_DEBUGF(SNMP_MIB_DEBUG,("node==%p *ident==%"S32_F"\n",(const void*)node,*ident));
while (node != NULL) {
node_type = node->node_type;
if (node_type == MIB_NODE_AR) {
const struct mib_array_node *an;
u16_t i;
if (ident_len > 0) {
/* array node (internal ROM or RAM, fixed length) */
an = (const struct mib_array_node*)(const void*)node;
i = 0;
while ((i < an->maxlength) && (an->entries[i].objid != *ident)) {
i++;
}
if (i < an->maxlength) {
/* found it, if available proceed to child, otherwise inspect leaf */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->entries[i].objid,*ident));
if (an->entries[i].nptr == NULL) {
/* a scalar leaf OR table,
inspect remaining instance number / table index */
np->ident_len = ident_len;
np->ident = ident;
return &an->node;
} else {
/* follow next child pointer */
ident++;
ident_len--;
node = an->entries[i].nptr;
}
} else {
/* search failed, identifier mismatch (nosuchname) */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed *ident==%"S32_F"\n",*ident));
return NULL;
}
} else {
/* search failed, short object identifier (nosuchname) */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed, short object identifier\n"));
return NULL;
}
} else if (node_type == MIB_NODE_LR) {
const struct mib_list_rootnode *lrn;
struct mib_list_node *ln;
if (ident_len > 0) {
/* list root node (internal 'RAM', variable length) */
lrn = (const struct mib_list_rootnode*)(const void*)node;
ln = lrn->head;
/* iterate over list, head to tail */
while ((ln != NULL) && (ln->objid != *ident)) {
ln = ln->next;
}
if (ln != NULL) {
/* found it, proceed to child */;
LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident));
if (ln->nptr == NULL) {
np->ident_len = ident_len;
np->ident = ident;
return &lrn->scalar.node;
} else {
/* follow next child pointer */
ident_len--;
ident++;
node = ln->nptr;
}
} else {
/* search failed */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed *ident==%"S32_F"\n",*ident));
return NULL;
}
} else {
/* search failed, short object identifier (nosuchname) */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed, short object identifier\n"));
return NULL;
}
} else if (node_type == MIB_NODE_EX) {
const struct mib_external_node *en;
u16_t i, len;
if (ident_len > 0) {
/* external node (addressing and access via functions) */
en = (const struct mib_external_node*)(const void*)node;
i = 0;
len = en->level_length(en->addr_inf,ext_level);
while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) != 0)) {
i++;
}
if (i < len) {
s32_t debug_id;
en->get_objid(en->addr_inf,ext_level,i,&debug_id);
LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid==%"S32_F" *ident==%"S32_F"\n",debug_id,*ident));
if ((ext_level + 1) == en->tree_levels) {
np->ident_len = ident_len;
np->ident = ident;
return &en->node;
} else {
/* found it, proceed to child */
ident_len--;
ident++;
ext_level++;
}
} else {
/* search failed */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed *ident==%"S32_F"\n",*ident));
return NULL;
}
} else {
/* search failed, short object identifier (nosuchname) */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed, short object identifier\n"));
return NULL;
}
} else if (node_type == MIB_NODE_SC) {
/* scalar node */
if ((ident_len == 1) && (*ident == 0)) {
np->ident_len = ident_len;
np->ident = ident;
return node;
} else {
/* search failed, short object identifier (nosuchname) */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, invalid object identifier length\n"));
return NULL;
}
} else {
/* unknown node_type */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type %"U16_F" unkown\n",(u16_t)node_type));
return NULL;
}
}
/* done, found nothing */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p\n",(const void*)node));
return NULL;
}
/**
* Test table for presence of at least one table entry.
*/
static u8_t
empty_table(const struct mib_node *node)
{
u8_t node_type;
u8_t empty = 0;
if (node != NULL) {
node_type = node->node_type;
if (node_type == MIB_NODE_LR) {
const struct mib_list_rootnode *lrn;
lrn = (const struct mib_list_rootnode*)(const void*)node;
if ((lrn->count == 0) || (lrn->head == NULL)) {
empty = 1;
}
} else if (node_type == MIB_NODE_AR) {
const struct mib_array_node *an;
an = (const struct mib_array_node*)(const void*)node;
if ((an->maxlength == 0) || (an->entries == NULL)) {
empty = 1;
}
} else if (node_type == MIB_NODE_EX) {
const struct mib_external_node *en;
en = (const struct mib_external_node*)(const void*)node;
if (en->tree_levels == 0) {
empty = 1;
}
}
}
return empty;
}
/**
* Tree expansion.
*/
const struct mib_node *
snmp_expand_tree(const struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret)
{
u8_t node_type, ext_level, climb_tree;
ext_level = 0;
/* reset node stack */
node_stack_cnt = 0;
while (node != NULL) {
climb_tree = 0;
node_type = node->node_type;
if (node_type == MIB_NODE_AR) {
const struct mib_array_node *an;
u16_t i;
/* array node (internal ROM or RAM, fixed length) */
an = (const struct mib_array_node*)(const void*)node;
if (ident_len > 0) {
i = 0;
while ((i < an->maxlength) && (an->entries[i].objid < *ident)) {
i++;
}
if (i < an->maxlength) {
LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->entries[i].objid,*ident));
/* add identifier to oidret */
oidret->id[oidret->len] = an->entries[i].objid;
(oidret->len)++;
if (an->entries[i].nptr == NULL) {
LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n"));
/* leaf node (e.g. in a fixed size table) */
if (an->entries[i].objid > *ident) {
return &an->node;
} else if ((i + 1) < an->maxlength) {
/* an->entries[i].objid == *ident */
(oidret->len)--;
oidret->id[oidret->len] = an->entries[i + 1].objid;
(oidret->len)++;
return &an->node;
} else {
/* (i + 1) == an->maxlength */
(oidret->len)--;
climb_tree = 1;
}
} else {
u16_t j;
LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n"));
/* non-leaf, store right child ptr and id */
LWIP_ASSERT("i < 0xff", i < 0xff);
j = i + 1;
while ((j < an->maxlength) && (empty_table(an->entries[j].nptr))) {
j++;
}
if (j < an->maxlength) {
struct nse cur_node;
cur_node.r_ptr = an->entries[j].nptr;
cur_node.r_id = an->entries[j].objid;
cur_node.r_nl = 0;
push_node(&cur_node);
} else {
push_node(&node_null);
}
if (an->entries[i].objid == *ident) {
ident_len--;
ident++;
} else {
/* an->entries[i].objid < *ident */
ident_len = 0;
}
/* follow next child pointer */
node = an->entries[i].nptr;
}
} else {
/* i == an->maxlength */
climb_tree = 1;
}
} else {
u16_t j;
/* ident_len == 0, complete with leftmost '.thing' */
j = 0;
while ((j < an->maxlength) && empty_table(an->entries[j].nptr)) {
j++;
}
if (j < an->maxlength) {
LWIP_DEBUGF(SNMP_MIB_DEBUG,("left an->entries[j].objid==%"S32_F"\n",an->entries[j].objid));
oidret->id[oidret->len] = an->entries[j].objid;
(oidret->len)++;
if (an->entries[j].nptr == NULL) {
/* leaf node */
return &an->node;
} else {
/* no leaf, continue */
node = an->entries[j].nptr;
}
} else {
/* j == an->maxlength */
climb_tree = 1;
}
}
} else if (node_type == MIB_NODE_LR) {
const struct mib_list_rootnode *lrn;
struct mib_list_node *ln;
/* list root node (internal 'RAM', variable length) */
lrn = (const struct mib_list_rootnode*)(const void*)node;
if (ident_len > 0) {
ln = lrn->head;
/* iterate over list, head to tail */
while ((ln != NULL) && (ln->objid < *ident)) {
ln = ln->next;
}
if (ln != NULL) {
LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident));
oidret->id[oidret->len] = ln->objid;
(oidret->len)++;
if (ln->nptr == NULL) {
/* leaf node */
if (ln->objid > *ident) {
return &lrn->scalar.node;
} else if (ln->next != NULL) {
/* ln->objid == *ident */
(oidret->len)--;
oidret->id[oidret->len] = ln->next->objid;
(oidret->len)++;
return &lrn->scalar.node;
} else {
/* ln->next == NULL */
(oidret->len)--;
climb_tree = 1;
}
} else {
struct mib_list_node *jn;
/* non-leaf, store right child ptr and id */
jn = ln->next;
while ((jn != NULL) && empty_table(jn->nptr)) {
jn = jn->next;
}
if (jn != NULL) {
struct nse cur_node;
cur_node.r_ptr = jn->nptr;
cur_node.r_id = jn->objid;
cur_node.r_nl = 0;
push_node(&cur_node);
} else {
push_node(&node_null);
}
if (ln->objid == *ident) {
ident_len--;
ident++;
} else {
/* ln->objid < *ident */
ident_len = 0;
}
/* follow next child pointer */
node = ln->nptr;
}
} else {
/* ln == NULL */
climb_tree = 1;
}
} else {
struct mib_list_node *jn;
/* ident_len == 0, complete with leftmost '.thing' */
jn = lrn->head;
while ((jn != NULL) && empty_table(jn->nptr)) {
jn = jn->next;
}
if (jn != NULL) {
LWIP_DEBUGF(SNMP_MIB_DEBUG,("left jn->objid==%"S32_F"\n",jn->objid));
oidret->id[oidret->len] = jn->objid;
(oidret->len)++;
if (jn->nptr == NULL) {
/* leaf node */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("jn->nptr == NULL\n"));
return &lrn->scalar.node;
} else {
/* no leaf, continue */
node = jn->nptr;
}
} else {
/* jn == NULL */
climb_tree = 1;
}
}
} else if (node_type == MIB_NODE_EX) {
const struct mib_external_node *en;
s32_t ex_id;
/* external node (addressing and access via functions) */
en = (const struct mib_external_node*)(const void*)node;
if (ident_len > 0) {
u16_t i, len;
i = 0;
len = en->level_length(en->addr_inf,ext_level);
while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) < 0)) {
i++;
}
if (i < len) {
/* add identifier to oidret */
en->get_objid(en->addr_inf,ext_level,i,&ex_id);
LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,ex_id,*ident));
oidret->id[oidret->len] = ex_id;
(oidret->len)++;
if ((ext_level + 1) == en->tree_levels) {
LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n"));
/* leaf node */
if (ex_id > *ident) {
return &en->node;
} else if ((i + 1) < len) {
/* ex_id == *ident */
en->get_objid(en->addr_inf,ext_level,i + 1,&ex_id);
(oidret->len)--;
oidret->id[oidret->len] = ex_id;
(oidret->len)++;
return &en->node;
} else {
/* (i + 1) == len */
(oidret->len)--;
climb_tree = 1;
}
} else {
u16_t j;
LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n"));
/* non-leaf, store right child ptr and id */
LWIP_ASSERT("i < 0xff", i < 0xff);
j = i + 1;
if (j < len) {
struct nse cur_node;
/* right node is the current external node */
cur_node.r_ptr = node;
en->get_objid(en->addr_inf,ext_level,j,&cur_node.r_id);
cur_node.r_nl = ext_level + 1;
push_node(&cur_node);
} else {
push_node(&node_null);
}
if (en->ident_cmp(en->addr_inf,ext_level,i,*ident) == 0) {
ident_len--;
ident++;
} else {
/* external id < *ident */
ident_len = 0;
}
/* proceed to child */
ext_level++;
}
} else {
/* i == len (en->level_len()) */
climb_tree = 1;
}
} else {
/* ident_len == 0, complete with leftmost '.thing' */
en->get_objid(en->addr_inf,ext_level,0,&ex_id);
LWIP_DEBUGF(SNMP_MIB_DEBUG,("left en->objid==%"S32_F"\n",ex_id));
oidret->id[oidret->len] = ex_id;
(oidret->len)++;
if ((ext_level + 1) == en->tree_levels) {
/* leaf node */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("(ext_level + 1) == en->tree_levels\n"));
return &en->node;
} else {
/* no leaf, proceed to child */
ext_level++;
}
}
} else if (node_type == MIB_NODE_SC) {
/* scalar node */
if (ident_len > 0) {
/* at .0 */
climb_tree = 1;
} else {
/* ident_len == 0, complete object identifier */
oidret->id[oidret->len] = 0;
(oidret->len)++;
/* leaf node */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("completed scalar leaf\n"));
return node;
}
} else {
/* unknown/unhandled node_type */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node_type %"U16_F" unkown\n",(u16_t)node_type));
return NULL;
}
if (climb_tree) {
struct nse child;
/* find right child ptr */
child.r_ptr = NULL;
child.r_id = 0;
child.r_nl = 0;
while ((node_stack_cnt > 0) && (child.r_ptr == NULL)) {
pop_node(&child);
/* trim returned oid */
(oidret->len)--;
}
if (child.r_ptr != NULL) {
/* incoming ident is useless beyond this point */
ident_len = 0;
oidret->id[oidret->len] = child.r_id;
oidret->len++;
node = child.r_ptr;
ext_level = child.r_nl;
} else {
/* tree ends here ... */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed, tree ends here\n"));
return NULL;
}
}
}
/* done, found nothing */
LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node==%p\n",(const void*)node));
return NULL;
}
/**
* Test object identifier for the iso.org.dod.internet prefix.
*
* @param ident_len the length of the supplied object identifier
* @param ident points to the array of sub identifiers
* @return 1 if it matches, 0 otherwise
*/
u8_t
snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident)
{
if ((ident_len > 3) &&
(ident[0] == 1) && (ident[1] == 3) &&
(ident[2] == 6) && (ident[3] == 1)) {
return 1;
} else {
return 0;
}
}
/**
* Expands object identifier to the iso.org.dod.internet
* prefix for use in getnext operation.
*
* @param ident_len the length of the supplied object identifier
* @param ident points to the array of sub identifiers
* @param oidret points to returned expanded object identifier
* @return 1 if it matches, 0 otherwise
*
* @note ident_len 0 is allowed, expanding to the first known object id!!
*/
u8_t
snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret)
{
const s32_t *prefix_ptr;
s32_t *ret_ptr;
u8_t i;
i = 0;
prefix_ptr = &prefix[0];
ret_ptr = &oidret->id[0];
ident_len = ((ident_len < 4)?ident_len:4);
while ((i < ident_len) && ((*ident) <= (*prefix_ptr))) {
*ret_ptr++ = *prefix_ptr++;
ident++;
i++;
}
if (i == ident_len) {
/* match, complete missing bits */
while (i < 4) {
*ret_ptr++ = *prefix_ptr++;
i++;
}
oidret->len = i;
return 1;
} else {
/* i != ident_len */
return 0;
}
}
void
noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
{
LWIP_UNUSED_ARG(ident_len);
LWIP_UNUSED_ARG(ident);
od->instance = MIB_OBJECT_NONE;
}
u16_t
noleafs_get_value(struct obj_def *od, void *value)
{
LWIP_UNUSED_ARG(od);
LWIP_UNUSED_ARG(value);
return 0;
}
u8_t
noleafs_set_test(struct obj_def *od, u16_t len, void *value)
{
LWIP_UNUSED_ARG(od);
LWIP_UNUSED_ARG(len);
LWIP_UNUSED_ARG(value);
/* can't set */
return 0;
}
void
noleafs_set_value(struct obj_def *od, u16_t len, void *value)
{
LWIP_UNUSED_ARG(od);
LWIP_UNUSED_ARG(len);
LWIP_UNUSED_ARG(value);
}
#endif /* LWIP_SNMP */

File diff suppressed because it is too large Load Diff

View File

@@ -1,673 +0,0 @@
/**
* @file
* SNMP output message processing (RFC1157).
*
* Output responses and traps are build in two passes:
*
* Pass 0: iterate over the output message backwards to determine encoding lengths
* Pass 1: the actual forward encoding of internal form into ASN1
*
* The single-pass encoding method described by Comer & Stevens
* requires extra buffer space and copying for reversal of the packet.
* The buffer requirement can be prohibitively large for big payloads
* (>= 484) therefore we use the two encoding passes.
*/
/*
* 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>
*/
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
#include "lwip/udp.h"
#include "lwip/netif.h"
#include "lwip/apps/snmp.h"
#include "lwip/snmp.h"
#include "lwip/apps/snmp_asn1.h"
#include "snmp_msg.h"
#include "lwip/sys.h"
#include <string.h>
#if !SNMP_COMMUNITY_EXT
#define snmp_community_trap snmp_community
#endif
struct snmp_trap_dst
{
/* destination IP address in network order */
ip_addr_t dip;
/* set to 0 when disabled, >0 when enabled */
u8_t enable;
};
struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS];
/** TRAP message structure */
struct snmp_msg_trap trap_msg;
static u16_t snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len);
static u16_t snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len);
static u16_t snmp_varbind_list_sum(struct snmp_varbind_root *root);
static u16_t snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p);
static u16_t snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p);
static u16_t snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs);
/**
* Sets enable switch for this trap destination.
* @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
* @param enable switch if 0 destination is disabled >0 enabled.
*/
void
snmp_trap_dst_enable(u8_t dst_idx, u8_t enable)
{
if (dst_idx < SNMP_TRAP_DESTINATIONS) {
trap_dst[dst_idx].enable = enable;
}
}
/**
* Sets IPv4 address for this trap destination.
* @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
* @param dst IPv4 address in host order.
*/
void
snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst)
{
if (dst_idx < SNMP_TRAP_DESTINATIONS) {
ip_addr_set(&trap_dst[dst_idx].dip, dst);
}
}
/**
* Sends a 'getresponse' message to the request originator.
*
* @param m_stat points to the current message request state source
* @return ERR_OK when success, ERR_MEM if we're out of memory
*
* @note the caller is responsible for filling in outvb in the m_stat
* and provide error-status and index (except for tooBig errors) ...
*/
err_t
snmp_send_response(struct snmp_msg_pstat *m_stat)
{
struct snmp_varbind_root emptyvb = {NULL, NULL, 0, 0, 0};
struct pbuf *p;
u16_t tot_len;
err_t err;
/* pass 0, calculate length fields */
tot_len = snmp_varbind_list_sum(&m_stat->outvb);
tot_len = snmp_resp_header_sum(m_stat, tot_len);
/* try allocating pbuf(s) for complete response */
p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM);
if (p == NULL) {
LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() tooBig\n"));
/* can't construct reply, return error-status tooBig */
m_stat->error_status = SNMP_ES_TOOBIG;
m_stat->error_index = 0;
/* pass 0, recalculate lengths, for empty varbind-list */
tot_len = snmp_varbind_list_sum(&emptyvb);
tot_len = snmp_resp_header_sum(m_stat, tot_len);
/* retry allocation once for header and empty varbind-list */
p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM);
}
if (p != NULL) {
/* first pbuf alloc try or retry alloc success */
u16_t ofs;
LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() p != NULL\n"));
/* pass 1, size error, encode packet ino the pbuf(s) */
ofs = snmp_resp_header_enc(m_stat, p);
snmp_varbind_list_enc(&m_stat->outvb, p, ofs);
switch (m_stat->error_status) {
case SNMP_ES_NOERROR:
/* nothing to do */
break;
case SNMP_ES_TOOBIG:
mib2_inc_snmpouttoobigs();
break;
case SNMP_ES_NOSUCHNAME:
mib2_inc_snmpoutnosuchnames();
break;
case SNMP_ES_BADVALUE:
mib2_inc_snmpoutbadvalues();
break;
case SNMP_ES_GENERROR:
mib2_inc_snmpoutgenerrs();
break;
default:
LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_send_response(): unknown error_status: %d\n", (int)m_stat->error_status));
break;
}
mib2_inc_snmpoutgetresponses();
mib2_inc_snmpoutpkts();
/** @todo do we need separate rx and tx pcbs for threaded case? */
/** connect to the originating source */
udp_connect(m_stat->pcb, &m_stat->sip, m_stat->sp);
err = udp_send(m_stat->pcb, p);
if (err == ERR_MEM) {
/** @todo release some memory, retry and return tooBig? tooMuchHassle? */
err = ERR_MEM;
} else {
err = ERR_OK;
}
/** disassociate remote address and port with this pcb */
udp_disconnect(m_stat->pcb);
pbuf_free(p);
LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() done\n"));
return err;
} else {
/* first pbuf alloc try or retry alloc failed
very low on memory, couldn't return tooBig */
return ERR_MEM;
}
}
/**
* Sends an generic or enterprise specific trap message.
*
* @param generic_trap is the trap code
* @param eoid points to enterprise object identifier
* @param specific_trap used for enterprise traps when generic_trap == 6
* @return ERR_OK when success, ERR_MEM if we're out of memory
*
* @note the caller is responsible for filling in outvb in the trap_msg
* @note the use of the enterprise identifier field
* is per RFC1215.
* Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
* and .iso.org.dod.internet.private.enterprises.yourenterprise
* (sysObjectID) for specific traps.
*/
err_t
snmp_send_trap(s8_t generic_trap, const struct snmp_obj_id *eoid, s32_t specific_trap)
{
struct snmp_trap_dst *td;
struct netif *dst_if;
const ip_addr_t* dst_ip;
struct pbuf *p;
u16_t i,tot_len;
err_t err = ERR_OK;
for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) {
if ((td->enable != 0) && !ip_addr_isany(&td->dip)) {
/* network order trap destination */
ip_addr_copy(trap_msg.dip, td->dip);
/* lookup current source address for this dst */
ip_route_get_local_ip(PCB_ISIPV6(trap_msg.pcb), &trap_msg.pcb->local_ip,
&td->dip, dst_if, dst_ip);
if ((dst_if != NULL) && (dst_ip != NULL)) {
trap_msg.sip_raw_len = (IP_IS_V6_VAL(*dst_ip) ? 16 : 4);
memcpy(trap_msg.sip_raw, dst_ip, trap_msg.sip_raw_len);
trap_msg.gen_trap = generic_trap;
trap_msg.spc_trap = specific_trap;
if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC) {
/* enterprise-Specific trap */
trap_msg.enterprise = eoid;
} else {
/* generic (MIB-II) trap */
mib2_get_snmpgrpid_ptr(&trap_msg.enterprise);
}
MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts);
/* pass 0, calculate length fields */
tot_len = snmp_varbind_list_sum(&trap_msg.outvb);
tot_len = snmp_trap_header_sum(&trap_msg, tot_len);
/* allocate pbuf(s) */
p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM);
if (p != NULL) {
u16_t ofs;
/* pass 1, encode packet ino the pbuf(s) */
ofs = snmp_trap_header_enc(&trap_msg, p);
snmp_varbind_list_enc(&trap_msg.outvb, p, ofs);
mib2_inc_snmpouttraps();
mib2_inc_snmpoutpkts();
/** send to the TRAP destination */
udp_sendto(trap_msg.pcb, p, &trap_msg.dip, SNMP_TRAP_PORT);
pbuf_free(p);
} else {
err = ERR_MEM;
}
} else {
/* routing error */
err = ERR_RTE;
}
}
}
return err;
}
void
snmp_coldstart_trap(void)
{
trap_msg.outvb.head = NULL;
trap_msg.outvb.tail = NULL;
trap_msg.outvb.count = 0;
snmp_send_trap(SNMP_GENTRAP_COLDSTART, NULL, 0);
}
void
snmp_authfail_trap(void)
{
u8_t enable;
mib2_get_snmpenableauthentraps(&enable);
if (enable == 1) {
trap_msg.outvb.head = NULL;
trap_msg.outvb.tail = NULL;
trap_msg.outvb.count = 0;
snmp_send_trap(SNMP_GENTRAP_AUTHFAIL, NULL, 0);
}
}
/**
* Sums response header field lengths from tail to head and
* returns resp_header_lengths for second encoding pass.
*
* @param vb_len varbind-list length
* @param rhl points to returned header lengths
* @return the required length for encoding the response header
*/
static u16_t
snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len)
{
u16_t tot_len;
s32_t snmp_req_ver;
struct snmp_resp_header_lengths *rhl;
rhl = &m_stat->rhl;
tot_len = vb_len;
snmp_asn1_enc_s32t_cnt(m_stat->error_index, &rhl->erridxlen);
snmp_asn1_enc_length_cnt(rhl->erridxlen, &rhl->erridxlenlen);
tot_len += 1 + rhl->erridxlenlen + rhl->erridxlen;
snmp_asn1_enc_s32t_cnt(m_stat->error_status, &rhl->errstatlen);
snmp_asn1_enc_length_cnt(rhl->errstatlen, &rhl->errstatlenlen);
tot_len += 1 + rhl->errstatlenlen + rhl->errstatlen;
snmp_asn1_enc_s32t_cnt(m_stat->rid, &rhl->ridlen);
snmp_asn1_enc_length_cnt(rhl->ridlen, &rhl->ridlenlen);
tot_len += 1 + rhl->ridlenlen + rhl->ridlen;
rhl->pdulen = tot_len;
snmp_asn1_enc_length_cnt(rhl->pdulen, &rhl->pdulenlen);
tot_len += 1 + rhl->pdulenlen;
rhl->comlen = m_stat->com_strlen;
snmp_asn1_enc_length_cnt(rhl->comlen, &rhl->comlenlen);
tot_len += 1 + rhl->comlenlen + rhl->comlen;
snmp_req_ver = m_stat->version;
snmp_asn1_enc_s32t_cnt(snmp_req_ver, &rhl->verlen);
snmp_asn1_enc_length_cnt(rhl->verlen, &rhl->verlenlen);
tot_len += 1 + rhl->verlen + rhl->verlenlen;
rhl->seqlen = tot_len;
snmp_asn1_enc_length_cnt(rhl->seqlen, &rhl->seqlenlen);
tot_len += 1 + rhl->seqlenlen;
return tot_len;
}
/**
* Sums trap header field lengths from tail to head and
* returns trap_header_lengths for second encoding pass.
*
* @param vb_len varbind-list length
* @param thl points to returned header lengths
* @return the required length for encoding the trap header
*/
static u16_t
snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len)
{
u16_t tot_len;
struct snmp_trap_header_lengths *thl;
thl = &m_trap->thl;
tot_len = vb_len;
snmp_asn1_enc_u32t_cnt(m_trap->ts, &thl->tslen);
snmp_asn1_enc_length_cnt(thl->tslen, &thl->tslenlen);
tot_len += 1 + thl->tslen + thl->tslenlen;
snmp_asn1_enc_s32t_cnt(m_trap->spc_trap, &thl->strplen);
snmp_asn1_enc_length_cnt(thl->strplen, &thl->strplenlen);
tot_len += 1 + thl->strplen + thl->strplenlen;
snmp_asn1_enc_s32t_cnt(m_trap->gen_trap, &thl->gtrplen);
snmp_asn1_enc_length_cnt(thl->gtrplen, &thl->gtrplenlen);
tot_len += 1 + thl->gtrplen + thl->gtrplenlen;
thl->aaddrlen = m_trap->sip_raw_len;
snmp_asn1_enc_length_cnt(thl->aaddrlen, &thl->aaddrlenlen);
tot_len += 1 + thl->aaddrlen + thl->aaddrlenlen;
snmp_asn1_enc_oid_cnt(m_trap->enterprise->len, &m_trap->enterprise->id[0], &thl->eidlen);
snmp_asn1_enc_length_cnt(thl->eidlen, &thl->eidlenlen);
tot_len += 1 + thl->eidlen + thl->eidlenlen;
thl->pdulen = tot_len;
snmp_asn1_enc_length_cnt(thl->pdulen, &thl->pdulenlen);
tot_len += 1 + thl->pdulenlen;
thl->comlen = (u16_t)strlen(snmp_community_trap);
snmp_asn1_enc_length_cnt(thl->comlen, &thl->comlenlen);
tot_len += 1 + thl->comlenlen + thl->comlen;
snmp_asn1_enc_s32t_cnt(snmp_version, &thl->verlen);
snmp_asn1_enc_length_cnt(thl->verlen, &thl->verlenlen);
tot_len += 1 + thl->verlen + thl->verlenlen;
thl->seqlen = tot_len;
snmp_asn1_enc_length_cnt(thl->seqlen, &thl->seqlenlen);
tot_len += 1 + thl->seqlenlen;
return tot_len;
}
/**
* Sums varbind lengths from tail to head and
* annotates lengths in varbind for second encoding pass.
*
* @param root points to the root of the variable binding list
* @return the required length for encoding the variable bindings
*/
static u16_t
snmp_varbind_list_sum(struct snmp_varbind_root *root)
{
struct snmp_varbind *vb;
u32_t *uint_ptr;
s32_t *sint_ptr;
u16_t tot_len;
tot_len = 0;
vb = root->tail;
while (vb != NULL) {
/* encoded value lenght depends on type */
switch (vb->value_type) {
case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
sint_ptr = (s32_t*)vb->value;
snmp_asn1_enc_s32t_cnt(*sint_ptr, &vb->vlen);
break;
case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
uint_ptr = (u32_t*)vb->value;
snmp_asn1_enc_u32t_cnt(*uint_ptr, &vb->vlen);
break;
case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
vb->vlen = vb->value_len;
break;
case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
sint_ptr = (s32_t*)vb->value;
snmp_asn1_enc_oid_cnt(vb->value_len / sizeof(s32_t), sint_ptr, &vb->vlen);
break;
default:
/* unsupported type */
vb->vlen = 0;
break;
}
/* encoding length of value length field */
snmp_asn1_enc_length_cnt(vb->vlen, &vb->vlenlen);
snmp_asn1_enc_oid_cnt(vb->ident_len, vb->ident, &vb->olen);
snmp_asn1_enc_length_cnt(vb->olen, &vb->olenlen);
vb->seqlen = 1 + vb->vlenlen + vb->vlen;
vb->seqlen += 1 + vb->olenlen + vb->olen;
snmp_asn1_enc_length_cnt(vb->seqlen, &vb->seqlenlen);
/* varbind seq */
tot_len += 1 + vb->seqlenlen + vb->seqlen;
vb = vb->prev;
}
/* varbind-list seq */
root->seqlen = tot_len;
snmp_asn1_enc_length_cnt(root->seqlen, &root->seqlenlen);
tot_len += 1 + root->seqlenlen;
return tot_len;
}
/**
* Encodes response header from head to tail.
*/
static u16_t
snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p)
{
u16_t ofs;
s32_t snmp_req_ver;
ofs = 0;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_stat->rhl.seqlen);
ofs += m_stat->rhl.seqlenlen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_stat->rhl.verlen);
ofs += m_stat->rhl.verlenlen;
snmp_req_ver = m_stat->version;
snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.verlen, snmp_req_ver);
ofs += m_stat->rhl.verlen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_stat->rhl.comlen);
ofs += m_stat->rhl.comlenlen;
snmp_asn1_enc_raw(p, ofs, m_stat->rhl.comlen, m_stat->community);
ofs += m_stat->rhl.comlen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_stat->rhl.pdulen);
ofs += m_stat->rhl.pdulenlen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_stat->rhl.ridlen);
ofs += m_stat->rhl.ridlenlen;
snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.ridlen, m_stat->rid);
ofs += m_stat->rhl.ridlen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_stat->rhl.errstatlen);
ofs += m_stat->rhl.errstatlenlen;
snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.errstatlen, m_stat->error_status);
ofs += m_stat->rhl.errstatlen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_stat->rhl.erridxlen);
ofs += m_stat->rhl.erridxlenlen;
snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.erridxlen, m_stat->error_index);
ofs += m_stat->rhl.erridxlen;
return ofs;
}
/**
* Encodes trap header from head to tail.
*/
static u16_t
snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p)
{
u16_t ofs;
ofs = 0;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_trap->thl.seqlen);
ofs += m_trap->thl.seqlenlen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_trap->thl.verlen);
ofs += m_trap->thl.verlenlen;
snmp_asn1_enc_s32t(p, ofs, m_trap->thl.verlen, snmp_version);
ofs += m_trap->thl.verlen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_trap->thl.comlen);
ofs += m_trap->thl.comlenlen;
snmp_asn1_enc_raw(p, ofs, m_trap->thl.comlen, (const u8_t *)&snmp_community_trap[0]);
ofs += m_trap->thl.comlen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_trap->thl.pdulen);
ofs += m_trap->thl.pdulenlen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_trap->thl.eidlen);
ofs += m_trap->thl.eidlenlen;
snmp_asn1_enc_oid(p, ofs, m_trap->enterprise->len, &m_trap->enterprise->id[0]);
ofs += m_trap->thl.eidlen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_trap->thl.aaddrlen);
ofs += m_trap->thl.aaddrlenlen;
snmp_asn1_enc_raw(p, ofs, m_trap->thl.aaddrlen, &m_trap->sip_raw[0]);
ofs += m_trap->thl.aaddrlen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_trap->thl.gtrplen);
ofs += m_trap->thl.gtrplenlen;
snmp_asn1_enc_u32t(p, ofs, m_trap->thl.gtrplen, m_trap->gen_trap);
ofs += m_trap->thl.gtrplen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_trap->thl.strplen);
ofs += m_trap->thl.strplenlen;
snmp_asn1_enc_u32t(p, ofs, m_trap->thl.strplen, m_trap->spc_trap);
ofs += m_trap->thl.strplen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS));
ofs += 1;
snmp_asn1_enc_length(p, ofs, m_trap->thl.tslen);
ofs += m_trap->thl.tslenlen;
snmp_asn1_enc_u32t(p, ofs, m_trap->thl.tslen, m_trap->ts);
ofs += m_trap->thl.tslen;
return ofs;
}
/**
* Encodes varbind list from head to tail.
*/
static u16_t
snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs)
{
struct snmp_varbind *vb;
s32_t *sint_ptr;
u32_t *uint_ptr;
u8_t *raw_ptr;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
ofs += 1;
snmp_asn1_enc_length(p, ofs, root->seqlen);
ofs += root->seqlenlen;
vb = root->head;
while (vb != NULL) {
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
ofs += 1;
snmp_asn1_enc_length(p, ofs, vb->seqlen);
ofs += vb->seqlenlen;
snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID));
ofs += 1;
snmp_asn1_enc_length(p, ofs, vb->olen);
ofs += vb->olenlen;
snmp_asn1_enc_oid(p, ofs, vb->ident_len, &vb->ident[0]);
ofs += vb->olen;
snmp_asn1_enc_type(p, ofs, vb->value_type);
ofs += 1;
snmp_asn1_enc_length(p, ofs, vb->vlen);
ofs += vb->vlenlen;
switch (vb->value_type) {
case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
sint_ptr = (s32_t*)vb->value;
snmp_asn1_enc_s32t(p, ofs, vb->vlen, *sint_ptr);
break;
case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
uint_ptr = (u32_t*)vb->value;
snmp_asn1_enc_u32t(p, ofs, vb->vlen, *uint_ptr);
break;
case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
raw_ptr = (u8_t*)vb->value;
snmp_asn1_enc_raw(p, ofs, vb->vlen, raw_ptr);
break;
case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
break;
case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
sint_ptr = (s32_t*)vb->value;
snmp_asn1_enc_oid(p, ofs, vb->value_len / sizeof(s32_t), sint_ptr);
break;
default:
/* unsupported type */
break;
}
ofs += vb->vlen;
vb = vb->next;
}
return ofs;
}
#endif /* LWIP_SNMP */

748
src/apps/snmp/snmp_asn1.c Normal file
View File

@@ -0,0 +1,748 @@
/**
* @file
* Abstract Syntax Notation One (ISO 8824, 8825) encoding
*
* @todo not optimised (yet), favor correctness over speed, favor speed over size
*/
/*
* 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>
*/
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
#include "snmp_asn1.h"
#define PBUF_OP_EXEC(code) \
if ((code) != ERR_OK) { \
return ERR_BUF; \
}
err_t
snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
{
u8_t data;
u8_t length_bytes_required;
/* write type */
if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
/* extended format is not used by SNMP so we do not accept those values */
return ERR_ARG;
}
if (tlv->type_len != 0) {
/* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */
return ERR_ARG;
}
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type));
tlv->type_len = 1;
/* write length */
if (tlv->value_len <= 127) {
length_bytes_required = 1;
} else if (tlv->value_len <= 255) {
length_bytes_required = 2;
} else {
length_bytes_required = 3;
}
/* check for forced min length */
if (tlv->length_len > 0) {
if (tlv->length_len < length_bytes_required) {
/* unable to code requested length in requested number of bytes */
return ERR_ARG;
}
length_bytes_required = tlv->length_len;
} else {
tlv->length_len = length_bytes_required;
}
if (length_bytes_required > 1) {
/* multi byte representation required */
length_bytes_required--;
data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
while (length_bytes_required > 1)
{
if (length_bytes_required == 2) {
/* append high byte */
data = (u8_t)(tlv->value_len >> 8);
} else {
/* append leading 0x00 */
data = 0x00;
}
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
length_bytes_required--;
}
}
/* append low byte */
data = (u8_t)(tlv->value_len & 0xFF);
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
return ERR_OK;
}
/**
* Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
*
* @param p points to output pbuf to encode raw data into
* @param ofs points to the offset within the pbuf chain
* @param raw_len raw data length
* @param raw points raw data
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
*/
err_t
snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len)
{
PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len));
return ERR_OK;
}
/**
* Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
*
* @param p points to output pbuf to encode value into
* @param ofs points to the offset within the pbuf chain
* @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
* @param value is the host order u32_t value to be encoded
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
*
* @see snmp_asn1_enc_u32t_cnt()
*/
err_t
snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value)
{
if (octets_needed > 5) {
return ERR_ARG;
}
if (octets_needed == 5) {
/* not enough bits in 'value' add leading 0x00 */
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
octets_needed--;
}
while (octets_needed > 1) {
octets_needed--;
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
}
/* (only) one least significant octet */
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
return ERR_OK;
}
/**
* Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
*
* @param p points to output pbuf to encode value into
* @param ofs points to the offset within the pbuf chain
* @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
* @param value is the host order u32_t value to be encoded
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
*
* @see snmp_asn1_enc_u64t_cnt()
*/
err_t
snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value)
{
if (octets_needed > 9) {
return ERR_ARG;
}
if (octets_needed == 9) {
/* not enough bits in 'value' add leading 0x00 */
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
octets_needed--;
}
while (octets_needed > 4) {
octets_needed--;
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> ((octets_needed-4) << 3))));
}
/* skip to low u32 */
value++;
while (octets_needed > 1) {
octets_needed--;
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> (octets_needed << 3))));
}
/* always write at least one octet (also in case of value == 0) */
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value)));
return ERR_OK;
}
/**
* Encodes s32_t integer into a pbuf chained ASN1 msg.
*
* @param p points to output pbuf to encode value into
* @param ofs points to the offset within the pbuf chain
* @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
* @param value is the host order s32_t value to be encoded
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
*
* @see snmp_asn1_enc_s32t_cnt()
*/
err_t
snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value)
{
while (octets_needed > 1) {
octets_needed--;
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
}
/* (only) one least significant octet */
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
return ERR_OK;
}
/**
* Encodes object identifier into a pbuf chained ASN1 msg.
*
* @param p points to output pbuf to encode oid into
* @param ofs points to the offset within the pbuf chain
* @param ident_len object identifier array length
* @param ident points to object identifier array
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
*/
err_t
snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len)
{
if (oid_len > 1) {
/* write compressed first two sub id's */
u32_t compressed_byte = ((oid[0] * 40) + oid[1]);
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte));
oid_len -= 2;
oid += 2;
} else {
/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
/* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
return ERR_ARG;
}
while (oid_len > 0) {
u32_t sub_id;
u8_t shift, tail;
oid_len--;
sub_id = *oid;
tail = 0;
shift = 28;
while (shift > 0) {
u8_t code;
code = (u8_t)(sub_id >> shift);
if ((code != 0) || (tail != 0)) {
tail = 1;
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
}
shift -= 7;
}
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
/* proceed to next sub-identifier */
oid++;
}
return ERR_OK;
}
/**
* Returns octet count for length.
*
* @param length
* @param octets_needed points to the return value
*/
void
snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
{
if (length < 0x80U) {
*octets_needed = 1;
} else if (length < 0x100U) {
*octets_needed = 2;
} else {
*octets_needed = 3;
}
}
/**
* Returns octet count for an u32_t.
*
* @param value
* @param octets_needed points to the return value
*
* @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
* as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
* of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
*/
void
snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
{
if (value < 0x80UL) {
*octets_needed = 1;
} else if (value < 0x8000UL) {
*octets_needed = 2;
} else if (value < 0x800000UL) {
*octets_needed = 3;
} else if (value < 0x80000000UL) {
*octets_needed = 4;
} else {
*octets_needed = 5;
}
}
/**
* Returns octet count for an u64_t.
*
* @param value
* @param octets_needed points to the return value
*
* @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
* as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
* of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
*/
void
snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed)
{
/* check if high u32 is 0 */
if (*value == 0x00)
{
/* only low u32 is important */
value++;
snmp_asn1_enc_u32t_cnt(*value, octets_needed);
} else {
/* low u32 does not matter for length determination */
snmp_asn1_enc_u32t_cnt(*value, octets_needed);
*octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
}
}
/**
* Returns octet count for an s32_t.
*
* @param value
* @param octets_needed points to the return value
*
* @note ASN coded integers are _always_ signed.
*/
void
snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
{
if (value < 0) {
value = ~value;
}
if (value < 0x80L) {
*octets_needed = 1;
} else if (value < 0x8000L) {
*octets_needed = 2;
} else if (value < 0x800000L) {
*octets_needed = 3;
} else {
*octets_needed = 4;
}
}
/**
* Returns octet count for an object identifier.
*
* @param ident_len object identifier array length
* @param ident points to object identifier array
* @param octets_needed points to the return value
*/
void
snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
{
u32_t sub_id;
*octets_needed = 0;
if (oid_len > 1) {
/* compressed prefix in one octet */
(*octets_needed)++;
oid_len -= 2;
oid += 2;
}
while (oid_len > 0) {
oid_len--;
sub_id = *oid;
sub_id >>= 7;
(*octets_needed)++;
while (sub_id > 0) {
sub_id >>= 7;
(*octets_needed)++;
}
oid++;
}
}
err_t
snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
{
u8_t data;
/* decode type first */
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
tlv->type = data;
if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
/* extended format is not used by SNMP so we do not accept those values */
return ERR_VAL;
}
tlv->type_len = 1;
/* now, decode length */
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
if (data < 0x80) { /* short form */
tlv->length_len = 1;
tlv->value_len = data;
} else if (data > 0x80) { /* long form */
u8_t length_bytes = data - 0x80;
tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
tlv->value_len = 0;
while (length_bytes > 0) {
/* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */
if (tlv->value_len > 0xFF) {
return ERR_VAL;
}
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
tlv->value_len <<= 8;
tlv->value_len |= data;
/* take care for special value used for indefinite length */
if (tlv->value_len == 0xFFFF) {
return ERR_VAL;
}
length_bytes--;
}
} else { /* data == 0x80 indefinite length form */
/* (not allowed for SNMP; RFC 1157, 3.2.2) */
return ERR_VAL;
//tlv->length_len = 1;
//tlv->value_len = 0xFFFF;
}
return ERR_OK;
}
/**
* Decodes positive integer (counter, gauge, timeticks) into u32_t.
*
* @param p points to a pbuf holding an ASN1 coded integer
* @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
* @param len length of the coded integer field
* @param value return host order integer
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
*
* @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
* as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
* of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
*/
err_t
snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
{
u8_t data;
if ((len > 0) && (len <= 5)) {
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
/* expecting sign bit to be zero, only unsigned please! */
if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
*value = data;
len--;
while (len > 0)
{
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
len--;
*value <<= 8;
*value |= data;
}
return ERR_OK;
}
}
return ERR_VAL;
}
/**
* Decodes large positive integer (counter64) into 2x u32_t.
*
* @param p points to a pbuf holding an ASN1 coded integer
* @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
* @param len length of the coded integer field
* @param value return host order integer
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
*
* @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
* as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
* of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
*/
err_t
snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
{
u8_t data;
if (len <= 4) {
/* high u32 is 0 */
*value = 0;
/* directly skip to low u32 */
value++;
}
if ((len > 0) && (len <= 9)) {
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
/* expecting sign bit to be zero, only unsigned please! */
if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
*value = data;
len--;
while (len > 0)
{
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
if (len == 4) {
/* skip to low u32 */
value++;
*value = 0;
} else {
*value <<= 8;
}
*value |= data;
len--;
}
return ERR_OK;
}
}
return ERR_VAL;
}
/**
* Decodes integer into s32_t.
*
* @param p points to a pbuf holding an ASN1 coded integer
* @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
* @param len length of the coded integer field
* @param value return host order integer
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
*
* @note ASN coded integers are _always_ signed!
*/
err_t
snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
{
#if BYTE_ORDER == LITTLE_ENDIAN
u8_t *lsb_ptr = (u8_t*)value;
#endif
#if BYTE_ORDER == BIG_ENDIAN
u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
#endif
u8_t sign;
u8_t data;
if ((len > 0) && (len < 5)) {
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
len--;
if (data & 0x80) {
/* negative, start from -1 */
*value = -1;
sign = 1;
*lsb_ptr &= data;
} else {
/* positive, start from 0 */
*value = 0;
sign = 0;
*lsb_ptr |= data;
}
/* OR/AND octets with value */
while (len > 0) {
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
len--;
#if BYTE_ORDER == LITTLE_ENDIAN
*value <<= 8;
#endif
#if BYTE_ORDER == BIG_ENDIAN
*value >>= 8;
#endif
if (sign) {
*lsb_ptr |= 255;
*lsb_ptr &= data;
} else {
*lsb_ptr |= data;
}
}
return ERR_OK;
}
return ERR_VAL;
}
/**
* Decodes object identifier from incoming message into array of u32_t.
*
* @param p points to a pbuf holding an ASN1 coded object identifier
* @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier
* @param len length of the coded object identifier
* @param oid return object identifier struct
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
*/
err_t
snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len)
{
u32_t *oid_ptr;
u8_t data;
*oid_len = 0;
oid_ptr = oid;
if (len > 0) {
if (oid_max_len < 2) {
return ERR_MEM;
}
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
len--;
/* first compressed octet */
if (data == 0x2B) {
/* (most) common case 1.3 (iso.org) */
*oid_ptr = 1;
oid_ptr++;
*oid_ptr = 3;
oid_ptr++;
} else if (data < 40) {
*oid_ptr = 0;
oid_ptr++;
*oid_ptr = data;
oid_ptr++;
} else if (data < 80) {
*oid_ptr = 1;
oid_ptr++;
*oid_ptr = data - 40;
oid_ptr++;
} else {
*oid_ptr = 2;
oid_ptr++;
*oid_ptr = data - 80;
oid_ptr++;
}
*oid_len = 2;
} else {
/* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
return ERR_OK;
}
while ((len > 0) && (*oid_len < oid_max_len)) {
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
len--;
if ((data & 0x80) == 0x00) {
/* sub-identifier uses single octet */
*oid_ptr = data;
} else {
/* sub-identifier uses multiple octets */
u32_t sub_id = (data & ~0x80);
while ((len > 0) && ((data & 0x80) != 0)) {
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
len--;
sub_id = (sub_id << 7) + (data & ~0x80);
}
if ((data & 0x80) != 0) {
/* "more bytes following" bit still set at end of len */
return ERR_VAL;
}
*oid_ptr = sub_id;
}
oid_ptr++;
(*oid_len)++;
}
if (len > 0) {
/* OID to long to fit in our buffer */
return ERR_MEM;
}
return ERR_OK;
}
/**
* Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
* from incoming message into array.
*
* @param p points to a pbuf holding an ASN1 coded raw data
* @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data
* @param len length of the coded raw data (zero is valid, e.g. empty string!)
* @param raw_len length of the raw return value
* @param raw return raw bytes
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
*/
err_t
snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len)
{
if (len > buf_max_len) {
/* not enough dst space */
return ERR_MEM;
}
*buf_len = len;
while (len > 0) {
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
buf++;
len--;
}
return ERR_OK;
}
#endif /* LWIP_SNMP */

105
src/apps/snmp/snmp_asn1.h Normal file
View File

@@ -0,0 +1,105 @@
/**
* @file
* Abstract Syntax Notation One (ISO 8824, 8825) codec.
*/
/*
* 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>
*/
#ifndef LWIP_HDR_APPS_SNMP_ASN1_H
#define LWIP_HDR_APPS_SNMP_ASN1_H
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP
#include "lwip/err.h"
#include "lwip/apps/snmp_core.h"
#include "snmp_pbuf_stream.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SNMP_ASN1_TLV_INDEFINITE_LENGTH 0x80
#define SNMP_ASN1_CLASS_MASK 0xC0
#define SNMP_ASN1_CONTENTTYPE_MASK 0x20
#define SNMP_ASN1_DATATYPE_MASK 0x1F
#define SNMP_ASN1_DATATYPE_EXTENDED 0x1F // DataType indicating that datatype is encoded in following bytes
/* context specific (SNMP) tags (from SNMP spec. RFC1157) */
#define SNMP_ASN1_CONTEXT_PDU_GET_REQ 0
#define SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ 1
#define SNMP_ASN1_CONTEXT_PDU_GET_RESP 2
#define SNMP_ASN1_CONTEXT_PDU_SET_REQ 3
#define SNMP_ASN1_CONTEXT_PDU_TRAP 4
#define SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ 5
#define SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT 0
#define SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW 2
struct snmp_asn1_tlv
{
u8_t type; /* only U8 because extended types are not specified by SNMP */
u8_t type_len; /* encoded length of 'type' field (normally 1) */
u8_t length_len; /* indicates how many bytes are required to encode the 'value_len' field */
u16_t value_len; /* encoded length of the value */
};
#define SNMP_ASN1_TLV_LENGTH(tlv) ((tlv).type_len + (tlv).length_len + (tlv).value_len)
#define SNMP_ASN1_SET_TLV_PARAMS(tlv, type_, length_len_, value_len_) do { (tlv).type = (type_); (tlv).type_len = 0; (tlv).length_len = (length_len_); (tlv).value_len = (value_len_); } while (0);
err_t snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv);
err_t snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value);
err_t snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value);
err_t snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value);
err_t snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len);
err_t snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len);
err_t snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv);
void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed);
void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed);
void snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed);
void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed);
void snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed);
err_t snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len);
err_t snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value);
err_t snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value);
err_t snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value);
err_t snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len);
#ifdef __cplusplus
}
#endif
#endif /* LWIP_SNMP */
#endif /* LWIP_HDR_APPS_SNMP_ASN1_H */

942
src/apps/snmp/snmp_core.c Normal file
View File

@@ -0,0 +1,942 @@
/**
* @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>
*/
#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"
#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 *default_mibs[] = { &mib2 };
static u8_t snmp_num_mibs = 1;
#else
static const struct snmp_mib *default_mibs[] = { NULL };
static u8_t snmp_num_mibs = 0;
#endif
/* List of known mibs */
static struct snmp_mib const **snmp_mibs = default_mibs;
/**
* 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;
}
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;
}
}
const struct snmp_obj_id* snmp_get_device_enterprise_oid(void)
{
return snmp_device_enterprise_oid;
}
/**
* Conversion from oid to lwIP ip_addr
* @param ident points to u32_t ident[4] input
* @param ip points to output struct
*/
u8_t
snmp_oid_to_ip(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_ANY);
return 0;
}
IP4_ADDR(ip, oid[0], oid[1], oid[2], oid[3]);
return 1;
}
/**
* Conversion from lwIP ip_addr to oid
* @param ip points to input struct
* @param ident points to u32_t ident[4] output
*/
void
snmp_ip_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);
}
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));
}
}
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));
}
}
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);
}
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;
}
}
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;
}
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;
}
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));
LWIP_ASSERT("'oid_len' param must be greater than 0!", (oid_len > 0));
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));
LWIP_ASSERT("'oid_len' param must be greater than 0!", (oid_len > 0));
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*)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*)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*)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*)(*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*)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;
}
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;
}
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;
}
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)
{
SMEMCPY(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;
}
u8_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 % 8) != 0);
} 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) / 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 */

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* 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.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Martin Hentschel <info@cl-soft.de>
*
*/
#ifndef LWIP_HDR_APPS_SNMP_CORE_PRIV_H
#define LWIP_HDR_APPS_SNMP_CORE_PRIV_H
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
#include "lwip/apps/snmp_core.h"
#include "snmp_asn1.h"
#ifdef __cplusplus
extern "C" {
#endif
/* (outdated) SNMPv1 error codes
* shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request
*/
#define SNMP_ERR_NOSUCHNAME 2
#define SNMP_ERR_BADVALUE 3
#define SNMP_ERR_READONLY 4
/* error codes which are internal and shall not be used by MIBS
* shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request
*/
#define SNMP_ERR_TOOBIG 1
#define SNMP_ERR_AUTHORIZATIONERROR 16
#define SNMP_ERR_NOSUCHOBJECT SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT
#define SNMP_ERR_ENDOFMIBVIEW SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW
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* snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id* oidret);
typedef u8_t (*snmp_validate_node_instance_method)(struct snmp_node_instance*, void*);
u8_t snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance* node_instance);
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);
#endif /* LWIP_SNMP */
#endif /* LWIP_HDR_APPS_SNMP_CORE_PRIV_H */

2243
src/apps/snmp/snmp_mib2.c Normal file

File diff suppressed because it is too large Load Diff

1183
src/apps/snmp/snmp_msg.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -30,24 +30,22 @@
* OF SUCH DAMAGE.
*
* Author: Christiaan Simons <christiaan.simons@axon.tv>
* Martin Hentschel <info@cl-soft.de>
*/
#ifndef LWIP_HDR_SNMP_MSG_H
#define LWIP_HDR_SNMP_MSG_H
#ifndef LWIP_HDR_APPS_SNMP_MSG_H
#define LWIP_HDR_APPS_SNMP_MSG_H
#include "lwip/apps/snmp_opts.h"
#include "lwip/apps/snmp.h"
#include "lwip/apps/snmp_structs.h"
#include "lwip/ip_addr.h"
#include "lwip/err.h"
#if LWIP_SNMP
#if SNMP_PRIVATE_MIB
/* When using a private MIB, you have to create a file 'private_mib.h' that contains
* a 'struct mib_array_node mib_private' which contains your MIB. */
#include "private_mib.h"
#endif
#include "lwip/apps/snmp.h"
#include "lwip/apps/snmp_core.h"
#include "snmp_pbuf_stream.h"
#include "lwip/ip_addr.h"
#include "lwip/err.h"
#ifdef __cplusplus
extern "C" {
@@ -64,249 +62,87 @@ extern "C" {
#define SNMP_TRAP_PORT 162
#endif
#define SNMP_ES_NOERROR 0
#define SNMP_ES_TOOBIG 1
#define SNMP_ES_NOSUCHNAME 2
#define SNMP_ES_BADVALUE 3
#define SNMP_ES_READONLY 4
#define SNMP_ES_GENERROR 5
#define SNMP_GENTRAP_COLDSTART 0
#define SNMP_GENTRAP_WARMSTART 1
#define SNMP_GENTRAP_AUTHFAIL 4
#define SNMP_GENTRAP_ENTERPRISESPC 6
/* version defines used in PDU */
#define SNMP_VERSION_1 0
#define SNMP_VERSION_2c 1
struct snmp_varbind
{
/* next pointer, NULL for last in list */
struct snmp_varbind *next;
/* previous pointer, NULL for first in list */
struct snmp_varbind *prev;
/* object identifier */
struct snmp_obj_id oid;
/* object identifier length (in s32_t) */
u8_t ident_len;
/* object identifier array */
s32_t *ident;
/* object value ASN1 type */
u8_t value_type;
/* value ASN1 type */
u8_t type;
/* object value length */
u16_t value_len;
/* object value */
void *value;
/* encoding varbind seq length length */
u8_t seqlenlen;
/* encoding object identifier length length */
u8_t olenlen;
/* encoding object value length length */
u8_t vlenlen;
/* encoding varbind seq length */
u16_t seqlen;
/* encoding object identifier length */
u16_t olen;
/* encoding object value length */
u16_t vlen;
};
struct snmp_varbind_root
struct snmp_varbind_enumerator
{
struct snmp_varbind *head;
struct snmp_varbind *tail;
/* number of variable bindings in list */
u8_t count;
/* encoding varbind-list seq length length */
u8_t seqlenlen;
/* encoding varbind-list seq length */
u16_t seqlen;
struct snmp_pbuf_stream pbuf_stream;
u16_t varbind_count;
};
/** output response message header length fields */
struct snmp_resp_header_lengths
typedef u8_t snmp_vb_enumerator_err_t;
#define SNMP_VB_ENUMERATOR_ERR_OK 0
#define SNMP_VB_ENUMERATOR_ERR_EOVB 1
#define SNMP_VB_ENUMERATOR_ERR_ASN1ERROR 2
#define SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH 3
void snmp_vb_enumerator_init(struct snmp_varbind_enumerator* enumerator, struct pbuf* p, u16_t offset, u16_t length);
snmp_vb_enumerator_err_t snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct snmp_varbind* varbind);
struct snmp_request
{
/* encoding error-index length length */
u8_t erridxlenlen;
/* encoding error-status length length */
u8_t errstatlenlen;
/* encoding request id length length */
u8_t ridlenlen;
/* encoding pdu length length */
u8_t pdulenlen;
/* encoding community length length */
u8_t comlenlen;
/* encoding version length length */
u8_t verlenlen;
/* encoding sequence length length */
u8_t seqlenlen;
/* encoding error-index length */
u16_t erridxlen;
/* encoding error-status length */
u16_t errstatlen;
/* encoding request id length */
u16_t ridlen;
/* encoding pdu length */
u16_t pdulen;
/* encoding community length */
u16_t comlen;
/* encoding version length */
u16_t verlen;
/* encoding sequence length */
u16_t seqlen;
};
/** output response message header length fields */
struct snmp_trap_header_lengths
{
/* encoding timestamp length length */
u8_t tslenlen;
/* encoding specific-trap length length */
u8_t strplenlen;
/* encoding generic-trap length length */
u8_t gtrplenlen;
/* encoding agent-addr length length */
u8_t aaddrlenlen;
/* encoding enterprise-id length length */
u8_t eidlenlen;
/* encoding pdu length length */
u8_t pdulenlen;
/* encoding community length length */
u8_t comlenlen;
/* encoding version length length */
u8_t verlenlen;
/* encoding sequence length length */
u8_t seqlenlen;
/* encoding timestamp length */
u16_t tslen;
/* encoding specific-trap length */
u16_t strplen;
/* encoding generic-trap length */
u16_t gtrplen;
/* encoding agent-addr length */
u16_t aaddrlen;
/* encoding enterprise-id length */
u16_t eidlen;
/* encoding pdu length */
u16_t pdulen;
/* encoding community length */
u16_t comlen;
/* encoding version length */
u16_t verlen;
/* encoding sequence length */
u16_t seqlen;
};
/* Accepting new SNMP messages. */
#define SNMP_MSG_EMPTY 0
/* Search for matching object for variable binding. */
#define SNMP_MSG_SEARCH_OBJ 1
/* Perform SNMP operation on in-memory object.
Pass-through states, for symmetry only. */
#define SNMP_MSG_INTERNAL_GET_OBJDEF 2
#define SNMP_MSG_INTERNAL_GET_VALUE 3
#define SNMP_MSG_INTERNAL_SET_TEST 4
#define SNMP_MSG_INTERNAL_GET_OBJDEF_S 5
#define SNMP_MSG_INTERNAL_SET_VALUE 6
/* Perform SNMP operation on object located externally.
In theory this could be used for building a proxy agent.
Practical use is for an enterprise spc. app. gateway. */
#define SNMP_MSG_EXTERNAL_GET_OBJDEF 7
#define SNMP_MSG_EXTERNAL_GET_VALUE 8
#define SNMP_MSG_EXTERNAL_SET_TEST 9
#define SNMP_MSG_EXTERNAL_GET_OBJDEF_S 10
#define SNMP_MSG_EXTERNAL_SET_VALUE 11
#define SNMP_COMMUNITY_STR_LEN 64
struct snmp_msg_pstat
{
/* lwIP local port (161) binding */
struct udp_pcb *pcb;
/* Communication handle */
void *handle;
/* source IP address */
ip_addr_t sip;
const ip_addr_t *source_ip;
/* source UDP port */
u16_t sp;
u16_t source_port;
/* incoming snmp version */
u8_t version;
/* community name (zero terminated) */
u8_t community[SNMP_MAX_COMMUNITY_STR_LEN + 1];
/* community string length (exclusive zero term) */
u16_t community_strlen;
/* request type */
u8_t rt;
u8_t request_type;
/* request ID */
s32_t rid;
s32_t request_id;
/* error status */
s32_t error_status;
/* error index */
s32_t error_index;
/* community name (zero terminated) */
u8_t community[SNMP_COMMUNITY_STR_LEN + 1];
/* community string length (exclusive zero term) */
u8_t com_strlen;
/* one out of MSG_EMPTY, MSG_DEMUX, MSG_INTERNAL, MSG_EXTERNAL_x */
u8_t state;
/* saved arguments for MSG_EXTERNAL_x */
const struct mib_external_node *ext_mib_node;
struct snmp_name_ptr ext_name_ptr;
struct obj_def ext_object_def;
struct snmp_obj_id ext_oid;
/* index into input variable binding list */
u8_t vb_idx;
/* ptr into input variable binding list */
struct snmp_varbind *vb_ptr;
/* list of variable bindings from input */
struct snmp_varbind_root invb;
/* list of variable bindings to output */
struct snmp_varbind_root outvb;
/* output response lengths used in ASN encoding */
struct snmp_resp_header_lengths rhl;
/* non-repeaters (getBulkRequest (SNMPv2c)) */
s32_t non_repeaters;
/* max-repetitions (getBulkRequest (SNMPv2c)) */
s32_t max_repetitions;
struct pbuf *inbound_pbuf;
struct snmp_varbind_enumerator inbound_varbind_enumerator;
u16_t inbound_varbind_offset;
u16_t inbound_varbind_len;
struct pbuf *outbound_pbuf;
struct snmp_pbuf_stream outbound_pbuf_stream;
u16_t outbound_pdu_offset;
u16_t outbound_error_status_offset;
u16_t outbound_error_index_offset;
u16_t outbound_varbind_offset;
u8_t value_buffer[SNMP_MAX_VALUE_SIZE];
};
struct snmp_msg_trap
{
/* lwIP local port (161) binding */
struct udp_pcb *pcb;
/* destination IP address in network order */
ip_addr_t dip;
/* source enterprise ID (sysObjectID) */
const struct snmp_obj_id *enterprise;
/* source IP address, raw network order format */
u8_t sip_raw[4];
/* source IP address length */
u8_t sip_raw_len;
/* generic trap code */
u32_t gen_trap;
/* specific trap code */
u32_t spc_trap;
/* timestamp */
u32_t ts;
/* list of variable bindings to output */
struct snmp_varbind_root outvb;
/* output trap lengths used in ASN encoding */
struct snmp_trap_header_lengths thl;
};
/** Agent Version constant, 0 = v1 oddity */
extern const s32_t snmp_version;
/** Agent community string */
extern const char *snmp_community;
#if SNMP_COMMUNITY_EXT
/** Agent community string for write access */
extern const char *snmp_community_write;
/** Agent community string for sending traps */
extern const char *snmp_community_trap;
#endif /* SNMP_COMMUNITY_EXT */
extern struct snmp_msg_trap trap_msg;
/** Varbind-list functions. */
struct snmp_varbind* snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u16_t len);
void snmp_varbind_free(struct snmp_varbind *vb);
void snmp_varbind_list_free(struct snmp_varbind_root *root);
void snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb);
struct snmp_varbind* snmp_varbind_tail_remove(struct snmp_varbind_root *root);
/** Handle an internal (recv) or external (private response) event. */
err_t snmp_send_response(struct snmp_msg_pstat *m_stat);
err_t snmp_send_trap(s8_t generic_trap, const struct snmp_obj_id *eoid, s32_t specific_trap);
void snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port);
err_t snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port);
#ifdef __cplusplus
}
@@ -314,4 +150,4 @@ err_t snmp_send_trap(s8_t generic_trap, const struct snmp_obj_id *eoid, s32_t sp
#endif /* LWIP_SNMP */
#endif /* LWIP_HDR_SNMP_MSG_H */
#endif /* LWIP_HDR_APPS_SNMP_MSG_H */

View File

@@ -0,0 +1,97 @@
/**
* @file
* SNMP netconn frontend.
*/
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* 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: Dirk Ziegelmeier <dziegel@gmx.de>
*/
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP && SNMP_USE_NETCONN
#include "lwip/api.h"
#include "lwip/udp.h"
#include "snmp_msg.h"
#include "lwip/sys.h"
/** SNMP netconn API worker thread */
static void
snmp_netconn_thread(void *arg)
{
struct netconn *conn;
struct netbuf *buf;
err_t err;
LWIP_UNUSED_ARG(arg);
conn = netconn_new(NETCONN_UDP);
LWIP_ERROR("snmp_netconn: invalid conn", (conn != NULL), return;);
//trap_msg.handle = conn;
//trap_msg.lip = &conn->pcb.udp->local_ip;
/* Bind to SNMP port with default IP address */
netconn_bind(conn, IP_ADDR_ANY, SNMP_IN_PORT);
do {
err = netconn_recv(conn, &buf);
if (err == ERR_OK) {
snmp_receive(conn, buf->p, &buf->addr, buf->port);
}
if(buf != NULL) {
netbuf_delete(buf);
}
} while(1);
}
err_t
snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port)
{
err_t result;
struct netbuf buf;
memset(&buf, 0, sizeof(buf));
buf.p = p;
result = netconn_sendto((struct netconn*)handle, &buf, dst, port);
return result;
}
/**
* Starts SNMP Agent.
*/
void
snmp_init(void)
{
sys_thread_new("snmp_netconn", snmp_netconn_thread, NULL, SNMP_STACK_SIZE, SNMP_THREAD_PRIO);
}
#endif /* LWIP_SNMP && SNMP_USE_NETCONN */

View File

@@ -0,0 +1,246 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* 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.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Martin Hentschel <info@cl-soft.de>
*
*/
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
#include "snmp_pbuf_stream.h"
#include "lwip/def.h"
err_t
snmp_pbuf_stream_init(struct snmp_pbuf_stream* pbuf_stream, struct pbuf* p, u16_t offset, u16_t length)
{
pbuf_stream->offset = offset;
pbuf_stream->length = length;
// skip to matching buffer
while (offset >= p->len) {
offset -= p->len;
p = p->next;
if (p == NULL) {
return ERR_BUF;
}
}
pbuf_stream->pbuffer = p;
pbuf_stream->pbuf_len = p->len;
if (offset > 0) {
pbuf_stream->pbuf_len-= offset;
pbuf_stream->pData = (u8_t*)(p->payload) + offset;
} else {
pbuf_stream->pData = (u8_t*)(p->payload);
}
return ERR_OK;
}
err_t
snmp_pbuf_stream_read(struct snmp_pbuf_stream* pbuf_stream, u8_t* data)
{
if (pbuf_stream->length == 0) {
return ERR_BUF;
}
if (pbuf_stream->pbuf_len == 0) {
/* we are at the end of current pbuf, skip to next */
pbuf_stream->pbuffer = pbuf_stream->pbuffer->next;
if ((pbuf_stream->pbuffer == NULL) || (pbuf_stream->pbuffer->len == 0)) {
return ERR_BUF;
}
pbuf_stream->pbuf_len = pbuf_stream->pbuffer->len;
pbuf_stream->pData = (u8_t*)(pbuf_stream->pbuffer->payload);
}
*data = *pbuf_stream->pData;
pbuf_stream->pData++;
pbuf_stream->pbuf_len--;
pbuf_stream->offset++;
pbuf_stream->length--;
return ERR_OK;
}
err_t
snmp_pbuf_stream_write(struct snmp_pbuf_stream* pbuf_stream, u8_t data)
{
if (pbuf_stream->length == 0) {
return ERR_BUF;
}
if (pbuf_stream->pbuf_len == 0) {
/* we are at the end of current pbuf, skip to next */
pbuf_stream->pbuffer = pbuf_stream->pbuffer->next;
if ((pbuf_stream->pbuffer == NULL) || (pbuf_stream->pbuffer->len == 0)) {
return ERR_BUF;
}
pbuf_stream->pbuf_len = pbuf_stream->pbuffer->len;
pbuf_stream->pData = (u8_t*)(pbuf_stream->pbuffer->payload);
}
*pbuf_stream->pData = data;
pbuf_stream->pData++;
pbuf_stream->pbuf_len--;
pbuf_stream->offset++;
pbuf_stream->length--;
return ERR_OK;
}
err_t
snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream* pbuf_stream, const void* buf, u16_t buf_len)
{
if (pbuf_stream->length < buf_len) {
return ERR_BUF;
}
while (buf_len > 0) {
u16_t chunk_len;
if (pbuf_stream->pbuf_len == 0) {
/* we are at the end of current pbuf, skip to next */
pbuf_stream->pbuffer = pbuf_stream->pbuffer->next;
if ((pbuf_stream->pbuffer == NULL) || (pbuf_stream->pbuffer->len == 0)) {
return ERR_BUF;
}
pbuf_stream->pbuf_len = pbuf_stream->pbuffer->len;
pbuf_stream->pData = (u8_t*)(pbuf_stream->pbuffer->payload);
}
chunk_len = LWIP_MIN(buf_len, pbuf_stream->pbuf_len);
MEMCPY(pbuf_stream->pData, buf, chunk_len);
pbuf_stream->pData += chunk_len;
pbuf_stream->pbuf_len -= chunk_len;
pbuf_stream->offset += chunk_len;
pbuf_stream->length -= chunk_len;
buf_len -= chunk_len;
buf = (const u8_t*)buf + chunk_len;
}
return ERR_OK;
}
err_t
snmp_pbuf_stream_writeto(struct snmp_pbuf_stream* pbuf_stream, struct snmp_pbuf_stream* target_pbuf_stream, u16_t len)
{
if ((pbuf_stream == NULL) || (target_pbuf_stream == NULL)) {
return ERR_ARG;
}
if ((len > pbuf_stream->length) || (len > target_pbuf_stream->length)) {
return ERR_ARG;
}
if (len == 0) {
len = LWIP_MIN(pbuf_stream->length, target_pbuf_stream->length);
}
while (len > 0) {
u16_t chunk_len;
err_t err;
if (pbuf_stream->pbuf_len == 0) {
/* we are at the end of current pbuf, skip to next */
pbuf_stream->pbuffer = pbuf_stream->pbuffer->next;
if ((pbuf_stream->pbuffer == NULL) || (pbuf_stream->pbuffer->len == 0)) {
return ERR_BUF;
}
pbuf_stream->pbuf_len = pbuf_stream->pbuffer->len;
pbuf_stream->pData = (u8_t*)(pbuf_stream->pbuffer->payload);
}
chunk_len = LWIP_MIN(len, pbuf_stream->pbuf_len);
err = snmp_pbuf_stream_writebuf(target_pbuf_stream, pbuf_stream->pData, chunk_len);
if (err != ERR_OK) {
return err;
}
pbuf_stream->pData += chunk_len;
pbuf_stream->pbuf_len -= chunk_len;
pbuf_stream->offset += chunk_len;
pbuf_stream->length -= chunk_len;
len -= chunk_len;
}
return ERR_OK;
}
err_t
snmp_pbuf_stream_seek(struct snmp_pbuf_stream* pbuf_stream, s32_t offset)
{
if ((offset < 0) || (offset > pbuf_stream->length)) {
/* we cannot seek backwards or forward behind stream end */
return ERR_ARG;
}
pbuf_stream->offset += (u16_t)offset;
pbuf_stream->length -= (u16_t)offset;
// skipt to matching buffer
while (offset > pbuf_stream->pbuf_len) {
offset -= pbuf_stream->pbuf_len;
pbuf_stream->pbuffer = pbuf_stream->pbuffer->next;
if ((pbuf_stream->pbuffer == NULL) || (pbuf_stream->pbuffer->len == 0)) {
return ERR_BUF;
}
pbuf_stream->pbuf_len = pbuf_stream->pbuffer->len;
pbuf_stream->pData = (u8_t*)pbuf_stream->pbuffer->payload;
}
if (offset > 0) {
pbuf_stream->pbuf_len -= (u16_t)offset;
pbuf_stream->pData = pbuf_stream->pData + offset;
}
return ERR_OK;
}
err_t
snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream* pbuf_stream, u32_t offset)
{
s32_t rel_offset = offset - pbuf_stream->offset;
return snmp_pbuf_stream_seek(pbuf_stream, rel_offset);
}
#endif /* LWIP_SNMP */

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* 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.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Martin Hentschel <info@cl-soft.de>
*
*/
#ifndef LWIP_HDR_APPS_SNMP_PBUF_STREAM_H
#define LWIP_HDR_APPS_SNMP_PBUF_STREAM_H
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP
#include "lwip/err.h"
#include "lwip/pbuf.h"
#ifdef __cplusplus
extern "C" {
#endif
struct snmp_pbuf_stream
{
struct pbuf* pbuffer;
u16_t pbuf_len;
u8_t* pData;
u16_t offset;
u16_t length;
};
err_t snmp_pbuf_stream_init(struct snmp_pbuf_stream* pbuf_stream, struct pbuf* p, u16_t offset, u16_t length);
err_t snmp_pbuf_stream_read(struct snmp_pbuf_stream* pbuf_stream, u8_t* data);
err_t snmp_pbuf_stream_write(struct snmp_pbuf_stream* pbuf_stream, u8_t data);
err_t snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream* pbuf_stream, const void* buf, u16_t buf_len);
err_t snmp_pbuf_stream_writeto(struct snmp_pbuf_stream* pbuf_stream, struct snmp_pbuf_stream* target_pbuf_stream, u16_t len);
err_t snmp_pbuf_stream_seek(struct snmp_pbuf_stream* pbuf_stream, s32_t offset);
err_t snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream* pbuf_stream, u32_t offset);
#ifdef __cplusplus
}
#endif
#endif /* LWIP_SNMP */
#endif /* LWIP_HDR_APPS_SNMP_PBUF_STREAM_H */

81
src/apps/snmp/snmp_raw.c Normal file
View File

@@ -0,0 +1,81 @@
/**
* @file
* SNMP RAW API frontend.
*/
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* 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: Dirk Ziegelmeier <dziegel@gmx.de>
*/
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP && SNMP_USE_RAW
#include "lwip/udp.h"
#include "snmp_msg.h"
/* UDP Protocol Control Block */
static struct udp_pcb *snmp_pcb;
/* lwIP UDP receive callback function */
static void
snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
LWIP_UNUSED_ARG(arg);
snmp_receive(pcb, p, addr, port);
pbuf_free(p);
}
err_t
snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port)
{
err_t result = udp_sendto((struct udp_pcb*)handle, p, dst, port);
return result;
}
/**
* Starts SNMP Agent.
* Allocates UDP pcb and binds it to IP_ADDR_ANY port 161.
*/
void
snmp_init(void)
{
snmp_pcb = udp_new();
if (snmp_pcb != NULL) {
//trap_msg.handle = snmp_pcb;
//trap_msg.lip = &snmp_pcb->local_ip;
udp_recv(snmp_pcb, snmp_recv, (void *)SNMP_IN_PORT);
udp_bind(snmp_pcb, IP_ADDR_ANY, SNMP_IN_PORT);
}
}
#endif /* LWIP_SNMP && SNMP_USE_RAW */

232
src/apps/snmp/snmp_scalar.c Normal file
View File

@@ -0,0 +1,232 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* 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.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Martin Hentschel <info@cl-soft.de>
*
*/
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
#include "lwip/apps/snmp_scalar.h"
#include "lwip/apps/snmp_core.h"
static u16_t snmp_scalar_array_get_value(struct snmp_node_instance* instance, void* value);
static snmp_err_t snmp_scalar_array_set_test(struct snmp_node_instance* instance, u16_t value_len, void* value);
static snmp_err_t snmp_scalar_array_set_value(struct snmp_node_instance* instance, u16_t value_len, void* value);
snmp_err_t
snmp_scalar_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
{
const struct snmp_scalar_node* scalar_node = (const struct snmp_scalar_node*)instance->node;
LWIP_UNUSED_ARG(root_oid);
LWIP_UNUSED_ARG(root_oid_len);
/* scalar only has one dedicated instance: .0 */
if ((instance->instance_oid.len != 1) || (instance->instance_oid.id[0] != 0))
{
return SNMP_ERR_NOSUCHINSTANCE;
}
instance->access = scalar_node->access;
instance->asn1_type = scalar_node->asn1_type;
instance->get_value = scalar_node->get_value;
instance->set_test = scalar_node->set_test;
instance->set_value = scalar_node->set_value;
return SNMP_ERR_NOERROR;
}
snmp_err_t
snmp_scalar_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
{
/* because our only instance is .0 we can only return a next instance if no instance oid is passed */
if (instance->instance_oid.len == 0)
{
instance->instance_oid.len = 1;
instance->instance_oid.id[0] = 0;
return snmp_scalar_get_instance(root_oid, root_oid_len, instance);
}
return SNMP_ERR_NOSUCHINSTANCE;
}
snmp_err_t
snmp_scalar_array_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
{
LWIP_UNUSED_ARG(root_oid);
LWIP_UNUSED_ARG(root_oid_len);
if ((instance->instance_oid.len == 2) && (instance->instance_oid.id[1] == 0))
{
const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)instance->node;
const struct snmp_scalar_array_node_def* array_node_def = array_node->array_nodes;
u32_t i = 0;
while (i < array_node->array_node_count)
{
if (array_node_def->oid == instance->instance_oid.id[0])
{
break;
}
array_node_def++;
i++;
}
if (i < array_node->array_node_count)
{
instance->access = array_node_def->access;
instance->asn1_type = array_node_def->asn1_type;
instance->get_value = snmp_scalar_array_get_value;
instance->set_test = snmp_scalar_array_set_test;
instance->set_value = snmp_scalar_array_set_value;
instance->reference.const_ptr = array_node_def;
return SNMP_ERR_NOERROR;
}
}
return SNMP_ERR_NOSUCHINSTANCE;
}
snmp_err_t
snmp_scalar_array_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
{
const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)instance->node;
const struct snmp_scalar_array_node_def* array_node_def = array_node->array_nodes;
const struct snmp_scalar_array_node_def* result = NULL;
LWIP_UNUSED_ARG(root_oid);
LWIP_UNUSED_ARG(root_oid_len);
if ((instance->instance_oid.len == 0) && (array_node->array_node_count > 0))
{
/* return node with lowest OID */
u16_t i = 0;
result = array_node_def;
array_node_def++;
for (i = 1; i < array_node->array_node_count; i++)
{
if (array_node_def->oid < result->oid)
{
result = array_node_def;
}
array_node_def++;
}
}
else if (instance->instance_oid.len >= 1)
{
if (instance->instance_oid.len == 1)
{
/* if we have the requested OID we return its instance, otherwise we search for the next available */
u16_t i = 0;
while (i < array_node->array_node_count)
{
if (array_node_def->oid == instance->instance_oid.id[0])
{
result = array_node_def;
break;
}
array_node_def++;
i++;
}
}
if (result == NULL)
{
u32_t oid_dist = 0xFFFFFFFFUL;
u16_t i = 0;
array_node_def = array_node->array_nodes; /* may be already at the end when if case before was executed without result -> reinitialize to start */
while (i < array_node->array_node_count)
{
if ((array_node_def->oid > instance->instance_oid.id[0]) && ((u32_t)(array_node_def->oid - instance->instance_oid.id[0]) < oid_dist))
{
result = array_node_def;
oid_dist = array_node_def->oid - instance->instance_oid.id[0];
}
array_node_def++;
i++;
}
}
}
if (result == NULL)
{
/* nothing to return */
return SNMP_ERR_NOSUCHINSTANCE;
}
instance->instance_oid.len = 2;
instance->instance_oid.id[0] = result->oid;
instance->instance_oid.id[1] = 0;
instance->access = result->access;
instance->asn1_type = result->asn1_type;
instance->get_value = snmp_scalar_array_get_value;
instance->set_test = snmp_scalar_array_set_test;
instance->set_value = snmp_scalar_array_set_value;
instance->reference.const_ptr = result;
return SNMP_ERR_NOERROR;
}
static u16_t
snmp_scalar_array_get_value(struct snmp_node_instance* instance, void* value)
{
const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)instance->node;
const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr;
return array_node->get_value(array_node_def, value);
}
static snmp_err_t
snmp_scalar_array_set_test(struct snmp_node_instance* instance, u16_t value_len, void* value)
{
const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)instance->node;
const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr;
return array_node->set_test(array_node_def, value_len, value);
}
static snmp_err_t
snmp_scalar_array_set_value(struct snmp_node_instance* instance, u16_t value_len, void* value)
{
const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)instance->node;
const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr;
return array_node->set_value(array_node_def, value_len, value);
}
#endif /* LWIP_SNMP */

362
src/apps/snmp/snmp_table.c Normal file
View File

@@ -0,0 +1,362 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* 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.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Martin Hentschel <info@cl-soft.de>
*
*/
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
#include "lwip/apps/snmp_core.h"
#include "lwip/apps/snmp_table.h"
snmp_err_t snmp_table_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
{
snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE;
const struct snmp_table_node* table_node = (const struct snmp_table_node*)instance->node;
LWIP_UNUSED_ARG(root_oid);
LWIP_UNUSED_ARG(root_oid_len);
/* check min. length (fixed row entry definition, column, row instance oid with at least one entry */
/* fixed row entry always has oid 1 */
if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1))
{
/* search column */
const struct snmp_table_col_def* col_def = table_node->columns;
u16_t i = table_node->column_count;
while (i > 0)
{
if (col_def->index == instance->instance_oid.id[1])
{
break;
}
col_def++;
i--;
}
if (i > 0)
{
/* everything may be overwritten by get_cell_instance_method() in order to implement special handling for single columns/cells */
instance->asn1_type = col_def->asn1_type;
instance->access = col_def->access;
instance->get_value = table_node->get_value;
instance->set_test = table_node->set_test;
instance->set_value = table_node->set_value;
ret = table_node->get_cell_instance(
&(instance->instance_oid.id[1]),
&(instance->instance_oid.id[2]),
instance->instance_oid.len-2,
instance);
}
}
return ret;
}
snmp_err_t snmp_table_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
{
const struct snmp_table_node* table_node = (const struct snmp_table_node*)instance->node;
const struct snmp_table_col_def* col_def;
struct snmp_obj_id row_oid;
u32_t column = 0;
snmp_err_t result;
LWIP_UNUSED_ARG(root_oid);
LWIP_UNUSED_ARG(root_oid_len);
/* check that first part of id is 0 or 1, referencing fixed row entry */
if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1))
{
return SNMP_ERR_NOSUCHINSTANCE;
}
if (instance->instance_oid.len > 1)
{
column = instance->instance_oid.id[1];
}
if (instance->instance_oid.len > 2)
{
snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2);
}
else
{
row_oid.len = 0;
}
instance->get_value = table_node->get_value;
instance->set_test = table_node->set_test;
instance->set_value = table_node->set_value;
/* resolve column and value */
do
{
u16_t i;
const struct snmp_table_col_def* next_col_def = NULL;
col_def = table_node->columns;
for (i=0; i<table_node->column_count; i++)
{
if (col_def->index == column)
{
next_col_def = col_def;
break;
}
else if ((col_def->index > column) && ((next_col_def == NULL) || (col_def->index < next_col_def->index)))
{
next_col_def = col_def;
}
col_def++;
}
if (next_col_def == NULL)
{
/* no further column found */
return SNMP_ERR_NOSUCHINSTANCE;
}
instance->asn1_type = next_col_def->asn1_type;
instance->access = next_col_def->access;
result = table_node->get_next_cell_instance(
&next_col_def->index,
&row_oid,
instance);
if (result == SNMP_ERR_NOERROR)
{
col_def = next_col_def;
break;
}
row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */
column = next_col_def->index + 1;
}
while (1);
// build resulting oid
instance->instance_oid.len = 2;
instance->instance_oid.id[0] = 1;
instance->instance_oid.id[1] = col_def->index;
snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len);
return SNMP_ERR_NOERROR;
}
snmp_err_t snmp_table_simple_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
{
snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE;
const struct snmp_table_simple_node* table_node = (const struct snmp_table_simple_node*)instance->node;
LWIP_UNUSED_ARG(root_oid);
LWIP_UNUSED_ARG(root_oid_len);
/* check min. length (fixed row entry definition, column, row instance oid with at least one entry */
/* fixed row entry always has oid 1 */
if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1))
{
ret = table_node->get_cell_value(
&(instance->instance_oid.id[1]),
&(instance->instance_oid.id[2]),
instance->instance_oid.len-2,
&instance->reference,
&instance->reference_len);
if (ret == SNMP_ERR_NOERROR)
{
/* search column */
const struct snmp_table_simple_col_def* col_def = table_node->columns;
u32_t i = table_node->column_count;
while (i > 0)
{
if (col_def->index == instance->instance_oid.id[1])
{
break;
}
col_def++;
i--;
}
if (i > 0)
{
instance->asn1_type = col_def->asn1_type;
instance->access = SNMP_NODE_INSTANCE_READ_ONLY;
instance->set_test = NULL;
instance->set_value = NULL;
switch (col_def->data_type)
{
case SNMP_VARIANT_VALUE_TYPE_U32: instance->get_value = snmp_table_extract_value_from_u32ref; break;
case SNMP_VARIANT_VALUE_TYPE_S32: instance->get_value = snmp_table_extract_value_from_s32ref; break;
case SNMP_VARIANT_VALUE_TYPE_PTR: // fall through
case SNMP_VARIANT_VALUE_TYPE_CONST_PTR: instance->get_value = snmp_table_extract_value_from_refconstptr; break;
default:
LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type));
return SNMP_ERR_GENERROR;
}
ret = SNMP_ERR_NOERROR;
}
else
{
ret = SNMP_ERR_NOSUCHINSTANCE;
}
}
}
return ret;
}
snmp_err_t snmp_table_simple_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
{
const struct snmp_table_simple_node* table_node = (const struct snmp_table_simple_node*)instance->node;
const struct snmp_table_simple_col_def* col_def;
struct snmp_obj_id row_oid;
u32_t column = 0;
snmp_err_t result;
LWIP_UNUSED_ARG(root_oid);
LWIP_UNUSED_ARG(root_oid_len);
/* check that first part of id is 0 or 1, referencing fixed row entry */
if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1))
{
return SNMP_ERR_NOSUCHINSTANCE;
}
if (instance->instance_oid.len > 1)
{
column = instance->instance_oid.id[1];
}
if (instance->instance_oid.len > 2)
{
snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2);
}
else
{
row_oid.len = 0;
}
/* resolve column and value */
do
{
u32_t i;
const struct snmp_table_simple_col_def* next_col_def = NULL;
col_def = table_node->columns;
for (i=0; i<table_node->column_count; i++)
{
if (col_def->index == column)
{
next_col_def = col_def;
break;
}
else if ((col_def->index > column) && ((next_col_def == NULL) || (col_def->index < next_col_def->index)))
{
next_col_def = col_def;
}
col_def++;
}
if (next_col_def == NULL)
{
/* no further column found */
return SNMP_ERR_NOSUCHINSTANCE;
}
result = table_node->get_next_cell_instance_and_value(
&next_col_def->index,
&row_oid,
&instance->reference,
&instance->reference_len);
if (result == SNMP_ERR_NOERROR)
{
col_def = next_col_def;
break;
}
row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */
column = next_col_def->index + 1;
}
while (1);
instance->asn1_type = col_def->asn1_type;
instance->access = SNMP_NODE_INSTANCE_READ_ONLY;
instance->set_test = NULL;
instance->set_value = NULL;
switch (col_def->data_type)
{
case SNMP_VARIANT_VALUE_TYPE_U32: instance->get_value = snmp_table_extract_value_from_u32ref; break;
case SNMP_VARIANT_VALUE_TYPE_S32: instance->get_value = snmp_table_extract_value_from_s32ref; break;
case SNMP_VARIANT_VALUE_TYPE_PTR: // fall through
case SNMP_VARIANT_VALUE_TYPE_CONST_PTR: instance->get_value = snmp_table_extract_value_from_refconstptr; break;
default:
LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type));
return SNMP_ERR_GENERROR;
}
// build resulting oid
instance->instance_oid.len = 2;
instance->instance_oid.id[0] = 1;
instance->instance_oid.id[1] = col_def->index;
snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len);
return SNMP_ERR_NOERROR;
}
u16_t
snmp_table_extract_value_from_s32ref(struct snmp_node_instance* instance, void* value)
{
s32_t *dst = (s32_t*)value;
*dst = instance->reference.s32;
return sizeof(*dst);
}
u16_t
snmp_table_extract_value_from_u32ref(struct snmp_node_instance* instance, void* value)
{
u32_t *dst = (u32_t*)value;
*dst = instance->reference.u32;
return sizeof(*dst);
}
u16_t
snmp_table_extract_value_from_refconstptr(struct snmp_node_instance* instance, void* value)
{
SMEMCPY(value, instance->reference.const_ptr, instance->reference_len);
return (u16_t)instance->reference_len;
}
#endif /* LWIP_SNMP */

View File

@@ -0,0 +1,230 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* 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: Dirk Ziegelmeier <dziegel@gmx.de>
*/
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
#include "lwip/apps/snmp_threadsync.h"
#include "lwip/apps/snmp_core.h"
#include "lwip/mem.h"
#include "lwip/sys.h"
struct threadsync_data
{
union {
snmp_err_t u8;
u16_t u16;
} retval;
union {
const u32_t *root_oid;
void *value;
} arg1;
union {
u8_t root_oid_len;
u16_t len;
} arg2;
const struct snmp_threadsync_node *threadsync_node;
struct snmp_node_instance proxy_instance;
};
static void
call_synced_function(struct threadsync_data *call_data, snmp_threadsync_called_fn fn)
{
sys_mutex_lock(&call_data->threadsync_node->locks->sem_usage_mutex);
call_data->threadsync_node->locks->sync_fn(fn, call_data);
sys_sem_wait(&call_data->threadsync_node->locks->sem);
sys_mutex_unlock(&call_data->threadsync_node->locks->sem_usage_mutex);
}
static void
threadsync_get_value_synced(void *ctx)
{
struct threadsync_data *call_data = (struct threadsync_data*)ctx;
call_data->retval.u16 = call_data->proxy_instance.get_value(&call_data->proxy_instance, call_data->arg1.value);
sys_sem_signal(&call_data->threadsync_node->locks->sem);
}
static u16_t
threadsync_get_value(struct snmp_node_instance* instance, void* value)
{
struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
call_data->arg1.value = value;
call_synced_function(call_data, threadsync_get_value_synced);
return call_data->retval.u16;
}
static void
threadsync_set_test_synced(void *ctx)
{
struct threadsync_data *call_data = (struct threadsync_data*)ctx;
call_data->retval.u8 = call_data->proxy_instance.set_test(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
sys_sem_signal(&call_data->threadsync_node->locks->sem);
}
static snmp_err_t
threadsync_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
{
struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
call_data->arg1.value = value;
call_data->arg2.len = len;
call_synced_function(call_data, threadsync_set_test_synced);
return call_data->retval.u8;
}
static void
threadsync_set_value_synced(void *ctx)
{
struct threadsync_data *call_data = (struct threadsync_data*)ctx;
call_data->retval.u8 = call_data->proxy_instance.set_value(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
sys_sem_signal(&call_data->threadsync_node->locks->sem);
}
static snmp_err_t
threadsync_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
{
struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
call_data->arg1.value = value;
call_data->arg2.len = len;
call_synced_function(call_data, threadsync_set_value_synced);
return call_data->retval.u8;
}
static void
threadsync_release_instance_synced(void* ctx)
{
struct threadsync_data *call_data = (struct threadsync_data*)ctx;
call_data->proxy_instance.release_instance(&call_data->proxy_instance);
sys_sem_signal(&call_data->threadsync_node->locks->sem);
}
static void
threadsync_release_instance(struct snmp_node_instance *instance)
{
struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
if(call_data->proxy_instance.release_instance != NULL) {
call_synced_function(call_data, threadsync_release_instance_synced);
}
mem_free(call_data);
}
static void
get_instance_synced(void* ctx)
{
struct threadsync_data *call_data = (struct threadsync_data*)ctx;
const struct snmp_leaf_node *leaf = (const struct snmp_leaf_node*)call_data->proxy_instance.node;
call_data->retval.u8 = leaf->get_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
sys_sem_signal(&call_data->threadsync_node->locks->sem);
}
static void
get_next_instance_synced(void* ctx)
{
struct threadsync_data *call_data = (struct threadsync_data*)ctx;
const struct snmp_leaf_node *leaf = (const struct snmp_leaf_node*)call_data->proxy_instance.node;
call_data->retval.u8 = leaf->get_next_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
sys_sem_signal(&call_data->threadsync_node->locks->sem);
}
static snmp_err_t
do_sync(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance, snmp_threadsync_called_fn fn)
{
const struct snmp_threadsync_node *threadsync_node = (const struct snmp_threadsync_node*)instance->node;
struct threadsync_data *call_data;
if(threadsync_node->node.node.oid != threadsync_node->target->node.oid) {
LWIP_DEBUGF(SNMP_DEBUG, ("Sync node OID does not match target node OID"));
return SNMP_ERR_NOSUCHINSTANCE;
}
call_data = (struct threadsync_data*)mem_malloc(sizeof(struct threadsync_data));
if(call_data != NULL) {
memset(&call_data->proxy_instance, 0, sizeof(call_data->proxy_instance));
instance->reference.ptr = call_data;
snmp_oid_assign(&call_data->proxy_instance.instance_oid, instance->instance_oid.id, instance->instance_oid.len);
call_data->proxy_instance.node = &threadsync_node->target->node;
call_data->threadsync_node = threadsync_node;
call_data->arg1.root_oid = root_oid;
call_data->arg2.root_oid_len = root_oid_len;
call_synced_function(call_data, fn);
instance->access = call_data->proxy_instance.access;
instance->asn1_type = call_data->proxy_instance.asn1_type;
instance->release_instance = threadsync_release_instance;
instance->get_value = (call_data->proxy_instance.get_value != NULL)? threadsync_get_value : NULL;
instance->set_value = (call_data->proxy_instance.set_value != NULL)? threadsync_set_value : NULL;
instance->set_test = (call_data->proxy_instance.set_test != NULL)? threadsync_set_test : NULL;
snmp_oid_assign(&instance->instance_oid, call_data->proxy_instance.instance_oid.id, call_data->proxy_instance.instance_oid.len);
return call_data->retval.u8;
} else {
LWIP_DEBUGF(SNMP_DEBUG, ("Out of memory"));
return SNMP_ERR_NOSUCHINSTANCE;
}
}
snmp_err_t
snmp_threadsync_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
{
return do_sync(root_oid, root_oid_len, instance, get_instance_synced);
}
snmp_err_t
snmp_threadsync_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
{
return do_sync(root_oid, root_oid_len, instance, get_next_instance_synced);
}
void snmp_threadsync_init(struct snmp_threadsync_locks *locks, snmp_threadsync_synchronizer_fn sync_fn)
{
sys_mutex_new(&locks->sem_usage_mutex);
sys_sem_new(&locks->sem, 0);
locks->sync_fn = sync_fn;
}
#endif /* LWIP_SNMP */

413
src/apps/snmp/snmp_traps.c Normal file
View File

@@ -0,0 +1,413 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* 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.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Martin Hentschel
* Christiaan Simons <christiaan.simons@axon.tv>
*
*/
#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 "lwip/ip_addr.h"
#define snmp_community_trap snmp_community
/** Agent community string for sending traps */
extern const char *snmp_community_trap;
struct snmp_trap_dst
{
/* destination IP address in network order */
ip_addr_t dip;
/* set to 0 when disabled, >0 when enabled */
u8_t enable;
};
static struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS];
static u8_t snmp_auth_traps_enabled = 0;
/** TRAP message structure */
//struct snmp_msg_trap trap_msg;
/**
* Sets enable switch for this trap destination.
* @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
* @param enable switch if 0 destination is disabled >0 enabled.
*/
void
snmp_trap_dst_enable(u8_t dst_idx, u8_t enable)
{
if (dst_idx < SNMP_TRAP_DESTINATIONS) {
trap_dst[dst_idx].enable = enable;
}
}
/**
* Sets IPv4 address for this trap destination.
* @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
* @param dst IPv4 address in host order.
*/
void
snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst)
{
if (dst_idx < SNMP_TRAP_DESTINATIONS) {
ip_addr_set(&trap_dst[dst_idx].dip, dst);
}
}
void
snmp_set_auth_traps_enabled(u8_t enable)
{
snmp_auth_traps_enabled = enable;
}
u8_t
snmp_get_auth_traps_enabled(void)
{
return snmp_auth_traps_enabled;
}
/**
* Sends an generic or enterprise specific trap message.
*
* @param generic_trap is the trap code
* @param eoid points to enterprise object identifier
* @param specific_trap used for enterprise traps when generic_trap == 6
* @return ERR_OK when success, ERR_MEM if we're out of memory
*
* @note the caller is responsible for filling in outvb in the trap_msg
* @note the use of the enterprise identifier field
* is per RFC1215.
* Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
* and .iso.org.dod.internet.private.enterprises.yourenterprise
* (sysObjectID) for specific traps.
*/
static err_t
snmp_send_trap(const struct snmp_obj_id *device_enterprise_oid, s32_t generic_trap, s32_t specific_trap)
{
LWIP_UNUSED_ARG(device_enterprise_oid);
LWIP_UNUSED_ARG(generic_trap);
LWIP_UNUSED_ARG(specific_trap);
return ERR_OK;
//struct snmp_trap_dst *td;
//struct netif *dst_if;
//const ip_addr_t* dst_ip;
//struct pbuf *p;
//u16_t i,tot_len;
//err_t err = ERR_OK;
//for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) {
// if ((td->enable != 0) && !ip_addr_isany(&td->dip)) {
// /* network order trap destination */
// ip_addr_copy(trap_msg.dip, td->dip);
// /* lookup current source address for this dst */
// ip_route_get_local_ip(IP_IS_V6(trap_msg.lip), trap_msg.lip,
// &td->dip, dst_if, dst_ip);
// if ((dst_if != NULL) && (dst_ip != NULL)) {
// trap_msg.sip_raw_len = (IP_IS_V6_VAL(*dst_ip) ? 16 : 4);
// MEMCPY(trap_msg.sip_raw, dst_ip, trap_msg.sip_raw_len);
// if (device_enterprise_oid == NULL) {
// trap_msg.enterprise = snmp_get_device_enterprise_oid();
// } else {
// trap_msg.enterprise = device_enterprise_oid;
// }
// trap_msg.gen_trap = generic_trap;
// if (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) {
// trap_msg.spc_trap = specific_trap;
// } else {
// trap_msg.spc_trap = 0;
// }
// MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts);
// /* pass 0, calculate length fields */
// tot_len = snmp_varbind_list_sum(&trap_msg.outvb);
// tot_len = snmp_trap_header_sum(&trap_msg, tot_len);
// /* allocate pbuf(s) */
// p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM);
// if (p != NULL) {
// u16_t ofs;
// /* pass 1, encode packet ino the pbuf(s) */
// ofs = snmp_trap_header_enc(&trap_msg, p);
// snmp_varbind_list_enc(&trap_msg.outvb, p, ofs);
// snmp_stats.outtraps++;
// snmp_stats.outpkts++;
// /** send to the TRAP destination */
// snmp_sendto(trap_msg.handle, p, &trap_msg.dip, SNMP_TRAP_PORT);
// } else {
// err = ERR_MEM;
// }
// } else {
// /* routing error */
// err = ERR_RTE;
// }
// }
//}
//return err;
}
err_t
snmp_send_trap_generic(s32_t generic_trap)
{
return snmp_send_trap(NULL, generic_trap, 0);
}
err_t snmp_send_trap_specific(s32_t specific_trap)
{
return snmp_send_trap(NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap);
}
void
snmp_coldstart_trap(void)
{
//trap_msg.outvb.head = NULL;
//trap_msg.outvb.tail = NULL;
//trap_msg.outvb.count = 0;
snmp_send_trap_generic(SNMP_GENTRAP_COLDSTART);
}
void
snmp_authfail_trap(void)
{
if (snmp_auth_traps_enabled != 0) {
//trap_msg.outvb.head = NULL;
//trap_msg.outvb.tail = NULL;
//trap_msg.outvb.count = 0;
snmp_send_trap_generic(SNMP_GENTRAP_AUTH_FAILURE);
}
}
//extern struct snmp_msg_trap trap_msg;
//struct snmp_msg_trap
//{
// /* Communication handle */
// void *handle;
// /* local IP address */
// ip_addr_t *lip;
// /* destination IP address */
// ip_addr_t dip;
//
// /* source enterprise ID (sysObjectID) */
// const struct snmp_obj_id *enterprise;
// /* source IP address, raw network order format */
// u8_t sip_raw[4];
// /* source IP address length */
// u8_t sip_raw_len;
// /* generic trap code */
// u32_t gen_trap;
// /* specific trap code */
// u32_t spc_trap;
// /* timestamp */
// u32_t ts;
// ///* list of variable bindings to output */
// //struct snmp_varbind_root outvb;
// ///* output trap lengths used in ASN encoding */
// //struct snmp_trap_header_lengths thl;
//};
/** output response message header length fields */
//struct snmp_trap_header_lengths
//{
// /* encoding timestamp length length */
// u8_t tslenlen;
// /* encoding specific-trap length length */
// u8_t strplenlen;
// /* encoding generic-trap length length */
// u8_t gtrplenlen;
// /* encoding agent-addr length length */
// u8_t aaddrlenlen;
// /* encoding enterprise-id length length */
// u8_t eidlenlen;
// /* encoding pdu length length */
// u8_t pdulenlen;
// /* encoding community length length */
// u8_t comlenlen;
// /* encoding version length length */
// u8_t verlenlen;
// /* encoding sequence length length */
// u8_t seqlenlen;
//
// /* encoding timestamp length */
// u16_t tslen;
// /* encoding specific-trap length */
// u16_t strplen;
// /* encoding generic-trap length */
// u16_t gtrplen;
// /* encoding agent-addr length */
// u16_t aaddrlen;
// /* encoding enterprise-id length */
// u16_t eidlen;
// /* encoding pdu length */
// u16_t pdulen;
// /* encoding community length */
// u16_t comlen;
// /* encoding version length */
// u16_t verlen;
// /* encoding sequence length */
// u16_t seqlen;
//};
/**
* Sums trap header field lengths from tail to head and
* returns trap_header_lengths for second encoding pass.
*
* @param vb_len varbind-list length
* @param thl points to returned header lengths
* @return the required length for encoding the trap header
*/
//static u16_t
//snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len)
//{
// u16_t tot_len;
// struct snmp_trap_header_lengths *thl;
//
// thl = &m_trap->thl;
// tot_len = vb_len;
//
// snmp_asn1_enc_u32t_cnt(m_trap->ts, &thl->tslen);
// snmp_asn1_enc_length_cnt(thl->tslen, &thl->tslenlen);
// tot_len += 1 + thl->tslen + thl->tslenlen;
//
// snmp_asn1_enc_s32t_cnt(m_trap->spc_trap, &thl->strplen);
// snmp_asn1_enc_length_cnt(thl->strplen, &thl->strplenlen);
// tot_len += 1 + thl->strplen + thl->strplenlen;
//
// snmp_asn1_enc_s32t_cnt(m_trap->gen_trap, &thl->gtrplen);
// snmp_asn1_enc_length_cnt(thl->gtrplen, &thl->gtrplenlen);
// tot_len += 1 + thl->gtrplen + thl->gtrplenlen;
//
// thl->aaddrlen = m_trap->sip_raw_len;
// snmp_asn1_enc_length_cnt(thl->aaddrlen, &thl->aaddrlenlen);
// tot_len += 1 + thl->aaddrlen + thl->aaddrlenlen;
//
// snmp_asn1_enc_oid_cnt(&m_trap->enterprise->id[0], m_trap->enterprise->len, &thl->eidlen);
// snmp_asn1_enc_length_cnt(thl->eidlen, &thl->eidlenlen);
// tot_len += 1 + thl->eidlen + thl->eidlenlen;
//
// thl->pdulen = tot_len;
// snmp_asn1_enc_length_cnt(thl->pdulen, &thl->pdulenlen);
// tot_len += 1 + thl->pdulenlen;
//
// thl->comlen = (u16_t)strlen(snmp_community_trap);
// snmp_asn1_enc_length_cnt(thl->comlen, &thl->comlenlen);
// tot_len += 1 + thl->comlenlen + thl->comlen;
//
// snmp_asn1_enc_s32t_cnt(snmp_version, &thl->verlen);
// snmp_asn1_enc_length_cnt(thl->verlen, &thl->verlenlen);
// tot_len += 1 + thl->verlen + thl->verlenlen;
//
// thl->seqlen = tot_len;
// snmp_asn1_enc_length_cnt(thl->seqlen, &thl->seqlenlen);
// tot_len += 1 + thl->seqlenlen;
//
// return tot_len;
//}
/**
* Encodes trap header from head to tail.
*/
//static u16_t
//snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p)
//{
// u16_t ofs;
//
// ofs = 0;
// snmp_asn1_enc_type(p, ofs, SNMP_ASN1_TYPE_SEQUENCE);
// ofs += 1;
// snmp_asn1_enc_length(p, ofs, m_trap->thl.seqlen);
// ofs += m_trap->thl.seqlenlen;
//
// snmp_asn1_enc_type(p, ofs, SNMP_ASN1_TYPE_INTEGER);
// ofs += 1;
// snmp_asn1_enc_length(p, ofs, m_trap->thl.verlen);
// ofs += m_trap->thl.verlenlen;
// snmp_asn1_enc_s32t(p, ofs, m_trap->thl.verlen, snmp_version);
// ofs += m_trap->thl.verlen;
//
// snmp_asn1_enc_type(p, ofs, SNMP_ASN1_TYPE_OCTET_STRING);
// ofs += 1;
// snmp_asn1_enc_length(p, ofs, m_trap->thl.comlen);
// ofs += m_trap->thl.comlenlen;
// snmp_asn1_enc_raw(p, ofs, m_trap->thl.comlen, (const u8_t *)&snmp_community_trap[0]);
// ofs += m_trap->thl.comlen;
//
// snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_TRAP));
// ofs += 1;
// snmp_asn1_enc_length(p, ofs, m_trap->thl.pdulen);
// ofs += m_trap->thl.pdulenlen;
//
// snmp_asn1_enc_type(p, ofs, SNMP_ASN1_TYPE_OBJECT_ID);
// ofs += 1;
// snmp_asn1_enc_length(p, ofs, m_trap->thl.eidlen);
// ofs += m_trap->thl.eidlenlen;
// snmp_asn1_enc_oid(p, ofs, m_trap->enterprise->len, &m_trap->enterprise->id[0]);
// ofs += m_trap->thl.eidlen;
//
// snmp_asn1_enc_type(p, ofs, SNMP_ASN1_TYPE_IPADDR);
// ofs += 1;
// snmp_asn1_enc_length(p, ofs, m_trap->thl.aaddrlen);
// ofs += m_trap->thl.aaddrlenlen;
// snmp_asn1_enc_raw(p, ofs, m_trap->thl.aaddrlen, &m_trap->sip_raw[0]);
// ofs += m_trap->thl.aaddrlen;
//
// snmp_asn1_enc_type(p, ofs, SNMP_ASN1_TYPE_INTEGER);
// ofs += 1;
// snmp_asn1_enc_length(p, ofs, m_trap->thl.gtrplen);
// ofs += m_trap->thl.gtrplenlen;
// snmp_asn1_enc_u32t(p, ofs, m_trap->thl.gtrplen, m_trap->gen_trap);
// ofs += m_trap->thl.gtrplen;
//
// snmp_asn1_enc_type(p, ofs, SNMP_ASN1_TYPE_INTEGER);
// ofs += 1;
// snmp_asn1_enc_length(p, ofs, m_trap->thl.strplen);
// ofs += m_trap->thl.strplenlen;
// snmp_asn1_enc_u32t(p, ofs, m_trap->thl.strplen, m_trap->spc_trap);
// ofs += m_trap->thl.strplen;
//
// snmp_asn1_enc_type(p, ofs, SNMP_ASN1_TYPE_TIMETICKS);
// ofs += 1;
// snmp_asn1_enc_length(p, ofs, m_trap->thl.tslen);
// ofs += m_trap->thl.tslenlen;
// snmp_asn1_enc_u32t(p, ofs, m_trap->thl.tslen, m_trap->ts);
// ofs += m_trap->thl.tslen;
//
// return ofs;
//}
#endif