mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2025-03-11 17:08:46 +00:00
The BIRD protocol SNMP makes it possible to retrieve management information through SNMP. This is accomplished by implementing AgentX protocol. The BIRD acts as an AgentX subagent, registers to master agent and provides management information. Master agent handles SNMP communication and forwards request to registered subagents. You will therefore need an additional component -- a SNMP daemon capable of acting as AgentX master agent. In theory, the information consumer don't have to support SNMP and could be very simple master agent for logging/monitoring the BIRD state. For more detail see provided documentation. This commit is squashed version of development history. Full development history could be found on branch `proto-snmp'.
878 lines
22 KiB
C
878 lines
22 KiB
C
#include "mib_tree.h"
|
|
#include "snmp_utils.h"
|
|
|
|
#ifdef allocz
|
|
#undef allocz
|
|
#endif
|
|
|
|
#define alloc(size) mb_alloc(p, size)
|
|
#define allocz(size) mb_allocz(p, size)
|
|
#define free(ptr) mb_free(ptr)
|
|
#define realloc(ptr, newsize) mib_mb_realloc(p, ptr, newsize)
|
|
|
|
/*
|
|
* mib_mb_realloc - fix mb_realloc for NULL
|
|
* @p: pool to use for NULL pointers
|
|
* @ptr: old pointer to be reallocated
|
|
* @size: new size of allocated memory block
|
|
*
|
|
* The mb_realloc() does not work with NULL as ptr.
|
|
*/
|
|
static inline void *
|
|
mib_mb_realloc(pool *p, void *ptr, unsigned size)
|
|
{
|
|
if (!ptr)
|
|
return mb_alloc(p, size);
|
|
|
|
return mb_realloc(ptr, size);
|
|
}
|
|
|
|
/**
|
|
* mib_tree_init - Initialize a MIB tree
|
|
* @p: allocation source pool
|
|
* @t: pointer to a tree being initialized
|
|
*
|
|
* By default the standard SNMP internet prefix (.1.3.6.1) is inserted into the
|
|
* tree.
|
|
*/
|
|
void
|
|
mib_tree_init(pool *p, struct mib_tree *t)
|
|
{
|
|
struct mib_node *node = &t->root;
|
|
node->c.id = 0;
|
|
node->c.flags = 0;
|
|
node->children = NULL;
|
|
node->child_len = 0;
|
|
|
|
struct oid *oid = tmp_alloc(
|
|
snmp_oid_size_from_len((uint) ARRAY_SIZE(snmp_internet)));
|
|
oid->n_subid = ARRAY_SIZE(snmp_internet);
|
|
oid->prefix = 0;
|
|
oid->include = 0;
|
|
oid->reserved = 0;
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(snmp_internet); i++)
|
|
oid->ids[i] = snmp_internet[i];
|
|
|
|
(void) mib_tree_add(p, t, oid, 0);
|
|
}
|
|
|
|
/**
|
|
* mib_tree_hint - preallocate child array for given OID
|
|
* @p: memory pool to allocated from
|
|
* @t: MIB tree to hint
|
|
* @oid: MIB subtree root with upto @size children
|
|
* @size: number of children in @oid subtree
|
|
*/
|
|
int
|
|
mib_tree_hint(pool *p, struct mib_tree *t, const struct oid *oid, uint size)
|
|
{
|
|
mib_node_u *node = mib_tree_add(p, t, oid, 0);
|
|
if (!node || mib_node_is_leaf(node))
|
|
return 0;
|
|
|
|
struct mib_node *inner = &node->inner;
|
|
if (inner->child_len >= size + 1)
|
|
return 1;
|
|
|
|
u32 old_len = inner->child_len;
|
|
inner->child_len = size + 1;
|
|
inner->children = realloc(inner->children,
|
|
inner->child_len * sizeof(mib_node_u *));
|
|
|
|
for (u32 i = old_len; i < inner->child_len; i++)
|
|
inner->children[i] = NULL;
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* mib_tree_add - Insert a new node to the tree
|
|
* @p: allocation source pool
|
|
* @t: MIB tree to insert to
|
|
* @oid: identification of inserted node.
|
|
* @is_leaf: flag signaling that inserted OID should be leaf node.
|
|
*
|
|
* Reinsertion only return already valid node pointer, no allocations are done
|
|
* in this case. Return pointer to node in the MIB tree @t or NULL if the
|
|
* requested insertion is invalid. Insertion is invalid if we want to insert
|
|
* node below a leaf or insert a leaf in place taken by normal node.
|
|
*
|
|
*/
|
|
mib_node_u *
|
|
mib_tree_add(pool *p, struct mib_tree *t, const struct oid *oid, int is_leaf)
|
|
{
|
|
// TODO may not function properly in snmp_internet prefix region
|
|
struct mib_walk_state walk;
|
|
mib_node_u *node;
|
|
|
|
/* The empty prefix is associated with the root tree node */
|
|
if (snmp_is_oid_empty(oid) && !is_leaf)
|
|
return (mib_node_u *) &t->root;
|
|
else if (snmp_is_oid_empty(oid))
|
|
return NULL;
|
|
|
|
mib_tree_walk_init(&walk, t);
|
|
node = mib_tree_find(t, &walk, oid);
|
|
ASSERT(walk.id_pos <= oid->n_subid + 1);
|
|
|
|
if (node)
|
|
{
|
|
if (mib_node_is_leaf(node) == is_leaf)
|
|
return node;
|
|
|
|
/* we are trying to insert a leaf node in place of inner node,
|
|
* or vice versa */
|
|
return NULL;
|
|
}
|
|
|
|
ASSERT(walk.id_pos < oid->n_subid + 1);
|
|
|
|
node = walk.stack[walk.stack_pos - 1];
|
|
/* we encounter leaf node before end of OID's id path */
|
|
if (mib_node_is_leaf(node))
|
|
return NULL;
|
|
|
|
struct mib_node *node_inner = &node->inner;
|
|
if (snmp_oid_is_prefixed(oid) &&
|
|
walk.stack_pos <= ARRAY_SIZE(snmp_internet) + 1)
|
|
{
|
|
ASSUME(walk.stack_pos && walk.stack[0] == (mib_node_u *) &t->root);
|
|
|
|
for (u32 id = walk.stack_pos - 1; id < ARRAY_SIZE(snmp_internet); id++)
|
|
{
|
|
if (snmp_internet[id] >= node_inner->child_len)
|
|
{
|
|
u32 old_len = node_inner->child_len;
|
|
node_inner->child_len = snmp_internet[id] + 1;
|
|
node_inner->children = realloc(node_inner->children,
|
|
node_inner->child_len * sizeof(mib_node_u *));
|
|
|
|
for (u32 i = old_len; i < node_inner->child_len; i++)
|
|
node_inner->children[i] = NULL;
|
|
}
|
|
|
|
node = allocz(sizeof(struct mib_node));
|
|
/* assign child into a parent's children array */
|
|
node_inner->children[snmp_internet[id]] = node;
|
|
node_inner = &node->inner;
|
|
node_inner->c.id = snmp_internet[id];
|
|
/* node_inner's fields c.flags, child_len, children defaults to zero or
|
|
* NULL respectively */
|
|
walk.stack[walk.stack_pos++] = node;
|
|
}
|
|
|
|
if (walk.stack_pos == ARRAY_SIZE(snmp_internet) + 1)
|
|
{
|
|
u32 old_len = node_inner->child_len;
|
|
node_inner->child_len = MAX(old_len, (u32) oid->prefix + 1);
|
|
node_inner->children = realloc(node_inner->children,
|
|
node_inner->child_len * sizeof(mib_node_u *));
|
|
|
|
for (u32 i = old_len; i < node_inner->child_len; i++)
|
|
node_inner->children[i] = NULL;
|
|
|
|
if (is_leaf && !oid->n_subid)
|
|
{
|
|
node = allocz(sizeof(struct mib_leaf));
|
|
node->empty.flags = MIB_TREE_LEAF;
|
|
}
|
|
else
|
|
{
|
|
node = allocz(sizeof(struct mib_node));
|
|
node->empty.flags = 0;
|
|
}
|
|
|
|
node->empty.id = oid->prefix;
|
|
/* add node into the parent's children array */
|
|
node_inner->children[oid->prefix] = node;
|
|
node_inner = &node->inner;
|
|
walk.stack[walk.stack_pos++] = node;
|
|
}
|
|
}
|
|
|
|
/* snmp_internet + 2 = empty + snmp_internet + prefix */
|
|
if (snmp_oid_is_prefixed(oid) &&
|
|
walk.stack_pos == ARRAY_SIZE(snmp_internet) + 2 &&
|
|
oid->n_subid == 0 &&
|
|
mib_node_is_leaf(node) == is_leaf)
|
|
return node;
|
|
|
|
if (mib_node_is_leaf(node))
|
|
return node;
|
|
|
|
u8 subids = oid->n_subid;
|
|
u32 old_len = node_inner->child_len;
|
|
u32 child_id = oid->ids[walk.id_pos];
|
|
node_inner->child_len = MAX(old_len, child_id + 1);
|
|
node_inner->children = realloc(node_inner->children,
|
|
node_inner->child_len * sizeof(mib_node_u *));
|
|
|
|
for (u32 i = old_len; i < node_inner->child_len; i++)
|
|
node_inner->children[i] = NULL;
|
|
|
|
struct mib_node *parent;
|
|
/* break to loop before last node in the oid */
|
|
for (; walk.id_pos < subids - 1;)
|
|
{
|
|
parent = node_inner;
|
|
node_inner = allocz(sizeof(struct mib_node));
|
|
|
|
parent->children[child_id] = (mib_node_u *) node_inner;
|
|
node_inner->c.id = child_id;
|
|
|
|
child_id = oid->ids[++walk.id_pos];
|
|
|
|
node_inner->child_len = child_id + 1;
|
|
node_inner->children = allocz(node_inner->child_len * sizeof(mib_node_u *));
|
|
}
|
|
|
|
parent = node_inner;
|
|
mib_node_u *last;
|
|
if (is_leaf)
|
|
{
|
|
last = allocz(sizeof(struct mib_leaf));
|
|
struct mib_leaf *leaf = &last->leaf;
|
|
|
|
parent->children[child_id] = (mib_node_u *) leaf;
|
|
leaf->c.id = child_id;
|
|
|
|
leaf->c.flags = MIB_TREE_LEAF;
|
|
}
|
|
else
|
|
{
|
|
last = allocz(sizeof(struct mib_node));
|
|
node_inner = &last->inner;
|
|
|
|
parent->children[child_id] = (mib_node_u *) node_inner;
|
|
node_inner->c.id = child_id;
|
|
/* fields c.flags, child_len and children are set by zeroed allocz() */
|
|
}
|
|
|
|
return last;
|
|
}
|
|
|
|
/**
|
|
* mib_tree_delete - delete a MIB subtree
|
|
* @t: MIB tree
|
|
* @walk: MIB tree walk state that specify the subtree
|
|
*
|
|
* Return number of nodes deleted in the subtree. It is possible to delete an empty
|
|
* prefix which leads to deletion of all nodes inside the MIB tree. Note that
|
|
* the empty prefix (tree root) node itself could be deleted therefore 0 may be
|
|
* returned in case of empty prefix deletion.
|
|
*/
|
|
int
|
|
mib_tree_delete(struct mib_tree *t, struct mib_walk_state *walk)
|
|
{
|
|
int deleted = 0;
|
|
if (!t)
|
|
return 0;
|
|
|
|
/* (walk->stack_pos < 2) It is impossible to delete root node */
|
|
if (!walk || walk->stack_pos == 0)
|
|
return 0;
|
|
|
|
if (walk->stack_pos == 1)
|
|
{
|
|
for (u32 child = 0; child < t->root.child_len; child++)
|
|
{
|
|
if (!t->root.children[child])
|
|
continue;
|
|
|
|
walk->stack_pos = 2;
|
|
walk->stack[0] = (mib_node_u*) &t->root;
|
|
walk->stack[1] = t->root.children[child];
|
|
|
|
deleted += mib_tree_delete(t, walk);
|
|
}
|
|
|
|
return deleted;
|
|
}
|
|
|
|
struct mib_node *parent = &walk->stack[walk->stack_pos - 2]->inner;
|
|
mib_node_u *node = walk->stack[walk->stack_pos - 1];
|
|
|
|
struct mib_walk_state delete = {
|
|
.id_pos = walk->id_pos,
|
|
.stack_pos = 2,
|
|
.stack = {
|
|
(mib_node_u *) parent,
|
|
node,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
u32 last_id = 0;
|
|
while (delete.stack_pos > 1)
|
|
{
|
|
continue_while: /* like outer continue, but skip always true condition */
|
|
parent = (struct mib_node *) delete.stack[delete.stack_pos - 2];
|
|
|
|
if (mib_node_is_leaf(node))
|
|
{
|
|
/* Free leaf node */
|
|
last_id = node->leaf.c.id;
|
|
parent->children[last_id] = NULL;
|
|
delete.stack[--delete.stack_pos] = NULL;
|
|
free(node);
|
|
deleted++;
|
|
node = delete.stack[delete.stack_pos - 1];
|
|
continue; /* here, we couldn't skip the while condition */
|
|
}
|
|
|
|
struct mib_node *node_inner = &node->inner;
|
|
mib_node_u *child = NULL;
|
|
for (u32 id = last_id; id < node_inner->child_len; id++)
|
|
{
|
|
/* Recursively traverse child nodes */
|
|
child = node_inner->children[id];
|
|
|
|
if (!child)
|
|
continue;
|
|
|
|
last_id = 0;
|
|
delete.stack[delete.stack_pos++] = child;
|
|
parent = node_inner;
|
|
node = child;
|
|
goto continue_while; /* outer continue */
|
|
}
|
|
|
|
/* Free inner node without any children */
|
|
last_id = node_inner->c.id;
|
|
parent->children[last_id] = NULL;
|
|
delete.stack[--delete.stack_pos] = NULL;
|
|
free(node_inner->children);
|
|
free(node_inner);
|
|
deleted++;
|
|
node = (mib_node_u *) parent;
|
|
|
|
/* skip check for deleted node in loop over children */
|
|
last_id++;
|
|
}
|
|
|
|
/* delete the node from original stack */
|
|
walk->stack[--walk->stack_pos] = NULL;
|
|
|
|
node = walk->stack[walk->stack_pos - 1];
|
|
struct mib_node *node_inner = &node->inner;
|
|
u32 id;
|
|
for (id = 0; id < node_inner->child_len; id++)
|
|
{
|
|
if (node_inner->children[id] != NULL)
|
|
break;
|
|
}
|
|
|
|
if (id == node_inner->child_len)
|
|
{
|
|
/* all the children are NULL */
|
|
free(node_inner->children);
|
|
node_inner->children = NULL;
|
|
node_inner->child_len = 0;
|
|
}
|
|
|
|
return deleted;
|
|
}
|
|
|
|
/**
|
|
* mib_tree_remove - delete a MIB subtree
|
|
* @t: MIB tree
|
|
* @oid: object identifier specifying the subtree
|
|
*
|
|
* This is a convenience wrapper around mib_tree_delete(). The mib_tree_remove()
|
|
* finds the corresponding node and deletes it. Return 0 if the OID was not
|
|
* found. Otherwise return number of deleted nodes (see mib_tree_delete() for
|
|
* more details).
|
|
*/
|
|
int
|
|
mib_tree_remove(struct mib_tree *t, const struct oid *oid)
|
|
{
|
|
struct mib_walk_state walk = { };
|
|
mib_node_u *node = mib_tree_find(t, &walk, oid);
|
|
|
|
if (!node)
|
|
return 0;
|
|
|
|
return mib_tree_delete(t, &walk);
|
|
}
|
|
|
|
/**
|
|
* mib_tree_find - Find a OID node in MIB tree
|
|
* @t: searched tree
|
|
* @walk: output search state
|
|
* @oid: searched node identification
|
|
*
|
|
* Return valid pointer to node in MIB tree or NULL. The search state @walk is
|
|
* always updated and contains the longest possible prefix of @oid present
|
|
* inside the tree @t. The @walk must not be NULL and must be blank (only
|
|
* initialized).
|
|
*/
|
|
mib_node_u *
|
|
mib_tree_find(const struct mib_tree *t, struct mib_walk_state *walk, const struct oid *oid)
|
|
{
|
|
ASSERT(t && walk);
|
|
|
|
if (!oid || snmp_is_oid_empty(oid))
|
|
{
|
|
walk->stack_pos = 1;
|
|
walk->stack[0] = (mib_node_u *) &t->root;
|
|
return (snmp_is_oid_empty(oid)) ? (mib_node_u *) &t->root : NULL;
|
|
}
|
|
|
|
mib_node_u *node;
|
|
struct mib_node *node_inner;
|
|
|
|
/* the OID id index to use */
|
|
u8 oid_pos = walk->id_pos;
|
|
|
|
if (walk->stack_pos > 0)
|
|
node = walk->stack[walk->stack_pos - 1];
|
|
else
|
|
node = walk->stack[walk->stack_pos++] = (mib_node_u *) &t->root;
|
|
|
|
if (mib_node_is_leaf(node))
|
|
{
|
|
/* In any of cases below we did not move in the tree therefore the
|
|
* walk->id_pos is left untouched. */
|
|
if (snmp_oid_is_prefixed(oid) &&
|
|
oid->n_subid + ARRAY_SIZE(snmp_internet) + 1 == walk->id_pos)
|
|
return node;
|
|
|
|
else if (snmp_oid_is_prefixed(oid) &&
|
|
oid->n_subid + ARRAY_SIZE(snmp_internet) + 1 > walk->id_pos)
|
|
return NULL;
|
|
|
|
else if (!snmp_oid_is_prefixed(oid) && oid->n_subid + 1 == walk->id_pos)
|
|
return node;
|
|
}
|
|
|
|
node_inner = &node->inner;
|
|
ASSERT(node); /* node may be leaf if OID is not in tree t */
|
|
|
|
/* Handling of prefixed OID */
|
|
if (snmp_oid_is_prefixed(oid) && walk->stack_pos < 6)
|
|
{
|
|
/* The movement inside implicit SNMP internet and following prefix is not
|
|
* projected to walk->id_pos. */
|
|
uint i = (uint) walk->stack_pos - 1;
|
|
/* walking the snmp_internet prefix itself */
|
|
for (; i < ARRAY_SIZE(snmp_internet); i++)
|
|
{
|
|
if (node_inner->child_len <= snmp_internet[i])
|
|
return NULL;
|
|
|
|
node = node_inner->children[snmp_internet[i]];
|
|
node_inner = &node->inner;
|
|
|
|
if (!node)
|
|
return NULL;
|
|
|
|
ASSERT(node->empty.id == snmp_internet[i]);
|
|
walk->stack[walk->stack_pos++] = node;
|
|
|
|
if (mib_node_is_leaf(node))
|
|
return NULL;
|
|
}
|
|
|
|
/* walking the prefix continuation (OID field oid->prefix) */
|
|
u8 prefix = oid->prefix;
|
|
if (node_inner->child_len <= prefix)
|
|
return NULL;
|
|
|
|
node = node_inner->children[prefix];
|
|
node_inner = &node->inner;
|
|
|
|
if (!node)
|
|
return NULL;
|
|
|
|
ASSERT(node->empty.id == prefix);
|
|
walk->stack[walk->stack_pos++] = node;
|
|
|
|
if (mib_node_is_leaf(node) && oid->n_subid > 0)
|
|
return NULL;
|
|
}
|
|
|
|
u8 subids = oid->n_subid;
|
|
if (subids == 0)
|
|
return (node == (mib_node_u *) &t->root) ? NULL : node;
|
|
|
|
/* loop for all OID's ids except the last one */
|
|
for (; oid_pos < subids - 1 && walk->stack_pos < MIB_WALK_STACK_SIZE + 1; oid_pos++)
|
|
{
|
|
u32 id = oid->ids[oid_pos];
|
|
if (node_inner->child_len <= id)
|
|
{
|
|
/* The walk->id_pos points after the last accepted OID id.
|
|
* This is correct because we did not find the last OID in the tree. */
|
|
walk->id_pos = oid_pos;
|
|
return NULL;
|
|
}
|
|
|
|
node = node_inner->children[id];
|
|
node_inner = &node->inner;
|
|
|
|
if (!node)
|
|
{
|
|
/* Same as above, the last node is not valid therefore the walk->is_pos
|
|
* points after the last accepted OID id. */
|
|
walk->id_pos = oid_pos;
|
|
return NULL;
|
|
}
|
|
|
|
ASSERT(node->empty.id == id);
|
|
walk->stack[walk->stack_pos++] = node;
|
|
|
|
if (mib_node_is_leaf(node))
|
|
{
|
|
/* We need to increment the oid_pos because the walk->is_pos suppose the
|
|
* pointer after the last valid OID id. */
|
|
walk->id_pos = ++oid_pos;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
walk->id_pos = oid_pos;
|
|
u32 last_id = oid->ids[oid_pos];
|
|
if (node_inner->child_len <= last_id ||
|
|
walk->stack_pos >= MIB_WALK_STACK_SIZE + 1)
|
|
return NULL;
|
|
|
|
node = node_inner->children[last_id];
|
|
node_inner = &node->inner;
|
|
|
|
if (!node)
|
|
return NULL;
|
|
|
|
/* here, the check of node being a leaf is intentionally omitted
|
|
* because we may need to search for a inner node */
|
|
ASSERT(node->empty.id == last_id);
|
|
|
|
/* We need to increment the oid_pos because the walk->is_pos suppose the
|
|
* pointer after the last valid OID id. */
|
|
walk->id_pos = ++oid_pos;
|
|
return walk->stack[walk->stack_pos++] = node;
|
|
}
|
|
|
|
/**
|
|
* mib_tree_walk_init - Initialize MIB tree walk state
|
|
* @walk: MIB tree walk state to init
|
|
* @t: optional MIB tree
|
|
*/
|
|
void
|
|
mib_tree_walk_init(struct mib_walk_state *walk, const struct mib_tree *t)
|
|
{
|
|
walk->id_pos = 0;
|
|
walk->stack_pos = (t != NULL) ? 1 : 0;
|
|
memset(&walk->stack, 0, sizeof(walk->stack));
|
|
|
|
if (t != NULL)
|
|
walk->stack[0] = (mib_node_u *) &t->root;
|
|
}
|
|
|
|
/*
|
|
* walk_is_prefixable - test prefixability of MIB walk state
|
|
* @walk: MIB tree walk state to use
|
|
*
|
|
* This function is functionally equivalent to snmp_oid_is_prefixable().
|
|
*/
|
|
static inline int
|
|
walk_is_prefixable(const struct mib_walk_state *walk)
|
|
{
|
|
/* empty prefix and oid->prefix (+2) */
|
|
if (walk->stack_pos < ARRAY_SIZE(snmp_internet) + 2)
|
|
return 0;
|
|
|
|
for (uint i = 0; i < ARRAY_SIZE(snmp_internet); i++)
|
|
{
|
|
if (walk->stack[i + 1]->empty.id != snmp_internet[i])
|
|
return 0;
|
|
}
|
|
|
|
u32 id = walk->stack[ARRAY_SIZE(snmp_internet) + 1]->empty.id;
|
|
return id > 0 && id <= UINT8_MAX;
|
|
}
|
|
|
|
/*
|
|
* mib_tree_walk_to_oid - retrieve OID from MIB tree walk state
|
|
* @walk: MIB tree walk state to transform
|
|
* @result: destination OID
|
|
* @subids: Maximal number of subids in available space
|
|
*
|
|
* Return 1 if the space is insufficient, 0 otherwise.
|
|
*/
|
|
int
|
|
mib_tree_walk_to_oid(const struct mib_walk_state *walk, struct oid *result, u32 subids)
|
|
{
|
|
ASSERT(walk && result);
|
|
|
|
/* the stack_pos point after last valid index, and the first is always empty
|
|
* prefix */
|
|
if (walk->stack_pos <= 1)
|
|
{
|
|
/* create a null valued OID; sets all n_subid, prefix, include and reserved */
|
|
memset(result, 0, sizeof(struct oid));
|
|
return 0;
|
|
}
|
|
|
|
u32 index;
|
|
if (walk_is_prefixable(walk))
|
|
{
|
|
if (walk->stack_pos - 2 > subids - (ARRAY_SIZE(snmp_internet) + 1))
|
|
return 1;
|
|
|
|
/* skip empty prefix, whole snmp_internet .1.3.6.1 and oid->prefix */
|
|
index = 2 + ARRAY_SIZE(snmp_internet);
|
|
result->n_subid = walk->stack_pos - (ARRAY_SIZE(snmp_internet) + 2);
|
|
result->prefix = \
|
|
walk->stack[ARRAY_SIZE(snmp_internet) + 1]->empty.id;
|
|
}
|
|
else
|
|
{
|
|
if (walk->stack_pos - 2 > subids)
|
|
return 1;
|
|
|
|
index = 1; /* skip empty prefix */
|
|
result->n_subid = walk->stack_pos - 1;
|
|
result->prefix = 0;
|
|
}
|
|
|
|
result->include = 0;
|
|
result->reserved = 0;
|
|
|
|
u32 i = 0;
|
|
/* the index could point after last stack array element */
|
|
for (; index < walk->stack_pos && index < MIB_WALK_STACK_SIZE; index++)
|
|
result->ids[i++] = walk->stack[index]->empty.id;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* mib_tree_walk_oid_compare - compare MIB tree walk state and OID
|
|
* @walk: left relation operand
|
|
* @oid: Object Identifier in cpu native byte order
|
|
*
|
|
* Return value semantics is the same as snmp_oid_compare().
|
|
*/
|
|
int
|
|
mib_tree_walk_oid_compare(const struct mib_walk_state *walk, const struct oid *oid)
|
|
{
|
|
/* code is very similar to snmp_oid_compare() */
|
|
if (!walk->stack_pos)
|
|
return -1;
|
|
|
|
uint walk_idx = 1;
|
|
u8 walk_subids = walk->stack_pos; /* left_subids */
|
|
u8 oid_subids = oid->n_subid; /* right_subids */
|
|
|
|
const u8 oid_prefix = oid->prefix;
|
|
|
|
if (oid_prefix != 0)
|
|
{
|
|
for (; walk_idx < walk_subids && walk_idx < ARRAY_SIZE(snmp_internet) + 1; walk_idx++)
|
|
{
|
|
u32 id = walk->stack[walk_idx]->empty.id;
|
|
if (id < snmp_internet[walk_idx - 1])
|
|
return -1;
|
|
else if (id > snmp_internet[walk_idx - 1])
|
|
return 1;
|
|
}
|
|
|
|
if (walk_idx < ARRAY_SIZE(snmp_internet) + 1)
|
|
return -1;
|
|
|
|
const u8 walk_prefix = walk->stack[walk_idx++]->empty.id;
|
|
if (walk_prefix < oid_prefix)
|
|
return -1;
|
|
else if (walk_prefix > oid_prefix)
|
|
return 1;
|
|
}
|
|
|
|
uint i = 0;
|
|
for (; i < oid_subids && walk_idx < walk_subids; i++, walk_idx++)
|
|
{
|
|
u32 walk_id = walk->stack[walk_idx]->empty.id;
|
|
u32 oid_id = oid->ids[i];
|
|
if (walk_id < oid_id)
|
|
return -1;
|
|
else if (walk_id > oid_id)
|
|
return 1;
|
|
}
|
|
|
|
if (walk_idx == walk_subids && i == oid_subids)
|
|
return 0;
|
|
else if (walk_idx == walk_subids)
|
|
return -1;
|
|
else /* if (i == oid_subids) */
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* mib_tree_walk_is_oid_descendant - check if OID is in walk subtree
|
|
* @walk: MIB tree walk state
|
|
* @oid: OID to use
|
|
*
|
|
* Return 0 if @walk specify same path in MIB tree as @oid, return +1 if @oid is
|
|
* in @walk subtree, return -1 otherwise.
|
|
*/
|
|
int
|
|
mib_tree_walk_is_oid_descendant(const struct mib_walk_state *walk, const struct oid *oid)
|
|
{
|
|
/* walk stack index skipped zero prefix and OID subidentifier index */
|
|
u32 i = 1, j = 0;
|
|
|
|
if (!walk->stack_pos && snmp_is_oid_empty(oid))
|
|
return 0;
|
|
|
|
if (snmp_oid_is_prefixed(oid))
|
|
{
|
|
for (; i < MIN(walk->stack_pos - 1, ARRAY_SIZE(snmp_internet) + 1); i++)
|
|
{
|
|
if (walk->stack[i]->empty.id != snmp_internet[i - 1])
|
|
return -1;
|
|
}
|
|
|
|
if (i == walk->stack_pos)
|
|
return +1;
|
|
|
|
if (i < walk->stack_pos &&
|
|
walk->stack[i]->empty.id != (u32) oid->prefix)
|
|
return -1;
|
|
|
|
i++;
|
|
}
|
|
|
|
u32 ids = oid->n_subid;
|
|
for (; i < walk->stack_pos && j < ids; i++, j++)
|
|
{
|
|
if (walk->stack[i]->empty.id != oid->ids[j])
|
|
return -1;
|
|
}
|
|
|
|
if (i < walk->stack_pos)
|
|
return -1;
|
|
else if (i == walk->stack_pos && j == ids)
|
|
return 0;
|
|
else if (i == walk->stack_pos)
|
|
return +1;
|
|
else
|
|
{
|
|
die("unreachable");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* mib_tree_walk_next - find MIB tree node successor in lexicagraphically ordered MIB tree
|
|
* @t: MIB tree to use
|
|
* @walk: MIB tree walk state to use
|
|
*/
|
|
mib_node_u *
|
|
mib_tree_walk_next(const struct mib_tree *t, struct mib_walk_state *walk)
|
|
{
|
|
ASSERT(t && walk);
|
|
|
|
u32 next_id = 0;
|
|
|
|
if (walk->stack_pos == 0)
|
|
return NULL;
|
|
|
|
mib_node_u *node = walk->stack[walk->stack_pos - 1];
|
|
|
|
if (mib_node_is_leaf(node))
|
|
{
|
|
next_id = node->leaf.c.id + 1;
|
|
walk->stack[--walk->stack_pos] = NULL;
|
|
node = walk->stack[walk->stack_pos - 1];
|
|
}
|
|
|
|
while (walk->stack_pos > 0)
|
|
{
|
|
node = walk->stack[walk->stack_pos - 1];
|
|
|
|
if (mib_node_is_leaf(node))
|
|
{
|
|
walk->stack[walk->stack_pos++] = node;
|
|
return node;
|
|
}
|
|
|
|
struct mib_node *node_inner = &node->inner;
|
|
for (u32 id = next_id; id < node_inner->child_len; id++)
|
|
{
|
|
mib_node_u *child = node_inner->children[id];
|
|
|
|
if (!child)
|
|
continue;
|
|
|
|
walk->stack[walk->stack_pos++] = child;
|
|
return child;
|
|
}
|
|
|
|
next_id = node_inner->c.id + 1;
|
|
walk->stack[--walk->stack_pos] = NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* mib_tree_walk_next_leaf - wrapper around mib_tree_walk_next returning only leafs
|
|
* @t: MIB tree to use
|
|
* @walk: MIB tree walk state
|
|
* @skip: lower value bound for next OID id
|
|
*/
|
|
struct mib_leaf *
|
|
mib_tree_walk_next_leaf(const struct mib_tree *t, struct mib_walk_state *walk, u32 skip)
|
|
{
|
|
(void)t;
|
|
|
|
if (walk->stack_pos == 0)
|
|
return NULL;
|
|
|
|
u32 next_id = skip;
|
|
mib_node_u *node = walk->stack[walk->stack_pos - 1];
|
|
|
|
if (mib_node_is_leaf(node) && walk->stack_pos > 1)
|
|
{
|
|
next_id = node->leaf.c.id + 1;
|
|
walk->stack[--walk->stack_pos] = NULL;
|
|
node = walk->stack[walk->stack_pos - 1];
|
|
}
|
|
else if (mib_node_is_leaf(node))
|
|
{
|
|
/* walk->stack_pos == 1, so we NULL out the last stack field */
|
|
walk->stack[--walk->stack_pos] = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
while (walk->stack_pos > 0)
|
|
{
|
|
continue_while:
|
|
node = walk->stack[walk->stack_pos - 1];
|
|
|
|
if (mib_node_is_leaf(node))
|
|
return (struct mib_leaf *) node;
|
|
|
|
struct mib_node *node_inner = &node->inner;
|
|
for (u32 id = next_id; id < node_inner->child_len; id++)
|
|
{
|
|
mib_node_u *child = node_inner->children[id];
|
|
|
|
if (!child)
|
|
continue;
|
|
|
|
next_id = 0;
|
|
walk->stack[walk->stack_pos++] = child;
|
|
/* node is assign at the beginning of the while loop (from stack) */
|
|
goto continue_while;
|
|
}
|
|
|
|
next_id = node->empty.id + 1;
|
|
walk->stack[--walk->stack_pos] = NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|