cdist/cdist/conf/type/__network_interface/files/symmetric-routing

241 lines
5.5 KiB
Bash
Executable File

#!/bin/sh
#
set -e
error() {
echo "[ERROR] $@" >&2
}
die() {
error "$@"
exit 1
}
info() {
echo "[INFO] $@" >&2
}
debug() {
if [ "$DEBUG" ]; then
echo "[DEBUG] $@" >&2
fi
}
usage() {
cat << EOS 1>&2
Usage: ${0##*/} [OPTIONS] ACTION INTERFACE IP_ADDRESS SUBNET_MASK_OR_PREFIX [GATEWAY]
(see -h for more information)
EOS
}
help() {
usage 2>&1 | head -n -1 1>&2
cat << EOS 1>&2
Setup policy based routing for the given interface
to ensure symmetric routing.
ACTION must be either 'up' or 'down' to add respectively remove the
routing table entries.
Options:
-h show this help message
-d run in debug mode
-x run with 'set -x' set
-n no action, just show what would be done without doing it
Examples:
${0##*/} up eth1 192.168.42.23 255.255.255.0 192.168.0.1
${0##*/} down eth1 192.168.42.23 255.255.255.0 192.168.0.1
# gateway is optional
${0##*/} up eth1 192.168.42.23 255.255.255.0
${0##*/} down eth1 192.168.42.23 255.255.255.0
# same but using prefix instead of subnet mask
${0##*/} up eth1 192.168.42.23 24 192.168.0.1
${0##*/} down eth1 192.168.42.23 24 192.168.0.1
EOS
}
die_usage() {
error "$@"
usage
exit 1
}
### Utility functions
# Convert ip to int.
ip2int() {
_ip="$1"
{ IFS=. read _a _b _c _d; } << _done
$_ip
_done
echo $(((((((_a << 8) | _b) << 8) | _c) << 8) | _d))
unset _ip _a _b _c _d
}
# Convert int to ip.
int2ip() {
_ui32=$1; shift
_ip=
for _n in 1 2 3 4; do
_ip=$((_ui32 & 0xff))${_ip:+.}$_ip
_ui32=$((_ui32 >> 8))
done
echo $_ip
unset _ui32 _ip _n
}
# Convert the given prefix into a subnet mask.
mask_from_prefix() {
_prefix="$1"
_mask=$((0xffffffff << (32 - $_prefix)))
int2ip $_mask
unset _prefix _mask
}
# Calculate network number from the given ip and prefix.
network_from_ip_and_prefix() {
_ip="$1"
_prefix="$2"
_addr=$(ip2int $_ip)
_mask=$((0xffffffff << (32 - $_prefix)))
int2ip $((_addr & _mask))
unset _ip _prefix _addr _mask
}
# Calculate number of bits in the given subnet mask.
prefix_from_mask() {
# Assumes there's no "255." after a non-255 byte in the mask
_mask="$1"
_x=${_mask##*255.}
set -- 0^^^128^192^224^240^248^252^254^ $(( (${#1} - ${#_x})*2 )) ${_x%%.*}
_x=${1%%$3*}
echo $(( $2 + (${#_x}/4) ))
unset _mask _x
}
rt_tables=/etc/iproute2/rt_tables
#rt_tables=/tmp/rt_tables
# Get and if required create a routing table for the given table name.
table_id_from_name() {
_interface="$1"
_table_id=$(awk -vname=$_interface '{ if ($2 == name) print $1 }' "$rt_tables")
if [ -z "$_table_id" ]; then
# find unused table id and create a new table for this interface
_used_ids=$(awk '$1 !~ /^(#| |255|254|253|0)/ { print $1 }' "$rt_tables")
for _tid in $(seq 1 252); do
if echo "$_used_ids" | grep -q "$_tid"; then
continue
else
_table_id="$_tid"
[ $NOACTION ] || printf '%s %s\n' "$_table_id" "$_interface" >> "$rt_tables"
break
fi
done
fi
echo "$_table_id"
unset _interface _table_id _used_ids _tid
}
### Parse command line arguments
NOACTION=
DEBUG=
SETX=
while getopts "ndxh" options
do
#echo "$flag" $OPTIND $OPTARG
case $options in
n) NOACTION=1;;
d) DEBUG=1;;
x) SETX=1;;
?|h) help
exit 0
;;
*) usage
exit 1
;;
esac
done
# Strip arguments allready handled by getopts
shift $((OPTIND-1))
[ "$SETX" ] && set -x
# Validate arguments
[ "$#" -ge 4 ] || die_usage "Expected at least 4 arguments, got: $#"
action="$1" # up | down
interface="$2"
ip_address="$3"
subnet_mask_or_prefix="$4"
gateway="$5"
debug "action: $action"
debug "interface: $interface"
debug "ip_address: $ip_address"
debug "subnet_mask_or_prefix: $subnet_mask_or_prefix"
debug "gateway: $gateway"
case "$subnet_mask_or_prefix" in
*.*)
# has a dot, must be a subnet mask
subnet_mask="$subnet_mask_or_prefix"
prefix=$(prefix_from_mask "$subnet_mask")
network="$(network_from_ip_and_prefix "$ip_address" "$prefix")"
;;
*)
# no dot, must be prefix
prefix="$subnet_mask_or_prefix"
subnet_mask="$(mask_from_prefix "$prefix")"
network="$(network_from_ip_and_prefix "$ip_address" "$prefix")"
;;
esac
table_name="$interface"
table_id="$(table_id_from_name "$table_name")"
debug "subnet_mask: $subnet_mask"
debug "prefix: $prefix"
debug "network: $network"
debug "table_name: $table_name"
debug "table_id: $table_id"
(
case "$action" in
up)
# setup routing table for interface
printf 'ip route add "%s/%s" dev "%s" proto static src "%s" table "%s"\n' \
"$network" "$prefix" "$interface" "$ip_address" "$table_name"
if [ -n "$gateway" ]; then
printf 'ip route add default via "%s" table "%s"\n' "$gateway" "$table_name"
fi
printf 'ip rule add from "%s" table "%s"\n' "$ip_address" "$table_name"
;;
down)
printf 'ip rule del from "%s" table "%s"\n' "$ip_address" "$table_name"
if [ -n "$gateway" ]; then
printf 'ip route del default via "%s" table "%s"\n' "$gateway" "$table_name"
fi
printf 'ip route del "%s/%s" dev "%s" proto static src "%s" table "%s"\n' \
"$network" "$prefix" "$interface" "$ip_address" "$table_name"
;;
*)
echo "Unknown action: $action" >&2
exit 1
;;
esac
# tell the kernel that it needs to re-parse the policy database
printf 'ip route flush cache\n'
) | (
if [ "$NOACTION" ]; then
cat
else
/bin/sh -s
fi
)