Generate load rules at runtime rather than at compile time.

Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
Tom Eastep 2012-01-15 19:23:44 -08:00
parent 364420c4eb
commit 58bf562747
6 changed files with 137 additions and 67 deletions

View File

@ -474,6 +474,7 @@ sub generate_script_3($) {
fi fi
EOF EOF
pop_indent; pop_indent;
setup_load_distribution;
setup_forwarding( $family , 1 ); setup_forwarding( $family , 1 );
push_indent; push_indent;
@ -494,6 +495,10 @@ EOF
set_state Started $config_dir set_state Started $config_dir
else else
setup_netfilter setup_netfilter
EOF
setup_load_distribution;
emit<<"EOF";
conditionally_flush_conntrack conditionally_flush_conntrack
EOF EOF
setup_forwarding( $family , 0 ); setup_forwarding( $family , 0 );

View File

@ -3,7 +3,7 @@
# #
# This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt] # This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt]
# #
# (c) 2007,2008,2009,2010,2011 - Tom Eastep (teastep@shorewall.net) # (c) 2007,2008,2009,2010,2011,2012 - Tom Eastep (teastep@shorewall.net)
# #
# Complete documentation is available at http://shorewall.net # Complete documentation is available at http://shorewall.net
# #
@ -984,10 +984,10 @@ sub emitstd {
# #
# Write passed message to the script with newline but no indentation. # Write passed message to the script with newline but no indentation.
# #
sub emit_unindented( $ ) { sub emit_unindented( $;$ ) {
assert( $script_enabled ); assert( $script_enabled );
print $script "$_[0]\n" if $script; print $script $_[1] ? "$_[0]" : "$_[0]\n" if $script;
} }
# #

View File

@ -3,7 +3,7 @@
# #
# This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt] # This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt]
# #
# (c) 2007,2008,2009,2010,2011 - Tom Eastep (teastep@shorewall.net) # (c) 2007,2008,2009,2010,2011,2012 - Tom Eastep (teastep@shorewall.net)
# #
# Complete documentation is available at http://shorewall.net # Complete documentation is available at http://shorewall.net
# #
@ -308,8 +308,7 @@ sub setup_interface_proc( $ ) {
} }
if ( @emitted ) { if ( @emitted ) {
emit( '', emit( 'if [ $COMMAND = enable ]; then' );
'if [ $COMMAND = enable ]; then' );
push_indent; push_indent;
emit "$_" for @emitted; emit "$_" for @emitted;
pop_indent; pop_indent;

View File

@ -38,7 +38,9 @@ our @EXPORT = qw( process_providers
setup_providers setup_providers
@routemarked_interfaces @routemarked_interfaces
handle_stickiness handle_stickiness
handle_optional_interfaces ); handle_optional_interfaces
setup_load_distribution
);
our @EXPORT_OK = qw( initialize lookup_provider ); our @EXPORT_OK = qw( initialize lookup_provider );
our $VERSION = '4.4_24'; our $VERSION = '4.4_24';
@ -54,11 +56,13 @@ my %routemarked_interfaces;
our @routemarked_interfaces; our @routemarked_interfaces;
my %provider_interfaces; my %provider_interfaces;
my @load_providers; my @load_providers;
my @load_interfaces;
my $balancing; my $balancing;
my $fallback; my $fallback;
my $first_default_route; my $first_default_route;
my $first_fallback_route; my $first_fallback_route;
my $maxload;
my %providers; my %providers;
@ -88,10 +92,12 @@ sub initialize( $ ) {
@routemarked_interfaces = (); @routemarked_interfaces = ();
%provider_interfaces = (); %provider_interfaces = ();
@load_providers = (); @load_providers = ();
@load_interfaces = ();
$balancing = 0; $balancing = 0;
$fallback = 0; $fallback = 0;
$first_default_route = 1; $first_default_route = 1;
$first_fallback_route = 1; $first_fallback_route = 1;
$maxload = 0;
%providers = ( local => { number => LOCAL_TABLE , mark => 0 , optional => 0 ,routes => [], rules => [] } , %providers = ( local => { number => LOCAL_TABLE , mark => 0 , optional => 0 ,routes => [], rules => [] } ,
main => { number => MAIN_TABLE , mark => 0 , optional => 0 ,routes => [], rules => [] } , main => { number => MAIN_TABLE , mark => 0 , optional => 0 ,routes => [], rules => [] } ,
@ -143,36 +149,21 @@ sub setup_route_marking() {
add_ijump $chainref, j => 'CONNMARK', targetopts => "--save-mark --mask $mask", mark => "! --mark 0/$mask"; add_ijump $chainref, j => 'CONNMARK', targetopts => "--save-mark --mask $mask", mark => "! --mark 0/$mask";
} }
if ( @load_providers ) { if ( @load_interfaces ) {
my $chainref1 = new_chain 'mangle', 'balance'; my $chainref1 = new_chain 'mangle', 'balance';
my @match; my @match;
add_ijump $chainref, g => $chainref1, mark => "--mark 0/$mask"; add_ijump $chainref, g => $chainref1, mark => "--mark 0/$mask";
add_ijump $mangle_table->{OUTPUT}, j => $chainref1, state_imatch( 'NEW,RELATED' ), mark => "--mark 0/$mask"; add_ijump $mangle_table->{OUTPUT}, j => $chainref1, state_imatch( 'NEW,RELATED' ), mark => "--mark 0/$mask";
for my $providerref ( @load_providers ) { for my $physical ( @load_interfaces ) {
my $chainref2 = new_chain( 'mangle', load_chain( $providerref->{physical} ) ); my $chainref2 = new_chain( 'mangle', load_chain( $physical ) );
dont_optimize $chainref2; dont_optimize $chainref2;
dont_move $chainref2; dont_move $chainref2;
dont_delete $chainref2; dont_delete $chainref2;
if ( $providerref->{optional} ) {
my $dev = chain_base $providerref->{physical};
my $base = uc $dev;
add_commands( $chainref2, qq(if [ -n "\$SW_${base}_IS_USABLE" ]; then) );
incr_cmd_level $chainref2;
}
add_ijump( $chainref2, j => 'MARK', targetopts => "--set-mark $providerref->{mark}/$globals{PROVIDER_MASK}", statistic => "--mode random --probability $providerref->{load}" );
if ( $providerref->{optional} ) {
add_commands( $chainref2 , 'fi' );
decr_cmd_level( $chainref2 );
}
add_ijump ( $chainref1, add_ijump ( $chainref1,
j => $chainref2 , j => $chainref2 ,
mark => "--mark 0/$mask" ); mark => "--mark 0/$mask" );
@ -463,6 +454,7 @@ sub process_a_provider() {
if ( $load ) { if ( $load ) {
fatal_error q(The 'balance=<weight>' and 'load=<load-factor>' options are mutually exclusive) if $balance > 1; fatal_error q(The 'balance=<weight>' and 'load=<load-factor>' options are mutually exclusive) if $balance > 1;
fatal_error q(The 'fallback=<weight>' and 'load=<load-factor>' options are mutually exclusive) if $default > 1; fatal_error q(The 'fallback=<weight>' and 'load=<load-factor>' options are mutually exclusive) if $default > 1;
$maxload += $load;
} }
if ( $local ) { if ( $local ) {
@ -556,7 +548,7 @@ sub process_a_provider() {
push @routemarked_providers, $providers{$table}; push @routemarked_providers, $providers{$table};
} }
push @load_providers, $providers{$table} if $load; push @load_interfaces, $physical if $load;
push @providers, $table; push @providers, $table;
@ -618,12 +610,22 @@ sub add_a_provider( $$ ) {
} }
} }
if ( $load ) { emit( qq(echo $load > \${VARDIR}/${physical}_load) ) if $load;
emit( qq(echo $load > \${VARDIR}/${physical}_load) );
emit( qq(echo "rm -f \${VARDIR}/${physical}_load" >> \${VARDIR}/undo_${table}_routing) );
}
emit( qq(echo "rm -f \${VARDIR}/${physical}.status" >> \${VARDIR}/undo_${table}_routing) ); emit( '',
"cat <<EOF >> \${VARDIR}/undo_${table}_routing" );
emit_unindented 'case \$COMMAND in';
emit_unindented ' enable|disable)';
emit_unindented ' ;;';
emit_unindented ' *)';
emit_unindented " rm -f \${VARDIR}/${physical}_load" if $load;
emit_unindented <<"CEOF", 1;
rm -f \${VARDIR}/${physical}.status
;;
esac
EOF
CEOF
# #
# /proc for this interface # /proc for this interface
# #
@ -734,8 +736,11 @@ sub add_a_provider( $$ ) {
my ( $tbl, $weight ); my ( $tbl, $weight );
emit( qq(echo 0 > \${VARDIR}/${physical}.status) );
if ( $optional ) { if ( $optional ) {
emit( 'if [ $COMMAND = enable ]; then' ); emit( '',
'if [ $COMMAND = enable ]; then' );
push_indent; push_indent;
@ -759,12 +764,13 @@ sub add_a_provider( $$ ) {
emit qq(add_gateway "nexthop dev $physical $realm" ) . $tbl; emit qq(add_gateway "nexthop dev $physical $realm" ) . $tbl;
} }
} }
emit '';
} else { } else {
$weight = 1; $weight = 1;
} }
emit( 'run_iptables -t mangle -A ' . load_chain( $physical ) . ' -m statistic --mode random --probability ' . $load, emit ( "distribute_load $maxload @load_interfaces" ) if $load;
'' ) if $load;
unless ( $shared ) { unless ( $shared ) {
emit( "setup_${dev}_tc" ) if $tcdevices->{$interface}; emit( "setup_${dev}_tc" ) if $tcdevices->{$interface};
@ -775,8 +781,7 @@ sub add_a_provider( $$ ) {
pop_indent; pop_indent;
emit( 'else' ); emit( 'else' );
emit( qq( echo 0 > \${VARDIR}/${physical}.status) , emit( qq( echo $weight > \${VARDIR}/${physical}_weight) ,
qq( echo $weight > \${VARDIR}/${physical}_weight) ,
qq( progress_message " Provider $table ($number) Started"), qq( progress_message " Provider $table ($number) Started"),
qq(fi\n) qq(fi\n)
); );
@ -791,7 +796,7 @@ sub add_a_provider( $$ ) {
push_indent; push_indent;
emit( qq(echo 0 > \${VARDIR}/${physical}.status) ); emit( qq(echo 1 > \${VARDIR}/${physical}.status) );
if ( $optional ) { if ( $optional ) {
if ( $shared ) { if ( $shared ) {
@ -851,8 +856,8 @@ sub add_a_provider( $$ ) {
emit (". $undo", emit (". $undo",
"> $undo" ); "> $undo" );
emit( '', emit ( '',
'run_iptables -t mangle -X ' . load_chain( $physical ) ) if $load; "distribute_load $maxload @load_interfaces" ) if $load;
unless ( $shared ) { unless ( $shared ) {
emit( '', emit( '',
@ -1223,7 +1228,7 @@ EOF
emit ( " if [ -z \"`\$IP -$family route ls table $providerref->{number}`\" ]; then", emit ( " if [ -z \"`\$IP -$family route ls table $providerref->{number}`\" ]; then",
" start_provider_$provider", " start_provider_$provider",
' else', ' else',
' startup_error "Interface $g_interface is already enabled"', " startup_error \"Interface $providerref->{physical} is already enabled\"",
' fi', ' fi',
' ;;' ' ;;'
); );
@ -1259,7 +1264,7 @@ EOF
" if [ -n \"`\$IP -$family route ls table $providerref->{number}`\" ]; then", " if [ -n \"`\$IP -$family route ls table $providerref->{number}`\" ]; then",
" stop_provider_$provider", " stop_provider_$provider",
' else', ' else',
' startup_error "Interface $g_interface is already disabled"', " startup_error \"Interface $providerref->{physical} is already disabled\"",
' fi', ' fi',
' ;;' ' ;;'
) if $providerref->{optional}; ) if $providerref->{optional};
@ -1302,7 +1307,7 @@ sub setup_providers() {
pop_indent; pop_indent;
emit "fi\n"; emit "fi\n";
setup_route_marking if @routemarked_interfaces || @load_providers; setup_route_marking if @routemarked_interfaces || @load_interfaces;
} else { } else {
emit "\nif [ -z \"\$g_noroutes\" ]; then"; emit "\nif [ -z \"\$g_noroutes\" ]; then";
@ -1574,10 +1579,17 @@ sub handle_stickiness( $ ) {
} }
} }
if ( @routemarked_providers || @load_providers ) { if ( @routemarked_providers || @load_interfaces ) {
delete_jumps $mangle_table->{PREROUTING}, $setstickyref unless @{$setstickyref->{rules}}; delete_jumps $mangle_table->{PREROUTING}, $setstickyref unless @{$setstickyref->{rules}};
delete_jumps $mangle_table->{OUTPUT}, $setstickoref unless @{$setstickoref->{rules}}; delete_jumps $mangle_table->{OUTPUT}, $setstickoref unless @{$setstickoref->{rules}};
} }
} }
sub setup_load_distribution() {
emit ( '',
" distribute_load $maxload @load_interfaces" ,
''
) if @load_interfaces;
}
1; 1;

View File

@ -563,3 +563,52 @@ debug_restore_input() {
esac esac
done done
} }
interface_up() {
return $(cat ${VARDIR}/$1.status)
}
distribute_load() {
local interface
local totalload
local load
local maxload
maxload=$1
shift
totalload=0
for interface in $@; do
if interface_up $interface; then
load=$(cat ${VARDIR}/${interface}_load)
eval ${interface}_load=$load
totalload=$( bc <<EOF
scale=8
$totalload + $load
EOF
)
fi
done
if [ $totalload ]; then
for interface in $@; do
qt $g_tool -t mangle -F ~$interface
eval load=\$${interface}_load
if [ -n "$load" ]; then
load=$(bc <<EOF
scale=8
( $load / $totalload ) * $maxload
EOF
)
totalload=$(bc <<EOF
scale=8
$totalload - $load
EOF
)
run_iptables -t mangle -A ~$interface -m statistic --mode random --probability $load
fi
done
fi
}

View File

@ -1345,7 +1345,7 @@ shorewall 2 2 - eth0 192.168.1.254 track,balance=2,optional<
10001: from all fwmark 0x200 lookup ISP2</programlisting> 10001: from all fwmark 0x200 lookup ISP2</programlisting>
</section> </section>
<section> <section id="load">
<title>An alternative form of balancing</title> <title>An alternative form of balancing</title>
<para>Beginning with Shorewall 4.5.0, an alternative to the <para>Beginning with Shorewall 4.5.0, an alternative to the
@ -1357,8 +1357,33 @@ shorewall 2 2 - eth0 192.168.1.254 track,balance=2,optional<
<firstterm>Statistic Match</firstterm> capability in your iptables and <firstterm>Statistic Match</firstterm> capability in your iptables and
kernel.</para> kernel.</para>
<para>This method works when there are two links to the same ISP where <para>This method works when there are multiple links to the same ISP
both links have the same default gateway.</para> where both links have the same default gateway.</para>
<para>The key features of this method are:</para>
<orderedlist>
<listitem>
<para>Providers to be balanced are given a <replaceable>load
factor</replaceable> using the <option>load</option>= option in
<ulink
url="manpages/shorewall-providers.html">shorewall-providers</ulink>
(5).</para>
</listitem>
<listitem>
<para>A load factor is a number in the range 0 &lt; number &lt;= 1
and specifies the probability that any particular new connection
will be assigned to the associated provider.</para>
</listitem>
<listitem>
<para>When one of the interfaces is disabled or enabled, the load
factors of the currently-available interfaces are adjusted so that
the sum of these remaining load factors totals to the sum of all
interfaces that specify <option>load</option>=.</para>
</listitem>
</orderedlist>
<para>Here's an example that sends 1/3 of the connections through <para>Here's an example that sends 1/3 of the connections through
provider ComcastC and the rest through ComastB.</para> provider ComcastC and the rest through ComastB.</para>
@ -1384,7 +1409,7 @@ ZONE_BITS=4
<para><filename>/etc/shorewall/providers</filename>:</para> <para><filename>/etc/shorewall/providers</filename>:</para>
<programlisting>#NAME NUMBER MARK DUPLICATE INTERFACE GATEWAY OPTIONS <programlisting>#NAME NUMBER MARK DUPLICATE INTERFACE GATEWAY OPTIONS
ComcastB 1 - - eth1 70.90.191.126 loose,balance ComcastB 1 - - eth1 70.90.191.126 loose,balance,load=0.66666667
ComcastC 2 - - eth0 detect loose,fallback,load=0.33333333</programlisting> ComcastC 2 - - eth0 detect loose,fallback,load=0.33333333</programlisting>
<note> <note>
@ -1411,26 +1436,6 @@ ComcastC 2 - - eth0 detect loose,fallback,load=0.33
<para>Priority = 1000 means that these rules will come before rules <para>Priority = 1000 means that these rules will come before rules
that select a provider based on marks.</para> that select a provider based on marks.</para>
</note> </note>
<para>As shown in the above example, this technique works best when
there are two providers.</para>
<orderedlist>
<listitem>
<para>One is configured with <option>balance</option> and the other
with <option>fallback</option>.</para>
</listitem>
<listitem>
<para>The provider with the <option>fallback</option> option is
configured whith load=<replaceable>number</replaceable> where the
<replaceable>number</replaceable> has a value in the range 0 &lt;
<replaceable>number</replaceable> &lt;= 1. This
<replaceable>number</replaceable> defines the probability that each
new connection will be sent to the fallback provider and may have up
to 8 digits to the right of the decimal point.</para>
</listitem>
</orderedlist>
</section> </section>
<section id="LinkMonitor"> <section id="LinkMonitor">