| /* |
| * 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; |
| } |
| } |
| |
| strncpy(ifrq->ifr_name, name, sizeof(ifrq->ifr_name)); |
| ifrq->ifr_name[ sizeof(ifrq->ifr_name)-1 ] = 0; |
| 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 = 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 = 6; |
| 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 = 131; |
| break; /* tunnel */ |
| #endif |
| #ifdef ARPHRD_SLIP |
| case ARPHRD_SLIP: |
| case ARPHRD_CSLIP: |
| case ARPHRD_SLIP6: |
| case ARPHRD_CSLIP6: |
| ifentry->type = 28; |
| break; /* slip */ |
| #endif |
| #ifdef ARPHRD_PPP |
| case ARPHRD_PPP: |
| ifentry->type = 23; |
| break; /* ppp */ |
| #endif |
| #ifdef ARPHRD_LOOPBACK |
| case ARPHRD_LOOPBACK: |
| ifentry->type = 24; |
| break; /* softwareLoopback */ |
| #endif |
| #ifdef ARPHRD_FDDI |
| case ARPHRD_FDDI: |
| ifentry->type = 15; |
| break; |
| #endif |
| #ifdef ARPHRD_ARCNET |
| case ARPHRD_ARCNET: |
| ifentry->type = 35; |
| break; |
| #endif |
| #ifdef ARPHRD_LOCALTLK |
| case ARPHRD_LOCALTLK: |
| ifentry->type = 42; |
| break; |
| #endif |
| #ifdef ARPHRD_HIPPI |
| case ARPHRD_HIPPI: |
| ifentry->type = 47; |
| break; |
| #endif |
| #ifdef ARPHRD_ATM |
| case ARPHRD_ATM: |
| ifentry->type = 37; |
| 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)); |
| } /* 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; |
| } |
| } |
| |
| 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; |
| } |
| } |
| |
| strncpy(ifrq.ifr_name, ifentry->name, sizeof(ifrq.ifr_name)); |
| ifrq.ifr_name[ sizeof(ifrq.ifr_name)-1 ] = 0; |
| 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", "mtu_get\n")); |
| |
| rc = _ioctl_get(fd, SIOCGIFINDEX, &ifrq, name); |
| if (rc < 0) |
| return 0; |
| |
| return ifrq.ifr_ifindex; |
| #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 != |
| 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; |
| } |