blob: bb3369ec8f3179a6ce988b40f1dcf38758cf30f2 [file] [log] [blame]
/*
* agent_registry.c
*
* Maintain a registry of MIB subtrees, together
* with related information regarding mibmodule, sessions, etc
*/
#define IN_SNMP_VARS_C
#include <config.h>
#if STDC_HEADERS
#include <string.h>
#include <stdlib.h>
#else
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#endif
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <fcntl.h>
#include "mibincl.h"
#include "snmpd.h"
#include "mibgroup/struct.h"
#include "mibgroup/mib_module_includes.h"
#ifdef USING_AGENTX_SUBAGENT_MODULE
#include "agentx/subagent.h"
#endif
#define UCD_REG_FLAG_SPLIT_REGISTRATION 0x1
struct subtree *subtrees;
int tree_compare(const struct subtree *ap, const struct subtree *bp)
{
return snmp_oid_compare(ap->name,ap->namelen,bp->name,bp->namelen);
}
int is_parent(oid *name1, int len1, oid *name2)
{
register int len = len1;
if ( name2 == NULL )
return 0; /* Null child - doesn't count */
/*
* Is name1 a strict prefix of name2 ?
*/
while(len-- > 0){
if (*name2++ != *name1++)
return 0; /* No */
}
return 1; /* Yes */
}
/*
* Merge overlapping registration entries,
* such that each subtree list (linked via 'children')
* relates to the same OID range
* This is done by splitting the enclosing entry into three:
* pre-match / match / post-match
* (any of which may in fact be empty)
*
* This is described in the AgentX protocol (RFC 2257 & successors)
* but is a more generally useful approach anyway
*/
void
merge_variables( struct subtree *first, struct subtree *second )
{
int num_variables = 0;
char *cp = (char *)first->variables;
int i;
if ( first->variables == NULL )
return;
/* Divide the 'variables' structure between the two trees */
for ( i = 0 ; i < first->variables_len ; i++ ) {
if ( snmp_oid_compare(((struct variable *)cp)->name,
((struct variable *)cp)->namelen,
second->name + first->namelen,
second->namelen - first->namelen) < 0 ) {
num_variables++;
cp += first->variables_width;
}
else
break;
}
second->variables = (struct variable *)cp;
second->variables_len -= num_variables;
first->variables_len = num_variables;
if ( first->variables_len == 0 )
first->variables = NULL;
if (second->variables_len == 0 )
second->variables = NULL;
}
void
merge_trees( struct subtree *existing, struct subtree *new_tree )
{
struct subtree temp; /* temporary 'post' entry */
struct subtree *post_ptr = &temp; /* tail of 'post' list */
struct subtree *match_ptr = new_tree; /* tail of 'match' list */
struct subtree *new_match, *new_post;
/* Link in the new entry, and the 'post' placeholder */
memset( &temp, 0, sizeof(struct subtree));
temp.next = existing->next;
new_tree->next = &temp;
existing->next = new_tree;
memcpy( temp.name, new_tree->name, new_tree->namelen*sizeof(oid) );
temp.namelen = new_tree->namelen;
temp.name[ (new_tree->namelen)-1 ]++;
/*
* Loop through the list of existing subtrees,
* splitting them and linking the new match/post
* subtrees into the relevant lists
*/
while ( existing ) {
new_match = (struct subtree *)malloc( sizeof( struct subtree ));
if ( new_match == NULL )
break;
new_post = (struct subtree *)malloc( sizeof( struct subtree ));
if ( new_post == NULL ) {
free( new_match );
break;
}
/* Set up the new entries ... */
memcpy( new_match, existing, sizeof(*existing));
memcpy( new_match->name, new_tree->name, new_tree->namelen*sizeof(oid));
new_match->namelen = new_tree->namelen;
new_match->flags |= UCD_REG_FLAG_SPLIT_REGISTRATION;
merge_variables( existing, new_match );
memcpy( new_post, existing, sizeof(*existing));
memcpy( new_post->name, new_tree->name, new_tree->namelen*sizeof(oid) );
new_post->namelen = new_tree->namelen;
new_post->name[ (new_tree->namelen)-1 ]++;
new_post->flags |= UCD_REG_FLAG_SPLIT_REGISTRATION;
merge_variables( new_match, new_post );
/* .... and link them in properly */
post_ptr->children = new_post;
new_post->children = NULL;
new_post->next = post_ptr->next;
match_ptr->children = new_match;
new_match->children = NULL;
new_match->next = post_ptr;
existing->next = match_ptr;
existing = existing->children;
match_ptr = match_ptr->children;
post_ptr = post_ptr ->children;
}
/* Finally, unlink the temporary 'post' entry */
new_tree->next = temp.children;
new_tree->children->next = temp.children;
}
int
load_subtree (struct subtree *new_subtree)
{
struct subtree *next_tree = subtrees;
struct subtree *previous = NULL;
new_subtree->flags &= ~(UCD_REG_FLAG_SPLIT_REGISTRATION);
previous = find_subtree_previous(new_subtree->name, new_subtree->namelen, subtrees);
if (previous) {
if ((snmp_oid_compare(new_subtree->name, new_subtree->namelen,
previous->name, previous->namelen) == 0)
&& (strlen(previous->label) > 0 ))
return -2; /* Duplicate registration */
next_tree = previous->next;
}
else
next_tree = subtrees;
/*
* Three possibilities:
* a) the new registration is a subtree of an existing one
* b) an existing registration is a subtree of the new one
* a) the new registration does not overlap
*/
if ( previous && (previous->namelen < new_subtree->namelen)
&& (is_parent(previous->name, previous->namelen, new_subtree->name))) {
merge_trees( previous, new_subtree );
}
else if ( next_tree && (next_tree->namelen < new_subtree->namelen)
&& (is_parent(new_subtree->name, new_subtree->namelen, next_tree->name))) {
merge_trees( new_subtree, next_tree );
}
else {
if (previous) {
while(previous) {
previous->next = new_subtree;
previous = previous->children;
}
}
else
subtrees = new_subtree;
new_subtree->next = next_tree;
}
return 0;
}
int
register_mib(const char *moduleName,
struct variable *var,
size_t varsize,
size_t numvars,
oid *mibloc,
size_t mibloclen)
{
struct subtree *subtree;
char c_oid[SPRINT_MAX_LEN];
int res;
subtree = (struct subtree *) malloc(sizeof(struct subtree));
if ( subtree == NULL )
return -1;
memset(subtree, 0, sizeof(struct subtree));
sprint_objid(c_oid, mibloc, mibloclen);
DEBUGMSGTL(("register_mib", "registering \"%s\" at %s\n",
moduleName, c_oid));
memcpy(subtree->name, mibloc, mibloclen*sizeof(oid));
memcpy(subtree->label, moduleName, strlen(moduleName)+1);
subtree->namelen = (u_char) mibloclen;
subtree->variables = (struct variable *) malloc(varsize*numvars);
memcpy(subtree->variables, var, numvars*varsize);
subtree->variables_len = numvars;
subtree->variables_width = varsize;
res = load_subtree(subtree);
#ifdef USING_AGENTX_SUBAGENT_MODULE
if ( agent_role == SUB_AGENT )
agentx_register( agentx_session, mibloc, mibloclen );
#endif
return res;
}
int
unload_subtree( oid *name, size_t len, struct subtree *previous)
{
struct subtree *list, *list_prev = NULL; /* loop through children */
struct subtree *prev, *prev_next; /* loop through previous children */
#define LABELSIZE 256
char label[LABELSIZE];
list = previous->next;
if ( snmp_oid_compare( list->name, list->namelen, name, len) != 0 )
return -1;
strcpy( label, list->label ); /* or save registration ID */
while (1) {
prev = previous;
while ( list != NULL ) {
if (!strcmp( list->label, label))
break; /* or check registration ID */
list_prev = list;
list = list->children;
}
if ( list == NULL )
break; /* break from infinite loop */
/* Identifier the successor to use instead of 'list' */
if ( list->children ) {
prev_next = list->children;
list->children->next = list->next;
}
else
prev_next = list->next;
while (prev != NULL) { /* Unlink 'list' from preceding entries */
if ( prev->next == list )
prev->next = prev_next;
prev = prev->children;
}
if (list_prev)
list_prev->children = list->children;
list->children = NULL;
list = free_subtree( list ); /* returns list->next */
previous = previous->next;
}
return 0;
}
int
unregister_mib(oid *name,
size_t len)
{
struct subtree *my_ptr;
int res;
my_ptr = find_subtree( name, len, subtrees );
if ( my_ptr == NULL )
return -1;
res = unload_subtree(name, len, subtrees);
#ifdef USING_AGENTX_SUBAGENT_MODULE
if ( agent_role == SUB_AGENT )
agentx_unregister( agentx_session, name, len );
#endif
return res;
}
struct subtree *
free_subtree(struct subtree *st)
{
struct subtree *ret = NULL;
if (st->variables != NULL)
free(st->variables);
if (st->children != NULL)
free_subtree(st->children);
if (st->next != NULL)
ret = st->next;
free(st);
return ret;
}
/* in_a_view: determines if a given snmp_pdu is allowed to see a
given name/namelen OID pointer
name IN - name of var, OUT - name matched
nameLen IN -number of sub-ids in name, OUT - subid-is in matched name
pi IN - relevant auth info re PDU
cvp IN - relevant auth info re mib module
*/
int
in_a_view(oid *name, /* IN - name of var, OUT - name matched */
size_t *namelen, /* IN -number of sub-ids in name*/
struct snmp_pdu *pdu, /* IN - relevant auth info re PDU */
int type) /* IN - variable type being checked */
{
if (pdu->flags & UCD_MSG_FLAG_ALWAYS_IN_VIEW)
return 1; /* Enable bypassing of view-based access control */
/* check for v1 and counter64s, since snmpv1 doesn't support it */
if (pdu->version == SNMP_VERSION_1 && type == ASN_COUNTER64)
return 0;
switch (pdu->version) {
case SNMP_VERSION_1:
case SNMP_VERSION_2c:
case SNMP_VERSION_3:
#ifdef USING_MIBII_VACM_VARS_MODULE
return vacm_in_view(pdu, name, *namelen);
#else
return 1;
#endif
}
return 0;
}
int
compare_tree(oid *name1,
size_t len1,
oid *name2,
size_t len2)
{
register int len;
/* len = minimum of len1 and len2 */
if (len1 < len2)
len = len1;
else
len = len2;
/* find first non-matching byte */
while(len-- > 0){
if (*name1 < *name2)
return -1;
if (*name2++ < *name1++)
return 1;
}
/* bytes match up to length of shorter string */
if (len1 < len2)
return -1; /* name1 shorter, so it is "less" */
/* name1 matches name2 for length of name2, or they are equal */
return 0;
}
struct subtree *find_subtree_previous(oid *name,
size_t len,
struct subtree *subtree)
{
struct subtree *myptr, *previous = NULL;
if ( subtree )
myptr = subtree;
else
myptr = subtrees; /* look through everything */
for( ; myptr != NULL; previous = myptr, myptr = myptr->next) {
if (snmp_oid_compare(name, len, myptr->name, myptr->namelen) < 0)
return previous;
}
return previous;
}
struct subtree *find_subtree_next(oid *name,
size_t len,
struct subtree *subtree)
{
struct subtree *myptr = NULL;
myptr = find_subtree_previous(name, len, subtree);
if ( myptr != NULL ) {
myptr = myptr->next;
while ( myptr && myptr->variables == NULL )
myptr = myptr->next;
return myptr;
}
else if ( snmp_oid_compare(name, len, subtree->name, subtree->namelen) < 0)
return subtree;
else
return NULL;
}
struct subtree *find_subtree(oid *name,
size_t len,
struct subtree *subtree)
{
struct subtree *myptr;
myptr = find_subtree_previous(name, len, subtree);
if (snmp_oid_compare(name, len, myptr->name, myptr->namelen) == 0)
return myptr;
return NULL;
}
struct snmp_session *get_session_for_oid( oid *name, size_t len)
{
struct subtree *myptr;
myptr = find_subtree_previous(name, len, subtrees);
while ( myptr && myptr->variables == NULL )
myptr = myptr->next;
if ( myptr == NULL )
return NULL;
else
return myptr->session;
}
static struct subtree root_subtrees[] = {
{ { 0 }, 1 }, /* ccitt */
{ { 1 }, 1 }, /* iso */
{ { 2 }, 1 } /* joint-ccitt-iso */
};
struct subtree subtrees_old[] = {
#include "mibgroup/mib_module_loads.h"
};
int subtree_old_size (void) {
return (sizeof(subtrees_old)/ sizeof(struct subtree));
}
void setup_tree (void)
{
extern struct subtree *subtrees,subtrees_old[];
int i;
if ( subtrees == NULL ) {
subtrees = &(root_subtrees[0]);
subtrees->next = &(root_subtrees[1]);
subtrees->next->next = &(root_subtrees[2]);
}
/* Go through the 'static' subtrees (subtrees_old),
and link them into the global subtree structure */
for ( i=0 ; i < subtree_old_size(); i++ )
load_subtree( &(subtrees_old[i]) );
/* No longer necessary to sort the mib tree - this is inherent in
the construction of the subtree structure */
}