diff options
Diffstat (limited to '')
-rw-r--r-- | agents/virt/common/Makefile.am | 24 | ||||
-rw-r--r-- | agents/virt/common/bcast.c | 347 | ||||
-rw-r--r-- | agents/virt/common/debug.c | 38 | ||||
-rw-r--r-- | agents/virt/common/fdops.c | 202 | ||||
-rw-r--r-- | agents/virt/common/ip_lookup.c | 326 | ||||
-rw-r--r-- | agents/virt/common/log.c | 204 | ||||
-rw-r--r-- | agents/virt/common/mcast.c | 388 | ||||
-rw-r--r-- | agents/virt/common/simple_auth.c | 466 | ||||
-rw-r--r-- | agents/virt/common/tcp.c | 386 |
9 files changed, 2381 insertions, 0 deletions
diff --git a/agents/virt/common/Makefile.am b/agents/virt/common/Makefile.am new file mode 100644 index 0000000..d5e4314 --- /dev/null +++ b/agents/virt/common/Makefile.am @@ -0,0 +1,24 @@ +############################################################################### +############################################################################### +## +## Copyright (C) 2009-2019 Red Hat, Inc. +## +## This copyrighted material is made available to anyone wishing to use, +## modify, copy, or redistribute it subject to the terms and conditions +## of the GNU General Public License v.2. +## +############################################################################### +############################################################################### + +MAINTAINERCLEANFILES = Makefile.in + +noinst_LTLIBRARIES = libfence_virt.la + +libfence_virt_la_SOURCES = mcast.c ip_lookup.c simple_auth.c tcp.c \ + debug.c fdops.c log.c + +libfence_virt_la_CFLAGS = $(VIRT_AM_CFLAGS) $(nss_CFLAGS) $(AM_CFLAGS) -Wno-cast-align + +libfence_virt_la_LDFLAGS = $(VIRT_AM_LDFLAGS) $(VIRT_COMMON_LDFLAGS) + +libfence_virt_la_LIBADD = $(nss_LIBS) diff --git a/agents/virt/common/bcast.c b/agents/virt/common/bcast.c new file mode 100644 index 0000000..2b498b7 --- /dev/null +++ b/agents/virt/common/bcast.c @@ -0,0 +1,347 @@ +/* + * Author: Lon Hohberger <lhh at redhat.com> + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/un.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netdb.h> +#include <sys/time.h> +#include <fcntl.h> +#include <errno.h> +#include <pthread.h> + +/* Local includes */ +#include "bcast.h" +#include "debug.h" + +LOGSYS_DECLARE_SUBSYS ("XVM", SYSLOGLEVEL); + +/** + Sets up a multicast receive socket + */ +int +ipv4_bcast_recv_sk(char *addr, int port) +{ + int sock, val; + struct sockaddr_in sin; + + /* Store broadcast address */ + memset(&sin, 0, sizeof(sin)); + sin.sin_family = PF_INET; + sin.sin_port = htons(port); + if (inet_pton(PF_INET, addr, + (void *)&sin.sin_addr.s_addr) < 0) { + log_printf(LOG_ERR, "Invalid broadcast address: %s\n", addr); + return -1; + } + + dbg_printf(4, "Setting up ipv4 broadcast receive (%s:%d)\n", addr, port); + sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + log_printf(LOG_ERR, "socket: %s\n", strerror(errno)); + close(sock); + sock = -1; + return 1; + } + + val = 1; + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, 1, sizeof(val)) < 0) { + log_printf(LOG_ERR, "setsockopt: %s\n", strerror(errno)); + close(sock); + return 1; + } + + /* + * Bind broadcast address + */ + if (bind(sock, (struct sockaddr *) &sin, + sizeof(struct sockaddr_in)) < 0) { + printf("bind failed: %s\n", strerror(errno)); + close(sock); + return -1; + } + + dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); + return sock; +} + + +/** + Set up multicast send socket + */ +int +ipv4_bcast_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt, + socklen_t tgt_len, int ttl) +{ + int val; + struct ip_mreq mreq; + struct sockaddr_in mcast; + struct sockaddr_in src; + int sock; + + if (tgt_len < sizeof(struct sockaddr_in)) { + errno = EINVAL; + return -1; + } + + /* Store multicast address */ + mcast.sin_family = PF_INET; + mcast.sin_port = htons(port); + if (inet_pton(PF_INET, addr, + (void *)&mcast.sin_addr.s_addr) < 0) { + printf("Invalid multicast address: %s\n", addr); + return -1; + } + mreq.imr_multiaddr.s_addr = mcast.sin_addr.s_addr; + + /* Store sending address */ + src.sin_family = PF_INET; + src.sin_port = htons(port); + if (inet_pton(PF_INET, send_addr, + (void *)&src.sin_addr.s_addr) < 0) { + printf("Invalid source address: %s\n", send_addr); + return -1; + } + mreq.imr_interface.s_addr = src.sin_addr.s_addr; + + + /************************* + * SET UP MULTICAST SEND * + *************************/ + dbg_printf(4, "Setting up ipv4 multicast send (%s:%d)\n", addr, port); + sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + perror("socket"); + return -1; + } + + /* + * Join Multicast group. + */ + dbg_printf(4, "Joining IP Multicast group (pass 1)\n"); + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) == -1) { + printf("Failed to add multicast membership to transmit " + "socket %s: %s\n", addr, strerror(errno)); + close(sock); + return -1; + } + + /* + * Join Multicast group. + */ + dbg_printf(4, "Joining IP Multicast group (pass 2)\n"); + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &src.sin_addr, + sizeof(src.sin_addr)) == -1) { + printf("Failed to bind multicast transmit socket to " + "%s: %s\n", addr, strerror(errno)); + close(sock); + return -1; + } + + /* + * set time to live to 2 hops. + */ + dbg_printf(4, "Setting TTL to %d for fd%d\n", ttl, sock); + val = ttl; + if (setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &val, + sizeof(val))) + printf("warning: setting TTL failed %s\n", strerror(errno)); + + memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in)); + + dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); + return sock; +} + + + +/** + Sets up a multicast receive (ipv6) socket + */ +int +ipv6_recv_sk(char *addr, int port) +{ + int sock, val; + struct ipv6_mreq mreq; + struct sockaddr_in6 sin; + + memset(&mreq, 0, sizeof(mreq)); + memset(&sin, 0, sizeof(sin)); + sin.sin6_family = PF_INET6; + sin.sin6_port = htons(port); + if (inet_pton(PF_INET6, addr, + (void *)&sin.sin6_addr) < 0) { + printf("Invalid multicast address: %s\n", addr); + return -1; + } + + memcpy(&mreq.ipv6mr_multiaddr, &sin.sin6_addr, + sizeof(struct in6_addr)); + + + /******************************** + * SET UP MULTICAST RECV SOCKET * + ********************************/ + dbg_printf(4, "Setting up ipv6 multicast receive (%s:%d)\n", addr, port); + sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) { + printf("socket: %s\n", strerror(errno)); + close(sock); + sock = -1; + return 1; + } + + /* + * When using Multicast, bind to the LOCAL address, not the MULTICAST + * address. + */ + memset(&sin, 0, sizeof(sin)); + sin.sin6_family = PF_INET6; + sin.sin6_port = htons(port); + sin.sin6_addr = in6addr_any; + if (bind(sock, (struct sockaddr *) &sin, + sizeof(struct sockaddr_in6)) < 0) { + printf("bind failed: %s\n", strerror(errno)); + close(sock); + return -1; + } + + dbg_printf(4, "Disabling IP Multicast loopback\n"); + val = 1; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, + sizeof(val)) != 0) { + printf("Failed to disable multicast loopback\n"); + close(sock); + return -1; + } + + /* + * Join multicast group + */ + dbg_printf(4, "Joining IP Multicast group\n"); + if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) == -1) { + printf("Failed to add multicast to socket %s: %s\n", + addr, strerror(errno)); + close(sock); + return -1; + } + + dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); + return sock; +} + + +/** + Set up ipv6 multicast send socket + */ +int +ipv6_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt, + socklen_t tgt_len, int ttl) +{ + int val; + struct ipv6_mreq mreq; + struct sockaddr_in6 mcast; + struct sockaddr_in6 src; + int sock; + + if (tgt_len < sizeof(struct sockaddr_in6)) { + errno = EINVAL; + return -1; + } + + memset(&mreq, 0, sizeof(mreq)); + + /* Store multicast address */ + mcast.sin6_family = PF_INET6; + mcast.sin6_port = htons(port); + if (inet_pton(PF_INET6, addr, + (void *)&mcast.sin6_addr) < 0) { + printf("Invalid multicast address: %s\n", addr); + return -1; + } + + memcpy(&mreq.ipv6mr_multiaddr, &mcast.sin6_addr, + sizeof(struct in6_addr)); + + /* Store sending address */ + src.sin6_family = PF_INET6; + src.sin6_port = htons(port); + if (inet_pton(PF_INET6, send_addr, + (void *)&src.sin6_addr) < 0) { + printf("Invalid source address: %s\n", send_addr); + return -1; + } + + /************************* + * SET UP MULTICAST SEND * + *************************/ + dbg_printf(4, "Setting up ipv6 multicast send (%s:%d)\n", addr, port); + sock = socket(PF_INET6, SOCK_DGRAM, 0); + if (sock < 0) { + perror("socket"); + return -1; + } + + dbg_printf(4, "Disabling IP Multicast loopback\n"); + val = 1; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, + sizeof(val)) != 0) { + printf("Failed to disable multicast loopback\n"); + close(sock); + return -1; + } + + /* + * Join Multicast group. + */ + dbg_printf(4, "Joining IP Multicast group\n"); + if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) == -1) { + printf("Failed to add multicast membership to transmit " + "socket %s: %s\n", addr, strerror(errno)); + close(sock); + return -1; + } + + /* + * Join Multicast group (part 2) + */ + /* + if (setsockopt(sock, IPPROTO_IPV6, IP_MULTICAST_IF, &src.sin6_addr, + sizeof(src.sin6_addr)) == -1) { + printf("Failed to bind multicast transmit socket to " + "%s: %s\n", addr, strerror(errno)); + close(sock); + return -1; + } + */ + + /* + * set time to live to 2 hops. + */ + val = ttl; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, + sizeof(val))) + printf("warning: setting TTL failed %s\n", strerror(errno)); + + memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in6)); + + dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); + return sock; +} diff --git a/agents/virt/common/debug.c b/agents/virt/common/debug.c new file mode 100644 index 0000000..5cf5359 --- /dev/null +++ b/agents/virt/common/debug.c @@ -0,0 +1,38 @@ +/* + Copyright Red Hat, Inc. 2006 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 675 Mass Ave, Cambridge, + MA 02139, USA. +*/ + +#include "config.h" + +#include <stdio.h> +#include "debug.h" + +static int _debug = 0; + +void +dset(int threshold) +{ + _debug = threshold; + dbg_printf(3, "Debugging threshold is now %d\n", threshold); +} + +int +dget(void) +{ + return _debug; +} diff --git a/agents/virt/common/fdops.c b/agents/virt/common/fdops.c new file mode 100644 index 0000000..329e9b7 --- /dev/null +++ b/agents/virt/common/fdops.c @@ -0,0 +1,202 @@ +/* + Copyright Red Hat, Inc. 2002-2003 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 675 Mass Ave, Cambridge, + MA 02139, USA. +*/ +/** @file + * Wrapper functions around read/write/select to retry in the event + * of interrupts. + */ + +#include "config.h" + +#include <unistd.h> +#include <sys/types.h> +#include <fcntl.h> +#include <errno.h> + +#include "fdops.h" + +/** + * This is a wrapper around select which will retry in the case we receive + * EINTR. This is necessary for _read_retry, since it wouldn't make sense + * to have _read_retry terminate if and only if two EINTRs were received + * in a row - one during the read() call, one during the select call... + * + * See select(2) for description of parameters. + */ +int +_select_retry(int fdmax, fd_set * rfds, fd_set * wfds, fd_set * xfds, + struct timeval *timeout) +{ + int rv; + + while (1) { + rv = select(fdmax, rfds, wfds, xfds, timeout); + if (rv == -1) { + /* return on EBADF/EINVAL/ENOMEM; continue on EINTR/EAGAIN/ENOMEM */ + if (errno == EINTR || errno == EAGAIN || errno == ENOMEM) + continue; + } + return rv; + } +} + +/** + * Retries a write in the event of a non-blocked interrupt signal. + * + * @param fd File descriptor to which we are writing. + * @param buf Data buffer to send. + * @param count Number of bytes in buf to send. + * @param timeout (struct timeval) telling us how long we should retry. + * @return The number of bytes written to the file descriptor, + * or -1 on error (with errno set appropriately). + */ +ssize_t +_write_retry(int fd, void *buf, int count, struct timeval * timeout) +{ + int n, total = 0, remain = count, rv = 0; + fd_set wfds, xfds; + char *tmp_buf = (char *)buf; + + while (total < count) { + + /* Create the write FD set of 1... */ + FD_ZERO(&wfds); + FD_SET(fd, &wfds); + FD_ZERO(&xfds); + FD_SET(fd, &xfds); + + /* wait for the fd to be available for writing */ + rv = _select_retry(fd + 1, NULL, &wfds, &xfds, timeout); + if (rv == -1) + return -1; + else if (rv == 0) { + errno = ETIMEDOUT; + return -1; + } + + if (FD_ISSET(fd, &xfds)) { + errno = EPIPE; + return -1; + } + + /* + * Attempt to write to fd + */ + n = write(fd, tmp_buf + total, remain); + + /* + * When we know our fd was select()ed and we receive 0 bytes + * when we write, the fd was closed. + */ + if ((n == 0) && (rv == 1)) { + errno = EPIPE; + return -1; + } + + if (n == -1) { + if ((errno == EAGAIN) || (errno == EINTR)) { + /* + * Not ready? + */ + continue; + } + + /* Other errors: EIO, EINVAL, etc */ + return -1; + } + + total += n; + remain -= n; + } + + return total; +} + +/** + * Retry reads until we (a) time out or (b) get our data. Of course, if + * timeout is NULL, it'll wait forever. + * + * @param sockfd File descriptor we want to read from. + * @param buf Preallocated buffer into which we will read data. + * @param count Number of bytes to read. + * @param timeout (struct timeval) describing how long we should retry. + * @return The number of bytes read on success, or -1 on failure. + Note that we will always return (count) or (-1). + */ +ssize_t +_read_retry(int sockfd, void *buf, int count, struct timeval * timeout) +{ + int n, total = 0, remain = count, rv = 0; + fd_set rfds, xfds; + char *tmp_buf = (char *)buf; + + while (total < count) { + FD_ZERO(&rfds); + FD_SET(sockfd, &rfds); + FD_ZERO(&xfds); + FD_SET(sockfd, &xfds); + + /* + * Select on the socket, in case it closes while we're not + * looking... + */ + rv = _select_retry(sockfd + 1, &rfds, NULL, &xfds, timeout); + if (rv == -1) + return -1; + else if (rv == 0) { + errno = ETIMEDOUT; + return -1; + } + + if (FD_ISSET(sockfd, &xfds)) { + errno = EPIPE; + return -1; + } + + /* + * Attempt to read off the socket + */ + n = read(sockfd, tmp_buf + total, remain); + + /* + * When we know our socket was select()ed and we receive 0 bytes + * when we read, the socket was closed. + */ + if ((n == 0) && (rv == 1)) { + errno = EPIPE; + return -1; + } + + if (n == -1) { + if ((errno == EAGAIN) || (errno == EINTR)) { + /* + * Not ready? Wait for data to become available + */ + continue; + } + + /* Other errors: EPIPE, EINVAL, etc */ + return -1; + } + + total += n; + remain -= n; + } + + return total; +} 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; +} + diff --git a/agents/virt/common/log.c b/agents/virt/common/log.c new file mode 100644 index 0000000..61018ed --- /dev/null +++ b/agents/virt/common/log.c @@ -0,0 +1,204 @@ +/** + * Syslog wrapper that does not block + * + * Lon Hohberger, 2009 + */ + +#include "config.h" + +#include <pthread.h> +#include <unistd.h> +#include <sys/syslog.h> +#include <signal.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> +#include <errno.h> + +#include "list.h" + +struct log_entry { + list_head(); + char *message; + int sev; + int bufsz; +}; + +#define MAX_QUEUE_LENGTH 10 +#define LOGLEN 256 + +static struct log_entry *_log_entries = NULL; +static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t log_cond = PTHREAD_COND_INITIALIZER; +static int log_size = 0; +static int dropped = 0; +static pthread_t thread_id = 0; + +void __real_syslog(int severity, const char *fmt, ...); +void __wrap_syslog(int severity, const char *fmt, ...); +void __wrap_closelog(void); + +static void * +_log_thread(void *arg) +{ + struct timeval tv; + struct timespec ts; + struct log_entry *entry; + + do { + gettimeofday(&tv, NULL); + ts.tv_sec = tv.tv_sec + 10; + ts.tv_nsec = tv.tv_usec; + + pthread_mutex_lock(&log_mutex); + + while (!(entry = _log_entries)) { + if (pthread_cond_timedwait(&log_cond, + &log_mutex, + &ts) == ETIMEDOUT) + goto out; + } + + list_remove(&_log_entries, entry); + --log_size; + if (log_size < 0) + raise(SIGSEGV); + pthread_mutex_unlock(&log_mutex); + + __real_syslog(entry->sev, entry->message); + free(entry->message); + free(entry); + } while (1); + +out: + thread_id = (pthread_t)0; + pthread_mutex_unlock(&log_mutex); + return NULL; +} + + +static int +insert_entry(int sev, char *buf, int bufsz) +{ + struct log_entry *lent; + pthread_attr_t attrs; + + lent = malloc(sizeof(*lent)); + if (!lent) + return -1; + lent->sev = sev; + lent->message = buf; + lent->bufsz = bufsz; + + pthread_mutex_lock(&log_mutex); + if (log_size >= MAX_QUEUE_LENGTH) { + free(lent->message); + free(lent); + + ++dropped; + lent = (struct log_entry *)(le(_log_entries)->le_prev); + + lent->sev = LOG_WARNING; + snprintf(lent->message, lent->bufsz, + "%d message(s) lost due to syslog load\n", + dropped + 1); + /* Dropped +1 because we overwrote a message to + * give the 'dropped' message */ + } else { + ++log_size; + dropped = 0; + list_insert(&_log_entries, lent); + } + + if (!thread_id) { + pthread_attr_init(&attrs); + pthread_attr_setinheritsched(&attrs, PTHREAD_INHERIT_SCHED); + + if (pthread_create(&thread_id, &attrs, _log_thread, NULL) < 0) + thread_id = 0; + pthread_mutex_unlock(&log_mutex); + } else { + pthread_mutex_unlock(&log_mutex); + pthread_cond_signal(&log_cond); + } + + return 0; +} + + +__attribute__((__format__ (__printf__, 2, 0))) +void +__wrap_syslog(int severity, const char *fmt, ...) +{ + va_list args; + char *logmsg; + + logmsg = malloc(LOGLEN); + if (!logmsg) + return; + memset(logmsg, 0, LOGLEN); + + va_start(args, fmt); + vsnprintf(logmsg + strlen(logmsg), LOGLEN - strlen(logmsg), + fmt, args); + va_end(args); + + insert_entry(severity, logmsg, LOGLEN); + + return; +} + + +void __real_closelog(void); + +void +__wrap_closelog(void) +{ + struct log_entry *lent; +#ifdef DEBUG + int lost = 0; +#endif + + if (thread_id != 0) { + pthread_cancel(thread_id); + pthread_join(thread_id, NULL); + thread_id = 0; + } + __real_closelog(); + while (_log_entries) { +#ifdef DEBUG + ++lost; +#endif + lent = _log_entries; + list_remove(&_log_entries, lent); + free(lent->message); + free(lent); + } + +#ifdef DEBUG + printf("%d lost\n", lost); +#endif +} + + +#ifdef STANDALONE +int +main(int argc, char**argv) +{ + int x; + + for (x = 0; x < 100; x++) { + syslog(1, "Yo %d\n", x); + } + sleep(1); + + closelog(); + + return 0; +} + +#endif diff --git a/agents/virt/common/mcast.c b/agents/virt/common/mcast.c new file mode 100644 index 0000000..cc79c64 --- /dev/null +++ b/agents/virt/common/mcast.c @@ -0,0 +1,388 @@ +/* + Copyright Red Hat, Inc. 2003, 2004, 2006 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 675 Mass Ave, Cambridge, + MA 02139, USA. +*/ +/* + * Author: Lon Hohberger <lhh at redhat.com> + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/un.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netdb.h> +#include <sys/time.h> +#include <fcntl.h> +#include <errno.h> +#include <pthread.h> + +/* Local includes */ +#include "mcast.h" +#include "debug.h" + +/** + Sets up a multicast receive socket + */ +int +ipv4_recv_sk(char *addr, int port, unsigned int ifindex) +{ + int sock; + struct ip_mreqn mreq; + struct sockaddr_in sin; + + memset(&mreq, 0, sizeof(mreq)); + memset(&sin, 0, sizeof(sin)); + + /* Store multicast address */ + if (inet_pton(PF_INET, addr, + (void *)&mreq.imr_multiaddr.s_addr) < 0) { + printf("Invalid multicast address: %s\n", addr); + return -1; + } + + /******************************** + * SET UP MULTICAST RECV SOCKET * + ********************************/ + dbg_printf(4, "Setting up ipv4 multicast receive (%s:%d)\n", addr, port); + sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + printf("socket: %s\n", strerror(errno)); + sock = -1; + return 1; + } + + /* + * When using Multicast, bind to the LOCAL address, not the MULTICAST + * address. + */ + sin.sin_family = PF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(sock, (struct sockaddr *) &sin, + sizeof(struct sockaddr_in)) < 0) { + printf("bind failed: %s\n", strerror(errno)); + close(sock); + return -1; + } + + /* + * Join multicast group + */ + /* mreq.imr_multiaddr.s_addr is set above */ + if (ifindex == 0) { + dbg_printf(4, "Setting mcast addr to INADDR_ANY due to ifindex of 0\n"); + mreq.imr_address.s_addr = htonl(INADDR_ANY); + } else { + mreq.imr_ifindex = ifindex; + } + dbg_printf(4, "Joining multicast group\n"); + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)) == -1) { + printf("Failed to bind multicast receive socket to " + "%s: %s\n", addr, strerror(errno)); + printf("Check network configuration.\n"); + close(sock); + return -1; + } + + dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); + return sock; +} + + +/** + Set up multicast send socket + */ +int +ipv4_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt, + socklen_t tgt_len) +{ + int val; + struct ip_mreq mreq; + struct sockaddr_in mcast; + struct sockaddr_in src; + int sock; + + if (tgt_len < sizeof(struct sockaddr_in)) { + errno = EINVAL; + return -1; + } + + memset(&mcast, 0, sizeof(mcast)); + memset(&src, 0, sizeof(src)); + + /* Store multicast address */ + mcast.sin_family = PF_INET; + mcast.sin_port = htons(port); + if (inet_pton(PF_INET, addr, + (void *)&mcast.sin_addr.s_addr) < 0) { + printf("Invalid multicast address: %s\n", addr); + return -1; + } + mreq.imr_multiaddr.s_addr = mcast.sin_addr.s_addr; + + /* Store sending address */ + src.sin_family = PF_INET; + src.sin_port = htons(port); + if (inet_pton(PF_INET, send_addr, + (void *)&src.sin_addr.s_addr) < 0) { + printf("Invalid source address: %s\n", send_addr); + return -1; + } + mreq.imr_interface.s_addr = src.sin_addr.s_addr; + + + /************************* + * SET UP MULTICAST SEND * + *************************/ + dbg_printf(4, "Setting up ipv4 multicast send (%s:%d)\n", addr, port); + sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + perror("socket"); + return -1; + } + + /* + * Join Multicast group. + */ + dbg_printf(4, "Joining IP Multicast group (pass 1)\n"); + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) == -1) { + printf("Failed to add multicast membership to transmit " + "socket %s: %s\n", addr, strerror(errno)); + close(sock); + return -1; + } + + /* + * Join Multicast group. + */ + dbg_printf(4, "Joining IP Multicast group (pass 2)\n"); + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &src.sin_addr, + sizeof(src.sin_addr)) == -1) { + printf("Failed to bind multicast transmit socket to " + "%s: %s\n", addr, strerror(errno)); + close(sock); + return -1; + } + + /* + * set time to live to 2 hops. + */ + dbg_printf(4, "Setting TTL to 2 for fd%d\n", sock); + val = 2; + if (setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &val, + sizeof(val))) + printf("warning: setting TTL failed %s\n", strerror(errno)); + + memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in)); + + dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); + return sock; +} + + + +/** + Sets up a multicast receive (ipv6) socket + */ +int +ipv6_recv_sk(char *addr, int port, unsigned int ifindex) +{ + int sock, val; + struct ipv6_mreq mreq; + struct sockaddr_in6 sin; + + memset(&mreq, 0, sizeof(mreq)); + memset(&sin, 0, sizeof(sin)); + sin.sin6_family = PF_INET6; + sin.sin6_port = htons(port); + if (inet_pton(PF_INET6, addr, + (void *)&sin.sin6_addr) < 0) { + printf("Invalid multicast address: %s\n", addr); + return -1; + } + + memcpy(&mreq.ipv6mr_multiaddr, &sin.sin6_addr, + sizeof(struct in6_addr)); + + mreq.ipv6mr_interface = ifindex; + + /******************************** + * SET UP MULTICAST RECV SOCKET * + ********************************/ + dbg_printf(4, "Setting up ipv6 multicast receive (%s:%d)\n", addr, port); + sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) { + printf("socket: %s\n", strerror(errno)); + sock = -1; + return 1; + } + + /* + * When using Multicast, bind to the LOCAL address, not the MULTICAST + * address. + */ + memset(&sin, 0, sizeof(sin)); + sin.sin6_family = PF_INET6; + sin.sin6_port = htons(port); + sin.sin6_addr = in6addr_any; + if (bind(sock, (struct sockaddr *) &sin, + sizeof(struct sockaddr_in6)) < 0) { + printf("bind failed: %s\n", strerror(errno)); + close(sock); + return -1; + } + + dbg_printf(4, "Disabling IP Multicast loopback\n"); + val = 1; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, + sizeof(val)) != 0) { + printf("Failed to disable multicast loopback\n"); + close(sock); + return -1; + } + + /* + * Join multicast group + */ + dbg_printf(4, "Joining IP Multicast group\n"); + if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) == -1) { + printf("Failed to add multicast to socket %s: %s\n", + addr, strerror(errno)); + close(sock); + return -1; + } + + dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); + return sock; +} + + +/** + Set up ipv6 multicast send socket + */ +int +ipv6_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt, + socklen_t tgt_len) +{ + int val; + struct ipv6_mreq mreq; + struct sockaddr_in6 mcast; + struct sockaddr_in6 src; + int sock; + + if (tgt_len < sizeof(struct sockaddr_in6)) { + errno = EINVAL; + return -1; + } + + memset(&mcast, 0, sizeof(mcast)); + memset(&src, 0, sizeof(src)); + memset(&mreq, 0, sizeof(mreq)); + + /* Store multicast address */ + mcast.sin6_family = PF_INET6; + mcast.sin6_port = htons(port); + if (inet_pton(PF_INET6, addr, + (void *)&mcast.sin6_addr) < 0) { + printf("Invalid multicast address: %s\n", addr); + return -1; + } + + memcpy(&mreq.ipv6mr_multiaddr, &mcast.sin6_addr, + sizeof(struct in6_addr)); + + /* Store sending address */ + src.sin6_family = PF_INET6; + src.sin6_port = htons(port); + if (inet_pton(PF_INET6, send_addr, + (void *)&src.sin6_addr) < 0) { + printf("Invalid source address: %s\n", send_addr); + return -1; + } + + /************************* + * SET UP MULTICAST SEND * + *************************/ + dbg_printf(4, "Setting up ipv6 multicast send (%s:%d)\n", addr, port); + sock = socket(PF_INET6, SOCK_DGRAM, 0); + if (sock < 0) { + perror("socket"); + return -1; + } + + dbg_printf(4, "Disabling IP Multicast loopback\n"); + val = 1; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, + sizeof(val)) != 0) { + printf("Failed to disable multicast loopback\n"); + close(sock); + return -1; + } + + /* + * Join Multicast group. + */ + dbg_printf(4, "Joining IP Multicast group\n"); + if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) == -1) { + printf("Failed to add multicast membership to transmit " + "socket %s: %s\n", addr, strerror(errno)); + close(sock); + return -1; + } + + /* + * Join Multicast group (part 2) + */ + /* + if (setsockopt(sock, IPPROTO_IPV6, IP_MULTICAST_IF, &src.sin6_addr, + sizeof(src.sin6_addr)) == -1) { + printf("Failed to bind multicast transmit socket to " + "%s: %s\n", addr, strerror(errno)); + close(sock); + return -1; + } + */ + + /* + * set time to live to 2 hops. + */ + val = 2; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, + sizeof(val))) + printf("warning: setting TTL failed %s\n", strerror(errno)); + + memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in6)); + + dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); + return sock; +} diff --git a/agents/virt/common/simple_auth.c b/agents/virt/common/simple_auth.c new file mode 100644 index 0000000..9f694c4 --- /dev/null +++ b/agents/virt/common/simple_auth.c @@ -0,0 +1,466 @@ +/* + Copyright Red Hat, Inc. 2006 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 675 Mass Ave, Cambridge, + MA 02139, USA. +*/ + +#include "config.h" + +#include <sys/types.h> +#include <string.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sechash.h> +#include <fcntl.h> +#include <stdio.h> +#include <errno.h> + +/* Local includes */ +#include "xvm.h" +#include "fdops.h" +#include "simple_auth.h" +#include "debug.h" + + +static void +print_hash(unsigned char *hash, size_t hashlen) +{ + int x; + + for (x = 0; x < hashlen; x++) + printf("%02x", (hash[x]&0xff)); +} + + +static int +sha_sign(fence_req_t *req, void *key, size_t key_len) +{ + unsigned char hash[SHA512_LENGTH]; + HASHContext *h; + HASH_HashType ht; + unsigned int rlen; + int devrand; + int ret; + + switch(req->hashtype) { + case HASH_SHA1: + ht = HASH_AlgSHA1; + break; + case HASH_SHA256: + ht = HASH_AlgSHA256; + break; + case HASH_SHA512: + ht = HASH_AlgSHA512; + break; + default: + dbg_printf(1, "Unknown hash type: %d\n", req->hashtype); + return -1; + } + + dbg_printf(4, "Opening /dev/urandom\n"); + devrand = open("/dev/urandom", O_RDONLY); + if (devrand < 0) { + dbg_printf(1, "Error: open: /dev/urandom: %s", strerror(errno)); + return -1; + } + + ret = _read_retry(devrand, req->random, sizeof(req->random), NULL); + if (ret <= 0) { + dbg_printf(1, "Error: read: /dev/urandom: %s", strerror(errno)); + close(devrand); + return -1; + } + close(devrand); + + memset(hash, 0, sizeof(hash)); + h = HASH_Create(ht); + if (!h) + return -1; + + HASH_Begin(h); + HASH_Update(h, key, key_len); + HASH_Update(h, (void *)req, sizeof(*req)); + HASH_End(h, hash, &rlen, sizeof(hash)); + HASH_Destroy(h); + + memcpy(req->hash, hash, sizeof(req->hash)); + return 0; +} + + +static int +sha_verify(fence_req_t *req, void *key, size_t key_len) +{ + unsigned char hash[SHA512_LENGTH]; + unsigned char pkt_hash[SHA512_LENGTH]; + HASHContext *h = NULL; + HASH_HashType ht; + unsigned int rlen; + int ret; + + switch(req->hashtype) { + case HASH_SHA1: + ht = HASH_AlgSHA1; + break; + case HASH_SHA256: + ht = HASH_AlgSHA256; + break; + case HASH_SHA512: + ht = HASH_AlgSHA512; + break; + default: + dbg_printf(3, "%s: no-op (HASH_NONE)\n", __FUNCTION__); + return 0; + } + + if (!key || !key_len) { + dbg_printf(3, "%s: Hashing requested when we have no key data\n", + __FUNCTION__); + return 0; + } + + memset(hash, 0, sizeof(hash)); + h = HASH_Create(ht); + if (!h) + return 0; + + memcpy(pkt_hash, req->hash, sizeof(pkt_hash)); + memset(req->hash, 0, sizeof(req->hash)); + + HASH_Begin(h); + HASH_Update(h, key, key_len); + HASH_Update(h, (void *)req, sizeof(*req)); + HASH_End(h, hash, &rlen, sizeof(hash)); + HASH_Destroy(h); + + memcpy(req->hash, pkt_hash, sizeof(req->hash)); + + ret = !memcmp(hash, pkt_hash, sizeof(hash)); + if (!ret) { + printf("Hash mismatch:\nPKT = "); + print_hash(pkt_hash, sizeof(pkt_hash)); + printf("\nEXP = "); + print_hash(hash, sizeof(hash)); + printf("\n"); + } + + return ret; +} + + +int +sign_request(fence_req_t *req, void *key, size_t key_len) +{ + memset(req->hash, 0, sizeof(req->hash)); + switch(req->hashtype) { + case HASH_NONE: + dbg_printf(3, "%s: no-op (HASH_NONE)\n", __FUNCTION__); + return 0; + case HASH_SHA1: + case HASH_SHA256: + case HASH_SHA512: + return sha_sign(req, key, key_len); + default: + break; + } + return -1; +} + + +int +verify_request(fence_req_t *req, fence_hash_t min, + void *key, size_t key_len) +{ + if (req->hashtype < min) { + printf("Hash type not strong enough (%d < %d)\n", + req->hashtype, min); + return 0; + } + switch(req->hashtype) { + case HASH_NONE: + return 1; + case HASH_SHA1: + case HASH_SHA256: + case HASH_SHA512: + return sha_verify(req, key, key_len); + default: + break; + } + return 0; +} + + +static int +sha_challenge(int fd, fence_auth_type_t auth, void *key, + size_t key_len, int timeout) +{ + fd_set rfds; + struct timeval tv; + unsigned char hash[MAX_HASH_LENGTH]; + unsigned char challenge[MAX_HASH_LENGTH]; + unsigned char response[MAX_HASH_LENGTH]; + int devrand; + int ret; + HASHContext *h; + HASH_HashType ht; + unsigned int rlen; + + devrand = open("/dev/urandom", O_RDONLY); + if (devrand < 0) { + dbg_printf(1, "Error: open /dev/urandom: %s", strerror(errno)); + return 0; + } + + tv.tv_sec = timeout; + tv.tv_usec = 0; + ret = _read_retry(devrand, challenge, sizeof(challenge), &tv); + if (ret < 0) { + dbg_printf(1, "Error: read: /dev/urandom: %s", strerror(errno)); + close(devrand); + return 0; + } + close(devrand); + + tv.tv_sec = timeout; + tv.tv_usec = 0; + ret = _write_retry(fd, challenge, sizeof(challenge), &tv); + if (ret < 0) { + dbg_printf(2, "Error: write: %s", strerror(errno)); + return 0; + } + + switch(auth) { + case HASH_SHA1: + ht = HASH_AlgSHA1; + break; + case HASH_SHA256: + ht = HASH_AlgSHA256; + break; + case HASH_SHA512: + ht = HASH_AlgSHA512; + break; + default: + return 0; + } + + memset(hash, 0, sizeof(hash)); + h = HASH_Create(ht); + if (!h) + return 0; + + HASH_Begin(h); + HASH_Update(h, key, key_len); + HASH_Update(h, challenge, sizeof(challenge)); + HASH_End(h, hash, &rlen, sizeof(hash)); + HASH_Destroy(h); + + memset(response, 0, sizeof(response)); + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + tv.tv_sec = timeout; + tv.tv_usec = 0; + if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0) { + dbg_printf(0, "Error: select: %s\n", strerror(errno)); + return 0; + } + + tv.tv_sec = timeout; + tv.tv_usec = 0; + ret = _read_retry(fd, response, sizeof(response), &tv); + if (ret < 0) { + dbg_printf(0, "Error reading challenge response: %s", strerror(errno)); + return 0; + } else if (ret < sizeof(response)) { + dbg_printf(0, + "read data from socket is too short(actual: %d, expected: %zu)\n", + ret, sizeof(response)); + return 0; + } + + ret = !memcmp(response, hash, sizeof(response)); + if (!ret) { + printf("Hash mismatch:\nC = "); + print_hash(challenge, sizeof(challenge)); + printf("\nH = "); + print_hash(hash, sizeof(hash)); + printf("\nR = "); + print_hash(response, sizeof(response)); + printf("\n"); + } + + return ret; +} + + +static int +sha_response(int fd, fence_auth_type_t auth, void *key, + size_t key_len, int timeout) +{ + fd_set rfds; + struct timeval tv; + unsigned char challenge[MAX_HASH_LENGTH]; + unsigned char hash[MAX_HASH_LENGTH]; + HASHContext *h; + HASH_HashType ht; + unsigned int rlen; + int ret; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + tv.tv_sec = timeout; + tv.tv_usec = 0; + if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0) { + dbg_printf(2, "Error: select: %s\n", strerror(errno)); + return 0; + } + + tv.tv_sec = timeout; + tv.tv_usec = 0; + if (_read_retry(fd, challenge, sizeof(challenge), &tv) < 0) { + dbg_printf(2, "Error reading challenge hash: %s\n", strerror(errno)); + return 0; + } + + switch(auth) { + case AUTH_SHA1: + ht = HASH_AlgSHA1; + break; + case AUTH_SHA256: + ht = HASH_AlgSHA256; + break; + case AUTH_SHA512: + ht = HASH_AlgSHA512; + break; + default: + dbg_printf(3, "%s: no-op (AUTH_NONE)\n", __FUNCTION__); + return 0; + } + + memset(hash, 0, sizeof(hash)); + h = HASH_Create(ht); /* */ + if (!h) + return 0; + + HASH_Begin(h); + HASH_Update(h, key, key_len); + HASH_Update(h, challenge, sizeof(challenge)); + HASH_End(h, hash, &rlen, sizeof(hash)); + HASH_Destroy(h); + + tv.tv_sec = timeout; + tv.tv_usec = 0; + ret = _write_retry(fd, hash, sizeof(hash), &tv); + if (ret < 0) { + perror("write"); + return 0; + } else if (ret < sizeof(hash)) { + dbg_printf(2, + "Only part of hash is written(actual: %d, expected: %zu)\n", + ret, + sizeof(hash)); + return 0; + } + + return 1; +} + + +int +sock_challenge(int fd, fence_auth_type_t auth, void *key, size_t key_len, + int timeout) +{ + switch(auth) { + case AUTH_NONE: + dbg_printf(3, "%s: no-op (AUTH_NONE)\n", __FUNCTION__); + return 1; + case AUTH_SHA1: + case AUTH_SHA256: + case AUTH_SHA512: + return sha_challenge(fd, auth, key, key_len, timeout); + default: + break; + } + return -1; +} + + +int +sock_response(int fd, fence_auth_type_t auth, void *key, size_t key_len, + int timeout) +{ + switch(auth) { + case AUTH_NONE: + dbg_printf(3, "%s: no-op (AUTH_NONE)\n", __FUNCTION__); + return 1; + case AUTH_SHA1: + case AUTH_SHA256: + case AUTH_SHA512: + return sha_response(fd, auth, key, key_len, timeout); + default: + break; + } + return -1; +} + + +int +read_key_file(char *file, char *key, size_t max_len) +{ + int fd; + int nread, remain = max_len; + char *p; + + dbg_printf(3, "Reading in key file %s into %p (%d max size)\n", + file, key, (int)max_len); + fd = open(file, O_RDONLY); + if (fd < 0) { + dbg_printf(2, "Error opening key file: %s\n", strerror(errno)); + return -1; + } + + memset(key, 0, max_len); + p = key; + remain = max_len; + + while (remain) { + nread = read(fd, p, remain); + if (nread < 0) { + if (errno == EINTR) + continue; + dbg_printf(2, "Error from read: %s\n", strerror(errno)); + close(fd); + return -1; + } + + if (nread == 0) { + dbg_printf(3, "Stopped reading @ %d bytes\n", + (int)max_len-remain); + break; + } + + p += nread; + remain -= nread; + } + + close(fd); + dbg_printf(3, "Actual key length = %d bytes\n", (int)max_len-remain); + + return (int)(max_len - remain); +} diff --git a/agents/virt/common/tcp.c b/agents/virt/common/tcp.c new file mode 100644 index 0000000..5796770 --- /dev/null +++ b/agents/virt/common/tcp.c @@ -0,0 +1,386 @@ +/* + Copyright Red Hat, Inc. 2002-2004, 2006 + Copyright Mission Critical Linux, 2000 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 675 Mass Ave, Cambridge, + MA 02139, USA. +*/ +/** @file + * + * @author Lon H. Hohberger <lhh at redhat.com> + * @author Jeff Moyer <jmoyer at redhat.com> + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include "tcp.h" +#include "debug.h" + +static int connect_nb(int fd, struct sockaddr *dest, socklen_t len, int timeout); +static int get_addr(const char *hostname, int family, struct sockaddr_storage *addr); + +/** + Set close-on-exec bit option for a socket. + + @param fd Socket to set CLOEXEC flag + @return 0 on success, -1 on failure + @see fcntl + */ +static int +set_cloexec(int fd) +{ + int flags = fcntl(fd, F_GETFD, 0); + flags |= FD_CLOEXEC; + return fcntl(fd, F_SETFD, flags); +} + + +/** + Bind to a port on the local IPv6 stack + + @param addr_str Address to listen on, NULL for inaddr6_any + @param port Port to bind to + @param backlog same as backlog for listen(2) + @return 0 on success, -1 on failure + @see ipv4_bind + */ +int +ipv6_listen(const char *addr_str, uint16_t port, int backlog) +{ + struct sockaddr_in6 _sin6; + int fd, opt=1; + + dbg_printf(4, "%s: Setting up ipv6 listen socket for %s:%d\n", + __FUNCTION__, addr_str, port); + + memset(&_sin6, 0, sizeof(_sin6)); + _sin6.sin6_family = PF_INET6; + _sin6.sin6_port = htons(port); + _sin6.sin6_flowinfo = 0; + + if (addr_str == NULL) { + _sin6.sin6_addr = in6addr_any; + } else { + struct sockaddr_storage ss; + + if (get_addr(addr_str, AF_INET6, &ss) == -1) { + dbg_printf(4, "%s: Can't get addr for %s\n", + __FUNCTION__, addr_str); + return -1; + } + + memcpy(&_sin6.sin6_addr, + &((struct sockaddr_in6 *)&ss)->sin6_addr, sizeof(_sin6.sin6_addr)); + } + + fd = socket(PF_INET6, SOCK_STREAM, 0); + if (fd < 0) + return -1; + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof (opt)) < 0) { + close(fd); + return -1; + } + + if (set_cloexec(fd) < 0) { + close(fd); + return -1; + } + + if (bind(fd, (struct sockaddr *)&_sin6, sizeof(_sin6)) < 0) { + close(fd); + return -1; + } + + if (listen(fd, backlog) < 0){ + close(fd); + return -1; + } + + dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd); + return fd; +} + + +/** + Bind to a port on the local IPv4 stack + + @param addr_str Address to listen on, NULL for inaddr_any + @param port Port to bind to + @param backlog same as backlog for listen(2) + @return 0 on success, -1 on failure + @see ipv6_bind + */ +int +ipv4_listen(const char *addr_str, uint16_t port, int backlog) +{ + struct sockaddr_in _sin; + int fd, opt=1; + + dbg_printf(4, "%s: Setting up ipv4 listen socket for %s:%d\n", + __FUNCTION__, addr_str, port); + + _sin.sin_family = PF_INET; + _sin.sin_port = htons(port); + + if (addr_str == NULL) { + _sin.sin_addr.s_addr = htonl(INADDR_ANY); + } else { + struct sockaddr_storage ss; + + if (get_addr(addr_str, AF_INET, &ss) == -1) { + dbg_printf(4, "%s: Can't get addr for %s\n", + __FUNCTION__, addr_str); + return -1; + } + + memcpy(&_sin.sin_addr, + &((struct sockaddr_in *)&ss)->sin_addr, sizeof(_sin.sin_addr)); + } + + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) + return -1; + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof (opt)) < 0) { + close(fd); + return -1; + } + + if (set_cloexec(fd) < 0) { + close(fd); + return -1; + } + + if (bind(fd, (struct sockaddr *)&_sin, sizeof(_sin)) < 0) { + close(fd); + return -1; + } + + if (listen(fd, backlog) < 0) { + close(fd); + return -1; + } + + dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd); + return fd; +} + + + +/** + Connect via ipv6 socket to a given IP address and port. + + @param in6_addr IPv6 address to connect to + @param port Port to connect to + @param timeout Timeout, in seconds, to wait for a completed + connection + @return 0 on success, -1 on failure + @see connect_nb, ipv4_connect + */ +int +ipv6_connect(struct in6_addr *in6_addr, uint16_t port, int timeout) +{ + struct sockaddr_in6 _sin6; + int fd, ret; + + dbg_printf(4, "%s: Connecting to client\n", __FUNCTION__); + fd = socket(PF_INET6, SOCK_STREAM, 0); + if (fd < 0) + return -1; + + memset(&_sin6, 0, sizeof(_sin6)); + _sin6.sin6_family = PF_INET6; + _sin6.sin6_port = htons(port); + _sin6.sin6_flowinfo = 0; + memcpy(&_sin6.sin6_addr, in6_addr, sizeof(_sin6.sin6_addr)); + + ret = connect_nb(fd, (struct sockaddr *)&_sin6, sizeof(_sin6), timeout); + if (ret < 0) { + close(fd); + return -1; + } + dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd); + return fd; +} + + +/** + Connect via ipv4 socket to a given IP address and port. + + @param in_addr IPv4 address to connect to + @param port Port to connect to + @param timeout Timeout, in seconds, to wait for a completed + connection + @return 0 on success, -1 on failure + @see connect_nb, ipv6_connect + */ +int +ipv4_connect(struct in_addr *in_addr, uint16_t port, int timeout) +{ + struct sockaddr_in _sin; + int fd, ret; + + dbg_printf(4, "%s: Connecting to client\n", __FUNCTION__); + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) + return -1; + + _sin.sin_family = PF_INET; + _sin.sin_port = htons(port); + memcpy(&_sin.sin_addr, in_addr, sizeof(_sin.sin_addr)); + + ret = connect_nb(fd, (struct sockaddr *)&_sin, sizeof(_sin), timeout); + if (ret < 0) { + close(fd); + return -1; + } + + dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd); + return fd; +} + + +/** + Connect in a non-blocking fashion to the designated address. + + @param fd File descriptor to connect + @param dest sockaddr (ipv4 or ipv6) to connect to. + @param len Length of dest + @param timeout Timeout, in seconds, to wait for a completed + connection. + @return 0 on success, -1 on failure. + */ +static int +connect_nb(int fd, struct sockaddr *dest, socklen_t len, int timeout) +{ + int ret, flags = 1, err; + unsigned l; + fd_set rfds, wfds; + struct timeval tv; + + /* + * Use TCP Keepalive + */ + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, + sizeof(flags))<0) { + return -1; + } + + /* + Set up non-blocking connect + */ + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) { + return -1; + } + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + return -1; + } + + ret = connect(fd, dest, len); + + if ((ret < 0) && (errno != EINPROGRESS)) + return -1; + + if (ret != 0) { + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + FD_ZERO(&wfds); + FD_SET(fd, &wfds); + + tv.tv_sec = timeout; + tv.tv_usec = 0; + + if (select(fd + 1, &rfds, &wfds, NULL, &tv) == 0) { + errno = ETIMEDOUT; + return -1; + } + /* XXX check for -1 from select */ + + if (FD_ISSET(fd, &rfds) || FD_ISSET(fd, &wfds)) { + l = sizeof(err); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, + (void *)&err, &l) < 0) { + close(fd); + return -1; + } + + if (err != 0) { + close(fd); + errno = err; + return -1; + } + + if (fcntl(fd, F_SETFL, flags) < 0) { + close(fd); + return -1; + } + return 0; + } + } + + errno = EIO; + return -1; +} + +static int +get_addr(const char *hostname, int family, struct sockaddr_storage *addr) +{ + struct addrinfo *res; + size_t len; + struct addrinfo ai; + + memset(&ai, 0, sizeof(ai)); + ai.ai_family = family; + + if (getaddrinfo(hostname, NULL, &ai, &res) != 0) + return -1; + + switch (res->ai_addr->sa_family) { + case AF_INET: + len = sizeof(struct sockaddr_in); + break; + case AF_INET6: + len = sizeof(struct sockaddr_in6); + break; + default: + goto out_fail; + } + + if (len < (size_t) res->ai_addrlen) + goto out_fail; + + memcpy(addr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + + return 0; + +out_fail: + freeaddrinfo(res); + return -1; +} |