File rttbl2.sh of Package cloud-multiroute

#!/bin/bash
# Usage: rttbl2.sh [-d] [-g]
#
# This script creates 2ndary routing tables and moves the routes from secondary interfaces to them.
# Needed for multi-NIC setups with multiple default routes.
# Also useful is you have several network interfaces in the same subnet, e.g. b/c you want to
# associate multiple floating IPs.
# Limitations: Only supports IPv4; multiple IPs per interface not thoroughly tested.
#
# (c) Kurt Garloff <kurt.garloff@t-systems.com>, 3/2017
# License: CC-BY-SA 3.0

# Option -d: debug: Don't performa any of the actions, only display
if test "$1" = "-d"; then DEBUG=1; fi
# Option -g: guessgw: For each ether interface with an IPv4 address, guess a default gateway (.1)
# and configure a default route via it.
unset GUESSGW
if test "$1" = "-g"; then GUESSGW=1; fi

# Collect ethernet network interfaces and IP addresses, default routes
IFACES=( $(ip addr show | grep -B1 'link/ether' | grep -v '^--$' | grep -v 'link/ether' | sed 's/^[0-9]*: *\([^:]*\):.*$/\1/') )
NOIF=${#IFACES[@]}
IPADDR=()
DEFROUTE=()
for ifno in $(seq 0 $(($NOIF-1))); do
	IPADDR[$ifno]=$(ip addr show ${IFACES[$ifno]} | grep 'inet ' | sed 's/^ *inet \([0-9/.]*\) .*$/\1/')
	DEFROUTE[$ifno]=$(ip route show | grep '^default' | grep "dev ${IFACES[$ifno]}" | sed -e 's/proto \(kernel\|dhcp\)//' -e 's/scope link//')
done

# Interface with default route
DEFIFACE=$(ip route | grep '^default' | sed 's/^default via [0-9.]* dev \([^ ]*\).*$/\1/')
DEFIFNO=$(for ifno in $(seq 0 $(($NOIF-1))); do if test "$DEFIFACE" = "${IFACES[$ifno]}"; then echo $ifno; break; fi; done)
DEFNET=${IPADDR[$DEFIFNO]}
DEFNETIP=${DEFNET%/*}
DEFNETPRE=${DEFNET##*/}

# prefixlen to netmask numeral
netmask()
{
	printf "0x%08x\n" $((((1<<$((32-$1)))-1)^0xffffffff))
}

# IPv4 to numeral
iptobin()
{
	I0=${1##*.}
	I=${1%.*}; I1=${I##*.}
	I=${I%.*}; I2=${I##*.}
	I=${I%.*}; I3=${I##*.}
	printf "0x%08x\n" $((((((($I3<<8)+$I2)<<8)+$I1)<<8)+$I0))
}

# numeral to IPv4
bintoip()
{
	printf "%i.%i.%i.%i\n" $(($1>>24)) $((($1>>16)&0xff)) $((($1>>8)&0xff)) $(($1&0xff))
}

# convert CIDR to Network address by masking out bits
iptonet()
{
	PRE=${1##*/}
	NET=${1%/*}
	mask=$(netmask $PRE)
	NEWNET=$(bintoip $(($(iptobin $NET)&$mask)))
	echo "$NEWNET/$PRE"
}

# guess gateway address (network + .1)
guessgw()
{
	PRE=${1##*/}
	NET=${1%/*}
	mask=$(netmask $PRE)
	GW=$(bintoip $((1+($(iptobin $NET)&$mask))))
	echo "$GW"
}

# Check wther IP is contained in Net/Prefix
# Args: $1 => IP to test; $2 => Net; $3 => PrefixLen
innet()
{
	mask=$(netmask $3)
	b1=$(iptobin $1)
	b2=$(iptobin $2)
	#echo test "\$(($b1&$mask)) == \$(($b2&$mask))"
	test $(($b1&$mask)) == $(($b2&$mask))
}

# Output and execute (if DEBUG is unset)
debug()
{
	echo "$@"
	if test -z "$DEBUG"; then
		eval "$@"
	fi
}

# MAIN
for ifno in $(seq 0 $(($NOIF-1))); do
	IPS=${IPADDR[$ifno]}
	IF=${IFACES[$ifno]}
	# Ignore interfaces without IP address
	if test -z "$IPS"; then continue; fi
	# Keep default interface on main table
	if test "$IF" == "$DEFIFACE"; then continue; fi
	# We only need to move if
	# * we have a default route on the iface (or GUESSGW is set)  OR
	# * we are in the same subnet
	# Find net in which this interface is
	read FIRSTIP REST < <(echo $IPS)
	FINDINNET=$(for no in $(seq 0 $(($NOIF-1))); do if test $ifno = $no; then continue; fi; DNET=${IPADDR[$no]}; if test -n "${DEFROUTE[$no]}" && innet "$FIRSTIP" "${DNET%/*}" "${DNET##*/}"; then echo $no; break; fi; done)
	if test -z "${DEFROUTE[$ifno]}" -a -z "$GUESSGW" -a -z "$FINDINNET"; then continue; fi
	RTBL="${IF}tbl"
	# Add routing table if not existing already
	if ! grep -q "$RTBL" /etc/iproute2/rt_tables; then
		debug "echo -e \"$(($ifno+100))\\t$RTBL\" >> /etc/iproute2/rt_tables"
	fi
	# Move routes ... and set routing rules
	for ipaddr in $IPS; do
		debug ip route del $(iptonet $ipaddr) dev $IF src ${ipaddr%/*}
		debug ip route add $(iptonet $ipaddr) dev $IF src ${ipaddr%/*} table $RTBL
		# Check whether rule already exists ...
		RULES=$(ip rule show)
		echo "$RULES" | grep -q "from ${ipaddr%/*} .*lookup $RTBL" && debug ip rule del from ${ipaddr%/*} table $RTBL 2>/dev/null
		debug ip rule add from ${ipaddr%/*} table $RTBL
		echo "$RULES" | grep -q "to ${ipaddr%/*} .*lookup $RTBL" && debug ip rule del to ${ipaddr%/*} table $RTBL 2>/dev/null
		debug ip rule add to ${ipaddr%/*} table $RTBL
	done
	# Move or add default route ...
	DEFR=${DEFROUTE[$ifno]}
	# Default route already there, just move to new table
	# (should not happen, as Linux normally only allows one default route)
	if test -n "$DEFR"; then
		debug ip route del $DEFR
		debug ip route add $DEFR table $RTBL
	else
		# Take default route from other interface in same subnet or guess .1 gateway
		if test -n "$FINDINNET" -a -n "${DEFROUTE[$FINDINNET]}"; then
			NEWRT=$(echo "${DEFROUTE[$FINDINNET]}" | sed "s/dev ${IFACES[$FINDINET]}/dev $IF/")
		else
			NEWRT="default via $(guessgw $FIRSTIP) dev $IF"
		fi
		debug ip route add $NEWRT table $RTBL
	fi
done

openSUSE Build Service is sponsored by