diff options
Diffstat (limited to 'agent/mibgroup/ip-mib/data_access/arp_netlink.c')
-rw-r--r-- | agent/mibgroup/ip-mib/data_access/arp_netlink.c | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/agent/mibgroup/ip-mib/data_access/arp_netlink.c b/agent/mibgroup/ip-mib/data_access/arp_netlink.c new file mode 100644 index 0000000..2059c23 --- /dev/null +++ b/agent/mibgroup/ip-mib/data_access/arp_netlink.c @@ -0,0 +1,304 @@ +/* + * 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 <errno.h> +#include <sys/types.h> +#include <linux/types.h> +#include <linux/rtnetlink.h> + + +static int fillup_entry_info(netsnmp_arp_entry *entry, struct nlmsghdr *h); +static void netsnmp_access_arp_read_netlink(int fd, void *data); + +/** + */ +netsnmp_arp_access * +netsnmp_access_arp_create(u_int init_flags, + NetsnmpAccessArpUpdate *update_hook, + NetsnmpAccessArpGC *gc_hook, + int *cache_timeout, int *cache_flags, + char *cache_expired) +{ + netsnmp_arp_access *access; + + access = SNMP_MALLOC_TYPEDEF(netsnmp_arp_access); + if (NULL == access) { + snmp_log(LOG_ERR,"malloc error in netsnmp_access_arp_create\n"); + return NULL; + } + + access->arch_magic = NULL; + access->magic = NULL; + access->update_hook = update_hook; + access->gc_hook = gc_hook; + access->synchronized = 0; + + if (cache_timeout != NULL) + *cache_timeout = 5; + if (cache_flags != NULL) + *cache_flags |= NETSNMP_CACHE_RESET_TIMER_ON_USE | NETSNMP_CACHE_DONT_FREE_BEFORE_LOAD; + access->cache_expired = cache_expired; + + DEBUGMSGTL(("access:netlink:arp", "create arp cache\n")); + + return access; +} + +int netsnmp_access_arp_delete(netsnmp_arp_access *access) +{ + if (NULL == access) + return 0; + + netsnmp_access_arp_unload(access); + free(access); + + return 0; +} + +int netsnmp_access_arp_load(netsnmp_arp_access *access) +{ + int r, fd = (uintptr_t) access->arch_magic; + struct { + struct nlmsghdr n; + struct ndmsg r; + } req; + + if (access->synchronized) + return 0; + + if (fd == 0) { + struct sockaddr_nl sa; + + fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + snmp_log(LOG_ERR,"netsnmp_access_arp_load: netlink socket create error\n"); + return -1; + } + access->arch_magic = (void *)(uintptr_t)fd; + + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + sa.nl_groups = RTMGRP_NEIGH; + if (bind(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) { + snmp_log(LOG_ERR,"netsnmp_access_arp_load: netlink bind failed\n"); + return -1; + } + + if (register_readfd(fd, netsnmp_access_arp_read_netlink, access) != 0) { + snmp_log(LOG_ERR,"netsnmp_access_arp_load: error registering netlink socket\n"); + return -1; + } + } + + DEBUGMSGTL(("access:netlink:arp", "synchronizing arp table\n")); + + access->generation++; + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_len = sizeof(req); + req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; + req.n.nlmsg_type = RTM_GETNEIGH; + req.r.ndm_family = AF_UNSPEC; + + r = send(fd, &req, req.n.nlmsg_len, 0); + if (r < 0) { + snmp_log(LOG_ERR,"netsnmp_access_arp_refresh: send failed\n"); + return -1; + } + + while (!access->synchronized) + netsnmp_access_arp_read_netlink(fd, access); + access->gc_hook(access); + + return 0; +} + +int netsnmp_access_arp_unload(netsnmp_arp_access *access) +{ + int fd; + + DEBUGMSGTL(("access:netlink:arp", "unload arp cache\n")); + + fd = (uintptr_t) access->arch_magic; + if (fd > 0) { + unregister_readfd(fd); + close(fd); + access->arch_magic = NULL; + access->synchronized = 0; + } + return 0; +} + +static void netsnmp_access_arp_read_netlink(int fd, void *data) +{ + netsnmp_arp_access *access = (netsnmp_arp_access *) data; + netsnmp_arp_entry *entry; + char buf[16384]; + struct nlmsghdr *h; + int r, len; + + do { + r = recv(fd, buf, sizeof(buf), MSG_DONTWAIT); + if (r < 0) { + if (errno == EINTR) + continue; + if (errno == EAGAIN) + return; + snmp_log(LOG_WARNING, "netlink buffer overrun\n"); + access->synchronized = 0; + if (access->cache_expired != NULL) + *access->cache_expired = 1; + return; + } + } while (0); + len = r; + + for (h = (struct nlmsghdr *) buf; NLMSG_OK(h, len); h = NLMSG_NEXT(h, len)) { + if (h->nlmsg_type == NLMSG_DONE) { + access->synchronized = 1; + continue; + } + + entry = netsnmp_access_arp_entry_create(); + if (NULL == entry) + break; + + DEBUGMSGTL(("access:netlink:arp", "arp netlink notification\n")); + + entry->generation = access->generation; + r = fillup_entry_info (entry, h); + if (r > 0) { + access->update_hook(access, entry); + } else { + if (r < 0) { + NETSNMP_LOGONCE((LOG_ERR, "filling entry info failed\n")); + DEBUGMSGTL(("access:netlink:arp", "filling entry info failed\n")); + } + netsnmp_access_arp_entry_free(entry); + } + } +} + +static int +fillup_entry_info(netsnmp_arp_entry *entry, struct nlmsghdr *nlmp) +{ + struct ndmsg *rtmp; + struct rtattr *tb[NDA_MAX + 1], *rta; + int length; + + rtmp = (struct ndmsg *) NLMSG_DATA(nlmp); + switch (nlmp->nlmsg_type) { + case RTM_NEWNEIGH: + if (rtmp->ndm_state == NUD_FAILED) + entry->flags = NETSNMP_ACCESS_ARP_ENTRY_FLAG_DELETE; + else + entry->flags = 0; + break; + case RTM_DELNEIGH: + entry->flags = NETSNMP_ACCESS_ARP_ENTRY_FLAG_DELETE; + break; + case RTM_GETNEIGH: + return 0; + default: + DEBUGMSGTL(("access:netlink:arp", + "Wrong Netlink message type %d\n", nlmp->nlmsg_type)); + return -1; + } + + if (rtmp->ndm_state == NUD_NOARP) { + /* NUD_NOARP is for broadcast addresses and similar, + * drop them silently */ + return 0; + } + + memset(tb, 0, sizeof(struct rtattr *) * (NDA_MAX + 1)); + length = nlmp->nlmsg_len - NLMSG_LENGTH(sizeof(*rtmp)); + 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); + } + + /* + * Fill up the index and addresses + */ + entry->if_index = rtmp->ndm_ifindex; + if (tb[NDA_DST]) { + entry->arp_ipaddress_len = RTA_PAYLOAD(tb[NDA_DST]); + if (entry->arp_ipaddress_len > sizeof(entry->arp_ipaddress)) { + snmp_log(LOG_ERR, "netlink ip address length %d is too long\n", + entry->arp_ipaddress_len); + return -1; + } + memcpy(entry->arp_ipaddress, RTA_DATA(tb[NDA_DST]), + entry->arp_ipaddress_len); + } + if (tb[NDA_LLADDR]) { + entry->arp_physaddress_len = RTA_PAYLOAD(tb[NDA_LLADDR]); + if (entry->arp_physaddress_len > sizeof(entry->arp_physaddress)) { + snmp_log(LOG_ERR, "netlink hw address length %d is too long\n", + entry->arp_physaddress_len); + return -1; + } + memcpy(entry->arp_physaddress, RTA_DATA(tb[NDA_LLADDR]), + entry->arp_physaddress_len); + } + + 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; + } + + return 1; +} |