TMP: add a ctdb snapshot of current ctdb master (git://git.samba.org/ctdb.git) to...
[obnox/samba/samba-obnox.git] / ctdb / config / events.d / 13.per_ip_routing
1 #!/bin/sh
2
3 . $CTDB_BASE/functions
4 loadconfig
5
6 ctdb_setup_service_state_dir "per_ip_routing"
7
8 [ -z "$CTDB_PER_IP_ROUTING_STATE" ] && {
9         CTDB_PER_IP_ROUTING_STATE="$service_state_dir"
10 }
11
12 AUTO_LINK_LOCAL="no"
13
14 case "$CTDB_PER_IP_ROUTING_CONF" in
15         __auto_link_local__)
16                 AUTO_LINK_LOCAL="yes"
17                 CTDB_PER_IP_ROUTING_CONF="$CTDB_PER_IP_ROUTING_STATE/auto_link_local.conf"
18                 ;;
19         *)
20                 [ -z "$CTDB_PER_IP_ROUTING_CONF" ] && {
21                         #echo "No config file found. Nothing to do for 13.per_ip_routing"
22                         exit 0;
23                 }
24                 ;;
25 esac
26
27 _low=$CTDB_PER_IP_ROUTING_TABLE_ID_LOW
28 _high=$CTDB_PER_IP_ROUTING_TABLE_ID_HIGH
29
30 test -z "$_low" && {
31         echo "$0: CTDB_PER_IP_ROUTING_TABLE_ID_LOW not configured";
32         exit 1;
33 }
34 test -z "$_high" && {
35         echo "$0: CTDB_PER_IP_ROUTING_TABLE_ID_HIGH not configured";
36         exit 1;
37 }
38 test "$_low" -ge "$_high" && {
39         echo "$0: CTDB_PER_IP_ROUTING_TABLE_ID_LOW[$_low] needs to be below CTDB_PER_IP_ROUTING_TABLE_ID_HIGH[$_high]";
40         exit 1;
41 }
42
43 test -z "$CTDB_PER_IP_ROUTING_RULE_PREF" && {
44         echo "$0: CTDB_PER_IP_ROUTING_RULE_PREF not configured";
45         exit 1;
46 }
47
48 locknesting=0
49 lock_root="$CTDB_PER_IP_ROUTING_STATE"
50 host=`hostname`
51
52 lock_debug()
53 {
54         echo -n ""
55 }
56
57 ############################
58 # grab a lock file. Not atomic, but close :)
59 # tries to cope with NFS
60 lock_file() {
61         if [ -z "$lock_root" ]; then
62                 lock_root=`pwd`;
63         fi
64         lckf="$lock_root/$1"
65         machine=`cat "$lckf" 2> /dev/null | cut -d: -f1`
66         pid=`cat "$lckf" 2> /dev/null | cut -d: -f2`
67
68         if [ "$pid" = "$$" ]; then
69                 locknesting=`expr $locknesting + 1`
70                 lock_debug "lock nesting now $locknesting"
71                 return 0
72         fi
73
74         if test -f "$lckf"; then
75                 test $machine = $host || {
76                         lock_debug "lock file $lckf is valid for other machine $machine"
77                         stat -c%y "$lckf"
78                         return 1
79                 }
80                 kill -0 $pid && {
81                         lock_debug "lock file $lckf is valid for process $pid"
82                         stat -c%y "$lckf"
83                         return 1
84                 }
85                 lock_debug "stale lock file $lckf for $machine:$pid"
86                 cat "$lckf"
87                 rm -f "$lckf"
88         fi
89         echo "$host:$$" > "$lckf"
90         return 0
91 }
92
93 ############################
94 # unlock a lock file
95 unlock_file() {
96         if [ -z "$lock_root" ]; then
97                 lock_root=`pwd`;
98         fi
99         if [ "$locknesting" != "0" ]; then
100                 locknesting=`expr $locknesting - 1`
101                 lock_debug "lock nesting now $locknesting"
102         else
103                 lckf="$lock_root/$1"
104                 rm -f "$lckf"
105         fi
106 }
107
108 generate_table_id () {
109         local _ip=$1
110         local _ipsdir="$CTDB_PER_IP_ROUTING_STATE/ips"
111         local _ipdir="$_ipsdir/$_ip"
112
113         mkdir -p $_ipdir
114
115         #echo "generate_table_id $_ip"
116
117         local _id=`cat $_ipdir/table_id 2>/dev/null| xargs`
118         test -n "$_id" && {
119                 #echo "IP: $_ip => OLD TABLE: $_id"
120                 table_id=$_id
121                 return 0;
122         }
123
124         local _low="$CTDB_PER_IP_ROUTING_TABLE_ID_LOW"
125         local _high="$CTDB_PER_IP_ROUTING_TABLE_ID_HIGH"
126
127         local _newid=""
128         for _id in `seq $_low $_high | xargs`; do
129                 local _table_lck="table_id_$_id.lock"
130                 lock_file $_table_lck 2>/dev/null || {
131                         continue;
132                 }
133                 local _taken=`grep "^$_id$" $_ipsdir/*/table_id 2>/dev/null| wc -l | xargs`
134                 test x"$_taken" != x"0" && {
135                         unlock_file $_table_lck
136                         #echo "tableid: $_id taken"
137                         continue
138                 }
139                 _newid=$_id;
140                 echo "$_newid" > $_ipdir/table_id
141                 unlock_file $_table_lck
142                 break;
143         done
144
145         test -z "$_newid" && {
146                 echo "generate_table_id: out of table ids: $_low - $_high"
147                 exit 1;
148         }
149
150         #echo "IP: $_ip => NEW TABLE: $_newid"
151         table_id=$_newid
152         return 0;
153 }
154
155 run_release_script_once()
156 {
157         local _script=$1
158
159         #echo "run_release_script_once[$_script]"
160
161         test -x "$_script" && {
162                 #echo "run it: start"
163                 $_script || {
164                         echo "release_script: $_script - failed $?"
165                         return $?;
166                 }
167                 #echo "run it: end"
168         }
169
170         echo '#!/bin/sh' > $_script
171         echo '#' >> $_script
172         echo >> $_script
173
174         chmod +x $_script
175
176         return 0;
177 }
178
179 generate_auto_link_local()
180 {
181         local _ip=$1
182         local _maskbits=$2
183
184         #echo "generate_auto_link_local $_ip $_maskbits"
185
186         local _netip=`ipv4_host_addr_to_net_addr $_ip $_maskbits`
187
188         local _line="$_ip $_netip/$_maskbits"
189
190         local _lockfile="$CTDB_PER_IP_ROUTING_CONF.lock"
191         local _script="$CTDB_PER_IP_ROUTING_CONF.$$.sh"
192
193         echo "#!/bin/sh" > $_script
194         echo "#" >> $_script
195         echo "" >> $_script
196         echo "_config=\`cat $CTDB_PER_IP_ROUTING_CONF 2>/dev/null\`" >> $_script
197         echo "_exact=\`echo -n \"\$_config\" | grep \"^$_line\$\" | wc -l | xargs\`" >> $_script
198         echo "" >> $_script
199
200         echo "test x\"\$_exact\" = x\"1\" && {" >> $_script
201         echo "    exit 0;" >> $_script
202         echo "}" >> $_script
203         echo "" >> $_script
204
205         echo "_tmp=\"$CTDB_PER_IP_ROUTING_CONF.$$.tmp\"" >> $_script
206         echo "echo -n \"\$_config\" | grep -v \"^$_ip \" | cat > \$_tmp || {" >> $_script
207         echo "    echo \"echo -n \\\"\$_config\\\" | grep -v \\\"^$_ip \\\" > \$_tmp - failed\"" >> $_script
208         echo "    exit 1;" >> $_script
209         echo "}" >> $_script
210         echo "echo \"$_line\" >> \$_tmp || {" >> $_script
211         echo "    echo \"echo \\\"$_line\\\" >> \$_tmp - failed\"" >> $_script
212         echo "    exit 1;" >> $_script
213         echo "}" >> $_script
214         echo "" >> $_script
215
216         echo "mv \$_tmp $CTDB_PER_IP_ROUTING_CONF || {" >> $_script
217         echo "    echo \"mv \$_tmp $CTDB_PER_IP_ROUTING_CONF - failed\"" >> $_script
218         echo "    exit 1;" >> $_script
219         echo "}" >> $_script
220         echo "" >> $_script
221
222         echo "echo \"Added '$_line' to $CTDB_PER_IP_ROUTING_CONF\"">> $_script
223         echo "exit 0" >> $_script
224
225         chmod +x $_script
226
227         test -f $_lockfile || {
228                 touch $_lockfile
229         }
230
231         flock --timeout 30 $_lockfile $_script
232         ret=$?
233         rm $_script
234         return $ret
235 }
236
237 generate_per_ip_routing()
238 {
239         local _ip=$1
240         local _maskbits=$2
241         local _iface=$3
242         local _readonly=$4
243         local _ipdir="$CTDB_PER_IP_ROUTING_STATE/ips/$_ip"
244
245         table_id=""
246         release_script="$_ipdir/per_ip_routing_release.sh"
247         setup_script="$_ipdir/per_ip_routing_setup.sh"
248
249         test x"$_readonly" = x"yes" && {
250                 test -d $_ipdir || {
251                         return 1;
252                 }
253                 return 0;
254         }
255
256         mkdir -p $_ipdir || {
257                 echo "mkdir -p $_ipdir failed"
258                 return 1;
259         }
260         echo "$_ip" > $_ipdir/ip
261
262         generate_table_id $_ip
263
264         test x"$AUTO_LINK_LOCAL" = x"yes" && {
265                 generate_auto_link_local $_ip $_maskbits
266         }
267
268         run_release_script_once $release_script
269
270         echo '#!/bin/sh' > $setup_script
271         echo '#' >> $setup_script
272         echo >> $setup_script
273         chmod +x $setup_script
274
275         return 0;
276 }
277
278 setup_per_ip_routing()
279 {
280         local _ip=$1
281         local _iface=$2
282         local _table_id=$3
283         local _release_script=$4
284         local _setup_script=$5
285
286         local _config=`cat $CTDB_PER_IP_ROUTING_CONF`
287         local _lines=`echo -n "$_config" | grep -n "^$_ip " | cut -d ':' -f1 | xargs`
288
289         local _pref="$CTDB_PER_IP_ROUTING_RULE_PREF"
290
291         test -n "$_lines" && {
292                 echo "ip rule del from $_ip pref $_pref table $_table_id" >> $_release_script
293                 echo "ip route flush table $_table_id 2>/dev/null" >> $_release_script
294
295                 cmd="ip rule del from $_ip pref $_pref 2>/dev/null"
296                 echo "$cmd" >> $_setup_script
297
298                 cmd="ip route flush table $_table_id 2>/dev/null"
299                 echo "$cmd" >> $_setup_script
300
301                 cmd="ip rule add from $_ip pref $_pref table $_table_id"
302                 echo "$cmd || {" >> $_setup_script
303                 echo "    echo \"$cmd - failed \$ret\"" >> $_setup_script
304                 echo "    exit \$ret" >> $_setup_script
305                 echo "}" >> $_setup_script
306         }
307         local _l
308         for _l in $_lines; do
309                 local _line=`echo -n "$_config" | head -n $_l | tail -n 1`
310                 local _dest=`echo -n "$_line" | cut -d ' ' -f 2`
311                 local _gw=`echo -n "$_line" | cut -d ' ' -f 3`
312
313                 local _via=""
314                 test -n "$_gw" && {
315                         _via="via $_gw"
316                 }
317
318                 cmd="ip route add $_dest $_via dev $_iface table $_table_id"
319                 echo "$cmd || {" >> $_setup_script
320                 echo "    echo \"$cmd - failed \$ret\"" >> $_setup_script
321                 echo "    exit \$ret" >> $_setup_script
322                 echo "}" >> $_setup_script
323         done
324
325         $_setup_script
326         return $?;
327 }
328
329 ctdb_check_args "$@"
330
331 case "$1" in
332      #############################
333      # called when ctdbd starts up
334      startup)
335         # cleanup old rules
336         pref=$CTDB_PER_IP_ROUTING_RULE_PREF
337         rules=`ip rule show | grep "^$pref:" | sed -e 's/.*from \([^ ][^ ]*\) lookup \([^ ][^ ]*\)/\2;\1/' | xargs`
338         for r in $rules; do
339                 table_id=`echo -n "$r" | cut -d ';' -f1`
340                 ip=`echo -n "$r" | cut -d ';' -f2-`
341
342                 echo "Removing ip rule for public address $ip for routing table $table_id"
343                 cmd="ip rule del from $ip table $table_id pref $pref"
344                 #echo $cmd
345                 eval $cmd
346                 cmd="ip route flush table $table_id"
347                 #echo $cmd
348                 eval $cmd 2>/dev/null
349         done
350
351         # make sure that we only respond to ARP messages from the NIC where
352         # a particular ip address is associated.
353         [ -f /proc/sys/net/ipv4/conf/all/arp_filter ] && {
354             echo 1 > /proc/sys/net/ipv4/conf/all/arp_filter
355         }
356
357         mkdir -p $CTDB_PER_IP_ROUTING_STATE
358
359         ;;
360
361      shutdown)
362
363         for s in $CTDB_PER_IP_ROUTING_STATE/ips/*/per_ip_routing_release.sh; do
364                 run_release_script_once "$s"
365         done
366         rm -rf $CTDB_PER_IP_ROUTING_STATE
367
368         ;;
369
370      ################################################
371      # called when ctdbd wants to claim an IP address
372      takeip)
373         iface=$2
374         ip=$3
375         maskbits=$4
376
377         ipv4_is_valid_addr $ip || {
378                 echo "$0: $1 not an ipv4 address skipping IP:$ip"
379                 exit 0;
380         }
381
382         [ ! -d "$CTDB_PER_IP_ROUTING_STATE" ] && {
383                 echo "$0: $1 No state directory found, waiting for startup."
384                 exit 0;
385         }
386
387         generate_per_ip_routing $ip $maskbits $iface "no" || {
388                 echo "$0: $1: generate_per_ip_routing $ip $maskbits $iface no - failed"
389                 exit 1;
390         }
391
392         setup_per_ip_routing $ip $iface $table_id $release_script $setup_script || {
393                 echo "$0: $1: setup_per_ip_routing $ip $iface $table_id $release_script $setup_script - failed"
394                 exit 1;
395         }
396
397         setup_iface_ip_readd_script $iface $ip $maskbits $setup_script || {
398                 echo "$0: $1: setup_iface_ip_readd_script $iface $ip $maskbits $setup_script - failed"
399                 exit 1;
400         }
401
402         # flush our route cache
403         echo 1 > /proc/sys/net/ipv4/route/flush
404         ctdb gratiousarp $ip $iface
405
406         ;;
407
408      ################################################
409      # called when ctdbd wants to claim an IP address
410      updateip)
411         oiface=$2
412         niface=$3
413         ip=$4
414         maskbits=$5
415
416         ipv4_is_valid_addr $ip || {
417                 echo "$0: $1 not an ipv4 address skipping IP:$ip"
418                 exit 0;
419         }
420
421         [ ! -d "$CTDB_PER_IP_ROUTING_STATE" ] && {
422                 echo "$0: $1 No state directory found, waiting for startup."
423                 exit 0;
424         }
425
426         generate_per_ip_routing $ip $maskbits $niface "no" || {
427                 echo "$0: $1: generate_per_ip_routing $ip $maskbits $niface no - failed"
428                 exit 1;
429         }
430
431         setup_per_ip_routing $ip $niface $table_id $release_script $setup_script || {
432                 echo "$0: $1: setup_per_ip_routing $ip $niface $table_id $release_script $setup_script - failed"
433                 exit 1;
434         }
435
436         setup_iface_ip_readd_script $niface $ip $maskbits $setup_script || {
437                 echo "$0: $1: setup_iface_ip_readd_script $niface $ip $maskbits $setup_script - failed"
438                 exit 1;
439         }
440
441         # flush our route cache
442         echo 1 > /proc/sys/net/ipv4/route/flush
443
444         ctdb gratiousarp $ip $niface
445         tickle_tcp_connections $ip
446
447         ;;
448
449      ##################################################
450      # called when ctdbd wants to release an IP address
451      releaseip)
452         iface=$2
453         ip=$3
454         maskbits=$4
455
456         ipv4_is_valid_addr $ip || {
457                 echo "$0: $1 not an ipv4 address skipping IP:$ip"
458                 exit 0;
459         }
460
461         [ ! -d "$CTDB_PER_IP_ROUTING_STATE" ] && {
462                 echo "$0: $1 No state directory found, waiting for startup."
463                 exit 0;
464         }
465
466         generate_per_ip_routing $ip $maskbits $iface "yes" || {
467                 echo "$0: $1: generate_per_ip_routing $ip $maskbits $iface yes - failed"
468                 exit 1;
469         }
470
471         run_release_script_once "$release_script"
472
473         ;;
474
475
476      ###########################################
477      # called when ctdbd has finished a recovery
478      recovered)
479         ;;
480
481      ####################################
482      # called when ctdbd is shutting down
483      shutdown)
484         ;;
485
486      monitor)
487         ;;
488     *)
489         ctdb_standard_event_handler "$@"
490         ;;
491 esac
492
493 exit 0
494