mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-23 02:01:55 +00:00
6cfa5416e1
Major rework of reconfiguration handling.
845 lines
21 KiB
C
845 lines
21 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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
// TODO: This function does not work with leaf nodes inside the snmp_internet prefix
|
|
// area
|
|
// Return NULL of failure, valid mib_node_u pointer otherwise
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* return -1 if walk_oid < oid
|
|
* return 0 if walk_oid == oid
|
|
* return +1 if walk_oid > oid
|
|
*
|
|
*/
|
|
// TODO tests, doc string
|
|
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 == walk_subids)
|
|
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_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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|