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