forked from extern/shorewall_code
Implement 'load=<load-factor>' in providers file.
Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
parent
531474592c
commit
7316a2c51a
@ -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
|
||||
#
|
||||
|
@ -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,33 +112,72 @@ 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';
|
||||
my $chainref1 = new_chain 'mangle', 'setsticky';
|
||||
my $chainref2 = new_chain 'mangle', 'setsticko';
|
||||
|
||||
my %marked_interfaces;
|
||||
if ( @routemarked_providers ) {
|
||||
my $chainref1 = new_chain 'mangle', 'setsticky';
|
||||
my $chainref2 = new_chain 'mangle', 'setsticko';
|
||||
|
||||
for my $providerref ( @routemarked_providers ) {
|
||||
my $interface = $providerref->{interface};
|
||||
my $physical = $providerref->{physical};
|
||||
my $mark = $providerref->{mark};
|
||||
my %marked_interfaces;
|
||||
|
||||
unless ( $marked_interfaces{$interface} ) {
|
||||
add_ijump $mangle_table->{PREROUTING} , j => $chainref, i => $physical, mark => "--mark 0/$mask";
|
||||
add_ijump $mangle_table->{PREROUTING} , j => $chainref1, i => "! $physical", mark => "--mark $mark/$mask";
|
||||
add_ijump $mangle_table->{OUTPUT} , j => $chainref2, mark => "--mark $mark/$mask";
|
||||
$marked_interfaces{$interface} = 1;
|
||||
for my $providerref ( @routemarked_providers ) {
|
||||
my $interface = $providerref->{interface};
|
||||
my $physical = $providerref->{physical};
|
||||
my $mark = $providerref->{mark};
|
||||
|
||||
unless ( $marked_interfaces{$interface} ) {
|
||||
add_ijump $mangle_table->{PREROUTING} , j => $chainref, i => $physical, mark => "--mark 0/$mask";
|
||||
add_ijump $mangle_table->{PREROUTING} , j => $chainref1, i => "! $physical", mark => "--mark $mark/$mask";
|
||||
add_ijump $mangle_table->{OUTPUT} , j => $chainref2, mark => "--mark $mark/$mask";
|
||||
$marked_interfaces{$interface} = 1;
|
||||
}
|
||||
|
||||
if ( $providerref->{shared} ) {
|
||||
add_commands( $chainref, qq(if [ -n "$providerref->{mac}" ]; then) ), incr_cmd_level( $chainref ) if $providerref->{optional};
|
||||
add_ijump $chainref, j => 'MARK', targetopts => "--set-mark $providerref->{mark}", imatch_source_dev( $interface ), mac => "--mac-source $providerref->{mac}";
|
||||
decr_cmd_level( $chainref ), add_commands( $chainref, "fi\n" ) if $providerref->{optional};
|
||||
} else {
|
||||
add_ijump $chainref, j => 'MARK', targetopts => "--set-mark $providerref->{mark}", imatch_source_dev( $interface );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $providerref->{shared} ) {
|
||||
add_commands( $chainref, qq(if [ -n "$providerref->{mac}" ]; then) ), incr_cmd_level( $chainref ) if $providerref->{optional};
|
||||
add_ijump $chainref, j => 'MARK', targetopts => "--set-mark $providerref->{mark}", imatch_source_dev( $interface ), mac => "--mac-source $providerref->{mac}";
|
||||
decr_cmd_level( $chainref ), add_commands( $chainref, "fi\n" ) if $providerref->{optional};
|
||||
} else {
|
||||
add_ijump $chainref, j => 'MARK', targetopts => "--set-mark $providerref->{mark}", imatch_source_dev( $interface );
|
||||
}
|
||||
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 ) {
|
||||
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( $$$ ) {
|
||||
@ -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 = '';
|
||||
@ -671,10 +725,10 @@ sub add_a_provider( $$ ) {
|
||||
}
|
||||
|
||||
emit( '' );
|
||||
|
||||
|
||||
my ( $tbl, $weight );
|
||||
|
||||
if ( $optional ) {
|
||||
if ( $optional ) {
|
||||
emit( 'if [ $COMMAND = enable ]; then' );
|
||||
|
||||
push_indent;
|
||||
@ -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}};
|
||||
}
|
||||
|
@ -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>
|
||||
|
||||
@ -1372,7 +1377,7 @@ ZONE_BITS=4
|
||||
</programlisting>
|
||||
|
||||
<note>
|
||||
<para> PROVIDER_OFFSET=16 and ZONE_BITS=4 means that the provider mask
|
||||
<para>PROVIDER_OFFSET=16 and ZONE_BITS=4 means that the provider mask
|
||||
will be 0xf0000.</para>
|
||||
</note>
|
||||
|
||||
@ -1380,11 +1385,10 @@ 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
|
||||
<para>The <option>loose</option> option is specified so that the
|
||||
compiler will not generate and rules based on interface IP addresses.
|
||||
That way we have complete control over the priority of such rules
|
||||
through entries in the rtrules file.</para>
|
||||
@ -1404,41 +1408,29 @@ ComcastC 2 - - eth0 detect loose,balance
|
||||
</note>
|
||||
|
||||
<note>
|
||||
<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>
|
||||
</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 <
|
||||
<replaceable>number</replaceable> <= 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">
|
||||
|
Loading…
Reference in New Issue
Block a user