blob: 1c00db14333390886987aeb84104cbbb88b28ed8 [file] [log] [blame]
/*
* AgentX sub-agent
*/
#include <net-snmp/net-snmp-config.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"
#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);
int handle_subagent_set_response(int op,
netsnmp_session * session,
int reqid, netsnmp_pdu *pdu,
void *magic);
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;
}
void
netsnmp_enable_subagent(void) {
netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE,
SUB_AGENT);
}
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;
}
}
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;
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;
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));
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);
}
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;
}
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;
}
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)
{
DEBUGMSGTL(("agentx/subagent",
"registering callbacks for session %p\n", s));
snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN,
subagent_shutdown, s);
snmp_register_callback(SNMP_CALLBACK_APPLICATION,
SNMPD_CALLBACK_REGISTER_OID,
agentx_registration_callback, s);
snmp_register_callback(SNMP_CALLBACK_APPLICATION,
SNMPD_CALLBACK_UNREGISTER_OID,
agentx_registration_callback, s);
snmp_register_callback(SNMP_CALLBACK_APPLICATION,
SNMPD_CALLBACK_REG_SYSOR,
agentx_sysOR_callback, s);
snmp_register_callback(SNMP_CALLBACK_APPLICATION,
SNMPD_CALLBACK_UNREG_SYSOR,
agentx_sysOR_callback, s);
}
/*
* 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, 1);
snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
SNMPD_CALLBACK_REGISTER_OID,
agentx_registration_callback, ss, 1);
snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
SNMPD_CALLBACK_UNREGISTER_OID,
agentx_registration_callback, ss, 1);
snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
SNMPD_CALLBACK_REG_SYSOR,
agentx_sysOR_callback, ss, 1);
snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
SNMPD_CALLBACK_UNREG_SYSOR,
agentx_sysOR_callback, ss, 1);
}
/*
* 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 */