ctdb-scripts: Fix remaining uses of "ctdb gratiousarp"
[mat/samba.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     # Use word splitting to squash whitespace
43     # shellcheck disable=SC2086
44     all_interfaces=$(echo $all_interfaces $ctdb_ifaces | tr ' ' '\n' | sort -u)
45 }
46
47 monitor_interfaces()
48 {
49         get_all_interfaces
50
51         down_interfaces_found=false
52         up_interfaces_found=false
53
54         # Note that this loop must not exit early.  It must process
55         # all interfaces so that the correct state for each interface
56         # is set in CTDB using setifacelink.
57         for _iface in $all_interfaces ; do
58                 if interface_monitor "$_iface" ; then
59                         up_interfaces_found=true
60                         $CTDB setifacelink "$_iface" up >/dev/null 2>&1
61                 else
62                         down_interfaces_found=true
63                         $CTDB setifacelink "$_iface" down >/dev/null 2>&1
64                 fi
65         done
66
67         if ! $down_interfaces_found ; then
68                 return 0
69         fi
70
71         if ! $up_interfaces_found ; then
72                 return 1
73         fi
74
75         if [ "$CTDB_PARTIALLY_ONLINE_INTERFACES" != "yes" ]; then
76                 return 1
77         fi
78
79         return 0
80 }
81
82 # Sets: iface, ip, maskbits
83 get_iface_ip_maskbits ()
84 {
85     _iface_in="$1"
86     ip="$2"
87     _maskbits_in="$3"
88
89     # Intentional word splitting here
90     # shellcheck disable=SC2046
91     set -- $(ip_maskbits_iface "$ip")
92     if [ -n "$1" ] ; then
93         maskbits="$1"
94         iface="$2"
95
96         if [ "$iface" != "$_iface_in" ] ; then
97             printf \
98                 'WARNING: Public IP %s hosted on interface %s but VNN says %s\n' \
99                 "$ip" "$iface" "$_iface_in"
100         fi
101         if [ "$maskbits" != "$_maskbits_in" ] ; then
102             printf \
103                 'WARNING: Public IP %s has %s bit netmask but VNN says %s\n' \
104                     "$ip" "$maskbits" "$_maskbits_in"
105         fi
106     else
107         die "ERROR: Unable to determine interface for IP ${ip}"
108     fi
109 }
110
111 ip_block ()
112 {
113         _ip="$1"
114         _iface="$2"
115
116         case "$_ip" in
117         *:*) _family="inet6" ;;
118         *)   _family="inet"  ;;
119         esac
120
121         # Extra delete copes with previously killed script
122         iptables_wrapper "$_family" \
123                          -D INPUT -i "$_iface" -d "$_ip" -j DROP 2>/dev/null
124         iptables_wrapper "$_family" \
125                          -I INPUT -i "$_iface" -d "$_ip" -j DROP
126 }
127
128 ip_unblock ()
129 {
130         _ip="$1"
131         _iface="$2"
132
133         case "$_ip" in
134         *:*) _family="inet6" ;;
135         *)   _family="inet"  ;;
136         esac
137
138         iptables_wrapper "$_family" \
139                          -D INPUT -i "$_iface" -d "$_ip" -j DROP 2>/dev/null
140 }
141
142 ctdb_check_args "$@"
143
144 case "$1" in
145 init)
146         # make sure that we only respond to ARP messages from the NIC where
147         # a particular ip address is associated.
148         get_proc sys/net/ipv4/conf/all/arp_filter >/dev/null 2>&1 && {
149             set_proc sys/net/ipv4/conf/all/arp_filter 1
150         }
151
152         _promote="sys/net/ipv4/conf/all/promote_secondaries"
153         get_proc "$_promote" >/dev/null 2>&1 || \
154             die "Public IPs only supported if promote_secondaries is available"
155
156         # make sure we drop any ips that might still be held if
157         # previous instance of ctdb got killed with -9 or similar
158         drop_all_public_ips
159         ;;
160
161 startup)
162         monitor_interfaces
163         ;;
164
165 takeip)
166         iface=$2
167         ip=$3
168         maskbits=$4
169
170         add_ip_to_iface "$iface" "$ip" "$maskbits" || {
171                 exit 1;
172         }
173
174         # In case a previous "releaseip" for this IP was killed...
175         ip_unblock "$ip" "$iface"
176
177         flush_route_cache
178         ;;
179
180 releaseip)
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) find existing connections, and kill them
189         # 3) remove the IP from the interface
190         # 4) remove the firewall rule
191         shift
192         get_iface_ip_maskbits "$@"
193
194         ip_block "$ip" "$iface"
195
196         kill_tcp_connections "$iface" "$ip"
197
198         delete_ip_from_iface "$iface" "$ip" "$maskbits" || {
199                 ip_unblock "$ip" "$iface"
200                 exit 1
201         }
202
203         ip_unblock "$ip" "$iface"
204
205         flush_route_cache
206         ;;
207
208 updateip)
209         # moving an IP is a bit more complex than it seems.
210         # First we drop all traffic on the old interface.
211         # Then we try to add the ip to the new interface and before
212         # we finally remove it from the old interface.
213         #
214         # 1) firewall this IP, so no new external packets arrive for it
215         # 2) remove the IP from the old interface (and new interface, to be sure)
216         # 3) add the IP to the new interface
217         # 4) remove the firewall rule
218         # 5) use ctdb gratarp to propagate the new mac address
219         # 6) use netstat -tn to find existing connections, and tickle them
220         _oiface=$2
221         niface=$3
222         _ip=$4
223         _maskbits=$5
224
225         get_iface_ip_maskbits "$_oiface" "$_ip" "$_maskbits"
226         oiface="$iface"
227
228         # Could check maskbits too.  However, that should never change
229         # so we want to notice if it does.
230         if [ "$oiface" = "$niface" ] ; then
231                 echo "Redundant \"updateip\" - ${ip} already on ${niface}"
232                 exit 0
233         fi
234
235         ip_block "$ip" "$oiface"
236
237         delete_ip_from_iface "$oiface" "$ip" "$maskbits" 2>/dev/null
238         delete_ip_from_iface "$niface" "$ip" "$maskbits" 2>/dev/null
239
240         add_ip_to_iface "$niface" "$ip" "$maskbits" || {
241                 ip_unblock "$ip" "$oiface"
242                 exit 1
243         }
244
245         ip_unblock "$ip" "$oiface"
246
247         flush_route_cache
248
249         # propagate the new mac address
250         $CTDB gratarp "$ip" "$niface"
251
252         # tickle all existing connections, so that dropped packets
253         # are retransmited and the tcp streams work
254         tickle_tcp_connections "$ip"
255         ;;
256
257 monitor)
258         monitor_interfaces || exit 1
259         ;;
260 esac
261
262 exit 0