#!/bin/sh # # Shorewall 4.1 -- /usr/share/shorewall/lib.providers # # This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt] # # (c) 1999,2000,2001,2002,2003,2004,2005,2006,2007 - Tom Eastep (teastep@shorewall.net) # # Complete documentation is available at http://shorewall.net # # This program is free software; you can redistribute it and/or modify # it under the terms of Version 2 of the GNU General Public License # as published by the Free Software Foundation. # # 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, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # This library is loaded by /usr/share/shorewall/compiler when the providers file is # non-empty. # # # Process the providers file # setup_providers() { local table local number local mark local duplicate local interface local gateway local options local provider local address local copy local route local loose local addresses local rulenum local rulebase local balance local save_indent save_indent="$INDENT" local mask mask= local first first=Yes local save_indent1 save_indent1= copy_table() { indent >&3 << __EOF__ ip route show table $duplicate | while read net route; do case \$net in default|nexthop) ;; *) run_ip route add table $number \$net \$route ;; esac done __EOF__ } copy_and_edit_table() { indent >&3 << __EOF__ ip route show table $duplicate | while read net route; do case \$net in default|nexthop) ;; *) case \$(find_device \$route) in `echo $copy\) | sed 's/ /|/g'` run_ip route add table $number \$net \$route ;; esac ;; esac done __EOF__ } balance_default_route() # $1 = weight { balance=yes save_command if [ -n "$first" ]; then if [ -n "$gateway" ] ; then save_command "DEFAULT_ROUTE=\"nexthop via $gateway dev $interface weight $1\"" else save_command "DEFAULT_ROUTE=\"nexthop dev $interface weight $1\"" fi first= else if [ -n "$gateway" ] ; then save_command "DEFAULT_ROUTE=\"\$DEFAULT_ROUTE nexthop via $gateway dev $interface weight $1\"" else save_command "DEFAULT_ROUTE=\"\$DEFAULT_ROUTE nexthop dev $interface weight $1\"" fi fi } add_a_provider() { local t local n local iface local option local optional optional= [ -n "$MANGLE_ENABLED" ] || fatal_error "Providers require mangle support in your kernel and iptables" for t in $PROVIDERS local main default unspec; do if [ "$t" = "$table" ]; then fatal_error "Duplicate Provider: $table, provider: \"$provider\"" fi eval n=\$${t}_number # # The following is because the %$#@ shell doesn't accept hex numbers in '-eq' tests # if [ $(($n)) -eq $(($number)) ]; then fatal_error "Duplicate Provider number: $number, provider: \"$provider\"" fi done eval ${table}_number=$number indent >&3 << __EOF__ # # Add Provider $table ($number) # __EOF__ save_command "if interface_is_usable $interface; then" save_indent1="$INDENT" INDENT="$INDENT " iface=$(chain_base $interface) save_command "${iface}_up=Yes" save_command "qt ip route flush table $number" indent >&3 << __EOF__ echo "qt ip route flush table $number" >> \${VARDIR}/undo_routing __EOF__ if [ "x${duplicate:=-}" != x- ]; then if [ "x${copy:=-}" != "x-" ]; then if [ "x${copy}" = xnone ]; then copy=$interface else copy="$interface $(separate_list $copy)" fi copy_and_edit_table else copy_table fi elif [ "x${copy:=-}" != x- ]; then fatal_error "A non-empty COPY column requires that a routing table be specified in the DUPLICATE column" fi if [ "x$gateway" = xdetect ] ; then gateway='$gateway' indent >&3 << __EOF__ gateway=\$(detect_gateway $interface) if [ -n "\$gateway" ]; then run_ip route replace \$gateway src \$(find_first_interface_address $interface) dev $interface table $number run_ip route add default via \$gateway dev $interface table $number else fatal_error "Unable to detect the gateway through interface $interface" fi __EOF__ elif [ "x$gateway" != "x-" -a -n "$gateway" ]; then indent >&3 << __EOF__ run_ip route replace $gateway src \$(find_first_interface_address $interface) dev $interface table $number run_ip route add default via $gateway dev $interface table $number __EOF__ else gateway= save_command "run_ip route add default dev $interface table $number" fi if [ x${mark} != x- ]; then verify_mark $mark if [ $(($mark)) -lt 256 ]; then if [ -n "$HIGH_ROUTE_MARKS" ]; then fatal_error "Invalid Mark Value ($mark) with HIGH_ROUTE_MARKS=Yes" fi elif [ -z "$HIGH_ROUTE_MARKS" ]; then fatal_error "Invalid Mark Value ($mark) with HIGH_ROUTE_MARKS=No" fi eval ${table}_mark=$mark [ -n "$DELETE_THEN_ADD" ] && qt ip rule del fwmark $mark indent >&3 << __EOF__ run_ip rule add fwmark $mark pref $((10000 + $mark)) table $number echo "qt ip rule del fwmark $mark" >> \${VARDIR}/undo_routing __EOF__ fi loose= for option in $(separate_list $options); do case $option in -) ;; track) list_search $interface $ROUTEMARK_INTERFACES && \ fatal_error "Interface $interface is tracked through an earlier provider" [ x${mark} = x- ] && fatal_error "The 'track' option requires a numeric value in the MARK column - Provider \"$provider\"" eval ${iface}_routemark=$mark ROUTEMARK_INTERFACES="$ROUTEMARK_INTERFACES $interface" ;; balance=*) balance_default_route ${option#*=} ;; balance) balance_default_route 1 ;; loose) loose=Yes ;; optional) optional=Yes ;; *) error_message "WARNING: Invalid option ($option) ignored in provider \"$provider\"" ;; esac done rulenum=0 if [ -z "$loose" ]; then rulebase=$(( 20000 + ( 256 * ($number-1) ) )) indent >&3 << __EOF__ rulenum=0 find_interface_addresses $interface | while read address; do __EOF__ [ -n "$DELETE_THEN_ADD" ] && save_command " qt ip rule del from \$address" indent >&3 << __EOF__ run_ip rule add from \$address pref \$(( $rulebase + \$rulenum )) table $number echo "qt ip rule del from \$address" >> \${VARDIR}/undo_routing rulenum=\$((\$rulenum + 1)) done __EOF__ elif [ -n "$DELETE_THEN_ADD" ]; then indent >&3 << __EOF__ find_interface_addresses $interface | while read address; do qt ip rule del from \$address done __EOF__ [ -n "$balance" ] && error_message "WARNING: 'balance' and 'loose' should not be specified together - Provider \"$provider\"" fi indent >&3 << __EOF__ progress_message " Provider $table ($number) Added" __EOF__ INDENT="$save_indent1" save_command else if [ -n "$optional" ]; then save_command " error_message \"WARNING: Interface $interface is not configured -- Provider $table ($number) not Added\"" save_command " ${iface}_up=" else save_command " fatal_error \"ERROR: Interface $interface is not configured -- Provider $table ($number) Cannot be Added\"" fi save_command fi save_command } verify_provider() { local p local n for p in $PROVIDERS main; do [ "$p" = "$1" ] && return 0 eval n=\$${p}_number [ "$n" = "$1" ] && return 0 done fatal_error "Unknown provider $1 in route rule \"$rule\"" } add_an_rtrule() { verify_provider $provider [ "x$source" = x- ] && source= [ "x$dest" = x- ] && dest= || dest="to $dest" [ -n "${source}${dest}" ] || fatal_error "You must specify either the source or destination in an rt rule: \"$rule\"" [ -n "${dest:=to 0.0.0.0/0}" ] if [ -n "$source" ]; then case $source in *:*) source="iif ${source%:*} from ${source#*:}" ;; *.*.*) source="from $source" ;; *) source="iif $source" ;; esac else source='from 0.0.0.0/0' fi case "$priority" in [0-9][0-9][0-9][0-9]|[0-9][0-9][0-9][0-9][0-9]) ;; *) fatal_error "Invalid priority ($priority) in rule \"$rule\"" ;; esac priority="priority $priority" [ -n "$DELETE_THEN_ADD" ] && save_command "qt ip rule del $source $dest $priority" save_command "run_ip rule add $source $dest $priority table $provider" indent >&3 << __EOF__ echo "qt ip rule del $source $dest $priority" >> \${VARDIR}/undo_routing __EOF__ progress_message "Routing rule \"$rule\" $DONE" } # # E x e c u t i o n B e g i n s H e r e # local_number=255 main_number=254 default_number=253 unspec_number=0 balance= progress_message2 "$DOING $1..." save_command save_command "if [ -z \"\$NOROUTES\" ]; then" INDENT="$INDENT " indent >&3 << __EOF__ # # Undo any changes made since the last time that we [re]started -- this will not restore the default route # undo_routing # # Save current routing table database so that it can be restored later # cp /etc/iproute2/rt_tables \${VARDIR}/ # # Capture the default route(s) if we don't have it (them) already. # [ -f \${VARDIR}/default_route ] || ip route list | grep -E '^\s*(default |nexthop )' > \${VARDIR}/default_route # # Initialize the file that holds 'undo' commands # > \${VARDIR}/undo_routing __EOF__ save_progress_message "Adding Providers..." save_command "DEFAULT_ROUTE=" while read table number mark duplicate interface gateway options copy; do provider="$table $number $mark $duplicate $interface $gateway $options $copy" add_a_provider PROVIDERS="$PROVIDERS $table" progress_message "Provider $provider $DONE" done < $TMP_DIR/providers if [ -n "$PROVIDERS" ]; then if [ -n "$balance" ]; then save_command "if [ -n \"\$DEFAULT_ROUTE\" ]; then" save_command " run_ip route replace default scope global \$DEFAULT_ROUTE" save_command " progress_message \"Default route '\$(echo \$DEFAULT_ROUTE | sed 's/\$\\s*//')' Added\"" save_command "else" save_command " error_message \"WARNING: No Default route added (all 'balance' providers are down)\"" save_command " restore_default_route" save_command "fi" save_command else save_command "#" save_command "# We don't have any 'balance' providers so we retore any default route that we've saved" save_command "#" save_command restore_default_route fi save_command "if [ -w /etc/iproute2/rt_tables ]; then" cat >&3 << __EOF__ ${INDENT} cat > /etc/iproute2/rt_tables <&3 << __EOF__ \$echocommand "$number\t$table" >> /etc/iproute2/rt_tables __EOF__ done save_command "fi" save_command if [ -s $TMP_DIR/route_rules ]; then progress_message2 "$DOING $(find_file route_rules)..." save_command while read source dest provider priority; do rule="$source $dest $priority $provider" add_an_rtrule done < $TMP_DIR/route_rules fi fi save_command save_command "run_ip route flush cache" INDENT="$save_indent" save_command "fi" save_command } # # Set up Route marking (Only called if $ROUTEMARK_INTERFACES is non-empty) # setup_route_marking() { local mask mask=0xFF local save_indent save_indent="$INDENT" [ -n "$HIGH_ROUTE_MARKS" ] && mask=0xFF00 run_iptables -t mangle -A PREROUTING -m connmark ! --mark 0/$mask -j CONNMARK --restore-mark --mask $mask run_iptables -t mangle -A OUTPUT -m connmark ! --mark 0/$mask -j CONNMARK --restore-mark --mask $mask createmanglechain routemark for interface in $ROUTEMARK_INTERFACES ; do iface=$(chain_base $interface) eval mark_value=\$${iface}_routemark save_command save_command "if [ -n \"\$${iface}_up\" ]; then" INDENT="$INDENT " run_iptables -t mangle -A PREROUTING -i $interface -m mark --mark 0/$mask -j routemark run_iptables -t mangle -A routemark -i $interface -j MARK --set-mark $mark_value INDENT="$save_indent" save_command "fi" done save_command run_iptables -t mangle -A routemark -m mark ! --mark 0/$mask -j CONNMARK --save-mark --mask $mask }