#!/bin/ksh93
#  ALTRAN_PROLOG_BEGIN_TAG
#  This is an automatically generated prolog.
#
#  Copyright (C) Altran ACT S.A.S. 2017,2018,2021.  All rights reserved.
#
#  ALTRAN_PROLOG_END_TAG
#
# @(#)  7d4c34b 43haes/usr/sbin/cluster/events/utils/cl_swap_IP_address.sh, 726, 2147A_aha726, Feb 05 2021 09:50 PM

###############################################################################
#
#  Name:  cl_swap_IP_address
#
#  This script is used during adapter swap and IP address takeover.
#  The first form, the specified interface is set from the old address
#  to the new address
#       cl_swap_IP_address cascading/rotating acquire/release interface \
#                new_address old_address netmask
#       i.e cl_swap_IP_address cascading acquire en0 \
#                1.1.1.2 1.1.1.1 255.255.255.128
#
#  The second form sets two interfaces in a single call. This is sometimes
#  necessary due to AIX routing quirks where an existing route gets 
#  spuriously deleted. An example where this is required is the case of 
#  swapping two interfaces.
#       cl_swap_IP_address swap_adapter swap \
#                interface1 address1 interface2 address2 netmask
#       i.e cl_swap_IP_address swap_adapter swap \
#                en0 1.1.1.1 en1 2.2.2.2 255.255.255.128
#
# Returns:      0 - success
#               1 - fatal error
#               2 - bad number of arguments
#               11 - recoverable error
# 
###############################################################################

###############################################################################
# Name: flush_arp
#
#	Flushes entire arp cache
#
# Returns: None.
###############################################################################
function flush_arp {
	[[ "$VERBOSE_LOGGING" == "high" ]] && set -x

	arp -an | grep "\?" | tr -d '()' | (while read host addr other ; do
		arp -d $addr
	done)
	return 0
}

###############################################################################
# Name: add_rc_check
#
#   Add an exit code check, message and error exit to a file.
#
# Arguments:
#       $1 - filename
#       $2 - function name (for message)
#
# Returns: N/A
###############################################################################
function add_rc_check {

    typeset PS4_FUNC="add_rc_check"
    [[ "$VERBOSE_LOGGING" == "high" ]] && set -x
    RR=$1
    FUNC=$2

    cat >>$RR<<-EOF
    rc=\$?
    if [[ \$rc != 0 ]]
    then
        echo "ERROR: $FUNC failed with code \$rc"
        cl_route_change_RC=\$rc
    fi

EOF

}




###############################################################################
# Name: check_alias_status
#
# Check that the desired address has been successfully aliased onto
# the adapter.
#
# Arguments:
#   Interface to check
#   IP address in dotted-decimal format
#   Netmask in dotted-decimal format
#
# Returns:
#   0 Interface has a proper entry
#   1 Interface does not have a proper entry
#
###############################################################################
function check_alias_status {

    [[ "$VERBOSE_LOGGING" == "high" ]] && set -x

    CH_INTERFACE=$1
    CH_ADDRESS=$2
    CH_ACQ_OR_RLSE=$3

    # Infiniband doesn't display alias addresses when the ifconfig command is
    # executed, so we have to get it from netstat

    # if we are called with seven arguments, then IF is not set. In that case,
    # use the CH_INTERFACE argument instead.

    IF_IB=${IF:-$CH_INTERFACE}
 
    IS_IB=$(echo $IF_IB|awk '{print index($0, "ib")}')
    
    if [[ $IS_IB != 1 ]]
    then
            ADDR=$(clifconfig $CH_INTERFACE | fgrep -w $CH_ADDRESS | awk '{print $2}')
    else
            ADDR=$(netstat -in | fgrep -w $CH_INTERFACE | awk '{print $4}' | fgrep -w $CH_ADDRESS)
    fi
    
    if [ "$CH_ACQ_OR_RLSE" = "acquire" ]
    then

        # if acquiring, make sure the alias IS on the interface
        [[ "$ADDR" != "$CH_ADDRESS" ]] && {
            cl_log 7324 "$PROGNAME: Failed to ifconfig alias $CH_ADDRESS on interface $CH_INTERFACE" $PROGNAME $CH_ADDRESS $CH_INTERFACE
            return 1
        }
    else
        # if releasing, make sure the alias IS NOT on the interface
        [[ "$ADDR" == "$CH_ADDRESS" ]] && {
            cl_log 7325 "$PROGNAME: Failed ifconfig delete alias $CH_ADDRESS from interface $CH_INTERFACE." $PROGNAME $CH_ADDRESS $CH_INTERFACE
            return 1
        }
    fi
    return 0
}


