blob: 6c6e75c736e1b1334004162217288a117a491428 [file] [log] [blame]
/*
* AgentX Administrative request handling
*/
#include "config.h"
#include <sys/types.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#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_NETINET_IN_H
#include <netinet/in.h>
#endif
#include "asn1.h"
#include "snmp_api.h"
#include "snmp_client.h"
#include "snmp_impl.h"
#include "snmp.h"
#include "system.h"
#include "agentx/protocol.h"
#include "agentx/client.h"
#include "snmp_agent.h"
#include "snmp_vars.h"
#include "var_struct.h"
#include "agent_registry.h"
#include "agent_trap.h"
#include "mibII/sysORTable.h"
#include "snmp_debug.h"
extern struct variable2 agentx_varlist[];
extern struct timeval starttime;
struct snmp_session *
find_agentx_session( struct snmp_session *session, int sessid)
{
struct snmp_session *sp;
for ( sp = session->subsession ; sp != NULL ; sp = sp->next ) {
if ( sp->sessid == sessid )
return sp;
}
return NULL;
}
int
open_agentx_session(struct snmp_session *session, struct snmp_pdu *pdu)
{
struct snmp_session *sp;
struct timeval now;
sp = (struct snmp_session *)malloc( sizeof( struct snmp_session ));
if ( sp == NULL ) {
session->s_snmp_errno = AGENTX_ERR_OPEN_FAILED;
return -1;
}
memcpy( sp, session, sizeof(struct snmp_session));
sp->sessid = snmp_get_next_sessid();
sp->version = pdu->version;
sp->timeout = pdu->time;
/*
* This next bit utilises unused SNMPv3 fields
* to store the subagent OID and description.
* This really ought to use AgentX-specific fields,
* but it hardly seems worth it for a one-off use.
*
* But I'm willing to be persuaded otherwise....
*/
sp->securityAuthProto =
snmp_duplicate_objid(pdu->variables->name, pdu->variables->name_length);
sp->securityAuthProtoLen = pdu->variables->name_length;
sp->securityName = strdup( pdu->variables->val.string );
gettimeofday(&now, NULL);
sp->engineTime = calculate_time_diff( &now, &starttime );
sp->subsession = session; /* link back to head */
sp->flags |= SNMP_FLAGS_SUBSESSION;
sp->next = session->subsession;
session->subsession = sp;
return sp->sessid;
}
int
close_agentx_session(struct snmp_session *session, int sessid)
{
struct snmp_session *sp, *prev = NULL;
for ( sp = session->subsession ; sp != NULL ; prev = sp, sp = sp->next ) {
if ( sp->sessid == sessid ) {
unregister_mibs_by_session( sp );
unregister_index_by_session( sp );
unregister_sysORTable_by_session( sp );
if ( prev )
prev->next = sp->next;
else
session->subsession = sp->next;
free( sp );
return AGENTX_ERR_NOERROR;
}
}
return AGENTX_ERR_NOT_OPEN;
}
int
register_agentx_list(struct snmp_session *session, struct snmp_pdu *pdu)
{
struct snmp_session *sp;
char buf[32];
oid ubound = 0;
DEBUGMSGTL(("agentx:register","in register_agentx_list\n"));
sp = find_agentx_session( session, pdu->sessid );
if ( sp == NULL )
return AGENTX_ERR_NOT_OPEN;
sprintf(buf, "AgentX subagent %ld", sp->sessid );
/*
* TODO: registration timeout
* registration context
*/
if ( pdu->range_subid )
ubound = pdu->variables->val.objid[ pdu->range_subid-1 ];
switch (register_mib_range(buf, (struct variable *)agentx_varlist,
sizeof(agentx_varlist[0]), 1,
pdu->variables->name, pdu->variables->name_length,
pdu->priority, pdu->range_subid, ubound, sp)) {
case MIB_REGISTERED_OK:
DEBUGMSGTL(("agentx:register",
"registered ok\n"));
return AGENTX_ERR_NOERROR;
case MIB_DUPLICATE_REGISTRATION:
DEBUGMSGTL(("agentx:register",
"duplicate registration\n"));
return AGENTX_ERR_DUPLICATE_REGISTRATION;
case MIB_REGISTRATION_FAILED:
default:
DEBUGMSGTL(("agentx:register",
"failed registration\n"));
return AGENTX_ERR_REQUEST_DENIED;
}
}
int
unregister_agentx_list(struct snmp_session *session, struct snmp_pdu *pdu)
{
struct snmp_session *sp;
oid ubound = 0;
sp = find_agentx_session( session, pdu->sessid );
if ( sp == NULL )
return AGENTX_ERR_NOT_OPEN;
switch (unregister_mib_range(pdu->variables->name,
pdu->variables->name_length,
pdu->priority, pdu->range_subid, ubound)) {
case MIB_UNREGISTERED_OK:
return AGENTX_ERR_NOERROR;
case MIB_NO_SUCH_REGISTRATION:
return AGENTX_ERR_UNKNOWN_REGISTRATION;
case MIB_UNREGISTRATION_FAILED:
default:
return AGENTX_ERR_REQUEST_DENIED;
}
}
int
allocate_idx_list(struct snmp_session *session, struct snmp_pdu *pdu)
{
struct snmp_session *sp;
struct variable_list *vp, *vp2, *next, *res;
int flags = 0;
sp = find_agentx_session( session, pdu->sessid );
if ( sp == NULL )
return AGENTX_ERR_NOT_OPEN;
if ( pdu->flags & AGENTX_MSG_FLAG_ANY_INSTANCE )
flags |= ALLOCATE_ANY_INDEX;
if ( pdu->flags & AGENTX_MSG_FLAG_NEW_INSTANCE )
flags |= ALLOCATE_NEW_INDEX;
/*
* XXX - what about errors?
*
* If any allocations fail, then we need to
* *fully* release the earlier ones.
* (i.e. remove them completely from the index registry,
* not simply mark them as available for re-use)
*
* For now - assume they all succeed.
*/
for ( vp = pdu->variables ; vp != NULL; vp = next ) {
next = vp->next_variable;
res = register_index( vp, flags, session );
if ( res == NULL ) {
/*
* If any allocations fail, we need to *fully* release
* all previous ones (i.e. remove them completely
* from the index registry)
*/
for ( vp2 = pdu->variables ; vp2 != vp ; vp2=vp2->next_variable )
remove_index( vp2, session );
return AGENTX_ERR_INDEX_NONE_AVAILABLE; /* XXX */
}
(void)snmp_clone_var( res, vp );
vp->next_variable = next;
}
return AGENTX_ERR_NOERROR;
}
int
release_idx_list(struct snmp_session *session, struct snmp_pdu *pdu)
{
struct snmp_session *sp;
struct variable_list *vp, *vp2;
int res;
sp = find_agentx_session( session, pdu->sessid );
if ( sp == NULL )
return AGENTX_ERR_NOT_OPEN;
for ( vp = pdu->variables ; vp != NULL; vp = vp->next_variable ) {
res = unregister_index( vp, TRUE, session );
/*
* If any releases fail,
* we need to reinstate all previous ones.
*/
if ( res != SNMP_ERR_NOERROR ) {
for ( vp2 = pdu->variables ; vp2 != vp; vp2 = vp2->next_variable )
(void) register_index( vp2, ALLOCATE_THIS_INDEX, session );
return AGENTX_ERR_INDEX_NOT_ALLOCATED; /* Probably */
}
}
return AGENTX_ERR_NOERROR;
}
int
add_agent_caps_list(struct snmp_session *session, struct snmp_pdu *pdu)
{
struct snmp_session *sp;
sp = find_agentx_session( session, pdu->sessid );
if ( sp == NULL )
return AGENTX_ERR_NOT_OPEN;
register_sysORTable_sess(pdu->variables->name,
pdu->variables->name_length,
(char *)pdu->variables->val.string, sp);
return AGENTX_ERR_NOERROR;
}
int
remove_agent_caps_list(struct snmp_session *session, struct snmp_pdu *pdu)
{
struct snmp_session *sp;
sp = find_agentx_session( session, pdu->sessid );
if ( sp == NULL )
return AGENTX_ERR_NOT_OPEN;
if ( unregister_sysORTable_sess(pdu->variables->name,
pdu->variables->name_length, sp) < 0 )
return AGENTX_ERR_UNKNOWN_AGENTCAPS;
else
return AGENTX_ERR_NOERROR;
}
int
agentx_notify(struct snmp_session *session, struct snmp_pdu *pdu)
{
struct snmp_session *sp;
struct variable_list *var;
int got_sysuptime = 0;
struct timeval now;
extern oid sysuptime_oid[], snmptrap_oid[];
extern size_t sysuptime_oid_len, snmptrap_oid_len;
sp = find_agentx_session( session, pdu->sessid );
if ( sp == NULL )
return AGENTX_ERR_NOT_OPEN;
var = pdu->variables;
if (!var)
return AGENTX_ERR_PROCESSING_ERROR;
if ( snmp_oid_compare( var->name, var->name_length,
sysuptime_oid, sysuptime_oid_len) == 0 ) {
got_sysuptime = 1;
var = var->next_variable;
}
if (!var || snmp_oid_compare( var->name, var->name_length,
snmptrap_oid, snmptrap_oid_len) != 0 )
return AGENTX_ERR_PROCESSING_ERROR;
/*
* If sysUptime isn't the first varbind, don't worry.
* send_trap_vars() will add it if necessary.
*
* Note that if this behaviour is altered, it will
* be necessary to add sysUptime here,
* as this is valid AgentX syntax.
*/
send_trap_vars( -1, -1, pdu->variables );
return AGENTX_ERR_NOERROR;
}
int
agentx_ping_response(struct snmp_session *session, struct snmp_pdu *pdu)
{
struct snmp_session *sp;
sp = find_agentx_session( session, pdu->sessid );
if ( sp == NULL )
return AGENTX_ERR_NOT_OPEN;
else
return AGENTX_ERR_NOERROR;
}
int
handle_master_agentx_packet(int operation,
struct snmp_session *session,
int reqid,
struct snmp_pdu *pdu,
void *magic)
{
struct agent_snmp_session *asp;
struct timeval now;
if ( magic )
asp = (struct agent_snmp_session *)magic;
else
asp = init_agent_snmp_session(session, pdu);
switch (pdu->command) {
case AGENTX_MSG_OPEN:
asp->pdu->sessid = open_agentx_session( session, pdu );
if ( asp->pdu->sessid == -1 )
asp->status = session->s_snmp_errno;
break;
case AGENTX_MSG_CLOSE:
asp->status = close_agentx_session( session, pdu->sessid );
break;
case AGENTX_MSG_REGISTER:
asp->status = register_agentx_list( session, pdu );
break;
case AGENTX_MSG_UNREGISTER:
asp->status = unregister_agentx_list( session, pdu );
break;
case AGENTX_MSG_INDEX_ALLOCATE:
asp->status = allocate_idx_list( session, asp->pdu );
if ( asp->status != AGENTX_ERR_NOERROR) {
snmp_free_pdu( asp->pdu );
asp->pdu = snmp_clone_pdu(pdu);
}
break;
case AGENTX_MSG_INDEX_DEALLOCATE:
asp->status = release_idx_list( session, pdu );
break;
case AGENTX_MSG_ADD_AGENT_CAPS:
asp->status = add_agent_caps_list( session, pdu );
break;
case AGENTX_MSG_REMOVE_AGENT_CAPS:
asp->status = remove_agent_caps_list( session, pdu );
break;
case AGENTX_MSG_NOTIFY:
asp->status = agentx_notify( session, pdu );
break;
case AGENTX_MSG_PING:
asp->status = agentx_ping_response( session, pdu );
break;
/* TODO: Other admin packets */
case AGENTX_MSG_GET:
case AGENTX_MSG_GETNEXT:
case AGENTX_MSG_GETBULK:
case AGENTX_MSG_TESTSET:
case AGENTX_MSG_COMMITSET:
case AGENTX_MSG_UNDOSET:
case AGENTX_MSG_CLEANUPSET:
case AGENTX_MSG_RESPONSE:
/* Shouldn't be handled here */
break;
default:
asp->status = AGENTX_ERR_PARSE_FAILED;
break;
}
if ( asp->outstanding_requests == NULL ) {
gettimeofday(&now, NULL);
asp->pdu->time = calculate_time_diff( &now, &starttime );
asp->pdu->command = AGENTX_MSG_RESPONSE;
asp->pdu->errstat = asp->status;
snmp_send( asp->session, asp->pdu );
asp->pdu = NULL;
free_agent_snmp_session(asp);
}
return 1;
}