diff options
Diffstat (limited to 'agents/virt/client/mcast.c')
-rw-r--r-- | agents/virt/client/mcast.c | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/agents/virt/client/mcast.c b/agents/virt/client/mcast.c new file mode 100644 index 0000000..42f0a6b --- /dev/null +++ b/agents/virt/client/mcast.c @@ -0,0 +1,393 @@ +/* + Copyright Red Hat, Inc. 2006-2017 + + 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/select.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> +#include <libgen.h> +#include <nss.h> + +/* Local includes */ +#include "xvm.h" +#include "ip_lookup.h" +#include "simple_auth.h" +#include "options.h" +#include "tcp.h" +#include "mcast.h" +#include "debug.h" +#include "fdops.h" +#include "client.h" + + +static int +tcp_wait_connect(int lfd, int retry_tenths) +{ + int fd; + fd_set rfds; + int n; + struct timeval tv; + + dbg_printf(3, "Waiting for connection from XVM host daemon.\n"); + FD_ZERO(&rfds); + FD_SET(lfd, &rfds); + tv.tv_sec = retry_tenths / 10; + tv.tv_usec = (retry_tenths % 10) * 100000; + + n = select(lfd + 1, &rfds, NULL, NULL, &tv); + if (n == 0) { + errno = ETIMEDOUT; + return -1; + } else if (n < 0) { + return -1; + } + + fd = accept(lfd, NULL, 0); + if (fd < 0) + return -1; + + return fd; +} + + +void +do_read_hostlist(int fd, int timeout) +{ + host_state_t hinfo; + fd_set rfds; + struct timeval tv; + int ret; + + do { + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + tv.tv_sec = timeout; + tv.tv_usec = 0; + + ret = _select_retry(fd+1, &rfds, NULL, NULL, &tv); + if (ret == 0) { + printf("Timed out!\n"); + break; + } + + ret = _read_retry(fd, &hinfo, sizeof(hinfo), &tv); + if (ret < sizeof(hinfo)) { + printf("Bad read!\n"); + break; + } + + if (strlen((char *)hinfo.uuid) == 0 && + strlen((char *)hinfo.domain) == 0) + break; + + printf("%-32s %s %s\n", hinfo.domain, hinfo.uuid, + (hinfo.state == 1) ? "on" : "off"); + } while (1); +} + + +static int +tcp_exchange(int fd, fence_auth_type_t auth, void *key, + size_t key_len, int timeout) +{ + fd_set rfds; + struct timeval tv; + char ret = 1; + + /* Ok, we're connected */ + dbg_printf(3, "Issuing TCP challenge\n"); + if (sock_challenge(fd, auth, key, key_len, timeout) <= 0) { + /* Challenge failed */ + printf("Invalid response to challenge\n"); + return 1; + } + + /* Now they'll send us one, so we need to respond here */ + dbg_printf(3, "Responding to TCP challenge\n"); + if (sock_response(fd, auth, key, key_len, timeout) <= 0) { + printf("Invalid response to challenge\n"); + return 1; + } + + dbg_printf(2, "TCP Exchange + Authentication done... \n"); + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + tv.tv_sec = timeout; + tv.tv_usec = 0; + + ret = 1; + dbg_printf(3, "Waiting for return value from XVM host\n"); + if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0) + return -1; + + /* Read return code */ + if (_read_retry(fd, &ret, 1, &tv) < 0) + ret = 1; + + if (ret == (char)RESP_HOSTLIST) /* hostlist */ { + do_read_hostlist(fd, timeout); + ret = 0; + } + + return ret; +} + + +static int +send_multicast_packets(ip_list_t *ipl, fence_virt_args_t *args, + uint32_t seqno, void *key, size_t key_len) +{ + fence_req_t freq; + int mc_sock; + ip_addr_t *ipa; + struct sockaddr_in tgt4; + struct sockaddr_in6 tgt6; + struct sockaddr *tgt; + socklen_t tgt_len; + + for (ipa = ipl->tqh_first; ipa; ipa = ipa->ipa_entries.tqe_next) { + + if (ipa->ipa_family != args->net.family) { + dbg_printf(2, "Ignoring %s: wrong family\n", ipa->ipa_address); + continue; + } + + if (args->net.family == PF_INET) { + mc_sock = ipv4_send_sk(ipa->ipa_address, args->net.addr, + args->net.port, + (struct sockaddr *)&tgt4, + sizeof(struct sockaddr_in)); + tgt = (struct sockaddr *)&tgt4; + tgt_len = sizeof(tgt4); + } else if (args->net.family == PF_INET6) { + mc_sock = ipv6_send_sk(ipa->ipa_address, args->net.addr, + args->net.port, + (struct sockaddr *)&tgt6, + sizeof(struct sockaddr_in6)); + tgt = (struct sockaddr *)&tgt6; + tgt_len = sizeof(tgt6); + } else { + dbg_printf(2, "Unsupported family %d\n", args->net.family); + return -1; + } + + if (mc_sock < 0) + continue; + + /* Build our packet */ + memset(&freq, 0, sizeof(freq)); + if (args->domain && strlen((char *)args->domain)) { + strncpy((char *)freq.domain, args->domain, + sizeof(freq.domain) - 1); + } + freq.request = args->op; + freq.hashtype = args->net.hash; + freq.seqno = seqno; + + /* Store source address */ + if (ipa->ipa_family == PF_INET) { + freq.addrlen = sizeof(struct in_addr); + /* XXX Swap order for in_addr ? XXX */ + if (inet_pton(PF_INET, ipa->ipa_address, freq.address) != 1) { + dbg_printf(2, "Unable to convert address\n"); + close(mc_sock); + return -1; + } + } else if (ipa->ipa_family == PF_INET6) { + freq.addrlen = sizeof(struct in6_addr); + if (inet_pton(PF_INET6, ipa->ipa_address, freq.address) != 1) { + dbg_printf(2, "Unable to convert address\n"); + close(mc_sock); + return -1; + } + } + + freq.flags = 0; + if (args->flags & F_USE_UUID) + freq.flags |= RF_UUID; + freq.family = ipa->ipa_family; + freq.port = args->net.port; + + sign_request(&freq, key, key_len); + + dbg_printf(3, "Sending to %s via %s\n", args->net.addr, + ipa->ipa_address); + + if(sendto(mc_sock, &freq, sizeof(freq), 0, + (struct sockaddr *)tgt, tgt_len) < 0) { + dbg_printf(3, "Unable to send packet to %s via %s\n", + args->net.addr, ipa->ipa_address); + } + + close(mc_sock); + } + + return 0; +} + + +/* TODO: Clean this up!!! */ +int +mcast_fence_virt(fence_virt_args_t *args) +{ + ip_list_t ipl; + char key[MAX_KEY_LEN]; + struct timeval tv; + int lfd = -1, key_len = 0, fd, ret; + int attempts = 0; + uint32_t seqno; + + /* Initialize NSS; required to do hashing, as silly as that + sounds... */ + if (NSS_NoDB_Init(NULL) != SECSuccess) { + printf("Could not initialize NSS\n"); + return 1; + } + + if (args->net.auth != AUTH_NONE || args->net.hash != HASH_NONE) { + key_len = read_key_file(args->net.key_file, key, sizeof(key)); + if (key_len < 0) { + printf("Could not read %s; trying without " + "authentication\n", args->net.key_file); + args->net.auth = AUTH_NONE; + args->net.hash = HASH_NONE; + key_len = 0; + } + } + + /* Do the real work */ + if (ip_build_list(&ipl) < 0) { + printf("Error building IP address list\n"); + return 1; + } + + attempts = args->timeout * 10 / args->retr_time; + + listen_loop: + do { + switch (args->net.auth) { + case AUTH_NONE: + case AUTH_SHA1: + case AUTH_SHA256: + case AUTH_SHA512: + if (args->net.family == PF_INET) { + lfd = ipv4_listen(NULL, args->net.port, 10); + } else { + lfd = ipv6_listen(NULL, args->net.port, 10); + } + break; + /*case AUTH_X509:*/ + /* XXX Setup SSL listener socket here */ + default: + return 1; + } + + if (lfd < 0) { + printf("Failed to listen: %s\n", strerror(errno)); + usleep(args->retr_time * 100000); + if (--attempts > 0) + goto listen_loop; + } + } while (0); + + if (lfd < 0) + return -1; + + gettimeofday(&tv, NULL); + seqno = (uint32_t)tv.tv_usec; + + do { + if (send_multicast_packets(&ipl, args, seqno, + key, key_len)) { + close(lfd); + return -1; + } + + switch (args->net.auth) { + case AUTH_NONE: + case AUTH_SHA1: + case AUTH_SHA256: + case AUTH_SHA512: + fd = tcp_wait_connect(lfd, args->retr_time); + if (fd < 0 && (errno == ETIMEDOUT || + errno == EINTR)) + continue; + break; + /* case AUTH_X509: + ... = ssl_wait_connect... */ + break; + default: + close(lfd); + return 1; + } + + break; + } while (--attempts); + + if (lfd >= 0) + close(lfd); + + if (fd < 0) { + if (attempts <= 0) { + printf("Timed out waiting for response\n"); + return 1; + } + printf("Operation failed: %s\n", strerror(errno)); + return -1; + } + + switch (args->net.auth) { + case AUTH_NONE: + case AUTH_SHA1: + case AUTH_SHA256: + case AUTH_SHA512: + ret = tcp_exchange(fd, args->net.auth, key, key_len, + args->timeout); + break; + /* case AUTH_X509: + return ssl_exchange(...); */ + default: + ret = 1; + break; + } + + close(fd); + return ret; +} |