diff options
Diffstat (limited to 'agent/mibgroup/if-mib/data_access')
-rw-r--r-- | agent/mibgroup/if-mib/data_access/interface.c | 873 | ||||
-rw-r--r-- | agent/mibgroup/if-mib/data_access/interface.h | 58 | ||||
-rw-r--r-- | agent/mibgroup/if-mib/data_access/interface_ioctl.c | 506 | ||||
-rw-r--r-- | agent/mibgroup/if-mib/data_access/interface_ioctl.h | 52 | ||||
-rw-r--r-- | agent/mibgroup/if-mib/data_access/interface_linux.c | 1213 | ||||
-rw-r--r-- | agent/mibgroup/if-mib/data_access/interface_openbsd.c | 407 | ||||
-rw-r--r-- | agent/mibgroup/if-mib/data_access/interface_solaris2.c | 363 | ||||
-rw-r--r-- | agent/mibgroup/if-mib/data_access/interface_sysctl.c | 581 | ||||
-rw-r--r-- | agent/mibgroup/if-mib/data_access/interface_sysctl.h | 31 |
9 files changed, 4084 insertions, 0 deletions
diff --git a/agent/mibgroup/if-mib/data_access/interface.c b/agent/mibgroup/if-mib/data_access/interface.c new file mode 100644 index 0000000..8cb73e8 --- /dev/null +++ b/agent/mibgroup/if-mib/data_access/interface.c @@ -0,0 +1,873 @@ +/* + * Interface MIB architecture support + * + * $Id$ + */ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-features.h> +#include <net-snmp/net-snmp-includes.h> + +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <net-snmp/library/snmp_enum.h> +#include <net-snmp/data_access/interface.h> + +#include "mibII/mibII_common.h" +#include "if-mib/ifTable/ifTable.h" +#include "if-mib/data_access/interface.h" + +netsnmp_feature_child_of(interface_all, libnetsnmpmibs) +netsnmp_feature_child_of(interface, interface_all) +netsnmp_feature_child_of(interface_access_entry_set_admin_status, interface_all) +netsnmp_feature_child_of(interface_legacy, interface_all) + +#ifdef NETSNMP_FEATURE_REQUIRE_INTERFACE_ACCESS_ENTRY_SET_ADMIN_STATUS +netsnmp_feature_require(interface_arch_set_admin_status) +#endif /* NETSNMP_FEATURE_REQUIRE_INTERFACE_ACCESS_ENTRY_SET_ADMIN_STATUS */ + +/**---------------------------------------------------------------------*/ +/* + * local static vars + */ +static netsnmp_conf_if_list *conf_list = NULL; +static int need_wrap_check = -1; +static int _access_interface_init = 0; + +/* + * local static prototypes + */ +static int _access_interface_entry_compare_name(const void *lhs, + const void *rhs); +#ifndef NETSNMP_ACCESS_INTERFACE_NOARCH +static void _access_interface_entry_release(netsnmp_interface_entry * entry, + void *unused); +#endif +static void _access_interface_entry_save_name(const char *name, oid index); +static void _parse_interface_config(const char *token, char *cptr); +static void _free_interface_config(void); + +/**---------------------------------------------------------------------*/ +/* + * external per-architecture functions prototypes + * + * These shouldn't be called by the general public, so they aren't in + * the header file. + */ +#ifndef NETSNMP_ACCESS_INTERFACE_NOARCH +extern void netsnmp_arch_interface_init(void); +extern int +netsnmp_arch_interface_container_load(netsnmp_container* container, + u_int load_flags); +extern int +netsnmp_arch_set_admin_status(netsnmp_interface_entry * entry, + int ifAdminStatus); +extern int netsnmp_arch_interface_index_find(const char*name); +#endif + + +/** + * initialization + */ +void +init_interface(void) +{ + snmpd_register_config_handler("interface", _parse_interface_config, + _free_interface_config, + "name type speed"); +} + + +void +netsnmp_access_interface_init(void) +{ + netsnmp_assert(0 == _access_interface_init); /* who is calling twice? */ + + if (1 == _access_interface_init) + return; + + _access_interface_init = 1; + +#ifndef NETSNMP_ACCESS_INTERFACE_NOARCH + { + netsnmp_container * ifcontainer; + + netsnmp_arch_interface_init(); + + /* + * load once to set up ifIndexes + */ + ifcontainer = netsnmp_access_interface_container_load(NULL, 0); + if(NULL != ifcontainer) + netsnmp_access_interface_container_free(ifcontainer, 0); + } +#endif +} + +/**---------------------------------------------------------------------*/ +/* + * container functions + */ +/** + * initialize interface container + */ +netsnmp_container * +netsnmp_access_interface_container_init(u_int flags) +{ + netsnmp_container *container1; + + DEBUGMSGTL(("access:interface:container", "init\n")); + + /* + * create the containers. one indexed by ifIndex, the other + * indexed by ifName. + */ + container1 = netsnmp_container_find("access_interface:table_container"); + if (NULL == container1) + return NULL; + + container1->container_name = strdup("interface container"); + if (flags & NETSNMP_ACCESS_INTERFACE_INIT_ADDL_IDX_BY_NAME) { + netsnmp_container *container2 = + netsnmp_container_find("access_interface_by_name:access_interface:table_container"); + if (NULL == container2) + return NULL; + + container2->container_name = strdup("interface name container"); + container2->compare = _access_interface_entry_compare_name; + + netsnmp_container_add_index(container1, container2); + } + + return container1; +} + +/** + * load interface information in specified container + * + * @param container empty container, or NULL to have one created for you + * @param load_flags flags to modify behaviour. Examples: + * NETSNMP_ACCESS_INTERFACE_INIT_ADDL_IDX_BY_NAME + * + * @retval NULL error + * @retval !NULL pointer to container + */ +#ifndef NETSNMP_ACCESS_INTERFACE_NOARCH +netsnmp_container* +netsnmp_access_interface_container_load(netsnmp_container* container, u_int load_flags) +{ + int rc; + + DEBUGMSGTL(("access:interface:container", "load\n")); + netsnmp_assert(1 == _access_interface_init); + + if (NULL == container) + container = netsnmp_access_interface_container_init(load_flags); + if (NULL == container) { + snmp_log(LOG_ERR, "no container specified/found for access_interface\n"); + return NULL; + } + + rc = netsnmp_arch_interface_container_load(container, load_flags); + if (0 != rc) { + netsnmp_access_interface_container_free(container, + NETSNMP_ACCESS_INTERFACE_FREE_NOFLAGS); + container = NULL; + } + + return container; +} + +void +netsnmp_access_interface_container_free(netsnmp_container *container, u_int free_flags) +{ + DEBUGMSGTL(("access:interface:container", "free\n")); + + if (NULL == container) { + snmp_log(LOG_ERR, "invalid container for netsnmp_access_interface_free\n"); + return; + } + + if(! (free_flags & NETSNMP_ACCESS_INTERFACE_FREE_DONT_CLEAR)) { + /* + * free all items. + */ + CONTAINER_CLEAR(container, + (netsnmp_container_obj_func*)_access_interface_entry_release, + NULL); + } + + CONTAINER_FREE(container); +} + +/** + * @retval 0 interface not found + */ +oid +netsnmp_access_interface_index_find(const char *name) +{ + DEBUGMSGTL(("access:interface:find", "index\n")); + netsnmp_assert(1 == _access_interface_init); + + return netsnmp_arch_interface_index_find(name); +} +#endif + +/**---------------------------------------------------------------------*/ +/* + * ifentry functions + */ +/** + */ +netsnmp_interface_entry * +netsnmp_access_interface_entry_get_by_index(netsnmp_container *container, oid index) +{ + netsnmp_index tmp; + + DEBUGMSGTL(("access:interface:entry", "by_index\n")); + netsnmp_assert(1 == _access_interface_init); + + if (NULL == container) { + snmp_log(LOG_ERR, + "invalid container for netsnmp_access_interface_entry_get_by_index\n"); + return NULL; + } + + tmp.len = 1; + tmp.oids = &index; + + return (netsnmp_interface_entry *) CONTAINER_FIND(container, &tmp); +} + +/** + */ +netsnmp_interface_entry * +netsnmp_access_interface_entry_get_by_name(netsnmp_container *container, + const char *name) +{ + netsnmp_interface_entry tmp; + + DEBUGMSGTL(("access:interface:entry", "by_name\n")); + netsnmp_assert(1 == _access_interface_init); + + if (NULL == container) { + snmp_log(LOG_ERR, + "invalid container for netsnmp_access_interface_entry_get_by_name\n"); + return NULL; + } + + if (NULL == container->next) { + snmp_log(LOG_ERR, + "secondary index missing for netsnmp_access_interface_entry_get_by_name\n"); + return NULL; + } + + tmp.name = NETSNMP_REMOVE_CONST(char *, name); + return (netsnmp_interface_entry*)CONTAINER_FIND(container->next, &tmp); +} + +/** + * @retval NULL index not found + */ +const char * +netsnmp_access_interface_name_find(oid index) +{ + DEBUGMSGTL(("access:interface:find", "name\n")); + netsnmp_assert(1 == _access_interface_init); + + return se_find_label_in_slist("interfaces", index); +} + +/** + */ +netsnmp_interface_entry * +netsnmp_access_interface_entry_create(const char *name, oid if_index) +{ + netsnmp_interface_entry *entry = + SNMP_MALLOC_TYPEDEF(netsnmp_interface_entry); + + DEBUGMSGTL(("access:interface:entry", "create\n")); + netsnmp_assert(1 == _access_interface_init); + + if(NULL == entry) + return NULL; + + if(NULL != name) + entry->name = strdup(name); + + /* + * get if index, and save name for reverse lookup + */ +#ifndef NETSNMP_ACCESS_INTERFACE_NOARCH + if (0 == if_index) + entry->index = netsnmp_access_interface_index_find(name); + else +#endif + entry->index = if_index; + _access_interface_entry_save_name(name, entry->index); + + entry->descr = strdup(name); + + /* + * make some assumptions + */ + entry->connector_present = 1; + + entry->oid_index.len = 1; + entry->oid_index.oids = (oid *) & entry->index; + + return entry; +} + +/** + */ +void +netsnmp_access_interface_entry_free(netsnmp_interface_entry * entry) +{ + DEBUGMSGTL(("access:interface:entry", "free\n")); + + if (NULL == entry) + return; + + /* + * SNMP_FREE not needed, for any of these, + * since the whole entry is about to be freed + */ + + if (NULL != entry->old_stats) + free(entry->old_stats); + + if (NULL != entry->name) + free(entry->name); + + if (NULL != entry->descr) + free(entry->descr); + + if (NULL != entry->paddr) + free(entry->paddr); + + free(entry); +} + +#ifndef NETSNMP_FEATURE_REMOVE_INTERFACE_LEGACY +/* + * Blech - backwards compatible mibII/interfaces style interface + * functions, so we don't have to update older modules to use + * the new code to get correct ifIndex values. + */ +#if defined( USING_IF_MIB_IFTABLE_IFTABLE_DATA_ACCESS_MODULE ) && \ + ! defined( NETSNMP_NO_BACKWARDS_COMPATABILITY ) + +static netsnmp_iterator *it = NULL; +static ifTable_rowreq_ctx *row = NULL; + +/** + * Setup an iterator for scanning the interfaces using the cached entry + * from if-mib/ifTable. + */ +void +Interface_Scan_Init(void) +{ + netsnmp_container *cont = NULL; + netsnmp_cache *cache = NULL; + + cache = netsnmp_cache_find_by_oid(ifTable_oid, ifTable_oid_size); + if (NULL != cache) { + netsnmp_cache_check_and_reload(cache); + cont = (netsnmp_container*) cache->magic; + } + + if (NULL != cont) { + if (NULL != it) + ITERATOR_RELEASE(it); + + it = CONTAINER_ITERATOR(cont); + } + + if (NULL != it) + row = (ifTable_rowreq_ctx*)ITERATOR_FIRST(it); +} + +int +Interface_Scan_Next(short *index, char *name, netsnmp_interface_entry **entry, + void *dc) +{ + int returnIndex = 0; + int ret; + if (index) + returnIndex = *index; + + ret = Interface_Scan_NextInt( &returnIndex, name, entry, dc ); + if (index) + *index = (returnIndex & 0x8fff); + return ret; +} + +int +Interface_Scan_NextInt(int *index, char *name, netsnmp_interface_entry **entry, + void *dc) +{ + netsnmp_interface_entry* e = NULL; + + if (NULL == row) + return 0; + + e = row->data.ifentry; + if(index) + *index = e->index; + + if(name) + strcpy(name, e->name); + + if (entry) + *entry = e; + + row = (ifTable_rowreq_ctx*) ITERATOR_NEXT(it); + + return 1; +} +#endif /* NETSNMP_NO_BACKWARDS_COMPATABILITY */ +#endif /* NETSNMP_FEATURE_REMOVE_INTERFACE_LEGACY */ + +#ifndef NETSNMP_FEATURE_REMOVE_INTERFACE_ACCESS_ENTRY_SET_ADMIN_STATUS +/** + * + * @retval 0 : success + * @retval < 0 : error + */ +#ifndef NETSNMP_ACCESS_INTERFACE_NOARCH +int +netsnmp_access_interface_entry_set_admin_status(netsnmp_interface_entry * entry, + int ifAdminStatus) +{ + int rc; + + DEBUGMSGTL(("access:interface:entry", "set_admin_status\n")); + + if (NULL == entry) + return -1; + + if ((ifAdminStatus < IFADMINSTATUS_UP) || + (ifAdminStatus > IFADMINSTATUS_TESTING)) + return -2; + + rc = netsnmp_arch_set_admin_status(entry, ifAdminStatus); + if (0 == rc) /* success */ + entry->admin_status = ifAdminStatus; + + return rc; +} +#endif +#endif /* NETSNMP_FEATURE_REMOVE_INTERFACE_ACCESS_ENTRY_SET_ADMIN_STATUS */ + +/**---------------------------------------------------------------------*/ +/* + * Utility routines + */ + +/** + */ +static int +_access_interface_entry_compare_name(const void *lhs, const void *rhs) +{ + return strcmp(((const netsnmp_interface_entry *) lhs)->name, + ((const netsnmp_interface_entry *) rhs)->name); +} + +/** + */ +static void +_access_interface_entry_release(netsnmp_interface_entry * entry, void *context) +{ + netsnmp_access_interface_entry_free(entry); +} + +/** + */ +static void +_access_interface_entry_save_name(const char *name, oid index) +{ + int tmp; + + if(NULL == name) + return; + + tmp = se_find_value_in_slist("interfaces", name); + if (tmp == SE_DNE) { + se_add_pair_to_slist("interfaces", strdup(name), index); + DEBUGMSGTL(("access:interface:ifIndex", + "saved ifIndex %" NETSNMP_PRIo "u for %s\n", + index, name)); + } + else + if (index != (oid)tmp) { + NETSNMP_LOGONCE((LOG_ERR, "IfIndex of an interface changed. Such " \ + "interfaces will appear multiple times in IF-MIB.\n")); + DEBUGMSGTL(("access:interface:ifIndex", + "index %" NETSNMP_PRIo "u != tmp for %s\n", + index, name)); + } +} + +/** + * update stats + * + * @retval 0 : success + * @retval -1 : error + */ +int +netsnmp_access_interface_entry_update_stats(netsnmp_interface_entry * prev_vals, + netsnmp_interface_entry * new_vals) +{ + DEBUGMSGTL(("access:interface", "check_wrap\n")); + + /* + * sanity checks + */ + if ((NULL == prev_vals) || (NULL == new_vals) || + (NULL == prev_vals->name) || (NULL == new_vals->name) || + (0 != strncmp(prev_vals->name, new_vals->name, strlen(prev_vals->name)))) + return -1; + + /* + * if we've determined that we have 64 bit counters, just copy them. + */ + if (0 == need_wrap_check) { + memcpy(&prev_vals->stats, &new_vals->stats, sizeof(new_vals->stats)); + return 0; + } + + if (NULL == prev_vals->old_stats) { + /* + * if we don't have old stats, copy previous stats + */ + prev_vals->old_stats = SNMP_MALLOC_TYPEDEF(netsnmp_interface_stats); + if (NULL == prev_vals->old_stats) { + return -2; + } + memcpy(prev_vals->old_stats, &prev_vals->stats, sizeof(prev_vals->stats)); + } + + if (0 != netsnmp_c64_check32_and_update(&prev_vals->stats.ibytes, + &new_vals->stats.ibytes, + &prev_vals->old_stats->ibytes, + &need_wrap_check)) + DEBUGMSGTL(("access:interface", + "Error expanding ifHCInOctets to 64bits\n")); + + if (new_vals->ns_flags & NETSNMP_INTERFACE_FLAGS_CALCULATE_UCAST) { + if (0 != netsnmp_c64_check32_and_update(&prev_vals->stats.iall, + &new_vals->stats.iall, + &prev_vals->old_stats->iall, + &need_wrap_check)) + DEBUGMSGTL(("access:interface", + "Error expanding packet count to 64bits\n")); + } else { + if (0 != netsnmp_c64_check32_and_update(&prev_vals->stats.iucast, + &new_vals->stats.iucast, + &prev_vals->old_stats->iucast, + &need_wrap_check)) + DEBUGMSGTL(("access:interface", + "Error expanding ifHCInUcastPkts to 64bits\n")); + } + + if (0 != netsnmp_c64_check32_and_update(&prev_vals->stats.iucast, + &new_vals->stats.iucast, + &prev_vals->old_stats->iucast, + &need_wrap_check)) + DEBUGMSGTL(("access:interface", + "Error expanding ifHCInUcastPkts to 64bits\n")); + + if (0 != netsnmp_c64_check32_and_update(&prev_vals->stats.imcast, + &new_vals->stats.imcast, + &prev_vals->old_stats->imcast, + &need_wrap_check)) + DEBUGMSGTL(("access:interface", + "Error expanding ifHCInMulticastPkts to 64bits\n")); + + if (0 != netsnmp_c64_check32_and_update(&prev_vals->stats.ibcast, + &new_vals->stats.ibcast, + &prev_vals->old_stats->ibcast, + &need_wrap_check)) + DEBUGMSGTL(("access:interface", + "Error expanding ifHCInBroadcastPkts to 64bits\n")); + + if (0 != netsnmp_c64_check32_and_update(&prev_vals->stats.obytes, + &new_vals->stats.obytes, + &prev_vals->old_stats->obytes, + &need_wrap_check)) + DEBUGMSGTL(("access:interface", + "Error expanding ifHCOutOctets to 64bits\n")); + + if (0 != netsnmp_c64_check32_and_update(&prev_vals->stats.oucast, + &new_vals->stats.oucast, + &prev_vals->old_stats->oucast, + &need_wrap_check)) + DEBUGMSGTL(("access:interface", + "Error expanding ifHCOutUcastPkts to 64bits\n")); + + if (0 != netsnmp_c64_check32_and_update(&prev_vals->stats.omcast, + &new_vals->stats.omcast, + &prev_vals->old_stats->omcast, + &need_wrap_check)) + DEBUGMSGTL(("access:interface", + "Error expanding ifHCOutMulticastPkts to 64bits\n")); + + if (0 != netsnmp_c64_check32_and_update(&prev_vals->stats.obcast, + &new_vals->stats.obcast, + &prev_vals->old_stats->obcast, + &need_wrap_check)) + DEBUGMSGTL(("access:interface", + "Error expanding ifHCOutBroadcastPkts to 64bits\n")); + + /* + * Copy 32 bit counters + */ + prev_vals->stats.ierrors = new_vals->stats.ierrors; + prev_vals->stats.idiscards = new_vals->stats.idiscards; + prev_vals->stats.iunknown_protos = new_vals->stats.iunknown_protos; + prev_vals->stats.inucast = new_vals->stats.inucast; + prev_vals->stats.oerrors = new_vals->stats.oerrors; + prev_vals->stats.odiscards = new_vals->stats.odiscards; + prev_vals->stats.oqlen = new_vals->stats.oqlen; + prev_vals->stats.collisions = new_vals->stats.collisions; + prev_vals->stats.onucast = new_vals->stats.onucast; + + /* + * if we've decided we no longer need to check wraps, free old stats + */ + if (0 == need_wrap_check) { + SNMP_FREE(prev_vals->old_stats); + } + else { + /* + * update old stats from new stats. + * careful - old_stats is a pointer to stats... + */ + memcpy(prev_vals->old_stats, &new_vals->stats, sizeof(new_vals->stats)); + } + + return 0; +} + +/** + * Calculate stats + * + * @retval 0 : success + * @retval -1 : error + */ +int +netsnmp_access_interface_entry_calculate_stats(netsnmp_interface_entry *entry) +{ + DEBUGMSGTL(("access:interface", "calculate_stats\n")); + if (entry->ns_flags & NETSNMP_INTERFACE_FLAGS_CALCULATE_UCAST) { + u64Subtract(&entry->stats.iall, &entry->stats.imcast, + &entry->stats.iucast); + } + return 0; +} + +/** + * copy interface entry data (after checking for counter wraps) + * + * @retval -2 : malloc failed + * @retval -1 : interfaces not the same + * @retval 0 : no error + */ +int +netsnmp_access_interface_entry_copy(netsnmp_interface_entry * lhs, + netsnmp_interface_entry * rhs) +{ + DEBUGMSGTL(("access:interface", "copy\n")); + + if ((NULL == lhs) || (NULL == rhs) || + (NULL == lhs->name) || (NULL == rhs->name) || + (0 != strncmp(lhs->name, rhs->name, strlen(rhs->name)))) + return -1; + + /* + * update stats + */ + netsnmp_access_interface_entry_update_stats(lhs, rhs); + netsnmp_access_interface_entry_calculate_stats(lhs); + + /* + * update data + */ + lhs->ns_flags = rhs->ns_flags; + if((NULL != lhs->descr) && (NULL != rhs->descr) && + (0 == strcmp(lhs->descr, rhs->descr))) + ; + else { + SNMP_FREE(lhs->descr); + if (rhs->descr) { + lhs->descr = strdup(rhs->descr); + if(NULL == lhs->descr) + return -2; + } + } + lhs->type = rhs->type; + lhs->speed = rhs->speed; + lhs->speed_high = rhs->speed_high; + lhs->retransmit_v6 = rhs->retransmit_v6; + lhs->retransmit_v4 = rhs->retransmit_v4; + lhs->reachable_time = rhs->reachable_time; + lhs->mtu = rhs->mtu; + lhs->lastchange = rhs->lastchange; + lhs->discontinuity = rhs->discontinuity; + lhs->reasm_max_v4 = rhs->reasm_max_v4; + lhs->reasm_max_v6 = rhs->reasm_max_v6; + lhs->admin_status = rhs->admin_status; + lhs->oper_status = rhs->oper_status; + lhs->promiscuous = rhs->promiscuous; + lhs->connector_present = rhs->connector_present; + lhs->forwarding_v6 = rhs->forwarding_v6; + lhs->os_flags = rhs->os_flags; + if(lhs->paddr_len == rhs->paddr_len) { + if(rhs->paddr_len) + memcpy(lhs->paddr,rhs->paddr,rhs->paddr_len); + } else { + SNMP_FREE(lhs->paddr); + if (rhs->paddr) { + lhs->paddr = (char*)malloc(rhs->paddr_len); + if(NULL == lhs->paddr) + return -2; + memcpy(lhs->paddr,rhs->paddr,rhs->paddr_len); + } + } + lhs->paddr_len = rhs->paddr_len; + + return 0; +} + +void +netsnmp_access_interface_entry_guess_speed(netsnmp_interface_entry *entry) +{ + if (entry->type == IANAIFTYPE_ETHERNETCSMACD) + entry->speed = 10000000; + else if (entry->type == IANAIFTYPE_SOFTWARELOOPBACK) + entry->speed = 10000000; + else if (entry->type == IANAIFTYPE_ISO88025TOKENRING) + entry->speed = 4000000; + else + entry->speed = 0; + entry->speed_high = entry->speed / 1000000LL; +} + +netsnmp_conf_if_list * +netsnmp_access_interface_entry_overrides_get(const char * name) +{ + netsnmp_conf_if_list * if_ptr; + + netsnmp_assert(1 == _access_interface_init); + if(NULL == name) + return NULL; + + for (if_ptr = conf_list; if_ptr; if_ptr = if_ptr->next) + if (!strcmp(if_ptr->name, name)) + break; + + return if_ptr; +} + +void +netsnmp_access_interface_entry_overrides(netsnmp_interface_entry *entry) +{ + netsnmp_conf_if_list * if_ptr; + + netsnmp_assert(1 == _access_interface_init); + if (NULL == entry) + return; + + /* + * enforce mib size limit + */ + if(entry->descr && (strlen(entry->descr) > 255)) + entry->descr[255] = 0; + + if_ptr = + netsnmp_access_interface_entry_overrides_get(entry->name); + if (if_ptr) { + entry->type = if_ptr->type; + if (if_ptr->speed > 0xffffffff) { + entry->speed = 0xffffffff; + } else { + entry->speed = if_ptr->speed; + } + entry->speed_high = if_ptr->speed / 1000000LL; + } +} + +/**---------------------------------------------------------------------*/ +/* + * interface config token + */ +/** + */ +static void +_parse_interface_config(const char *token, char *cptr) +{ + netsnmp_conf_if_list *if_ptr, *if_new; + char *name, *type, *speed, *ecp; + char *st; + + name = strtok_r(cptr, " \t", &st); + if (!name) { + config_perror("Missing NAME parameter"); + return; + } + type = strtok_r(NULL, " \t", &st); + if (!type) { + config_perror("Missing TYPE parameter"); + return; + } + speed = strtok_r(NULL, " \t", &st); + if (!speed) { + config_perror("Missing SPEED parameter"); + return; + } + if_ptr = conf_list; + while (if_ptr) + if (strcmp(if_ptr->name, name)) + if_ptr = if_ptr->next; + else + break; + if (if_ptr) + config_pwarn("Duplicate interface specification"); + if_new = SNMP_MALLOC_TYPEDEF(netsnmp_conf_if_list); + if (!if_new) { + config_perror("Out of memory"); + return; + } + if_new->speed = strtoull(speed, &ecp, 0); + if (*ecp) { + config_perror("Bad SPEED value"); + free(if_new); + return; + } + if_new->type = strtol(type, &ecp, 0); + if (*ecp || if_new->type < 0) { + config_perror("Bad TYPE"); + free(if_new); + return; + } + if_new->name = strdup(name); + if (!if_new->name) { + config_perror("Out of memory"); + free(if_new); + return; + } + if_new->next = conf_list; + conf_list = if_new; +} + +static void +_free_interface_config(void) +{ + netsnmp_conf_if_list *if_ptr = conf_list, *if_next; + while (if_ptr) { + if_next = if_ptr->next; + free(NETSNMP_REMOVE_CONST(char *, if_ptr->name)); + free(if_ptr); + if_ptr = if_next; + } + conf_list = NULL; +} diff --git a/agent/mibgroup/if-mib/data_access/interface.h b/agent/mibgroup/if-mib/data_access/interface.h new file mode 100644 index 0000000..353b254 --- /dev/null +++ b/agent/mibgroup/if-mib/data_access/interface.h @@ -0,0 +1,58 @@ +/* + * interface data access header + * + * $Id$ + */ +#ifndef NETSNMP_ACCESS_INTERFACE_CONFIG_H +#define NETSNMP_ACCESS_INTERFACE_CONFIG_H + +/* + * all platforms use this generic code + */ +config_require(if-mib/data_access/interface) + +/**---------------------------------------------------------------------*/ +/* + * configure required files + * + * Notes: + * + * 1) prefer functionality over platform, where possible. If a method + * is available for multiple platforms, test that first. That way + * when a new platform is ported, it won't need a new test here. + * + * 2) don't do detail requirements here. If, for example, + * HPUX11 had different reuirements than other HPUX, that should + * be handled in the *_hpux.h header file. + */ + +#ifdef NETSNMP_INCLUDE_IFTABLE_REWRITES + +config_exclude(mibII/interfaces) + +# if defined( linux ) + + config_require(util_funcs) + config_require(if-mib/data_access/interface_linux) + config_require(if-mib/data_access/interface_ioctl) + +# elif defined( openbsd3 ) || \ + defined( freebsd4 ) || defined( freebsd5 ) || defined( freebsd6 ) || \ + defined( darwin ) || defined( dragonfly ) || defined( netbsd1 ) + + config_require(if-mib/data_access/interface_sysctl) + +# elif defined( solaris2 ) + + config_require(if-mib/data_access/interface_solaris2) + +# else + + config_error(This platform does not yet support IF-MIB rewrites) + +# endif +#else +# define NETSNMP_ACCESS_INTERFACE_NOARCH 1 +#endif + +#endif /* NETSNMP_ACCESS_INTERFACE_CONFIG_H */ diff --git a/agent/mibgroup/if-mib/data_access/interface_ioctl.c b/agent/mibgroup/if-mib/data_access/interface_ioctl.c new file mode 100644 index 0000000..db2c245 --- /dev/null +++ b/agent/mibgroup/if-mib/data_access/interface_ioctl.c @@ -0,0 +1,506 @@ +/* + * Interface MIB architecture support + * + * $Id$ + */ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-features.h> +#include <net-snmp/net-snmp-includes.h> +#include "mibII/mibII_common.h" +#include "if-mib/ifTable/ifTable_constants.h" + +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <net-snmp/data_access/interface.h> +#include <net-snmp/data_access/ipaddress.h> +#include "if-mib/data_access/interface.h" + +netsnmp_feature_child_of(interface_ioctl_flags_set, interface_all) + +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#ifdef HAVE_NET_IF_ARP_H +#include <net/if_arp.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#include "interface_ioctl.h" +#include "ip-mib/data_access/ipaddress_ioctl.h" + +/** + * ioctl wrapper + * + * @param fd : socket fd to use w/ioctl, or -1 to open/close one + * @param which + * @param ifrq + * param ifentry : ifentry to update + * @param name + * + * @retval 0 : success + * @retval -1 : invalid parameters + * @retval -2 : couldn't create socket + * @retval -3 : ioctl call failed + */ +static int +_ioctl_get(int fd, int which, struct ifreq *ifrq, const char* name) +{ + int ourfd = -1, rc = 0; + + DEBUGMSGTL(("verbose:access:interface:ioctl", + "ioctl %d for %s\n", which, name)); + + /* + * sanity checks + */ + if(NULL == name) { + snmp_log(LOG_ERR, "invalid ifentry\n"); + return -1; + } + + /* + * create socket for ioctls + */ + if(fd < 0) { + fd = ourfd = socket(AF_INET, SOCK_DGRAM, 0); + if(ourfd < 0) { + snmp_log(LOG_ERR,"couldn't create socket\n"); + return -2; + } + } + + strlcpy(ifrq->ifr_name, name, sizeof(ifrq->ifr_name)); + rc = ioctl(fd, which, ifrq); + if (rc < 0) { + snmp_log(LOG_ERR,"ioctl %d returned %d\n", which, rc); + rc = -3; + } + + if(ourfd >= 0) + close(ourfd); + + return rc; +} + +#ifdef SIOCGIFHWADDR +/** + * interface entry physaddr ioctl wrapper + * + * @param fd : socket fd to use w/ioctl, or -1 to open/close one + * @param ifentry : ifentry to update + * + * @retval 0 : success + * @retval -1 : invalid parameters + * @retval -2 : couldn't create socket + * @retval -3 : ioctl call failed + * @retval -4 : malloc error + */ +int +netsnmp_access_interface_ioctl_physaddr_get(int fd, + netsnmp_interface_entry *ifentry) +{ + struct ifreq ifrq; + int rc = 0; + + DEBUGMSGTL(("access:interface:ioctl", "physaddr_get\n")); + + if((NULL != ifentry->paddr) && + (ifentry->paddr_len != IFHWADDRLEN)) { + SNMP_FREE(ifentry->paddr); + } + if(NULL == ifentry->paddr) + ifentry->paddr = (char*)malloc(IFHWADDRLEN); + + if(NULL == ifentry->paddr) { + rc = -4; + } else { + + /* + * NOTE: this ioctl does not guarantee 6 bytes of a physaddr. + * In particular, a 'sit0' interface only appears to get back + * 4 bytes of sa_data. Uncomment this memset, and suddenly + * the sit interface will be 0:0:0:0:?:? where ? is whatever was + * in the memory before. Not sure if this memset should be done + * for every ioctl, as the rest seem to work ok... + */ + memset(ifrq.ifr_hwaddr.sa_data, (0), IFHWADDRLEN); + ifentry->paddr_len = IFHWADDRLEN; + rc = _ioctl_get(fd, SIOCGIFHWADDR, &ifrq, ifentry->name); + if (rc < 0) { + memset(ifentry->paddr, (0), IFHWADDRLEN); + rc = -3; /* msg already logged */ + } + else { + memcpy(ifentry->paddr, ifrq.ifr_hwaddr.sa_data, IFHWADDRLEN); + + /* + * arphrd defines vary greatly. ETHER seems to be the only common one + */ +#ifdef ARPHRD_ETHER + switch (ifrq.ifr_hwaddr.sa_family) { + case ARPHRD_ETHER: + ifentry->type = IANAIFTYPE_ETHERNETCSMACD; + break; +#if defined(ARPHRD_TUNNEL) || defined(ARPHRD_IPGRE) || defined(ARPHRD_SIT) +#ifdef ARPHRD_TUNNEL + case ARPHRD_TUNNEL: + case ARPHRD_TUNNEL6: +#endif +#ifdef ARPHRD_IPGRE + case ARPHRD_IPGRE: +#endif +#ifdef ARPHRD_SIT + case ARPHRD_SIT: +#endif + ifentry->type = IANAIFTYPE_TUNNEL; + break; /* tunnel */ +#endif +#ifdef ARPHRD_INFINIBAND + case ARPHRD_INFINIBAND: + ifentry->type = IANAIFTYPE_INFINIBAND; + break; +#endif +#ifdef ARPHRD_SLIP + case ARPHRD_SLIP: + case ARPHRD_CSLIP: + case ARPHRD_SLIP6: + case ARPHRD_CSLIP6: + ifentry->type = IANAIFTYPE_SLIP; + break; /* slip */ +#endif +#ifdef ARPHRD_PPP + case ARPHRD_PPP: + ifentry->type = IANAIFTYPE_PPP; + break; /* ppp */ +#endif +#ifdef ARPHRD_LOOPBACK + case ARPHRD_LOOPBACK: + ifentry->type = IANAIFTYPE_SOFTWARELOOPBACK; + break; /* softwareLoopback */ +#endif +#ifdef ARPHRD_FDDI + case ARPHRD_FDDI: + ifentry->type = IANAIFTYPE_FDDI; + break; +#endif +#ifdef ARPHRD_ARCNET + case ARPHRD_ARCNET: + ifentry->type = IANAIFTYPE_ARCNET; + break; +#endif +#ifdef ARPHRD_LOCALTLK + case ARPHRD_LOCALTLK: + ifentry->type = IANAIFTYPE_LOCALTALK; + break; +#endif +#ifdef ARPHRD_HIPPI + case ARPHRD_HIPPI: + ifentry->type = IANAIFTYPE_HIPPI; + break; +#endif +#ifdef ARPHRD_ATM + case ARPHRD_ATM: + ifentry->type = IANAIFTYPE_ATM; + break; +#endif + /* + * XXX: more if_arp.h:ARPHRD_xxx to IANAifType mappings... + */ + default: + DEBUGMSGTL(("access:interface:ioctl", "unknown entry type %d\n", + ifrq.ifr_hwaddr.sa_family)); + ifentry->type = IANAIFTYPE_OTHER; + } /* switch */ +#endif /* ARPHRD_LOOPBACK */ + + } + } + + return rc; +} +#endif /* SIOCGIFHWADDR */ + + +#ifdef SIOCGIFFLAGS +/** + * interface entry flags ioctl wrapper + * + * @param fd : socket fd to use w/ioctl, or -1 to open/close one + * @param ifentry : ifentry to update + * + * @retval 0 : success + * @retval -1 : invalid parameters + * @retval -2 : couldn't create socket + * @retval -3 : ioctl call failed + */ +int +netsnmp_access_interface_ioctl_flags_get(int fd, + netsnmp_interface_entry *ifentry) +{ + struct ifreq ifrq; + int rc = 0; + + DEBUGMSGTL(("access:interface:ioctl", "flags_get\n")); + + rc = _ioctl_get(fd, SIOCGIFFLAGS, &ifrq, ifentry->name); + if (rc < 0) { + ifentry->ns_flags &= ~NETSNMP_INTERFACE_FLAGS_HAS_IF_FLAGS; + return rc; /* msg already logged */ + } + else { + ifentry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_IF_FLAGS; + ifentry->os_flags = ifrq.ifr_flags; + + /* + * ifOperStatus description: + * If ifAdminStatus is down(2) then ifOperStatus should be down(2). + */ + if(ifentry->os_flags & IFF_UP) { + ifentry->admin_status = IFADMINSTATUS_UP; + if(ifentry->os_flags & IFF_RUNNING) + ifentry->oper_status = IFOPERSTATUS_UP; + else + ifentry->oper_status = IFOPERSTATUS_DOWN; + } + else { + ifentry->admin_status = IFADMINSTATUS_DOWN; + ifentry->oper_status = IFOPERSTATUS_DOWN; + } + + /* + * ifConnectorPresent description: + * This object has the value 'true(1)' if the interface sublayer has a + * physical connector and the value 'false(2)' otherwise." + * So, at very least, false(2) should be returned for loopback devices. + */ + if(ifentry->os_flags & IFF_LOOPBACK) { + ifentry->connector_present = 0; + } + else { + ifentry->connector_present = 1; + } + } + + return rc; +} + +#ifndef NETSNMP_FEATURE_REMOVE_INTERFACE_IOCTL_FLAGS_SET +/** + * interface entry flags ioctl wrapper + * + * @param fd : socket fd to use w/ioctl, or -1 to open/close one + * @param ifentry : ifentry to update + * + * @retval 0 : success + * @retval -1 : invalid parameters + * @retval -2 : couldn't create socket + * @retval -3 : ioctl get call failed + * @retval -4 : ioctl set call failed + */ +int +netsnmp_access_interface_ioctl_flags_set(int fd, + netsnmp_interface_entry *ifentry, + unsigned int flags, int and_complement) +{ + struct ifreq ifrq; + int ourfd = -1, rc = 0; + + DEBUGMSGTL(("access:interface:ioctl", "flags_set\n")); + + /* + * sanity checks + */ + if((NULL == ifentry) || (NULL == ifentry->name)) { + snmp_log(LOG_ERR, "invalid ifentry\n"); + return -1; + } + + /* + * create socket for ioctls + */ + if(fd < 0) { + fd = ourfd = socket(AF_INET, SOCK_DGRAM, 0); + if(ourfd < 0) { + snmp_log(LOG_ERR,"couldn't create socket\n"); + return -2; + } + } + + strlcpy(ifrq.ifr_name, ifentry->name, sizeof(ifrq.ifr_name)); + rc = ioctl(fd, SIOCGIFFLAGS, &ifrq); + if(rc < 0) { + snmp_log(LOG_ERR,"error getting flags\n"); + close(fd); + return -3; + } + if(0 == and_complement) + ifrq.ifr_flags |= flags; + else + ifrq.ifr_flags &= ~flags; + rc = ioctl(fd, SIOCSIFFLAGS, &ifrq); + if(rc < 0) { + close(fd); + snmp_log(LOG_ERR,"error setting flags\n"); + ifentry->os_flags = 0; + return -4; + } + + if(ourfd >= 0) + close(ourfd); + + ifentry->os_flags = ifrq.ifr_flags; + + return 0; +} +#endif /* NETSNMP_FEATURE_REMOVE_INTERFACE_IOCTL_FLAGS_SET */ +#endif /* SIOCGIFFLAGS */ + +#ifdef SIOCGIFMTU +/** + * interface entry mtu ioctl wrapper + * + * @param fd : socket fd to use w/ioctl, or -1 to open/close one + * @param ifentry : ifentry to update + * + * @retval 0 : success + * @retval -1 : invalid parameters + * @retval -2 : couldn't create socket + * @retval -3 : ioctl call failed + */ +int +netsnmp_access_interface_ioctl_mtu_get(int fd, + netsnmp_interface_entry *ifentry) +{ + struct ifreq ifrq; + int rc = 0; + + DEBUGMSGTL(("access:interface:ioctl", "mtu_get\n")); + + rc = _ioctl_get(fd, SIOCGIFMTU, &ifrq, ifentry->name); + if (rc < 0) { + ifentry->mtu = 0; + return rc; /* msg already logged */ + } + else { + ifentry->mtu = ifrq.ifr_mtu; + } + + return rc; +} +#endif /* SIOCGIFMTU */ + +/** + * interface entry ifIndex ioctl wrapper + * + * @param fd : socket fd to use w/ioctl, or -1 to open/close one + * @param name : ifentry to update + * + * @retval 0 : not found + * @retval !0 : ifIndex + */ +oid +netsnmp_access_interface_ioctl_ifindex_get(int fd, const char *name) +{ +#ifndef SIOCGIFINDEX + return 0; +#else + struct ifreq ifrq; + int rc = 0; + + DEBUGMSGTL(("access:interface:ioctl", "ifindex_get\n")); + + rc = _ioctl_get(fd, SIOCGIFINDEX, &ifrq, name); + if (rc < 0) { + DEBUGMSGTL(("access:interface:ioctl", + "ifindex_get error on inerface '%s'\n", name)); + return 0; + } + +#if defined(__FreeBSD__) /* ? Should use HAVE_STRUCT_IFREQ_IFR_INDEX */ + return ifrq.ifr_index; +#else + return ifrq.ifr_ifindex; +#endif +#endif /* SIOCGIFINDEX */ +} + +/** + * check an interface for ipv4 addresses + * + * @param sd : open socket descriptor + * @param if_name : optional name. takes precedent over if_index. + * @param if_index: optional if index. only used if no if_name specified + * @param flags : + * + * @retval < 0 : error + * @retval 0 : no ip v4 addresses + * @retval 1 : 1 or more ip v4 addresses + */ +int +netsnmp_access_interface_ioctl_has_ipv4(int sd, const char *if_name, + int if_index, u_int *flags) +{ + int i, interfaces = 0; + struct ifconf ifc; + struct ifreq *ifrp; + + /* + * one or the other + */ + if ((NULL == flags) || + ((0 == if_index) && (NULL == if_name))) { + return -1; + } + + interfaces = netsnmp_access_ipaddress_ioctl_get_interface_count(sd, &ifc); + if(interfaces < 0) { + close(sd); + return -2; + } + netsnmp_assert(NULL != ifc.ifc_buf); + + *flags &= ~NETSNMP_INTERFACE_FLAGS_HAS_IPV4; + + ifrp = ifc.ifc_req; + for(i=0; i < interfaces; ++i, ++ifrp) { + + DEBUGMSGTL(("access:ipaddress:container", + " interface %d, %s\n", i, ifrp->ifr_name)); + + /* + * search for matching if_name or if_index + */ + if (NULL != if_name) { + if (strncmp(if_name, ifrp->ifr_name, sizeof(ifrp->ifr_name)) != 0) + continue; + } + else { + /* + * I think that Linux and Solaris both use ':' in the + * interface name for aliases. + */ + char *ptr = strchr(ifrp->ifr_name, ':'); + if (NULL != ptr) + *ptr = 0; + + if (if_index != (int)netsnmp_access_interface_ioctl_ifindex_get(sd, ifrp->ifr_name)) + continue; + } + + /* + * check and set v4 or v6 flag, and break if we've found both + */ + if (AF_INET == ifrp->ifr_addr.sa_family) { + *flags |= NETSNMP_INTERFACE_FLAGS_HAS_IPV4; + break; + } + } + + /* + * clean up + */ + free(ifc.ifc_buf); + + return 0; +} diff --git a/agent/mibgroup/if-mib/data_access/interface_ioctl.h b/agent/mibgroup/if-mib/data_access/interface_ioctl.h new file mode 100644 index 0000000..d765876 --- /dev/null +++ b/agent/mibgroup/if-mib/data_access/interface_ioctl.h @@ -0,0 +1,52 @@ +/* + * ioctl interface data access header + * + * $Id$ + */ +#ifndef NETSNMP_ACCESS_INTERFACE_IOCTL_H +#define NETSNMP_ACCESS_INTERFACE_IOCTL_H + +/* + * need ipaddress functions to get ipversions of an interface +*/ +config_require(ip-mib/data_access/ipaddress) + +#ifdef __cplusplus +extern "C" { +#endif + +/**---------------------------------------------------------------------*/ +/**/ + +int +netsnmp_access_interface_ioctl_physaddr_get(int fd, + netsnmp_interface_entry *ifentry); + +int +netsnmp_access_interface_ioctl_flags_get(int fd, + netsnmp_interface_entry *ifentry); + +int +netsnmp_access_interface_ioctl_flags_set(int fd, + netsnmp_interface_entry *ifentry, + unsigned int flags, + int and_complement); + +int +netsnmp_access_interface_ioctl_mtu_get(int fd, + netsnmp_interface_entry *ifentry); + +oid +netsnmp_access_interface_ioctl_ifindex_get(int fd, const char *name); + +int +netsnmp_access_interface_ioctl_has_ipv4(int sd, const char *if_name, + int if_index, u_int *flags); + +/**---------------------------------------------------------------------*/ + +# ifdef __cplusplus +} +#endif + +#endif /* NETSNMP_ACCESS_INTERFACE_IOCTL_H */ diff --git a/agent/mibgroup/if-mib/data_access/interface_linux.c b/agent/mibgroup/if-mib/data_access/interface_linux.c new file mode 100644 index 0000000..3419811 --- /dev/null +++ b/agent/mibgroup/if-mib/data_access/interface_linux.c @@ -0,0 +1,1213 @@ +/* + * Interface MIB architecture support + * + * $Id$ + */ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-features.h> +#include <net-snmp/net-snmp-includes.h> + +netsnmp_feature_require(fd_event_manager) +netsnmp_feature_require(delete_prefix_info) +netsnmp_feature_require(create_prefix_info) +netsnmp_feature_child_of(interface_arch_set_admin_status, interface_all) + +#ifdef NETSNMP_FEATURE_REQUIRE_INTERFACE_ARCH_SET_ADMIN_STATUS +netsnmp_feature_require(interface_ioctl_flags_set) +#endif /* NETSNMP_FEATURE_REQUIRE_INTERFACE_ARCH_SET_ADMIN_STATUS */ + +#ifdef HAVE_PCI_LOOKUP_NAME +#include <pci/pci.h> +static struct pci_access *pci_access; +#endif + +#ifdef HAVE_LINUX_ETHTOOL_H +#include <linux/types.h> +#ifndef HAVE_PCI_LOOKUP_NAME +typedef __u64 u64; /* hack, so we may include kernel's ethtool.h */ +typedef __u32 u32; /* ditto */ +typedef __u16 u16; /* ditto */ +typedef __u8 u8; /* ditto */ +#endif + +#include <linux/ethtool.h> +#endif /* HAVE_LINUX_ETHTOOL_H */ + +#include "mibII/mibII_common.h" +#include "if-mib/ifTable/ifTable_constants.h" + +#include <net-snmp/agent/net-snmp-agent-includes.h> + +#if HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#else +#error "linux should have sys/ioctl header" +#endif + +#include <net-snmp/data_access/interface.h> +#include <net-snmp/data_access/ipaddress.h> +#include "if-mib/data_access/interface.h" +#include "mibgroup/util_funcs.h" +#include "interface_ioctl.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> + +#include <linux/sockios.h> +#include <linux/if_ether.h> + +#ifndef IF_NAMESIZE +#define IF_NAMESIZE 16 +#endif + +#ifndef SIOCGMIIPHY +#define SIOCGMIIPHY 0x8947 +#endif + +#ifndef SIOCGMIIREG +#define SIOCGMIIREG 0x8948 +#endif + +#ifdef NETSNMP_ENABLE_IPV6 +#if defined(HAVE_LINUX_RTNETLINK_H) +#include <linux/rtnetlink.h> +#ifdef RTMGRP_IPV6_PREFIX +#define SUPPORT_PREFIX_FLAGS 1 +#endif /* RTMGRP_IPV6_PREFIX */ +#endif /* HAVE_LINUX_RTNETLINK_H */ +#endif /* NETSNMP_ENABLE_IPV6 */ +unsigned long long +netsnmp_linux_interface_get_if_speed(int fd, const char *name, + unsigned long long defaultspeed); +#ifdef HAVE_LINUX_ETHTOOL_H +unsigned long long +netsnmp_linux_interface_get_if_speed_mii(int fd, const char *name, + unsigned long long defaultspeed); +#endif + +#define PROC_SYS_NET_IPVx_NEIGH_RETRANS_TIME_MS "/proc/sys/net/ipv%d/neigh/%s/retrans_time_ms" +#define PROC_SYS_NET_IPVx_NEIGH_RETRANS_TIME "/proc/sys/net/ipv%d/neigh/%s/retrans_time" +static const char *proc_sys_retrans_time; +static unsigned short retrans_time_factor = 1; + + +#define PROC_SYS_NET_IPVx_BASE_REACHABLE_TIME_MS "/proc/sys/net/ipv%d/neigh/%s/base_reachable_time_ms" +#define PROC_SYS_NET_IPVx_BASE_REACHABLE_TIME "/proc/sys/net/ipv%d/neigh/%s/base_reachable_time" +static const char *proc_sys_basereachable_time; +static unsigned short basereachable_time_ms = 0; +#ifdef SUPPORT_PREFIX_FLAGS +prefix_cbx *prefix_head_list = NULL; +netsnmp_prefix_listen_info list_info; +#define IF_PREFIX_ONLINK 0x01 +#define IF_PREFIX_AUTOCONF 0x02 + +int netsnmp_prefix_listen(void); +#endif + + +void +netsnmp_arch_interface_init(void) +{ + /* + * Check which retransmit time interface is available + */ + char proc_path[ 64+IF_NAMESIZE]; + char proc_path2[64+IF_NAMESIZE]; + struct stat st; + + snprintf(proc_path, sizeof(proc_path), + PROC_SYS_NET_IPVx_NEIGH_RETRANS_TIME_MS, 6, "default"); + snprintf(proc_path2, sizeof(proc_path2), + PROC_SYS_NET_IPVx_NEIGH_RETRANS_TIME_MS, 4, "default"); + + if ((stat(proc_path, &st) == 0) || (stat(proc_path2, &st) == 0)) { + proc_sys_retrans_time = PROC_SYS_NET_IPVx_NEIGH_RETRANS_TIME_MS; + } else { + proc_sys_retrans_time = PROC_SYS_NET_IPVx_NEIGH_RETRANS_TIME; + retrans_time_factor = 10; + } + + snprintf(proc_path, sizeof(proc_path), PROC_SYS_NET_IPVx_BASE_REACHABLE_TIME_MS, 6, "default"); + snprintf(proc_path2, sizeof(proc_path), PROC_SYS_NET_IPVx_BASE_REACHABLE_TIME, 4, "default"); + + if ((stat(proc_path, &st) == 0) || (stat(proc_path2, &st) == 0)) { + proc_sys_basereachable_time = PROC_SYS_NET_IPVx_BASE_REACHABLE_TIME_MS; + basereachable_time_ms = 1; + } + else { + proc_sys_basereachable_time = PROC_SYS_NET_IPVx_BASE_REACHABLE_TIME; + } + +#ifdef SUPPORT_PREFIX_FLAGS + list_info.list_head = &prefix_head_list; + netsnmp_prefix_listen(); +#endif + +#ifdef HAVE_PCI_LOOKUP_NAME + pci_access = pci_alloc(); + if (pci_access) + pci_init(pci_access); + else + snmp_log(LOG_ERR, "Unable to create pci access method\n"); +#endif +} + +/* + * find the ifIndex for an interface name + * NOTE: The Linux version is not efficient for large numbers of calls. + * consider using netsnmp_access_interface_ioctl_ifindex_get() + * for loops which need to look up a lot of indexes. + * + * @retval 0 : no index found + * @retval >0: ifIndex for interface + */ +oid +netsnmp_arch_interface_index_find(const char *name) +{ + return netsnmp_access_interface_ioctl_ifindex_get(-1, name); +} + + +/* + * check for ipv6 addresses + */ +void +_arch_interface_has_ipv6(oid if_index, u_int *flags, + netsnmp_container *addr_container) +{ +#ifdef NETSNMP_ENABLE_IPV6 + netsnmp_ipaddress_entry *addr_entry = NULL; + netsnmp_iterator *addr_it = NULL; + u_int addr_container_flags = 0; /* must init to 0 */ +#endif + + if (NULL == flags) + return; + + *flags &= ~NETSNMP_INTERFACE_FLAGS_HAS_IPV6; + +#ifdef NETSNMP_ENABLE_IPV6 + /* + * get ipv6 addresses + */ + if (NULL == addr_container) { + /* + * we only care about ipv6, if we need to allocate our own + * temporary container. set the flags (which we also use later + * to determine if we need to free the container). + */ + addr_container_flags = NETSNMP_ACCESS_IPADDRESS_LOAD_IPV6_ONLY; + addr_container = + netsnmp_access_ipaddress_container_load(NULL, + addr_container_flags); + if (NULL == addr_container) { + DEBUGMSGTL(("access:ifcontainer", + "couldn't get ip addresses container\n")); + return; + } + } + else { + /* + * addr_container flags must be 0, so we don't release the + * user's container. + */ + netsnmp_assert(0 == addr_container_flags); + } + + + /* + * get an ipaddress container iterator, and look for ipv6 addrs + */ + addr_it = CONTAINER_ITERATOR(addr_container); + if (NULL == addr_it) { + DEBUGMSGTL(("access:ifcontainer", + "couldn't get ip addresses iterator\n")); + if (0!=addr_container_flags) + netsnmp_access_ipaddress_container_free(addr_container, 0); + return; + } + + addr_entry = ITERATOR_FIRST(addr_it); + for( ; addr_entry ; addr_entry = ITERATOR_NEXT(addr_it) ) { + /* + * skip non matching indexes and ipv4 addresses + */ + if ((if_index != addr_entry->if_index) || + (4 == addr_entry->ia_address_len)) + continue; + + /* + * found one! no need to keep looking, set the flag and bail + */ + *flags |= NETSNMP_INTERFACE_FLAGS_HAS_IPV6; + break; + } + + /* + * make mama proud and clean up after ourselves + */ + ITERATOR_RELEASE(addr_it); + if (0!=addr_container_flags) + netsnmp_access_ipaddress_container_free(addr_container, 0); +#endif +} + +/** + * @internal + */ +static void +_arch_interface_flags_v4_get(netsnmp_interface_entry *entry) +{ + FILE *fin; + char line[256]; + + /* + * get the retransmit time + */ + snprintf(line,sizeof(line), proc_sys_retrans_time, 4, + entry->name); + if (!(fin = fopen(line, "r"))) { + DEBUGMSGTL(("access:interface", + "Failed to open %s\n", line)); + } + else { + if (fgets(line, sizeof(line), fin)) { + entry->retransmit_v4 = atoi(line) * retrans_time_factor; + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_V4_RETRANSMIT; + } + fclose(fin); + } +} + +#ifdef HAVE_PCI_LOOKUP_NAME + +/* Get value from sysfs file */ +static int sysfs_get_id(const char *path, unsigned short *id) +{ + FILE *fin; + int n; + + if (!(fin = fopen(path, "r"))) { + DEBUGMSGTL(("access:interface", + "Failed to open %s\n", path)); + return 0; + } + + n = fscanf(fin, "%hx", id); + fclose(fin); + + return n == 1; +} + +/* Get interface description for PCI device + * by using sysfs to find vendor and device + * then lookup name (-lpci) + * + * For software interfaces there is no PCI information + * so description will not be set. + */ +static void +_arch_interface_description_get(netsnmp_interface_entry *entry) +{ + const char *descr; + char buf[256]; + unsigned short vendor_id, device_id; + + if (!pci_access) + return; + + snprintf(buf, sizeof(buf), + "/sys/class/net/%s/device/vendor", entry->name); + + if (!sysfs_get_id(buf, &vendor_id)) + return; + + snprintf(buf, sizeof(buf), + "/sys/class/net/%s/device/device", entry->name); + + if (!sysfs_get_id(buf, &device_id)) + return; + + descr = pci_lookup_name(pci_access, buf, sizeof(buf), + PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE, + vendor_id, device_id, 0, 0); + if (descr) { + free(entry->descr); + entry->descr = strdup(descr); + } else { + DEBUGMSGTL(("access:interface", + "Failed pci_lookup_name vendor=%#hx device=%#hx\n", + vendor_id, device_id)); + } +} +#endif + + +#ifdef NETSNMP_ENABLE_IPV6 +/** + * @internal + */ +static void +_arch_interface_flags_v6_get(netsnmp_interface_entry *entry) +{ + FILE *fin; + char line[256]; + + /* + * get the retransmit time + */ + snprintf(line,sizeof(line), proc_sys_retrans_time, 6, + entry->name); + if (!(fin = fopen(line, "r"))) { + DEBUGMSGTL(("access:interface", + "Failed to open %s\n", line)); + } + else { + if (fgets(line, sizeof(line), fin)) { + entry->retransmit_v6 = atoi(line) * retrans_time_factor; + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_V6_RETRANSMIT; + } + fclose(fin); + } + + /* + * get the forwarding status + */ + snprintf(line, sizeof(line), "/proc/sys/net/ipv6/conf/%s/forwarding", + entry->name); + if (!(fin = fopen(line, "r"))) { + DEBUGMSGTL(("access:interface", + "Failed to open %s\n", line)); + } + else { + if (fgets(line, sizeof(line), fin)) { + entry->forwarding_v6 = atoi(line); + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_V6_FORWARDING; + } + fclose(fin); + } + + /* + * get the reachable time + */ + snprintf(line, sizeof(line), proc_sys_basereachable_time, 6, entry->name); + if (!(fin = fopen(line, "r"))) { + DEBUGMSGTL(("access:interface", + "Failed to open %s\n", line)); + } + else { + if (fgets(line, sizeof(line), fin)) { + if (basereachable_time_ms) { + entry->reachable_time = atoi(line); /* millisec */ + } else { + entry->reachable_time = atoi(line)*1000; /* sec to millisec */ + } + + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_V6_REACHABLE; + } + fclose(fin); + } +} +#endif /* NETSNMP_ENABLE_IPV6 */ + +/** + * @internal + */ +static int +_parse_stats(netsnmp_interface_entry *entry, char *stats, int expected) +{ + /* + * scanline_2_2: + * [ IN ] + * byte pkts errs drop fifo frame cmprs mcst | + * [ OUT ] + * byte pkts errs drop fifo colls carrier compressed + */ +#ifdef SCNuMAX + uintmax_t rec_pkt, rec_oct, rec_err, rec_drop, rec_mcast; + uintmax_t snd_pkt, snd_oct, snd_err, snd_drop, coll; + const char *scan_line_2_2 = + "%" SCNuMAX " %" SCNuMAX " %" SCNuMAX " %" SCNuMAX + " %*" SCNuMAX " %*" SCNuMAX " %*" SCNuMAX " %" SCNuMAX + " %" SCNuMAX " %" SCNuMAX " %" SCNuMAX " %" SCNuMAX + " %*" SCNuMAX " %" SCNuMAX; + const char *scan_line_2_0 = + "%" SCNuMAX " %" SCNuMAX " %*" SCNuMAX " %*" SCNuMAX + " %*" SCNuMAX " %" SCNuMAX " %" SCNuMAX " %*" SCNuMAX + " %*" SCNuMAX " %" SCNuMAX; +#else + unsigned long rec_pkt, rec_oct, rec_err, rec_drop, rec_mcast; + unsigned long snd_pkt, snd_oct, snd_err, snd_drop, coll; + const char *scan_line_2_2 = + "%lu %lu %lu %lu %*lu %*lu %*lu %lu %lu %lu %lu %lu %*lu %lu"; + const char *scan_line_2_0 = + "%lu %lu %*lu %*lu %*lu %lu %lu %*lu %*lu %lu"; +#endif + static const char *scan_line_to_use = NULL; + int scan_count; + + if (10 == expected) + scan_line_to_use = scan_line_2_2; + else { + netsnmp_assert(5 == expected); + scan_line_to_use = scan_line_2_0; + } + + while (*stats == ' ') + stats++; + + if ((*stats == 'N') && + (0 == strncmp(stats, "No statistics available", + strlen("No statistics available")))) + return -1; + + /* + * Now parse the rest of the line (i.e. starting from 'stats') + * to extract the relevant statistics, and populate + * data structure accordingly. + * Use the entry flags field to indicate which counters are valid + */ + rec_pkt = rec_oct = rec_err = rec_drop = rec_mcast = 0; + snd_pkt = snd_oct = snd_err = snd_drop = coll = 0; + if (scan_line_to_use == scan_line_2_2) { + scan_count = sscanf(stats, scan_line_to_use, + &rec_oct, &rec_pkt, &rec_err, &rec_drop, &rec_mcast, + &snd_oct, &snd_pkt, &snd_err, &snd_drop, + &coll); + if (scan_count == expected) { + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_BYTES; + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_DROPS; + /* + * 2.4 kernel includes a single multicast (input) counter? + */ + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_MCAST_PKTS; + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_HIGH_SPEED; +#ifdef SCNuMAX /* XXX - should be flag for 64-bit variables */ + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_HIGH_BYTES; + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_HIGH_PACKETS; +#endif + } + } else { + scan_count = sscanf(stats, scan_line_to_use, + &rec_pkt, &rec_err, + &snd_pkt, &snd_err, &coll); + if (scan_count == expected) { + entry->ns_flags &= ~NETSNMP_INTERFACE_FLAGS_HAS_MCAST_PKTS; + rec_oct = rec_drop = 0; + snd_oct = snd_drop = 0; + } + } + if(scan_count != expected) { + snmp_log(LOG_ERR, + "error scanning interface data (expected %d, got %d)\n", + expected, scan_count); + return scan_count; + } + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_ACTIVE; + + /* + * linux previous to 1.3.~13 may miss transmitted loopback pkts: + */ + if (!strcmp(entry->name, "lo") && rec_pkt > 0 && !snd_pkt) + snd_pkt = rec_pkt; + + /* + * subtract out multicast packets from rec_pkt before + * we store it as unicast counter. + */ + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_CALCULATE_UCAST; + entry->stats.ibytes.low = rec_oct & 0xffffffff; + entry->stats.iall.low = rec_pkt & 0xffffffff; + entry->stats.imcast.low = rec_mcast & 0xffffffff; + entry->stats.obytes.low = snd_oct & 0xffffffff; + entry->stats.oucast.low = snd_pkt & 0xffffffff; +#ifdef SCNuMAX /* XXX - should be flag for 64-bit variables */ + entry->stats.ibytes.high = rec_oct >> 32; + entry->stats.iall.high = rec_pkt >> 32; + entry->stats.imcast.high = rec_mcast >> 32; + entry->stats.obytes.high = snd_oct >> 32; + entry->stats.oucast.high = snd_pkt >> 32; +#endif + entry->stats.ierrors = rec_err; + entry->stats.idiscards = rec_drop; + entry->stats.oerrors = snd_err; + entry->stats.odiscards = snd_drop; + entry->stats.collisions = coll; + + /* + * calculated stats. + * + * we have imcast, but not ibcast. + */ + entry->stats.inucast = entry->stats.imcast.low + + entry->stats.ibcast.low; + entry->stats.onucast = entry->stats.omcast.low + + entry->stats.obcast.low; + + return 0; +} + +/* + * + * @retval 0 success + * @retval -1 no container specified + * @retval -2 could not open /proc/net/dev + * @retval -3 could not create entry (probably malloc) + */ +int +netsnmp_arch_interface_container_load(netsnmp_container* container, + u_int load_flags) +{ + FILE *devin; + char line[256]; + netsnmp_interface_entry *entry = NULL; + static char scan_expected = 0; + int fd; +#ifdef NETSNMP_ENABLE_IPV6 + netsnmp_container *addr_container; +#endif + + DEBUGMSGTL(("access:interface:container:arch", "load (flags %x)\n", + load_flags)); + + if (NULL == container) { + snmp_log(LOG_ERR, "no container specified/found for interface\n"); + return -1; + } + + if (!(devin = fopen("/proc/net/dev", "r"))) { + DEBUGMSGTL(("access:interface", + "Failed to load Interface Table (linux1)\n")); + NETSNMP_LOGONCE((LOG_ERR, "cannot open /proc/net/dev ...\n")); + return -2; + } + + /* + * create socket for ioctls + */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if(fd < 0) { + snmp_log(LOG_ERR, "could not create socket\n"); + fclose(devin); + return -2; + } + +#ifdef NETSNMP_ENABLE_IPV6 + /* + * get ipv6 addresses + */ + addr_container = netsnmp_access_ipaddress_container_load(NULL, 0); +#endif + + /* + * Read the first two lines of the file, containing the header + * This indicates which version of the kernel we're working with, + * and hence which statistics are actually available. + * + * Wes originally suggested parsing the field names in this header + * to detect the position of individual fields directly, + * but I suspect this is probably more trouble than it's worth. + */ + fgets(line, sizeof(line), devin); + fgets(line, sizeof(line), devin); + + if( 0 == scan_expected ) { + if (strstr(line, "compressed")) { + scan_expected = 10; + DEBUGMSGTL(("access:interface", + "using linux 2.2 kernel /proc/net/dev\n")); + } else { + scan_expected = 5; + DEBUGMSGTL(("access:interface", + "using linux 2.0 kernel /proc/net/dev\n")); + } + } + + /* + * The rest of the file provides the statistics for each interface. + * Read in each line in turn, isolate the interface name + * and retrieve (or create) the corresponding data structure. + */ + while (fgets(line, sizeof(line), devin)) { + char *stats, *ifstart = line; + u_int flags; + oid if_index; + + flags = 0; + if (line[strlen(line) - 1] == '\n') + line[strlen(line) - 1] = '\0'; + + while (*ifstart && *ifstart == ' ') + ifstart++; + + if ((!*ifstart) || ((stats = strrchr(ifstart, ':')) == NULL)) { + snmp_log(LOG_ERR, + "interface data format error 1, line ==|%s|\n", line); + continue; + } + if ((scan_expected == 10) && ((stats - line) < 6)) { + snmp_log(LOG_ERR, + "interface data format error 2 (%d < 6), line ==|%s|\n", + (int)(stats - line), line); + } + + DEBUGMSGTL(("9:access:ifcontainer", "processing '%s'\n", ifstart)); + + /* + * get index via ioctl. + * If we've met this interface before, use the same index. + * Otherwise find an unused index value and use that. + */ + *stats++ = 0; /* null terminate name */ + + if_index = netsnmp_arch_interface_index_find(ifstart); + + /* + * set address type flags. + * the only way I know of to check an interface for + * ip version is to look for ip addresses. If anyone + * knows a better way, put it here! + */ +#ifdef NETSNMP_ENABLE_IPV6 + _arch_interface_has_ipv6(if_index, &flags, addr_container); +#endif + netsnmp_access_interface_ioctl_has_ipv4(fd, ifstart, 0, &flags); + + /* + * do we only want one address type? + */ + if (((load_flags & NETSNMP_ACCESS_INTERFACE_LOAD_IP4_ONLY) && + ((flags & NETSNMP_INTERFACE_FLAGS_HAS_IPV4) == 0)) || + ((load_flags & NETSNMP_ACCESS_INTERFACE_LOAD_IP6_ONLY) && + ((flags & NETSNMP_INTERFACE_FLAGS_HAS_IPV6) == 0))) { + DEBUGMSGTL(("9:access:ifcontainer", + "interface '%s' excluded by ip version\n", + ifstart)); + continue; + } + + entry = netsnmp_access_interface_entry_create(ifstart, 0); + if(NULL == entry) { +#ifdef NETSNMP_ENABLE_IPV6 + netsnmp_access_ipaddress_container_free(addr_container, 0); +#endif + netsnmp_access_interface_container_free(container, + NETSNMP_ACCESS_INTERFACE_FREE_NOFLAGS); + fclose(devin); + close(fd); + return -3; + } + entry->ns_flags = flags; /* initial flags; we'll set more later */ + +#ifdef HAVE_PCI_LOOKUP_NAME + _arch_interface_description_get(entry); +#endif + + + /* + * use ioctls for some stuff + * (ignore rc, so we get as much info as possible) + */ + netsnmp_access_interface_ioctl_physaddr_get(fd, entry); + + /* + * physaddr should have set type. make some guesses (based + * on name) if not. + */ + if(0 == entry->type) { + typedef struct _match_if { + int mi_type; + const char *mi_name; + } *pmatch_if, match_if; + + static match_if lmatch_if[] = { + {IANAIFTYPE_SOFTWARELOOPBACK, "lo"}, + {IANAIFTYPE_ETHERNETCSMACD, "eth"}, + {IANAIFTYPE_ETHERNETCSMACD, "vmnet"}, + {IANAIFTYPE_ISO88025TOKENRING, "tr"}, + {IANAIFTYPE_FASTETHER, "feth"}, + {IANAIFTYPE_GIGABITETHERNET,"gig"}, + {IANAIFTYPE_INFINIBAND,"ib"}, + {IANAIFTYPE_PPP, "ppp"}, + {IANAIFTYPE_SLIP, "sl"}, + {IANAIFTYPE_TUNNEL, "sit"}, + {IANAIFTYPE_BASICISDN, "ippp"}, + {IANAIFTYPE_PROPVIRTUAL, "bond"}, /* Bonding driver find fastest slave */ + {IANAIFTYPE_PROPVIRTUAL, "vad"}, /* ANS driver - ?speed? */ + {0, NULL} /* end of list */ + }; + + int len; + register pmatch_if pm; + + for (pm = lmatch_if; pm->mi_name; pm++) { + len = strlen(pm->mi_name); + if (0 == strncmp(entry->name, pm->mi_name, len)) { + entry->type = pm->mi_type; + break; + } + } + if(NULL == pm->mi_name) + entry->type = IANAIFTYPE_OTHER; + } + + /* + * interface identifier is specified based on physaddr and type + */ + switch (entry->type) { + case IANAIFTYPE_ETHERNETCSMACD: + case IANAIFTYPE_ETHERNET3MBIT: + case IANAIFTYPE_FASTETHER: + case IANAIFTYPE_FASTETHERFX: + case IANAIFTYPE_GIGABITETHERNET: + case IANAIFTYPE_FDDI: + case IANAIFTYPE_ISO88025TOKENRING: + if (NULL != entry->paddr && ETH_ALEN != entry->paddr_len) + break; + + entry->v6_if_id_len = entry->paddr_len + 2; + memcpy(entry->v6_if_id, entry->paddr, 3); + memcpy(entry->v6_if_id + 5, entry->paddr + 3, 3); + entry->v6_if_id[0] ^= 2; + entry->v6_if_id[3] = 0xFF; + entry->v6_if_id[4] = 0xFE; + + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_V6_IFID; + break; + + case IANAIFTYPE_SOFTWARELOOPBACK: + entry->v6_if_id_len = 0; + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_V6_IFID; + break; + } + + if (IANAIFTYPE_ETHERNETCSMACD == entry->type) { + unsigned long long speed; + unsigned long long defaultspeed = NOMINAL_LINK_SPEED; + if (!(entry->os_flags & IFF_RUNNING)) { + /* + * use speed 0 if the if speed cannot be determined *and* the + * interface is down + */ + defaultspeed = 0; + } + speed = netsnmp_linux_interface_get_if_speed(fd, + entry->name, defaultspeed); + if (speed > 0xffffffffL) { + entry->speed = 0xffffffff; + } else + entry->speed = speed; + entry->speed_high = speed / 1000000LL; + } +#ifdef APPLIED_PATCH_836390 /* xxx-rks ifspeed fixes */ + else if (IANAIFTYPE_PROPVIRTUAL == entry->type) + entry->speed = _get_bonded_if_speed(entry); +#endif + else + netsnmp_access_interface_entry_guess_speed(entry); + + netsnmp_access_interface_ioctl_flags_get(fd, entry); + + netsnmp_access_interface_ioctl_mtu_get(fd, entry); + + /* + * Zero speed means link problem. + * - i'm not sure this is always true... + */ + if((entry->speed == 0) && (entry->os_flags & IFF_UP)) { + entry->os_flags &= ~IFF_RUNNING; + } + + /* + * check for promiscuous mode. + * NOTE: there are 2 ways to set promiscuous mode in Linux + * (kernels later than 2.2.something) - using ioctls and + * using setsockopt. The ioctl method tested here does not + * detect if an interface was set using setsockopt. google + * on IFF_PROMISC and linux to see lots of arguments about it. + */ + if(entry->os_flags & IFF_PROMISC) { + entry->promiscuous = 1; /* boolean */ + } + + /* + * hardcoded max packet size + * (see ip_frag_reasm: if(len > 65535) goto out_oversize;) + */ + entry->reasm_max_v4 = entry->reasm_max_v6 = 65535; + entry->ns_flags |= + NETSNMP_INTERFACE_FLAGS_HAS_V4_REASMMAX | + NETSNMP_INTERFACE_FLAGS_HAS_V6_REASMMAX; + + netsnmp_access_interface_entry_overrides(entry); + + if (! (load_flags & NETSNMP_ACCESS_INTERFACE_LOAD_NO_STATS)) + _parse_stats(entry, stats, scan_expected); + + if (flags & NETSNMP_INTERFACE_FLAGS_HAS_IPV4) + _arch_interface_flags_v4_get(entry); + +#ifdef NETSNMP_ENABLE_IPV6 + if (flags & NETSNMP_INTERFACE_FLAGS_HAS_IPV6) + _arch_interface_flags_v6_get(entry); +#endif /* NETSNMP_ENABLE_IPV6 */ + + /* + * add to container + */ + CONTAINER_INSERT(container, entry); + } +#ifdef NETSNMP_ENABLE_IPV6 + netsnmp_access_ipaddress_container_free(addr_container, 0); +#endif + fclose(devin); + close(fd); + return 0; +} + +#ifndef NETSNMP_FEATURE_REMOVE_INTERFACE_ARCH_SET_ADMIN_STATUS +int +netsnmp_arch_set_admin_status(netsnmp_interface_entry * entry, + int ifAdminStatus_val) +{ + int and_complement; + + DEBUGMSGTL(("access:interface:arch", "set_admin_status\n")); + + if(IFADMINSTATUS_UP == ifAdminStatus_val) + and_complement = 0; /* |= */ + else + and_complement = 1; /* &= ~ */ + + return netsnmp_access_interface_ioctl_flags_set(-1, entry, + IFF_UP, and_complement); +} +#endif /* NETSNMP_FEATURE_REMOVE_INTERFACE_ARCH_SET_ADMIN_STATUS */ + +#ifdef HAVE_LINUX_ETHTOOL_H +/** + * Determines network interface speed from ETHTOOL_GSET + */ +unsigned long long +netsnmp_linux_interface_get_if_speed(int fd, const char *name, + unsigned long long defaultspeed) +{ + int ret; + struct ifreq ifr; + struct ethtool_cmd edata; + uint16_t speed_hi; + uint32_t speed; + + memset(&ifr, 0, sizeof(ifr)); + memset(&edata, 0, sizeof(edata)); + edata.cmd = ETHTOOL_GSET; + + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (char *) &edata; + + ret = ioctl(fd, SIOCETHTOOL, &ifr); + if (ret == -1 || edata.speed == 0) { + DEBUGMSGTL(("mibII/interfaces", "ETHTOOL_GSET on %s failed (%d / %d)\n", + ifr.ifr_name, ret, edata.speed)); + return netsnmp_linux_interface_get_if_speed_mii(fd,name,defaultspeed); + } + +#ifdef HAVE_STRUCT_ETHTOOL_CMD_SPEED_HI + speed_hi = edata.speed_hi; +#else + speed_hi = 0; +#endif + speed = speed_hi << 16 | edata.speed; + if (speed == 0xffff || speed == 0xffffffffUL /*SPEED_UNKNOWN*/) + speed = defaultspeed; + /* return in bps */ + DEBUGMSGTL(("mibII/interfaces", "ETHTOOL_GSET on %s speed = %#x -> %d\n", + ifr.ifr_name, speed_hi << 16 | edata.speed, speed)); + return speed * 1000LL * 1000LL; +} +#endif + +/** + * Determines network interface speed from MII + */ +unsigned long long +#ifdef HAVE_LINUX_ETHTOOL_H +netsnmp_linux_interface_get_if_speed_mii(int fd, const char *name, + unsigned long long defaultspeed) +#else +netsnmp_linux_interface_get_if_speed(int fd, const char *name, + unsigned long long defaultspeed) +#endif +{ + unsigned long long retspeed = defaultspeed; + struct ifreq ifr; + + /* the code is based on mii-diag utility by Donald Becker + * see ftp://ftp.scyld.com/pub/diag/mii-diag.c + */ + ushort *data = (ushort *)(&ifr.ifr_data); + unsigned phy_id; + int mii_reg, i; + ushort mii_val[32]; + ushort bmcr, bmsr, nway_advert, lkpar; + const unsigned long long media_speeds[] = {10000000, 10000000, 100000000, 100000000, 10000000, 0}; + /* It corresponds to "10baseT", "10baseT-FD", "100baseTx", "100baseTx-FD", "100baseT4", "Flow-control", 0, */ + + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + data[0] = 0; + + /* + * SIOCGMIIPHY has been defined since at least kernel 2.4.10 (Sept 2001). + * It's probably safe to drop the interim SIOCDEVPRIVATE handling now! + */ + if (ioctl(fd, SIOCGMIIPHY, &ifr) < 0) { + DEBUGMSGTL(("mibII/interfaces", "SIOCGMIIPHY on %s failed\n", + ifr.ifr_name)); + return retspeed; + } + + /* Begin getting mii register values */ + phy_id = data[0]; + for (mii_reg = 0; mii_reg < 8; mii_reg++){ + data[0] = phy_id; + data[1] = mii_reg; + if(ioctl(fd, SIOCGMIIREG, &ifr) <0){ + DEBUGMSGTL(("mibII/interfaces", "SIOCGMIIREG on %s failed\n", ifr.ifr_name)); + } + mii_val[mii_reg] = data[3]; + } + /*Parsing of mii values*/ + /*Invalid basic mode control register*/ + if (mii_val[0] == 0xffff || mii_val[1] == 0x0000) { + DEBUGMSGTL(("mibII/interfaces", "No MII transceiver present!.\n")); + return retspeed; + } + /* Descriptive rename. */ + bmcr = mii_val[0]; /*basic mode control register*/ + bmsr = mii_val[1]; /* basic mode status register*/ + nway_advert = mii_val[4]; /* autonegotiation advertisement*/ + lkpar = mii_val[5]; /*link partner ability*/ + + /*Check for link existence, returns 0 if link is absent*/ + if ((bmsr & 0x0016) != 0x0004){ + DEBUGMSGTL(("mibII/interfaces", "No link...\n")); + retspeed = 0; + return retspeed; + } + + if(!(bmcr & 0x1000) ){ + DEBUGMSGTL(("mibII/interfaces", "Auto-negotiation disabled.\n")); + retspeed = bmcr & 0x2000 ? 100000000 : 10000000; + return retspeed; + } + /* Link partner got our advertised abilities */ + if (lkpar & 0x4000) { + int negotiated = nway_advert & lkpar & 0x3e0; + int max_capability = 0; + /* Scan for the highest negotiated capability, highest priority + (100baseTx-FDX) to lowest (10baseT-HDX). */ + int media_priority[] = {8, 9, 7, 6, 5}; /* media_names[i-5] */ + for (i = 0; media_priority[i]; i++){ + if (negotiated & (1 << media_priority[i])) { + max_capability = media_priority[i]; + break; + } + } + if (max_capability) + retspeed = media_speeds[max_capability - 5]; + else + DEBUGMSGTL(("mibII/interfaces", "No common media type was autonegotiated!\n")); + }else if(lkpar & 0x00A0){ + retspeed = (lkpar & 0x0080) ? 100000000 : 10000000; + } + return retspeed; +} +#ifdef SUPPORT_PREFIX_FLAGS +void netsnmp_prefix_process(int fd, void *data); + +/* Open netlink socket to watch new ipv6 addresses and prefixes. */ +int netsnmp_prefix_listen() +{ + struct { + struct nlmsghdr n; + struct ifinfomsg r; + char buf[1024]; + } req; + + struct rtattr *rta; + int status; + struct sockaddr_nl localaddrinfo; + unsigned groups = 0; + + int fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + snmp_log(LOG_ERR, "netsnmp_prefix_listen: Cannot create socket.\n"); + return -1; + } + + memset(&localaddrinfo, 0, sizeof(struct sockaddr_nl)); + + groups |= RTMGRP_IPV6_IFADDR; + groups |= RTMGRP_IPV6_PREFIX; + localaddrinfo.nl_family = AF_NETLINK; + localaddrinfo.nl_groups = groups; + + if (bind(fd, (struct sockaddr*)&localaddrinfo, sizeof(localaddrinfo)) < 0) { + snmp_log(LOG_ERR,"netsnmp_prefix_listen: Bind failed.\n"); + close(fd); + return -1; + } + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; + req.n.nlmsg_type = RTM_GETLINK; + req.r.ifi_family = AF_INET6; + rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.n.nlmsg_len)); + rta->rta_len = RTA_LENGTH(16); + + status = send(fd, &req, req.n.nlmsg_len, 0); + if (status < 0) { + snmp_log(LOG_ERR,"netsnmp_prefix_listen: send failed\n"); + close(fd); + return -1; + } + + if (register_readfd(fd, netsnmp_prefix_process, NULL) != 0) { + snmp_log(LOG_ERR,"netsnmp_prefix_listen: error registering netlink socket\n"); + close(fd); + return -1; + } + return 0; +} + +/* Process one incoming netlink packets. + * RTM_NEWADDR and RTM_NEWPREFIX usually arrive in separate packets + * -> information from these packets must be stored locally and + * new prefix is added when information from both packets is complete. + */ +void netsnmp_prefix_process(int fd, void *data) +{ + int status; + char buf[16384]; + struct nlmsghdr *nlmp; + struct rtattr *rtatp; + struct ifaddrmsg *ifa; + struct prefixmsg *prefix; + struct in6_addr *in6p; + + /* these values must persist between calls */ + static char in6pAddr[40]; + static int have_addr = 0,have_prefix = 0; + static int onlink = 2,autonomous = 2; /*Assume as false*/ + + int iret; + prefix_cbx *new; + int len, req_len, length; + + status = recv(fd, buf, sizeof(buf), 0); + if (status < 0) { + if (errno == EINTR) + return; + snmp_log(LOG_ERR,"netsnmp_prefix_listen: Receive failed.\n"); + return; + } + + if(status == 0){ + DEBUGMSGTL(("access:interface:prefix", "End of File\n")); + return; + } + + for(nlmp = (struct nlmsghdr *)buf; status > sizeof(*nlmp);){ + len = nlmp->nlmsg_len; + req_len = len - sizeof(*nlmp); + + if (req_len < 0 || len > status) { + snmp_log(LOG_ERR,"netsnmp_prefix_listen: Error in length.\n"); + return; + } + + if (!NLMSG_OK(nlmp, status)) { + DEBUGMSGTL(("access:interface:prefix", "NLMSG not OK\n")); + continue; + } + + if (nlmp->nlmsg_type == RTM_NEWADDR || nlmp->nlmsg_type == RTM_DELADDR) { + ifa = NLMSG_DATA(nlmp); + length = nlmp->nlmsg_len; + length -= NLMSG_LENGTH(sizeof(*ifa)); + + if (length < 0) { + DEBUGMSGTL(("access:interface:prefix", "wrong nlmsg length %d\n", length)); + continue; + } + + if(!ifa->ifa_flags) { + rtatp = IFA_RTA(ifa); + while (RTA_OK(rtatp, length)) { + if (rtatp->rta_type == IFA_ADDRESS){ + in6p = (struct in6_addr *) RTA_DATA(rtatp); + if(nlmp->nlmsg_type == RTM_DELADDR) { + snprintf(in6pAddr, sizeof(in6pAddr), "%04x%04x%04x%04x%04x%04x%04x%04x", NIP6(*in6p)); + have_addr = -1; + break; + } else { + snprintf(in6pAddr, sizeof(in6pAddr), "%04x%04x%04x%04x%04x%04x%04x%04x", NIP6(*in6p)); + have_addr = 1; + break; + } + } + rtatp = RTA_NEXT(rtatp,length); + } + } + } + + if(nlmp->nlmsg_type == RTM_NEWPREFIX) { + prefix = NLMSG_DATA(nlmp); + length = nlmp->nlmsg_len; + length -= NLMSG_LENGTH(sizeof(*prefix)); + + if (length < 0) { + DEBUGMSGTL(("access:interface:prefix", "wrong nlmsg length %d\n", length)); + continue; + } + have_prefix = 1; + if (prefix->prefix_flags & IF_PREFIX_ONLINK) { + onlink = 1; + } + if (prefix->prefix_flags & IF_PREFIX_AUTOCONF) { + autonomous = 1; + } + } + status -= NLMSG_ALIGN(len); + nlmp = (struct nlmsghdr*)((char*)nlmp + NLMSG_ALIGN(len)); + } + + if((have_addr == 1) && (have_prefix == 1)){ + if(!(new = net_snmp_create_prefix_info (onlink, autonomous, in6pAddr))) + DEBUGMSGTL(("access:interface:prefix", "Unable to create prefix info\n")); + else { + + iret = net_snmp_search_update_prefix_info (list_info.list_head, new, 0); + if(iret < 0) { + DEBUGMSGTL(("access:interface:prefix", "Unable to add/update prefix info\n")); + free(new); + } + if(iret == 2) /*Only when enrty already exists and we are only updating*/ + free(new); + } + have_addr = have_prefix = 0; + onlink = autonomous = 2; /*Set to defaults again*/ + } else if (have_addr == -1) { + iret = net_snmp_delete_prefix_info (list_info.list_head, in6pAddr); + if(iret < 0) + DEBUGMSGTL(("access:interface:prefix", "Unable to delete the prefix info\n")); + if(!iret) + DEBUGMSGTL(("access:interface:prefix", "Unable to find the node to delete\n")); + have_addr = 0; + } +} +#endif + diff --git a/agent/mibgroup/if-mib/data_access/interface_openbsd.c b/agent/mibgroup/if-mib/data_access/interface_openbsd.c new file mode 100644 index 0000000..8340dc6 --- /dev/null +++ b/agent/mibgroup/if-mib/data_access/interface_openbsd.c @@ -0,0 +1,407 @@ +/* + * Interface MIB architecture support + * + * $Id$ + */ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-features.h> +#include <net-snmp/net-snmp-includes.h> +#include "mibII/mibII_common.h" +#include "if-mib/ifTable/ifTable_constants.h" + +netsnmp_feature_child_of(interface_arch_set_admin_status, interface_all) + +#include <net-snmp/agent/net-snmp-agent-includes.h> + +#if HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#else +#error "linux should have sys/ioctl header" +#endif + +#include <net-snmp/data_access/interface.h> +#include <net-snmp/data_access/ipaddress.h> +#include "if-mib/data_access/interface.h" + +#include <sys/types.h> +#include <sys/time.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_media.h> + +#define starttime (*(const struct timeval*)netsnmp_get_agent_starttime()) + +int +netsnmp_openbsd_interface_get_if_speed(char *name, u_int *speed, u_int *speed_high); + +void +netsnmp_arch_interface_init(void) +{ + /* + * nothing to do + */ +} + +/* + * find the ifIndex for an interface name + * + * @retval 0 : no index found + * @retval >0: ifIndex for interface + */ +oid +netsnmp_arch_interface_index_find(const char *name) +{ + return if_nametoindex(name); +} + +/* sa_len roundup macro. */ +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + +/* + * + * @retval 0 success + * @retval -1 no container specified + * @retval -2 could not get interface info + * @retval -3 could not create entry (probably malloc) + */ +int +netsnmp_arch_interface_container_load(netsnmp_container* container, + u_int load_flags) +{ + netsnmp_interface_entry *entry = NULL; + u_char *if_list = NULL, *cp; + size_t if_list_size = 0; + struct if_msghdr *ifp; + int sysctl_oid[] = { CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0 }; + struct ifa_msghdr *ifa; + struct sockaddr *a; + struct sockaddr_dl *adl; + int amask; + char *if_name; + int flags; + + DEBUGMSGTL(("access:interface:container:arch", + "load (flags %p)\n", load_flags)); + + if (NULL == container) { + snmp_log(LOG_ERR, "no container specified/found for interface\n"); + return -1; + } + + if (sysctl(sysctl_oid, sizeof(sysctl_oid) / sizeof(int), 0, &if_list_size, 0, 0) == -1) { + snmp_log(LOG_ERR, "could not get interface info (size)\n"); + return -2; + } + + if_list = (u_char*)malloc(if_list_size); + if (if_list == NULL) { + snmp_log(LOG_ERR, "could not allocate memory for interface info (%u bytes)\n", if_list_size); + return -3; + } else { + DEBUGMSGTL(("access:interface:container:arch", + "allocated %u bytes for if_list\n", if_list_size)); + } + + if (sysctl(sysctl_oid, sizeof(sysctl_oid) / sizeof(int), if_list, &if_list_size, 0, 0) == -1) { + snmp_log(LOG_ERR, "could not get interface info\n"); + free(if_list); + return -2; + } + + /* 1st pass: create interface entries */ + for (cp = if_list; cp < if_list + if_list_size; cp += ifp->ifm_msglen) { + + ifp = (struct if_msghdr *) cp; + if_name = NULL; + flags = 0; + adl = NULL; + + if (ifp->ifm_type != RTM_IFINFO) + continue; + + if (ifp->ifm_addrs & RTA_IFP) { + a = (struct sockaddr *) (ifp + 1); + /* if_msghdr is followed by one or more sockaddrs, of which we need only RTA_IFP */ + /* most of the time RTA_IFP is the first address we get, hence the shortcut */ + if ((ifp->ifm_addrs & (~RTA_IFP - 1)) != 0) { + /* skip all addresses up to RTA_IFP. */ + for (amask = (RTA_IFP >> 1); amask != 0; amask >>= 1) { + if (ifp->ifm_addrs & amask) { a = (struct sockaddr *) ( ((char *) a) + ROUNDUP(a->sa_len) ); } + } + } + adl = (struct sockaddr_dl *) a; + if_name = (char *) adl->sdl_data; + if_name[adl->sdl_nlen] = '\0'; + } + if (!(ifp->ifm_addrs & RTA_IFP) || if_name == NULL) { + snmp_log(LOG_ERR, "ifm_index %u: no interface name in message, skipping\n", ifp->ifm_index); + continue; + } + + entry = netsnmp_access_interface_entry_create(if_name, ifp->ifm_index); + if(NULL == entry) { + netsnmp_access_interface_container_free(container, + NETSNMP_ACCESS_INTERFACE_FREE_NOFLAGS); + free(if_list); + return -3; + } + + /* get physical address */ + if (adl != NULL && adl->sdl_alen > 0) { + entry->paddr_len = adl->sdl_alen; + entry->paddr = (char*)malloc(entry->paddr_len); + memcpy(entry->paddr, adl->sdl_data + adl->sdl_nlen, adl->sdl_alen); + DEBUGMSGTL(("access:interface:container:arch", + "%s: paddr_len=%d, entry->paddr=%x:%x:%x:%x:%x:%x\n", + if_name, entry->paddr_len, + entry->paddr[0], entry->paddr[1], entry->paddr[2], + entry->paddr[3], entry->paddr[4], entry->paddr[5])); + } else { + entry->paddr = (char*)malloc(6); + entry->paddr_len = 6; + memset(entry->paddr, 0, 6); + } + + entry->mtu = ifp->ifm_data.ifi_mtu; + entry->type = ifp->ifm_data.ifi_type; + + + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_IF_FLAGS; + entry->os_flags = ifp->ifm_flags; + + if (ifp->ifm_flags & IFF_UP) { + entry->admin_status = IFADMINSTATUS_UP; +#if defined( LINK_STATE_UP ) && defined( LINK_STATE_DOWN ) + if (ifp->ifm_data.ifi_link_state == LINK_STATE_UP) { + entry->oper_status = IFOPERSTATUS_UP; + } else if (ifp->ifm_data.ifi_link_state == LINK_STATE_DOWN) { + entry->oper_status = IFOPERSTATUS_DOWN; + } else { + entry->oper_status = IFOPERSTATUS_UNKNOWN; + } +#else + entry->oper_status = IFOPERSTATUS_UP; +#endif + } else { + entry->admin_status = IFADMINSTATUS_DOWN; + /* IF-MIB specifically says that ifOperStatus should be down in this case */ + entry->oper_status = IFOPERSTATUS_DOWN; + } + + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_V4_REASMMAX; + entry->reasm_max = IP_MAXPACKET; + + /* get counters */ + entry->stats.ibytes.low = ifp->ifm_data.ifi_ibytes; entry->stats.ibytes.high = 0; + entry->stats.iucast.low = ifp->ifm_data.ifi_ipackets; entry->stats.iucast.high = 0; + entry->stats.imcast.low = ifp->ifm_data.ifi_imcasts; entry->stats.imcast.high = 0; + entry->stats.ierrors = ifp->ifm_data.ifi_ierrors; + entry->stats.idiscards = ifp->ifm_data.ifi_iqdrops; + entry->stats.iunknown_protos = ifp->ifm_data.ifi_noproto; + + entry->stats.obytes.low = ifp->ifm_data.ifi_obytes; entry->stats.obytes.high = 0; + entry->stats.oucast.low = ifp->ifm_data.ifi_opackets; entry->stats.oucast.high = 0; + entry->stats.omcast.low = ifp->ifm_data.ifi_omcasts; entry->stats.omcast.high = 0; + entry->stats.oerrors = ifp->ifm_data.ifi_oerrors; + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_BYTES | + NETSNMP_INTERFACE_FLAGS_HAS_DROPS | + NETSNMP_INTERFACE_FLAGS_HAS_MCAST_PKTS; + + if (timercmp(&ifp->ifm_data.ifi_lastchange, &starttime, >)) { + entry->lastchange = (ifp->ifm_data.ifi_lastchange.tv_sec - starttime.tv_sec) * 100; + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_LASTCHANGE; + } else { + entry->lastchange = 0; + } + + if (ifp->ifm_flags & IFF_PROMISC) entry->promiscuous = 1; + + /* try to guess the speed from media type */ + netsnmp_openbsd_interface_get_if_speed(entry->name, &entry->speed, &entry->speed_high); + if (entry->speed_high != 0) { + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_HIGH_SPEED; + } else { + /* or resort to ifi_baudrate */ + entry->speed = ifp->ifm_data.ifi_baudrate; + } + + netsnmp_access_interface_entry_overrides(entry); + + CONTAINER_INSERT(container, entry); + DEBUGMSGTL(("access:interface:container:arch", + "created entry %u for %s\n", entry->index, entry->name)); + } /* for (each interface entry) */ + + /* pass 2: walk addresses */ + for (cp = if_list; cp < if_list + if_list_size; cp += ifa->ifam_msglen) { + + ifa = (struct ifa_msghdr *) cp; + + if (ifa->ifam_type != RTM_NEWADDR) + continue; + + DEBUGMSGTL(("access:interface:container:arch", + "received 0x%x in RTM_NEWADDR for ifindex %u\n", + ifa->ifam_addrs, ifa->ifam_index)); + + entry = netsnmp_access_interface_entry_get_by_index(container, ifa->ifam_index); + if (entry == NULL) { + snmp_log(LOG_ERR, "address for a nonexistent interface? index=%d", ifa->ifam_index); + continue; + } + + /* walk the list of addresses received. + we do not use actual addresses, the sole purpose of this is to set flags */ + a = (struct sockaddr *) (ifa + 1); + for (amask = ifa->ifam_addrs; amask != 0; amask >>= 1) { + if ((amask & 1) != 0) { + DEBUGMSGTL(("access:interface:container:arch", + "%s: a=%p, sa_len=%d, sa_family=0x%x\n", + entry->name, a, a->sa_len, a->sa_family)); + + if (a->sa_family == AF_INET) + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_IPV4; + else if (a->sa_family == AF_INET6) + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_IPV6; + + a = (struct sockaddr *) ( ((char *) a) + ROUNDUP(a->sa_len) ); + } + } + DEBUGMSGTL(("access:interface:container:arch", + "%s: flags=0x%x\n", entry->name, entry->ns_flags)); + } + + if (if_list != NULL) + free(if_list); + + return 0; +} + +/* + * subroutine to translate known media typed to speed. + * see /usr/include/net/if_media.h for definitions + */ + +void +_openbsd_interface_ifmedia_to_speed(int media, u_int *speed, u_int *speed_high) +{ + *speed = 0; *speed_high = 0; + + switch (IFM_TYPE(media)) { + case IFM_ETHER: + switch (IFM_SUBTYPE(media)) { + case IFM_10_T: + case IFM_10_2: + case IFM_10_5: + case IFM_10_STP: + case IFM_10_FL: + *speed = 10000000; *speed_high = 10; break; + case IFM_100_TX: + case IFM_100_FX: + case IFM_100_T4: + case IFM_100_VG: + case IFM_100_T2: + *speed = 100000000; *speed_high = 100; break; + case IFM_1000_LX: + case IFM_1000_SX: + case IFM_1000_CX: +#ifdef IFM_1000_T + case IFM_1000_T: +#endif + *speed = 1000000000; *speed_high = 1000; break; +#ifdef IFM_10GBASE_SR + case IFM_10GBASE_SR: + case IFM_10GBASE_LR: + *speed = (u_int) -1; /* 4294967295; */ *speed_high = 10000; break; +#endif + } + break; + } + return; +} + +/* + * @retval 0 speed could not be determined (error, unknown media) + * @retval >0 speed, equal to *speed. + * sets *speed=2^31 and returns *speed_high=speed/10^6 as required by ifSpeed/ifHighSpeed. + */ + +int +netsnmp_openbsd_interface_get_if_speed(char *name, u_int *speed, u_int *speed_high) +{ + int s; + struct ifmediareq ifmr; + int *media_list, i; + u_int t_speed, t_speed_high; + u_int m_speed, m_speed_high; + + *speed = 0; *speed_high = 0; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + return 0; + } + + memset(&ifmr, 0, sizeof(ifmr)); + strlcpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0 || ifmr.ifm_count == 0) { + close(s); + return 0; + } + + /* + * try to get speed from current media. + * if unsuccessful (e.g., interface is down), get a list of capabilities, + * try each and return maximum speed the interface is capable of. + */ + + _openbsd_interface_ifmedia_to_speed(ifmr.ifm_current, speed, speed_high); + + if (*speed == 0 && + (media_list = (int *) malloc(ifmr.ifm_count * sizeof(int))) != NULL ) { + + ifmr.ifm_ulist = media_list; + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) == 0) { + m_speed = 0; m_speed_high = 0; + for (i = 0; i < ifmr.ifm_count; i++) { + + _openbsd_interface_ifmedia_to_speed(media_list[i], &t_speed, &t_speed_high); + + if (t_speed_high > m_speed_high || + (t_speed_high == m_speed_high && t_speed > t_speed)) { + m_speed_high = t_speed_high; m_speed = t_speed; + } + } + *speed = m_speed; *speed_high = m_speed_high; + } + free(media_list); + } + + close(s); + + DEBUGMSGTL(("access:interface:container:arch", + "%s: speed: %u, speed_high: %u\n", + name, *speed, *speed_high)); + + return *speed; +} + +#ifndef NETSNMP_FEATURE_REMOVE_INTERFACE_ARCH_SET_ADMIN_STATUS +int +netsnmp_arch_set_admin_status(netsnmp_interface_entry * entry, + int ifAdminStatus_val) +{ + DEBUGMSGTL(("access:interface:arch", "set_admin_status\n")); + + /* TODO: implement this call */ + + /* not implemented */ + snmp_log(LOG_ERR, "netsnmp_arch_set_admin_status is not (yet) implemented for FreeBSD.\n"); + + return -4; +} +#endif /* NETSNMP_FEATURE_REMOVE_INTERFACE_ARCH_SET_ADMIN_STATUS */ + diff --git a/agent/mibgroup/if-mib/data_access/interface_solaris2.c b/agent/mibgroup/if-mib/data_access/interface_solaris2.c new file mode 100644 index 0000000..8aa47f8 --- /dev/null +++ b/agent/mibgroup/if-mib/data_access/interface_solaris2.c @@ -0,0 +1,363 @@ +/* + * Interface MIB architecture support for Solaris + */ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-features.h> +#include <net-snmp/net-snmp-includes.h> +#include "if-mib/ifTable/ifTable_constants.h" +#include "kernel_sunos5.h" +#include "mibII/mibII_common.h" + +#include <net-snmp/agent/net-snmp-agent-includes.h> + +#include <net-snmp/data_access/interface.h> +#include "if-mib/data_access/interface.h" +#include <sys/ioctl.h> +#include <sys/sockio.h> +#include <strings.h> +#include <string.h> + +netsnmp_feature_child_of(interface_arch_set_admin_status, interface_all) + +static int _set_ip_flags_v4(netsnmp_interface_entry *, mib2_ifEntry_t *); +static int _match_ifname_v4addr(void *ifname, void *ipaddr); +static int _get_v4addr(mib2_ifEntry_t *ife, mib2_ipAddrEntry_t *e); + +static int _set_ip_flags_v6(netsnmp_interface_entry *, mib2_ifEntry_t *); +#ifdef SOLARIS_HAVE_IPV6_MIB_SUPPORT +static int _get_v6addr(mib2_ifEntry_t *ife, mib2_ipv6AddrEntry_t *ipv6e); +static int _match_ifname_v6addr(void *ifname, void *ipaddr); +#endif + +void +netsnmp_arch_interface_init(void) +{ + init_kernel_sunos5(); +} + +/* + * find the ifIndex for an interface name + * + * @retval 0 : no index found + * @retval >0: ifIndex for interface + */ +oid +netsnmp_arch_interface_index_find(const char *name) +{ +#if defined(HAVE_IF_NAMETOINDEX) + return if_nametoindex(name); +#else /* use GIFINDEX */ + return solaris2_if_nametoindex(name, strlen(name)); +#endif /* defined(HAVE_IF_NAMETOINDEX) */ +} + +/* + * @retval 0 success + * @retval -1 no container specified + * @retval -2 could not create entry (probably malloc) + */ +int +netsnmp_arch_interface_container_load(netsnmp_container* container, + u_int l_flags) +{ + netsnmp_interface_entry *entry = NULL; + mib2_ifEntry_t ife; + int rc; + req_e req = GET_FIRST; + int error = 0; + + DEBUGMSGTL(("access:interface:container:arch", "load (flags %u)\n", + l_flags)); + + if (container == NULL) { + snmp_log(LOG_ERR, + "no container specified/found for interface\n"); + return -1; + } + + while ((rc = getMibstat(MIB_INTERFACES, &ife, sizeof(ife), req, + &Get_everything, NULL)) == 0) { + + req = GET_NEXT; + + DEBUGMSGTL(("access:interface:container:arch", + "processing '%s'\n", ife.ifDescr.o_bytes)); + entry = + netsnmp_access_interface_entry_create(ife.ifDescr.o_bytes, + ife.ifIndex); + if (entry == NULL) { + error = 1; + break; + } + entry->ns_flags = 0; + + if (l_flags & NETSNMP_ACCESS_INTERFACE_LOAD_IP4_ONLY && + _set_ip_flags_v4(entry, &ife) == 0) { + netsnmp_access_interface_entry_free(entry); + continue; + } else if (l_flags & NETSNMP_ACCESS_INTERFACE_LOAD_IP6_ONLY && + _set_ip_flags_v6(entry, &ife) == 0) { + netsnmp_access_interface_entry_free(entry); + continue; + } else { + (void) _set_ip_flags_v4(entry, &ife); + (void) _set_ip_flags_v6(entry, &ife); + } + + /* + * collect the information needed by IF-MIB + */ + entry->paddr = (char*)malloc(ife.ifPhysAddress.o_length); + if (entry->paddr == NULL) { + netsnmp_access_interface_entry_free(entry); + error = 1; + break; + } + entry->paddr_len = ife.ifPhysAddress.o_length; + (void)memcpy(entry->paddr, ife.ifPhysAddress.o_bytes, + ife.ifPhysAddress.o_length); + + entry->type = ife.ifType; + entry->mtu = ife.ifMtu; + entry->speed = ife.ifSpeed; + entry->speed_high = entry->speed / 1000000; + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_HIGH_SPEED; + entry->oper_status = ife.ifOperStatus; + entry->admin_status = ife.ifAdminStatus; + + if (ife.flags & IFF_PROMISC) + entry->promiscuous = 1; + + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_ACTIVE; + + /* + * Interface Stats. + */ + if (! (l_flags & NETSNMP_ACCESS_INTERFACE_LOAD_NO_STATS)) { + entry->ns_flags |= + NETSNMP_INTERFACE_FLAGS_HAS_BYTES | + NETSNMP_INTERFACE_FLAGS_HAS_DROPS | + NETSNMP_INTERFACE_FLAGS_HAS_MCAST_PKTS; + if (ife.ifHCInOctets > 0 || ife.ifHCOutOctets > 0) { + /* + * We make the assumption that if we have + * a 64-bit Octet counter, then the other + * counters are 64-bit as well. + */ + DEBUGMSGTL(("access:interface:container:arch", + "interface '%s' have 64-bit stat counters\n", + entry->name)); + entry->ns_flags |= + NETSNMP_INTERFACE_FLAGS_HAS_HIGH_BYTES | + NETSNMP_INTERFACE_FLAGS_HAS_HIGH_PACKETS; + /* in stats */ + entry->stats.ibytes.low = ife.ifHCInOctets & 0xffffffff; + entry->stats.ibytes.high = ife.ifHCInOctets >> 32; + entry->stats.iucast.low = ife.ifHCInUcastPkts & 0xffffffff; + entry->stats.iucast.high = ife.ifHCInUcastPkts >> 32; + entry->stats.imcast.low = ife.ifHCInMulticastPkts & 0xffffffff; + entry->stats.imcast.high = ife.ifHCInMulticastPkts >> 32; + entry->stats.ibcast.low = ife.ifHCInBroadcastPkts & 0xffffffff; + entry->stats.ibcast.high = ife.ifHCInBroadcastPkts >> 32; + /* out stats */ + entry->stats.obytes.low = ife.ifHCOutOctets & 0xffffffff; + entry->stats.obytes.high = ife.ifHCOutOctets >> 32; + entry->stats.oucast.low = ife.ifHCOutUcastPkts & 0xffffffff; + entry->stats.oucast.high = ife.ifHCOutUcastPkts >> 32; + entry->stats.omcast.low = ife.ifHCOutMulticastPkts & 0xffffffff; + entry->stats.omcast.high = ife.ifHCOutMulticastPkts >> 32; + entry->stats.obcast.low = ife.ifHCOutBroadcastPkts & 0xffffffff; + entry->stats.obcast.high = ife.ifHCOutBroadcastPkts >> 32; + } else { + DEBUGMSGTL(("access:interface:container:arch", + "interface '%s' have 32-bit stat counters\n", + entry->name)); + /* in stats */ + entry->stats.ibytes.low = ife.ifInOctets; + entry->stats.iucast.low = ife.ifInUcastPkts; + entry->stats.imcast.low = ife.ifHCInMulticastPkts & 0xffffffff; + entry->stats.ibcast.low = ife.ifHCInBroadcastPkts & 0xffffffff; + /* out stats */ + entry->stats.obytes.low = ife.ifOutOctets; + entry->stats.oucast.low = ife.ifOutUcastPkts; + entry->stats.omcast.low = ife.ifHCOutMulticastPkts & 0xffffffff; + entry->stats.obcast.low = ife.ifHCOutBroadcastPkts & 0xffffffff; + } + /* in stats */ + entry->stats.ierrors = ife.ifInErrors; + entry->stats.idiscards = ife.ifInDiscards; + entry->stats.iunknown_protos = ife.ifInUnknownProtos; + entry->stats.inucast = ife.ifInNUcastPkts; + /* out stats */ + entry->stats.oerrors = ife.ifOutErrors; + entry->stats.odiscards = ife.ifOutDiscards; + entry->stats.onucast = ife.ifOutNUcastPkts; + entry->stats.oqlen = ife.ifOutQLen; + + /* other stats */ + entry->stats.collisions = ife.ifCollisions; + } + + netsnmp_access_interface_entry_overrides(entry); + + /* + * add to container + */ + CONTAINER_INSERT(container, entry); + } + DEBUGMSGTL(("access:interface:container:arch", "rc = %d\n", rc)); + + if (error) { + DEBUGMSGTL(("access:interface:container:arch", + "error %d, free container\n", error)); + netsnmp_access_interface_container_free(container, + NETSNMP_ACCESS_INTERFACE_FREE_NOFLAGS); + return -2; + } + + return 0; +} +/** + * @internal + */ +static int +_set_ip_flags_v4(netsnmp_interface_entry *entry, mib2_ifEntry_t *ife) +{ + mib2_ipAddrEntry_t ipv4e; + + if (_get_v4addr(ife, &ipv4e) > 0) { + entry->reasm_max_v4 = ipv4e.ipAdEntReasmMaxSize; + entry->ns_flags |= + NETSNMP_INTERFACE_FLAGS_HAS_IPV4 | + NETSNMP_INTERFACE_FLAGS_HAS_V4_REASMMAX; +#if defined( SOLARIS_HAVE_RFC4293_SUPPORT ) + entry->retransmit_v4 = ipv4e.ipAdEntRetransmitTime; + entry->ns_flags |= + NETSNMP_INTERFACE_FLAGS_HAS_V4_RETRANSMIT; +#endif + return (1); + } + return (0); +} + +/** + * @internal + */ +static int +_set_ip_flags_v6(netsnmp_interface_entry *entry, mib2_ifEntry_t *ife) +{ +#ifdef SOLARIS_HAVE_IPV6_MIB_SUPPORT + mib2_ipv6AddrEntry_t ipv6e; + + if (_get_v6addr(ife, &ipv6e) > 0) { + entry->ns_flags |= + NETSNMP_INTERFACE_FLAGS_HAS_IPV6; +#if defined( SOLARIS_HAVE_RFC4293_SUPPORT ) + if (ipv6e.ipv6AddrIdentifierLen <= sizeof(entry->v6_if_id)) { + entry->v6_if_id_len = ipv6e.ipv6AddrIdentifierLen; + (void)memcpy(&entry->v6_if_id, &ipv6e.ipv6AddrIdentifier, + entry->v6_if_id_len); + entry->ns_flags |= + NETSNMP_INTERFACE_FLAGS_HAS_V6_IFID; + } + entry->reasm_max_v6 = ipv6e.ipv6AddrReasmMaxSize; + entry->retransmit_v6 = ipv6e.ipv6AddrRetransmitTime; + entry->reachable_time = ipv6e.ipv6AddrReachableTime; + entry->ns_flags |= + NETSNMP_INTERFACE_FLAGS_HAS_V6_REASMMAX | + NETSNMP_INTERFACE_FLAGS_HAS_V6_RETRANSMIT | + NETSNMP_INTERFACE_FLAGS_HAS_V6_REACHABLE; + + /* XXX forwarding info missing */ +#else + /* XXX Don't have this info, 1500 is the minimum */ + entry->reasm_max_v6 = 1500; + entry->ns_flags |= + NETSNMP_INTERFACE_FLAGS_HAS_V6_REASMMAX; /* ??? */ +#endif /* SOLARIS_HAVE_RFC4293_SUPPORT */ + return (1); + } +#endif /* SOLARIS_HAVE_IPV6_MIB_SUPPORT */ + return (0); +} + +/** + * @internal + */ +static int +_match_ifname_v4addr(void *ifname, void *ipaddr) +{ + DeviceName *devname = &((mib2_ipAddrEntry_t *)ipaddr)->ipAdEntIfIndex; + + return (strncmp((char *)ifname, devname->o_bytes, devname->o_length)); + +} + +/** + * @internal + * + * Search for address entry that belongs to the IF entry. + * Returns 1 if an address was found, in which case the entry + * will be stored in ipv4e. If not entry was found, 0 is returned. + * + */ +static int +_get_v4addr(mib2_ifEntry_t *ife, mib2_ipAddrEntry_t *ipv4e) +{ + int rc; + + if ((rc = getMibstat(MIB_IP_ADDR, ipv4e, sizeof(*ipv4e), GET_EXACT, + &_match_ifname_v4addr, &ife->ifDescr.o_bytes)) == 0) + return (1); + memset(ipv4e, '\0', sizeof(*ipv4e)); + return (0); +} + +#ifdef SOLARIS_HAVE_IPV6_MIB_SUPPORT +/** + * @internal + */ +static int +_match_ifname_v6addr(void *ifname, void *ipaddr) +{ + DeviceName *devname = &((mib2_ipv6AddrEntry_t*)ipaddr)->ipv6AddrIfIndex; + + return (strncmp((char *)ifname, devname->o_bytes, devname->o_length)); + +} + +/** + * @internal + * + * Search for address entry that belongs to the IF entry. + * Returns 1 if an address was found, in which case the entry + * will be stored in ipv4e. If not entry was found, 0 is returned. + * + */ +static int +_get_v6addr(mib2_ifEntry_t *ife, mib2_ipv6AddrEntry_t *ipv6e) +{ + int rc; + + if ((rc = getMibstat(MIB_IP6_ADDR, ipv6e, sizeof(*ipv6e), GET_EXACT, + &_match_ifname_v6addr, &ife->ifDescr.o_bytes)) == 0) { + return (1); + } + memset(ipv6e, '\0', sizeof(*ipv6e)); + return (0); +} +#endif /* SOLARIS_HAVE_IPV6_MIB_SUPPORT */ + +#ifndef NETSNMP_FEATURE_REMOVE_INTERFACE_ARCH_SET_ADMIN_STATUS +int +netsnmp_arch_set_admin_status(netsnmp_interface_entry * entry, + int ifAdminStatus_val) +{ + DEBUGMSGTL(("access:interface:arch", "set_admin_status\n")); + + /* + * XXX Not supported yet + */ + return (-1); +} +#endif /* NETSNMP_FEATURE_REMOVE_INTERFACE_ARCH_SET_ADMIN_STATUS */ diff --git a/agent/mibgroup/if-mib/data_access/interface_sysctl.c b/agent/mibgroup/if-mib/data_access/interface_sysctl.c new file mode 100644 index 0000000..1deff82 --- /dev/null +++ b/agent/mibgroup/if-mib/data_access/interface_sysctl.c @@ -0,0 +1,581 @@ +/* + * Interface MIB architecture support + * + * Based on patch 1362403, submited by Rojer + * + * $Id$ + */ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-features.h> +#include <net-snmp/net-snmp-includes.h> +#include "mibII/mibII_common.h" +#include "if-mib/ifTable/ifTable_constants.h" + +#include <net-snmp/agent/net-snmp-agent-includes.h> + +#if HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#else +#error "BSD should have sys/ioctl header" +#endif + +#include <net-snmp/data_access/interface.h> +#include <net-snmp/data_access/ipaddress.h> +#include "if-mib/data_access/interface.h" + +#include <sys/types.h> +#include <sys/time.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_media.h> + +netsnmp_feature_child_of(interface_arch_set_admin_status, interface_all) + +/* + * account for minor differences between FreeBSD and OpenBSD. + * If this gets unruly, split stuff out into the respective + * files (interface_*BDS.c). + */ +/* + * FreeBSD has 2 promiscuous flags: kernel/user; check either + * http://unix.derkeiler.com/Mailing-Lists/FreeBSD/net/2004-09/0289.html + * which says: + * The first flag (IFF_PROMISC) is the one that the kernel code uses and + * sets on an interface's ifp structure. The second one is the one that + * comes from user space programs and is sent to the routine ifhwioctl() + * to set the first flag. + */ +#ifdef IFF_PPROMISC +# define ARCH_PROMISC_FLAG (IFF_PPROMISC|IFF_PROMISC) +#else +# define ARCH_PROMISC_FLAG IFF_PROMISC +#endif + +#define starttime (*(const struct timeval*)netsnmp_get_agent_starttime()) + +/* sa_len roundup macro. */ +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + +void +netsnmp_arch_interface_init(void) +{ + /* + * nothing to do + */ +} + +/* + * find the ifIndex for an interface name + * + * @retval 0 : no index found + * @retval >0: ifIndex for interface + */ +oid +netsnmp_arch_interface_index_find(const char *name) +{ + return if_nametoindex(name); +} + +/* + * subroutine to translate known media typed to speed. + * see /usr/include/net/if_media.h for definitions + */ + +void +netsnmp_sysctl_ifmedia_to_speed(int media, u_int *speed, + u_int *speed_high) +{ + *speed = 0; + *speed_high = 0; + + switch (IFM_TYPE(media)) { + case IFM_ETHER: + switch (IFM_SUBTYPE(media)) { + case IFM_10_T: + case IFM_10_2: + case IFM_10_5: + case IFM_10_STP: + case IFM_10_FL: + *speed = 10000000; + *speed_high = 10; break; + case IFM_100_TX: + case IFM_100_FX: + case IFM_100_T4: + case IFM_100_VG: + case IFM_100_T2: + *speed = 100000000; + *speed_high = 100; break; + case IFM_1000_LX: + case IFM_1000_CX: +#ifdef IFM_1000_T + case IFM_1000_T: +#endif + *speed = 1000000000; + *speed_high = 1000; break; +#ifdef IFM_10GBASE_SR + case IFM_10GBASE_SR: + case IFM_10GBASE_LR: + *speed = (u_int) -1; /* 4294967295; */ + *speed_high = 10000; break; +#endif + } + break; + case IFM_IEEE80211: + switch (IFM_SUBTYPE(media)) { + case IFM_IEEE80211_FH1: + case IFM_IEEE80211_DS1: + *speed = 1000000; + *speed_high = 1; + break; + case IFM_IEEE80211_FH2: + case IFM_IEEE80211_DS2: + *speed = 2000000; + *speed_high = 2; + break; + case IFM_IEEE80211_DS5: + *speed = 5500000; + *speed_high = 5; + break; + case IFM_IEEE80211_DS11: + *speed = 11000000; + *speed_high = 11; + break; + case IFM_IEEE80211_DS22: + *speed = 22000000; + *speed_high = 22; + break; +#ifdef IFM_IEEE80211_OFDM6 + case IFM_IEEE80211_OFDM6: + *speed = 6000000; + *speed_high = 6; + break; + case IFM_IEEE80211_OFDM9: + *speed = 9000000; + *speed_high = 9; + break; + case IFM_IEEE80211_OFDM12: + *speed = 12000000; + *speed_high = 12; + break; + case IFM_IEEE80211_OFDM18: + *speed = 18000000; + *speed_high = 18; + break; + case IFM_IEEE80211_OFDM24: + *speed = 24000000; + *speed_high = 24; + break; + case IFM_IEEE80211_OFDM36: + *speed = 36000000; + *speed_high = 36; + break; + case IFM_IEEE80211_OFDM48: + *speed = 48000000; + *speed_high = 48; + break; + case IFM_IEEE80211_OFDM54: + *speed = 54000000; + *speed_high = 54; + break; + case IFM_IEEE80211_OFDM72: + *speed = 72000000; + *speed_high = 72; + break; +#endif + } + break; +#if defined(IFM_TOKEN) + case IFM_TOKEN: + switch (IFM_SUBTYPE(media)) { + case IFM_TOK_STP4: + case IFM_TOK_UTP4: + *speed = 4000000; + *speed_high = 4; + break; + case IFM_TOK_STP16: + case IFM_TOK_UTP16: + *speed = 16000000; + *speed_high = 16; + break; +#if defined(IFM_TOK_STP100) /* guessing if you've got one, you've got the other.. */ + case IFM_TOK_STP100: + case IFM_TOK_UTP100: + *speed = 100000000; + *speed_high = 100; + break; +#endif /* IFM_TOK_STP100 */ + } + break; +#endif /* IFM_TOKEN */ +#ifdef IFM_ATM + case IFM_ATM: + switch (IFM_SUBTYPE(media)) { + case IFM_ATM_MM_155: + case IFM_ATM_SM_155: + *speed = 155000000; + *speed_high = 155; + break; + case IFM_ATM_MM_622: + case IFM_ATM_SM_622: + *speed = 622000000; + *speed_high = 622; + break; + } +#endif + } + return; +} + +/* + * @retval 0 speed could not be determined (error, unknown media) + * @retval >0 speed, equal to *speed. + * sets *speed=2^31 and returns *speed_high=speed/10^6 as required + * by ifSpeed/ifHighSpeed. + */ + +int +netsnmp_sysctl_get_if_speed(char *name, u_int *speed, + u_int *speed_high) +{ + int s; + struct ifmediareq ifmr; + int *media_list, i; + u_int t_speed, t_speed_high; + u_int m_speed, m_speed_high; + + *speed = 0; + *speed_high = 0; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + return 0; + } + + memset(&ifmr, 0, sizeof(ifmr)); + strlcpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); + + DEBUGMSGTL(("access:interface:container:sysctl"," speed in\n")); + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0 || + ifmr.ifm_count == 0) { + + close(s); + return 0; + } + + /* + * try to get speed from current media. + * if unsuccessful (e.g., interface is down), get a list of capabilities, + * try each and return maximum speed the interface is capable of. + */ + + netsnmp_sysctl_ifmedia_to_speed(ifmr.ifm_current, speed, speed_high); + + if (*speed == 0 && + (media_list = (int *) malloc(ifmr.ifm_count * sizeof(int))) != NULL ) { + + ifmr.ifm_ulist = media_list; + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) == 0) { + m_speed = 0; + m_speed_high = 0; + for (i = 0; i < ifmr.ifm_count; i++) { + + netsnmp_sysctl_ifmedia_to_speed(media_list[i], &t_speed, + &t_speed_high); + if (t_speed_high > m_speed_high || + (t_speed_high == m_speed_high && t_speed > m_speed)) { + m_speed_high = t_speed_high; + m_speed = t_speed; + } + } + *speed = m_speed; + *speed_high = m_speed_high; + } + free(media_list); + } + + close(s); + + DEBUGMSGTL(("access:interface:container:sysctl", + "%s: speed: %u, speed_high: %u\n", + name, *speed, *speed_high)); + + return *speed; +} + +static void set_counter(struct counter64 *c, uint64_t v) +{ + c->low = (uint32_t)(v); + c->high = (v) >> 32; +} + +/* + * + * @retval 0 success + * @retval -1 no container specified + * @retval -2 could not get interface info + * @retval -3 could not create entry (probably malloc) + */ +int +netsnmp_arch_interface_container_load(netsnmp_container* container, + u_int load_flags) +{ + netsnmp_interface_entry *entry = NULL; + u_char *if_list = NULL, *cp; + size_t if_list_size = 0; + struct if_msghdr *ifp; + int sysctl_oid[] = { CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0 }; + struct ifa_msghdr *ifa; + struct sockaddr *a; + struct sockaddr_dl *adl; + int amask; + char *if_name; + int flags; +#ifdef HAVE_STRUCT_IFNET_IF_LASTCHANGE_TV_NSEC + struct timespec startspec; + + TIMEVAL_TO_TIMESPEC(&starttime, &startspec); +#endif + + DEBUGMSGTL(("access:interface:container:sysctl", + "load (flags %u)\n", load_flags)); + + if (NULL == container) { + snmp_log(LOG_ERR, "no container specified/found for interface\n"); + return -1; + } + + if (sysctl(sysctl_oid, sizeof(sysctl_oid)/sizeof(int), 0, + &if_list_size, 0, 0) == -1) { + snmp_log(LOG_ERR, "could not get interface info (size)\n"); + return -2; + } + + if_list = (u_char*)malloc(if_list_size); + if (if_list == NULL) { + snmp_log(LOG_ERR, "could not allocate memory for interface info " + "(%zu bytes)\n", if_list_size); + return -3; + } else { + DEBUGMSGTL(("access:interface:container:sysctl", + "allocated %zu bytes for if_list\n", if_list_size)); + } + + if (sysctl(sysctl_oid, sizeof(sysctl_oid)/sizeof(int), if_list, + &if_list_size, 0, 0) == -1) { + snmp_log(LOG_ERR, "could not get interface info\n"); + free(if_list); + return -2; + } + + /* 1st pass: create interface entries */ + for (cp = if_list; cp < if_list + if_list_size; cp += ifp->ifm_msglen) { + + ifp = (struct if_msghdr *) cp; + if_name = NULL; + flags = 0; + adl = NULL; + + if (ifp->ifm_type != RTM_IFINFO) + continue; + + if (ifp->ifm_addrs & RTA_IFP) { + a = (struct sockaddr *) (ifp + 1); + /* + * if_msghdr is followed by one or more sockaddrs, of which we + * need only RTA_IFP. most of the time RTA_IFP is the first + * address we get, hence the shortcut. + */ + if ((ifp->ifm_addrs & (~RTA_IFP - 1)) != 0) { + /* skip all addresses up to RTA_IFP. */ + for (amask = (RTA_IFP >> 1); amask != 0; amask >>= 1) { + if (ifp->ifm_addrs & amask) + a = (struct sockaddr *) + ( ((char *) a) + ROUNDUP(a->sa_len) ); + } + } + adl = (struct sockaddr_dl *) a; + if_name = (char *) adl->sdl_data; + if_name[adl->sdl_nlen] = '\0'; + } + if (!(ifp->ifm_addrs & RTA_IFP) || if_name == NULL) { + snmp_log(LOG_ERR, "ifm_index %u: no interface name in message, " + "skipping\n", ifp->ifm_index); + continue; + } + + entry = netsnmp_access_interface_entry_create(if_name, ifp->ifm_index); + if(NULL == entry) { + netsnmp_access_interface_container_free(container, + NETSNMP_ACCESS_INTERFACE_FREE_NOFLAGS); + free(if_list); + return -3; + } + + /* get physical address */ + if (adl != NULL && adl->sdl_alen > 0) { + entry->paddr_len = adl->sdl_alen; + entry->paddr = (char*)malloc(entry->paddr_len); + memcpy(entry->paddr, adl->sdl_data + adl->sdl_nlen, adl->sdl_alen); + DEBUGMSGTL(("access:interface:container:sysctl", + "%s: paddr_len=%d, entry->paddr=%x:%x:%x:%x:%x:%x\n", + if_name, entry->paddr_len, + entry->paddr[0], entry->paddr[1], entry->paddr[2], + entry->paddr[3], entry->paddr[4], entry->paddr[5])); + } else { + entry->paddr = (char*)malloc(6); + entry->paddr_len = 6; + memset(entry->paddr, 0, 6); + } + + entry->mtu = ifp->ifm_data.ifi_mtu; + entry->type = ifp->ifm_data.ifi_type; + + + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_IF_FLAGS; + entry->os_flags = ifp->ifm_flags; + + if (ifp->ifm_flags & IFF_UP) { + entry->admin_status = IFADMINSTATUS_UP; +#if defined( LINK_STATE_UP ) && defined( LINK_STATE_DOWN ) + if (ifp->ifm_data.ifi_link_state == LINK_STATE_UP) { + entry->oper_status = IFOPERSTATUS_UP; + } else if (ifp->ifm_data.ifi_link_state == LINK_STATE_DOWN) { + entry->oper_status = IFOPERSTATUS_DOWN; + } else +#endif + { + /* + * link state is unknown, which is not very useful to report. + * use running state instead. + */ + entry->oper_status = ifp->ifm_flags & IFF_RUNNING ? 1 : 2; + } + } else { + entry->admin_status = IFADMINSTATUS_DOWN; + /* + * IF-MIB specifically says that ifOperStatus should be down in + * this case + */ + entry->oper_status = IFOPERSTATUS_DOWN; + } + + entry->reasm_max_v4 = entry->reasm_max_v6 = IP_MAXPACKET; + entry->ns_flags |= + NETSNMP_INTERFACE_FLAGS_HAS_V4_REASMMAX | + NETSNMP_INTERFACE_FLAGS_HAS_V6_REASMMAX; + + /* get counters */ + set_counter(&entry->stats.ibytes, ifp->ifm_data.ifi_ibytes); + set_counter(&entry->stats.iucast, ifp->ifm_data.ifi_ipackets); + set_counter(&entry->stats.imcast, ifp->ifm_data.ifi_imcasts); + entry->stats.ierrors = ifp->ifm_data.ifi_ierrors; + entry->stats.idiscards = ifp->ifm_data.ifi_iqdrops; + entry->stats.iunknown_protos = ifp->ifm_data.ifi_noproto; + + set_counter(&entry->stats.obytes, ifp->ifm_data.ifi_obytes); + set_counter(&entry->stats.oucast, ifp->ifm_data.ifi_opackets); + set_counter(&entry->stats.omcast, ifp->ifm_data.ifi_omcasts); + entry->stats.oerrors = ifp->ifm_data.ifi_oerrors; + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_BYTES | + NETSNMP_INTERFACE_FLAGS_HAS_DROPS | + NETSNMP_INTERFACE_FLAGS_HAS_MCAST_PKTS; + +#ifdef HAVE_STRUCT_IFNET_IF_LASTCHANGE_TV_NSEC + if (timespeccmp(&ifp->ifm_data.ifi_lastchange, &startspec, >)) { +#else + if (timercmp(&ifp->ifm_data.ifi_lastchange, &starttime, >)) { +#endif + entry->lastchange = (ifp->ifm_data.ifi_lastchange.tv_sec - + starttime.tv_sec) * 100; + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_LASTCHANGE; + } else { + entry->lastchange = 0; + } + + if (ifp->ifm_flags & ARCH_PROMISC_FLAG) + entry->promiscuous = 1; + + /* try to guess the speed from media type */ + netsnmp_sysctl_get_if_speed(entry->name, &entry->speed, + &entry->speed_high); + if (entry->speed_high != 0) { + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_HIGH_SPEED; + } else { + /* or resort to ifi_baudrate */ + entry->speed = ifp->ifm_data.ifi_baudrate; + } + + netsnmp_access_interface_entry_overrides(entry); + + CONTAINER_INSERT(container, entry); + DEBUGMSGTL(("access:interface:container:sysctl", + "created entry %d for %s\n", (int)entry->index, entry->name)); + } /* for (each interface entry) */ + + /* pass 2: walk addresses */ + for (cp = if_list; cp < if_list + if_list_size; cp += ifa->ifam_msglen) { + + ifa = (struct ifa_msghdr *) cp; + + if (ifa->ifam_type != RTM_NEWADDR) + continue; + + DEBUGMSGTL(("access:interface:container:sysctl", + "received 0x%x in RTM_NEWADDR for ifindex %u\n", + ifa->ifam_addrs, ifa->ifam_index)); + + entry = netsnmp_access_interface_entry_get_by_index(container, + ifa->ifam_index); + if (entry == NULL) { + snmp_log(LOG_ERR, "address for a nonexistent interface? index=%d", + ifa->ifam_index); + continue; + } + + /* + * walk the list of addresses received. we do not use actual + * addresses, the sole purpose of this is to set flags + */ + a = (struct sockaddr *) (ifa + 1); + for (amask = ifa->ifam_addrs; amask != 0; amask >>= 1) { + if ((amask & 1) != 0) { + DEBUGMSGTL(("access:interface:container:sysctl", + "%s: a=%p, sa_len=%d, sa_family=0x%x\n", + entry->name, a, a->sa_len, a->sa_family)); + + if (a->sa_family == AF_INET) + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_IPV4; + else if (a->sa_family == AF_INET6) + entry->ns_flags |= NETSNMP_INTERFACE_FLAGS_HAS_IPV6; + + a = (struct sockaddr *) ( ((char *) a) + ROUNDUP(a->sa_len) ); + } + } + DEBUGMSGTL(("access:interface:container:sysctl", + "%s: flags=0x%x\n", entry->name, entry->ns_flags)); + } + + if (if_list != NULL) + free(if_list); + + return 0; +} + +#ifndef NETSNMP_FEATURE_REMOVE_INTERFACE_ARCH_SET_ADMIN_STATUS +int +netsnmp_arch_set_admin_status(netsnmp_interface_entry * entry, + int ifAdminStatus_val) +{ + DEBUGMSGTL(("access:interface:arch", "set_admin_status\n")); + + /* TODO: implement this call */ + + /* not implemented */ + snmp_log(LOG_ERR, "netsnmp_arch_set_admin_status not (yet) implemented " + "for BSD sysctl.\n"); + + return -4; +} +#endif /* NETSNMP_FEATURE_REMOVE_INTERFACE_ARCH_SET_ADMIN_STATUS */ diff --git a/agent/mibgroup/if-mib/data_access/interface_sysctl.h b/agent/mibgroup/if-mib/data_access/interface_sysctl.h new file mode 100644 index 0000000..2a267d4 --- /dev/null +++ b/agent/mibgroup/if-mib/data_access/interface_sysctl.h @@ -0,0 +1,31 @@ +/* + * Interface MIB architecture support + * + * Based on patch 1362403, submited by Rojer + * + * $Id$ + */ +#ifndef INTERFACE_SYSCTL_H +#define INTERFACE_SYSCTL_H + +#ifdef __cplusplus +extern "C" { +#endif + +int +netsnmp_access_interface_sysctl_container_load(netsnmp_container* container, + u_int load_flags); + +void +netsnmp_access_interface_sysctl_ifmedia_to_speed(int media, u_int *speed, + u_int *speed_high); + +int +netsnmp_access_interface_sysctl_get_if_speed(char *name, u_int *speed, + u_int *speed_high); + +# ifdef __cplusplus +} +#endif + +#endif |