ctdb-scripts: Fix regression in updateip code
[obnox/samba/samba-obnox.git] / ctdb / config / events.d / 10.interface
index 018f767e4447f53f299aa97ecc35ef16f80b1cf1..7445200b3287014db9034b9a64f7ad94344324c7 100755 (executable)
@@ -46,12 +46,36 @@ get_all_interfaces ()
 
     # Get the interfaces for which CTDB has public IPs configured.
     # That is, for all but the 1st line, get the 1st field.
-    ctdb_ifaces=$(ctdb -Y ifaces | sed -e '1d' -e 's@^:@@' -e 's@:.*@@')
+    ctdb_ifaces=$(ctdb -X ifaces | sed -e '1d' -e 's@^|@@' -e 's@|.*@@')
 
     # Add $ctdb_interfaces and uniquify
     all_interfaces=$(echo $all_interfaces $ctdb_ifaces | tr ' ' '\n' | sort -u)
 }
 
+get_real_iface ()
+{
+    # Output of "ip link show <iface>"
+    _iface_info="$1"
+
+    # Extract the full interface description to see if it is a VLAN
+    _t=$(echo "$_iface_info" |
+               awk 'NR == 1 { iface = $2; sub(":$", "", iface) ; \
+                              print iface }')
+    case "$_t" in
+       *@*)
+           # VLAN: use the underlying interface, after the '@'
+           echo "${_t##*@}"
+           ;;
+       *)
+           # Not a regular VLAN.  For backward compatibility, assume
+           # there is some other sort of VLAN that doesn't have the
+           # '@' in the output and only use what is before a '.'.  If
+           # there is no '.' then this will be the whole interface
+           # name.
+           echo "${_t%%.*}"
+    esac
+}
+
 monitor_interfaces()
 {
        get_all_interfaces
@@ -65,7 +89,7 @@ monitor_interfaces()
        # problem with an interface then set fail=true and continue.
        for iface in $all_interfaces ; do
 
-           ip link show $iface 2>/dev/null >/dev/null || {
+           _iface_info=$(ip link show $iface 2>&1) || {
                echo "ERROR: Interface $iface does not exist but it is used by public addresses."
                mark_down $iface
                continue
@@ -74,7 +98,7 @@ monitor_interfaces()
            # These interfaces are sometimes bond devices
            # When we use VLANs for bond interfaces, there will only
            # be an entry in /proc for the underlying real interface
-           realiface=`echo $iface |sed -e 's/\..*$//'`
+           realiface=$(get_real_iface "$_iface_info")
            bi=$(get_proc "net/bonding/$realiface" 2>/dev/null) && {
                echo "$bi" | grep -q 'Currently Active Slave: None' && {
                        echo "ERROR: No active slaves for bond device $realiface"
@@ -106,7 +130,7 @@ monitor_interfaces()
                mark_up $iface
                ;;
            ib*)
-               # we dont know how to test ib links
+               # we don't know how to test ib links
                mark_up $iface
                ;;
            *)
@@ -137,6 +161,34 @@ monitor_interfaces()
        return 1
 }
 
+# Sets: iface, ip, maskbits, family
+get_iface_ip_maskbits_family ()
+{
+    _iface_in="$1"
+    ip="$2"
+    _maskbits_in="$3"
+
+    set -- $(ip_maskbits_iface "$ip")
+    if [ -n "$1" ] ; then
+       maskbits="$1"
+       iface="$2"
+       family="$3"
+
+       if [ "$iface" != "$_iface_in" ] ; then
+           printf \
+               'WARNING: Public IP %s hosted on interface %s but VNN says %s\n' \
+               "$ip" "$iface" "$_iface_in"
+       fi
+       if [ "$maskbits" != "$_maskbits_in" ] ; then
+           printf \
+               'WARNING: Public IP %s has %s bit netmask but VNN says %s\n' \
+                   "$ip" "$maskbits" "$_maskbits_in"
+       fi
+    else
+       die "ERROR: Unable to determine interface for IP ${ip}"
+    fi
+}
+
 ctdb_check_args "$@"
 
 case "$1" in 
@@ -152,6 +204,10 @@ case "$1" in
        _promote="sys/net/ipv4/conf/all/promote_secondaries"
        get_proc "$_promote" >/dev/null 2>&1 || \
            die "Public IPs only supported if promote_secondaries is available"
+
+       # make sure we drop any ips that might still be held if
+       # previous instance of ctdb got killed with -9 or similar
+       drop_all_public_ips
        ;;
 
      #############################
@@ -174,10 +230,13 @@ case "$1" in
        }
 
        # cope with the script being killed while we have the interface blocked
-       iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
+       case "$ip" in
+           *:*) family="inet6" ;;
+           *)   family="inet"  ;;
+       esac
+       iptables_wrapper $family -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
 
-       # flush our route cache
-       set_proc sys/net/ipv4/route/flush 1
+       flush_route_cache
        ;;
 
 
@@ -194,25 +253,23 @@ case "$1" in
        # 2) use netstat -tn to find existing connections, and kill them 
        # 3) remove the IP from the interface
        # 4) remove the firewall rule
-       iface=$2
-       ip=$3
-       maskbits=$4
+       shift
+       get_iface_ip_maskbits_family "$@"
 
-       failed=0
        # we do an extra delete to cope with the script being killed
-       iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
-       iptables -I INPUT -i $iface -d $ip -j DROP
+       iptables_wrapper $family -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
+       iptables_wrapper $family -I INPUT -i $iface -d $ip -j DROP
        kill_tcp_connections $ip
 
        delete_ip_from_iface $iface $ip $maskbits || {
-               iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
-               exit 1;
+           iptables_wrapper $family \
+                            -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
+               exit 1
        }
 
-       iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
+       iptables_wrapper $family -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
 
-       # flush our route cache
-       set_proc sys/net/ipv4/route/flush 1
+       flush_route_cache
        ;;
 
      ##################################################
@@ -224,34 +281,36 @@ case "$1" in
        # we finally remove it from the old interface.
        #
        # 1) firewall this IP, so no new external packets arrive for it
-       # 2) add the IP to the new interface
-       # 3) remove the IP from the old interface
+       # 2) remove the IP from the old interface (and new interface, to be sure)
+       # 3) add the IP to the new interface
        # 4) remove the firewall rule
        # 5) use ctdb gratiousarp to propagate the new mac address
        # 6) use netstat -tn to find existing connections, and tickle them
-       oiface=$2
+       _oiface=$2
        niface=$3
-       ip=$4
-       maskbits=$5
+       _ip=$4
+       _maskbits=$5
+
+       get_iface_ip_maskbits_family "$_oiface" "$_ip" "$_maskbits"
+       oiface="$iface"
 
-       failed=0
        # we do an extra delete to cope with the script being killed
-       iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
-       iptables -I INPUT -i $oiface -d $ip -j DROP
+       iptables_wrapper $family -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
+       iptables_wrapper $family -I INPUT -i $oiface -d $ip -j DROP
 
        delete_ip_from_iface $oiface $ip $maskbits 2>/dev/null
        delete_ip_from_iface $niface $ip $maskbits 2>/dev/null
 
        add_ip_to_iface $niface $ip $maskbits || {
-               iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
-               exit 1;
+           iptables_wrapper $family \
+                            -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
+           exit 1
        }
 
        # cope with the script being killed while we have the interface blocked
-       iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
+       iptables_wrapper $family -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
 
-       # flush our route cache
-       set_proc sys/net/ipv4/route/flush 1
+       flush_route_cache
 
        # propagate the new mac address
        ctdb gratiousarp $ip $niface