| /* |
| * Interface MIB architecture support |
| * |
| * $Id:$ |
| */ |
| #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/defaultrouter.h> |
| |
| #include "ip-mib/ipDefaultRouterTable/ipDefaultRouterTable.h" |
| |
| #include <asm/types.h> |
| #ifdef HAVE_LINUX_RTNETLINK_H |
| #include <linux/netlink.h> |
| #include <linux/rtnetlink.h> |
| #endif |
| #include <sys/socket.h> |
| #include <arpa/inet.h> |
| #include <errno.h> |
| #include <unistd.h> |
| |
| #define RCVBUF_SIZE 32768 |
| #define SNDBUF_SIZE 512 |
| |
| #ifdef NETSNMP_ENABLE_IPV6 |
| #define DR_ADDRSTRLEN INET6_ADDRSTRLEN |
| #else |
| #define DR_ADDRSTRLEN INET_ADDRSTRLEN |
| #endif |
| |
| /**---------------------------------------------------------------------*/ |
| /* |
| * local static prototypes |
| */ |
| static int _load(netsnmp_container *container); |
| |
| |
| /* |
| * initialize arch specific storage |
| * |
| * @retval 0: success |
| * @retval <0: error |
| */ |
| int |
| netsnmp_arch_defaultrouter_entry_init(netsnmp_defaultrouter_entry *entry) |
| { |
| /* |
| * init |
| */ |
| return 0; |
| } |
| |
| /** |
| * |
| * @retval 0 no errors |
| * @retval !0 errors |
| */ |
| int |
| netsnmp_arch_defaultrouter_container_load(netsnmp_container *container, |
| u_int load_flags) |
| { |
| int rc = 0; |
| |
| DEBUGMSGTL(("access:defaultrouter:entry:arch", "load (linux)\n")); |
| |
| rc = _load(container); |
| if (rc < 0) { |
| u_int flags = NETSNMP_ACCESS_DEFAULTROUTER_FREE_KEEP_CONTAINER; |
| netsnmp_access_defaultrouter_container_free(container, flags); |
| } |
| |
| return rc; |
| } |
| |
| /** |
| * |
| * @retval 0 no errors |
| * @retval !0 errors |
| */ |
| static int |
| _load(netsnmp_container *container) |
| { |
| #ifndef HAVE_LINUX_RTNETLINK_H |
| DEBUGMSGTL(("access:defaultrouter", |
| "cannot get default router information" |
| "as netlink socket is not available\n")); |
| return -1; |
| #else |
| int rc = 0; |
| int idx_offset = 0; |
| netsnmp_defaultrouter_entry *entry; |
| int nlsk; |
| struct sockaddr_nl addr; |
| int rcvbuf_size = RCVBUF_SIZE; |
| union { |
| struct nlmsghdr hdr; |
| unsigned char rcvbuf[RCVBUF_SIZE]; |
| } rcvbuf_union; |
| union { |
| struct nlmsghdr hdr; |
| unsigned char sndbuf[SNDBUF_SIZE]; |
| } sndbuf_union; |
| unsigned char *const rcvbuf = rcvbuf_union.rcvbuf; |
| unsigned char *const sndbuf = sndbuf_union.sndbuf; |
| struct nlmsghdr *hdr; |
| struct rtmsg *rthdr; |
| int count; |
| int end_of_message = 0; |
| long hz = sysconf(_SC_CLK_TCK); |
| |
| netsnmp_assert(NULL != container); |
| |
| /* |
| * Open a netlink socket |
| */ |
| nlsk = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); |
| if (nlsk < 0) { |
| snmp_log(LOG_ERR, "Could not open netlink socket : %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| |
| if (setsockopt(nlsk, SOL_SOCKET, SO_RCVBUF, |
| &rcvbuf_size, sizeof(rcvbuf_size)) < 0) { |
| snmp_log(LOG_ERR, "Could not open netlink socket : %s\n", |
| strerror(errno)); |
| close(nlsk); |
| return -1; |
| } |
| |
| memset(&addr, '\0', sizeof(struct sockaddr_nl)); |
| addr.nl_family = AF_NETLINK; |
| |
| memset(sndbuf, '\0', SNDBUF_SIZE); |
| hdr = &sndbuf_union.hdr; |
| hdr->nlmsg_type = RTM_GETROUTE; |
| hdr->nlmsg_pid = getpid(); |
| hdr->nlmsg_seq = 0; |
| hdr->nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; |
| hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); |
| rthdr = (struct rtmsg *)NLMSG_DATA(hdr); |
| rthdr->rtm_table = RT_TABLE_MAIN; |
| |
| /* |
| * Send a request to the kernel to dump the routing table to us |
| */ |
| count = sendto(nlsk, sndbuf, hdr->nlmsg_len, 0, |
| (struct sockaddr *)&addr, sizeof(struct sockaddr_nl)); |
| if (count < 0) { |
| snmp_log(LOG_ERR, "unable to send netlink message to kernel : %s\n", |
| strerror(errno)); |
| close(nlsk); |
| return -2; |
| } |
| |
| /* |
| * Now listen for response |
| */ |
| do { |
| struct nlmsghdr *nlmhp; |
| struct rtmsg *rtmp; |
| struct rtattr *rtap; |
| struct rta_cacheinfo *rtci; |
| socklen_t sock_len; |
| int rtcount; |
| |
| memset(rcvbuf, '\0', RCVBUF_SIZE); |
| sock_len = sizeof(struct sockaddr_nl); |
| |
| /* |
| * Get the message |
| */ |
| count = recvfrom(nlsk, rcvbuf, RCVBUF_SIZE, 0, |
| (struct sockaddr *)&addr, &sock_len); |
| if (count < 0) { |
| snmp_log(LOG_ERR, "unable to receive netlink messages: %s\n", |
| strerror(errno)); |
| rc = -1; |
| break; |
| } |
| |
| /* |
| * Walk all of the returned messages |
| */ |
| nlmhp = &rcvbuf_union.hdr; |
| while (NLMSG_OK(nlmhp, count)) { |
| u_char addresstype; |
| char address[NETSNMP_ACCESS_DEFAULTROUTER_BUF_SIZE + 1]; |
| size_t address_len = 0; |
| int if_index = -1; |
| u_long lifetime = 0; |
| int preference = -3; |
| |
| /* |
| * Make sure the message is ok |
| */ |
| if (nlmhp->nlmsg_type == NLMSG_ERROR) { |
| snmp_log(LOG_ERR, "kernel produced nlmsg err\n"); |
| rc = -1; |
| break; |
| } |
| |
| /* |
| * End of message, we're done |
| */ |
| if (nlmhp->nlmsg_type & NLMSG_DONE) { |
| end_of_message = 1; |
| break; |
| } |
| |
| /* |
| * Get the pointer to the rtmsg struct |
| */ |
| rtmp = NLMSG_DATA(nlmhp); |
| |
| /* |
| * zero length destination is a default route |
| */ |
| if (rtmp->rtm_dst_len != 0) |
| goto next_nlmsghdr; |
| |
| /* |
| * Start scanning the attributes for needed info |
| */ |
| if (rtmp->rtm_family == AF_INET) { |
| addresstype = INETADDRESSTYPE_IPV4; |
| lifetime = IPDEFAULTROUTERLIFETIME_MAX; /* infinity */ |
| } |
| #ifdef NETSNMP_ENABLE_IPV6 |
| else if (rtmp->rtm_family == AF_INET6) { |
| addresstype = INETADDRESSTYPE_IPV6; |
| /* router lifetime for IPv6 is retrieved by RTA_CACHEINFO */ |
| lifetime = 0; |
| } |
| #endif |
| else |
| goto next_nlmsghdr; /* skip, we don't care about this route */ |
| |
| preference = 0; /* preference is medium(0) for now */ |
| |
| rtap = RTM_RTA(rtmp); |
| rtcount = RTM_PAYLOAD(nlmhp); |
| while (RTA_OK(rtap, rtcount)) { |
| switch (rtap->rta_type) { |
| case RTA_OIF: |
| if_index = *(int *)(RTA_DATA(rtap)); |
| break; |
| |
| case RTA_GATEWAY: |
| address_len = RTA_PAYLOAD(rtap); |
| memset(address, '\0', sizeof(address)); |
| memcpy(address, RTA_DATA(rtap), address_len); |
| break; |
| |
| #ifdef NETSNMP_ENABLE_IPV6 |
| case RTA_CACHEINFO: |
| rtci = RTA_DATA(rtap); |
| if ((rtmp->rtm_flags & RTM_F_CLONED) || |
| (rtci && rtci->rta_expires)) { |
| lifetime = rtci->rta_expires / hz; |
| } |
| break; |
| #endif |
| |
| default: |
| break; |
| } /* switch */ |
| |
| rtap = RTA_NEXT(rtap, rtcount); |
| } /* while RTA_OK(rtap) */ |
| |
| if (address_len != 0 && if_index != -1 && |
| lifetime != 0 && preference != -3 ) { |
| DEBUGIF("access:defaultrouter") { |
| char addr_str[DR_ADDRSTRLEN]; |
| memset(addr_str, '\0', DR_ADDRSTRLEN); |
| |
| if (rtmp->rtm_family == AF_INET) |
| inet_ntop(AF_INET, address, addr_str, DR_ADDRSTRLEN); |
| #ifdef NETSNMP_ENABLE_IPV6 |
| else |
| inet_ntop(AF_INET6, address, addr_str, DR_ADDRSTRLEN); |
| #endif |
| DEBUGMSGTL(("access:defaultrouter", |
| "found default route: %s if_index %d " |
| "lifetime %lu preference %d\n", |
| addr_str, if_index, lifetime, preference)); |
| } |
| |
| entry = netsnmp_access_defaultrouter_entry_create(); |
| if (NULL == entry) { |
| rc = -3; |
| break; |
| } |
| |
| entry->ns_dr_index = ++idx_offset; |
| entry->dr_addresstype = addresstype; |
| entry->dr_address_len = address_len; |
| memcpy(entry->dr_address, address, |
| NETSNMP_ACCESS_DEFAULTROUTER_BUF_SIZE); |
| entry->dr_if_index = if_index; |
| entry->dr_lifetime = lifetime; |
| entry->dr_preference = preference; |
| |
| if (CONTAINER_INSERT(container, entry) < 0) |
| { |
| DEBUGMSGTL(("access:arp:container", |
| "error with defaultrouter_entry: " |
| "insert into container failed.\n")); |
| netsnmp_access_defaultrouter_entry_free(entry); |
| } |
| } |
| |
| next_nlmsghdr: |
| nlmhp = NLMSG_NEXT(nlmhp, count); |
| } /* while NLMSG_OK(nlmhp) */ |
| |
| if (rc < 0) |
| break; |
| |
| } while (!end_of_message); |
| |
| close(nlsk); |
| return rc; |
| #endif /* HAVE_LINUX_RTNETLINK_H */ |
| } |