0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-23 02:01:55 +00:00
bird/proto/snmp/snmp_utils.c
2024-08-15 18:23:41 +02:00

940 lines
23 KiB
C

/*
* BIRD -- Simple Network Management Protocol (SNMP) helper functions
*
* (c) 2022 Vojtech Vilimek <vojtech.vilimek@nic.cz>
* (c) 2022 CZ.NIC z.s.p.o
*
* Can be freely distributed and used under the terms of the GNU GPL.
*
*/
#include "snmp_utils.h"
#include <stdio.h>
inline void
snmp_pdu_context(struct snmp_pdu *pdu, struct snmp_proto *p, sock *sk)
{
pdu->p = p;
pdu->error = AGENTX_RES_NO_ERROR;
pdu->buffer = sk->tpos;
pdu->size = sk->tbuf + sk->tbsize - sk->tpos;
pdu->index = 0;
pdu->sr_vb_start = NULL;
pdu->sr_o_end = NULL;
}
/**
* snmp_session - store packet ids from protocol to header
* @p: source SNMP protocol instance
* @h: dest PDU header
*/
inline void
snmp_session(const struct snmp_proto *p, struct agentx_header *h)
{
STORE_U32(h->session_id, p->session_id);
STORE_U32(h->transaction_id, p->transaction_id);
STORE_U32(h->packet_id, p->packet_id);
}
inline void *
snmp_varbind_data(const struct agentx_varbind *vb)
{
uint name_size = snmp_oid_size(&vb->name);
return (void *) &vb->name + name_size;
}
/**
* snmp_is_oid_empty - check if oid is null-valued
* @oid: object identifier to check
*
* Test if the oid header is full of zeroes. For NULL-pointer @oid returns 0.
* We ignore include field to prevent weird behaviour.
*/
inline int
snmp_is_oid_empty(const struct oid *oid)
{
/* We intentionaly ignore padding that should be zeroed */
if (oid != NULL)
return oid->n_subid == 0 && oid->prefix == 0;
else
return 0;
}
/*
* snmp_oid_is_prefixable - check for prefixed form conversion possibility
* @oid: Object Identifier in packet byte order to check
*
* Check if it is possible to convert @oid to prefixed form. The condition of
* that is standart .1.3.6.1 internet prefix and 5-th id that fits in one byte.
*/
inline int
snmp_pkt_oid_is_prefixable(const struct oid *oid)
{
if (LOAD_U8(oid->n_subid) < 5)
return 0;
for (int i = 0; i < 4; i++)
if (LOAD_U32(oid->ids[i]) != snmp_internet[i])
return 0;
if (LOAD_U32(oid->ids[4]) >= 256)
return 0;
return 1;
}
/*
* snmp_oid_copy - copy OID from one place to another
* @dest: destination to use
* @src: OID to be copied from
*/
void
snmp_oid_copy(struct oid *dest, const struct oid *src)
{
dest->n_subid = src->n_subid;
dest->prefix = src->prefix;
dest->include = src->include ? 1 : 0;
dest->reserved = 0;
memcpy(dest->ids, src->ids, src->n_subid * sizeof(u32));
}
/*
* snmp_oid_from_buf - copy OID from RX buffer to dest in native byte order
* @dst: destination to use (native byte order)
* @src: OID to be copied from (packet byte order)
*/
void
snmp_oid_from_buf(struct oid *dst, const struct oid *src)
{
dst->n_subid = LOAD_U8(src->n_subid);
dst->prefix = LOAD_U8(src->prefix);
dst->include = LOAD_U8(src->include) ? 1 : 0;
dst->reserved = 0;
for (uint i = 0; i < dst->n_subid; i++)
dst->ids[i] = LOAD_U32(src->ids[i]);
}
/*
* snmp_oid_to_buf - copy OID to TX buffer with packet byte order
* @dst: destination to use (packet byte order)
* @src: OID to be copied from (native byte order)
*/
void
snmp_oid_to_buf(struct oid *dst, const struct oid *src)
{
STORE_U8(dst->n_subid, src->n_subid);
STORE_U8(dst->prefix, src->prefix);
STORE_U8(dst->include, (src->include) ? 1 : 0);
STORE_U8(dst->reserved, 0);
for (uint i = 0; i < src->n_subid; i++)
STORE_U32(dst->ids[i], src->ids[i]);
}
/**
* snmp_str_size_from_len - return in-buffer octet string size
* @len: length of C-string, returned from strlen()
*/
inline size_t
snmp_str_size_from_len(uint len)
{
return 4 + BIRD_ALIGN(len, 4);
}
/**
* snmp_str_size - return in packet size of supplied string
* @str: measured string
*
* Returned value is string length aligned to 4 byte with 32bit length
* annotation included.
*/
inline size_t
snmp_str_size(const char *str)
{
return snmp_str_size_from_len(strlen(str));
}
/**
* snmp_oid_size - measure size of OID in bytes
* @o: object identifier to use
*
* Work for both packet and cpu native byte orders.
*/
uint
snmp_oid_size(const struct oid *o)
{
/* LOAD_U8() is in both cases basic mem read */
return 4 + (LOAD_U8(o->n_subid) * 4);
}
/*
* snmp_oid_size_from_len - return size of OID with @n_subid subids in bytes
* @n_subid: number of subids in ids array
*/
inline size_t
snmp_oid_size_from_len(uint n_subid)
{
return sizeof(struct oid) + n_subid * sizeof(u32);
}
static inline uint
snmp_get_octet_size(const struct agentx_octet_str *str)
{
return str->length;
}
/**
* snmp_varbind_header_size - measure size of VarBind without data in bytes
* @vb_name: VarBind OID name
*
* Return size including whole OID as well as the VarBind header.
*/
uint
snmp_varbind_header_size(const struct oid *vb_name)
{
ASSUME(vb_name);
return snmp_oid_size(vb_name) + OFFSETOF(struct agentx_varbind, name);
}
uint
snmp_varbind_size_unsafe(const struct agentx_varbind *vb)
{
ASSUME(snmp_test_varbind_type(vb->type));
int value_size = agentx_type_size(vb->type);
uint vb_header = snmp_varbind_header_size(&vb->name);
if (value_size == 0)
return vb_header;
if (value_size > 0)
return vb_header + value_size;
switch (vb->type)
{
case AGENTX_OBJECT_ID:;
struct oid *oid = snmp_varbind_data(vb);
return vb_header + snmp_oid_size(oid);
case AGENTX_OCTET_STRING:
case AGENTX_IP_ADDRESS:
case AGENTX_OPAQUE:;
struct agentx_octet_str *string = snmp_varbind_data(vb);
return vb_header + snmp_get_octet_size(string);
default:
/* Shouldn't happen */
die("getting size of VarBind with unknown type (%u)", vb->type);
return 0;
}
}
/**
* snmp_varbind_size_from_len - get size in-buffer VarBind for known OID and data
* @n_subid: number of subidentifiers of the VarBind's OID name
* @type: type of VarBind
* @len: length of variably long data
*
* For types with fixed size the @len is not used. For types such as Octet
* String, or OID the @len is used directly.
*
* Return number of bytes used by VarBind in specified form.
*/
inline size_t
snmp_varbind_size_from_len(uint n_subid, enum agentx_type type, uint len)
{
size_t sz = snmp_oid_size_from_len(n_subid)
+ sizeof(struct agentx_varbind) - sizeof(struct oid);
int data_sz = agentx_type_size(type);
if (data_sz < 0)
sz += len;
else
sz += data_sz;
return sz;
}
/*
* snmp_test_varbind - test validity of VarBind type
* @type: Type of VarBind in cpu native byte order
*/
int
snmp_test_varbind_type(u16 type)
{
if (type == AGENTX_INTEGER ||
type == AGENTX_OCTET_STRING ||
type == AGENTX_NULL ||
type == AGENTX_OBJECT_ID ||
type == AGENTX_IP_ADDRESS ||
type == AGENTX_COUNTER_32 ||
type == AGENTX_GAUGE_32 ||
type == AGENTX_TIME_TICKS ||
type == AGENTX_OPAQUE ||
type == AGENTX_COUNTER_64 ||
type == AGENTX_NO_SUCH_OBJECT ||
type == AGENTX_NO_SUCH_INSTANCE ||
type == AGENTX_END_OF_MIB_VIEW)
return 1;
else
return 0;
}
/**
* snmp_valid_ip4_index - check IPv4 address validity in oid
* @o: object identifier holding ip address
* @start: index of first address id
*/
int
snmp_valid_ip4_index(const struct oid *o, uint start)
{
if (start + 3 < o->n_subid)
return snmp_valid_ip4_index_unsafe(o, start);
else
return 0;
}
/**
* snmp_valid_ip4_index_unsafe - check validity of IPv4 address in oid
* @o: object identifier holding ip address
* @start: index of first address id
*
* This function is unsafe - no checks of object identifier ids
* length sufficiency is done.
*/
int
snmp_valid_ip4_index_unsafe(const struct oid *o, uint start)
{
for (int i = 0; i < 4; i++)
if (o->ids[start + i] >= 256)
return 0;
return 1;
}
/*
* snmp_put_nstr - copy c-string into buffer with limit
* @buf: destination buffer
* @str: string to use
* @len: number of characters to use from string
*/
byte *
snmp_put_nstr(byte *buf, const char *str, uint len)
{
uint alen = BIRD_ALIGN(len, 4);
struct agentx_octet_str *octet = (void *) buf;
STORE_U32(octet->length, len);
memcpy(&octet->data, str, len);
buf += len + sizeof(octet->length);
/* Insert zero padding in the gap at the end */
for (uint i = 0; i < alen - len; i++)
buf[i] = '\0';
return buf + (alen - len);
}
/**
* snmp_put_str - put string into SNMP PDU transcieve buffer
* @buf: pointer to first unoccupied buffer byte
* @str: string to place
*
* Handles all conditions specified by RFC, namely string length annotation
* and padding 4 byte alignment with zeroes. Return NULL if string is too large
* for SNMP message.
*/
byte *
snmp_put_str(byte *buf, const char *str)
{
uint len = strlen(str);
return snmp_put_nstr(buf, str, len);
}
byte *
snmp_put_ip4(byte *buf, ip4_addr addr)
{
/* octet string has size 4 bytes */
STATIC_ASSERT(sizeof(ip4_addr) == sizeof(u32));
STORE_PTR(buf, sizeof(ip4_addr));
/* Always use Network byte order */
put_u32(buf+4, ip4_to_u32(addr));
return buf + 8;
}
byte *
snmp_put_blank(byte *buf)
{
STORE_PTR(buf, 0);
return buf + 4;
}
/**
* snmp_put_fbyte - put one padded byte to SNMP PDU transcieve buffer
* @buf: pointer to free buffer byte
* @data: byte to use
*
* Put @data into buffer @buf with 3B zeroed padding.
*/
byte *
snmp_put_fbyte(byte *buf, u8 data)
{
STORE_U8(*buf++, data);
memset(buf, 0, 3); /* we fill the 24bit padding with zeros */
return buf + 3;
}
/**
* snmp_oid_compare - find the lexicographical order relation between @left and @right
* @left: left object id relation operant
* @right: right object id relation operant
*
* both @left and @right has to be non-blank.
* function returns 0 if left == right,
* -1 if left < right,
* and 1 otherwise
*/
int
snmp_oid_compare(const struct oid *left, const struct oid *right)
{
const u8 left_subids = left->n_subid;
u8 right_subids = right->n_subid; /* see hack for more info */
const u8 left_prefix = left->prefix;
const u8 right_prefix = right->prefix;
if (left_prefix == 0 && right_prefix == 0)
goto test_ids;
if (right_prefix == 0)
return (-1) * snmp_oid_compare(right, left);
if (left_prefix == 0)
{
uint bound = MIN((uint) left_subids, (uint) ARRAY_SIZE(snmp_internet));
for (uint idx = 0; idx < bound; idx++)
{
u32 id = left->ids[idx];
if (id < snmp_internet[idx])
return -1;
else if (id > snmp_internet[idx])
return 1;
}
if (left_subids <= ARRAY_SIZE(snmp_internet))
return -1;
/* check prefix */
if (left->ids[4] < (u32) right_prefix)
return -1;
else if (left->ids[4] > (u32) right_prefix)
return 1;
/* the right prefix is already checked (+1) */
int limit = MIN(left_subids - (int) (ARRAY_SIZE(snmp_internet) + 1),
(int) right_subids);
for (int i = 0; i < limit; i++)
{
u32 left_id = left->ids[i + ARRAY_SIZE(snmp_internet) + 1];
u32 right_id = right->ids[i];
if (left_id < right_id)
return -1;
else if (left_id > right_id)
return 1;
}
/* hack: we known at this point that right has >= 5 subids
* (implicit in snmp_internet and oid->prefix), so
* we simplify to common case by altering left_subids */
right_subids += 5;
goto all_same;
}
if (left_prefix < right_prefix)
return -1;
else if (left_prefix > right_prefix)
return 1;
test_ids:
for (int i = 0; i < MIN(left->n_subid, right->n_subid); i++)
{
u32 left_id = left->ids[i];
u32 right_id = right->ids[i];
if (left_id < right_id)
return -1;
else if (left_id > right_id)
return 1;
}
all_same:
/* shorter sequence is before longer in lexicografical order */
if (left_subids < right_subids)
return -1;
else if (left_subids > right_subids)
return 1;
else
return 0;
}
struct snmp_registration *
snmp_registration_create(struct snmp_proto *p, enum agentx_mibs mib)
{
struct snmp_registration *r;
r = mb_alloc(p->p.pool, sizeof(struct snmp_registration));
r->n.prev = r->n.next = NULL;
r->session_id = p->session_id;
r->transaction_id = p->transaction_id;
/* will be incremented by snmp_session() macro during packet assembly */
r->packet_id = p->packet_id + 1;
r->mib = mib;
add_tail(&p->registration_queue, &r->n);
return r;
}
int
snmp_registration_match(struct snmp_registration *r, struct agentx_header *h)
{
return (LOAD_U32(r->session_id) == h->session_id) &&
(LOAD_U32(r->transaction_id) == h->transaction_id) &&
(LOAD_U32(r->packet_id) == h->packet_id);
}
/*
* agentx_type_size - get in packet VarBind type size
* @type: VarBind type
*
* Returns length of agentx_type @type in bytes, Variable length types result in
* -1.
*/
int
agentx_type_size(enum agentx_type type)
{
/*
* AGENTX_NULL, AGENTX_NO_SUCH_OBJECT, AGENTX_NO_SUCH_INSTANCE,
* AGENTX_END_OF_MIB_VIEW
*/
if (type >= AGENTX_NO_SUCH_OBJECT || type == AGENTX_NULL)
return 0;
/* AGENTX_INTEGER, AGENTX_COUNTER_32, AGENTX_GAUGE_32, AGENTX_TIME_TICKS */
if (type >= AGENTX_COUNTER_32 && type <= AGENTX_TIME_TICKS ||
type == AGENTX_INTEGER)
return 4;
if (type == AGENTX_COUNTER_64)
return 8;
if (AGENTX_IP_ADDRESS)
return snmp_str_size_from_len(4);
/* AGENTX_OBJECT_ID, AGENTX_OCTET_STRING, AGENTX_OPAQUE */
else
return -1;
}
static inline void
snmp_varbind_type32(struct agentx_varbind *vb, struct snmp_pdu *c, enum agentx_type type, u32 val)
{
ASSUME(agentx_type_size(type) == 4); /* type as 4B representation */
vb->type = type;
u32 *data = snmp_varbind_data(vb);
STORE_PTR(data, val);
data++;
c->buffer = (byte *) data;
}
inline void
snmp_varbind_int(struct snmp_pdu *c, u32 val)
{
snmp_varbind_type32(c->sr_vb_start, c, AGENTX_INTEGER, val);
}
inline void
snmp_varbind_counter32(struct snmp_pdu *c, u32 val)
{
snmp_varbind_type32(c->sr_vb_start, c, AGENTX_COUNTER_32, val);
}
inline void
snmp_varbind_ticks(struct snmp_pdu *c, u32 val)
{
snmp_varbind_type32(c->sr_vb_start, c, AGENTX_TIME_TICKS, val);
}
inline void
snmp_varbind_gauge32(struct snmp_pdu *c, s64 time)
{
snmp_varbind_type32(c->sr_vb_start, c,
AGENTX_GAUGE_32, MAX(0, MIN(time, UINT32_MAX)));
}
inline void
snmp_varbind_ip4(struct snmp_pdu *c, ip4_addr addr)
{
c->sr_vb_start->type = AGENTX_IP_ADDRESS;
c->buffer = snmp_put_ip4(snmp_varbind_data(c->sr_vb_start), addr);
}
/*
* snmp_varbind_nstr - fill varbind context with octet string
* @vb: VarBind to use
* @c: PDU information
* @str: C-string to put as the VarBind data
* @len: length of the string @str
*
* Beware: this function assumes there is enough space in the underlaying
* TX buffer. The caller has to provide that, see snmp_str_size_from_len() for
* more info.
*/
void
snmp_varbind_nstr(struct snmp_pdu *c, const char *str, uint len)
{
c->sr_vb_start->type = AGENTX_OCTET_STRING;
c->buffer = snmp_put_nstr(snmp_varbind_data(c->sr_vb_start), str, len);
}
/*
* snmp_varbind_oid - fill VarBind data with OID @oid_val
* @oid_val - Object Identifier in cpu native byte order
*
* Function puts the @oid_val to the packet byte order.
*/
void
snmp_varbind_oid(struct snmp_pdu *c, const struct oid *oid_val)
{
c->sr_vb_start->type = AGENTX_OBJECT_ID;
snmp_oid_to_buf(snmp_varbind_data(c->sr_vb_start), oid_val);
}
inline enum agentx_type
snmp_search_res_to_type(enum snmp_search_res r)
{
ASSUME(r != SNMP_SEARCH_OK);
enum agentx_type type_arr[] = {
[SNMP_SEARCH_NO_OBJECT] = AGENTX_NO_SUCH_OBJECT,
[SNMP_SEARCH_NO_INSTANCE] = AGENTX_NO_SUCH_INSTANCE,
[SNMP_SEARCH_END_OF_VIEW] = AGENTX_END_OF_MIB_VIEW,
};
return type_arr[r];
}
inline int
snmp_test_close_reason(byte value)
{
if (value >= (byte) AGENTX_CLOSE_OTHER &&
value <= (byte) AGENTX_CLOSE_BY_MANAGER)
return 1;
else
return 0;
}
/*
* Debugging
*/
void UNUSED
snmp_oid_dump(const struct oid *oid)
{
log(L_WARN "OID DUMP ========");
if (oid == NULL)
{
log(L_WARN "is eqaul to NULL");
log(L_WARN "OID DUMP END ====");
log(L_WARN ".");
return;
}
else if (snmp_is_oid_empty(oid))
{
log(L_WARN "is empty");
log(L_WARN "OID DUMP END ====");
log(L_WARN ".");
return;
}
log(L_WARN " #ids: %4u prefix %3u include: %5s",
oid->n_subid, oid->prefix, (oid->include)? "true" : "false");
log(L_WARN "IDS -------------");
for (int i = 0; i < oid->n_subid; i++)
log(L_WARN " %2u: %11u ~ 0x%08X", i, oid->ids[i], oid->ids[i]);
log(L_WARN "OID DUMP END ====");
log(L_WARN);
}
void UNUSED
snmp_oid_log(const struct oid *oid)
{
char buf[1024] = { };
char *pos = buf;
if (snmp_oid_is_prefixed(oid))
{
for (uint i = 0; i < ARRAY_SIZE(snmp_internet); i++)
pos += snprintf(pos, buf + 1024 - pos, ".%u", snmp_internet[i]);
pos += snprintf(pos, buf + 1024 - pos, ".%u", oid->prefix);
}
for (int id = 0; id < oid->n_subid; id++)
pos += snprintf(pos, buf + 1024 - pos, ".%u", oid->ids[id]);
log(L_WARN, "%s", buf);
}
/*
* snmp_oid_common_ancestor - find a common ancestor
* @left: first OID
* @right: second OID
* @out: buffer for result
*
* The @out must be large enough to always fit the resulting OID, a safe value
* is minimum between number of left subids and right subids. The result might
* be NULL OID in cases where there is no common subid. The result could be also
* viewed as longest common prefix. Note that if both @left and @right are
* prefixable but not prefixed the result in @out will also not be prefixed.
*
* This function is used intensively by snmp_test.c.
*/
void
snmp_oid_common_ancestor(const struct oid *left, const struct oid *right, struct oid *out)
{
ASSERT(left && right && out);
out->include = 0;
out->reserved = 0;
out->prefix = 0;
u32 offset = 0;
u8 left_ids = left->n_subid, right_ids = right->n_subid;
int l = snmp_oid_is_prefixed(left), r = snmp_oid_is_prefixed(right);
if (l && r)
{
if (left->prefix != right->prefix)
{
out->n_subid = 4;
for (uint id = 0; id < ARRAY_SIZE(snmp_internet); id++)
out->ids[id] = snmp_internet[id];
return;
}
out->prefix = left->prefix;
}
else if (!l && r)
{
if (left_ids == 0)
{
/* finish creating NULL OID */
out->n_subid = 0;
return;
}
for (uint id = 0; id < MIN(ARRAY_SIZE(snmp_internet), left_ids); id++)
{
if (left->ids[id] != snmp_internet[id])
{
out->n_subid = id;
return;
}
out->ids[id] = snmp_internet[id];
}
if (left_ids <= ARRAY_SIZE(snmp_internet))
{
out->n_subid = left_ids;
return;
}
/* index 4 is conresponding to the prefix in prefixed OID */
if (left->ids[4] != (u32) right->prefix)
{
out->n_subid = ARRAY_SIZE(snmp_internet);
return;
}
/* delete snmp_internet from out->ids and store OID prefix */
offset = ARRAY_SIZE(snmp_internet) + 1;
out->n_subid = out->n_subid - ARRAY_SIZE(snmp_internet);
out->prefix = right->prefix;
}
else if (l && !r)
{
snmp_oid_common_ancestor(right, left, out);
return;
}
ASSERT(offset <= left_ids);
u8 subids = 0;
for (u32 id = 0; id < MIN(left_ids - offset, right_ids); id++)
{
if (left->ids[offset + id] == right->ids[id])
{
subids++;
out->ids[id] = right->ids[id];
}
else
break;
}
out->n_subid = subids;
}
/*
* SNMP MIB tree walking
*/
struct mib_leaf *
snmp_walk_init(struct mib_tree *tree, struct mib_walk_state *walk, struct snmp_pdu *c)
{
mib_tree_walk_init(walk, tree);
mib_node_u *node = mib_tree_find(tree, walk, &c->sr_vb_start->name);
// TODO hide me in mib_tree code
/* mib_tree_find() returns NULL if the oid is longer than existing any path */
if (node == NULL && walk->stack_pos > 0)
node = walk->stack[walk->stack_pos - 1];
return (!node || !mib_node_is_leaf(node)) ? NULL : &node->leaf;
}
struct mib_leaf *
snmp_walk_next(struct mib_tree *tree, struct mib_walk_state *walk, struct snmp_pdu *c)
{
ASSUME(tree && walk);
if (!walk->stack_pos)
return NULL;
mib_node_u *node = walk->stack[walk->stack_pos - 1];
int found = 0;
struct mib_leaf *leaf = &node->leaf;
if (mib_node_is_leaf(node) && leaf->call_next)
{
const struct oid *oid = &c->sr_vb_start->name;
if (mib_tree_walk_oid_compare(walk, oid) > 0)
{
int old = snmp_oid_size(&c->sr_vb_start->name);
if (mib_tree_walk_to_oid(walk,
&c->sr_vb_start->name, 20 * sizeof(u32)))
return NULL;
int new = snmp_oid_size(&c->sr_vb_start->name);
c->buffer += (new - old);
}
found = !leaf->call_next(walk, c);
}
else if (mib_node_is_leaf(node) && c->sr_vb_start->name.include)
{
found = 1;
c->sr_vb_start->name.include = 0;
}
const struct oid *oid = &c->sr_vb_start->name;
u32 skip = (walk->id_pos < oid->n_subid) ?
oid->ids[walk->id_pos] : 0;
while (!found && (leaf = mib_tree_walk_next_leaf(tree, walk, skip)) != NULL)
{
/* mib_tree_walk_next() forces VarBind's name OID overwriting */
int old = snmp_oid_size(&c->sr_vb_start->name);
// TODO autogrow
if (mib_tree_walk_to_oid(walk, &c->sr_vb_start->name, 20 * sizeof(u32)))
return NULL;
int new = snmp_oid_size(&c->sr_vb_start->name);
c->buffer += (new - old);
if (leaf->call_next && !leaf->call_next(walk, c))
found = 1;
else if (!leaf->call_next)
found = 1;
oid = &c->sr_vb_start->name;
skip = (walk->id_pos < oid->n_subid) ?
oid->ids[walk->id_pos] : 0;
}
if (!found)
return NULL;
return leaf;
}
enum snmp_search_res
snmp_walk_fill(struct mib_leaf *leaf, struct mib_walk_state *walk, struct snmp_pdu *c)
{
struct agentx_varbind *vb = c->sr_vb_start;
enum snmp_search_res res;
/* The OID c->sr_vb_start->name is either left untouched for agentx-Get-PDU,
* or updated by snmp_walk_next() for agentx-GetNext-PDU and agentx-GetBulk-PDU
*
* The null OID in c->sr_o_end means no limits. The OID c->sr_o_end is always
* null for agentx-Get-PDU and therefore evaluates to 0.
*/
if (!snmp_check_search_limit(&c->sr_vb_start->name, c->sr_o_end))
{
res = SNMP_SEARCH_END_OF_VIEW;
vb->type = snmp_search_res_to_type(res);
return res;
}
if (!leaf)
return SNMP_SEARCH_NO_OBJECT;
uint size = 0;
enum agentx_type type = AGENTX_NULL;
if (leaf->size >= 0)
{
if (leaf->type == AGENTX_OCTET_STRING || leaf->type == AGENTX_OPAQUE ||
leaf->type == AGENTX_OBJECT_ID)
{
type = leaf->type;
size = leaf->size;
}
else if (leaf->type != AGENTX_INVALID)
{
type = leaf->type;
size = agentx_type_size(leaf->type);
}
else
size = leaf->size;
}
(void) snmp_tbuf_reserve(c, size);
vb->type = (u16) type;
res = leaf->filler(walk, c);
vb = c->sr_vb_start;
if (res != SNMP_SEARCH_OK)
vb->type = snmp_search_res_to_type(res);
ASSUME(vb->type == leaf->type || vb->type == AGENTX_END_OF_MIB_VIEW ||
vb->type == AGENTX_NO_SUCH_OBJECT || vb->type == AGENTX_NO_SUCH_INSTANCE);
return res;
}