###############################################################################
# Name: alias_replace_routes
#
#   For the list of interfaces given on command line, moves each route
#   to 127.1 (lo0) and writes a file (name in $1) containing a shell
#   script that will restore the original routing table
#
# Arguments:
#       $1 - filename
#       $2 - failing interface name
#       Also makes use of following variables, global or set in main routine
#       LOCALNODENAME, NET, NETMASK, ADDR1, INVOCATION_FLAG, PERSISTENT,
#	ACQ_OR_RLSE
#
# Returns: 1 on error, 0 otherwise
###############################################################################
function alias_replace_routes {

    [[ "$VERBOSE_LOGGING" == "high" ]] && set -x

    RR=$1
    shift
    interfaces="$*"
    RC=0

    cp /dev/null $RR

    cat >$RR<<-EOF
	#!/bin/ksh
	#
	# Script created by $PROGNAME on $(date)
	#
	PATH=$PATH
	PS4='$PS4'
	export VERBOSE_LOGGING=\${VERBOSE_LOGGING:-"high"}
	[[ "\$VERBOSE_LOGGING" = "high" ]] && set -x
	: Starting \$0 at \$(date)
	#
	EOF

    #exclude all the IPv6 addresses(colon seperated) from netstat along with [Ll]ink.
    LOCADDRS=$(netstat -in | awk '$3 !~ "[Ll]ink" && $3 !~ ":" && $3 !~ "Network" {print $4}')

    netstat -rnC
    # according to the netstat command man page:
    # U = Up
    # c = Access to this route creates a cloned route
    # H = route to a host (use -host instead of -net)
    # W = the route is a cloned route
    # b = the route broadcast route

    for ifce in $interfaces ; do
	integer I=1
	NXTSVC=""
	#exclude all the IPv6 addresses(colon seperated) from netstat along with [Ll]ink.
       	IFADDRS=$(netstat -in | awk '$3 !~ "[Ll]ink" && $3 !~ ":" && ($1 == "'$ifce'" || $1 == "'$ifce\*'") {print $4}') 
	SVCADDRS=$(cllsif -J "$OP_SEP" -Spi $LOCALNODENAME | \
	           grep "$OP_SEP$NET$OP_SEP" | grep -E "${OP_SEP}service${OP_SEP}|${OP_SEP}persistent${OP_SEP}"\
	           | cut -d"$OP_SEP" -f7 | sort -u)
                
	#since, we are going to hadle only IPv4 service IP addresses exclude 
	#all the IPv6 service IP addresses from the SVCADDRS list
	SVCADDRS=$(echo "$SVCADDRS" | awk '$1 !~ ":" {print $1}')

	PERSISTENT_IP=$(cllsif -J "$OP_SEP" -Spi $LOCALNODENAME | \
	               grep "$OP_SEP$NET$OP_SEP" | grep -E \
	               "${OP_SEP}persistent${OP_SEP}" | cut -d"$OP_SEP" -f7)
	routeaddr=""
	for ifaddr in $IFADDRS
	do
	    for svcaddr in $SVCADDRS
	    do
		# Select only the addresses in the same subnet as that of the service address which we are releasing
		if [[ $(clgetnet $ifaddr $NETMASK) == $(clgetnet $svcaddr $NETMASK) && $(clgetnet $ifaddr $NETMASK) == $(clgetnet $ADDR1 $NETMASK) && "$ifaddr" == "$svcaddr" ]]
		then
		    if [[ -z "$routeaddr" ]]
		    then
			routeaddr=$ifaddr
		    fi
		    if [[ "$ifaddr" != "$ADDR1" && ( "$ADDR1" != "$PERSISTENT_IP" || ( ( ${INVOCATION_FLAG:-0} != 0 ) && ( $INVOCATION_FLAG == "USER_REQUESTED" ) && "$ADDR1" == "$PERSISTENT_IP" ) ) ]]
		    then
			NXTSVC=$svcaddr
			break
		    fi
		fi
	    done
	    if [[ -n $NXTSVC ]]
	    then
		break
	    fi
	done

        # If there is no service address in the same subnet, check if there are any other address
        # in the same subnet as that of the routes we are removing
        # as we needed to update when we are removing the address that has net routes.
        NXTADDR=""
	bootaddr=""
        if [[ -z $NXTSVC ]]
        then
		bootaddr=$(cllsif -J "$OP_SEP" -Spi $LOCALNODENAME | grep "$OP_SEP$NET$OP_SEP" | grep "${OP_SEP}boot${OP_SEP}" | awk -F"$OP_SEP" '$9 == "'$ifce'" { print $7; }')
                for ifaddr in $IFADDRS
                do
			if [[ "$ifaddr" == "$bootaddr" && $(clgetnet $ifaddr $NETMASK) == $(clgetnet $bootaddr $NETMASK) && $(clgetnet $ifaddr $NETMASK) == $(clgetnet $ADDR1 $NETMASK)  && "$ifaddr" != "$ADDR1" ]]
			then
				NXTADDR=$ifaddr
				break
			fi
                done
        fi

	swaproute=0
        NETSTAT_FLAGS="-nrf inet"

		    if [[ "$ADDR1" == "$routeaddr" ]]
		    then
			# swapping addr has net route
			swaproute=1
		    fi

	netstat $NETSTAT_FLAGS | fgrep -w $ifce | while read DEST GW FLAGS OTHER ; do
	    LOOPBACK="127.0.0.$I"

	    case $FLAGS in
		U|Uc)
		    if [[ $(clgetnet $ADDR1 $NETMASK) == $(clgetnet $GW $NETMASK) ]]
		    then
			if [[ "$ADDR1" == "$GW" || ("$PERSISTENT_IP" == "$GW" && -z $ACQ_OR_RLSE) ]]
			then
			    if [[ ( $PERSISTENT != "" || "$ADDR1" == "$GW" ) || ( ( ${INVOCATION_FLAG:-0} == 0 ) || ( $INVOCATION_FLAG != "USER_REQUESTED" ) ) ]]
			    then
				# all routes created by cloning are evil
				route delete -net $DEST $GW
			    fi
			fi
		    fi
		    ;;
		U*W*)
		    # all routes created by cloning are evil
		    route delete -host $DEST $GW
		    ;;
		U*H*b*)
		    # Delete broadcast routes if failing address or persistent IP is gateway
		    if [[ "$ADDR1" == "$GW" || ( "$PERSISTENT_IP" == "$GW" && -z $ACQ_OR_RLSE ) ]]
		    then
			if [[ ( $PERSISTENT != "" || "$ADDR1" == "$GW" ) || ( ( ${INVOCATION_FLAG:-0} == 0) || ( $INVOCATION_FLAG != "USER_REQUESTED" ) ) ]]
			then
			    route delete -host $DEST $GW
			fi
		    fi
		    ;;
		U*H*)
		    # If $DEST is a locally configured address, do
		    # nothing with this route since it may interfere
		    # with netmon logic.
		    integer found=0
		    for addr in $LOCADDRS
		    do
			if [[ "$addr" == "$DEST" ]]
			then
			    found=1
			    break
			fi
		    done
		    if (( $found == 0 ))
		    then
			if [[ $(clgetnet $ADDR1 $NETMASK) == $(clgetnet $GW $NETMASK) ]]
			then
			    if [[ ( ( ${INVOCATION_FLAG:-0} == 0 ) || ( $INVOCATION_FLAG != "USER_REQUESTED" ) ) || $swaproute == 1 ]]
			    then
				# into RR we put the route command:
				if [[ -z $ACQ_OR_RLSE || "$routeaddr" == "$PERSISTENT_IP" ]] && [[ -n "$routeaddr" ]]
				then
					print "cl_route_change -if$routeaddr $DEST $LOOPBACK $GW inet" >>$RR
		    			add_rc_check $RR "cl_route_change"
				else
					print "cl_route_change $DEST $LOOPBACK $GW inet" >>$RR
		    			add_rc_check $RR "cl_route_change"
				fi	
				# which will undo:
				cl_route_change $DEST $GW $LOOPBACK inet
				RC=$?
				: cl_route_change completed with $RC
				I=I+1
			    fi
			fi
		    fi
		    ;;
		U*)
		    if [[ $(clgetnet $ADDR1 $NETMASK) == $(clgetnet $GW $NETMASK) ]]
		    then
			if [[ ( ( ${INVOCATION_FLAG:-0} == 0 ) || ( $INVOCATION_FLAG != "USER_REQUESTED" ) ) || $swaproute == 1 ]]
			then
			    # into RR we put the route command:
			    if [[ -z $ACQ_OR_RLSE || "$routeaddr" == "$PERSISTENT_IP" ]] && [[ -n "$routeaddr" ]]
			    then
				print "cl_route_change -if$routeaddr $DEST $LOOPBACK $GW inet" >>$RR
		    		add_rc_check $RR "cl_route_change"
			    else
				print "cl_route_change $DEST $LOOPBACK $GW inet" >>$RR
		    		add_rc_check $RR "cl_route_change"
			    fi 
			    # which will undo:
			    cl_route_change $DEST $GW $LOOPBACK inet
			    RC=$?
			    : cl_route_change completed with $RC
			    I=I+1
			fi
		    fi
		    ;;
	    esac
	done
    done
    echo "exit \$cl_route_change_RC" >> $RR
    chmod +x $RR
    return $RC
}

