diff --git a/src/core/snmp/mib_structs.c b/src/core/snmp/mib_structs.c index 0325cd6a..c451bf0c 100644 --- a/src/core/snmp/mib_structs.c +++ b/src/core/snmp/mib_structs.c @@ -32,11 +32,41 @@ * Author: Christiaan Simons */ -#include "lwip/opt.h" #include "lwip/snmp_structs.h" #if LWIP_SNMP +/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */ +const s32_t prefix[4] = {1, 3, 6, 1}; + +#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN) +static u8_t node_stack_cnt = 0; +static struct mib_node* node_stack[NODE_STACK_SIZE]; + +static void +push_node(struct mib_node* node) +{ + if (node_stack_cnt < NODE_STACK_SIZE) + { + node_stack[node_stack_cnt] = node; + node_stack_cnt++; + } +} + +static struct mib_node* +pop_node(void) +{ + if (node_stack_cnt > 0) + { + node_stack_cnt--; + return node_stack[node_stack_cnt]; + } + else + { + return NULL; + } +} + /** * Searches tree for the supplied (scalar?) object identifier. * @@ -60,17 +90,17 @@ snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct obj struct mib_array_node *an; u16_t i; - /* array node (internal ROM or RAM, fixed length) */ - an = (struct mib_array_node *)node; - i = 0; - while ((i < an->maxlength) && (an->objid[i] != *ident)) - { - i++; - } - if (i < an->maxlength) - { - if (ident_len > 0) - { + if (ident_len > 0) + { + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + i = 0; + while ((i < an->maxlength) && (an->objid[i] != *ident)) + { + i++; + } + if (i < an->maxlength) + { /* found it, if available proceed to child, otherwise inspect leaf */ LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F,i,an->objid[i],*ident)); if (an->nptr[i] == NULL) @@ -102,15 +132,15 @@ snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct obj } else { - /* search failed, short object identifier (nosuchname) */ - LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, short object identifier")); + /* search failed, identifier mismatch (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed *ident==%"S32_F,*ident)); return NULL; } } else { - /* search failed, identifier mismatch (nosuchname) */ - LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed *ident==%"S32_F,*ident)); + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, short object identifier")); return NULL; } } @@ -119,18 +149,18 @@ snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct obj struct mib_list_rootnode *lrn; struct mib_list_node *ln; - /* list root node (internal 'RAM', variable length) */ - lrn = (struct mib_list_rootnode *)node; - ln = lrn->head; - /* iterate over list, head to tail */ - while ((ln != NULL) && (ln->objid != *ident)) + if (ident_len > 0) { - ln = ln->next; - } - if (ln != NULL) - { - if (ident_len > 0) - { + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid != *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { /* found it, proceed to child */; LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F,ln->objid,*ident)); if (ln->nptr == NULL) @@ -158,15 +188,15 @@ snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct obj } else { - /* search failed, short object identifier (nosuchname) */ - LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, short object identifier")); + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed *ident==%"S32_F,*ident)); return NULL; } } else { - /* search failed */ - LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed *ident==%"S32_F,*ident)); + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, short object identifier")); return NULL; } } @@ -175,17 +205,17 @@ snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct obj struct mib_external_node *en; u16_t i; - /* external node (addressing and access via functions) */ - en = (struct mib_external_node *)node; - i = 0; - while ((i < en->count) && en->ident_cmp(i,*ident)) - { - i++; - } - if (i < en->count) - { - if (ident_len > 0) - { + if (ident_len > 0) + { + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + i = 0; + while ((i < en->count) && en->ident_cmp(i,*ident)) + { + i++; + } + if (i < en->count) + { if (en->get_nptr(i) == NULL) { /** @todo, this object is elsewhere, we can only start the request, @@ -203,18 +233,125 @@ snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct obj } else { - /* search failed, short object identifier (nosuchname) */ - LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, short object identifier")); + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed *ident==%"S32_F,*ident)); return NULL; } } else { - /* search failed */ - LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed *ident==%"S32_F,*ident)); + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, short object identifier")); return NULL; } } + else + { + /* unknown node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type "U16_F" unkown",(u16_t)node_type)); + return NULL; + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p",(void*)node)); + return NULL; +} + +/** + * Tree expansion. + * + */ +struct mib_node * +snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + u8_t node_type; + + /* reset stack */ + node_stack_cnt = 0; + + while (node != NULL) + { + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + if (ident_len > 0) + { + i = 0; + while ((i < an->maxlength) && (an->objid[i] < *ident)) + { + i++; + } + if (i < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F,i,an->objid[i],*ident)); + + /* add identifier to oidret */ + oidret->id[oidret->len] = an->objid[i]; + (oidret->len)++; + + if (an->nptr[i] == NULL) + { + /* leaf node */ + /* + if scalar: add '.0' + @todo if tabular: if not empty add first index, nextThing otherwise + */ + oidret->id[oidret->len] = 0; + (oidret->len)++; + return (struct mib_node*)an; + } + else + { + /* follow next child pointer */ + ident_len--; + ident++; + node = an->nptr[i]; + } + } + else + { + /* @todo return to parent */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed *ident==%"S32_F,*ident)); + return NULL; + } + } + else + { + /* short object identifier, complete it */ + + /* add leftmost '.Thing' */ + oidret->id[oidret->len] = an->objid[0]; + (oidret->len)++; + + if (an->nptr[0] == NULL) + { + /* leaf node */ + /* + if scalar: add '.0' + @todo if tabular: if not empty add first index, nextThing otherwise + */ + oidret->id[oidret->len] = 0; + (oidret->len)++; + return (struct mib_node*)an; + } + else + { + /* no leaf, continue */ + node = an->nptr[0]; + } + } + } + else + { + /* unknown/unhandled node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type "U16_F" unkown",(u16_t)node_type)); + return NULL; + } } /* done, found nothing */ LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p",(void*)node)); @@ -243,4 +380,51 @@ snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident) } } +/** + * Expands object identifier to the iso.org.dod.internet + * prefix for use in getnext operation. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param oidret points to returned expanded object identifier + * @return 1 if it matches, 0 otherwise + * + * @note ident_len 0 is allowed, expanding to the first known object id!! + */ +u8_t +snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + const s32_t *prefix_ptr; + s32_t *ret_ptr; + u8_t i; + + i = 0; + prefix_ptr = &prefix[0]; + ret_ptr = &oidret->id[0]; + ident_len = ((ident_len < 4)?ident_len:4); + while ((i < ident_len) && ((*ident) <= (*prefix_ptr))) + { + *ret_ptr++ = *prefix_ptr++; + ident++; + i++; + } + if (i == ident_len) + { + /* match, complete missing bits */ + while (i < 4) + { + *ret_ptr++ = *prefix_ptr++; + i++; + } + oidret->len = i; + return 1; + } + else + { + /* i != ident_len */ + return 0; + } +} + #endif /* LWIP_SNMP */ + diff --git a/src/core/snmp/msg_in.c b/src/core/snmp/msg_in.c index 095ab919..93b95c69 100644 --- a/src/core/snmp/msg_in.c +++ b/src/core/snmp/msg_in.c @@ -50,7 +50,6 @@ #if LWIP_SNMP -#define LWIP_SNMP_DBG_LOOPBACK_TST 0 #define SNMP_CONCURRENT_REQUESTS 2 /* public (non-static) constants */ @@ -102,6 +101,7 @@ snmp_init(void) trap_msg.pcb = snmp1_pcb; } +#if 0 /** * called for each variable binding (also for the fist one) */ @@ -150,13 +150,10 @@ snmp_msg_event(struct snmp_msg_pstat *msg_ps) else { /* mn == NULL, noSuchName */ + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->outvb = msg_ps->invb; msg_ps->error_status = SNMP_ES_NOSUCHNAME; msg_ps->error_index = 1 + msg_ps->vb_idx; - msg_ps->outvb.head = NULL; - msg_ps->outvb.tail = NULL; - msg_ps->outvb.count = 0; - msg_ps->outvb.seqlen = 0; - msg_ps->outvb.seqlenlen = 1; snmp_send_response(msg_ps); msg_ps->state = SNMP_MSG_EMPTY; } @@ -179,6 +176,7 @@ snmp_msg_event(struct snmp_msg_pstat *msg_ps) { } } +#endif /* lwIP UDP receive callback function */ static void @@ -264,34 +262,99 @@ snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, } if (mn != NULL) { - if (msg_ps->invb.head->value != NULL) + struct snmp_varbind *vb; + + /* allocate output varbind */ + vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind)); + if (vb != NULL) { - LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv free value before vb recycle")); - mem_free(msg_ps->invb.head->value); - } - msg_ps->invb.head->value_type = object_def.asn_type; - msg_ps->invb.head->value_len = object_def.v_len; - msg_ps->invb.head->value = mem_malloc(object_def.v_len); - if (msg_ps->invb.head->value != NULL) - { - mn->get_value(&object_def, object_def.v_len, msg_ps->invb.head->value); + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->invb.head->ident; + vb->ident_len = msg_ps->invb.head->ident_len; + /* ensure this memory is refereced once only */ + msg_ps->invb.head->ident = NULL; + msg_ps->invb.head->ident_len = 0; + + vb->value_type = object_def.asn_type; + vb->value_len = object_def.v_len; + vb->value = mem_malloc(object_def.v_len); + if (vb->value != NULL) + { + mn->get_value(&object_def, object_def.v_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate variable space")); + } } else { - LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate variable space")); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space")); } - msg_ps->outvb = msg_ps->invb; } else { /* mn == NULL, noSuchName */ + /* @todo move used names back from outvb to invb */ + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->outvb = msg_ps->invb; + msg_ps->error_status = SNMP_ES_NOSUCHNAME; + msg_ps->error_index = 1 + msg_ps->vb_idx; + } + } + else if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ) + { + struct snmp_obj_id oid; + + if (snmp_iso_prefix_expand(msg_ps->invb.head->ident_len, msg_ps->invb.head->ident, &oid)) + { + /** @todo expand tree and complete oid */ +#if 1 + if (msg_ps->invb.head->ident_len > 3) + { + /* can offset ident_len and ident */ + mn = snmp_expand_tree((struct mib_node*)&internet, + msg_ps->invb.head->ident_len - 4, + msg_ps->invb.head->ident + 4, &oid); + } + else + { + /* can't offset ident_len -4, ident + 4 */ + mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid); + } +#else + mn = (struct mib_node*)&internet; +#endif + } + else + { + mn = NULL; + } + if (mn != NULL) + { + struct snmp_varbind *vb; + + vb = snmp_varbind_alloc(&oid, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL), 0); + if (vb != NULL) + { + snmp_varbind_tail_add(&msg_ps->outvb, vb); + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space")); + } + } + else + { + /* mn == NULL, noSuchName */ + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->outvb = msg_ps->invb; msg_ps->error_status = SNMP_ES_NOSUCHNAME; msg_ps->error_index = 1 + msg_ps->vb_idx; - msg_ps->outvb.head = NULL; - msg_ps->outvb.tail = NULL; - msg_ps->outvb.count = 0; - msg_ps->outvb.seqlen = 0; - msg_ps->outvb.seqlenlen = 1; } } else @@ -322,8 +385,8 @@ snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, } /* free varbinds (if available) */ snmp_varbind_list_free(&msg_ps->invb); + snmp_varbind_list_free(&msg_ps->outvb); msg_ps->state = SNMP_MSG_EMPTY; - } else { @@ -770,17 +833,25 @@ snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len) vb->prev = NULL; i = oid->len; vb->ident_len = i; - /* allocate array of s32_t for our object identifier */ - vb->ident = (s32_t*)mem_malloc(sizeof(s32_t) * i); - if (vb->ident == NULL) + if (i > 0) { - mem_free(vb); - return NULL; + /* allocate array of s32_t for our object identifier */ + vb->ident = (s32_t*)mem_malloc(sizeof(s32_t) * i); + if (vb->ident == NULL) + { + mem_free(vb); + return NULL; + } + while(i > 0) + { + i--; + vb->ident[i] = oid->id[i]; + } } - while(i > 0) + else { - i--; - vb->ident[i] = oid->id[i]; + /* i == 0, pass zero length object identifier */ + vb->ident = NULL; } vb->value_type = type; vb->value_len = len; @@ -790,7 +861,10 @@ snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len) vb->value = mem_malloc(len); if (vb->value == NULL) { - mem_free(vb->ident); + if (vb->ident != NULL) + { + mem_free(vb->ident); + } mem_free(vb); return NULL; } @@ -821,15 +895,18 @@ snmp_varbind_free(struct snmp_varbind *vb) static void snmp_varbind_list_free(struct snmp_varbind_root *root) { - struct snmp_varbind *vb; + struct snmp_varbind *vb, *prev; vb = root->tail; while ( vb != NULL ) { + prev = vb->prev; snmp_varbind_free(vb); - vb = vb->prev; + vb = prev; } root->count = 0; + root->head = NULL; + root->tail = NULL; } static void