From 99deb41bbcc453a47db42e2c89b0ae64b6e2cad2 Mon Sep 17 00:00:00 2001 From: teastep Date: Wed, 25 Feb 2009 16:56:48 +0000 Subject: [PATCH] First cut at pinning local clients to providers git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@9525 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb --- Shorewall/Shorewall/Chains.pm | 12 ++++++++ Shorewall/Shorewall/Providers.pm | 53 ++++++++++++++++++++++++++++++-- Shorewall/Shorewall/Tc.pm | 24 ++++++++++++++- Shorewall/Shorewall/Zones.pm | 16 +++++----- 4 files changed, 94 insertions(+), 11 deletions(-) diff --git a/Shorewall/Shorewall/Chains.pm b/Shorewall/Shorewall/Chains.pm index 4adc2db3c..facfd32f5 100644 --- a/Shorewall/Shorewall/Chains.pm +++ b/Shorewall/Shorewall/Chains.pm @@ -72,6 +72,7 @@ our %EXPORT_TAGS = ( add_command add_commands move_rules + purge_rules insert_rule1 add_tunnel_rule process_comment @@ -665,6 +666,17 @@ sub move_rules( $$ ) { } } +# +# Purge the rules from the passed chain are return them +# +sub purge_rules( $ ) { + my $chainref = shift; + my @rules = @{$chainref->{rules}}; + $chainref->{rules} = []; + $chainref->{referenced} = 0; + @rules; +} + # # Change the passed interface name so it is a legal shell variable name. # diff --git a/Shorewall/Shorewall/Providers.pm b/Shorewall/Shorewall/Providers.pm index d9dbfd349..8184565c9 100644 --- a/Shorewall/Shorewall/Providers.pm +++ b/Shorewall/Shorewall/Providers.pm @@ -33,7 +33,7 @@ use Shorewall::Chains qw(:DEFAULT :internal); use strict; our @ISA = qw(Exporter); -our @EXPORT = qw( setup_providers @routemarked_interfaces); +our @EXPORT = qw( setup_providers @routemarked_interfaces handle_stickiness ); our @EXPORT_OK = qw( initialize lookup_provider ); our $VERSION = 4.2.4; @@ -101,18 +101,21 @@ sub setup_route_marking() { add_rule $mangle_table->{PREROUTING} , "-m connmark ! --mark 0/$mask -j CONNMARK --restore-mark --mask $mask"; add_rule $mangle_table->{OUTPUT} , "-m connmark ! --mark 0/$mask -j CONNMARK --restore-mark --mask $mask"; - my $chainref = new_chain 'mangle', 'routemark'; + my $chainref = new_chain 'mangle', 'routemark'; + my $chainref1 = new_chain 'mangle', 'stickymark'; my %marked_interfaces; for my $providerref ( @routemarked_providers ) { my $interface = $providerref->{interface}; + my $mark = $providerref->{mark}; my $base = uc chain_base $interface; add_command( $chainref, qq(if [ -n "\$${base}_IS_UP" ]; then) ), incr_cmd_level( $chainref ) if $providerref->{optional}; unless ( $marked_interfaces{$interface} ) { add_rule $mangle_table->{PREROUTING} , "-i $interface -m mark --mark 0/$mask -j routemark"; + add_jump $mangle_table->{PREROUTING} , $chainref1, 0, "-i ! $interface -m mark --mark $mark/$mask "; $marked_interfaces{$interface} = 1; } @@ -754,4 +757,50 @@ sub lookup_provider( $ ) { $providerref->{shared} ? $providerref->{number} : 0; } +# +# The Tc module has collected the 'sticky' rules in the 'sticky' chain. In this function, we apply them +# to the 'tracked' providers +# +sub handle_stickiness() { + my $stickyref = $mangle_table->{sticky}; + my $stickymarkref = $mangle_table->{stickymark}; + my $tcpreref = $mangle_table->{tcpre}; + my @rules = purge_rules $stickyref; + my %marked_interfaces; + my $sticky = 1; + + fatal_error "There are STICKY tcrules but no 'track' providers" unless @routemarked_providers; + + for my $providerref ( @routemarked_providers ) { + my $interface = $providerref->{interface}; + my $base = uc chain_base $interface; + my $mark = $providerref->{mark}; + + for my $rule ( @rules ) { + my $rule1; + my $list = sprintf "sticky%03d" , $sticky++; + + $rule =~ s/-A //; + + for my $chainref ( $tcpreref, $stickymarkref ) { + + add_command( $chainref, qq(if [ -n "\$${base}_IS_UP" ]; then) ), incr_cmd_level( $chainref ) if $providerref->{optional}; + + if ( $chainref->{name} eq 'tcpre' ) { + $rule1 = $rule; + $rule1 =~ s/-j RETURN/-m recent --name $list --update --seconds 120 -j MARK --set-mark $mark/; + } else { + $rule1 = $rule; + $rule1 =~ s/-j RETURN/-m mark --mark $mark -m recent --name $list --set/; + } + + add_rule $chainref, $rule1; + + decr_cmd_level( $chainref), add_command( $chainref, "fi" ) if $providerref->{optional}; + + } + } + } +} + 1; diff --git a/Shorewall/Shorewall/Tc.pm b/Shorewall/Shorewall/Tc.pm index 09a1492f8..80a035c54 100644 --- a/Shorewall/Shorewall/Tc.pm +++ b/Shorewall/Shorewall/Tc.pm @@ -97,6 +97,12 @@ our @tccmd = ( { match => sub ( $ ) { $_[0] eq 'SAVE' } , mask => '' , connmark => 0 } , + { match => sub ( $ ) { $_[0] eq 'SAME' }, + target => 'sticky' , + mark => NOMARK , + mask => '' , + connmark => 0 + } , { match => sub ( $ ) { $_[0] =~ '\|.*'} , target => 'MARK --or-mark' , mark => HIGHMARK , @@ -149,6 +155,7 @@ our @tcdevices; our %tcdevices; our @devnums; our $devnum; +our $sticky; # @@ -171,7 +178,8 @@ our %tcclasses; our %restrictions = ( tcpre => PREROUTE_RESTRICT , tcpost => POSTROUTE_RESTRICT , tcfor => NO_RESTRICT , - tcout => OUTPUT_RESTRICT ); + tcout => OUTPUT_RESTRICT , + sticky => PREROUTE_RESTRICT ); our $family; @@ -194,6 +202,7 @@ sub initialize( $ ) { %tcclasses = (); @devnums = (); $devnum = 0; + $sticky = 0; } INIT { @@ -214,6 +223,7 @@ sub process_tc_rule( $$$$$$$$$$$$ ) { my $classid = 0; my $device = ''; my $fw = firewall_zone; + my $list; if ( $source ) { if ( $source eq $fw ) { @@ -256,6 +266,8 @@ sub process_tc_rule( $$$$$$$$$$$$ ) { my ($cmd, $rest) = split( '/', $mark, 2 ); + $list = ''; + unless ( $classid ) { MARK: { @@ -274,6 +286,11 @@ sub process_tc_rule( $$$$$$$$$$$$ ) { $mark =~ s/^[|&]//; } + if ( $target eq 'sticky ' ) { + $target = 'RETURN'; + $chain = 'sticky'; + } + if ( $rest ) { fatal_error "Invalid MARK ($originalmark)" if $marktype == NOMARK; @@ -904,6 +921,7 @@ sub setup_tc() { if ( $capabilities{MANGLE_ENABLED} && $config{MANGLE_ENABLED} ) { ensure_mangle_chain 'tcpre'; ensure_mangle_chain 'tcout'; + new_chain 'mangle', 'sticky'; if ( $capabilities{MANGLE_FORWARD} ) { ensure_mangle_chain 'tcfor'; @@ -966,6 +984,10 @@ sub setup_tc() { for ( @deferred_rules ) { add_rule ensure_chain( 'mangle' , 'tcpost' ), $_; } + + if ( $mangle_table->{sticky}{referenced} ) { + handle_stickiness; + } } 1; diff --git a/Shorewall/Shorewall/Zones.pm b/Shorewall/Shorewall/Zones.pm index 1f6b42ccf..8905f4ad9 100644 --- a/Shorewall/Shorewall/Zones.pm +++ b/Shorewall/Shorewall/Zones.pm @@ -735,14 +735,14 @@ sub validate_interfaces_file( $ ) } } - my $optionsref = {}; - my $hostoptionsref = {}; - my %options; - my %hostoptions; + + my $hostoptionsref = {}; if ( $options ) { + my %hostoptions; + for my $option (split_list1 $options, 'option' ) { next if $option eq '-'; @@ -817,20 +817,20 @@ sub validate_interfaces_file( $ ) require_capability( 'PHYSDEV_MATCH', 'The "bridge" option', 's'); fatal_error "Bridges may not have wildcard names" if $wildcard; } + + $hostoptionsref = \%hostoptions; + } elsif ( $port ) { $options{port} = 1; } - $optionsref = \%options; - $hostoptionsref = \%hostoptions; - $interfaces{$interface} = { name => $interface , bridge => $bridge , nets => 0 , number => ++$num , root => $root , broadcasts => $broadcasts , - options => $optionsref }; + options => \%options }; push @ifaces, $interface;