diff -urNp old/agent/mibgroup/ip-mib/data_access/ipaddress_common.c new/agent/mibgroup/ip-mib/data_access/ipaddress_common.c --- old/agent/mibgroup/ip-mib/data_access/ipaddress_common.c 2012-10-10 00:28:58.000000000 +0200 +++ new/agent/mibgroup/ip-mib/data_access/ipaddress_common.c 2017-04-04 11:02:42.391951747 +0200 @@ -67,6 +67,7 @@ netsnmp_container * netsnmp_access_ipaddress_container_init(u_int flags) { netsnmp_container *container1; + int rc; DEBUGMSGTL(("access:ipaddress:container", "init\n")); @@ -80,6 +81,7 @@ netsnmp_access_ipaddress_container_init( return NULL; } container1->container_name = strdup("ia_index"); + CONTAINER_SET_OPTIONS(container1, CONTAINER_KEY_ALLOW_DUPLICATES, rc); if (flags & NETSNMP_ACCESS_IPADDRESS_INIT_ADDL_IDX_BY_ADDR) { netsnmp_container *container2 = @@ -92,6 +94,13 @@ netsnmp_access_ipaddress_container_init( container2->compare = _access_ipaddress_entry_compare_addr; container2->container_name = strdup("ia_addr"); + + /* + * With allowed duplicates, CONTAINER_INSERT does not need to sort whole + * container and check for duplicates. We remove duplicates manually in + * netsnmp_access_ipaddress_container_load. + */ + CONTAINER_SET_OPTIONS(container2, CONTAINER_KEY_ALLOW_DUPLICATES, rc); netsnmp_container_add_index(container1, container2); } @@ -100,6 +109,53 @@ netsnmp_access_ipaddress_container_init( } /** + * Remove duplicate entries from the container. + * This function returns new copy of the container and destroys + * the original one. Use like this: + * c = _remove_duplicates(c, flags); + */ +static netsnmp_container * +_remove_duplicates(netsnmp_container *container, u_int container_flags) +{ + netsnmp_container *c; + netsnmp_iterator *it; + netsnmp_container *ret; + netsnmp_ipaddress_entry *entry, *prev_entry; + + if (! (container_flags & NETSNMP_ACCESS_IPADDRESS_INIT_ADDL_IDX_BY_ADDR)) { + /* We don't have address index, we can't detect duplicates */ + return container; + } + + ret = netsnmp_access_ipaddress_container_init(container_flags); + + /* use the IpAddress index */ + c = container->next; + it = CONTAINER_ITERATOR(c); + /* Sort the address index */ + CONTAINER_FIND(c, ITERATOR_FIRST(it)); + + + /* + * Sequentially iterate over sorted container and add only unique entries + * to 'ret' + */ + prev_entry = NULL; + for (entry = ITERATOR_FIRST(it); entry; entry = ITERATOR_NEXT(it)) { + if (prev_entry && _access_ipaddress_entry_compare_addr(prev_entry, entry) == 0) { + /* 'entry' is duplicate of the previous one -> delete it */ + netsnmp_access_ipaddress_entry_free(entry); + } else { + CONTAINER_INSERT(ret, entry); + prev_entry = entry; + } + } + CONTAINER_FREE(container); + free(it); + return ret; +} + +/** * @retval NULL error * @retval !NULL pointer to container */ @@ -112,9 +168,10 @@ netsnmp_access_ipaddress_container_load( DEBUGMSGTL(("access:ipaddress:container", "load\n")); + if (load_flags & NETSNMP_ACCESS_IPADDRESS_LOAD_ADDL_IDX_BY_ADDR) + container_flags |= NETSNMP_ACCESS_IPADDRESS_INIT_ADDL_IDX_BY_ADDR; + if (NULL == container) { - if (load_flags & NETSNMP_ACCESS_IPADDRESS_LOAD_ADDL_IDX_BY_ADDR) - container_flags |= NETSNMP_ACCESS_IPADDRESS_INIT_ADDL_IDX_BY_ADDR; container = netsnmp_access_ipaddress_container_init(container_flags); } if (NULL == container) { @@ -129,6 +186,9 @@ netsnmp_access_ipaddress_container_load( container = NULL; } + if (container) + container = _remove_duplicates(container, container_flags); + return container; } diff -urNp old/agent/mibgroup/ip-mib/ipAddressTable/ipAddressTable_data_access.c new/agent/mibgroup/ip-mib/ipAddressTable/ipAddressTable_data_access.c --- old/agent/mibgroup/ip-mib/ipAddressTable/ipAddressTable_data_access.c 2012-10-10 00:28:58.000000000 +0200 +++ new/agent/mibgroup/ip-mib/ipAddressTable/ipAddressTable_data_access.c 2017-04-04 13:26:34.332529808 +0200 @@ -137,6 +137,13 @@ ipAddressTable_container_init(netsnmp_co *container_ptr_ptr = netsnmp_container_find("ipAddressTable:table_container"); if (NULL != *container_ptr_ptr) { + /* + * The container has ALLOW_DUPLICATES flag to speed up CONTAINER_INSERT + * operations (it does not need to check for duplicates), however we + * (manually) ensure that we won't insert any duplicates there. + */ + int rc; + CONTAINER_SET_OPTIONS(*container_ptr_ptr, CONTAINER_KEY_ALLOW_DUPLICATES, rc); (*container_ptr_ptr)->container_name = strdup("ipAddressTable"); ipAddressTable_container_load(*container_ptr_ptr); CONTAINER_FOR_EACH(*container_ptr_ptr, @@ -205,8 +212,9 @@ static void _check_entry_for_updates(ipAddressTable_rowreq_ctx * rowreq_ctx, void **magic) { - netsnmp_container *ipaddress_container = (netsnmp_container*)magic[0]; + netsnmp_container *ipaddress_container = magic[0]; netsnmp_container *to_delete = (netsnmp_container*)magic[1]; + netsnmp_container *to_ignore = (netsnmp_container *) magic[2]; /* * check for matching entry using secondary index. @@ -234,10 +242,21 @@ _check_entry_for_updates(ipAddressTable_ rowreq_ctx->ipAddressLastChanged = netsnmp_get_agent_uptime(); /* - * remove entry from ifcontainer + * Remember not to add this entry from 'ipaddress_container' to 'container' later. + * Simple CONTAINER_REMOVE(ipaddress_container, ..) would be slow. */ - CONTAINER_REMOVE(ipaddress_container, ipaddress_entry); - netsnmp_access_ipaddress_entry_free(ipaddress_entry); + if (NULL == to_ignore) { + magic[2] = to_ignore = netsnmp_container_find("access_ipaddress:table_container"); + if (NULL == to_ignore) { + snmp_log(LOG_ERR, "couldn't create ignore container\n"); + } else { + /* to speed up insertion */ + int rc; + CONTAINER_SET_OPTIONS(to_ignore, CONTAINER_KEY_ALLOW_DUPLICATES, rc); + } + } + if (NULL != to_ignore) + CONTAINER_INSERT(to_ignore, ipaddress_entry); } } @@ -246,8 +265,11 @@ _check_entry_for_updates(ipAddressTable_ */ static void _add_new_entry(netsnmp_ipaddress_entry *ipaddress_entry, - netsnmp_container *container) + void **magic) { + netsnmp_container *container = magic[0]; + netsnmp_container *to_ignore = magic[2]; + ipAddressTable_rowreq_ctx *rowreq_ctx; DEBUGMSGTL(("ipAddressTable:access", "creating new entry\n")); @@ -255,6 +277,11 @@ _add_new_entry(netsnmp_ipaddress_entry * netsnmp_assert(NULL != ipaddress_entry); netsnmp_assert(NULL != container); + if (to_ignore && CONTAINER_FIND(to_ignore, ipaddress_entry)) { + /* this entry already is in 'container', skip it */ + return; + } + /* * allocate an row context and set the index(es) */ @@ -329,36 +356,44 @@ int ipAddressTable_container_load(netsnmp_container *container) { netsnmp_container *ipaddress_container; - void *tmp_ptr[2]; + void *tmp_ptr[3]; DEBUGMSGTL(("verbose:ipAddressTable:ipAddressTable_cache_load", "called\n")); /* - * TODO:351:M: |-> Load/update data in the ipAddressTable container. + * Load/update data in the ipAddressTable container. * loop over your ipAddressTable data, allocate a rowreq context, * set the index(es) [and data, optionally] and insert into * the container. */ + /* + * netsnmp_access_ipaddress_container_load makes sure that + * ipaddress_container does not contain any duplicate entries, + */ + ipaddress_container = netsnmp_access_ipaddress_container_load(NULL, NETSNMP_ACCESS_IPADDRESS_LOAD_ADDL_IDX_BY_ADDR); /* * we just got a fresh copy of interface data. compare it to * what we've already got, and make any adjustments, saving - * missing addresses to be deleted. + * missing addresses to be deleted. Also, prune interfaces in + * ipaddress_container, so only the new interfaces remain. */ tmp_ptr[0] = ipaddress_container->next; - tmp_ptr[1] = NULL; + tmp_ptr[1] = NULL; /* list of interfaces to be removed from 'container' */ + tmp_ptr[2] = NULL; /* list of interfaces to be ignored in ipaddress_container */ CONTAINER_FOR_EACH(container, (netsnmp_container_obj_func *) _check_entry_for_updates, tmp_ptr); /* * now add any new interfaces */ + tmp_ptr[0] = container; CONTAINER_FOR_EACH(ipaddress_container, (netsnmp_container_obj_func *) _add_new_entry, - container); + tmp_ptr); /* * free the container. we've either claimed each entry, or released it, @@ -396,6 +431,19 @@ ipAddressTable_container_load(netsnmp_co */ CONTAINER_REMOVE(tmp_container, NULL); } + CONTAINER_FREE(tmp_container); + } + + if (NULL != tmp_ptr[2]) { + /* list of interfaces to be ignored in ipaddress_container - free it */ + netsnmp_container *to_ignore = (netsnmp_container *) tmp_ptr[2]; + netsnmp_ipaddress_entry *ipaddress_entry; + while (CONTAINER_SIZE(to_ignore)) { + ipaddress_entry = (netsnmp_ipaddress_entry*)CONTAINER_FIRST(to_ignore); + CONTAINER_REMOVE(to_ignore, ipaddress_entry); + netsnmp_access_ipaddress_entry_free(ipaddress_entry); + } + CONTAINER_FREE(to_ignore); } DEBUGMSGT(("verbose:ipAddressTable:ipAddressTable_cache_load", diff -urNp old/agent/mibgroup/mibII/ipAddr.c new/agent/mibgroup/mibII/ipAddr.c --- old/agent/mibgroup/mibII/ipAddr.c 2012-10-10 00:28:58.000000000 +0200 +++ new/agent/mibgroup/mibII/ipAddr.c 2017-04-04 13:28:56.547268946 +0200 @@ -493,14 +493,16 @@ Address_Scan_Next(Index, Retin_ifaddr) } #elif defined(linux) +#include static struct ifreq *ifr; static int ifr_counter; static void Address_Scan_Init(void) { - int num_interfaces = 0; + int i; int fd; + int lastlen = 0; /* get info about all interfaces */ @@ -508,30 +510,45 @@ Address_Scan_Init(void) SNMP_FREE(ifc.ifc_buf); ifr_counter = 0; - do + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) - { - DEBUGMSGTL(("snmpd", "socket open failure in Address_Scan_Init\n")); - return; - } - num_interfaces += 16; - - ifc.ifc_len = sizeof(struct ifreq) * num_interfaces; - ifc.ifc_buf = (char*) realloc(ifc.ifc_buf, ifc.ifc_len); - - if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) - { - ifr=NULL; - close(fd); - return; - } - close(fd); + DEBUGMSGTL(("snmpd", "socket open failure in Address_Scan_Init\n")); + return; + } + + /* + * Cope with lots of interfaces and brokenness of ioctl SIOCGIFCONF + * on some platforms; see W. R. Stevens, ``Unix Network Programming + * Volume I'', p.435... + */ + + for (i = 8;; i *= 2) { + ifc.ifc_len = sizeof(struct ifreq) * i; + ifc.ifc_req = calloc(i, sizeof(struct ifreq)); + + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { + if (errno != EINVAL || lastlen != 0) { + /* + * Something has gone genuinely wrong... + */ + snmp_log(LOG_ERR, "bad rc from ioctl, errno %d", errno); + SNMP_FREE(ifc.ifc_buf); + close(fd); + return; + } + } else { + if (ifc.ifc_len == lastlen) { + /* + * The length is the same as the last time; we're done... + */ + break; + } + lastlen = ifc.ifc_len; + } + free(ifc.ifc_buf); /* no SNMP_FREE, getting ready to reassign */ } - while (ifc.ifc_len >= (sizeof(struct ifreq) * num_interfaces)); - - ifr = ifc.ifc_req; close(fd); + ifr = ifc.ifc_req; } /*