More IPv6 Infrastructure

git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@8856 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb
This commit is contained in:
teastep 2008-11-02 21:09:25 +00:00
parent 618d8a440a
commit 2db8cc0207
2 changed files with 559 additions and 101 deletions

View File

@ -37,6 +37,7 @@ our @ISA = qw(Exporter);
our @EXPORT = qw(
add_rule
add_jump
add_6jump
insert_rule
new_chain
new_manual_chain
@ -90,12 +91,15 @@ our %EXPORT_TAGS = (
forward_chain
zone_forward_chain
use_forward_chain
use_forward_6chain
input_chain
zone_input_chain
use_input_chain
use_input_6chain
output_chain
zone_output_chain
use_output_chain
use_output_6chain
masq_chain
syn_flood_chain
mac_chain
@ -630,6 +634,33 @@ sub add_jump( $$;$ ) {
add_rule ($fromref, join( '', $predicate, "-j $to" ) );
}
sub add_6jump( $$;$ ) {
my ( $fromref, $to, $predicate ) = @_;
$predicate |= '';
my $toref;
#
# The second argument may be a scalar (chain name or builtin target) or a chain reference
#
if ( reftype $to ) {
$toref = $to;
$to = $toref->{name};
} else {
#
# Ensure that we have the chain unless it is a builtin like 'ACCEPT'
#
$toref = ensure_6chain( $fromref->{table} , $to ) unless ( $targets6{$to} || 0 ) & STANDARD;
}
#
# If the destination is a chain, mark it referenced
#
$toref->{referenced} = 1 if $toref;
add_rule ($fromref, join( '', $predicate, "-j $to" ) );
}
#
# Insert a rule into a chain. Arguments are:
#
@ -718,6 +749,15 @@ sub use_forward_chain($) {
$interfaceref->{nets} > 1;
}
sub use_forward_6chain($) {
my $interface = $_[0];
my $interfaceref = find_6interface($interface);
#
# We must use the interfaces's chain if the interface is associated with multiple zone nets
#
$interfaceref->{nets} > 1;
}
#
# Input Chain for an interface
#
@ -762,6 +802,32 @@ sub use_input_chain($) {
! $chainref->{referenced};
}
sub use_input_6chain($) {
my $interface = $_[0];
my $interfaceref = find_6interface($interface);
my $nets = $interfaceref->{nets};
#
# We must use the interfaces's chain if the interface is associated with multiple zone nets
#
return 1 if $nets > 1;
#
# Don't need it if it isn't associated with any zone
#
return 0 unless $nets;
#
# Interface associated with a single zone -- use the zone's input chain if it has one
#
my $chainref = $filter6_table->{zone_input_chain $interfaceref->{zone}};
return 0 if $chainref;
#
# Use the '<zone>2fw' chain if it is referenced.
#
$chainref = $filter6_table->{join( '' , $interfaceref->{zone} , '2' , firewall_zone )};
! $chainref->{referenced};
}
#
# Output Chain for an interface
#
@ -806,6 +872,32 @@ sub use_output_chain($) {
! $chainref->{referenced};
}
sub use_output_6chain($) {
my $interface = $_[0];
my $interfaceref = find_6interface($interface);
my $nets = $interfaceref->{nets};
#
# We must use the interfaces's chain if the interface is associated with multiple zone nets
#
return 1 if $nets > 1;
#
# Don't need it if it isn't associated with any zone
#
return 0 unless $nets;
#
# Interface associated with a single zone -- use the zone's output chain if it has one
#
my $chainref = $filter6_table->{zone_output_chain $interfaceref->{zone}};
return 0 if $chainref;
#
# Use the 'fw2<zone>' chain if it is referenced.
#
$chainref = $filter6_table->{join( '', firewall_zone , '2', $interfaceref->{zone} )};
! $chainref->{referenced};
}
#
# Masquerade Chain for an interface
#
@ -2767,35 +2859,6 @@ sub create_chainlist_reload($) {
emit "}\n";
}
#
# Returns true if we're to use the interface's output chain
#
sub use_output_6chain($) {
my $interface = $_[0];
my $interfaceref = find_interface6($interface);
my $nets = $interfaceref->{nets};
#
# We must use the interfaces's chain if the interface is associated with multiple zone nets
#
return 1 if $nets > 1;
#
# Don't need it if it isn't associated with any zone
#
return 0 unless $nets;
#
# Interface associated with a single zone -- use the zone's output chain if it has one
#
my $chainref = $filter6_table->{zone_output_chain $interfaceref->{zone4}};
return 0 if $chainref;
#
# Use the 'fw2<zone>' chain if it is referenced.
#
$chainref = $filter6_table->{join( '', firewall_zone , '2', $interfaceref->{zone} )};
! $chainref->{referenced};
}
#
# Create a new 6chain and return a reference to it.
#

View File

@ -2041,7 +2041,7 @@ sub process_6rule ( $$$$$$$$$$$ ) {
}
}
} else {
my $destzone = (split( /:/, $dest, 2 ) )[0];
my $destzone = (split( /;/, $dest, 2 ) )[0];
$destzone = firewall_zone unless defined_zone( $destzone ); # We do this to allow 'REDIRECT all ...'; process_rule1 will catch the case where the dest zone is invalid
if ( $intrazone || ( $zone ne $destzone ) ) {
process_6rule1 $target, $zone, $dest , $proto, $ports, $sports, $ratelimit, $user, $mark, $connlimit, $time, 1;
@ -2051,7 +2051,7 @@ sub process_6rule ( $$$$$$$$$$$ ) {
}
} elsif ( $dest eq 'all' ) {
for my $zone ( all_6zones ) {
my $sourcezone = ( split( /:/, $source, 2 ) )[0];
my $sourcezone = ( split( /;/, $source, 2 ) )[0];
if ( ( $includedstfw || ( zone_type( $zone ) ne 'firewall') ) && ( ( $sourcezone ne $zone ) || $intrazone) ) {
process_6rule1 $target, $source, $zone , $proto, $ports, $sports, $ratelimit, $user, $mark, $connlimit, $time, 1;
}
@ -2109,6 +2109,57 @@ sub process_6rules() {
$section = 'DONE';
}
#
# Helper functions for generate_matrix()
#-----------------------------------------
#
# Return the target for rules from $zone to $zone1.
#
sub rules_target( $$ ) {
my ( $zone, $zone1 ) = @_;
my $chain = "${zone}2${zone1}";
my $chainref = $filter_table->{$chain};
return $chain if $chainref && $chainref->{referenced};
return 'ACCEPT' if $zone eq $zone1;
fatal_error "Internal Error in rules_target()" unless $chainref;
if ( $chainref->{policy} ne 'CONTINUE' ) {
my $policyref = $filter_table->{$chainref->{policychain}};
return $policyref->{name} if $policyref;
fatal_error "No policy defined for zone $zone to zone $zone1";
}
'';
}
#
# Insert the passed exclusions at the front of the passed chain.
#
sub insert_exclusions( $$ ) {
my ( $chainref, $exclusionsref ) = @_;
my $num = 1;
for my $host ( @{$exclusionsref} ) {
my ( $interface, $net ) = split /:/, $host;
insert_rule $chainref , $num++, join( '', match_dest_dev $interface , match_dest_net( $net ), '-j RETURN' );
}
}
#
# Add the passed exclusions at the end of the passed chain.
#
sub add_exclusions ( $$ ) {
my ( $chainref, $exclusionsref ) = @_;
for my $host ( @{$exclusionsref} ) {
my ( $interface, $net ) = split /:/, $host;
add_rule $chainref , join( '', match_dest_dev $interface, match_dest_net( $net ), '-j RETURN' );
}
}
#
# To quote an old comment, "generate_matrix makes a sow's ear out of a silk purse".
#
@ -2118,76 +2169,7 @@ sub process_6rules() {
# The function traverses the full "source-zone by destination-zone" matrix and generates the rules necessary to direct traffic through the right set of filter-table rules.
#
sub generate_matrix() {
#
# Helper functions for generate_matrix()
#-----------------------------------------
#
# Return the target for rules from $zone to $zone1.
#
sub rules_target( $$ ) {
my ( $zone, $zone1 ) = @_;
my $chain = "${zone}2${zone1}";
my $chainref = $filter_table->{$chain};
return $chain if $chainref && $chainref->{referenced};
return 'ACCEPT' if $zone eq $zone1;
fatal_error "Internal Error in rules_target()" unless $chainref;
if ( $chainref->{policy} ne 'CONTINUE' ) {
my $policyref = $filter_table->{$chainref->{policychain}};
return $policyref->{name} if $policyref;
fatal_error "No policy defined for zone $zone to zone $zone1";
}
'';
}
#
# Add a jump to the passed chain ($chainref) to the dynamic zone chain for the passed zone.
#
sub create_zone_dyn_chain( $$ ) {
my ( $zone , $chainref ) = @_;
add_jump $chainref, zone_dynamic_chain $zone;
}
#
# Insert the passed exclusions at the front of the passed chain.
#
sub insert_exclusions( $$ ) {
my ( $chainref, $exclusionsref ) = @_;
my $num = 1;
for my $host ( @{$exclusionsref} ) {
my ( $interface, $net ) = split /:/, $host;
insert_rule $chainref , $num++, join( '', match_dest_dev $interface , match_dest_net( $net ), '-j RETURN' );
}
}
#
# Add the passed exclusions at the end of the passed chain.
#
sub add_exclusions ( $$ ) {
my ( $chainref, $exclusionsref ) = @_;
for my $host ( @{$exclusionsref} ) {
my ( $interface, $net ) = split /:/, $host;
add_rule $chainref , join( '', match_dest_dev $interface, match_dest_net( $net ), '-j RETURN' );
}
}
#
# Set a breakpoint in this function if you want to step through generate_matrix().
#
sub start_matrix() {
progress_message2 'Generating Rule Matrix...';
}
#
# G e n e r a t e _ M a t r i x ( ) S t a r t s H e r e
#
start_matrix;
progress_message2 'Generating Rule Matrix...';
my $exclusion_seq = 1;
my %chain_exclusions;
@ -2646,6 +2628,419 @@ sub generate_matrix() {
}
}
#
# Helper functions for generate_6matrix()
#-----------------------------------------
#
# Return the target for rules from $zone to $zone1.
#
sub rules6_target( $$ ) {
my ( $zone, $zone1 ) = @_;
my $chain = "${zone}2${zone1}";
my $chainref = $filter6_table->{$chain};
return $chain if $chainref && $chainref->{referenced};
return 'ACCEPT' if $zone eq $zone1;
fatal_error "Internal Error in rules6_target()" unless $chainref;
if ( $chainref->{policy} ne 'CONTINUE' ) {
my $policyref = $filter6_table->{$chainref->{policychain}};
return $policyref->{name} if $policyref;
fatal_error "No policy defined for zone $zone to zone $zone1";
}
'';
}
sub generate_6matrix() {
progress_message2 'Generating IPv6 Rule Matrix...';
my $exclusion_seq = 1;
my %chain_exclusions;
my %policy_exclusions;
my @interfaces = ( all_6interfaces );
my $fw = firewall_zone;
my @zones = non_firewall_6zones;
#
# Special processing for complex configurations
#
for my $zone ( @zones ) {
my $zoneref = find_zone( $zone );
next if @zones <= 2 && ! $zoneref->{options}{complex};
my $exclusions = $zoneref->{exclusions};
my $frwd_ref = new_standard_chain zone_forward_chain( $zone );
if ( @$exclusions ) {
my $in_ref = new_standard_chain zone_input_chain $zone;
my $out_ref = new_standard_chain zone_output_chain $zone;
add_rule ensure_filter_chain( "${zone}2${zone}", 1 ) , '-j ACCEPT' if rules_target( $zone, $zone ) eq 'ACCEPT';
for my $host ( @$exclusions ) {
my ( $interface, $net ) = split /:/, $host;
my $rule = match_source_dev( $interface ) . match_source_net( $net ) . '-j RETURN';
add_rule $frwd_ref , $rule;
add_rule $in_ref , $rule;
add_rule $out_ref , match_dest_dev( $interface ) . match_dest_net( $net ) . '-j RETURN';
}
}
if ( $capabilities{POLICY_MATCH} ) {
my $type = $zoneref->{type};
my $source_ref = ( $zoneref->{hosts}{ipsec4} ) || {};
for my $interface ( sort { interface_number( $a ) <=> interface_number( $b ) } keys %$source_ref ) {
my $sourcechainref;
my $interfacematch = '';
if ( use_forward_chain( $interface ) ) {
$sourcechainref = $filter_table->{forward_chain $interface};
} else {
$sourcechainref = $filter_table->{FORWARD};
$interfacematch = match_source_dev $interface;
move_rules( $filter_table->{forward_chain $interface} , $frwd_ref );
}
my $arrayref = $source_ref->{$interface};
for my $hostref ( @{$arrayref} ) {
my $ipsec_match = match_ipsec_in $zone , $hostref;
for my $net ( @{$hostref->{hosts}} ) {
add_jump6(
$sourcechainref,
$frwd_ref,
join( '', $interfacematch , match_source_net( $net ), $ipsec_match )
);
}
}
}
}
}
#
# Main source-zone matrix-generation loop
#
for my $zone ( @zones ) {
my $zoneref = find_6zone( $zone );
my $source_hosts_ref = $zoneref->{hosts};
my $chain1 = rules6_target firewall_zone , $zone;
my $chain2 = rules6_target $zone, firewall_zone;
my $chain3 = rules6_target $zone, $zone;
my $complex = $zoneref->{options}{complex} || 0;
my $type = $zoneref->{type};
my $exclusions = $zoneref->{exclusions};
my $frwd_ref = $filter6_table->{zone_forward_chain $zone};
my $chain = 0;
#
# Take care of INPUT and OUTPUT jumps
#
for my $typeref ( values %$source_hosts_ref ) {
for my $interface ( sort { interface_number( $a ) <=> interface_number( $b ) } keys %$typeref ) {
my $arrayref = $typeref->{$interface};
for my $hostref ( @$arrayref ) {
my $ipsec_in_match = match_ipsec_in $zone , $hostref;
my $ipsec_out_match = match_ipsec_out $zone , $hostref;
for my $net ( @{$hostref->{hosts}} ) {
my $dest = match_dest_6net $net;
if ( $chain1 ) {
my $nextchain;
my $outputref;
my $interfacematch = '';
if ( use_output_6chain $interface ) {
$outputref = $filter6_table->{output_chain $interface};
} else {
$outputref = $filter6_table->{OUTPUT};
$interfacematch = match_dest_6dev $interface;
}
if ( @$exclusions ) {
my $output = zone_output_chain $zone;
add_6jump $outputref , $output, join( '', $interfacematch, $dest, $ipsec_out_match );
add_6jump $filter_table->{$output} , $chain1;
$nextchain = $output;
} else {
add_6jump $outputref , $chain1, join( '', $interfacematch, $dest, $ipsec_out_match );
$nextchain = $chain1;
}
add_6jump( $outputref , $nextchain, join('', $interfacematch, '-d 255.255.255.255 ' , $ipsec_out_match ) )
if $hostref->{options}{broadcast};
move_rules( $filter6_table->{output_chain $interface} , $filter_table->{$nextchain} ) unless use_output_6chain $interface;
}
next if $hostref->{options}{destonly};
clearrule;
my $source = match_source_net $net;
my $inputchainref;
my $interfacematch = '';
if ( use_input_6chain $interface ) {
$inputchainref = $filter6_table->{input_chain $interface};
} else {
$inputchainref = $filter6_table->{INPUT};
$interfacematch = match_source_6dev $interface;
}
if ( $chain2 ) {
my $nextchain;
if ( @$exclusions ) {
my $input = zone_input_chain $zone;
add_6jump $inputchainref, $input, join( '', $interfacematch, $source, $ipsec_in_match );
add_6jump $filter6_table->{ $input } , $chain2;
$nextchain = $input;
} else {
add_6jump $inputchainref, $chain2, join( '', $interfacematch, $source, $ipsec_in_match );
$nextchain = $chain2;
}
move_rules( $filter6_table->{input_chain $interface} , $filter_table->{$nextchain} ) unless use_input_6chain $interface;
}
if ( $frwd_ref && $hostref->{ipsec} ne 'ipsec' ) {
if ( use_forward_6chain $interface ) {
add_6jump $filter6_table->{forward_chain $interface} , $frwd_ref, join( '', $source, $ipsec_in_match );
} else {
add_6jump $filter6_table->{FORWARD} , $frwd_ref, join( '', match_source_6dev( $interface ) , $source, $ipsec_in_match );
move_rules ( $filter6_table->{forward_chain $interface} , $frwd_ref );
}
}
}
}
}
}
#
# F O R W A R D I N G
#
my @dest_zones;
my $last_chain = '';
if ( $config{OPTIMIZE} > 0 ) {
my @temp_zones;
ZONE1:
for my $zone1 ( @zones ) {
my $zone1ref = find_6zone( $zone1 );
my $policy = $filter6_table->{"${zone}2${zone1}"}->{policy};
next if $policy eq 'NONE';
my $chain = rules6_target $zone, $zone1;
next unless $chain;
if ( $zone eq $zone1 ) {
next if ( scalar ( keys( %{ $zoneref->{interfaces}} ) ) < 2 ) && ! ( $zoneref->{options}{in_out}{routeback} || @$exclusions );
}
if ( $zone1ref->{type} eq 'bport6' ) {
next unless $zoneref->{bridge} eq $zone1ref->{bridge};
}
if ( $chain =~ /2all$/ ) {
if ( $chain ne $last_chain ) {
$last_chain = $chain;
push @dest_zones, @temp_zones;
@temp_zones = ( $zone1 );
} elsif ( $policy eq 'ACCEPT' ) {
push @temp_zones , $zone1;
} else {
$last_chain = $chain;
@temp_zones = ( $zone1 );
}
} else {
push @dest_zones, @temp_zones, $zone1;
@temp_zones = ();
$last_chain = '';
}
}
if ( $last_chain && @temp_zones == 1 ) {
push @dest_zones, @temp_zones;
$last_chain = '';
}
} else {
@dest_zones = @zones ;
}
#
# Here it is -- THE BIG UGLY!!!!!!!!!!!!
#
# We now loop through the destination zones creating jumps to the rules chain for each source/dest combination.
# @dest_zones is the list of destination zones that we need to handle from this source zone
#
ZONE1:
for my $zone1 ( @dest_zones ) {
my $zone1ref = find_6zone( $zone1 );
my $policy = $filter6_table->{"${zone}2${zone1}"}->{policy};
next if $policy eq 'NONE';
my $chain = rules6_target $zone, $zone1;
next unless $chain; # CONTINUE policy with no rules
my $num_ifaces = 0;
if ( $zone eq $zone1 ) {
next ZONE1 if ( $num_ifaces = scalar( keys ( %{$zoneref->{interfaces}} ) ) ) < 2 && ! ( $zoneref->{options}{in_out}{routeback} || @$exclusions );
}
if ( $zone1ref->{type} eq 'bport6' ) {
next ZONE1 unless $zoneref->{bridge} eq $zone1ref->{bridge};
}
my $chainref = $filter_table->{$chain};
my $exclusions1 = $zone1ref->{exclusions};
my $dest_hosts_ref = $zone1ref->{hosts};
if ( @$exclusions1 ) {
if ( $chain eq "all2$zone1" ) {
unless ( $chain_exclusions{$chain} ) {
$chain_exclusions{$chain} = 1;
insert_exclusions $chainref , $exclusions1;
}
} elsif ( $chain =~ /2all$/ ) {
my $chain1 = $policy_exclusions{"${chain}_${zone1}"};
unless ( $chain1 ) {
$chain1 = newexclusionchain;
$policy_exclusions{"${chain}_${zone1}"} = $chain1;
my $chain1ref = ensure_filter_chain $chain1, 0;
add_exclusions $chain1ref, $exclusions1;
add_6jump $chain1ref, $chain;
}
$chain = $chain1;
} else {
fatal_error "Fatal Error in generate_matrix()" if $chain eq 'ACCEPT';
insert_exclusions $chainref , $exclusions1;
}
}
if ( $frwd_ref ) {
for my $typeref ( values %$dest_hosts_ref ) {
for my $interface ( sort { interface_number( $a ) <=> interface_number( $b ) } keys %$typeref ) {
my $arrayref = $typeref->{$interface};
for my $hostref ( @$arrayref ) {
next if $hostref->{options}{sourceonly};
if ( $zone ne $zone1 || $num_ifaces > 1 || $hostref->{options}{routeback} ) {
my $ipsec_out_match = match_ipsec_out $zone1 , $hostref;
for my $net ( @{$hostref->{hosts}} ) {
add_6jump $frwd_ref, $chain, join( '', match_dest_6dev( $interface) , match_dest_6net($net), $ipsec_out_match );
}
}
}
}
}
} else {
for my $typeref ( values %$source_hosts_ref ) {
for my $interface ( keys %$typeref ) {
my $arrayref = $typeref->{$interface};
my $chain3ref;
my $match_source_dev = '';
if ( use_forward_chain $interface ) {
$chain3ref = $filter6_table->{forward_chain $interface};
} else {
$chain3ref = $filter_table->{FORWARD};
$match_source_dev = match_source_6dev $interface;
}
for my $hostref ( @$arrayref ) {
next if $hostref->{options}{destonly};
for my $net ( @{$hostref->{hosts}} ) {
for my $type1ref ( values %$dest_hosts_ref ) {
for my $interface1 ( keys %$type1ref ) {
my $array1ref = $type1ref->{$interface1};
for my $host1ref ( @$array1ref ) {
next if $host1ref->{options}{sourceonly};
my $ipsec_out_match = match_ipsec_out $zone1 , $host1ref;
for my $net1 ( @{$host1ref->{hosts}} ) {
unless ( $interface eq $interface1 && $net eq $net1 && ! $host1ref->{options}{routeback} ) {
#
# We defer evaluation of the source net match to accomodate systems without $capabilities{KLUDEFREE};
#
add_6jump(
$chain3ref ,
$chain ,
join( '',
$match_source_dev,
match_dest_6dev($interface1),
match_source_6net($net),
match_dest_6net($net1),
$ipsec_out_match )
);
}
}
}
}
}
}
}
}
}
}
#
# E N D F O R W A R D I N G
#
# Now add an unconditional jump to the last unique policy-only chain determined above, if any
#
add_6jump $frwd_ref , $last_chain if $last_chain;
}
}
#
# Add the jumps to the interface chains from filter FORWARD, INPUT, OUTPUT
#
for my $interface ( @interfaces ) {
add_6jump( $filter6_table->{FORWARD} , forward_chain $interface , match_source_dev( $interface ) ) if use_forward_6chain $interface;
add_6jump( $filter6_table->{INPUT} , input_chain $interface , match_source_dev( $interface ) ) if use_input_6chain $interface;
if ( use_output_6chain $interface ) {
add_6jump $filter6_table->{OUTPUT} , output_chain $interface , "-o $interface " unless get_interface_option( $interface, 'port' );
}
}
my $chainref = $filter6_table->{"${fw}2${fw}"};
add_rule $filter_table->{OUTPUT} , "-o lo -j " . ($chainref->{referenced} ? "$chainref->{name}" : 'ACCEPT' );
add_rule $filter_table->{INPUT} , '-i lo -j ACCEPT';
my %builtins = ( mangle => [ qw/PREROUTING INPUT FORWARD POSTROUTING/ ] ,
nat=> [ qw/PREROUTING OUTPUT POSTROUTING/ ] ,
filter=> [ qw/INPUT FORWARD OUTPUT/ ] );
complete_standard_6chain $filter6_table->{INPUT} , 'all' , firewall_zone , 'DROP';
complete_standard_6chain $filter6_table->{OUTPUT} , firewall_zone , 'all', 'REJECT';
complete_standard_6chain $filter6_table->{FORWARD} , 'all' , 'all', 'REJECT';
if ( $config{LOGALLNEW} ) {
for my $table qw/mangle filter/ {
for my $chain ( @{$builtins{$table}} ) {
log_rule_limit
$config{LOGALLNEW} ,
$chain6_table{$table}{$chain} ,
$table ,
$chain ,
'' ,
'' ,
'insert' ,
'-m state --state NEW ';
}
}
}
}
sub setup_mss( ) {
my $clampmss = $config{CLAMPMSS};
my $option;