diff options
Diffstat (limited to 'agents/virt/common/ip_lookup.c')
-rw-r--r-- | agents/virt/common/ip_lookup.c | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/agents/virt/common/ip_lookup.c b/agents/virt/common/ip_lookup.c new file mode 100644 index 0000000..aded8ea --- /dev/null +++ b/agents/virt/common/ip_lookup.c @@ -0,0 +1,326 @@ +/* + Copyright Red Hat, Inc. 2004, 2006 + + The Magma Cluster API Library is free software; you can redistribute + it and/or modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either version + 2.1 of the License, or (at your option) any later version. + + The Magma Cluster API Library is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. + */ +/** @file + * Build lists of IPs on the system, excepting loopback ipv6 link-local + */ + +#include "config.h" + +#include <asm/types.h> +#include <sys/types.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <netdb.h> + +/* Local includes */ +#include "ip_lookup.h" +#include "debug.h" + +static int +send_addr_dump(int fd, int family) +{ + struct nlmsghdr *nh; + struct rtgenmsg *g; + char buf[256]; + struct sockaddr_nl addr; + + memset(&addr,0,sizeof(addr)); + addr.nl_family = PF_NETLINK; + + memset(buf, 0, sizeof(buf)); + nh = (struct nlmsghdr *)buf; + g = (struct rtgenmsg *)(buf + sizeof(struct nlmsghdr)); + + nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); + nh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; + nh->nlmsg_type = RTM_GETADDR; + g->rtgen_family = family; + + return sendto(fd, buf, nh->nlmsg_len, 0, (struct sockaddr *)&addr, + sizeof(addr)); +} + + +static int +add_ip(ip_list_t *ipl, char *ipaddr, char family) +{ + ip_addr_t *ipa; + + if (family == PF_INET6) { + /* Avoid loopback */ + if (!strcmp(ipaddr, "::1")) + return -1; + + /* Avoid link-local addresses */ + if (!strncmp(ipaddr, "fe80", 4)) + return -1; + if (!strncmp(ipaddr, "fe90", 4)) + return -1; + if (!strncmp(ipaddr, "fea0", 4)) + return -1; + if (!strncmp(ipaddr, "feb0", 4)) + return -1; + } + + ipa = calloc(1, sizeof(*ipa)); + if (!ipa) + return -1; + ipa->ipa_family = family; + ipa->ipa_address = strdup(ipaddr); + + dbg_printf(4, "Adding IP %s to list (family %d)\n", ipaddr, family); + TAILQ_INSERT_TAIL(ipl, ipa, ipa_entries); + + return 0; +} + + +static int +add_ip_addresses(int family, ip_list_t *ipl) +{ + /* List ipv4 addresses */ + struct nlmsghdr *nh; + struct ifaddrmsg *ifa; + struct rtattr *rta, *nrta; + struct nlmsgerr *err; + char buf[10240]; + char outbuf[256]; + char label[256]; + int x, fd, len; + + dbg_printf(5, "Connecting to Netlink...\n"); + fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + perror("socket"); + exit(1); + } + + dbg_printf(5, "Sending address dump request\n"); + if (send_addr_dump(fd, family) < 0) { + perror("sendto"); + close(fd); + return -1; + } + memset(buf, 0, sizeof(buf)); + + dbg_printf(5, "Waiting for response\n"); + x = recvfrom(fd, buf, sizeof(buf), 0, NULL, 0); + if (x < 0) { + perror("recvfrom"); + close(fd); + return -1; + } + + dbg_printf(5, "Received %d bytes\n", x); + + nh = (struct nlmsghdr *)buf; + while (NLMSG_OK(nh, x)) { + + switch(nh->nlmsg_type) { + case NLMSG_DONE: + close(fd); + return 0; + + case NLMSG_ERROR: + err = (struct nlmsgerr*)NLMSG_DATA(nh); + if (nh->nlmsg_len < + NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + fprintf(stderr, "ERROR truncated"); + } else { + errno = -err->error; + perror("RTNETLINK answers"); + } + close(fd); + return -1; + + case RTM_NEWADDR: + break; + + default: + nh = NLMSG_NEXT(nh, x); + continue; + } + + /* RTM_NEWADDR */ + len = NLMSG_PAYLOAD(nh,0); + ifa = NLMSG_DATA(nh); + + /* Make sure we got the type we expect back */ + if (ifa->ifa_family != family) { + nh = NLMSG_NEXT(nh, x); + continue; + } + + rta = IFA_RTA(ifa); + len -= sizeof(struct ifaddrmsg); + do { + /* Make sure we've got a valid rtaddr field */ + if (!RTA_OK(rta, len)) { + dbg_printf(5, "!RTA_OK(rta, len)\n"); + break; + } + + if (rta->rta_type == IFA_ADDRESS) { + inet_ntop(family, RTA_DATA(rta), outbuf, + sizeof(outbuf) ); + add_ip(ipl, outbuf, family); + } + + if (rta->rta_type == IFA_LABEL) { + memset(label, 0, sizeof(label)); + strncpy(label, (char *)RTA_DATA(rta), sizeof(label) - 1); + dbg_printf(5, "Skipping label: %s\n", + label); + } + + nrta = RTA_NEXT(rta, len); + len -= (nrta - rta); + rta = nrta; + } while (RTA_OK(rta, len)); + + nh = NLMSG_NEXT(nh, x); + } + + dbg_printf(5, "Closing Netlink connection\n"); + close(fd); + return 0; +} + + +int +ip_search(ip_list_t *ipl, char *ip_name) +{ + ip_addr_t *ipa; + + dbg_printf(5, "Looking for IP address %s in IP list %p...", ip_name, ipl); + ipa = ipl->tqh_first; + for (ipa = ipl->tqh_first; ipa; ipa = ipa->ipa_entries.tqe_next) { + if (!strcmp(ip_name, ipa->ipa_address)) { + dbg_printf(4,"Found\n"); + return 0; + } + } + dbg_printf(5, "Not found\n"); + return 1; +} + + +int +ip_free_list(ip_list_t *ipl) +{ + ip_addr_t *ipa; + + dbg_printf(5, "Tearing down IP list @ %p\n", ipl); + while ((ipa = ipl->tqh_first)) { + TAILQ_REMOVE(ipl, ipa, ipa_entries); + free(ipa->ipa_address); + free(ipa); + } + return 0; +} + + +int +ip_build_list(ip_list_t *ipl) +{ + dbg_printf(5, "Build IP address list\n"); + TAILQ_INIT(ipl); + if (add_ip_addresses(PF_INET6, ipl) < 0) { + ip_free_list(ipl); + return -1; + } + if (add_ip_addresses(PF_INET, ipl) < 0) { + ip_free_list(ipl); + return -1; + } + return 0; +} + + +/** + Look up the interface name which corresponds to the given hostname and + return the list of matching attrinfo structures. We do this by looking + up all the possible physical and virtual network interfaces on the machine + and checking the hostname/IP mappings for each active IP address incurred. + + @param nodename Interface name + @param ret_ai Structure pointer to allocate & return. + @return -1 on failure or 0 on success. + */ +int +ip_lookup(char *nodename, struct addrinfo **ret_ai) +{ + char ip_name[256]; + struct addrinfo *ai = NULL; + struct addrinfo *n; + void *p; + ip_list_t ipl; + int ret = -1; + + dbg_printf(5, "Looking for IP matching %s\n", nodename); + /* Build list of IP addresses configured locally */ + if (ip_build_list(&ipl) < 0) + return -1; + + /* Get list of addresses for the host-name/ip */ + if (getaddrinfo(nodename, NULL, NULL, &ai) != 0) + return -1; + + + /* Traverse list of addresses for given host-name/ip */ + for (n = ai; n; n = n->ai_next) { + if (n->ai_family != PF_INET && n->ai_family != PF_INET6) + continue; + + if (n->ai_family == PF_INET) + p = &(((struct sockaddr_in *)n->ai_addr)->sin_addr); + else + p = &(((struct sockaddr_in6 *)n->ai_addr)->sin6_addr); + + if (!inet_ntop(n->ai_family, p, ip_name, + sizeof(ip_name))) + continue; + + /* Search local interfaces for this IP address */ + if (ip_search(&ipl, ip_name) != 0) + continue; + + /* Found it */ + ret = 0; + break; + } + + /* Clean up */ + if (!ret_ai) + freeaddrinfo(ai); + else + *ret_ai = ai; + + ip_free_list(&ipl); + + return ret; +} + |