Implement 'load=<load-factor>' in providers file.

Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
Tom Eastep 2012-01-13 16:37:05 -08:00
parent 531474592c
commit 7316a2c51a
3 changed files with 135 additions and 67 deletions

View File

@ -138,6 +138,7 @@ our %EXPORT_TAGS = (
snat_chain
ecn_chain
notrack_chain
load_chain
first_chains
option_chains
reserved_name
@ -666,11 +667,19 @@ sub set_rule_option( $$$ ) {
my $opttype = $opttype{$option} || MATCH;
if ( exists $ruleref->{$option} ) {
assert( defined $ruleref->{$option} );
assert( defined( my $value1 = $ruleref->{$option} ) );
if ( $opttype == MATCH ) {
assert( $globals{KLUDGEFREE} );
$ruleref->{$option} = [ $ruleref->{$option} ] unless reftype $ruleref->{$option};
unless ( reftype $value1 ) {
unless ( reftype $value ) {
return if $value1 eq $value;
}
$ruleref->{$option} = [ $ruleref->{$option} ];
}
push @{$ruleref->{$option}}, ( reftype $value ? @$value : $value );
} elsif ( $opttype == EXCLUSIVE ) {
$ruleref->{$option} .= ",$value";
@ -1775,6 +1784,13 @@ sub notrack_chain( $ )
$_[0] . '_notrk';
}
#
# Load Chain for a provider
#
sub load_chain( $ ) {
'~' . $_[0];
}
#
# SNAT Chain to an interface
#

View File

@ -53,6 +53,7 @@ my @routemarked_providers;
my %routemarked_interfaces;
our @routemarked_interfaces;
my %provider_interfaces;
my @load_providers;
my $balancing;
my $fallback;
@ -86,6 +87,7 @@ sub initialize( $ ) {
%routemarked_interfaces = ();
@routemarked_interfaces = ();
%provider_interfaces = ();
@load_providers = ();
$balancing = 0;
$fallback = 0;
$first_default_route = 1;
@ -110,6 +112,8 @@ sub setup_route_marking() {
add_ijump $mangle_table->{$_} , j => 'CONNMARK', targetopts => "--restore-mark --mask $mask", connmark => "! --mark 0/$mask" for qw/PREROUTING OUTPUT/;
my $chainref = new_chain 'mangle', 'routemark';
if ( @routemarked_providers ) {
my $chainref1 = new_chain 'mangle', 'setsticky';
my $chainref2 = new_chain 'mangle', 'setsticko';
@ -139,6 +143,43 @@ sub setup_route_marking() {
add_ijump $chainref, j => 'CONNMARK', targetopts => "--save-mark --mask $mask", mark => "! --mark 0/$mask";
}
if ( @load_providers ) {
my $chainref1 = new_chain 'mangle', 'balance';
my @match;
add_ijump $chainref, g => $chainref1, mark => "--mark 0/$mask";
add_ijump $mangle_table->{OUTPUT}, j => $chainref1, state_imatch( 'NEW,RELATED' ), mark => "--mark 0/$mask";
for my $providerref ( @load_providers ) {
my $chainref2 = new_chain( 'mangle', load_chain( $providerref->{provider} ) );
dont_optimize $chainref2;
dont_move $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,
j => $chainref2 ,
mark => "--mark 0/$mask" );
}
}
}
sub copy_table( $$$ ) {
my ( $duplicate, $number, $realm ) = @_;
@ -366,8 +407,8 @@ sub process_a_provider() {
$gateway = '';
}
my ( $loose, $track, $balance , $default, $default_balance, $optional, $mtu, $local ) =
(0, $config{TRACK_PROVIDERS}, 0 , 0, $config{USE_DEFAULT_RT} ? 1 : 0, interface_is_optional( $interface ), '' , 0 );
my ( $loose, $track, $balance , $default, $default_balance, $optional, $mtu, $local , $load ) =
(0, $config{TRACK_PROVIDERS}, 0 , 0, $config{USE_DEFAULT_RT} ? 1 : 0, interface_is_optional( $interface ), '' , 0 , 0 );
unless ( $options eq '-' ) {
for my $option ( split_list $options, 'option' ) {
@ -408,6 +449,9 @@ sub process_a_provider() {
$local = 1;
$track = 0 if $config{TRACK_PROVIDERS};
$default_balance = 0 if $config{USE_DEFAULT_RT};
} elsif ( $option =~ /^load=(0?\.\d{1,8})/ ) {
$load = $1;
require_capability 'STATISTIC_MATCH', "load=$load", 's';
} else {
fatal_error "Invalid option ($option)";
}
@ -416,6 +460,11 @@ sub process_a_provider() {
fatal_error q(The 'balance' and 'fallback' options are mutually exclusive) if $balance && $default;
if ( $load ) {
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;
}
if ( $local ) {
fatal_error "GATEWAY not valid with 'local' provider" unless $gatewaycase eq 'none';
fatal_error "'track' not valid with 'local'" if $track;
@ -488,6 +537,7 @@ sub process_a_provider() {
duplicate => $duplicate ,
address => $address ,
local => $local ,
load => $load ,
rules => [] ,
routes => [] ,
};
@ -506,6 +556,8 @@ sub process_a_provider() {
push @routemarked_providers, $providers{$table};
}
push @load_providers, $providers{$table} if $load;
push @providers, $table;
progress_message " Provider \"$currentline\" $done";
@ -537,6 +589,8 @@ sub add_a_provider( $$ ) {
my $duplicate = $providerref->{duplicate};
my $address = $providerref->{address};
my $local = $providerref->{local};
my $load = $providerref->{load};
my $dev = chain_base $physical;
my $base = uc $dev;
my $realm = '';
@ -703,6 +757,9 @@ sub add_a_provider( $$ ) {
$weight = 1;
}
emit( 'run_iptables -t mangle -A ' . load_chain( $table ) . ' -m statistic --mode random --probability ' . $load,
'' ) if $load;
unless ( $shared ) {
emit( "setup_${dev}_tc" ) if $tcdevices->{$interface};
}
@ -784,6 +841,9 @@ sub add_a_provider( $$ ) {
emit (". $undo",
"> $undo" );
emit( '',
'run_iptables -t mangle -X ' . load_chain( $table ) ) if $load;
unless ( $shared ) {
emit( '',
"qt \$TC qdisc del dev $physical root",
@ -1232,7 +1292,7 @@ sub setup_providers() {
pop_indent;
emit "fi\n";
setup_route_marking if @routemarked_interfaces;
setup_route_marking if @routemarked_interfaces || @load_providers;
} else {
emit "\nif [ -z \"\$g_noroutes\" ]; then";
@ -1504,7 +1564,7 @@ sub handle_stickiness( $ ) {
}
}
if ( @routemarked_providers ) {
if ( @routemarked_providers || @load_providers ) {
delete_jumps $mangle_table->{PREROUTING}, $setstickyref unless @{$setstickyref->{rules}};
delete_jumps $mangle_table->{OUTPUT}, $setstickoref unless @{$setstickoref->{rules}};
}

View File

@ -1300,9 +1300,8 @@ lillycat: #</programlisting>
<para>Although 'balance' is automatically assumed when
USE_DEFAULT_RT=Yes, you can easily cause all traffic to use one provider
except when you explicitly direct it to use the other provider via
<ulink
url="manpages/shorewall-rtrules.html">shorewall-rtrules</ulink> (5)
or <ulink
<ulink url="manpages/shorewall-rtrules.html">shorewall-rtrules</ulink>
(5) or <ulink
url="manpages/shorewall-tcrules.html">shorewall-tcrules</ulink>
(5).</para>
@ -1354,9 +1353,15 @@ shorewall 2 2 - eth0 192.168.1.254 track,balance=2,optional<
<ulink
url="manpages/shorewall-providers.html">shorewall-providers</ulink> (5)
is available in the form of a PROBABILITY column in <ulink
url="???">shorewall-tcrules</ulink> (5).</para>
url="???">shorewall-tcrules</ulink> (5). This feature requires the
<firstterm>Statistic Match</firstterm> capability in your iptables and
kernel.</para>
<para>Here's an example:</para>
<para>This method works when there are two links to the same ISP where
both links have the same default gateway.</para>
<para>Here's an example that sends 1/3 of the connections through
provider ComcastC and the rest through ComastB.</para>
<para><filename>/etc/shorewall/shorewall.conf</filename>:</para>
@ -1380,8 +1385,7 @@ ZONE_BITS=4
<programlisting>#NAME NUMBER MARK DUPLICATE INTERFACE GATEWAY OPTIONS
ComcastB 1 - - eth1 70.90.191.126 loose,balance
ComcastC 2 - - eth0 detect loose,balance
</programlisting>
ComcastC 2 - - eth0 detect loose,fallback,load=0.33333333</programlisting>
<note>
<para>The <option>loose</option> option is specified so that the
@ -1408,37 +1412,25 @@ ComcastC 2 - - eth0 detect loose,balance
that select a provider based on marks.</para>
</note>
<para><filename>/etc/shorewall/tcrules</filename>:</para>
<para>As shown in the above example, this technique works best when
there are two providers.</para>
<programlisting> #MARK SOURCE DEST PROTO DEST
# PORT(S)
CONTINUE - 70.90.191.120/29
CONTINUE - 10.0.10.0/24
CONTINUE - - tcp 80
<orderedlist>
<listitem>
<para>One is configured with <option>balance</option> and the other
with <option>fallback</option>.</para>
</listitem>
# 70.90.191.120/29 is the local public subnet. 10.0.10.0/24 is a
# local network on eth1. We don't want to mark TCP 80, because
# we run a transparent proxy on the firewall.
0X10000/0xf0000 eth2 - ; probability=0.66666667
0x20000/0xf0000 eth2 - ; test=0/0x30000
# The above two split traffic entering the firewall through eth2
# (local LAN) between the two providers with 2/3 of the traffic
# going to eth1 and 1/3 going to eth0.
CONTINUE fw:70.90.191.120/29
CONTINUE fw 172.20.1.0/22
CONTINUE fw 70.90.191.120/29
CONTINUE fw 10.0.10.0/24
# Similar to rules above
0X10000/0xf0000 fw - ; probability=0.66666667
0x20000/0xf0000 fw - ; test=0/0x30000
# Again, split traffic from the firewall 2:1 in favor of eth1.
</programlisting>
<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 id="LinkMonitor">