mirror of
https://gitlab.com/shorewall/code.git
synced 2024-11-15 12:14:32 +01:00
Break out rule processing into a module
git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@5542 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb
This commit is contained in:
parent
24f61c69c7
commit
b54ea15e2e
465
New/Shorewall/Rules.pm
Normal file
465
New/Shorewall/Rules.pm
Normal file
@ -0,0 +1,465 @@
|
|||||||
|
package Shorewall::Rules;
|
||||||
|
require Exporter;
|
||||||
|
use Shorewall::Common;
|
||||||
|
use Shorewall::Config;
|
||||||
|
use Shorewall::Zones;
|
||||||
|
use Shorewall::Chains;
|
||||||
|
use Shorewall::Actions;
|
||||||
|
use Shorewall::Macros;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
|
||||||
|
our @ISA = qw(Exporter);
|
||||||
|
our @EXPORT = qw( process_rules );
|
||||||
|
our @EXPORT_OK = qw( process_rule process_rule1 );
|
||||||
|
our @VERSION = 1.00;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Set to one if we find a SECTION
|
||||||
|
#
|
||||||
|
my $sectioned = 0;
|
||||||
|
|
||||||
|
sub process_rule1 ( $$$$$$$$$ );
|
||||||
|
|
||||||
|
#
|
||||||
|
# Expand a macro rule from the rules file
|
||||||
|
#
|
||||||
|
sub process_macro ( $$$$$$$$$$$ ) {
|
||||||
|
my ($macrofile, $target, $param, $source, $dest, $proto, $ports, $sports, $origdest, $rate, $user) = @_;
|
||||||
|
|
||||||
|
my $standard = ( $macrofile =~ /^($env{SHAREDIR})/ );
|
||||||
|
|
||||||
|
progress_message "..Expanding Macro $macrofile...";
|
||||||
|
|
||||||
|
open M, $macrofile or fatal_error "Unable to open $macrofile: $!";
|
||||||
|
|
||||||
|
while ( $line = <M> ) {
|
||||||
|
chomp $line;
|
||||||
|
next if $line =~ /^\s*#/;
|
||||||
|
next if $line =~ /^\s*$/;
|
||||||
|
$line =~ s/\s+/ /g;
|
||||||
|
$line =~ s/#.*$//;
|
||||||
|
$line = expand_shell_variables $line unless $standard;
|
||||||
|
|
||||||
|
my ( $mtarget, $msource, $mdest, $mproto, $mports, $msports, $mrate, $muser ) = split /\s+/, $line;
|
||||||
|
|
||||||
|
$mtarget = merge_levels $target, $mtarget;
|
||||||
|
|
||||||
|
if ( $mtarget =~ /^PARAM:?/ ) {
|
||||||
|
fatal_error 'PARAM requires that a parameter be supplied in macro invocation' unless $param;
|
||||||
|
$mtarget = substitute_action $param, $mtarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $action = isolate_basic_target $mtarget;
|
||||||
|
my $actiontype = $targets{$action};
|
||||||
|
|
||||||
|
if ( $actiontype & ACTION ) {
|
||||||
|
unless ( $usedactions{$action} ) {
|
||||||
|
createactionchain $mtarget;
|
||||||
|
$usedactions{$mtarget} = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mtarget = find_logactionchain $mtarget;
|
||||||
|
} else {
|
||||||
|
fatal_error "Invalid Action ($mtarget) in rule \"$line\"" unless $actiontype & STANDARD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $msource ) {
|
||||||
|
if ( ( $msource eq '-' ) || ( $msource eq 'SOURCE' ) ) {
|
||||||
|
$msource = $source || '';
|
||||||
|
} elsif ( $msource eq 'DEST' ) {
|
||||||
|
$msource = $dest || '';
|
||||||
|
} else {
|
||||||
|
$msource = merge_macro_source_dest $msource, $source;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$msource = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$msource = '' if $msource eq '-';
|
||||||
|
|
||||||
|
if ( $mdest ) {
|
||||||
|
if ( ( $mdest eq '-' ) || ( $mdest eq 'DEST' ) ) {
|
||||||
|
$mdest = $dest || '';
|
||||||
|
} elsif ( $mdest eq 'SOURCE' ) {
|
||||||
|
$mdest = $source || '';
|
||||||
|
} else {
|
||||||
|
$mdest = merge_macro_source_dest $mdest, $dest;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$mdest = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$mdest = '' if $mdest eq '-';
|
||||||
|
|
||||||
|
$mproto = merge_macro_column $mproto, $proto;
|
||||||
|
$mports = merge_macro_column $mports, $ports;
|
||||||
|
$msports = merge_macro_column $msports, $sports;
|
||||||
|
$mrate = merge_macro_column $mrate, $rate;
|
||||||
|
$muser = merge_macro_column $muser, $user;
|
||||||
|
|
||||||
|
process_rule1 $mtarget, $msource, $mdest, $mproto, $mports, $msports, $origdest, $rate, $user;
|
||||||
|
|
||||||
|
progress_message " Rule \"$line\" $done"; }
|
||||||
|
|
||||||
|
close M;
|
||||||
|
|
||||||
|
progress_message '..End Macro'
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Once a rule has been completely resolved by macro expansion, it is processed by this function.
|
||||||
|
#
|
||||||
|
sub process_rule1 ( $$$$$$$$$ ) {
|
||||||
|
my ( $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user ) = @_;
|
||||||
|
my ( $action, $loglevel) = split_action $target;
|
||||||
|
my $rule = '';
|
||||||
|
my $actionchainref;
|
||||||
|
|
||||||
|
$ports = '' unless defined $ports;
|
||||||
|
$sports = '' unless defined $sports;
|
||||||
|
$origdest = '' unless defined $origdest;
|
||||||
|
$ratelimit = '' unless defined $ratelimit;
|
||||||
|
$user = '' unless defined $user;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Determine the validity of the action
|
||||||
|
#
|
||||||
|
my $actiontype = $targets{$action} || find_macro( isolate_basic_target $action );
|
||||||
|
|
||||||
|
fatal_error "Unknown action ($action) in rule \"$line\"" unless $actiontype;
|
||||||
|
|
||||||
|
if ( $actiontype == MACRO ) {
|
||||||
|
process_macro
|
||||||
|
$macros{isolate_basic_target $action}, $
|
||||||
|
target ,
|
||||||
|
(split '/', $action)[1] ,
|
||||||
|
$source,
|
||||||
|
$dest,
|
||||||
|
$proto,
|
||||||
|
$ports,
|
||||||
|
$sports,
|
||||||
|
$origdest,
|
||||||
|
$ratelimit,
|
||||||
|
$user;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# We can now dispense with the postfix characters
|
||||||
|
#
|
||||||
|
$action =~ s/[\+\-!]$//;
|
||||||
|
#
|
||||||
|
# Mark target as used
|
||||||
|
#
|
||||||
|
if ( $actiontype & ACTION ) {
|
||||||
|
unless ( $usedactions{$target} ) {
|
||||||
|
$usedactions{$target} = 1;
|
||||||
|
createactionchain $target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# Take care of irregular syntax and targets
|
||||||
|
#
|
||||||
|
if ( $actiontype & REDIRECT ) {
|
||||||
|
if ( $dest eq '-' ) {
|
||||||
|
$dest = "$firewall_zone";
|
||||||
|
} else {
|
||||||
|
$dest = "$firewall_zone" . '::' . "$dest";
|
||||||
|
}
|
||||||
|
} elsif ( $action eq 'REJECT' ) {
|
||||||
|
$action = 'reject';
|
||||||
|
} elsif ( $action eq 'CONTINUE' ) {
|
||||||
|
$action = 'RETURN';
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# Isolate and validate source and destination zones
|
||||||
|
#
|
||||||
|
my $sourcezone;
|
||||||
|
my $destzone;
|
||||||
|
|
||||||
|
if ( $source =~ /^(.+?):(.*)/ ) {
|
||||||
|
$sourcezone = $1;
|
||||||
|
$source = $2;
|
||||||
|
} else {
|
||||||
|
$sourcezone = $source;
|
||||||
|
$source = ALLIPv4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $dest =~ /^(.+?):(.*)/ ) {
|
||||||
|
$destzone = $1;
|
||||||
|
$dest = $2;
|
||||||
|
} else {
|
||||||
|
$destzone = $dest;
|
||||||
|
$dest = ALLIPv4;
|
||||||
|
}
|
||||||
|
|
||||||
|
fatal_error "Unknown source zone ($sourcezone) in rule \"$line\"" unless $zones{$sourcezone};
|
||||||
|
fatal_error "Unknown destination zone ($destzone) in rule \"$line\"" unless $zones{$destzone};
|
||||||
|
#
|
||||||
|
# Take care of chain
|
||||||
|
#
|
||||||
|
my $chain = "${sourcezone}2${destzone}";
|
||||||
|
my $chainref = ensure_filter_chain $chain, 1;
|
||||||
|
#
|
||||||
|
# Validate Policy
|
||||||
|
#
|
||||||
|
my $policy = $chainref->{policy};
|
||||||
|
fatal_error "No policy defined from $sourcezone to zone $destzone" unless $policy;
|
||||||
|
fatal_error "Rules may not override a NONE policy: rule \"$line\"" if $policy eq 'NONE';
|
||||||
|
#
|
||||||
|
# Generate Fixed part of the rule
|
||||||
|
#
|
||||||
|
$rule = do_proto $proto, $ports, $sports . do_ratelimit( $ratelimit ) . ( do_user $user );
|
||||||
|
|
||||||
|
$origdest = ALLIPv4 unless $origdest and $origdest ne '-';
|
||||||
|
#
|
||||||
|
# Generate NAT rule(s), if any
|
||||||
|
#
|
||||||
|
if ( $actiontype & NATRULE ) {
|
||||||
|
my ( $server, $serverport , $natchain );
|
||||||
|
fatal_error "$target rules not allowed in the $section SECTION" if $section ne 'NEW';
|
||||||
|
#
|
||||||
|
# Isolate server port
|
||||||
|
#
|
||||||
|
if ( $dest =~ /^(.*)(:(\d+))$/ ) {
|
||||||
|
$server = $1;
|
||||||
|
$serverport = $3;
|
||||||
|
} else {
|
||||||
|
$server = $dest;
|
||||||
|
$serverport = '';
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# After DNAT, dest port will be the server port
|
||||||
|
#
|
||||||
|
$ports = $serverport if $serverport;
|
||||||
|
|
||||||
|
fatal_error "A server must be specified in the DEST column in $action rules: \"$line\"" unless ( $actiontype & REDIRECT ) || $server;
|
||||||
|
fatal_error "Invalid server ($server), rule: \"$line\"" if $server =~ /:/;
|
||||||
|
#
|
||||||
|
# Generate the target
|
||||||
|
#
|
||||||
|
my $target = '';
|
||||||
|
|
||||||
|
if ( $action eq 'SAME' ) {
|
||||||
|
fatal_error 'Port mapping not allowed in SAME rules' if $serverport;
|
||||||
|
$target = '-j SAME ';
|
||||||
|
for my $serv ( split /,/, $server ) {
|
||||||
|
$target .= "--to $serv ";
|
||||||
|
}
|
||||||
|
|
||||||
|
$serverport = $ports;
|
||||||
|
} elsif ( $action eq ' -j DNAT' ) {
|
||||||
|
$serverport = ":$serverport" if $serverport;
|
||||||
|
for my $serv ( split /,/, $server ) {
|
||||||
|
$target .= "--to ${serv}${serverport} ";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$target = '-j REDIRECT --to-port ' . ( $serverport ? $serverport : $ports );
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# And generate the nat table rule(s)
|
||||||
|
#
|
||||||
|
expand_rule
|
||||||
|
ensure_chain ('nat' , $zones{$sourcezone}{type} eq 'firewall' ? 'OUTPUT' : dnat_chain $sourcezone ) ,
|
||||||
|
$rule ,
|
||||||
|
$source ,
|
||||||
|
$origdest ,
|
||||||
|
'' ,
|
||||||
|
$target ,
|
||||||
|
$loglevel ,
|
||||||
|
$action ,
|
||||||
|
$serverport ? do_proto( $proto, '', '' ) : '';
|
||||||
|
#
|
||||||
|
# After NAT, the destination port will be the server port; Also, we log NAT rules in the nat table rather than in the filter table.
|
||||||
|
#
|
||||||
|
unless ( $actiontype & NATONLY ) {
|
||||||
|
$rule = do_proto $proto, $ports, $sports . do_ratelimit( $ratelimit ) . do_user $user;
|
||||||
|
$loglevel = '';
|
||||||
|
}
|
||||||
|
} elsif ( $actiontype & NONAT ) {
|
||||||
|
#
|
||||||
|
# NONAT or ACCEPT+ -- May not specify a destination interface
|
||||||
|
#
|
||||||
|
fatal_error "Invalid DEST ($dest) in $action rule \"$line\"" if $dest =~ /:/;
|
||||||
|
|
||||||
|
expand_rule
|
||||||
|
ensure_chain ('nat' , $zones{$sourcezone}{type} eq 'firewall' ? 'OUTPUT' : dnat_chain $sourcezone) ,
|
||||||
|
$rule ,
|
||||||
|
$source ,
|
||||||
|
$dest ,
|
||||||
|
'' ,
|
||||||
|
'-j RETURN ' ,
|
||||||
|
$loglevel ,
|
||||||
|
$action ,
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# Add filter table rule, unless this is a NATONLY rule type
|
||||||
|
#
|
||||||
|
unless ( $actiontype & NATONLY ) {
|
||||||
|
|
||||||
|
if ( $actiontype & ACTION ) {
|
||||||
|
$action = (find_logactionchain $target)->{name};
|
||||||
|
$loglevel = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
expand_rule
|
||||||
|
ensure_chain ('filter', $chain ) ,
|
||||||
|
$rule ,
|
||||||
|
$source ,
|
||||||
|
$dest ,
|
||||||
|
$origdest ,
|
||||||
|
"-j $action " ,
|
||||||
|
$loglevel ,
|
||||||
|
$action ,
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Process a Record in the rules file
|
||||||
|
#
|
||||||
|
sub process_rule ( $$$$$$$$$ ) {
|
||||||
|
my ( $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user ) = @_;
|
||||||
|
my $intrazone = 0;
|
||||||
|
my $includesrcfw = 1;
|
||||||
|
my $includedstfw = 1;
|
||||||
|
my $optimize = $config{OPTIMIZE};
|
||||||
|
my $thisline = $line;
|
||||||
|
#
|
||||||
|
# Section Names are optional so once we get to an actual rule, we need to be sure that
|
||||||
|
# we close off any missing sections.
|
||||||
|
#
|
||||||
|
unless ( $sectioned ) {
|
||||||
|
finish_section 'ESTABLISHED,RELATED';
|
||||||
|
$section = 'NEW';
|
||||||
|
$sectioned = 1;
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# Handle Wildcards
|
||||||
|
#
|
||||||
|
if ( $source =~ /^all[-+]/ ) {
|
||||||
|
if ( $source eq 'all+' ) {
|
||||||
|
$source = 'all';
|
||||||
|
$intrazone = 1;
|
||||||
|
} elsif ( ( $source eq 'all+-' ) || ( $source eq 'all-+' ) ) {
|
||||||
|
$source = 'all';
|
||||||
|
$intrazone = 1;
|
||||||
|
$includesrcfw = 0;
|
||||||
|
} elsif ( $source eq 'all-' ) {
|
||||||
|
$source = 'all';
|
||||||
|
$includesrcfw = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $dest =~ /^all[-+]/ ) {
|
||||||
|
if ( $dest eq 'all+' ) {
|
||||||
|
$dest = 'all';
|
||||||
|
$intrazone = 1;
|
||||||
|
} elsif ( ( $dest eq 'all+-' ) || ( $dest eq 'all-+' ) ) {
|
||||||
|
$dest = 'all';
|
||||||
|
$intrazone = 1;
|
||||||
|
$includedstfw = 0;
|
||||||
|
} elsif ( $source eq 'all-' ) {
|
||||||
|
$dest = 'all';
|
||||||
|
$includedstfw = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $action = isolate_basic_target $target;
|
||||||
|
|
||||||
|
$optimize = 0 if $action =~ /!^/;
|
||||||
|
|
||||||
|
if ( $source eq 'all' ) {
|
||||||
|
for my $zone ( @zones ) {
|
||||||
|
if ( $includesrcfw || ( $zones{$zone}{type} ne 'firewall' ) ) {
|
||||||
|
if ( $dest eq 'all' ) {
|
||||||
|
for my $zone1 ( @zones ) {
|
||||||
|
if ( $includedstfw || ( $zones{$zone1}{type} ne 'firewall' ) ) {
|
||||||
|
if ( $intrazone || ( $zone ne $zone1 ) ) {
|
||||||
|
my $policychainref = $filter_table->{"${zone}2${zone1}"}{policychain};
|
||||||
|
fatal_error "No policy from zone $zone to zone $zone1" unless $policychainref;
|
||||||
|
if ( ( ( my $policy ) = $policychainref->{policy} ) ne 'NONE' ) {
|
||||||
|
if ( $optimize > 0 ) {
|
||||||
|
my $loglevel = $policychainref->{loglevel};
|
||||||
|
if ( $loglevel ) {
|
||||||
|
next if $target eq "${policy}:$loglevel}";
|
||||||
|
} else {
|
||||||
|
next if $action eq $policy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process_rule1 $target, $zone, $zone1 , $proto, $ports, $sports, $origdest, $ratelimit, $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
process_rule1 $target, $zone, $dest , $proto, $ports, $sports, $origdest, $ratelimit, $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elsif ( $dest eq 'all' ) {
|
||||||
|
for my $zone1 ( @zones ) {
|
||||||
|
my $zone = ( split /:/, $source )[0];
|
||||||
|
if ( ( $includedstfw || ( $zones{$zone1}{type} ne 'firewall') ) &&( ( $zone ne $zone1 ) || $intrazone) ) {
|
||||||
|
process_rule1 $target, $source, $zone1 , $proto, $ports, $sports, $origdest, $ratelimit, $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
process_rule1 $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress_message " Rule \"$thisline\" $done";
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Process the Rules File
|
||||||
|
#
|
||||||
|
sub process_rules() {
|
||||||
|
|
||||||
|
open RULES, "$ENV{TMP_DIR}/rules" or fatal_error "Unable to open stripped rules file: $!";
|
||||||
|
|
||||||
|
while ( $line = <RULES> ) {
|
||||||
|
|
||||||
|
chomp $line;
|
||||||
|
$line =~ s/\s+/ /g;
|
||||||
|
|
||||||
|
my ( $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user, $extra ) = split /\s+/, $line;
|
||||||
|
|
||||||
|
if ( $target eq 'COMMENT' ) {
|
||||||
|
if ( $capabilities{COMMENTS} ) {
|
||||||
|
( $comment = $line ) =~ s/^\s*COMMENT\s*//;
|
||||||
|
$comment =~ s/\s*$//;
|
||||||
|
} else {
|
||||||
|
warning_message "COMMENT ignored -- requires comment support in iptables/Netfilter";
|
||||||
|
}
|
||||||
|
} elsif ( $target eq 'SECTION' ) {
|
||||||
|
fatal_error "Invalid SECTION $source" unless defined $sections{$source};
|
||||||
|
fatal_error "Duplicate or out of order SECTION $source" if $sections{$source};
|
||||||
|
fatal_error "Invalid Section $source $dest" if $dest;
|
||||||
|
$sectioned = 1;
|
||||||
|
$sections{$source} = 1;
|
||||||
|
|
||||||
|
if ( $section eq 'RELATED' ) {
|
||||||
|
$sections{ESTABLISHED} = 1;
|
||||||
|
finish_section 'ESTABLISHED';
|
||||||
|
} elsif ( $section eq 'NEW' ) {
|
||||||
|
@sections{'ESTABLISHED','RELATED'} = ( 1, 1 );
|
||||||
|
finish_section ( ( $section eq 'RELATED' ) ? 'RELATED' : 'ESTABLISHED,RELATED' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$section = $source;
|
||||||
|
} else {
|
||||||
|
fatal_error "Invalid rules file entry: \"$line\"" if $extra;
|
||||||
|
process_rule $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close RULES;
|
||||||
|
|
||||||
|
$comment = '';
|
||||||
|
$section = 'DONE';
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
449
New/compiler.pl
449
New/compiler.pl
@ -16,11 +16,7 @@ use Shorewall::Policy;
|
|||||||
use Shorewall::Macros;
|
use Shorewall::Macros;
|
||||||
use Shorewall::Actions;
|
use Shorewall::Actions;
|
||||||
use Shorewall::Accounting;
|
use Shorewall::Accounting;
|
||||||
|
use Shorewall::Rules;
|
||||||
#
|
|
||||||
# Set to one if we find a SECTION
|
|
||||||
#
|
|
||||||
my $sectioned = 0;
|
|
||||||
|
|
||||||
sub process_tos() {
|
sub process_tos() {
|
||||||
my $chain = 'pretos';
|
my $chain = 'pretos';
|
||||||
@ -486,449 +482,6 @@ sub setup_mac_lists( $ ) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub process_rule1 ( $$$$$$$$$ );
|
|
||||||
|
|
||||||
#
|
|
||||||
# Expand a macro rule from the rules file
|
|
||||||
#
|
|
||||||
sub process_macro ( $$$$$$$$$$$ ) {
|
|
||||||
my ($macrofile, $target, $param, $source, $dest, $proto, $ports, $sports, $origdest, $rate, $user) = @_;
|
|
||||||
|
|
||||||
my $standard = ( $macrofile =~ /^($env{SHAREDIR})/ );
|
|
||||||
|
|
||||||
progress_message "..Expanding Macro $macrofile...";
|
|
||||||
|
|
||||||
open M, $macrofile or fatal_error "Unable to open $macrofile: $!";
|
|
||||||
|
|
||||||
while ( $line = <M> ) {
|
|
||||||
chomp $line;
|
|
||||||
next if $line =~ /^\s*#/;
|
|
||||||
next if $line =~ /^\s*$/;
|
|
||||||
$line =~ s/\s+/ /g;
|
|
||||||
$line =~ s/#.*$//;
|
|
||||||
$line = expand_shell_variables $line unless $standard;
|
|
||||||
|
|
||||||
my ( $mtarget, $msource, $mdest, $mproto, $mports, $msports, $mrate, $muser ) = split /\s+/, $line;
|
|
||||||
|
|
||||||
$mtarget = merge_levels $target, $mtarget;
|
|
||||||
|
|
||||||
if ( $mtarget =~ /^PARAM:?/ ) {
|
|
||||||
fatal_error 'PARAM requires that a parameter be supplied in macro invocation' unless $param;
|
|
||||||
$mtarget = substitute_action $param, $mtarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
my $action = isolate_basic_target $mtarget;
|
|
||||||
my $actiontype = $targets{$action};
|
|
||||||
|
|
||||||
if ( $actiontype & ACTION ) {
|
|
||||||
unless ( $usedactions{$action} ) {
|
|
||||||
createactionchain $mtarget;
|
|
||||||
$usedactions{$mtarget} = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$mtarget = find_logactionchain $mtarget;
|
|
||||||
} else {
|
|
||||||
fatal_error "Invalid Action ($mtarget) in rule \"$line\"" unless $actiontype & STANDARD;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $msource ) {
|
|
||||||
if ( ( $msource eq '-' ) || ( $msource eq 'SOURCE' ) ) {
|
|
||||||
$msource = $source || '';
|
|
||||||
} elsif ( $msource eq 'DEST' ) {
|
|
||||||
$msource = $dest || '';
|
|
||||||
} else {
|
|
||||||
$msource = merge_macro_source_dest $msource, $source;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$msource = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$msource = '' if $msource eq '-';
|
|
||||||
|
|
||||||
if ( $mdest ) {
|
|
||||||
if ( ( $mdest eq '-' ) || ( $mdest eq 'DEST' ) ) {
|
|
||||||
$mdest = $dest || '';
|
|
||||||
} elsif ( $mdest eq 'SOURCE' ) {
|
|
||||||
$mdest = $source || '';
|
|
||||||
} else {
|
|
||||||
$mdest = merge_macro_source_dest $mdest, $dest;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$mdest = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdest = '' if $mdest eq '-';
|
|
||||||
|
|
||||||
$mproto = merge_macro_column $mproto, $proto;
|
|
||||||
$mports = merge_macro_column $mports, $ports;
|
|
||||||
$msports = merge_macro_column $msports, $sports;
|
|
||||||
$mrate = merge_macro_column $mrate, $rate;
|
|
||||||
$muser = merge_macro_column $muser, $user;
|
|
||||||
|
|
||||||
process_rule1 $mtarget, $msource, $mdest, $mproto, $mports, $msports, $origdest, $rate, $user;
|
|
||||||
|
|
||||||
progress_message " Rule \"$line\" $done"; }
|
|
||||||
|
|
||||||
close M;
|
|
||||||
|
|
||||||
progress_message '..End Macro'
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Once a rule has been completely resolved by macro expansion, it is processed by this function.
|
|
||||||
#
|
|
||||||
sub process_rule1 ( $$$$$$$$$ ) {
|
|
||||||
my ( $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user ) = @_;
|
|
||||||
my ( $action, $loglevel) = split_action $target;
|
|
||||||
my $rule = '';
|
|
||||||
my $actionchainref;
|
|
||||||
|
|
||||||
$ports = '' unless defined $ports;
|
|
||||||
$sports = '' unless defined $sports;
|
|
||||||
$origdest = '' unless defined $origdest;
|
|
||||||
$ratelimit = '' unless defined $ratelimit;
|
|
||||||
$user = '' unless defined $user;
|
|
||||||
|
|
||||||
#
|
|
||||||
# Determine the validity of the action
|
|
||||||
#
|
|
||||||
my $actiontype = $targets{$action} || find_macro( isolate_basic_target $action );
|
|
||||||
|
|
||||||
fatal_error "Unknown action ($action) in rule \"$line\"" unless $actiontype;
|
|
||||||
|
|
||||||
if ( $actiontype == MACRO ) {
|
|
||||||
process_macro
|
|
||||||
$macros{isolate_basic_target $action}, $
|
|
||||||
target ,
|
|
||||||
(split '/', $action)[1] ,
|
|
||||||
$source,
|
|
||||||
$dest,
|
|
||||||
$proto,
|
|
||||||
$ports,
|
|
||||||
$sports,
|
|
||||||
$origdest,
|
|
||||||
$ratelimit,
|
|
||||||
$user;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#
|
|
||||||
# We can now dispense with the postfix characters
|
|
||||||
#
|
|
||||||
$action =~ s/[\+\-!]$//;
|
|
||||||
#
|
|
||||||
# Mark target as used
|
|
||||||
#
|
|
||||||
if ( $actiontype & ACTION ) {
|
|
||||||
unless ( $usedactions{$target} ) {
|
|
||||||
$usedactions{$target} = 1;
|
|
||||||
createactionchain $target;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#
|
|
||||||
# Take care of irregular syntax and targets
|
|
||||||
#
|
|
||||||
if ( $actiontype & REDIRECT ) {
|
|
||||||
if ( $dest eq '-' ) {
|
|
||||||
$dest = "$firewall_zone";
|
|
||||||
} else {
|
|
||||||
$dest = "$firewall_zone" . '::' . "$dest";
|
|
||||||
}
|
|
||||||
} elsif ( $action eq 'REJECT' ) {
|
|
||||||
$action = 'reject';
|
|
||||||
} elsif ( $action eq 'CONTINUE' ) {
|
|
||||||
$action = 'RETURN';
|
|
||||||
}
|
|
||||||
#
|
|
||||||
# Isolate and validate source and destination zones
|
|
||||||
#
|
|
||||||
my $sourcezone;
|
|
||||||
my $destzone;
|
|
||||||
|
|
||||||
if ( $source =~ /^(.+?):(.*)/ ) {
|
|
||||||
$sourcezone = $1;
|
|
||||||
$source = $2;
|
|
||||||
} else {
|
|
||||||
$sourcezone = $source;
|
|
||||||
$source = ALLIPv4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $dest =~ /^(.+?):(.*)/ ) {
|
|
||||||
$destzone = $1;
|
|
||||||
$dest = $2;
|
|
||||||
} else {
|
|
||||||
$destzone = $dest;
|
|
||||||
$dest = ALLIPv4;
|
|
||||||
}
|
|
||||||
|
|
||||||
fatal_error "Unknown source zone ($sourcezone) in rule \"$line\"" unless $zones{$sourcezone};
|
|
||||||
fatal_error "Unknown destination zone ($destzone) in rule \"$line\"" unless $zones{$destzone};
|
|
||||||
#
|
|
||||||
# Take care of chain
|
|
||||||
#
|
|
||||||
my $chain = "${sourcezone}2${destzone}";
|
|
||||||
my $chainref = ensure_filter_chain $chain, 1;
|
|
||||||
#
|
|
||||||
# Validate Policy
|
|
||||||
#
|
|
||||||
my $policy = $chainref->{policy};
|
|
||||||
fatal_error "No policy defined from $sourcezone to zone $destzone" unless $policy;
|
|
||||||
fatal_error "Rules may not override a NONE policy: rule \"$line\"" if $policy eq 'NONE';
|
|
||||||
#
|
|
||||||
# Generate Fixed part of the rule
|
|
||||||
#
|
|
||||||
$rule = do_proto $proto, $ports, $sports . do_ratelimit( $ratelimit ) . ( do_user $user );
|
|
||||||
|
|
||||||
$origdest = ALLIPv4 unless $origdest and $origdest ne '-';
|
|
||||||
#
|
|
||||||
# Generate NAT rule(s), if any
|
|
||||||
#
|
|
||||||
if ( $actiontype & NATRULE ) {
|
|
||||||
my ( $server, $serverport , $natchain );
|
|
||||||
fatal_error "$target rules not allowed in the $section SECTION" if $section ne 'NEW';
|
|
||||||
#
|
|
||||||
# Isolate server port
|
|
||||||
#
|
|
||||||
if ( $dest =~ /^(.*)(:(\d+))$/ ) {
|
|
||||||
$server = $1;
|
|
||||||
$serverport = $3;
|
|
||||||
} else {
|
|
||||||
$server = $dest;
|
|
||||||
$serverport = '';
|
|
||||||
}
|
|
||||||
#
|
|
||||||
# After DNAT, dest port will be the server port
|
|
||||||
#
|
|
||||||
$ports = $serverport if $serverport;
|
|
||||||
|
|
||||||
fatal_error "A server must be specified in the DEST column in $action rules: \"$line\"" unless ( $actiontype & REDIRECT ) || $server;
|
|
||||||
fatal_error "Invalid server ($server), rule: \"$line\"" if $server =~ /:/;
|
|
||||||
#
|
|
||||||
# Generate the target
|
|
||||||
#
|
|
||||||
my $target = '';
|
|
||||||
|
|
||||||
if ( $action eq 'SAME' ) {
|
|
||||||
fatal_error 'Port mapping not allowed in SAME rules' if $serverport;
|
|
||||||
$target = '-j SAME ';
|
|
||||||
for my $serv ( split /,/, $server ) {
|
|
||||||
$target .= "--to $serv ";
|
|
||||||
}
|
|
||||||
|
|
||||||
$serverport = $ports;
|
|
||||||
} elsif ( $action eq ' -j DNAT' ) {
|
|
||||||
$serverport = ":$serverport" if $serverport;
|
|
||||||
for my $serv ( split /,/, $server ) {
|
|
||||||
$target .= "--to ${serv}${serverport} ";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$target = '-j REDIRECT --to-port ' . ( $serverport ? $serverport : $ports );
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# And generate the nat table rule(s)
|
|
||||||
#
|
|
||||||
expand_rule
|
|
||||||
ensure_chain ('nat' , $zones{$sourcezone}{type} eq 'firewall' ? 'OUTPUT' : dnat_chain $sourcezone ) ,
|
|
||||||
$rule ,
|
|
||||||
$source ,
|
|
||||||
$origdest ,
|
|
||||||
'' ,
|
|
||||||
$target ,
|
|
||||||
$loglevel ,
|
|
||||||
$action ,
|
|
||||||
$serverport ? do_proto( $proto, '', '' ) : '';
|
|
||||||
#
|
|
||||||
# After NAT, the destination port will be the server port; Also, we log NAT rules in the nat table rather than in the filter table.
|
|
||||||
#
|
|
||||||
unless ( $actiontype & NATONLY ) {
|
|
||||||
$rule = do_proto $proto, $ports, $sports . do_ratelimit( $ratelimit ) . do_user $user;
|
|
||||||
$loglevel = '';
|
|
||||||
}
|
|
||||||
} elsif ( $actiontype & NONAT ) {
|
|
||||||
#
|
|
||||||
# NONAT or ACCEPT+ -- May not specify a destination interface
|
|
||||||
#
|
|
||||||
fatal_error "Invalid DEST ($dest) in $action rule \"$line\"" if $dest =~ /:/;
|
|
||||||
|
|
||||||
expand_rule
|
|
||||||
ensure_chain ('nat' , $zones{$sourcezone}{type} eq 'firewall' ? 'OUTPUT' : dnat_chain $sourcezone) ,
|
|
||||||
$rule ,
|
|
||||||
$source ,
|
|
||||||
$dest ,
|
|
||||||
'' ,
|
|
||||||
'-j RETURN ' ,
|
|
||||||
$loglevel ,
|
|
||||||
$action ,
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
#
|
|
||||||
# Add filter table rule, unless this is a NATONLY rule type
|
|
||||||
#
|
|
||||||
unless ( $actiontype & NATONLY ) {
|
|
||||||
|
|
||||||
if ( $actiontype & ACTION ) {
|
|
||||||
$action = (find_logactionchain $target)->{name};
|
|
||||||
$loglevel = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
expand_rule
|
|
||||||
ensure_chain ('filter', $chain ) ,
|
|
||||||
$rule ,
|
|
||||||
$source ,
|
|
||||||
$dest ,
|
|
||||||
$origdest ,
|
|
||||||
"-j $action " ,
|
|
||||||
$loglevel ,
|
|
||||||
$action ,
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Process a Record in the rules file
|
|
||||||
#
|
|
||||||
sub process_rule ( $$$$$$$$$ ) {
|
|
||||||
my ( $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user ) = @_;
|
|
||||||
my $intrazone = 0;
|
|
||||||
my $includesrcfw = 1;
|
|
||||||
my $includedstfw = 1;
|
|
||||||
my $optimize = $config{OPTIMIZE};
|
|
||||||
my $thisline = $line;
|
|
||||||
#
|
|
||||||
# Section Names are optional so once we get to an actual rule, we need to be sure that
|
|
||||||
# we close off any missing sections.
|
|
||||||
#
|
|
||||||
unless ( $sectioned ) {
|
|
||||||
finish_section 'ESTABLISHED,RELATED';
|
|
||||||
$section = 'NEW';
|
|
||||||
$sectioned = 1;
|
|
||||||
}
|
|
||||||
#
|
|
||||||
# Handle Wildcards
|
|
||||||
#
|
|
||||||
if ( $source =~ /^all[-+]/ ) {
|
|
||||||
if ( $source eq 'all+' ) {
|
|
||||||
$source = 'all';
|
|
||||||
$intrazone = 1;
|
|
||||||
} elsif ( ( $source eq 'all+-' ) || ( $source eq 'all-+' ) ) {
|
|
||||||
$source = 'all';
|
|
||||||
$intrazone = 1;
|
|
||||||
$includesrcfw = 0;
|
|
||||||
} elsif ( $source eq 'all-' ) {
|
|
||||||
$source = 'all';
|
|
||||||
$includesrcfw = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $dest =~ /^all[-+]/ ) {
|
|
||||||
if ( $dest eq 'all+' ) {
|
|
||||||
$dest = 'all';
|
|
||||||
$intrazone = 1;
|
|
||||||
} elsif ( ( $dest eq 'all+-' ) || ( $dest eq 'all-+' ) ) {
|
|
||||||
$dest = 'all';
|
|
||||||
$intrazone = 1;
|
|
||||||
$includedstfw = 0;
|
|
||||||
} elsif ( $source eq 'all-' ) {
|
|
||||||
$dest = 'all';
|
|
||||||
$includedstfw = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
my $action = isolate_basic_target $target;
|
|
||||||
|
|
||||||
$optimize = 0 if $action =~ /!^/;
|
|
||||||
|
|
||||||
if ( $source eq 'all' ) {
|
|
||||||
for my $zone ( @zones ) {
|
|
||||||
if ( $includesrcfw || ( $zones{$zone}{type} ne 'firewall' ) ) {
|
|
||||||
if ( $dest eq 'all' ) {
|
|
||||||
for my $zone1 ( @zones ) {
|
|
||||||
if ( $includedstfw || ( $zones{$zone1}{type} ne 'firewall' ) ) {
|
|
||||||
if ( $intrazone || ( $zone ne $zone1 ) ) {
|
|
||||||
my $policychainref = $filter_table->{"${zone}2${zone1}"}{policychain};
|
|
||||||
fatal_error "No policy from zone $zone to zone $zone1" unless $policychainref;
|
|
||||||
if ( ( ( my $policy ) = $policychainref->{policy} ) ne 'NONE' ) {
|
|
||||||
if ( $optimize > 0 ) {
|
|
||||||
my $loglevel = $policychainref->{loglevel};
|
|
||||||
if ( $loglevel ) {
|
|
||||||
next if $target eq "${policy}:$loglevel}";
|
|
||||||
} else {
|
|
||||||
next if $action eq $policy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
process_rule1 $target, $zone, $zone1 , $proto, $ports, $sports, $origdest, $ratelimit, $user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
process_rule1 $target, $zone, $dest , $proto, $ports, $sports, $origdest, $ratelimit, $user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} elsif ( $dest eq 'all' ) {
|
|
||||||
for my $zone1 ( @zones ) {
|
|
||||||
my $zone = ( split /:/, $source )[0];
|
|
||||||
if ( ( $includedstfw || ( $zones{$zone1}{type} ne 'firewall') ) &&( ( $zone ne $zone1 ) || $intrazone) ) {
|
|
||||||
process_rule1 $target, $source, $zone1 , $proto, $ports, $sports, $origdest, $ratelimit, $user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
process_rule1 $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user;
|
|
||||||
}
|
|
||||||
|
|
||||||
progress_message " Rule \"$thisline\" $done";
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Process the Rules File
|
|
||||||
#
|
|
||||||
sub process_rules() {
|
|
||||||
|
|
||||||
open RULES, "$ENV{TMP_DIR}/rules" or fatal_error "Unable to open stripped rules file: $!";
|
|
||||||
|
|
||||||
while ( $line = <RULES> ) {
|
|
||||||
|
|
||||||
chomp $line;
|
|
||||||
$line =~ s/\s+/ /g;
|
|
||||||
|
|
||||||
my ( $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user, $extra ) = split /\s+/, $line;
|
|
||||||
|
|
||||||
if ( $target eq 'COMMENT' ) {
|
|
||||||
if ( $capabilities{COMMENTS} ) {
|
|
||||||
( $comment = $line ) =~ s/^\s*COMMENT\s*//;
|
|
||||||
$comment =~ s/\s*$//;
|
|
||||||
} else {
|
|
||||||
warning_message "COMMENT ignored -- requires comment support in iptables/Netfilter";
|
|
||||||
}
|
|
||||||
} elsif ( $target eq 'SECTION' ) {
|
|
||||||
fatal_error "Invalid SECTION $source" unless defined $sections{$source};
|
|
||||||
fatal_error "Duplicate or out of order SECTION $source" if $sections{$source};
|
|
||||||
fatal_error "Invalid Section $source $dest" if $dest;
|
|
||||||
$sectioned = 1;
|
|
||||||
$sections{$source} = 1;
|
|
||||||
|
|
||||||
if ( $section eq 'RELATED' ) {
|
|
||||||
$sections{ESTABLISHED} = 1;
|
|
||||||
finish_section 'ESTABLISHED';
|
|
||||||
} elsif ( $section eq 'NEW' ) {
|
|
||||||
@sections{'ESTABLISHED','RELATED'} = ( 1, 1 );
|
|
||||||
finish_section ( ( $section eq 'RELATED' ) ? 'RELATED' : 'ESTABLISHED,RELATED' );
|
|
||||||
}
|
|
||||||
|
|
||||||
$section = $source;
|
|
||||||
} else {
|
|
||||||
fatal_error "Invalid rules file entry: \"$line\"" if $extra;
|
|
||||||
process_rule $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close RULES;
|
|
||||||
|
|
||||||
$comment = '';
|
|
||||||
$section = 'DONE';
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# To quote an old comment, generate_matrix makes a sows ear out of a silk purse.
|
# To quote an old comment, generate_matrix makes a sows ear out of a silk purse.
|
||||||
#
|
#
|
||||||
|
Loading…
Reference in New Issue
Block a user