4 * Maintain a registry of index allocations
5 * (Primarily required for AgentX support,
6 * but it could be more widely useable).
10 #include <net-snmp/net-snmp-config.h>
18 #include <sys/types.h>
24 #if TIME_WITH_SYS_TIME
26 # include <sys/timeb.h>
28 # include <sys/time.h>
33 # include <sys/time.h>
39 #include <netinet/in.h>
46 #include <net-snmp/net-snmp-includes.h>
47 #include <net-snmp/agent/net-snmp-agent-includes.h>
48 #include <net-snmp/agent/agent_callbacks.h>
49 #include <net-snmp/agent/agent_index.h>
52 #include "mibgroup/struct.h"
53 #include <net-snmp/agent/table.h>
54 #include <net-snmp/agent/table_iterator.h>
55 #include "mib_module_includes.h"
57 #ifdef USING_AGENTX_SUBAGENT_MODULE
58 #include "agentx/subagent.h"
59 #include "agentx/client.h"
62 #if defined(BRCM_SNMP_DEBUG) || !defined(BUILD_ILMI)
65 * Initial support for index allocation
69 netsnmp_variable_list *varbind; /* or pointer to var_list ? */
71 netsnmp_session *session;
72 struct snmp_index *next_oid;
73 struct snmp_index *prev_oid;
74 struct snmp_index *next_idx;
75 } *snmp_index_head = NULL;
79 extern netsnmp_session *main_session;
82 * The caller is responsible for free()ing the memory returned by
87 register_string_index(oid * name, size_t name_len, char *cp)
89 netsnmp_variable_list varbind, *res;
91 memset(&varbind, 0, sizeof(netsnmp_variable_list));
92 varbind.type = ASN_OCTET_STR;
93 snmp_set_var_objid(&varbind, name, name_len);
94 if (cp != ANY_STRING_INDEX) {
95 snmp_set_var_value(&varbind, (u_char *) cp, strlen(cp));
96 res = register_index(&varbind, ALLOCATE_THIS_INDEX, main_session);
98 res = register_index(&varbind, ALLOCATE_ANY_INDEX, main_session);
104 char *rv = strdup(res->val.string);
111 register_int_index(oid * name, size_t name_len, int val)
113 netsnmp_variable_list varbind, *res;
115 memset(&varbind, 0, sizeof(netsnmp_variable_list));
116 varbind.type = ASN_INTEGER;
117 snmp_set_var_objid(&varbind, name, name_len);
118 varbind.val.string = varbind.buf;
119 if (val != ANY_INTEGER_INDEX) {
120 varbind.val_len = sizeof(long);
121 *varbind.val.integer = val;
122 res = register_index(&varbind, ALLOCATE_THIS_INDEX, main_session);
124 res = register_index(&varbind, ALLOCATE_ANY_INDEX, main_session);
130 int rv = *(res->val.integer);
137 * The caller is responsible for free()ing the memory returned by
141 netsnmp_variable_list *
142 register_oid_index(oid * name, size_t name_len,
143 oid * value, size_t value_len)
145 netsnmp_variable_list varbind;
147 memset(&varbind, 0, sizeof(netsnmp_variable_list));
148 varbind.type = ASN_OBJECT_ID;
149 snmp_set_var_objid(&varbind, name, name_len);
150 if (value != ANY_OID_INDEX) {
151 snmp_set_var_value(&varbind, (u_char *) value,
152 value_len * sizeof(oid));
153 return register_index(&varbind, ALLOCATE_THIS_INDEX, main_session);
155 return register_index(&varbind, ALLOCATE_ANY_INDEX, main_session);
160 * The caller is responsible for free()ing the memory returned by
164 netsnmp_variable_list *
165 register_index(netsnmp_variable_list * varbind, int flags,
166 netsnmp_session * ss)
168 netsnmp_variable_list *rv = NULL;
169 struct snmp_index *new_index, *idxptr, *idxptr2;
170 struct snmp_index *prev_oid_ptr, *prev_idx_ptr;
173 DEBUGMSGTL(("register_index", "register "));
174 DEBUGMSGVAR(("register_index", varbind));
175 DEBUGMSG(("register_index", "for session %08p\n", ss));
177 #if defined(USING_AGENTX_SUBAGENT_MODULE) && !defined(TESTING)
178 if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
179 NETSNMP_DS_AGENT_ROLE) == SUB_AGENT) {
180 return (agentx_register_index(ss, varbind, flags));
184 * Look for the requested OID entry
190 for (idxptr = snmp_index_head; idxptr != NULL;
191 prev_oid_ptr = idxptr, idxptr = idxptr->next_oid) {
192 if ((res = snmp_oid_compare(varbind->name, varbind->name_length,
193 idxptr->varbind->name,
194 idxptr->varbind->name_length)) <= 0)
199 * Found the OID - now look at the registered indices
201 if (res == 0 && idxptr) {
202 if (varbind->type != idxptr->varbind->type)
203 return NULL; /* wrong type */
206 * If we've been asked for an arbitrary new value,
207 * then find the end of the list.
208 * If we've been asked for any arbitrary value,
209 * then look for an unused entry, and use that.
210 * If there aren't any, continue as for new.
211 * Otherwise, locate the given value in the (sorted)
212 * list of already allocated values
214 if (flags & ALLOCATE_ANY_INDEX) {
215 for (idxptr2 = idxptr; idxptr2 != NULL;
216 prev_idx_ptr = idxptr2, idxptr2 = idxptr2->next_idx) {
218 if (flags == ALLOCATE_ANY_INDEX && !(idxptr2->allocated)) {
220 snmp_clone_varbind(idxptr2->varbind)) != NULL) {
221 idxptr2->session = ss;
222 idxptr2->allocated = 1;
228 for (idxptr2 = idxptr; idxptr2 != NULL;
229 prev_idx_ptr = idxptr2, idxptr2 = idxptr2->next_idx) {
230 switch (varbind->type) {
233 (*varbind->val.integer -
234 *idxptr2->varbind->val.integer);
237 i = SNMP_MIN(varbind->val_len,
238 idxptr2->varbind->val_len);
240 memcmp(varbind->val.string,
241 idxptr2->varbind->val.string, i);
245 snmp_oid_compare(varbind->val.objid,
246 varbind->val_len / sizeof(oid),
247 idxptr2->varbind->val.objid,
248 idxptr2->varbind->val_len /
252 return NULL; /* wrong type */
258 if (idxptr2->allocated) {
260 * No good: the index is in use.
265 * Okay, it's unallocated, we can just claim ownership
269 snmp_clone_varbind(idxptr2->varbind)) != NULL) {
270 idxptr2->session = ss;
271 idxptr2->allocated = 1;
280 * OK - we've now located where the new entry needs to
281 * be fitted into the index registry tree
283 * 'prev_oid_ptr' points to the head of the OID index
284 * list prior to this one. If this is null, then
285 * it means that this is the first OID in the list.
286 * 'idxptr' points either to the head of this OID list,
287 * or the next OID (if this is a new OID request)
288 * These can be distinguished by the value of 'res'.
290 * 'prev_idx_ptr' points to the index entry that sorts
291 * immediately prior to the requested value (if any).
292 * If an arbitrary value is required, then this will
293 * point to the last allocated index.
294 * If this pointer is null, then either this is a new
295 * OID request, or the requested value is the first
297 * 'idxptr2' points to the next sorted index (if any)
298 * but is not actually needed any more.
301 * I hope you've been paying attention.
302 * There'll be a test later :-)
306 * We proceed by creating the new entry
307 * (by copying the entry provided)
309 new_index = (struct snmp_index *) calloc(1, sizeof(struct snmp_index));
310 if (new_index == NULL)
313 if (0 == snmp_varlist_add_variable(&new_index->varbind,
315 varbind->name_length,
320 * if (snmp_clone_var( varbind, new_index->varbind ) != 0 )
325 new_index->session = ss;
326 new_index->allocated = 1;
328 if (varbind->type == ASN_OCTET_STR && flags == ALLOCATE_THIS_INDEX)
329 new_index->varbind->val.string[new_index->varbind->val_len] = 0;
332 * If we've been given a value, then we can use that, but
333 * otherwise, we need to create a new value for this entry.
334 * Note that ANY_INDEX and NEW_INDEX are both covered by this
335 * test (since NEW_INDEX & ANY_INDEX = ANY_INDEX, remember?)
337 if (flags & ALLOCATE_ANY_INDEX) {
339 if (snmp_clone_var(prev_idx_ptr->varbind, new_index->varbind)
345 new_index->varbind->val.string = new_index->varbind->buf;
347 switch (varbind->type) {
350 (*new_index->varbind->val.integer)++;
352 *(new_index->varbind->val.integer) = 1;
353 new_index->varbind->val_len = sizeof(long);
357 i = new_index->varbind->val_len - 1;
358 while (new_index->varbind->buf[i] == 'z') {
359 new_index->varbind->buf[i] = 'a';
362 i = new_index->varbind->val_len;
363 new_index->varbind->buf[i] = 'a';
364 new_index->varbind->buf[i + 1] = 0;
367 new_index->varbind->buf[i]++;
369 strcpy((char *) new_index->varbind->buf, "aaaa");
370 new_index->varbind->val_len =
371 strlen((char *) new_index->varbind->buf);
375 i = prev_idx_ptr->varbind->val_len / sizeof(oid) - 1;
376 while (new_index->varbind->val.objid[i] == 255) {
377 new_index->varbind->val.objid[i] = 1;
379 if (i == 0 && new_index->varbind->val.objid[0] == 2) {
380 new_index->varbind->val.objid[0] = 1;
381 i = new_index->varbind->val_len / sizeof(oid);
382 new_index->varbind->val.objid[i] = 0;
383 new_index->varbind->val_len += sizeof(oid);
386 new_index->varbind->val.objid[i]++;
389 * If the requested OID name is small enough,
390 * * append another OID (1) and use this as the
391 * * default starting value for new indexes.
393 if ((varbind->name_length + 1) * sizeof(oid) <= 40) {
394 for (i = 0; i < (int) varbind->name_length; i++)
395 new_index->varbind->val.objid[i] =
397 new_index->varbind->val.objid[varbind->name_length] =
399 new_index->varbind->val_len =
400 (varbind->name_length + 1) * sizeof(oid);
403 * Otherwise use '.1.1.1.1...'
405 i = 40 / sizeof(oid);
408 new_index->varbind->val_len = i * (sizeof(oid));
409 for (i--; i >= 0; i--)
410 new_index->varbind->val.objid[i] = 1;
415 snmp_free_var(new_index->varbind);
417 return NULL; /* Index type not supported */
422 * Try to duplicate the new varbind for return.
425 if ((rv = snmp_clone_varbind(new_index->varbind)) == NULL) {
426 snmp_free_var(new_index->varbind);
432 * Right - we've set up the new entry.
433 * All that remains is to link it into the tree.
434 * There are a number of possible cases here,
435 * so watch carefully.
438 new_index->next_idx = prev_idx_ptr->next_idx;
439 new_index->next_oid = prev_idx_ptr->next_oid;
440 prev_idx_ptr->next_idx = new_index;
442 if (res == 0 && idxptr) {
443 new_index->next_idx = idxptr;
444 new_index->next_oid = idxptr->next_oid;
446 new_index->next_idx = NULL;
447 new_index->next_oid = idxptr;
451 while (prev_oid_ptr) {
452 prev_oid_ptr->next_oid = new_index;
453 prev_oid_ptr = prev_oid_ptr->next_idx;
456 snmp_index_head = new_index;
462 * Release an allocated index,
463 * to allow it to be used elsewhere
466 release_index(netsnmp_variable_list * varbind)
468 return (unregister_index(varbind, TRUE, NULL));
472 * Completely remove an allocated index,
473 * due to errors in the registration process.
476 remove_index(netsnmp_variable_list * varbind, netsnmp_session * ss)
478 return (unregister_index(varbind, FALSE, ss));
482 unregister_index_by_session(netsnmp_session * ss)
484 struct snmp_index *idxptr, *idxptr2;
485 for (idxptr = snmp_index_head; idxptr != NULL;
486 idxptr = idxptr->next_oid)
487 for (idxptr2 = idxptr; idxptr2 != NULL;
488 idxptr2 = idxptr2->next_idx)
489 if (idxptr2->session == ss) {
490 idxptr2->allocated = 0;
491 idxptr2->session = NULL;
497 unregister_index(netsnmp_variable_list * varbind, int remember,
498 netsnmp_session * ss)
500 struct snmp_index *idxptr, *idxptr2;
501 struct snmp_index *prev_oid_ptr, *prev_idx_ptr;
504 #if defined(USING_AGENTX_SUBAGENT_MODULE) && !defined(TESTING)
505 if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
506 NETSNMP_DS_AGENT_ROLE) == SUB_AGENT) {
507 return (agentx_unregister_index(ss, varbind));
511 * Look for the requested OID entry
517 for (idxptr = snmp_index_head; idxptr != NULL;
518 prev_oid_ptr = idxptr, idxptr = idxptr->next_oid) {
519 if ((res = snmp_oid_compare(varbind->name, varbind->name_length,
520 idxptr->varbind->name,
521 idxptr->varbind->name_length)) <= 0)
526 return INDEX_ERR_NOT_ALLOCATED;
527 if (varbind->type != idxptr->varbind->type)
528 return INDEX_ERR_WRONG_TYPE;
530 for (idxptr2 = idxptr; idxptr2 != NULL;
531 prev_idx_ptr = idxptr2, idxptr2 = idxptr2->next_idx) {
532 i = SNMP_MIN(varbind->val_len, idxptr2->varbind->val_len);
534 memcmp(varbind->val.string, idxptr2->varbind->val.string, i);
538 if (res2 != 0 || (res2 == 0 && !idxptr2->allocated)) {
539 return INDEX_ERR_NOT_ALLOCATED;
541 if (ss != idxptr2->session)
542 return INDEX_ERR_WRONG_SESSION;
545 * If this is a "normal" index unregistration,
546 * mark the index entry as unused, but leave
547 * it in situ. This allows differentiation
548 * between ANY_INDEX and NEW_INDEX
551 idxptr2->allocated = 0; /* Unused index */
552 idxptr2->session = NULL;
553 return SNMP_ERR_NOERROR;
556 * If this is a failed attempt to register a
557 * number of indexes, the successful ones
558 * must be removed completely.
561 prev_idx_ptr->next_idx = idxptr2->next_idx;
562 } else if (prev_oid_ptr) {
563 if (idxptr2->next_idx) /* Use p_idx_ptr as a temp variable */
564 prev_idx_ptr = idxptr2->next_idx;
566 prev_idx_ptr = idxptr2->next_oid;
567 while (prev_oid_ptr) {
568 prev_oid_ptr->next_oid = prev_idx_ptr;
569 prev_oid_ptr = prev_oid_ptr->next_idx;
572 if (idxptr2->next_idx)
573 snmp_index_head = idxptr2->next_idx;
575 snmp_index_head = idxptr2->next_oid;
577 snmp_free_var(idxptr2->varbind);
579 return SNMP_ERR_NOERROR;
583 unregister_string_index(oid * name, size_t name_len, char *cp)
585 netsnmp_variable_list varbind;
587 memset(&varbind, 0, sizeof(netsnmp_variable_list));
588 varbind.type = ASN_OCTET_STR;
589 snmp_set_var_objid(&varbind, name, name_len);
590 snmp_set_var_value(&varbind, (u_char *) cp, strlen(cp));
591 return (unregister_index(&varbind, FALSE, main_session));
595 unregister_int_index(oid * name, size_t name_len, int val)
597 netsnmp_variable_list varbind;
599 memset(&varbind, 0, sizeof(netsnmp_variable_list));
600 varbind.type = ASN_INTEGER;
601 snmp_set_var_objid(&varbind, name, name_len);
602 varbind.val.string = varbind.buf;
603 varbind.val_len = sizeof(long);
604 *varbind.val.integer = val;
605 return (unregister_index(&varbind, FALSE, main_session));
609 unregister_oid_index(oid * name, size_t name_len,
610 oid * value, size_t value_len)
612 netsnmp_variable_list varbind;
614 memset(&varbind, 0, sizeof(netsnmp_variable_list));
615 varbind.type = ASN_OBJECT_ID;
616 snmp_set_var_objid(&varbind, name, name_len);
617 snmp_set_var_value(&varbind, (u_char *) value,
618 value_len * sizeof(oid));
619 return (unregister_index(&varbind, FALSE, main_session));
621 #endif /* not defined BUILD_ILMI */
623 #ifdef BRCM_SNMP_DEBUG
625 dump_idx_registry(void)
627 struct snmp_index *idxptr, *idxptr2;
628 u_char *sbuf = NULL, *ebuf = NULL;
629 size_t sbuf_len = 0, sout_len = 0, ebuf_len = 0, eout_len = 0;
631 if (snmp_index_head != NULL) {
632 printf("\nIndex Allocations:\n");
635 for (idxptr = snmp_index_head; idxptr != NULL;
636 idxptr = idxptr->next_oid) {
638 if (sprint_realloc_objid(&sbuf, &sbuf_len, &sout_len, 1,
639 idxptr->varbind->name,
640 idxptr->varbind->name_length)) {
641 printf("%s indexes:\n", sbuf);
643 printf("%s [TRUNCATED] indexes:\n", sbuf);
646 for (idxptr2 = idxptr; idxptr2 != NULL;
647 idxptr2 = idxptr2->next_idx) {
648 switch (idxptr2->varbind->type) {
650 printf(" %ld for session %8p, allocated %d\n",
651 *idxptr2->varbind->val.integer, idxptr2->session,
655 printf(" \"%s\" for session %8p, allocated %d\n",
656 idxptr2->varbind->val.string, idxptr2->session,
661 if (sprint_realloc_objid(&ebuf, &ebuf_len, &eout_len, 1,
662 idxptr2->varbind->val.objid,
663 idxptr2->varbind->val_len /
665 printf(" %s for session %8p, allocated %d\n", ebuf,
666 idxptr2->session, idxptr2->allocated);
669 (" %s [TRUNCATED] for sess %8p, allocated %d\n",
670 ebuf, idxptr2->session, idxptr2->allocated);
674 printf("unsupported type (%d/0x%02x)\n",
675 idxptr2->varbind->type, idxptr2->varbind->type);
689 count_indexes(oid * name, size_t namelen, int include_unallocated)
691 struct snmp_index *i = NULL, *j = NULL;
694 for (i = snmp_index_head; i != NULL; i = i->next_oid) {
695 if (netsnmp_oid_equals(name, namelen,
697 i->varbind->name_length) == 0) {
698 for (j = i; j != NULL; j = j->next_idx) {
699 if (j->allocated || include_unallocated) {
707 #endif /* BRCM_SNMP_DEBUG */
711 netsnmp_variable_list varbind;
712 netsnmp_session main_sess, *main_session = &main_sess;
715 test_string_register(int n, char *cp)
717 varbind->name[4] = n;
718 if (register_string_index(varbind->name, varbind.name_length, cp) ==
720 printf("allocating %s failed\n", cp);
724 test_int_register(int n, int val)
726 varbind->name[4] = n;
727 if (register_int_index(varbind->name, varbind.name_length, val) == -1)
728 printf("allocating %d/%d failed\n", n, val);
732 test_oid_register(int n, int subid)
734 netsnmp_variable_list *res;
736 varbind->name[4] = n;
738 varbind->val.objid[5] = subid;
739 res = register_oid_index(varbind->name, varbind.name_length,
741 varbind->val_len / sizeof(oid));
744 register_oid_index(varbind->name, varbind.name_length, NULL,
748 printf("allocating %d/%d failed\n", n, subid);
752 main(int argc, char argv[])
754 oid name[] = { 1, 2, 3, 4, 0 };
757 memset(&varbind, 0, sizeof(netsnmp_variable_list));
758 snmp_set_var_objid(&varbind, name, 5);
759 varbind->type = ASN_OCTET_STR;
761 * Test index structure linking:
764 test_string_register(20, "empty OID");
765 test_string_register(10, "first OID");
766 test_string_register(40, "last OID");
767 test_string_register(30, "middle OID");
770 * b) sorted by index value
772 test_string_register(25, "eee: empty IDX");
773 test_string_register(25, "aaa: first IDX");
774 test_string_register(25, "zzz: last IDX");
775 test_string_register(25, "mmm: middle IDX");
776 printf("This next one should fail....\n");
777 test_string_register(25, "eee: empty IDX"); /* duplicate */
781 * c) test initial index linking
783 test_string_register(5, "eee: empty initial IDX");
784 test_string_register(5, "aaa: replace initial IDX");
790 unregister_index_by_session(main_session);
792 * Now test index allocation
795 test_int_register(110, -1); /* empty */
796 test_int_register(110, -1); /* append */
797 test_int_register(110, 10); /* append exact */
798 printf("This next one should fail....\n");
799 test_int_register(110, 10); /* exact duplicate */
801 test_int_register(110, -1); /* append */
802 test_int_register(110, 5); /* insert exact */
807 test_string_register(120, NULL); /* empty */
808 test_string_register(120, NULL); /* append */
809 test_string_register(120, "aaaz");
810 test_string_register(120, NULL); /* minor rollover */
811 test_string_register(120, "zzzz");
812 test_string_register(120, NULL); /* major rollover */
818 test_oid_register(130, -1); /* empty */
819 test_oid_register(130, -1); /* append */
821 varbind->val_len = varbind.name_length * sizeof(oid);
822 memcpy(varbind->buf, varbind.name, varbind.val_len);
823 varbind->val.objid = (oid *) varbind.buf;
824 varbind->val_len += sizeof(oid);
826 test_oid_register(130, 255); /* append exact */
827 test_oid_register(130, -1); /* minor rollover */
828 test_oid_register(130, 100); /* insert exact */
829 printf("This next one should fail....\n");
830 test_oid_register(130, 100); /* exact duplicate */
833 varbind->val.objid = (oid *) varbind.buf;
834 for (i = 0; i < 6; i++)
835 varbind->val.objid[i] = 255;
836 varbind->val.objid[0] = 1;
837 test_oid_register(130, 255); /* set up rollover */
838 test_oid_register(130, -1); /* medium rollover */
840 for (i = 0; i < 6; i++)
841 varbind->val.objid[i] = 255;
842 varbind->val.objid[0] = 2;
843 test_oid_register(130, 255); /* set up rollover */
844 test_oid_register(130, -1); /* major rollover */
852 * Test the various "invalid" requests
853 * (unsupported types, mis-matched types, etc)
855 printf("The rest of these should fail....\n");
856 test_oid_register(110, -1);
857 test_oid_register(110, 100);
858 test_oid_register(120, -1);
859 test_oid_register(120, 100);
860 test_string_register(110, NULL);
861 test_string_register(110, "aaaa");
862 test_string_register(130, NULL);
863 test_string_register(130, "aaaa");
864 test_int_register(120, -1);
865 test_int_register(120, 1);
866 test_int_register(130, -1);
867 test_int_register(130, 1);
868 printf("done - this dump should be the same as before\n");
872 #endif /* ifndef BUILD_ILMI */