mirror of https://github.com/sipwise/heartbeat.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
467 lines
11 KiB
467 lines
11 KiB
#!/bin/sh
|
|
#
|
|
# Description: IPsrcaddr - Preferred source address modification
|
|
#
|
|
# Author: John Sutton <john@scl.co.uk>
|
|
# Support: linux-ha@lists.linux-ha.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
|
|
# default 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 default 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_ROOT}/resource.d/heartbeat/.ocf-shellfuncs
|
|
|
|
#######################################################################
|
|
|
|
USAGE="usage: $0 {start|stop|status|monitor|validate-all|meta-data}";
|
|
|
|
CMDSHOW="$IP2UTIL route show to exact 0/0"
|
|
CMDCHANGE="$IP2UTIL route change to "
|
|
|
|
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>
|
|
|
|
<longdesc lang="en">
|
|
Resource script for IPsrcaddr. It manages the preferred source address
|
|
modification.
|
|
</longdesc>
|
|
<shortdesc lang="en">IPsrcaddr resource agent</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="" />
|
|
</parameter>
|
|
</parameters>
|
|
|
|
<actions>
|
|
<action name="start" timeout="40" />
|
|
<action name="stop" timeout="40" />
|
|
<action name="stop" timeout="20" />
|
|
<action name="monitor" depth="0" timeout="10" interval="10" start-delay="10" />
|
|
<action name="validate-all" timeout="5" />
|
|
<action name="meta-data" timeout="5" />
|
|
</actions>
|
|
</resource-agent>
|
|
END
|
|
}
|
|
|
|
errorexit() {
|
|
ocf_log err "$*"
|
|
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="[`echo -en ' \t'`]"
|
|
OCTET="[0-9]\{1,3\}"
|
|
IPADDR="\($OCTET\.\)\{3\}$OCTET"
|
|
SRCCLAUSE="src$WS$WS*\($IPADDR\)"
|
|
MATCHROUTE="\(.*${WS}\)\($SRCCLAUSE\)\($WS.*\|$\)"
|
|
|
|
srca_read() {
|
|
# Capture the default route - doublequotes prevent word splitting...
|
|
DEFROUTE="`$CMDSHOW`" || errorexit "command '$CMDSHOW' failed"
|
|
|
|
# ... so we can make sure there is only 1 default route
|
|
[ 1 -eq `echo "$DEFROUTE" | wc -l` ] || \
|
|
errorexit "more than 1 default route exists"
|
|
|
|
# But there might still be no default route
|
|
[ -z "$DEFROUTE" ] && errorexit "no default route exists"
|
|
|
|
# Sed out the source ip address if it exists
|
|
SRCIP=`echo $DEFROUTE | sed -n "s/$MATCHROUTE/\3/p"`
|
|
|
|
# and what remains after stripping out the source ip address clause
|
|
ROUTE_WO_SRC=`echo $DEFROUTE | sed "s/$MATCHROUTE/\1\5/"`
|
|
|
|
[ -z "$SRCIP" ] && return 1
|
|
[ $SRCIP = $1 ] && return 0
|
|
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
|
|
|
|
$CMDCHANGE $ROUTE_WO_SRC src $1 || \
|
|
errorexit "command '$CMDCHANGE $ROUTE_WO_SRC src $1' failed"
|
|
|
|
return $?
|
|
}
|
|
|
|
#
|
|
# 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 default 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"
|
|
|
|
$CMDCHANGE $ROUTE_WO_SRC || \
|
|
errorexit "command '$CMDCHANGE $ROUTE_WO_SRC' failed"
|
|
|
|
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_log err "IP address [$ip] is a loopback address, thus can not be preferred source address"
|
|
exit $OCF_ERR_ARGS
|
|
fi
|
|
else
|
|
true
|
|
fi
|
|
# return $OCF_SUCCESS
|
|
;;
|
|
*) #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() {
|
|
|
|
$IFCONFIG $IFCONFIG_A_OPT |
|
|
while read ifname linkstuff
|
|
do
|
|
: Read gave us ifname = $ifname
|
|
|
|
read inet addr junk
|
|
: Read gave us inet = $inet addr = $addr
|
|
|
|
while
|
|
read line && [ "X$line" != "X" ]
|
|
do
|
|
: Nothing
|
|
done
|
|
|
|
case "$SYSTYPE" in
|
|
*BSD)
|
|
$IFCONFIG | grep "$BASEIP" -B`$IFCONFIG | grep -c inet` | grep "UP," | cut -d ":" -f 1
|
|
return 0;;
|
|
*)
|
|
: "comparing $BASEIP to $addr (from ifconfig)"
|
|
case $addr in
|
|
addr:$BASEIP) echo $ifname; return $OCF_SUCCESS;;
|
|
$BASEIP) echo $ifname; return $OCF_SUCCESS;;
|
|
esac
|
|
continue;;
|
|
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() {
|
|
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"`
|
|
# echo $IF
|
|
if [ -z "$IF" ]; then
|
|
return $OCF_NOT_RUNNING
|
|
fi
|
|
|
|
case $IF in
|
|
lo*)
|
|
ocf_log err "IP address [$BASEIP] is served by loopback, thus can not be preferred source address"
|
|
exit $OCF_ERR_ARGS
|
|
;;
|
|
*)return $OCF_SUCCESS;;
|
|
esac
|
|
;;
|
|
|
|
*)
|
|
if [ -z "$IF" ]; then
|
|
return $OCF_NOT_RUNNING
|
|
else
|
|
return $OCF_SUCCESS
|
|
fi;;
|
|
esac
|
|
}
|
|
|
|
|
|
srca_validate_all() {
|
|
|
|
check_binary $AWK
|
|
check_binary $IPROUTE
|
|
check_binary $IFCONFIG
|
|
|
|
# The IP address should be in good shape
|
|
if CheckIP "$ipaddress"; then
|
|
:
|
|
else
|
|
ocf_log err "Invalid IP address [$ipaddress]"
|
|
exit $OCF_ERR_ARGS
|
|
fi
|
|
|
|
# We should serve this IP address of course
|
|
if ip_status "$ipaddress"; then
|
|
:
|
|
else
|
|
ocf_log err "We are not serving [$ipaddress], hence can not make it a preferred source address"
|
|
exit $OCF_ERR_ARGS
|
|
fi
|
|
}
|
|
|
|
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
|
|
|
|
if
|
|
[ -z "$OCF_RESKEY_ipaddress" ]
|
|
then
|
|
# usage
|
|
ocf_log err "Please set OCF_RESKEY_ipaddress to the preferred source IP address!"
|
|
exit $OCF_ERR_ARGS
|
|
fi
|
|
|
|
ipaddress="$OCF_RESKEY_ipaddress"
|
|
|
|
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?
|
|
#
|