From mboxrd@z Thu Jan 1 00:00:00 1970 From: lhh@sourceware.org Date: 1 Dec 2006 15:49:41 -0000 Subject: [Cluster-devel] cluster/fence/agents/xvm Makefile README TODO ... Message-ID: <20061201154941.17754.qmail@sourceware.org> List-Id: To: cluster-devel.redhat.com MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit CVSROOT: /cvs/cluster Module name: cluster Branch: RHEL4 Changes by: lhh at sourceware.org 2006-12-01 15:49:38 Added files: fence/agents/xvm: Makefile README TODO debug.c fence_xvm.c ip_lookup.c ip_lookup.h mcast.c mcast.h options.c options.h simple_auth.c simple_auth.h tcp.c tcp.h xvm.h Log message: Add support for fence_xvm to RHEL4 branch (requires seamonkey-nss-devel and seamonkey-nspr-devel to compile) Patches: http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/Makefile.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.5.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/README.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.1.6.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/TODO.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.2.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/debug.c.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.1.6.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/fence_xvm.c.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.4.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/ip_lookup.c.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.3.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/ip_lookup.h.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.1.6.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/mcast.c.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.2.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/mcast.h.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.1.6.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/options.c.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.4.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/options.h.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.2.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/simple_auth.c.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.3.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/simple_auth.h.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.1.6.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/tcp.c.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.2.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/tcp.h.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.1.6.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/xvm/xvm.h.diff?cvsroot=cluster&only_with_tag=RHEL4&r1=NONE&r2=1.2.2.1 /cvs/cluster/cluster/fence/agents/xvm/Makefile,v --> standard output revision 1.5.2.1 --- cluster/fence/agents/xvm/Makefile +++ - 2006-12-01 15:49:39.073388000 +0000 @@ -0,0 +1,55 @@ +############################################################################### +############################################################################### +## +## Copyright (C) 2006 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. +## +############################################################################### +############################################################################### + +top_srcdir=../.. +include ${top_srcdir}/make/defines.mk + +TARGETS=fence_xvm + +fence_xvm_SOURCE = fence_xvm.c mcast.c ip_lookup.c simple_auth.c tcp.c \ + options.c debug.c + +# +# RHEL4 branch notes! +# +# RHEL4 includes seamonkey-nss and seamonkey-nspr; we need both +# seamonkey-nss-devel and seamonkey-nspr-devel to be installed in +# order to build. On RHEL4, these provide pkgconfig (note: +# mozilla-config does not work) scripts to determine their +# include locations. +# +INCLUDE=-I${top_srcdir}/include -I${top_srcdir}/config \ + $(shell pkg-config --cflags mozilla-nss mozilla-nspr) \ + -I../../../ccs/lib + +CFLAGS+=-DFENCE_RELEASE_NAME=\"${RELEASE}\" \ + -Wall -Werror -Wstrict-prototypes -Wshadow -ggdb -D_GNU_SOURCE + +LIBS+=-L../../../ccs/lib -lnss3 + +all: ${TARGETS} + +fence_xvm: ${fence_xvm_SOURCE:.c=.o} + gcc -o $@ $^ $(LIBS) + +%.o: %.c + gcc $(CFLAGS) -c -o $@ $^ $(INCLUDES) + +clean: + rm -f $(TARGETS) *~ *.o + +install: all + if [ ! -d ${sbindir} ]; then \ + install -d ${sbindir}; \ + fi + install -m755 ${TARGETS} ${sbindir} + /cvs/cluster/cluster/fence/agents/xvm/README,v --> standard output revision 1.1.6.1 --- cluster/fence/agents/xvm/README +++ - 2006-12-01 15:49:39.162701000 +0000 @@ -0,0 +1,182 @@ +I. Fence_xvm - virtual machine fencing agent + +Fence_xvm is an agent which establishes a communications link between +a cluster of virtual machines (VC) and a cluster of domain0/physical +nodes which are hosting the virtual cluster. Its operations are +fairly simple. + + (a) Start a listener service. + (b) Send a multicast packet requesting that a VM be fenced. + (c) Authenticate client. + (e) Read response. + (f) Exit with success/failure, depending on the response received. + +If any of the above steps fail, the fencing agent exits with a failure +code and fencing is retried by the virtual cluster at a later time. +Because of the simplicty of fence_xvm, it is not necessary that +fence_xvm be run from within a virtualized guest - all it needs is +libnspr and libnss and a shared private key (for authentication; we +would hate to receive a false positive response from a node not in the +cluster!). + + +II. Fence_xvmd - The virtual machine fencing host + +Fence_xvmd is a daemon which runs on physical hosts (e.g. in domain0) +of the cluster hosting the virtual cluster. It listens on a port +for multicast traffic from virtual cluster(s), and takes actions. +Multiple disjoint virtual clusters can coexist on a single physical +host cluster, but this requires multiple instances of fence_xvmd. + +NOTE: fence_xvmd *MUST* be run on ALL nodes in a given cluster which +will be hosting virtual machines if fence_xvm is to be used for +fencing! + +There are a couple of ways the multicast packet is handled, +depending on the state of the host OS. It might be hosting the VM, +or it might not. Furthermore, the VM might "reside" on a host which +has failed. + +In order to be able to guarantee safe fencing of a VM even if the +last- known host is down, we must store the last-known locations of +each virtual machine in some sort of cluster-wide way. For this, we +use the AIS Checkpointing API, which is provided by OpenAIS. Every +few seconds, fence_xvmd queries the hypervisor via libvirt and +stores any local VM states in a checkpoint. In the event of a +physical node failure (which consequently causes the failure of one +or more guests), we can then read the checkpoint section corresponding +to the guest we need to fence to find out the previous +owner. With that information, we can then check with CMAN to see if +the last-known host node has been fenced. If so, then the VM is +clean as well. The physical cluster must, therefore, have fencing +in order for fence_xvmd to work. + +Operation of a node hosting a VM which needs to be fenced: + + (a) Receive multicast packet + (b) Authenticate multicast packet + (c) Open connection to host contained within multicast + packet. + (d) Authenticate server. + (e) Carry out fencing operation (e.g. call libvirt to destroy or + reboot the VM; there is no "on" method at this point). + (f) If operation succeeds, send success response. + +Operation of high-node-ID: + + (a) Receive multicast packet + (b) Authenticate multicast packet + (c) Read VM state from checkpoint + (d) Check liveliness of nodeID hosting VM (if alive, do nothing) + (e) Open connection to host contained within multicast + packet. + (f) Check with CMAN to see if last-known host has been fenced. + (If it has not; do nothing -- this is why the physical + cluster also needs fencing!) + (g) Authenticate server & send response. + (h) If last-known host has been fenced, send success response. + +NOTE: There is always a possibility that a VM is started again +before the fencing operation and checkpoint update for that VM +occurs. If the VM has booted and rejoined the cluster, fencing will +not be necessary. If it is in the process of booting, but has not +yet joined the cluster, fencing will also not be necessary - because +it will not be using cluster resources yet. + + +III. Security considerations + +While fencing is generally expected to run on a more or less trusted +network, there are cases where it may not be. + +* The multicast packet is subject to replay attacks, but because no +fencing action is taken based solely on the information contained +within the packet, this should not allow an attacker to maliciously +fence a VM from outside the cluster, though it may be possible to +cause a DoS of fence_xvmd if enough multicast packets are sent. + +* The only currently supported authentication mechanisms are simple +challenge-response based on a shared private key and pseudorandom +number generation. + +* An attacker with access to the shared key(s) can easily fence any +known VM, even if they are not on a cluster node. + +* Different shared keys should be used for different virtual +clusters on the same subnet (whether in the same physical cluster +or not). Additionally, multiple fence_xvmd instances must be run +(each listening on a different multicast IP + port combination). + +IV. Configuration + +Generate a random key file. An example of how to generate it is: + + dd if=/dev/urandom of=/etc/cluster/fence_xvm.key bs=4096 count=1 + +Distribute the generated key file to all virtual machines in a +cluster as well as all physical host nodes which will be hosting +that particular cluster of guests. More simply, everything involved +with hosting the virtual cluster as well as the virtual cluster +itself must have the same key file; it acts as a password. + +The key should not be placed on shared file systems (because shared +file systems require the cluster, which requires fencing...). +Furthermore, it is considered 'unsupported' to join a host cluster +and a guest cluster in one management domain. + +A. Configuring the host (physical) cluster + +On the host cluster, you need to add the following tag as a +child of the tag in /etc/cluster/cluster.conf: + + + +(Do not forget to increment the configuration version number and +run 'ccs_tool update /etc/cluster/cluster.conf' !). + +Start fence_xvmd on all host nodes if it isn't already running. +Just run 'fence_xvmd'. The next time the cluster is restarted, +fence_xvmd will start automatically; it is started by the cman +script if you have the above tag in cluster.conf. + +B. Configuring the guest (virtual) cluster + +On the guest cluster, you need to set up per-node fencing. This +is a fairly simple task as well. First, you need to add a fence +device for 'xvm'. Simply add the following to the +tag in the guest cluster's cluster.conf: + + + +After doing this, each node also needs individual fencing set up. +For each tag, you will need to add something like +the following: + + + + + + + +For example, if you have a virtual host named 'vm1.test.com' with a +corresponding virtual domain name of 'domU-vm1' in the dom0 cluster, +and a node ID of 1, the tag for that virtual machine +would look like so: + + + + + + + + + +C. Advanced configuration + +Any advanced configuration parameters (e.g. changing authentication, +hashing, key file, etc.) should be included in the tag +in the host cluster and the tag in the guest +cluster. For a complete list of advanced parameters, see: + + fence_xvmd -h + fence_xvm -h /cvs/cluster/cluster/fence/agents/xvm/TODO,v --> standard output revision 1.2.2.1 --- cluster/fence/agents/xvm/TODO +++ - 2006-12-01 15:49:39.275150000 +0000 @@ -0,0 +1,33 @@ +High Priority / Blockers: + +* Nothing at this time. + +Medium Priority: + +* Need to add ability for fence_xvmd to forcefully fence the host +dom0 if it's not responding. Medium because it should not be the +default behavior since fencing a host can affect multiple domains +across potentially multiple domU clusters. This will be a server- +side configuration option; domUs will not be able to override it. + +* Support multiple authentication keys in fence_xvmd simultaneously +so that we can fence multiple clusters with only one instance of +fence_xvmd running on a given dom0. + +Low Priority: + +* Turn README in to man pages. + +* Make sure CMAN is running and/or restart/reconnect if CMAN goes +away and comes back. (If CMAN dies, we have big problems anyway) + +* Add SSL connection support. (Challenge/response on a trusted +network should be okay.) + +* Make sure addresses contained in the multicast packet are always +in network-byte order. Low because it will be unlikely that the +host-byte ordering of a domU and its dom0 will be different. + +* Make sure node IDs and VM states stored in openais checkpoints +are in network-byte order and swap back/forth if not. + /cvs/cluster/cluster/fence/agents/xvm/debug.c,v --> standard output revision 1.1.6.1 --- cluster/fence/agents/xvm/debug.c +++ - 2006-12-01 15:49:39.368367000 +0000 @@ -0,0 +1,34 @@ +/* + 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 "xvm.h" + +static int _debug = 0; + +inline void +dset(int threshold) +{ + _debug = threshold; + dprintf(3, "Debugging threshold is now %d\n", threshold); +} + +inline int +dget(void) +{ + return _debug; +} /cvs/cluster/cluster/fence/agents/xvm/fence_xvm.c,v --> standard output revision 1.4.2.1 --- cluster/fence/agents/xvm/fence_xvm.c +++ - 2006-12-01 15:49:39.470749000 +0000 @@ -0,0 +1,365 @@ +/* + 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. +*/ +/* + * @file fence_xvmd.c: Implementation of server daemon for Xen virtual + * machine fencing. This uses SA AIS CKPT b.1.0 checkpointing API to + * store virtual machine states. + * + * Author: Lon Hohberger + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Local includes */ +#include "xvm.h" +#include "ip_lookup.h" +#include "simple_auth.h" +#include "options.h" +#include "tcp.h" +#include "mcast.h" + + +int +tcp_wait_connect(int lfd, int retry_tenths) +{ + int fd; + fd_set rfds; + int n; + struct timeval tv; + + dprintf(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; +} + + +int +tcp_exchange(int fd, fence_auth_type_t auth, void *key, + size_t key_len, int timeout) +{ + char ret; + fd_set rfds; + struct timeval tv; + + /* Ok, we're connected */ + dprintf(3, "Issuing TCP challenge\n"); + if (tcp_challenge(fd, auth, key, key_len, timeout) <= 0) { + /* Challenge failed */ + printf("Invalid response to challenge\n"); + return 0; + } + + /* Now they'll send us one, so we need to respond here */ + dprintf(3, "Responding to TCP challenge\n"); + if (tcp_response(fd, auth, key, key_len, timeout) <= 0) { + printf("Invalid response to challenge\n"); + return 0; + } + + dprintf(2, "TCP Exchange + Authentication done... \n"); + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + tv.tv_sec = timeout; + tv.tv_usec = 0; + + ret = 1; + dprintf(3, "Waiting for return value from XVM host\n"); + if (select(fd + 1, &rfds, NULL, NULL, &tv) <= 0) + return -1; + + /* Read return code */ + read(fd, &ret, 1); + close(fd); + if (ret == 0) + printf("Remote: Operation was successful\n"); + else + printf("Remote: Operation failed\n"); + return ret; +} + + +int +send_multicast_packets(ip_list_t *ipl, fence_xvm_args_t *args, 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->family) { + dprintf(2, "Ignoring %s: wrong family\n", ipa->ipa_address); + continue; + } + + if (args->family == PF_INET) { + mc_sock = ipv4_send_sk(ipa->ipa_address, args->addr, + args->port, + (struct sockaddr *)&tgt4, + sizeof(struct sockaddr_in)); + tgt = (struct sockaddr *)&tgt4; + tgt_len = sizeof(tgt4); + + } else if (args->family == PF_INET6) { + mc_sock = ipv6_send_sk(ipa->ipa_address, args->addr, + args->port, + (struct sockaddr *)&tgt6, + sizeof(struct sockaddr_in6)); + tgt = (struct sockaddr *)&tgt6; + tgt_len = sizeof(tgt6); + } else { + dprintf(2, "Unsupported family %d\n", args->family); + return -1; + } + + if (mc_sock < 0) + continue; + + /* Build our packet */ + memset(&freq, 0, sizeof(freq)); + strncpy((char *)freq.domain, args->domain, + sizeof(freq.domain)); + freq.request = args->op; + freq.hashtype = args->hash; + + /* Store source address */ + if (ipa->ipa_family == PF_INET) { + freq.addrlen = sizeof(struct in_addr); + /* XXX Swap order for in_addr ? XXX */ + inet_pton(PF_INET, ipa->ipa_address, freq.address); + } else if (ipa->ipa_family == PF_INET6) { + freq.addrlen = sizeof(struct in6_addr); + inet_pton(PF_INET6, ipa->ipa_address, freq.address); + } + + freq.flags = 0; + if (args->flags & F_USE_UUID) + freq.flags |= RF_UUID; + freq.family = ipa->ipa_family; + freq.port = args->port; + + sign_request(&freq, key, key_len); + + dprintf(3, "Sending to %s via %s\n", args->addr, + ipa->ipa_address); + + sendto(mc_sock, &freq, sizeof(freq), 0, + (struct sockaddr *)tgt, tgt_len); + + close(mc_sock); + } + + return 0; +} + + +/* TODO: Clean this up!!! */ +int +fence_xen_domain(fence_xvm_args_t *args) +{ + ip_list_t ipl; + char key[4096]; + int lfd, key_len = 0, fd; + int attempts = 0; + + if (args->auth != AUTH_NONE || args->hash != HASH_NONE) { + key_len = read_key_file(args->key_file, key, sizeof(key)); + if (key_len < 0) { + printf("Could not read key file\n"); + return 1; + } + } + + /* Do the real work */ + if (ip_build_list(&ipl) < 0) { + printf("Error building IP address list\n"); + return 1; + } + + switch (args->auth) { + case AUTH_NONE: + case AUTH_SHA1: + case AUTH_SHA256: + case AUTH_SHA512: + if (args->family == PF_INET) { + lfd = ipv4_listen(args->port, 10); + } else { + lfd = ipv6_listen(args->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)); + return 1; + } + + attempts = args->timeout * 10 / args->retr_time; + + do { + if (send_multicast_packets(&ipl, args, key, key_len)) { + return -1; + } + + switch (args->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: + return 1; + } + + break; + } while (--attempts); + + if (fd < 0) { + if (attempts <= 0) { + printf("Timed out waiting for response\n"); + return 1; + } + printf("Fencing failed: %s\n", strerror(errno)); + return -1; + } + + switch (args->auth) { + case AUTH_NONE: + case AUTH_SHA1: + case AUTH_SHA256: + case AUTH_SHA512: + return tcp_exchange(fd, args->auth, key, key_len, + args->timeout); + break; + /* case AUTH_X509: + return ssl_exchange(...); */ + default: + return 1; + } + + return 1; +} + + +int +main(int argc, char **argv) +{ + fence_xvm_args_t args; + char *my_options = "di:a:p:r:C:c:k:H:uo:t:?hV"; + + args_init(&args); + if (argc == 1) { + args_get_stdin(my_options, &args); + } else { + args_get_getopt(argc, argv, my_options, &args); + } + + if (args.flags & F_HELP) { + args_usage(argv[0], my_options, 0); + args_usage(argv[0], my_options, 1); + exit(0); + } + + if (args.flags & F_VERSION) { + printf("%s %s\n", basename(argv[0]), XVM_VERSION); +#ifdef FENCE_RELEASE_NAME + printf("fence release %s\n", FENCE_RELEASE_NAME); +#endif + exit(0); + } + + args_finalize(&args); + dset(args.debug); + + if (args.debug > 0) + args_print(&args); + + /* Additional validation here */ + if (!args.domain) { + printf("No domain specified!\n"); + args.flags |= F_ERR; + } + + if (args.flags & F_ERR) { + args_usage(argv[0], my_options, (argc == 1)); + exit(1); + } + + /* 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; + } + + return fence_xen_domain(&args); +} /cvs/cluster/cluster/fence/agents/xvm/ip_lookup.c,v --> standard output revision 1.3.2.1 --- cluster/fence/agents/xvm/ip_lookup.c +++ - 2006-12-01 15:49:39.592613000 +0000 @@ -0,0 +1,321 @@ +/* + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef IFA_MAX +#include +#endif + +/* Local includes */ +#include "ip_lookup.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; + } + + dprintf(4, "Adding IP %s to list (family %d)\n", ipaddr, family); + + ipa = malloc(sizeof(*ipa)); + memset(ipa, 0, sizeof(*ipa)); + ipa->ipa_family = family; + ipa->ipa_address = strdup(ipaddr); + + 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]; + int x, fd, len; + + dprintf(5, "Connecting to Netlink...\n"); + fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + perror("socket"); + exit(1); + } + + dprintf(5, "Sending address dump request\n"); + send_addr_dump(fd, family); + memset(buf, 0, sizeof(buf)); + + dprintf(5, "Waiting for response\n"); + x = recvfrom(fd, buf, sizeof(buf), 0, NULL, 0); + if (x < 0) { + perror("recvfrom"); + return -1; + } + + dprintf(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 = (struct rtattr *)((void *)ifa + sizeof(*ifa)); + len -= sizeof(*ifa); + do { + /* Make sure we've got a valid rtaddr field */ + if (!RTA_OK(rta, len)) { + dprintf(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) { + dprintf(5, "Skipping label: %s\n", + (char *)RTA_DATA(rta)); + } + + nrta = RTA_NEXT(rta, len); + if (!nrta) + break; + + len -= ((void *)nrta - (void *)rta); + rta = nrta; + } while (RTA_OK(rta, len)); + + nh = NLMSG_NEXT(nh, x); + } + + dprintf(5, "Closing Netlink connection\n"); + close(fd); + return 0; +} + + +int +ip_search(ip_list_t *ipl, char *ip_name) +{ + ip_addr_t *ipa; + + dprintf(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)) { + dprintf(4,"Found\n"); + return 0; + } + } + dprintf(5, "Not found\n"); + return 1; +} + + +int +ip_free_list(ip_list_t *ipl) +{ + ip_addr_t *ipa; + + dprintf(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) +{ + dprintf(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; + + dprintf(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; +} + /cvs/cluster/cluster/fence/agents/xvm/ip_lookup.h,v --> standard output revision 1.1.6.1 --- cluster/fence/agents/xvm/ip_lookup.h +++ - 2006-12-01 15:49:39.682836000 +0000 @@ -0,0 +1,40 @@ +/* + 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 + * Header for ip_lookup.c + */ +#ifndef _IP_LOOKUP_H +#define _IP_LOOKUP_H + +#include + +typedef struct _ip_address { + TAILQ_ENTRY(_ip_address) ipa_entries; + char ipa_family; + char *ipa_address; +} ip_addr_t; + +typedef TAILQ_HEAD(_ip_list, _ip_address) ip_list_t; + +int ip_search(ip_list_t *ipl, char *ip_name); +int ip_free_list(ip_list_t *ipl); +int ip_build_list(ip_list_t *ipl); +int ip_lookup(char *, struct addrinfo **); + +#endif /cvs/cluster/cluster/fence/agents/xvm/mcast.c,v --> standard output revision 1.2.2.1 --- cluster/fence/agents/xvm/mcast.c +++ - 2006-12-01 15:49:39.781466000 +0000 @@ -0,0 +1,372 @@ +/* + 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 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Local includes */ +#include "mcast.h" + +/** + Sets up a multicast receive socket + */ +int +ipv4_recv_sk(char *addr, int port) +{ + int sock; + struct ip_mreq mreq; + struct sockaddr_in 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 * + ********************************/ + dprintf(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)); + close(sock); + 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 */ + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + dprintf(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; + } + + dprintf(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; + } + + /* 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 * + *************************/ + dprintf(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. + */ + dprintf(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. + */ + dprintf(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. + */ + dprintf(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)); + + dprintf(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 * + ********************************/ + dprintf(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; + } + + dprintf(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 + */ + dprintf(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; + } + + dprintf(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(&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 * + *************************/ + dprintf(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; + } + + dprintf(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. + */ + dprintf(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)); + + dprintf(4, "%s: success, fd = %d\n", __FUNCTION__, sock); + return sock; +} /cvs/cluster/cluster/fence/agents/xvm/mcast.h,v --> standard output revision 1.1.6.1 --- cluster/fence/agents/xvm/mcast.h +++ - 2006-12-01 15:49:39.917288000 +0000 @@ -0,0 +1,32 @@ +/* + 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. +*/ +#ifndef _XVM_MCAST_H +#define _XVM_MCAST_H + +#define IPV4_MCAST_DEFAULT "225.0.0.12" +#define IPV6_MCAST_DEFAULT "ff05::3:1" + +int ipv4_recv_sk(char *addr, int port); +int ipv4_send_sk(char *src_addr, char *addr, int port, + struct sockaddr *src, socklen_t slen); +int ipv6_recv_sk(char *addr, int port); +int ipv6_send_sk(char *src_addr, char *addr, int port, + struct sockaddr *src, socklen_t slen); + +#endif /cvs/cluster/cluster/fence/agents/xvm/options.c,v --> standard output revision 1.4.2.1 --- cluster/fence/agents/xvm/options.c +++ - 2006-12-01 15:49:40.071321000 +0000 @@ -0,0 +1,637 @@ +/* + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Local includes */ +#include "xvm.h" +#include "simple_auth.h" +#include "mcast.h" +#include "options.h" + + + +/* Assignment functions */ + +static inline void +assign_debug(fence_xvm_args_t *args, struct arg_info *arg, char *value) +{ + if (!value) { + /* GNU getopt sets optarg to NULL for options w/o a param + We rely on this here... */ + args->debug++; + return; + } + + args->debug = atoi(value); + if (args->debug < 0) { + args->debug = 1; + } +} + + +static inline void +assign_foreground(fence_xvm_args_t *args, struct arg_info *arg, + char *value) +{ + args->flags |= F_FOREGROUND; +} + + +static inline void +assign_family(fence_xvm_args_t *args, struct arg_info *arg, + char *value) +{ + if (!strcasecmp(value, "ipv4")) { + args->family = PF_INET; + } else if (!strcasecmp(value, "ipv6")) { + args->family = PF_INET6; + } else if (!strcasecmp(value, "auto")) { + args->family = 0; + } else { + printf("Unsupported family: '%s'\n", value); + args->flags |= F_ERR; + } +} + + +static inline void +assign_address(fence_xvm_args_t *args, struct arg_info *arg, char *value) +{ + args->addr = strdup(value); +} + + +static inline void +assign_port(fence_xvm_args_t *args, struct arg_info *arg, char *value) +{ + args->port = atoi(value); + if (args->port <= 0 || args->port >= 65500) { + printf("Invalid port: '%s'\n", value); + args->flags |= F_ERR; + } +} + + +static inline void +assign_retrans(fence_xvm_args_t *args, struct arg_info *arg, char *value) +{ + args->retr_time = atoi(value); + if (args->retr_time <= 0) { + printf("Invalid retransmit time: '%s'\n", value); + args->flags |= F_ERR; + } +} + +static inline void +assign_hash(fence_xvm_args_t *args, struct arg_info *arg, char *value) +{ + if (!strcasecmp(value, "none")) { + args->hash = HASH_NONE; + } else if (!strcasecmp(value, "sha1")) { + args->hash = HASH_SHA1; + } else if (!strcasecmp(value, "sha256")) { + args->hash = HASH_SHA256; + } else if (!strcasecmp(value, "sha512")) { + args->hash = HASH_SHA512; + } else { + printf("Unsupported hash: %s\n", value); + args->flags |= F_ERR; + } +} + + +static inline void +assign_auth(fence_xvm_args_t *args, struct arg_info *arg, char *value) +{ + if (!strcasecmp(value, "none")) { + args->auth = AUTH_NONE; + } else if (!strcasecmp(value, "sha1")) { + args->auth = AUTH_SHA1; + } else if (!strcasecmp(value, "sha256")) { + args->auth = AUTH_SHA256; + } else if (!strcasecmp(value, "sha512")) { + args->auth = AUTH_SHA512; + } else { + printf("Unsupported auth type: %s\n", value); + args->flags |= F_ERR; + } +} + +static inline void +assign_key(fence_xvm_args_t *args, struct arg_info *arg, char *value) +{ + struct stat st; + + args->key_file = strdup(value); + + if (stat(value, &st) == -1) { + printf("Invalid key file: '%s' (%s)\n", value, + strerror(errno)); + args->flags |= F_ERR; + } +} + + +static inline void +assign_op(fence_xvm_args_t *args, struct arg_info *arg, char *value) +{ + if (!strcasecmp(value, "null")) { + args->op = FENCE_NULL; + } else if (!strcasecmp(value, "off")) { + args->op = FENCE_OFF; + } else if (!strcasecmp(value, "reboot")) { + args->op = FENCE_REBOOT; + } else { + printf("Unsupported operation: %s\n", value); + args->flags |= F_ERR; + } +} + + +static inline void +assign_domain(fence_xvm_args_t *args, struct arg_info *arg, char *value) +{ + if (args->domain) { + printf("Domain/UUID may not be specified more than once\n"); + args->flags |= F_ERR; + return; + } + + args->domain = strdup(value); + + if (strlen(value) <= 0) { + printf("Invalid domain name\n"); + args->flags |= F_ERR; + } + + if (strlen(value) >= MAX_DOMAINNAME_LENGTH) { + errno = ENAMETOOLONG; + printf("Invalid domain name: '%s' (%s)\n", + value, strerror(errno)); + args->flags |= F_ERR; + } +} + + +static inline void +assign_uuid_lookup(fence_xvm_args_t *args, struct arg_info *arg, char *value) +{ + if (!value) { + /* GNU getopt sets optarg to NULL for options w/o a param + We rely on this here... */ + args->flags |= F_USE_UUID; + return; + } + + args->flags |= ( !!atoi(value) ? F_USE_UUID : 0); +} + + +static inline void +assign_timeout(fence_xvm_args_t *args, struct arg_info *arg, char *value) +{ + args->timeout = atoi(value); + if (args->timeout <= 0) { + printf("Invalid timeout: '%s'\n", value); + args->flags |= F_ERR; + } +} + + +static inline void +assign_help(fence_xvm_args_t *args, struct arg_info *arg, char *value) +{ + args->flags |= F_HELP; +} + + +static inline void +assign_version(fence_xvm_args_t *args, struct arg_info *arg, char *value) +{ + args->flags |= F_VERSION; +} + + +static inline void +assign_noccs(fence_xvm_args_t *args, struct arg_info *arg, char *value) +{ + args->flags |= F_NOCCS; +} + + +/** ALL valid command line and stdin arguments for this fencing agent */ +static struct arg_info _arg_info[] = { + { '\xff', NULL, "agent", + "Not user serviceable", + NULL }, + + { '\xff', NULL, "self", + "Not user serviceable", + NULL }, + + { 'd', "-d", "debug", + "Enable debugging mode", + assign_debug }, + + { 'f', "-f", NULL, + "Foreground mode (do not fork)", + assign_foreground }, + + { 'i', "-i ", "ip_family", + "IP Family ([auto], ipv4, ipv6)", + assign_family }, + + { 'a', "-a
", "multicast_address", + "Multicast address (default=225.0.0.12 / ff02::3:1)", + assign_address }, + + { 'p', "-p ", "port", + "IP port (default=1229)", + assign_port }, + + { 'r', "-r ", "retrans", + "Multicast retransmit time (in 1/10sec; default=20)", + assign_retrans }, + + { 'c', "-c ", "hash", + "Packet hash strength (none, sha1, [sha256], sha512)", + assign_hash }, + + { 'C', "-C ", "auth", + "Authentication (none, sha1, [sha256], sha512)", + assign_auth }, + + { 'k', "-k ", "key_file", + "Shared key file (default=/etc/cluster/fence_xvm.key)", + assign_key }, + + { 'o', "-o ", "option", + "Fencing operation (null, off, [reboot])", + assign_op }, + + { 'H', "-H ", "domain", + "Xen host (domain name) to fence", + assign_domain }, + + { 'u', "-u", "use_uuid", + "Treat as UUID instead of domain name", + assign_uuid_lookup }, + + { 't', "-t ", "timeout", + "Fencing timeout (in seconds; default=30)", + assign_timeout }, + + { 'h', "-h", NULL, + "Help", + assign_help }, + + { '?', "-?", NULL, + "Help (alternate)", + assign_help }, + + { 'X', "-X", NULL, + "Do not connect to CCS for configuration", + assign_noccs }, + + { 'V', "-V", NULL, + "Display version and exit", + assign_version }, + + /* Terminator */ + { 0, NULL, NULL, NULL, NULL } +}; + + +struct arg_info * +find_arg_by_char(char arg) +{ + int x = 0; + + for (x = 0; _arg_info[x].opt != 0; x++) { + if (_arg_info[x].opt == arg) + return &_arg_info[x]; + } + + return NULL; +} + + +struct arg_info * +find_arg_by_string(char *arg) +{ + int x = 0; + + for (x = 0; _arg_info[x].opt != 0; x++) { + if (!_arg_info[x].stdin_opt) + continue; + if (!strcasecmp(_arg_info[x].stdin_opt, arg)) + return &_arg_info[x]; + } + + return NULL; +} + + +/* ============================================================= */ + +/** + Initialize an args structure. + + @param args Pointer to args structure to initialize. + */ +void +args_init(fence_xvm_args_t *args) +{ + args->addr = NULL; + args->domain = NULL; + args->key_file = DEFAULT_KEY_FILE; + args->op = FENCE_REBOOT; + args->hash = DEFAULT_HASH; + args->auth = DEFAULT_AUTH; + args->port = 1229; + args->family = PF_INET; + args->timeout = 30; + args->retr_time = 20; + args->flags = 0; + args->debug = 0; +} + + +#define _pr_int(piece) printf(" %s = %d\n", #piece, piece) +#define _pr_str(piece) printf(" %s = %s\n", #piece, piece) + + +/** + Prints out the contents of an args structure for debugging. + + @param args Pointer to args structure to print out. + */ +void +args_print(fence_xvm_args_t *args) +{ + printf("-- args @ %p --\n", args); + _pr_str(args->addr); + _pr_str(args->domain); + _pr_str(args->key_file); + _pr_int(args->op); + _pr_int(args->hash); + _pr_int(args->auth); + _pr_int(args->port); + _pr_int(args->family); + _pr_int(args->timeout); + _pr_int(args->retr_time); + _pr_int(args->flags); + _pr_int(args->debug); + printf("-- end args --\n"); +} + + +/** + Print out arguments and help information based on what is allowed in + the getopt string optstr. + + @param progname Program name. + @param optstr Getopt(3) style options string + @param print_stdin 0 = print command line options + description, + 1 = print fence-style stdin args + description + */ +void +args_usage(char *progname, char *optstr, int print_stdin) +{ + int x; + struct arg_info *arg; + + if (print_stdin) { + printf("With no command line argument, arguments are " + "read from standard input.\n"); + printf("Arguments read from standard input take " + "the form of:\n\n"); + printf(" arg1=value1\n"); + printf(" arg2=value2\n\n"); + } else { + if (progname) { + printf("usage: %s [args]\n", progname); + } else { + printf("usage: fence_xvm [args]\n"); + } + } + + for (x = 0; x < strlen(optstr); x++) { + arg = find_arg_by_char(optstr[x]); + if (!arg) + continue; + + if (print_stdin) { + if (arg && arg->stdin_opt) + printf(" %-20.20s %-55.55s\n", + arg->stdin_opt, arg->desc); + } else { + printf(" %-20.20s %-55.55s\n", arg->opt_desc, + arg->desc); + } + } + + printf("\n"); +} + + +/** + Remove leading and trailing whitespace from a line of text. + + @param line Line to clean up + @param linelen Max size of line + @return 0 on success, -1 on failure + */ +int +cleanup(char *line, size_t linelen) +{ + char *p; + int x; + + /* Remove leading whitespace. */ + p = line; + for (x = 0; x <= linelen; x++) { + switch (line[x]) { + case '\t': + case ' ': + break; + case '\n': + case '\r': + return -1; + default: + goto eol; + } + } +eol: + /* Move the remainder down by as many whitespace chars as we + chewed up */ + if (x) + memmove(p, &line[x], linelen-x); + + /* Remove trailing whitespace. */ + for (x=0; x <= linelen; x++) { + switch(line[x]) { + case '\t': + case ' ': + case '\r': + case '\n': + line[x] = 0; + case 0: + /* End of line */ + return 0; + } + } + + return -1; +} + + +/** + Parse args from stdin and assign to the specified args structure. + + @param optstr Command line option string in getopt(3) format + @param args Args structure to fill in. + */ +void +args_get_stdin(char *optstr, fence_xvm_args_t *args) +{ + char in[256]; + int line = 0; + char *name, *val; + struct arg_info *arg; + + while (fgets(in, sizeof(in), stdin)) { + ++line; + + if (in[0] == '#') + continue; + + if (cleanup(in, sizeof(in)) == -1) + continue; + + name = in; + if ((val = strchr(in, '='))) { + *val = 0; + ++val; + } + + arg = find_arg_by_string(name); + if (!arg || (arg->opt != '\xff' && + !strchr(optstr, arg->opt))) { + fprintf(stderr, + "parse warning: " + "illegal variable '%s' on line %d\n", name, + line); + continue; + } + + if (arg->assign) + arg->assign(args, arg, val); + } +} + + +/** + Parse args from stdin and assign to the specified args structure. + + @param optstr Command line option string in getopt(3) format + @param args Args structure to fill in. + */ +void +args_get_getopt(int argc, char **argv, char *optstr, fence_xvm_args_t *args) +{ + int opt; + struct arg_info *arg; + + while ((opt = getopt(argc, argv, optstr)) != EOF) { + + arg = find_arg_by_char(opt); + + if (!arg) { + args->flags |= F_ERR; + continue; + } + + if (arg->assign) + arg->assign(args, arg, optarg); + } +} + + +void +args_finalize(fence_xvm_args_t *args) +{ + char *addr = NULL; + + if (!args->addr) { + switch(args->family) { + case 0: + case PF_INET: + addr = IPV4_MCAST_DEFAULT; + break; + case PF_INET6: + addr = IPV6_MCAST_DEFAULT; + break; + default: + args->flags |= F_ERR; + break; + } + } + + if (!args->addr) + args->addr = addr; + + if (!args->addr) { + printf("No multicast address available\n"); + args->flags |= F_ERR; + } + + if (!args->addr) + return; + if (args->family) + return; + + /* Set family */ + if (strchr(args->addr, ':')) + args->family = PF_INET6; + if (strchr(args->addr, '.')) + args->family = PF_INET; + if (!args->family) { + printf("Could not determine address family\n"); + args->flags |= F_ERR; + } +} /cvs/cluster/cluster/fence/agents/xvm/options.h,v --> standard output revision 1.2.2.1 --- cluster/fence/agents/xvm/options.h +++ - 2006-12-01 15:49:40.217515000 +0000 @@ -0,0 +1,70 @@ +/* + 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. +*/ +#ifndef _XVM_OPTIONS_H +#define _XVM_OPTIONS_H + +typedef enum { + F_FOREGROUND = 0x1, + F_NOCCS = 0x2, + F_ERR = 0x4, + F_HELP = 0x8, + F_USE_UUID = 0x10, + F_VERSION = 0x20, + F_CCSERR = 0x40, + F_CCSFAIL = 0x80 +} arg_flags_t; + + +typedef struct { + char *addr; + char *domain; + char *key_file; + fence_cmd_t op; + fence_hash_t hash; + fence_auth_type_t auth; + int port; + int family; + int timeout; + int retr_time; + arg_flags_t flags; + int debug; +} fence_xvm_args_t; + +/* Private structure for commandline / stdin fencing args */ +struct arg_info { + char opt; + char *opt_desc; + char *stdin_opt; + char *desc; + void (*assign)(fence_xvm_args_t *, struct arg_info *, char *); +}; + + +/* Get options */ +void args_init(fence_xvm_args_t *args); +void args_finalize(fence_xvm_args_t *args); + +void args_get_getopt(int argc, char **argv, char *optstr, + fence_xvm_args_t *args); +void args_get_stdin(char *optstr, fence_xvm_args_t *args); +void args_get_ccs(char *optstr, fence_xvm_args_t *args); +void args_usage(char *progname, char *optstr, int print_stdin); +void args_print(fence_xvm_args_t *args); + +#endif /cvs/cluster/cluster/fence/agents/xvm/simple_auth.c,v --> standard output revision 1.3.2.1 --- cluster/fence/agents/xvm/simple_auth.c +++ - 2006-12-01 15:49:40.330092000 +0000 @@ -0,0 +1,410 @@ +/* + 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* Local includes */ +#include "xvm.h" +#include "simple_auth.h" + + +void +print_hash(unsigned char *hash, size_t hashlen) +{ + int x; + + for (x = 0; x < hashlen; x++) + printf("%02x", (hash[x]&0xff)); +} + + +static void +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; + + 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: + return; + } + + dprintf(4, "Opening /dev/urandom\n"); + devrand = open("/dev/urandom", O_RDONLY); + if (devrand >= 0) { + if (read(devrand, req->random, sizeof(req->random)) < 0) { + perror("read /dev/urandom"); + } + close(devrand); + } + + memset(hash, 0, sizeof(hash)); + h = HASH_Create(ht); + if (!h) + return; + + 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)); +} + + +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: + dprintf(3, "%s: no-op (HASH_NONE)\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: + dprintf(3, "%s: no-op (HASH_NONE)\n", __FUNCTION__); + return 0; + case HASH_SHA1: + case HASH_SHA256: + case HASH_SHA512: + sha_sign(req, key, key_len); + return 0; + 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; +} + + +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 (read(devrand, challenge, sizeof(challenge)) < 0) { + perror("read /dev/urandom"); + return 0; + } + close(devrand); + + if (write(fd, challenge, sizeof(challenge)) < 0) { + perror("write"); + 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(fd + 1, &rfds, NULL, NULL, &tv) <= 0) { + perror("select"); + return 0; + } + + if (read(fd, response, sizeof(response)) < sizeof(response)) { + perror("read"); + 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; +} + + +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; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + tv.tv_sec = timeout; + tv.tv_usec = 0; + if (select(fd + 1, &rfds, NULL, NULL, &tv) <= 0) { + perror("select"); + return 0; + } + + if (read(fd, challenge, sizeof(challenge)) < 0) { + perror("read"); + 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: + dprintf(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); + + if (write(fd, hash, sizeof(hash)) < sizeof(hash)) { + perror("read"); + return 0; + } + + return 1; +} + + +int +tcp_challenge(int fd, fence_auth_type_t auth, void *key, size_t key_len, + int timeout) +{ + switch(auth) { + case AUTH_NONE: + dprintf(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 +tcp_response(int fd, fence_auth_type_t auth, void *key, size_t key_len, + int timeout) +{ + switch(auth) { + case AUTH_NONE: + dprintf(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; + + dprintf(3, "Reading in key file %s into %p (%d len)", + file, key, (int)max_len); + fd = open(file, O_RDONLY); + if (fd < 0) { + return -1; + } + + memset(key, 0, max_len); + p = key; + remain = max_len; + + while (remain) { + nread = read(fd, p, remain); + if (nread < 0) { + dprintf(2, "Error from read: %s\n", strerror(errno)); + close(fd); + return -1; + } + + if (nread == 0) { + dprintf(3, "Stopped reading @ %d bytes", + (int)max_len-remain); + break; + } + + p += nread; + remain -= nread; + } + + dprintf(3, "Actual key length = %d bytes", (int)max_len-remain); + close(fd); + + return 0; +} /cvs/cluster/cluster/fence/agents/xvm/simple_auth.h,v --> standard output revision 1.1.6.1 --- cluster/fence/agents/xvm/simple_auth.h +++ - 2006-12-01 15:49:40.710929000 +0000 @@ -0,0 +1,35 @@ +/* + 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. +*/ +#ifndef _XVM_SIMPLE_AUTH_H +#define _XVM_SIMPLE_AUTH_H + +#include + +/* 2-way challenge/response simple auth */ +#define DEFAULT_KEY_FILE "/etc/cluster/fence_xvm.key" + +int read_key_file(char *, char *, size_t); +int tcp_challenge(int, fence_auth_type_t, void *, size_t, int); +int tcp_response(int, fence_auth_type_t, void *, size_t, int); +int sign_request(fence_req_t *, void *, size_t); +int verify_request(fence_req_t *, fence_hash_t, void *, size_t); + +/* SSL certificate-based authentication TBD */ + +#endif /cvs/cluster/cluster/fence/agents/xvm/tcp.c,v --> standard output revision 1.2.2.1 --- cluster/fence/agents/xvm/tcp.c +++ - 2006-12-01 15:49:41.038003000 +0000 @@ -0,0 +1,298 @@ +/* + 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 + * @author Jeff Moyer + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int connect_nb(int fd, struct sockaddr *dest, socklen_t len, int timeout); + +/** + 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 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(uint16_t port, int backlog) +{ + struct sockaddr_in6 _sin6; + int fd, ret; + + dprintf(4, "%s: Setting up ipv6 listen socket\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; + _sin6.sin6_addr = in6addr_any; + + ret = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&ret, sizeof (ret)); + + ret = set_cloexec(fd); + if (ret < 0) { + close(fd); + return -1; + } + + ret = bind(fd, (struct sockaddr *)&_sin6, sizeof(_sin6)); + if (ret < 0) { + close(fd); + return -1; + } + + if (listen(fd, backlog) < 0){ + close(fd); + return -1; + } + + dprintf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd); + return fd; +} + + +/** + Bind to a port on the local IPv4 stack + + @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(uint16_t port, int backlog) +{ + struct sockaddr_in _sin; + int fd, ret; + + dprintf(4, "%s: Setting up ipv4 listen socket\n", __FUNCTION__); + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) + return -1; + + _sin.sin_family = PF_INET; + _sin.sin_port = htons(port); + _sin.sin_addr.s_addr = htonl(INADDR_ANY); + + ret = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&ret, sizeof (ret)); + + ret = set_cloexec(fd); + if (ret < 0) { + close(fd); + return -1; + } + + ret = bind(fd, (struct sockaddr *)&_sin, sizeof(_sin)); + if (ret < 0) { + close(fd); + return -1; + } + + if (listen(fd, backlog) < 0){ + close(fd); + return -1; + } + + dprintf(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; + + dprintf(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; + } + dprintf(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; + + dprintf(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; + } + + dprintf(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); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + + 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; + } + + fcntl(fd, F_SETFL, flags); + return 0; + } + } + + errno = EIO; + return -1; +} /cvs/cluster/cluster/fence/agents/xvm/tcp.h,v --> standard output revision 1.1.6.1 --- cluster/fence/agents/xvm/tcp.h +++ - 2006-12-01 15:49:41.151694000 +0000 @@ -0,0 +1,27 @@ +/* + 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. +*/ +#ifndef _XVM_TCP_H +#define _XVM_TCP_H + +int ipv4_connect(struct in_addr *in_addr, uint16_t port, int timeout); +int ipv6_connect(struct in6_addr *in6_addr, uint16_t port, int timeout); +int ipv4_listen(uint16_t port, int backlog); +int ipv6_listen(uint16_t port, int backlog); + +#endif /cvs/cluster/cluster/fence/agents/xvm/xvm.h,v --> standard output revision 1.2.2.1 --- cluster/fence/agents/xvm/xvm.h +++ - 2006-12-01 15:49:41.429287000 +0000 @@ -0,0 +1,86 @@ +/* + 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. +*/ +#ifndef _XVM_H +#define _XVM_H + +#include +#include +#include + +#define XVM_VERSION "0.9.3" + +#define MAX_DOMAINNAME_LENGTH 64 /* XXX MAXHOSTNAMELEN */ +#define MAX_ADDR_LEN sizeof(struct sockaddr_in6) +#define DOMAIN0NAME "Domain-0" +#define DOMAIN0UUID "00000000-0000-0000-0000-000000000000" + +typedef enum { + HASH_NONE = 0x0, /* No packet signing */ + HASH_SHA1 = 0x1, /* SHA1 signing */ + HASH_SHA256 = 0x2, /* SHA256 signing */ + HASH_SHA512 = 0x3 /* SHA512 signing */ +} fence_hash_t; + +#define DEFAULT_HASH HASH_SHA256 + +typedef enum { + AUTH_NONE = 0x0, /* Plain TCP */ + AUTH_SHA1 = 0x1, /* Challenge-response (SHA1) */ + AUTH_SHA256 = 0x2, /* Challenge-response (SHA256) */ + AUTH_SHA512 = 0x3, /* Challenge-response (SHA512) */ + /* AUTH_SSL_X509 = 0x10 SSL X509 certificates */ +} fence_auth_type_t; + +#define DEFAULT_AUTH AUTH_SHA256 + +typedef enum { + FENCE_NULL = 0x0, + FENCE_OFF = 0x1, /* Turn the VM off */ + FENCE_REBOOT = 0x2 /* Hit the reset button */ + /* FENCE_ON = 0x3 Turn the VM on */ +} fence_cmd_t; + +#define MAX_HASH_LENGTH SHA512_LENGTH + +typedef struct __attribute__ ((packed)) _fence_req { + uint8_t request; /* Fence request */ + uint8_t hashtype; /* Hash type used */ + uint8_t addrlen; /* Length of address */ + uint8_t flags; /* Special flags */ +#define RF_UUID 0x1 /* Flag specifying UUID */ + uint8_t domain[MAX_DOMAINNAME_LENGTH]; /* Domain to fence*/ + uint8_t address[MAX_ADDR_LEN]; /* We're this IP */ + uint16_t port; /* Port we bound to */ + uint8_t random[10]; /* Random Data */ + uint32_t family; /* Address family */ + uint8_t hash[MAX_HASH_LENGTH]; /* Binary hash */ +} fence_req_t; + + +inline void dset(int); +inline int dget(void); + +#define dprintf(level, fmt, args...) \ +do { \ + if (dget()>=level) \ + printf(fmt, ##args); \ +} while(0) + + +#endif