blob: 5bfbf9ab830e423f50d90cc0dfebb77c14d8b53e [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>
#include <net-snmp/data_access/interface.h>
#include <net-snmp/data_access/ipaddress.h>
#include "if-mib/data_access/interface.h"
#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;
}
/**
* 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 /* 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;
}