| /* |
| * Interface MIB architecture support |
| */ |
| #include <net-snmp/net-snmp-config.h> |
| #include <net-snmp/net-snmp-includes.h> |
| |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| #include <net-snmp/data_access/arp.h> |
| #include <net-snmp/data_access/interface.h> |
| |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <net/if_arp.h> |
| #include <arpa/inet.h> |
| #include <linux/types.h> |
| #include <asm/types.h> |
| #ifdef HAVE_LINUX_RTNETLINK_H |
| #include <linux/rtnetlink.h> |
| #endif |
| #ifdef NETSNMP_ENABLE_IPV6 |
| #define NIP6(addr) \ |
| ntohs((addr).s6_addr16[0]), \ |
| ntohs((addr).s6_addr16[1]), \ |
| ntohs((addr).s6_addr16[2]), \ |
| ntohs((addr).s6_addr16[3]), \ |
| ntohs((addr).s6_addr16[4]), \ |
| ntohs((addr).s6_addr16[5]), \ |
| ntohs((addr).s6_addr16[6]), \ |
| ntohs((addr).s6_addr16[7]) |
| #define NIP6_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" |
| #endif |
| |
| int _load_v4(netsnmp_container *container, int idx_offset); |
| #ifdef NETSNMP_ENABLE_IPV6 |
| static int _load_v6(netsnmp_container *container, int idx_offset); |
| #endif |
| #ifdef HAVE_LINUX_RTNETLINK_H |
| int get_translation_table_info (int sd, int *status, |
| char *buff, size_t size); |
| int fillup_entry_info(netsnmp_arp_entry *entry, |
| struct nlmsghdr *nlmp); |
| #endif |
| /** |
| */ |
| int |
| netsnmp_access_arp_container_arch_load(netsnmp_container *container) |
| { |
| int rc = 0, idx_offset = 0; |
| |
| rc = _load_v4(container, idx_offset); |
| if(rc < 0) { |
| u_int flags = NETSNMP_ACCESS_ARP_FREE_KEEP_CONTAINER; |
| netsnmp_access_arp_container_free(container, flags); |
| } |
| |
| #if defined (NETSNMP_ENABLE_IPV6) |
| idx_offset = (rc < 0) ? 0 : rc; |
| |
| rc = _load_v6(container, idx_offset); |
| if(rc < 0) { |
| u_int flags = NETSNMP_ACCESS_ARP_FREE_KEEP_CONTAINER; |
| netsnmp_access_arp_container_free(container, flags); |
| } |
| #endif |
| |
| /* |
| * return no errors (0) if we found any interfaces |
| */ |
| if(rc > 0) |
| rc = 0; |
| |
| return rc; |
| } |
| |
| /** |
| */ |
| int |
| _load_v4(netsnmp_container *container, int idx_offset) |
| { |
| FILE *in; |
| char line[128]; |
| int rc = 0; |
| netsnmp_arp_entry *entry; |
| char arp[3*NETSNMP_ACCESS_ARP_PHYSADDR_BUF_SIZE+1]; |
| char *arp_token; |
| int i; |
| |
| netsnmp_assert(NULL != container); |
| |
| #define PROCFILE "/proc/net/arp" |
| if (!(in = fopen(PROCFILE, "r"))) { |
| snmp_log(LOG_DEBUG,"could not open " PROCFILE "\n"); |
| return -2; |
| } |
| |
| /* |
| * Get rid of the header line |
| */ |
| fgets(line, sizeof(line), in); |
| |
| /* |
| * IP address | HW | Flag | HW address | Mask | Device |
| * 192.168.1.4 0x1 0x2 00:40:63:CC:1C:8C * eth0 |
| */ |
| while (fgets(line, sizeof(line), in)) { |
| |
| int za, zb, zc, zd; |
| unsigned int tmp_flags; |
| char ifname[21]; |
| |
| rc = sscanf(line, |
| "%d.%d.%d.%d 0x%*x 0x%x %96s %*[^ ] %20s\n", |
| &za, &zb, &zc, &zd, &tmp_flags, arp, ifname); |
| if (7 != rc) { |
| snmp_log(LOG_ERR, PROCFILE " data format error (%d!=12)\n", rc); |
| snmp_log(LOG_ERR, " line ==|%s|\n", line); |
| continue; |
| } |
| DEBUGMSGTL(("access:arp:container", |
| "ip addr %d.%d.%d.%d, flags 0x%X, hw addr " |
| "%s, name %s\n", |
| za,zb,zc,zd, tmp_flags, arp, ifname )); |
| |
| /* |
| */ |
| entry = netsnmp_access_arp_entry_create(); |
| if(NULL == entry) { |
| rc = -3; |
| break; |
| } |
| |
| /* |
| * look up ifIndex |
| */ |
| entry->if_index = netsnmp_access_interface_index_find(ifname); |
| if(0 == entry->if_index) { |
| snmp_log(LOG_ERR,"couldn't find ifIndex for '%s', skipping\n", |
| ifname); |
| netsnmp_access_arp_entry_free(entry); |
| continue; |
| } |
| |
| /* |
| * now that we've passed all the possible 'continue', assign |
| * index offset. |
| */ |
| entry->ns_arp_index = ++idx_offset; |
| |
| /* |
| * parse ip addr |
| */ |
| entry->arp_ipaddress[0] = za; |
| entry->arp_ipaddress[1] = zb; |
| entry->arp_ipaddress[2] = zc; |
| entry->arp_ipaddress[3] = zd; |
| entry->arp_ipaddress_len = 4; |
| |
| /* |
| * parse hw addr |
| */ |
| for (arp_token = strtok(arp, ":"), i=0; arp_token != NULL; arp_token = strtok(NULL, ":"), i++) { |
| entry->arp_physaddress[i] = strtol(arp_token, NULL, 16); |
| } |
| entry->arp_physaddress_len = i; |
| |
| /* |
| * what can we do with hw? from arp manpage: |
| |
| default value of this parameter is ether (i.e. hardware code |
| 0x01 for IEEE 802.3 10Mbps Ethernet). Other values might |
| include network technologies such as ARCnet (arcnet) , PROnet |
| (pronet) , AX.25 (ax25) and NET/ROM (netrom). |
| */ |
| |
| /* |
| * parse mask |
| */ |
| /* xxx-rks: what is mask? how to interpret '*'? */ |
| |
| |
| /* |
| * process type |
| */ |
| if(tmp_flags & ATF_PERM) |
| entry->arp_type = INETNETTOMEDIATYPE_STATIC; |
| else |
| entry->arp_type = INETNETTOMEDIATYPE_DYNAMIC; |
| |
| /* |
| * process status |
| * if flags are 0, we can't tell the difference between |
| * stale or incomplete. |
| */ |
| if(tmp_flags & ATF_COM) |
| entry->arp_state = INETNETTOMEDIASTATE_REACHABLE; |
| else |
| entry->arp_state = INETNETTOMEDIASTATE_UNKNOWN; |
| |
| /* |
| * add entry to container |
| */ |
| if (CONTAINER_INSERT(container, entry) < 0) |
| { |
| DEBUGMSGTL(("access:arp:container","error with arp_entry: insert into container failed.\n")); |
| netsnmp_access_arp_entry_free(entry); |
| continue; |
| } |
| } |
| |
| fclose(in); |
| if( rc < 0 ) |
| return rc; |
| |
| return idx_offset; |
| } |
| |
| #if defined (NETSNMP_ENABLE_IPV6) |
| static int |
| _load_v6(netsnmp_container *container, int idx_offset) |
| { |
| char buffer[16384]; |
| #if defined(HAVE_LINUX_RTNETLINK_H) |
| struct nlmsghdr *nlmp; |
| #endif |
| int sd = 0; |
| int status = 0; |
| int rc = 0; |
| int len, req_len; |
| netsnmp_arp_entry *entry; |
| |
| netsnmp_assert(NULL != container); |
| #if defined(HAVE_LINUX_RTNETLINK_H) |
| if((sd = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { |
| snmp_log(LOG_ERR,"Unable to create netlink socket\n"); |
| return -2; |
| } |
| |
| if(get_translation_table_info (sd, &status, buffer, sizeof(buffer)) < 0) { |
| snmp_log(LOG_ERR,"Unable to fetch translation table info\n"); |
| close(sd); |
| return -2; |
| } |
| |
| for (nlmp = (struct nlmsghdr *)buffer; status > sizeof(*nlmp); ) { |
| len = nlmp->nlmsg_len; |
| req_len = len - sizeof(*nlmp); |
| if (req_len < 0 || len > status) { |
| snmp_log(LOG_ERR,"invalid length\n"); |
| return -2; |
| } |
| if (!NLMSG_OK (nlmp, status)) { |
| snmp_log(LOG_ERR,"NLMSG not OK\n"); |
| return -2; |
| } |
| entry = netsnmp_access_arp_entry_create(); |
| if(NULL == entry) { |
| rc = -3; |
| break; |
| } |
| entry->ns_arp_index = ++idx_offset; |
| if(fillup_entry_info (entry, nlmp) < 0) { |
| DEBUGMSGTL(("access:arp:load_v6", "skipping netlink message that" |
| " did not contain valid ARP information\n")); |
| netsnmp_access_arp_entry_free(entry); |
| status -= NLMSG_ALIGN(len); |
| nlmp = (struct nlmsghdr*)((char*)nlmp + NLMSG_ALIGN(len)); |
| continue; |
| } |
| CONTAINER_INSERT(container, entry); |
| status -= NLMSG_ALIGN(len); |
| nlmp = (struct nlmsghdr*)((char*)nlmp + NLMSG_ALIGN(len)); |
| } |
| |
| close(sd); |
| #endif |
| if(rc<0) { |
| return rc; |
| } |
| |
| return idx_offset; |
| } |
| #if defined(HAVE_LINUX_RTNETLINK_H) |
| int |
| get_translation_table_info (int sd, int *status, char *buff, size_t size) |
| { |
| struct { |
| struct nlmsghdr n; |
| struct ndmsg r; |
| char buf[1024]; |
| } req; |
| struct rtattr *rta; |
| |
| memset(&req, 0, sizeof(req)); |
| req.n.nlmsg_len = NLMSG_LENGTH (sizeof(struct ndmsg)); |
| req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; |
| req.n.nlmsg_type = RTM_GETNEIGH; |
| |
| req.r.ndm_family = AF_INET6; |
| rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.n.nlmsg_len)); |
| rta->rta_len = RTA_LENGTH(16); |
| |
| if(send(sd, &req, req.n.nlmsg_len, 0) < 0) { |
| snmp_log(LOG_ERR,"Sending request failed\n"); |
| return -1; |
| } |
| if((*status = recv(sd, buff, size, 0)) < 0) { |
| snmp_log(LOG_ERR,"Recieving request failed\n"); |
| return -1; |
| } |
| if(*status == 0) { |
| snmp_log(LOG_ERR,"End of file\n"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int |
| fillup_entry_info(netsnmp_arp_entry *entry, struct nlmsghdr *nlmp) |
| { |
| struct ndmsg *rtmp; |
| struct in6_addr *in6p; |
| struct rtattr *tb[NDA_MAX + 1], *rta; |
| size_t in_len, out_len; |
| unsigned int i; |
| int length; |
| char addr[40]; |
| u_char *buf; |
| u_char *hwaddr; |
| |
| rtmp = (struct ndmsg *) NLMSG_DATA(nlmp); |
| if (nlmp->nlmsg_type != RTM_NEWNEIGH) { |
| snmp_log(LOG_ERR, "Wrong netlink message type %d\n", nlmp->nlmsg_type); |
| return -1; |
| } |
| |
| if (rtmp->ndm_state != NUD_NOARP) { |
| memset(tb, 0, sizeof(struct rtattr *) * (NDA_MAX + 1)); |
| length = nlmp->nlmsg_len - NLMSG_LENGTH(sizeof(*rtmp)); |
| if (length < 0) { |
| snmp_log(LOG_ERR, "netlink message length %d < %d is invalid\n", |
| nlmp->nlmsg_len, (int)NLMSG_LENGTH(sizeof(*rtmp))); |
| return -1; |
| } |
| /* |
| * this is what the kernel-removed NDA_RTA define did |
| */ |
| rta = ((struct rtattr *) (((char *) (rtmp)) + |
| NLMSG_ALIGN(sizeof(struct ndmsg)))); |
| while (RTA_OK(rta, length)) { |
| if (rta->rta_type <= NDA_MAX) |
| tb[rta->rta_type] = rta; |
| rta = RTA_NEXT(rta, length); |
| } |
| if (length) { |
| snmp_log(LOG_ERR, "Received uneven number of netlink" |
| " messages - %d bytes remaining\n", length); |
| return -1; |
| } |
| /* |
| * Fill up the index |
| */ |
| entry->if_index = rtmp->ndm_ifindex; |
| /* |
| * Fill up ip address |
| */ |
| if (tb[NDA_DST]) { |
| memset(&addr, '\0', sizeof(addr)); |
| in6p = (struct in6_addr *) RTA_DATA(tb[NDA_DST]); |
| sprintf(addr, NIP6_FMT, NIP6(*in6p)); |
| in_len = entry->arp_ipaddress_len = |
| sizeof(entry->arp_ipaddress); |
| netsnmp_assert(16 == in_len); |
| out_len = 0; |
| buf = entry->arp_ipaddress; |
| if (1 != netsnmp_hex_to_binary(&buf, &in_len, |
| &out_len, 0, addr, ":")) { |
| snmp_log(LOG_ERR, "error parsing '%s', skipping\n", |
| entry->arp_ipaddress); |
| return -1; |
| } |
| netsnmp_assert(16 == out_len); |
| entry->arp_ipaddress_len = out_len; |
| } |
| if (tb[NDA_LLADDR]) { |
| memset(&addr, '\0', sizeof(addr)); |
| hwaddr = RTA_DATA(tb[NDA_LLADDR]); |
| entry->arp_physaddress_len = RTA_PAYLOAD(tb[NDA_LLADDR]); |
| buf = entry->arp_physaddress; |
| for (i = 0; i < entry->arp_physaddress_len; i++) |
| entry->arp_physaddress[i] = hwaddr[i]; |
| } |
| |
| switch (rtmp->ndm_state) { |
| case NUD_INCOMPLETE: |
| entry->arp_state = INETNETTOMEDIASTATE_INCOMPLETE; |
| break; |
| case NUD_REACHABLE: |
| case NUD_PERMANENT: |
| entry->arp_state = INETNETTOMEDIASTATE_REACHABLE; |
| break; |
| case NUD_STALE: |
| entry->arp_state = INETNETTOMEDIASTATE_STALE; |
| break; |
| case NUD_DELAY: |
| entry->arp_state = INETNETTOMEDIASTATE_DELAY; |
| break; |
| case NUD_PROBE: |
| entry->arp_state = INETNETTOMEDIASTATE_PROBE; |
| break; |
| case NUD_FAILED: |
| entry->arp_state = INETNETTOMEDIASTATE_INVALID; |
| break; |
| case NUD_NONE: |
| entry->arp_state = INETNETTOMEDIASTATE_UNKNOWN; |
| break; |
| default: |
| snmp_log(LOG_ERR, "Unrecognized ARP entry state %d", rtmp->ndm_state); |
| break; |
| } |
| |
| switch (rtmp->ndm_state) { |
| case NUD_INCOMPLETE: |
| case NUD_FAILED: |
| case NUD_NONE: |
| entry->arp_type = INETNETTOMEDIATYPE_INVALID; |
| break; |
| case NUD_REACHABLE: |
| case NUD_STALE: |
| case NUD_DELAY: |
| case NUD_PROBE: |
| entry->arp_type = INETNETTOMEDIATYPE_DYNAMIC; |
| break; |
| case NUD_PERMANENT: |
| entry->arp_type = INETNETTOMEDIATYPE_STATIC; |
| break; |
| default: |
| entry->arp_type = INETNETTOMEDIATYPE_LOCAL; |
| break; |
| } |
| } else { |
| return -1; /* could not create data for this interface */ |
| } |
| |
| return 0; |
| } |
| #endif |
| #endif |