3 #################################
4 # interface event script for ctdb
5 # this adds/removes IPs from your
8 [ -n "$CTDB_BASE" ] || \
9 CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD")
11 . "${CTDB_BASE}/functions"
15 [ -z "$CTDB_PUBLIC_ADDRESSES" ] && {
16 CTDB_PUBLIC_ADDRESSES="${CTDB_BASE}/public_addresses"
19 [ ! -f "$CTDB_PUBLIC_ADDRESSES" ] && {
20 if [ "$1" = "init" ]; then
21 echo "No public addresses file found. Nothing to do for 10.interfaces"
26 # This sets $all_interfaces as a side-effect.
29 # Get all the interfaces listed in the public_addresses file
30 all_interfaces=$(sed -e "s/^[^\t ]*[\t ]*//" \
32 -e "s/[\t ]*$//" "$CTDB_PUBLIC_ADDRESSES")
34 # Add some special interfaces if they're defined
35 [ "$CTDB_PUBLIC_INTERFACE" ] && all_interfaces="$CTDB_PUBLIC_INTERFACE $all_interfaces"
37 # Get the interfaces for which CTDB has public IPs configured.
38 # That is, for all but the 1st line, get the 1st field.
39 ctdb_ifaces=$($CTDB -X ifaces | sed -e '1d' -e 's@^|@@' -e 's@|.*@@')
41 # Add $ctdb_interfaces and uniquify
42 all_interfaces=$(echo $all_interfaces $ctdb_ifaces | tr ' ' '\n' | sort -u)
49 down_interfaces_found=false
50 up_interfaces_found=false
52 # Note that this loop must not exit early. It must process
53 # all interfaces so that the correct state for each interface
54 # is set in CTDB using setifacelink.
55 for _iface in $all_interfaces ; do
56 if interface_monitor "$_iface" ; then
57 up_interfaces_found=true
58 $CTDB setifacelink "$_iface" up >/dev/null 2>&1
60 down_interfaces_found=true
61 $CTDB setifacelink "$_iface" down >/dev/null 2>&1
65 if ! $down_interfaces_found ; then
69 if ! $up_interfaces_found ; then
73 if [ "$CTDB_PARTIALLY_ONLINE_INTERFACES" != "yes" ]; then
80 # Sets: iface, ip, maskbits
81 get_iface_ip_maskbits ()
87 set -- $(ip_maskbits_iface "$ip")
92 if [ "$iface" != "$_iface_in" ] ; then
94 'WARNING: Public IP %s hosted on interface %s but VNN says %s\n' \
95 "$ip" "$iface" "$_iface_in"
97 if [ "$maskbits" != "$_maskbits_in" ] ; then
99 'WARNING: Public IP %s has %s bit netmask but VNN says %s\n' \
100 "$ip" "$maskbits" "$_maskbits_in"
103 die "ERROR: Unable to determine interface for IP ${ip}"
113 *:*) _family="inet6" ;;
117 # Extra delete copes with previously killed script
118 iptables_wrapper "$_family" \
119 -D INPUT -i "$_iface" -d "$_ip" -j DROP 2>/dev/null
120 iptables_wrapper "$_family" \
121 -I INPUT -i "$_iface" -d "$_ip" -j DROP
130 *:*) _family="inet6" ;;
134 iptables_wrapper "$_family" \
135 -D INPUT -i "$_iface" -d "$_ip" -j DROP 2>/dev/null
142 # make sure that we only respond to ARP messages from the NIC where
143 # a particular ip address is associated.
144 get_proc sys/net/ipv4/conf/all/arp_filter >/dev/null 2>&1 && {
145 set_proc sys/net/ipv4/conf/all/arp_filter 1
148 _promote="sys/net/ipv4/conf/all/promote_secondaries"
149 get_proc "$_promote" >/dev/null 2>&1 || \
150 die "Public IPs only supported if promote_secondaries is available"
152 # make sure we drop any ips that might still be held if
153 # previous instance of ctdb got killed with -9 or similar
166 add_ip_to_iface "$iface" "$ip" "$maskbits" || {
170 # In case a previous "releaseip" for this IP was killed...
171 ip_unblock "$ip" "$iface"
177 # releasing an IP is a bit more complex than it seems. Once the IP
178 # is released, any open tcp connections to that IP on this host will end
179 # up being stuck. Some of them (such as NFS connections) will be unkillable
180 # so we need to use the killtcp ctdb function to kill them off. We also
181 # need to make sure that no new connections get established while we are
182 # doing this! So what we do is this:
183 # 1) firewall this IP, so no new external packets arrive for it
184 # 2) find existing connections, and kill them
185 # 3) remove the IP from the interface
186 # 4) remove the firewall rule
188 get_iface_ip_maskbits "$@"
190 ip_block "$ip" "$iface"
192 kill_tcp_connections "$iface" "$ip"
194 delete_ip_from_iface "$iface" "$ip" "$maskbits" || {
195 ip_unblock "$ip" "$iface"
199 ip_unblock "$ip" "$iface"
205 # moving an IP is a bit more complex than it seems.
206 # First we drop all traffic on the old interface.
207 # Then we try to add the ip to the new interface and before
208 # we finally remove it from the old interface.
210 # 1) firewall this IP, so no new external packets arrive for it
211 # 2) remove the IP from the old interface (and new interface, to be sure)
212 # 3) add the IP to the new interface
213 # 4) remove the firewall rule
214 # 5) use ctdb gratiousarp to propagate the new mac address
215 # 6) use netstat -tn to find existing connections, and tickle them
221 get_iface_ip_maskbits "$_oiface" "$_ip" "$_maskbits"
224 ip_block "$ip" "$oiface"
226 delete_ip_from_iface "$oiface" "$ip" "$maskbits" 2>/dev/null
227 delete_ip_from_iface "$niface" "$ip" "$maskbits" 2>/dev/null
229 add_ip_to_iface "$niface" "$ip" "$maskbits" || {
230 ip_unblock "$ip" "$oiface"
234 ip_unblock "$ip" "$oiface"
238 # propagate the new mac address
239 $CTDB gratiousarp "$ip" "$niface"
241 # tickle all existing connections, so that dropped packets
242 # are retransmited and the tcp streams work
243 tickle_tcp_connections "$ip"
247 monitor_interfaces || exit 1
250 ctdb_standard_event_handler "$@"