#!/bin/sh # # Shorewall 4.0 -- /usr/share/shorewall/lib.providers # # This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] # # (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., 675 Mass Ave, Cambridge, MA 02139, 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 number mark duplicate interface gateway options provider address copy route loose addresses rulenum rulebase balance save_indent="$INDENT" mask= first=Yes 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 n iface option 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 indent >&3 << __EOF__ qt ip rule del fwmark $mark 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 qt ip rule del from \$address 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__ else 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 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 "$source" ] && case $source in *:*) source="iif ${source%:*} from ${source#*:}" ;; *.*.*) source="from $source" ;; *) source="iif $source" ;; esac 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" 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=0xFF mark_op="--set-mark" save_indent="$INDENT" [ -n "$HIGH_ROUTE_MARKS" ] && mask=0xFF00 && mark_op="--or-mark" 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 $mark_op $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 }