Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F3155337
IPsrcaddr
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
18 KB
Referenced Files
None
Subscribers
None
IPsrcaddr
View Options
#!/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_pref_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_pref=${OCF_RESKEY_pref_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}";
echo "$OCF_RESKEY_ipaddress" | grep -q ":" && FAMILY="inet6" || FAMILY="inet"
[ "$FAMILY" = "inet6" ] && [ "$OCF_RESKEY_destination" = "0.0.0.0/0" ] && OCF_RESKEY_destination="::/0"
CMDSHOW="$IP2UTIL -f $FAMILY route show $TABLE to exact $OCF_RESKEY_destination"
CMDCHANGE="$IP2UTIL -f $FAMILY route change to "
if [ "$OCF_RESKEY_destination" != "0.0.0.0/0" ] && [ "$OCF_RESKEY_destination" != "::/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="pref">
<longdesc lang="en">
IPv6 route preference (low, medium or high). Only needed if incorrect pref value is used.
</longdesc>
<shortdesc lang="en">IPv6 route preference.</shortdesc>
<content type="string" default="${OCF_RESKEY_pref_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:]]"
case "$FAMILY" in
inet)
GROUP="[0-9]\{1,3\}"
IPADDR="\($GROUP\.\)\{3\}$GROUP"
;;
inet6)
GROUP="[0-9a-f]\{0,4\}"
IPADDR="\($GROUP\:\)\{0,\}$GROUP"
;;
esac
SRCCLAUSE="src$WS$WS*\($IPADDR\)"
MATCHROUTE="\(.*${WS}\)proto [^ ]\+\(.*${WS}\)\($SRCCLAUSE\)\($WS.*\|$\)"
METRICCLAUSE=".*\(metric$WS[^ ]\+\).*"
PROTOCLAUSE=".*\(proto$WS[^ ]\+\).*"
PREFCLAUSE=".*\(pref$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" ] || [ "$OCF_RESKEY_destination" = "::/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/\4/p"`
# and what remains after stripping out the source ip address clause
ROUTE_WO_SRC=`echo $ROUTE | sed "s/$MATCHROUTE/\1\2\6/"`
# using "src <ip>" only returns output if there's a match
if [ "$OCF_RESKEY_destination" != "0.0.0.0/0" ] && [ "$OCF_RESKEY_destination" != "::/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
# NetworkManager manages routes with proto static/kernel
[ -z "$OCF_RESKEY_proto" ] && echo "$PROTO" | grep -q "proto \(kernel\|static\)" && PROTO="proto keepalived"
$IP2UTIL route replace $TABLE $NETWORK dev $INTERFACE $PROTO src $1 $METRIC $PREF || \
errorexit "command 'ip route replace $TABLE $NETWORK dev $INTERFACE $PROTO src $1 $METRIC $PREF' failed"
if [ "$OCF_RESKEY_destination" = "0.0.0.0/0" ] || [ "$OCF_RESKEY_destination" = "::/0" ]; then
$CMDCHANGE $ROUTE_WO_SRC dev $INTERFACE $PROTO src $1 || \
errorexit "command '$CMDCHANGE $ROUTE_WO_SRC dev $INTERFACE $PROTO 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"
[ "$FAMILY" = "inet" ] && OPTS="$OPTS src $PRIMARY_IP"
$IP2UTIL route replace $TABLE $NETWORK dev $INTERFACE $OPTS $METRIC $PREF || \
errorexit "command 'ip route replace $TABLE $NETWORK dev $INTERFACE $OPTS $METRIC $PREF' failed"
if [ "$OCF_RESKEY_destination" = "0.0.0.0/0" ] || [ "$OCF_RESKEY_destination" = "::/0" ]; then
$CMDCHANGE $ROUTE_WO_SRC dev $INTERFACE proto static || \
errorexit "command '$CMDCHANGE $ROUTE_WO_SRC dev $INTERFACE proto static' 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 with ".", 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 :)
}
CheckIP6() {
ip="$1"
case $ip in
*[!0-9a-f:]*) #got invalid char
false;;
*:::*) # more than 2 consecutive ":", which is invalid
false;;
*::*::*) # more than 1 "::", which is invalid
false;;
esac
}
#
# 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 $FAMILY 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
:
elif CheckIP6 "$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}'`
case "$FAMILY" in
inet)
LISTCMD="$IP2UTIL -f $FAMILY route list dev $INTERFACE scope link $PROTO match $ipaddress"
;;
inet6)
LISTCMD="$IP2UTIL -f $FAMILY route list dev $INTERFACE $PROTO match $ipaddress"
;;
esac
LISTROUTE=`$LISTCMD`
[ -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" ] || [ "$FAMILY" = "inet6" ]; then
METRIC=`echo $LISTROUTE | sed -n "s/$METRICCLAUSE/\1/p"`
else
METRIC=""
fi
if [ "$FAMILY" = "inet6" ]; then
if [ -z "$OCF_RESKEY_pref" ]; then
PREF=`echo $LISTROUTE | sed -n "s/$PREFCLAUSE/\1/p"`
else
PREF="pref $OCF_RESKEY_pref"
fi
fi
if [ "$OCF_RESKEY_destination" = "0.0.0.0/0" ] || [ "$OCF_RESKEY_destination" = "::/0" ] ;then
NETWORK=`echo $LISTROUTE | grep -m 1 -o '^[^ ]*'`
if [ -z "$NETWORK" ]; then
err_str="command '$LISTCMD' 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?
#
File Metadata
Details
Attached
Mime Type
text/x-shellscript
Expires
Wed, Feb 26, 9:22 PM (3 h, 37 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1465785
Default Alt Text
IPsrcaddr (18 KB)
Attached To
Mode
rR Resource Agents
Attached
Detach File
Event Timeline
Log In to Comment