blob: 5f5c7fffcde35afd4282973faef497360788c941 [file] [log] [blame]
/*
* Interface MIB architecture support
*
* $Id$
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include "mibII/mibII_common.h"
#include "if-mib/ifTable/ifTable_constants.h"
#include "if-mib/data_access/interface.h"
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/library/snmp_enum.h>
#include <net-snmp/data_access/interface.h>
/**---------------------------------------------------------------------*/
/*
* 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);
}
/*
* 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 netsnmp_container *c = NULL;
static netsnmp_interface_entry *e = NULL;
/**
*
*/
void
Interface_Scan_Init(void)
{
/*
* ifTable container shouldn't change, so we shouldn' have to
* re-fetch it every time.
*/
if (NULL != c)
netsnmp_access_interface_container_free(c, 0);
c = netsnmp_access_interface_container_load(NULL, 0);
if (NULL != c) {
if (NULL != it)
ITERATOR_RELEASE(it);
it = CONTAINER_ITERATOR(c);
}
if (NULL != it)
e = (netsnmp_interface_entry*)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)
{
if (NULL == e)
return 0;
if(index)
*index = e->index;
if(name)
strcpy(name, e->name);
if (entry)
*entry = e;
e = (netsnmp_interface_entry*)ITERATOR_NEXT(it);
return 1;
}
#endif /* NETSNMP_NO_BACKWARDS_COMPATABILITY */
/**
*
* @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
/**---------------------------------------------------------------------*/
/*
* 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 {
if (NULL != lhs->descr)
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 {
if (NULL != lhs->paddr)
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;
}