###############################################################################
# Name: disable_pmtu_gated
#
#  Disable PMTU discovery and the gated daemon temporarily.
#  Global variables DISABLE_TCP_PMTU, DISABLE_UDP_PMTU and DISABLE_GATED
#  are set to retain current values.
#
# Arguments: 0
#
# Returns: N/A
###############################################################################
function disable_pmtu_gated {
	
	DISABLE_TCP_PMTU=0
	DISABLE_UDP_PMTU=0
	DISABLE_TCP_PMTU=$(no -o tcp_pmtu_discover | cut -f3,3 -d" ")
	DISABLE_UDP_PMTU=$(no -o udp_pmtu_discover | cut -f3,3 -d" ")
	no -o tcp_pmtu_discover=0
	no -o udp_pmtu_discover=0

	# Note that running gated is not presently supported by HACMP.
	# The user may experience side effects if gated is not implemented
	# properly.  HACMP does at least attempt to stop gated:

	ps -eo "pid,comm" | grep -qw gated 
	DISABLE_GATED=$?

	if (( $DISABLE_GATED == 0 ))
	then

            # This loop waits for gated to exit before continuing on.
            # The counter is set to 10 which means we will loop for
            # a maximum of 10 seconds before continuing on.  This
            # counter can be increased however if it is increased
            # it might be wise to also increase the nim grace period
            # to prevent false fail_adapter events from occuring.

	    integer GATED_COUNT=10;
	    stopsrc -s gated 2> /dev/null
	    while (( GATED_COUNT ))
	    do
		ps -eo "pid,comm" | grep -qw gated 
		if (( $? == 1 ))
		then
		break;
		fi
		(( GATED_COUNT-- ))
		sleep 1
	    done
	fi

	return 0
}
###############################################################################
# Name: enable_pmtu_gated
#
#  Reenable PMTU discovery and restart gated daemon.
#  Global variables DISABLE_TCP_PMTU, DISABLE_UDP_PMTU and DISABLE_GATED
#  hold values previously set in the disable_pmtu_gated subroutine.
#
# Arguments: 0
#
# Returns: N/A
###############################################################################
function enable_pmtu_gated {
	
	# if PMTU discovery was disabled, then reenable it.

	no -o tcp_pmtu_discover=$DISABLE_TCP_PMTU
	no -o udp_pmtu_discover=$DISABLE_UDP_PMTU

	# if gated was on when we entered this script, start it back

        [[ $DISABLE_GATED == 0 ]] &&
        {
            startsrc -s gated;
            sleep 5;
	}

	return 0
}


