a82e4295dd103a1c4717fd09a7cf5f2bf2ffe589
[obnox/samba/samba-obnox.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 . $CTDB_BASE/functions
9 loadconfig
10
11 [ -z "$CTDB_PUBLIC_ADDRESSES" ] && {
12         CTDB_PUBLIC_ADDRESSES=$CTDB_BASE/public_addresses
13 }
14
15 [ ! -f "$CTDB_PUBLIC_ADDRESSES" ] && {
16         exit 0
17 }
18
19 mark_up ()
20 {
21     ok=1
22     ctdb setifacelink $1 up >/dev/null 2>&1
23 }
24
25 mark_down ()
26 {
27     fail=1
28     ctdb setifacelink $1 down >/dev/null 2>&1
29 }
30
31 monitor_interfaces()
32 {
33         INTERFACES=`cat $CTDB_PUBLIC_ADDRESSES |
34                 sed -e "s/^[^\t ]*[\t ]*//" -e "s/,/ /g" -e "s/[\t ]*$//"`
35
36         [ "$CTDB_PUBLIC_INTERFACE" ] && INTERFACES="$CTDB_PUBLIC_INTERFACE $INTERFACES"
37         [ "$CTDB_NATGW_PUBLIC_IFACE" ] && INTERFACES="$CTDB_NATGW_PUBLIC_IFACE $INTERFACES"
38
39
40         # For all but the 1st line, get the 2nd last field with commas
41         # changes to spaces.
42         IFACES=`ctdb -Y ip -v | sed -e '1d' -e 's/:[^:]*:$//' -e 's/^.*://' -e 's/,/ /g'`
43
44         INTERFACES=`for IFACE in $INTERFACES $IFACES ; do echo $IFACE ; done | sort | uniq`
45
46         fail=0
47         ok=0
48         for IFACE in $INTERFACES ; do
49
50             ip addr show $IFACE 2>/dev/null >/dev/null || {
51                 echo Interface $IFACE does not exist but it is used by public addresses.
52                 continue
53             }
54
55             # These interfaces are sometimes bond devices
56             # When we use VLANs for bond interfaces, there will only
57             # be an entry in /proc for the underlying real interface
58             REALIFACE=`echo $IFACE |sed -e 's/\..*$//'`
59             bi=$(get_proc "net/bonding/$REALIFACE" 2>/dev/null) && {
60                 echo "$bi" | grep -q 'Currently Active Slave: None' && {
61                         echo "ERROR: No active slaves for bond device $REALIFACE"
62                         mark_down $IFACE
63                         continue;
64                 }
65                 echo "$bi" | grep -q '^MII Status: up' || {
66                         echo "ERROR: public network interface $REALIFACE is down"
67                         mark_down $IFACE
68                         continue;
69                 }
70                 echo "$bi" | grep -q '^Bonding Mode: IEEE 802.3ad Dynamic link aggregation' && {
71                         echo "$bi" | grep 'MII Status:' | tail -n +2 | grep -q '^MII Status: up' || {
72                                 echo No active slaves for 802.ad bond device $REALIFACE
73                                 mark_down $IFACE
74                                 continue
75                         }
76                 }
77                 mark_up $IFACE
78                 continue;
79             }
80
81             case $IFACE in
82             lo*)
83                 # loopback is always working
84                 mark_up $IFACE
85                 ;;
86             ib*)
87                 # we dont know how to test ib links
88                 mark_up $IFACE
89                 ;;
90             *)
91                 [ -z "$IFACE" ] || {
92                     [ "$(basename $(readlink /sys/class/net/$IFACE/device/driver) 2>/dev/null)" = virtio_net ] ||
93                     ethtool $IFACE | grep -q 'Link detected: yes' || {
94                         # On some systems, this is not successful when a
95                         # cable is plugged but the interface has not been
96                         # brought up previously. Bring the interface up and
97                         # try again...
98                         ip link set $IFACE up
99                         ethtool $IFACE | grep -q 'Link detected: yes' || {
100                             echo "ERROR: No link on the public network interface $IFACE"
101                             mark_down $IFACE
102                             continue
103                         }
104                     }
105                     mark_up $IFACE
106                 }
107                 ;;
108             esac
109
110         done
111
112         test x"$fail" = x"0" && {
113                 return 0;
114         }
115
116         test x"$ok" = x"1" && {
117                 return 2;
118         }
119
120         return 1;
121 }
122
123 case "$1" in 
124      #############################
125      # called when ctdbd starts up
126      init)
127         # make sure that we only respond to ARP messages from the NIC where
128         # a particular ip address is associated.
129         get_proc sys/net/ipv4/conf/all/arp_filter >/dev/null 2>&1 && {
130             set_proc sys/net/ipv4/conf/all/arp_filter 1
131         }
132         ;;
133
134      #############################
135      # called after ctdbd has done its initial recovery
136      # and we start the services to become healthy
137      startup)
138         # Assume all links are good initially
139         INTERFACES=`for IFACE in $INTERFACES ; do echo $IFACE ; done | sort | uniq`
140
141         for IFACE in $INTERFACES ; do
142                 ctdb setifacelink $IFACE down >/dev/null 2>/dev/null
143         done
144         
145         monitor_interfaces
146
147         ;;
148
149
150      ################################################
151      # called when ctdbd wants to claim an IP address
152      takeip)
153         if [ $# != 4 ]; then
154            echo "must supply interface, IP and maskbits"
155            exit 1
156         fi
157         iface=$2
158         ip=$3
159         maskbits=$4
160
161         add_ip_to_iface $iface $ip $maskbits || {
162                 exit 1;
163         }
164
165         # cope with the script being killed while we have the interface blocked
166         iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
167
168         # flush our route cache
169         set_proc sys/net/ipv4/route/flush 1
170         ;;
171
172
173      ##################################################
174      # called when ctdbd wants to release an IP address
175      releaseip)
176         if [ $# != 4 ]; then
177            echo "must supply interface, IP and maskbits"
178            exit 1
179         fi
180
181         # releasing an IP is a bit more complex than it seems. Once the IP
182         # is released, any open tcp connections to that IP on this host will end
183         # up being stuck. Some of them (such as NFS connections) will be unkillable
184         # so we need to use the killtcp ctdb function to kill them off. We also
185         # need to make sure that no new connections get established while we are 
186         # doing this! So what we do is this:
187         # 1) firewall this IP, so no new external packets arrive for it
188         # 2) use netstat -tn to find existing connections, and kill them 
189         # 3) remove the IP from the interface
190         # 4) remove the firewall rule
191         iface=$2
192         ip=$3
193         maskbits=$4
194
195         failed=0
196         # we do an extra delete to cope with the script being killed
197         iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
198         iptables -I INPUT -i $iface -d $ip -j DROP
199         kill_tcp_connections $ip
200
201         delete_ip_from_iface $iface $ip $maskbits || {
202                 iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
203                 exit 1;
204         }
205
206         iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
207
208         # flush our route cache
209         set_proc sys/net/ipv4/route/flush 1
210         ;;
211
212      ##################################################
213      # called when ctdbd wants to update an IP address
214      updateip)
215         if [ $# != 5 ]; then
216            echo "must supply old interface, new interface, IP and maskbits"
217            exit 1
218         fi
219
220         # moving an IP is a bit more complex than it seems.
221         # First we drop all traffic on the old interface.
222         # Then we try to add the ip to the new interface and before
223         # we finally remove it from the old interface.
224         #
225         # 1) firewall this IP, so no new external packets arrive for it
226         # 2) add the IP to the new interface
227         # 3) remove the IP from the old interface
228         # 4) remove the firewall rule
229         # 5) use ctdb gratiousarp to propagate the new mac address
230         # 6) use netstat -tn to find existing connections, and tickle them
231         oiface=$2
232         niface=$3
233         ip=$4
234         maskbits=$5
235
236         failed=0
237         # we do an extra delete to cope with the script being killed
238         iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
239         iptables -I INPUT -i $oiface -d $ip -j DROP
240
241         delete_ip_from_iface $oiface $ip $maskbits 2>/dev/null
242         delete_ip_from_iface $niface $ip $maskbits 2>/dev/null
243
244         add_ip_to_iface $niface $ip $maskbits || {
245                 iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
246                 exit 1;
247         }
248
249         # cope with the script being killed while we have the interface blocked
250         iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
251
252         # flush our route cache
253         set_proc sys/net/ipv4/route/flush 1
254
255         # propagate the new mac address
256         ctdb gratiousarp $ip $niface
257
258         # tickle all existing connections, so that dropped packets
259         # are retransmited and the tcp streams work
260
261         tickle_tcp_connections $ip
262
263         ;;
264
265
266      ###########################################
267      # called when ctdbd has finished a recovery
268      recovered)
269         ;;
270
271      ####################################
272      # called when ctdbd is shutting down
273      shutdown)
274         ;;
275
276      monitor)
277         monitor_interfaces
278         ret=$?
279
280         test x"$ret" = x"2" && {
281                 test x"$CTDB_PARTIALLY_ONLINE_INTERFACES" != x"yes" && {
282                         exit 1;
283                 }
284                 # as long as we have one interface available don't become
285                 # unhealthy
286                 ret=0
287         }
288
289         test x"$ret" != x"0" && {
290                 exit 1;
291         }
292         ;;
293     *)
294         ctdb_standard_event_handler "$@"
295         ;;
296 esac
297
298 exit 0
299