/**
* @file
* SNMP input message processing (RFC1157).
*/
/*
* 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
* 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>
*/
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
#include "lwip/snmp_asn1.h"
#include "lwip/snmp_msg.h"
#include "lwip/snmp_structs.h"
#include <string.h>
/* public (non-static) constants */
/** SNMP v1 == 0 */
/** default SNMP community string */
/* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */
/* UDP Protocol Control Block */
static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
/**
* Starts SNMP Agent.
* Allocates UDP pcb and binds it to IP_ADDR_ANY port 161.
*/
void
snmp_init(void)
{
u8_t i;
{
}
msg_ps = &msg_input_list[0];
for (i=0; i<SNMP_CONCURRENT_REQUESTS; i++)
{
msg_ps->error_index = 0;
msg_ps++;
}
#ifdef SNMP_PRIVATE_MIB_INIT
/* If defined, this must be a function-like define to initialize the
* private MIB after the stack has been initialized.
* The private MIB can also be initialized in tcpip_callback (or after
* the stack is initialized), this define is only for convenience. */
#endif /* SNMP_PRIVATE_MIB_INIT */
/* The coldstart trap will only be output
if our outgoing interface is up & configured */
}
static void
{
/* move names back from outvb to invb */
int v;
}
/* free outvb */
/* we send invb back */
/* error index must be 0 for error too big */
}
static void
{
{
/* serious memory problem, can't return tooBig */
}
else
{
}
/* free varbinds (if available) */
}
/**
* Service an internal or external event for SNMP GET.
*
* @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
* @param msg_ps points to the assosicated message process state
*/
static void
{
LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
{
/* get_object_def() answer*/
/* translate answer into a known lifeform */
{
}
else
{
/* search failed, object id points to unknown object (nosuchname) */
}
}
{
/* get_value() answer */
/* allocate output varbind */
{
/* move name from invb to outvb */
/* ensure this memory is refereced once only */
{
LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
{
/* search again (if vb_idx < msg_ps->invb.count) */
}
else
{
}
}
else
{
/* vb->value_len == 0, empty value (e.g. empty string) */
/* search again (if vb_idx < msg_ps->invb.count) */
}
}
else
{
}
}
{
{
}
else
{
}
/** test object identifier for .iso.org.dod.internet prefix */
{
{
{
/* external object */
/* save en && args in msg_ps!! */
}
else
{
/* internal object */
{
}
else
{
/* search failed, object id points to unknown object (nosuchname) */
}
{
/* allocate output varbind */
{
/* move name from invb to outvb */
/* ensure this memory is refereced once only */
{
LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low",
{
}
else
{
}
}
else
{
/* vb->value_len == 0, empty value (e.g. empty string) */
}
}
else
{
}
}
}
}
}
else
{
}
{
/* mn == NULL, noSuchName */
}
}
{
}
}
/**
* Service an internal or external event for SNMP GETNEXT.
*
* @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
* @param msg_ps points to the assosicated message process state
*/
static void
{
LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
{
/* get_object_def() answer*/
/* translate answer into a known lifeform */
en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def);
{
}
else
{
/* search failed, object id points to unknown object (nosuchname) */
}
}
{
/* get_value() answer */
{
}
else
{
}
}
{
{
}
else
{
}
{
{
/* can offset ident_len and ident */
}
else
{
/* can't offset ident_len -4, ident + 4 */
}
}
else
{
}
{
{
/* external object */
/* save en && args in msg_ps!! */
}
else
{
/* internal object */
{
}
else
{
}
}
}
{
/* mn == NULL, noSuchName */
}
}
{
}
}
/**
* Service an internal or external event for SNMP SET.
*
* @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
* @param msg_ps points to the assosicated message process state
*/
static void
{
LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
{
/* get_object_def() answer*/
/* translate answer into a known lifeform */
{
}
else
{
/* search failed, object id points to unknown object (nosuchname) */
}
}
{
/* set_test() answer*/
{
{
}
else
{
/* bad value */
}
}
else
{
/* object not available for set */
}
}
{
/* get_object_def() answer*/
/* translate answer into a known lifeform */
{
}
else
{
/* set_value failed, object has disappeared for some odd reason?? */
}
}
{
/** set_value_a() */
/** @todo use set_value_pc() if toobig */
}
/* test all values before setting */
{
{
}
else
{
}
/** test object identifier for .iso.org.dod.internet prefix */
{
{
{
/* external object */
/* save en && args in msg_ps!! */
}
else
{
/* internal object */
{
}
else
{
/* search failed, object id points to unknown object (nosuchname) */
}
{
{
{
}
else
{
/* bad value */
}
}
else
{
/* object not available for set */
}
}
}
}
}
else
{
}
{
/* mn == NULL, noSuchName */
}
}
{
}
/* set all values "atomically" (be as "atomic" as possible) */
{
{
}
else
{
}
/* skip iso prefix test, was done previously while settesting() */
/* check if object is still available
(e.g. external hot-plug thingy present?) */
{
{
/* external object */
/* save en && args in msg_ps!! */
}
else
{
/* internal object */
}
}
}
{
/* simply echo the input if we can set it
@todo do we need to return the actual value?
e.g. if value is silently modified or behaves sticky? */
}
}
/**
* Handle one internal or external event.
*
* @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
*/
void
{
{
{
}
{
}
{
}
}
}
/* lwIP UDP receive callback function */
static void
{
/* suppress unused argument warning */
/* traverse input message process list, look for SNMP_MSG_EMPTY */
msg_ps = &msg_input_list[0];
req_idx = 0;
{
req_idx++;
msg_ps++;
}
if (req_idx == SNMP_CONCURRENT_REQUESTS)
{
/* exceeding number of concurrent requests */
pbuf_free(p);
return;
}
/* accepting request */
/* record used 'protocol control block' */
/* source address (network order) */
/* source port (host order (lwIP oddity)) */
/* check total length, version, community, pdu type */
/* Only accept requests and requests without error (be robust) */
/* Reject response and trap headers or error requests as input! */
(msg_ps->error_index != 0)) )
{
/* header check failed drop request silently, do not return error! */
pbuf_free(p);
return;
}
/* Builds a list of variable bindings. Copy the varbinds from the pbuf
chain to glue them when these are divided over two or more pbuf's. */
/* we've decoded the incoming message, release input msg now */
pbuf_free(p);
{
/* varbind-list decode failed, or varbind list empty.
drop request silently, do not return error!
(errors are only returned for a specific varbind failure) */
return;
}
msg_ps->error_index = 0;
/* find object for each variable binding */
/* first variable binding from list to inspect */
/* handle input event and as much objects as possible in one go */
}
/**
* Checks and decodes incoming SNMP message header, logs header errors.
*
* @param p points to pbuf chain of SNMP message (UDP payload)
* @param ofs points to first octet of SNMP message
* @param pdu_len the length of the UDP payload
* @param ofs_ret returns the ofset of the variable bindings
* @param m_stat points to the current message request state return
* @return
* - ERR_OK SNMP header is sane and accepted
* - ERR_ARG SNMP header is either malformed or rejected
*/
static err_t
snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
{
{
return ERR_ARG;
}
{
/* can't decode or no integer (version) */
return ERR_ARG;
}
{
/* can't decode */
return ERR_ARG;
}
if (version != 0)
{
/* not version 1 */
return ERR_ARG;
}
{
/* can't decode or no octet string (community) */
return ERR_ARG;
}
{
return ERR_ARG;
}
/* add zero terminator */
{
/** @todo: move this if we need to check more names */
return ERR_ARG;
}
{
return ERR_ARG;
}
switch(type)
{
/* GetRequest PDU */
break;
/* GetNextRequest PDU */
break;
/* GetResponse PDU */
break;
/* SetRequest PDU */
break;
/* Trap PDU */
break;
default:
break;
}
{
/* unsupported input PDU for this agent (no parse error) */
return ERR_ARG;
}
{
/* decoded PDU length does not equal actual payload length */
return ERR_ARG;
}
{
/* can't decode or no integer (request ID) */
return ERR_ARG;
}
{
/* can't decode */
return ERR_ARG;
}
{
/* can't decode or no integer (error-status) */
return ERR_ARG;
}
/* must be noError (0) for incoming requests.
log errors for mib-2 completeness and for debug purposes */
{
/* can't decode */
return ERR_ARG;
}
switch (m_stat->error_status)
{
case SNMP_ES_TOOBIG:
break;
case SNMP_ES_NOSUCHNAME:
break;
case SNMP_ES_BADVALUE:
break;
case SNMP_ES_READONLY:
break;
case SNMP_ES_GENERROR:
break;
}
{
/* can't decode or no integer (error-index) */
return ERR_ARG;
}
/* must be 0 for incoming requests.
decode anyway to catch bad integers (and dirty tricks) */
{
/* can't decode */
return ERR_ARG;
}
return ERR_OK;
}
static err_t
{
/* variable binding list */
{
return ERR_ARG;
}
/* start with empty list */
while (vb_len > 0)
{
{
/* free varbinds (if available) */
return ERR_ARG;
}
{
/* can't decode object name length */
/* free varbinds (if available) */
return ERR_ARG;
}
{
/* can't decode object name */
/* free varbinds (if available) */
return ERR_ARG;
}
{
/* can't decode object value length */
/* free varbinds (if available) */
return ERR_ARG;
}
switch (type)
{
{
}
else
{
}
break;
{
}
else
{
}
break;
{
}
else
{
}
break;
{
}
else
{
}
break;
{
{
while(i > 0)
{
i--;
}
}
else
{
}
}
break;
if (len == 4)
{
/* must be exactly 4 octets! */
{
}
else
{
}
}
else
{
}
break;
default:
break;
}
{
/* free varbinds (if available) */
return ERR_ARG;
}
}
{
}
else
{
}
return ERR_OK;
}
struct snmp_varbind*
{
{
u8_t i;
if (i > 0)
{
/* allocate array of s32_t for our object identifier */
{
return NULL;
}
while(i > 0)
{
i--;
}
}
else
{
/* i == 0, pass zero length object identifier */
}
if (len > 0)
{
LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
/* allocate raw bytes for our object value */
{
{
}
return NULL;
}
}
else
{
/* ASN1_NUL type, or zero length ASN1_OC_STR */
}
}
else
{
}
return vb;
}
void
{
{
}
{
}
}
void
{
{
}
}
void
{
{
/* add first varbind to list */
}
else
{
/* add nth varbind to list tail */
}
}
struct snmp_varbind*
{
{
/* remove tail varbind */
}
else
{
/* nothing to remove */
}
return vb;
}
#endif /* LWIP_SNMP */