###############################################################################
#
# Main entry point
#
###############################################################################

PROGNAME=${0##*/}
export PATH="$(/usr/es/sbin/cluster/utilities/cl_get_path all)"

# Including Availability metrics library file
. /usr/es/lib/ksh93/availability/cl_amlib

[[ "$VERBOSE_LOGGING" == "high" ]] && {
    set -x
    version='1.9.14.8'
}
OP_SEP="$(cl_get_path -S)"
export LC_ALL=C
RESTORE_ROUTES=/usr/es/sbin/cluster/.restore_routes

cl_echo 33 "Starting execution of $0 with parameters $*" $0 "$*"

typeset -i oslevel
oslevel=$(/usr/bin/oslevel -r|/usr/bin/sed 's/-//g')


# the number of arguments passed determines (in part) what kind of
# swap we will be doing
[[ $# == 6 || $# == 7 ]] && {
    if [[ $# == 7 ]] ; then
        #  If PMTU discovery is enabled, we need to disable it temporarily.
        #  If gated is running , disable it temporarily
   	disable_pmtu_gated
    fi

    # save the current setting of ipignoreredirects then turn it on
    PRIOR_IPIGNORE_REDIRECTS_VALUE=$(no -a | grep ipignoreredirects | awk '{ print $3 }')
    /usr/sbin/no -o ipignoreredirects=1
}

# if JOB_TYPE is set, and it doesn't equal to "GROUP", then 
# we are processing for process_resources
PROC_RES=false
if [[ ${JOB_TYPE:-0} != 0 && $JOB_TYPE != "GROUP" ]]; then
   PROC_RES=true
fi

set -u

RC=0

case $# in
    #
    # This form changes a single interface, e.g. acquiring or releasing 
    #
    6)
	netstat -in
	netstat -rnC
        CASC_OR_ROT=$1		# rg relationship which owns the label
        ACQ_OR_RLSE=$2		# action
	IF=$3 			# this is the interface (adapter)
	ADDR=$4			# address to be acquired or released
	OLD_ADDR=$5		# current address on the interface
	NETMASK=$6		#

	# For cascading release, need to get the boot adapter list
        if [[ "$CASC_OR_ROT" == "cascading" && "$ACQ_OR_RLSE" == "release" ]]
	then
	    NET=$(cllsif -J "$OP_SEP" -Sw -n $OLD_ADDR | cut -f3 -d"$OP_SEP")
            ADDR=$(cllsif -J "$OP_SEP" -Si $LOCALNODENAME | 
                   grep "${OP_SEP}$NET${OP_SEP}" | \
                   grep "${OP_SEP}boot${OP_SEP}" | cut -d"$OP_SEP" -f7 |
                   tail -1)

  	fi

	# get the network this new addr belongs to
	NET=$(cllsif -J "$OP_SEP" -Sw -n $ADDR | cut -f3 -d"$OP_SEP")

		# figure out how to set the alias first option
		ALIAS_FIRST=$(clodmget -q"identifier=$ADDR" -f max_aliases -n HACMPadapter)
		NUM_ADDRS=$(LC_ALL=C ifconfig $IF | grep -c -w "inet")
       		if [[ "$ACQ_OR_RLSE" == "acquire" ]]
       		then
                    # Format for consumption by cl_am utility
                    amlog_trace $AM_SERVICEIP_ALIAS_BEGIN "Aliasing Service IP|$ADDR"
            
                    cl_echo 7310 "$PROGNAME: Configuring network interface $IF with aliased IP address $ADDR" $PROGNAME $IF $ADDR

		    # alias add the specified address to the specified interface
		    case $ALIAS_FIRST in
			    1027|771|1539|1283)
			    	#The user has chosen this address to be the first Address.
				clifconfig $IF alias $ADDR netmask $NETMASK firstalias
				;;
			    0|1|2)
			    	#Find if other service addresses are present on this adapter.
				# if not then this will be the first address.
				if (( $NUM_ADDRS > 1 ))
				then
					clifconfig $IF alias $ADDR netmask $NETMASK
				else
					clifconfig $IF alias $ADDR netmask $NETMASK firstalias
				fi

				;;
			    515)
			    	#If persistent is on this adapter it will stay as First
				#If not the service will be first.
				PERSIST_IF=""
				PERSISTON=$(cllsif -J "$OP_SEP" -Sp $LOCALNODENAME | \
					awk -F"$OP_SEP" -v NET="$NET" '{if ($2 == "persistent" \
					&& $3 == NET) print $1}' | sort)
				if [[ -n "$PERSISTON" ]]
				then
					PERSIST_IF=$(LC_ALL=C clgetif -a $PERSISTON 2>/dev/null)
				fi
				if [[ -n "$PERSIST_IF"  && $IF == $PERSIST_IF ]]
				then
					clifconfig $IF alias $ADDR netmask $NETMASK 
				else
					# There might be an address with 1539.
					if (( $NUM_ADDRS > 1 ))
					then
						clifconfig $IF alias $ADDR netmask $NETMASK
					else
						clifconfig $IF alias $ADDR netmask $NETMASK firstalias
					fi
				fi
				;;
			    *)
				clifconfig $IF alias $ADDR netmask $NETMASK 
				;;
                    esac

		    # tell the clstrmgr to start monitoring this alias
		    hats_adapter_notify $IF -e $ADDR alias

                    # make sure the alias is there
		    check_alias_status $IF $ADDR $ACQ_OR_RLSE
		    RC=$?
                    # Format for consumption by cl_am utility
                    if [[ $RC != 0 ]]
                    then
                        amlog_err $AM_SERVICEIP_ALIAS_FAILURE "Aliasing Service IP|$ADDR"
                    else
                        amlog_trace $AM_SERVICEIP_ALIAS_END "Aliasing Service IP|$ADDR"
                    fi

		else
		    cl_echo 7320 "$PROGNAME: Removing aliased IP address $OLD_ADDR from adapter $IF" $PROGNAME $OLD_ADDR $IF
                    # Format for consumption by cl_am utility
                    amlog_trace $AM_SERVICEIP_DEALIAS_BEGIN "Deliasing Service IP|$OLD_ADDR"
		    PERSISTENT=""
		    ADDR1=$OLD_ADDR
		    disable_pmtu_gated
		    alias_replace_routes $RESTORE_ROUTES $IF
		    RC=$?
		    : alias_replace_routes completed with $RC

		    # delete the alias for the specified address
       		    clifconfig $IF delete $OLD_ADDR

                    if [[ $swaproute == 1 ]]
                    then
                        if [[ -n $NXTSVC ]]
                        then
                            TEMP_ALIAS_FIRST=$(clodmget -q "identifier=$NXTSVC" -f max_aliases HACMPadapter)
                            TEMP_NUM_ADDRS=$(LC_ALL=C ifconfig $IF | grep -v $NXTSVC | grep -c -w "inet")
                            # alias add the specified address to the specified interface
                            case $TEMP_ALIAS_FIRST in
                                    1027|771|1539)
                                        #The user has chosen this address to be the first Address.
                                        clifconfig $IF alias $NXTSVC netmask $NETMASK firstalias
                                        ;;
                                    0|1|2)
                                        #Find if other service addresses are present on this adapter.
                                        # if not then this will be the first address.
                                        if (( $TEMP_NUM_ADDRS > 1 ))
                                        then
                                                clifconfig $IF alias $NXTSVC netmask $NETMASK
                                        else
                                                clifconfig $IF alias $NXTSVC netmask $NETMASK firstalias
                                        fi

                                        ;;
                                    515)
                                        #If persistent is on this adapter it will stay as First
                                        #If not the service will be first.
                                        PERSIST_IF=""
                                        PERSISTON=$(cllsif -J "$OP_SEP" -Sp $LOCALNODENAME | \
                                        awk -F"$OP_SEP" -v NET="$NET" '{if ($2 == "persistent" \
                                                && $3 == NET) print $1}' | sort)
                                        if [[ -n "$PERSISTON" ]]
                                        then
                                                PERSIST_IF=$(LC_ALL=C clgetif -a $PERSISTON 2>/dev/null)
                                        fi
                                        if [[ -n "$PERSIST_IF"  && $IF == $PERSIST_IF ]]
                                        then
                                                clifconfig $IF alias $NXTSVC netmask $NETMASK
                                        else
                                                # There might be an address with 1539.
                                                if (( $TEMP_NUM_ADDRS > 1 ))
                                                then
                                                        clifconfig $IF alias $NXTSVC netmask $NETMASK
                                                else
                                                        clifconfig $IF alias $NXTSVC netmask $NETMASK firstalias
                                                fi
                                        fi
                                        ;;
                                    *)
                                        clifconfig $IF alias $NXTSVC netmask $NETMASK
                                        ;;
                            esac
                        elif [[ -n $NXTADDR ]]
                        then
                            # In case of single adapter network, the bootIP and serviceIP are in same subnet, during ServiceIP 
                            # acquitstion bootIP routes are replaced with serviceIP routes. We are aliasing the bootIP
                            # to restore the bootIP routes during the ServiceIP release.
                            # But this alias may lead false network up events due to CAA and RSCT issues
                            # To avoid this we are adding sleep 6seconds before aliasing the bootIP, so that CAA and RSCT will not
                            # generate any false events. This is a temporary fix till RSCT gives a permanent fix and this fix is required 
                            # for AIX 71 TL3 and earlier verions of AIX 71 TL 3 releases
                            if (( $oslevel <= 710003 ))
                            then
                                sleep 6
                            fi
                            clifconfig $IF alias $NXTADDR netmask $NETMASK
                        fi
                    fi

		    $RESTORE_ROUTES
		    [[ $? == 0 && $RC != 0 ]] && RC=1
                    : Completed $RESTORE_ROUTES with return code $RC
		    enable_pmtu_gated       # Reset pmtu and restart gated

		    # tell the clstrmgr not to monitor this alias any more
		    hats_adapter_notify $IF -d $OLD_ADDR alias
        	
                    # verify the alias was removed
		    check_alias_status $IF $OLD_ADDR $ACQ_OR_RLSE
                    RC1=$?
		    [[ $RC1 == 0 && $RC != 0 ]] && RC=1
            
                    # Format for consumption by cl_am utility
                    if [[ $RC1 != 0 ]]
                    then
                        amlog_err $AM_SERVICEIP_DEALIAS_FAILURE "Dealiasing Service IP|$OLD_ADDR"
                    else
                        amlog_trace $AM_SERVICEIP_DEALIAS_END "Deliasing Service IP|$OLD_ADDR"
                    fi
            
                fi
        	[[ $RC != 0 ]] &&
        	{
			[[ -z $ADDR ]] && ADDR=$OLD_ADDR
			# tell the resource manager the results
			cl_RMupdate resource_error $ADDR $PROGNAME

			if [[ $PROC_RES == false ]]; then
			    exit 1
			else
			    exit 11
			fi

        	}
		flush_arp

	netstat -in
	netstat -rnC
	;;

    #
    # this form for changing two address e.g. swap_adapter
    #
    7)
        UNUSED_1=$1
        UNUSED_2=$2
	IF1=$3		# interface currently holding the standby address
	ADDR1=$4	# the address to be recovered (service)
	IF2=$5		# interface currently holding the service
	ADDR2=$6	# the standby address 
	NETMASK=$7
	ACQ_OR_RLSE=""

	PERSISTENT=$(clodmget -q "function=persistent and nodename=$LOCALNODENAME and identifier=$ADDR1" -f ip_label -n HACMPadapter)
	if [[ -n $PERSISTENT ]]; then
		# The label passed in as the "service" label is actually
		# a persistent label, and it should be swapped to the
		# selected boot/standby.  So just do that then exit.
		NET=$(cllsif -J "$OP_SEP" -Sw -n $ADDR2 | cut -f3 -d"$OP_SEP")
		cl_configure_persistent_address swap -n $NET -a $IF1 -f $IF2
		RC=$?
		netstat -in
		netstat -rnC
		flush_arp
            	enable_pmtu_gated   #Reset pmtu and restart gated
	    	/usr/sbin/no -o ipignoreredirects=$PRIOR_IPIGNORE_REDIRECTS_VALUE
		exit $RC
	fi


        #
        # AIX route management
        #
        # This support in AIX currently only works for IPv4. This 
        # change assumes AF_INET returned by cllsif is determined
        # by family_type = 1 in HACMPnetwork and further this means
        # the specified IP address is an IPV4 address. The assumption
        # is that AF_INET6 is used for IPV6 and AF_INET is used for IPV4
        #
         
	NET=$(cllsif -J "$OP_SEP" -Sw -n $ADDR2 | cut -f3 -d"$OP_SEP")
        NET_FAMILY=$(cllsif -J "$OP_SEP" -Sw -n $ADDR1 | cut -f15 -d "$OP_SEP")

           # new transfer argument is only for IPV4
           # If the IF1 and IF2 are same or the address does not exist on $IF2 transfer is not used as it fails 
	   if [[ "$NET_FAMILY" == "AF_INET" && $IF1 != $IF2 && 
             -n  $(ifconfig $IF2 | awk -v serv_addr=$ADDR1 '($1=="inet" && $2==serv_addr)') ]]
	   then
                # get the setting for first alias option
                ALIAS_FIRST_TRANS=$(clodmget -q"identifier=$ADDR1" -f max_aliases HACMPadapter)
                NUM_ADDRS_ON_TOINF=$(LC_ALL=C ifconfig $IF1 | grep -c -w "inet")

                case $ALIAS_FIRST_TRANS in
                    1027|771|1539|1283)
                        #The user has chosen this address to be the first Address.
                        clifconfig $IF2 $ADDR1 transfer $IF1 firstalias
                        ;;
                    0|1|2)
                        #Find if other service addresses are present on this adapter.
                        # if not then this will be the first address.
                        if (( $NUM_ADDRS_ON_TOINF > 1 ))
                        then
                                clifconfig $IF2 $ADDR1 transfer $IF1
                        else
                                clifconfig $IF2 $ADDR1 transfer $IF1 firstalias
                        fi
                        ;;
                    515)
                        #If persistent is on this adapter it will stay as First
                        #If not the service will be first.
                        PERSIST_IF=""
                        PERSISTON=$(cllsif -J "$OP_SEP" -Sp $LOCALNODENAME | \
                                awk -F"$OP_SEP" -v NET="$NET" '{if ($2 == "persistent" \
                                && $3 == NET) print $1}' | sort)
                        if [[ -n "$PERSISTON" ]]
                        then
                                PERSIST_IF=$(LC_ALL=C clgetif -a $PERSISTON 2>/dev/null)
                        fi
                        if [[ -n "$PERSIST_IF"  && $IF1 == $PERSIST_IF ]]
                        then
                                clifconfig $IF2 $ADDR1 transfer $IF1
                        else
                                # There might be an address with 1539.
                                if (( $NUM_ADDRS_ON_TOINF > 1 ))
                                then
                                        clifconfig $IF2 $ADDR1 transfer $IF1
                                else
                                        clifconfig $IF2 $ADDR1 transfer $IF1 firstalias
                                fi
                        fi
                        ;;
                    *)
                        clifconfig $IF2 $ADDR1 transfer $IF1
                        ;;
                esac

	   else
                
		# handle the swap without using the ifconfig transfer option
		alias_replace_routes $RESTORE_ROUTES $IF2
		RC=$?

		if [[ (${INVOCATION_FLAG:-0} == 0) || ($INVOCATION_FLAG != "USER_REQUESTED") ]]
		then
		    cl_configure_persistent_address swap -n $NET -a $IF1 -f $IF2
		    netstat -in
		    netstat -rnC
		fi

		# Delete the alias service from the current interface
     		clifconfig $IF2 delete $ADDR1

		# add the alias service to the target interface
                ALIAS_FIRST_TRANS=$(clodmget -q"identifier=$ADDR1" -f max_aliases HACMPadapter)
                NUM_ADDRS_ON_TOINF=$(LC_ALL=C ifconfig $IF1 | grep -c -w "inet")
                case $ALIAS_FIRST_TRANS in
                        1027|771|1539|1283)
                            #The user has chosen this address to be the first Address.
                            clifconfig $IF1 alias $ADDR1 netmask $NETMASK firstalias
                           ;;
                        0|1|2)
                           #Find if other service addresses are present on this adapter.
                           # if not then this will be the first address.
                           if (( $NUM_ADDRS_ON_TOINF > 1 ))
                           then
                                   clifconfig $IF1 alias $ADDR1 netmask $NETMASK
                           else
                                   clifconfig $IF1 alias $ADDR1 netmask $NETMASK firstalias
                           fi
                           ;;
                        515)
                           #If persistent is on this adapter it will stay as First
                           #If not the service will be first.
                           PERSIST_IF=""
                           PERSISTON=$(cllsif -J "$OP_SEP" -Sp $LOCALNODENAME | \
                                   awk -F"$OP_SEP" -v NET="$NET" '{if ($2 == "persistent" \
                                  && $3 == NET) print $1}' | sort)
                           if [[ -n "$PERSISTON" ]]
                            then
                                   PERSIST_IF=$(LC_ALL=C clgetif -a $PERSISTON 2>/dev/null)
                          fi
                           if [[ -n "$PERSIST_IF"  && $IF1 == $PERSIST_IF ]]
                           then
                                   clifconfig $IF1 alias $ADDR1 netmask $NETMASK
                           else
                                   # There might be an address with 1539.
                                    if (( $NUM_ADDRS_ON_TOINF > 1 ))
                                    then
                                          clifconfig $IF1 alias $ADDR1 netmask $NETMASK
                                   else
                                          clifconfig $IF1 alias $ADDR1 netmask $NETMASK firstalias
                                   fi
                           fi
                           ;;
                       *)
                           clifconfig $IF1 alias $ADDR1 netmask $NETMASK
                           ;;
                esac

		# Add back pre-existing routes
		$RESTORE_ROUTES
		RC=$?
		: Completed $RESTORE_ROUTES with return code $RC
		if [[ ( ${INVOCATION_FLAG:-0} != 0 ) && ( $INVOCATION_FLAG == "USER_REQUESTED" ) ]]
		then
		    if [[ -n $NXTSVC && $swaproute == 1 ]]
		    then
                        clifconfig $ifce alias $NXTSVC netmask $NETMASK
		    fi
		fi

                #
                # If the subnets are equal and there is a default route
                # on this interface bounce the boot on failed interface.
                # This will move the default route to working adapter.
                #
                # find boots
                BOOT1=$(cllsif -J "$OP_SEP" -Si $LOCALNODENAME | \
                            awk -F"$OP_SEP" -v if1=$IF1 \
                            '($2=="boot" && $9==if1) {printf("%s\n",$7)}')
                BOOT2=$(cllsif -J "$OP_SEP" -Si $LOCALNODENAME | \
                            awk -F"$OP_SEP" -v if2=$IF2 \
                            '($2=="boot" && $9==if2) {printf("%s\n",$7)}')
                # find route to delete 
                BROUTE=$(netstat -rn|awk -v if2=$IF2 -v boot2=$BOOT2 \
                       '(NR > 4 && $6==if2 && $1!="default" && $2==boot2 && $3=="U") \
                       {printf("%s %s",$1,$2)}')

                if [[ $(clgetnet $BOOT1 $NETMASK) == $(clgetnet $BOOT2 $NETMASK) || 
                   ($(clgetnet $BOOT2 $NETMASK) == $(clgetnet $ADDR1 $NETMASK)) ]]
                then
                    # The subnets are equal check for default.
                    DEFAULTROUTE=$(netstat -rn|awk -v if2=$IF2 '(NR > 4 && $6==if2 && $1=="default")')
                    if [[ -n $DEFAULTROUTE ]]
                    then
	    		#exclude all the IPv6 addresses(colon seperated) from netstat along with [Ll]ink.
			LOCADDRS=$(netstat -in | awk '$3 !~ "[Ll]ink" && $3 !~ ":" && $3 !~ "Network" && ($1 == "'$IF2'" || $1 == "'$IF2\*'") {print $4}')
			SVCADDRS=$(cllsif -J "$OP_SEP" -Spi $LOCALNODENAME | \
			             grep "$OP_SEP$NET$OP_SEP" | grep -E \
			             "${OP_SEP}service${OP_SEP}" | cut -d"$OP_SEP" -f7 | sort -u)
                         
			#since, we are going to hadle only IPv4 service IP addresses 
			#exclude all the IPv6 service IP addresses from the SVCADDRS list
			SVCADDRS=$(echo "$SVCADDRS" | awk '$1 !~ ":" {print $1}')
			integer found=0
			for locaddr in $LOCADDRS
			do
			    for svcaddr in $SVCADDRS
			    do
				if [[ "$locaddr" == "$svcaddr" || ("$locaddr" == "$PERSISTENT_IP" && $(clgetnet $locaddr $NETMASK) == $(clgetnet $BOOT2 $NETMASK)) ]]
				then
				    found=1
				    break
				fi
			    done
			    if (( $found == 1 ))
			    then
				break
			    fi
			done
			
			set -A locaddrs $LOCADDRS
			LADDRLIST=$(echo ${locaddrs[@]} | tr ' ' '\n' | sort -u)
			if [[ $found == 0 && ( ( ${INVOCATION_FLAG:-0} == 0 ) || ( $INVOCATION_FLAG != "USER_REQUESTED" ) ) ]]
			then
                            clifconfig $IF2 delete $BOOT2
                            clifconfig $IF2 alias $BOOT2 netmask $NETMASK

                   	    if [[ -n $BROUTE ]]
                   	    then
                      		route delete -net $BROUTE
			    fi
			fi
                    fi
                fi
	    fi  # non- transfer capable

		netstat -in
		netstat -rnC
		flush_arp
		enable_pmtu_gated

		# Tell the cluster manager to start monitoring this alias
		hats_adapter_notify $IF1 -e $ADDR1  alias

		check_alias_status $IF1 $ADDR1 "acquire"
        	(( $? != 0 )) &&
                {
			# tell the resource manager the results
			cl_RMupdate resource_error $ADDR1 $PROGNAME
			
			if [[ $PROC_RES == false ]]; then
			    exit 1
			else
			    exit 11
			fi
                }

		enable_pmtu_gated
	;;

    *)
    # bad arg count
    echo "$PROGNAME An internal error occured.\nPlease report this problem to IBM  support."
    exit 2

esac

# restore ipignoreredirects to its original value
no -o ipignoreredirects=$PRIOR_IPIGNORE_REDIRECTS_VALUE


cl_echo 32 "Completed execution of $0 with parameters $*.  Exit status = $RC" $0 "$*" $RC

date

exit $RC
