blob: d36fe2d336c4318c1bb37a14647e2d6a35ddae96 [file] [log] [blame]
/*
* AgentX utility routines
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-features.h>
#include <stdio.h>
#include <errno.h>
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/types.h>
#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_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/agent/agent_index.h>
#include "agentx/protocol.h"
#include "agentx/client.h"
#include "agentx/subagent.h"
netsnmp_feature_require(set_agent_uptime)
/*
* AgentX handling utility routines
*
* Mostly wrappers round, or re-writes of
* the SNMP equivalents
*/
int
agentx_synch_input(int op,
netsnmp_session * session,
int reqid, netsnmp_pdu *pdu, void *magic)
{
struct synch_state *state = (struct synch_state *) magic;
if (!state || reqid != state->reqid) {
return handle_agentx_packet(op, session, reqid, pdu, magic);
}
DEBUGMSGTL(("agentx/subagent", "synching input, op 0x%02x\n", op));
state->waiting = 0;
if (op == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
if (pdu->command == AGENTX_MSG_RESPONSE) {
state->pdu = snmp_clone_pdu(pdu);
state->status = STAT_SUCCESS;
session->s_snmp_errno = SNMPERR_SUCCESS;
/*
* Synchronise sysUpTime with the master agent
*/
netsnmp_set_agent_uptime(pdu->time);
}
} else if (op == NETSNMP_CALLBACK_OP_TIMED_OUT) {
state->pdu = NULL;
state->status = STAT_TIMEOUT;
session->s_snmp_errno = SNMPERR_TIMEOUT;
} else if (op == NETSNMP_CALLBACK_OP_DISCONNECT) {
return handle_agentx_packet(op, session, reqid, pdu, magic);
}
return 1;
}
int
agentx_synch_response(netsnmp_session * ss, netsnmp_pdu *pdu,
netsnmp_pdu **response)
{
return snmp_synch_response_cb(ss, pdu, response, agentx_synch_input);
}
/*
* AgentX PofE convenience functions
*/
int
agentx_open_session(netsnmp_session * ss)
{
netsnmp_pdu *pdu, *response;
extern oid version_sysoid[];
extern int version_sysoid_len;
u_long timeout;
DEBUGMSGTL(("agentx/subagent", "opening session \n"));
if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) {
return 0;
}
pdu = snmp_pdu_create(AGENTX_MSG_OPEN);
if (pdu == NULL)
return 0;
timeout = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_AGENT_AGENTX_TIMEOUT);
if (timeout < 0)
pdu->time = 0;
else
/* for master TIMEOUT is usec, but Agentx Open specifies sec */
pdu->time = timeout/ONE_SEC;
snmp_add_var(pdu, version_sysoid, version_sysoid_len,
's', "Net-SNMP AgentX sub-agent");
if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS)
return 0;
if (!response)
return 0;
if (response->errstat != SNMP_ERR_NOERROR) {
snmp_free_pdu(response);
return 0;
}
ss->sessid = response->sessid;
snmp_free_pdu(response);
DEBUGMSGTL(("agentx/subagent", "open \n"));
return 1;
}
int
agentx_close_session(netsnmp_session * ss, int why)
{
netsnmp_pdu *pdu, *response;
DEBUGMSGTL(("agentx/subagent", "closing session\n"));
if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) {
return 0;
}
pdu = snmp_pdu_create(AGENTX_MSG_CLOSE);
if (pdu == NULL)
return 0;
pdu->time = 0;
pdu->errstat = why;
pdu->sessid = ss->sessid;
if (agentx_synch_response(ss, pdu, &response) == STAT_SUCCESS)
snmp_free_pdu(response);
DEBUGMSGTL(("agentx/subagent", "closed\n"));
return 1;
}
int
agentx_register(netsnmp_session * ss, oid start[], size_t startlen,
int priority, int range_subid, oid range_ubound,
int timeout, u_char flags, const char *contextName)
{
netsnmp_pdu *pdu, *response;
DEBUGMSGTL(("agentx/subagent", "registering: "));
DEBUGMSGOIDRANGE(("agentx/subagent", start, startlen, range_subid,
range_ubound));
DEBUGMSG(("agentx/subagent", "\n"));
if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) {
return 0;
}
pdu = snmp_pdu_create(AGENTX_MSG_REGISTER);
if (pdu == NULL) {
return 0;
}
pdu->time = timeout;
pdu->priority = priority;
pdu->sessid = ss->sessid;
pdu->range_subid = range_subid;
if (contextName) {
pdu->flags |= AGENTX_MSG_FLAG_NON_DEFAULT_CONTEXT;
pdu->community = (u_char *) strdup(contextName);
pdu->community_len = strlen(contextName);
}
if (flags & FULLY_QUALIFIED_INSTANCE) {
pdu->flags |= AGENTX_MSG_FLAG_INSTANCE_REGISTER;
}
if (range_subid) {
snmp_pdu_add_variable(pdu, start, startlen, ASN_OBJECT_ID,
(u_char *) start, startlen * sizeof(oid));
pdu->variables->val.objid[range_subid - 1] = range_ubound;
} else {
snmp_add_null_var(pdu, start, startlen);
}
if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) {
DEBUGMSGTL(("agentx/subagent", "registering failed!\n"));
return 0;
}
if (response->errstat != SNMP_ERR_NOERROR) {
snmp_log(LOG_ERR,"registering pdu failed: %ld!\n", response->errstat);
snmp_free_pdu(response);
return 0;
}
snmp_free_pdu(response);
DEBUGMSGTL(("agentx/subagent", "registered\n"));
return 1;
}
int
agentx_unregister(netsnmp_session * ss, oid start[], size_t startlen,
int priority, int range_subid, oid range_ubound,
const char *contextName)
{
netsnmp_pdu *pdu, *response;
if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) {
return 0;
}
DEBUGMSGTL(("agentx/subagent", "unregistering: "));
DEBUGMSGOIDRANGE(("agentx/subagent", start, startlen, range_subid,
range_ubound));
DEBUGMSG(("agentx/subagent", "\n"));
pdu = snmp_pdu_create(AGENTX_MSG_UNREGISTER);
if (pdu == NULL) {
return 0;
}
pdu->time = 0;
pdu->priority = priority;
pdu->sessid = ss->sessid;
pdu->range_subid = range_subid;
if (contextName) {
pdu->flags |= AGENTX_MSG_FLAG_NON_DEFAULT_CONTEXT;
pdu->community = (u_char *) strdup(contextName);
pdu->community_len = strlen(contextName);
}
if (range_subid) {
snmp_pdu_add_variable(pdu, start, startlen, ASN_OBJECT_ID,
(u_char *) start, startlen * sizeof(oid));
pdu->variables->val.objid[range_subid - 1] = range_ubound;
} else {
snmp_add_null_var(pdu, start, startlen);
}
if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS)
return 0;
if (response->errstat != SNMP_ERR_NOERROR) {
snmp_free_pdu(response);
return 0;
}
snmp_free_pdu(response);
DEBUGMSGTL(("agentx/subagent", "unregistered\n"));
return 1;
}
netsnmp_variable_list *
agentx_register_index(netsnmp_session * ss,
netsnmp_variable_list * varbind, int flags)
{
netsnmp_pdu *pdu, *response;
netsnmp_variable_list *varbind2;
if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) {
return NULL;
}
/*
* Make a copy of the index request varbind
* for the AgentX request PDU
* (since the pdu structure will be freed)
*/
varbind2 =
(netsnmp_variable_list *) malloc(sizeof(netsnmp_variable_list));
if (varbind2 == NULL)
return NULL;
if (snmp_clone_var(varbind, varbind2)) {
snmp_free_varbind(varbind2);
return NULL;
}
if (varbind2->val.string == NULL)
varbind2->val.string = varbind2->buf; /* ensure it points somewhere */
pdu = snmp_pdu_create(AGENTX_MSG_INDEX_ALLOCATE);
if (pdu == NULL) {
snmp_free_varbind(varbind2);
return NULL;
}
pdu->time = 0;
pdu->sessid = ss->sessid;
if (flags == ALLOCATE_ANY_INDEX)
pdu->flags |= AGENTX_MSG_FLAG_ANY_INSTANCE;
if (flags == ALLOCATE_NEW_INDEX)
pdu->flags |= AGENTX_MSG_FLAG_NEW_INSTANCE;
/*
* Just send a single index request varbind.
* Although the AgentX protocol supports
* multiple index allocations in a single
* request, the model used in the net-snmp agent
* doesn't currently take advantage of this.
* I believe this is our prerogative - just as
* long as the master side Index request handler
* can cope with multiple index requests.
*/
pdu->variables = varbind2;
if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS)
return NULL;
if (response->errstat != SNMP_ERR_NOERROR) {
snmp_free_pdu(response);
return NULL;
}
/*
* Unlink the (single) response varbind to return
* to the main driving index request routine.
*
* This is a memory leak, as nothing will ever
* release this varbind. If this becomes a problem,
* we'll need to keep a list of these here, and
* free the memory in the "index release" routine.
* But the master side never frees these either (by
* design, since it still needs them), so expecting
* the subagent to is discrimination, pure & simple :-)
*/
varbind2 = response->variables;
response->variables = NULL;
snmp_free_pdu(response);
return varbind2;
}
int
agentx_unregister_index(netsnmp_session * ss,
netsnmp_variable_list * varbind)
{
netsnmp_pdu *pdu, *response;
netsnmp_variable_list *varbind2;
if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) {
return -1;
}
/*
* Make a copy of the index request varbind
* for the AgentX request PDU
* (since the pdu structure will be freed)
*/
varbind2 =
(netsnmp_variable_list *) malloc(sizeof(netsnmp_variable_list));
if (varbind2 == NULL)
return -1;
if (snmp_clone_var(varbind, varbind2)) {
snmp_free_varbind(varbind2);
return -1;
}
pdu = snmp_pdu_create(AGENTX_MSG_INDEX_DEALLOCATE);
if (pdu == NULL) {
snmp_free_varbind(varbind2);
return -1;
}
pdu->time = 0;
pdu->sessid = ss->sessid;
/*
* Just send a single index release varbind.
* (as above)
*/
pdu->variables = varbind2;
if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS)
return -1;
if (response->errstat != SNMP_ERR_NOERROR) {
snmp_free_pdu(response);
return -1; /* XXX - say why */
}
snmp_free_pdu(response);
return SNMP_ERR_NOERROR;
}
int
agentx_add_agentcaps(netsnmp_session * ss,
const oid * agent_cap, size_t agent_caplen,
const char *descr)
{
netsnmp_pdu *pdu, *response;
if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) {
return 0;
}
pdu = snmp_pdu_create(AGENTX_MSG_ADD_AGENT_CAPS);
if (pdu == NULL)
return 0;
pdu->time = 0;
pdu->sessid = ss->sessid;
snmp_add_var(pdu, agent_cap, agent_caplen, 's', descr);
if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS)
return 0;
if (response->errstat != SNMP_ERR_NOERROR) {
snmp_free_pdu(response);
return 0;
}
snmp_free_pdu(response);
return 1;
}
int
agentx_remove_agentcaps(netsnmp_session * ss,
const oid * agent_cap, size_t agent_caplen)
{
netsnmp_pdu *pdu, *response;
if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) {
return 0;
}
pdu = snmp_pdu_create(AGENTX_MSG_REMOVE_AGENT_CAPS);
if (pdu == NULL)
return 0;
pdu->time = 0;
pdu->sessid = ss->sessid;
snmp_add_null_var(pdu, agent_cap, agent_caplen);
if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS)
return 0;
if (response->errstat != SNMP_ERR_NOERROR) {
snmp_free_pdu(response);
return 0;
}
snmp_free_pdu(response);
return 1;
}
int
agentx_send_ping(netsnmp_session * ss)
{
netsnmp_pdu *pdu, *response;
if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) {
return 0;
}
pdu = snmp_pdu_create(AGENTX_MSG_PING);
if (pdu == NULL)
return 0;
pdu->time = 0;
pdu->sessid = ss->sessid;
if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS)
return 0;
if (response->errstat != SNMP_ERR_NOERROR) {
snmp_free_pdu(response);
return 0;
}
snmp_free_pdu(response);
return 1;
}