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