diff options
Diffstat (limited to 'agent/mibgroup/if-mib/data_access/interface_ioctl.c')
-rw-r--r-- | agent/mibgroup/if-mib/data_access/interface_ioctl.c | 506 |
1 files changed, 506 insertions, 0 deletions
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; +} |