mirror of
https://gitlab.com/shorewall/code.git
synced 2024-12-16 11:20:53 +01:00
Get Modularized code to work with 'use strict'
git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@5529 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb
This commit is contained in:
parent
5ec37963d8
commit
0b03e47766
@ -1,8 +1,25 @@
|
||||
package Shorewall::Chains;
|
||||
require Exporter;
|
||||
|
||||
use Shorewall::Common;
|
||||
use Shorewall::Config;
|
||||
use Shorewall::Zones;
|
||||
use Shorewall::Interfaces;
|
||||
|
||||
use strict;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw( add_rule
|
||||
our @EXPORT = qw( STANDARD
|
||||
NATRULE
|
||||
BUILTIN
|
||||
NONAT
|
||||
NATONLY
|
||||
REDIRECT
|
||||
ACTION
|
||||
MACRO
|
||||
LOGRULE
|
||||
|
||||
add_rule
|
||||
insert_rule
|
||||
chain_base
|
||||
forward_chain
|
||||
@ -28,13 +45,39 @@ our @EXPORT = qw( add_rule
|
||||
initialize_chain_table
|
||||
dump_chain_table
|
||||
finish_section
|
||||
newexclusionchain
|
||||
clearrule
|
||||
do_proto
|
||||
mac_match
|
||||
numeric_value
|
||||
verify_mark
|
||||
verify_small_mark
|
||||
validate_mark
|
||||
do_test
|
||||
do_ratelimit
|
||||
do_user
|
||||
iprange_match
|
||||
match_source_net
|
||||
match_dest_net
|
||||
match_orig_dest
|
||||
match_ipsec_in
|
||||
match_ipsec_out
|
||||
log_rule_limit
|
||||
log_rule
|
||||
expand_rule
|
||||
addnatjump
|
||||
insertnatjump
|
||||
|
||||
@policy_chains
|
||||
%chain_table
|
||||
$nat_table
|
||||
$mangle_table
|
||||
$filter_table
|
||||
$section );
|
||||
$section
|
||||
%sections
|
||||
$comment
|
||||
%targets
|
||||
);
|
||||
our @EXPORT_OK = ();
|
||||
our @VERSION = 1.00;
|
||||
|
||||
@ -73,10 +116,74 @@ our %chain_table = ( raw => {} ,
|
||||
our $nat_table = $chain_table{nat};
|
||||
our $mangle_table = $chain_table{mangle};
|
||||
our $filter_table = $chain_table{filter};
|
||||
|
||||
#
|
||||
# These get set to 1 as sections are encountered.
|
||||
#
|
||||
our %sections = ( ESTABLISHED => 0,
|
||||
RELATED => 0,
|
||||
NEW => 0
|
||||
);
|
||||
#
|
||||
# Current rules file section.
|
||||
#
|
||||
our $section = 'ESTABLISHED';
|
||||
#
|
||||
# Contents of last COMMENT line.
|
||||
#
|
||||
our $comment = '';
|
||||
# Target Table. Each entry maps a target to a set of flags defined as follows.
|
||||
#
|
||||
use constant { STANDARD => 1, #defined by Netfilter
|
||||
NATRULE => 2, #Involved NAT
|
||||
BUILTIN => 4, #A built-in action
|
||||
NONAT => 8, #'NONAT' or 'ACCEPT+'
|
||||
NATONLY => 16, #'DNAT-' or 'REDIRECT-'
|
||||
REDIRECT => 32, #'REDIRECT'
|
||||
ACTION => 64, #An action
|
||||
MACRO => 128, #A Macro
|
||||
LOGRULE => 256, #'LOG'
|
||||
};
|
||||
#
|
||||
# As new targets (Actions and Macros) are discovered, they are added to the table
|
||||
#
|
||||
our %targets = ('ACCEPT' => STANDARD,
|
||||
'ACCEPT+' => STANDARD + NONAT,
|
||||
'ACCEPT!' => STANDARD,
|
||||
'NONAT' => STANDARD + NONAT,
|
||||
'DROP' => STANDARD,
|
||||
'DROP!' => STANDARD,
|
||||
'REJECT' => STANDARD,
|
||||
'REJECT!' => STANDARD,
|
||||
'DNAT' => NATRULE,
|
||||
'DNAT-' => NATRULE + NATONLY,
|
||||
'REDIRECT' => NATRULE + REDIRECT,
|
||||
'REDIRECT-' => NATRULE + REDIRECT + NATONLY,
|
||||
'LOG' => STANDARD + LOGRULE,
|
||||
'CONTINUE' => STANDARD,
|
||||
'QUEUE' => STANDARD,
|
||||
'SAME' => NATRULE,
|
||||
'SAME-' => NATRULE + NATONLY,
|
||||
'dropBcast' => BUILTIN + ACTION,
|
||||
'allowBcast' => BUILTIN + ACTION,
|
||||
'dropNotSyn' => BUILTIN + ACTION,
|
||||
'rejNotSyn' => BUILTIN + ACTION,
|
||||
'dropInvalid' => BUILTIN + ACTION,
|
||||
'allowInvalid' => BUILTIN + ACTION,
|
||||
'allowinUPnP' => BUILTIN + ACTION,
|
||||
'forwardUPnP' => BUILTIN + ACTION,
|
||||
'Limit' => BUILTIN + ACTION,
|
||||
);
|
||||
#
|
||||
# Used to sequence 'exclusion' chains with names 'excl0', 'excl1', ...
|
||||
#
|
||||
my $exclseq = 0;
|
||||
#
|
||||
# Used to suppress duplicate match specifications.
|
||||
#
|
||||
my $ipsetmatch = 0;
|
||||
my $iprangematch = 0;
|
||||
#
|
||||
|
||||
#
|
||||
# Add a rule to a chain. Arguments are:
|
||||
@ -424,4 +531,592 @@ sub finish_section ( $ ) {
|
||||
}
|
||||
}
|
||||
|
||||
sub newexclusionchain() {
|
||||
my $seq = $exclseq++;
|
||||
"excl${seq}";
|
||||
}
|
||||
|
||||
sub clearrule() {
|
||||
$iprangematch = $ipsetmatch = 0;
|
||||
}
|
||||
|
||||
#
|
||||
# Handle parsing of PROTO, DEST PORT(S) , SOURCE PORTS(S). Returns the appropriate match string.
|
||||
#
|
||||
sub do_proto( $$$ )
|
||||
{
|
||||
my ($proto, $ports, $sports ) = @_;
|
||||
|
||||
my $output = '';
|
||||
|
||||
$proto = '' unless defined $proto;
|
||||
$ports = '' unless defined $ports;
|
||||
$sports = '' unless defined $sports;
|
||||
|
||||
$proto = '' if $proto eq '-';
|
||||
$ports = '' if $ports eq '-';
|
||||
$sports = '' if $sports eq '-';
|
||||
|
||||
if ( $proto ) {
|
||||
if ( $proto =~ /^(tcp|udp|6|17)$/i ) {
|
||||
$output = "-p $proto ";
|
||||
if ( $ports ) {
|
||||
my @ports = split /,/, $ports;
|
||||
my $count = @ports;
|
||||
|
||||
if ( $count > 1 ) {
|
||||
fatal_error "Port list requires Multiport support in your kernel/iptables: $ports" unless $capabilities{MULTIPORT};
|
||||
fatal_error "Port range in a list requires Extended Multiport Support in your kernel/iptables: $ports" unless $capabilities{XMULTIPORT};
|
||||
|
||||
for my $port ( @ports ) {
|
||||
$count++ if $port =~ /:/;
|
||||
}
|
||||
|
||||
fatal_error "Too many entries in port list: $ports" if $count > 15;
|
||||
|
||||
$output .= "-m multiport --dports $ports ";
|
||||
} else {
|
||||
$output .= "--dport $ports ";
|
||||
}
|
||||
}
|
||||
|
||||
if ( $sports ) {
|
||||
my @ports = split /,/, $sports;
|
||||
my $count = @ports;
|
||||
|
||||
if ( $count > 1 ) {
|
||||
fatal_error "Port list requires Multiport support in your kernel/iptables: $sports" unless $capabilities{MULTIPORT};
|
||||
fatal_error "Port range in a list requires Extended Multiport Support in your kernel/iptables: $sports" unless $capabilities{XMULTIPORT};
|
||||
|
||||
for my $port ( @ports ) {
|
||||
$count++ if $port =~ /:/;
|
||||
}
|
||||
|
||||
fatal_error "Too many entries in port list: $sports" if $count > 15;
|
||||
|
||||
$output .= "-m multiport --sports $sports ";
|
||||
} else {
|
||||
$output .= "--sport $sports ";
|
||||
}
|
||||
}
|
||||
} elsif ( $proto =~ /^(icmp|1)$/i ) {
|
||||
$output .= "-p icmp --icmp-type $ports " if $ports;
|
||||
fatal_error 'SOURCE PORT(S) not permitted with ICMP' if $sports;
|
||||
} elsif ( $proto =~ /^(ipp2p(:(tcp|udp|all)))?$/i ) {
|
||||
fatal_error 'PROTO = ipp2p requires IPP2P match support in your kernel/iptables' unless $capabilities{IPP2P};
|
||||
$proto = $2 ? $3 : 'tcp';
|
||||
$ports = 'ipp2p' unless $ports;
|
||||
$output .= "-p $proto -m ipp2p --$ports ";
|
||||
}
|
||||
} elsif ( $ports || $sports ) {
|
||||
fatal_error "SOURCE/DEST PORT(S) not allowed without PROTO, rule \"$line\""
|
||||
}
|
||||
|
||||
$output;
|
||||
}
|
||||
|
||||
sub mac_match( $ ) {
|
||||
my $mac = $_[0];
|
||||
|
||||
$mac =~ s/^(!?)~//;
|
||||
$mac =~ s/^!// if my $invert = $1 ? '! ' : '';
|
||||
$mac =~ s/-/:/g;
|
||||
|
||||
"--match mac --mac-source ${invert}$mac ";
|
||||
}
|
||||
|
||||
#
|
||||
# Convert value to decimal number
|
||||
#
|
||||
sub numeric_value ( $ ) {
|
||||
my $mark = $_[0];
|
||||
$mark =~ /^0x/ ? hex $mark : $mark =~ /^0/ ? oct $mark : $mark;
|
||||
}
|
||||
|
||||
#
|
||||
# Mark validatation functions
|
||||
#
|
||||
sub verify_mark( $ ) {
|
||||
my $mark = $_[0];
|
||||
my $limit = $config{HIGH_ROUTE_MARKS} ? 0xFFFF : 0xFF;
|
||||
|
||||
fatal_error "Invalid Mark or Mask value: $mark"
|
||||
unless "\L$mark" =~ /^(0x[a-f0-9]+|0[0-7]*|[0-9]*)$/ && numeric_value( $mark ) <= $limit;
|
||||
}
|
||||
|
||||
sub verify_small_mark( $ ) {
|
||||
verify_mark ( (my $mark) = $_[0] );
|
||||
fatal_error "Mark value ($mark) too large" if numeric_value( $mark ) > 0xFF;
|
||||
}
|
||||
|
||||
sub validate_mark( $ ) {
|
||||
for ( split '/', $_[0] ) {
|
||||
verify_mark $_;
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Generate an appropriate -m [conn]mark match string for the contents of a MARK column
|
||||
#
|
||||
|
||||
sub do_test ( $$ )
|
||||
{
|
||||
my ($testval, $mask) = @_;
|
||||
|
||||
return '' unless $testval and $testval ne '-';
|
||||
|
||||
my $invert = $testval =~ s/^!// ? '! ' : '';
|
||||
my $match = $testval =~ s/:C$// ? '-m connmark ' : '-m mark ';
|
||||
|
||||
$testval .= '/0xFF' unless ( $testval =~ '/' );
|
||||
|
||||
"${invert}$match $testval ";
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Create a "-m limit" match for the passed LIMIT/BURST
|
||||
#
|
||||
sub do_ratelimit( $ ) {
|
||||
my $rate = $_[0];
|
||||
|
||||
return '' unless $rate and $rate ne '-';
|
||||
|
||||
if ( $rate =~ /^([^:]+):([^:]+)$/ ) {
|
||||
"-m limit --limit $1 --limit-burst $2 ";
|
||||
} else {
|
||||
"-m limit --limit $rate ";
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Create a "-m owner" match for the passed USER/GROUP
|
||||
#
|
||||
sub do_user( $ ) {
|
||||
my $user = $_[0];
|
||||
my $rule = ' -m owner';
|
||||
|
||||
return '' unless $user and $user ne '-';
|
||||
|
||||
if ( $user =~ /^(!)?(.*)\+(.*)$/ ) {
|
||||
$rule .= "! --cmd-owner $2 " if $2;
|
||||
$user = "!$1";
|
||||
} elsif ( $user =~ /^(.*)\+(.*)$/ ) {
|
||||
$rule .= "--cmd-owner $2 " if $2;
|
||||
$user = $1;
|
||||
}
|
||||
|
||||
if ( $user =~ /^!(.*):(.*)$/ ) {
|
||||
$rule .= "! --uid-owner $1 " if $1;
|
||||
$rule .= "! --gid-owner $2 " if $2;
|
||||
} elsif ( $user =~ /^(.*):(.*)$/ ) {
|
||||
$rule .= "--uid-owner $1 " if $1;
|
||||
$rule .= "--gid-owner $2 " if $2;
|
||||
} elsif ( $user =~ /^!/ ) {
|
||||
$rule .= "! --uid-owner $user ";
|
||||
} else {
|
||||
$rule .= "--uid-owner $user ";
|
||||
}
|
||||
|
||||
$rule;
|
||||
}
|
||||
|
||||
#
|
||||
# Avoid generating a second '-m iprange' in a single rule.
|
||||
#
|
||||
sub iprange_match() {
|
||||
my $match = '';
|
||||
unless ( $iprangematch ) {
|
||||
$match = '-m iprange ';
|
||||
$iprangematch = 1;
|
||||
}
|
||||
|
||||
$match;
|
||||
}
|
||||
|
||||
#
|
||||
# Match a Source. Currently only handles IP addresses and ranges
|
||||
#
|
||||
sub match_source_net( $ ) {
|
||||
my $net = $_[0];
|
||||
|
||||
if ( $net =~ /^(!?).*\..*\..*\..*-.*\..*\..*\..*/ ) {
|
||||
$net =~ s/!// if my $invert = $1 ? '! ' : '';
|
||||
|
||||
iprange_match . "${invert}--src-range $net ";
|
||||
} elsif ( $net =~ /^(!?)~(.*)$/ ) {
|
||||
( $net = $2 ) =~ s/-/:/g;
|
||||
"-m mac --mac-source $1 $net "
|
||||
} elsif ( $net =~ /^!/ ) {
|
||||
$net =~ s/!//;
|
||||
"-s ! $net ";
|
||||
} else {
|
||||
$net eq ALLIPv4 ? '' : "-s $net ";
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Match a Source. Currently only handles IP addresses and ranges
|
||||
#
|
||||
sub match_dest_net( $ ) {
|
||||
my $net = $_[0];
|
||||
|
||||
if ( $net =~ /^(!?).*\..*\..*\..*-.*\..*\..*\..*/ ) {
|
||||
$net =~ s/!// if my $invert = $1 ? '! ' : '';
|
||||
|
||||
iprange_match . "${invert}--src-range $net ";
|
||||
} elsif ( $net =~ /^!/ ) {
|
||||
$net =~ s/!//;
|
||||
"-d ! $net ";
|
||||
} else {
|
||||
$net eq ALLIPv4 ? '' : "-d $net ";
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Match original destination
|
||||
#
|
||||
sub match_orig_dest ( $ ) {
|
||||
my $net = $_[0];
|
||||
|
||||
return '' if $net eq ALLIPv4;
|
||||
|
||||
if ( $net =~ /^!/ ) {
|
||||
$net =~ s/!//;
|
||||
"-m conntrack --ctorigdst ! $net ";
|
||||
} else {
|
||||
$net eq ALLIPv4 ? '' : "-m conntrack --ctorigdst $net ";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Match Source IPSEC
|
||||
#
|
||||
sub match_ipsec_in( $$ ) {
|
||||
my ( $zone , $hostref ) = @_;
|
||||
my $match = '-m policy --dir in --pol ';
|
||||
my $zoneref = $zones{$zone};
|
||||
my $optionsref = $zoneref->{options};
|
||||
|
||||
if ( $zoneref->{type} eq 'ipsec4' ) {
|
||||
$match .= "ipsec $optionsref->{in_out}{ipsec}$optionsref->{in}{ipsec}";
|
||||
} elsif ( $capabilities{POLICY_MATCH} ) {
|
||||
$match .= "$hostref->{ipsec} $optionsref->{in_out}{ipsec}$optionsref->{in}{ipsec}";
|
||||
} else {
|
||||
'';
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Match Dest IPSEC
|
||||
#
|
||||
sub match_ipsec_out( $$ ) {
|
||||
my ( $zone , $hostref ) = @_;
|
||||
my $match = '-m policy --dir out --pol ';
|
||||
my $zoneref = $zones{$zone};
|
||||
my $optionsref = $zoneref->{options};
|
||||
|
||||
if ( $zoneref->{type} eq 'ipsec4' ) {
|
||||
$match .= "ipsec $optionsref->{in_out}{ipsec}$optionsref->{out}{ipsec}";
|
||||
} elsif ( $capabilities{POLICY_MATCH} ) {
|
||||
$match .= "$hostref->{ipsec} $optionsref->{in_out}{ipsec}$optionsref->{out}{ipsec}"
|
||||
} else {
|
||||
'';
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Generate a log message
|
||||
#
|
||||
sub log_rule_limit( $$$$$$$$ ) {
|
||||
my ($level, $chainref, $chain, $disposition, $limit, $tag, $command, $predicates ) = @_;
|
||||
|
||||
my $prefix;
|
||||
|
||||
$limit = $env{LOGLIMIT} unless $limit;
|
||||
|
||||
if ( $tag ) {
|
||||
if ( $config{LOGTAGONLY} ) {
|
||||
$chain = $tag;
|
||||
$tag = '';
|
||||
} else {
|
||||
$tag .= ' ';
|
||||
}
|
||||
} else {
|
||||
$tag = '' unless defined $tag;
|
||||
}
|
||||
|
||||
if ( $env{LOGRULENUMBERS} ) {
|
||||
$prefix = (sprintf $config{LOGFORMAT} , $chain , $chainref->{log}++, $disposition ) . $tag;
|
||||
} else {
|
||||
$prefix = (sprintf $config{LOGFORMAT} , $chain , $disposition) . $tag;
|
||||
}
|
||||
|
||||
if ( length $prefix > 29 ) {
|
||||
$prefix = substr $prefix, 0, 29;
|
||||
warning_message "Log Prefix shortened to \"$prefix\"";
|
||||
}
|
||||
|
||||
if ( $level eq 'ULOG' ) {
|
||||
$prefix = "-j ULOG $env{LOGPARMS} --ulog-prefix \"$prefix\" ";
|
||||
} else {
|
||||
$prefix = "-j LOG $env{LOGPARMS} --log-level $level --log-prefix \"$prefix\" ";
|
||||
}
|
||||
|
||||
if ( $command eq 'add' ) {
|
||||
add_rule ( $chainref, $predicates . $prefix );
|
||||
} else {
|
||||
insert_rule ( $chainref , 1 , $predicates . $prefix );
|
||||
}
|
||||
}
|
||||
|
||||
sub log_rule( $$$$ ) {
|
||||
my ( $level, $chainref, $disposition, $predicates ) = @_;
|
||||
|
||||
log_rule_limit $level, $chainref, $chainref->{name} , $disposition, $env{LOGLIMIT}, '', 'add', $predicates;
|
||||
}
|
||||
|
||||
#
|
||||
# This function provides a uniform way to generate rules (something the original Shorewall sorely needed).
|
||||
#
|
||||
sub expand_rule( $$$$$$$$$ )
|
||||
{
|
||||
my ($chainref , $rule, $source, $dest, $origdest, $target, $loglevel , $disposition, $exceptionrule ) = @_;
|
||||
my ($iiface, $diface, $inets, $dnets, $iexcl, $dexcl, $onets , $oexcl );
|
||||
|
||||
#
|
||||
# Isolate Source Interface, if any
|
||||
#
|
||||
if ( $source ) {
|
||||
if ( $source eq '-' ) {
|
||||
$source = '';
|
||||
} elsif ( $source =~ /^([^:]+):([^:]+)$/ ) {
|
||||
$iiface = $1;
|
||||
$inets = $2;
|
||||
} elsif ( $source =~ /\+|~|\..*\./ ) {
|
||||
$inets = $source;
|
||||
} else {
|
||||
$iiface = $source;
|
||||
}
|
||||
} else {
|
||||
$source = '';
|
||||
}
|
||||
#
|
||||
# Verify Inteface, if any
|
||||
#
|
||||
if ( $iiface ) {
|
||||
fatal_error "Unknown Interface ($iiface): \"$line\"" unless known_interface $iiface;
|
||||
$rule .= "-i $iiface ";
|
||||
}
|
||||
|
||||
#
|
||||
# Isolate Destination Interface, if any
|
||||
#
|
||||
if ( $dest ) {
|
||||
if ( $dest eq '-' ) {
|
||||
$dest = '';
|
||||
} elsif ( $dest =~ /^([^:]+):([^:]+)$/ ) {
|
||||
$diface = $1;
|
||||
$dnets = $2;
|
||||
} elsif ( $dest =~ /\+|~|\..*\./ ) {
|
||||
$dnets = $dest;
|
||||
} else {
|
||||
$diface = $dest;
|
||||
}
|
||||
} else {
|
||||
$dest = '';
|
||||
}
|
||||
#
|
||||
# Verify Destination Interface, if any
|
||||
#
|
||||
if ( $diface ) {
|
||||
fatal_error "Unknown Interface ($diface) in rule \"$line\"" unless known_interface $diface;
|
||||
$rule .= "-o $diface ";
|
||||
}
|
||||
|
||||
#
|
||||
# Handle Log Level
|
||||
#
|
||||
my $logtag;
|
||||
|
||||
if ( $loglevel ) {
|
||||
( $loglevel, $logtag ) = split /:/, $loglevel;
|
||||
|
||||
if ( $loglevel =~ /^none!?$/i ) {
|
||||
return 1 if $disposition eq 'LOG';
|
||||
$loglevel = $logtag = '';
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Determine if there is Source Exclusion
|
||||
#
|
||||
|
||||
if ( $inets ) {
|
||||
if ( $inets =~ /^([^!]+)?!([^!]+)$/ ) {
|
||||
$inets = $1;
|
||||
$iexcl = $2;
|
||||
} else {
|
||||
$iexcl = '';
|
||||
}
|
||||
|
||||
if ( ! $inets ) {
|
||||
my @iexcl = split /,/, $iexcl;
|
||||
if ( @iexcl == 1 ) {
|
||||
$rule .= match_source_net "!$iexcl ";
|
||||
$iexcl = '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$iexcl = '';
|
||||
}
|
||||
|
||||
#
|
||||
# Determine if there is Destination Exclusion
|
||||
# $dexcl = '';
|
||||
|
||||
|
||||
if ( $dnets ) {
|
||||
if ( $dnets =~ /^([^!]+)?!([^!]+)$/ ) {
|
||||
$dnets = $1;
|
||||
$dexcl = $2;
|
||||
} else {
|
||||
$dexcl = '';
|
||||
}
|
||||
|
||||
if ( ! $dnets ) {
|
||||
my @dexcl = split /,/, $dexcl;
|
||||
if ( @dexcl == 1 ) {
|
||||
$rule .= match_dest_net "!$dexcl ";
|
||||
$dexcl = '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$dexcl = '';
|
||||
}
|
||||
|
||||
if ( $origdest ) {
|
||||
if ( $origdest =~ /^([^!]+)?!([^!]+)$/ ) {
|
||||
$onets = $1;
|
||||
$oexcl = $2;
|
||||
} else {
|
||||
$oexcl = '';
|
||||
}
|
||||
|
||||
if ( ! $onets ) {
|
||||
my @oexcl = split /,/, $oexcl;
|
||||
if ( @oexcl == 1 ) {
|
||||
$rule .= "-m conntrack --ctorigdst ! $oexcl ";
|
||||
$oexcl = '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$oexcl = '';
|
||||
}
|
||||
|
||||
$inets = ALLIPv4 unless $inets;
|
||||
$dnets = ALLIPv4 unless $dnets;
|
||||
$onets = ALLIPv4 unless $onets;
|
||||
|
||||
if ( $iexcl || $dexcl || $oexcl ) {
|
||||
#
|
||||
# We have non-trivial exclusion -- need to create an exclusion chain
|
||||
#
|
||||
my $echain = newexclusionchain;
|
||||
|
||||
#
|
||||
# Use the current rule and sent all possible matches to the exclusion chain
|
||||
#
|
||||
for my $onet ( split /,/, $onets ) {
|
||||
$onet = match_orig_dest $onet;
|
||||
for my $inet ( split /,/, $inets ) {
|
||||
$inet = match_source_net $inet;
|
||||
for my $dnet ( split /,/, $dnets ) {
|
||||
add_rule $chainref, $rule . $inet . ( match_dest_net $dnet ) . $onet . "-j $echain";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# The final rule in the exclusion chain will not qualify the source or destination
|
||||
#
|
||||
$inets = ALLIPv4;
|
||||
$dnets = ALLIPv4;
|
||||
|
||||
#
|
||||
# Create the Exclusion Chain
|
||||
#
|
||||
my $echainref = new_chain $chainref->{table}, $echain;
|
||||
|
||||
#
|
||||
# Generate RETURNs for each exclusion
|
||||
#
|
||||
for my $net ( split ',', $iexcl ) {
|
||||
add_rule $echainref, ( match_source_net $net ) . '-j RETURN';
|
||||
}
|
||||
|
||||
for my $net ( split ',', $dexcl ) {
|
||||
add_rule $echainref, ( match_dest_net $net ) . '-j RETURN';
|
||||
}
|
||||
|
||||
for my $net ( split ',', $oexcl ) {
|
||||
add_rule $echainref, ( match_orig_dest $net ) . '-j RETURN';
|
||||
}
|
||||
|
||||
#
|
||||
# Log rule
|
||||
#
|
||||
log_rule_limit $loglevel , $echainref , $chainref->{name}, $disposition , '', $logtag , 'add' , '' if $loglevel;
|
||||
#
|
||||
# Generate Final Rule
|
||||
#
|
||||
add_rule $echainref, $exceptionrule . $target unless $disposition eq 'LOG';
|
||||
|
||||
} else {
|
||||
#
|
||||
# No exclusions
|
||||
#
|
||||
for my $onet ( split /,/, $onets ) {
|
||||
$onet = match_orig_dest $onet;
|
||||
for my $inet ( split /,/, $inets ) {
|
||||
$inet = match_source_net $inet;
|
||||
for my $dnet ( split /,/, $dnets ) {
|
||||
log_rule_limit $loglevel , $chainref , $chainref->{name}, $disposition , '' , $logtag , 'add' , $rule . $inet . match_dest_net( $dnet ) . $onet if $loglevel;
|
||||
add_rule $chainref, $rule . $inet . match_dest_net( $dnet ) . $onet . $target unless $disposition eq 'LOG';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# If the destination chain exists, then at the end of the source chain add a jump to the destination.
|
||||
#
|
||||
sub addnatjump( $$$ ) {
|
||||
my ( $source , $dest, $predicates ) = @_;
|
||||
|
||||
my $destref = $nat_table->{$dest} || {};
|
||||
|
||||
if ( $destref->{referenced} ) {
|
||||
add_rule $nat_table->{$source} , $predicates . "-j $dest";
|
||||
} else {
|
||||
clearrule;
|
||||
}
|
||||
}
|
||||
#
|
||||
# If the destination chain exists, then at the position in the source chain given by $$countref, add a jump to the destination.
|
||||
#
|
||||
sub insertnatjump( $$$$ ) {
|
||||
my ( $source, $dest, $countref, $predicates ) = @_;
|
||||
|
||||
my $destref = $nat_table->{$dest} || {};
|
||||
|
||||
if ( $destref->{referenced} ) {
|
||||
insert_rule $nat_table->{$source} , ($$countref)++, $predicates . "-j $dest";
|
||||
} else {
|
||||
clearrule;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -1,11 +1,17 @@
|
||||
package Shorewall::Common;
|
||||
require Exporter;
|
||||
use File::Basename;
|
||||
use File::Temp qw/ tempfile tempdir /;
|
||||
|
||||
use strict;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(warning_message
|
||||
our @EXPORT = qw(ALLIPv4
|
||||
|
||||
warning_message
|
||||
fatal_error
|
||||
create_temp_object
|
||||
finalize_object
|
||||
emit
|
||||
emit_unindented
|
||||
save_progress_message
|
||||
@ -17,15 +23,28 @@ our @EXPORT = qw(warning_message
|
||||
pop_indent
|
||||
copy
|
||||
copy1
|
||||
|
||||
|
||||
@allipv4
|
||||
@rfc1918_networks
|
||||
$line);
|
||||
our @EXPORT_OK = ();
|
||||
our @VERSION = 1.00;
|
||||
|
||||
#
|
||||
# Some IPv4 useful stuff
|
||||
#
|
||||
our @allipv4 = ( '0.0.0.0/0' );
|
||||
|
||||
use constant { ALLIPv4 => '0.0.0.0/0' };
|
||||
|
||||
our @rfc1918_networks = ( "10.0.0.0/24", "172.16.0.0/12", "192.168.0.0/16" );
|
||||
|
||||
our $line = ''; # Current config file line
|
||||
my $object = 0; # Object file Handle Reference
|
||||
my $lastlineblank = 0; # Avoid extra blank lines in the output
|
||||
my $object = 0; # Object file Handle Reference
|
||||
my $lastlineblank = 0; # Avoid extra blank lines in the output
|
||||
my $indent = '';
|
||||
my ( $dir, $file ); # Object's Directory and File
|
||||
my $tempfile; # Temporary File Name
|
||||
|
||||
#
|
||||
# Issue a Warning Message
|
||||
@ -46,16 +65,29 @@ sub fatal_error
|
||||
die;
|
||||
}
|
||||
|
||||
sub create_temp_object() {
|
||||
my $tempfile;
|
||||
sub create_temp_object( $ ) {
|
||||
my $objectfile = $_[0];
|
||||
my $suffix;
|
||||
|
||||
eval {
|
||||
( $file, $dir, $suffix ) = fileparse( $objectfile );
|
||||
fatal_error "Directory $dir does not exist" unless -d $dir;
|
||||
fatal_error "$dir is a Symbolic Link" if -l $dir;
|
||||
fatal_error "$objectfile is a Directory" if -d $objectfile;
|
||||
fatal_error "$dir is a Symbolic Link" if -l $objectfile;
|
||||
fatal_error "$objectfile exists and is not a compiled script" if -e _ && ! -x _;
|
||||
( $object, $tempfile ) = tempfile ( 'tempfileXXXX' , DIR => $dir );
|
||||
};
|
||||
|
||||
fatal_error "$@" if $@;
|
||||
|
||||
return $tempfile;
|
||||
$file = "$dir/$file.$suffix";
|
||||
|
||||
}
|
||||
|
||||
sub finalize_object() {
|
||||
rename $tempfile, $file;
|
||||
chmod 0700, $file;
|
||||
}
|
||||
|
||||
#
|
||||
|
@ -5,6 +5,8 @@ use Shorewall::Config;
|
||||
use Shorewall::Zones;
|
||||
use Shorewall::Interfaces;
|
||||
|
||||
use strict;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw( validate_hosts_file );
|
||||
our @EXPORT_OK = ();
|
||||
|
@ -4,8 +4,10 @@ use Shorewall::Common;
|
||||
use Shorewall::Config;
|
||||
use Shorewall::Zones;
|
||||
|
||||
use strict;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw( validate_interfaces_file dump_interface_info known_interface @interfaces %interfaces );
|
||||
our @EXPORT = qw( add_group_to_zone validate_interfaces_file dump_interface_info known_interface @interfaces );
|
||||
our @EXPORT_OK = ();
|
||||
our @VERSION = 1.00;
|
||||
|
||||
@ -25,6 +27,60 @@ our @VERSION = 1.00;
|
||||
our @interfaces;
|
||||
our %interfaces;
|
||||
|
||||
sub add_group_to_zone($$$$$)
|
||||
{
|
||||
my ($zone, $type, $interface, $networks, $options) = @_;
|
||||
my $typeref;
|
||||
my $interfaceref;
|
||||
my $arrayref;
|
||||
my $zoneref = $zones{$zone};
|
||||
my $zonetype = $zoneref->{type};
|
||||
my $ifacezone = $interfaces{$interface}{zone};
|
||||
|
||||
$zoneref->{interfaces}{$interface} = 1;
|
||||
|
||||
my @newnetworks;
|
||||
my @exclusions;
|
||||
my $new = \@newnetworks;
|
||||
my $switched = 0;
|
||||
|
||||
$ifacezone = '' unless defined $ifacezone;
|
||||
|
||||
for my $host ( @$networks ) {
|
||||
if ( $host =~ /^!.*/ ) {
|
||||
fatal_error "Invalid host group: @$networks" if $switched;
|
||||
$switched = 1;
|
||||
$new = \@exclusions;
|
||||
}
|
||||
|
||||
unless ( $switched ) {
|
||||
if ( $type eq $zonetype ) {
|
||||
fatal_error "Duplicate Host Group ($interface:$host) in zone $zone" if $ifacezone eq $zone;
|
||||
$ifacezone = $zone if $host eq ALLIPv4;
|
||||
}
|
||||
}
|
||||
|
||||
push @$new, $switched ? "$interface:$host" : $host;
|
||||
}
|
||||
|
||||
$zoneref->{options}{in_out}{routeback} = 1 if $options->{routeback};
|
||||
|
||||
$typeref = ( $zoneref->{hosts} || ( $zoneref->{hosts} = {} ) );
|
||||
$interfaceref = ( $typeref->{$type} || ( $interfaceref = $typeref->{$type} = {} ) );
|
||||
$arrayref = ( $interfaceref->{$interface} || ( $interfaceref->{$interface} = [] ) );
|
||||
|
||||
$zoneref->{options}{complex} = 1 if @$arrayref || ( @newnetworks > 1 );
|
||||
|
||||
my %h;
|
||||
|
||||
$h{options} = $options;
|
||||
$h{hosts} = \@newnetworks;
|
||||
$h{ipsec} = $type eq 'ipsec' ? 'ipsec' : 'none';
|
||||
|
||||
push @{$zoneref->{exclusions}}, @exclusions;
|
||||
push @{$arrayref}, \%h;
|
||||
}
|
||||
|
||||
#
|
||||
# Parse the interfaces file.
|
||||
#
|
||||
|
@ -1,6 +1,14 @@
|
||||
package Shorewall::Rules;
|
||||
require Exporter;
|
||||
|
||||
use strict;
|
||||
|
||||
use Shorewall::Common;
|
||||
use Shorewall::Config;
|
||||
use Shorewall::Chains;
|
||||
use Shorewall::Zones;
|
||||
use Shorewall::Interfaces;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw( STANDARD
|
||||
NATRULE
|
||||
@ -11,54 +19,612 @@ our @EXPORT = qw( STANDARD
|
||||
ACTION
|
||||
MACRO
|
||||
LOGRULE
|
||||
|
||||
|
||||
do_proto
|
||||
mac_match
|
||||
numeric_value
|
||||
verify_mark
|
||||
verify_small_mark
|
||||
validate_mark
|
||||
do_test
|
||||
do_ratelimit
|
||||
do_user
|
||||
iprange_match
|
||||
match_source_net
|
||||
match_dest_net
|
||||
match_orig_dest
|
||||
match_ipsec_in
|
||||
match_ipsec_out
|
||||
log_rule_limit
|
||||
log_rule
|
||||
expand_rule
|
||||
addnatjump
|
||||
insertnatjump
|
||||
|
||||
%targets
|
||||
);
|
||||
our @EXPORT_OK = ();
|
||||
our @VERSION = 1.00;
|
||||
|
||||
#
|
||||
# Target Table. Each entry maps a target to a set of flags defined as follows.
|
||||
|
||||
#
|
||||
use constant { STANDARD => 1, #defined by Netfilter
|
||||
NATRULE => 2, #Involved NAT
|
||||
BUILTIN => 4, #A built-in action
|
||||
NONAT => 8, #'NONAT' or 'ACCEPT+'
|
||||
NATONLY => 16, #'DNAT-' or 'REDIRECT-'
|
||||
REDIRECT => 32, #'REDIRECT'
|
||||
ACTION => 64, #An action
|
||||
MACRO => 128, #A Macro
|
||||
LOGRULE => 256, #'LOG'
|
||||
};
|
||||
# Handle parsing of PROTO, DEST PORT(S) , SOURCE PORTS(S). Returns the appropriate match string.
|
||||
#
|
||||
# As new targets (Actions and Macros) are discovered, they are added to the table
|
||||
sub do_proto( $$$ )
|
||||
{
|
||||
my ($proto, $ports, $sports ) = @_;
|
||||
|
||||
my $output = '';
|
||||
|
||||
$proto = '' unless defined $proto;
|
||||
$ports = '' unless defined $ports;
|
||||
$sports = '' unless defined $sports;
|
||||
|
||||
$proto = '' if $proto eq '-';
|
||||
$ports = '' if $ports eq '-';
|
||||
$sports = '' if $sports eq '-';
|
||||
|
||||
if ( $proto ) {
|
||||
if ( $proto =~ /^(tcp|udp|6|17)$/i ) {
|
||||
$output = "-p $proto ";
|
||||
if ( $ports ) {
|
||||
my @ports = split /,/, $ports;
|
||||
my $count = @ports;
|
||||
|
||||
if ( $count > 1 ) {
|
||||
fatal_error "Port list requires Multiport support in your kernel/iptables: $ports" unless $capabilities{MULTIPORT};
|
||||
fatal_error "Port range in a list requires Extended Multiport Support in your kernel/iptables: $ports" unless $capabilities{XMULTIPORT};
|
||||
|
||||
for my $port ( @ports ) {
|
||||
$count++ if $port =~ /:/;
|
||||
}
|
||||
|
||||
fatal_error "Too many entries in port list: $ports" if $count > 15;
|
||||
|
||||
$output .= "-m multiport --dports $ports ";
|
||||
} else {
|
||||
$output .= "--dport $ports ";
|
||||
}
|
||||
}
|
||||
|
||||
if ( $sports ) {
|
||||
my @ports = split /,/, $sports;
|
||||
my $count = @ports;
|
||||
|
||||
if ( $count > 1 ) {
|
||||
fatal_error "Port list requires Multiport support in your kernel/iptables: $sports" unless $capabilities{MULTIPORT};
|
||||
fatal_error "Port range in a list requires Extended Multiport Support in your kernel/iptables: $sports" unless $capabilities{XMULTIPORT};
|
||||
|
||||
for my $port ( @ports ) {
|
||||
$count++ if $port =~ /:/;
|
||||
}
|
||||
|
||||
fatal_error "Too many entries in port list: $sports" if $count > 15;
|
||||
|
||||
$output .= "-m multiport --sports $sports ";
|
||||
} else {
|
||||
$output .= "--sport $sports ";
|
||||
}
|
||||
}
|
||||
} elsif ( $proto =~ /^(icmp|1)$/i ) {
|
||||
$output .= "-p icmp --icmp-type $ports " if $ports;
|
||||
fatal_error 'SOURCE PORT(S) not permitted with ICMP' if $sports;
|
||||
} elsif ( $proto =~ /^(ipp2p(:(tcp|udp|all)))?$/i ) {
|
||||
fatal_error 'PROTO = ipp2p requires IPP2P match support in your kernel/iptables' unless $capabilities{IPP2P};
|
||||
$proto = $2 ? $3 : 'tcp';
|
||||
$ports = 'ipp2p' unless $ports;
|
||||
$output .= "-p $proto -m ipp2p --$ports ";
|
||||
}
|
||||
} elsif ( $ports || $sports ) {
|
||||
fatal_error "SOURCE/DEST PORT(S) not allowed without PROTO, rule \"$line\""
|
||||
}
|
||||
|
||||
$output;
|
||||
}
|
||||
|
||||
sub mac_match( $ ) {
|
||||
my $mac = $_[0];
|
||||
|
||||
$mac =~ s/^(!?)~//;
|
||||
$mac =~ s/^!// if my $invert = $1 ? '! ' : '';
|
||||
$mac =~ s/-/:/g;
|
||||
|
||||
"--match mac --mac-source ${invert}$mac ";
|
||||
}
|
||||
|
||||
#
|
||||
our %targets = ('ACCEPT' => STANDARD,
|
||||
'ACCEPT+' => STANDARD + NONAT,
|
||||
'ACCEPT!' => STANDARD,
|
||||
'NONAT' => STANDARD + NONAT,
|
||||
'DROP' => STANDARD,
|
||||
'DROP!' => STANDARD,
|
||||
'REJECT' => STANDARD,
|
||||
'REJECT!' => STANDARD,
|
||||
'DNAT' => NATRULE,
|
||||
'DNAT-' => NATRULE + NATONLY,
|
||||
'REDIRECT' => NATRULE + REDIRECT,
|
||||
'REDIRECT-' => NATRULE + REDIRECT + NATONLY,
|
||||
'LOG' => STANDARD + LOGRULE,
|
||||
'CONTINUE' => STANDARD,
|
||||
'QUEUE' => STANDARD,
|
||||
'SAME' => NATRULE,
|
||||
'SAME-' => NATRULE + NATONLY,
|
||||
'dropBcast' => BUILTIN + ACTION,
|
||||
'allowBcast' => BUILTIN + ACTION,
|
||||
'dropNotSyn' => BUILTIN + ACTION,
|
||||
'rejNotSyn' => BUILTIN + ACTION,
|
||||
'dropInvalid' => BUILTIN + ACTION,
|
||||
'allowInvalid' => BUILTIN + ACTION,
|
||||
'allowinUPnP' => BUILTIN + ACTION,
|
||||
'forwardUPnP' => BUILTIN + ACTION,
|
||||
'Limit' => BUILTIN + ACTION,
|
||||
);
|
||||
# Convert value to decimal number
|
||||
#
|
||||
sub numeric_value ( $ ) {
|
||||
my $mark = $_[0];
|
||||
$mark =~ /^0x/ ? hex $mark : $mark =~ /^0/ ? oct $mark : $mark;
|
||||
}
|
||||
|
||||
#
|
||||
# Mark validatation functions
|
||||
#
|
||||
sub verify_mark( $ ) {
|
||||
my $mark = $_[0];
|
||||
my $limit = $config{HIGH_ROUTE_MARKS} ? 0xFFFF : 0xFF;
|
||||
|
||||
fatal_error "Invalid Mark or Mask value: $mark"
|
||||
unless "\L$mark" =~ /^(0x[a-f0-9]+|0[0-7]*|[0-9]*)$/ && numeric_value( $mark ) <= $limit;
|
||||
}
|
||||
|
||||
sub verify_small_mark( $ ) {
|
||||
verify_mark ( (my $mark) = $_[0] );
|
||||
fatal_error "Mark value ($mark) too large" if numeric_value( $mark ) > 0xFF;
|
||||
}
|
||||
|
||||
sub validate_mark( $ ) {
|
||||
for ( split '/', $_[0] ) {
|
||||
verify_mark $_;
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Generate an appropriate -m [conn]mark match string for the contents of a MARK column
|
||||
#
|
||||
|
||||
sub do_test ( $$ )
|
||||
{
|
||||
my ($testval, $mask) = @_;
|
||||
|
||||
return '' unless $testval and $testval ne '-';
|
||||
|
||||
my $invert = $testval =~ s/^!// ? '! ' : '';
|
||||
my $match = $testval =~ s/:C$// ? '-m connmark ' : '-m mark ';
|
||||
|
||||
$testval .= '/0xFF' unless ( $testval =~ '/' );
|
||||
|
||||
"${invert}$match $testval ";
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Create a "-m limit" match for the passed LIMIT/BURST
|
||||
#
|
||||
sub do_ratelimit( $ ) {
|
||||
my $rate = $_[0];
|
||||
|
||||
return '' unless $rate and $rate ne '-';
|
||||
|
||||
if ( $rate =~ /^([^:]+):([^:]+)$/ ) {
|
||||
"-m limit --limit $1 --limit-burst $2 ";
|
||||
} else {
|
||||
"-m limit --limit $rate ";
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Create a "-m owner" match for the passed USER/GROUP
|
||||
#
|
||||
sub do_user( $ ) {
|
||||
my $user = $_[0];
|
||||
my $rule = ' -m owner';
|
||||
|
||||
return '' unless $user and $user ne '-';
|
||||
|
||||
if ( $user =~ /^(!)?(.*)\+(.*)$/ ) {
|
||||
$rule .= "! --cmd-owner $2 " if $2;
|
||||
$user = "!$1";
|
||||
} elsif ( $user =~ /^(.*)\+(.*)$/ ) {
|
||||
$rule .= "--cmd-owner $2 " if $2;
|
||||
$user = $1;
|
||||
}
|
||||
|
||||
if ( $user =~ /^!(.*):(.*)$/ ) {
|
||||
$rule .= "! --uid-owner $1 " if $1;
|
||||
$rule .= "! --gid-owner $2 " if $2;
|
||||
} elsif ( $user =~ /^(.*):(.*)$/ ) {
|
||||
$rule .= "--uid-owner $1 " if $1;
|
||||
$rule .= "--gid-owner $2 " if $2;
|
||||
} elsif ( $user =~ /^!/ ) {
|
||||
$rule .= "! --uid-owner $user ";
|
||||
} else {
|
||||
$rule .= "--uid-owner $user ";
|
||||
}
|
||||
|
||||
$rule;
|
||||
}
|
||||
|
||||
#
|
||||
# Avoid generating a second '-m iprange' in a single rule.
|
||||
#
|
||||
sub iprange_match() {
|
||||
my $match = '';
|
||||
unless ( $iprangematch ) {
|
||||
$match = '-m iprange ';
|
||||
$iprangematch = 1;
|
||||
}
|
||||
|
||||
$match;
|
||||
}
|
||||
|
||||
#
|
||||
# Match a Source. Currently only handles IP addresses and ranges
|
||||
#
|
||||
sub match_source_net( $ ) {
|
||||
my $net = $_[0];
|
||||
|
||||
if ( $net =~ /^(!?).*\..*\..*\..*-.*\..*\..*\..*/ ) {
|
||||
$net =~ s/!// if my $invert = $1 ? '! ' : '';
|
||||
|
||||
iprange_match . "${invert}--src-range $net ";
|
||||
} elsif ( $net =~ /^(!?)~(.*)$/ ) {
|
||||
( $net = $2 ) =~ s/-/:/g;
|
||||
"-m mac --mac-source $1 $net "
|
||||
} elsif ( $net =~ /^!/ ) {
|
||||
$net =~ s/!//;
|
||||
"-s ! $net ";
|
||||
} else {
|
||||
$net eq ALLIPv4 ? '' : "-s $net ";
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Match a Source. Currently only handles IP addresses and ranges
|
||||
#
|
||||
sub match_dest_net( $ ) {
|
||||
my $net = $_[0];
|
||||
|
||||
if ( $net =~ /^(!?).*\..*\..*\..*-.*\..*\..*\..*/ ) {
|
||||
$net =~ s/!// if my $invert = $1 ? '! ' : '';
|
||||
|
||||
iprange_match . "${invert}--src-range $net ";
|
||||
} elsif ( $net =~ /^!/ ) {
|
||||
$net =~ s/!//;
|
||||
"-d ! $net ";
|
||||
} else {
|
||||
$net eq ALLIPv4 ? '' : "-d $net ";
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Match original destination
|
||||
#
|
||||
sub match_orig_dest ( $ ) {
|
||||
my $net = $_[0];
|
||||
|
||||
return '' if $net eq ALLIPv4;
|
||||
|
||||
if ( $net =~ /^!/ ) {
|
||||
$net =~ s/!//;
|
||||
"-m conntrack --ctorigdst ! $net ";
|
||||
} else {
|
||||
$net eq ALLIPv4 ? '' : "-m conntrack --ctorigdst $net ";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Match Source IPSEC
|
||||
#
|
||||
sub match_ipsec_in( $$ ) {
|
||||
my ( $zone , $hostref ) = @_;
|
||||
my $match = '-m policy --dir in --pol ';
|
||||
my $zoneref = $zones{$zone};
|
||||
my $optionsref = $zoneref->{options};
|
||||
|
||||
if ( $zoneref->{type} eq 'ipsec4' ) {
|
||||
$match .= "ipsec $optionsref->{in_out}{ipsec}$optionsref->{in}{ipsec}";
|
||||
} elsif ( $capabilities{POLICY_MATCH} ) {
|
||||
$match .= "$hostref->{ipsec} $optionsref->{in_out}{ipsec}$optionsref->{in}{ipsec}";
|
||||
} else {
|
||||
'';
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Match Dest IPSEC
|
||||
#
|
||||
sub match_ipsec_out( $$ ) {
|
||||
my ( $zone , $hostref ) = @_;
|
||||
my $match = '-m policy --dir out --pol ';
|
||||
my $zoneref = $zones{$zone};
|
||||
my $optionsref = $zoneref->{options};
|
||||
|
||||
if ( $zoneref->{type} eq 'ipsec4' ) {
|
||||
$match .= "ipsec $optionsref->{in_out}{ipsec}$optionsref->{out}{ipsec}";
|
||||
} elsif ( $capabilities{POLICY_MATCH} ) {
|
||||
$match .= "$hostref->{ipsec} $optionsref->{in_out}{ipsec}$optionsref->{out}{ipsec}"
|
||||
} else {
|
||||
'';
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Generate a log message
|
||||
#
|
||||
sub log_rule_limit( $$$$$$$$ ) {
|
||||
my ($level, $chainref, $chain, $disposition, $limit, $tag, $command, $predicates ) = @_;
|
||||
|
||||
my $prefix;
|
||||
|
||||
$limit = $env{LOGLIMIT} unless $limit;
|
||||
|
||||
if ( $tag ) {
|
||||
if ( $config{LOGTAGONLY} ) {
|
||||
$chain = $tag;
|
||||
$tag = '';
|
||||
} else {
|
||||
$tag .= ' ';
|
||||
}
|
||||
} else {
|
||||
$tag = '' unless defined $tag;
|
||||
}
|
||||
|
||||
if ( $env{LOGRULENUMBERS} ) {
|
||||
$prefix = (sprintf $config{LOGFORMAT} , $chain , $chainref->{log}++, $disposition ) . $tag;
|
||||
} else {
|
||||
$prefix = (sprintf $config{LOGFORMAT} , $chain , $disposition) . $tag;
|
||||
}
|
||||
|
||||
if ( length $prefix > 29 ) {
|
||||
$prefix = substr $prefix, 0, 29;
|
||||
warning_message "Log Prefix shortened to \"$prefix\"";
|
||||
}
|
||||
|
||||
if ( $level eq 'ULOG' ) {
|
||||
$prefix = "-j ULOG $env{LOGPARMS} --ulog-prefix \"$prefix\" ";
|
||||
} else {
|
||||
$prefix = "-j LOG $env{LOGPARMS} --log-level $level --log-prefix \"$prefix\" ";
|
||||
}
|
||||
|
||||
if ( $command eq 'add' ) {
|
||||
add_rule ( $chainref, $predicates . $prefix );
|
||||
} else {
|
||||
insert_rule ( $chainref , 1 , $predicates . $prefix );
|
||||
}
|
||||
}
|
||||
|
||||
sub log_rule( $$$$ ) {
|
||||
my ( $level, $chainref, $disposition, $predicates ) = @_;
|
||||
|
||||
log_rule_limit $level, $chainref, $chainref->{name} , $disposition, $env{LOGLIMIT}, '', 'add', $predicates;
|
||||
}
|
||||
|
||||
#
|
||||
# This function provides a uniform way to generate rules (something the original Shorewall sorely needed).
|
||||
#
|
||||
sub expand_rule( $$$$$$$$$ )
|
||||
{
|
||||
my ($chainref , $rule, $source, $dest, $origdest, $target, $loglevel , $disposition, $exceptionrule ) = @_;
|
||||
my ($iiface, $diface, $inets, $dnets, $iexcl, $dexcl, $onets , $oexcl );
|
||||
|
||||
#
|
||||
# Isolate Source Interface, if any
|
||||
#
|
||||
if ( $source ) {
|
||||
if ( $source eq '-' ) {
|
||||
$source = '';
|
||||
} elsif ( $source =~ /^([^:]+):([^:]+)$/ ) {
|
||||
$iiface = $1;
|
||||
$inets = $2;
|
||||
} elsif ( $source =~ /\+|~|\..*\./ ) {
|
||||
$inets = $source;
|
||||
} else {
|
||||
$iiface = $source;
|
||||
}
|
||||
} else {
|
||||
$source = '';
|
||||
}
|
||||
#
|
||||
# Verify Inteface, if any
|
||||
#
|
||||
if ( $iiface ) {
|
||||
fatal_error "Unknown Interface ($iiface): \"$line\"" unless known_interface $iiface;
|
||||
$rule .= "-i $iiface ";
|
||||
}
|
||||
|
||||
#
|
||||
# Isolate Destination Interface, if any
|
||||
#
|
||||
if ( $dest ) {
|
||||
if ( $dest eq '-' ) {
|
||||
$dest = '';
|
||||
} elsif ( $dest =~ /^([^:]+):([^:]+)$/ ) {
|
||||
$diface = $1;
|
||||
$dnets = $2;
|
||||
} elsif ( $dest =~ /\+|~|\..*\./ ) {
|
||||
$dnets = $dest;
|
||||
} else {
|
||||
$diface = $dest;
|
||||
}
|
||||
} else {
|
||||
$dest = '';
|
||||
}
|
||||
#
|
||||
# Verify Destination Interface, if any
|
||||
#
|
||||
if ( $diface ) {
|
||||
fatal_error "Unknown Interface ($diface) in rule \"$line\"" unless known_interface $diface;
|
||||
$rule .= "-o $diface ";
|
||||
}
|
||||
|
||||
#
|
||||
# Handle Log Level
|
||||
#
|
||||
my $logtag;
|
||||
|
||||
if ( $loglevel ) {
|
||||
( $loglevel, $logtag ) = split /:/, $loglevel;
|
||||
|
||||
if ( $loglevel =~ /^none!?$/i ) {
|
||||
return 1 if $disposition eq 'LOG';
|
||||
$loglevel = $logtag = '';
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Determine if there is Source Exclusion
|
||||
#
|
||||
|
||||
if ( $inets ) {
|
||||
if ( $inets =~ /^([^!]+)?!([^!]+)$/ ) {
|
||||
$inets = $1;
|
||||
$iexcl = $2;
|
||||
} else {
|
||||
$iexcl = '';
|
||||
}
|
||||
|
||||
if ( ! $inets ) {
|
||||
my @iexcl = split /,/, $iexcl;
|
||||
if ( @iexcl == 1 ) {
|
||||
$rule .= match_source_net "!$iexcl ";
|
||||
$iexcl = '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$iexcl = '';
|
||||
}
|
||||
|
||||
#
|
||||
# Determine if there is Destination Exclusion
|
||||
# $dexcl = '';
|
||||
|
||||
|
||||
if ( $dnets ) {
|
||||
if ( $dnets =~ /^([^!]+)?!([^!]+)$/ ) {
|
||||
$dnets = $1;
|
||||
$dexcl = $2;
|
||||
} else {
|
||||
$dexcl = '';
|
||||
}
|
||||
|
||||
if ( ! $dnets ) {
|
||||
my @dexcl = split /,/, $dexcl;
|
||||
if ( @dexcl == 1 ) {
|
||||
$rule .= match_dest_net "!$dexcl ";
|
||||
$dexcl = '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$dexcl = '';
|
||||
}
|
||||
|
||||
if ( $origdest ) {
|
||||
if ( $origdest =~ /^([^!]+)?!([^!]+)$/ ) {
|
||||
$onets = $1;
|
||||
$oexcl = $2;
|
||||
} else {
|
||||
$oexcl = '';
|
||||
}
|
||||
|
||||
if ( ! $onets ) {
|
||||
my @oexcl = split /,/, $oexcl;
|
||||
if ( @oexcl == 1 ) {
|
||||
$rule .= "-m conntrack --ctorigdst ! $oexcl ";
|
||||
$oexcl = '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$oexcl = '';
|
||||
}
|
||||
|
||||
$inets = ALLIPv4 unless $inets;
|
||||
$dnets = ALLIPv4 unless $dnets;
|
||||
$onets = ALLIPv4 unless $onets;
|
||||
|
||||
if ( $iexcl || $dexcl || $oexcl ) {
|
||||
#
|
||||
# We have non-trivial exclusion -- need to create an exclusion chain
|
||||
#
|
||||
my $echain = newexclusionchain;
|
||||
|
||||
#
|
||||
# Use the current rule and sent all possible matches to the exclusion chain
|
||||
#
|
||||
for my $onet ( split /,/, $onets ) {
|
||||
$onet = match_orig_dest $onet;
|
||||
for my $inet ( split /,/, $inets ) {
|
||||
$inet = match_source_net $inet;
|
||||
for my $dnet ( split /,/, $dnets ) {
|
||||
add_rule $chainref, $rule . $inet . ( match_dest_net $dnet ) . $onet . "-j $echain";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# The final rule in the exclusion chain will not qualify the source or destination
|
||||
#
|
||||
$inets = ALLIPv4;
|
||||
$dnets = ALLIPv4;
|
||||
|
||||
#
|
||||
# Create the Exclusion Chain
|
||||
#
|
||||
my $echainref = new_chain $chainref->{table}, $echain;
|
||||
|
||||
#
|
||||
# Generate RETURNs for each exclusion
|
||||
#
|
||||
for my $net ( split ',', $iexcl ) {
|
||||
add_rule $echainref, ( match_source_net $net ) . '-j RETURN';
|
||||
}
|
||||
|
||||
for my $net ( split ',', $dexcl ) {
|
||||
add_rule $echainref, ( match_dest_net $net ) . '-j RETURN';
|
||||
}
|
||||
|
||||
for my $net ( split ',', $oexcl ) {
|
||||
add_rule $echainref, ( match_orig_dest $net ) . '-j RETURN';
|
||||
}
|
||||
|
||||
#
|
||||
# Log rule
|
||||
#
|
||||
log_rule_limit $loglevel , $echainref , $chainref->{name}, $disposition , '', $logtag , 'add' , '' if $loglevel;
|
||||
#
|
||||
# Generate Final Rule
|
||||
#
|
||||
add_rule $echainref, $exceptionrule . $target unless $disposition eq 'LOG';
|
||||
|
||||
} else {
|
||||
#
|
||||
# No exclusions
|
||||
#
|
||||
for my $onet ( split /,/, $onets ) {
|
||||
$onet = match_orig_dest $onet;
|
||||
for my $inet ( split /,/, $inets ) {
|
||||
$inet = match_source_net $inet;
|
||||
for my $dnet ( split /,/, $dnets ) {
|
||||
log_rule_limit $loglevel , $chainref , $chainref->{name}, $disposition , '' , $logtag , 'add' , $rule . $inet . match_dest_net( $dnet ) . $onet if $loglevel;
|
||||
add_rule $chainref, $rule . $inet . match_dest_net( $dnet ) . $onet . $target unless $disposition eq 'LOG';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# If the destination chain exists, then at the end of the source chain add a jump to the destination.
|
||||
#
|
||||
sub addnatjump( $$$ ) {
|
||||
my ( $source , $dest, $predicates ) = @_;
|
||||
|
||||
my $destref = $nat_table->{$dest} || {};
|
||||
|
||||
if ( $destref->{referenced} ) {
|
||||
add_rule $nat_table->{$source} , $predicates . "-j $dest";
|
||||
} else {
|
||||
clearrule;
|
||||
}
|
||||
}
|
||||
#
|
||||
# If the destination chain exists, then at the position in the source chain given by $$countref, add a jump to the destination.
|
||||
#
|
||||
sub insertnatjump( $$$$ ) {
|
||||
my ( $source, $dest, $countref, $predicates ) = @_;
|
||||
|
||||
my $destref = $nat_table->{$dest} || {};
|
||||
|
||||
if ( $destref->{referenced} ) {
|
||||
insert_rule $nat_table->{$source} , ($$countref)++, $predicates . "-j $dest";
|
||||
} else {
|
||||
clearrule;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -3,6 +3,8 @@ require Exporter;
|
||||
use Shorewall::Common;
|
||||
use Shorewall::Config;
|
||||
|
||||
use strict;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw( NOTHING
|
||||
NUMERIC
|
||||
@ -11,13 +13,14 @@ our @EXPORT = qw( NOTHING
|
||||
IPSECMODE
|
||||
|
||||
determine_zones
|
||||
add_group_to_zone
|
||||
dump_zone_info
|
||||
zone_report
|
||||
|
||||
@zones
|
||||
%zones
|
||||
$firewall_zone );
|
||||
$firewall_zone
|
||||
%interfaces );
|
||||
|
||||
our @EXPORT_OK = ();
|
||||
our @VERSION = 1.00;
|
||||
|
||||
@ -64,6 +67,21 @@ our @zones;
|
||||
our %zones;
|
||||
our $firewall_zone;
|
||||
|
||||
#
|
||||
# Interface Table.
|
||||
#
|
||||
# @interfaces lists the interface names in the order that they appear in the interfaces file.
|
||||
#
|
||||
# %interfaces { <interface1> => { root => <name without trailing '+'>
|
||||
# broadcast => [ <bcast1>, ... ]
|
||||
# options => { <option1> = <val1> ,
|
||||
# ...
|
||||
# }
|
||||
# zone => <zone name>
|
||||
# }
|
||||
#
|
||||
our %interfaces;
|
||||
|
||||
#
|
||||
# Parse the passed option list and return a reference to a hash as follows:
|
||||
#
|
||||
@ -226,60 +244,6 @@ sub determine_zones()
|
||||
}
|
||||
}
|
||||
|
||||
sub add_group_to_zone($$$$$)
|
||||
{
|
||||
my ($zone, $type, $interface, $networks, $options) = @_;
|
||||
my $typeref;
|
||||
my $interfaceref;
|
||||
my $arrayref;
|
||||
my $zoneref = $zones{$zone};
|
||||
my $zonetype = $zoneref->{type};
|
||||
my $ifacezone = $interfaces{$interface}{zone};
|
||||
|
||||
$zoneref->{interfaces}{$interface} = 1;
|
||||
|
||||
my @newnetworks;
|
||||
my @exclusions;
|
||||
my $new = \@newnetworks;
|
||||
my $switched = 0;
|
||||
|
||||
$ifacezone = '' unless defined $ifacezone;
|
||||
|
||||
for my $host ( @$networks ) {
|
||||
if ( $host =~ /^!.*/ ) {
|
||||
fatal_error "Invalid host group: @$networks" if $switched;
|
||||
$switched = 1;
|
||||
$new = \@exclusions;
|
||||
}
|
||||
|
||||
unless ( $switched ) {
|
||||
if ( $type eq $zonetype ) {
|
||||
fatal_error "Duplicate Host Group ($interface:$host) in zone $zone" if $ifacezone eq $zone;
|
||||
$ifacezone = $zone if $host eq ALLIPv4;
|
||||
}
|
||||
}
|
||||
|
||||
push @$new, $switched ? "$interface:$host" : $host;
|
||||
}
|
||||
|
||||
$zoneref->{options}{in_out}{routeback} = 1 if $options->{routeback};
|
||||
|
||||
$typeref = ( $zoneref->{hosts} || ( $zoneref->{hosts} = {} ) );
|
||||
$interfaceref = ( $typeref->{$type} || ( $interfaceref = $typeref->{$type} = {} ) );
|
||||
$arrayref = ( $interfaceref->{$interface} || ( $interfaceref->{$interface} = [] ) );
|
||||
|
||||
$zoneref->{options}{complex} = 1 if @$arrayref || ( @newnetworks > 1 );
|
||||
|
||||
my %h;
|
||||
|
||||
$h{options} = $options;
|
||||
$h{hosts} = \@newnetworks;
|
||||
$h{ipsec} = $type eq 'ipsec' ? 'ipsec' : 'none';
|
||||
|
||||
push @{$zoneref->{exclusions}}, @exclusions;
|
||||
push @{$arrayref}, \%h;
|
||||
}
|
||||
|
||||
#
|
||||
# Dump out all information about zones.
|
||||
#
|
||||
|
636
New/compiler.pl
636
New/compiler.pl
@ -10,45 +10,15 @@ use Shorewall::Chains;
|
||||
use Shorewall::Zones;
|
||||
use Shorewall::Interfaces;
|
||||
use Shorewall::Hosts;
|
||||
use Shorewall::Rules;
|
||||
|
||||
|
||||
my ( $command, $doing, $done ) = qw/ compile Compiling Compiled/; #describe the current command, it's present progressive, and it's completion.
|
||||
|
||||
my $tempfile = ''; # Temporary object file name
|
||||
|
||||
#
|
||||
# Contents of last COMMENT line.
|
||||
#
|
||||
my $comment = '';
|
||||
#
|
||||
# Used to sequence 'exclusion' chains with names 'excl0', 'excl1', ...
|
||||
#
|
||||
my $exclseq = 0;
|
||||
#
|
||||
# These are used to avoid duplicate '-m iprange' and '-m ipset' specifications in the same rule.
|
||||
#
|
||||
my $iprangematch = 0;
|
||||
my $ipsetmatch = 0;
|
||||
#
|
||||
# These get set to 1 as sections are encountered.
|
||||
#
|
||||
my %sections = ( ESTABLISHED => 0,
|
||||
RELATED => 0,
|
||||
NEW => 0
|
||||
);
|
||||
#
|
||||
# Set to one if we find a SECTION
|
||||
#
|
||||
my $sectioned = 0;
|
||||
#
|
||||
# Some IPv4 useful stuff
|
||||
#
|
||||
my @allipv4 = ( '0.0.0.0/0' );
|
||||
|
||||
use constant { ALLIPv4 => '0.0.0.0/0' };
|
||||
|
||||
my @rfc1918_networks = ( "10.0.0.0/24", "172.16.0.0/12", "192.168.0.0/16" );
|
||||
|
||||
# Action Table
|
||||
#
|
||||
@ -488,558 +458,6 @@ sub validate_policy()
|
||||
close POLICY;
|
||||
}
|
||||
|
||||
#
|
||||
# Handle parsing of PROTO, DEST PORT(S) , SOURCE PORTS(S). Returns the appropriate match string.
|
||||
#
|
||||
sub do_proto( $$$ )
|
||||
{
|
||||
my ($proto, $ports, $sports ) = @_;
|
||||
|
||||
my $output = '';
|
||||
|
||||
$proto = '' unless defined $proto;
|
||||
$ports = '' unless defined $ports;
|
||||
$sports = '' unless defined $sports;
|
||||
|
||||
$proto = '' if $proto eq '-';
|
||||
$ports = '' if $ports eq '-';
|
||||
$sports = '' if $sports eq '-';
|
||||
|
||||
if ( $proto ) {
|
||||
if ( $proto =~ /^(tcp|udp|6|17)$/i ) {
|
||||
$output = "-p $proto ";
|
||||
if ( $ports ) {
|
||||
my @ports = split /,/, $ports;
|
||||
my $count = @ports;
|
||||
|
||||
if ( $count > 1 ) {
|
||||
fatal_error "Port list requires Multiport support in your kernel/iptables: $ports" unless $capabilities{MULTIPORT};
|
||||
fatal_error "Port range in a list requires Extended Multiport Support in your kernel/iptables: $ports" unless $capabilities{XMULTIPORT};
|
||||
|
||||
for my $port ( @ports ) {
|
||||
$count++ if $port =~ /:/;
|
||||
}
|
||||
|
||||
fatal_error "Too many entries in port list: $ports" if $count > 15;
|
||||
|
||||
$output .= "-m multiport --dports $ports ";
|
||||
} else {
|
||||
$output .= "--dport $ports ";
|
||||
}
|
||||
}
|
||||
|
||||
if ( $sports ) {
|
||||
my @ports = split /,/, $sports;
|
||||
my $count = @ports;
|
||||
|
||||
if ( $count > 1 ) {
|
||||
fatal_error "Port list requires Multiport support in your kernel/iptables: $sports" unless $capabilities{MULTIPORT};
|
||||
fatal_error "Port range in a list requires Extended Multiport Support in your kernel/iptables: $sports" unless $capabilities{XMULTIPORT};
|
||||
|
||||
for my $port ( @ports ) {
|
||||
$count++ if $port =~ /:/;
|
||||
}
|
||||
|
||||
fatal_error "Too many entries in port list: $sports" if $count > 15;
|
||||
|
||||
$output .= "-m multiport --sports $sports ";
|
||||
} else {
|
||||
$output .= "--sport $sports ";
|
||||
}
|
||||
}
|
||||
} elsif ( $proto =~ /^(icmp|1)$/i ) {
|
||||
$output .= "-p icmp --icmp-type $ports " if $ports;
|
||||
fatal_error 'SOURCE PORT(S) not permitted with ICMP' if $sports;
|
||||
} elsif ( $proto =~ /^(ipp2p(:(tcp|udp|all)))?$/i ) {
|
||||
fatal_error 'PROTO = ipp2p requires IPP2P match support in your kernel/iptables' unless $capabilities{IPP2P};
|
||||
$proto = $2 ? $3 : 'tcp';
|
||||
$ports = 'ipp2p' unless $ports;
|
||||
$output .= "-p $proto -m ipp2p --$ports ";
|
||||
}
|
||||
} elsif ( $ports || $sports ) {
|
||||
fatal_error "SOURCE/DEST PORT(S) not allowed without PROTO, rule \"$line\""
|
||||
}
|
||||
|
||||
$output;
|
||||
}
|
||||
|
||||
sub mac_match( $ ) {
|
||||
my $mac = $_[0];
|
||||
|
||||
$mac =~ s/^(!?)~//;
|
||||
$mac =~ s/^!// if my $invert = $1 ? '! ' : '';
|
||||
$mac =~ s/-/:/g;
|
||||
|
||||
"--match mac --mac-source ${invert}$mac ";
|
||||
}
|
||||
|
||||
#
|
||||
# Convert value to decimal number
|
||||
#
|
||||
sub numeric_value ( $ ) {
|
||||
my $mark = $_[0];
|
||||
$mark =~ /^0x/ ? hex $mark : $mark =~ /^0/ ? oct $mark : $mark;
|
||||
}
|
||||
|
||||
#
|
||||
# Mark validatation functions
|
||||
#
|
||||
sub verify_mark( $ ) {
|
||||
my $mark = $_[0];
|
||||
my $limit = $config{HIGH_ROUTE_MARKS} ? 0xFFFF : 0xFF;
|
||||
|
||||
fatal_error "Invalid Mark or Mask value: $mark"
|
||||
unless "\L$mark" =~ /^(0x[a-f0-9]+|0[0-7]*|[0-9]*)$/ && numeric_value( $mark ) <= $limit;
|
||||
}
|
||||
|
||||
sub verify_small_mark( $ ) {
|
||||
verify_mark ( (my $mark) = $_[0] );
|
||||
fatal_error "Mark value ($mark) too large" if numeric_value( $mark ) > 0xFF;
|
||||
}
|
||||
|
||||
sub validate_mark( $ ) {
|
||||
for ( split '/', $_[0] ) {
|
||||
verify_mark $_;
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Generate an appropriate -m [conn]mark match string for the contents of a MARK column
|
||||
#
|
||||
|
||||
sub do_test ( $$ )
|
||||
{
|
||||
my ($testval, $mask) = @_;
|
||||
|
||||
return '' unless $testval and $testval ne '-';
|
||||
|
||||
my $invert = $testval =~ s/^!// ? '! ' : '';
|
||||
my $match = $testval =~ s/:C$// ? '-m connmark ' : '-m mark ';
|
||||
|
||||
$testval .= '/0xFF' unless ( $testval =~ '/' );
|
||||
|
||||
"${invert}$match $testval ";
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Create a "-m limit" match for the passed LIMIT/BURST
|
||||
#
|
||||
sub do_ratelimit( $ ) {
|
||||
my $rate = $_[0];
|
||||
|
||||
return '' unless $rate and $rate ne '-';
|
||||
|
||||
if ( $rate =~ /^([^:]+):([^:]+)$/ ) {
|
||||
"-m limit --limit $1 --limit-burst $2 ";
|
||||
} else {
|
||||
"-m limit --limit $rate ";
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Create a "-m owner" match for the passed USER/GROUP
|
||||
#
|
||||
sub do_user( $ ) {
|
||||
my $user = $_[0];
|
||||
my $rule = ' -m owner';
|
||||
|
||||
return '' unless $user and $user ne '-';
|
||||
|
||||
if ( $user =~ /^(!)?(.*)\+(.*)$/ ) {
|
||||
$rule .= "! --cmd-owner $2 " if $2;
|
||||
$user = "!$1";
|
||||
} elsif ( $user =~ /^(.*)\+(.*)$/ ) {
|
||||
$rule .= "--cmd-owner $2 " if $2;
|
||||
$user = $1;
|
||||
}
|
||||
|
||||
if ( $user =~ /^!(.*):(.*)$/ ) {
|
||||
$rule .= "! --uid-owner $1 " if $1;
|
||||
$rule .= "! --gid-owner $2 " if $2;
|
||||
} elsif ( $user =~ /^(.*):(.*)$/ ) {
|
||||
$rule .= "--uid-owner $1 " if $1;
|
||||
$rule .= "--gid-owner $2 " if $2;
|
||||
} elsif ( $user =~ /^!/ ) {
|
||||
$rule .= "! --uid-owner $user ";
|
||||
} else {
|
||||
$rule .= "--uid-owner $user ";
|
||||
}
|
||||
|
||||
$rule;
|
||||
}
|
||||
|
||||
#
|
||||
# Avoid generating a second '-m iprange' in a single rule.
|
||||
#
|
||||
sub iprange_match() {
|
||||
my $match = '';
|
||||
unless ( $iprangematch ) {
|
||||
$match = '-m iprange ';
|
||||
$iprangematch = 1;
|
||||
}
|
||||
|
||||
$match;
|
||||
}
|
||||
|
||||
#
|
||||
# Match a Source. Currently only handles IP addresses and ranges
|
||||
#
|
||||
sub match_source_net( $ ) {
|
||||
my $net = $_[0];
|
||||
|
||||
if ( $net =~ /^(!?).*\..*\..*\..*-.*\..*\..*\..*/ ) {
|
||||
$net =~ s/!// if my $invert = $1 ? '! ' : '';
|
||||
|
||||
iprange_match . "${invert}--src-range $net ";
|
||||
} elsif ( $net =~ /^(!?)~(.*)$/ ) {
|
||||
( $net = $2 ) =~ s/-/:/g;
|
||||
"-m mac --mac-source $1 $net "
|
||||
} elsif ( $net =~ /^!/ ) {
|
||||
$net =~ s/!//;
|
||||
"-s ! $net ";
|
||||
} else {
|
||||
$net eq ALLIPv4 ? '' : "-s $net ";
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Match a Source. Currently only handles IP addresses and ranges
|
||||
#
|
||||
sub match_dest_net( $ ) {
|
||||
my $net = $_[0];
|
||||
|
||||
if ( $net =~ /^(!?).*\..*\..*\..*-.*\..*\..*\..*/ ) {
|
||||
$net =~ s/!// if my $invert = $1 ? '! ' : '';
|
||||
|
||||
iprange_match . "${invert}--src-range $net ";
|
||||
} elsif ( $net =~ /^!/ ) {
|
||||
$net =~ s/!//;
|
||||
"-d ! $net ";
|
||||
} else {
|
||||
$net eq ALLIPv4 ? '' : "-d $net ";
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Match original destination
|
||||
#
|
||||
sub match_orig_dest ( $ ) {
|
||||
my $net = $_[0];
|
||||
|
||||
return '' if $net eq ALLIPv4;
|
||||
|
||||
if ( $net =~ /^!/ ) {
|
||||
$net =~ s/!//;
|
||||
"-m conntrack --ctorigdst ! $net ";
|
||||
} else {
|
||||
$net eq ALLIPv4 ? '' : "-m conntrack --ctorigdst $net ";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Match Source IPSEC
|
||||
#
|
||||
sub match_ipsec_in( $$ ) {
|
||||
my ( $zone , $hostref ) = @_;
|
||||
my $match = '-m policy --dir in --pol ';
|
||||
my $zoneref = $zones{$zone};
|
||||
my $optionsref = $zoneref->{options};
|
||||
|
||||
if ( $zoneref->{type} eq 'ipsec4' ) {
|
||||
$match .= "ipsec $optionsref->{in_out}{ipsec}$optionsref->{in}{ipsec}";
|
||||
} elsif ( $capabilities{POLICY_MATCH} ) {
|
||||
$match .= "$hostref->{ipsec} $optionsref->{in_out}{ipsec}$optionsref->{in}{ipsec}";
|
||||
} else {
|
||||
'';
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Match Dest IPSEC
|
||||
#
|
||||
sub match_ipsec_out( $$ ) {
|
||||
my ( $zone , $hostref ) = @_;
|
||||
my $match = '-m policy --dir out --pol ';
|
||||
my $zoneref = $zones{$zone};
|
||||
my $optionsref = $zoneref->{options};
|
||||
|
||||
if ( $zoneref->{type} eq 'ipsec4' ) {
|
||||
$match .= "ipsec $optionsref->{in_out}{ipsec}$optionsref->{out}{ipsec}";
|
||||
} elsif ( $capabilities{POLICY_MATCH} ) {
|
||||
$match .= "$hostref->{ipsec} $optionsref->{in_out}{ipsec}$optionsref->{out}{ipsec}"
|
||||
} else {
|
||||
'';
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Generate a log message
|
||||
#
|
||||
sub log_rule_limit( $$$$$$$$ ) {
|
||||
my ($level, $chainref, $chain, $disposition, $limit, $tag, $command, $predicates ) = @_;
|
||||
|
||||
my $prefix;
|
||||
|
||||
$limit = $env{LOGLIMIT} unless $limit;
|
||||
|
||||
if ( $tag ) {
|
||||
if ( $config{LOGTAGONLY} ) {
|
||||
$chain = $tag;
|
||||
$tag = '';
|
||||
} else {
|
||||
$tag .= ' ';
|
||||
}
|
||||
} else {
|
||||
$tag = '' unless defined $tag;
|
||||
}
|
||||
|
||||
if ( $env{LOGRULENUMBERS} ) {
|
||||
$prefix = (sprintf $config{LOGFORMAT} , $chain , $chainref->{log}++, $disposition ) . $tag;
|
||||
} else {
|
||||
$prefix = (sprintf $config{LOGFORMAT} , $chain , $disposition) . $tag;
|
||||
}
|
||||
|
||||
if ( length $prefix > 29 ) {
|
||||
$prefix = substr $prefix, 0, 29;
|
||||
warning_message "Log Prefix shortened to \"$prefix\"";
|
||||
}
|
||||
|
||||
if ( $level eq 'ULOG' ) {
|
||||
$prefix = "-j ULOG $env{LOGPARMS} --ulog-prefix \"$prefix\" ";
|
||||
} else {
|
||||
$prefix = "-j LOG $env{LOGPARMS} --log-level $level --log-prefix \"$prefix\" ";
|
||||
}
|
||||
|
||||
if ( $command eq 'add' ) {
|
||||
add_rule ( $chainref, $predicates . $prefix );
|
||||
} else {
|
||||
insert_rule ( $chainref , 1 , $predicates . $prefix );
|
||||
}
|
||||
}
|
||||
|
||||
sub log_rule( $$$$ ) {
|
||||
my ( $level, $chainref, $disposition, $predicates ) = @_;
|
||||
|
||||
log_rule_limit $level, $chainref, $chainref->{name} , $disposition, $env{LOGLIMIT}, '', 'add', $predicates;
|
||||
}
|
||||
|
||||
#
|
||||
# This function provides a uniform way to generate rules (something the original Shorewall sorely needed).
|
||||
#
|
||||
sub expand_rule( $$$$$$$$$ )
|
||||
{
|
||||
my ($chainref , $rule, $source, $dest, $origdest, $target, $loglevel , $disposition, $exceptionrule ) = @_;
|
||||
my ($iiface, $diface, $inets, $dnets, $iexcl, $dexcl, $onets , $oexcl );
|
||||
|
||||
#
|
||||
# Isolate Source Interface, if any
|
||||
#
|
||||
if ( $source ) {
|
||||
if ( $source eq '-' ) {
|
||||
$source = '';
|
||||
} elsif ( $source =~ /^([^:]+):([^:]+)$/ ) {
|
||||
$iiface = $1;
|
||||
$inets = $2;
|
||||
} elsif ( $source =~ /\+|~|\..*\./ ) {
|
||||
$inets = $source;
|
||||
} else {
|
||||
$iiface = $source;
|
||||
}
|
||||
} else {
|
||||
$source = '';
|
||||
}
|
||||
#
|
||||
# Verify Inteface, if any
|
||||
#
|
||||
if ( $iiface ) {
|
||||
fatal_error "Unknown Interface ($iiface): \"$line\"" unless known_interface $iiface;
|
||||
$rule .= "-i $iiface ";
|
||||
}
|
||||
|
||||
#
|
||||
# Isolate Destination Interface, if any
|
||||
#
|
||||
if ( $dest ) {
|
||||
if ( $dest eq '-' ) {
|
||||
$dest = '';
|
||||
} elsif ( $dest =~ /^([^:]+):([^:]+)$/ ) {
|
||||
$diface = $1;
|
||||
$dnets = $2;
|
||||
} elsif ( $dest =~ /\+|~|\..*\./ ) {
|
||||
$dnets = $dest;
|
||||
} else {
|
||||
$diface = $dest;
|
||||
}
|
||||
} else {
|
||||
$dest = '';
|
||||
}
|
||||
#
|
||||
# Verify Destination Interface, if any
|
||||
#
|
||||
if ( $diface ) {
|
||||
fatal_error "Unknown Interface ($diface) in rule \"$line\"" unless known_interface $diface;
|
||||
$rule .= "-o $diface ";
|
||||
}
|
||||
|
||||
#
|
||||
# Handle Log Level
|
||||
#
|
||||
my $logtag;
|
||||
|
||||
if ( $loglevel ) {
|
||||
( $loglevel, $logtag ) = split /:/, $loglevel;
|
||||
|
||||
if ( $loglevel =~ /^none!?$/i ) {
|
||||
return 1 if $disposition eq 'LOG';
|
||||
$loglevel = $logtag = '';
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Determine if there is Source Exclusion
|
||||
#
|
||||
|
||||
if ( $inets ) {
|
||||
if ( $inets =~ /^([^!]+)?!([^!]+)$/ ) {
|
||||
$inets = $1;
|
||||
$iexcl = $2;
|
||||
} else {
|
||||
$iexcl = '';
|
||||
}
|
||||
|
||||
if ( ! $inets ) {
|
||||
my @iexcl = split /,/, $iexcl;
|
||||
if ( @iexcl == 1 ) {
|
||||
$rule .= match_source_net "!$iexcl ";
|
||||
$iexcl = '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$iexcl = '';
|
||||
}
|
||||
|
||||
#
|
||||
# Determine if there is Destination Exclusion
|
||||
# $dexcl = '';
|
||||
|
||||
|
||||
if ( $dnets ) {
|
||||
if ( $dnets =~ /^([^!]+)?!([^!]+)$/ ) {
|
||||
$dnets = $1;
|
||||
$dexcl = $2;
|
||||
} else {
|
||||
$dexcl = '';
|
||||
}
|
||||
|
||||
if ( ! $dnets ) {
|
||||
my @dexcl = split /,/, $dexcl;
|
||||
if ( @dexcl == 1 ) {
|
||||
$rule .= match_dest_net "!$dexcl ";
|
||||
$dexcl = '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$dexcl = '';
|
||||
}
|
||||
|
||||
if ( $origdest ) {
|
||||
if ( $origdest =~ /^([^!]+)?!([^!]+)$/ ) {
|
||||
$onets = $1;
|
||||
$oexcl = $2;
|
||||
} else {
|
||||
$oexcl = '';
|
||||
}
|
||||
|
||||
if ( ! $onets ) {
|
||||
my @oexcl = split /,/, $oexcl;
|
||||
if ( @oexcl == 1 ) {
|
||||
$rule .= "-m conntrack --ctorigdst ! $oexcl ";
|
||||
$oexcl = '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$oexcl = '';
|
||||
}
|
||||
|
||||
$inets = ALLIPv4 unless $inets;
|
||||
$dnets = ALLIPv4 unless $dnets;
|
||||
$onets = ALLIPv4 unless $onets;
|
||||
|
||||
if ( $iexcl || $dexcl || $oexcl ) {
|
||||
#
|
||||
# We have non-trivial exclusion -- need to create an exclusion chain
|
||||
#
|
||||
my $echain = "excl$exclseq";
|
||||
|
||||
$exclseq++;
|
||||
|
||||
#
|
||||
# Use the current rule and sent all possible matches to the exclusion chain
|
||||
#
|
||||
for my $onet ( split /,/, $onets ) {
|
||||
$onet = match_orig_dest $onet;
|
||||
for my $inet ( split /,/, $inets ) {
|
||||
$inet = match_source_net $inet;
|
||||
for my $dnet ( split /,/, $dnets ) {
|
||||
add_rule $chainref, $rule . $inet . ( match_dest_net $dnet ) . $onet . "-j $echain";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# The final rule in the exclusion chain will not qualify the source or destination
|
||||
#
|
||||
$inets = ALLIPv4;
|
||||
$dnets = ALLIPv4;
|
||||
|
||||
#
|
||||
# Create the Exclusion Chain
|
||||
#
|
||||
my $echainref = new_chain $chainref->{table}, $echain;
|
||||
|
||||
#
|
||||
# Generate RETURNs for each exclusion
|
||||
#
|
||||
for my $net ( split ',', $iexcl ) {
|
||||
add_rule $echainref, ( match_source_net $net ) . '-j RETURN';
|
||||
}
|
||||
|
||||
for my $net ( split ',', $dexcl ) {
|
||||
add_rule $echainref, ( match_dest_net $net ) . '-j RETURN';
|
||||
}
|
||||
|
||||
for my $net ( split ',', $oexcl ) {
|
||||
add_rule $echainref, ( match_orig_dest $net ) . '-j RETURN';
|
||||
}
|
||||
|
||||
#
|
||||
# Log rule
|
||||
#
|
||||
log_rule_limit $loglevel , $echainref , $chainref->{name}, $disposition , '', $logtag , 'add' , '' if $loglevel;
|
||||
#
|
||||
# Generate Final Rule
|
||||
#
|
||||
add_rule $echainref, $exceptionrule . $target unless $disposition eq 'LOG';
|
||||
|
||||
} else {
|
||||
#
|
||||
# No exclusions
|
||||
#
|
||||
for my $onet ( split /,/, $onets ) {
|
||||
$onet = match_orig_dest $onet;
|
||||
for my $inet ( split /,/, $inets ) {
|
||||
$inet = match_source_net $inet;
|
||||
for my $dnet ( split /,/, $dnets ) {
|
||||
log_rule_limit $loglevel , $chainref , $chainref->{name}, $disposition , '' , $logtag , 'add' , $rule . $inet . match_dest_net( $dnet ) . $onet if $loglevel;
|
||||
add_rule $chainref, $rule . $inet . match_dest_net( $dnet ) . $onet . $target unless $disposition eq 'LOG';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub process_tos() {
|
||||
my $chain = 'pretos';
|
||||
my $stdchain = 'PREROUTING';
|
||||
@ -3383,35 +2801,6 @@ sub generate_matrix() {
|
||||
#
|
||||
# Helper functions for generate_matrix()
|
||||
#-----------------------------------------
|
||||
#
|
||||
# If the destination chain exists, then at the end of the source chain add a jump to the destination.
|
||||
#
|
||||
sub addnatjump( $$$ ) {
|
||||
my ( $source , $dest, $predicates ) = @_;
|
||||
|
||||
my $destref = $nat_table->{$dest} || {};
|
||||
|
||||
if ( $destref->{referenced} ) {
|
||||
add_rule $nat_table->{$source} , $predicates . "-j $dest";
|
||||
} else {
|
||||
$iprangematch = $ipsetmatch = 0;
|
||||
}
|
||||
}
|
||||
#
|
||||
# If the destination chain exists, then at the position in the source chain given by $$countref, add a jump to the destination.
|
||||
#
|
||||
sub insertnatjump( $$$$ ) {
|
||||
my ( $source, $dest, $countref, $predicates ) = @_;
|
||||
|
||||
my $destref = $nat_table->{$dest} || {};
|
||||
|
||||
if ( $destref->{referenced} ) {
|
||||
insert_rule $nat_table->{$source} , ($$countref)++, $predicates . "-j $dest";
|
||||
} else {
|
||||
$iprangematch = $ipsetmatch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Return the target for rules from the $zone to $zone1.
|
||||
#
|
||||
@ -3687,8 +3076,7 @@ sub generate_matrix() {
|
||||
my $chain1 = $policy_exclusions{"${chain}_${zone1}"};
|
||||
|
||||
unless ( $chain ) {
|
||||
$chain1="excl_$exclseq";
|
||||
$exclseq++;
|
||||
$chain1 = newexclusionchain;
|
||||
$policy_exclusions{"${chain}_${zone1}"} = $chain1;
|
||||
my $chain1ref = ensure_filter_chain $chain1, 0;
|
||||
add_exclusions $chain1ref, $exclusions1;
|
||||
@ -4417,28 +3805,13 @@ sub generate_script_3() {
|
||||
sub compile_firewall( $ ) {
|
||||
|
||||
my $objectfile = $_[0];
|
||||
my ( $dir, $file );
|
||||
|
||||
( $command, $doing, $done ) = qw/ check Checking Checked / unless $objectfile;
|
||||
|
||||
initialize_chain_table;
|
||||
|
||||
if ( $command eq 'compile' ) {
|
||||
eval {
|
||||
( $file, $dir, my $suffix ) = fileparse( $objectfile );
|
||||
fatal_error "Directory $dir does not exist" unless -d $dir;
|
||||
fatal_error "$dir is a Symbolic Link" if -l $dir;
|
||||
fatal_error "$objectfile is a Directory" if -d $objectfile;
|
||||
fatal_error "$dir is a Symbolic Link" if -l $objectfile;
|
||||
fatal_error "$objectfile exists and is not a compiled script" if -e _ && ! -x _;
|
||||
|
||||
$file = "$file.$suffix" if $suffix;
|
||||
};
|
||||
|
||||
fatal_error "$@" if $@;
|
||||
|
||||
$tempfile = create_temp_object;
|
||||
|
||||
create_temp_object( $objectfile );
|
||||
generate_script_1;
|
||||
}
|
||||
|
||||
@ -4574,10 +3947,7 @@ sub compile_firewall( $ ) {
|
||||
generate_matrix;
|
||||
dump_chain_table if $ENV{DEBUG};
|
||||
generate_script_3;
|
||||
|
||||
$file = "$dir/$file";
|
||||
rename $tempfile, $file;
|
||||
chmod 0700, $file;
|
||||
finalize_object;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user