#!/bin/bash ####################################################################### # Initialization: : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs # Defaults OCF_RESKEY_cidr_netmask_default="32" OCF_RESKEY_base_veth_default="" # may be omited if OVS used OCF_RESKEY_gateway_default="none" # can be "none", "link", IPaddr OCF_RESKEY_gateway_metric_default=0 # can be "", or metric value OCF_RESKEY_also_check_interfaces_default="" # can be "", or list of interfaces OCF_RESKEY_other_networks_default="" # can be "", or list of networks in CIDR format : ${HA_LOGTAG="ocf-ns_IPaddr2"} : ${HA_LOGFACILITY="daemon"} : ${OCF_RESKEY_cidr_netmask=${OCF_RESKEY_cidr_netmask_default}} : ${OCF_RESKEY_base_veth=${OCF_RESKEY_base_veth_default}} : ${OCF_RESKEY_gateway=${OCF_RESKEY_gateway_default}} : ${OCF_RESKEY_gateway_metric=${OCF_RESKEY_gateway_metric_default}} : ${OCF_RESKEY_also_check_interfaces=${OCF_RESKEY_also_check_interfaces_default}} : ${OCF_RESKEY_other_networks=${OCF_RESKEY_other_networks_default}} FAMILY='inet' RUN_IN_NS="ip netns exec $OCF_RESKEY_ns " SH="/bin/bash" ####################################################################### ####################################################################### meta_data() { cat < 1.0 This Linux-specific resource manages IP address inside network namespace. This Linux-specific resource manages IP address inside network namespace. Name of the bridge that has network namespace with VIP connected to it. Name of the bridge. The IPv4 address to be configured in dotted quad notation, for example "192.168.1.1". IPv4 address The netmask for the interface in CIDR format (e.g., 24 and not 255.255.255.0) If unspecified, the script will also try to determine this from the routing table. CIDR netmask You can specify an additional label for your IP address here. This label is appended to your interface name. A label can be specified in nic parameter but it is deprecated. If a label is specified in nic name, this parameter has no effect. Interface label Name of network namespace.\n Should be present. Name of network namespace. Name of base system side veth pair tail.\n Should be present. Name of base system side veth pair tail. Name of net.namespace side veth pair tail.\n Should be present. Name of net.namespace side veth pair tail. Default route address.\n Can be "", "link" or IP address. Default route address. Default route address.\n Can be "", "link" or IP address. Default route address. Iptables rules that should be started along with IP in the namespace.\n Iptables rules associated with IP start in ns. Iptables rules that should be stopped along with IP in the namespace.\n Iptables rules associated with IP stop in ns. Iptables comment to associate with rules.\n Iptables comment to associate with rules. Network interfaces list (ex. NIC), that should be in UP state for monitor action returns succesful.\n Network interfaces list (ex. NIC), that should be in UP state for monitor action returns succesful. Additional routes that should be added to this resource. Routes will be added via value ns_veth. Should be space separated list of networks in CIDR format. List of addtional routes to add routes for. END exit $OCF_SUCCESS } ip_validate() { if [[ X`uname -s` != "XLinux" ]] ; then ocf_log err "ns_IPaddr2 only supported Linux." exit $OCF_ERR_INSTALLED fi if [ -z "$OCF_RESKEY_ip" ] ; then ocf_log err "IP address not given" exit $OCF_ERR_CONFIGURED fi if [ -z "$OCF_RESKEY_ns" ] ; then ocf_log err "Network namespace not given" exit $OCF_ERR_CONFIGURED fi if [ -z "$OCF_RESKEY_cidr_netmask" ] ; then ocf_log err "CIDR Netmask not given" exit $OCF_ERR_CONFIGURED fi if [ -z "$OCF_RESKEY_ns_veth" ] ; then ocf_log err "NS veth tail name not given" exit $OCF_ERR_CONFIGURED fi if ! ocf_is_decimal "$OCF_RESKEY_gateway_metric"; then ocf_log err "Gateway_metric should be a positive digital value" exit $OCF_ERR_CONFIGURED fi return $OCF_SUCCESS } # # Find out which interfaces serve the given IP address and netmask. # The arguments are an IP address and a netmask. # Its output are interface names devided by spaces (e.g., "eth0 eth1"). # find_interface() { local ipaddr="$1" local netmask="$2" [ -z "$netmask" ] || ipaddr="$ipaddr/$netmask" # # List interfaces but exclude FreeS/WAN ipsecN virtual interfaces local iface="`ip -o -f inet addr show \ | grep "\ $ipaddr" \ | cut -d ' ' -f2 \ | grep -v '^ipsec[[0-9]][[0-9]]*$'`" local rc=$? echo "$iface" return $rc } find_interface_in_ns() { local ns="$1" local ipaddr="$2" local netmask="$3" [ -z "$netmask" ] || ipaddr="$ipaddr/$netmask" # # List interfaces but exclude FreeS/WAN ipsecN virtual interfaces local iface=`ip netns exec $ns ip -o -f inet addr show \ | grep "\ $ipaddr" \ | cut -d ' ' -f2 \ | grep -v '^ipsec[[0-9]][[0-9]]*$'` local rc=$? echo "$iface" return $rc } setup_routes() { local network if [ ! -z "$OCF_RESKEY_other_networks" ] ; then for network in ${OCF_RESKEY_other_networks} ; do ocf_log debug "Adding route on the host system to ${network}: ${OCF_RESKEY_namespace_ip}" ocf_run $RUN_IN_NS ip route add ${network} dev ${OCF_RESKEY_ns_veth} done fi } # add veth to bridge if not already added add_to_bridge() { local br="$1" local veth="$2" local ns_veth="$3" local bridge_mtu=`cat /sys/class/net/${br}/mtu` # check which bridge (OVS or LNX) used if [[ -d /sys/class/net/${br}/brif ]] ; then # LNX if [[ ! -d /sys/class/net/${br}/brif/${veth} ]] ; then # attach 1-st jack to an LNX bridge ocf_run brctl addif $br $veth || return $OCF_ERR_GENERIC fi else # OVS ovs-vsctl list port $veth > /dev/null 2>&1 if [[ $? != 0 ]] ; then # attach 1-st jack to an OVS bridge ocf_run ovs-vsctl --may-exist add-port $br $veth || return $OCF_ERR_GENERIC fi fi # adjust MTU ocf_run ip link set mtu $bridge_mtu dev $veth ocf_run $RUN_IN_NS ip link set mtu $bridge_mtu dev $ns_veth return $OCF_SUCCESS } remove_from_bridge() { if [[ -d /sys/class/net/${OCF_RESKEY_bridge}/brif ]] ; then # native linux bridges if [[ -d /sys/class/net/${OCF_RESKEY_bridge}/brif/${OCF_RESKEY_base_veth} ]] ; then ocf_run brctl delif $OCF_RESKEY_bridge $OCF_RESKEY_base_veth || return $OCF_ERR_GENERIC else ocf_log warn "Jack ${OCF_RESKEY_base_veth} not a member of ${OCF_RESKEY_bridge} yet." fi else # OVS bridge ocf_run ovs-vsctl del-port $OCF_RESKEY_bridge $OCF_RESKEY_base_veth || return $OCF_ERR_GENERIC fi return $OCF_SUCCESS } ####################################################################### check_ns() { local ns=$(ip netns list | awk "/${OCF_RESKEY_ns}/ {print \$1}") [[ "$ns" != "$OCF_RESKEY_ns" ]] && return $OCF_ERR_GENERIC return $OCF_SUCCESS } get_ns() { local rc check_ns || ocf_run ip netns add $OCF_RESKEY_ns ocf_run $RUN_IN_NS ip link set up dev lo || return $OCF_ERR_GENERIC return $OCF_SUCCESS } get_or_create_veth_pair() { local rc local rc1 # check tail of veth-pair in base system ip link show $OCF_RESKEY_base_veth 2>&1 > /dev/null rc=$? if [[ $rc != 0 ]] ; then # 1st jack not found, need to create pair and attach 2nd jack to the net.namespace # create veth pair and put 2nd jack to the net.ns ocf_run ip link add $OCF_RESKEY_base_veth type veth peer name $OCF_RESKEY_ns_veth ocf_run ip link set dev $OCF_RESKEY_ns_veth netns $OCF_RESKEY_ns ocf_run ip link set up dev $OCF_RESKEY_base_veth ocf_run $RUN_IN_NS ip link set up dev $OCF_RESKEY_ns_veth sleep 1 # connect veth-pair to the bridge and adjust MTU add_to_bridge $OCF_RESKEY_bridge $OCF_RESKEY_base_veth $OCF_RESKEY_ns_veth fi return $OCF_SUCCESS } check_interfaces_for_up_state() { local interfaces=$(echo "$1" | tr " ,:;" "\n") local rc=$OCF_SUCCESS for i in $interfaces ; do rv=$(cat /sys/class/net/$i/carrier) # can return non-zero error-code for administrative-downed interface if [[ $? != 0 || $rv != "1" ]] ; then rc=$OCF_NOT_RUNNING break fi done return $rc } ip_prepare() { local rc ip_validate [[ $? != 0 ]] && return $OCF_ERR_GENERIC # create or get existing network namespace get_ns || return $OCF_ERR_GENERIC # create or get existing pair of veth interfaces get_or_create_veth_pair || return $OCF_ERR_GENERIC # attach IP address inside network namespace ocf_run $RUN_IN_NS ip addr replace "$OCF_RESKEY_ip/$OCF_RESKEY_cidr_netmask" dev $OCF_RESKEY_ns_veth [[ $? != 0 ]] && return $OCF_ERR_GENERIC # setup default routing in namespace if gateway given if [[ "$OCF_RESKEY_gateway" == 'link' ]] ; then ocf_run $RUN_IN_NS ip route replace default dev $OCF_RESKEY_ns_veth metric $OCF_RESKEY_gateway_metric elif [[ "$OCF_RESKEY_gateway" == 'none' ]] ; then echo "Setup default gateway -- do nothing." else ocf_run $RUN_IN_NS ip route replace default via $OCF_RESKEY_gateway metric $OCF_RESKEY_gateway_metric fi # Send Gratuitous ARP REQUEST packets to update all neighbours in a detached background process ARGS="-U -c 32 -w 10 -I $OCF_RESKEY_ns_veth -q $OCF_RESKEY_ip" $RUN_IN_NS arping $ARGS 2>&1 > /dev/null & # Send Gratuitous ARP REPLY packets to update all neighbours in a detached background process ARGS="-A -c 32 -w 10 -I $OCF_RESKEY_ns_veth -q $OCF_RESKEY_ip" $RUN_IN_NS arping $ARGS 2>&1 > /dev/null & return $OCF_SUCCESS } iptables_start() { local ns_iptables_rules local rule # setup iptables rules if given if [[ "$OCF_RESKEY_ns_iptables_start_rules" != "false" ]] ; then IFS=';' read -a ns_iptables_rules <<< "$OCF_RESKEY_ns_iptables_start_rules" for rule in "${ns_iptables_rules[@]}" ; do ocf_run $RUN_IN_NS $rule done fi setup_routes return $OCF_SUCCESS } iptables_stop() { local ns_iptables_rules local rule # remove iptables rules if given if [[ $OCF_RESKEY_ns_iptables_stop_rules != "false" ]] ; then IFS=';' read -a ns_iptables_rules <<< "$OCF_RESKEY_ns_iptables_stop_rules" for rule in "${ns_iptables_rules[@]}" ; do ocf_run $RUN_IN_NS $rule done fi return $OCF_SUCCESS } ip_start() { check_interfaces_for_up_state "$OCF_RESKEY_bridge:$OCF_RESKEY_also_check_interfaces" || return $OCF_ERR_GENERIC ip_prepare rc=$? if [[ $rc != $OCF_SUCCESS ]] ; then # cleanun ns ip_stop rc=$OCF_ERR_GENERIC else iptables_start fi return $rc } ip_stop() { local rc ip_validate if [ -n "$OCF_RESKEY_bridge" ] ; then remove_from_bridge fi # destroy veth-pair in base system ocf_run ip link show $OCF_RESKEY_base_veth rc=$? if [[ $rc == 0 ]] ; then ocf_run ip link set down dev $OCF_RESKEY_base_veth && sleep 1 && # prevent race ocf_run ip link del dev $OCF_RESKEY_base_veth rc=$? else rc=0 fi if [[ $rc == 0 ]] ; then rc=$OCF_SUCCESS # it means stop was success iptables_stop else rc=$OCF_ERR_GENERIC fi return $rc } check_patchcord_exists_in_bridge() { local br="$1" local veth="$2" if [[ -d /sys/class/net/${br}/brif ]] ; then # LNX test -L /sys/class/net/${br}/brif/${veth} || return $OCF_ERR_GENERIC else # OVS ovs-vsctl list-ports "${br}" | grep "${veth}" || return $OCF_ERR_GENERIC fi return $OCF_SUCCESS } ip_monitor() { local rc ip_validate check_ns || return $OCF_NOT_RUNNING local iface=$(find_interface_in_ns $OCF_RESKEY_ns $OCF_RESKEY_ip $OCF_RESKEY_cidr_netmask) [ -z "$iface" ] && return $OCF_NOT_RUNNING check_patchcord_exists_in_bridge $OCF_RESKEY_bridge $OCF_RESKEY_base_veth || return $OCF_ERR_GENERIC check_interfaces_for_up_state "$OCF_RESKEY_bridge:$OCF_RESKEY_also_check_interfaces" || return $OCF_NOT_RUNNING # use arping here, because no IP from VIP network allowed on host system ocf_run arping -c 10 -w 2 -I $OCF_RESKEY_bridge $OCF_RESKEY_ip || return $OCF_NOT_RUNNING # Send Gratuitous ARP REQUEST packets to update all neighbours in a detached background process ARGS="-U -c 5 -w 2 -I $OCF_RESKEY_ns_veth -q $OCF_RESKEY_ip" $RUN_IN_NS arping $ARGS 2>&1 > /dev/null & # Send Gratuitous ARP REPLY packets to update all neighbours in a detached background process ARGS="-A -c 5 -w 2 -I $OCF_RESKEY_ns_veth -q $OCF_RESKEY_ip" $RUN_IN_NS arping $ARGS 2>&1 > /dev/null & return $OCF_SUCCESS } ip_usage() { cat <