mirror of
https://github.com/firehol/firehol.git
synced 2024-06-16 03:58:22 +00:00
1957 lines
47 KiB
Bash
Executable File
1957 lines
47 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Link Balancer - Policy Based Routing for humans...
|
|
#
|
|
# Copyright
|
|
#
|
|
# Copyright (C) 2015-2017 Costa Tsaousis <costa@tsaousis.gr>
|
|
# Copyright (C) 2015-2017 Phil Whineray <phil@sanewall.org>
|
|
#
|
|
# License
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
# See the file COPYING for details.
|
|
#
|
|
READLINK_CMD=${READLINK_CMD:-readlink}
|
|
BASENAME_CMD=${BASENAME_CMD:-basename}
|
|
DIRNAME_CMD=${DIRNAME_CMD:-dirname}
|
|
function realdir {
|
|
local r="$1"; local t=$($READLINK_CMD "$r")
|
|
while [ "$t" ]; do
|
|
r=$(cd $($DIRNAME_CMD "$r") && cd $($DIRNAME_CMD "$t") && pwd -P)/$($BASENAME_CMD "$t")
|
|
t=$($READLINK_CMD "$r")
|
|
done
|
|
$DIRNAME_CMD "$r"
|
|
}
|
|
PROGRAM_FILE="$0"
|
|
PROGRAM_DIR="${FIREHOL_OVERRIDE_PROGRAM_DIR:-$(realdir "$0")}"
|
|
PROGRAM_PWD="${PWD}"
|
|
declare -a PROGRAM_ORIGINAL_ARGS=("${@}")
|
|
|
|
for functions_file in install.config functions.common
|
|
do
|
|
if [ -r "$PROGRAM_DIR/$functions_file" ]
|
|
then
|
|
source "$PROGRAM_DIR/$functions_file"
|
|
else
|
|
1>&2 echo "Cannot access $PROGRAM_DIR/$functions_file"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
common_disable_localization || exit
|
|
common_private_umask || exit
|
|
common_require_root || exit
|
|
|
|
# make sure sbin is included in the path
|
|
# it seems that pppd ip-up.d script need this
|
|
export PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin"
|
|
|
|
if [ "$LB_DEBUGGING" ]; then set -v; set -x; fi
|
|
|
|
# link-balancer temporary directory.
|
|
# every instance of link-balancer creates a random directory
|
|
# within this one.
|
|
LB_RUN_DIR="$LOCALSTATEDIR/run/link-balancer"
|
|
|
|
# If this is set to 1, no checks will be made if the gateways are available.
|
|
# All gateways will be assumed active, if their interfaces are found
|
|
# operational.
|
|
# WHY: When we have a multi-ppp environment the default gateway of the system
|
|
# will be composed my multiple 'nexthop' routes. When a ppp device fails
|
|
# the kernel will remove the entire default gateway, not just the 'nexthop'
|
|
# that is not available any more.
|
|
# By calling link-balancer with option '-f' will enable this option.
|
|
# This option will make link-balancer restore the default gateway as soon
|
|
# as possible.
|
|
# 'link-balancer -f' should thus by called by ppp/ip-down scripts.
|
|
FORCE_ALWAYSON=0
|
|
|
|
# If set to 1, link-balancer will not change anything. It will just show
|
|
# what it will execute if run without it.
|
|
DRY_RUN=0
|
|
|
|
# If set to 1, link-balancer will output to standard-error all temporary files
|
|
# it generates during its execution.
|
|
DEBUG_ALL_DATA=0
|
|
|
|
# The step which increments the priority of the rules for each rule statement
|
|
# in the configuration file.
|
|
LB_RULE_STEP=100
|
|
|
|
# Keeps a list of all tables that have been altered during its run.
|
|
declare -A LB_ALTERED=()
|
|
|
|
# Where is the link-balancer configuration files?
|
|
LB_CONFIG_DIR="${FIREHOL_CONFIG_DIR}"
|
|
|
|
# Link-balancer main configuration file.
|
|
LB_CONFIG="${LB_CONFIG_DIR}/link-balancer.conf"
|
|
|
|
# takes either 4 or 6 to switch the functions in ipv4 or ipv6
|
|
LB_DEFAULT_IPV=4
|
|
|
|
marksreset() { :; }
|
|
markdef() { :; }
|
|
if [ -r "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" ]
|
|
then
|
|
source "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" || exit 1
|
|
fi
|
|
|
|
# temporary variable (default LB_DEFAULT_IPV=4)
|
|
LB_IPV=
|
|
|
|
RUNNING_ON_TERMINAL=0
|
|
if [ "z$1" = "z-nc" ]
|
|
then
|
|
shift
|
|
else
|
|
common_setup_terminal && RUNNING_ON_TERMINAL=1
|
|
fi
|
|
|
|
# if called with the parameter 'boot', create a new screen that
|
|
# will run forever.
|
|
if [ "$1" = "boot" ]
|
|
then
|
|
shift
|
|
common_require_cmd $PROGRAM_FILE SCREEN_CMD
|
|
$SCREEN_CMD -S 'link-balancer' -d -m "$0" loop "${@}"
|
|
exit 0
|
|
fi
|
|
|
|
# if called with the parameter 'loop', run forever.
|
|
if [ "$1" = "loop" ]
|
|
then
|
|
shift
|
|
secs="${1}"
|
|
secs=$[ secs + 1 - 1 ]
|
|
test "${secs}" = "0" && secs=300
|
|
shift
|
|
|
|
while [ 1 ]
|
|
do
|
|
$0 "${@}"
|
|
echo "Waiting for $secs secs..."
|
|
$SLEEP_CMD $secs
|
|
done
|
|
exit 0
|
|
fi
|
|
|
|
if [ ! -d "${LB_RUN_DIR}" ]
|
|
then
|
|
$MKDIR_CMD -p "${LB_RUN_DIR}" || exit 1
|
|
$CHOWN_CMD root:root "${LB_RUN_DIR}" || exit 1
|
|
$CHMOD_CMD 700 "${LB_RUN_DIR}" || exit 1
|
|
fi
|
|
|
|
# get an exclusive lock or wait for it to be available
|
|
LOCKFILE="${LB_RUN_DIR}/link-balancer.lock"
|
|
[ "${FLOCKER}" != "${LOCKFILE}" ] && exec $ENV_CMD FLOCKER="${LOCKFILE}" $FLOCK_CMD -e "${LOCKFILE}" "$0" "$@" || :
|
|
|
|
cd "${LB_RUN_DIR}" || exit 1
|
|
LB_DIR="`$MKTEMP_CMD -d "${LB_RUN_DIR}/temp-XXXXXXXXXX"`" || exit 1
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Default FireHOL marks
|
|
|
|
declare -A MARKS_BITS='([connmark]="6" [usermark]="7" )'
|
|
declare -A MARKS_MASKS='([connmark]="0x0000003f" [usermark]="0x00001fc0" )'
|
|
declare -A MARKS_MAX='([connmark]="63" [usermark]="127" )'
|
|
declare -A MARKS_SHIFT='([connmark]="0" [usermark]="6" )'
|
|
|
|
FIREHOL_SPOOL_DIR="${FIREHOL_SPOOL_DIR-$LOCALSTATEDIR/spool/firehol}"
|
|
if [ -f "${FIREHOL_SPOOL_DIR}/marks.conf" ]
|
|
then
|
|
source "${FIREHOL_SPOOL_DIR}/marks.conf" || exit 1
|
|
fi
|
|
|
|
mark_value() {
|
|
local name="${1}"; shift
|
|
local x=
|
|
|
|
if [ -z "${name}" ]
|
|
then
|
|
error "Cannot find the value of mark with name '${name}'."
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "${1}" ]
|
|
then
|
|
error "Empty mark value given for mark ${name}."
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "${MARKS_MASKS[$name]}" ]
|
|
then
|
|
error "Mark $name does not exist."
|
|
return 1
|
|
fi
|
|
|
|
for x in ${@//,/ }
|
|
do
|
|
local x=$[ x + 1 - 1 ]
|
|
if [ $x -gt ${MARKS_MAX[$name]} -o $x -lt 0 ]
|
|
then
|
|
error "Cannot get mark $name of value $x. Mark $name is configured to get values from 0 to ${MARKS_MAX[$name]}. Change firehol-defaults.conf to add more."
|
|
return 1
|
|
fi
|
|
|
|
#echo "$[ x << ${MARKS_SHIFT[$name]} ]/${MARKS_MASKS[$name]}"
|
|
printf "0x%08x/${MARKS_MASKS[$name]}\n" "$[ x << ${MARKS_SHIFT[$name]} ]"
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Find in the BASH execution stack, the line and the source file that has called us.
|
|
# Before first use the variable PROGRAM_FILE should be set to the file to be excluded.
|
|
# It also sets the variable LAST_CONFIG_LINE on each run.
|
|
FORCE_CONFIG_LINEID=
|
|
LAST_CONFIG_LINE=
|
|
config_line() {
|
|
if [ ! -z "${FORCE_CONFIG_LINEID}" ]
|
|
then
|
|
LAST_CONFIG_LINE="${FORCE_CONFIG_LINEID}"
|
|
else
|
|
# find the config line in the BASH stack
|
|
# start from 2
|
|
# 0 is this line
|
|
# 1 is the caller - our line for sure
|
|
# 2 is the caller's caller - possibly a config file line
|
|
local i= all=${#BASH_SOURCE}
|
|
for (( i = 2; i < $all; i++ ))
|
|
do
|
|
[ ! "${BASH_SOURCE[$i]}" = "${PROGRAM_FILE}" ] && break
|
|
done
|
|
LAST_CONFIG_LINE="${BASH_LINENO[$[i-1]]}@${BASH_SOURCE[$i]}: ${FUNCNAME[$[i-1]]}:"
|
|
fi
|
|
test ! "z$1" = "z-ne" && echo "${LAST_CONFIG_LINE}"
|
|
}
|
|
|
|
LB_RESULT_CODE=1
|
|
lb_exit() {
|
|
cd /tmp
|
|
|
|
if [ ${DEBUG_ALL_DATA} -eq 1 ]
|
|
then
|
|
echo >&2
|
|
echo >&2
|
|
echo >&2 "---------------------------------------------------------------------"
|
|
echo >&2 "Temporary folder contents:"
|
|
$LS_CMD -l "${LB_DIR}/" >&2
|
|
|
|
for x in "${LB_DIR}"/*
|
|
do
|
|
echo >&2
|
|
echo >&2
|
|
echo >&2 "---------------------------------------------------------------------"
|
|
echo >&2 "${x}"
|
|
echo >&2
|
|
$CAT_CMD >&2 "${x}"
|
|
done
|
|
fi
|
|
|
|
if [ ! -z "${LB_DIR}" -a -d "${LB_DIR}" ]
|
|
then
|
|
$RM_CMD -rf "${LB_DIR}"
|
|
fi
|
|
|
|
enable trap
|
|
enable exit
|
|
trap exit EXIT
|
|
exit ${LB_RESULT_CODE}
|
|
}
|
|
|
|
trap lb_exit EXIT
|
|
trap lb_exit SIGHUP
|
|
trap lb_exit INT
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# COMMON FUNCTIONS
|
|
|
|
syslog() {
|
|
if [ ${DRY_RUN} -eq 0 ]
|
|
then
|
|
$LOGGER_CMD -p daemon.info -t "LinkBalancer" "${*}"
|
|
fi
|
|
}
|
|
|
|
last_debug_depth=0
|
|
DEBUGCMDNNL() {
|
|
local prefix="${1}"; shift
|
|
local name="${1}"; shift
|
|
local depth="${1}"; shift
|
|
|
|
test "$depth" = "" && local depth=$last_debug_depth
|
|
last_debug_depth=$depth
|
|
|
|
# printf >&2 "`date`"
|
|
printf >&2 "% $((depth))s ${prefix} %s " " " "${name}"
|
|
printf >&2 " ${*} "
|
|
}
|
|
|
|
DEBUGCMD() {
|
|
DEBUGCMDNNL "-" "${@}"
|
|
printf >&2 "\n"
|
|
}
|
|
|
|
warning() {
|
|
local line="$(config_line)"
|
|
syslog "WARNING ${line} ${@}"
|
|
|
|
echo >&2
|
|
echo >&2 -e "${COLOR_YELLOW}${COLOR_BOLD}WARNING ${line} $@ ${COLOR_RESET}"
|
|
echo >&2
|
|
|
|
return 0
|
|
}
|
|
|
|
error() {
|
|
local line="$(config_line)"
|
|
syslog "ERROR ${line} ${@}"
|
|
|
|
echo >&2
|
|
echo >&2 -e "${COLOR_RED}${COLOR_BOLD}ERROR ${line} $@ ${COLOR_RESET}"
|
|
echo >&2
|
|
|
|
exit 1
|
|
}
|
|
|
|
run() {
|
|
printf >&2 "${COLOR_BOLD}"
|
|
DEBUGCMDNNL "#" "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}" " ... "
|
|
|
|
local tmp="`$MKTEMP_CMD "${LB_DIR}/run-$$-${RANDOM}-XXXXXXXXXX"`"
|
|
"${@}" >"$tmp" 2>"$tmp.err"
|
|
local ret=$?
|
|
|
|
if [ $ret -eq 0 ]
|
|
then
|
|
printf >&2 "${COLOR_RESET}${COLOR_BGGREEN}${COLOR_BLACK}${COLOR_BOLD} OK ${COLOR_RESET}\n"
|
|
else
|
|
printf >&2 "${COLOR_RESET}${COLOR_BGRED}${COLOR_WHITE}${COLOR_BOLD}${COLOR_BLINK} FAILED ${COLOR_RESET}\n"
|
|
fi
|
|
$CAT_CMD >&2 "$tmp.err"
|
|
$CAT_CMD "$tmp"
|
|
$RM_CMD "$tmp" "$tmp.err"
|
|
return $ret
|
|
}
|
|
|
|
action() {
|
|
if [ ${DRY_RUN} -eq 0 ]
|
|
then
|
|
printf >&2 "${COLOR_YELLOW}"
|
|
run "${@}"
|
|
local ret=$?
|
|
|
|
if [ $ret -ne 0 ]
|
|
then
|
|
warning "Command: '${@}' exited with code $ret."
|
|
else
|
|
syslog "Successfully run: '${@}'."
|
|
fi
|
|
|
|
return $ret
|
|
fi
|
|
|
|
DEBUGCMDNNL "#" "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}" " ... "
|
|
printf >&2 "DEFERRED\n"
|
|
return 0
|
|
}
|
|
|
|
trim_spaces() {
|
|
$SED_CMD -e "s/[\t\\ ]\+/ /g" -e "s/ \+$//g" -e "s/^ \+//g"
|
|
}
|
|
|
|
loadfile() {
|
|
local file="$1"; shift
|
|
|
|
cd "${LB_CONFIG_DIR}"
|
|
test ! -f "${file}" && syslog "WARNING: file '${file}' does not exist."
|
|
$CAT_CMD "${file}" | $SED_CMD -e "s/#.*$//g" | trim_spaces | $TR_CMD '\n' ' '
|
|
local ret=$?
|
|
cd "${LB_RUN_DIR}"
|
|
|
|
return $ret
|
|
}
|
|
|
|
ipv4() {
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
local cmd="${1}"; shift
|
|
|
|
case "${cmd}" in
|
|
gateway|table|rules|mark|fwmark|connmark|src|dst|from|to|tos|not)
|
|
"${cmd}" ipv4 "${@}"
|
|
return $?
|
|
;;
|
|
|
|
default)
|
|
error "Command '${cmd}' inherits IP version from its parent. Do not give ${FUNCNAME} for '${cmd}'."
|
|
exit 1
|
|
;;
|
|
|
|
*)
|
|
error "Command '${cmd}' cannot change IP version."
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
ipv6() {
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
local cmd="${1}"; shift
|
|
|
|
case "${cmd}" in
|
|
gateway|table|rules|mark|fwmark|connmark|src|dst|from|to|tos|not)
|
|
"${cmd}" ipv6 "${@}"
|
|
return $?
|
|
;;
|
|
|
|
default)
|
|
error "Command '${cmd}' inherits IP version from its parent. Do not give ${FUNCNAME} for '${cmd}'."
|
|
exit 1
|
|
;;
|
|
|
|
*)
|
|
error "Command '${cmd}' cannot change IP version."
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# gateway section
|
|
|
|
get_interface_ips() {
|
|
|
|
local ipv="4"
|
|
test "${LB_IPV}" = "6" && local ipv="6"
|
|
|
|
local inet="inet"
|
|
test "${LB_IPV}" = "6" && local inet="inet6"
|
|
|
|
|
|
run $IP_CMD -${ipv} addr show dev "$1" |\
|
|
trim_spaces |\
|
|
$GREP_CMD "${inet} " |\
|
|
$CUT_CMD -d ' ' -f 2 |\
|
|
$CUT_CMD -d '/' -f 1 |\
|
|
$TR_CMD '\n' ' '
|
|
}
|
|
|
|
get_interface_routes() {
|
|
local dev="${1}"; shift
|
|
|
|
local ipv=4
|
|
test "${LB_IPV}" = "6" && local ipv=6
|
|
|
|
local hostnet="/32"
|
|
test "${LB_IPV}" = "6" && local hostnet="/128"
|
|
|
|
local i=
|
|
for i in ${@}
|
|
do
|
|
run $IP_CMD -${ipv} route show dev "${dev}" scope link src "${i}" |\
|
|
trim_spaces |\
|
|
$CUT_CMD -d ' ' -f 1 |\
|
|
$SED_CMD "s|/${hostnet}||g" |\
|
|
$GREP_CMD -v "/" |\
|
|
$TR_CMD '\n' ' '
|
|
done
|
|
}
|
|
|
|
CHECK_PING_COUNT=3
|
|
CHECK_PING_WAIT=10
|
|
check_ping() {
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
|
|
local dev="$1"
|
|
local src="$2"
|
|
local dst="$3"
|
|
|
|
local cmd="$PING_CMD"
|
|
test "${LB_IPV}" = "6" && local cmd="$PING6_CMD"
|
|
|
|
run $cmd -I ${src} -c ${CHECK_PING_COUNT} -w ${CHECK_PING_WAIT} ${dst} >/dev/null
|
|
return $?
|
|
}
|
|
|
|
CHECK_TRACEROUTE_PORT=80
|
|
CHECK_TRACEROUTE_WAIT=2
|
|
check_traceroute() {
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
|
|
local dev="$1"
|
|
local src="$2"
|
|
local dst="$3"
|
|
|
|
local cmd="$TRACEROUTE_CMD -4"
|
|
test "${LB_IPV}" = "6" && local cmd="$TRACEROUTE_CMD -6"
|
|
|
|
local found="`run $cmd -i "${dev}" -r -s ${src} -w ${CHECK_TRACEROUTE_WAIT} -Tn -p ${CHECK_TRACEROUTE_PORT} ${dst} | $GREP_CMD ${dst}`"
|
|
test ! -z "${found}" && return 0
|
|
return 1
|
|
}
|
|
|
|
check_alwayson() {
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
|
|
local dev="$1"
|
|
local src="$2"
|
|
local dst="$3"
|
|
|
|
return 0
|
|
}
|
|
|
|
# this array has one entry per gateway.
|
|
# - the array index is the gateway name
|
|
# - the array value is the number of distinct gateways alive
|
|
declare -A LB_GATEWAYS=()
|
|
|
|
# keep track of the gateways IP version
|
|
declare -A LB_GATEWAYS_IPV=()
|
|
|
|
gateway4() { gateway ipv4 "${@}"; }
|
|
gateway6() { gateway ipv6 "${@}"; }
|
|
gateway() {
|
|
printf >&2 "\n-------------------------------------------------------------------------------\n"
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
LB_IPV=${LB_DEFAULT_IPV}
|
|
|
|
if [ ! -z "${work_route}" ]
|
|
then
|
|
local name="${work_route}"
|
|
else
|
|
local name="${1}"
|
|
shift
|
|
fi
|
|
|
|
local gw=
|
|
local dev=
|
|
local src=
|
|
local dst="gateway"
|
|
local check="ping"
|
|
|
|
while [ ! -z "${1}" ]
|
|
do
|
|
case "${1}" in
|
|
ipv4)
|
|
LB_IPV=4
|
|
;;
|
|
|
|
ipv6)
|
|
LB_IPV=6
|
|
;;
|
|
|
|
on|dev)
|
|
local dev="${2}"
|
|
shift
|
|
;;
|
|
|
|
gateway|gw|via)
|
|
local gw="${2//,/ }"
|
|
shift
|
|
;;
|
|
|
|
src|from)
|
|
local src="${2//,/ }"
|
|
shift
|
|
;;
|
|
|
|
check)
|
|
local check="${2}"
|
|
local dst="${3//,/ }"
|
|
shift 2
|
|
;;
|
|
|
|
*)
|
|
error "${FUNCNAME} '${name}': cannot understand parameter '${1}'."
|
|
return 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
LB_GATEWAYS[$name]=0
|
|
LB_GATEWAYS_IPV[$name]=${LB_IPV}
|
|
|
|
# if we don't have source IPs, find them from the interface
|
|
if [ -z "${src}" -o "${src}" = "auto" ]
|
|
then
|
|
local src="`get_interface_ips "${dev}" | $SORT_CMD -u`"
|
|
fi
|
|
|
|
# if we don't have source IPs, cannot proceed
|
|
if [ -z "${src}" ]
|
|
then
|
|
warning "Cannot find gateway ${name} IPs."
|
|
return 1
|
|
fi
|
|
|
|
# if we don't have a gateway, cannot proceed
|
|
if [ -z "${dev}" ]
|
|
then
|
|
error "${FUNCNAME} '${name}': cannot add a gateway without a device."
|
|
return 2
|
|
fi
|
|
|
|
# if we don't have gateway IPs, find them from the interface
|
|
test -z "${gw}" -o "${gw}" = "auto" -o "${gw}" = "detect" && local gw="`get_interface_routes "${dev}" ${src}`"
|
|
|
|
# if we don't have a gateway, cannot proceed
|
|
if [ -z "${gw}" ]
|
|
then
|
|
warning "Cannot find ${name} gateway IPs."
|
|
return 3
|
|
fi
|
|
|
|
# if we don't have a destination, use the gateway
|
|
test -z "${dst}" -o "${dst}" = "auto" -o "${dst}" = "detect" -o "${dst}" = "gateway" && local dst="${gw}"
|
|
test $FORCE_ALWAYSON -eq 1 && local check="alwayson"
|
|
|
|
$CAT_CMD <<EOF_GW
|
|
|
|
--- GATEWAY CONFIGURATION ----
|
|
Gateway : ${name}
|
|
Via IP : ${gw}
|
|
Source IPs: ${src}
|
|
Check : ${check} ${dst}
|
|
IP VERSION: ${LB_IPV}
|
|
|
|
EOF_GW
|
|
|
|
echo "${src}" >"${LB_DIR}/gateway.${name}.source-ips"
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Check if the gateway is alive
|
|
|
|
echo " CHECKING IF GATEWAY ${name} IS ALIVE"
|
|
|
|
local alive=0
|
|
local s=
|
|
for s in ${src}
|
|
do
|
|
local d=
|
|
for d in ${dst}
|
|
do
|
|
test $alive -ne 0 && break
|
|
|
|
check_${check} ${dev} ${s} ${d}
|
|
test $? -eq 0 && local alive=1
|
|
done
|
|
done
|
|
|
|
if [ ${alive} -eq 0 ]
|
|
then
|
|
echo
|
|
echo " Gateway ${name} is not alive."
|
|
return 99
|
|
fi
|
|
|
|
local count=0
|
|
local g=
|
|
for g in ${gw}
|
|
do
|
|
local count=$[count + 1]
|
|
echo "via ${g} dev ${dev}" >>"${LB_DIR}/gateway.${name}.paths"
|
|
done
|
|
|
|
LB_GATEWAYS[$name]="${count}"
|
|
|
|
echo >&2
|
|
echo >&2 -e " ${COLOR_GREEN}${COLOR_BOLD}IPv${LB_IPV} gateway '${name}' is alive, having ${count} active path(s).${COLOR_RESET}"
|
|
return 0
|
|
}
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# table section
|
|
|
|
# this array has one entry per table.
|
|
# - the array index is the table name
|
|
# - the array value is the number of default gateways available
|
|
declare -A LB_TABLES=()
|
|
declare -A LB_TABLES_FALLBACK=()
|
|
|
|
# keep track of the IP version per table
|
|
# if a table is used by both IPv4 and IPv6, '46' should be stored
|
|
declare -A LB_TABLES_IPV=()
|
|
|
|
# keep track of all origin tables
|
|
# if a table is used by both IPv4 and IPv6, '46' should be stored
|
|
declare -A LB_ORIGIN_TABLES=()
|
|
|
|
get_existing_routing_table() {
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
|
|
local table="${1}"
|
|
|
|
if [ "${LB_IPV}" = "6" ]
|
|
then
|
|
run $IP_CMD -6 -o route show table "${table}" |\
|
|
trim_spaces |\
|
|
$SORT_CMD
|
|
else
|
|
run $IP_CMD -4 -o route show table "${table}" |\
|
|
$GREP_CMD -v " via 127.0.0.1 dev lo" |\
|
|
$SED_CMD -e 's/linkdown//' |\
|
|
trim_spaces |\
|
|
$SORT_CMD
|
|
fi
|
|
}
|
|
|
|
work_table=
|
|
work_ipv=
|
|
table4() { table ipv4 "${@}"; }
|
|
table6() { table ipv6 "${@}"; }
|
|
table() {
|
|
printf >&2 "\n-------------------------------------------------------------------------------\n"
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
LB_IPV=${LB_DEFAULT_IPV}
|
|
|
|
local table="$1"; shift
|
|
|
|
if [ -z "${table}" ]
|
|
then
|
|
error "${FUNCNAME} needs a name."
|
|
return 1
|
|
fi
|
|
|
|
local origin="main"
|
|
local copy_default=1
|
|
while [ ! -z "${1}" ]
|
|
do
|
|
case "${1}" in
|
|
ipv4)
|
|
LB_IPV=4
|
|
;;
|
|
|
|
ipv6)
|
|
LB_IPV=6
|
|
;;
|
|
|
|
from|copy|duplicate)
|
|
local origin="${2}"
|
|
shift
|
|
;;
|
|
|
|
nodefault|keepdefault|nodef)
|
|
local copy_default=0
|
|
;;
|
|
|
|
*)
|
|
error "${FUNCNAME} '${table}': Cannot understand option '${1}'."
|
|
return 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# set LB_TABLES_IPV
|
|
if [ -z "${LB_TABLES_IPV[$table]}" ]
|
|
then
|
|
LB_TABLES_IPV[$table]="${LB_IPV}"
|
|
elif [ "${LB_TABLES_IPV[$table]}" = "${LB_IPV}" ]
|
|
then
|
|
error "${FUNCNAME}: table ${table} already exists in IPv${LB_IPV}."
|
|
exit 1
|
|
else
|
|
LB_TABLES_IPV[$table]="46"
|
|
fi
|
|
|
|
if [ "${table}" = "${origin}" -o -z "${origin}" -o "${origin}" = "none" ]
|
|
then
|
|
local origin="none"
|
|
fi
|
|
|
|
# give a summary to the user
|
|
$CAT_CMD <<EOF_TABLE
|
|
|
|
--- TABLE CONFIGURATION ---
|
|
Table : ${table}
|
|
Copy from : ${origin}
|
|
IP VERSION : ${LB_IPV}
|
|
|
|
EOF_TABLE
|
|
|
|
if [ ! "${origin}" = "none" ]
|
|
then
|
|
# just link it
|
|
# The source file may not exist yet. This is OK.
|
|
# Later, at finizize_tables() we will check the links to find
|
|
# the dependencies between the tables so that they will be
|
|
# activated in the correct order, so that in one pass all
|
|
# tables will get the final routes.
|
|
# We will also check if the user requested a circular
|
|
# dependency, and stop processing in this case.
|
|
$LN_CMD -s "${LB_DIR}/table.${origin}.routes.${LB_IPV}" "${LB_DIR}/table.${table}.origin_routes.${LB_IPV}"
|
|
test $copy_default -eq 1 && $LN_CMD -s "${LB_DIR}/table.${origin}.default.${LB_IPV}" "${LB_DIR}/table.${table}.origin_default.${LB_IPV}"
|
|
fi
|
|
|
|
work_table="${table}"
|
|
work_ipv="${LB_IPV}"
|
|
LB_TABLES[$table]=0
|
|
LB_TABLES_FALLBACK[$table]=0
|
|
|
|
if [ ! "${origin}" = "none" ]
|
|
then
|
|
# set LB_ORIGIN_TABLES
|
|
if [ -z "${LB_ORIGIN_TABLES[$origin]}" ]
|
|
then
|
|
LB_ORIGIN_TABLES[$origin]="${LB_IPV}"
|
|
elif [ ! "${LB_ORIGIN_TABLES[$origin]}" = "${LB_IPV}" ]
|
|
then
|
|
LB_ORIGIN_TABLES[$origin]="46"
|
|
fi
|
|
fi
|
|
|
|
# instruct the finalizer to run IPvX tables
|
|
$TOUCH_CMD "${LB_DIR}/tables.${LB_IPV}"
|
|
}
|
|
|
|
fallback() {
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
default "${@}" weight 0
|
|
}
|
|
|
|
default() {
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
|
|
if [ -z "${work_table}" ]
|
|
then
|
|
error "Statement 'default' should appear after a 'table'."
|
|
exit 1
|
|
fi
|
|
|
|
# check IPvX is as it should
|
|
LB_IPV=${work_ipv}
|
|
if [ ! "${LB_TABLES_IPV[${work_table}]}" = "${LB_IPV}" -a ! "${LB_TABLES_IPV[${work_table}]}" = "46" ]
|
|
then
|
|
error "${FUNCNAME} INTERNAL ERROR: Inherited IPv${LB_IPV} but table is IPv${LB_TABLES_IPV[${work_table}]}."
|
|
exit 1
|
|
fi
|
|
|
|
local gw=
|
|
local weight=100
|
|
while [ ! -z "${1}" ]
|
|
do
|
|
case "${1}" in
|
|
via|gateway|gw)
|
|
gw="${2}"
|
|
shift
|
|
;;
|
|
|
|
weight)
|
|
local weight="${2}"
|
|
shift
|
|
;;
|
|
|
|
*)
|
|
error "${FUNCNAME} on '${work_table}': Cannot understand option '${1}'."
|
|
return 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [ -z "${gw}" ]
|
|
then
|
|
error "${FUNCNAME} on '${work_table}': Cannot work without a gateway."
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "${LB_GATEWAYS[$gw]}" ]
|
|
then
|
|
warning "'${gw}' on '${work_table}': Unknown gateway '${gw}' requested."
|
|
return 1
|
|
fi
|
|
|
|
if [ ! "${LB_GATEWAYS_IPV[$gw]}" = "${LB_IPV}" ]
|
|
then
|
|
error "${FUNCNAME} '${gw}' on '${work_table}': Cannot assign an IPv${LB_GATEWAYS_IPV[$gw]} gateway to an IPv${LB_IPV} table."
|
|
exit 1
|
|
fi
|
|
|
|
if [ "${LB_GATEWAYS[$gw]}" = "0" ]
|
|
then
|
|
warning "'${gw}' on '${work_table}': Ignoring gateway '${gw}', it is not alive."
|
|
return 1
|
|
fi
|
|
|
|
if [ ! -s "${LB_DIR}/gateway.${gw}.paths" ]
|
|
then
|
|
warning "'${gw}' on '${work_table}': Gateway '${gw}' does not have any routes."
|
|
return 1
|
|
fi
|
|
|
|
local context="default"
|
|
if [ "${weight}" = "0" ]
|
|
then
|
|
local context="fallback"
|
|
local weight="100"
|
|
local count=${LB_TABLES_FALLBACK[${work_table}]}
|
|
else
|
|
local count=${LB_TABLES[${work_table}]}
|
|
fi
|
|
|
|
while read
|
|
do
|
|
count=$[count + 1]
|
|
echo "nexthop ${REPLY} weight ${weight}" >>"${LB_DIR}/table.${work_table}.${context}.${LB_IPV}"
|
|
done <"${LB_DIR}/gateway.${gw}.paths"
|
|
|
|
if [ "${context}" = "default" ]
|
|
then
|
|
LB_TABLES[${work_table}]=$count
|
|
else
|
|
LB_TABLES_FALLBACK[${work_table}]=$count
|
|
fi
|
|
}
|
|
|
|
lb_diff_route() {
|
|
local cmd="$1"; shift
|
|
local table="$1"; shift
|
|
|
|
case "${cmd}" in
|
|
add)
|
|
action $IP_CMD -${LB_IPV} route add table ${table} "${@}" || action $IP_CMD -${LB_IPV} route append table ${table} "${@}"
|
|
return $?
|
|
;;
|
|
|
|
delete)
|
|
action $IP_CMD -${LB_IPV} route del table ${table} "${@}"
|
|
return $?
|
|
;;
|
|
|
|
same)
|
|
return 0
|
|
;;
|
|
esac
|
|
return 1
|
|
}
|
|
|
|
update_table() {
|
|
printf >&2 "\n -----------------------------------------------\n"
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
|
|
LB_IPV="${1}"; shift
|
|
local table="${1}"; shift
|
|
|
|
# check IPvX is as it should
|
|
if [ ! "${LB_TABLES_IPV[${table}]}" = "${LB_IPV}" -a ! "${LB_TABLES_IPV[${table}]}" = "46" ]
|
|
then
|
|
error "${FUNCNAME} INTERNAL ERROR: Inherited IPv${LB_IPV} but table is IPv${LB_TABLES_IPV[${work_table}]}."
|
|
exit 1
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------
|
|
# unit testing - make sure it works right
|
|
|
|
if [ -h "${LB_DIR}/table.${table}.origin_routes.${LB_IPV}" -a ! -f "${LB_DIR}/table.${table}.origin_routes.${LB_IPV}" ]
|
|
then
|
|
error "INTERNAL ERROR: table.${table}.origin_routes.${LB_IPV} is an unresolvable link!"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -h "${LB_DIR}/table.${table}.origin_default.${LB_IPV}" -a ! -f "${LB_DIR}/table.${table}.origin_default.${LB_IPV}" ]
|
|
then
|
|
error "INTERNAL ERROR: table.${table}.origin_default.${LB_IPV} is an unresolvable link!"
|
|
exit 1
|
|
fi
|
|
|
|
|
|
# ---------------------------------------------------------------------
|
|
# decide what will be done for this table
|
|
|
|
# 1. if 'table.${table}.origin_routes' exists, we copy the routing table
|
|
# 2. if 'table.${table}.default' or 'table.${table}.origin_default' exist, we set the default gateway
|
|
# 3. if none of the above is present, we do nothing
|
|
|
|
local routing_filter_cmd="$CAT_CMD"
|
|
if [ -f "${LB_DIR}/table.${table}.origin_routes.${LB_IPV}" -a \( -f "${LB_DIR}/table.${table}.default.${LB_IPV}" -o -f "${LB_DIR}/table.${table}.fallback.${LB_IPV}" -o -f "${LB_DIR}/table.${table}.origin_default.${LB_IPV}" \) ]
|
|
then
|
|
# we do both routes copy and set default gateway
|
|
routing_filter_cmd="$CAT_CMD"
|
|
|
|
elif [ -f "${LB_DIR}/table.${table}.default.${LB_IPV}" -o -f "${LB_DIR}/table.${table}.origin_default.${LB_IPV}" ]
|
|
then
|
|
# we should only process default gateway
|
|
routing_filter_cmd="$GREP_CMD ^default"
|
|
|
|
elif [ -f "${LB_DIR}/table.${table}.origin_routes.${LB_IPV}" ]
|
|
then
|
|
# we should only copy the routing table without the default gateway
|
|
routing_filter_cmd="$GREP_CMD -v ^default"
|
|
|
|
else
|
|
# process both routes and default gateway
|
|
routing_filter_cmd="$CAT_CMD"
|
|
|
|
# commented due to issue 78
|
|
# nothing to be done - just return
|
|
#echo >&2 -e " ${COLOR_GREEN}${COLOR_BOLD}Nothing to be done for table ${table}${COLOR_RESET}"
|
|
#return 0
|
|
fi
|
|
|
|
|
|
# ---------------------------------------------------------------------
|
|
# get the existing routing table
|
|
|
|
get_existing_routing_table "${table}" >"${LB_DIR}/table.${table}.existing_routing_table.${LB_IPV}"
|
|
|
|
# if we don't have origin, assume the existing is the origin
|
|
if [ ! -f "${LB_DIR}/table.${table}.origin_routes.${LB_IPV}" ]
|
|
then
|
|
$CAT_CMD "${LB_DIR}/table.${table}.existing_routing_table.${LB_IPV}" |\
|
|
$GREP_CMD -v ^default >"${LB_DIR}/table.${table}.origin_routes.${LB_IPV}"
|
|
fi
|
|
|
|
if [ ! -f "${LB_DIR}/table.${table}.origin_default.${LB_IPV}" ]
|
|
then
|
|
$CAT_CMD "${LB_DIR}/table.${table}.existing_routing_table.${LB_IPV}" |\
|
|
$GREP_CMD -v ^default >"${LB_DIR}/table.${table}.origin_default.${LB_IPV}"
|
|
fi
|
|
|
|
$CAT_CMD "${LB_DIR}/table.${table}.existing_routing_table.${LB_IPV}" |\
|
|
${routing_filter_cmd} >"${LB_DIR}/table.${table}.existing_routes.${LB_IPV}"
|
|
|
|
|
|
# ---------------------------------------------------------------------
|
|
# generate the new routing table
|
|
|
|
local context="default"
|
|
local gateways=${LB_TABLES[${table}]}
|
|
if [ ${gateways} -eq 0 ]
|
|
then
|
|
# there are no active gateways
|
|
# do we have a fallback?
|
|
if [ ${LB_TABLES_FALLBACK[${table}]} -gt 0 ]
|
|
then
|
|
# we have a fallback
|
|
local gateways=${LB_TABLES_FALLBACK[${table}]}
|
|
local context="fallback"
|
|
|
|
echo >&2 " USING fallback gateways!"
|
|
syslog "Using fallback gateways for table ${table}."
|
|
fi
|
|
fi
|
|
|
|
(
|
|
$CAT_CMD "${LB_DIR}/table.${table}.origin_routes.${LB_IPV}"
|
|
|
|
if [ ${gateways} -eq 0 ]
|
|
then
|
|
# we don't have a gateway alive
|
|
# copy the origin default gateway, if we have to
|
|
$CAT_CMD "${LB_DIR}/table.${table}.origin_default.${LB_IPV}"
|
|
|
|
elif [ ${gateways} -eq 1 ]
|
|
then
|
|
# we only have one gateway alive
|
|
|
|
printf "default "
|
|
$CAT_CMD "${LB_DIR}/table.${table}.${context}.${LB_IPV}" |\
|
|
$SED_CMD -e "s/nexthop //g" -e "s/ weight [0-9]\+//g"
|
|
|
|
else
|
|
# we have many (2+) gateways alive
|
|
|
|
printf "default "
|
|
$CAT_CMD "${LB_DIR}/table.${table}.${context}.${LB_IPV}" |\
|
|
$TR_CMD '\n' ' '
|
|
|
|
fi
|
|
) | trim_spaces |\
|
|
$SORT_CMD -u >"${LB_DIR}/table.${table}.new_routing_table.${LB_IPV}"
|
|
|
|
# keep the new routes of our dependands
|
|
$CAT_CMD "${LB_DIR}/table.${table}.new_routing_table.${LB_IPV}" |\
|
|
$GREP_CMD -v ^default >"${LB_DIR}/table.${table}.routes.${LB_IPV}"
|
|
|
|
$CAT_CMD "${LB_DIR}/table.${table}.new_routing_table.${LB_IPV}" |\
|
|
$GREP_CMD ^default >"${LB_DIR}/table.${table}.default.${LB_IPV}"
|
|
|
|
# generate the new routing table for diff
|
|
$CAT_CMD "${LB_DIR}/table.${table}.new_routing_table.${LB_IPV}" |\
|
|
${routing_filter_cmd} >"${LB_DIR}/table.${table}.new_routes.${LB_IPV}"
|
|
|
|
# ---------------------------------------------------------------------
|
|
# diff magic...
|
|
# generate a script to process the diffs of the two files
|
|
|
|
#echo "EXISTING"
|
|
#cat "${LB_DIR}/table.${table}.existing_routes.${LB_IPV}"
|
|
#echo "NEW filter: ${routing_filter_cmd}"
|
|
#cat "${LB_DIR}/table.${table}.new_routes.${LB_IPV}"
|
|
|
|
$DIFF_CMD >"${LB_DIR}/table.${table}.script.${LB_IPV}" \
|
|
--old-line-format="lb_diff_route delete ${table} %L" \
|
|
--new-line-format="lb_diff_route add ${table} %L" \
|
|
--unchanged-line-format="lb_diff_route same ${table} %L" \
|
|
"${LB_DIR}/table.${table}.existing_routes.${LB_IPV}" \
|
|
"${LB_DIR}/table.${table}.new_routes.${LB_IPV}"
|
|
|
|
if [ $? -eq 1 ]
|
|
then
|
|
source "${LB_DIR}/table.${table}.script.${LB_IPV}"
|
|
LB_ALTERED[table.${table}]=1
|
|
|
|
local def=0
|
|
test ! -z "`$GREP_CMD " default " "${LB_DIR}/table.${table}.script.${LB_IPV}"`" && def=1
|
|
|
|
echo >&2
|
|
echo >&2 -e " ${COLOR_YELLOW}${COLOR_BOLD}Updated routing table ${table}!${COLOR_RESET}"
|
|
|
|
updated_routes "${table}" "${def}"
|
|
else
|
|
echo >&2
|
|
echo >&2 -e " ${COLOR_GREEN}${COLOR_BOLD}Table's ${table} routing is already as it should.${COLOR_RESET}"
|
|
echo >&2
|
|
fi
|
|
}
|
|
|
|
finalize_tables() {
|
|
printf >&2 "\n-------------------------------------------------------------------------------\n"
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
|
|
LB_IPV="${1}"
|
|
if [ -z "${LB_IPV}" ]
|
|
then
|
|
error "INTERNAL ERROR: Detected empty LB_IPV."
|
|
exit 1
|
|
fi
|
|
|
|
# put all the tables in this variable
|
|
local -A all=()
|
|
local x=
|
|
for x in "${!LB_ORIGIN_TABLES[@]}"
|
|
do
|
|
test "${LB_ORIGIN_TABLES[$x]}" = "${LB_IPV}" -o "${LB_ORIGIN_TABLES[$x]}" = "46" && all[$x]=1
|
|
done
|
|
for x in "${!LB_TABLES[@]}"
|
|
do
|
|
test "${LB_TABLES_IPV[$x]}" = "${LB_IPV}" -o "${LB_TABLES_IPV[$x]}" = "46" && all[$x]=1
|
|
done
|
|
|
|
# execute the tables with circular dependency check
|
|
while [ "${#all[@]}" -ne 0 ]
|
|
do
|
|
local found=0
|
|
for x in "${!all[@]}"
|
|
do
|
|
printf >&2 "\n > Checking dependency for IPv${LB_IPV} table ${x}... "
|
|
|
|
if [ ! -h "${LB_DIR}/table.${x}.origin_routes.${LB_IPV}" -o -f "${LB_DIR}/table.${x}.origin_routes.${LB_IPV}" ]
|
|
then
|
|
if [ ! -z "${LB_TABLES[$x]}" ]
|
|
then
|
|
printf >&2 "READY!\n"
|
|
update_table "${LB_IPV}" "${x}"
|
|
else
|
|
printf >&2 "ORIGIN ONLY\n"
|
|
|
|
get_existing_routing_table "${x}" >"${LB_DIR}/table.${x}.existing_routing_table.${LB_IPV}" || exit 1
|
|
|
|
$CAT_CMD "${LB_DIR}/table.${x}.existing_routing_table" |\
|
|
$GREP_CMD -v ^default >"${LB_DIR}/table.${x}.routes.${LB_IPV}"
|
|
|
|
$CAT_CMD "${LB_DIR}/table.${x}.existing_routing_table" |\
|
|
$GREP_CMD ^default >"${LB_DIR}/table.${x}.default.${LB_IPV}"
|
|
fi
|
|
|
|
unset all[$x]
|
|
local found=1
|
|
break
|
|
else
|
|
printf >&2 "NOT READY\n"
|
|
fi
|
|
done
|
|
|
|
if [ $found -eq 0 ]
|
|
then
|
|
error "CIRCULAR DEPENDENCY among the tables detected."
|
|
exit 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
# this is to overwritten by the caller
|
|
# it is called only when routing rules are updated (added/removed)
|
|
updated_routes() {
|
|
local table="${1}"
|
|
local def="${2}" # if this is 1, the default route has been updated
|
|
|
|
return 0
|
|
}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# policy based routing - rules section
|
|
|
|
LB_DO_RULES_IPV4=0
|
|
LB_DO_RULES_IPV6=0
|
|
LB_RULES_DEFAULT_IPV=${LB_DEFAULT_IPV}
|
|
|
|
policy4() { policy ipv4 "${@}"; }
|
|
policy6() { policy ipv6 "${@}"; }
|
|
policy() {
|
|
printf >&2 "\n-------------------------------------------------------------------------------\n"
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
LB_RULES_DEFAULT_IPV=${LB_DEFAULT_IPV}
|
|
|
|
while [ ! -z "${1}" ]
|
|
do
|
|
case "${1}" in
|
|
ipv4)
|
|
LB_RULES_DEFAULT_IPV=4
|
|
;;
|
|
|
|
ipv6)
|
|
LB_RULES_DEFAULT_IPV=6
|
|
;;
|
|
|
|
*)
|
|
error "${FUNCNAME}: Cannot understand option '${1}'."
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [ "${LB_RULES_DEFAULT_IPV}" = "6" ]
|
|
then
|
|
LB_DO_RULES_IPV6=1
|
|
else
|
|
LB_DO_RULES_IPV4=1
|
|
fi
|
|
$TOUCH_CMD "${LB_DIR}/rules.${LB_RULES_DEFAULT_IPV}"
|
|
}
|
|
|
|
LB_RULE_BASE4=1000
|
|
LB_RULE_BASE6=1000
|
|
|
|
rules4() { rules ipv4 "${@}"; }
|
|
rules6() { rules ipv6 "${@}"; }
|
|
rules() {
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
LB_IPV=${LB_RULES_DEFAULT_IPV}
|
|
|
|
local -a tos=()
|
|
local -a mark=()
|
|
local -a src=()
|
|
local -a dst=()
|
|
local -a inface=()
|
|
|
|
local marktype=
|
|
|
|
local action=
|
|
local table=
|
|
local not=
|
|
local cmd=
|
|
while [ ! -z "${1}" ]
|
|
do
|
|
case "${1}" in
|
|
ipv4)
|
|
LB_IPV=4
|
|
LB_DO_RULES_IPV4=1
|
|
$TOUCH_CMD "${LB_DIR}/rules.${LB_IPV}"
|
|
;;
|
|
|
|
ipv6)
|
|
LB_IPV=6
|
|
LB_DO_RULES_IPV6=1
|
|
$TOUCH_CMD "${LB_DIR}/rules.${LB_IPV}"
|
|
;;
|
|
|
|
at|lookup|table)
|
|
local action="lookup ${2}"
|
|
local table="${2}"
|
|
shift
|
|
;;
|
|
|
|
# nat) # this converts the rule to 'masquerade' and gives a warning it is deprecated
|
|
|
|
prohibit|unreachable) # 'reject' gives: Error: argument "reject" is wrong: Failed to parse rule type
|
|
local action="${1}"
|
|
;;
|
|
|
|
rawmark)
|
|
local marktype=
|
|
local cmd="mark"
|
|
;;
|
|
|
|
mark)
|
|
local marktype="usermark"
|
|
local cmd="mark"
|
|
;;
|
|
|
|
custommark)
|
|
local marktype="${2}"; shift
|
|
local cmd="mark"
|
|
if [ -z "${MARKS_MASKS[$marktype]}" ]
|
|
then
|
|
error "Mark type '${marktype}' is not defined."
|
|
exit 1
|
|
fi
|
|
;;
|
|
|
|
connmark)
|
|
local marktype="connmark"
|
|
local cmd="mark"
|
|
;;
|
|
|
|
src|from)
|
|
local cmd="src"
|
|
;;
|
|
|
|
inface|iif)
|
|
local cmd="inface"
|
|
;;
|
|
|
|
dst|to)
|
|
local cmd="dst"
|
|
;;
|
|
|
|
tos)
|
|
local cmd="tos"
|
|
;;
|
|
|
|
not)
|
|
local not="not"
|
|
;;
|
|
|
|
gw-src-ips)
|
|
local x=${2}; shift
|
|
if [ -z "${LB_GATEWAYS[$x]}" ]
|
|
then
|
|
error "Cannot find gateway '${x}'."
|
|
exit 1
|
|
fi
|
|
if [ ! "${LB_GATEWAYS_IPV[$x]}" = "${LB_IPV}" ]
|
|
then
|
|
error "Cannot add IPv${LB_GATEWAYS_IPV[$x]} gateway source IPs to IPv${LB_IPV} rules."
|
|
exit 1
|
|
fi
|
|
if [ "${LB_GATEWAYS[$x]}" -eq 0 ]
|
|
then
|
|
warning "Ignoring rules for gateway '${x}'. Gateway '${x}' is not alive."
|
|
else
|
|
src+=(`loadfile "${LB_DIR}/gateway.${x}.source-ips"`)
|
|
fi
|
|
;;
|
|
|
|
loadfile)
|
|
case "${cmd}" in
|
|
src) src+=(`loadfile "${2}"`);;
|
|
dst) dst+=(`loadfile "${2}"`);;
|
|
esac
|
|
shift
|
|
;;
|
|
|
|
*)
|
|
case "${cmd}" in
|
|
mark)
|
|
if [ -z "${marktype}" ]
|
|
then
|
|
mark+=( ${1//,/ } )
|
|
else
|
|
mark+=( $(mark_value $marktype ${1//,/ }) )
|
|
fi
|
|
;;
|
|
|
|
tos) tos+=( ${1//,/ } );;
|
|
src) src+=( ${1//,/ } );;
|
|
dst) dst+=( ${1//,/ } );;
|
|
inface) inface+=( ${1//,/ } );;
|
|
esac
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
local base=
|
|
if [ "${LB_IPV}" = "6" ]
|
|
then
|
|
local base=${LB_RULE_BASE6}
|
|
LB_DO_RULES_IPV6=1
|
|
else
|
|
local base=${LB_RULE_BASE4}
|
|
LB_DO_RULES_IPV4=1
|
|
fi
|
|
$TOUCH_CMD "${LB_DIR}/rules.${LB_IPV}"
|
|
|
|
# give some space between rules
|
|
base=$[ ( (base + LB_RULE_STEP) / LB_RULE_STEP ) * LB_RULE_STEP ]
|
|
|
|
# put it back, in case we return before the end
|
|
if [ "${LB_IPV}" = "6" ]
|
|
then
|
|
LB_RULE_BASE6=${base}
|
|
else
|
|
LB_RULE_BASE4=${base}
|
|
fi
|
|
|
|
if [ "${#mark[*]}${#tos[*]}${#src[*]}${#dst[*]}${#inface[*]}" = "00000" ]
|
|
then
|
|
warning "No rules generated."
|
|
return 0
|
|
fi
|
|
|
|
if [ ! -z "${table}" ]
|
|
then
|
|
if [ -z "${LB_TABLES[$table]}" ]
|
|
then
|
|
error "${FUNCNAME}: table $table does not exist."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
test ${#mark[@]} -eq 0 && mark=('any')
|
|
test ${#tos[@]} -eq 0 && tos=('any')
|
|
test ${#src[@]} -eq 0 && src=('any')
|
|
test ${#dst[@]} -eq 0 && dst=('any')
|
|
test ${#inface[@]} -eq 0 && inface=('any')
|
|
|
|
local m=
|
|
local t=
|
|
local s=
|
|
local d=
|
|
local i=
|
|
for m in ${mark[@]}
|
|
do
|
|
test "${m}" = "any" && local m=
|
|
test ! -z "${m}" && local m="fwmark ${m}"
|
|
|
|
for t in ${tos[@]}
|
|
do
|
|
test "${t}" = "any" && local t=
|
|
test ! -z "${t}" && local t="tos ${t}"
|
|
|
|
for i in ${inface[@]}
|
|
do
|
|
test "${i}" = "any" && local i=
|
|
test ! -z "${i}" && local d="iif ${i}"
|
|
|
|
for s in ${src[@]}
|
|
do
|
|
test "${s}" = "any" && local s="all"
|
|
test ! -z "${s}" && local s="from ${s}"
|
|
|
|
for d in ${dst[@]}
|
|
do
|
|
test "${d}" = "any" && local d=
|
|
test ! -z "${d}" && local d="to ${d}"
|
|
|
|
echo "priority ${base} ${not} ${s} ${d} ${t} ${m} ${i} ${action}" >>"${LB_DIR}/rules.${LB_IPV}"
|
|
base=$[base + 1]
|
|
done
|
|
done
|
|
done
|
|
done
|
|
done
|
|
|
|
# put the priority back to the global variable
|
|
if [ "${LB_IPV}" = "6" ]
|
|
then
|
|
LB_RULE_BASE6=${base}
|
|
else
|
|
LB_RULE_BASE4=${base}
|
|
fi
|
|
}
|
|
|
|
mark() { rules ${FUNCNAME} "${@}"; }
|
|
fwmark() { rules ${FUNCNAME} "${@}"; }
|
|
connmark() { rules ${FUNCNAME} "${@}"; }
|
|
custommark() { rules ${FUNCNAME} "${@}"; }
|
|
rawmark() { rules ${FUNCNAME} "${@}"; }
|
|
src() { rules ${FUNCNAME} "${@}"; }
|
|
dst() { rules ${FUNCNAME} "${@}"; }
|
|
from() { rules ${FUNCNAME} "${@}"; }
|
|
to() { rules ${FUNCNAME} "${@}"; }
|
|
tos() { rules ${FUNCNAME} "${@}"; }
|
|
not() { rules ${FUNCNAME} "${@}"; }
|
|
|
|
# function to be called by the diff generated script
|
|
lb_diff_rule() {
|
|
local cmd="$1"; shift
|
|
|
|
case "${cmd}" in
|
|
add)
|
|
action $IP_CMD -${LB_IPV} rule add "${@}"
|
|
return $?
|
|
;;
|
|
|
|
delete)
|
|
action $IP_CMD -${LB_IPV} rule del "${@}"
|
|
return $?
|
|
;;
|
|
|
|
same)
|
|
return 0
|
|
;;
|
|
esac
|
|
return 1
|
|
}
|
|
|
|
normalize_rules() {
|
|
$SED_CMD -e "s| fwmark 0x\([0-9a-fA-F]\)\([ /]\)| fwmark 0x0\1\2|g" \
|
|
-e "s| tos 0x\([0-9a-fA-F]\) | tos 0x0\1|g" \
|
|
-e "s| tos 0x02 | tos mincost |g" \
|
|
-e "s| tos 0x04 | tos reliability |g" \
|
|
-e "s| tos 0x08 | tos throughput |g" \
|
|
-e "s| tos 0x0a | tos lowdelay |g" \
|
|
-e "s| tos 2 | tos mincost |g" \
|
|
-e "s| tos 4 | tos reliability |g" \
|
|
-e "s| tos 8 | tos throughput |g" \
|
|
-e "s| tos 10 | tos lowdelay |g" \
|
|
-e "s|0x0\+|0x|g"
|
|
}
|
|
|
|
finalize_rules() {
|
|
printf >&2 "\n-------------------------------------------------------------------------------\n"
|
|
DEBUGCMD "${FUNCNAME}" "${#FUNCNAME[*]}" "${@}"
|
|
|
|
LB_IPV="${1}"
|
|
if [ -z "${LB_IPV}" ]
|
|
then
|
|
error "INTERNAL ERROR: Detected empty LB_IPV."
|
|
exit 1
|
|
fi
|
|
|
|
# copy the tables and remove 'main' from it
|
|
local string=$(declare -p LB_TABLES)
|
|
eval "local -A tables=${string#*=}"
|
|
unset tables[main]
|
|
|
|
# get all the rules for all the tables except 'main'
|
|
run $IP_CMD -${LB_IPV} rule show |\
|
|
trim_spaces |\
|
|
$EGREP_CMD -v "^(0|32766|32767)" |\
|
|
$SED_CMD "s/\(^[0-9]\+\): /priority \1 /g" |\
|
|
normalize_rules |\
|
|
$SORT_CMD >"${LB_DIR}/rules.existing.${LB_IPV}"
|
|
|
|
# fix the generated rules, from the rules()
|
|
$CAT_CMD "${LB_DIR}/rules.${LB_IPV}" |\
|
|
trim_spaces |\
|
|
normalize_rules |\
|
|
$SORT_CMD -u >"${LB_DIR}/rules.new.${LB_IPV}"
|
|
|
|
# diff magic...
|
|
# generate a script to process the diffs of the two files
|
|
$DIFF_CMD >"${LB_DIR}/rules.script.${LB_IPV}" \
|
|
--old-line-format="lb_diff_rule delete ${table} %L" \
|
|
--new-line-format="lb_diff_rule add ${table} %L" \
|
|
--unchanged-line-format="lb_diff_rule same ${table} %L" \
|
|
"${LB_DIR}/rules.existing.${LB_IPV}" \
|
|
"${LB_DIR}/rules.new.${LB_IPV}"
|
|
|
|
if [ $? -eq 1 ]
|
|
then
|
|
source "${LB_DIR}/rules.script.${LB_IPV}"
|
|
LB_ALTERED[routing.rules.ipv${LB_IPV}]=1
|
|
|
|
echo >&2
|
|
echo >&2 -e " ${COLOR_YELLOW}${COLOR_BOLD}Updated IPv${LB_IPV} routing rules!${COLOR_RESET}"
|
|
|
|
updated_rules
|
|
else
|
|
echo >&2
|
|
echo >&2 -e " ${COLOR_GREEN}${COLOR_BOLD}Nothing to be done for IPv${LB_IPV} routing rules.${COLOR_RESET}"
|
|
fi
|
|
}
|
|
|
|
# this is to overwritten by the caller
|
|
# it is called only when routing rules are updated (added/removed)
|
|
updated_rules() {
|
|
return 0
|
|
}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# other functions
|
|
|
|
# this tries to find all the IPs of an Autonomous System (AS).
|
|
# 1. it requires an IP from the caller.
|
|
# 2. it queries whois for this IP to find its AS.
|
|
# 3. it queries RIPE to get all the IP address space for this AS.
|
|
asips() {
|
|
common_require_cmd $PROGRAM_FILE WHOIS_CMD
|
|
common_require_cmd $PROGRAM_FILE JQ_CMD
|
|
common_require_cmd $PROGRAM_FILE HEAD_CMD
|
|
common_require_cmd $PROGRAM_FILE WGET_CMD
|
|
|
|
local ip="${1}"
|
|
|
|
if [ -z "${ip}" ]
|
|
then
|
|
echo >&2 "${FUNCNAME} requires an IP address."
|
|
echo >&2 "Commands: 'whois' and 'jq' have to be installed for this to work."
|
|
return 1
|
|
fi
|
|
|
|
echo >&2
|
|
echo >&2 "Querying whois for IP ${ip}..."
|
|
local as="`$WHOIS_CMD "${1}" | $GREP_CMD "origin:" | trim_spaces | $CUT_CMD -d ' ' -f 2 | $GREP_CMD ^AS | $HEAD_CMD -n 1`"
|
|
if [ -z "${as}" ]
|
|
then
|
|
echo >&2 "Cannot find the Autonomous System of IP '${ip}'."
|
|
return 1
|
|
fi
|
|
echo >&2 "IP ${ip} is part of Autonomous System: ${as}."
|
|
|
|
echo >&2
|
|
echo >&2 "Querying RIPE for ${as}..."
|
|
$WGET_CMD -O - "https://stat.ripe.net/data/as-routing-consistency/data.json?resource=${as}" |\
|
|
$JQ_CMD .data.prefixes[].prefix |\
|
|
$SED_CMD -e 's| ||g' -e 's|"||g' |\
|
|
$EGREP_CMD "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+" |\
|
|
$IPRANGE_CMD
|
|
}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# help
|
|
|
|
emit_version() {
|
|
$CAT_CMD <<EOF
|
|
|
|
FireHOL LinkBalancer $VERSION
|
|
(C) Copyright 2015 Costa Tsaousis <costa@tsaousis.gr>
|
|
(C) Copyright 2015 Phil Whineray <phil@firehol.org>
|
|
FireHOL is distributed under the GPL v2+.
|
|
Home Page: http://firehol.org
|
|
|
|
-------------------------------------------------------------------------
|
|
Get notified of new FireHOL releases by subscribing to the mailing list:
|
|
http://lists.firehol.org/mailman/listinfo/firehol-support/
|
|
-------------------------------------------------------------------------
|
|
|
|
EOF
|
|
}
|
|
|
|
|
|
help() {
|
|
|
|
$CAT_CMD <<EOFHELP
|
|
Modes:
|
|
|
|
${PROGRAM_FILE} boot [seconds]
|
|
|
|
- default 'seconds' is 300 (run every 5 mins)
|
|
|
|
Runs LinkBalancer in a screen session in the background.
|
|
To see it running run: 'screen -r link-balancer'
|
|
To exit screen without stopping it, press: Control-a then d.
|
|
|
|
|
|
${PROGRAM_FILE} loop [seconds]
|
|
|
|
like above, but run in foreground, not in a screen session.
|
|
|
|
|
|
${PROGRAM_FILE} -h|--help
|
|
|
|
Present this help file.
|
|
|
|
|
|
${PROGRAM_FILE} [-t|--test] [-f|--fast] [-d|--debug]
|
|
|
|
-t or --test
|
|
|
|
Do not alter anything on the system.
|
|
Just show what would have been done.
|
|
|
|
-f or --fast
|
|
|
|
Assume all gateways are up, if their interfaces are up.
|
|
This should be run from '/etc/ppp/ip-down' to quickly
|
|
restore the default gateways of the system.
|
|
|
|
-d or --debug
|
|
|
|
At the end of the execution, output all temporary
|
|
files.
|
|
|
|
EOFHELP
|
|
|
|
$CAT_CMD >"${LB_CONFIG}.example" <<EOFCONFIG
|
|
# Link Balancer Configuration file
|
|
|
|
# default is IPv4.
|
|
# You can change the default to IPv6 by setting this to "6":
|
|
LB_DEFAULT_IPV="4"
|
|
|
|
# You can also specify IPv4 or IPv6 for gateways, tables and policy by
|
|
# using gateway4 gateway6 table4 table6 policy4 policy6
|
|
# If you use these without the number, the default above will be used.
|
|
# You can also run 'ipv4 gateway ...' or 'ipv4 gateway ...'
|
|
# for all these statements.
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# define all the gateways
|
|
|
|
# TEMPLATE:
|
|
#
|
|
# gateway GATEWAY_NAME dev DEVICE [gw "IPs"] [src "IPs"] [check METHOD "IPs" ]
|
|
#
|
|
# - DEVICE is the interface via which the gateway is accessible
|
|
# - gw IP is autodetected if not given
|
|
# - src IP is autodetected if not given
|
|
# - METHODs: ping traceroute alwayson (default is ping)
|
|
# - when check IPs is empty the gw IPs are used
|
|
#
|
|
# In general
|
|
# - for point to point PPP interfaces everything is autodetected
|
|
# - for point to lan PPP interfaces, you have to give gw IP and src IP
|
|
# - for eth devices you have to specify the gw IP
|
|
#
|
|
# Hint: If you use PPP devices, specify in the peers file of pppd the
|
|
# unit number you want it to use. This way, you will know that, for example,
|
|
# you office VPN is always at ppp14.
|
|
# Another way is to rename the ppp device in /etc/ppp/ip-up, so that
|
|
# instead of having 'ppp14', you will have the interface as 'work1'.
|
|
#
|
|
# examples:
|
|
|
|
# wan providers
|
|
gateway dsl1 dev ppp11
|
|
gateway dsl2 dev eth3 gw 2.2.2.2
|
|
gateway dsl3 dev eth4 gw 3.3.3.3
|
|
|
|
# dual path office VPN
|
|
gateway work1 dev ppp14 check 172.16.1.2
|
|
gateway work2 dev tun0 check 172.16.1.4
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# define all the routing tables
|
|
|
|
# Tables can be defined using numbers from 1 to 252.
|
|
# Do not use tables 0, 253, 254 and 255. They are system tables.
|
|
|
|
# You can give names to tables by editing /etc/iproute2/rt_tables
|
|
# If you give names, you can use them here.
|
|
|
|
# TEMPLATE:
|
|
#
|
|
# table NAME [from ORIGIN_TABLE_NAME] [nodefault]
|
|
#
|
|
# - NAME and ORIGIN_TABLE_NAME can either be IDs or names
|
|
# - The default ORIGIN_TABLE_NAME is: main
|
|
# - nodefault means in case the table is left without a default gateway,
|
|
# do not copy the default gateway of the ORIGIN_TABLE_NAME
|
|
#
|
|
# then for each table, define the default gateways you want:
|
|
#
|
|
# default via GATEWAY_NAME [weight 0-250]
|
|
#
|
|
# - GATEWAY_NAME is the name of the gateway as given in the gateways section.
|
|
# - default weight is 100. Weights can be given as 0 to 255.
|
|
# - weight 0 is special. It means don't use this unless there is no
|
|
# other gateway available.
|
|
#
|
|
# you can add as many default gateways per table, as you need.
|
|
#
|
|
# You should define one table per gateway and one for load-balancing the
|
|
# gateways. You need one table per gateway, so that if you receive requests
|
|
# from the gateways, you will respond to these requests via the same path.
|
|
#
|
|
# examples:
|
|
|
|
# one table per WAN gateway
|
|
table dsl1
|
|
default via dsl1
|
|
|
|
table dsl2
|
|
default via dsl2
|
|
|
|
table dsl3
|
|
default via dsl3
|
|
|
|
# this is the main system routing table
|
|
table main
|
|
default via dsl1 weight 250
|
|
default via dsl2 weight 150
|
|
default via dsl3 weight 50
|
|
|
|
|
|
# one table per WORK gateway
|
|
table work1 from WORK # we copy WORK here, not 'main'
|
|
default via work1
|
|
|
|
table work2 from WORK # we copy WORK here, not 'main'
|
|
default via work2
|
|
|
|
# this is based on 'main'
|
|
# but we don't need its default gateway
|
|
table WORK nodefault
|
|
default via work1 weight 100
|
|
default via work2 weight 50
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# define rules for selecting tables
|
|
|
|
policy
|
|
|
|
# define a mark for each gateway table.
|
|
# this makes packets originated from them to be routed back to them
|
|
connmark 0x1 table dsl1
|
|
connmark 0x2 table dsl2
|
|
connmark 0x3 table dsl3
|
|
connmark 0x11 table work1
|
|
connmark 0x12 table work2
|
|
|
|
# You will have to add these connmark rules at the top of your firehol.conf:
|
|
# connmark 0x1 interface ppp11
|
|
# connmark 0x2 interface eth3
|
|
# connmark 0x3 interface eth4
|
|
# connmark 0x11 interface ppp14
|
|
# connmark 0x12 interface tun0
|
|
|
|
# Now you can add policy routing rules.
|
|
# TEMPLATE:
|
|
# rules [not] [from|src IPs] [to|dst IPs] [mark X] [tos X] table TABLE_NAME
|
|
#
|
|
# example:
|
|
|
|
# clients at 192.168.1.0 use only dsl1
|
|
rules src 192.168.1.0/24 table dsl1
|
|
|
|
# web server at 1.2.3.4 is routed only via dsl2
|
|
rules dst 1.2.3.4 table dsl2
|
|
|
|
# You can use LinkBalancer to find all the IP address space of your upstream
|
|
# providers:
|
|
# Run:
|
|
#
|
|
# ${PROGRAM_FILE} asips ONE_IP_OF_YOUR_PROVIDER
|
|
#
|
|
# LinkBalancer will query RIPE to find all the IPs of your provider.
|
|
# So you can add:
|
|
|
|
rules dst IPS_OF_PROVIDER_1 table dsl1
|
|
rules dst IPS_OF_PROVIDER_2 table dsl2
|
|
rules dst IPS_OF_PROVIDER_3 table dsl3
|
|
|
|
# You can also have all the IPs in separate files:
|
|
# Run:
|
|
#
|
|
# ${PROGRAM_FILE} asips ONE_IP_OF_YOUR_PROVIDER_1 >$SYSCONFDIR/firehol/PROVIDER1_IPS
|
|
#
|
|
# Then:
|
|
rules dst loadfile PROVIDER1_IPS table dsl1
|
|
|
|
|
|
# Last, you may want to route local traffic to the proper table.
|
|
# This is not required through.
|
|
# It will only help if you run daemons that bind to specific IPs
|
|
# and you want them to be routed based on these IPs.
|
|
|
|
rules src gw-src-ips dsl1 table dsl1
|
|
rules src gw-src-ips dsl2 table dsl2
|
|
rules src gw-src-ips dsl3 table dsl3
|
|
rules src gw-src-ips work1 table work1
|
|
rules src gw-src-ips work2 table work2
|
|
|
|
|
|
EOFCONFIG
|
|
|
|
echo >&2 "Saved ${LB_CONFIG}.example for your convenience..."
|
|
|
|
}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# options
|
|
|
|
emit_version
|
|
|
|
while [ ! -z "$1" ]
|
|
do
|
|
case "$1" in
|
|
-h|--help)
|
|
help
|
|
exit 0
|
|
;;
|
|
|
|
-t|--test)
|
|
DRY_RUN=1
|
|
;;
|
|
|
|
-f|--fast)
|
|
FORCE_ALWAYSON=1
|
|
;;
|
|
|
|
-d|--debug)
|
|
DEBUG_ALL_DATA=1
|
|
;;
|
|
|
|
asips)
|
|
shift
|
|
asips "${@}"
|
|
LB_RESULT_CODE=$?
|
|
exit ${LB_RESULT_CODE}
|
|
;;
|
|
|
|
*)
|
|
echo >&2 "Unknown option '$1'"
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
syslog "Started"
|
|
|
|
if [ ! -f "${LB_CONFIG}" ]
|
|
then
|
|
echo >&2 "Cannot find config file '${LB_CONFIG}'"
|
|
exit 1
|
|
fi
|
|
|
|
printf >&2 "\n\n###############################################################################\n"
|
|
printf >&2 "PROCESSING CONFIGURATION\n"
|
|
|
|
# process the script
|
|
enable -n trap # Disable the trap buildin shell command.
|
|
{ source "${LB_CONFIG}"; }
|
|
ret=$?
|
|
enable trap
|
|
if [ $ret -ne 0 ]
|
|
then
|
|
error "Preprocessing of '${LB_CONFIG}' failed with code $ret. No changes made to your system."
|
|
exit 1
|
|
fi
|
|
|
|
# apply the changes
|
|
printf >&2 "\n\n###############################################################################\n"
|
|
printf >&2 "APPLYING CHANGES\n"
|
|
|
|
test -f "${LB_DIR}/tables.4" && finalize_tables 4
|
|
test -f "${LB_DIR}/tables.6" && finalize_tables 6
|
|
test ${LB_DO_RULES_IPV4} -eq 1 && finalize_rules 4
|
|
test ${LB_DO_RULES_IPV6} -eq 1 && finalize_rules 6
|
|
LB_RESULT_CODE=0
|
|
|
|
# all done!
|
|
echo >&2
|
|
test ! -z "${!LB_ALTERED[*]}" && warning "Altered: ${!LB_ALTERED[@]}"
|
|
|
|
# No need to cleanup
|
|
# The trap will cleanup the temporary directory
|