2007-06-04 09:09:03 +04:00
#!/bin/sh
#################################
# interface event script for ctdb
# this adds/removes IPs from your
# public interface
2013-01-03 08:26:12 +04:00
[ -n "$CTDB_BASE" ] || \
2016-06-29 10:36:05 +03:00
CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD")
. "${CTDB_BASE}/functions"
2013-01-03 08:26:12 +04:00
2018-02-20 04:56:42 +03:00
load_script_options
2007-06-04 09:09:03 +04:00
2018-03-08 07:11:51 +03:00
ctdb_public_addresses="${CTDB_BASE}/public_addresses"
2007-06-04 09:09:03 +04:00
2018-03-08 07:11:51 +03:00
if [ ! -f "$ctdb_public_addresses" ]; then
if [ "$1" = "init" ] ; then
echo "No public addresses file found"
2011-03-26 23:32:34 +03:00
fi
2007-09-04 03:50:07 +04:00
exit 0
2018-03-08 07:11:51 +03:00
fi
2007-06-04 09:09:03 +04:00
2011-07-05 11:18:30 +04:00
# This sets $all_interfaces as a side-effect.
get_all_interfaces ()
2009-12-14 13:59:45 +03:00
{
2011-07-05 11:18:30 +04:00
# Get all the interfaces listed in the public_addresses file
2016-06-29 11:11:44 +03:00
all_interfaces=$(sed -e "s/^[^\t ]*[\t ]*//" \
-e "s/,/ /g" \
2018-03-08 07:11:51 +03:00
-e "s/[\t ]*$//" "$ctdb_public_addresses")
2009-12-14 13:59:45 +03:00
2013-07-12 06:33:36 +04:00
# Get the interfaces for which CTDB has public IPs configured.
# That is, for all but the 1st line, get the 1st field.
2016-06-08 13:37:00 +03:00
ctdb_ifaces=$($CTDB -X ifaces | sed -e '1d' -e 's@^|@@' -e 's@|.*@@')
2009-12-22 17:25:30 +03:00
2017-07-05 16:37:04 +03:00
# Add $ctdb_ifaces and make $all_interfaces unique
2016-07-06 10:31:51 +03:00
# Use word splitting to squash whitespace
# shellcheck disable=SC2086
2011-07-05 11:18:30 +04:00
all_interfaces=$(echo $all_interfaces $ctdb_ifaces | tr ' ' '\n' | sort -u)
}
2009-12-22 17:25:30 +03:00
2015-12-18 07:30:18 +03:00
monitor_interfaces()
{
get_all_interfaces
2016-01-15 13:22:16 +03:00
down_interfaces_found=false
2015-12-18 07:30:18 +03:00
up_interfaces_found=false
2009-12-14 13:59:45 +03:00
2015-12-18 07:30:18 +03:00
# Note that this loop must not exit early. It must process
# all interfaces so that the correct state for each interface
2016-01-15 13:20:26 +03:00
# is set in CTDB using setifacelink.
2015-12-18 07:30:18 +03:00
for _iface in $all_interfaces ; do
if interface_monitor "$_iface" ; then
2016-01-15 13:20:26 +03:00
up_interfaces_found=true
2016-06-08 13:37:00 +03:00
$CTDB setifacelink "$_iface" up >/dev/null 2>&1
2015-12-18 07:30:18 +03:00
else
2016-01-15 13:22:16 +03:00
down_interfaces_found=true
2016-06-08 13:37:00 +03:00
$CTDB setifacelink "$_iface" down >/dev/null 2>&1
2015-12-18 07:30:18 +03:00
fi
2009-12-14 13:59:45 +03:00
done
2016-01-15 13:22:16 +03:00
if ! $down_interfaces_found ; then
2015-12-16 11:18:49 +03:00
return 0
2016-01-12 20:59:15 +03:00
fi
if ! $up_interfaces_found ; then
return 1
fi
if [ "$CTDB_PARTIALLY_ONLINE_INTERFACES" != "yes" ]; then
2015-12-16 11:18:49 +03:00
return 1
fi
2016-01-12 20:59:15 +03:00
return 0
2009-12-14 13:59:45 +03:00
}
2016-05-13 13:24:52 +03:00
# Sets: iface, ip, maskbits
get_iface_ip_maskbits ()
2014-11-21 06:46:00 +03:00
{
_iface_in="$1"
ip="$2"
_maskbits_in="$3"
2016-07-06 10:31:51 +03:00
# Intentional word splitting here
# shellcheck disable=SC2046
2014-11-21 06:46:00 +03:00
set -- $(ip_maskbits_iface "$ip")
if [ -n "$1" ] ; then
maskbits="$1"
iface="$2"
if [ "$iface" != "$_iface_in" ] ; then
printf \
'WARNING: Public IP %s hosted on interface %s but VNN says %s\n' \
"$ip" "$iface" "$_iface_in"
fi
if [ "$maskbits" != "$_maskbits_in" ] ; then
printf \
'WARNING: Public IP %s has %s bit netmask but VNN says %s\n' \
"$ip" "$maskbits" "$_maskbits_in"
fi
else
die "ERROR: Unable to determine interface for IP ${ip}"
fi
}
2016-05-13 12:43:05 +03:00
ip_block ()
{
_ip="$1"
_iface="$2"
case "$_ip" in
*:*) _family="inet6" ;;
*) _family="inet" ;;
esac
# Extra delete copes with previously killed script
iptables_wrapper "$_family" \
-D INPUT -i "$_iface" -d "$_ip" -j DROP 2>/dev/null
iptables_wrapper "$_family" \
-I INPUT -i "$_iface" -d "$_ip" -j DROP
}
ip_unblock ()
{
_ip="$1"
_iface="$2"
case "$_ip" in
*:*) _family="inet6" ;;
*) _family="inet" ;;
esac
iptables_wrapper "$_family" \
-D INPUT -i "$_iface" -d "$_ip" -j DROP 2>/dev/null
}
2011-08-23 10:36:19 +04:00
ctdb_check_args "$@"
2015-12-18 05:16:27 +03:00
case "$1" in
2016-07-06 07:44:14 +03:00
init)
2007-09-08 02:09:02 +04:00
# make sure that we only respond to ARP messages from the NIC where
# a particular ip address is associated.
2011-06-28 09:36:28 +04:00
get_proc sys/net/ipv4/conf/all/arp_filter >/dev/null 2>&1 && {
set_proc sys/net/ipv4/conf/all/arp_filter 1
2007-09-12 07:23:36 +04:00
}
2014-01-28 07:41:25 +04:00
_promote="sys/net/ipv4/conf/all/promote_secondaries"
get_proc "$_promote" >/dev/null 2>&1 || \
die "Public IPs only supported if promote_secondaries is available"
2015-05-08 08:18:48 +03:00
# make sure we drop any ips that might still be held if
# previous instance of ctdb got killed with -9 or similar
drop_all_public_ips
2007-06-04 09:09:03 +04:00
;;
2016-07-06 07:44:14 +03:00
startup)
2009-12-22 17:25:30 +03:00
monitor_interfaces
;;
2016-07-06 07:44:14 +03:00
takeip)
2009-12-01 09:43:47 +03:00
iface=$2
ip=$3
maskbits=$4
2007-06-04 09:09:03 +04:00
2016-06-29 11:11:44 +03:00
add_ip_to_iface "$iface" "$ip" "$maskbits" || {
2009-12-18 13:08:22 +03:00
exit 1;
2007-06-04 09:09:03 +04:00
}
2009-12-18 13:08:22 +03:00
2016-05-13 12:43:05 +03:00
# In case a previous "releaseip" for this IP was killed...
ip_unblock "$ip" "$iface"
2007-06-04 09:09:03 +04:00
2014-11-21 06:46:00 +03:00
flush_route_cache
2007-06-04 09:09:03 +04:00
;;
2016-07-06 07:44:14 +03:00
releaseip)
2007-09-13 04:24:48 +04:00
# releasing an IP is a bit more complex than it seems. Once the IP
# is released, any open tcp connections to that IP on this host will end
# up being stuck. Some of them (such as NFS connections) will be unkillable
# so we need to use the killtcp ctdb function to kill them off. We also
2016-05-13 12:43:05 +03:00
# need to make sure that no new connections get established while we are
2007-09-13 04:24:48 +04:00
# doing this! So what we do is this:
# 1) firewall this IP, so no new external packets arrive for it
2016-05-13 12:43:05 +03:00
# 2) find existing connections, and kill them
2007-09-13 04:24:48 +04:00
# 3) remove the IP from the interface
# 4) remove the firewall rule
2014-11-21 06:46:00 +03:00
shift
2016-05-13 13:24:52 +03:00
get_iface_ip_maskbits "$@"
2007-09-13 04:24:48 +04:00
2016-05-13 12:43:05 +03:00
ip_block "$ip" "$iface"
2016-03-11 07:27:10 +03:00
kill_tcp_connections "$iface" "$ip"
2007-09-14 05:56:40 +04:00
2016-06-29 11:11:44 +03:00
delete_ip_from_iface "$iface" "$ip" "$maskbits" || {
2016-05-13 12:43:05 +03:00
ip_unblock "$ip" "$iface"
2014-11-21 06:46:00 +03:00
exit 1
2007-09-14 05:56:40 +04:00
}
2009-12-18 13:08:22 +03:00
2016-05-13 12:43:05 +03:00
ip_unblock "$ip" "$iface"
2007-06-04 09:09:03 +04:00
2014-11-21 06:46:00 +03:00
flush_route_cache
2007-06-04 09:09:03 +04:00
;;
2016-07-06 07:44:14 +03:00
updateip)
2009-12-21 10:40:50 +03:00
# moving an IP is a bit more complex than it seems.
# First we drop all traffic on the old interface.
# Then we try to add the ip to the new interface and before
# we finally remove it from the old interface.
#
# 1) firewall this IP, so no new external packets arrive for it
2014-11-21 06:46:00 +03:00
# 2) remove the IP from the old interface (and new interface, to be sure)
# 3) add the IP to the new interface
2009-12-21 10:40:50 +03:00
# 4) remove the firewall rule
2017-01-16 05:38:50 +03:00
# 5) use ctdb gratarp to propagate the new mac address
2009-12-21 10:40:50 +03:00
# 6) use netstat -tn to find existing connections, and tickle them
2014-11-21 06:46:00 +03:00
_oiface=$2
2009-12-21 10:40:50 +03:00
niface=$3
2014-11-21 06:46:00 +03:00
_ip=$4
_maskbits=$5
2016-05-13 13:24:52 +03:00
get_iface_ip_maskbits "$_oiface" "$_ip" "$_maskbits"
2014-11-21 06:46:00 +03:00
oiface="$iface"
2009-12-21 10:40:50 +03:00
2016-07-29 12:29:23 +03:00
# Could check maskbits too. However, that should never change
# so we want to notice if it does.
if [ "$oiface" = "$niface" ] ; then
echo "Redundant \"updateip\" - ${ip} already on ${niface}"
exit 0
fi
2016-05-13 12:43:05 +03:00
ip_block "$ip" "$oiface"
2009-12-21 10:40:50 +03:00
2016-06-29 11:11:44 +03:00
delete_ip_from_iface "$oiface" "$ip" "$maskbits" 2>/dev/null
delete_ip_from_iface "$niface" "$ip" "$maskbits" 2>/dev/null
2009-12-21 10:40:50 +03:00
2016-06-29 11:11:44 +03:00
add_ip_to_iface "$niface" "$ip" "$maskbits" || {
2016-05-13 12:43:05 +03:00
ip_unblock "$ip" "$oiface"
exit 1
2009-12-21 10:40:50 +03:00
}
2016-05-13 12:43:05 +03:00
ip_unblock "$ip" "$oiface"
2009-12-21 10:40:50 +03:00
2014-11-21 06:46:00 +03:00
flush_route_cache
2009-12-21 10:40:50 +03:00
# propagate the new mac address
2017-01-16 05:38:50 +03:00
$CTDB gratarp "$ip" "$niface"
2009-12-21 10:40:50 +03:00
# tickle all existing connections, so that dropped packets
# are retransmited and the tcp streams work
2016-06-29 11:11:44 +03:00
tickle_tcp_connections "$ip"
2009-12-21 10:40:50 +03:00
;;
2016-07-06 07:44:14 +03:00
monitor)
2011-06-28 10:27:01 +04:00
monitor_interfaces || exit 1
2007-06-06 06:08:42 +04:00
;;
2007-06-04 09:09:03 +04:00
esac
exit 0