1d5aaa4d3aad52780a83b22c570c332cc08dd22a
[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 ]*//" -e "s/,/ /g" -e "s/[\t ]*$//" $CTDB_PUBLIC_ADDRESSES)
31
32     # Add some special interfaces if they're defined
33     [ "$CTDB_PUBLIC_INTERFACE" ] && all_interfaces="$CTDB_PUBLIC_INTERFACE $all_interfaces"
34
35     # Get the interfaces for which CTDB has public IPs configured.
36     # That is, for all but the 1st line, get the 1st field.
37     ctdb_ifaces=$($CTDB -X ifaces | sed -e '1d' -e 's@^|@@' -e 's@|.*@@')
38
39     # Add $ctdb_interfaces and uniquify
40     all_interfaces=$(echo $all_interfaces $ctdb_ifaces | tr ' ' '\n' | sort -u)
41 }
42
43 monitor_interfaces()
44 {
45         get_all_interfaces
46
47         down_interfaces_found=false
48         up_interfaces_found=false
49
50         # Note that this loop must not exit early.  It must process
51         # all interfaces so that the correct state for each interface
52         # is set in CTDB using setifacelink.
53         for _iface in $all_interfaces ; do
54                 if interface_monitor "$_iface" ; then
55                         up_interfaces_found=true
56                         $CTDB setifacelink "$_iface" up >/dev/null 2>&1
57                 else
58                         down_interfaces_found=true
59                         $CTDB setifacelink "$_iface" down >/dev/null 2>&1
60                 fi
61         done
62
63         if ! $down_interfaces_found ; then
64                 return 0
65         fi
66
67         if ! $up_interfaces_found ; then
68                 return 1
69         fi
70
71         if [ "$CTDB_PARTIALLY_ONLINE_INTERFACES" != "yes" ]; then
72                 return 1
73         fi
74
75         return 0
76 }
77
78 # Sets: iface, ip, maskbits
79 get_iface_ip_maskbits ()
80 {
81     _iface_in="$1"
82     ip="$2"
83     _maskbits_in="$3"
84
85     set -- $(ip_maskbits_iface "$ip")
86     if [ -n "$1" ] ; then
87         maskbits="$1"
88         iface="$2"
89
90         if [ "$iface" != "$_iface_in" ] ; then
91             printf \
92                 'WARNING: Public IP %s hosted on interface %s but VNN says %s\n' \
93                 "$ip" "$iface" "$_iface_in"
94         fi
95         if [ "$maskbits" != "$_maskbits_in" ] ; then
96             printf \
97                 'WARNING: Public IP %s has %s bit netmask but VNN says %s\n' \
98                     "$ip" "$maskbits" "$_maskbits_in"
99         fi
100     else
101         die "ERROR: Unable to determine interface for IP ${ip}"
102     fi
103 }
104
105 ip_block ()
106 {
107         _ip="$1"
108         _iface="$2"
109
110         case "$_ip" in
111         *:*) _family="inet6" ;;
112         *)   _family="inet"  ;;
113         esac
114
115         # Extra delete copes with previously killed script
116         iptables_wrapper "$_family" \
117                          -D INPUT -i "$_iface" -d "$_ip" -j DROP 2>/dev/null
118         iptables_wrapper "$_family" \
119                          -I INPUT -i "$_iface" -d "$_ip" -j DROP
120 }
121
122 ip_unblock ()
123 {
124         _ip="$1"
125         _iface="$2"
126
127         case "$_ip" in
128         *:*) _family="inet6" ;;
129         *)   _family="inet"  ;;
130         esac
131
132         iptables_wrapper "$_family" \
133                          -D INPUT -i "$_iface" -d "$_ip" -j DROP 2>/dev/null
134 }
135
136 ctdb_check_args "$@"
137
138 case "$1" in
139     init)
140         # make sure that we only respond to ARP messages from the NIC where
141         # a particular ip address is associated.
142         get_proc sys/net/ipv4/conf/all/arp_filter >/dev/null 2>&1 && {
143             set_proc sys/net/ipv4/conf/all/arp_filter 1
144         }
145
146         _promote="sys/net/ipv4/conf/all/promote_secondaries"
147         get_proc "$_promote" >/dev/null 2>&1 || \
148             die "Public IPs only supported if promote_secondaries is available"
149
150         # make sure we drop any ips that might still be held if
151         # previous instance of ctdb got killed with -9 or similar
152         drop_all_public_ips
153         ;;
154
155     startup)
156         monitor_interfaces
157         ;;
158
159     takeip)
160         iface=$2
161         ip=$3
162         maskbits=$4
163
164         add_ip_to_iface $iface $ip $maskbits || {
165                 exit 1;
166         }
167
168         # In case a previous "releaseip" for this IP was killed...
169         ip_unblock "$ip" "$iface"
170
171         flush_route_cache
172         ;;
173
174     releaseip)
175         # releasing an IP is a bit more complex than it seems. Once the IP
176         # is released, any open tcp connections to that IP on this host will end
177         # up being stuck. Some of them (such as NFS connections) will be unkillable
178         # so we need to use the killtcp ctdb function to kill them off. We also
179         # need to make sure that no new connections get established while we are
180         # doing this! So what we do is this:
181         # 1) firewall this IP, so no new external packets arrive for it
182         # 2) find existing connections, and kill them
183         # 3) remove the IP from the interface
184         # 4) remove the firewall rule
185         shift
186         get_iface_ip_maskbits "$@"
187
188         ip_block "$ip" "$iface"
189
190         kill_tcp_connections "$iface" "$ip"
191
192         delete_ip_from_iface $iface $ip $maskbits || {
193                 ip_unblock "$ip" "$iface"
194                 exit 1
195         }
196
197         ip_unblock "$ip" "$iface"
198
199         flush_route_cache
200         ;;
201
202     updateip)
203         # moving an IP is a bit more complex than it seems.
204         # First we drop all traffic on the old interface.
205         # Then we try to add the ip to the new interface and before
206         # we finally remove it from the old interface.
207         #
208         # 1) firewall this IP, so no new external packets arrive for it
209         # 2) remove the IP from the old interface (and new interface, to be sure)
210         # 3) add the IP to the new interface
211         # 4) remove the firewall rule
212         # 5) use ctdb gratiousarp to propagate the new mac address
213         # 6) use netstat -tn to find existing connections, and tickle them
214         _oiface=$2
215         niface=$3
216         _ip=$4
217         _maskbits=$5
218
219         get_iface_ip_maskbits "$_oiface" "$_ip" "$_maskbits"
220         oiface="$iface"
221
222         ip_block "$ip" "$oiface"
223
224         delete_ip_from_iface $oiface $ip $maskbits 2>/dev/null
225         delete_ip_from_iface $niface $ip $maskbits 2>/dev/null
226
227         add_ip_to_iface $niface $ip $maskbits || {
228                 ip_unblock "$ip" "$oiface"
229                 exit 1
230         }
231
232         ip_unblock "$ip" "$oiface"
233
234         flush_route_cache
235
236         # propagate the new mac address
237         $CTDB gratiousarp $ip $niface
238
239         # tickle all existing connections, so that dropped packets
240         # are retransmited and the tcp streams work
241         tickle_tcp_connections $ip
242         ;;
243
244     monitor)
245         monitor_interfaces || exit 1
246         ;;
247     *)
248         ctdb_standard_event_handler "$@"
249         ;;
250 esac
251
252 exit 0