4233 lines
129 KiB
C
4233 lines
129 KiB
C
/*
|
|
* snmp_agent.c
|
|
*
|
|
* Simple Network Management Protocol (RFC 1067).
|
|
*/
|
|
/* Portions of this file are subject to the following copyrights. See
|
|
* the Net-SNMP's COPYING file for more details and other copyrights
|
|
* that may apply:
|
|
*/
|
|
/***********************************************************
|
|
Copyright 1988, 1989 by Carnegie Mellon University
|
|
|
|
All Rights Reserved
|
|
|
|
Permission to use, copy, modify, and distribute this software and its
|
|
documentation for any purpose and without fee is hereby granted,
|
|
provided that the above copyright notice appear in all copies and that
|
|
both that copyright notice and this permission notice appear in
|
|
supporting documentation, and that the name of CMU not be
|
|
used in advertising or publicity pertaining to distribution of the
|
|
software without specific, written prior permission.
|
|
|
|
CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
|
|
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
|
|
CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
|
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
|
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
SOFTWARE.
|
|
******************************************************************/
|
|
/*
|
|
* Portions of this file are copyrighted by:
|
|
* Copyright © 2003 Sun Microsystems, Inc. All rights
|
|
* reserved. Use is subject to license terms specified in the
|
|
* COPYING file distributed with the Net-SNMP package.
|
|
*
|
|
* Portions of this file are copyrighted by:
|
|
* Copyright (c) 2016 VMware, Inc. All rights reserved.
|
|
* Use is subject to license terms specified in the COPYING file
|
|
* distributed with the Net-SNMP package.
|
|
*/
|
|
/** @defgroup snmp_agent net-snmp agent related processing
|
|
* @ingroup agent
|
|
*
|
|
* @{
|
|
*/
|
|
#include <net-snmp/net-snmp-config.h>
|
|
#include <net-snmp/net-snmp-features.h>
|
|
|
|
#include <sys/types.h>
|
|
#ifdef HAVE_LIMITS_H
|
|
#include <limits.h>
|
|
#endif
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
#if HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#if HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
#if TIME_WITH_SYS_TIME
|
|
# include <sys/time.h>
|
|
# include <time.h>
|
|
#else
|
|
# if HAVE_SYS_TIME_H
|
|
# include <sys/time.h>
|
|
# else
|
|
# include <time.h>
|
|
# endif
|
|
#endif
|
|
#if HAVE_SYS_SELECT_H
|
|
#include <sys/select.h>
|
|
#endif
|
|
#if HAVE_NETINET_IN_H
|
|
#include <netinet/in.h>
|
|
#endif
|
|
#include <errno.h>
|
|
|
|
#define SNMP_NEED_REQUEST_LIST
|
|
#include <net-snmp/net-snmp-includes.h>
|
|
#include <net-snmp/agent/net-snmp-agent-includes.h>
|
|
#include <net-snmp/library/large_fd_set.h>
|
|
#include <net-snmp/library/snmp_assert.h>
|
|
#include "agent_global_vars.h"
|
|
|
|
#if HAVE_SYSLOG_H
|
|
#include <syslog.h>
|
|
#endif
|
|
|
|
#ifdef NETSNMP_USE_LIBWRAP
|
|
#include <tcpd.h>
|
|
int allow_severity = LOG_INFO;
|
|
int deny_severity = LOG_WARNING;
|
|
#endif
|
|
|
|
#include "snmpd.h"
|
|
#include <net-snmp/agent/mib_module_config.h>
|
|
#include <net-snmp/agent/mib_modules.h>
|
|
|
|
#ifdef USING_AGENTX_PROTOCOL_MODULE
|
|
#include "agentx/protocol.h"
|
|
#endif
|
|
|
|
#ifdef USING_AGENTX_MASTER_MODULE
|
|
#include "agentx/master.h"
|
|
#endif
|
|
|
|
#ifdef USING_SMUX_MODULE
|
|
#include "smux/smux.h"
|
|
#endif
|
|
|
|
netsnmp_feature_child_of(snmp_agent, libnetsnmpagent)
|
|
netsnmp_feature_child_of(agent_debugging_utilities, libnetsnmpagent)
|
|
|
|
netsnmp_feature_child_of(allocate_globalcacheid, snmp_agent)
|
|
netsnmp_feature_child_of(free_agent_snmp_session_by_session, snmp_agent)
|
|
netsnmp_feature_child_of(check_all_requests_error, snmp_agent)
|
|
netsnmp_feature_child_of(check_requests_error, snmp_agent)
|
|
netsnmp_feature_child_of(request_set_error_idx, snmp_agent)
|
|
netsnmp_feature_child_of(set_agent_uptime, snmp_agent)
|
|
netsnmp_feature_child_of(agent_check_and_process, snmp_agent)
|
|
|
|
netsnmp_feature_child_of(dump_sess_list, agent_debugging_utilities)
|
|
|
|
netsnmp_feature_child_of(agent_remove_list_data, netsnmp_unused)
|
|
netsnmp_feature_child_of(set_all_requests_error, netsnmp_unused)
|
|
netsnmp_feature_child_of(addrcache_age, netsnmp_unused)
|
|
netsnmp_feature_child_of(delete_subtree_cache, netsnmp_unused)
|
|
|
|
#ifndef NETSNMP_NO_PDU_STATS
|
|
|
|
static netsnmp_container *_pdu_stats = NULL;
|
|
static int _pdu_stats_max = 0;
|
|
static u_long _pdu_stats_threshold = 0;
|
|
static u_long _pdu_stats_current_lowest = 0;
|
|
|
|
netsnmp_container *
|
|
netsnmp_get_pdu_stats(void)
|
|
{
|
|
return _pdu_stats;
|
|
}
|
|
|
|
int _pdu_stats_compare(const netsnmp_pdu_stats * lhs,
|
|
const netsnmp_pdu_stats * rhs)
|
|
{
|
|
if (NULL == lhs || NULL == rhs) {
|
|
snmp_log(LOG_WARNING,
|
|
"WARNING: results undefined for compares with NULL\n");
|
|
return 0;
|
|
}
|
|
|
|
/** we want list sorted in reverse order, so invert tests */
|
|
if (lhs->processing_time > rhs->processing_time)
|
|
return -1;
|
|
else if (lhs->processing_time < rhs->processing_time)
|
|
return 1;
|
|
|
|
if (lhs->timestamp > rhs->timestamp)
|
|
return -1;
|
|
else if (lhs->timestamp < rhs->timestamp)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
_pdu_stats_init(void) {
|
|
static int done = 0;
|
|
if ((NULL != _pdu_stats) || (++done != 1))
|
|
return;
|
|
|
|
_pdu_stats = netsnmp_container_find("netsnmp_pdustats:binary_array");
|
|
if (NULL == _pdu_stats) {
|
|
done = 0;
|
|
return;
|
|
}
|
|
|
|
_pdu_stats->compare = (netsnmp_container_compare*)_pdu_stats_compare;
|
|
_pdu_stats->get_subset = NULL; /** subsets not supported */
|
|
|
|
_pdu_stats_max = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
|
|
NETSNMP_DS_AGENT_PDU_STATS_MAX);
|
|
_pdu_stats_threshold =
|
|
netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
|
|
NETSNMP_DS_AGENT_PDU_STATS_THRESHOLD);
|
|
if (_pdu_stats_threshold < 100)
|
|
_pdu_stats_threshold = 100;
|
|
DEBUGMSGTL(("stats:pdu", "max: %d, threshold %ld ms\n", _pdu_stats_max,
|
|
_pdu_stats_threshold));
|
|
}
|
|
|
|
|
|
static void
|
|
_pdu_stats_shutdown(void)
|
|
{
|
|
netsnmp_pdu_stats *entry;
|
|
int x = 0;
|
|
|
|
if (NULL == _pdu_stats)
|
|
return;
|
|
|
|
for( ; x < CONTAINER_SIZE(_pdu_stats); ++x) {
|
|
CONTAINER_GET_AT(_pdu_stats, x, (void**)&entry);
|
|
if (NULL == entry)
|
|
continue;
|
|
snmp_free_pdu(entry->pdu);
|
|
free(entry);
|
|
}
|
|
CONTAINER_FREE(_pdu_stats);
|
|
_pdu_stats = NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
_dump_pdu_stats(void)
|
|
{
|
|
int x = 0;
|
|
struct tm *tm;
|
|
char timestr[40];
|
|
netsnmp_pdu_stats *entry;
|
|
|
|
for( ; x < CONTAINER_SIZE(_pdu_stats); ++x) {
|
|
netsnmp_pdu *response;
|
|
netsnmp_variable_list *vars;
|
|
CONTAINER_GET_AT(_pdu_stats, x, (void**)&entry);
|
|
if (NULL == entry) {
|
|
DEBUGMSGT_NC(("9:stats:pdu", "[%d] ERROR\n", x));
|
|
continue;
|
|
}
|
|
tm = localtime(&entry->timestamp);
|
|
if (NULL == tm)
|
|
sprintf(timestr, "UNKNOWN");
|
|
else if (strftime(timestr, sizeof(timestr), "%m/%d/%Y %H:%M:%S",
|
|
tm) == 0)
|
|
sprintf(timestr, "UNKNOWN");
|
|
|
|
DEBUGMSGT_NC(("9:stats:pdu", "[%d] %ld ms, %s\n",
|
|
x, entry->processing_time, timestr));
|
|
response = entry->pdu;
|
|
if (response->errstat == SNMP_ERR_NOERROR) {
|
|
for (vars = response->variables; vars;
|
|
vars = vars->next_variable) {
|
|
DEBUGMSGT_NC(("9:stats:pdu", " vb "));
|
|
DEBUGMSGVAR(("9:stats:pdu", vars));
|
|
DEBUGMSG_NC(("9:stats:pdu", "\n"));
|
|
}
|
|
} else {
|
|
DEBUGMSGT_NC(("9:stats:pdu", "Error in packet: Reason: %s\n",
|
|
snmp_errstring(response->errstat)));
|
|
if (response->errindex != 0) {
|
|
int count;
|
|
DEBUGMSGT_NC(("9:stats:pdu", "Failed object: "));
|
|
for (count = 1, vars = response->variables;
|
|
vars && count != response->errindex;
|
|
vars = vars->next_variable, count++)
|
|
/*EMPTY*/;
|
|
if (vars) {
|
|
DEBUGMSGOID(("9:stats:pdu", vars->name,
|
|
vars->name_length));
|
|
}
|
|
DEBUGMSG_NC(("9:stats:pdu", "\n"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif /* NETSNMP_NO_PDU_STATS */
|
|
|
|
|
|
NETSNMP_INLINE void
|
|
netsnmp_agent_add_list_data(netsnmp_agent_request_info *ari,
|
|
netsnmp_data_list *node)
|
|
{
|
|
if (ari) {
|
|
if (ari->agent_data) {
|
|
netsnmp_add_list_data(&ari->agent_data, node);
|
|
} else {
|
|
ari->agent_data = node;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_AGENT_REMOVE_LIST_DATA
|
|
NETSNMP_INLINE int
|
|
netsnmp_agent_remove_list_data(netsnmp_agent_request_info *ari,
|
|
const char * name)
|
|
{
|
|
if ((NULL == ari) || (NULL == ari->agent_data))
|
|
return 1;
|
|
|
|
return netsnmp_remove_list_node(&ari->agent_data, name);
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_AGENT_REMOVE_LIST_DATA */
|
|
|
|
NETSNMP_INLINE void *
|
|
netsnmp_agent_get_list_data(netsnmp_agent_request_info *ari,
|
|
const char *name)
|
|
{
|
|
if (ari) {
|
|
return netsnmp_get_list_data(ari->agent_data, name);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
NETSNMP_INLINE void
|
|
netsnmp_free_agent_data_set(netsnmp_agent_request_info *ari)
|
|
{
|
|
if (ari) {
|
|
netsnmp_free_list_data(ari->agent_data);
|
|
}
|
|
}
|
|
|
|
NETSNMP_INLINE void
|
|
netsnmp_free_agent_data_sets(netsnmp_agent_request_info *ari)
|
|
{
|
|
if (ari) {
|
|
netsnmp_free_all_list_data(ari->agent_data);
|
|
}
|
|
}
|
|
|
|
NETSNMP_INLINE void
|
|
netsnmp_free_agent_request_info(netsnmp_agent_request_info *ari)
|
|
{
|
|
if (ari) {
|
|
if (ari->agent_data) {
|
|
netsnmp_free_all_list_data(ari->agent_data);
|
|
}
|
|
SNMP_FREE(ari);
|
|
}
|
|
}
|
|
|
|
const oid version_sysoid[] = { NETSNMP_SYSTEM_MIB };
|
|
const int version_sysoid_len = OID_LENGTH(version_sysoid);
|
|
|
|
#define SNMP_ADDRCACHE_SIZE 10
|
|
#define SNMP_ADDRCACHE_MAXAGE 300 /* in seconds */
|
|
|
|
enum {
|
|
SNMP_ADDRCACHE_UNUSED = 0,
|
|
SNMP_ADDRCACHE_USED = 1
|
|
};
|
|
|
|
struct addrCache {
|
|
char *addr;
|
|
int status;
|
|
struct timeval lastHitM;
|
|
};
|
|
|
|
static struct addrCache addrCache[SNMP_ADDRCACHE_SIZE];
|
|
int log_addresses = 0;
|
|
|
|
|
|
|
|
typedef struct _agent_nsap {
|
|
int handle;
|
|
netsnmp_transport *t;
|
|
void *s; /* Opaque internal session pointer. */
|
|
struct _agent_nsap *next;
|
|
} agent_nsap;
|
|
|
|
static agent_nsap *agent_nsap_list = NULL;
|
|
static netsnmp_agent_session *agent_session_list = NULL;
|
|
netsnmp_agent_session *netsnmp_processing_set = NULL;
|
|
netsnmp_agent_session *agent_delegated_list = NULL;
|
|
netsnmp_agent_session *netsnmp_agent_queued_list = NULL;
|
|
|
|
|
|
int handle_pdu(netsnmp_agent_session *asp);
|
|
int netsnmp_handle_request(netsnmp_agent_session *asp,
|
|
int status);
|
|
int check_delayed_request(netsnmp_agent_session *asp);
|
|
int handle_getnext_loop(netsnmp_agent_session *asp);
|
|
int handle_set_loop(netsnmp_agent_session *asp);
|
|
|
|
int netsnmp_remove_from_delegated(netsnmp_agent_session *asp);
|
|
|
|
|
|
int netsnmp_running = 1;
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_ALLOCATE_GLOBALCACHEID
|
|
static int current_globalid = 0;
|
|
|
|
int
|
|
netsnmp_allocate_globalcacheid(void)
|
|
{
|
|
return ++current_globalid;
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_ALLOCATE_GLOBALCACHEID */
|
|
|
|
int
|
|
netsnmp_get_local_cachid(netsnmp_cachemap *cache_store, int globalid)
|
|
{
|
|
while (cache_store != NULL) {
|
|
if (cache_store->globalid == globalid)
|
|
return cache_store->cacheid;
|
|
cache_store = cache_store->next;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
netsnmp_cachemap *
|
|
netsnmp_get_or_add_local_cachid(netsnmp_cachemap **cache_store,
|
|
int globalid, int localid)
|
|
{
|
|
netsnmp_cachemap *tmpp;
|
|
|
|
tmpp = SNMP_MALLOC_TYPEDEF(netsnmp_cachemap);
|
|
if (tmpp != NULL) {
|
|
if (*cache_store) {
|
|
tmpp->next = *cache_store;
|
|
*cache_store = tmpp;
|
|
} else {
|
|
*cache_store = tmpp;
|
|
}
|
|
|
|
tmpp->globalid = globalid;
|
|
tmpp->cacheid = localid;
|
|
}
|
|
return tmpp;
|
|
}
|
|
|
|
void
|
|
netsnmp_free_cachemap(netsnmp_cachemap *cache_store)
|
|
{
|
|
netsnmp_cachemap *tmpp;
|
|
while (cache_store) {
|
|
tmpp = cache_store;
|
|
cache_store = cache_store->next;
|
|
SNMP_FREE(tmpp);
|
|
}
|
|
}
|
|
|
|
|
|
typedef struct agent_set_cache_s {
|
|
/*
|
|
* match on these 2
|
|
*/
|
|
int transID;
|
|
netsnmp_session *sess;
|
|
|
|
/*
|
|
* store this info
|
|
*/
|
|
netsnmp_tree_cache *treecache;
|
|
int treecache_len;
|
|
int treecache_num;
|
|
|
|
int vbcount;
|
|
netsnmp_request_info *requests;
|
|
netsnmp_variable_list *saved_vars;
|
|
netsnmp_data_list *agent_data;
|
|
|
|
/*
|
|
* list
|
|
*/
|
|
struct agent_set_cache_s *next;
|
|
} agent_set_cache;
|
|
|
|
static agent_set_cache *Sets = NULL;
|
|
|
|
agent_set_cache *
|
|
save_set_cache(netsnmp_agent_session *asp)
|
|
{
|
|
agent_set_cache *ptr;
|
|
|
|
if (!asp || !asp->reqinfo || !asp->pdu)
|
|
return NULL;
|
|
|
|
ptr = SNMP_MALLOC_TYPEDEF(agent_set_cache);
|
|
if (ptr == NULL)
|
|
return NULL;
|
|
|
|
/*
|
|
* Save the important information
|
|
*/
|
|
DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p saved in cache (mode %d)\n",
|
|
asp, asp->reqinfo, asp->pdu->command));
|
|
ptr->transID = asp->pdu->transid;
|
|
ptr->sess = asp->session;
|
|
ptr->treecache = asp->treecache;
|
|
ptr->treecache_len = asp->treecache_len;
|
|
ptr->treecache_num = asp->treecache_num;
|
|
ptr->agent_data = asp->reqinfo->agent_data;
|
|
ptr->requests = asp->requests;
|
|
ptr->saved_vars = asp->pdu->variables; /* requests contains pointers to variables */
|
|
ptr->vbcount = asp->vbcount;
|
|
|
|
/*
|
|
* make the agent forget about what we've saved
|
|
*/
|
|
asp->treecache = NULL;
|
|
asp->reqinfo->agent_data = NULL;
|
|
asp->pdu->variables = NULL;
|
|
asp->requests = NULL;
|
|
|
|
ptr->next = Sets;
|
|
Sets = ptr;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
int
|
|
get_set_cache(netsnmp_agent_session *asp)
|
|
{
|
|
agent_set_cache *ptr, *prev = NULL;
|
|
|
|
for (ptr = Sets; ptr != NULL; ptr = ptr->next) {
|
|
if (ptr->sess == asp->session && ptr->transID == asp->pdu->transid) {
|
|
/*
|
|
* remove this item from list
|
|
*/
|
|
if (prev)
|
|
prev->next = ptr->next;
|
|
else
|
|
Sets = ptr->next;
|
|
|
|
/*
|
|
* found it. Get the needed data
|
|
*/
|
|
asp->treecache = ptr->treecache;
|
|
asp->treecache_len = ptr->treecache_len;
|
|
asp->treecache_num = ptr->treecache_num;
|
|
|
|
/*
|
|
* Free previously allocated requests before overwriting by
|
|
* cached ones, otherwise memory leaks!
|
|
*/
|
|
if (asp->requests) {
|
|
/*
|
|
* I don't think this case should ever happen. Please email
|
|
* the net-snmp-coders@lists.sourceforge.net if you have
|
|
* a test case that hits this condition. -- rstory
|
|
*/
|
|
int i;
|
|
netsnmp_assert(NULL == asp->requests); /* see note above */
|
|
for (i = 0; i < asp->vbcount; i++) {
|
|
netsnmp_free_request_data_sets(&asp->requests[i]);
|
|
}
|
|
free(asp->requests);
|
|
}
|
|
/*
|
|
* If we replace asp->requests with the info from the set cache,
|
|
* we should replace asp->pdu->variables also with the cached
|
|
* info, as asp->requests contains pointers to them. And we
|
|
* should also free the current asp->pdu->variables list...
|
|
*/
|
|
if (ptr->saved_vars) {
|
|
if (asp->pdu->variables)
|
|
snmp_free_varbind(asp->pdu->variables);
|
|
asp->pdu->variables = ptr->saved_vars;
|
|
asp->vbcount = ptr->vbcount;
|
|
} else {
|
|
/*
|
|
* when would we not have saved variables? someone
|
|
* let me know if they hit this condition. -- rstory
|
|
*/
|
|
netsnmp_assert(NULL != ptr->saved_vars);
|
|
}
|
|
asp->requests = ptr->requests;
|
|
|
|
netsnmp_assert(NULL != asp->reqinfo);
|
|
asp->reqinfo->asp = asp;
|
|
asp->reqinfo->agent_data = ptr->agent_data;
|
|
|
|
/*
|
|
* update request reqinfo, if it's out of date.
|
|
* yyy-rks: investigate when/why sometimes they match,
|
|
* sometimes they don't.
|
|
*/
|
|
if(asp->requests->agent_req_info != asp->reqinfo) {
|
|
/*
|
|
* - one don't match case: agentx subagents. prev asp & reqinfo
|
|
* freed, request reqinfo ptrs not cleared.
|
|
*/
|
|
netsnmp_request_info *tmp = asp->requests;
|
|
DEBUGMSGTL(("verbose:asp",
|
|
" reqinfo %p doesn't match cached reqinfo %p\n",
|
|
asp->reqinfo, asp->requests->agent_req_info));
|
|
for(; tmp; tmp = tmp->next)
|
|
tmp->agent_req_info = asp->reqinfo;
|
|
} else {
|
|
/*
|
|
* - match case: ?
|
|
*/
|
|
DEBUGMSGTL(("verbose:asp",
|
|
" reqinfo %p matches cached reqinfo %p\n",
|
|
asp->reqinfo, asp->requests->agent_req_info));
|
|
}
|
|
|
|
SNMP_FREE(ptr);
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
prev = ptr;
|
|
}
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
|
|
/* Bulkcache holds the values for the *repeating* varbinds (only),
|
|
* but ordered "by column" - i.e. the repetitions for each
|
|
* repeating varbind follow on immediately from one another,
|
|
* rather than being interleaved, as required by the protocol.
|
|
*
|
|
* So we need to rearrange the varbind list so it's ordered "by row".
|
|
*
|
|
* In the following code chunk:
|
|
* n = # non-repeating varbinds
|
|
* r = # repeating varbinds
|
|
* asp->vbcount = # varbinds in the incoming PDU
|
|
* (So asp->vbcount = n+r)
|
|
*
|
|
* repeats = Desired # of repetitions (of 'r' varbinds)
|
|
*/
|
|
NETSNMP_STATIC_INLINE void
|
|
_reorder_getbulk(netsnmp_agent_session *asp)
|
|
{
|
|
int i, n = 0, r = 0;
|
|
int repeats;
|
|
int j, k;
|
|
int all_eoMib;
|
|
netsnmp_variable_list *prev = NULL, *curr;
|
|
|
|
if (NULL == asp || NULL == asp->pdu || asp->vbcount == 0)
|
|
return;
|
|
|
|
if (asp->pdu->errstat < asp->vbcount) {
|
|
n = asp->pdu->errstat;
|
|
} else {
|
|
n = asp->vbcount;
|
|
}
|
|
if ((r = asp->vbcount - n) < 0) {
|
|
r = 0;
|
|
}
|
|
|
|
DEBUGMSGTL(("snmp_agent:bulk", "reorder n=%d, r=%d\n", n, r));
|
|
|
|
/* we do nothing if there is nothing repeated */
|
|
if (r == 0)
|
|
return;
|
|
|
|
repeats = asp->pdu->errindex;
|
|
/* Fix endOfMibView entries. */
|
|
for (i = 0; i < r; i++) {
|
|
prev = NULL;
|
|
for (j = 0; j < repeats; j++) {
|
|
curr = asp->bulkcache[i * repeats + j];
|
|
/*
|
|
* If we don't have a valid name for a given repetition
|
|
* (and probably for all the ones that follow as well),
|
|
* extend the previous result to indicate 'endOfMibView'.
|
|
* Or if the repetition already has type endOfMibView make
|
|
* sure it has the correct objid (i.e. that of the previous
|
|
* entry or that of the original request).
|
|
*/
|
|
if (curr->name_length == 0 || curr->type == SNMP_ENDOFMIBVIEW) {
|
|
if (prev == NULL) {
|
|
/* Use objid from original pdu. */
|
|
prev = asp->orig_pdu->variables;
|
|
for (k = i; prev && k > 0; k--)
|
|
prev = prev->next_variable;
|
|
}
|
|
if (prev) {
|
|
snmp_set_var_objid(curr, prev->name, prev->name_length);
|
|
snmp_set_var_typed_value(curr, SNMP_ENDOFMIBVIEW, NULL, 0);
|
|
}
|
|
}
|
|
prev = curr;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* For each of the original repeating varbinds (except the last),
|
|
* go through the block of results for that varbind,
|
|
* and link each instance to the corresponding instance
|
|
* in the next block.
|
|
*/
|
|
for (i = 0; i < r - 1; i++) {
|
|
for (j = 0; j < repeats; j++) {
|
|
asp->bulkcache[i * repeats + j]->next_variable =
|
|
asp->bulkcache[(i + 1) * repeats + j];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* For the last of the original repeating varbinds,
|
|
* go through that block of results, and link each
|
|
* instance to the *next* instance in the *first* block.
|
|
*
|
|
* The very last instance of this block is left untouched
|
|
* since it (correctly) points to the end of the list.
|
|
*/
|
|
for (j = 0; j < repeats - 1; j++) {
|
|
asp->bulkcache[(r - 1) * repeats + j]->next_variable =
|
|
asp->bulkcache[j + 1];
|
|
}
|
|
|
|
/*
|
|
* If we've got a full row of endOfMibViews, then we
|
|
* can truncate the result varbind list after that.
|
|
*
|
|
* Look for endOfMibView exception values in the list of
|
|
* repetitions for the first varbind, and check the
|
|
* corresponding instances for the other varbinds
|
|
* (following the next_variable links).
|
|
*
|
|
* If they're all endOfMibView too, then we can terminate
|
|
* the linked list there, and free any redundant varbinds.
|
|
*/
|
|
all_eoMib = 0;
|
|
for (i = 0; i < repeats; i++) {
|
|
if (asp->bulkcache[i]->type == SNMP_ENDOFMIBVIEW) {
|
|
all_eoMib = 1;
|
|
for (j = 1, prev=asp->bulkcache[i];
|
|
j < r;
|
|
j++, prev=prev->next_variable) {
|
|
if (prev->type != SNMP_ENDOFMIBVIEW) {
|
|
all_eoMib = 0;
|
|
break; /* Found a real value */
|
|
}
|
|
}
|
|
if (all_eoMib) {
|
|
/*
|
|
* This is indeed a full endOfMibView row.
|
|
* Terminate the list here & free the rest.
|
|
*/
|
|
snmp_free_varbind( prev->next_variable );
|
|
prev->next_variable = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* EndOfMibView replies to a GETNEXT request should according to RFC3416
|
|
* have the object ID set to that of the request. Our tree search
|
|
* algorithm will sometimes break that requirement. This function will
|
|
* fix that.
|
|
*/
|
|
NETSNMP_STATIC_INLINE void
|
|
_fix_endofmibview(netsnmp_agent_session *asp)
|
|
{
|
|
netsnmp_variable_list *vb, *ovb;
|
|
|
|
if (asp->vbcount == 0) /* Nothing to do! */
|
|
return;
|
|
|
|
for (vb = asp->pdu->variables, ovb = asp->orig_pdu->variables;
|
|
vb && ovb; vb = vb->next_variable, ovb = ovb->next_variable) {
|
|
if (vb->type == SNMP_ENDOFMIBVIEW)
|
|
snmp_set_var_objid(vb, ovb->name, ovb->name_length);
|
|
}
|
|
}
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_AGENT_CHECK_AND_PROCESS
|
|
/**
|
|
* This function checks for packets arriving on the SNMP port and
|
|
* processes them(snmp_read) if some are found, using the select(). If block
|
|
* is non zero, the function call blocks until a packet arrives
|
|
*
|
|
* @param block used to control blocking in the select() function, 1 = block
|
|
* forever, and 0 = don't block
|
|
*
|
|
* @return Returns a positive integer if packets were processed, and -1 if an
|
|
* error was found.
|
|
*
|
|
*/
|
|
int
|
|
agent_check_and_process(int block)
|
|
{
|
|
int numfds;
|
|
netsnmp_large_fd_set readfds;
|
|
netsnmp_large_fd_set writefds;
|
|
netsnmp_large_fd_set exceptfds;
|
|
struct timeval timeout = { LONG_MAX, 0 }, *tvp = &timeout;
|
|
int count;
|
|
int fakeblock = 0;
|
|
|
|
numfds = 0;
|
|
netsnmp_large_fd_set_init(&readfds, FD_SETSIZE);
|
|
netsnmp_large_fd_set_init(&writefds, FD_SETSIZE);
|
|
netsnmp_large_fd_set_init(&exceptfds, FD_SETSIZE);
|
|
NETSNMP_LARGE_FD_ZERO(&readfds);
|
|
NETSNMP_LARGE_FD_ZERO(&writefds);
|
|
NETSNMP_LARGE_FD_ZERO(&exceptfds);
|
|
snmp_select_info2(&numfds, &readfds, tvp, &fakeblock);
|
|
if (block != 0 && fakeblock != 0) {
|
|
/*
|
|
* There are no alarms registered, and the caller asked for blocking, so
|
|
* let select() block forever.
|
|
*/
|
|
|
|
tvp = NULL;
|
|
} else if (block != 0 && fakeblock == 0) {
|
|
/*
|
|
* The caller asked for blocking, but there is an alarm due sooner than
|
|
* LONG_MAX seconds from now, so use the modified timeout returned by
|
|
* snmp_select_info as the timeout for select().
|
|
*/
|
|
|
|
} else if (block == 0) {
|
|
/*
|
|
* The caller does not want us to block at all.
|
|
*/
|
|
|
|
timerclear(tvp);
|
|
}
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_FD_EVENT_MANAGER
|
|
netsnmp_external_event_info2(&numfds, &readfds, &writefds, &exceptfds);
|
|
#endif /* NETSNMP_FEATURE_REMOVE_FD_EVENT_MANAGER */
|
|
|
|
count = netsnmp_large_fd_set_select(numfds, &readfds, &writefds, &exceptfds, tvp);
|
|
|
|
if (count > 0) {
|
|
/*
|
|
* packets found, process them
|
|
*/
|
|
#ifndef NETSNMP_FEATURE_REMOVE_FD_EVENT_MANAGER
|
|
netsnmp_dispatch_external_events2(&count, &readfds, &writefds, &exceptfds);
|
|
#endif /* NETSNMP_FEATURE_REMOVE_FD_EVENT_MANAGER */
|
|
|
|
snmp_read2(&readfds);
|
|
} else
|
|
switch (count) {
|
|
case 0:
|
|
snmp_timeout();
|
|
break;
|
|
case -1:
|
|
if (errno != EINTR) {
|
|
snmp_log_perror("select");
|
|
}
|
|
count = -1;
|
|
goto exit;
|
|
default:
|
|
snmp_log(LOG_ERR, "select returned %d\n", count);
|
|
count = -1;
|
|
goto exit;
|
|
} /* endif -- count>0 */
|
|
|
|
/*
|
|
* see if persistent store needs to be saved
|
|
*/
|
|
snmp_store_if_needed();
|
|
|
|
/*
|
|
* Run requested alarms.
|
|
*/
|
|
run_alarms();
|
|
|
|
netsnmp_check_outstanding_agent_requests();
|
|
|
|
exit:
|
|
netsnmp_large_fd_set_cleanup(&readfds);
|
|
netsnmp_large_fd_set_cleanup(&writefds);
|
|
netsnmp_large_fd_set_cleanup(&exceptfds);
|
|
return count;
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_AGENT_CHECK_AND_PROCESS */
|
|
|
|
/*
|
|
* Set up the address cache.
|
|
*/
|
|
void
|
|
netsnmp_addrcache_initialise(void)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
|
|
addrCache[i].addr = NULL;
|
|
addrCache[i].status = SNMP_ADDRCACHE_UNUSED;
|
|
}
|
|
}
|
|
|
|
void netsnmp_addrcache_destroy(void)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
|
|
if (addrCache[i].status == SNMP_ADDRCACHE_USED) {
|
|
free(addrCache[i].addr);
|
|
addrCache[i].status = SNMP_ADDRCACHE_UNUSED;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Adds a new entry to the cache of addresses that
|
|
* have recently made connections to the agent.
|
|
* Returns 0 if the entry already exists (but updates
|
|
* the entry with a new timestamp) and 1 if the
|
|
* entry did not previously exist.
|
|
*
|
|
* Implements a simple LRU cache replacement
|
|
* policy. Uses a linear search, which should be
|
|
* okay, as long as SNMP_ADDRCACHE_SIZE remains
|
|
* relatively small.
|
|
*
|
|
* @retval 0 : updated existing entry
|
|
* @retval 1 : added new entry
|
|
*/
|
|
int
|
|
netsnmp_addrcache_add(const char *addr)
|
|
{
|
|
int oldest = -1; /* Index of the oldest cache entry */
|
|
int unused = -1; /* Index of the first free cache entry */
|
|
int i; /* Looping variable */
|
|
int rc = -1;
|
|
struct timeval now; /* What time is it now? */
|
|
struct timeval aged; /* Oldest allowable cache entry */
|
|
|
|
/*
|
|
* First get the current and oldest allowable timestamps
|
|
*/
|
|
netsnmp_get_monotonic_clock(&now);
|
|
aged.tv_sec = now.tv_sec - SNMP_ADDRCACHE_MAXAGE;
|
|
aged.tv_usec = now.tv_usec;
|
|
|
|
/*
|
|
* Now look for a place to put this thing
|
|
*/
|
|
for(i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
|
|
if (addrCache[i].status == SNMP_ADDRCACHE_UNUSED) { /* If unused */
|
|
/*
|
|
* remember this location, in case addr isn't in the cache
|
|
*/
|
|
if (unused < 0)
|
|
unused = i;
|
|
}
|
|
else { /* If used */
|
|
if ((NULL != addr) && (strcmp(addrCache[i].addr, addr) == 0)) {
|
|
/*
|
|
* found a match
|
|
*/
|
|
addrCache[i].lastHitM = now;
|
|
if (timercmp(&addrCache[i].lastHitM, &aged, <))
|
|
rc = 1; /* should have expired, so is new */
|
|
else
|
|
rc = 0; /* not expired, so is existing entry */
|
|
break;
|
|
}
|
|
else {
|
|
/*
|
|
* Used, but not this address. check if it's stale.
|
|
*/
|
|
if (timercmp(&addrCache[i].lastHitM, &aged, <)) {
|
|
/*
|
|
* Stale, reuse
|
|
*/
|
|
SNMP_FREE(addrCache[i].addr);
|
|
addrCache[i].status = SNMP_ADDRCACHE_UNUSED;
|
|
/*
|
|
* remember this location, in case addr isn't in the cache
|
|
*/
|
|
if (unused < 0)
|
|
unused = i;
|
|
}
|
|
else {
|
|
/*
|
|
* Still fresh, but a candidate for LRU replacement
|
|
*/
|
|
if (oldest < 0)
|
|
oldest = i;
|
|
else if (timercmp(&addrCache[i].lastHitM,
|
|
&addrCache[oldest].lastHitM, <))
|
|
oldest = i;
|
|
} /* fresh */
|
|
} /* used, no match */
|
|
} /* used */
|
|
} /* for loop */
|
|
|
|
if ((-1 == rc) && (NULL != addr)) {
|
|
/*
|
|
* We didn't find the entry in the cache
|
|
*/
|
|
if (unused >= 0) {
|
|
/*
|
|
* If we have a slot free anyway, use it
|
|
*/
|
|
addrCache[unused].addr = strdup(addr);
|
|
addrCache[unused].status = SNMP_ADDRCACHE_USED;
|
|
addrCache[unused].lastHitM = now;
|
|
}
|
|
else { /* Otherwise, replace oldest entry */
|
|
if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
|
|
NETSNMP_DS_AGENT_VERBOSE))
|
|
snmp_log(LOG_INFO, "Purging address from address cache: %s",
|
|
addrCache[oldest].addr);
|
|
|
|
free(addrCache[oldest].addr);
|
|
addrCache[oldest].addr = strdup(addr);
|
|
addrCache[oldest].lastHitM = now;
|
|
}
|
|
rc = 1;
|
|
}
|
|
if ((log_addresses && (1 == rc)) ||
|
|
netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
|
|
NETSNMP_DS_AGENT_VERBOSE)) {
|
|
snmp_log(LOG_INFO, "Received SNMP packet(s) from %s\n", addr);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Age the entries in the address cache.
|
|
*
|
|
* backwards compatability; not used anywhere
|
|
*/
|
|
#ifndef NETSNMP_FEATURE_REMOVE_ADDRCACHE_AGE
|
|
void
|
|
netsnmp_addrcache_age(void)
|
|
{
|
|
(void)netsnmp_addrcache_add(NULL);
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_ADDRCACHE_AGE */
|
|
|
|
/*******************************************************************-o-******
|
|
* netsnmp_agent_check_packet
|
|
*
|
|
* Parameters:
|
|
* session, transport, transport_data, transport_data_length
|
|
*
|
|
* Returns:
|
|
* 1 On success.
|
|
* 0 On error.
|
|
*
|
|
* Handler for all incoming messages (a.k.a. packets) for the agent. If using
|
|
* the libwrap utility, log the connection and deny/allow the access. Print
|
|
* output when appropriate, and increment the incoming counter.
|
|
*
|
|
*/
|
|
|
|
int
|
|
netsnmp_agent_check_packet(netsnmp_session * session,
|
|
netsnmp_transport *transport,
|
|
void *transport_data, int transport_data_length)
|
|
{
|
|
char *addr_string = NULL;
|
|
#ifdef NETSNMP_USE_LIBWRAP
|
|
char *tcpudpaddr = NULL, *name;
|
|
short not_log_connection;
|
|
|
|
name = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
|
|
NETSNMP_DS_LIB_APPTYPE);
|
|
|
|
/* not_log_connection will be 1 if we should skip the messages */
|
|
not_log_connection = netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
|
|
NETSNMP_DS_AGENT_DONT_LOG_TCPWRAPPERS_CONNECTS);
|
|
|
|
/*
|
|
* handle the error case
|
|
* default to logging the messages
|
|
*/
|
|
if (not_log_connection == SNMPERR_GENERR) not_log_connection = 0;
|
|
#endif
|
|
|
|
/*
|
|
* Log the message and/or dump the message.
|
|
* Optionally cache the network address of the sender.
|
|
*/
|
|
|
|
if (transport != NULL && transport->f_fmtaddr != NULL) {
|
|
/*
|
|
* Okay I do know how to format this address for logging.
|
|
*/
|
|
addr_string = transport->f_fmtaddr(transport, transport_data,
|
|
transport_data_length);
|
|
/*
|
|
* Don't forget to free() it.
|
|
*/
|
|
}
|
|
#ifdef NETSNMP_USE_LIBWRAP
|
|
/* Catch udp,udp6,tcp,tcp6 transports using "[" */
|
|
if (addr_string)
|
|
tcpudpaddr = strstr(addr_string, "[");
|
|
if ( tcpudpaddr != 0 ) {
|
|
char sbuf[64];
|
|
char *xp;
|
|
|
|
strlcpy(sbuf, tcpudpaddr + 1, sizeof(sbuf));
|
|
xp = strstr(sbuf, "]");
|
|
if (xp)
|
|
*xp = '\0';
|
|
|
|
if (hosts_ctl(name, STRING_UNKNOWN, sbuf, STRING_UNKNOWN)) {
|
|
if (!not_log_connection) {
|
|
snmp_log(allow_severity, "Connection from %s\n", addr_string);
|
|
}
|
|
} else {
|
|
snmp_log(deny_severity, "Connection from %s REFUSED\n",
|
|
addr_string);
|
|
SNMP_FREE(addr_string);
|
|
return 0;
|
|
}
|
|
} else {
|
|
/*
|
|
* don't log callback connections.
|
|
* What about 'Local IPC', 'IPX' and 'AAL5 PVC'?
|
|
*/
|
|
if (0 == strncmp(addr_string, "callback", 8))
|
|
;
|
|
else if (hosts_ctl(name, STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN)){
|
|
if (!not_log_connection) {
|
|
snmp_log(allow_severity, "Connection from <UNKNOWN> (%s)\n", addr_string);
|
|
};
|
|
SNMP_FREE(addr_string);
|
|
addr_string = strdup("<UNKNOWN>");
|
|
} else {
|
|
snmp_log(deny_severity, "Connection from <UNKNOWN> (%s) REFUSED\n", addr_string);
|
|
SNMP_FREE(addr_string);
|
|
return 0;
|
|
}
|
|
}
|
|
#endif /*NETSNMP_USE_LIBWRAP */
|
|
|
|
snmp_increment_statistic(STAT_SNMPINPKTS);
|
|
|
|
if (addr_string != NULL) {
|
|
netsnmp_addrcache_add(addr_string);
|
|
SNMP_FREE(addr_string);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
int
|
|
netsnmp_agent_check_parse(netsnmp_session * session, netsnmp_pdu *pdu,
|
|
int result)
|
|
{
|
|
if (result == 0) {
|
|
if (snmp_get_do_logging() &&
|
|
netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
|
|
NETSNMP_DS_AGENT_VERBOSE)) {
|
|
netsnmp_variable_list *var_ptr;
|
|
|
|
switch (pdu->command) {
|
|
case SNMP_MSG_GET:
|
|
snmp_log(LOG_DEBUG, " GET message\n");
|
|
break;
|
|
case SNMP_MSG_GETNEXT:
|
|
snmp_log(LOG_DEBUG, " GETNEXT message\n");
|
|
break;
|
|
case SNMP_MSG_RESPONSE:
|
|
snmp_log(LOG_DEBUG, " RESPONSE message\n");
|
|
break;
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
case SNMP_MSG_SET:
|
|
snmp_log(LOG_DEBUG, " SET message\n");
|
|
break;
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
case SNMP_MSG_TRAP:
|
|
snmp_log(LOG_DEBUG, " TRAP message\n");
|
|
break;
|
|
case SNMP_MSG_GETBULK:
|
|
snmp_log(LOG_DEBUG, " GETBULK message, non-rep=%ld, max_rep=%ld\n",
|
|
pdu->errstat, pdu->errindex);
|
|
break;
|
|
case SNMP_MSG_INFORM:
|
|
snmp_log(LOG_DEBUG, " INFORM message\n");
|
|
break;
|
|
case SNMP_MSG_TRAP2:
|
|
snmp_log(LOG_DEBUG, " TRAP2 message\n");
|
|
break;
|
|
case SNMP_MSG_REPORT:
|
|
snmp_log(LOG_DEBUG, " REPORT message\n");
|
|
break;
|
|
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
case SNMP_MSG_INTERNAL_SET_RESERVE1:
|
|
snmp_log(LOG_DEBUG, " INTERNAL RESERVE1 message\n");
|
|
break;
|
|
|
|
case SNMP_MSG_INTERNAL_SET_RESERVE2:
|
|
snmp_log(LOG_DEBUG, " INTERNAL RESERVE2 message\n");
|
|
break;
|
|
|
|
case SNMP_MSG_INTERNAL_SET_ACTION:
|
|
snmp_log(LOG_DEBUG, " INTERNAL ACTION message\n");
|
|
break;
|
|
|
|
case SNMP_MSG_INTERNAL_SET_COMMIT:
|
|
snmp_log(LOG_DEBUG, " INTERNAL COMMIT message\n");
|
|
break;
|
|
|
|
case SNMP_MSG_INTERNAL_SET_FREE:
|
|
snmp_log(LOG_DEBUG, " INTERNAL FREE message\n");
|
|
break;
|
|
|
|
case SNMP_MSG_INTERNAL_SET_UNDO:
|
|
snmp_log(LOG_DEBUG, " INTERNAL UNDO message\n");
|
|
break;
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
default:
|
|
snmp_log(LOG_DEBUG, " UNKNOWN message, type=%02X\n",
|
|
pdu->command);
|
|
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
|
return 0;
|
|
}
|
|
|
|
for (var_ptr = pdu->variables; var_ptr != NULL;
|
|
var_ptr = var_ptr->next_variable) {
|
|
size_t c_oidlen = 256, c_outlen = 0;
|
|
u_char *c_oid = (u_char *) malloc(c_oidlen);
|
|
|
|
if (c_oid) {
|
|
if (!sprint_realloc_objid
|
|
(&c_oid, &c_oidlen, &c_outlen, 1, var_ptr->name,
|
|
var_ptr->name_length)) {
|
|
snmp_log(LOG_DEBUG, " -- %s [TRUNCATED]\n",
|
|
c_oid);
|
|
} else {
|
|
snmp_log(LOG_DEBUG, " -- %s\n", c_oid);
|
|
}
|
|
SNMP_FREE(c_oid);
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
return 0; /* XXX: does it matter what the return value
|
|
* is? Yes: if we return 0, then the PDU is
|
|
* dumped. */
|
|
}
|
|
|
|
|
|
/*
|
|
* Global access to the primary session structure for this agent.
|
|
* for Index Allocation use initially.
|
|
*/
|
|
|
|
/*
|
|
* I don't understand what this is for at the moment. AFAICS as long as it
|
|
* gets set and points at a session, that's fine. ???
|
|
*/
|
|
|
|
netsnmp_session *main_session = NULL;
|
|
|
|
|
|
|
|
/*
|
|
* Set up an agent session on the given transport. Return a handle
|
|
* which may later be used to de-register this transport. A return
|
|
* value of -1 indicates an error.
|
|
*/
|
|
|
|
int
|
|
netsnmp_register_agent_nsap(netsnmp_transport *t)
|
|
{
|
|
netsnmp_session *s, *sp = NULL;
|
|
agent_nsap *a = NULL, *n = NULL, **prevNext = &agent_nsap_list;
|
|
int handle = 0;
|
|
void *isp = NULL;
|
|
|
|
if (t == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
DEBUGMSGTL(("netsnmp_register_agent_nsap", "fd %d\n", t->sock));
|
|
|
|
n = (agent_nsap *) malloc(sizeof(agent_nsap));
|
|
if (n == NULL) {
|
|
return -1;
|
|
}
|
|
s = (netsnmp_session *) malloc(sizeof(netsnmp_session));
|
|
if (s == NULL) {
|
|
SNMP_FREE(n);
|
|
return -1;
|
|
}
|
|
snmp_sess_init(s);
|
|
|
|
/*
|
|
* Set up the session appropriately for an agent.
|
|
*/
|
|
|
|
s->version = SNMP_DEFAULT_VERSION;
|
|
s->callback = handle_snmp_packet;
|
|
s->authenticator = NULL;
|
|
s->flags = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
|
|
NETSNMP_DS_AGENT_FLAGS);
|
|
s->isAuthoritative = SNMP_SESS_AUTHORITATIVE;
|
|
|
|
/* Optional supplimental transport configuration information and
|
|
final call to actually open the transport */
|
|
if (netsnmp_sess_config_transport(s->transport_configuration, t)
|
|
!= SNMPERR_SUCCESS) {
|
|
SNMP_FREE(s);
|
|
SNMP_FREE(n);
|
|
return -1;
|
|
}
|
|
|
|
|
|
if (t->f_open)
|
|
t = t->f_open(t);
|
|
|
|
if (NULL == t) {
|
|
SNMP_FREE(s);
|
|
SNMP_FREE(n);
|
|
return -1;
|
|
}
|
|
|
|
t->flags |= NETSNMP_TRANSPORT_FLAG_OPENED;
|
|
|
|
sp = snmp_add(s, t, netsnmp_agent_check_packet,
|
|
netsnmp_agent_check_parse);
|
|
if (sp == NULL) {
|
|
SNMP_FREE(s);
|
|
SNMP_FREE(n);
|
|
return -1;
|
|
}
|
|
|
|
isp = snmp_sess_pointer(sp);
|
|
if (isp == NULL) { /* over-cautious */
|
|
SNMP_FREE(s);
|
|
SNMP_FREE(n);
|
|
return -1;
|
|
}
|
|
|
|
n->s = isp;
|
|
n->t = t;
|
|
|
|
if (main_session == NULL) {
|
|
main_session = snmp_sess_session(isp);
|
|
}
|
|
|
|
for (a = agent_nsap_list; a != NULL && handle + 1 >= a->handle;
|
|
a = a->next) {
|
|
handle = a->handle;
|
|
prevNext = &(a->next);
|
|
}
|
|
|
|
if (handle < INT_MAX) {
|
|
n->handle = handle + 1;
|
|
n->next = a;
|
|
*prevNext = n;
|
|
SNMP_FREE(s);
|
|
DEBUGMSGTL(("netsnmp_register_agent_nsap", "handle %d\n", n->handle));
|
|
return n->handle;
|
|
} else {
|
|
SNMP_FREE(s);
|
|
SNMP_FREE(n);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
void
|
|
netsnmp_deregister_agent_nsap(int handle)
|
|
{
|
|
agent_nsap *a = NULL, **prevNext = &agent_nsap_list;
|
|
int main_session_deregistered = 0;
|
|
|
|
DEBUGMSGTL(("netsnmp_deregister_agent_nsap", "handle %d\n", handle));
|
|
|
|
for (a = agent_nsap_list; a != NULL && a->handle < handle; a = a->next) {
|
|
prevNext = &(a->next);
|
|
}
|
|
|
|
if (a != NULL && a->handle == handle) {
|
|
*prevNext = a->next;
|
|
if (snmp_sess_session_lookup(a->s)) {
|
|
if (main_session == snmp_sess_session(a->s)) {
|
|
main_session_deregistered = 1;
|
|
}
|
|
snmp_close(snmp_sess_session(a->s));
|
|
/*
|
|
* The above free()s the transport and session pointers.
|
|
*/
|
|
}
|
|
SNMP_FREE(a);
|
|
}
|
|
|
|
/*
|
|
* If we've deregistered the session that main_session used to point to,
|
|
* then make it point to another one, or in the last resort, make it equal
|
|
* to NULL. Basically this shouldn't ever happen in normal operation
|
|
* because main_session starts off pointing at the first session added by
|
|
* init_master_agent(), which then discards the handle.
|
|
*/
|
|
|
|
if (main_session_deregistered) {
|
|
if (agent_nsap_list != NULL) {
|
|
DEBUGMSGTL(("snmp_agent",
|
|
"WARNING: main_session ptr changed from %p to %p\n",
|
|
main_session, snmp_sess_session(agent_nsap_list->s)));
|
|
main_session = snmp_sess_session(agent_nsap_list->s);
|
|
} else {
|
|
DEBUGMSGTL(("snmp_agent",
|
|
"WARNING: main_session ptr changed from %p to NULL\n",
|
|
main_session));
|
|
main_session = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
netsnmp_agent_listen_on(const char *port)
|
|
{
|
|
netsnmp_transport *transport;
|
|
int handle;
|
|
|
|
if (NULL == port)
|
|
return -1;
|
|
|
|
transport = netsnmp_transport_open_server("snmp", port);
|
|
if (transport == NULL) {
|
|
snmp_log(LOG_ERR, "Error opening specified endpoint \"%s\"\n", port);
|
|
return -1;
|
|
}
|
|
|
|
handle = netsnmp_register_agent_nsap(transport);
|
|
if (handle < 0) {
|
|
snmp_log(LOG_ERR, "Error registering specified transport \"%s\" as an "
|
|
"agent NSAP\n", port);
|
|
return -1;
|
|
} else {
|
|
DEBUGMSGTL(("snmp_agent",
|
|
"init_master_agent; \"%s\" registered as an agent NSAP\n",
|
|
port));
|
|
}
|
|
|
|
return handle;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* This function has been modified to use the experimental
|
|
* netsnmp_register_agent_nsap interface. The major responsibility of this
|
|
* function now is to interpret a string specified to the agent (via -p on the
|
|
* command line, or from a configuration file) as a list of agent NSAPs on
|
|
* which to listen for SNMP packets. Typically, when you add a new transport
|
|
* domain "foo", you add code here such that if the "foo" code is compiled
|
|
* into the agent (SNMP_TRANSPORT_FOO_DOMAIN is defined), then a token of the
|
|
* form "foo:bletch-3a0054ef%wob&wob" gets turned into the appropriate
|
|
* transport descriptor. netsnmp_register_agent_nsap is then called with that
|
|
* transport descriptor and sets up a listening agent session on it.
|
|
*
|
|
* Everything then works much as normal: the agent runs in an infinite loop
|
|
* (in the snmpd.c/receive()routine), which calls snmp_read() when a request
|
|
* is readable on any of the given transports. This routine then traverses
|
|
* the library 'Sessions' list to identify the relevant session and eventually
|
|
* invokes '_sess_read'. This then processes the incoming packet, calling the
|
|
* pre_parse, parse, post_parse and callback routines in turn.
|
|
*
|
|
* JBPN 20001117
|
|
*/
|
|
|
|
int
|
|
init_master_agent(void)
|
|
{
|
|
char *cptr;
|
|
char *buf = NULL;
|
|
char *st;
|
|
|
|
/* default to a default cache size */
|
|
netsnmp_set_lookup_cache_size(-1);
|
|
|
|
if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
|
|
NETSNMP_DS_AGENT_ROLE) != MASTER_AGENT) {
|
|
DEBUGMSGTL(("snmp_agent",
|
|
"init_master_agent; not master agent\n"));
|
|
|
|
netsnmp_assert("agent role !master && !sub_agent");
|
|
|
|
return 0; /* No error if ! MASTER_AGENT */
|
|
}
|
|
|
|
#ifndef NETSNMP_NO_LISTEN_SUPPORT
|
|
/*
|
|
* Have specific agent ports been specified?
|
|
*/
|
|
cptr = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID,
|
|
NETSNMP_DS_AGENT_PORTS);
|
|
|
|
if (cptr) {
|
|
buf = strdup(cptr);
|
|
if (!buf) {
|
|
snmp_log(LOG_ERR,
|
|
"Error processing transport \"%s\"\n", cptr);
|
|
return 1;
|
|
}
|
|
} else {
|
|
/*
|
|
* No, so just specify the default port.
|
|
*/
|
|
buf = strdup("");
|
|
}
|
|
|
|
DEBUGMSGTL(("snmp_agent", "final port spec: \"%s\"\n", buf));
|
|
st = buf;
|
|
do {
|
|
/*
|
|
* Specification format:
|
|
*
|
|
* NONE: (a pseudo-transport)
|
|
* UDP:[address:]port (also default if no transport is specified)
|
|
* TCP:[address:]port (if supported)
|
|
* Unix:pathname (if supported)
|
|
* AAL5PVC:itf.vpi.vci (if supported)
|
|
* IPX:[network]:node[/port] (if supported)
|
|
*
|
|
*/
|
|
|
|
cptr = st;
|
|
st = strchr(st, ',');
|
|
if (st)
|
|
*st++ = '\0';
|
|
|
|
DEBUGMSGTL(("snmp_agent", "installing master agent on port %s\n",
|
|
cptr));
|
|
|
|
if (strncasecmp(cptr, "none", 4) == 0) {
|
|
DEBUGMSGTL(("snmp_agent",
|
|
"init_master_agent; pseudo-transport \"none\" "
|
|
"requested\n"));
|
|
break;
|
|
}
|
|
if (-1 == netsnmp_agent_listen_on(cptr)) {
|
|
SNMP_FREE(buf);
|
|
return 1;
|
|
}
|
|
} while(st && *st != '\0');
|
|
SNMP_FREE(buf);
|
|
#endif /* NETSNMP_NO_LISTEN_SUPPORT */
|
|
|
|
#ifdef USING_AGENTX_MASTER_MODULE
|
|
if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
|
|
NETSNMP_DS_AGENT_AGENTX_MASTER) == 1)
|
|
real_init_master();
|
|
#endif
|
|
#ifdef USING_SMUX_MODULE
|
|
if(should_init("smux"))
|
|
real_init_smux();
|
|
#endif
|
|
|
|
#ifndef NETSNMP_NO_PDU_STATS
|
|
_pdu_stats_init();
|
|
#endif /* NETSNMP_NO_PDU_STATS */
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
clear_nsap_list(void)
|
|
{
|
|
DEBUGMSGTL(("clear_nsap_list", "clear the nsap list\n"));
|
|
|
|
while (agent_nsap_list != NULL)
|
|
netsnmp_deregister_agent_nsap(agent_nsap_list->handle);
|
|
}
|
|
|
|
void
|
|
shutdown_master_agent(void)
|
|
{
|
|
clear_nsap_list();
|
|
|
|
#ifndef NETSNMP_NO_PDU_STATS
|
|
_pdu_stats_shutdown();
|
|
#endif /* NETSNMP_NO_PDU_STATS */
|
|
}
|
|
|
|
|
|
netsnmp_agent_session *
|
|
init_agent_snmp_session(netsnmp_session * session, netsnmp_pdu *pdu)
|
|
{
|
|
netsnmp_agent_session *asp = (netsnmp_agent_session *)
|
|
calloc(1, sizeof(netsnmp_agent_session));
|
|
|
|
if (asp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
DEBUGMSGTL(("snmp_agent","agent_sesion %8p created\n", asp));
|
|
asp->session = session;
|
|
asp->pdu = snmp_clone_pdu(pdu);
|
|
asp->orig_pdu = snmp_clone_pdu(pdu);
|
|
asp->rw = READ;
|
|
asp->exact = TRUE;
|
|
asp->next = NULL;
|
|
asp->mode = RESERVE1;
|
|
asp->status = SNMP_ERR_NOERROR;
|
|
asp->index = 0;
|
|
asp->oldmode = 0;
|
|
asp->treecache_num = -1;
|
|
asp->treecache_len = 0;
|
|
asp->reqinfo = SNMP_MALLOC_TYPEDEF(netsnmp_agent_request_info);
|
|
asp->flags = SNMP_AGENT_FLAGS_NONE;
|
|
DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p created\n",
|
|
asp, asp->reqinfo));
|
|
|
|
return asp;
|
|
}
|
|
|
|
void
|
|
free_agent_snmp_session(netsnmp_agent_session *asp)
|
|
{
|
|
if (!asp)
|
|
return;
|
|
|
|
DEBUGMSGTL(("snmp_agent","agent_session %8p released\n", asp));
|
|
|
|
netsnmp_remove_from_delegated(asp);
|
|
|
|
DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p freed\n",
|
|
asp, asp->reqinfo));
|
|
if (asp->orig_pdu)
|
|
snmp_free_pdu(asp->orig_pdu);
|
|
if (asp->pdu)
|
|
snmp_free_pdu(asp->pdu);
|
|
if (asp->reqinfo)
|
|
netsnmp_free_agent_request_info(asp->reqinfo);
|
|
SNMP_FREE(asp->treecache);
|
|
SNMP_FREE(asp->bulkcache);
|
|
if (asp->requests) {
|
|
int i;
|
|
for (i = 0; i < asp->vbcount; i++) {
|
|
netsnmp_free_request_data_sets(&asp->requests[i]);
|
|
}
|
|
SNMP_FREE(asp->requests);
|
|
}
|
|
if (asp->cache_store) {
|
|
netsnmp_free_cachemap(asp->cache_store);
|
|
asp->cache_store = NULL;
|
|
}
|
|
SNMP_FREE(asp);
|
|
}
|
|
|
|
int
|
|
netsnmp_check_for_delegated(netsnmp_agent_session *asp)
|
|
{
|
|
int i;
|
|
netsnmp_request_info *request;
|
|
|
|
if (NULL == asp->treecache)
|
|
return 0;
|
|
|
|
if (asp->flags & SNMP_AGENT_FLAGS_CANCEL_IN_PROGRESS)
|
|
return 0;
|
|
|
|
for (i = 0; i <= asp->treecache_num; i++) {
|
|
for (request = asp->treecache[i].requests_begin; request;
|
|
request = request->next) {
|
|
if (request->delegated)
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
netsnmp_check_delegated_chain_for(netsnmp_agent_session *asp)
|
|
{
|
|
netsnmp_agent_session *asptmp;
|
|
for (asptmp = agent_delegated_list; asptmp; asptmp = asptmp->next) {
|
|
if (asptmp == asp)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
netsnmp_check_for_delegated_and_add(netsnmp_agent_session *asp)
|
|
{
|
|
if (netsnmp_check_for_delegated(asp)) {
|
|
if (!netsnmp_check_delegated_chain_for(asp)) {
|
|
/*
|
|
* add to delegated request chain
|
|
*/
|
|
asp->next = agent_delegated_list;
|
|
agent_delegated_list = asp;
|
|
DEBUGMSGTL(("snmp_agent", "delegate session == %8p\n", asp));
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
netsnmp_remove_from_delegated(netsnmp_agent_session *asp)
|
|
{
|
|
netsnmp_agent_session *curr, *prev = NULL;
|
|
|
|
for (curr = agent_delegated_list; curr; prev = curr, curr = curr->next) {
|
|
/*
|
|
* is this us?
|
|
*/
|
|
if (curr != asp)
|
|
continue;
|
|
|
|
/*
|
|
* remove from queue
|
|
*/
|
|
if (prev != NULL)
|
|
prev->next = asp->next;
|
|
else
|
|
agent_delegated_list = asp->next;
|
|
|
|
DEBUGMSGTL(("snmp_agent", "remove delegated session == %8p\n", asp));
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* netsnmp_remove_delegated_requests_for_session
|
|
*
|
|
* called when a session is being closed. Check all delegated requests to
|
|
* see if the are waiting on this session, and if set, set the status for
|
|
* that request to GENERR.
|
|
*/
|
|
int
|
|
netsnmp_remove_delegated_requests_for_session(netsnmp_session *sess)
|
|
{
|
|
netsnmp_agent_session *asp;
|
|
int total_count = 0;
|
|
|
|
for (asp = agent_delegated_list; asp; asp = asp->next) {
|
|
/*
|
|
* check each request
|
|
*/
|
|
int i;
|
|
int count = 0;
|
|
netsnmp_request_info *request;
|
|
for (i = 0; i <= asp->treecache_num; i++) {
|
|
for (request = asp->treecache[i].requests_begin; request;
|
|
request = request->next) {
|
|
/*
|
|
* check session
|
|
*/
|
|
netsnmp_assert(NULL!=request->subtree);
|
|
if(request->subtree->session != sess)
|
|
continue;
|
|
|
|
/*
|
|
* matched! mark request as done
|
|
*/
|
|
netsnmp_request_set_error(request, SNMP_ERR_GENERR);
|
|
++count;
|
|
}
|
|
}
|
|
if (count) {
|
|
asp->flags |= SNMP_AGENT_FLAGS_CANCEL_IN_PROGRESS;
|
|
total_count += count;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* if we found any, that request may be finished now
|
|
*/
|
|
if(total_count) {
|
|
DEBUGMSGTL(("snmp_agent", "removed %d delegated request(s) for session "
|
|
"%8p\n", total_count, sess));
|
|
netsnmp_check_delegated_requests();
|
|
}
|
|
|
|
return total_count;
|
|
}
|
|
|
|
#if 0
|
|
int
|
|
netsnmp_check_queued_chain_for(netsnmp_agent_session *asp)
|
|
{
|
|
netsnmp_agent_session *asptmp;
|
|
for (asptmp = netsnmp_agent_queued_list; asptmp; asptmp = asptmp->next) {
|
|
if (asptmp == asp)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
netsnmp_add_queued(netsnmp_agent_session *asp)
|
|
{
|
|
netsnmp_agent_session *asp_tmp;
|
|
|
|
/*
|
|
* first item?
|
|
*/
|
|
if (NULL == netsnmp_agent_queued_list) {
|
|
netsnmp_agent_queued_list = asp;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* add to end of queued request chain
|
|
*/
|
|
asp_tmp = netsnmp_agent_queued_list;
|
|
for (; asp_tmp; asp_tmp = asp_tmp->next) {
|
|
/*
|
|
* already in queue?
|
|
*/
|
|
if (asp_tmp == asp)
|
|
break;
|
|
|
|
/*
|
|
* end of queue?
|
|
*/
|
|
if (NULL == asp_tmp->next)
|
|
asp_tmp->next = asp;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#ifndef NETSNMP_NO_PDU_STATS
|
|
/*
|
|
* netsnmp_pdu_stats_process: record time for pdu processing
|
|
*/
|
|
int
|
|
netsnmp_pdu_stats_process(netsnmp_agent_session *asp)
|
|
{
|
|
netsnmp_pdu_stats *new_entry, *old = NULL;
|
|
struct timeval tv_end;
|
|
marker_t start, end = &tv_end;
|
|
u_long msec;
|
|
|
|
if (NULL == asp) {
|
|
DEBUGMSGTL(("stats:pdu", "netsnmp_pdu_stats_process bad params\n"));
|
|
return -1;
|
|
}
|
|
|
|
/** get start/end time */
|
|
netsnmp_set_monotonic_marker(&end);
|
|
start = (marker_t)netsnmp_agent_get_list_data(asp->reqinfo,
|
|
"netsnmp_pdu_stats");
|
|
if (NULL == start) {
|
|
DEBUGMSGTL(("stats:pdu:stop", "start time not found!\n"));
|
|
return -1;
|
|
}
|
|
|
|
msec = uatime_diff(start, end);
|
|
DEBUGMSGTL(("stats:pdu:stop", "pdu processing took %ld msec\n", msec));
|
|
|
|
/** bail if below threshold or less than current low time */
|
|
if (msec <= _pdu_stats_threshold || msec < _pdu_stats_current_lowest) {
|
|
DEBUGMSGTL(("9:stats:pdu",
|
|
"time below thresholds (%ld/%ld); ignoring\n",
|
|
_pdu_stats_threshold, _pdu_stats_current_lowest));
|
|
return 0;
|
|
}
|
|
|
|
/** insert in list. if list goes over max size, truncate last entry. */
|
|
new_entry = SNMP_MALLOC_TYPEDEF(netsnmp_pdu_stats);
|
|
if (NULL == new_entry) {
|
|
snmp_log(LOG_ERR, "malloc failed for pdu stats entry\n");
|
|
return -1;
|
|
}
|
|
new_entry->processing_time = msec;
|
|
time(&new_entry->timestamp);
|
|
new_entry->pdu = snmp_clone_pdu(asp->pdu);
|
|
|
|
CONTAINER_INSERT(_pdu_stats, new_entry);
|
|
|
|
if (CONTAINER_SIZE(_pdu_stats) > _pdu_stats_max) {
|
|
DEBUGMSGTL(("9:stats:pdu", "dropping old/low stat\n"));
|
|
CONTAINER_REMOVE_AT(_pdu_stats, _pdu_stats_max, (void**)&old);
|
|
if (old) {
|
|
snmp_free_pdu(old->pdu);
|
|
free(old);
|
|
}
|
|
}
|
|
|
|
if (CONTAINER_SIZE(_pdu_stats) < _pdu_stats_max)
|
|
_pdu_stats_current_lowest = 0; /* take anything over threshold */
|
|
else {
|
|
CONTAINER_GET_AT(_pdu_stats, _pdu_stats_max - 1, (void**)&old);
|
|
if (old)
|
|
_pdu_stats_current_lowest = old->processing_time;
|
|
}
|
|
|
|
DEBUGIF("9:stats:pdu") {
|
|
_dump_pdu_stats();
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
#endif /* NETSNMP_NO_PDU_STATS */
|
|
|
|
int
|
|
netsnmp_wrap_up_request(netsnmp_agent_session *asp, int status)
|
|
{
|
|
#ifndef NETSNMP_NO_PDU_STATS
|
|
if (_pdu_stats_max > 0)
|
|
netsnmp_pdu_stats_process(asp);
|
|
#endif /* NETSNMP_NO_PDU_STATS */
|
|
|
|
/*
|
|
* if this request was a set, clear the global now that we are
|
|
* done.
|
|
*/
|
|
if (asp == netsnmp_processing_set) {
|
|
DEBUGMSGTL(("snmp_agent", "SET request complete, asp = %8p\n",
|
|
asp));
|
|
netsnmp_processing_set = NULL;
|
|
}
|
|
|
|
if (asp->pdu) {
|
|
/*
|
|
* If we've got an error status, then this needs to be
|
|
* passed back up to the higher levels....
|
|
*/
|
|
if ( status != 0 && asp->status == 0 )
|
|
asp->status = status;
|
|
|
|
switch (asp->pdu->command) {
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
case SNMP_MSG_INTERNAL_SET_BEGIN:
|
|
case SNMP_MSG_INTERNAL_SET_RESERVE1:
|
|
case SNMP_MSG_INTERNAL_SET_RESERVE2:
|
|
case SNMP_MSG_INTERNAL_SET_ACTION:
|
|
/*
|
|
* some stuff needs to be saved in special subagent cases
|
|
*/
|
|
save_set_cache(asp);
|
|
break;
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
case SNMP_MSG_GETNEXT:
|
|
_fix_endofmibview(asp);
|
|
break;
|
|
|
|
case SNMP_MSG_GETBULK:
|
|
/*
|
|
* for a GETBULK response we need to rearrange the varbinds
|
|
*/
|
|
_reorder_getbulk(asp);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* May need to "dumb down" a SET error status for a
|
|
* v1 query. See RFC2576 - section 4.3
|
|
*/
|
|
#ifndef NETSNMP_DISABLE_SNMPV1
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
if ((asp->pdu->command == SNMP_MSG_SET) &&
|
|
(asp->pdu->version == SNMP_VERSION_1)) {
|
|
switch (asp->status) {
|
|
case SNMP_ERR_WRONGVALUE:
|
|
case SNMP_ERR_WRONGENCODING:
|
|
case SNMP_ERR_WRONGTYPE:
|
|
case SNMP_ERR_WRONGLENGTH:
|
|
case SNMP_ERR_INCONSISTENTVALUE:
|
|
status = SNMP_ERR_BADVALUE;
|
|
asp->status = SNMP_ERR_BADVALUE;
|
|
break;
|
|
case SNMP_ERR_NOACCESS:
|
|
case SNMP_ERR_NOTWRITABLE:
|
|
case SNMP_ERR_NOCREATION:
|
|
case SNMP_ERR_INCONSISTENTNAME:
|
|
case SNMP_ERR_AUTHORIZATIONERROR:
|
|
status = SNMP_ERR_NOSUCHNAME;
|
|
asp->status = SNMP_ERR_NOSUCHNAME;
|
|
break;
|
|
case SNMP_ERR_RESOURCEUNAVAILABLE:
|
|
case SNMP_ERR_COMMITFAILED:
|
|
case SNMP_ERR_UNDOFAILED:
|
|
status = SNMP_ERR_GENERR;
|
|
asp->status = SNMP_ERR_GENERR;
|
|
break;
|
|
}
|
|
}
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
/*
|
|
* Similarly we may need to "dumb down" v2 exception
|
|
* types to throw an error for a v1 query.
|
|
* See RFC2576 - section 4.1.2.3
|
|
*/
|
|
if (
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
(asp->pdu->command != SNMP_MSG_SET) &&
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
(asp->pdu->version == SNMP_VERSION_1)) {
|
|
netsnmp_variable_list *var_ptr = asp->pdu->variables;
|
|
int i = 1;
|
|
|
|
while (var_ptr != NULL) {
|
|
switch (var_ptr->type) {
|
|
case SNMP_NOSUCHOBJECT:
|
|
case SNMP_NOSUCHINSTANCE:
|
|
case SNMP_ENDOFMIBVIEW:
|
|
case ASN_COUNTER64:
|
|
status = SNMP_ERR_NOSUCHNAME;
|
|
asp->status = SNMP_ERR_NOSUCHNAME;
|
|
asp->index = i;
|
|
break;
|
|
}
|
|
var_ptr = var_ptr->next_variable;
|
|
++i;
|
|
}
|
|
}
|
|
#endif /* snmpv1 support */
|
|
|
|
/** so far so good? try and build packet */
|
|
if (status == SNMP_ERR_NOERROR) {
|
|
struct session_list *slp = snmp_sess_pointer(asp->session);
|
|
|
|
/** build packet to send */
|
|
asp->pdu->command = SNMP_MSG_RESPONSE;
|
|
asp->pdu->errstat = asp->status;
|
|
asp->pdu->errindex = asp->index;
|
|
status = _build_initial_pdu_packet(slp, asp->pdu,
|
|
SNMP_MSG_GETBULK == asp->orig_pdu->command);
|
|
if (SNMPERR_SUCCESS != status){
|
|
if (SNMPERR_TOO_LONG == asp->session->s_snmp_errno)
|
|
status = asp->status = SNMP_ERR_TOOBIG;
|
|
else
|
|
status = asp->status = SNMP_ERR_GENERR;
|
|
}
|
|
}
|
|
|
|
if (status == SNMP_ERR_NOERROR)
|
|
snmp_increment_statistic_by(
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
(asp->pdu->command == SNMP_MSG_SET ?
|
|
STAT_SNMPINTOTALSETVARS : STAT_SNMPINTOTALREQVARS),
|
|
#else
|
|
STAT_SNMPINTOTALREQVARS,
|
|
#endif
|
|
count_varbinds(asp->pdu->variables));
|
|
} /** if asp->pdu */
|
|
|
|
/*
|
|
* Update the snmp error-count statistics
|
|
* XXX - should we include the V2 errors in this or not?
|
|
*/
|
|
#define INCLUDE_V2ERRORS_IN_V1STATS
|
|
|
|
switch (status) {
|
|
#ifdef INCLUDE_V2ERRORS_IN_V1STATS
|
|
case SNMP_ERR_WRONGVALUE:
|
|
case SNMP_ERR_WRONGENCODING:
|
|
case SNMP_ERR_WRONGTYPE:
|
|
case SNMP_ERR_WRONGLENGTH:
|
|
case SNMP_ERR_INCONSISTENTVALUE:
|
|
#endif
|
|
case SNMP_ERR_BADVALUE:
|
|
snmp_increment_statistic(STAT_SNMPOUTBADVALUES);
|
|
break;
|
|
#ifdef INCLUDE_V2ERRORS_IN_V1STATS
|
|
case SNMP_ERR_NOACCESS:
|
|
case SNMP_ERR_NOTWRITABLE:
|
|
case SNMP_ERR_NOCREATION:
|
|
case SNMP_ERR_INCONSISTENTNAME:
|
|
case SNMP_ERR_AUTHORIZATIONERROR:
|
|
#endif
|
|
case SNMP_ERR_NOSUCHNAME:
|
|
snmp_increment_statistic(STAT_SNMPOUTNOSUCHNAMES);
|
|
break;
|
|
#ifdef INCLUDE_V2ERRORS_IN_V1STATS
|
|
case SNMP_ERR_RESOURCEUNAVAILABLE:
|
|
case SNMP_ERR_COMMITFAILED:
|
|
case SNMP_ERR_UNDOFAILED:
|
|
#endif
|
|
case SNMP_ERR_GENERR:
|
|
snmp_increment_statistic(STAT_SNMPOUTGENERRS);
|
|
break;
|
|
|
|
case SNMP_ERR_TOOBIG:
|
|
snmp_increment_statistic(STAT_SNMPOUTTOOBIGS);
|
|
break;
|
|
}
|
|
|
|
if ((status != SNMP_ERR_NOERROR) || (NULL == asp->pdu)) {
|
|
/** Use a copy of the original request to report failures. */
|
|
snmp_free_pdu(asp->pdu);
|
|
asp->pdu = asp->orig_pdu;
|
|
asp->orig_pdu = NULL;
|
|
}
|
|
if (asp->pdu) {
|
|
asp->pdu->command = SNMP_MSG_RESPONSE;
|
|
asp->pdu->errstat = asp->status;
|
|
asp->pdu->errindex = asp->index;
|
|
if (!snmp_send(asp->session, asp->pdu) &&
|
|
asp->session->s_snmp_errno != SNMPERR_SUCCESS) {
|
|
netsnmp_variable_list *var_ptr;
|
|
snmp_perror("send response");
|
|
for (var_ptr = asp->pdu->variables; var_ptr != NULL;
|
|
var_ptr = var_ptr->next_variable) {
|
|
size_t c_oidlen = 256, c_outlen = 0;
|
|
u_char *c_oid = (u_char *) malloc(c_oidlen);
|
|
|
|
if (c_oid) {
|
|
if (!sprint_realloc_objid (&c_oid, &c_oidlen, &c_outlen, 1,
|
|
var_ptr->name,
|
|
var_ptr->name_length)) {
|
|
snmp_log(LOG_ERR, " -- %s [TRUNCATED]\n", c_oid);
|
|
} else {
|
|
snmp_log(LOG_ERR, " -- %s\n", c_oid);
|
|
}
|
|
SNMP_FREE(c_oid);
|
|
}
|
|
}
|
|
snmp_free_pdu(asp->pdu);
|
|
}
|
|
snmp_increment_statistic(STAT_SNMPOUTPKTS);
|
|
snmp_increment_statistic(STAT_SNMPOUTGETRESPONSES);
|
|
asp->pdu = NULL;
|
|
netsnmp_remove_and_free_agent_snmp_session(asp);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_DUMP_SESS_LIST
|
|
void
|
|
dump_sess_list(void)
|
|
{
|
|
netsnmp_agent_session *a;
|
|
|
|
DEBUGMSGTL(("snmp_agent", "DUMP agent_sess_list -> "));
|
|
for (a = agent_session_list; a != NULL; a = a->next) {
|
|
DEBUGMSG(("snmp_agent", "%8p[session %8p] -> ", a, a->session));
|
|
}
|
|
DEBUGMSG(("snmp_agent", "[NIL]\n"));
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_DUMP_SESS_LIST */
|
|
|
|
void
|
|
netsnmp_remove_and_free_agent_snmp_session(netsnmp_agent_session *asp)
|
|
{
|
|
netsnmp_agent_session *a, **prevNext = &agent_session_list;
|
|
|
|
DEBUGMSGTL(("snmp_agent", "REMOVE session == %8p\n", asp));
|
|
|
|
for (a = agent_session_list; a != NULL; a = *prevNext) {
|
|
if (a == asp) {
|
|
*prevNext = a->next;
|
|
a->next = NULL;
|
|
free_agent_snmp_session(a);
|
|
asp = NULL;
|
|
break;
|
|
} else {
|
|
prevNext = &(a->next);
|
|
}
|
|
}
|
|
|
|
if (a == NULL && asp != NULL) {
|
|
/*
|
|
* We coulnd't find it on the list, so free it anyway.
|
|
*/
|
|
free_agent_snmp_session(asp);
|
|
}
|
|
}
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_FREE_AGENT_SNMP_SESSION_BY_SESSION
|
|
void
|
|
netsnmp_free_agent_snmp_session_by_session(netsnmp_session * sess,
|
|
void (*free_request)
|
|
(netsnmp_request_list *))
|
|
{
|
|
netsnmp_agent_session *a, *next, **prevNext = &agent_session_list;
|
|
|
|
DEBUGMSGTL(("snmp_agent", "REMOVE session == %8p\n", sess));
|
|
|
|
for (a = agent_session_list; a != NULL; a = next) {
|
|
if (a->session == sess) {
|
|
*prevNext = a->next;
|
|
next = a->next;
|
|
free_agent_snmp_session(a);
|
|
} else {
|
|
prevNext = &(a->next);
|
|
next = a->next;
|
|
}
|
|
}
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_FREE_AGENT_SNMP_SESSION_BY_SESSION */
|
|
|
|
/** handles an incoming SNMP packet into the agent */
|
|
int
|
|
handle_snmp_packet(int op, netsnmp_session * session, int reqid,
|
|
netsnmp_pdu *pdu, void *magic)
|
|
{
|
|
netsnmp_agent_session *asp;
|
|
int status, access_ret, rc;
|
|
|
|
/*
|
|
* We only support receiving here.
|
|
*/
|
|
if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* RESPONSE messages won't get this far, but TRAP-like messages
|
|
* might.
|
|
*/
|
|
if (pdu->command == SNMP_MSG_TRAP || pdu->command == SNMP_MSG_INFORM ||
|
|
pdu->command == SNMP_MSG_TRAP2) {
|
|
DEBUGMSGTL(("snmp_agent", "received trap-like PDU (%02x)\n",
|
|
pdu->command));
|
|
pdu->command = SNMP_MSG_TRAP2;
|
|
snmp_increment_statistic(STAT_SNMPUNKNOWNPDUHANDLERS);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* send snmpv3 authfail trap.
|
|
*/
|
|
if (pdu->version == SNMP_VERSION_3 &&
|
|
session->s_snmp_errno == SNMPERR_USM_AUTHENTICATIONFAILURE) {
|
|
send_easy_trap(SNMP_TRAP_AUTHFAIL, 0);
|
|
return 1;
|
|
}
|
|
|
|
if (magic == NULL) {
|
|
asp = init_agent_snmp_session(session, pdu);
|
|
status = SNMP_ERR_NOERROR;
|
|
} else {
|
|
asp = (netsnmp_agent_session *) magic;
|
|
status = asp->status;
|
|
}
|
|
|
|
#if defined(NETSNMP_DISABLE_SET_SUPPORT) && !defined(NETSNMP_NO_WRITE_SUPPORT)
|
|
if (pdu->command == SNMP_MSG_SET) {
|
|
/** Silvercreek protocol tests send set with 0 varbinds */
|
|
if (NULL == pdu->variables)
|
|
return netsnmp_wrap_up_request(asp, SNMP_ERR_NOERROR);
|
|
asp->index = 1;
|
|
return netsnmp_wrap_up_request(asp, SNMP_ERR_NOTWRITABLE);
|
|
}
|
|
#endif /* NETSNMP_DISABLE_SET_SUPPORT && !NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
if ((access_ret = check_access(asp->pdu)) != 0) {
|
|
if (access_ret == VACM_NOSUCHCONTEXT) {
|
|
/*
|
|
* rfc3413 section 3.2, step 5 says that we increment the
|
|
* counter but don't return a response of any kind
|
|
*/
|
|
|
|
/*
|
|
* we currently don't support unavailable contexts, as
|
|
* there is no reason to that I currently know of
|
|
*/
|
|
snmp_increment_statistic(STAT_SNMPUNKNOWNCONTEXTS);
|
|
|
|
/*
|
|
* drop the request
|
|
*/
|
|
netsnmp_remove_and_free_agent_snmp_session(asp);
|
|
return 0;
|
|
} else {
|
|
/*
|
|
* access control setup is incorrect
|
|
*/
|
|
send_easy_trap(SNMP_TRAP_AUTHFAIL, 0);
|
|
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
|
|
#if defined(NETSNMP_DISABLE_SNMPV1)
|
|
if (asp->pdu->version != SNMP_VERSION_2c) {
|
|
#else
|
|
#if defined(NETSNMP_DISABLE_SNMPV2C)
|
|
if (asp->pdu->version != SNMP_VERSION_1) {
|
|
#else
|
|
if (asp->pdu->version != SNMP_VERSION_1
|
|
&& asp->pdu->version != SNMP_VERSION_2c) {
|
|
#endif
|
|
#endif
|
|
asp->pdu->errstat = SNMP_ERR_AUTHORIZATIONERROR;
|
|
asp->pdu->command = SNMP_MSG_RESPONSE;
|
|
snmp_increment_statistic(STAT_SNMPOUTPKTS);
|
|
if (!snmp_send(asp->session, asp->pdu))
|
|
snmp_free_pdu(asp->pdu);
|
|
asp->pdu = NULL;
|
|
netsnmp_remove_and_free_agent_snmp_session(asp);
|
|
return 1;
|
|
} else {
|
|
#endif /* support for community based SNMP */
|
|
/*
|
|
* drop the request
|
|
*/
|
|
netsnmp_remove_and_free_agent_snmp_session(asp);
|
|
return 0;
|
|
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
|
|
}
|
|
#endif /* support for community based SNMP */
|
|
}
|
|
}
|
|
|
|
rc = netsnmp_handle_request(asp, status);
|
|
|
|
/*
|
|
* done
|
|
*/
|
|
DEBUGMSGTL(("snmp_agent", "end of handle_snmp_packet, asp = %8p\n",
|
|
asp));
|
|
return rc;
|
|
}
|
|
|
|
netsnmp_request_info *
|
|
netsnmp_add_varbind_to_cache(netsnmp_agent_session *asp, int vbcount,
|
|
netsnmp_variable_list * varbind_ptr,
|
|
netsnmp_subtree *tp)
|
|
{
|
|
netsnmp_request_info *request = NULL;
|
|
|
|
DEBUGMSGTL(("snmp_agent", "add_vb_to_cache(%8p, %d, ", asp, vbcount));
|
|
DEBUGMSGOID(("snmp_agent", varbind_ptr->name,
|
|
varbind_ptr->name_length));
|
|
DEBUGMSG(("snmp_agent", ", %8p)\n", tp));
|
|
|
|
if (tp &&
|
|
(asp->pdu->command == SNMP_MSG_GETNEXT ||
|
|
asp->pdu->command == SNMP_MSG_GETBULK)) {
|
|
int result;
|
|
int prefix_len;
|
|
|
|
prefix_len = netsnmp_oid_find_prefix(tp->start_a,
|
|
tp->start_len,
|
|
tp->end_a, tp->end_len);
|
|
if (prefix_len < 1) {
|
|
result = VACM_NOTINVIEW; /* ack... bad bad thing happened */
|
|
} else {
|
|
result =
|
|
netsnmp_acm_check_subtree(asp->pdu, tp->start_a, prefix_len);
|
|
}
|
|
|
|
while (result == VACM_NOTINVIEW) {
|
|
/* the entire subtree is not in view. Skip it. */
|
|
/** @todo make this be more intelligent about ranges.
|
|
Right now we merely take the highest level
|
|
commonality of a registration range and use that.
|
|
At times we might be able to be smarter about
|
|
checking the range itself as opposed to the node
|
|
above where the range exists, but I doubt this will
|
|
come up all that frequently. */
|
|
tp = tp->next;
|
|
if (tp) {
|
|
prefix_len = netsnmp_oid_find_prefix(tp->start_a,
|
|
tp->start_len,
|
|
tp->end_a,
|
|
tp->end_len);
|
|
if (prefix_len < 1) {
|
|
/* ack... bad bad thing happened */
|
|
result = VACM_NOTINVIEW;
|
|
} else {
|
|
result =
|
|
netsnmp_acm_check_subtree(asp->pdu,
|
|
tp->start_a, prefix_len);
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
if (tp == NULL) {
|
|
/*
|
|
* no appropriate registration found
|
|
*/
|
|
/*
|
|
* make up the response ourselves
|
|
*/
|
|
switch (asp->pdu->command) {
|
|
case SNMP_MSG_GETNEXT:
|
|
case SNMP_MSG_GETBULK:
|
|
varbind_ptr->type = SNMP_ENDOFMIBVIEW;
|
|
break;
|
|
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
case SNMP_MSG_SET:
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
case SNMP_MSG_GET:
|
|
varbind_ptr->type = SNMP_NOSUCHOBJECT;
|
|
break;
|
|
|
|
default:
|
|
return NULL; /* shouldn't get here */
|
|
}
|
|
} else {
|
|
int cacheid;
|
|
|
|
DEBUGMSGTL(("snmp_agent", "tp->start "));
|
|
DEBUGMSGOID(("snmp_agent", tp->start_a, tp->start_len));
|
|
DEBUGMSG(("snmp_agent", ", tp->end "));
|
|
DEBUGMSGOID(("snmp_agent", tp->end_a, tp->end_len));
|
|
DEBUGMSG(("snmp_agent", ", \n"));
|
|
|
|
/*
|
|
* malloc the request structure
|
|
*/
|
|
request = &(asp->requests[vbcount - 1]);
|
|
request->index = vbcount;
|
|
request->delegated = 0;
|
|
request->processed = 0;
|
|
request->status = 0;
|
|
request->subtree = tp;
|
|
request->agent_req_info = asp->reqinfo;
|
|
if (request->parent_data) {
|
|
netsnmp_free_request_data_sets(request);
|
|
}
|
|
DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p assigned to request\n",
|
|
asp, asp->reqinfo));
|
|
|
|
/*
|
|
* for non-SET modes, set the type to NULL
|
|
*/
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
if (!MODE_IS_SET(asp->pdu->command)) {
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p assigned to request\n",
|
|
asp, asp->reqinfo));
|
|
if (varbind_ptr->type == ASN_PRIV_INCL_RANGE) {
|
|
DEBUGMSGTL(("snmp_agent", "varbind %d is inclusive\n",
|
|
request->index));
|
|
request->inclusive = 1;
|
|
}
|
|
varbind_ptr->type = ASN_NULL;
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
}
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
/*
|
|
* place them in a cache
|
|
*/
|
|
if (tp->global_cacheid) {
|
|
/*
|
|
* we need to merge all marked subtrees together
|
|
*/
|
|
if (asp->cache_store && -1 !=
|
|
(cacheid = netsnmp_get_local_cachid(asp->cache_store,
|
|
tp->global_cacheid))) {
|
|
} else {
|
|
cacheid = ++(asp->treecache_num);
|
|
netsnmp_get_or_add_local_cachid(&asp->cache_store,
|
|
tp->global_cacheid,
|
|
cacheid);
|
|
goto mallocslot; /* XXX: ick */
|
|
}
|
|
} else if (tp->cacheid > -1 && tp->cacheid <= asp->treecache_num &&
|
|
asp->treecache[tp->cacheid].subtree == tp) {
|
|
/*
|
|
* we have already added a request to this tree
|
|
* pointer before
|
|
*/
|
|
cacheid = tp->cacheid;
|
|
} else {
|
|
cacheid = ++(asp->treecache_num);
|
|
mallocslot:
|
|
/*
|
|
* new slot needed
|
|
*/
|
|
if (asp->treecache_num >= asp->treecache_len) {
|
|
/*
|
|
* exapand cache array
|
|
*/
|
|
/*
|
|
* WWW: non-linear expansion needed (with cap)
|
|
*/
|
|
#define CACHE_GROW_SIZE 16
|
|
asp->treecache_len =
|
|
(asp->treecache_len + CACHE_GROW_SIZE);
|
|
asp->treecache =
|
|
(netsnmp_tree_cache *)realloc(asp->treecache,
|
|
sizeof(netsnmp_tree_cache) *
|
|
asp->treecache_len);
|
|
if (asp->treecache == NULL)
|
|
return NULL;
|
|
memset(&(asp->treecache[cacheid]), 0x00,
|
|
sizeof(netsnmp_tree_cache) * (CACHE_GROW_SIZE));
|
|
}
|
|
asp->treecache[cacheid].subtree = tp;
|
|
asp->treecache[cacheid].requests_begin = request;
|
|
tp->cacheid = cacheid;
|
|
}
|
|
|
|
/*
|
|
* if this is a search type, get the ending range oid as well
|
|
*/
|
|
if (asp->pdu->command == SNMP_MSG_GETNEXT ||
|
|
asp->pdu->command == SNMP_MSG_GETBULK) {
|
|
request->range_end = tp->end_a;
|
|
request->range_end_len = tp->end_len;
|
|
} else {
|
|
request->range_end = NULL;
|
|
request->range_end_len = 0;
|
|
}
|
|
|
|
/*
|
|
* link into chain
|
|
*/
|
|
if (asp->treecache[cacheid].requests_end)
|
|
asp->treecache[cacheid].requests_end->next = request;
|
|
request->next = NULL;
|
|
request->prev = asp->treecache[cacheid].requests_end;
|
|
asp->treecache[cacheid].requests_end = request;
|
|
|
|
/*
|
|
* add the given request to the list of requests they need
|
|
* to handle results for
|
|
*/
|
|
request->requestvb = request->requestvb_start = varbind_ptr;
|
|
}
|
|
return request;
|
|
}
|
|
|
|
/*
|
|
* check the ACM(s) for the results on each of the varbinds.
|
|
* If ACM disallows it, replace the value with type
|
|
*
|
|
* Returns number of varbinds with ACM errors
|
|
*/
|
|
int
|
|
check_acm(netsnmp_agent_session *asp, u_char type)
|
|
{
|
|
int view;
|
|
int i, j, k;
|
|
netsnmp_request_info *request;
|
|
int ret = 0;
|
|
netsnmp_variable_list *vb, *vb2, *vbc;
|
|
int earliest = 0;
|
|
|
|
for (i = 0; i <= asp->treecache_num; i++) {
|
|
for (request = asp->treecache[i].requests_begin;
|
|
request; request = request->next) {
|
|
/*
|
|
* for each request, run it through in_a_view()
|
|
*/
|
|
earliest = 0;
|
|
for(j = request->repeat, vb = request->requestvb_start;
|
|
vb && j > -1;
|
|
j--, vb = vb->next_variable) {
|
|
if (vb->type != ASN_NULL &&
|
|
vb->type != ASN_PRIV_RETRY) { /* not yet processed */
|
|
view =
|
|
in_a_view(vb->name, &vb->name_length,
|
|
asp->pdu, vb->type);
|
|
|
|
/*
|
|
* if a ACM error occurs, mark it as type passed in
|
|
*/
|
|
if (view != VACM_SUCCESS) {
|
|
ret++;
|
|
if (request->repeat < request->orig_repeat) {
|
|
/* basically this means a GETBULK */
|
|
request->repeat++;
|
|
if (!earliest) {
|
|
request->requestvb = vb;
|
|
earliest = 1;
|
|
}
|
|
|
|
/* ugh. if a whole now exists, we need to
|
|
move the contents up the chain and fill
|
|
in at the end else we won't end up
|
|
lexographically sorted properly */
|
|
if (j > -1 && vb->next_variable &&
|
|
vb->next_variable->type != ASN_NULL &&
|
|
vb->next_variable->type != ASN_PRIV_RETRY) {
|
|
for(k = j, vbc = vb, vb2 = vb->next_variable;
|
|
k > -2 && vbc && vb2;
|
|
k--, vbc = vb2, vb2 = vb2->next_variable) {
|
|
/* clone next into the current */
|
|
snmp_clone_var(vb2, vbc);
|
|
vbc->next_variable = vb2;
|
|
}
|
|
}
|
|
}
|
|
snmp_set_var_typed_value(vb, type, NULL, 0);
|
|
if (ASN_PRIV_RETRY == type)
|
|
request->inclusive = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
int
|
|
netsnmp_create_subtree_cache(netsnmp_agent_session *asp)
|
|
{
|
|
netsnmp_subtree *tp;
|
|
netsnmp_variable_list *varbind_ptr, *vbsave, *vbptr, **prevNext;
|
|
int view;
|
|
int vbcount = 0;
|
|
int bulkcount = 0, bulkrep = 0;
|
|
int i = 0, n = 0, r = 0;
|
|
netsnmp_request_info *request;
|
|
|
|
if (NULL == asp || NULL == asp->pdu)
|
|
return SNMP_ERR_GENERR;
|
|
|
|
if (asp->pdu->msgMaxSize == 0)
|
|
asp->pdu->msgMaxSize = netsnmp_max_send_msg_size();
|
|
DEBUGMSGTL(("msgMaxSize", "pdu max size %lu\n", asp->pdu->msgMaxSize));
|
|
|
|
if (asp->treecache == NULL && asp->treecache_len == 0) {
|
|
asp->treecache_len = SNMP_MAX(1 + asp->vbcount / 4, 16);
|
|
asp->treecache =
|
|
(netsnmp_tree_cache *)calloc(asp->treecache_len, sizeof(netsnmp_tree_cache));
|
|
if (asp->treecache == NULL)
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
asp->treecache_num = -1;
|
|
|
|
if (asp->pdu->command == SNMP_MSG_GETBULK) {
|
|
/*
|
|
* getbulk prep
|
|
*/
|
|
int count = count_varbinds(asp->pdu->variables);
|
|
if (asp->pdu->errstat < 0) {
|
|
asp->pdu->errstat = 0;
|
|
}
|
|
if (asp->pdu->errindex < 0) {
|
|
asp->pdu->errindex = 0;
|
|
}
|
|
|
|
if (asp->pdu->errstat < count) {
|
|
n = asp->pdu->errstat;
|
|
} else {
|
|
n = count;
|
|
}
|
|
if ((r = count - n) <= 0) {
|
|
r = 0;
|
|
asp->bulkcache = NULL;
|
|
} else {
|
|
int maxbulk =
|
|
netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
|
|
NETSNMP_DS_AGENT_MAX_GETBULKREPEATS);
|
|
int maxresponses =
|
|
netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
|
|
NETSNMP_DS_AGENT_MAX_GETBULKRESPONSES);
|
|
int avgvarbind =
|
|
netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
|
|
NETSNMP_DS_AGENT_AVG_BULKVARBINDSIZE);
|
|
|
|
if (maxresponses == 0)
|
|
maxresponses = 100; /* more than reasonable default */
|
|
|
|
/* ensure that the total number of responses fits in a mallocable
|
|
* result vector
|
|
*/
|
|
if (maxresponses < 0 ||
|
|
maxresponses > (int)(INT_MAX / sizeof(struct varbind_list *)))
|
|
maxresponses = (int)(INT_MAX / sizeof(struct varbind_list *));
|
|
DEBUGMSGTL(("snmp_agent:bulk", "maxresponse %d\n", maxresponses));
|
|
|
|
/* reduce maxresponses by dividing the sessions max size by a
|
|
* (very) rough aproximation of the size of an average
|
|
* varbind. 15 seems to be a reasonable balance between getting
|
|
* enough varbinds to fill the packet vs retrieving varbinds
|
|
* that will be discarded to make the response fit the packet size.
|
|
*/
|
|
if (avgvarbind == 0)
|
|
avgvarbind = 15;
|
|
|
|
if (maxresponses > (asp->pdu->msgMaxSize / avgvarbind)) {
|
|
maxresponses = asp->pdu->msgMaxSize / avgvarbind;
|
|
DEBUGMSGTL(("snmp_agent:bulk",
|
|
"lowering maxresponse to %d based pdusession msgMaxSize %ld and avgBulkVarbindSize %d\n",
|
|
maxresponses, asp->pdu->msgMaxSize, avgvarbind));
|
|
}
|
|
|
|
/* ensure that the maximum number of repetitions will fit in the
|
|
* result vector
|
|
*/
|
|
if (maxbulk <= 0 || maxbulk > maxresponses / r)
|
|
maxbulk = maxresponses / r;
|
|
|
|
/* limit getbulk number of repeats to a configured size */
|
|
if (asp->pdu->errindex > maxbulk) {
|
|
asp->pdu->errindex = maxbulk;
|
|
DEBUGMSGTL(("snmp_agent:bulk",
|
|
"lowering requested getbulk repeats to %ld\n",
|
|
asp->pdu->errindex));
|
|
}
|
|
|
|
asp->bulkcache =
|
|
(netsnmp_variable_list **) malloc(
|
|
(n + asp->pdu->errindex * r) * sizeof(struct varbind_list *));
|
|
|
|
if (!asp->bulkcache) {
|
|
DEBUGMSGTL(("snmp_agent:bulk", "Bulkcache malloc failed\n"));
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
}
|
|
DEBUGMSGTL(("snmp_agent:bulk", "GETBULK N = %d, M = %ld, R = %d\n",
|
|
n, asp->pdu->errindex, r));
|
|
}
|
|
|
|
/*
|
|
* collect varbinds into their registered trees
|
|
*/
|
|
prevNext = &(asp->pdu->variables);
|
|
for (varbind_ptr = asp->pdu->variables; varbind_ptr;
|
|
varbind_ptr = vbsave) {
|
|
|
|
/*
|
|
* getbulk mess with this pointer, so save it
|
|
*/
|
|
vbsave = varbind_ptr->next_variable;
|
|
|
|
if (asp->pdu->command == SNMP_MSG_GETBULK) {
|
|
if (n > 0) {
|
|
n--;
|
|
} else {
|
|
/*
|
|
* repeate request varbinds on GETBULK. These will
|
|
* have to be properly rearranged later though as
|
|
* responses are supposed to actually be interlaced
|
|
* with each other. This is done with the asp->bulkcache.
|
|
*/
|
|
bulkrep = asp->pdu->errindex - 1;
|
|
if (asp->pdu->errindex > 0) {
|
|
vbptr = varbind_ptr;
|
|
asp->bulkcache[bulkcount++] = vbptr;
|
|
|
|
for (i = 1; i < asp->pdu->errindex; i++) {
|
|
vbptr->next_variable =
|
|
SNMP_MALLOC_STRUCT(variable_list);
|
|
/*
|
|
* don't clone the oid as it's got to be
|
|
* overwritten anyway
|
|
*/
|
|
if (!vbptr->next_variable) {
|
|
/*
|
|
* XXXWWW: ack!!!
|
|
*/
|
|
DEBUGMSGTL(("snmp_agent", "NextVar malloc failed\n"));
|
|
} else {
|
|
vbptr = vbptr->next_variable;
|
|
vbptr->name_length = 0;
|
|
vbptr->type = ASN_NULL;
|
|
asp->bulkcache[bulkcount++] = vbptr;
|
|
}
|
|
}
|
|
vbptr->next_variable = vbsave;
|
|
} else {
|
|
/*
|
|
* 0 repeats requested for this varbind, so take it off
|
|
* the list.
|
|
*/
|
|
vbptr = varbind_ptr;
|
|
*prevNext = vbptr->next_variable;
|
|
vbptr->next_variable = NULL;
|
|
snmp_free_varbind(vbptr);
|
|
asp->vbcount--;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* count the varbinds
|
|
*/
|
|
++vbcount;
|
|
|
|
/*
|
|
* find the owning tree
|
|
*/
|
|
tp = netsnmp_subtree_find(varbind_ptr->name, varbind_ptr->name_length,
|
|
NULL, asp->pdu->contextName);
|
|
|
|
/*
|
|
* check access control
|
|
*/
|
|
switch (asp->pdu->command) {
|
|
case SNMP_MSG_GET:
|
|
view = in_a_view(varbind_ptr->name, &varbind_ptr->name_length,
|
|
asp->pdu, varbind_ptr->type);
|
|
if (view != VACM_SUCCESS)
|
|
snmp_set_var_typed_value(varbind_ptr, SNMP_NOSUCHOBJECT,
|
|
NULL, 0);
|
|
break;
|
|
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
case SNMP_MSG_SET:
|
|
view = in_a_view(varbind_ptr->name, &varbind_ptr->name_length,
|
|
asp->pdu, varbind_ptr->type);
|
|
if (view != VACM_SUCCESS) {
|
|
asp->index = vbcount;
|
|
return SNMP_ERR_NOACCESS;
|
|
}
|
|
break;
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
case SNMP_MSG_GETNEXT:
|
|
case SNMP_MSG_GETBULK:
|
|
default:
|
|
view = VACM_SUCCESS;
|
|
/*
|
|
* XXXWWW: check VACM here to see if "tp" is even worthwhile
|
|
*/
|
|
}
|
|
if (view == VACM_SUCCESS) {
|
|
request = netsnmp_add_varbind_to_cache(asp, vbcount, varbind_ptr,
|
|
tp);
|
|
if (request && asp->pdu->command == SNMP_MSG_GETBULK) {
|
|
request->repeat = request->orig_repeat = bulkrep;
|
|
}
|
|
}
|
|
|
|
prevNext = &(varbind_ptr->next_variable);
|
|
}
|
|
|
|
return SNMPERR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* this function is only applicable in getnext like contexts
|
|
*/
|
|
int
|
|
netsnmp_reassign_requests(netsnmp_agent_session *asp)
|
|
{
|
|
/*
|
|
* assume all the requests have been filled or rejected by the
|
|
* subtrees, so reassign the rejected ones to the next subtree in
|
|
* the chain
|
|
*/
|
|
|
|
int i;
|
|
|
|
/*
|
|
* get old info
|
|
*/
|
|
netsnmp_tree_cache *old_treecache = asp->treecache;
|
|
|
|
/*
|
|
* malloc new space
|
|
*/
|
|
asp->treecache =
|
|
(netsnmp_tree_cache *) calloc(asp->treecache_len,
|
|
sizeof(netsnmp_tree_cache));
|
|
|
|
if (asp->treecache == NULL)
|
|
return SNMP_ERR_GENERR;
|
|
|
|
asp->treecache_num = -1;
|
|
if (asp->cache_store) {
|
|
netsnmp_free_cachemap(asp->cache_store);
|
|
asp->cache_store = NULL;
|
|
}
|
|
|
|
for (i = 0; i < asp->vbcount; i++) {
|
|
if (asp->requests[i].requestvb == NULL) {
|
|
/*
|
|
* Occurs when there's a mixture of still active
|
|
* and "endOfMibView" repetitions
|
|
*/
|
|
continue;
|
|
}
|
|
if (asp->requests[i].requestvb->type == ASN_NULL) {
|
|
if (!netsnmp_add_varbind_to_cache(asp, asp->requests[i].index,
|
|
asp->requests[i].requestvb,
|
|
asp->requests[i].subtree->next)) {
|
|
SNMP_FREE(old_treecache);
|
|
}
|
|
} else if (asp->requests[i].requestvb->type == ASN_PRIV_RETRY) {
|
|
/*
|
|
* re-add the same subtree
|
|
*/
|
|
asp->requests[i].requestvb->type = ASN_NULL;
|
|
if (!netsnmp_add_varbind_to_cache(asp, asp->requests[i].index,
|
|
asp->requests[i].requestvb,
|
|
asp->requests[i].subtree)) {
|
|
SNMP_FREE(old_treecache);
|
|
}
|
|
}
|
|
}
|
|
|
|
SNMP_FREE(old_treecache);
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
|
|
void
|
|
netsnmp_delete_request_infos(netsnmp_request_info *reqlist)
|
|
{
|
|
while (reqlist) {
|
|
netsnmp_free_request_data_sets(reqlist);
|
|
reqlist = reqlist->next;
|
|
}
|
|
}
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_DELETE_SUBTREE_CACHE
|
|
void
|
|
netsnmp_delete_subtree_cache(netsnmp_agent_session *asp)
|
|
{
|
|
while (asp->treecache_num >= 0) {
|
|
/*
|
|
* don't delete subtrees
|
|
*/
|
|
netsnmp_delete_request_infos(asp->treecache[asp->treecache_num].
|
|
requests_begin);
|
|
asp->treecache_num--;
|
|
}
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_DELETE_SUBTREE_CACHE */
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_CHECK_ALL_REQUESTS_ERROR
|
|
/*
|
|
* check all requests for errors
|
|
*
|
|
* @Note:
|
|
* This function is a little different from the others in that
|
|
* it does not use any linked lists, instead using the original
|
|
* asp requests array. This is of particular importance for
|
|
* cases where the linked lists are unreliable. One known instance
|
|
* of this scenario occurs when the row_merge helper is used, which
|
|
* may temporarily disrupts linked lists during its (and its childrens)
|
|
* handling of requests.
|
|
*/
|
|
int
|
|
netsnmp_check_all_requests_error(netsnmp_agent_session *asp,
|
|
int look_for_specific)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* find any errors marked in the requests
|
|
*/
|
|
for( i = 0; i < asp->vbcount; ++i ) {
|
|
if ((SNMP_ERR_NOERROR != asp->requests[i].status) &&
|
|
(!look_for_specific ||
|
|
asp->requests[i].status == look_for_specific))
|
|
return asp->requests[i].status;
|
|
}
|
|
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_CHECK_ALL_REQUESTS_ERROR */
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_CHECK_REQUESTS_ERROR
|
|
int
|
|
netsnmp_check_requests_error(netsnmp_request_info *requests)
|
|
{
|
|
/*
|
|
* find any errors marked in the requests
|
|
*/
|
|
for (;requests;requests = requests->next) {
|
|
if (requests->status != SNMP_ERR_NOERROR)
|
|
return requests->status;
|
|
}
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_CHECK_REQUESTS_ERROR */
|
|
|
|
int
|
|
netsnmp_check_requests_status(netsnmp_agent_session *asp,
|
|
netsnmp_request_info *requests,
|
|
int look_for_specific)
|
|
{
|
|
/*
|
|
* find any errors marked in the requests
|
|
*/
|
|
while (requests) {
|
|
if(requests->agent_req_info != asp->reqinfo) {
|
|
DEBUGMSGTL(("verbose:asp",
|
|
"**reqinfo %p doesn't match cached reqinfo %p\n",
|
|
asp->reqinfo, requests->agent_req_info));
|
|
}
|
|
if (requests->status != SNMP_ERR_NOERROR &&
|
|
(!look_for_specific || requests->status == look_for_specific)
|
|
&& (look_for_specific || asp->index == 0
|
|
|| requests->index < asp->index)) {
|
|
asp->index = requests->index;
|
|
asp->status = requests->status;
|
|
}
|
|
requests = requests->next;
|
|
}
|
|
return asp->status;
|
|
}
|
|
|
|
int
|
|
netsnmp_check_all_requests_status(netsnmp_agent_session *asp,
|
|
int look_for_specific)
|
|
{
|
|
int i;
|
|
for (i = 0; i <= asp->treecache_num; i++) {
|
|
netsnmp_check_requests_status(asp,
|
|
asp->treecache[i].requests_begin,
|
|
look_for_specific);
|
|
}
|
|
return asp->status;
|
|
}
|
|
|
|
int
|
|
handle_var_requests(netsnmp_agent_session *asp)
|
|
{
|
|
int i, retstatus = SNMP_ERR_NOERROR,
|
|
status = SNMP_ERR_NOERROR, final_status = SNMP_ERR_NOERROR;
|
|
netsnmp_handler_registration *reginfo;
|
|
|
|
asp->reqinfo->asp = asp;
|
|
asp->reqinfo->mode = asp->mode;
|
|
|
|
/*
|
|
* now, have the subtrees in the cache go search for their results
|
|
*/
|
|
for (i = 0; i <= asp->treecache_num; i++) {
|
|
/*
|
|
* don't call handlers w/null reginfo.
|
|
* - when is this? sub agent disconnected while request processing?
|
|
* - should this case encompass more of this subroutine?
|
|
* - does check_request_status make send if handlers weren't called?
|
|
*/
|
|
if(NULL != asp->treecache[i].subtree->reginfo) {
|
|
reginfo = asp->treecache[i].subtree->reginfo;
|
|
status = netsnmp_call_handlers(reginfo, asp->reqinfo,
|
|
asp->treecache[i].requests_begin);
|
|
}
|
|
else
|
|
status = SNMP_ERR_GENERR;
|
|
|
|
/*
|
|
* find any errors marked in the requests. For later parts of
|
|
* SET processing, only check for new errors specific to that
|
|
* set processing directive (which must superceed the previous
|
|
* errors).
|
|
*/
|
|
switch (asp->mode) {
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
case MODE_SET_COMMIT:
|
|
retstatus = netsnmp_check_requests_status(asp,
|
|
asp->treecache[i].
|
|
requests_begin,
|
|
SNMP_ERR_COMMITFAILED);
|
|
break;
|
|
|
|
case MODE_SET_UNDO:
|
|
retstatus = netsnmp_check_requests_status(asp,
|
|
asp->treecache[i].
|
|
requests_begin,
|
|
SNMP_ERR_UNDOFAILED);
|
|
break;
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
default:
|
|
retstatus = netsnmp_check_requests_status(asp,
|
|
asp->treecache[i].
|
|
requests_begin, 0);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* always take lowest varbind if possible
|
|
*/
|
|
if (retstatus != SNMP_ERR_NOERROR) {
|
|
status = retstatus;
|
|
}
|
|
|
|
/*
|
|
* other things we know less about (no index)
|
|
*/
|
|
/*
|
|
* WWW: drop support for this?
|
|
*/
|
|
if (final_status == SNMP_ERR_NOERROR && status != SNMP_ERR_NOERROR) {
|
|
/*
|
|
* we can't break here, since some processing needs to be
|
|
* done for all requests anyway (IE, SET handling for UNDO
|
|
* needs to be called regardless of previous status
|
|
* results.
|
|
* WWW: This should be predictable though and
|
|
* breaking should be possible in some cases (eg GET,
|
|
* GETNEXT, ...)
|
|
*/
|
|
final_status = status;
|
|
}
|
|
}
|
|
|
|
return final_status;
|
|
}
|
|
|
|
void
|
|
netsnmp_check_delegated_requests(void)
|
|
{
|
|
netsnmp_agent_session *asp, *prev_asp = NULL, *next_asp = NULL;
|
|
|
|
for (asp = agent_delegated_list; asp; asp = next_asp) {
|
|
next_asp = asp->next; /* save in case we clean up asp */
|
|
if (!netsnmp_check_for_delegated(asp)) {
|
|
|
|
/*
|
|
* we're done with this one, remove from queue
|
|
*/
|
|
if (prev_asp != NULL)
|
|
prev_asp->next = asp->next;
|
|
else
|
|
agent_delegated_list = asp->next;
|
|
asp->next = NULL;
|
|
|
|
/*
|
|
* check request status
|
|
*/
|
|
netsnmp_check_all_requests_status(asp, 0);
|
|
|
|
/*
|
|
* continue processing or finish up
|
|
*/
|
|
check_delayed_request(asp);
|
|
|
|
/*
|
|
* if head was removed, don't drop it if it
|
|
* was it re-queued
|
|
*/
|
|
if ((prev_asp == NULL) && (agent_delegated_list == asp)) {
|
|
prev_asp = asp;
|
|
}
|
|
} else {
|
|
|
|
/*
|
|
* asp is still on the queue
|
|
*/
|
|
prev_asp = asp;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* loop through our sessions known delegated sessions and check to see
|
|
* if they've completed yet. If there are no more delegated sessions,
|
|
* check for and process any queued requests
|
|
*/
|
|
void
|
|
netsnmp_check_outstanding_agent_requests(void)
|
|
{
|
|
netsnmp_agent_session *asp;
|
|
|
|
/*
|
|
* deal with delegated requests
|
|
*/
|
|
netsnmp_check_delegated_requests();
|
|
|
|
/*
|
|
* if we are processing a set and there are more delegated
|
|
* requests, keep waiting before getting to queued requests.
|
|
*/
|
|
if (netsnmp_processing_set && (NULL != agent_delegated_list))
|
|
return;
|
|
|
|
while (netsnmp_agent_queued_list) {
|
|
|
|
/*
|
|
* if we are processing a set, the first item better be
|
|
* the set being (or waiting to be) processed.
|
|
*/
|
|
netsnmp_assert((!netsnmp_processing_set) ||
|
|
(netsnmp_processing_set == netsnmp_agent_queued_list));
|
|
|
|
/*
|
|
* if the top request is a set, don't pop it
|
|
* off if there are delegated requests
|
|
*/
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
if ((netsnmp_agent_queued_list->pdu->command == SNMP_MSG_SET) &&
|
|
(agent_delegated_list)) {
|
|
|
|
netsnmp_assert(netsnmp_processing_set == NULL);
|
|
|
|
netsnmp_processing_set = netsnmp_agent_queued_list;
|
|
DEBUGMSGTL(("snmp_agent", "SET request remains queued while "
|
|
"delegated requests finish, asp = %8p\n",
|
|
agent_delegated_list));
|
|
break;
|
|
}
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
/*
|
|
* pop the first request and process it
|
|
*/
|
|
asp = netsnmp_agent_queued_list;
|
|
netsnmp_agent_queued_list = asp->next;
|
|
DEBUGMSGTL(("snmp_agent",
|
|
"processing queued request, asp = %8p\n", asp));
|
|
|
|
netsnmp_handle_request(asp, asp->status);
|
|
|
|
/*
|
|
* if we hit a set, stop
|
|
*/
|
|
if (NULL != netsnmp_processing_set)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/** Decide if the requested transaction_id is still being processed
|
|
within the agent. This is used to validate whether a delayed cache
|
|
(containing possibly freed pointers) is still usable.
|
|
|
|
returns SNMPERR_SUCCESS if it's still valid, or SNMPERR_GENERR if not. */
|
|
int
|
|
netsnmp_check_transaction_id(int transaction_id)
|
|
{
|
|
netsnmp_agent_session *asp;
|
|
|
|
for (asp = agent_delegated_list; asp; asp = asp->next) {
|
|
if (asp->pdu->transid == transaction_id)
|
|
return SNMPERR_SUCCESS;
|
|
}
|
|
return SNMPERR_GENERR;
|
|
}
|
|
|
|
|
|
/*
|
|
* check_delayed_request(asp)
|
|
*
|
|
* Called to rexamine a set of requests and continue processing them
|
|
* once all the previous (delayed) requests have been handled one way
|
|
* or another.
|
|
*/
|
|
|
|
int
|
|
check_delayed_request(netsnmp_agent_session *asp)
|
|
{
|
|
int status = SNMP_ERR_NOERROR;
|
|
|
|
DEBUGMSGTL(("snmp_agent", "processing delegated request, asp = %8p\n",
|
|
asp));
|
|
|
|
switch (asp->mode) {
|
|
case SNMP_MSG_GETBULK:
|
|
case SNMP_MSG_GETNEXT:
|
|
netsnmp_check_all_requests_status(asp, 0);
|
|
if (asp->flags & SNMP_AGENT_FLAGS_CANCEL_IN_PROGRESS) {
|
|
DEBUGMSGTL(("snmp_agent","canceling next walk for asp %p\n", asp));
|
|
break;
|
|
}
|
|
handle_getnext_loop(asp);
|
|
if (netsnmp_check_for_delegated(asp) &&
|
|
netsnmp_check_transaction_id(asp->pdu->transid) !=
|
|
SNMPERR_SUCCESS) {
|
|
/*
|
|
* add to delegated request chain
|
|
*/
|
|
if (!netsnmp_check_delegated_chain_for(asp)) {
|
|
asp->next = agent_delegated_list;
|
|
agent_delegated_list = asp;
|
|
}
|
|
}
|
|
break;
|
|
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
case MODE_SET_COMMIT:
|
|
netsnmp_check_all_requests_status(asp, SNMP_ERR_COMMITFAILED);
|
|
goto settop;
|
|
|
|
case MODE_SET_UNDO:
|
|
netsnmp_check_all_requests_status(asp, SNMP_ERR_UNDOFAILED);
|
|
goto settop;
|
|
|
|
case MODE_SET_BEGIN:
|
|
case MODE_SET_RESERVE1:
|
|
case MODE_SET_RESERVE2:
|
|
case MODE_SET_ACTION:
|
|
case MODE_SET_FREE:
|
|
settop:
|
|
/* If we should do only one pass, this mean we */
|
|
/* should not reenter this function */
|
|
if ((asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY)) {
|
|
/* We should have finished the processing after the first */
|
|
/* handle_set_loop, so just wrap up */
|
|
break;
|
|
}
|
|
handle_set_loop(asp);
|
|
if (asp->mode != FINISHED_SUCCESS && asp->mode != FINISHED_FAILURE) {
|
|
|
|
if (netsnmp_check_for_delegated_and_add(asp)) {
|
|
/*
|
|
* add to delegated request chain
|
|
*/
|
|
if (!asp->status)
|
|
asp->status = status;
|
|
}
|
|
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
break;
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* if we don't have anything outstanding (delegated), wrap up
|
|
*/
|
|
if (!netsnmp_check_for_delegated(asp))
|
|
return netsnmp_wrap_up_request(asp, status);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/** returns 1 if there are valid GETNEXT requests left. Returns 0 if not. */
|
|
int
|
|
check_getnext_results(netsnmp_agent_session *asp)
|
|
{
|
|
/*
|
|
* get old info
|
|
*/
|
|
netsnmp_tree_cache *old_treecache = asp->treecache;
|
|
int old_treecache_num = asp->treecache_num;
|
|
int count = 0;
|
|
int i, special = 0;
|
|
netsnmp_request_info *request;
|
|
|
|
if (asp->mode == SNMP_MSG_GET) {
|
|
/*
|
|
* Special case for doing INCLUSIVE getNext operations in
|
|
* AgentX subagents.
|
|
*/
|
|
DEBUGMSGTL(("snmp_agent",
|
|
"asp->mode == SNMP_MSG_GET in ch_getnext\n"));
|
|
asp->mode = asp->oldmode;
|
|
special = 1;
|
|
}
|
|
|
|
for (i = 0; i <= old_treecache_num; i++) {
|
|
for (request = old_treecache[i].requests_begin; request;
|
|
request = request->next) {
|
|
|
|
/*
|
|
* If we have just done the special case AgentX GET, then any
|
|
* requests which were not INCLUSIVE will now have a wrong
|
|
* response, so junk them and retry from the same place (except
|
|
* that this time the handler will be called in "inexact"
|
|
* mode).
|
|
*/
|
|
|
|
if (special) {
|
|
if (!request->inclusive) {
|
|
DEBUGMSGTL(("snmp_agent",
|
|
"request %d wasn't inclusive\n",
|
|
request->index));
|
|
snmp_set_var_typed_value(request->requestvb,
|
|
ASN_PRIV_RETRY, NULL, 0);
|
|
} else if (request->requestvb->type == ASN_NULL ||
|
|
request->requestvb->type == SNMP_NOSUCHINSTANCE ||
|
|
request->requestvb->type == SNMP_NOSUCHOBJECT) {
|
|
/*
|
|
* it was inclusive, but no results. Still retry this
|
|
* search.
|
|
*/
|
|
snmp_set_var_typed_value(request->requestvb,
|
|
ASN_PRIV_RETRY, NULL, 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* out of range?
|
|
*/
|
|
if (snmp_oid_compare(request->requestvb->name,
|
|
request->requestvb->name_length,
|
|
request->range_end,
|
|
request->range_end_len) >= 0) {
|
|
/*
|
|
* ack, it's beyond the accepted end of range.
|
|
*/
|
|
/*
|
|
* fix it by setting the oid to the end of range oid instead
|
|
*/
|
|
DEBUGMSGTL(("check_getnext_results",
|
|
"request response %d out of range\n",
|
|
request->index));
|
|
/*
|
|
* I'm not sure why inclusive is set unconditionally here (see
|
|
* comments for revision 1.161), but it causes a problem for
|
|
* GETBULK over an overridden variable. The bulk-to-next
|
|
* handler re-uses the same request for multiple varbinds,
|
|
* and once inclusive was set, it was never cleared. So, a
|
|
* hack. Instead of setting it to 1, set it to 2, so bulk-to
|
|
* next can clear it later. As of the time of this hack, all
|
|
* checks of this var are boolean checks (not == 1), so this
|
|
* should be safe. Cross your fingers.
|
|
*/
|
|
request->inclusive = 2;
|
|
/*
|
|
* XXX: should set this to the original OID?
|
|
*/
|
|
snmp_set_var_objid(request->requestvb,
|
|
request->range_end,
|
|
request->range_end_len);
|
|
snmp_set_var_typed_value(request->requestvb, ASN_NULL,
|
|
NULL, 0);
|
|
}
|
|
|
|
/*
|
|
* mark any existent requests with illegal results as NULL
|
|
*/
|
|
if (request->requestvb->type == SNMP_ENDOFMIBVIEW) {
|
|
/*
|
|
* illegal response from a subagent. Change it back to NULL
|
|
* xxx-rks: err, how do we know this is a subagent?
|
|
*/
|
|
request->requestvb->type = ASN_NULL;
|
|
request->inclusive = 1;
|
|
}
|
|
|
|
if (request->requestvb->type == ASN_NULL ||
|
|
request->requestvb->type == ASN_PRIV_RETRY ||
|
|
(asp->reqinfo->mode == MODE_GETBULK
|
|
&& request->repeat > 0))
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/** repeatedly calls getnext handlers looking for an answer till all
|
|
requests are satisified. It's expected that one pass has been made
|
|
before entering this function */
|
|
int
|
|
handle_getnext_loop(netsnmp_agent_session *asp)
|
|
{
|
|
int status, rough_size, count = 0, total, val_len;
|
|
netsnmp_variable_list *var_ptr, *last_var = NULL;
|
|
|
|
if (NULL == asp || NULL == asp->pdu)
|
|
return SNMP_ERR_GENERR;
|
|
|
|
total = count_varbinds(asp->pdu->variables);
|
|
|
|
/*
|
|
* loop
|
|
*/
|
|
while (netsnmp_running) {
|
|
|
|
/*
|
|
* bail for now if anything is delegated.
|
|
*/
|
|
if (netsnmp_check_for_delegated(asp)) {
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
|
|
/*
|
|
* check vacm against results
|
|
*/
|
|
check_acm(asp, ASN_PRIV_RETRY);
|
|
|
|
/*
|
|
* need to keep going we're not done yet.
|
|
*/
|
|
if (!check_getnext_results(asp))
|
|
/*
|
|
* nothing left, quit now
|
|
*/
|
|
break;
|
|
|
|
count = rough_size = 0;
|
|
DEBUGMSGTL(("results:intermediate",
|
|
"getnext results, before next pass:\n"));
|
|
for (var_ptr = asp->pdu->variables; var_ptr;
|
|
var_ptr = var_ptr->next_variable) {
|
|
if ((var_ptr->type == ASN_NULL && 0 == var_ptr->name_length) ||
|
|
(var_ptr->type == ASN_PRIV_RETRY)) {
|
|
continue;
|
|
}
|
|
++count;
|
|
DEBUGIF("results:intermediate") {
|
|
DEBUGMSGTL(("results:intermediate", "\t"));
|
|
DEBUGMSGVAR(("results:intermediate", var_ptr));
|
|
DEBUGMSG(("results:intermediate", "\n"));
|
|
}
|
|
/*
|
|
* make a very rough guesstimate of the encoded varbind size by
|
|
* adding the name and val lengths. If these rough sizes add up
|
|
* to more than the msgMaxSize, stop gathing new varbinds.
|
|
*
|
|
* [Increasing the accuracy of this estimate would allow us to
|
|
* do better at filling packets and collecting fewer varbinds that
|
|
* we'll later have to trim. This is left as an exercise for the
|
|
* reader.]
|
|
*/
|
|
rough_size += var_ptr->name_length;
|
|
#if (SIZEOF_LONG == 8)
|
|
/** sizeof(oid) is 8 on 64bit systems :-( Hardcode for 4 */
|
|
if (ASN_OBJECT_ID == var_ptr->type)
|
|
val_len = (var_ptr->val_len / 2);
|
|
else
|
|
#endif
|
|
val_len = var_ptr->val_len;
|
|
|
|
DEBUGMSGTL(("results:intermediate", "\t+ %" NETSNMP_PRIz "d %d = %d\n",
|
|
var_ptr->name_length, val_len, rough_size));
|
|
if (rough_size > asp->pdu->msgMaxSize) {
|
|
DEBUGMSGTL(("results",
|
|
"estimating packet too big; stop gathering\n"));
|
|
asp->pdu->flags |= UCD_MSG_FLAG_BULK_TOOBIG |
|
|
UCD_MSG_FLAG_FORWARD_ENCODE;
|
|
var_ptr->type = ASN_PRIV_STOP;
|
|
if (NULL != last_var)
|
|
last_var->next_variable = NULL;
|
|
break;
|
|
}
|
|
last_var = var_ptr;
|
|
}
|
|
if (rough_size > asp->pdu->msgMaxSize)
|
|
break;
|
|
|
|
netsnmp_reassign_requests(asp);
|
|
status = handle_var_requests(asp);
|
|
if (status != SNMP_ERR_NOERROR) {
|
|
return status; /* should never really happen */
|
|
}
|
|
}
|
|
DEBUGMSGTL(("results:summary", "gathered %d/%d varbinds\n", count,
|
|
total));
|
|
if (!netsnmp_running) {
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
int
|
|
handle_set(netsnmp_agent_session *asp)
|
|
{
|
|
int status;
|
|
/*
|
|
* SETS require 3-4 passes through the var_op_list.
|
|
* The first two
|
|
* passes verify that all types, lengths, and values are valid
|
|
* and may reserve resources and the third does the set and a
|
|
* fourth executes any actions. Then the identical GET RESPONSE
|
|
* packet is returned.
|
|
* If either of the first two passes returns an error, another
|
|
* pass is made so that any reserved resources can be freed.
|
|
* If the third pass returns an error, another pass is
|
|
* made so that
|
|
* any changes can be reversed.
|
|
* If the fourth pass (or any of the error handling passes)
|
|
* return an error, we'd rather not know about it!
|
|
*/
|
|
if (!(asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY)) {
|
|
switch (asp->mode) {
|
|
case MODE_SET_BEGIN:
|
|
snmp_increment_statistic(STAT_SNMPINSETREQUESTS);
|
|
asp->rw = WRITE; /* WWW: still needed? */
|
|
asp->mode = MODE_SET_RESERVE1;
|
|
asp->status = SNMP_ERR_NOERROR;
|
|
break;
|
|
|
|
case MODE_SET_RESERVE1:
|
|
|
|
if (asp->status != SNMP_ERR_NOERROR)
|
|
asp->mode = MODE_SET_FREE;
|
|
else
|
|
asp->mode = MODE_SET_RESERVE2;
|
|
break;
|
|
|
|
case MODE_SET_RESERVE2:
|
|
if (asp->status != SNMP_ERR_NOERROR)
|
|
asp->mode = MODE_SET_FREE;
|
|
else
|
|
asp->mode = MODE_SET_ACTION;
|
|
break;
|
|
|
|
case MODE_SET_ACTION:
|
|
if (asp->status != SNMP_ERR_NOERROR)
|
|
asp->mode = MODE_SET_UNDO;
|
|
else
|
|
asp->mode = MODE_SET_COMMIT;
|
|
break;
|
|
|
|
case MODE_SET_COMMIT:
|
|
if (asp->status != SNMP_ERR_NOERROR) {
|
|
asp->mode = FINISHED_FAILURE;
|
|
} else {
|
|
asp->mode = FINISHED_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case MODE_SET_UNDO:
|
|
asp->mode = FINISHED_FAILURE;
|
|
break;
|
|
|
|
case MODE_SET_FREE:
|
|
asp->mode = FINISHED_FAILURE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (asp->mode != FINISHED_SUCCESS && asp->mode != FINISHED_FAILURE) {
|
|
DEBUGMSGTL(("agent_set", "doing set mode = %d (%s)\n", asp->mode,
|
|
se_find_label_in_slist("agent_mode", asp->mode)));
|
|
status = handle_var_requests(asp);
|
|
DEBUGMSGTL(("agent_set", "did set mode = %d, status = %d\n",
|
|
asp->mode, status));
|
|
if ((status != SNMP_ERR_NOERROR && asp->status == SNMP_ERR_NOERROR) ||
|
|
status == SNMP_ERR_COMMITFAILED ||
|
|
status == SNMP_ERR_UNDOFAILED) {
|
|
asp->status = status;
|
|
}
|
|
}
|
|
return asp->status;
|
|
}
|
|
|
|
int
|
|
handle_set_loop(netsnmp_agent_session *asp)
|
|
{
|
|
while (asp->mode != FINISHED_FAILURE && asp->mode != FINISHED_SUCCESS) {
|
|
handle_set(asp);
|
|
if (netsnmp_check_for_delegated(asp)) {
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
if (asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY) {
|
|
return asp->status;
|
|
}
|
|
}
|
|
return asp->status;
|
|
}
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
int
|
|
netsnmp_handle_request(netsnmp_agent_session *asp, int status)
|
|
{
|
|
#ifndef NETSNMP_NO_PDU_STATS
|
|
if (_pdu_stats_max > 0) {
|
|
/** tag new pdus with a start time */
|
|
marker_t start;
|
|
start = (marker_t)netsnmp_agent_get_list_data(asp->reqinfo,
|
|
"netsnmp_pdu_stats");
|
|
if (NULL == start) {
|
|
netsnmp_data_list *data_list;
|
|
DEBUGMSGTL(("stats:pdu:start", "starting pdu processing\n"));
|
|
|
|
netsnmp_set_monotonic_marker(&start); /* will alloc space */
|
|
data_list = netsnmp_create_data_list("netsnmp_pdu_stats", start,
|
|
free);
|
|
if (NULL == data_list) {
|
|
free(start);
|
|
snmp_log(LOG_WARNING, "error creating data list for stats\n");
|
|
} else
|
|
netsnmp_agent_add_list_data(asp->reqinfo, data_list);
|
|
}
|
|
}
|
|
#endif /* NETSNMP_NO_PDU_STATS */
|
|
|
|
/*
|
|
* if this isn't a delegated request trying to finish,
|
|
* processing of a set request should not start until all
|
|
* delegated requests have completed, and no other new requests
|
|
* should be processed until the set request completes.
|
|
*/
|
|
if ((0 == netsnmp_check_delegated_chain_for(asp)) &&
|
|
(asp != netsnmp_processing_set)) {
|
|
/*
|
|
* if we are processing a set and this is not a delegated
|
|
* request, queue the request
|
|
*/
|
|
if (netsnmp_processing_set) {
|
|
netsnmp_add_queued(asp);
|
|
DEBUGMSGTL(("snmp_agent",
|
|
"request queued while processing set, "
|
|
"asp = %8p\n", asp));
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* check for set request
|
|
*/
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
if (asp->pdu->command == SNMP_MSG_SET) {
|
|
netsnmp_processing_set = asp;
|
|
|
|
/*
|
|
* if there are delegated requests, we must wait for them
|
|
* to finish.
|
|
*/
|
|
if (agent_delegated_list) {
|
|
DEBUGMSGTL(("snmp_agent", "SET request queued while "
|
|
"delegated requests finish, asp = %8p\n",
|
|
asp));
|
|
netsnmp_add_queued(asp);
|
|
return 1;
|
|
}
|
|
}
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
}
|
|
|
|
/*
|
|
* process the request
|
|
*/
|
|
status = handle_pdu(asp);
|
|
|
|
/*
|
|
* print the results in appropriate debugging mode
|
|
*/
|
|
DEBUGIF("results") {
|
|
netsnmp_variable_list *var_ptr;
|
|
DEBUGMSGTL(("results", "request results (status = %d):\n",
|
|
status));
|
|
for (var_ptr = asp->pdu->variables; var_ptr;
|
|
var_ptr = var_ptr->next_variable) {
|
|
DEBUGMSGTL(("results", "\t"));
|
|
DEBUGMSGVAR(("results", var_ptr));
|
|
DEBUGMSG(("results", "\n"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* check for uncompleted requests
|
|
*/
|
|
if (netsnmp_check_for_delegated_and_add(asp)) {
|
|
/*
|
|
* add to delegated request chain
|
|
*/
|
|
asp->status = status;
|
|
} else {
|
|
/*
|
|
* if we don't have anything outstanding (delegated), wrap up
|
|
*/
|
|
return netsnmp_wrap_up_request(asp, status);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
handle_pdu(netsnmp_agent_session *asp)
|
|
{
|
|
int status, inclusives = 0;
|
|
netsnmp_variable_list *v = NULL;
|
|
|
|
/*
|
|
* for illegal requests, mark all nodes as ASN_NULL
|
|
*/
|
|
switch (asp->pdu->command) {
|
|
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
case SNMP_MSG_INTERNAL_SET_RESERVE2:
|
|
case SNMP_MSG_INTERNAL_SET_ACTION:
|
|
case SNMP_MSG_INTERNAL_SET_COMMIT:
|
|
case SNMP_MSG_INTERNAL_SET_FREE:
|
|
case SNMP_MSG_INTERNAL_SET_UNDO:
|
|
status = get_set_cache(asp);
|
|
if (status != SNMP_ERR_NOERROR)
|
|
return status;
|
|
break;
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
case SNMP_MSG_GET:
|
|
case SNMP_MSG_GETNEXT:
|
|
case SNMP_MSG_GETBULK:
|
|
for (v = asp->pdu->variables; v != NULL; v = v->next_variable) {
|
|
if (v->type == ASN_PRIV_INCL_RANGE) {
|
|
/*
|
|
* Leave the type for now (it gets set to
|
|
* ASN_NULL in netsnmp_add_varbind_to_cache,
|
|
* called by netsnmp_create_subtree_cache below).
|
|
* If we set it to ASN_NULL now, we wouldn't be
|
|
* able to distinguish INCLUSIVE search
|
|
* ranges.
|
|
*/
|
|
inclusives++;
|
|
} else {
|
|
snmp_set_var_typed_value(v, ASN_NULL, NULL, 0);
|
|
}
|
|
}
|
|
/* FALL THROUGH */
|
|
|
|
default:
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
case SNMP_MSG_INTERNAL_SET_BEGIN:
|
|
case SNMP_MSG_INTERNAL_SET_RESERVE1:
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
asp->vbcount = count_varbinds(asp->pdu->variables);
|
|
if (asp->vbcount) /* efence doesn't like 0 size allocs */
|
|
asp->requests = (netsnmp_request_info *)
|
|
calloc(asp->vbcount, sizeof(netsnmp_request_info));
|
|
/*
|
|
* collect varbinds
|
|
*/
|
|
status = netsnmp_create_subtree_cache(asp);
|
|
if (status != SNMP_ERR_NOERROR)
|
|
return status;
|
|
}
|
|
|
|
asp->mode = asp->pdu->command;
|
|
switch (asp->mode) {
|
|
case SNMP_MSG_GET:
|
|
/*
|
|
* increment the message type counter
|
|
*/
|
|
snmp_increment_statistic(STAT_SNMPINGETREQUESTS);
|
|
|
|
/*
|
|
* check vacm ahead of time
|
|
*/
|
|
check_acm(asp, SNMP_NOSUCHOBJECT);
|
|
|
|
/*
|
|
* get the results
|
|
*/
|
|
status = handle_var_requests(asp);
|
|
|
|
/*
|
|
* Deal with unhandled results -> noSuchInstance (rather
|
|
* than noSuchObject -- in that case, the type will
|
|
* already have been set to noSuchObject when we realised
|
|
* we couldn't find an appropriate tree).
|
|
*/
|
|
if (status == SNMP_ERR_NOERROR)
|
|
snmp_replace_var_types(asp->pdu->variables, ASN_NULL,
|
|
SNMP_NOSUCHINSTANCE);
|
|
break;
|
|
|
|
case SNMP_MSG_GETNEXT:
|
|
snmp_increment_statistic(STAT_SNMPINGETNEXTS);
|
|
/* FALL THROUGH */
|
|
|
|
case SNMP_MSG_GETBULK: /* note: there is no getbulk stat */
|
|
/*
|
|
* loop through our mib tree till we find an
|
|
* appropriate response to return to the caller.
|
|
*/
|
|
|
|
if (inclusives) {
|
|
/*
|
|
* This is a special case for AgentX INCLUSIVE getNext
|
|
* requests where a result lexi-equal to the request is okay
|
|
* but if such a result does not exist, we still want the
|
|
* lexi-next one. So basically we do a GET first, and if any
|
|
* of the INCLUSIVE requests are satisfied, we use that
|
|
* value. Then, unsatisfied INCLUSIVE requests, and
|
|
* non-INCLUSIVE requests get done as normal.
|
|
*/
|
|
|
|
DEBUGMSGTL(("snmp_agent", "inclusive range(s) in getNext\n"));
|
|
asp->oldmode = asp->mode;
|
|
asp->mode = SNMP_MSG_GET;
|
|
}
|
|
|
|
/*
|
|
* first pass
|
|
*/
|
|
status = handle_var_requests(asp);
|
|
if (status != SNMP_ERR_NOERROR) {
|
|
if (!inclusives)
|
|
return status; /* should never really happen */
|
|
else
|
|
asp->status = SNMP_ERR_NOERROR;
|
|
}
|
|
|
|
/*
|
|
* loop through our mib tree till we find an
|
|
* appropriate response to return to the caller.
|
|
*/
|
|
|
|
status = handle_getnext_loop(asp);
|
|
break;
|
|
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
case SNMP_MSG_SET:
|
|
#ifdef NETSNMP_DISABLE_SET_SUPPORT
|
|
return SNMP_ERR_NOTWRITABLE;
|
|
#else
|
|
/*
|
|
* check access permissions first
|
|
*/
|
|
if (check_acm(asp, SNMP_NOSUCHOBJECT))
|
|
return SNMP_ERR_NOTWRITABLE;
|
|
|
|
asp->mode = MODE_SET_BEGIN;
|
|
status = handle_set_loop(asp);
|
|
#endif
|
|
break;
|
|
|
|
case SNMP_MSG_INTERNAL_SET_BEGIN:
|
|
case SNMP_MSG_INTERNAL_SET_RESERVE1:
|
|
case SNMP_MSG_INTERNAL_SET_RESERVE2:
|
|
case SNMP_MSG_INTERNAL_SET_ACTION:
|
|
case SNMP_MSG_INTERNAL_SET_COMMIT:
|
|
case SNMP_MSG_INTERNAL_SET_FREE:
|
|
case SNMP_MSG_INTERNAL_SET_UNDO:
|
|
asp->pdu->flags |= UCD_MSG_FLAG_ONE_PASS_ONLY;
|
|
status = handle_set_loop(asp);
|
|
/*
|
|
* asp related cache is saved in cleanup
|
|
*/
|
|
break;
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
case SNMP_MSG_RESPONSE:
|
|
snmp_increment_statistic(STAT_SNMPINGETRESPONSES);
|
|
return SNMP_ERR_NOERROR;
|
|
|
|
case SNMP_MSG_TRAP:
|
|
case SNMP_MSG_TRAP2:
|
|
snmp_increment_statistic(STAT_SNMPINTRAPS);
|
|
return SNMP_ERR_NOERROR;
|
|
|
|
default:
|
|
/*
|
|
* WWW: are reports counted somewhere ?
|
|
*/
|
|
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
|
return SNMPERR_GENERR; /* shouldn't get here */
|
|
/*
|
|
* WWW
|
|
*/
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/** set error for a request
|
|
* \internal external interface: netsnmp_request_set_error
|
|
*/
|
|
NETSNMP_STATIC_INLINE int
|
|
_request_set_error(netsnmp_request_info *request, int mode, int error_value)
|
|
{
|
|
if (!request)
|
|
return SNMPERR_NO_VARS;
|
|
|
|
request->processed = 1;
|
|
request->delegated = REQUEST_IS_NOT_DELEGATED;
|
|
|
|
switch (error_value) {
|
|
case SNMP_NOSUCHOBJECT:
|
|
case SNMP_NOSUCHINSTANCE:
|
|
case SNMP_ENDOFMIBVIEW:
|
|
/*
|
|
* these are exceptions that should be put in the varbind
|
|
* in the case of a GET but should be translated for a SET
|
|
* into a real error status code and put in the request
|
|
*/
|
|
switch (mode) {
|
|
case MODE_GET:
|
|
case MODE_GETNEXT:
|
|
case MODE_GETBULK:
|
|
request->requestvb->type = error_value;
|
|
break;
|
|
|
|
/*
|
|
* These are technically illegal to set by the
|
|
* client APIs for these modes. But accepting
|
|
* them here allows the 'sparse_table' helper to
|
|
* provide some common table handling processing
|
|
*
|
|
snmp_log(LOG_ERR, "Illegal error_value %d for mode %d ignored\n",
|
|
error_value, mode);
|
|
return SNMPERR_VALUE;
|
|
*/
|
|
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
case SNMP_MSG_INTERNAL_SET_RESERVE1:
|
|
request->status = SNMP_ERR_NOCREATION;
|
|
break;
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
default:
|
|
request->status = SNMP_ERR_NOSUCHNAME; /* WWW: correct? */
|
|
break;
|
|
}
|
|
break; /* never get here */
|
|
|
|
default:
|
|
if (error_value < 0) {
|
|
/*
|
|
* illegal local error code. translate to generr
|
|
*/
|
|
/*
|
|
* WWW: full translation map?
|
|
*/
|
|
snmp_log(LOG_ERR, "Illegal error_value %d translated to %d\n",
|
|
error_value, SNMP_ERR_GENERR);
|
|
request->status = SNMP_ERR_GENERR;
|
|
} else {
|
|
/*
|
|
* WWW: translations and mode checking?
|
|
*/
|
|
request->status = error_value;
|
|
}
|
|
break;
|
|
}
|
|
return SNMPERR_SUCCESS;
|
|
}
|
|
|
|
/** set error for a request
|
|
* @param request request which has error
|
|
* @param error_value error value for request
|
|
*/
|
|
int
|
|
netsnmp_request_set_error(netsnmp_request_info *request, int error_value)
|
|
{
|
|
if (!request || !request->agent_req_info)
|
|
return SNMPERR_NO_VARS;
|
|
|
|
return _request_set_error(request, request->agent_req_info->mode,
|
|
error_value);
|
|
}
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_REQUEST_SET_ERROR_IDX
|
|
/** set error for a request within a request list
|
|
* @param request head of the request list
|
|
* @param error_value error value for request
|
|
* @param idx index of the request which has the error
|
|
*/
|
|
int
|
|
netsnmp_request_set_error_idx(netsnmp_request_info *request,
|
|
int error_value, int idx)
|
|
{
|
|
int i;
|
|
netsnmp_request_info *req = request;
|
|
|
|
if (!request || !request->agent_req_info)
|
|
return SNMPERR_NO_VARS;
|
|
|
|
/*
|
|
* Skip to the indicated varbind
|
|
*/
|
|
for ( i=2; i<idx; i++) {
|
|
req = req->next;
|
|
if (!req)
|
|
return SNMPERR_NO_VARS;
|
|
}
|
|
|
|
return _request_set_error(req, request->agent_req_info->mode,
|
|
error_value);
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_REQUEST_SET_ERROR_IDX */
|
|
|
|
/** set error for all requests
|
|
* @param requests request list
|
|
* @param error error value for requests
|
|
* @return SNMPERR_SUCCESS, or an error code
|
|
*/
|
|
NETSNMP_INLINE int
|
|
netsnmp_request_set_error_all( netsnmp_request_info *requests, int error)
|
|
{
|
|
int mode, rc, result = SNMPERR_SUCCESS;
|
|
|
|
if((NULL == requests) || (NULL == requests->agent_req_info))
|
|
return SNMPERR_NO_VARS;
|
|
|
|
mode = requests->agent_req_info->mode; /* every req has same mode */
|
|
|
|
for(; requests ; requests = requests->next) {
|
|
|
|
/** paranoid sanity checks */
|
|
netsnmp_assert(NULL != requests->agent_req_info);
|
|
netsnmp_assert(mode == requests->agent_req_info->mode);
|
|
|
|
/*
|
|
* set error for this request. Log any errors, save the last
|
|
* to return to the user.
|
|
*/
|
|
if((rc = _request_set_error(requests, mode, error))) {
|
|
snmp_log(LOG_WARNING,"got %d while setting request error\n", rc);
|
|
result = rc;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Return the difference between pm and the agent start time in hundredths of
|
|
* a second.
|
|
* \deprecated Don't use in new code.
|
|
*
|
|
* @param[in] pm An absolute time as e.g. reported by gettimeofday().
|
|
*/
|
|
u_long
|
|
netsnmp_marker_uptime(marker_t pm)
|
|
{
|
|
u_long res;
|
|
const_marker_t start = netsnmp_get_agent_starttime();
|
|
|
|
res = uatime_hdiff(start, pm);
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Return the difference between tv and the agent start time in hundredths of
|
|
* a second.
|
|
*
|
|
* \deprecated Use netsnmp_get_agent_uptime() instead.
|
|
*
|
|
* @param[in] tv An absolute time as e.g. reported by gettimeofday().
|
|
*/
|
|
u_long
|
|
netsnmp_timeval_uptime(struct timeval * tv)
|
|
{
|
|
return netsnmp_marker_uptime((marker_t) tv);
|
|
}
|
|
|
|
|
|
struct timeval starttime;
|
|
static struct timeval starttimeM;
|
|
|
|
/**
|
|
* Return a pointer to the variable in which the Net-SNMP start time has
|
|
* been stored.
|
|
*
|
|
* @note Use netsnmp_get_agent_runtime() instead of this function if you need
|
|
* to know how much time elapsed since netsnmp_set_agent_starttime() has been
|
|
* called.
|
|
*/
|
|
const_marker_t
|
|
netsnmp_get_agent_starttime(void)
|
|
{
|
|
return &starttime;
|
|
}
|
|
|
|
/**
|
|
* Report the time that elapsed since the agent start time in hundredths of a
|
|
* second.
|
|
*
|
|
* @see See also netsnmp_set_agent_starttime().
|
|
*/
|
|
uint64_t
|
|
netsnmp_get_agent_runtime(void)
|
|
{
|
|
struct timeval now, delta;
|
|
|
|
netsnmp_get_monotonic_clock(&now);
|
|
NETSNMP_TIMERSUB(&now, &starttimeM, &delta);
|
|
return (uint64_t)(delta.tv_sec * (uint64_t)100 + delta.tv_usec / 10000);
|
|
}
|
|
|
|
/**
|
|
* Set the time at which Net-SNMP started either to the current time
|
|
* (if s == NULL) or to *s (if s is not NULL).
|
|
*
|
|
* @see See also netsnmp_set_agent_uptime().
|
|
*/
|
|
void
|
|
netsnmp_set_agent_starttime(marker_t s)
|
|
{
|
|
if (s) {
|
|
struct timeval nowA, nowM;
|
|
|
|
starttime = *(struct timeval*)s;
|
|
gettimeofday(&nowA, NULL);
|
|
netsnmp_get_monotonic_clock(&nowM);
|
|
NETSNMP_TIMERSUB(&starttime, &nowA, &starttimeM);
|
|
NETSNMP_TIMERADD(&starttimeM, &nowM, &starttimeM);
|
|
} else {
|
|
gettimeofday(&starttime, NULL);
|
|
netsnmp_get_monotonic_clock(&starttimeM);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the current value of 'sysUpTime'
|
|
*/
|
|
u_long
|
|
netsnmp_get_agent_uptime(void)
|
|
{
|
|
struct timeval now, delta;
|
|
|
|
netsnmp_get_monotonic_clock(&now);
|
|
NETSNMP_TIMERSUB(&now, &starttimeM, &delta);
|
|
return (u_long)(delta.tv_sec * 100UL + delta.tv_usec / 10000);
|
|
}
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_SET_AGENT_UPTIME
|
|
/**
|
|
* Set the start time from which 'sysUpTime' is computed.
|
|
*
|
|
* @param[in] hsec New sysUpTime in hundredths of a second.
|
|
*
|
|
* @see See also netsnmp_set_agent_starttime().
|
|
*/
|
|
void
|
|
netsnmp_set_agent_uptime(u_long hsec)
|
|
{
|
|
struct timeval nowA, nowM;
|
|
struct timeval new_uptime;
|
|
|
|
gettimeofday(&nowA, NULL);
|
|
netsnmp_get_monotonic_clock(&nowM);
|
|
new_uptime.tv_sec = hsec / 100;
|
|
new_uptime.tv_usec = (uint32_t)(hsec - new_uptime.tv_sec * 100) * 10000L;
|
|
NETSNMP_TIMERSUB(&nowA, &new_uptime, &starttime);
|
|
NETSNMP_TIMERSUB(&nowM, &new_uptime, &starttimeM);
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_SET_AGENT_UPTIME */
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* deprecated functions
|
|
*
|
|
*/
|
|
|
|
/** set error for a request
|
|
* \deprecated, use netsnmp_request_set_error instead
|
|
* @param reqinfo agent_request_info pointer for request
|
|
* @param request request_info pointer
|
|
* @param error_value error value for requests
|
|
* @return error_value
|
|
*/
|
|
int
|
|
netsnmp_set_request_error(netsnmp_agent_request_info *reqinfo,
|
|
netsnmp_request_info *request, int error_value)
|
|
{
|
|
if (!request || !reqinfo)
|
|
return error_value;
|
|
|
|
_request_set_error(request, reqinfo->mode, error_value);
|
|
|
|
return error_value;
|
|
}
|
|
|
|
/** set error for a request
|
|
* \deprecated, use netsnmp_request_set_error instead
|
|
* @param mode Net-SNMP agent processing mode
|
|
* @param request request_info pointer
|
|
* @param error_value error value for requests
|
|
* @return error_value
|
|
*/
|
|
int
|
|
netsnmp_set_mode_request_error(int mode, netsnmp_request_info *request,
|
|
int error_value)
|
|
{
|
|
_request_set_error(request, mode, error_value);
|
|
|
|
return error_value;
|
|
}
|
|
|
|
/** set error for all request
|
|
* \deprecated use netsnmp_request_set_error_all
|
|
* @param reqinfo agent_request_info pointer for requests
|
|
* @param requests request list
|
|
* @param error_value error value for requests
|
|
* @return error_value
|
|
*/
|
|
#ifndef NETSNMP_FEATURE_REMOVE_SET_ALL_REQUESTS_ERROR
|
|
int
|
|
netsnmp_set_all_requests_error(netsnmp_agent_request_info *reqinfo,
|
|
netsnmp_request_info *requests,
|
|
int error_value)
|
|
{
|
|
netsnmp_request_set_error_all(requests, error_value);
|
|
return error_value;
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_SET_ALL_REQUESTS_ERROR */
|
|
/** @} */
|