summaryrefslogtreecommitdiffstats
path: root/heartbeat/IPsrcaddr
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xheartbeat/IPsrcaddr631
1 files changed, 631 insertions, 0 deletions
diff --git a/heartbeat/IPsrcaddr b/heartbeat/IPsrcaddr
new file mode 100755
index 0000000..c732ce8
--- /dev/null
+++ b/heartbeat/IPsrcaddr
@@ -0,0 +1,631 @@
+#!/bin/sh
+#
+# Description: IPsrcaddr - Preferred source(/dest) address modification
+#
+# Author: John Sutton <john@scl.co.uk>
+# Support: users@clusterlabs.org
+# License: GNU General Public License (GPL)
+# Copyright: SCL Internet
+#
+# Based on the IPaddr script.
+#
+# This script manages the preferred source address associated with
+# packets which originate on the localhost and are routed through the
+# matching route. By default, i.e. without the use of this script or
+# similar, these packets will carry the IP of the primary i.e. the
+# non-aliased interface. This can be a nuisance if you need to ensure
+# that such packets carry the same IP irrespective of which host in
+# a redundant cluster they actually originate from.
+#
+# It can add a preferred source address, or remove one.
+#
+# usage: IPsrcaddr {start|stop|status|monitor|validate-all|meta-data}
+#
+# The "start" arg adds a preferred source address.
+#
+# Surprisingly, the "stop" arg removes it. :-)
+#
+# NOTES:
+#
+# 1) There must be one and not more than 1 matching route! Mainly because
+# I can't see why you should have more than one. And if there is more
+# than one, we would have to box clever to find out which one is to be
+# modified, or we would have to pass its identity as an argument.
+#
+# 2) The script depends on Alexey Kuznetsov's ip utility from the
+# iproute aka iproute2 package.
+#
+# 3) No checking is done to see if the passed in IP address can
+# reasonably be associated with the interface on which the default
+# route exists. So unless you want to deliberately spoof your source IP,
+# check it! Normally, I would expect that your haresources looks
+# something like:
+#
+# nodename ip1 ip2 ... ipN IPsrcaddr::ipX
+#
+# where ipX is one of the ip1 to ipN.
+#
+# OCF parameters are as below:
+# OCF_RESKEY_ipaddress
+
+#######################################################################
+# Initialization:
+: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
+. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
+. ${OCF_FUNCTIONS_DIR}/findif.sh
+
+# Defaults
+OCF_RESKEY_ipaddress_default=""
+OCF_RESKEY_cidr_netmask_default=""
+OCF_RESKEY_destination_default="0.0.0.0/0"
+OCF_RESKEY_proto_default=""
+OCF_RESKEY_metric_default=""
+OCF_RESKEY_table_default=""
+
+: ${OCF_RESKEY_ipaddress=${OCF_RESKEY_ipaddress_default}}
+: ${OCF_RESKEY_cidr_netmask=${OCF_RESKEY_cidr_netmask_default}}
+: ${OCF_RESKEY_destination=${OCF_RESKEY_destination_default}}
+: ${OCF_RESKEY_proto=${OCF_RESKEY_proto_default}}
+: ${OCF_RESKEY_metric=${OCF_RESKEY_metric_default}}
+: ${OCF_RESKEY_table=${OCF_RESKEY_table_default}}
+#######################################################################
+
+[ -z "$OCF_RESKEY_proto" ] && PROTO="" || PROTO="proto $OCF_RESKEY_proto"
+[ -z "$OCF_RESKEY_table" ] && TABLE="" || TABLE="table $OCF_RESKEY_table"
+
+USAGE="usage: $0 {start|stop|status|monitor|validate-all|meta-data}";
+
+ CMDSHOW="$IP2UTIL route show $TABLE to exact $OCF_RESKEY_destination"
+CMDCHANGE="$IP2UTIL route change to "
+
+if [ "$OCF_RESKEY_destination" != "0.0.0.0/0" ]; then
+ CMDSHOW="$CMDSHOW src $OCF_RESKEY_ipaddress"
+fi
+
+if [ "$OCF_RESKEY_table" = "local" ]; then
+ TABLE="$TABLE local"
+fi
+
+SYSTYPE="`uname -s`"
+
+usage() {
+ echo $USAGE >&2
+}
+
+meta_data() {
+ cat <<END
+<?xml version="1.0"?>
+<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
+<resource-agent name="IPsrcaddr" version="1.0">
+<version>1.0</version>
+
+<longdesc lang="en">
+Resource script for IPsrcaddr. It manages the preferred source address
+modification.
+
+Note: DHCP should not be enabled for the interface serving the preferred
+source address. Enabling DHCP may result in unexpected behavior, such as
+the automatic addition of duplicate or conflicting routes. This may
+cause the IPsrcaddr resource to fail, or it may produce undesired
+behavior while the resource continues to run.
+</longdesc>
+<shortdesc lang="en">Manages the preferred source address for outgoing IP packets</shortdesc>
+
+<parameters>
+<parameter name="ipaddress" unique="0" required="1">
+<longdesc lang="en">
+The IP address.
+</longdesc>
+<shortdesc lang="en">IP address</shortdesc>
+<content type="string" default="${OCF_RESKEY_ipaddress_default}" />
+</parameter>
+
+<parameter name="cidr_netmask">
+<longdesc lang="en">
+The netmask for the interface in CIDR format. (ie, 24), or in
+dotted quad notation 255.255.255.0).
+</longdesc>
+<shortdesc lang="en">Netmask</shortdesc>
+<content type="string" default="${OCF_RESKEY_cidr_netmask_default}"/>
+</parameter>
+
+<parameter name="destination">
+<longdesc lang="en">
+The destination IP/subnet for the route (default: $OCF_RESKEY_destination_default)
+</longdesc>
+<shortdesc lang="en">Destination IP/subnet</shortdesc>
+<content type="string" default="${OCF_RESKEY_destination_default}" />
+</parameter>
+
+<parameter name="proto">
+<longdesc lang="en">
+Proto to match when finding network. E.g. "kernel".
+</longdesc>
+<shortdesc lang="en">Proto</shortdesc>
+<content type="string" default="${OCF_RESKEY_proto_default}" />
+</parameter>
+
+<parameter name="metric">
+<longdesc lang="en">
+Metric. Only needed if incorrect metric value is used.
+</longdesc>
+<shortdesc lang="en">Metric</shortdesc>
+<content type="string" default="${OCF_RESKEY_metric_default}" />
+</parameter>
+
+<parameter name="table">
+<longdesc lang="en">
+Table to modify and use for interface lookup. E.g. "local".
+
+The table has to have a route matching the "destination" parameter.
+
+This can be used for policy based routing. See man ip-rule(8).
+</longdesc>
+<shortdesc lang="en">Table</shortdesc>
+<content type="string" default="${OCF_RESKEY_table_default}" />
+</parameter>
+
+</parameters>
+
+<actions>
+<action name="start" timeout="20s" />
+<action name="stop" timeout="20s" />
+<action name="monitor" depth="0" timeout="20s" interval="10s" />
+<action name="validate-all" timeout="5s" />
+<action name="meta-data" timeout="5s" />
+</actions>
+</resource-agent>
+END
+}
+
+errorexit() {
+ ocf_exit_reason "$*"
+ exit $OCF_ERR_GENERIC
+}
+
+#
+# We can distinguish 3 cases: no preferred source address, a
+# preferred source address exists which matches that specified, and one
+# exists but doesn't match that specified. srca_read() returns 1,0,2
+# respectively.
+#
+# The output of route show is something along the lines of:
+#
+# default via X.X.X.X dev eth1 src Y.Y.Y.Y
+#
+# where the src clause "src Y.Y.Y.Y" may or may not be present
+
+WS="[[:blank:]]"
+OCTET="[0-9]\{1,3\}"
+IPADDR="\($OCTET\.\)\{3\}$OCTET"
+SRCCLAUSE="src$WS$WS*\($IPADDR\)"
+MATCHROUTE="\(.*${WS}\)\($SRCCLAUSE\)\($WS.*\|$\)"
+METRICCLAUSE=".*\(metric$WS[^ ]\+\)"
+PROTOCLAUSE=".*\(proto$WS[^ ]\+\).*"
+FINDIF=findif
+
+# findif needs that to be set
+export OCF_RESKEY_ip=$OCF_RESKEY_ipaddress
+
+srca_read() {
+ # Capture matching route - doublequotes prevent word splitting...
+ ROUTE="`$CMDSHOW dev $INTERFACE 2> /dev/null`" || errorexit "command '$CMDSHOW' failed"
+
+ # ... so we can make sure there is only 1 matching route
+ [ 1 -eq `echo "$ROUTE" | wc -l` ] || \
+ errorexit "more than 1 matching route exists"
+
+ # But there might still be no matching route
+ [ "$OCF_RESKEY_destination" = "0.0.0.0/0" ] && [ -z "$ROUTE" ] && \
+ ! ocf_is_probe && [ "$__OCF_ACTION" != stop ] && errorexit "no matching route exists"
+
+ # Sed out the source ip address if it exists
+ SRCIP=`echo $ROUTE | sed -n "s/$MATCHROUTE/\3/p"`
+
+ # and what remains after stripping out the source ip address clause
+ ROUTE_WO_SRC=`echo $ROUTE | sed "s/$MATCHROUTE/\1\5/"`
+
+ # using "src <ip>" only returns output if there's a match
+ if [ "$OCF_RESKEY_destination" != "0.0.0.0/0" ]; then
+ [ -z "$ROUTE" ] && return 1 || return 0
+ fi
+
+ [ -z "$SRCIP" ] && return 1
+ [ $SRCIP = $1 ] && return 0
+ [ "$__OCF_ACTION" = "monitor" ] || [ "$__OCF_ACTION" = "status" ] && [ "${ROUTE%% *}" = "default" ] && return 1
+ return 2
+}
+
+#
+# Add (or change if it already exists) the preferred source address
+# The exit code should conform to LSB exit codes.
+#
+
+srca_start() {
+ srca_read $1
+
+ rc=$?
+ if [ $rc = 0 ]; then
+ rc=$OCF_SUCCESS
+ ocf_log info "The ip route has been already set.($NETWORK, $INTERFACE, $ROUTE_WO_SRC)"
+ else
+ $IP2UTIL route replace $TABLE $NETWORK dev $INTERFACE $PROTO src $1 $METRIC || \
+ errorexit "command 'ip route replace $TABLE $NETWORK dev $INTERFACE $PROTO src $1 $METRIC' failed"
+
+ if [ "$OCF_RESKEY_destination" = "0.0.0.0/0" ] ;then
+ $CMDCHANGE $ROUTE_WO_SRC src $1 || \
+ errorexit "command '$CMDCHANGE $ROUTE_WO_SRC src $1' failed"
+ fi
+ rc=$?
+ fi
+
+ return $rc
+}
+
+#
+# Remove (if it exists) the preferred source address.
+# If one exists but it's not the same as the one specified, that's
+# an error. Maybe that's the wrong behaviour because if this fails
+# then when IPaddr releases the associated interface (if there is one)
+# your matching route will also get dropped ;-(
+# The exit code should conform to LSB exit codes.
+#
+
+srca_stop() {
+ srca_read $1
+ rc=$?
+
+ if [ $rc = 1 ]; then
+ # We do not have a preferred source address for now
+ ocf_log info "No preferred source address defined, nothing to stop"
+ exit $OCF_SUCCESS
+ fi
+
+ [ $rc = 2 ] && errorexit "The address you specified to stop does not match the preferred source address"
+
+ if [ -z "$TABLE" ] || [ "${TABLE#table }" = "main" ]; then
+ SCOPE="link"
+ else
+ SCOPE="host"
+ fi
+
+ PRIMARY_IP="$($IP2UTIL -4 -o addr show dev $INTERFACE primary | awk '{split($4,a,"/");print a[1]}')"
+ OPTS="proto kernel scope $SCOPE src $PRIMARY_IP"
+
+ $IP2UTIL route replace $TABLE $NETWORK dev $INTERFACE $OPTS $METRIC || \
+ errorexit "command 'ip route replace $TABLE $NETWORK dev $INTERFACE $OPTS $METRIC' failed"
+
+ if [ "$OCF_RESKEY_destination" = "0.0.0.0/0" ] ;then
+ $CMDCHANGE $ROUTE_WO_SRC src $PRIMARY_IP || \
+ errorexit "command '$CMDCHANGE $ROUTE_WO_SRC src $PRIMARY_IP' failed"
+ fi
+
+ return $?
+}
+
+srca_status() {
+ srca_read $1
+
+ case $? in
+ 0) echo "OK"
+ return $OCF_SUCCESS;;
+
+ 1) echo "No preferred source address defined"
+ return $OCF_NOT_RUNNING;;
+
+ 2) echo "Preferred source address has incorrect value"
+ return $OCF_ERR_GENERIC;;
+ esac
+}
+
+# A not reliable IP address checking function, which only picks up those _obvious_ violations...
+#
+# It accepts IPv4 address in dotted quad notation, for example "192.168.1.1"
+#
+# 100% confidence whenever it reports "negative",
+# but may get false "positive" answer.
+#
+CheckIP() {
+ ip="$1"
+ case $ip in
+ *[!0-9.]*) #got invalid char
+ false;;
+ .*|*.) #begin or end by ".", which is invalid
+ false;;
+ *..*) #consecutive ".", which is invalid
+ false;;
+ *.*.*.*.*) #four decimal dots, which is too many
+ false;;
+ *.*.*.*) #exactly three decimal dots, candidate, evaluate each field
+ local IFS=.
+ set -- $ip
+ if
+ ( [ $1 -le 254 ] && [ $2 -le 254 ] && [ $3 -le 254 ] && [ $4 -le 254 ] )
+ then
+ if [ $1 -eq 127 ]; then
+ ocf_exit_reason "IP address [$ip] is a loopback address, thus can not be preferred source address"
+ exit $OCF_ERR_CONFIGURED
+ fi
+ else
+ true
+ fi
+ ;;
+ *) #less than three decimal dots
+ false;;
+ esac
+ return $? # This return is unnecessary, this comment too :)
+}
+
+#
+# Find out which interface or alias serves the given IP address
+# The argument is an IP address, and its output
+# is an (aliased) interface name (e.g., "eth0" and "eth0:0").
+#
+find_interface_solaris() {
+
+
+ $IFCONFIG $IFCONFIG_A_OPT | $AWK '{if ($0 ~ /.*: / && NR > 1) {print "\n"$0} else {print}}' |
+ while read ifname linkstuff
+ do
+ : ifname = $ifname
+ read inet addr junk
+ : inet = $inet addr = $addr
+ while
+ read line && [ "X$line" != "X" ]
+ do
+ : Nothing
+ done
+
+ # This doesn't look right for a box with multiple NICs.
+ # It looks like it always selects the first interface on
+ # a machine. Yet, we appear to use the results for this case too...
+ ifname=`echo "$ifname" | sed s'%:*$%%'`
+
+ case $addr in
+ addr:$BASEIP) echo $ifname; return $OCF_SUCCESS;;
+ $BASEIP) echo $ifname; return $OCF_SUCCESS;;
+ esac
+ done
+ return $OCF_ERR_GENERIC
+}
+
+
+#
+# Find out which interface or alias serves the given IP address
+# The argument is an IP address, and its output
+# is an (aliased) interface name (e.g., "eth0" and "eth0:0").
+#
+find_interface_generic() {
+
+ local iface=`$IP2UTIL -o -f inet addr show | grep "\ $BASEIP" \
+ | cut -d ' ' -f2 | grep -v '^ipsec[0-9][0-9]*$'`
+ if [ -z "$iface" ]; then
+ return $OCF_ERR_GENERIC
+ else
+ echo $iface
+ return $OCF_SUCCESS
+ fi
+}
+
+
+#
+# Find out which interface or alias serves the given IP address
+# The argument is an IP address, and its output
+# is an (aliased) interface name (e.g., "eth0" and "eth0:0").
+#
+find_interface() {
+ case "$SYSTYPE" in
+ SunOS)
+ IF=`find_interface_solaris $BASEIP`
+ ;;
+ *)
+ IF=`find_interface_generic $BASEIP`
+ ;;
+ esac
+
+ echo $IF
+ return $OCF_SUCCESS;
+}
+
+
+ip_status() {
+
+ BASEIP="$1"
+ case "$SYSTYPE" in
+ Darwin)
+ # Treat Darwin the same as the other BSD variants (matched as *BSD)
+ SYSTYPE="${SYSTYPE}BSD"
+ ;;
+ *)
+ ;;
+ esac
+
+
+ case "$SYSTYPE" in
+ *BSD)
+ $IFCONFIG $IFCONFIG_A_OPT | grep "inet.*[: ]$BASEIP " >/dev/null 2>&1
+ if [ $? = 0 ]; then
+ return $OCF_SUCCESS
+ else
+ return $OCF_NOT_RUNNING
+ fi;;
+
+ Linux|SunOS)
+ IF=`find_interface "$BASEIP"`
+ if [ -z "$IF" ]; then
+ return $OCF_NOT_RUNNING
+ fi
+
+ case $IF in
+ lo*)
+ ocf_exit_reason "IP address [$BASEIP] is served by loopback, thus can not be preferred source address"
+ exit $OCF_ERR_CONFIGURED
+ ;;
+ *)return $OCF_SUCCESS;;
+ esac
+ ;;
+
+ *)
+ if [ -z "$IF" ]; then
+ return $OCF_NOT_RUNNING
+ else
+ return $OCF_SUCCESS
+ fi;;
+ esac
+}
+
+
+srca_validate_all() {
+
+ if [ -z "$OCF_RESKEY_ipaddress" ]; then
+ # usage
+ ocf_exit_reason "Please set OCF_RESKEY_ipaddress to the preferred source IP address!"
+ return $OCF_ERR_CONFIGURED
+ fi
+
+ if ! echo "$OCF_RESKEY_destination" | grep -q "/"; then
+ return $OCF_ERR_CONFIGURED
+ fi
+
+
+ if ! [ "x$SYSTYPE" = "xLinux" ]; then
+ # checks after this point are only relevant for linux.
+ return $OCF_SUCCESS
+ fi
+
+ check_binary $AWK
+ case "$SYSTYPE" in
+ *BSD|SunOS)
+ check_binary $IFCONFIG
+ ;;
+ esac
+
+# The IP address should be in good shape
+ if CheckIP "$ipaddress"; then
+ :
+ else
+ ocf_exit_reason "Invalid IP address [$ipaddress]"
+ return $OCF_ERR_CONFIGURED
+ fi
+
+ if ocf_is_probe; then
+ return $OCF_SUCCESS
+ fi
+
+# We should serve this IP address of course
+ if [ "$OCF_CHECK_LEVEL" -eq 10 ]; then
+ if ip_status "$ipaddress"; then
+ :
+ else
+ ocf_exit_reason "We are not serving [$ipaddress], hence can not make it a preferred source address"
+ return $OCF_ERR_INSTALLED
+ fi
+ fi
+ return $OCF_SUCCESS
+}
+
+if
+ ( [ $# -ne 1 ] )
+then
+ usage
+ exit $OCF_ERR_ARGS
+fi
+
+# These operations do not require the OCF instance parameters to be set
+case $1 in
+ meta-data) meta_data
+ exit $OCF_SUCCESS
+ ;;
+ usage) usage
+ exit $OCF_SUCCESS
+ ;;
+ *)
+ ;;
+esac
+
+ipaddress="$OCF_RESKEY_ipaddress"
+
+[ "$__OCF_ACTION" != "validate-all" ] && OCF_CHECK_LEVEL=10
+srca_validate_all
+rc=$?
+if [ $rc -ne $OCF_SUCCESS ]; then
+ case $1 in
+ # if we can't validate the configuration during a stop, that
+ # means the resources isn't configured correctly. There's no way
+ # to actually stop the resource in this situation because there's
+ # no way it could have even started. Return success here
+ # to indicate that the resource is not running, otherwise the
+ # stop action will fail causing the node to be fenced just because
+ # of a mis configuration.
+ stop) exit $OCF_SUCCESS;;
+ *) exit $rc;;
+ esac
+fi
+
+findif_out=`$FINDIF`
+rc=$?
+[ $rc -ne 0 ] && {
+ ocf_exit_reason "[$FINDIF] failed"
+ exit $rc
+}
+
+INTERFACE=`echo $findif_out | awk '{print $1}'`
+LISTROUTE=`$IP2UTIL route list dev $INTERFACE scope link $PROTO match $ipaddress`
+[ -z "$PROTO" ] && PROTO=`echo $LISTROUTE | sed -n "s/$PROTOCLAUSE/\1/p"`
+if [ -n "$OCF_RESKEY_metric" ]; then
+ METRIC="metric $OCF_RESKEY_metric"
+elif [ -z "$TABLE" ] || [ "${TABLE#table }" = "main" ]; then
+ METRIC=`echo $LISTROUTE | sed -n "s/$METRICCLAUSE/\1/p"`
+else
+ METRIC=""
+fi
+if [ "$OCF_RESKEY_destination" = "0.0.0.0/0" ] ;then
+ NETWORK=`echo $LISTROUTE | grep -m 1 -o '^[^ ]*'`
+
+ if [ -z "$NETWORK" ]; then
+ err_str="command '$IP2UTIL route list dev $INTERFACE scope link $PROTO"
+ err_str="$err_str match $ipaddress' failed to find a matching route"
+
+ if [ "$__OCF_ACTION" = "start" ]; then
+ ocf_exit_reason "$err_str"
+ exit $OCF_ERR_ARGS
+ elif ! ocf_is_probe; then
+ ocf_log warn "$err_str"
+ else
+ ocf_log debug "$err_str"
+ fi
+ fi
+else
+ NETWORK="$OCF_RESKEY_destination"
+fi
+
+case $1 in
+ start) srca_start $ipaddress
+ ;;
+ stop) srca_stop $ipaddress
+ ;;
+ status) srca_status $ipaddress
+ ;;
+ monitor) srca_status $ipaddress
+ ;;
+ validate-all) srca_validate_all
+ ;;
+ *) usage
+ exit $OCF_ERR_UNIMPLEMENTED
+ ;;
+esac
+
+exit $?
+
+#
+# Version 0.3 2002/11/04 17:00:00 John Sutton <john@scl.co.uk>
+# Name changed from IPsrcroute to IPsrcaddr and now reports errors
+# using ha_log rather than on stderr.
+#
+# Version 0.2 2002/11/02 17:00:00 John Sutton <john@scl.co.uk>
+# Changed status output to "OK" to satisfy ResourceManager's
+# we_own_resource() function.
+#
+# Version 0.1 2002/11/01 17:00:00 John Sutton <john@scl.co.uk>
+# First effort but does the job?
+#