summaryrefslogtreecommitdiff
path: root/agent/mibgroup/agentx/subagent.c
diff options
context:
space:
mode:
Diffstat (limited to 'agent/mibgroup/agentx/subagent.c')
-rw-r--r--agent/mibgroup/agentx/subagent.c1094
1 files changed, 1094 insertions, 0 deletions
diff --git a/agent/mibgroup/agentx/subagent.c b/agent/mibgroup/agentx/subagent.c
new file mode 100644
index 0000000..b3da321
--- /dev/null
+++ b/agent/mibgroup/agentx/subagent.c
@@ -0,0 +1,1094 @@
+/*
+ * AgentX sub-agent
+ */
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-features.h>
+
+#include <sys/types.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.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_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <net-snmp/library/snmp_assert.h>
+
+#include "snmpd.h"
+#include "agentx/protocol.h"
+#include "agentx/client.h"
+#include "agentx/agentx_config.h"
+#include <net-snmp/agent/agent_callbacks.h>
+#include <net-snmp/agent/agent_trap.h>
+#include <net-snmp/agent/sysORTable.h>
+#include <net-snmp/agent/agent_sysORTable.h>
+
+#include "subagent.h"
+
+netsnmp_feature_child_of(agentx_subagent, agentx_all)
+netsnmp_feature_child_of(agentx_enable_subagent, agentx_subagent)
+
+netsnmp_feature_require(remove_trap_session)
+
+#ifdef USING_AGENTX_SUBAGENT_MODULE
+
+static SNMPCallback subagent_register_ping_alarm;
+static SNMPAlarmCallback agentx_reopen_session;
+void agentx_register_callbacks(netsnmp_session * s);
+void agentx_unregister_callbacks(netsnmp_session * ss);
+int handle_subagent_response(int op, netsnmp_session * session,
+ int reqid, netsnmp_pdu *pdu,
+ void *magic);
+#ifndef NETSNMP_NO_WRITE_SUPPORT
+int handle_subagent_set_response(int op,
+ netsnmp_session * session,
+ int reqid, netsnmp_pdu *pdu,
+ void *magic);
+#endif /* !NETSNMP_NO_WRITE_SUPPORT */
+void subagent_startup_callback(unsigned int clientreg,
+ void *clientarg);
+int subagent_open_master_session(void);
+
+typedef struct _net_snmpsubagent_magic_s {
+ int original_command;
+ netsnmp_session *session;
+ netsnmp_variable_list *ovars;
+} ns_subagent_magic;
+
+struct agent_netsnmp_set_info {
+ int transID;
+ int mode;
+ int errstat;
+ time_t uptime;
+ netsnmp_session *sess;
+ netsnmp_variable_list *var_list;
+
+ struct agent_netsnmp_set_info *next;
+};
+
+static struct agent_netsnmp_set_info *Sets = NULL;
+
+netsnmp_session *agentx_callback_sess = NULL;
+extern int callback_master_num;
+extern netsnmp_session *main_session; /* from snmp_agent.c */
+
+int
+subagent_startup(int majorID, int minorID,
+ void *serverarg, void *clientarg)
+{
+ DEBUGMSGTL(("agentx/subagent", "connecting to master...\n"));
+ /*
+ * if a valid ping interval has been defined, call agentx_reopen_session
+ * to try to connect to master or setup a ping alarm if it couldn't
+ * succeed. if no ping interval was set up, just try to connect once.
+ */
+ if (netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_AGENTX_PING_INTERVAL) > 0)
+ agentx_reopen_session(0, NULL);
+ else {
+ subagent_open_master_session();
+ }
+ return 0;
+}
+
+static void
+subagent_init_callback_session(void)
+{
+ if (agentx_callback_sess == NULL) {
+ agentx_callback_sess = netsnmp_callback_open(callback_master_num,
+ handle_subagent_response,
+ NULL, NULL);
+ DEBUGMSGTL(("agentx/subagent", "subagent_init sess %p\n",
+ agentx_callback_sess));
+ }
+}
+
+static int subagent_init_init = 0;
+/**
+ * init subagent callback (local) session and connect to master agent
+ *
+ * @returns 0 for success, !0 otherwise
+ */
+int
+subagent_init(void)
+{
+ int rc = 0;
+
+ DEBUGMSGTL(("agentx/subagent", "initializing....\n"));
+
+ if (++subagent_init_init != 1)
+ return 0;
+
+ netsnmp_assert(netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_ROLE) == SUB_AGENT);
+
+#ifndef NETSNMP_TRANSPORT_CALLBACK_DOMAIN
+ snmp_log(LOG_WARNING,"AgentX subagent has been disabled because "
+ "the callback transport is not available.\n");
+ return -1;
+#endif /* NETSNMP_TRANSPORT_CALLBACK_DOMAIN */
+
+ /*
+ * open (local) callback session
+ */
+ subagent_init_callback_session();
+ if (NULL == agentx_callback_sess)
+ return -1;
+
+ snmp_register_callback(SNMP_CALLBACK_LIBRARY,
+ SNMP_CALLBACK_POST_READ_CONFIG,
+ subagent_startup, NULL);
+
+ DEBUGMSGTL(("agentx/subagent", "initializing.... DONE\n"));
+
+ return rc;
+}
+
+#ifndef NETSNMP_FEATURE_REMOVE_AGENTX_ENABLE_SUBAGENT
+void
+netsnmp_enable_subagent(void) {
+ netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE,
+ SUB_AGENT);
+}
+#endif /* NETSNMP_FEATURE_REMOVE_AGENTX_ENABLE_SUBAGENT */
+
+#ifndef NETSNMP_NO_WRITE_SUPPORT
+struct agent_netsnmp_set_info *
+save_set_vars(netsnmp_session * ss, netsnmp_pdu *pdu)
+{
+ struct agent_netsnmp_set_info *ptr;
+
+ ptr = (struct agent_netsnmp_set_info *)
+ malloc(sizeof(struct agent_netsnmp_set_info));
+ if (ptr == NULL)
+ return NULL;
+
+ /*
+ * Save the important information
+ */
+ ptr->transID = pdu->transid;
+ ptr->sess = ss;
+ ptr->mode = SNMP_MSG_INTERNAL_SET_RESERVE1;
+ ptr->uptime = netsnmp_get_agent_uptime();
+
+ ptr->var_list = snmp_clone_varbind(pdu->variables);
+ if (ptr->var_list == NULL) {
+ free(ptr);
+ return NULL;
+ }
+
+ ptr->next = Sets;
+ Sets = ptr;
+
+ return ptr;
+}
+
+struct agent_netsnmp_set_info *
+restore_set_vars(netsnmp_session * sess, netsnmp_pdu *pdu)
+{
+ struct agent_netsnmp_set_info *ptr;
+
+ for (ptr = Sets; ptr != NULL; ptr = ptr->next)
+ if (ptr->sess == sess && ptr->transID == pdu->transid)
+ break;
+
+ if (ptr == NULL || ptr->var_list == NULL)
+ return NULL;
+
+ pdu->variables = snmp_clone_varbind(ptr->var_list);
+ if (pdu->variables == NULL)
+ return NULL;
+
+ return ptr;
+}
+
+
+void
+free_set_vars(netsnmp_session * ss, netsnmp_pdu *pdu)
+{
+ struct agent_netsnmp_set_info *ptr, *prev = NULL;
+
+ for (ptr = Sets; ptr != NULL; ptr = ptr->next) {
+ if (ptr->sess == ss && ptr->transID == pdu->transid) {
+ if (prev)
+ prev->next = ptr->next;
+ else
+ Sets = ptr->next;
+ snmp_free_varbind(ptr->var_list);
+ free(ptr);
+ return;
+ }
+ prev = ptr;
+ }
+}
+#endif /* !NETSNMP_NO_WRITE_SUPPORT */
+
+static void
+send_agentx_error(netsnmp_session *session, netsnmp_pdu *pdu, int errstat, int errindex)
+{
+ pdu = snmp_clone_pdu(pdu);
+ pdu->command = AGENTX_MSG_RESPONSE;
+ pdu->version = session->version;
+ pdu->errstat = errstat;
+ pdu->errindex = errindex;
+ snmp_free_varbind(pdu->variables);
+ pdu->variables = NULL;
+
+ DEBUGMSGTL(("agentx/subagent", "Sending AgentX response error stat %d idx %d\n",
+ errstat, errindex));
+ if (!snmp_send(session, pdu)) {
+ snmp_free_pdu(pdu);
+ }
+}
+
+int
+handle_agentx_packet(int operation, netsnmp_session * session, int reqid,
+ netsnmp_pdu *pdu, void *magic)
+{
+ struct agent_netsnmp_set_info *asi = NULL;
+ snmp_callback mycallback;
+ netsnmp_pdu *internal_pdu = NULL;
+ void *retmagic = NULL;
+ ns_subagent_magic *smagic = NULL;
+ int result;
+
+ if (operation == NETSNMP_CALLBACK_OP_DISCONNECT) {
+ struct synch_state *state = (struct synch_state *) magic;
+ int period =
+ netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_AGENTX_PING_INTERVAL);
+ DEBUGMSGTL(("agentx/subagent",
+ "transport disconnect indication\n"));
+
+ /*
+ * deal with existing session. This happend if agentx sends
+ * a message to the master, but the master goes away before
+ * a response is sent. agentx will spin in snmp_synch_response_cb,
+ * waiting for a response. At the very least, the waiting
+ * flag must be set to break that loop. The rest is copied
+ * from disconnect handling in snmp_sync_input.
+ */
+ if(state) {
+ state->waiting = 0;
+ state->pdu = NULL;
+ state->status = STAT_ERROR;
+ session->s_snmp_errno = SNMPERR_ABORT;
+ SET_SNMP_ERROR(SNMPERR_ABORT);
+ }
+
+ /*
+ * Deregister the ping alarm, if any, and invalidate all other
+ * references to this session.
+ */
+ if (session->securityModel != SNMP_DEFAULT_SECMODEL) {
+ snmp_alarm_unregister(session->securityModel);
+ }
+ snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
+ SNMPD_CALLBACK_INDEX_STOP, (void *) session);
+ agentx_unregister_callbacks(session);
+ remove_trap_session(session);
+ register_mib_detach();
+ main_session = NULL;
+ if (period != 0) {
+ /*
+ * Pings are enabled, so periodically attempt to re-establish contact
+ * with the master agent. Don't worry about the handle,
+ * agentx_reopen_session unregisters itself if it succeeds in talking
+ * to the master agent.
+ */
+ snmp_alarm_register(period, SA_REPEAT, agentx_reopen_session, NULL);
+ snmp_log(LOG_INFO, "AgentX master disconnected us, reconnecting in %d\n", period);
+ } else {
+ snmp_log(LOG_INFO, "AgentX master disconnected us, not reconnecting\n");
+ }
+ return 0;
+ } else if (operation != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
+ DEBUGMSGTL(("agentx/subagent", "unexpected callback op %d\n",
+ operation));
+ return 1;
+ }
+
+ /*
+ * ok, we have a pdu from the net. Modify as needed
+ */
+
+ DEBUGMSGTL(("agentx/subagent", "handling AgentX request (req=0x%x,trans="
+ "0x%x,sess=0x%x)\n", (unsigned)pdu->reqid,
+ (unsigned)pdu->transid, (unsigned)pdu->sessid));
+ pdu->version = AGENTX_VERSION_1;
+ pdu->flags |= UCD_MSG_FLAG_ALWAYS_IN_VIEW;
+
+ /* Master agent is alive, no need to ping */
+ if (session->securityModel != SNMP_DEFAULT_SECMODEL) {
+ snmp_alarm_reset(session->securityModel);
+ }
+
+ if (pdu->command == AGENTX_MSG_GET
+ || pdu->command == AGENTX_MSG_GETNEXT
+ || pdu->command == AGENTX_MSG_GETBULK) {
+ smagic =
+ (ns_subagent_magic *) calloc(1, sizeof(ns_subagent_magic));
+ if (smagic == NULL) {
+ DEBUGMSGTL(("agentx/subagent", "couldn't malloc() smagic\n"));
+ /* would like to send_agentx_error(), but it needs memory too */
+ return 1;
+ }
+ smagic->original_command = pdu->command;
+ smagic->session = session;
+ smagic->ovars = NULL;
+ retmagic = (void *) smagic;
+ }
+
+ switch (pdu->command) {
+ case AGENTX_MSG_GET:
+ DEBUGMSGTL(("agentx/subagent", " -> get\n"));
+ pdu->command = SNMP_MSG_GET;
+ mycallback = handle_subagent_response;
+ break;
+
+ case AGENTX_MSG_GETNEXT:
+ DEBUGMSGTL(("agentx/subagent", " -> getnext\n"));
+ pdu->command = SNMP_MSG_GETNEXT;
+
+ /*
+ * We have to save a copy of the original variable list here because
+ * if the master agent has requested scoping for some of the varbinds
+ * that information is stored there.
+ */
+
+ smagic->ovars = snmp_clone_varbind(pdu->variables);
+ DEBUGMSGTL(("agentx/subagent", "saved variables\n"));
+ mycallback = handle_subagent_response;
+ break;
+
+ case AGENTX_MSG_GETBULK:
+ /*
+ * WWWXXX
+ */
+ DEBUGMSGTL(("agentx/subagent", " -> getbulk\n"));
+ pdu->command = SNMP_MSG_GETBULK;
+
+ /*
+ * We have to save a copy of the original variable list here because
+ * if the master agent has requested scoping for some of the varbinds
+ * that information is stored there.
+ */
+
+ smagic->ovars = snmp_clone_varbind(pdu->variables);
+ DEBUGMSGTL(("agentx/subagent", "saved variables at %p\n",
+ smagic->ovars));
+ mycallback = handle_subagent_response;
+ break;
+
+ case AGENTX_MSG_RESPONSE:
+ SNMP_FREE(smagic);
+ DEBUGMSGTL(("agentx/subagent", " -> response\n"));
+ return 1;
+
+#ifndef NETSNMP_NO_WRITE_SUPPORT
+ case AGENTX_MSG_TESTSET:
+ /*
+ * XXXWWW we have to map this twice to both RESERVE1 and RESERVE2
+ */
+ DEBUGMSGTL(("agentx/subagent", " -> testset\n"));
+ asi = save_set_vars(session, pdu);
+ if (asi == NULL) {
+ SNMP_FREE(smagic);
+ snmp_log(LOG_WARNING, "save_set_vars() failed\n");
+ send_agentx_error(session, pdu, AGENTX_ERR_PARSE_FAILED, 0);
+ return 1;
+ }
+ asi->mode = pdu->command = SNMP_MSG_INTERNAL_SET_RESERVE1;
+ mycallback = handle_subagent_set_response;
+ retmagic = asi;
+ break;
+
+ case AGENTX_MSG_COMMITSET:
+ DEBUGMSGTL(("agentx/subagent", " -> commitset\n"));
+ asi = restore_set_vars(session, pdu);
+ if (asi == NULL) {
+ SNMP_FREE(smagic);
+ snmp_log(LOG_WARNING, "restore_set_vars() failed\n");
+ send_agentx_error(session, pdu, AGENTX_ERR_PROCESSING_ERROR, 0);
+ return 1;
+ }
+ if (asi->mode != SNMP_MSG_INTERNAL_SET_RESERVE2) {
+ SNMP_FREE(smagic);
+ snmp_log(LOG_WARNING,
+ "dropping bad AgentX request (wrong mode %d)\n",
+ asi->mode);
+ send_agentx_error(session, pdu, AGENTX_ERR_PROCESSING_ERROR, 0);
+ return 1;
+ }
+ asi->mode = pdu->command = SNMP_MSG_INTERNAL_SET_ACTION;
+ mycallback = handle_subagent_set_response;
+ retmagic = asi;
+ break;
+
+ case AGENTX_MSG_CLEANUPSET:
+ DEBUGMSGTL(("agentx/subagent", " -> cleanupset\n"));
+ asi = restore_set_vars(session, pdu);
+ if (asi == NULL) {
+ SNMP_FREE(smagic);
+ snmp_log(LOG_WARNING, "restore_set_vars() failed\n");
+ send_agentx_error(session, pdu, AGENTX_ERR_PROCESSING_ERROR, 0);
+ return 1;
+ }
+ if (asi->mode == SNMP_MSG_INTERNAL_SET_RESERVE1 ||
+ asi->mode == SNMP_MSG_INTERNAL_SET_RESERVE2) {
+ asi->mode = pdu->command = SNMP_MSG_INTERNAL_SET_FREE;
+ } else if (asi->mode == SNMP_MSG_INTERNAL_SET_ACTION) {
+ asi->mode = pdu->command = SNMP_MSG_INTERNAL_SET_COMMIT;
+ } else {
+ snmp_log(LOG_WARNING,
+ "dropping bad AgentX request (wrong mode %d)\n",
+ asi->mode);
+ SNMP_FREE(retmagic);
+ return 1;
+ }
+ mycallback = handle_subagent_set_response;
+ retmagic = asi;
+ break;
+
+ case AGENTX_MSG_UNDOSET:
+ DEBUGMSGTL(("agentx/subagent", " -> undoset\n"));
+ asi = restore_set_vars(session, pdu);
+ if (asi == NULL) {
+ SNMP_FREE(smagic);
+ snmp_log(LOG_WARNING, "restore_set_vars() failed\n");
+ send_agentx_error(session, pdu, AGENTX_ERR_PROCESSING_ERROR, 0);
+ return 1;
+ }
+ asi->mode = pdu->command = SNMP_MSG_INTERNAL_SET_UNDO;
+ mycallback = handle_subagent_set_response;
+ retmagic = asi;
+ break;
+#endif /* !NETSNMP_NO_WRITE_SUPPORT */
+
+ default:
+ SNMP_FREE(smagic);
+ DEBUGMSGTL(("agentx/subagent", " -> unknown command %d (%02x)\n",
+ pdu->command, pdu->command));
+ return 0;
+ }
+
+ /*
+ * submit the pdu to the internal handler
+ */
+
+ /*
+ * We have to clone the PDU here, because when we return from this
+ * callback, sess_process_packet will free(pdu), but this call also
+ * free()s its argument PDU.
+ */
+
+ internal_pdu = snmp_clone_pdu(pdu);
+ internal_pdu->contextName = (char *) internal_pdu->community;
+ internal_pdu->contextNameLen = internal_pdu->community_len;
+ internal_pdu->community = NULL;
+ internal_pdu->community_len = 0;
+ result = snmp_async_send(agentx_callback_sess, internal_pdu, mycallback,
+ retmagic);
+ if (result == 0) {
+ snmp_free_pdu( internal_pdu );
+ }
+ return 1;
+}
+
+static int
+_invalid_op_and_magic(int op, ns_subagent_magic *smagic)
+{
+ int invalid = 0;
+
+ if (smagic && (snmp_sess_pointer(smagic->session) == NULL ||
+ op == NETSNMP_CALLBACK_OP_TIMED_OUT)) {
+ if (smagic->ovars != NULL) {
+ snmp_free_varbind(smagic->ovars);
+ }
+ free(smagic);
+ invalid = 1;
+ }
+
+ if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE || smagic == NULL)
+ invalid = 1;
+
+ return invalid;
+}
+
+int
+handle_subagent_response(int op, netsnmp_session * session, int reqid,
+ netsnmp_pdu *pdu, void *magic)
+{
+ ns_subagent_magic *smagic = (ns_subagent_magic *) magic;
+ netsnmp_variable_list *u = NULL, *v = NULL;
+ int rc = 0;
+
+ if (_invalid_op_and_magic(op, magic)) {
+ return 1;
+ }
+
+ pdu = snmp_clone_pdu(pdu);
+ DEBUGMSGTL(("agentx/subagent",
+ "handling AgentX response (cmd 0x%02x orig_cmd 0x%02x)"
+ " (req=0x%x,trans=0x%x,sess=0x%x)\n",
+ pdu->command, smagic->original_command,
+ (unsigned)pdu->reqid, (unsigned)pdu->transid,
+ (unsigned)pdu->sessid));
+
+#ifndef NETSNMP_NO_WRITE_SUPPORT
+ if (pdu->command == SNMP_MSG_INTERNAL_SET_FREE ||
+ pdu->command == SNMP_MSG_INTERNAL_SET_UNDO ||
+ pdu->command == SNMP_MSG_INTERNAL_SET_COMMIT) {
+ free_set_vars(smagic->session, pdu);
+ }
+#endif /* !NETSNMP_NO_WRITE_SUPPORT */
+
+ if (smagic->original_command == AGENTX_MSG_GETNEXT) {
+ DEBUGMSGTL(("agentx/subagent",
+ "do getNext scope processing %p %p\n", smagic->ovars,
+ pdu->variables));
+ for (u = smagic->ovars, v = pdu->variables; u != NULL && v != NULL;
+ u = u->next_variable, v = v->next_variable) {
+ if (snmp_oid_compare
+ (u->val.objid, u->val_len / sizeof(oid), nullOid,
+ nullOidLen/sizeof(oid)) != 0) {
+ /*
+ * The master agent requested scoping for this variable.
+ */
+ rc = snmp_oid_compare(v->name, v->name_length,
+ u->val.objid,
+ u->val_len / sizeof(oid));
+ DEBUGMSGTL(("agentx/subagent", "result "));
+ DEBUGMSGOID(("agentx/subagent", v->name, v->name_length));
+ DEBUGMSG(("agentx/subagent", " scope to "));
+ DEBUGMSGOID(("agentx/subagent",
+ u->val.objid, u->val_len / sizeof(oid)));
+ DEBUGMSG(("agentx/subagent", " result %d\n", rc));
+
+ if (rc >= 0) {
+ /*
+ * The varbind is out of scope. From RFC2741, p. 66: "If
+ * the subagent cannot locate an appropriate variable,
+ * v.name is set to the starting OID, and the VarBind is
+ * set to `endOfMibView'".
+ */
+ snmp_set_var_objid(v, u->name, u->name_length);
+ snmp_set_var_typed_value(v, SNMP_ENDOFMIBVIEW, NULL, 0);
+ DEBUGMSGTL(("agentx/subagent",
+ "scope violation -- return endOfMibView\n"));
+ }
+ } else {
+ DEBUGMSGTL(("agentx/subagent", "unscoped var\n"));
+ }
+ }
+ }
+
+ /*
+ * XXXJBPN: similar for GETBULK but the varbinds can get re-ordered I
+ * think which makes it er more difficult.
+ */
+
+ if (smagic->ovars != NULL) {
+ snmp_free_varbind(smagic->ovars);
+ }
+
+ pdu->command = AGENTX_MSG_RESPONSE;
+ pdu->version = smagic->session->version;
+
+ if (!snmp_send(smagic->session, pdu)) {
+ snmp_free_pdu(pdu);
+ }
+ DEBUGMSGTL(("agentx/subagent", " FINISHED\n"));
+ free(smagic);
+ return 1;
+}
+
+#ifndef NETSNMP_NO_WRITE_SUPPORT
+int
+handle_subagent_set_response(int op, netsnmp_session * session, int reqid,
+ netsnmp_pdu *pdu, void *magic)
+{
+ netsnmp_session *retsess;
+ struct agent_netsnmp_set_info *asi;
+ int result;
+
+ if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE || magic == NULL) {
+ return 1;
+ }
+
+ DEBUGMSGTL(("agentx/subagent",
+ "handling agentx subagent set response (mode=%d,req=0x%x,"
+ "trans=0x%x,sess=0x%x)\n",
+ (unsigned)pdu->command, (unsigned)pdu->reqid,
+ (unsigned)pdu->transid, (unsigned)pdu->sessid));
+ pdu = snmp_clone_pdu(pdu);
+
+ asi = (struct agent_netsnmp_set_info *) magic;
+ retsess = asi->sess;
+ asi->errstat = pdu->errstat;
+
+ if (asi->mode == SNMP_MSG_INTERNAL_SET_RESERVE1) {
+ /*
+ * reloop for RESERVE2 mode, an internal only agent mode
+ */
+ /*
+ * XXX: check exception statuses of reserve1 first
+ */
+ if (!pdu->errstat) {
+ asi->mode = pdu->command = SNMP_MSG_INTERNAL_SET_RESERVE2;
+ result = snmp_async_send(agentx_callback_sess, pdu,
+ handle_subagent_set_response, asi);
+ if (result == 0) {
+ snmp_free_pdu( pdu );
+ }
+ DEBUGMSGTL(("agentx/subagent",
+ " going from RESERVE1 -> RESERVE2\n"));
+ return 1;
+ }
+ } else {
+ if (asi->mode == SNMP_MSG_INTERNAL_SET_FREE ||
+ asi->mode == SNMP_MSG_INTERNAL_SET_UNDO ||
+ asi->mode == SNMP_MSG_INTERNAL_SET_COMMIT) {
+ free_set_vars(retsess, pdu);
+ }
+ snmp_free_varbind(pdu->variables);
+ pdu->variables = NULL; /* the variables were added by us */
+ }
+
+ netsnmp_assert(retsess != NULL);
+ pdu->command = AGENTX_MSG_RESPONSE;
+ pdu->version = retsess->version;
+
+ if (!snmp_send(retsess, pdu)) {
+ snmp_free_pdu(pdu);
+ }
+ DEBUGMSGTL(("agentx/subagent", " FINISHED\n"));
+ return 1;
+}
+#endif /* !NETSNMP_NO_WRITE_SUPPORT */
+
+
+int
+agentx_registration_callback(int majorID, int minorID, void *serverarg,
+ void *clientarg)
+{
+ struct register_parameters *reg_parms =
+ (struct register_parameters *) serverarg;
+ netsnmp_session *agentx_ss = *(netsnmp_session **)clientarg;
+
+ if (minorID == SNMPD_CALLBACK_REGISTER_OID)
+ return agentx_register(agentx_ss,
+ reg_parms->name, reg_parms->namelen,
+ reg_parms->priority,
+ reg_parms->range_subid,
+ reg_parms->range_ubound, reg_parms->timeout,
+ reg_parms->flags,
+ reg_parms->contextName);
+ else
+ return agentx_unregister(agentx_ss,
+ reg_parms->name, reg_parms->namelen,
+ reg_parms->priority,
+ reg_parms->range_subid,
+ reg_parms->range_ubound,
+ reg_parms->contextName);
+}
+
+
+static int
+agentx_sysOR_callback(int majorID, int minorID, void *serverarg,
+ void *clientarg)
+{
+ const struct register_sysOR_parameters *reg_parms =
+ (const struct register_sysOR_parameters *) serverarg;
+ netsnmp_session *agentx_ss = *(netsnmp_session **)clientarg;
+
+ if (minorID == SNMPD_CALLBACK_REG_SYSOR)
+ return agentx_add_agentcaps(agentx_ss,
+ reg_parms->name, reg_parms->namelen,
+ reg_parms->descr);
+ else
+ return agentx_remove_agentcaps(agentx_ss,
+ reg_parms->name,
+ reg_parms->namelen);
+}
+
+
+static int
+subagent_shutdown(int majorID, int minorID, void *serverarg, void *clientarg)
+{
+ netsnmp_session *thesession = *(netsnmp_session **)clientarg;
+ DEBUGMSGTL(("agentx/subagent", "shutting down session....\n"));
+ if (thesession == NULL) {
+ DEBUGMSGTL(("agentx/subagent", "Empty session to shutdown\n"));
+ main_session = NULL;
+ return 0;
+ }
+ agentx_close_session(thesession, AGENTX_CLOSE_SHUTDOWN);
+ snmp_close(thesession);
+ if (main_session != NULL) {
+ remove_trap_session(main_session);
+ main_session = NULL;
+ }
+ DEBUGMSGTL(("agentx/subagent", "shut down finished.\n"));
+
+ subagent_init_init = 0;
+ return 1;
+}
+
+
+
+/*
+ * Register all the "standard" AgentX callbacks for the given session.
+ */
+
+void
+agentx_register_callbacks(netsnmp_session * s)
+{
+ netsnmp_session *sess_p;
+
+ DEBUGMSGTL(("agentx/subagent",
+ "registering callbacks for session %p\n", s));
+ memdup((u_char **)&sess_p, &s, sizeof(s));
+ netsnmp_assert(sess_p);
+ s->myvoid = sess_p;
+ if (!sess_p)
+ return;
+ snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN,
+ subagent_shutdown, sess_p);
+ snmp_register_callback(SNMP_CALLBACK_APPLICATION,
+ SNMPD_CALLBACK_REGISTER_OID,
+ agentx_registration_callback, sess_p);
+ snmp_register_callback(SNMP_CALLBACK_APPLICATION,
+ SNMPD_CALLBACK_UNREGISTER_OID,
+ agentx_registration_callback, sess_p);
+ snmp_register_callback(SNMP_CALLBACK_APPLICATION,
+ SNMPD_CALLBACK_REG_SYSOR,
+ agentx_sysOR_callback, sess_p);
+ snmp_register_callback(SNMP_CALLBACK_APPLICATION,
+ SNMPD_CALLBACK_UNREG_SYSOR,
+ agentx_sysOR_callback, sess_p);
+}
+
+/*
+ * Unregister all the callbacks associated with this session.
+ */
+
+void
+agentx_unregister_callbacks(netsnmp_session * ss)
+{
+ DEBUGMSGTL(("agentx/subagent",
+ "unregistering callbacks for session %p\n", ss));
+ snmp_unregister_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN,
+ subagent_shutdown, ss->myvoid, 1);
+ snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
+ SNMPD_CALLBACK_REGISTER_OID,
+ agentx_registration_callback, ss->myvoid, 1);
+ snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
+ SNMPD_CALLBACK_UNREGISTER_OID,
+ agentx_registration_callback, ss->myvoid, 1);
+ snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
+ SNMPD_CALLBACK_REG_SYSOR,
+ agentx_sysOR_callback, ss->myvoid, 1);
+ snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
+ SNMPD_CALLBACK_UNREG_SYSOR,
+ agentx_sysOR_callback, ss->myvoid, 1);
+ SNMP_FREE(ss->myvoid);
+}
+
+/*
+ * Open a session to the master agent.
+ */
+int
+subagent_open_master_session(void)
+{
+ netsnmp_transport *t;
+ netsnmp_session sess;
+ const char *agentx_socket;
+
+ DEBUGMSGTL(("agentx/subagent", "opening session...\n"));
+
+ if (main_session) {
+ snmp_log(LOG_WARNING,
+ "AgentX session to master agent attempted to be re-opened.\n");
+ return -1;
+ }
+
+ snmp_sess_init(&sess);
+ sess.version = AGENTX_VERSION_1;
+ sess.retries = SNMP_DEFAULT_RETRIES;
+ sess.timeout = SNMP_DEFAULT_TIMEOUT;
+ sess.flags |= SNMP_FLAGS_STREAM_SOCKET;
+ sess.callback = handle_agentx_packet;
+ sess.authenticator = NULL;
+
+ agentx_socket = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_X_SOCKET);
+ t = netsnmp_transport_open_client("agentx", agentx_socket);
+ if (t == NULL) {
+ /*
+ * Diagnose snmp_open errors with the input
+ * netsnmp_session pointer.
+ */
+ if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_NO_CONNECTION_WARNINGS)) {
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "Warning: "
+ "Failed to connect to the agentx master agent (%s)",
+ agentx_socket ? agentx_socket : "[NIL]");
+ if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) {
+ netsnmp_sess_log_error(LOG_WARNING, buf, &sess);
+ } else {
+ snmp_sess_perror(buf, &sess);
+ }
+ }
+ return -1;
+ }
+
+ main_session =
+ snmp_add_full(&sess, t, NULL, agentx_parse, NULL, NULL,
+ agentx_realloc_build, agentx_check_packet, NULL);
+
+ if (main_session == NULL) {
+ if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_NO_CONNECTION_WARNINGS)) {
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "Error: "
+ "Failed to create the agentx master agent session (%s)",
+ agentx_socket);
+ snmp_sess_perror(buf, &sess);
+ }
+ netsnmp_transport_free(t);
+ return -1;
+ }
+
+ /*
+ * I don't know why 1 is success instead of the usual 0 = noerr,
+ * but that's what the function returns.
+ */
+ if (1 != agentx_open_session(main_session)) {
+ snmp_close(main_session);
+ main_session = NULL;
+ return -1;
+ }
+
+ /*
+ * subagent_register_ping_alarm assumes that securityModel will
+ * be set to SNMP_DEFAULT_SECMODEL on new AgentX sessions.
+ * This field is then (ab)used to hold the alarm stash.
+ *
+ * Why is the securityModel field used for this purpose, I hear you ask.
+ * Damn good question! (See SVN revision 4886)
+ */
+ main_session->securityModel = SNMP_DEFAULT_SECMODEL;
+
+ if (add_trap_session(main_session, AGENTX_MSG_NOTIFY, 1,
+ AGENTX_VERSION_1)) {
+ DEBUGMSGTL(("agentx/subagent", " trap session registered OK\n"));
+ } else {
+ DEBUGMSGTL(("agentx/subagent",
+ "trap session registration failed\n"));
+ snmp_close(main_session);
+ main_session = NULL;
+ return -1;
+ }
+
+ agentx_register_callbacks(main_session);
+
+ snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
+ SNMPD_CALLBACK_INDEX_START, (void *) main_session);
+
+ snmp_log(LOG_INFO, "NET-SNMP version %s AgentX subagent connected\n",
+ netsnmp_get_version());
+ DEBUGMSGTL(("agentx/subagent", "opening session... DONE (%p)\n",
+ main_session));
+
+ return 0;
+}
+
+static void
+agentx_reopen_sysORTable(const struct sysORTable* data, void* v)
+{
+ netsnmp_session *agentx_ss = (netsnmp_session *) v;
+
+ agentx_add_agentcaps(agentx_ss, data->OR_oid, data->OR_oidlen,
+ data->OR_descr);
+}
+
+/*
+ * Alarm callback function to open a session to the master agent. If a
+ * transport disconnection callback occurs, indicating that the master agent
+ * has died (or there has been some strange communication problem), this
+ * alarm is called repeatedly to try to re-open the connection.
+ */
+
+void
+agentx_reopen_session(unsigned int clientreg, void *clientarg)
+{
+ DEBUGMSGTL(("agentx/subagent", "agentx_reopen_session(%d) called\n",
+ clientreg));
+
+ if (subagent_open_master_session() == 0) {
+ /*
+ * Successful. Delete the alarm handle if one exists.
+ */
+ if (clientreg != 0) {
+ snmp_alarm_unregister(clientreg);
+ }
+
+ /*
+ * Reregister all our nodes.
+ */
+ register_mib_reattach();
+
+ /*
+ * Reregister all our sysOREntries
+ */
+ netsnmp_sysORTable_foreach(&agentx_reopen_sysORTable, main_session);
+
+ /*
+ * Register a ping alarm (if need be).
+ */
+ subagent_register_ping_alarm(0, 0, NULL, main_session);
+ } else {
+ if (clientreg == 0) {
+ /*
+ * Register a reattach alarm for later
+ */
+ subagent_register_ping_alarm(0, 0, NULL, main_session);
+ }
+ }
+}
+
+/*
+ * If a valid session is passed in (through clientarg), register a
+ * ping handler to ping it frequently, else register an attempt to try
+ * and open it again later.
+ */
+
+static int
+subagent_register_ping_alarm(int majorID, int minorID,
+ void *serverarg, void *clientarg)
+{
+
+ netsnmp_session *ss = (netsnmp_session *) clientarg;
+ int ping_interval =
+ netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_AGENTX_PING_INTERVAL);
+
+ if (!ping_interval) /* don't do anything if not setup properly */
+ return 0;
+
+ /*
+ * register a ping alarm, if desired
+ */
+ if (ss) {
+ if (ss->securityModel != SNMP_DEFAULT_SECMODEL) {
+ DEBUGMSGTL(("agentx/subagent",
+ "unregister existing alarm %d\n",
+ ss->securityModel));
+ snmp_alarm_unregister(ss->securityModel);
+ }
+
+ DEBUGMSGTL(("agentx/subagent",
+ "register ping alarm every %d seconds\n",
+ ping_interval));
+ /*
+ * we re-use the securityModel parameter for an alarm stash,
+ * since agentx doesn't need it
+ */
+ ss->securityModel = snmp_alarm_register(ping_interval, SA_REPEAT,
+ agentx_check_session, ss);
+ } else {
+ /*
+ * attempt to open it later instead
+ */
+ DEBUGMSGTL(("agentx/subagent",
+ "subagent not properly attached, postponing registration till later....\n"));
+ snmp_alarm_register(ping_interval, SA_REPEAT,
+ agentx_reopen_session, NULL);
+ }
+ return 0;
+}
+
+/*
+ * check a session validity for connectivity to the master agent. If
+ * not functioning, close and start attempts to reopen the session
+ */
+void
+agentx_check_session(unsigned int clientreg, void *clientarg)
+{
+ netsnmp_session *ss = (netsnmp_session *) clientarg;
+ if (!ss) {
+ if (clientreg)
+ snmp_alarm_unregister(clientreg);
+ return;
+ }
+ DEBUGMSGTL(("agentx/subagent", "checking status of session %p\n", ss));
+
+ if (!agentx_send_ping(ss)) {
+ snmp_log(LOG_WARNING,
+ "AgentX master agent failed to respond to ping. Attempting to re-register.\n");
+ /*
+ * master agent disappeared? Try and re-register.
+ * close first, just to be sure .
+ */
+ agentx_unregister_callbacks(ss);
+ agentx_close_session(ss, AGENTX_CLOSE_TIMEOUT);
+ snmp_alarm_unregister(clientreg); /* delete ping alarm timer */
+ snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
+ SNMPD_CALLBACK_INDEX_STOP, (void *) ss);
+ register_mib_detach();
+ if (main_session != NULL) {
+ remove_trap_session(ss);
+ snmp_close(main_session);
+ /*
+ * We need to remove the callbacks attached to the callback
+ * session because they have a magic callback data structure
+ * which includes a pointer to the main session
+ * (which is no longer valid).
+ *
+ * Given that the main session is not responsive anyway.
+ * it shoudn't matter if we lose some outstanding requests.
+ */
+ if (agentx_callback_sess != NULL ) {
+ snmp_close(agentx_callback_sess);
+ agentx_callback_sess = NULL;
+
+ subagent_init_callback_session();
+ }
+ main_session = NULL;
+ agentx_reopen_session(0, NULL);
+ }
+ else {
+ snmp_close(main_session);
+ main_session = NULL;
+ }
+ } else {
+ DEBUGMSGTL(("agentx/subagent", "session %p responded to ping\n",
+ ss));
+ }
+}
+
+
+#endif /* USING_AGENTX_SUBAGENT_MODULE */