blob: 5da2fdc8a4e8a998f537f569815226f77c53f670 [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 <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>
extern struct timeval 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 = 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 = 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 = 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;
}
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;
}