4 * Simple Network Management Protocol (RFC 1067).
6 /***********************************************************
7 Copyright 1988, 1989 by Carnegie Mellon University
11 Permission to use, copy, modify, and distribute this software and its
12 documentation for any purpose and without fee is hereby granted,
13 provided that the above copyright notice appear in all copies and that
14 both that copyright notice and this permission notice appear in
15 supporting documentation, and that the name of CMU not be
16 used in advertising or publicity pertaining to distribution of the
17 software without specific, written prior permission.
19 CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
20 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
21 CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
22 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
23 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
24 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
26 ******************************************************************/
28 #include <net-snmp/net-snmp-config.h>
30 #include <sys/types.h>
43 #if TIME_WITH_SYS_TIME
45 # include <sys/timeb.h>
47 # include <sys/time.h>
52 # include <sys/time.h>
58 #include <sys/select.h>
61 #include <netinet/in.h>
72 #define SNMP_NEED_REQUEST_LIST
73 #include <net-snmp/net-snmp-includes.h>
74 #include <net-snmp/agent/net-snmp-agent-includes.h>
75 #include <net-snmp/library/snmp_assert.h>
80 int allow_severity = LOG_INFO;
81 int deny_severity = LOG_WARNING;
85 #include "mibgroup/struct.h"
86 #include "mibgroup/util_funcs.h"
87 #include <net-snmp/agent/mib_module_config.h>
89 #ifdef USING_AGENTX_PROTOCOL_MODULE
90 #include "agentx/protocol.h"
93 #ifdef USING_AGENTX_MASTER_MODULE
94 #include "agentx/master.h"
97 #define SNMP_ADDRCACHE_SIZE 10
101 enum { SNMP_ADDRCACHE_UNUSED = 0,
102 SNMP_ADDRCACHE_USED = 1,
103 SNMP_ADDRCACHE_OLD = 2
107 static struct addrCache addrCache[SNMP_ADDRCACHE_SIZE];
109 int log_addresses = 0;
113 typedef struct _agent_nsap {
115 netsnmp_transport *t;
116 void *s; /* Opaque internal session pointer. */
117 struct _agent_nsap *next;
120 static agent_nsap *agent_nsap_list = NULL;
121 static netsnmp_agent_session *agent_session_list = NULL;
122 static netsnmp_agent_session *netsnmp_processing_set = NULL;
123 netsnmp_agent_session *agent_delegated_list = NULL;
124 netsnmp_agent_session *netsnmp_agent_queued_list = NULL;
127 int netsnmp_agent_check_packet(netsnmp_session *,
128 struct netsnmp_transport_s *,
130 int netsnmp_agent_check_parse(netsnmp_session *, netsnmp_pdu *,
132 void delete_subnetsnmp_tree_cache(netsnmp_agent_session *asp);
133 int handle_pdu(netsnmp_agent_session *asp);
134 int netsnmp_handle_request(netsnmp_agent_session *asp,
136 int netsnmp_wrap_up_request(netsnmp_agent_session *asp,
138 int check_delayed_request(netsnmp_agent_session *asp);
139 int handle_getnext_loop(netsnmp_agent_session *asp);
140 int handle_set_loop(netsnmp_agent_session *asp);
142 int netsnmp_check_queued_chain_for(netsnmp_agent_session *asp);
143 int netsnmp_add_queued(netsnmp_agent_session *asp);
144 int netsnmp_remove_from_delegated(netsnmp_agent_session *asp);
147 static int current_globalid = 0;
150 netsnmp_allocate_globalcacheid(void)
152 return ++current_globalid;
156 netsnmp_get_local_cachid(netsnmp_cachemap *cache_store, int globalid)
158 while (cache_store != NULL) {
159 if (cache_store->globalid == globalid)
160 return cache_store->cacheid;
161 cache_store = cache_store->next;
167 netsnmp_get_or_add_local_cachid(netsnmp_cachemap **cache_store,
168 int globalid, int localid)
170 netsnmp_cachemap *tmpp;
172 tmpp = SNMP_MALLOC_TYPEDEF(netsnmp_cachemap);
174 tmpp->next = *cache_store;
180 tmpp->globalid = globalid;
181 tmpp->cacheid = localid;
186 netsnmp_free_cachemap(netsnmp_cachemap *cache_store)
188 netsnmp_cachemap *tmpp;
189 while (cache_store) {
191 cache_store = cache_store->next;
197 typedef struct agent_set_cache_s {
202 netsnmp_session *sess;
207 netsnmp_tree_cache *treecache;
212 netsnmp_request_info *requests;
213 netsnmp_data_list *agent_data;
218 struct agent_set_cache_s *next;
221 static agent_set_cache *Sets = NULL;
224 save_set_cache(netsnmp_agent_session *asp)
226 agent_set_cache *ptr;
228 if (!asp->reqinfo || !asp->pdu)
231 ptr = SNMP_MALLOC_TYPEDEF(agent_set_cache);
236 * Save the important information
238 ptr->transID = asp->pdu->transid;
239 ptr->sess = asp->session;
240 ptr->treecache = asp->treecache;
241 ptr->treecache_len = asp->treecache_len;
242 ptr->treecache_num = asp->treecache_num;
243 ptr->agent_data = asp->reqinfo->agent_data;
244 ptr->requests = asp->requests;
245 ptr->vbcount = asp->vbcount;
248 * make the agent forget about what we've saved
250 asp->treecache = NULL;
251 asp->reqinfo->agent_data = NULL;
252 asp->pdu->variables = NULL;
253 asp->requests = NULL;
262 get_set_cache(netsnmp_agent_session *asp)
264 agent_set_cache *ptr, *prev = NULL;
266 for (ptr = Sets; ptr != NULL; ptr = ptr->next) {
267 if (ptr->sess == asp->session && ptr->transID == asp->pdu->transid) {
269 prev->next = ptr->next;
274 * found it. Get the needed data
276 asp->treecache = ptr->treecache;
277 asp->treecache_len = ptr->treecache_len;
278 asp->treecache_num = ptr->treecache_num;
279 asp->requests = ptr->requests;
280 asp->vbcount = ptr->vbcount;
283 SNMP_MALLOC_TYPEDEF(netsnmp_agent_request_info);
285 asp->reqinfo->asp = asp;
286 asp->reqinfo->agent_data = ptr->agent_data;
290 return SNMP_ERR_NOERROR;
294 return SNMP_ERR_GENERR;
300 static int SessionID = 0;
306 agent_check_and_process(int block)
310 struct timeval timeout = { LONG_MAX, 0 }, *tvp = &timeout;
316 snmp_select_info(&numfds, &fdset, tvp, &fakeblock);
317 if (block != 0 && fakeblock != 0) {
319 * There are no alarms registered, and the caller asked for blocking, so
320 * let select() block forever.
324 } else if (block != 0 && fakeblock == 0) {
326 * The caller asked for blocking, but there is an alarm due sooner than
327 * LONG_MAX seconds from now, so use the modified timeout returned by
328 * snmp_select_info as the timeout for select().
331 } else if (block == 0) {
333 * The caller does not want us to block at all.
340 count = select(numfds, &fdset, 0, 0, tvp);
344 * packets found, process them
353 if (errno != EINTR) {
354 snmp_log_perror("select");
358 snmp_log(LOG_ERR, "select returned %d\n", count);
360 } /* endif -- count>0 */
363 * Run requested alarms.
373 * Set up the address cache.
376 netsnmp_addrcache_initialise(void)
380 for (i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
381 addrCache[i].addr = NULL;
382 addrCache[i].status = SNMP_ADDRCACHE_UNUSED;
389 * Age the entries in the address cache.
393 netsnmp_addrcache_age(void)
398 for (i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
399 if (addrCache[i].status == SNMP_ADDRCACHE_OLD) {
400 addrCache[i].status = SNMP_ADDRCACHE_UNUSED;
401 if (addrCache[i].addr != NULL) {
402 free(addrCache[i].addr);
403 addrCache[i].addr = NULL;
406 if (addrCache[i].status == SNMP_ADDRCACHE_USED) {
407 addrCache[i].status = SNMP_ADDRCACHE_OLD;
412 /*******************************************************************-o-******
413 * netsnmp_agent_check_packet
416 * session, transport, transport_data, transport_data_length
422 * Handler for all incoming messages (a.k.a. packets) for the agent. If using
423 * the libwrap utility, log the connection and deny/allow the access. Print
424 * output when appropriate, and increment the incoming counter.
429 netsnmp_agent_check_packet(netsnmp_session * session,
430 netsnmp_transport *transport,
431 void *transport_data, int transport_data_length)
433 char *addr_string = NULL;
437 * Log the message and/or dump the message.
438 * Optionally cache the network address of the sender.
441 if (transport != NULL && transport->f_fmtaddr != NULL) {
443 * Okay I do know how to format this address for logging.
445 addr_string = transport->f_fmtaddr(transport, transport_data,
446 transport_data_length);
448 * Don't forget to free() it.
452 if (addr_string != NULL) {
453 if (hosts_ctl("snmpd", STRING_UNKNOWN, addr_string, STRING_UNKNOWN)) {
454 snmp_log(allow_severity, "Connection from %s\n", addr_string);
456 snmp_log(deny_severity, "Connection from %s REFUSED\n",
462 if (hosts_ctl("snmp", STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN)){
463 snmp_log(allow_severity, "Connection from <UNKNOWN>\n");
464 addr_string = strdup("<UNKNOWN>");
466 snmp_log(deny_severity, "Connection from <UNKNOWN> REFUSED\n");
470 #endif /*USE_LIBWRAP */
472 snmp_increment_statistic(STAT_SNMPINPKTS);
474 if (log_addresses || netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
475 NETSNMP_DS_AGENT_VERBOSE)) {
476 for (i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
477 if ((addrCache[i].status != SNMP_ADDRCACHE_UNUSED) &&
478 (strcmp(addrCache[i].addr, addr_string) == 0)) {
483 if (i >= SNMP_ADDRCACHE_SIZE ||
484 netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
485 NETSNMP_DS_AGENT_VERBOSE)) {
487 * Address wasn't in the cache, so log the packet...
489 snmp_log(LOG_INFO, "Received SNMP packet(s) from %s\n",
492 * ...and try to cache the address.
494 for (i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
495 if (addrCache[i].status == SNMP_ADDRCACHE_UNUSED) {
496 if (addrCache[i].addr != NULL) {
497 free(addrCache[i].addr);
499 addrCache[i].addr = addr_string;
500 addrCache[i].status = SNMP_ADDRCACHE_USED;
501 addr_string = NULL; /* Don't free this 'temporary' string
502 * since it's now part of the cache */
506 if (i >= SNMP_ADDRCACHE_SIZE) {
508 * We didn't find a free slot to cache the address. Perhaps
509 * we should be using an LRU replacement policy here or
510 * something. Oh well.
512 DEBUGMSGTL(("netsnmp_agent_check_packet",
516 addrCache[i].status = SNMP_ADDRCACHE_USED;
520 if (addr_string != NULL) {
529 netsnmp_agent_check_parse(netsnmp_session * session, netsnmp_pdu *pdu,
533 if (snmp_get_do_logging() &&
534 netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
535 NETSNMP_DS_AGENT_VERBOSE)) {
536 netsnmp_variable_list *var_ptr;
538 switch (pdu->command) {
540 snmp_log(LOG_DEBUG, " GET message\n");
542 case SNMP_MSG_GETNEXT:
543 snmp_log(LOG_DEBUG, " GETNEXT message\n");
545 case SNMP_MSG_RESPONSE:
546 snmp_log(LOG_DEBUG, " RESPONSE message\n");
549 snmp_log(LOG_DEBUG, " SET message\n");
552 snmp_log(LOG_DEBUG, " TRAP message\n");
554 case SNMP_MSG_GETBULK:
555 snmp_log(LOG_DEBUG, " GETBULK message, non-rep=%d, max_rep=%d\n",
556 pdu->errstat, pdu->errindex);
558 case SNMP_MSG_INFORM:
559 snmp_log(LOG_DEBUG, " INFORM message\n");
562 snmp_log(LOG_DEBUG, " TRAP2 message\n");
564 case SNMP_MSG_REPORT:
565 snmp_log(LOG_DEBUG, " REPORT message\n");
568 case SNMP_MSG_INTERNAL_SET_RESERVE1:
569 snmp_log(LOG_DEBUG, " INTERNAL RESERVE1 message\n");
572 case SNMP_MSG_INTERNAL_SET_RESERVE2:
573 snmp_log(LOG_DEBUG, " INTERNAL RESERVE2 message\n");
576 case SNMP_MSG_INTERNAL_SET_ACTION:
577 snmp_log(LOG_DEBUG, " INTERNAL ACTION message\n");
580 case SNMP_MSG_INTERNAL_SET_COMMIT:
581 snmp_log(LOG_DEBUG, " INTERNAL COMMIT message\n");
584 case SNMP_MSG_INTERNAL_SET_FREE:
585 snmp_log(LOG_DEBUG, " INTERNAL FREE message\n");
588 case SNMP_MSG_INTERNAL_SET_UNDO:
589 snmp_log(LOG_DEBUG, " INTERNAL UNDO message\n");
593 snmp_log(LOG_DEBUG, " UNKNOWN message, type=%02X\n",
595 snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
599 for (var_ptr = pdu->variables; var_ptr != NULL;
600 var_ptr = var_ptr->next_variable) {
601 size_t c_oidlen = 256, c_outlen = 0;
602 u_char *c_oid = (u_char *) malloc(c_oidlen);
605 if (!sprint_realloc_objid
606 (&c_oid, &c_oidlen, &c_outlen, 1, var_ptr->name,
607 var_ptr->name_length)) {
608 snmp_log(LOG_DEBUG, " -- %s [TRUNCATED]\n",
611 snmp_log(LOG_DEBUG, " -- %s\n", c_oid);
619 return 0; /* XXX: does it matter what the return value
620 * is? Yes: if we return 0, then the PDU is
626 * Global access to the primary session structure for this agent.
627 * for Index Allocation use initially.
631 * I don't understand what this is for at the moment. AFAICS as long as it
632 * gets set and points at a session, that's fine. ???
635 netsnmp_session *main_session = NULL;
640 * Set up an agent session on the given transport. Return a handle
641 * which may later be used to de-register this transport. A return
642 * value of -1 indicates an error.
646 netsnmp_register_agent_nsap(netsnmp_transport *t)
648 netsnmp_session *s, *sp = NULL;
649 agent_nsap *a = NULL, *n = NULL, **prevNext = &agent_nsap_list;
657 DEBUGMSGTL(("netsnmp_register_agent_nsap", "fd %d\n", t->sock));
659 n = (agent_nsap *) malloc(sizeof(agent_nsap));
663 s = (netsnmp_session *) malloc(sizeof(netsnmp_session));
668 memset(s, 0, sizeof(netsnmp_session));
672 * Set up the session appropriately for an agent.
675 s->version = SNMP_DEFAULT_VERSION;
676 s->callback = handle_snmp_packet;
677 s->authenticator = NULL;
678 s->flags = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
679 NETSNMP_DS_AGENT_FLAGS);
680 s->isAuthoritative = SNMP_SESS_AUTHORITATIVE;
682 sp = snmp_add(s, t, netsnmp_agent_check_packet,
683 netsnmp_agent_check_parse);
690 isp = snmp_sess_pointer(sp);
691 if (isp == NULL) { /* over-cautious */
700 if (main_session == NULL) {
701 main_session = snmp_sess_session(isp);
704 for (a = agent_nsap_list; a != NULL && handle + 1 >= a->handle;
707 prevNext = &(a->next);
710 if (handle < INT_MAX) {
711 n->handle = handle + 1;
724 netsnmp_deregister_agent_nsap(int handle)
726 agent_nsap *a = NULL, **prevNext = &agent_nsap_list;
727 int main_session_deregistered = 0;
729 DEBUGMSGTL(("netsnmp_deregister_agent_nsap", "handle %d\n", handle));
731 for (a = agent_nsap_list; a != NULL && a->handle < handle; a = a->next) {
732 prevNext = &(a->next);
735 if (a != NULL && a->handle == handle) {
737 if (main_session == snmp_sess_session(a->s)) {
738 main_session_deregistered = 1;
740 snmp_close(snmp_sess_session(a->s));
742 * The above free()s the transport and session pointers.
748 * If we've deregistered the session that main_session used to point to,
749 * then make it point to another one, or in the last resort, make it equal
750 * to NULL. Basically this shouldn't ever happen in normal operation
751 * because main_session starts off pointing at the first session added by
752 * init_master_agent(), which then discards the handle.
755 if (main_session_deregistered) {
756 if (agent_nsap_list != NULL) {
757 DEBUGMSGTL(("snmp_agent",
758 "WARNING: main_session ptr changed from %p to %p\n",
759 main_session, snmp_sess_session(agent_nsap_list->s)));
760 main_session = snmp_sess_session(agent_nsap_list->s);
762 DEBUGMSGTL(("snmp_agent",
763 "WARNING: main_session ptr changed from %p to NULL\n",
774 * This function has been modified to use the experimental
775 * netsnmp_register_agent_nsap interface. The major responsibility of this
776 * function now is to interpret a string specified to the agent (via -p on the
777 * command line, or from a configuration file) as a list of agent NSAPs on
778 * which to listen for SNMP packets. Typically, when you add a new transport
779 * domain "foo", you add code here such that if the "foo" code is compiled
780 * into the agent (SNMP_TRANSPORT_FOO_DOMAIN is defined), then a token of the
781 * form "foo:bletch-3a0054ef%wob&wob" gets turned into the appropriate
782 * transport descriptor. netsnmp_register_agent_nsap is then called with that
783 * transport descriptor and sets up a listening agent session on it.
785 * Everything then works much as normal: the agent runs in an infinite loop
786 * (in the snmpd.c/receive()routine), which calls snmp_read() when a request
787 * is readable on any of the given transports. This routine then traverses
788 * the library 'Sessions' list to identify the relevant session and eventually
789 * invokes '_sess_read'. This then processes the incoming packet, calling the
790 * pre_parse, parse, post_parse and callback routines in turn.
796 init_master_agent(void)
798 netsnmp_transport *transport;
800 char buf[SPRINT_MAX_LEN];
802 /* default to turning off lookup caching */
803 netsnmp_set_lookup_cache_size(0);
805 if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
806 NETSNMP_DS_AGENT_ROLE) != MASTER_AGENT) {
807 DEBUGMSGTL(("snmp_agent",
808 "init_master_agent; not master agent\n"));
809 return 0; /* No error if ! MASTER_AGENT */
811 #ifdef USING_AGENTX_MASTER_MODULE
812 if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
813 NETSNMP_DS_AGENT_AGENTX_MASTER) == 1)
818 * Have specific agent ports been specified?
820 cptr = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID,
821 NETSNMP_DS_AGENT_PORTS);
824 snprintf(buf, sizeof(buf), "%s", cptr);
825 buf[ sizeof(buf)-1 ] = 0;
828 * No, so just specify the default port.
830 if (netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_FLAGS) & SNMP_FLAGS_STREAM_SOCKET) {
831 sprintf(buf, "tcp:%d", SNMP_PORT);
833 sprintf(buf, "udp:%d", SNMP_PORT);
837 DEBUGMSGTL(("snmp_agent", "final port spec: %s\n", buf));
838 cptr = strtok(buf, ",");
841 * Specification format:
843 * NONE: (a pseudo-transport)
844 * UDP:[address:]port (also default if no transport is specified)
845 * TCP:[address:]port (if supported)
846 * Unix:pathname (if supported)
847 * AAL5PVC:itf.vpi.vci (if supported)
848 * IPX:[network]:node[/port] (if supported)
852 DEBUGMSGTL(("snmp_agent", "installing master agent on port %s\n",
855 if (!cptr || !(*cptr)) {
856 snmp_log(LOG_ERR, "improper port specification\n");
860 if (strncasecmp(cptr, "none", 4) == 0) {
861 DEBUGMSGTL(("snmp_agent",
862 "init_master_agent; pseudo-transport \"none\" requested\n"));
865 transport = netsnmp_tdomain_transport(cptr, 1, "udp");
867 if (transport == NULL) {
868 snmp_log(LOG_ERR, "Error opening specified endpoint \"%s\"\n",
873 if (netsnmp_register_agent_nsap(transport) == 0) {
875 "Error registering specified transport \"%s\" as an agent NSAP\n",
879 DEBUGMSGTL(("snmp_agent",
880 "init_master_agent; \"%s\" registered as an agent NSAP\n",
885 * Next transport please...
887 cptr = strtok(NULL, ",");
895 netsnmp_agent_session *
896 init_agent_snmp_session(netsnmp_session * session, netsnmp_pdu *pdu)
898 netsnmp_agent_session *asp = (netsnmp_agent_session *)
899 calloc(1, sizeof(netsnmp_agent_session));
905 DEBUGMSGTL(("snmp_agent","agent_sesion %08p created\n", asp));
906 asp->session = session;
907 asp->pdu = snmp_clone_pdu(pdu);
908 asp->orig_pdu = snmp_clone_pdu(pdu);
912 asp->mode = RESERVE1;
913 asp->status = SNMP_ERR_NOERROR;
916 asp->treecache_num = -1;
917 asp->treecache_len = 0;
923 free_agent_snmp_session(netsnmp_agent_session *asp)
928 DEBUGMSGTL(("snmp_agent","agent_sesion %08p released\n", asp));
930 netsnmp_remove_from_delegated(asp);
933 snmp_free_pdu(asp->orig_pdu);
935 snmp_free_pdu(asp->pdu);
937 netsnmp_free_agent_request_info(asp->reqinfo);
938 if (asp->treecache) {
939 free(asp->treecache);
941 if (asp->bulkcache) {
942 free(asp->bulkcache);
946 for (i = 0; i < asp->vbcount; i++) {
947 netsnmp_free_request_data_sets(&asp->requests[i]);
953 if (asp->cache_store) {
954 netsnmp_free_cachemap(asp->cache_store);
955 asp->cache_store = NULL;
961 netsnmp_check_for_delegated(netsnmp_agent_session *asp)
964 netsnmp_request_info *request;
966 if (NULL == asp->treecache)
969 for (i = 0; i <= asp->treecache_num; i++) {
970 for (request = asp->treecache[i].requests_begin; request;
971 request = request->next) {
972 if (request->delegated)
980 netsnmp_check_delegated_chain_for(netsnmp_agent_session *asp)
982 netsnmp_agent_session *asptmp;
983 for (asptmp = agent_delegated_list; asptmp; asptmp = asptmp->next) {
991 netsnmp_check_for_delegated_and_add(netsnmp_agent_session *asp)
993 if (netsnmp_check_for_delegated(asp)) {
994 if (!netsnmp_check_delegated_chain_for(asp)) {
996 * add to delegated request chain
998 asp->next = agent_delegated_list;
999 agent_delegated_list = asp;
1000 DEBUGMSGTL(("snmp_agent", "delegate session == %08p\n", asp));
1008 netsnmp_remove_from_delegated(netsnmp_agent_session *asp)
1010 netsnmp_agent_session *curr, *prev = NULL;
1012 for (curr = agent_delegated_list; curr; prev = curr, curr = curr->next) {
1023 prev->next = asp->next;
1025 agent_delegated_list = asp->next;
1027 DEBUGMSGTL(("snmp_agent", "remove delegated session == %08p\n", asp));
1036 * netsnmp_remove_delegated_requests_for_session
1038 * called when a session is being closed. Check all delegated requests to
1039 * see if the are waiting on this session, and if set, set the status for
1040 * that request to GENERR.
1043 netsnmp_remove_delegated_requests_for_session(netsnmp_session *sess)
1045 netsnmp_agent_session *asp;
1048 for (asp = agent_delegated_list; asp; asp = asp->next) {
1050 * check each request
1052 netsnmp_request_info *request;
1053 for(request = asp->requests; request; request = request->next) {
1057 netsnmp_assert(NULL!=request->subtree);
1058 if(request->subtree->session != sess)
1062 * matched! mark request as done
1064 netsnmp_set_mode_request_error(MODE_SET_BEGIN, request,
1071 * if we found any, that request may be finished now
1074 DEBUGMSGTL(("snmp_agent", "removed %d delegated request(s) for session "
1075 "%08p\n", count, sess));
1076 netsnmp_check_outstanding_agent_requests();
1083 netsnmp_check_queued_chain_for(netsnmp_agent_session *asp)
1085 netsnmp_agent_session *asptmp;
1086 for (asptmp = netsnmp_agent_queued_list; asptmp; asptmp = asptmp->next) {
1094 netsnmp_add_queued(netsnmp_agent_session *asp)
1096 netsnmp_agent_session *asp_tmp;
1101 if (NULL == netsnmp_agent_queued_list) {
1102 netsnmp_agent_queued_list = asp;
1108 * add to end of queued request chain
1110 asp_tmp = netsnmp_agent_queued_list;
1111 for (; asp_tmp; asp_tmp = asp_tmp->next) {
1121 if (NULL == asp_tmp->next)
1122 asp_tmp->next = asp;
1129 netsnmp_wrap_up_request(netsnmp_agent_session *asp, int status)
1131 netsnmp_variable_list *var_ptr;
1132 int i, n = 0, r = 0;
1135 * if this request was a set, clear the global now that we are
1138 if (asp == netsnmp_processing_set) {
1139 DEBUGMSGTL(("snmp_agent", "SET request complete, asp = %08p\n",
1141 netsnmp_processing_set = NULL;
1145 * some stuff needs to be saved in special subagent cases
1149 switch (asp->pdu->command) {
1150 case SNMP_MSG_INTERNAL_SET_BEGIN:
1151 case SNMP_MSG_INTERNAL_SET_RESERVE1:
1152 case SNMP_MSG_INTERNAL_SET_RESERVE2:
1153 case SNMP_MSG_INTERNAL_SET_ACTION:
1154 save_set_cache(asp);
1159 * if this is a GETBULK response we need to rearrange the varbinds
1161 if (asp->pdu->command == SNMP_MSG_GETBULK) {
1162 int repeats = asp->pdu->errindex;
1165 if (asp->pdu->errstat < asp->vbcount) {
1166 n = asp->pdu->errstat;
1170 if ((r = asp->vbcount - n) < 0) {
1174 for (i = 0; i < r - 1; i++) {
1175 for (j = 0; j < repeats; j++) {
1176 asp->bulkcache[i * repeats + j]->next_variable =
1177 asp->bulkcache[(i + 1) * repeats + j];
1181 for (j = 0; j < repeats - 1; j++) {
1182 asp->bulkcache[(r - 1) * repeats + j]->next_variable =
1183 asp->bulkcache[j + 1];
1189 * May need to "dumb down" a SET error status for a
1190 * v1 query. See RFC2576 - section 4.3
1192 if ((asp->pdu->command == SNMP_MSG_SET) &&
1193 (asp->pdu->version == SNMP_VERSION_1)) {
1195 case SNMP_ERR_WRONGVALUE:
1196 case SNMP_ERR_WRONGENCODING:
1197 case SNMP_ERR_WRONGTYPE:
1198 case SNMP_ERR_WRONGLENGTH:
1199 case SNMP_ERR_INCONSISTENTVALUE:
1200 status = SNMP_ERR_BADVALUE;
1201 asp->status = SNMP_ERR_BADVALUE;
1203 case SNMP_ERR_NOACCESS:
1204 case SNMP_ERR_NOTWRITABLE:
1205 case SNMP_ERR_NOCREATION:
1206 case SNMP_ERR_INCONSISTENTNAME:
1207 case SNMP_ERR_AUTHORIZATIONERROR:
1208 status = SNMP_ERR_NOSUCHNAME;
1209 asp->status = SNMP_ERR_NOSUCHNAME;
1211 case SNMP_ERR_RESOURCEUNAVAILABLE:
1212 case SNMP_ERR_COMMITFAILED:
1213 case SNMP_ERR_UNDOFAILED:
1214 status = SNMP_ERR_GENERR;
1215 asp->status = SNMP_ERR_GENERR;
1220 * Similarly we may need to "dumb down" v2 exception
1221 * types to throw an error for a v1 query.
1222 * See RFC2576 - section 4.1.2.3
1224 if ((asp->pdu->command != SNMP_MSG_SET) &&
1225 (asp->pdu->version == SNMP_VERSION_1)) {
1226 for (var_ptr = asp->pdu->variables, i = 1;
1227 var_ptr != NULL; var_ptr = var_ptr->next_variable, i++) {
1228 switch (var_ptr->type) {
1229 case SNMP_NOSUCHOBJECT:
1230 case SNMP_NOSUCHINSTANCE:
1231 case SNMP_ENDOFMIBVIEW:
1233 status = SNMP_ERR_NOSUCHNAME;
1234 asp->status = SNMP_ERR_NOSUCHNAME;
1240 } /** if asp->pdu */
1243 * Update the snmp error-count statistics
1244 * XXX - should we include the V2 errors in this or not?
1246 #define INCLUDE_V2ERRORS_IN_V1STATS
1249 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
1250 case SNMP_ERR_WRONGVALUE:
1251 case SNMP_ERR_WRONGENCODING:
1252 case SNMP_ERR_WRONGTYPE:
1253 case SNMP_ERR_WRONGLENGTH:
1254 case SNMP_ERR_INCONSISTENTVALUE:
1256 case SNMP_ERR_BADVALUE:
1257 snmp_increment_statistic(STAT_SNMPOUTBADVALUES);
1259 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
1260 case SNMP_ERR_NOACCESS:
1261 case SNMP_ERR_NOTWRITABLE:
1262 case SNMP_ERR_NOCREATION:
1263 case SNMP_ERR_INCONSISTENTNAME:
1264 case SNMP_ERR_AUTHORIZATIONERROR:
1266 case SNMP_ERR_NOSUCHNAME:
1267 snmp_increment_statistic(STAT_SNMPOUTNOSUCHNAMES);
1269 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
1270 case SNMP_ERR_RESOURCEUNAVAILABLE:
1271 case SNMP_ERR_COMMITFAILED:
1272 case SNMP_ERR_UNDOFAILED:
1274 case SNMP_ERR_GENERR:
1275 snmp_increment_statistic(STAT_SNMPOUTGENERRS);
1278 case SNMP_ERR_TOOBIG:
1279 snmp_increment_statistic(STAT_SNMPOUTTOOBIGS);
1283 if ((status == SNMP_ERR_NOERROR) && (asp->pdu)) {
1284 snmp_increment_statistic_by((asp->pdu->command == SNMP_MSG_SET ?
1285 STAT_SNMPINTOTALSETVARS :
1286 STAT_SNMPINTOTALREQVARS),
1287 count_varbinds(asp->pdu->variables));
1290 * Use a copy of the original request
1291 * to report failures.
1293 snmp_free_pdu(asp->pdu);
1294 asp->pdu = asp->orig_pdu;
1295 asp->orig_pdu = NULL;
1298 asp->pdu->command = SNMP_MSG_RESPONSE;
1299 asp->pdu->errstat = asp->status;
1300 asp->pdu->errindex = asp->index;
1301 if (!snmp_send(asp->session, asp->pdu)) {
1302 snmp_free_pdu(asp->pdu);
1305 snmp_increment_statistic(STAT_SNMPOUTPKTS);
1306 snmp_increment_statistic(STAT_SNMPOUTGETRESPONSES);
1308 netsnmp_remove_and_free_agent_snmp_session(asp);
1314 dump_sess_list(void)
1316 netsnmp_agent_session *a;
1318 DEBUGMSGTL(("snmp_agent", "DUMP agent_sess_list -> "));
1319 for (a = agent_session_list; a != NULL; a = a->next) {
1320 DEBUGMSG(("snmp_agent", "%08p[session %08p] -> ", a, a->session));
1322 DEBUGMSG(("snmp_agent", "[NIL]\n"));
1326 netsnmp_remove_and_free_agent_snmp_session(netsnmp_agent_session *asp)
1328 netsnmp_agent_session *a, **prevNext = &agent_session_list;
1330 DEBUGMSGTL(("snmp_agent", "REMOVE session == %08p\n", asp));
1332 for (a = agent_session_list; a != NULL; a = *prevNext) {
1334 *prevNext = a->next;
1336 free_agent_snmp_session(a);
1340 prevNext = &(a->next);
1344 if (a == NULL && asp != NULL) {
1346 * We coulnd't find it on the list, so free it anyway.
1348 free_agent_snmp_session(asp);
1353 netsnmp_free_agent_snmp_session_by_session(netsnmp_session * sess,
1354 void (*free_request)
1355 (netsnmp_request_list *))
1357 netsnmp_agent_session *a, *next, **prevNext = &agent_session_list;
1359 DEBUGMSGTL(("snmp_agent", "REMOVE session == %08p\n", sess));
1361 for (a = agent_session_list; a != NULL; a = next) {
1362 if (a->session == sess) {
1363 *prevNext = a->next;
1365 free_agent_snmp_session(a);
1367 prevNext = &(a->next);
1373 /** handles an incoming SNMP packet into the agent */
1375 handle_snmp_packet(int op, netsnmp_session * session, int reqid,
1376 netsnmp_pdu *pdu, void *magic)
1378 netsnmp_agent_session *asp;
1379 int status, access_ret, rc;
1382 * We only support receiving here.
1384 if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
1389 * RESPONSE messages won't get this far, but TRAP-like messages
1392 if (pdu->command == SNMP_MSG_TRAP || pdu->command == SNMP_MSG_INFORM ||
1393 pdu->command == SNMP_MSG_TRAP2) {
1394 DEBUGMSGTL(("snmp_agent", "received trap-like PDU (%02x)\n",
1396 pdu->command = SNMP_MSG_TRAP2;
1397 snmp_increment_statistic(STAT_SNMPUNKNOWNPDUHANDLERS);
1401 if (magic == NULL) {
1402 asp = init_agent_snmp_session(session, pdu);
1403 status = SNMP_ERR_NOERROR;
1405 asp = (netsnmp_agent_session *) magic;
1406 status = asp->status;
1409 if ((access_ret = check_access(pdu)) != 0) {
1410 if (access_ret == VACM_NOSUCHCONTEXT) {
1412 * rfc2573 section 3.2, step 5 says that we increment the
1413 * counter but don't return a response of any kind
1417 * we currently don't support unavailable contexts, as
1418 * there is no reason to that I currently know of
1420 snmp_increment_statistic(STAT_SNMPUNKNOWNCONTEXTS);
1425 netsnmp_remove_and_free_agent_snmp_session(asp);
1429 * access control setup is incorrect
1431 send_easy_trap(SNMP_TRAP_AUTHFAIL, 0);
1432 if (asp->pdu->version != SNMP_VERSION_1
1433 && asp->pdu->version != SNMP_VERSION_2c) {
1434 asp->pdu->errstat = SNMP_ERR_AUTHORIZATIONERROR;
1435 asp->pdu->command = SNMP_MSG_RESPONSE;
1436 snmp_increment_statistic(STAT_SNMPOUTPKTS);
1437 if (!snmp_send(asp->session, asp->pdu))
1438 snmp_free_pdu(asp->pdu);
1440 netsnmp_remove_and_free_agent_snmp_session(asp);
1446 netsnmp_remove_and_free_agent_snmp_session(asp);
1452 rc = netsnmp_handle_request(asp, status);
1457 DEBUGMSGTL(("snmp_agent", "end of handle_snmp_packet, asp = %08p\n",
1462 netsnmp_request_info *
1463 netsnmp_add_varbind_to_cache(netsnmp_agent_session *asp, int vbcount,
1464 netsnmp_variable_list * varbind_ptr,
1465 netsnmp_subtree *tp)
1467 netsnmp_request_info *request = NULL;
1470 DEBUGMSGTL(("snmp_agent", "add_vb_to_cache(%8p, %d, ", asp, vbcount));
1471 DEBUGMSGOID(("snmp_agent", varbind_ptr->name,
1472 varbind_ptr->name_length));
1473 DEBUGMSG(("snmp_agent", ", %8p)\n", tp));
1476 (asp->pdu->command == SNMP_MSG_GETNEXT ||
1477 asp->pdu->command == SNMP_MSG_GETBULK)) {
1481 prefix_len = netsnmp_oid_find_prefix(tp->start_a,
1483 tp->end_a, tp->end_len);
1485 netsnmp_acm_check_subtree(asp->pdu, tp->start_a, prefix_len);
1487 while (result == VACM_NOTINVIEW) {
1488 /* the entire subtree is not in view. Skip it. */
1489 /** @todo make this be more intelligent about ranges.
1490 Right now we merely take the highest level
1491 commonality of a registration range and use that.
1492 At times we might be able to be smarter about
1493 checking the range itself as opposed to the node
1494 above where the range exists, but I doubt this will
1495 come up all that frequently. */
1498 prefix_len = netsnmp_oid_find_prefix(tp->start_a,
1503 netsnmp_acm_check_subtree(asp->pdu,
1504 tp->start_a, prefix_len);
1510 * no appropriate registration found
1513 * make up the response ourselves
1515 switch (asp->pdu->command) {
1516 case SNMP_MSG_GETNEXT:
1517 case SNMP_MSG_GETBULK:
1518 varbind_ptr->type = SNMP_ENDOFMIBVIEW;
1522 varbind_ptr->type = SNMP_NOSUCHOBJECT;
1526 varbind_ptr->type = SNMP_NOSUCHOBJECT;
1530 return NULL; /* shouldn't get here */
1533 DEBUGMSGTL(("snmp_agent", "tp->start "));
1534 DEBUGMSGOID(("snmp_agent", tp->start_a, tp->start_len));
1535 DEBUGMSG(("snmp_agent", ", tp->end "));
1536 DEBUGMSGOID(("snmp_agent", tp->end_a, tp->end_len));
1537 DEBUGMSG(("snmp_agent", ", \n"));
1540 * malloc the request structure
1542 request = &(asp->requests[vbcount - 1]);
1543 request->index = vbcount;
1544 request->delegated = 0;
1545 request->processed = 0;
1546 request->status = 0;
1547 request->subtree = tp;
1548 if (request->parent_data) {
1549 netsnmp_free_request_data_sets(request);
1553 * for non-SET modes, set the type to NULL
1555 if (!MODE_IS_SET(asp->pdu->command)) {
1556 if (varbind_ptr->type == ASN_PRIV_INCL_RANGE) {
1557 DEBUGMSGTL(("snmp_agent", "varbind %d is inclusive\n",
1559 request->inclusive = 1;
1561 varbind_ptr->type = ASN_NULL;
1565 * place them in a cache
1567 if (tp->global_cacheid) {
1569 * we need to merge all marked subtrees together
1571 if (asp->cache_store && -1 !=
1572 (cacheid = netsnmp_get_local_cachid(asp->cache_store,
1573 tp->global_cacheid))) {
1575 cacheid = ++(asp->treecache_num);
1576 netsnmp_get_or_add_local_cachid(&asp->cache_store,
1579 goto mallocslot; /* XXX: ick */
1581 } else if (tp->cacheid > -1 && tp->cacheid <= asp->treecache_num &&
1582 asp->treecache[tp->cacheid].subtree == tp) {
1584 * we have already added a request to this tree
1587 cacheid = tp->cacheid;
1589 cacheid = ++(asp->treecache_num);
1594 if (asp->treecache_num >= asp->treecache_len) {
1596 * exapand cache array
1599 * WWW: non-linear expansion needed (with cap)
1601 #define CACHE_GROW_SIZE 16
1602 asp->treecache_len =
1603 (asp->treecache_len + CACHE_GROW_SIZE);
1605 realloc(asp->treecache,
1606 sizeof(netsnmp_tree_cache) *
1607 asp->treecache_len);
1608 if (asp->treecache == NULL)
1610 memset(&(asp->treecache[cacheid]), 0x00,
1611 sizeof(netsnmp_tree_cache) * (CACHE_GROW_SIZE));
1613 asp->treecache[cacheid].subtree = tp;
1614 asp->treecache[cacheid].requests_begin = request;
1615 tp->cacheid = cacheid;
1619 * if this is a search type, get the ending range oid as well
1621 if (asp->pdu->command == SNMP_MSG_GETNEXT ||
1622 asp->pdu->command == SNMP_MSG_GETBULK) {
1623 request->range_end = tp->end_a;
1624 request->range_end_len = tp->end_len;
1626 request->range_end = NULL;
1627 request->range_end_len = 0;
1633 if (asp->treecache[cacheid].requests_end)
1634 asp->treecache[cacheid].requests_end->next = request;
1635 request->next = NULL;
1636 request->prev = asp->treecache[cacheid].requests_end;
1637 asp->treecache[cacheid].requests_end = request;
1640 * add the given request to the list of requests they need
1641 * to handle results for
1643 request->requestvb = varbind_ptr;
1649 * check the ACM(s) for the results on each of the varbinds.
1650 * If ACM disallows it, replace the value with type
1652 * Returns number of varbinds with ACM errors
1655 check_acm(netsnmp_agent_session *asp, u_char type)
1659 netsnmp_request_info *request;
1661 netsnmp_variable_list *vb;
1663 for (i = 0; i <= asp->treecache_num; i++) {
1664 for (request = asp->treecache[i].requests_begin;
1665 request; request = request->next) {
1667 * for each request, run it through in_a_view()
1669 vb = request->requestvb;
1670 if (vb->type == ASN_NULL) /* not yet processed */
1673 in_a_view(vb->name, &vb->name_length, asp->pdu, vb->type);
1676 * if a ACM error occurs, mark it as type passed in
1678 if (view != VACM_SUCCESS) {
1680 snmp_set_var_typed_value(vb, type, NULL, 0);
1689 netsnmp_create_subtree_cache(netsnmp_agent_session *asp)
1691 netsnmp_subtree *tp;
1692 netsnmp_variable_list *varbind_ptr, *vbsave, *vbptr, **prevNext;
1695 int bulkcount = 0, bulkrep = 0;
1696 int i = 0, n = 0, r = 0;
1697 netsnmp_request_info *request;
1699 if (asp->treecache == NULL && asp->treecache_len == 0) {
1700 asp->treecache_len = SNMP_MAX(1 + asp->vbcount / 4, 16);
1702 calloc(asp->treecache_len, sizeof(netsnmp_tree_cache));
1703 if (asp->treecache == NULL)
1704 return SNMP_ERR_GENERR;
1706 asp->treecache_num = -1;
1708 if (asp->pdu->command == SNMP_MSG_GETBULK) {
1712 int count = count_varbinds(asp->pdu->variables);
1714 if (asp->pdu->errstat < 0) {
1715 asp->pdu->errstat = 0;
1717 if (asp->pdu->errindex < 0) {
1718 asp->pdu->errindex = 0;
1721 if (asp->pdu->errstat < count) {
1722 n = asp->pdu->errstat;
1726 if ((r = count - n) < 0) {
1728 asp->bulkcache = NULL;
1731 (netsnmp_variable_list **) malloc(asp->pdu->errindex * r *
1735 DEBUGMSGTL(("snmp_agent", "GETBULK N = %d, M = %d, R = %d\n",
1736 n, asp->pdu->errindex, r));
1740 * collect varbinds into their registered trees
1742 prevNext = &(asp->pdu->variables);
1743 for (varbind_ptr = asp->pdu->variables; varbind_ptr;
1744 varbind_ptr = vbsave) {
1747 * getbulk mess with this pointer, so save it
1749 vbsave = varbind_ptr->next_variable;
1751 if (asp->pdu->command == SNMP_MSG_GETBULK) {
1756 * repeate request varbinds on GETBULK. These will
1757 * have to be properly rearranged later though as
1758 * responses are supposed to actually be interlaced
1759 * with each other. This is done with the asp->bulkcache.
1761 bulkrep = asp->pdu->errindex - 1;
1762 if (asp->pdu->errindex > 0) {
1763 vbptr = varbind_ptr;
1764 asp->bulkcache[bulkcount++] = vbptr;
1766 for (i = 1; i < asp->pdu->errindex; i++) {
1767 vbptr->next_variable =
1768 SNMP_MALLOC_STRUCT(variable_list);
1770 * don't clone the oid as it's got to be
1771 * overwwritten anyway
1773 if (!vbptr->next_variable) {
1778 vbptr = vbptr->next_variable;
1779 vbptr->name_length = 0;
1780 vbptr->type = ASN_NULL;
1781 asp->bulkcache[bulkcount++] = vbptr;
1784 vbptr->next_variable = vbsave;
1787 * 0 repeats requested for this varbind, so take it off
1790 vbptr = varbind_ptr;
1791 *prevNext = vbptr->next_variable;
1792 vbptr->next_variable = NULL;
1793 snmp_free_varbind(vbptr);
1801 * count the varbinds
1806 * find the owning tree
1808 tp = netsnmp_subtree_find(varbind_ptr->name, varbind_ptr->name_length,
1809 NULL, asp->pdu->contextName);
1812 * check access control
1814 switch (asp->pdu->command) {
1816 view = in_a_view(varbind_ptr->name, &varbind_ptr->name_length,
1817 asp->pdu, varbind_ptr->type);
1818 if (view != VACM_SUCCESS)
1819 snmp_set_var_typed_value(varbind_ptr, SNMP_NOSUCHOBJECT,
1824 view = in_a_view(varbind_ptr->name, &varbind_ptr->name_length,
1825 asp->pdu, varbind_ptr->type);
1826 if (view != VACM_SUCCESS)
1827 return SNMP_ERR_NOTWRITABLE;
1830 case SNMP_MSG_GETNEXT:
1831 case SNMP_MSG_GETBULK:
1833 view = VACM_SUCCESS;
1835 * XXXWWW: check VACM here to see if "tp" is even worthwhile
1838 if (view == VACM_SUCCESS) {
1839 request = netsnmp_add_varbind_to_cache(asp, vbcount, varbind_ptr,
1841 if (request && asp->pdu->command == SNMP_MSG_GETBULK) {
1842 request->repeat = bulkrep;
1845 return SNMP_ERR_GENERR;
1848 prevNext = &(varbind_ptr->next_variable);
1851 return SNMPERR_SUCCESS;
1855 * this function is only applicable in getnext like contexts
1858 netsnmp_reassign_requests(netsnmp_agent_session *asp)
1861 * assume all the requests have been filled or rejected by the
1862 * subtrees, so reassign the rejected ones to the next subtree in
1871 netsnmp_tree_cache *old_treecache = asp->treecache;
1877 (netsnmp_tree_cache *) calloc(asp->treecache_len,
1878 sizeof(netsnmp_tree_cache));
1879 asp->treecache_num = -1;
1880 if (asp->cache_store) {
1881 netsnmp_free_cachemap(asp->cache_store);
1882 asp->cache_store = NULL;
1885 for (i = 0; i < asp->vbcount; i++) {
1886 if (asp->requests[i].requestvb->type == ASN_NULL) {
1887 if (!netsnmp_add_varbind_to_cache(asp, asp->requests[i].index,
1888 asp->requests[i].requestvb,
1889 asp->requests[i].subtree->next)) {
1890 if (old_treecache != NULL) {
1891 free(old_treecache);
1893 return SNMP_ERR_GENERR;
1895 } else if (asp->requests[i].requestvb->type == ASN_PRIV_RETRY) {
1897 * re-add the same subtree
1899 asp->requests[i].requestvb->type = ASN_NULL;
1900 if (!netsnmp_add_varbind_to_cache(asp, asp->requests[i].index,
1901 asp->requests[i].requestvb,
1902 asp->requests[i].subtree)) {
1903 if (old_treecache != NULL) {
1904 free(old_treecache);
1906 return SNMP_ERR_GENERR;
1911 if (old_treecache != NULL) {
1912 free(old_treecache);
1914 return SNMP_ERR_NOERROR;
1918 netsnmp_delete_request_infos(netsnmp_request_info *reqlist)
1921 netsnmp_free_request_data_sets(reqlist);
1922 reqlist = reqlist->next;
1927 netsnmp_delete_subtree_cache(netsnmp_agent_session *asp)
1929 while (asp->treecache_num >= 0) {
1931 * don't delete subtrees
1933 netsnmp_delete_request_infos(asp->treecache[asp->treecache_num].
1935 asp->treecache_num--;
1940 netsnmp_check_requests_status(netsnmp_agent_session *asp,
1941 netsnmp_request_info *requests,
1942 int look_for_specific)
1945 * find any errors marked in the requests
1948 if (requests->status != SNMP_ERR_NOERROR &&
1949 (!look_for_specific || requests->status == look_for_specific)
1950 && (look_for_specific || asp->index == 0
1951 || requests->index < asp->index)) {
1952 asp->index = requests->index;
1953 asp->status = requests->status;
1955 requests = requests->next;
1961 netsnmp_check_all_requests_status(netsnmp_agent_session *asp,
1962 int look_for_specific)
1965 for (i = 0; i <= asp->treecache_num; i++) {
1966 netsnmp_check_requests_status(asp,
1967 asp->treecache[i].requests_begin,
1974 handle_var_requests(netsnmp_agent_session *asp)
1976 int i, retstatus = SNMP_ERR_NOERROR,
1977 status = SNMP_ERR_NOERROR, final_status = SNMP_ERR_NOERROR;
1978 netsnmp_handler_registration *reginfo;
1981 * create the netsnmp_agent_request_info data
1983 if (!asp->reqinfo) {
1984 asp->reqinfo = SNMP_MALLOC_TYPEDEF(netsnmp_agent_request_info);
1986 return SNMP_ERR_GENERR;
1989 asp->reqinfo->asp = asp;
1990 asp->reqinfo->mode = asp->mode;
1993 * now, have the subtrees in the cache go search for their results
1995 for (i = 0; i <= asp->treecache_num; i++) {
1996 reginfo = asp->treecache[i].subtree->reginfo;
1997 status = netsnmp_call_handlers(reginfo, asp->reqinfo,
1998 asp->treecache[i].requests_begin);
2001 * find any errors marked in the requests. For later parts of
2002 * SET processing, only check for new errors specific to that
2003 * set processing directive (which must superceed the previous
2006 switch (asp->mode) {
2007 case MODE_SET_COMMIT:
2008 retstatus = netsnmp_check_requests_status(asp,
2011 SNMP_ERR_COMMITFAILED);
2015 retstatus = netsnmp_check_requests_status(asp,
2018 SNMP_ERR_UNDOFAILED);
2022 retstatus = netsnmp_check_requests_status(asp,
2029 * always take lowest varbind if possible
2031 if (retstatus != SNMP_ERR_NOERROR) {
2036 * other things we know less about (no index)
2039 * WWW: drop support for this?
2041 if (final_status == SNMP_ERR_NOERROR && status != SNMP_ERR_NOERROR) {
2043 * we can't break here, since some processing needs to be
2044 * done for all requests anyway (IE, SET handling for UNDO
2045 * needs to be called regardless of previous status
2047 * WWW: This should be predictable though and
2048 * breaking should be possible in some cases (eg GET,
2051 final_status = status;
2055 return final_status;
2059 * loop through our sessions known delegated sessions and check to see
2060 * if they've completed yet. If there are no more delegated sessions,
2061 * check for and process any queued requests
2064 netsnmp_check_outstanding_agent_requests(void)
2066 netsnmp_agent_session *asp, *prev_asp = NULL, *next_asp = NULL;
2069 * deal with delegated requests
2071 for (asp = agent_delegated_list; asp; prev_asp = asp, asp = next_asp) {
2072 next_asp = asp->next; /* save in case we clean up asp */
2073 if (!netsnmp_check_for_delegated(asp)) {
2076 * we're done with this one, remove from queue
2078 if (prev_asp != NULL)
2079 prev_asp->next = asp->next;
2081 agent_delegated_list = asp->next;
2084 * check request status
2086 netsnmp_check_all_requests_status(asp, 0);
2089 * continue processing or finish up
2091 check_delayed_request(asp);
2096 * if we are processing a set and there are more delegated
2097 * requests, keep waiting before getting to queued requests.
2099 if (netsnmp_processing_set && (NULL != agent_delegated_list))
2102 while (netsnmp_agent_queued_list) {
2105 * if we are processing a set, the first item better be
2106 * the set being (or waiting to be) processed.
2108 netsnmp_assert((!netsnmp_processing_set) ||
2109 (netsnmp_processing_set == netsnmp_agent_queued_list));
2112 * if the top request is a set, don't pop it
2113 * off if there are delegated requests
2115 if ((netsnmp_agent_queued_list->pdu->command == SNMP_MSG_SET) &&
2116 (agent_delegated_list)) {
2118 netsnmp_assert(netsnmp_processing_set == NULL);
2120 netsnmp_processing_set = netsnmp_agent_queued_list;
2121 DEBUGMSGTL(("snmp_agent", "SET request remains queued while "
2122 "delegated requests finish, asp = %08p\n", asp));
2127 * pop the first request and process it
2129 asp = netsnmp_agent_queued_list;
2130 netsnmp_agent_queued_list = asp->next;
2131 DEBUGMSGTL(("snmp_agent",
2132 "processing queued request, asp = %08p\n", asp));
2134 netsnmp_handle_request(asp, asp->status);
2137 * if we hit a set, stop
2139 if (NULL != netsnmp_processing_set)
2144 /** Decide if the requested transaction_id is still being processed
2145 within the agent. This is used to validate whether a delayed cache
2146 (containing possibly freed pointers) is still usable.
2148 returns SNMPERR_SUCCESS if it's still valid, or SNMPERR_GENERR if not. */
2150 netsnmp_check_transaction_id(int transaction_id)
2152 netsnmp_agent_session *asp, *prev_asp = NULL;
2154 for (asp = agent_delegated_list; asp; prev_asp = asp, asp = asp->next) {
2155 if (asp->pdu->transid == transaction_id)
2156 return SNMPERR_SUCCESS;
2158 return SNMPERR_GENERR;
2163 * check_delayed_request(asp)
2165 * Called to rexamine a set of requests and continue processing them
2166 * once all the previous (delayed) requests have been handled one way
2171 check_delayed_request(netsnmp_agent_session *asp)
2173 int status = SNMP_ERR_NOERROR;
2175 DEBUGMSGTL(("snmp_agent", "processing delegated request, asp = %08p\n",
2178 switch (asp->mode) {
2179 case SNMP_MSG_GETBULK:
2180 case SNMP_MSG_GETNEXT:
2181 netsnmp_check_all_requests_status(asp, 0);
2182 handle_getnext_loop(asp);
2183 if (netsnmp_check_for_delegated(asp) &&
2184 netsnmp_check_transaction_id(asp->pdu->transid) !=
2187 * add to delegated request chain
2189 if (!netsnmp_check_delegated_chain_for(asp)) {
2190 asp->next = agent_delegated_list;
2191 agent_delegated_list = asp;
2196 case MODE_SET_COMMIT:
2197 netsnmp_check_all_requests_status(asp, SNMP_ERR_COMMITFAILED);
2201 netsnmp_check_all_requests_status(asp, SNMP_ERR_UNDOFAILED);
2204 case MODE_SET_BEGIN:
2205 case MODE_SET_RESERVE1:
2206 case MODE_SET_RESERVE2:
2207 case MODE_SET_ACTION:
2210 handle_set_loop(asp);
2211 if (asp->mode != FINISHED_SUCCESS && asp->mode != FINISHED_FAILURE) {
2213 if (netsnmp_check_for_delegated_and_add(asp)) {
2215 * add to delegated request chain
2218 asp->status = status;
2221 return SNMP_ERR_NOERROR;
2230 * if we don't have anything outstanding (delegated), wrap up
2232 if (!netsnmp_check_for_delegated(asp))
2233 return netsnmp_wrap_up_request(asp, status);
2238 /** returns 1 if there are valid GETNEXT requests left. Returns 0 if not. */
2240 check_getnext_results(netsnmp_agent_session *asp)
2245 netsnmp_tree_cache *old_treecache = asp->treecache;
2246 int old_treecache_num = asp->treecache_num;
2249 netsnmp_request_info *request;
2251 if (asp->mode == SNMP_MSG_GET) {
2253 * Special case for doing INCLUSIVE getNext operations in
2256 DEBUGMSGTL(("snmp_agent",
2257 "asp->mode == SNMP_MSG_GET in ch_getnext\n"));
2258 asp->mode = asp->oldmode;
2262 for (i = 0; i <= old_treecache_num; i++) {
2263 for (request = old_treecache[i].requests_begin; request;
2264 request = request->next) {
2267 * If we have just done the special case AgentX GET, then any
2268 * requests which were not INCLUSIVE will now have a wrong
2269 * response, so junk them and retry from the same place (except
2270 * that this time the handler will be called in "inexact"
2275 if (!request->inclusive) {
2276 DEBUGMSGTL(("snmp_agent",
2277 "request %d wasn't inclusive\n",
2279 snmp_set_var_typed_value(request->requestvb,
2280 ASN_PRIV_RETRY, NULL, 0);
2281 } else if (request->requestvb->type == ASN_NULL ||
2282 request->requestvb->type == SNMP_NOSUCHINSTANCE ||
2283 request->requestvb->type == SNMP_NOSUCHOBJECT) {
2285 * it was inclusive, but no results. Still retry this
2288 snmp_set_var_typed_value(request->requestvb,
2289 ASN_PRIV_RETRY, NULL, 0);
2296 if (snmp_oid_compare(request->requestvb->name,
2297 request->requestvb->name_length,
2299 request->range_end_len) >= 0) {
2301 * ack, it's beyond the accepted end of range.
2304 * fix it by setting the oid to the end of range oid instead
2306 DEBUGMSGTL(("check_getnext_results",
2307 "request response %d out of range\n",
2309 request->inclusive = 1;
2311 * XXX: should set this to the original OID?
2313 snmp_set_var_objid(request->requestvb,
2315 request->range_end_len);
2316 snmp_set_var_typed_value(request->requestvb, ASN_NULL,
2321 * mark any existent requests with illegal results as NULL
2323 if (request->requestvb->type == SNMP_ENDOFMIBVIEW) {
2325 * illegal response from a subagent. Change it back to NULL
2327 request->requestvb->type = ASN_NULL;
2328 request->inclusive = 1;
2331 if (request->requestvb->type == ASN_NULL ||
2332 request->requestvb->type == ASN_PRIV_RETRY ||
2333 (asp->reqinfo->mode == MODE_GETBULK
2334 && request->repeat > 0))
2341 /** repeatedly calls getnext handlers looking for an answer till all
2342 requests are satisified. It's expected that one pass has been made
2343 before entering this function */
2345 handle_getnext_loop(netsnmp_agent_session *asp)
2348 netsnmp_variable_list *var_ptr;
2356 * bail for now if anything is delegated.
2358 if (netsnmp_check_for_delegated(asp)) {
2359 return SNMP_ERR_NOERROR;
2363 * check vacm against results
2365 check_acm(asp, ASN_PRIV_RETRY);
2368 * need to keep going we're not done yet.
2370 if (!check_getnext_results(asp))
2372 * nothing left, quit now
2377 * never had a request (empty pdu), quit now
2380 * XXXWWW: huh? this would be too late, no? shouldn't we
2381 * catch this earlier?
2388 DEBUGIF("results") {
2389 DEBUGMSGTL(("results",
2390 "getnext results, before next pass:\n"));
2391 for (var_ptr = asp->pdu->variables; var_ptr;
2392 var_ptr = var_ptr->next_variable) {
2393 DEBUGMSGTL(("results", "\t"));
2394 DEBUGMSGVAR(("results", var_ptr));
2395 DEBUGMSG(("results", "\n"));
2399 netsnmp_reassign_requests(asp);
2400 status = handle_var_requests(asp);
2401 if (status != SNMP_ERR_NOERROR) {
2402 return status; /* should never really happen */
2405 return SNMP_ERR_NOERROR;
2409 handle_set(netsnmp_agent_session *asp)
2413 * SETS require 3-4 passes through the var_op_list.
2415 * passes verify that all types, lengths, and values are valid
2416 * and may reserve resources and the third does the set and a
2417 * fourth executes any actions. Then the identical GET RESPONSE
2418 * packet is returned.
2419 * If either of the first two passes returns an error, another
2420 * pass is made so that any reserved resources can be freed.
2421 * If the third pass returns an error, another pass is
2423 * any changes can be reversed.
2424 * If the fourth pass (or any of the error handling passes)
2425 * return an error, we'd rather not know about it!
2427 if (!(asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY)) {
2428 switch (asp->mode) {
2429 case MODE_SET_BEGIN:
2430 snmp_increment_statistic(STAT_SNMPINSETREQUESTS);
2431 asp->rw = WRITE; /* WWW: still needed? */
2432 asp->mode = MODE_SET_RESERVE1;
2433 asp->status = SNMP_ERR_NOERROR;
2436 case MODE_SET_RESERVE1:
2438 if (asp->status != SNMP_ERR_NOERROR)
2439 asp->mode = MODE_SET_FREE;
2441 asp->mode = MODE_SET_RESERVE2;
2444 case MODE_SET_RESERVE2:
2445 if (asp->status != SNMP_ERR_NOERROR)
2446 asp->mode = MODE_SET_FREE;
2448 asp->mode = MODE_SET_ACTION;
2451 case MODE_SET_ACTION:
2452 if (asp->status != SNMP_ERR_NOERROR)
2453 asp->mode = MODE_SET_UNDO;
2455 asp->mode = MODE_SET_COMMIT;
2458 case MODE_SET_COMMIT:
2459 if (asp->status != SNMP_ERR_NOERROR) {
2460 asp->mode = FINISHED_FAILURE;
2462 asp->mode = FINISHED_SUCCESS;
2467 asp->mode = FINISHED_FAILURE;
2471 asp->mode = FINISHED_FAILURE;
2476 if (asp->mode != FINISHED_SUCCESS && asp->mode != FINISHED_FAILURE) {
2477 DEBUGMSGTL(("agent_set", "doing set mode = %d (%s)\n", asp->mode,
2478 se_find_label_in_slist("agent_mode", asp->mode)));
2479 status = handle_var_requests(asp);
2480 DEBUGMSGTL(("agent_set", "did set mode = %d, status = %d\n",
2481 asp->mode, status));
2482 if ((status != SNMP_ERR_NOERROR && asp->status == SNMP_ERR_NOERROR) ||
2483 status == SNMP_ERR_COMMITFAILED ||
2484 status == SNMP_ERR_UNDOFAILED) {
2485 asp->status = status;
2492 handle_set_loop(netsnmp_agent_session *asp)
2494 while (asp->mode != FINISHED_FAILURE && asp->mode != FINISHED_SUCCESS) {
2496 if (netsnmp_check_for_delegated(asp)) {
2497 return SNMP_ERR_NOERROR;
2499 if (asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY) {
2507 netsnmp_handle_request(netsnmp_agent_session *asp, int status)
2510 * if this isn't a delegated request trying to finish,
2511 * processing of a set request should not start until all
2512 * delegated requests have completed, and no other new requests
2513 * should be processed until the set request completes.
2515 if ((0 == netsnmp_check_delegated_chain_for(asp)) &&
2516 (asp != netsnmp_processing_set)) {
2518 * if we are processing a set and this is not a delegated
2519 * request, queue the request
2521 if (netsnmp_processing_set) {
2522 netsnmp_add_queued(asp);
2523 DEBUGMSGTL(("snmp_agent",
2524 "request queued while processing set, "
2525 "asp = %08p\n", asp));
2530 * check for set request
2532 if (asp->pdu->command == SNMP_MSG_SET) {
2533 netsnmp_processing_set = asp;
2536 * if there are delegated requests, we must wait for them
2539 if (agent_delegated_list) {
2540 DEBUGMSGTL(("snmp_agent", "SET request queued while "
2541 "delegated requests finish, asp = %08p\n",
2543 netsnmp_add_queued(asp);
2550 * process the request
2552 status = handle_pdu(asp);
2555 * print the results in appropriate debugging mode
2557 DEBUGIF("results") {
2558 netsnmp_variable_list *var_ptr;
2559 DEBUGMSGTL(("results", "request results (status = %d):\n",
2561 for (var_ptr = asp->pdu->variables; var_ptr;
2562 var_ptr = var_ptr->next_variable) {
2563 DEBUGMSGTL(("results", "\t"));
2564 DEBUGMSGVAR(("results", var_ptr));
2565 DEBUGMSG(("results", "\n"));
2570 * check for uncompleted requests
2572 if (netsnmp_check_for_delegated_and_add(asp)) {
2574 * add to delegated request chain
2576 asp->status = status;
2579 * if we don't have anything outstanding (delegated), wrap up
2581 return netsnmp_wrap_up_request(asp, status);
2588 handle_pdu(netsnmp_agent_session *asp)
2590 int status, inclusives = 0;
2591 netsnmp_variable_list *v = NULL;
2594 * for illegal requests, mark all nodes as ASN_NULL
2596 switch (asp->pdu->command) {
2598 case SNMP_MSG_INTERNAL_SET_RESERVE2:
2599 case SNMP_MSG_INTERNAL_SET_ACTION:
2600 case SNMP_MSG_INTERNAL_SET_COMMIT:
2601 case SNMP_MSG_INTERNAL_SET_FREE:
2602 case SNMP_MSG_INTERNAL_SET_UNDO:
2603 status = get_set_cache(asp);
2604 if (status != SNMP_ERR_NOERROR)
2609 case SNMP_MSG_GETNEXT:
2610 case SNMP_MSG_GETBULK:
2611 for (v = asp->pdu->variables; v != NULL; v = v->next_variable) {
2612 if (v->type == ASN_PRIV_INCL_RANGE) {
2614 * Leave the type for now (it gets set to
2615 * ASN_NULL in netsnmp_add_varbind_to_cache,
2616 * called by create_subnetsnmp_tree_cache below).
2617 * If we set it to ASN_NULL now, we wouldn't be
2618 * able to distinguish INCLUSIVE search
2623 snmp_set_var_typed_value(v, ASN_NULL, NULL, 0);
2630 case SNMP_MSG_INTERNAL_SET_BEGIN:
2631 case SNMP_MSG_INTERNAL_SET_RESERVE1:
2633 asp->vbcount = count_varbinds(asp->pdu->variables);
2634 if (asp->vbcount) /* efence doesn't like 0 size allocs */
2635 asp->requests = (netsnmp_request_info *)
2636 calloc(asp->vbcount, sizeof(netsnmp_request_info));
2640 status = netsnmp_create_subtree_cache(asp);
2641 if (status != SNMP_ERR_NOERROR)
2645 asp->mode = asp->pdu->command;
2646 switch (asp->mode) {
2649 * increment the message type counter
2651 snmp_increment_statistic(STAT_SNMPINGETREQUESTS);
2654 * check vacm ahead of time
2656 check_acm(asp, SNMP_NOSUCHOBJECT);
2661 status = handle_var_requests(asp);
2664 * Deal with unhandled results -> noSuchInstance (rather
2665 * than noSuchObject -- in that case, the type will
2666 * already have been set to noSuchObject when we realised
2667 * we couldn't find an appropriate tree).
2669 if (status == SNMP_ERR_NOERROR)
2670 snmp_replace_var_types(asp->pdu->variables, ASN_NULL,
2671 SNMP_NOSUCHINSTANCE);
2674 case SNMP_MSG_GETNEXT:
2675 snmp_increment_statistic(STAT_SNMPINGETNEXTS);
2680 case SNMP_MSG_GETBULK: /* note: there is no getbulk stat */
2682 * loop through our mib tree till we find an
2683 * appropriate response to return to the caller.
2688 * This is a special case for AgentX INCLUSIVE getNext
2689 * requests where a result lexi-equal to the request is okay
2690 * but if such a result does not exist, we still want the
2691 * lexi-next one. So basically we do a GET first, and if any
2692 * of the INCLUSIVE requests are satisfied, we use that
2693 * value. Then, unsatisfied INCLUSIVE requests, and
2694 * non-INCLUSIVE requests get done as normal.
2697 DEBUGMSGTL(("snmp_agent", "inclusive range(s) in getNext\n"));
2698 asp->oldmode = asp->mode;
2699 asp->mode = SNMP_MSG_GET;
2705 status = handle_var_requests(asp);
2706 if (status != SNMP_ERR_NOERROR) {
2708 return status; /* should never really happen */
2710 asp->status = SNMP_ERR_NOERROR;
2714 * loop through our mib tree till we find an
2715 * appropriate response to return to the caller.
2718 status = handle_getnext_loop(asp);
2723 * check access permissions first
2725 if (check_acm(asp, SNMP_NOSUCHOBJECT))
2726 return SNMP_ERR_NOTWRITABLE;
2728 asp->mode = MODE_SET_BEGIN;
2729 status = handle_set_loop(asp);
2733 case SNMP_MSG_INTERNAL_SET_BEGIN:
2734 case SNMP_MSG_INTERNAL_SET_RESERVE1:
2735 case SNMP_MSG_INTERNAL_SET_RESERVE2:
2736 case SNMP_MSG_INTERNAL_SET_ACTION:
2737 case SNMP_MSG_INTERNAL_SET_COMMIT:
2738 case SNMP_MSG_INTERNAL_SET_FREE:
2739 case SNMP_MSG_INTERNAL_SET_UNDO:
2740 asp->pdu->flags |= UCD_MSG_FLAG_ONE_PASS_ONLY;
2741 status = handle_set_loop(asp);
2743 * asp related cache is saved in cleanup
2747 case SNMP_MSG_RESPONSE:
2748 snmp_increment_statistic(STAT_SNMPINGETRESPONSES);
2749 return SNMP_ERR_NOERROR;
2752 case SNMP_MSG_TRAP2:
2753 snmp_increment_statistic(STAT_SNMPINTRAPS);
2754 return SNMP_ERR_NOERROR;
2758 * WWW: are reports counted somewhere ?
2760 snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
2761 return SNMPERR_GENERR; /* shouldn't get here */
2770 netsnmp_set_request_error(netsnmp_agent_request_info *reqinfo,
2771 netsnmp_request_info *request, int error_value)
2773 if (!request || !reqinfo)
2776 return netsnmp_set_mode_request_error(reqinfo->mode, request,
2781 netsnmp_set_mode_request_error(int mode, netsnmp_request_info *request,
2787 request->processed = 1;
2788 request->delegated = REQUEST_IS_NOT_DELEGATED;
2790 switch (error_value) {
2791 case SNMP_NOSUCHOBJECT:
2792 case SNMP_NOSUCHINSTANCE:
2793 case SNMP_ENDOFMIBVIEW:
2795 * these are exceptions that should be put in the varbind
2796 * in the case of a GET but should be translated for a SET
2797 * into a real error status code and put in the request
2801 request->requestvb->type = error_value;
2807 * ignore these. They're illegal to set by the
2808 * client APIs for these modes
2810 snmp_log(LOG_ERR, "Illegal error_value %d for mode %d ignored\n",
2815 request->status = SNMP_ERR_NOSUCHNAME; /* WWW: correct? */
2818 break; /* never get here */
2821 if (request->status < 0) {
2823 * illegal local error code. translate to generr
2826 * WWW: full translation map?
2828 snmp_log(LOG_ERR, "Illegal error_value %d translated to %d\n",
2829 error_value, SNMP_ERR_GENERR);
2830 request->status = SNMP_ERR_GENERR;
2833 * WWW: translations and mode checking?
2835 request->status = error_value;
2843 netsnmp_set_all_requests_error(netsnmp_agent_request_info *reqinfo,
2844 netsnmp_request_info *requests,
2848 netsnmp_set_request_error(reqinfo, requests, error_value);
2849 requests = requests->next;
2854 extern struct timeval starttime;
2857 * Return the value of 'sysUpTime' at the given marker
2860 netsnmp_marker_uptime(marker_t pm)
2863 marker_t start = (marker_t) & starttime;
2865 res = uatime_hdiff(start, pm);
2866 return res; /* atime_diff works in msec, not csec */
2870 * struct timeval equivalents of these
2873 netsnmp_timeval_uptime(struct timeval * tv)
2875 return netsnmp_marker_uptime((marker_t) tv);
2879 * Return the current value of 'sysUpTime'
2882 netsnmp_get_agent_uptime(void)
2885 gettimeofday(&now, NULL);
2887 return netsnmp_timeval_uptime(&now);
2893 netsnmp_agent_add_list_data(netsnmp_agent_request_info *ari,
2894 netsnmp_data_list *node)
2897 if (ari->agent_data) {
2898 netsnmp_add_list_data(&ari->agent_data, node);
2900 ari->agent_data = node;
2905 NETSNMP_INLINE void *
2906 netsnmp_agent_get_list_data(netsnmp_agent_request_info *ari,
2910 return netsnmp_get_list_data(ari->agent_data, name);
2916 netsnmp_free_agent_data_set(netsnmp_agent_request_info *ari)
2919 netsnmp_free_list_data(ari->agent_data);
2924 netsnmp_free_agent_data_sets(netsnmp_agent_request_info *ari)
2927 netsnmp_free_all_list_data(ari->agent_data);
2932 netsnmp_free_agent_request_info(netsnmp_agent_request_info *ari)
2935 if (ari->agent_data) {
2936 netsnmp_free_all_list_data(ari->agent_data);