· 8 years ago · May 20, 2018, 11:02 AM
1#!/bin/sh
2#
3# $Id: IPaddr2.in,v 1.24 2006/08/09 13:01:54 lars Exp $
4#
5# OCF Resource Agent compliant IPaddr2 script.
6#
7# Based on work by Tuomo Soini, ported to the OCF RA API by Lars
8# Marowsky-Brée. Implements Cluster Alias IP functionality too.
9#
10# Cluster Alias IP cleanup, fixes and testing by Michael Schwartzkopff
11#
12#
13# Copyright (c) 2003 Tuomo Soini
14# Copyright (c) 2004-2006 SUSE LINUX AG, Lars Marowsky-Brée
15# All Rights Reserved.
16#
17# This program is free software; you can redistribute it and/or modify
18# it under the terms of version 2 of the GNU General Public License as
19# published by the Free Software Foundation.
20#
21# This program is distributed in the hope that it would be useful, but
22# WITHOUT ANY WARRANTY; without even the implied warranty of
23# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
24#
25# Further, this software is distributed without any warranty that it is
26# free of the rightful claim of any third person regarding infringement
27# or the like. Any license provided herein, whether implied or
28# otherwise, applies only to this software file. Patent licenses, if
29# any, provided herein do not apply to combinations of this program with
30# other software, or any other product whatsoever.
31#
32# You should have received a copy of the GNU General Public License
33# along with this program; if not, write the Free Software Foundation,
34# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
35#
36#
37
38
39# TODO:
40# - There ought to be an ocf_run_cmd function which does all logging,
41# timeout handling etc for us
42# - Make this the standard IP address agent on Linux; the other
43# platforms simply should ignore the additional parameters OR can use
44# the legacy heartbeat resource script...
45# - Check LVS <-> clusterip incompatibilities.
46#
47# OCF parameters are as below
48# OCF_RESKEY_ip
49# OCF_RESKEY_broadcast
50# OCF_RESKEY_nic
51# OCF_RESKEY_cidr_netmask
52# OCF_RESKEY_iflabel
53# OCF_RESKEY_mac
54# OCF_RESKEY_clusterip_hash
55# OCF_RESKEY_arp_interval
56# OCF_RESKEY_arp_count
57# OCF_RESKEY_arp_bg
58# OCF_RESKEY_arp_mac
59# OCF_RESKEY_preferred_lft
60#
61# OCF_RESKEY_CRM_meta_clone
62# OCF_RESKEY_CRM_meta_clone_max
63
64
65#######################################################################
66# Initialization:
67
68: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
69. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
70. ${OCF_FUNCTIONS_DIR}/findif.sh
71
72# Defaults
73OCF_RESKEY_lvs_support_default=false
74OCF_RESKEY_lvs_ipv6_addrlabel_default=false
75OCF_RESKEY_lvs_ipv6_addrlabel_value_default=99
76OCF_RESKEY_clusterip_hash_default="sourceip-sourceport"
77OCF_RESKEY_unique_clone_address_default=false
78OCF_RESKEY_arp_interval_default=200
79OCF_RESKEY_arp_count_default=5
80OCF_RESKEY_arp_count_refresh_default=0
81OCF_RESKEY_arp_bg_default=true
82OCF_RESKEY_arp_mac_default="ffffffffffff"
83OCF_RESKEY_run_arping_default=false
84OCF_RESKEY_preferred_lft_default="forever"
85
86: ${OCF_RESKEY_lvs_support=${OCF_RESKEY_lvs_support_default}}
87: ${OCF_RESKEY_lvs_ipv6_addrlabel=${OCF_RESKEY_lvs_ipv6_addrlabel_default}}
88: ${OCF_RESKEY_lvs_ipv6_addrlabel_value=${OCF_RESKEY_lvs_ipv6_addrlabel_value_default}}
89: ${OCF_RESKEY_clusterip_hash=${OCF_RESKEY_clusterip_hash_default}}
90: ${OCF_RESKEY_unique_clone_address=${OCF_RESKEY_unique_clone_address_default}}
91: ${OCF_RESKEY_arp_interval=${OCF_RESKEY_arp_interval_default}}
92: ${OCF_RESKEY_arp_count=${OCF_RESKEY_arp_count_default}}
93: ${OCF_RESKEY_arp_count_refresh=${OCF_RESKEY_arp_count_refresh_default}}
94: ${OCF_RESKEY_arp_bg=${OCF_RESKEY_arp_bg_default}}
95: ${OCF_RESKEY_arp_mac=${OCF_RESKEY_arp_mac_default}}
96: ${OCF_RESKEY_run_arping=${OCF_RESKEY_run_arping_default}}
97: ${OCF_RESKEY_preferred_lft=${OCF_RESKEY_preferred_lft_default}}
98#######################################################################
99
100SENDARP=$HA_BIN/send_arp
101SENDUA=$HA_BIN/send_ua
102FINDIF=findif
103VLDIR=$HA_RSCTMP
104SENDARPPIDDIR=$HA_RSCTMP
105CIP_lockfile=$HA_RSCTMP/IPaddr2-CIP-${OCF_RESKEY_ip}
106
107#######################################################################
108
109meta_data() {
110 cat <<END
111<?xml version="1.0"?>
112<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
113<resource-agent name="IPaddr2">
114<version>1.0</version>
115
116<longdesc lang="en">
117This Linux-specific resource manages IP alias IP addresses.
118It can add an IP alias, or remove one.
119In addition, it can implement Cluster Alias IP functionality
120if invoked as a clone resource.
121
122If used as a clone, you should explicitly set clone-node-max >= 2,
123and/or clone-max < number of nodes. In case of node failure,
124clone instances need to be re-allocated on surviving nodes.
125This would not be possible if there is already an instance on those nodes,
126and clone-node-max=1 (which is the default).
127</longdesc>
128
129<shortdesc lang="en">Manages virtual IPv4 and IPv6 addresses (Linux specific version)</shortdesc>
130
131<parameters>
132<parameter name="ip" unique="1" required="1">
133<longdesc lang="en">
134The IPv4 (dotted quad notation) or IPv6 address (colon hexadecimal notation)
135example IPv4 "192.168.1.1".
136example IPv6 "2001:db8:DC28:0:0:FC57:D4C8:1FFF".
137</longdesc>
138<shortdesc lang="en">IPv4 or IPv6 address</shortdesc>
139<content type="string" default="" />
140</parameter>
141<parameter name="nic" unique="0">
142<longdesc lang="en">
143The base network interface on which the IP address will be brought
144online.
145If left empty, the script will try and determine this from the
146routing table.
147
148Do NOT specify an alias interface in the form eth0:1 or anything here;
149rather, specify the base interface only.
150If you want a label, see the iflabel parameter.
151
152Prerequisite:
153
154There must be at least one static IP address, which is not managed by
155the cluster, assigned to the network interface.
156If you can not assign any static IP address on the interface,
157modify this kernel parameter:
158
159sysctl -w net.ipv4.conf.all.promote_secondaries=1 # (or per device)
160</longdesc>
161<shortdesc lang="en">Network interface</shortdesc>
162<content type="string"/>
163</parameter>
164
165<parameter name="cidr_netmask">
166<longdesc lang="en">
167The netmask for the interface in CIDR format
168(e.g., 24 and not 255.255.255.0)
169
170If unspecified, the script will also try to determine this from the
171routing table.
172</longdesc>
173<shortdesc lang="en">CIDR netmask</shortdesc>
174<content type="string" default=""/>
175</parameter>
176
177<parameter name="broadcast">
178<longdesc lang="en">
179Broadcast address associated with the IP. If left empty, the script will
180determine this from the netmask.
181</longdesc>
182<shortdesc lang="en">Broadcast address</shortdesc>
183<content type="string" default=""/>
184</parameter>
185
186<parameter name="iflabel">
187<longdesc lang="en">
188You can specify an additional label for your IP address here.
189This label is appended to your interface name.
190
191A label can be specified in nic parameter but it is deprecated.
192If a label is specified in nic name, this parameter has no effect.
193</longdesc>
194<shortdesc lang="en">Interface label</shortdesc>
195<content type="string" default=""/>
196</parameter>
197
198<parameter name="lvs_support">
199<longdesc lang="en">
200Enable support for LVS Direct Routing configurations. In case a IP
201address is stopped, only move it to the loopback device to allow the
202local node to continue to service requests, but no longer advertise it
203on the network.
204
205Notes for IPv6:
206It is not necessary to enable this option on IPv6.
207Instead, enable 'lvs_ipv6_addrlabel' option for LVS-DR usage on IPv6.
208</longdesc>
209<shortdesc lang="en">Enable support for LVS DR</shortdesc>
210<content type="boolean" default="${OCF_RESKEY_lvs_support_default}"/>
211</parameter>
212
213<parameter name="lvs_ipv6_addrlabel">
214<longdesc lang="en">
215Enable adding IPv6 address label so IPv6 traffic originating from
216the address's interface does not use this address as the source.
217This is necessary for LVS-DR health checks to realservers to work. Without it,
218the most recently added IPv6 address (probably the address added by IPaddr2)
219will be used as the source address for IPv6 traffic from that interface and
220since that address exists on loopback on the realservers, the realserver
221response to pings/connections will never leave its loopback.
222See RFC3484 for the detail of the source address selection.
223
224See also 'lvs_ipv6_addrlabel_value' parameter.
225</longdesc>
226<shortdesc lang="en">Enable adding IPv6 address label.</shortdesc>
227<content type="boolean" default="${OCF_RESKEY_lvs_ipv6_addrlabel_default}"/>
228</parameter>
229
230<parameter name="lvs_ipv6_addrlabel_value">
231<longdesc lang="en">
232Specify IPv6 address label value used when 'lvs_ipv6_addrlabel' is enabled.
233The value should be an unused label in the policy table
234which is shown by 'ip addrlabel list' command.
235You would rarely need to change this parameter.
236</longdesc>
237<shortdesc lang="en">IPv6 address label value.</shortdesc>
238<content type="integer" default="${OCF_RESKEY_lvs_ipv6_addrlabel_value_default}"/>
239</parameter>
240
241<parameter name="mac">
242<longdesc lang="en">
243Set the interface MAC address explicitly. Currently only used in case of
244the Cluster IP Alias. Leave empty to chose automatically.
245
246</longdesc>
247<shortdesc lang="en">Cluster IP MAC address</shortdesc>
248<content type="string" default=""/>
249</parameter>
250
251<parameter name="clusterip_hash">
252<longdesc lang="en">
253Specify the hashing algorithm used for the Cluster IP functionality.
254
255</longdesc>
256<shortdesc lang="en">Cluster IP hashing function</shortdesc>
257<content type="string" default="${OCF_RESKEY_clusterip_hash_default}"/>
258</parameter>
259
260<parameter name="unique_clone_address">
261<longdesc lang="en">
262If true, add the clone ID to the supplied value of IP to create
263a unique address to manage
264</longdesc>
265<shortdesc lang="en">Create a unique address for cloned instances</shortdesc>
266<content type="boolean" default="${OCF_RESKEY_unique_clone_address_default}"/>
267</parameter>
268
269<parameter name="arp_interval">
270<longdesc lang="en">
271Specify the interval between unsolicited ARP packets in milliseconds.
272</longdesc>
273<shortdesc lang="en">ARP packet interval in ms</shortdesc>
274<content type="integer" default="${OCF_RESKEY_arp_interval_default}"/>
275</parameter>
276
277<parameter name="arp_count">
278<longdesc lang="en">
279Number of unsolicited ARP packets to send at resource initialization.
280</longdesc>
281<shortdesc lang="en">ARP packet count sent during initialization</shortdesc>
282<content type="integer" default="${OCF_RESKEY_arp_count_default}"/>
283</parameter>
284
285<parameter name="arp_count_refresh">
286<longdesc lang="en">
287Number of unsolicited ARP packets to send during resource monitoring. Doing
288so helps mitigate issues of stuck ARP caches resulting from split-brain
289situations.
290</longdesc>
291<shortdesc lang="en">ARP packet count sent during monitoring</shortdesc>
292<content type="integer" default="${OCF_RESKEY_arp_count_refresh_default}"/>
293</parameter>
294
295<parameter name="arp_bg">
296<longdesc lang="en">
297Whether or not to send the ARP packets in the background.
298</longdesc>
299<shortdesc lang="en">ARP from background</shortdesc>
300<content type="string" default="${OCF_RESKEY_arp_bg_default}"/>
301</parameter>
302
303<parameter name="arp_mac">
304<longdesc lang="en">
305MAC address to send the ARP packets to.
306
307You really shouldn't be touching this.
308
309</longdesc>
310<shortdesc lang="en">ARP MAC</shortdesc>
311<content type="string" default="${OCF_RESKEY_arp_mac_default}"/>
312</parameter>
313
314<parameter name="arp_sender">
315<longdesc lang="en">
316The program to send ARP packets with on start. For infiniband
317interfaces, default is ipoibarping. If ipoibarping is not
318available, set this to send_arp.
319</longdesc>
320<shortdesc lang="en">ARP sender</shortdesc>
321<content type="string" default=""/>
322</parameter>
323
324<parameter name="flush_routes">
325<longdesc lang="en">
326Flush the routing table on stop. This is for
327applications which use the cluster IP address
328and which run on the same physical host that the
329IP address lives on. The Linux kernel may force that
330application to take a shortcut to the local loopback
331interface, instead of the interface the address
332is really bound to. Under those circumstances, an
333application may, somewhat unexpectedly, continue
334to use connections for some time even after the
335IP address is deconfigured. Set this parameter in
336order to immediately disable said shortcut when the
337IP address goes away.
338</longdesc>
339<shortdesc lang="en">Flush kernel routing table on stop</shortdesc>
340<content type="boolean" default="false"/>
341</parameter>
342
343<parameter name="run_arping">
344<longdesc lang="en">
345Whether or not to run arping for IPv4 collision detection check.
346</longdesc>
347<shortdesc lang="en">Run arping for IPv4 collision detection check</shortdesc>
348<content type="string" default="${OCF_RESKEY_run_arping_default}"/>
349</parameter>
350
351<parameter name="preferred_lft">
352<longdesc lang="en">
353For IPv6, set the preferred lifetime of the IP address.
354This can be used to ensure that the created IP address will not
355be used as a source address for routing.
356Expects a value as specified in section 5.5.4 of RFC 4862.
357</longdesc>
358<shortdesc lang="en">IPv6 preferred lifetime</shortdesc>
359<content type="string" default="${OCF_RESKEY_preferred_lft_default}"/>
360</parameter>
361
362</parameters>
363<actions>
364<action name="start" timeout="20s" />
365<action name="stop" timeout="20s" />
366<action name="status" depth="0" timeout="20s" interval="10s" />
367<action name="monitor" depth="0" timeout="20s" interval="10s" />
368<action name="meta-data" timeout="5s" />
369<action name="validate-all" timeout="20s" />
370</actions>
371</resource-agent>
372END
373
374 exit $OCF_SUCCESS
375}
376
377ip_init() {
378 local rc
379
380 if [ X`uname -s` != "XLinux" ]; then
381 ocf_exit_reason "IPaddr2 only supported Linux."
382 exit $OCF_ERR_INSTALLED
383 fi
384
385 if [ X"$OCF_RESKEY_ip" = "X" ] && [ "$__OCF_ACTION" != "stop" ]; then
386 ocf_exit_reason "IP address (the ip parameter) is mandatory"
387 exit $OCF_ERR_CONFIGURED
388 fi
389
390 if
391 case $__OCF_ACTION in
392 start|stop) ocf_is_root;;
393 *) true;;
394 esac
395 then
396 : YAY!
397 else
398 ocf_exit_reason "You must be root for $__OCF_ACTION operation."
399 exit $OCF_ERR_PERM
400 fi
401
402 BASEIP="$OCF_RESKEY_ip"
403 BRDCAST="$OCF_RESKEY_broadcast"
404 NIC="$OCF_RESKEY_nic"
405 # Note: We had a version out there for a while which used
406 # netmask instead of cidr_netmask. Don't remove this aliasing code!
407 if
408 [ ! -z "$OCF_RESKEY_netmask" -a -z "$OCF_RESKEY_cidr_netmask" ]
409 then
410 OCF_RESKEY_cidr_netmask=$OCF_RESKEY_netmask
411 export OCF_RESKEY_cidr_netmask
412 fi
413 NETMASK="$OCF_RESKEY_cidr_netmask"
414 IFLABEL="$OCF_RESKEY_iflabel"
415 IF_MAC="$OCF_RESKEY_mac"
416
417 IP_INC_GLOBAL=${OCF_RESKEY_CRM_meta_clone_max:-1}
418 IP_INC_NO=`expr ${OCF_RESKEY_CRM_meta_clone:-0} + 1`
419
420 if ocf_is_true ${OCF_RESKEY_lvs_support} && [ $IP_INC_GLOBAL -gt 1 ]; then
421 ocf_exit_reason "LVS and load sharing do not go together well"
422 exit $OCF_ERR_CONFIGURED
423 fi
424
425 if ocf_is_decimal "$IP_INC_GLOBAL" && [ $IP_INC_GLOBAL -gt 0 ]; then
426 :
427 else
428 ocf_exit_reason "Invalid meta-attribute clone_max [$IP_INC_GLOBAL], should be positive integer"
429 exit $OCF_ERR_CONFIGURED
430 fi
431
432 echo $OCF_RESKEY_ip | grep -qs ":"
433 if [ $? -ne 0 ];then
434 FAMILY=inet
435 if ocf_is_true $OCF_RESKEY_lvs_ipv6_addrlabel ;then
436 ocf_exit_reason "IPv4 does not support lvs_ipv6_addrlabel"
437 exit $OCF_ERR_CONFIGURED
438 fi
439 else
440 FAMILY=inet6
441 if ocf_is_true $OCF_RESKEY_lvs_support ;then
442 ocf_exit_reason "The IPv6 does not support lvs_support"
443 exit $OCF_ERR_CONFIGURED
444 fi
445 if ocf_is_true $OCF_RESKEY_lvs_ipv6_addrlabel ;then
446 if ocf_is_decimal "$OCF_RESKEY_lvs_ipv6_addrlabel_value" && [ $OCF_RESKEY_lvs_ipv6_addrlabel_value -ge 0 ]; then
447 :
448 else
449 ocf_exit_reason "Invalid lvs_ipv6_addrlabel_value [$OCF_RESKEY_lvs_ipv6_addrlabel_value], should be positive integer"
450 exit $OCF_ERR_CONFIGURED
451 fi
452 fi
453 fi
454
455 # support nic:iflabel format in nic parameter
456 case $NIC in
457 *:*)
458 IFLABEL=`echo $NIC | sed 's/[^:]*://'`
459 NIC=`echo $NIC | sed 's/:.*//'`
460 # only the base name should be passed to findif
461 OCF_RESKEY_nic=$NIC
462 ;;
463 esac
464
465 # $FINDIF takes its parameters from the environment
466 #
467 NICINFO=`$FINDIF`
468 rc=$?
469 if
470 [ $rc -eq 0 ]
471 then
472 NICINFO=`echo "$NICINFO" | sed -e 's/netmask\ //;s/broadcast\ //'`
473 NIC=`echo "$NICINFO" | cut -d" " -f1`
474 NETMASK=`echo "$NICINFO" | cut -d" " -f2`
475 BRDCAST=`echo "$NICINFO" | cut -d" " -f3`
476 else
477 # findif couldn't find the interface
478 if ocf_is_probe; then
479 ocf_log info "[$FINDIF] failed"
480 exit $OCF_NOT_RUNNING
481 elif [ "$__OCF_ACTION" = stop ]; then
482 ocf_log warn "[$FINDIF] failed"
483 exit $OCF_SUCCESS
484 else
485 ocf_log err "[$FINDIF] failed"
486 exit $rc
487 fi
488 fi
489
490 SENDARPPIDFILE="$SENDARPPIDDIR/send_arp-$OCF_RESKEY_ip"
491
492 if [ -n "$IFLABEL" ]; then
493 IFLABEL=${NIC}:${IFLABEL}
494 fi
495
496 if [ "$IP_INC_GLOBAL" -gt 1 ] && ! ocf_is_true "$OCF_RESKEY_unique_clone_address"; then
497 IP_CIP="yes"
498 IP_CIP_HASH="${OCF_RESKEY_clusterip_hash}"
499 if [ -z "$IF_MAC" ]; then
500 # Choose a MAC
501 # 1. Concatenate some input together
502 # 2. This doesn't need to be a cryptographically
503 # secure hash.
504 # 3. Drop everything after the first 6 octets (12 chars)
505 # 4. Delimit the octets with ':'
506 # 5. Make sure the first octet is odd,
507 # so the result is a multicast MAC
508 IF_MAC=`echo $OCF_RESKEY_ip $NETMASK $BRDCAST | \
509 md5sum | \
510 sed -e 's#\(............\).*#\1#' \
511 -e 's#..#&:#g; s#:$##' \
512 -e 's#^\(.\)[02468aAcCeE]#\11#'`
513 fi
514 IP_CIP_FILE="/proc/net/ipt_CLUSTERIP/$OCF_RESKEY_ip"
515 fi
516}
517
518#
519# Find out which interfaces serve the given IP address and netmask.
520# The arguments are an IP address and a netmask.
521# Its output are interface names devided by spaces (e.g., "eth0 eth1").
522#
523find_interface() {
524 local ipaddr="$1"
525 local netmask="$2"
526
527 #
528 # List interfaces but exclude FreeS/WAN ipsecN virtual interfaces
529 #
530 local iface="`$IP2UTIL -o -f $FAMILY addr show \
531 | grep "\ $ipaddr/$netmask" \
532 | cut -d ' ' -f2 \
533 | grep -v '^ipsec[0-9][0-9]*$'`"
534
535 echo "$iface"
536 return 0
537}
538
539#
540# Delete an interface
541#
542delete_interface () {
543 ipaddr="$1"
544 iface="$2"
545 netmask="$3"
546
547 CMD="$IP2UTIL -f $FAMILY addr delete $ipaddr/$netmask dev $iface"
548
549 ocf_run $CMD || return $OCF_ERR_GENERIC
550
551 if ocf_is_true $OCF_RESKEY_flush_routes; then
552 ocf_run $IP2UTIL route flush cache
553 fi
554
555 if [ "$FAMILY" = "inet6" ] && ocf_is_true $OCF_RESKEY_lvs_ipv6_addrlabel ;then
556 delete_ipv6_addrlabel $ipaddr
557 fi
558
559 return $OCF_SUCCESS
560}
561
562#
563# Add an interface
564#
565add_interface () {
566 local cmd msg ipaddr netmask broadcast iface label
567
568 ipaddr="$1"
569 netmask="$2"
570 broadcast="$3"
571 iface="$4"
572 label="$5"
573
574 if [ "$FAMILY" = "inet" ] && ocf_is_true $OCF_RESKEY_run_arping &&
575 check_binary arping; then
576 arping -q -c 2 -w 3 -D -I $iface $ipaddr
577 if [ $? = 1 ]; then
578 ocf_log err "IPv4 address collision $ipaddr [DAD]"
579 return $OCF_ERR_CONFIGURED
580 fi
581 fi
582
583 if [ "$FAMILY" = "inet6" ] && ocf_is_true $OCF_RESKEY_lvs_ipv6_addrlabel ;then
584 add_ipv6_addrlabel $ipaddr
585 fi
586
587 cmd="$IP2UTIL -f $FAMILY addr add $ipaddr/$netmask dev $iface"
588 msg="Adding $FAMILY address $ipaddr/$netmask to device $iface"
589 if [ "$broadcast" != "none" ]; then
590 cmd="$IP2UTIL -f $FAMILY addr add $ipaddr/$netmask brd $broadcast dev $iface"
591 msg="Adding $FAMILY address $ipaddr/$netmask with broadcast address $broadcast to device $iface"
592 fi
593
594 if [ ! -z "$label" ]; then
595 cmd="$cmd label $label"
596 msg="${msg} (with label $label)"
597 fi
598 if [ "$FAMILY" = "inet6" ] ;then
599 cmd="$cmd preferred_lft $OCF_RESKEY_preferred_lft"
600 msg="${msg} (with preferred_lft $OCF_RESKEY_preferred_lft)"
601 fi
602
603 ocf_log info "$msg"
604 ocf_run $cmd || return $OCF_ERR_GENERIC
605
606 msg="Bringing device $iface up"
607 cmd="$IP2UTIL link set $iface up"
608 ocf_log info "$msg"
609 ocf_run $cmd || return $OCF_ERR_GENERIC
610
611 return $OCF_SUCCESS
612}
613
614#
615# Delete a route
616#
617delete_route () {
618 prefix="$1"
619 iface="$2"
620
621 CMD="$IP2UTIL route delete $prefix dev $iface"
622
623 ocf_log info "$CMD"
624 $CMD
625
626 return $?
627}
628
629# On Linux systems the (hidden) loopback interface may
630# conflict with the requested IP address. If so, this
631# unoriginal code will remove the offending loopback address
632# and save it in VLDIR so it can be added back in later
633# when the IPaddr is released.
634#
635# TODO: This is very ugly and should be controlled by an additional
636# instance parameter. Or even: multi-state, with the IP only being
637# "active" on the master!?
638#
639remove_conflicting_loopback() {
640 ipaddr="$1"
641 netmask="$2"
642 broadcast="$3"
643 ifname="$4"
644
645 ocf_log info "Removing conflicting loopback $ifname."
646 if
647 echo "$ipaddr $netmask $broadcast $ifname" > "$VLDIR/$ipaddr"
648 then
649 : Saved loopback information in $VLDIR/$ipaddr
650 else
651 ocf_log err "Could not save conflicting loopback $ifname." \
652 "it will not be restored."
653 fi
654 delete_interface "$ipaddr" "$ifname" "$netmask"
655 # Forcibly remove the route (if it exists) to the loopback.
656 delete_route "$ipaddr" "$ifname"
657}
658
659#
660# On Linux systems the (hidden) loopback interface may
661# need to be restored if it has been taken down previously
662# by remove_conflicting_loopback()
663#
664restore_loopback() {
665 ipaddr="$1"
666
667 if [ -s "$VLDIR/$ipaddr" ]; then
668 ifinfo=`cat "$VLDIR/$ipaddr"`
669 ocf_log info "Restoring loopback IP Address " \
670 "$ifinfo."
671 add_interface $ifinfo
672 rm -f "$VLDIR/$ipaddr"
673 fi
674}
675
676add_ipv6_addrlabel() {
677 local cmd ipaddr value
678 ipaddr="$1"
679 value="$OCF_RESKEY_lvs_ipv6_addrlabel_value"
680
681 cmd="$IP2UTIL addrlabel add prefix $ipaddr label $value"
682 ocf_log info "Adding IPv6 address label prefix $ipaddr label $value"
683 ocf_run $cmd || ocf_log warn "$cmd failed."
684}
685
686delete_ipv6_addrlabel() {
687 local cmd ipaddr value
688 ipaddr="$1"
689 value="$OCF_RESKEY_lvs_ipv6_addrlabel_value"
690
691 cmd="$IP2UTIL addrlabel del prefix $ipaddr label $value"
692 ocf_run $cmd # an error can be ignored
693}
694
695is_infiniband() {
696 $IP2UTIL link show $NIC | grep link/infiniband >/dev/null
697}
698
699#
700# Run send_arp to note peers about new mac address
701#
702run_send_arp() {
703 if [ "x$IP_CIP" = "xyes" ] ; then
704 if [ x = "x$IF_MAC" ] ; then
705 MY_MAC=auto
706 else
707 MY_MAC=`echo ${IF_MAC} | sed -e 's/://g'`
708 fi
709 else
710 MY_MAC=auto
711 fi
712 if [ "x$1" = "xrefresh" ] ; then
713 ARP_COUNT=$OCF_RESKEY_arp_count_refresh
714 LOGLEVEL=debug
715 else
716 ARP_COUNT=$OCF_RESKEY_arp_count
717 LOGLEVEL=info
718 fi
719 if [ $ARP_COUNT -ne 0 ] ; then
720 ARGS="-i $OCF_RESKEY_arp_interval -c $ARP_COUNT -p $SENDARPPIDFILE -I $NIC -m $MY_MAC $OCF_RESKEY_ip"
721 ocf_log $LOGLEVEL "$SENDARP $ARGS"
722 output=$($SENDARP $ARGS 2>&1)
723 rc=$?
724 if [ $rc -ne $OCF_SUCCESS ]; then
725 if ! ocf_is_true $OCF_RESKEY_arp_bg; then
726 ocf_log err "send_arp output: $output"
727 fi
728 ocf_exit_reason "Could not send gratuitous arps"
729 exit $OCF_ERR_GENERIC
730 fi
731 fi
732}
733
734#
735# Run send_ua to note send ICMPv6 Unsolicited Neighbor Advertisements.
736#
737run_send_ua() {
738 local i
739
740 # Duplicate Address Detection [DAD]
741 # Kernel will flag the IP as 'tentative' until it ensured that
742 # there is no duplicates.
743 # If there is, it will flag it as 'dadfailed'
744 for i in $(seq 1 10); do
745 ipstatus=$($IP2UTIL -o -f $FAMILY addr show dev $NIC to $OCF_RESKEY_ip/$NETMASK)
746 case "$ipstatus" in
747 *dadfailed*)
748 ocf_log err "IPv6 address collision $OCF_RESKEY_ip [DAD]"
749 $IP2UTIL -f $FAMILY addr del dev $NIC $OCF_RESKEY_ip/$NETMASK
750 if [ $? -ne 0 ]; then
751 ocf_log err "Could not delete IPv6 address"
752 fi
753 return $OCF_ERR_GENERIC
754 ;;
755 *tentative*)
756 if [ $i -eq 10 ]; then
757 ofc_log warn "IPv6 address : DAD is still in tentative"
758 fi
759 ;;
760 *)
761 break
762 ;;
763 esac
764 sleep 1
765 done
766 # Now the address should be usable
767
768 ARGS="-i $OCF_RESKEY_arp_interval -c $OCF_RESKEY_arp_count $OCF_RESKEY_ip $NETMASK $NIC"
769 ocf_log info "$SENDUA $ARGS"
770 $SENDUA $ARGS || ocf_log err "Could not send ICMPv6 Unsolicited Neighbor Advertisements."
771}
772
773#
774# Run ipoibarping to note peers about new Infiniband address
775#
776run_send_ib_arp() {
777 if [ "x$1" = "xrefresh" ] ; then
778 ARP_COUNT=$OCF_RESKEY_arp_count_refresh
779 LOGLEVEL=debug
780 else
781 ARP_COUNT=$OCF_RESKEY_arp_count
782 LOGLEVEL=info
783 fi
784 if [ $ARP_COUNT -ne 0 ] ; then
785 ARGS="-q -c $ARP_COUNT -U -I $NIC $OCF_RESKEY_ip"
786 ocf_log $LOGLEVEL "ipoibarping $ARGS"
787 output=$(ipoibarping $ARGS 2>&1)
788 rc=$?
789 if [ $rc -ne $OCF_SUCCESS ]; then
790 if ! ocf_is_true $OCF_RESKEY_arp_bg; then
791 ocf_log err "ipoibarping output: $output"
792 fi
793 ocf_exit_reason "Could not send gratuitous arps"
794 exit $OCF_ERR_GENERIC
795 fi
796 fi
797}
798
799# Do we already serve this IP address on the given $NIC?
800#
801# returns:
802# ok = served (for CIP: + hash bucket)
803# partial = served and no hash bucket (CIP only)
804# partial2 = served and no CIP iptables rule
805# no = nothing
806#
807ip_served() {
808 if [ -z "$NIC" ]; then # no nic found or specified
809 echo "no"
810 return 0
811 fi
812
813 cur_nic="`find_interface $OCF_RESKEY_ip $NETMASK`"
814
815 if [ -z "$cur_nic" ]; then
816 echo "no"
817 return 0
818 fi
819
820 if [ -z "$IP_CIP" ]; then
821 for i in $cur_nic; do
822 # only mark as served when on the same interfaces as $NIC
823 [ "$i" = "$NIC" ] || continue
824 echo "ok"
825 return 0
826 done
827 # There used to be logic here to pretend "not served",
828 # if ${OCF_RESKEY_lvs_support} was enabled, and the IP was
829 # found active on "lo*" only. With lvs_support on, you should
830 # have NIC != lo, so thats already filtered
831 # by the continue above.
832
833 echo "no"
834 return 0
835 fi
836
837 # Special handling for the CIP:
838 if [ ! -e $IP_CIP_FILE ]; then
839 echo "partial2"
840 return 0
841 fi
842 if egrep -q "(^|,)${IP_INC_NO}(,|$)" $IP_CIP_FILE ; then
843 echo "ok"
844 return 0
845 else
846 echo "partial"
847 return 0
848 fi
849
850 exit $OCF_ERR_GENERIC
851}
852
853#######################################################################
854
855ip_usage() {
856 cat <<END
857usage: $0 {start|stop|status|monitor|validate-all|meta-data}
858
859Expects to have a fully populated OCF RA-compliant environment set.
860END
861}
862
863ip_start() {
864 if [ -z "$NIC" ]; then
865 ocf_exit_reason "No nic found or specified"
866 exit $OCF_ERR_CONFIGURED
867 fi
868
869 if [ -n "$IP_CIP" ]; then
870 # Cluster IPs need special processing when the first bucket
871 # is added to the node... take a lock to make sure only one
872 # process executes that code
873 ocf_take_lock $CIP_lockfile
874 ocf_release_lock_on_exit $CIP_lockfile
875 fi
876
877 #
878 # Do we already service this IP address on $NIC?
879 #
880 local ip_status=`ip_served`
881
882 if [ "$ip_status" = "ok" ]; then
883 exit $OCF_SUCCESS
884 fi
885
886 if [ -n "$IP_CIP" ] && [ $ip_status = "no" ] || [ $ip_status = "partial2" ]; then
887 $MODPROBE ip_conntrack
888 $IPTABLES -I INPUT -d $OCF_RESKEY_ip -i $NIC -j CLUSTERIP \
889 --new \
890 --clustermac $IF_MAC \
891 --total-nodes $IP_INC_GLOBAL \
892 --local-node $IP_INC_NO \
893 --hashmode $IP_CIP_HASH
894 if [ $? -ne 0 ]; then
895 ocf_exit_reason "iptables failed"
896 exit $OCF_ERR_GENERIC
897 fi
898 fi
899
900 if [ -n "$IP_CIP" ] && [ $ip_status = "partial" ]; then
901 echo "+$IP_INC_NO" >$IP_CIP_FILE
902 fi
903
904 if [ "$ip_status" = "no" ]; then
905 if ocf_is_true ${OCF_RESKEY_lvs_support}; then
906 for i in `find_interface $OCF_RESKEY_ip 32`; do
907 case $i in
908 lo*)
909 remove_conflicting_loopback $OCF_RESKEY_ip 32 255.255.255.255 lo
910 ;;
911 esac
912 done
913 fi
914
915 add_interface $OCF_RESKEY_ip $NETMASK ${BRDCAST:-none} $NIC $IFLABEL
916 rc=$?
917
918 if [ $rc -ne $OCF_SUCCESS ]; then
919 ocf_exit_reason "Failed to add $OCF_RESKEY_ip"
920 exit $rc
921 fi
922 fi
923
924 case $NIC in
925 lo*)
926 : no need to run send_arp on loopback
927 ;;
928 *)
929 if [ $FAMILY = "inet" ];then
930 $ARP_SEND_FUN
931 else
932 if [ -x $SENDUA ]; then
933 run_send_ua
934 if [ $? -ne 0 ]; then
935 ocf_exit_reason "run_send_ua failed."
936 exit $OCF_ERR_GENERIC
937 fi
938 fi
939 fi
940 ;;
941 esac
942 exit $OCF_SUCCESS
943}
944
945ip_stop() {
946 local ip_del_if="yes"
947 if [ -n "$IP_CIP" ]; then
948 # Cluster IPs need special processing when the last bucket
949 # is removed from the node... take a lock to make sure only one
950 # process executes that code
951 ocf_take_lock $CIP_lockfile
952 ocf_release_lock_on_exit $CIP_lockfile
953 fi
954
955 if [ -f "$SENDARPPIDFILE" ] ; then
956 kill `cat "$SENDARPPIDFILE"`
957 if [ $? -ne 0 ]; then
958 ocf_log warn "Could not kill previously running send_arp for $OCF_RESKEY_ip"
959 else
960 ocf_log info "killed previously running send_arp for $OCF_RESKEY_ip"
961 rm -f "$SENDARPPIDFILE"
962 fi
963 fi
964 local ip_status=`ip_served`
965 ocf_log info "IP status = $ip_status, IP_CIP=$IP_CIP"
966
967 if [ $ip_status = "no" ]; then
968 : Requested interface not in use
969 exit $OCF_SUCCESS
970 fi
971
972 if [ -n "$IP_CIP" ] && [ $ip_status != "partial2" ]; then
973 if [ $ip_status = "partial" ]; then
974 exit $OCF_SUCCESS
975 fi
976 echo "-$IP_INC_NO" >$IP_CIP_FILE
977 if [ "x$(cat $IP_CIP_FILE)" = "x" ]; then
978 ocf_log info $OCF_RESKEY_ip, $IP_CIP_HASH
979 i=1
980 while [ $i -le $IP_INC_GLOBAL ]; do
981 ocf_log info $i
982 $IPTABLES -D INPUT -d $OCF_RESKEY_ip -i $NIC -j CLUSTERIP \
983 --new \
984 --clustermac $IF_MAC \
985 --total-nodes $IP_INC_GLOBAL \
986 --local-node $i \
987 --hashmode $IP_CIP_HASH
988 i=`expr $i + 1`
989 done
990 else
991 ip_del_if="no"
992 fi
993 fi
994
995 if [ "$ip_del_if" = "yes" ]; then
996 delete_interface $OCF_RESKEY_ip $NIC $NETMASK
997 if [ $? -ne 0 ]; then
998 ocf_exit_reason "Unable to remove IP [${OCF_RESKEY_ip} from interface [ $NIC ]"
999 exit $OCF_ERR_GENERIC
1000 fi
1001
1002 if ocf_is_true ${OCF_RESKEY_lvs_support}; then
1003 restore_loopback "$OCF_RESKEY_ip"
1004 fi
1005 fi
1006
1007 exit $OCF_SUCCESS
1008}
1009
1010ip_monitor() {
1011 # TODO: Implement more elaborate monitoring like checking for
1012 # interface health maybe via a daemon like FailSafe etc...
1013
1014 local ip_status=`ip_served`
1015 case $ip_status in
1016 ok)
1017 $ARP_SEND_FUN refresh
1018 return $OCF_SUCCESS
1019 ;;
1020 partial|no|partial2)
1021 exit $OCF_NOT_RUNNING
1022 ;;
1023 *)
1024 # Errors on this interface?
1025 return $OCF_ERR_GENERIC
1026 ;;
1027 esac
1028}
1029
1030# make sure that we have something to send ARPs with
1031set_send_arp_program() {
1032 ARP_SEND_FUN=run_send_arp
1033 if [ -n "$OCF_RESKEY_arp_sender" ]; then
1034 case "$OCF_RESKEY_arp_sender" in
1035 send_arp)
1036 check_binary $SENDARP
1037 ;;
1038 ipoibarping)
1039 check_binary ipoibarping
1040 ARP_SEND_FUN=run_send_ib_arp
1041 ;;
1042 *)
1043 ocf_exit_reason "unrecognized arp_sender value: $OCF_RESKEY_arp_sender"
1044 exit $OCF_ERR_CONFIGURED
1045 ;;
1046 esac
1047 else
1048 if is_infiniband; then
1049 ARP_SEND_FUN=run_send_ib_arp
1050 if ! have_binary ipoibarping; then
1051 [ "$__OCF_ACTION" = start ] &&
1052 ocf_log warn "using send_arp for infiniband because ipoibarping is not available (set arp_sender to \"send_arp\" to suppress this message)"
1053 check_binary $SENDARP
1054 ARP_SEND_FUN=run_send_arp
1055 fi
1056 fi
1057 fi
1058}
1059
1060ip_validate() {
1061 check_binary $IP2UTIL
1062 IP_CIP=
1063
1064 ip_init
1065
1066 set_send_arp_program
1067
1068 if [ -n "$IP_CIP" ]; then
1069 check_binary $IPTABLES
1070 check_binary $MODPROBE
1071 fi
1072
1073# $BASEIP, $NETMASK, $NIC , $IP_INC_GLOBAL, and $BRDCAST have been checked within ip_init,
1074# do not bother here.
1075
1076 if ocf_is_true "$OCF_RESKEY_unique_clone_address" &&
1077 ! ocf_is_true "$OCF_RESKEY_CRM_meta_globally_unique"; then
1078 ocf_exit_reason "unique_clone_address makes sense only with meta globally_unique set"
1079 exit $OCF_ERR_CONFIGURED
1080 fi
1081
1082 if ocf_is_decimal "$OCF_RESKEY_arp_interval" && [ $OCF_RESKEY_arp_interval -gt 0 ]; then
1083 :
1084 else
1085 ocf_exit_reason "Invalid OCF_RESKEY_arp_interval [$OCF_RESKEY_arp_interval]"
1086 exit $OCF_ERR_CONFIGURED
1087 fi
1088
1089 if ocf_is_decimal "$OCF_RESKEY_arp_count" && [ $OCF_RESKEY_arp_count -gt 0 ]; then
1090 :
1091 else
1092 ocf_exit_reason "Invalid OCF_RESKEY_arp_count [$OCF_RESKEY_arp_count]"
1093 exit $OCF_ERR_CONFIGURED
1094 fi
1095
1096 if [ -z "$OCF_RESKEY_preferred_lft" ]; then
1097 ocf_exit_reason "Empty value is invalid for OCF_RESKEY_preferred_lft"
1098 exit $OCF_ERR_CONFIGURED
1099 fi
1100
1101 if [ -n "$IP_CIP" ]; then
1102
1103 local valid=1
1104
1105 case $IP_CIP_HASH in
1106 sourceip|sourceip-sourceport|sourceip-sourceport-destport)
1107 ;;
1108 *)
1109 ocf_exit_reason "Invalid OCF_RESKEY_clusterip_hash [$IP_CIP_HASH]"
1110 exit $OCF_ERR_CONFIGURED
1111 ;;
1112 esac
1113
1114 if ocf_is_true ${OCF_RESKEY_lvs_support}; then
1115 ocf_exit_reason "LVS and load sharing not advised to try"
1116 exit $OCF_ERR_CONFIGURED
1117 fi
1118
1119 case $IF_MAC in
1120 [0-9a-zA-Z][13579bBdDfF][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z])
1121 ;;
1122 *)
1123 valid=0
1124 ;;
1125 esac
1126
1127 if [ $valid -eq 0 ]; then
1128 ocf_exit_reason "Invalid IF_MAC [$IF_MAC]"
1129 exit $OCF_ERR_CONFIGURED
1130 fi
1131
1132 fi
1133}
1134
1135if ocf_is_true "$OCF_RESKEY_unique_clone_address"; then
1136 prefix=`echo $OCF_RESKEY_ip | awk -F. '{print $1"."$2"."$3}'`
1137 suffix=`echo $OCF_RESKEY_ip | awk -F. '{print $4}'`
1138 suffix=`expr ${OCF_RESKEY_CRM_meta_clone:-0} + $suffix`
1139 OCF_RESKEY_ip="$prefix.$suffix"
1140fi
1141
1142case $__OCF_ACTION in
1143meta-data) meta_data
1144 ;;
1145usage|help) ip_usage
1146 exit $OCF_SUCCESS
1147 ;;
1148esac
1149
1150ip_validate
1151
1152case $__OCF_ACTION in
1153start) ip_start
1154 ;;
1155stop) ip_stop
1156 ;;
1157status) ip_status=`ip_served`
1158 if [ $ip_status = "ok" ]; then
1159 echo "running"
1160 exit $OCF_SUCCESS
1161 else
1162 echo "stopped"
1163 exit $OCF_NOT_RUNNING
1164 fi
1165 ;;
1166monitor) ip_monitor
1167 ;;
1168validate-all) ;;
1169*) ip_usage
1170 exit $OCF_ERR_UNIMPLEMENTED
1171 ;;
1172esac
1173# vi:sw=4:ts=8: