diff options
Diffstat (limited to '')
-rwxr-xr-x | heartbeat/IPaddr2 | 1357 |
1 files changed, 1357 insertions, 0 deletions
diff --git a/heartbeat/IPaddr2 b/heartbeat/IPaddr2 new file mode 100755 index 0000000..97a7431 --- /dev/null +++ b/heartbeat/IPaddr2 @@ -0,0 +1,1357 @@ +#!/bin/sh +# +# $Id: IPaddr2.in,v 1.24 2006/08/09 13:01:54 lars Exp $ +# +# OCF Resource Agent compliant IPaddr2 script. +# +# Based on work by Tuomo Soini, ported to the OCF RA API by Lars +# Marowsky-Brée. Implements Cluster Alias IP functionality too. +# +# Cluster Alias IP cleanup, fixes and testing by Michael Schwartzkopff +# +# +# Copyright (c) 2003 Tuomo Soini +# Copyright (c) 2004-2006 SUSE LINUX AG, Lars Marowsky-Brée +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of version 2 of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Further, this software is distributed without any warranty that it is +# free of the rightful claim of any third person regarding infringement +# or the like. Any license provided herein, whether implied or +# otherwise, applies only to this software file. Patent licenses, if +# any, provided herein do not apply to combinations of this program with +# other software, or any other product whatsoever. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. +# +# + + +# TODO: +# - There ought to be an ocf_run_cmd function which does all logging, +# timeout handling etc for us +# - Make this the standard IP address agent on Linux; the other +# platforms simply should ignore the additional parameters OR can use +# the legacy heartbeat resource script... +# - Check LVS <-> clusterip incompatibilities. +# +# OCF parameters are as below +# OCF_RESKEY_ip +# OCF_RESKEY_broadcast +# OCF_RESKEY_nic +# OCF_RESKEY_cidr_netmask +# OCF_RESKEY_iflabel +# OCF_RESKEY_mac +# OCF_RESKEY_clusterip_hash +# OCF_RESKEY_arp_interval +# OCF_RESKEY_arp_count +# OCF_RESKEY_arp_bg +# OCF_RESKEY_preferred_lft +# +# OCF_RESKEY_CRM_meta_clone +# OCF_RESKEY_CRM_meta_clone_max + + +####################################################################### +# Initialization: + +: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} +. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs +. ${OCF_FUNCTIONS_DIR}/findif.sh + +# Defaults +OCF_RESKEY_ip_default="" +OCF_RESKEY_cidr_netmask_default="" +OCF_RESKEY_broadcast_default="" +OCF_RESKEY_iflabel_default="" +OCF_RESKEY_cidr_netmask_default="" +OCF_RESKEY_lvs_support_default=false +OCF_RESKEY_lvs_ipv6_addrlabel_default=false +OCF_RESKEY_lvs_ipv6_addrlabel_value_default=99 +OCF_RESKEY_clusterip_hash_default="sourceip-sourceport" +OCF_RESKEY_mac_default="" +OCF_RESKEY_unique_clone_address_default=false +OCF_RESKEY_arp_interval_default=200 +OCF_RESKEY_arp_count_default=5 +OCF_RESKEY_arp_count_refresh_default=0 +OCF_RESKEY_arp_bg_default="" +OCF_RESKEY_arp_sender_default="" +OCF_RESKEY_send_arp_opts_default="" +OCF_RESKEY_flush_routes_default="false" +OCF_RESKEY_run_arping_default=false +OCF_RESKEY_nodad_default=false +OCF_RESKEY_noprefixroute_default="false" +OCF_RESKEY_preferred_lft_default="forever" +OCF_RESKEY_network_namespace_default="" + +: ${OCF_RESKEY_ip=${OCF_RESKEY_ip_default}} +: ${OCF_RESKEY_cidr_netmask=${OCF_RESKEY_cidr_netmask_default}} +: ${OCF_RESKEY_broadcast=${OCF_RESKEY_broadcast_default}} +: ${OCF_RESKEY_iflabel=${OCF_RESKEY_iflabel_default}} +: ${OCF_RESKEY_lvs_support=${OCF_RESKEY_lvs_support_default}} +: ${OCF_RESKEY_lvs_ipv6_addrlabel=${OCF_RESKEY_lvs_ipv6_addrlabel_default}} +: ${OCF_RESKEY_lvs_ipv6_addrlabel_value=${OCF_RESKEY_lvs_ipv6_addrlabel_value_default}} +: ${OCF_RESKEY_clusterip_hash=${OCF_RESKEY_clusterip_hash_default}} +: ${OCF_RESKEY_mac=${OCF_RESKEY_mac_default}} +: ${OCF_RESKEY_unique_clone_address=${OCF_RESKEY_unique_clone_address_default}} +: ${OCF_RESKEY_arp_interval=${OCF_RESKEY_arp_interval_default}} +: ${OCF_RESKEY_arp_count=${OCF_RESKEY_arp_count_default}} +: ${OCF_RESKEY_arp_count_refresh=${OCF_RESKEY_arp_count_refresh_default}} +: ${OCF_RESKEY_arp_bg=${OCF_RESKEY_arp_bg_default}} +: ${OCF_RESKEY_arp_sender=${OCF_RESKEY_arp_sender_default}} +: ${OCF_RESKEY_send_arp_opts=${OCF_RESKEY_send_arp_opts_default}} +: ${OCF_RESKEY_flush_routes=${OCF_RESKEY_flush_routes_default}} +: ${OCF_RESKEY_run_arping=${OCF_RESKEY_run_arping_default}} +: ${OCF_RESKEY_nodad=${OCF_RESKEY_nodad_default}} +: ${OCF_RESKEY_noprefixroute=${OCF_RESKEY_noprefixroute_default}} +: ${OCF_RESKEY_preferred_lft=${OCF_RESKEY_preferred_lft_default}} +: ${OCF_RESKEY_network_namespace=${OCF_RESKEY_network_namespace_default}} + +####################################################################### + +SENDARP=$HA_BIN/send_arp +SENDUA=$HA_BIN/send_ua +FINDIF=findif +VLDIR=$HA_RSCTMP +SENDARPPIDDIR=$HA_RSCTMP +CIP_lockfile=$HA_RSCTMP/IPaddr2-CIP-${OCF_RESKEY_ip} + +IPADDR2_CIP_IPTABLES=$IPTABLES + +####################################################################### + +meta_data() { + cat <<END +<?xml version="1.0"?> +<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd"> +<resource-agent name="IPaddr2" version="1.0"> +<version>1.0</version> + +<longdesc lang="en"> +This Linux-specific resource manages IP alias IP addresses. +It can add an IP alias, or remove one. +In addition, it can implement Cluster Alias IP functionality +if invoked as a clone resource. + +If used as a clone, "shared address with a trivial, stateless +(autonomous) load-balancing/mutual exclusion on ingress" mode gets +applied (as opposed to "assume resource uniqueness" mode otherwise). +For that, Linux firewall (kernel and userspace) is assumed, and since +recent distributions are ambivalent in plain "iptables" command to +particular back-end resolution, "iptables-legacy" (when present) gets +prioritized so as to avoid incompatibilities (note that respective +ipt_CLUSTERIP firewall extension in use here is, at the same time, +marked deprecated, yet said "legacy" layer can make it workable, +literally, to this day) with "netfilter" one (as in "iptables-nft"). +In that case, you should explicitly set clone-node-max >= 2, +and/or clone-max < number of nodes. In case of node failure, +clone instances need to be re-allocated on surviving nodes. +This would not be possible if there is already an instance +on those nodes, and clone-node-max=1 (which is the default). + +When the specified IP address gets assigned to a respective interface, the +resource agent sends unsolicited ARP (Address Resolution Protocol, IPv4) or NA +(Neighbor Advertisement, IPv6) packets to inform neighboring machines about the +change. This functionality is controlled for both IPv4 and IPv6 by shared +'arp_*' parameters. +</longdesc> + +<shortdesc lang="en">Manages virtual IPv4 and IPv6 addresses (Linux specific version)</shortdesc> + +<parameters> +<parameter name="ip" unique="1" required="1"> +<longdesc lang="en"> +The IPv4 (dotted quad notation) or IPv6 address (colon hexadecimal notation) +example IPv4 "192.168.1.1". +example IPv6 "2001:db8:DC28:0:0:FC57:D4C8:1FFF". +</longdesc> +<shortdesc lang="en">IPv4 or IPv6 address</shortdesc> +<content type="string" default="${OCF_RESKEY_ip_default}" /> +</parameter> +<parameter name="nic" unique="0"> +<longdesc lang="en"> +The base network interface on which the IP address will be brought +online. +If left empty, the script will try and determine this from the +routing table. + +Do NOT specify an alias interface in the form eth0:1 or anything here; +rather, specify the base interface only. +If you want a label, see the iflabel parameter. + +Prerequisite: + +There must be at least one static IP address, which is not managed by +the cluster, assigned to the network interface. +If you can not assign any static IP address on the interface, +modify this kernel parameter: + +sysctl -w net.ipv4.conf.all.promote_secondaries=1 # (or per device) +</longdesc> +<shortdesc lang="en">Network interface</shortdesc> +<content type="string"/> +</parameter> + +<parameter name="cidr_netmask"> +<longdesc lang="en"> +The netmask for the interface in CIDR format +(e.g., 24 and not 255.255.255.0) + +If unspecified, the script will also try to determine this from the +routing table. +</longdesc> +<shortdesc lang="en">CIDR netmask</shortdesc> +<content type="string" default="${OCF_RESKEY_cidr_netmask_default}"/> +</parameter> + +<parameter name="broadcast"> +<longdesc lang="en"> +Broadcast address associated with the IP. It is possible to use the +special symbols '+' and '-' instead of the broadcast address. In this +case, the broadcast address is derived by setting/resetting the host +bits of the interface prefix. +</longdesc> +<shortdesc lang="en">Broadcast address</shortdesc> +<content type="string" default="${OCF_RESKEY_broadcast_default}"/> +</parameter> + +<parameter name="iflabel"> +<longdesc lang="en"> +You can specify an additional label for your IP address here. +This label is appended to your interface name. + +The kernel allows alphanumeric labels up to a maximum length of 15 +characters including the interface name and colon (e.g. eth0:foobar1234) + +A label can be specified in nic parameter but it is deprecated. +If a label is specified in nic name, this parameter has no effect. +</longdesc> +<shortdesc lang="en">Interface label</shortdesc> +<content type="string" default="${OCF_RESKEY_iflabel_default}"/> +</parameter> + +<parameter name="lvs_support"> +<longdesc lang="en"> +Enable support for LVS Direct Routing configurations. In case a IP +address is stopped, only move it to the loopback device to allow the +local node to continue to service requests, but no longer advertise it +on the network. + +Notes for IPv6: +It is not necessary to enable this option on IPv6. +Instead, enable 'lvs_ipv6_addrlabel' option for LVS-DR usage on IPv6. +</longdesc> +<shortdesc lang="en">Enable support for LVS DR</shortdesc> +<content type="boolean" default="${OCF_RESKEY_lvs_support_default}"/> +</parameter> + +<parameter name="lvs_ipv6_addrlabel"> +<longdesc lang="en"> +Enable adding IPv6 address label so IPv6 traffic originating from +the address's interface does not use this address as the source. +This is necessary for LVS-DR health checks to realservers to work. Without it, +the most recently added IPv6 address (probably the address added by IPaddr2) +will be used as the source address for IPv6 traffic from that interface and +since that address exists on loopback on the realservers, the realserver +response to pings/connections will never leave its loopback. +See RFC3484 for the detail of the source address selection. + +See also 'lvs_ipv6_addrlabel_value' parameter. +</longdesc> +<shortdesc lang="en">Enable adding IPv6 address label.</shortdesc> +<content type="boolean" default="${OCF_RESKEY_lvs_ipv6_addrlabel_default}"/> +</parameter> + +<parameter name="lvs_ipv6_addrlabel_value"> +<longdesc lang="en"> +Specify IPv6 address label value used when 'lvs_ipv6_addrlabel' is enabled. +The value should be an unused label in the policy table +which is shown by 'ip addrlabel list' command. +You would rarely need to change this parameter. +</longdesc> +<shortdesc lang="en">IPv6 address label value.</shortdesc> +<content type="integer" default="${OCF_RESKEY_lvs_ipv6_addrlabel_value_default}"/> +</parameter> + +<parameter name="mac"> +<longdesc lang="en"> +Set the interface MAC address explicitly. Currently only used in case of +the Cluster IP Alias. Leave empty to chose automatically. + +</longdesc> +<shortdesc lang="en">Cluster IP MAC address</shortdesc> +<content type="string" default="${OCF_RESKEY_mac_default}"/> +</parameter> + +<parameter name="clusterip_hash"> +<longdesc lang="en"> +Specify the hashing algorithm used for the Cluster IP functionality. + +</longdesc> +<shortdesc lang="en">Cluster IP hashing function</shortdesc> +<content type="string" default="${OCF_RESKEY_clusterip_hash_default}"/> +</parameter> + +<parameter name="unique_clone_address"> +<longdesc lang="en"> +If true, add the clone ID to the supplied value of IP to create +a unique address to manage +</longdesc> +<shortdesc lang="en">Create a unique address for cloned instances</shortdesc> +<content type="boolean" default="${OCF_RESKEY_unique_clone_address_default}"/> +</parameter> + +<parameter name="arp_interval"> +<longdesc lang="en"> +Specify the interval between unsolicited ARP (IPv4) or NA (IPv6) packets in +milliseconds. + +This parameter is deprecated and used for the backward compatibility only. +It is effective only for the send_arp binary which is built with libnet, +and send_ua for IPv6. It has no effect for other arp_sender. +</longdesc> +<shortdesc lang="en">ARP/NA packet interval in ms (deprecated)</shortdesc> +<content type="integer" default="${OCF_RESKEY_arp_interval_default}"/> +</parameter> + +<parameter name="arp_count"> +<longdesc lang="en"> +Number of unsolicited ARP (IPv4) or NA (IPv6) packets to send at resource +initialization. +</longdesc> +<shortdesc lang="en">ARP/NA packet count sent during initialization</shortdesc> +<content type="integer" default="${OCF_RESKEY_arp_count_default}"/> +</parameter> + +<parameter name="arp_count_refresh"> +<longdesc lang="en"> +For IPv4, number of unsolicited ARP packets to send during resource monitoring. +Doing so helps mitigate issues of stuck ARP caches resulting from split-brain +situations. +</longdesc> +<shortdesc lang="en">ARP packet count sent during monitoring</shortdesc> +<content type="integer" default="${OCF_RESKEY_arp_count_refresh_default}"/> +</parameter> + +<parameter name="arp_bg"> +<longdesc lang="en"> +Whether or not to send the ARP (IPv4) or NA (IPv6) packets in the background. +The default is true for IPv4 and false for IPv6. +</longdesc> +<shortdesc lang="en">ARP/NA from background</shortdesc> +<content type="string" default="${OCF_RESKEY_arp_bg_default}"/> +</parameter> + +<parameter name="arp_sender"> +<longdesc lang="en"> +For IPv4, the program to send ARP packets with on start. Available options are: + - send_arp: default + - ipoibarping: default for infiniband interfaces if ipoibarping is available + - iputils_arping: use arping in iputils package + - libnet_arping: use another variant of arping based on libnet +</longdesc> +<shortdesc lang="en">ARP sender</shortdesc> +<content type="string" default="${OCF_RESKEY_arp_sender_default}"/> +</parameter> + +<parameter name="send_arp_opts"> +<longdesc lang="en"> +For IPv4, extra options to pass to the arp_sender program. +Available options are vary depending on which arp_sender is used. + +A typical use case is specifying '-A' for iputils_arping to use +ARP REPLY instead of ARP REQUEST as Gratuitous ARPs. +</longdesc> +<shortdesc lang="en">Options for ARP sender</shortdesc> +<content type="string" default="${OCF_RESKEY_send_arp_opts_default}"/> +</parameter> + +<parameter name="flush_routes"> +<longdesc lang="en"> +Flush the routing table on stop. This is for +applications which use the cluster IP address +and which run on the same physical host that the +IP address lives on. The Linux kernel may force that +application to take a shortcut to the local loopback +interface, instead of the interface the address +is really bound to. Under those circumstances, an +application may, somewhat unexpectedly, continue +to use connections for some time even after the +IP address is deconfigured. Set this parameter in +order to immediately disable said shortcut when the +IP address goes away. +</longdesc> +<shortdesc lang="en">Flush kernel routing table on stop</shortdesc> +<content type="boolean" default="${OCF_RESKEY_flush_routes_default}"/> +</parameter> + +<parameter name="run_arping"> +<longdesc lang="en"> +For IPv4, whether or not to run arping for collision detection check. +</longdesc> +<shortdesc lang="en">Run arping for IPv4 collision detection check</shortdesc> +<content type="string" default="${OCF_RESKEY_run_arping_default}"/> +</parameter> + +<parameter name="nodad"> +<longdesc lang="en"> +For IPv6, do not perform Duplicate Address Detection when adding the address. +</longdesc> +<shortdesc lang="en">Use nodad flag</shortdesc> +<content type="string" default="${OCF_RESKEY_nodad_default}"/> +</parameter> + +<parameter name="noprefixroute"> +<longdesc lang="en"> +Use noprefixroute flag (see 'man ip-address'). +</longdesc> +<shortdesc lang="en">Use noprefixroute flag</shortdesc> +<content type="string" default="${OCF_RESKEY_noprefixroute_default}"/> +</parameter> + +<parameter name="preferred_lft"> +<longdesc lang="en"> +For IPv6, set the preferred lifetime of the IP address. +This can be used to ensure that the created IP address will not +be used as a source address for routing. +Expects a value as specified in section 5.5.4 of RFC 4862. +</longdesc> +<shortdesc lang="en">IPv6 preferred lifetime</shortdesc> +<content type="string" default="${OCF_RESKEY_preferred_lft_default}"/> +</parameter> + +<parameter name="network_namespace"> +<longdesc lang="en"> +Specifies the network namespace to operate within. +The namespace must already exist, and the interface to be used must be within +the namespace. +</longdesc> +<shortdesc lang="en">Network namespace to use</shortdesc> +<content type="string" default="${OCF_RESKEY_network_namespace_default}"/> +</parameter> +</parameters> + +<actions> +<action name="start" timeout="20s" /> +<action name="stop" timeout="20s" /> +<action name="status" depth="0" timeout="20s" interval="10s" /> +<action name="monitor" depth="0" timeout="20s" interval="10s" /> +<action name="meta-data" timeout="5s" /> +<action name="validate-all" timeout="20s" /> +</actions> +</resource-agent> +END + + exit $OCF_SUCCESS +} + +ip_init() { + local rc + + if [ X`uname -s` != "XLinux" ]; then + ocf_exit_reason "IPaddr2 only supported Linux." + exit $OCF_ERR_INSTALLED + fi + + if [ X"$OCF_RESKEY_ip" = "X" ] && [ "$__OCF_ACTION" != "stop" ]; then + ocf_exit_reason "IP address (the ip parameter) is mandatory" + exit $OCF_ERR_CONFIGURED + fi + + if + case $__OCF_ACTION in + start|stop) ocf_is_root;; + *) true;; + esac + then + : YAY! + else + ocf_exit_reason "You must be root for $__OCF_ACTION operation." + exit $OCF_ERR_PERM + fi + + BASEIP="$OCF_RESKEY_ip" + BRDCAST="$OCF_RESKEY_broadcast" + NIC="$OCF_RESKEY_nic" + # Note: We had a version out there for a while which used + # netmask instead of cidr_netmask. Don't remove this aliasing code! + if + [ ! -z "$OCF_RESKEY_netmask" -a -z "$OCF_RESKEY_cidr_netmask" ] + then + OCF_RESKEY_cidr_netmask=$OCF_RESKEY_netmask + export OCF_RESKEY_cidr_netmask + fi + NETMASK="$OCF_RESKEY_cidr_netmask" + IFLABEL="$OCF_RESKEY_iflabel" + IF_MAC="$OCF_RESKEY_mac" + + IP_INC_GLOBAL=${OCF_RESKEY_CRM_meta_clone_max:-1} + IP_INC_NO=`expr ${OCF_RESKEY_CRM_meta_clone:-0} + 1` + + if ocf_is_true ${OCF_RESKEY_lvs_support} && [ $IP_INC_GLOBAL -gt 1 ]; then + ocf_exit_reason "LVS and load sharing do not go together well" + exit $OCF_ERR_CONFIGURED + fi + + if ocf_is_decimal "$IP_INC_GLOBAL" && [ $IP_INC_GLOBAL -gt 0 ]; then + : + else + ocf_exit_reason "Invalid meta-attribute clone_max [$IP_INC_GLOBAL], should be positive integer" + exit $OCF_ERR_CONFIGURED + fi + + echo $OCF_RESKEY_ip | grep -qs ":" + if [ $? -ne 0 ];then + FAMILY=inet + if ocf_is_true $OCF_RESKEY_lvs_ipv6_addrlabel ;then + ocf_exit_reason "IPv4 does not support lvs_ipv6_addrlabel" + exit $OCF_ERR_CONFIGURED + fi + if [ -z "$OCF_RESKEY_arp_bg" ]; then + OCF_RESKEY_arp_bg=true + fi + else + FAMILY=inet6 + # address sanitization defined in RFC5952 + SANITIZED_IP=$($IP2UTIL route get $OCF_RESKEY_ip 2> /dev/null | awk '$1~/:/ {print $1} $2~/:/ {print $2}') + if [ -n "$SANITIZED_IP" ]; then + OCF_RESKEY_ip="$SANITIZED_IP" + fi + + if ocf_is_true $OCF_RESKEY_lvs_support ;then + ocf_exit_reason "The IPv6 does not support lvs_support" + exit $OCF_ERR_CONFIGURED + fi + if ocf_is_true $OCF_RESKEY_lvs_ipv6_addrlabel ;then + if ocf_is_decimal "$OCF_RESKEY_lvs_ipv6_addrlabel_value" && [ $OCF_RESKEY_lvs_ipv6_addrlabel_value -ge 0 ]; then + : + else + ocf_exit_reason "Invalid lvs_ipv6_addrlabel_value [$OCF_RESKEY_lvs_ipv6_addrlabel_value], should be positive integer" + exit $OCF_ERR_CONFIGURED + fi + fi + if [ -z "$OCF_RESKEY_arp_bg" ]; then + OCF_RESKEY_arp_bg=false + fi + fi + + # support nic:iflabel format in nic parameter + case $NIC in + *:*) + IFLABEL=`echo $NIC | sed 's/[^:]*://'` + NIC=`echo $NIC | sed 's/:.*//'` + # only the base name should be passed to findif + OCF_RESKEY_nic=$NIC + ;; + esac + + # $FINDIF takes its parameters from the environment + # + NICINFO=`$FINDIF` + rc=$? + if + [ $rc -eq 0 ] + then + NICINFO=`echo "$NICINFO" | sed -e 's/netmask\ //;s/broadcast\ //'` + NIC=`echo "$NICINFO" | cut -d" " -f1` + NETMASK=`echo "$NICINFO" | cut -d" " -f2` + BRDCAST=`echo "$NICINFO" | cut -d" " -f3` + else + # findif couldn't find the interface + if ocf_is_probe; then + ocf_log info "[$FINDIF] failed" + exit $OCF_NOT_RUNNING + elif [ "$__OCF_ACTION" = stop ]; then + ocf_log warn "[$FINDIF] failed" + exit $OCF_SUCCESS + else + ocf_exit_reason "[$FINDIF] failed" + exit $rc + fi + fi + + SENDARPPIDFILE="$SENDARPPIDDIR/send_arp-$OCF_RESKEY_ip" + + if [ -n "$IFLABEL" ]; then + IFLABEL=${NIC}:${IFLABEL} + if [ ${#IFLABEL} -gt 15 ]; then + ocf_exit_reason "Interface label [$IFLABEL] exceeds maximum character limit of 15" + exit $OCF_ERR_CONFIGURED + fi + fi + + if [ "$IP_INC_GLOBAL" -gt 1 ] && ! ocf_is_true "$OCF_RESKEY_unique_clone_address"; then + IP_CIP="yes" + IP_CIP_HASH="${OCF_RESKEY_clusterip_hash}" + if [ -z "$IF_MAC" ]; then + # Choose a MAC + # 1. Concatenate some input together + # 2. This doesn't need to be a cryptographically + # secure hash. + # 3. Drop everything after the first 6 octets (12 chars) + # 4. Delimit the octets with ':' + # 5. Make sure the first octet is odd, + # so the result is a multicast MAC + IF_MAC=`echo $OCF_RESKEY_ip $NETMASK $BRDCAST | \ + md5sum | \ + sed -e 's#\(............\).*#\1#' \ + -e 's#..#&:#g; s#:$##' \ + -e 's#^\(.\)[02468aAcCeE]#\11#'` + fi + IP_CIP_FILE="/proc/net/ipt_CLUSTERIP/$OCF_RESKEY_ip" + fi +} + +# +# Find out which interfaces serve the given IP address and netmask. +# The arguments are an IP address and a netmask. +# Its output are interface names devided by spaces (e.g., "eth0 eth1"). +# +find_interface() { + local ipaddr="$1" + local netmask="$2" + + # + # List interfaces but exclude FreeS/WAN ipsecN virtual interfaces + # + local iface="`$IP2UTIL -o -f $FAMILY addr show \ + | grep "\ $ipaddr/$netmask" \ + | cut -d ' ' -f2 \ + | grep -v '^ipsec[0-9][0-9]*$'`" + + echo "$iface" + return 0 +} + +# +# Delete an interface +# +delete_interface () { + ipaddr="$1" + iface="$2" + netmask="$3" + + CMD="$IP2UTIL -f $FAMILY addr delete $ipaddr/$netmask dev $iface" + + ocf_run $CMD || return $OCF_ERR_GENERIC + + if ocf_is_true $OCF_RESKEY_flush_routes; then + ocf_run $IP2UTIL route flush cache + fi + + if [ "$FAMILY" = "inet6" ] && ocf_is_true $OCF_RESKEY_lvs_ipv6_addrlabel ;then + delete_ipv6_addrlabel $ipaddr + fi + + return $OCF_SUCCESS +} + +# +# Add an interface +# +add_interface () { + local cmd msg extra_opts ipaddr netmask broadcast iface label + + ipaddr="$1" + netmask="$2" + broadcast="$3" + iface="$4" + label="$5" + + if [ "$FAMILY" = "inet" ] && ocf_is_true $OCF_RESKEY_run_arping && + check_binary arping; then + arping -q -c 2 -w 3 -D -I $iface $ipaddr + if [ $? = 1 ]; then + ocf_log err "IPv4 address collision $ipaddr [DAD]" + return $OCF_ERR_GENERIC + fi + fi + + if [ "$FAMILY" = "inet6" ] && ocf_is_true $OCF_RESKEY_lvs_ipv6_addrlabel ;then + add_ipv6_addrlabel $ipaddr + fi + + cmd="$IP2UTIL -f $FAMILY addr add $ipaddr/$netmask dev $iface" + msg="Adding $FAMILY address $ipaddr/$netmask to device $iface" + if [ "$broadcast" != "none" ]; then + cmd="$IP2UTIL -f $FAMILY addr add $ipaddr/$netmask brd $broadcast dev $iface" + msg="Adding $FAMILY address $ipaddr/$netmask with broadcast address $broadcast to device $iface" + fi + + extra_opts="" + if [ "$FAMILY" = "inet6" ] && ocf_is_true "${OCF_RESKEY_nodad}"; then + extra_opts="$extra_opts nodad" + fi + + if ocf_is_true "${OCF_RESKEY_noprefixroute}"; then + extra_opts="$extra_opts noprefixroute" + fi + + if [ ! -z "$label" ]; then + extra_opts="$extra_opts label $label" + fi + if [ "$FAMILY" = "inet6" ] ;then + extra_opts="$extra_opts preferred_lft $OCF_RESKEY_preferred_lft" + fi + if [ -n "$extra_opts" ]; then + cmd="$cmd$extra_opts" + msg="$msg (with$extra_opts)" + fi + + ocf_log info "$msg" + ocf_run $cmd || return $OCF_ERR_GENERIC + + msg="Bringing device $iface up" + cmd="$IP2UTIL link set $iface up" + ocf_log info "$msg" + ocf_run $cmd || return $OCF_ERR_GENERIC + + return $OCF_SUCCESS +} + +# +# Delete a route +# +delete_route () { + prefix="$1" + iface="$2" + + CMD="$IP2UTIL route delete $prefix dev $iface" + + ocf_log info "$CMD" + $CMD + + return $? +} + +# On Linux systems the (hidden) loopback interface may +# conflict with the requested IP address. If so, this +# unoriginal code will remove the offending loopback address +# and save it in VLDIR so it can be added back in later +# when the IPaddr is released. +# +# TODO: This is very ugly and should be controlled by an additional +# instance parameter. Or even: multi-state, with the IP only being +# "active" on the master!? +# +remove_conflicting_loopback() { + ipaddr="$1" + netmask="$2" + broadcast="$3" + ifname="$4" + + ocf_log info "Removing conflicting loopback $ifname." + if + echo "$ipaddr $netmask $broadcast $ifname" > "$VLDIR/$ipaddr" + then + : Saved loopback information in $VLDIR/$ipaddr + else + ocf_log err "Could not save conflicting loopback $ifname." \ + "it will not be restored." + fi + delete_interface "$ipaddr" "$ifname" "$netmask" + # Forcibly remove the route (if it exists) to the loopback. + delete_route "$ipaddr" "$ifname" +} + +# +# On Linux systems the (hidden) loopback interface may +# need to be restored if it has been taken down previously +# by remove_conflicting_loopback() +# +restore_loopback() { + ipaddr="$1" + + if [ -s "$VLDIR/$ipaddr" ]; then + ifinfo=`cat "$VLDIR/$ipaddr"` + ocf_log info "Restoring loopback IP Address " \ + "$ifinfo." + add_interface $ifinfo + rm -f "$VLDIR/$ipaddr" + fi +} + +add_ipv6_addrlabel() { + local cmd ipaddr value + ipaddr="$1" + value="$OCF_RESKEY_lvs_ipv6_addrlabel_value" + + cmd="$IP2UTIL addrlabel add prefix $ipaddr label $value" + ocf_log info "Adding IPv6 address label prefix $ipaddr label $value" + ocf_run $cmd || ocf_log warn "$cmd failed." +} + +delete_ipv6_addrlabel() { + local cmd ipaddr value + ipaddr="$1" + value="$OCF_RESKEY_lvs_ipv6_addrlabel_value" + + cmd="$IP2UTIL addrlabel del prefix $ipaddr label $value" + ocf_run $cmd # an error can be ignored +} + +is_infiniband() { + $IP2UTIL link show $NIC | grep link/infiniband >/dev/null +} + +log_arp_sender() { + local cmdline + local output + local rc + cmdline="$@" + + output=$($cmdline 2>&1) + rc=$? + if [ $rc -ne 0 ] && \ + [ "$ARP_SENDER" != "libnet_arping" ] ; then + # libnet_arping always return an error as no answers + ocf_log err "Could not send gratuitous arps: rc=$rc" + fi + ocf_log $LOGLEVEL "$output" +} + +# wrapper function to manage PID file to run arping in background +run_with_pidfile() { + local cmdline + local pid + local rc + + cmdline="$@" + + $cmdline & + pid=$! + echo "$pid" > $SENDARPPIDFILE + wait $pid + rc=$? + rm -f $SENDARPPIDFILE + return $rc +} + +build_arp_sender_cmd() { + case "$ARP_SENDER" in + send_arp) + if [ "x$IP_CIP" = "xyes" ] ; then + if [ x = "x$IF_MAC" ] ; then + MY_MAC=auto + else + # send_arp.linux should return without doing anything in this case + MY_MAC=`echo ${IF_MAC} | sed -e 's/://g'` + fi + else + MY_MAC=auto + fi + + ARGS="$OCF_RESKEY_send_arp_opts -i $OCF_RESKEY_arp_interval -r $ARP_COUNT -p $SENDARPPIDFILE $NIC $OCF_RESKEY_ip $MY_MAC not_used not_used" + ARP_SENDER_CMD="$SENDARP $ARGS" + ;; + iputils_arping) + ARGS="$OCF_RESKEY_send_arp_opts -U -c $ARP_COUNT -I $NIC $OCF_RESKEY_ip" + ARP_SENDER_CMD="run_with_pidfile arping $ARGS" + ;; + libnet_arping) + ARGS="$OCF_RESKEY_send_arp_opts -U -c $ARP_COUNT -i $NIC -S $OCF_RESKEY_ip $OCF_RESKEY_ip" + ARP_SENDER_CMD="run_with_pidfile arping $ARGS" + ;; + ipoibarping) + ARGS="-q -c $ARP_COUNT -U -I $NIC $OCF_RESKEY_ip" + ARP_SENDER_CMD="ipoibarping $ARGS" + ;; + *) + # should not occur + ocf_exit_reason "unrecognized arp_sender value: $ARP_SENDER" + exit $OCF_ERR_GENERIC + ;; + esac +} + +# +# Send Unsolicited ARPs to update neighbor's ARP cache +# +run_arp_sender() { + if [ "x$1" = "xrefresh" ] ; then + ARP_COUNT=$OCF_RESKEY_arp_count_refresh + LOGLEVEL=debug + else + ARP_COUNT=$OCF_RESKEY_arp_count + LOGLEVEL=info + fi + if [ $ARP_COUNT -eq 0 ] ; then + return + fi + + # do not need to send Gratuitous ARPs in the Cluster IP configuration + # except send_arp.libnet binary to retain the old behavior + if [ "x$IP_CIP" = "xyes" ] && \ + [ "x$ARP_SENDER" != "xsend_arp" ] ; then + ocf_log info "Gratuitous ARPs are not sent in the Cluster IP configuration" + return + fi + + # prepare arguments for each arp sender program + # $ARP_SENDER_CMD should be set + build_arp_sender_cmd + + ocf_log $LOGLEVEL "$ARP_SENDER_CMD" + + if ocf_is_true $OCF_RESKEY_arp_bg; then + log_arp_sender $ARP_SENDER_CMD & + else + log_arp_sender $ARP_SENDER_CMD + fi +} + +log_send_ua() { + local cmdline + local output + local rc + + cmdline="$@" + output=$($cmdline 2>&1) + rc=$? + if [ $rc -ne 0 ] ; then + ocf_log err "Could not send ICMPv6 Unsolicited Neighbor Advertisements: rc=$rc" + fi + ocf_log info "$output" + return $rc +} + +# +# Run send_ua to note send ICMPv6 Unsolicited Neighbor Advertisements. +# +run_send_ua() { + local i + + # Duplicate Address Detection [DAD] + # Kernel will flag the IP as 'tentative' until it ensured that + # there is no duplicates. + # If there is, it will flag it as 'dadfailed' + for i in $(seq 1 10); do + ipstatus=$($IP2UTIL -o -f $FAMILY addr show dev $NIC to $OCF_RESKEY_ip/$NETMASK) + case "$ipstatus" in + *dadfailed*) + ocf_log err "IPv6 address collision $OCF_RESKEY_ip [DAD]" + $IP2UTIL -f $FAMILY addr del dev $NIC $OCF_RESKEY_ip/$NETMASK + if [ $? -ne 0 ]; then + ocf_log err "Could not delete IPv6 address" + fi + return $OCF_ERR_GENERIC + ;; + *tentative*) + if [ $i -eq 10 ]; then + ocf_log warn "IPv6 address : DAD is still in tentative" + fi + ;; + *) + break + ;; + esac + sleep 1 + done + # Now the address should be usable + + ARGS="-i $OCF_RESKEY_arp_interval -c $OCF_RESKEY_arp_count $OCF_RESKEY_ip $NETMASK $NIC" + ocf_log info "$SENDUA $ARGS" + if ocf_is_true $OCF_RESKEY_arp_bg; then + log_send_ua $SENDUA $ARGS & + else + log_send_ua $SENDUA $ARGS + fi +} + +# Do we already serve this IP address on the given $NIC? +# +# returns: +# ok = served (for CIP: + hash bucket) +# partial = served and no hash bucket (CIP only) +# partial2 = served and no CIP iptables rule +# no = nothing +# +ip_served() { + if [ -z "$NIC" ]; then # no nic found or specified + echo "no" + return 0 + fi + + cur_nic="`find_interface $OCF_RESKEY_ip $NETMASK`" + + if [ -z "$cur_nic" ]; then + echo "no" + return 0 + fi + + if [ -z "$IP_CIP" ]; then + for i in $cur_nic; do + # only mark as served when on the same interfaces as $NIC + [ "$i" = "$NIC" ] || continue + echo "ok" + return 0 + done + # There used to be logic here to pretend "not served", + # if ${OCF_RESKEY_lvs_support} was enabled, and the IP was + # found active on "lo*" only. With lvs_support on, you should + # have NIC != lo, so thats already filtered + # by the continue above. + + echo "no" + return 0 + fi + + # Special handling for the CIP: + if [ ! -e $IP_CIP_FILE ]; then + echo "partial2" + return 0 + fi + if egrep -q "(^|,)${IP_INC_NO}(,|$)" $IP_CIP_FILE ; then + echo "ok" + return 0 + else + echo "partial" + return 0 + fi + + exit $OCF_ERR_GENERIC +} + +####################################################################### + +ip_usage() { + cat <<END +usage: $0 {start|stop|status|monitor|validate-all|meta-data} + +Expects to have a fully populated OCF RA-compliant environment set. +END +} + +ip_start() { + if [ -z "$NIC" ]; then + ocf_exit_reason "No nic found or specified" + exit $OCF_ERR_CONFIGURED + fi + + if [ -n "$IP_CIP" ]; then + # Cluster IPs need special processing when the first bucket + # is added to the node... take a lock to make sure only one + # process executes that code + ocf_take_lock $CIP_lockfile + ocf_release_lock_on_exit $CIP_lockfile + fi + + # + # Do we already service this IP address on $NIC? + # + local ip_status=`ip_served` + + if [ "$ip_status" = "ok" ]; then + exit $OCF_SUCCESS + fi + + if [ -n "$IP_CIP" ] && ([ $ip_status = "no" ] || [ $ip_status = "partial2" ]); then + $MODPROBE ip_conntrack + $IPADDR2_CIP_IPTABLES -I INPUT -d $OCF_RESKEY_ip -i $NIC -j CLUSTERIP \ + --new \ + --clustermac $IF_MAC \ + --total-nodes $IP_INC_GLOBAL \ + --local-node $IP_INC_NO \ + --hashmode $IP_CIP_HASH + if [ $? -ne 0 ]; then + ocf_exit_reason "iptables failed" + exit $OCF_ERR_GENERIC + fi + fi + + if [ -n "$IP_CIP" ] && [ $ip_status = "partial" ]; then + echo "+$IP_INC_NO" >$IP_CIP_FILE + fi + + if [ "$ip_status" = "no" ]; then + if ocf_is_true ${OCF_RESKEY_lvs_support}; then + for i in `find_interface $OCF_RESKEY_ip 32`; do + case $i in + lo*) + remove_conflicting_loopback $OCF_RESKEY_ip 32 255.255.255.255 lo + ;; + esac + done + fi + + add_interface $OCF_RESKEY_ip $NETMASK ${BRDCAST:-none} $NIC $IFLABEL + rc=$? + + if [ $rc -ne $OCF_SUCCESS ]; then + ocf_exit_reason "Failed to add $OCF_RESKEY_ip" + exit $rc + fi + fi + + case $NIC in + lo*) + : no need to run send_arp on loopback + ;; + *) + if [ $FAMILY = "inet" ];then + run_arp_sender + else + if [ -x $SENDUA ]; then + run_send_ua + if [ $? -ne 0 ]; then + ocf_exit_reason "run_send_ua failed." + exit $OCF_ERR_GENERIC + fi + fi + fi + ;; + esac + exit $OCF_SUCCESS +} + +ip_stop() { + local ip_del_if="yes" + if [ -n "$IP_CIP" ]; then + # Cluster IPs need special processing when the last bucket + # is removed from the node... take a lock to make sure only one + # process executes that code + ocf_take_lock $CIP_lockfile + ocf_release_lock_on_exit $CIP_lockfile + fi + + if [ -f "$SENDARPPIDFILE" ] ; then + kill `cat "$SENDARPPIDFILE"` + if [ $? -ne 0 ]; then + ocf_log warn "Could not kill previously running send_arp for $OCF_RESKEY_ip" + else + ocf_log info "killed previously running send_arp for $OCF_RESKEY_ip" + fi + rm -f "$SENDARPPIDFILE" + fi + local ip_status=`ip_served` + ocf_log info "IP status = $ip_status, IP_CIP=$IP_CIP" + + if [ $ip_status = "no" ]; then + : Requested interface not in use + exit $OCF_SUCCESS + fi + + if [ -n "$IP_CIP" ] && [ $ip_status != "partial2" ]; then + if [ $ip_status = "partial" ]; then + exit $OCF_SUCCESS + fi + echo "-$IP_INC_NO" >$IP_CIP_FILE + if [ "x$(cat $IP_CIP_FILE)" = "x" ]; then + ocf_log info $OCF_RESKEY_ip, $IP_CIP_HASH + i=1 + while [ $i -le $IP_INC_GLOBAL ]; do + ocf_log info $i + $IPADDR2_CIP_IPTABLES -D INPUT -d $OCF_RESKEY_ip -i $NIC -j CLUSTERIP \ + --new \ + --clustermac $IF_MAC \ + --total-nodes $IP_INC_GLOBAL \ + --local-node $i \ + --hashmode $IP_CIP_HASH + i=`expr $i + 1` + done + else + ip_del_if="no" + fi + fi + + if [ "$ip_del_if" = "yes" ]; then + delete_interface $OCF_RESKEY_ip $NIC $NETMASK + if [ $? -ne 0 ]; then + ocf_exit_reason "Unable to remove IP [${OCF_RESKEY_ip} from interface [ $NIC ]" + exit $OCF_ERR_GENERIC + fi + + if ocf_is_true ${OCF_RESKEY_lvs_support}; then + restore_loopback "$OCF_RESKEY_ip" + fi + fi + + exit $OCF_SUCCESS +} + +ip_monitor() { + # TODO: Implement more elaborate monitoring like checking for + # interface health maybe via a daemon like FailSafe etc... + + local ip_status=`ip_served` + case $ip_status in + ok) + run_arp_sender refresh + return $OCF_SUCCESS + ;; + partial|no|partial2) + exit $OCF_NOT_RUNNING + ;; + *) + # Errors on this interface? + return $OCF_ERR_GENERIC + ;; + esac +} + +# make sure that we have something to send ARPs with +set_send_arp_program() { + ARP_SENDER=send_arp + if [ -n "$OCF_RESKEY_arp_sender" ]; then + case "$OCF_RESKEY_arp_sender" in + send_arp) + check_binary $SENDARP + ;; + iputils_arping) + check_binary arping + ;; + libnet_arping) + check_binary arping + ;; + ipoibarping) + check_binary ipoibarping + ;; + *) + ocf_exit_reason "unrecognized arp_sender value: $OCF_RESKEY_arp_sender" + exit $OCF_ERR_CONFIGURED + ;; + esac + ARP_SENDER="$OCF_RESKEY_arp_sender" + else + if is_infiniband; then + ARP_SENDER=ipoibarping + if ! have_binary ipoibarping; then + [ "$__OCF_ACTION" = start ] && + ocf_log warn "using send_arp for infiniband because ipoibarping is not available (set arp_sender to \"send_arp\" to suppress this message)" + check_binary $SENDARP + ARP_SENDER=send_arp + fi + fi + fi +} + +ip_validate() { + check_binary $IP2UTIL + IP_CIP= + + if [ -n "$OCF_RESKEY_network_namespace" ]; then + OCF_RESKEY_network_namespace= exec $IP2UTIL netns exec "$OCF_RESKEY_network_namespace" "$0" "$__OCF_ACTION" + fi + + ip_init + + set_send_arp_program + + if [ -n "$IP_CIP" ]; then + if have_binary "$IPTABLES_LEGACY"; then + IPADDR2_CIP_IPTABLES="$IPTABLES_LEGACY" + fi + check_binary "$IPADDR2_CIP_IPTABLES" + check_binary $MODPROBE + fi + +# $BASEIP, $NETMASK, $NIC , $IP_INC_GLOBAL, and $BRDCAST have been checked within ip_init, +# do not bother here. + + if ocf_is_true "$OCF_RESKEY_unique_clone_address" && + ! ocf_is_true "$OCF_RESKEY_CRM_meta_globally_unique"; then + ocf_exit_reason "unique_clone_address makes sense only with meta globally_unique set" + exit $OCF_ERR_CONFIGURED + fi + + if ocf_is_decimal "$OCF_RESKEY_arp_interval" && [ $OCF_RESKEY_arp_interval -gt 0 ]; then + : + else + ocf_exit_reason "Invalid OCF_RESKEY_arp_interval [$OCF_RESKEY_arp_interval]" + exit $OCF_ERR_CONFIGURED + fi + + if ocf_is_decimal "$OCF_RESKEY_arp_count" && [ $OCF_RESKEY_arp_count -gt 0 ]; then + : + else + ocf_exit_reason "Invalid OCF_RESKEY_arp_count [$OCF_RESKEY_arp_count]" + exit $OCF_ERR_CONFIGURED + fi + + if [ -z "$OCF_RESKEY_preferred_lft" ]; then + ocf_exit_reason "Empty value is invalid for OCF_RESKEY_preferred_lft" + exit $OCF_ERR_CONFIGURED + fi + + if [ -n "$IP_CIP" ]; then + + local valid=1 + + case $IP_CIP_HASH in + sourceip|sourceip-sourceport|sourceip-sourceport-destport) + ;; + *) + ocf_exit_reason "Invalid OCF_RESKEY_clusterip_hash [$IP_CIP_HASH]" + exit $OCF_ERR_CONFIGURED + ;; + esac + + if ocf_is_true ${OCF_RESKEY_lvs_support}; then + ocf_exit_reason "LVS and load sharing not advised to try" + exit $OCF_ERR_CONFIGURED + fi + + case $IF_MAC in + [0-9a-zA-Z][13579bBdDfF][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]) + ;; + *) + valid=0 + ;; + esac + + if [ $valid -eq 0 ]; then + ocf_exit_reason "Invalid IF_MAC [$IF_MAC]" + exit $OCF_ERR_CONFIGURED + fi + + fi +} + +if ocf_is_true "$OCF_RESKEY_unique_clone_address"; then + prefix=`echo $OCF_RESKEY_ip | awk -F. '{print $1"."$2"."$3}'` + suffix=`echo $OCF_RESKEY_ip | awk -F. '{print $4}'` + suffix=`expr ${OCF_RESKEY_CRM_meta_clone:-0} + $suffix` + OCF_RESKEY_ip="$prefix.$suffix" +fi + +case $__OCF_ACTION in +meta-data) meta_data + ;; +usage|help) ip_usage + exit $OCF_SUCCESS + ;; +esac + +ip_validate + +case $__OCF_ACTION in +start) ip_start + ;; +stop) ip_stop + ;; +status) ip_status=`ip_served` + if [ $ip_status = "ok" ]; then + echo "running" + exit $OCF_SUCCESS + else + echo "stopped" + exit $OCF_NOT_RUNNING + fi + ;; +monitor) ip_monitor + ;; +validate-all) ;; +*) ip_usage + exit $OCF_ERR_UNIMPLEMENTED + ;; +esac +# vi:sw=4:ts=8: |