ctdb-scripts: Quote some variable expansions
[nivanova/samba-autobuild/.git] / ctdb / config / events.d / 10.interface
1 #!/bin/sh
2
3 #################################
4 # interface event script for ctdb
5 # this adds/removes IPs from your 
6 # public interface
7
8 [ -n "$CTDB_BASE" ] || \
9     CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD")
10
11 . "${CTDB_BASE}/functions"
12
13 loadconfig
14
15 [ -z "$CTDB_PUBLIC_ADDRESSES" ] && {
16         CTDB_PUBLIC_ADDRESSES="${CTDB_BASE}/public_addresses"
17 }
18
19 [ ! -f "$CTDB_PUBLIC_ADDRESSES" ] && {
20         if [ "$1" = "init" ]; then
21                 echo "No public addresses file found. Nothing to do for 10.interfaces"
22         fi
23         exit 0
24 }
25
26 # This sets $all_interfaces as a side-effect.
27 get_all_interfaces ()
28 {
29     # Get all the interfaces listed in the public_addresses file
30     all_interfaces=$(sed -e "s/^[^\t ]*[\t ]*//" \
31                          -e "s/,/ /g" \
32                          -e "s/[\t ]*$//" "$CTDB_PUBLIC_ADDRESSES")
33
34     # Add some special interfaces if they're defined
35     [ "$CTDB_PUBLIC_INTERFACE" ] && all_interfaces="$CTDB_PUBLIC_INTERFACE $all_interfaces"
36
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@|.*@@')
40
41     # Add $ctdb_interfaces and uniquify
42     all_interfaces=$(echo $all_interfaces $ctdb_ifaces | tr ' ' '\n' | sort -u)
43 }
44
45 monitor_interfaces()
46 {
47         get_all_interfaces
48
49         down_interfaces_found=false
50         up_interfaces_found=false
51
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
59                 else
60                         down_interfaces_found=true
61                         $CTDB setifacelink "$_iface" down >/dev/null 2>&1
62                 fi
63         done
64
65         if ! $down_interfaces_found ; then
66                 return 0
67         fi
68
69         if ! $up_interfaces_found ; then
70                 return 1
71         fi
72
73         if [ "$CTDB_PARTIALLY_ONLINE_INTERFACES" != "yes" ]; then
74                 return 1
75         fi
76
77         return 0
78 }
79
80 # Sets: iface, ip, maskbits
81 get_iface_ip_maskbits ()
82 {
83     _iface_in="$1"
84     ip="$2"
85     _maskbits_in="$3"
86
87     set -- $(ip_maskbits_iface "$ip")
88     if [ -n "$1" ] ; then
89         maskbits="$1"
90         iface="$2"
91
92         if [ "$iface" != "$_iface_in" ] ; then
93             printf \
94                 'WARNING: Public IP %s hosted on interface %s but VNN says %s\n' \
95                 "$ip" "$iface" "$_iface_in"
96         fi
97         if [ "$maskbits" != "$_maskbits_in" ] ; then
98             printf \
99                 'WARNING: Public IP %s has %s bit netmask but VNN says %s\n' \
100                     "$ip" "$maskbits" "$_maskbits_in"
101         fi
102     else
103         die "ERROR: Unable to determine interface for IP ${ip}"
104     fi
105 }
106
107 ip_block ()
108 {
109         _ip="$1"
110         _iface="$2"
111
112         case "$_ip" in
113         *:*) _family="inet6" ;;
114         *)   _family="inet"  ;;
115         esac
116
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
122 }
123
124 ip_unblock ()
125 {
126         _ip="$1"
127         _iface="$2"
128
129         case "$_ip" in
130         *:*) _family="inet6" ;;
131         *)   _family="inet"  ;;
132         esac
133
134         iptables_wrapper "$_family" \
135                          -D INPUT -i "$_iface" -d "$_ip" -j DROP 2>/dev/null
136 }
137
138 ctdb_check_args "$@"
139
140 case "$1" in
141     init)
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
146         }
147
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"
151
152         # make sure we drop any ips that might still be held if
153         # previous instance of ctdb got killed with -9 or similar
154         drop_all_public_ips
155         ;;
156
157     startup)
158         monitor_interfaces
159         ;;
160
161     takeip)
162         iface=$2
163         ip=$3
164         maskbits=$4
165
166         add_ip_to_iface "$iface" "$ip" "$maskbits" || {
167                 exit 1;
168         }
169
170         # In case a previous "releaseip" for this IP was killed...
171         ip_unblock "$ip" "$iface"
172
173         flush_route_cache
174         ;;
175
176     releaseip)
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
187         shift
188         get_iface_ip_maskbits "$@"
189
190         ip_block "$ip" "$iface"
191
192         kill_tcp_connections "$iface" "$ip"
193
194         delete_ip_from_iface "$iface" "$ip" "$maskbits" || {
195                 ip_unblock "$ip" "$iface"
196                 exit 1
197         }
198
199         ip_unblock "$ip" "$iface"
200
201         flush_route_cache
202         ;;
203
204     updateip)
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.
209         #
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
216         _oiface=$2
217         niface=$3
218         _ip=$4
219         _maskbits=$5
220
221         get_iface_ip_maskbits "$_oiface" "$_ip" "$_maskbits"
222         oiface="$iface"
223
224         ip_block "$ip" "$oiface"
225
226         delete_ip_from_iface "$oiface" "$ip" "$maskbits" 2>/dev/null
227         delete_ip_from_iface "$niface" "$ip" "$maskbits" 2>/dev/null
228
229         add_ip_to_iface "$niface" "$ip" "$maskbits" || {
230                 ip_unblock "$ip" "$oiface"
231                 exit 1
232         }
233
234         ip_unblock "$ip" "$oiface"
235
236         flush_route_cache
237
238         # propagate the new mac address
239         $CTDB gratiousarp "$ip" "$niface"
240
241         # tickle all existing connections, so that dropped packets
242         # are retransmited and the tcp streams work
243         tickle_tcp_connections "$ip"
244         ;;
245
246     monitor)
247         monitor_interfaces || exit 1
248         ;;
249     *)
250         ctdb_standard_event_handler "$@"
251         ;;
252 esac
253
254 exit 0