2007-03-15 22:55:22 +01:00
#
2007-06-11 21:39:30 +02:00
# Shorewall-perl 4.0 -- /usr/share/shorewall-perl/Shorewall/Rules.pm
2007-03-15 22:55:22 +01:00
#
# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
#
# (c) 2007 - Tom Eastep (teastep@shorewall.net)
#
# Complete documentation is available at http://shorewall.net
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of Version 2 of the GNU General Public License
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
2007-04-19 01:55:25 +02:00
# This module contains the high-level code for dealing with rules.
2007-03-15 22:55:22 +01:00
#
2007-03-15 04:17:56 +01:00
package Shorewall::Rules ;
require Exporter ;
use Shorewall::Common ;
use Shorewall::Config ;
2007-05-11 17:39:11 +02:00
use Shorewall::IPAddrs ;
2007-03-15 04:17:56 +01:00
use Shorewall::Zones ;
2007-03-15 22:55:22 +01:00
use Shorewall::Interfaces ;
2007-03-15 04:17:56 +01:00
use Shorewall::Chains ;
2007-03-15 22:55:22 +01:00
use Shorewall::Hosts ;
2007-03-15 04:17:56 +01:00
use Shorewall::Actions ;
use Shorewall::Macros ;
2007-03-15 04:25:08 +01:00
use Shorewall::Policy ;
2007-03-16 17:26:34 +01:00
use Shorewall::Proc ;
2007-03-15 04:17:56 +01:00
use strict ;
our @ ISA = qw( Exporter ) ;
2007-03-28 19:43:09 +02:00
our @ EXPORT = qw( process_tos
2007-03-28 20:48:43 +02:00
setup_ecn
2007-04-08 16:42:26 +02:00
add_common_rules
2007-03-21 21:08:05 +01:00
setup_mac_lists
process_criticalhosts
process_routestopped
process_rules
generate_matrix
setup_mss
2007-03-21 21:35:40 +01:00
dump_rule_chains
2007-03-21 21:08:05 +01:00
) ;
2007-06-14 01:02:39 +02:00
our @ EXPORT_OK = qw( process_rule process_rule1 initialize ) ;
2007-03-15 04:17:56 +01:00
our @ VERSION = 1.00 ;
2007-03-21 21:08:05 +01:00
#
# Keep track of chains for the /var/lib/shorewall[-lite]/chains file
#
2007-06-05 18:49:13 +02:00
our @ rule_chains ;
2007-03-15 04:17:56 +01:00
#
# Set to one if we find a SECTION
#
2007-06-14 01:02:39 +02:00
our $ sectioned ;
2007-06-15 00:07:45 +02:00
#
# Initialize globals -- we take this novel approach to globals initialization to allow
# the compiler to run multiple times in the same process. The
# initialize() function does globals initialization for this
# module and is called from an INIT block below. The function is
# also called by Shorewall::Compiler::compiler at the beginning of
# the second and subsequent calls to that function.
#
2007-06-14 01:02:39 +02:00
sub initialize () {
@ rule_chains = ( ) ;
$ sectioned = 0 ;
}
INIT {
initialize ;
}
2007-03-15 04:17:56 +01:00
2007-05-12 20:13:11 +02:00
use constant { MAX_MACRO_NEST_LEVEL = > 5 } ;
2007-03-15 22:55:22 +01:00
sub process_tos () {
2007-03-28 19:43:09 +02:00
my $ chain = $ capabilities { MANGLE_FORWARD } ? 'fortos' : 'pretos' ;
my $ stdchain = $ capabilities { MANGLE_FORWARD } ? 'FORWARD' : 'PREROUTING' ;
2007-03-15 22:55:22 +01:00
2007-05-03 19:28:03 +02:00
my % tosoptions = ( 'minimize-delay' = > 0x10 ,
'maximize-throughput' = > 0x08 ,
'maximize-reliability' = > 0x04 ,
'minimize-cost' = > 0x02 ,
'normal-service' = > 0x00 ) ;
2007-03-30 17:57:08 +02:00
if ( my $ fn = open_file 'tos' ) {
2007-03-29 20:57:53 +02:00
my $ first_entry = 1 ;
2007-03-15 22:55:22 +01:00
2007-03-29 20:57:53 +02:00
my ( $ pretosref , $ outtosref ) ;
2007-04-08 16:42:26 +02:00
2007-03-29 19:02:13 +02:00
while ( read_a_line ) {
2007-03-27 01:17:46 +02:00
2007-03-29 20:57:53 +02:00
if ( $ first_entry ) {
2007-03-30 17:57:08 +02:00
progress_message2 "$doing $fn..." ;
2007-05-05 16:42:30 +02:00
$ pretosref = ensure_chain 'mangle' , $ chain ;
$ outtosref = ensure_chain 'mangle' , 'outtos' ;
2007-03-29 20:57:53 +02:00
$ first_entry = 0 ;
}
2007-05-17 16:10:46 +02:00
my ( $ src , $ dst , $ proto , $ sports , $ ports , $ tos , $ mark ) = split_line 6 , 7 , 'tos file entry' ;
2007-05-09 21:03:09 +02:00
2007-03-30 04:05:11 +02:00
fatal_error "TOS field required" unless $ tos ne '-' ;
2007-03-25 21:27:25 +02:00
2007-05-03 19:28:03 +02:00
if ( defined ( my $ tosval = $ tosoptions { "\L$tos" } ) ) {
$ tos = $ tosval ;
} elsif ( numeric_value ( $ tos ) > 0x1e ) {
2007-06-16 23:08:12 +02:00
fatal_error "Invalid TOS value ($tos)" ;
2007-05-03 19:28:03 +02:00
}
2007-03-25 21:27:25 +02:00
my $ chainref ;
my $ restriction = NO_RESTRICT ;
2007-05-17 16:10:46 +02:00
my ( $ srczone , $ source , $ remainder ) = split ( /:/ , $ src , 3 ) ;
fatal_error "Invalid SOURCE" if defined $ remainder ;
2007-03-27 01:17:46 +02:00
2007-03-25 21:27:25 +02:00
if ( $ srczone eq $ firewall_zone ) {
$ chainref = $ outtosref ;
$ src = $ source || '-' ;
$ restriction = OUTPUT_RESTRICT ;
} else {
$ chainref = $ pretosref ;
2007-03-25 21:52:01 +02:00
$ src =~ s/^all:?// ;
2007-03-25 21:27:25 +02:00
}
2007-03-25 21:43:33 +02:00
2007-03-25 23:04:24 +02:00
$ dst =~ s/^all:?// ;
2007-03-27 01:17:46 +02:00
2007-04-08 16:42:26 +02:00
expand_rule
2007-03-25 21:27:25 +02:00
$ chainref ,
$ restriction ,
2007-05-01 20:30:10 +02:00
do_proto ( $ proto , $ ports , $ sports ) . do_test ( $ mark , 0xFF ) ,
2007-03-25 21:27:25 +02:00
$ src ,
$ dst ,
'' ,
"-j TOS --set-tos $tos" ,
'' ,
'' ,
'' ;
2007-03-15 22:55:22 +01:00
}
2007-03-29 20:57:53 +02:00
unless ( $ first_entry ) {
2007-05-05 16:42:30 +02:00
add_rule $ mangle_table - > { $ stdchain } , "-j $chain" if $ pretosref - > { referenced } ;
add_rule $ mangle_table - > { OUTPUT } , "-j outtos" if $ outtosref - > { referenced } ;
2007-03-29 20:57:53 +02:00
}
2007-03-15 22:55:22 +01:00
}
}
2007-03-28 20:48:43 +02:00
#
# Setup ECN disabling rules
#
sub setup_ecn ()
{
my % interfaces ;
my @ hosts ;
2007-03-30 17:57:08 +02:00
if ( my $ fn = open_file 'ecn' ) {
my $ first_entry = 1 ;
2007-03-28 20:48:43 +02:00
2007-03-29 19:02:13 +02:00
while ( read_a_line ) {
2007-03-28 20:48:43 +02:00
2007-03-30 17:57:08 +02:00
if ( $ first_entry ) {
progress_message2 "$doing $fn..." ;
$ first_entry = 0 ;
}
2007-05-17 16:10:46 +02:00
my ( $ interface , $ hosts ) = split_line 1 , 2 , 'ecn file entry' ;
2007-05-09 21:03:09 +02:00
2007-06-16 23:08:12 +02:00
fatal_error "Unknown interface ($interface)" unless known_interface $ interface ;
2007-03-28 20:48:43 +02:00
$ interfaces { $ interface } = 1 ;
$ hosts = ALLIPv4 if $ hosts eq '-' ;
for my $ host ( split /,/ , $ hosts ) {
push @ hosts , [ $ interface , $ host ] ;
}
}
if ( @ hosts ) {
my @ interfaces = ( keys % interfaces ) ;
progress_message "$doing ECN control on @interfaces..." ;
for my $ interface ( @ interfaces ) {
my $ chainref = ensure_chain 'mangle' , ecn_chain ( $ interface ) ;
2007-04-08 16:42:26 +02:00
2007-03-28 20:48:43 +02:00
if ( $ capabilities { MANGLE_FORWARD } ) {
add_rule $ mangle_table - > { POSTROUTING } , "-p tcp -o $interface -j $chainref->{name}" ;
} else {
add_rule $ mangle_table - > { PREROUTING } , "-p tcp -o $interface -j $chainref->{name}" ;
add_rule $ mangle_table - > { OUTPUT } , "-p tcp -o $interface -j $chainref->{name}" ;
}
}
for my $ host ( @ hosts ) {
2007-03-28 21:09:27 +02:00
add_rule $ mangle_table - > { ecn_chain $ host - > [ 0 ] } , join ( '' , '-p tcp ' , match_dest_net ( $ host - > [ 1 ] ) , ' -j ECN --ecn-tcp-remove' ) ;
2007-03-28 20:48:43 +02:00
}
}
}
}
2007-03-15 22:55:22 +01:00
sub add_rule_pair ( $$$$ ) {
my ( $ chainref , $ predicate , $ target , $ level ) = @ _ ;
2007-05-12 02:33:48 +02:00
log_rule ( $ level , $ chainref , "\U$target" , $ predicate ) if defined $ level && $ level ne '' ;
2007-03-15 22:55:22 +01:00
add_rule $ chainref , "${predicate}-j $target" ;
}
sub setup_rfc1918_filteration ( $ ) {
my $ listref = $ _ [ 0 ] ;
my $ norfc1918ref = new_standard_chain 'norfc1918' ;
my $ rfc1918ref = new_standard_chain 'rfc1918' ;
my $ chainref = $ norfc1918ref ;
log_rule $ config { RFC1918_LOG_LEVEL } , $ rfc1918ref , 'DROP' , '' ;
add_rule $ rfc1918ref , '-j DROP' ;
2007-03-29 03:14:13 +02:00
$ chainref = new_standard_chain 'rfc1918d' if $ config { RFC1918_STRICT } ;
2007-03-15 22:55:22 +01:00
2007-03-30 17:57:08 +02:00
my $ fn = open_file 'rfc1918' ;
my $ first_entry = 1 ;
2007-03-27 01:17:46 +02:00
2007-03-29 19:02:13 +02:00
while ( read_a_line ) {
2007-03-15 22:55:22 +01:00
2007-03-30 17:57:08 +02:00
if ( $ first_entry ) {
progress_message2 "$doing $fn..." ;
$ first_entry = 0 ;
}
2007-05-09 21:03:09 +02:00
my ( $ networks , $ target ) = split_line 2 , 2 , 'rfc1918 file' ;
my $ s_target ;
2007-03-15 22:55:22 +01:00
if ( $ target eq 'logdrop' ) {
$ target = 'rfc1918' ;
$ s_target = 'rfc1918' ;
} elsif ( $ target eq 'DROP' ) {
$ s_target = 'DROP' ;
} elsif ( $ target eq 'RETURN' ) {
2007-04-24 23:36:10 +02:00
$ s_target = $ config { RFC1918_STRICT } ? 'rfc1918d' : 'RETURN' ;
2007-03-15 22:55:22 +01:00
} else {
fatal_error "Invalid target ($target) for $networks" ;
}
for my $ network ( split /,/ , $ networks ) {
add_rule $ norfc1918ref , match_source_net ( $ network ) . "-j $s_target" ;
add_rule $ chainref , match_orig_dest ( $ network ) . "-j $target" ;
}
}
add_rule $ norfc1918ref , '-j rfc1918d' if $ config { RFC1918_STRICT } ;
for my $ hostref ( @$ listref ) {
my $ interface = $ hostref - > [ 0 ] ;
my $ ipsec = $ hostref - > [ 1 ] ;
my $ policy = $ capabilities { POLICY_MATCH } ? "-m policy --pol $ipsec --dir in " : '' ;
for my $ chain ( @ { first_chains $ interface } ) {
2007-03-26 02:46:15 +02:00
add_rule $ filter_table - > { $ chain } , join ( '' , '-m state --state NEW ' , match_source_net ( $ hostref - > [ 2 ] ) , "${policy}-j norfc1918" ) ;
2007-03-15 22:55:22 +01:00
}
}
}
sub setup_syn_flood_chains () {
for my $ chainref ( @ policy_chains ) {
my $ limit = $ chainref - > { synparams } ;
if ( $ limit ) {
2007-05-26 04:57:27 +02:00
my $ level = $ chainref - > { loglevel } ;
2007-03-15 22:55:22 +01:00
my $ synchainref = new_chain 'filter' , syn_chain $ chainref - > { name } ;
2007-05-26 04:57:27 +02:00
add_rule $ synchainref , "${limit}-j RETURN" ;
2007-05-08 04:34:58 +02:00
log_rule_limit $ level , $ synchainref , $ chainref - > { name } , 'DROP' , '-m limit --limit 5/min --limit-burst 5 ' , '' , 'add' , ''
2007-04-25 02:19:55 +02:00
if $ level ne '' ;
2007-03-15 22:55:22 +01:00
add_rule $ synchainref , '-j DROP' ;
}
}
}
sub setup_blacklist () {
2007-03-16 23:19:32 +01:00
my $ hosts = find_hosts_by_option 'blacklist' ;
2007-03-31 20:44:48 +02:00
my $ chainref ;
my ( $ level , $ disposition ) = @ config { 'BLACKLIST_LOGLEVEL' , 'BLACKLIST_DISPOSITION' } ;
my $ target = $ disposition eq 'REJECT' ? 'reject' : $ disposition ;
2007-03-15 22:55:22 +01:00
2007-03-16 23:19:32 +01:00
if ( @$ hosts ) {
2007-03-31 20:44:48 +02:00
$ chainref = new_standard_chain 'blacklst' ;
2007-03-16 23:19:32 +01:00
2007-04-25 01:15:27 +02:00
if ( defined $ level && $ level ne '' ) {
2007-03-31 20:44:48 +02:00
my $ logchainref = new_standard_chain 'blacklog' ;
2007-03-27 01:17:46 +02:00
2007-03-31 20:44:48 +02:00
log_rule_limit ( $ level , $ logchainref , 'blacklst' , $ disposition , "$globals{LOGLIMIT}" , '' , 'add' , '' ) ;
2007-03-27 01:17:46 +02:00
2007-03-31 20:44:48 +02:00
add_rule $ logchainref , "-j $target" ;
2007-03-15 22:55:22 +01:00
2007-03-16 23:19:32 +01:00
$ target = 'blacklog' ;
}
2007-03-31 20:44:48 +02:00
}
2007-03-15 22:55:22 +01:00
2007-03-31 20:44:48 +02:00
BLACKLIST:
{
2007-03-30 17:57:08 +02:00
if ( my $ fn = open_file 'blacklist' ) {
2007-03-16 23:19:32 +01:00
2007-03-30 17:57:08 +02:00
my $ first_entry = 1 ;
2007-03-16 23:19:32 +01:00
2007-03-29 19:02:13 +02:00
while ( read_a_line ) {
2007-03-27 01:17:46 +02:00
2007-03-30 17:57:08 +02:00
if ( $ first_entry ) {
2007-03-31 20:44:48 +02:00
unless ( @$ hosts ) {
warning_message "The entries in $fn have been ignored because there are no 'blacklist' interfaces" ;
close_file ;
last BLACKLIST ;
}
2007-03-30 17:57:08 +02:00
progress_message2 "$doing $fn..." ;
$ first_entry = 0 ;
}
2007-05-09 21:03:09 +02:00
my ( $ networks , $ protocol , $ ports ) = split_line 1 , 3 , 'blacklist file' ;
2007-04-08 16:42:26 +02:00
expand_rule (
2007-03-31 20:44:48 +02:00
$ chainref ,
NO_RESTRICT ,
do_proto ( $ protocol , $ ports , '' ) ,
$ networks ,
'' ,
'' ,
"-j $target" ,
'' ,
$ disposition ,
'' ) ;
2007-04-08 16:42:26 +02:00
2007-03-16 23:19:32 +01:00
progress_message " \"$line\" added to blacklist" ;
}
2007-03-15 22:55:22 +01:00
}
2007-03-16 23:19:32 +01:00
my $ state = $ config { BLACKLISTNEWONLY } ? '-m state --state NEW,INVALID ' : '' ;
2007-03-27 01:17:46 +02:00
2007-03-16 23:19:32 +01:00
for my $ hostref ( @$ hosts ) {
my $ interface = $ hostref - > [ 0 ] ;
my $ ipsec = $ hostref - > [ 1 ] ;
my $ policy = $ capabilities { POLICY_MATCH } ? "-m policy --pol $ipsec --dir in " : '' ;
my $ network = $ hostref - > [ 2 ] ;
my $ source = match_source_net $ network ;
2007-04-08 16:42:26 +02:00
2007-03-16 23:19:32 +01:00
for my $ chain ( @ { first_chains $ interface } ) {
add_rule $ filter_table - > { $ chain } , "${source}${state}${policy}-j blacklst" ;
}
2007-04-08 16:42:26 +02:00
2007-03-16 23:19:32 +01:00
progress_message " Blacklisting enabled on ${interface}:${network}" ;
}
2007-03-15 22:55:22 +01:00
}
}
2007-03-16 00:18:58 +01:00
sub process_criticalhosts () {
2007-03-31 00:38:09 +02:00
my @ critical = ( ) ;
2007-03-16 00:18:58 +01:00
2007-03-31 00:40:13 +02:00
my $ fn = open_file 'routestopped' ;
2007-03-16 00:18:58 +01:00
2007-03-31 00:38:09 +02:00
my $ first_entry = 1 ;
2007-03-16 00:18:58 +01:00
2007-03-29 19:02:13 +02:00
while ( read_a_line ) {
2007-03-16 00:18:58 +01:00
my $ routeback = 0 ;
2007-03-27 01:17:46 +02:00
2007-03-31 00:38:09 +02:00
if ( $ first_entry ) {
progress_message2 "$doing $fn for critical hosts..." ;
$ first_entry = 0 ;
}
2007-05-09 21:03:09 +02:00
my ( $ interface , $ hosts , $ options ) = split_line 1 , 3 , 'routestopped file' ;
2007-04-01 17:38:05 +02:00
$ hosts = ALLIPv4 unless $ hosts ne '-' ;
2007-03-16 00:18:58 +01:00
my @ hosts ;
for my $ host ( split /,/ , $ hosts ) {
push @ hosts , "$interface:$hosts" ;
}
unless ( $ options eq '-' ) {
for my $ option ( split /,/ , $ options ) {
unless ( $ option eq 'routeback' || $ option eq 'source' || $ option eq 'dest' ) {
if ( $ option eq 'critical' ) {
2007-04-08 16:42:26 +02:00
push @ critical , @ hosts ;
2007-03-16 00:18:58 +01:00
} else {
2007-03-30 04:05:11 +02:00
warning_message "Unknown routestopped option ( $option ) ignored" ;
2007-03-16 00:18:58 +01:00
}
}
}
}
}
\ @ critical ;
}
sub process_routestopped () {
my ( @ allhosts , % source , % dest ) ;
2007-03-30 17:57:08 +02:00
my $ fn = open_file 'routestopped' ;
2007-03-16 00:18:58 +01:00
2007-03-30 17:57:08 +02:00
my $ first_entry = 1 ;
2007-03-16 00:18:58 +01:00
2007-03-29 19:02:13 +02:00
while ( read_a_line ) {
2007-03-16 00:18:58 +01:00
my $ routeback = 0 ;
2007-03-27 01:17:46 +02:00
2007-03-30 17:57:08 +02:00
if ( $ first_entry ) {
progress_message2 "$doing $fn..." ;
$ first_entry = 0 ;
}
2007-05-09 21:03:09 +02:00
my ( $ interface , $ hosts , $ options ) = split_line 1 , 3 , 'routestopped file' ;
2007-03-16 00:18:58 +01:00
$ hosts = ALLIPv4 unless $ hosts && $ hosts ne '-' ;
my @ hosts ;
for my $ host ( split /,/ , $ hosts ) {
2007-06-18 23:42:36 +02:00
push @ hosts , "$interface:$host" ;
2007-03-16 00:18:58 +01:00
}
unless ( $ options eq '-' ) {
for my $ option ( split /,/ , $ options ) {
if ( $ option eq 'routeback' ) {
if ( $ routeback ) {
2007-03-30 04:05:11 +02:00
warning_message "Duplicate 'routeback' option ignored" ;
2007-03-16 00:18:58 +01:00
} else {
$ routeback = 1 ;
2007-03-27 01:17:46 +02:00
2007-03-16 00:18:58 +01:00
for my $ host ( split /,/ , $ hosts ) {
my $ source = match_source_net $ host ;
my $ dest = match_dest_net $ host ;
emit "run_iptables -A FORWARD -i $interface -o $interface $source $dest -j ACCEPT" ;
2007-04-25 23:03:40 +02:00
clearrule ;
2007-03-16 00:18:58 +01:00
}
}
} elsif ( $ option eq 'source' ) {
for my $ host ( split /,/ , $ hosts ) {
$ source { "$interface:$host" } = 1 ;
}
} elsif ( $ option eq 'dest' ) {
for my $ host ( split /,/ , $ hosts ) {
$ dest { "$interface:$host" } = 1 ;
2007-03-27 01:17:46 +02:00
}
2007-03-16 00:18:58 +01:00
} else {
2007-03-30 04:05:11 +02:00
warning_message "Unknown routestopped option ( $option ) ignored" unless $ option eq 'critical' ;
2007-03-16 00:18:58 +01:00
}
}
}
push @ allhosts , @ hosts ;
}
for my $ host ( @ allhosts ) {
2007-03-16 01:58:25 +01:00
my ( $ interface , $ h ) = split /:/ , $ host ;
2007-03-16 00:18:58 +01:00
my $ source = match_source_net $ h ;
my $ dest = match_dest_net $ h ;
2007-03-27 01:17:46 +02:00
2007-04-09 05:51:25 +02:00
emit "\$IPTABLES -A INPUT -i $interface $source -j ACCEPT" ;
emit "\$IPTABLES -A OUTPUT -o $interface $dest -j ACCEPT" if $ config { ADMINISABSENTMINDED } ;
2007-03-27 01:17:46 +02:00
2007-03-16 00:18:58 +01:00
my $ matched = 0 ;
if ( $ source { $ host } ) {
2007-04-09 05:51:25 +02:00
emit "\$IPTABLES -A FORWARD -i $interface $source -j ACCEPT" ;
2007-03-16 00:18:58 +01:00
$ matched = 1 ;
}
if ( $ dest { $ host } ) {
2007-04-09 05:51:25 +02:00
emit "\$IPTABLES -A FORWARD -o $interface $dest -j ACCEPT" ;
2007-03-16 00:18:58 +01:00
$ matched = 1 ;
}
2007-03-27 01:17:46 +02:00
2007-03-16 00:18:58 +01:00
unless ( $ matched ) {
for my $ host1 ( @ allhosts ) {
unless ( $ host eq $ host1 ) {
2007-03-16 01:58:25 +01:00
my ( $ interface1 , $ h1 ) = split /:/ , $ host1 ;
2007-03-16 00:18:58 +01:00
my $ dest1 = match_dest_net $ h1 ;
emit "\$IPTABLES -A FORWARD -i $interface -o $interface1 $source $dest1 -j ACCEPT" ;
2007-04-25 23:03:40 +02:00
clearrule ;
2007-03-16 00:18:58 +01:00
}
}
}
}
}
2007-03-15 22:55:22 +01:00
sub add_common_rules () {
my $ interface ;
my $ chainref ;
my $ level ;
my $ target ;
my $ rule ;
my $ list ;
my $ chain ;
my $ rejectref = new_standard_chain 'reject' ;
2007-04-24 23:27:40 +02:00
$ level = $ config { BLACKLIST_LOGLEVEL } ;
2007-03-15 22:55:22 +01:00
2007-03-16 23:19:32 +01:00
add_rule_pair new_standard_chain ( 'logdrop' ) , ' ' , 'DROP' , $ level ;
2007-05-12 02:28:14 +02:00
add_rule_pair new_standard_chain ( 'logreject' ) , ' ' , 'reject' , $ level ;
2007-03-15 22:55:22 +01:00
2007-03-16 23:19:32 +01:00
new_standard_chain 'dynamic' ;
my $ state = $ config { BLACKLISTNEWONLY } ? '-m state --state NEW,INVALID ' : '' ;
2007-03-27 01:17:46 +02:00
2007-03-15 22:55:22 +01:00
for $ interface ( @ interfaces ) {
2007-05-12 20:13:11 +02:00
for $ chain ( @ { first_chains $ interface } ) {
2007-03-15 22:55:22 +01:00
add_rule new_standard_chain ( $ chain ) , "$state -j dynamic" ;
}
new_standard_chain output_chain ( $ interface ) ;
}
2007-05-20 17:51:42 +02:00
2007-05-20 18:25:53 +02:00
run_user_exit1 'initdone' ;
2007-04-10 20:06:09 +02:00
2007-03-15 22:55:22 +01:00
setup_blacklist ;
$ list = find_hosts_by_option 'nosmurfs' ;
2007-04-23 20:26:20 +02:00
$ chainref = new_standard_chain 'smurfs' ;
2007-03-15 22:55:22 +01:00
2007-04-23 20:26:20 +02:00
add_rule $ chainref , '-s 0.0.0.0 -j RETURN' ;
2007-03-31 18:58:14 +02:00
2007-04-23 20:26:20 +02:00
add_rule_pair $ chainref , '-m addrtype --src-type BROADCAST ' , 'DROP' , $ config { SMURF_LOG_LEVEL } ;
2007-05-15 22:40:15 +02:00
add_rule_pair $ chainref , '-s 224.0.0.0/4 ' , 'DROP' , $ config { SMURF_LOG_LEVEL } ;
2007-04-23 20:26:20 +02:00
add_rule $ rejectref , '-m addrtype --src-type BROADCAST -j DROP' ;
2007-05-15 22:40:15 +02:00
add_rule $ rejectref , '-s 224.0.0.0/4 -j DROP' ;
2007-03-27 01:17:46 +02:00
2007-03-15 22:55:22 +01:00
if ( @$ list ) {
2007-03-30 17:57:08 +02:00
progress_message2 'Adding Anti-smurf Rules' ;
2007-03-15 22:55:22 +01:00
for my $ hostref ( @$ list ) {
$ interface = $ hostref - > [ 0 ] ;
my $ ipsec = $ hostref - > [ 1 ] ;
my $ policy = $ capabilities { POLICY_MATCH } ? "-m policy --pol $ipsec --dir in " : '' ;
for $ chain ( @ { first_chains $ interface } ) {
2007-03-26 02:46:15 +02:00
add_rule $ filter_table - > { $ chain } , join ( '' , '-m state --state NEW,INVALID ' , match_source_net ( $ hostref - > [ 2 ] ) , "${policy}-j smurfs" ) ;
2007-03-15 22:55:22 +01:00
}
}
}
2007-03-27 01:17:46 +02:00
2007-03-15 22:55:22 +01:00
add_rule $ rejectref , '-p tcp -j REJECT --reject-with tcp-reset' ;
2007-03-27 01:17:46 +02:00
2007-03-15 22:55:22 +01:00
if ( $ capabilities { ENHANCED_REJECT } ) {
add_rule $ rejectref , '-p udp -j REJECT' ;
add_rule $ rejectref , '-p icmp -j REJECT --reject-with icmp-host-unreachable' ;
add_rule $ rejectref , '-j REJECT --reject-with icmp-host-prohibited' ;
} else {
add_rule $ rejectref , '-j REJECT' ;
}
$ list = find_interfaces_by_option 'dhcp' ;
if ( @$ list ) {
2007-03-30 17:57:08 +02:00
progress_message2 'Adding rules for DHCP' ;
2007-03-15 22:55:22 +01:00
for $ interface ( @$ list ) {
2007-05-10 20:31:24 +02:00
for $ chain ( input_chain $ interface , output_chain $ interface ) {
2007-03-15 22:55:22 +01:00
add_rule $ filter_table - > { $ chain } , '-p udp --dport 67:68 -j ACCEPT' ;
}
2007-06-06 22:06:16 +02:00
add_rule $ filter_table - > { forward_chain $ interface } , "-p udp -o $interface --dport 67:68 -j ACCEPT" if $ interfaces { $ interface } { options } { bridge } ;
2007-03-15 22:55:22 +01:00
}
}
$ list = find_hosts_by_option 'norfc1918' ;
2007-03-30 17:57:08 +02:00
setup_rfc1918_filteration $ list if @$ list ;
2007-03-15 22:55:22 +01:00
$ list = find_hosts_by_option 'tcpflags' ;
if ( @$ list ) {
my $ disposition ;
2007-03-30 17:57:08 +02:00
progress_message2 "$doing TCP Flags filtering..." ;
2007-03-27 01:17:46 +02:00
2007-03-15 22:55:22 +01:00
$ chainref = new_standard_chain 'tcpflags' ;
2007-04-25 01:15:27 +02:00
if ( $ config { TCP_FLAGS_LOG_LEVEL } ne '' ) {
2007-03-15 22:55:22 +01:00
my $ logflagsref = new_standard_chain 'logflags' ;
2007-03-27 01:17:46 +02:00
2007-03-31 19:44:16 +02:00
my $ savelogparms = $ globals { LOGPARMS } ;
2007-03-15 22:55:22 +01:00
2007-05-02 17:49:41 +02:00
$ globals { LOGPARMS } = "$globals{LOGPARMS}--log-ip-options " unless $ config { TCP_FLAGS_LOG_LEVEL } eq 'ULOG' ;
2007-03-27 01:17:46 +02:00
2007-03-15 22:55:22 +01:00
log_rule $ config { TCP_FLAGS_LOG_LEVEL } , $ logflagsref , $ config { TCP_FLAGS_DISPOSITION } , '' ;
2007-03-27 01:17:46 +02:00
2007-03-31 19:44:16 +02:00
$ globals { LOGPARMS } = $ savelogparms ;
2007-03-27 01:17:46 +02:00
2007-03-15 22:55:22 +01:00
if ( $ config { TCP_FLAGS_DISPOSITION } eq 'REJECT' ) {
add_rule $ logflagsref , '-j REJECT --reject-with tcp-reset' ;
} else {
add_rule $ logflagsref , "-j $config{TCP_FLAGS_DISPOSITION}" ;
}
$ disposition = 'logflags' ;
} else {
$ disposition = $ config { TCP_FLAGS_DISPOSITION } ;
}
add_rule $ chainref , "-p tcp --tcp-flags ALL FIN,URG,PSH -j $disposition" ;
add_rule $ chainref , "-p tcp --tcp-flags ALL NONE -j $disposition" ;
add_rule $ chainref , "-p tcp --tcp-flags SYN,RST SYN,RST -j $disposition" ;
add_rule $ chainref , "-p tcp --tcp-flags SYN,FIN SYN,FIN -j $disposition" ;
add_rule $ chainref , "-p tcp --syn --sport 0 -j $disposition" ;
for my $ hostref ( @$ list ) {
$ interface = $ hostref - > [ 0 ] ;
my $ ipsec = $ hostref - > [ 1 ] ;
my $ policy = $ capabilities { POLICY_MATCH } ? "-m policy --pol $ipsec --dir in " : '' ;
for $ chain ( @ { first_chains $ interface } ) {
2007-03-26 02:46:15 +02:00
add_rule $ filter_table - > { $ chain } , join ( '' , '-p tcp ' , match_source_net ( $ hostref - > [ 2 ] ) , "${policy}-j tcpflags" ) ;
2007-03-15 22:55:22 +01:00
}
}
}
if ( $ config { DYNAMIC_ZONES } ) {
for $ interface ( @ interfaces ) {
for $ chain ( @ { dynamic_chains $ interface } ) {
new_standard_chain $ chain ;
}
2007-03-27 01:17:46 +02:00
2007-05-12 20:13:11 +02:00
mark_referenced ( new_chain 'nat' , $ chain = dynamic_in ( $ interface ) ) ;
2007-03-27 01:17:46 +02:00
2007-04-24 01:17:30 +02:00
add_rule $ filter_table - > { input_chain $ interface } , "-j $chain" ;
add_rule $ filter_table - > { forward_chain $ interface } , '-j ' . dynamic_fwd $ interface ;
add_rule $ filter_table - > { output_chain $ interface } , '-j ' . dynamic_out $ interface ;
}
2007-03-27 01:17:46 +02:00
}
2007-03-15 22:55:22 +01:00
$ list = find_interfaces_by_option 'upnp' ;
if ( @$ list ) {
2007-03-30 17:57:08 +02:00
progress_message2 '$doing UPnP' ;
2007-03-15 22:55:22 +01:00
2007-05-12 20:13:11 +02:00
mark_referenced ( new_chain ( 'nat' , 'UPnP' ) ) ;
2007-03-15 22:55:22 +01:00
for $ interface ( @$ list ) {
2007-06-06 22:06:16 +02:00
add_rule $ nat_table - > { PREROUTING } , match_source_dev ( $ interface ) . '-j UPnP' ;
2007-03-15 22:55:22 +01:00
}
}
setup_syn_flood_chains ;
}
my % maclist_targets = ( ACCEPT = > { target = > 'RETURN' , mangle = > 1 } ,
REJECT = > { target = > 'reject' , mangle = > 0 } ,
DROP = > { target = > 'DROP' , mangle = > 1 } ) ;
sub setup_mac_lists ( $ ) {
my $ phase = $ _ [ 0 ] ;
my % maclist_interfaces ;
my $ table = $ config { MACLIST_TABLE } ;
my $ maclist_hosts = find_hosts_by_option 'maclist' ;
2007-04-24 23:05:26 +02:00
my $ target = $ globals { MACLIST_TARGET } ;
my $ level = $ config { MACLIST_LOG_LEVEL } ;
my $ disposition = $ config { MACLIST_DISPOSITION } ;
my $ ttl = $ config { MACLIST_TTL } ;
2007-03-19 04:57:58 +01:00
for my $ hostref ( @$ maclist_hosts ) {
2007-03-22 15:41:32 +01:00
$ maclist_interfaces { $ hostref - > [ 0 ] } = 1 ;
2007-03-15 22:55:22 +01:00
}
my @ maclist_interfaces = ( sort keys % maclist_interfaces ) ;
2007-03-27 01:17:46 +02:00
2007-03-15 22:55:22 +01:00
progress_message " $doing MAC Verification for @maclist_interfaces -- Phase $phase..." ;
if ( $ phase == 1 ) {
2007-04-23 19:19:12 +02:00
2007-03-15 22:55:22 +01:00
for my $ interface ( @ maclist_interfaces ) {
my $ chainref = new_chain $ table , mac_chain $ interface ;
2007-03-27 01:17:46 +02:00
2007-03-15 22:55:22 +01:00
add_rule $ chainref , '-s 0.0.0.0 -d 255.255.255.255 -p udp --dport 67:68 -j RETURN'
if ( $ table eq 'mangle' ) && $ interfaces { $ interface } { options } { dhcp } ;
2007-03-27 01:17:46 +02:00
2007-04-24 23:05:26 +02:00
if ( $ ttl ) {
2007-03-15 22:55:22 +01:00
my $ chain1ref = new_chain $ table , macrecent_target $ interface ;
my $ chain = $ chainref - > { name } ;
2007-04-24 23:05:26 +02:00
add_rule $ chainref , "-m recent --rcheck --seconds $ttl --name $chain -j RETURN" ;
2007-03-15 22:55:22 +01:00
add_rule $ chainref , "-j $chain1ref->{name}" ;
add_rule $ chainref , "-m recent --update --name $chain -j RETURN" ;
add_rule $ chainref , "-m recent --set --name $chain" ;
}
}
2007-03-30 17:57:08 +02:00
my $ fn = open_file 'maclist' ;
my $ first_entry = 1 ;
2007-03-15 22:55:22 +01:00
2007-03-29 19:02:13 +02:00
while ( read_a_line ) {
2007-03-15 22:55:22 +01:00
2007-03-30 17:57:08 +02:00
if ( $ first_entry ) {
progress_message2 "$doing $fn..." ;
$ first_entry = 0 ;
}
2007-05-10 17:29:41 +02:00
my ( $ disposition , $ interface , $ mac , $ addresses ) = split_line1 3 , 4 , 'maclist file' ;
2007-05-09 21:03:09 +02:00
2007-03-15 22:55:22 +01:00
if ( $ disposition eq 'COMMENT' ) {
2007-05-09 20:22:40 +02:00
process_comment ;
2007-03-15 22:55:22 +01:00
} else {
2007-05-17 16:10:46 +02:00
( $ disposition , my ( $ level , $ remainder ) ) = split ( /:/ , $ disposition , 3 ) ;
fatal_error "Invalid log level" if defined $ remainder ;
2007-03-15 22:55:22 +01:00
my $ targetref = $ maclist_targets { $ disposition } ;
2007-06-16 23:08:12 +02:00
fatal_error "Invalid DISPOSITION ($disposition)" if ! $ targetref || ( ( $ table eq 'mangle' ) && ! $ targetref - > { mangle } ) ;
2007-03-15 22:55:22 +01:00
2007-06-08 00:23:17 +02:00
unless ( $ maclist_interfaces { $ interface } ) {
2007-06-08 01:34:11 +02:00
next if get_interface_option ( $ interface , 'optional' ) && get_interface_option ( $ interface , 'detectnets' ) ;
2007-06-08 00:23:17 +02:00
fatal_error "No hosts on $interface have the maclist option specified" ;
}
2007-03-15 22:55:22 +01:00
2007-04-24 23:05:26 +02:00
my $ chainref = $ chain_table { $ table } { ( $ ttl ? macrecent_target $ interface : mac_chain $ interface ) } ;
2007-03-15 22:55:22 +01:00
$ mac = '' unless $ mac && ( $ mac ne '-' ) ;
$ addresses = '' unless $ addresses && ( $ addresses ne '-' ) ;
fatal_error "You must specify a MAC address or an IP address" unless $ mac || $ addresses ;
$ mac = mac_match $ mac if $ mac ;
if ( $ addresses ) {
for my $ address ( split ',' , $ addresses ) {
my $ source = match_source_net $ address ;
2007-04-25 01:15:27 +02:00
log_rule_limit $ level , $ chainref , mac_chain ( $ interface ) , $ disposition , '' , '' , 'add' , "${mac}${source}"
if defined $ level && $ level ne '' ;
2007-03-15 22:55:22 +01:00
add_rule $ chainref , "${mac}${source}-j $targetref->{target}" ;
}
} else {
2007-04-25 01:15:27 +02:00
log_rule_limit $ level , $ chainref , mac_chain ( $ interface ) , $ disposition , '' , '' , 'add' , $ mac
if defined $ level && $ level ne '' ;
2007-03-15 22:55:22 +01:00
add_rule $ chainref , "$mac-j $targetref->{target}" ;
}
progress_message " Maclist entry \"$line\" $done" ;
}
}
$ comment = '' ;
2007-04-18 22:53:25 +02:00
#
# Generate jumps from the input and forward chains
#
2007-03-15 22:55:22 +01:00
for my $ hostref ( @$ maclist_hosts ) {
my $ interface = $ hostref - > [ 0 ] ;
my $ ipsec = $ hostref - > [ 1 ] ;
my $ policy = $ capabilities { POLICY_MATCH } ? "-m policy --pol $ipsec --dir in " : '' ;
my $ source = match_source_net $ hostref - > [ 2 ] ;
my $ target = mac_chain $ interface ;
if ( $ table eq 'filter' ) {
for my $ chain ( @ { first_chains $ interface } ) {
2007-04-23 01:21:34 +02:00
add_rule $ filter_table - > { $ chain } , "${source}-m state --state NEW ${policy}-j $target" ;
2007-03-15 22:55:22 +01:00
}
} else {
2007-06-07 01:39:27 +02:00
add_rule $ mangle_table - > { PREROUTING } , match_source_dev ( $ interface ) . "${source}-m state --state NEW ${policy}-j $target" ;
2007-03-15 22:55:22 +01:00
}
}
} else {
for my $ interface ( @ maclist_interfaces ) {
2007-04-24 23:05:26 +02:00
my $ chainref = $ chain_table { $ table } { ( $ ttl ? macrecent_target $ interface : mac_chain $ interface ) } ;
2007-03-22 21:24:21 +01:00
my $ chain = $ chainref - > { name } ;
2007-04-24 22:53:07 +02:00
if ( $ level ne '' || $ disposition ne 'ACCEPT' ) {
2007-06-07 16:46:57 +02:00
my $ variable = get_interface_addresses $ interfaces { $ interface } { bridge } ;
2007-04-27 18:18:42 +02:00
add_commands ( $ chainref ,
"for address in $variable; do" ,
" echo \"-A $chainref->{name} -s \$address -m addrtype --dst-type BROADCAST -j RETURN\" >&3" ,
2007-05-15 22:40:15 +02:00
" echo \"-A $chainref->{name} -s \$address -d 224.0.0.0/4 -j RETURN\" >&3" ,
2007-04-27 18:18:42 +02:00
'done' ) ;
2007-03-22 21:24:21 +01:00
}
2007-05-20 18:37:09 +02:00
run_user_exit2 ( 'maclog' , $ chainref ) ;
2007-04-10 20:06:09 +02:00
2007-04-24 22:53:07 +02:00
log_rule_limit $ level , $ chainref , $ chain , $ disposition , '' , '' , 'add' , '' if $ level ne '' ;
2007-03-15 22:55:22 +01:00
add_rule $ chainref , "-j $target" ;
}
}
}
2007-06-06 02:47:27 +02:00
sub process_rule1 ( $ $ $ $ $ $ $ $ $ $ $ ) ;
2007-03-15 04:17:56 +01:00
#
# Expand a macro rule from the rules file
#
2007-06-06 02:47:27 +02:00
sub process_macro ( $$$$$$$$$$$$$ ) {
my ( $ macrofile , $ target , $ param , $ source , $ dest , $ proto , $ ports , $ sports , $ origdest , $ rate , $ user , $ mark , $ wildcard ) = @ _ ;
2007-03-15 04:17:56 +01:00
progress_message "..Expanding Macro $macrofile..." ;
2007-03-29 19:02:13 +02:00
push_open $ macrofile ;
2007-03-15 04:17:56 +01:00
2007-03-29 19:02:13 +02:00
while ( read_a_line ) {
2007-03-27 01:17:46 +02:00
2007-04-01 17:38:05 +02:00
my ( $ mtarget , $ msource , $ mdest , $ mproto , $ mports , $ msports , $ mrate , $ muser ) = split_line 1 , 8 , 'macro file' ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:17:56 +01:00
$ mtarget = merge_levels $ target , $ mtarget ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:17:56 +01:00
if ( $ mtarget =~ /^PARAM:?/ ) {
2007-05-12 20:13:11 +02:00
fatal_error 'PARAM requires a parameter to be supplied in macro invocation' unless $ param ne '' ;
$ mtarget = substitute_param $ param , $ mtarget ;
2007-03-15 04:17:56 +01:00
}
2007-05-22 01:09:40 +02:00
my $ action = isolate_basic_target $ mtarget ;
2007-06-16 23:08:12 +02:00
fatal_error "Invalid or missing ACTION ($mtarget)" unless defined $ action ;
2007-05-22 01:09:40 +02:00
2007-05-12 20:13:11 +02:00
my $ actiontype = $ targets { $ action } || find_macro ( $ action ) ;
2007-03-15 04:17:56 +01:00
2007-05-13 17:39:20 +02:00
fatal_error "Invalid Action ($mtarget) in macro" unless $ actiontype & ( ACTION + STANDARD + NATRULE + MACRO ) ;
2007-03-15 04:17:56 +01:00
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 '-' ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:17:56 +01:00
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 ;
2007-03-27 01:17:46 +02:00
2007-06-06 02:47:27 +02:00
process_rule1 $ mtarget , $ msource , $ mdest , $ mproto , $ mports , $ msports , $ origdest , $ mrate , $ muser , $ mark , $ wildcard ;
2007-03-15 04:17:56 +01:00
2007-04-23 20:28:07 +02:00
progress_message " Rule \"$line\" $done" ;
}
2007-03-15 04:17:56 +01:00
2007-03-29 19:02:13 +02:00
pop_open ;
2007-03-15 04:17:56 +01:00
2007-05-13 17:39:20 +02:00
progress_message "..End Macro $macrofile"
2007-03-15 04:17:56 +01:00
}
2007-05-12 20:13:11 +02:00
my $ macro_nest_level = 0 ;
my $ current_param = '' ;
my @ param_stack ;
2007-03-15 04:17:56 +01:00
#
2007-04-11 00:52:45 +02:00
# Once a rule has been completely resolved by macro expansion and wildcard (source and/or dest zone == 'all'), it is processed by this function.
2007-03-15 04:17:56 +01:00
#
2007-06-06 02:47:27 +02:00
sub process_rule1 ( $$$$$$$$$$$ ) {
my ( $ target , $ source , $ dest , $ proto , $ ports , $ sports , $ origdest , $ ratelimit , $ user , $ mark , $ wildcard ) = @ _ ;
2007-03-15 04:17:56 +01:00
my ( $ action , $ loglevel ) = split_action $ target ;
2007-03-26 23:49:03 +02:00
my ( $ basictarget , $ param ) = split '/' , $ action ;
2007-03-15 04:17:56 +01:00
my $ rule = '' ;
my $ actionchainref ;
2007-06-06 18:20:33 +02:00
my $ optimize = $ wildcard ? ( $ basictarget =~ /!$/ ? 0 : $ config { OPTIMIZE } ) : 0 ;
2007-03-15 04:17:56 +01:00
2007-05-12 20:13:11 +02:00
$ param = '' unless defined $ param ;
2007-06-06 18:20:33 +02:00
2007-03-15 04:17:56 +01:00
#
# Determine the validity of the action
#
2007-03-26 23:49:03 +02:00
my $ actiontype = $ targets { $ basictarget } || find_macro ( $ basictarget ) ;
2007-06-06 18:20:33 +02:00
2007-03-30 04:05:11 +02:00
fatal_error "Unknown action ($action)" unless $ actiontype ;
2007-03-15 04:17:56 +01:00
if ( $ actiontype == MACRO ) {
2007-03-29 06:22:10 +02:00
#
2007-05-12 20:13:11 +02:00
# Will call process_rule1() recursively for each rule in the macro body
2007-03-29 06:22:10 +02:00
#
2007-05-12 20:13:11 +02:00
fatal_error "Macro invocations nested too deeply" if + + $ macro_nest_level > MAX_MACRO_NEST_LEVEL ;
if ( $ param ne '' ) {
push @ param_stack , $ current_param ;
$ current_param = $ param ;
}
2007-06-06 18:20:33 +02:00
2007-05-04 16:16:18 +02:00
process_macro ( $ macros { $ basictarget } ,
$ target ,
2007-05-12 20:13:11 +02:00
$ current_param ,
2007-05-04 16:16:18 +02:00
$ source ,
$ dest ,
$ proto ,
$ ports ,
$ sports ,
$ origdest ,
$ ratelimit ,
$ user ,
2007-06-06 02:47:27 +02:00
$ mark ,
$ wildcard ) ;
2007-05-12 20:13:11 +02:00
$ macro_nest_level - - ;
2007-06-06 18:20:33 +02:00
2007-05-12 20:13:11 +02:00
$ current_param = pop @ param_stack if $ param ne '' ;
2007-03-15 04:17:56 +01:00
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 {
2007-03-26 02:46:15 +02:00
$ dest = join ( '' , $ firewall_zone , '::' , $ dest ) ;
2007-03-15 04:17:56 +01:00
}
} elsif ( $ action eq 'REJECT' ) {
$ action = 'reject' ;
} elsif ( $ action eq 'CONTINUE' ) {
$ action = 'RETURN' ;
2007-04-30 17:25:07 +02:00
} elsif ( $ actiontype & LOGRULE ) {
fatal_error 'LOG requires a log level' unless defined $ loglevel and $ loglevel ne '' ;
2007-03-15 04:17:56 +01:00
}
#
# Isolate and validate source and destination zones
#
my $ sourcezone ;
my $ destzone ;
2007-06-06 18:20:33 +02:00
2007-03-15 04:17:56 +01:00
if ( $ source =~ /^(.+?):(.*)/ ) {
$ sourcezone = $ 1 ;
$ source = $ 2 ;
} else {
$ sourcezone = $ source ;
$ source = ALLIPv4 ;
}
2007-03-27 01:17:46 +02:00
2007-03-15 04:17:56 +01:00
if ( $ dest =~ /^(.+?):(.*)/ ) {
$ destzone = $ 1 ;
$ dest = $ 2 ;
} else {
$ destzone = $ dest ;
$ dest = ALLIPv4 ;
}
2007-06-06 18:20:33 +02:00
2007-05-04 03:28:37 +02:00
fatal_error "Missing source zone" if $ sourcezone eq '-' ;
2007-05-05 02:02:21 +02:00
fatal_error "Unknown source zone ($sourcezone)" unless $ zones { $ sourcezone } ;
2007-05-04 03:28:37 +02:00
fatal_error "Missing destination zone" if $ destzone eq '-' ;
2007-03-30 04:05:11 +02:00
fatal_error "Unknown destination zone ($destzone)" unless $ zones { $ destzone } ;
2007-03-25 21:43:33 +02:00
my $ restriction = NO_RESTRICT ;
if ( $ sourcezone eq $ firewall_zone ) {
$ restriction = $ destzone eq $ firewall_zone ? ALL_RESTRICT : OUTPUT_RESTRICT ;
} else {
$ restriction = INPUT_RESTRICT if $ destzone eq $ firewall_zone ;
}
2007-03-15 04:17:56 +01:00
#
2007-06-06 02:47:27 +02:00
# Check for illegal bridge port rule
#
2007-06-06 18:38:35 +02:00
if ( $ zones { $ destzone } - > { type } eq 'bport4' ) {
2007-06-07 04:21:54 +02:00
unless ( $ zones { $ sourcezone } { bridge } eq $ zones { $ destzone } { bridge } || single_interface ( $ sourcezone ) eq $ zones { $ destzone } { bridge } ) {
2007-06-06 02:47:27 +02:00
return 1 if $ wildcard ;
fatal_error "Rules with a DESTINATION Bridge Port zone must have a SOURCE zone on the same bridge" ;
}
}
#
2007-03-15 04:17:56 +01:00
# Take care of chain
#
my $ chain = "${sourcezone}2${destzone}" ;
2007-06-16 18:36:51 +02:00
my $ chainref = ensure_chain 'filter' , $ chain ;
2007-03-15 04:17:56 +01:00
#
# Validate Policy
#
my $ policy = $ chainref - > { policy } ;
2007-06-06 02:47:27 +02:00
2007-03-26 02:46:15 +02:00
fatal_error "No policy defined from zone $sourcezone to zone $destzone" unless $ policy ;
2007-06-06 02:47:27 +02:00
if ( $ policy eq 'NONE' ) {
return 1 if $ wildcard ;
fatal_error "Rules may not override a NONE policy" ;
}
2007-03-15 04:17:56 +01:00
#
2007-06-06 18:20:33 +02:00
# Handle Optimization
#
if ( $ optimize > 0 ) {
2007-06-14 01:02:39 +02:00
my $ loglevel = $ filter_table - > { $ chainref - > { policychain } } { loglevel } ;
2007-06-06 18:20:33 +02:00
if ( $ loglevel ne '' ) {
return 1 if $ target eq "${policy}:$loglevel}" ;
} else {
return 1 if $ basictarget eq $ policy ;
}
}
#
2007-06-16 18:36:51 +02:00
# Mark the chain as referenced and add appropriate rules from earlier sections.
#
$ chainref = ensure_filter_chain $ chain , 1 ;
#
2007-05-27 13:58:19 +02:00
# For compatibility with older Shorewall versions
#
$ origdest = ALLIPv4 if $ origdest eq 'all' ;
#
2007-03-15 04:17:56 +01:00
# Generate Fixed part of the rule
#
2007-05-03 18:30:59 +02:00
$ rule = join ( '' , do_proto ( $ proto , $ ports , $ sports ) , do_ratelimit ( $ ratelimit , $ basictarget ) , do_user ( $ user ) , do_test ( $ mark , 0xFF ) ) ;
2007-03-15 04:17:56 +01:00
2007-05-09 00:28:48 +02:00
unless ( $ section eq 'NEW' ) {
2007-05-08 16:16:20 +02:00
fatal_error "Entries in the $section SECTION of the rules file not permitted with FASTACCEPT=Yes" if $ config { FASTACCEPT } ;
fatal_error "$basictarget rules are not allowed in the $section SECTION" if $ actiontype & NONAT ;
$ rule . = "-m state --state $section "
}
2007-03-15 04:17:56 +01:00
#
# Generate NAT rule(s), if any
#
if ( $ actiontype & NATRULE ) {
2007-05-04 17:12:29 +02:00
my ( $ server , $ serverport ) ;
2007-03-15 04:17:56 +01:00
fatal_error "$target rules not allowed in the $section SECTION" if $ section ne 'NEW' ;
2007-05-04 17:12:29 +02:00
require_capability ( 'NAT_ENABLED' , "$basictarget rules" , '' ) ;
2007-03-15 04:17:56 +01:00
#
# Isolate server port
#
if ( $ dest =~ /^(.*)(:(\d+))$/ ) {
$ server = $ 1 ;
$ serverport = $ 3 ;
} else {
$ server = $ dest ;
$ serverport = '' ;
}
2007-05-04 16:16:18 +02:00
2007-03-15 04:17:56 +01:00
#
2007-05-09 16:48:18 +02:00
# After DNAT, dest port will be the server port. Capture it here because $serverport gets modified below.
2007-03-15 04:17:56 +01:00
#
2007-05-09 16:48:18 +02:00
my $ servport = $ serverport ne '' ? $ serverport : $ ports ;
2007-03-15 04:17:56 +01:00
2007-05-04 16:55:02 +02:00
fatal_error "A server must be specified in the DEST column in $action rules" unless ( $ actiontype & REDIRECT ) || $ server ne ALLIPv4 ;
2007-03-30 04:05:11 +02:00
fatal_error "Invalid server ($server)" if $ server =~ /:/ ;
2007-03-15 04:17:56 +01:00
#
# Generate the target
#
my $ target = '' ;
2007-03-23 23:47:21 +01:00
if ( $ actiontype & REDIRECT ) {
2007-05-09 16:48:18 +02:00
$ target = '-j REDIRECT --to-port ' . ( $ serverport ne '' ? $ serverport : $ ports ) ;
2007-03-23 23:47:21 +01:00
} else {
if ( $ action eq 'SAME' ) {
fatal_error 'Port mapping not allowed in SAME rules' if $ serverport ;
2007-04-19 23:14:18 +02:00
fatal_error 'SAME not allowed with SOURCE=$FW' if $ sourcezone eq $ firewall_zone ;
2007-03-23 23:47:21 +01:00
$ target = '-j SAME ' ;
for my $ serv ( split /,/ , $ server ) {
$ target . = "--to $serv " ;
}
2007-03-29 05:07:48 +02:00
} elsif ( $ action eq 'DNAT' ) {
$ target = '-j DNAT ' ;
2007-03-23 23:47:21 +01:00
$ serverport = ":$serverport" if $ serverport ;
for my $ serv ( split /,/ , $ server ) {
2007-03-29 05:07:48 +02:00
$ target . = "--to-destination ${serv}${serverport} " ;
2007-03-23 23:47:21 +01:00
}
2007-03-15 04:17:56 +01:00
}
2007-03-29 06:22:10 +02:00
unless ( $ origdest && $ origdest ne '-' && $ origdest ne 'detect' ) {
2007-03-23 23:47:21 +01:00
if ( $ config { DETECT_DNAT_IPADDRS } ) {
my $ interfacesref = $ zones { $ sourcezone } { interfaces } ;
my @ interfaces = keys %$ interfacesref ;
$ origdest = @ interfaces ? "detect:@interfaces" : ALLIPv4 ;
} else {
$ origdest = ALLIPv4 ;
}
2007-03-23 02:37:23 +01:00
}
}
2007-03-15 04:17:56 +01:00
#
# And generate the nat table rule(s)
#
2007-05-04 16:16:18 +02:00
expand_rule ( ensure_chain ( 'nat' , $ zones { $ sourcezone } { type } eq 'firewall' ? 'OUTPUT' : dnat_chain $ sourcezone ) ,
PREROUTE_RESTRICT ,
$ rule ,
$ source ,
$ origdest ,
'' ,
$ target ,
$ loglevel ,
$ action ,
$ serverport ? do_proto ( $ proto , '' , '' ) : '' ) ;
2007-03-15 04:17:56 +01:00
#
2007-03-29 06:22:10 +02:00
# After NAT:
# - the destination port will be the server port
# - the destination IP will be the server IP
# - there will be no log level (we log NAT rules in the nat table rather than in the filter table).
2007-05-09 00:42:35 +02:00
# - the target will be ACCEPT.
2007-03-15 04:17:56 +01:00
#
unless ( $ actiontype & NATONLY ) {
2007-05-09 16:48:18 +02:00
$ rule = join ( '' , do_proto ( $ proto , $ servport , $ sports ) , do_ratelimit ( $ ratelimit , 'ACCEPT' ) , do_user $ user , do_test ( $ mark , 0xFF ) ) ;
2007-03-15 04:17:56 +01:00
$ loglevel = '' ;
2007-03-29 05:07:48 +02:00
$ dest = $ server ;
$ action = 'ACCEPT' ;
2007-03-15 04:17:56 +01:00
}
2007-05-09 00:28:48 +02:00
} elsif ( $ actiontype & NONAT ) {
#
# NONAT or ACCEPT+ -- May not specify a destination interface
#
fatal_error "Invalid DEST ($dest) in $action rule" if $ dest =~ /:/ ;
$ origdest = '' unless $ origdest and $ origdest ne '-' ;
2007-03-23 17:12:36 +01:00
2007-05-09 00:28:48 +02:00
if ( $ origdest eq 'detect' ) {
my $ interfacesref = $ zones { $ sourcezone } { interfaces } ;
my $ interfaces = "@$interfacesref" ;
$ origdest = $ interfaces ? "detect:$interfaces" : ALLIPv4 ;
2007-03-23 02:37:23 +01:00
}
2007-05-09 00:28:48 +02:00
expand_rule ( ensure_chain ( 'nat' , $ zones { $ sourcezone } { type } eq 'firewall' ? 'OUTPUT' : dnat_chain $ sourcezone ) ,
PREROUTE_RESTRICT ,
$ rule ,
$ source ,
$ dest ,
$ origdest ,
'-j RETURN ' ,
$ loglevel ,
$ action ,
'' ) ;
2007-03-15 04:17:56 +01:00
}
#
# Add filter table rule, unless this is a NATONLY rule type
#
unless ( $ actiontype & NATONLY ) {
if ( $ actiontype & ACTION ) {
$ action = ( find_logactionchain $ target ) - > { name } ;
$ loglevel = '' ;
}
2007-03-29 06:22:10 +02:00
unless ( $ origdest eq '-' ) {
2007-05-04 17:12:29 +02:00
require_capability ( 'CONNTRACK_MATCH' , 'ORIGINAL DEST in a non-NAT rule' , 's' ) unless $ actiontype & NATRULE ;
2007-03-29 06:22:10 +02:00
} else {
$ origdest = '' ;
}
2007-05-04 16:16:18 +02:00
expand_rule ( ensure_chain ( 'filter' , $ chain ) ,
$ restriction ,
$ rule ,
$ source ,
$ dest ,
$ origdest ,
"-j $action " ,
$ loglevel ,
$ action ,
'' ) ;
2007-03-15 04:17:56 +01:00
}
}
#
2007-04-21 23:24:38 +02:00
# Process a Record in the rules file
#
2007-05-09 17:08:39 +02:00
# Deals with the ugliness of wildcard zones ('all' in SOURCE and/or DEST column).
2007-03-15 04:17:56 +01:00
#
2007-05-01 20:30:10 +02:00
sub process_rule ( $$$$$$$$$$ ) {
my ( $ target , $ source , $ dest , $ proto , $ ports , $ sports , $ origdest , $ ratelimit , $ user , $ mark ) = @ _ ;
2007-03-15 04:17:56 +01:00
my $ intrazone = 0 ;
my $ includesrcfw = 1 ;
my $ includedstfw = 1 ;
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' ;
2007-05-10 18:13:16 +02:00
$ sections { $ section = 'NEW' } = 1 ;
2007-03-15 04:17:56 +01:00
$ sectioned = 1 ;
}
2007-04-01 01:43:10 +02:00
2007-03-15 04:17:56 +01:00
#
# 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 ;
2007-05-09 17:08:39 +02:00
} else {
fatal_error "Invalid SOURCE ($source)" unless $ source eq 'all' ;
2007-03-15 04:17:56 +01:00
}
}
if ( $ dest =~ /^all[-+]/ ) {
if ( $ dest eq 'all+' ) {
$ dest = 'all' ;
$ intrazone = 1 ;
} elsif ( ( $ dest eq 'all+-' ) || ( $ dest eq 'all-+' ) ) {
$ dest = 'all' ;
$ intrazone = 1 ;
$ includedstfw = 0 ;
2007-05-09 17:08:39 +02:00
} elsif ( $ dest eq 'all-' ) {
2007-03-15 04:17:56 +01:00
$ dest = 'all' ;
$ includedstfw = 0 ;
2007-05-09 17:08:39 +02:00
} else {
fatal_error "Invalid DEST ($dest)" unless $ dest eq 'all' ;
2007-03-15 04:17:56 +01:00
}
2007-05-09 17:08:39 +02:00
2007-03-15 04:17:56 +01:00
}
my $ action = isolate_basic_target $ target ;
2007-06-16 23:08:12 +02:00
fatal_error "Invalid or missing ACTION ($target)" unless defined $ action ;
2007-05-21 21:50:24 +02:00
2007-03-15 04:17:56 +01:00
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 ) ) {
2007-06-06 02:47:27 +02:00
process_rule1 $ target , $ zone , $ zone1 , $ proto , $ ports , $ sports , $ origdest , $ ratelimit , $ user , $ mark , 1 ;
2007-03-15 04:17:56 +01:00
}
2007-04-08 16:42:26 +02:00
}
2007-03-15 04:17:56 +01:00
}
} else {
2007-05-17 16:10:46 +02:00
my $ destzone = ( split ( /:/ , $ dest , 2 ) ) [ 0 ] ;
2007-05-04 20:55:57 +02:00
$ destzone = $ firewall_zone unless $ zones { $ destzone } ; # We do this to allow 'REDIRECT all ...'; process_rule1 will catch the case where the dest zone is invalid
2007-04-21 16:07:37 +02:00
if ( $ intrazone || ( $ zone ne $ destzone ) ) {
2007-06-06 02:47:27 +02:00
process_rule1 $ target , $ zone , $ dest , $ proto , $ ports , $ sports , $ origdest , $ ratelimit , $ user , $ mark , 1 ;
2007-04-20 02:02:41 +02:00
}
2007-03-15 04:17:56 +01:00
}
2007-04-08 16:42:26 +02:00
}
2007-03-15 04:17:56 +01:00
}
} elsif ( $ dest eq 'all' ) {
2007-04-20 18:47:04 +02:00
for my $ zone ( @ zones ) {
2007-05-17 16:10:46 +02:00
my $ sourcezone = ( split ( /:/ , $ source , 2 ) ) [ 0 ] ;
2007-04-20 18:47:04 +02:00
if ( ( $ includedstfw || ( $ zones { $ zone } { type } ne 'firewall' ) ) && ( ( $ sourcezone ne $ zone ) || $ intrazone ) ) {
2007-06-06 02:47:27 +02:00
process_rule1 $ target , $ source , $ zone , $ proto , $ ports , $ sports , $ origdest , $ ratelimit , $ user , $ mark , 1 ;
2007-03-15 04:17:56 +01:00
}
}
} else {
2007-06-06 02:47:27 +02:00
process_rule1 $ target , $ source , $ dest , $ proto , $ ports , $ sports , $ origdest , $ ratelimit , $ user , $ mark , 0 ;
2007-03-15 04:17:56 +01:00
}
progress_message " Rule \"$thisline\" $done" ;
}
#
# Process the Rules File
#
sub process_rules () {
2007-03-30 17:57:08 +02:00
my $ fn = open_file 'rules' ;
my $ first_entry = 1 ;
2007-03-15 04:17:56 +01:00
2007-03-29 19:02:13 +02:00
while ( read_a_line ) {
2007-03-15 04:17:56 +01:00
2007-03-30 17:57:08 +02:00
if ( $ first_entry ) {
progress_message2 "$doing $fn..." ;
$ first_entry = 0 ;
}
2007-05-10 17:29:41 +02:00
my ( $ target , $ source , $ dest , $ proto , $ ports , $ sports , $ origdest , $ ratelimit , $ user , $ mark ) = split_line2 1 , 10 , 'rules file' ;
2007-05-09 21:03:09 +02:00
2007-03-15 04:17:56 +01:00
if ( $ target eq 'COMMENT' ) {
2007-05-09 20:22:40 +02:00
process_comment ;
2007-03-15 04:17:56 +01:00
} 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 } ;
2007-03-29 06:22:10 +02:00
fatal_error "Invalid Section $source $dest" if $ dest ;
2007-03-15 04:17:56 +01:00
$ sectioned = 1 ;
$ sections { $ source } = 1 ;
2007-05-08 02:48:05 +02:00
if ( $ source eq 'RELATED' ) {
2007-03-15 04:17:56 +01:00
$ sections { ESTABLISHED } = 1 ;
finish_section 'ESTABLISHED' ;
2007-05-08 02:48:05 +02:00
} elsif ( $ source eq 'NEW' ) {
2007-03-15 04:17:56 +01:00
@ sections { 'ESTABLISHED' , 'RELATED' } = ( 1 , 1 ) ;
finish_section ( ( $ section eq 'RELATED' ) ? 'RELATED' : 'ESTABLISHED,RELATED' ) ;
}
$ section = $ source ;
} else {
2007-04-19 23:24:25 +02:00
if ( "\L$source" =~ /^none(:.*)?$/ || "\L$dest" =~ /^none(:.*)?$/ ) {
progress_message "Rule \"$line\" ignored."
} else {
2007-05-01 20:30:10 +02:00
process_rule $ target , $ source , $ dest , $ proto , $ ports , $ sports , $ origdest , $ ratelimit , $ user , $ mark ;
2007-04-19 23:24:25 +02:00
}
2007-03-15 04:17:56 +01:00
}
}
2007-03-27 01:17:46 +02:00
2007-03-15 04:17:56 +01:00
$ comment = '' ;
$ section = 'DONE' ;
}
2007-03-15 04:25:08 +01:00
#
2007-05-16 00:42:00 +02:00
# To quote an old comment, "generate_matrix makes a sows ear out of a silk purse".
2007-03-15 04:25:08 +01:00
#
# The biggest disadvantage of the zone-policy-rule model used by Shorewall is that it doesn't scale well as the number of zones increases (Order N**2 where N = number of zones).
# A major goal of the rewrite of the compiler in Perl was to restrict those scaling effects to this functions and the rules that it generates.
#
2007-03-30 20:55:49 +02:00
# The function traverses the full "source-zone X destination-zone" matrix and generates the rules necessary to direct traffic through the right set of filter-table rules.
2007-04-08 16:42:26 +02:00
#
2007-03-15 04:25:08 +01:00
sub generate_matrix () {
#
# Helper functions for generate_matrix()
#-----------------------------------------
#
2007-06-08 22:10:43 +02:00
# Return the target for rules from $zone to $zone1.
2007-03-15 04:25:08 +01:00
#
sub rules_target ( $$ ) {
my ( $ zone , $ zone1 ) = @ _ ;
my $ chain = "${zone}2${zone1}" ;
my $ chainref = $ filter_table - > { $ chain } ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
return $ chain if $ chainref && $ chainref - > { referenced } ;
2007-06-08 22:12:39 +02:00
return 'ACCEPT' if $ zone eq $ zone1 ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
if ( $ chainref - > { policy } ne 'CONTINUE' ) {
2007-06-14 01:02:39 +02:00
my $ policyref = $ filter_table - > { $ chainref - > { policychain } } ;
2007-03-15 04:25:08 +01:00
return $ policyref - > { name } if $ policyref ;
fatal_error "No policy defined for zone $zone to zone $zone1" ;
}
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
'' ;
}
#
# Add a jump to the passed chain ($chainref) to the dynamic zone chain for the passed zone.
#
sub create_zone_dyn_chain ( $$ ) {
my ( $ zone , $ chainref ) = @ _ ;
my $ name = "${zone}_dyn" ;
new_standard_chain $ name ;
add_rule $ chainref , "-j $name" ;
}
#
# Insert the passed exclusions at the front of the passed chain.
#
sub insert_exclusions ( $$ ) {
my ( $ chainref , $ exclusionsref ) = @ _ ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
my $ num = 1 ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
for my $ host ( @ { $ exclusionsref } ) {
my ( $ interface , $ net ) = split /:/ , $ host ;
2007-06-08 18:15:21 +02:00
insert_rule $ chainref , $ num + + , join ( '' , match_dest_dev $ interface , match_dest_net ( $ net ) , '-j RETURN' ) ;
2007-03-15 04:25:08 +01:00
}
}
#
# Add the passed exclusions at the end of the passed chain.
#
sub add_exclusions ( $$ ) {
my ( $ chainref , $ exclusionsref ) = @ _ ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
for my $ host ( @ { $ exclusionsref } ) {
my ( $ interface , $ net ) = split /:/ , $ host ;
2007-06-08 18:15:21 +02:00
add_rule $ chainref , join ( '' , match_dest_dev $ interface , match_dest_net ( $ net ) , '-j RETURN' ) ;
2007-03-15 04:25:08 +01:00
}
2007-03-27 01:17:46 +02:00
}
2007-05-16 00:42:00 +02:00
#
# Set a breakpoint in this function if you want to step through generate_matrix().
#
sub start_matrix () {
}
#
# Generate_Matrix() Starts Here
#
start_matrix ;
2007-03-15 04:25:08 +01:00
my $ prerouting_rule = 1 ;
my $ postrouting_rule = 1 ;
my $ exclusion_seq = 1 ;
my % chain_exclusions ;
my % policy_exclusions ;
for my $ interface ( @ interfaces ) {
2007-06-06 02:47:27 +02:00
addnatjump 'POSTROUTING' , snat_chain ( $ interface ) , match_dest_dev ( $ interface ) ;
2007-03-15 04:25:08 +01:00
}
if ( $ config { DYNAMIC_ZONES } ) {
for my $ interface ( @ interfaces ) {
2007-06-06 02:47:27 +02:00
addnatjump 'PREROUTING' , dynamic_in ( $ interface ) , match_source_dev ( $ interface ) ;
2007-03-15 04:25:08 +01:00
}
}
addnatjump 'PREROUTING' , 'nat_in' , '' ;
addnatjump 'POSTROUTING' , 'nat_out' , '' ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
for my $ interface ( @ interfaces ) {
2007-06-06 02:47:27 +02:00
addnatjump 'PREROUTING' , input_chain ( $ interface ) , match_source_dev ( $ interface ) ;
addnatjump 'POSTROUTING' , output_chain ( $ interface ) , match_dest_dev ( $ interface ) ;
2007-03-15 04:25:08 +01:00
}
2007-06-08 22:10:43 +02:00
#
# Special processing for complex zones
#
2007-03-15 04:25:08 +01:00
for my $ zone ( grep $ zones { $ _ } { options } { complex } , @ zones ) {
my $ frwd_ref = new_standard_chain "${zone}_frwd" ;
my $ zoneref = $ zones { $ zone } ;
my $ exclusions = $ zoneref - > { exclusions } ;
if ( @$ exclusions ) {
my $ in_ref = new_standard_chain "${zone}_input" ;
my $ out_ref = new_standard_chain "${zone}_output" ;
2007-03-27 01:17:46 +02:00
2007-04-25 17:33:15 +02:00
add_rule ensure_filter_chain ( "${zone}2${zone}" , 1 ) , '-j ACCEPT' if rules_target ( $ zone , $ zone ) eq 'ACCEPT' ;
2007-03-15 04:25:08 +01:00
for my $ host ( @$ exclusions ) {
my ( $ interface , $ net ) = split /:/ , $ host ;
2007-06-08 18:08:55 +02:00
my $ rule = match_source_dev ( $ interface ) . match_source_net ( $ net ) . "-j RETURN" ;
2007-06-06 02:47:27 +02:00
add_rule $ frwd_ref , $ rule ;
add_rule $ in_ref , $ rule ;
add_rule $ out_ref , $ rule ;
2007-03-15 04:25:08 +01:00
}
2007-05-15 01:14:30 +02:00
}
2007-03-27 01:17:46 +02:00
2007-05-15 01:14:30 +02:00
if ( $ capabilities { POLICY_MATCH } ) {
my $ type = $ zoneref - > { type } ;
my $ source_ref = ( $ zoneref - > { hosts } { ipsec } ) || { } ;
2007-03-15 04:25:08 +01:00
2007-05-15 01:14:30 +02:00
if ( $ config { DYNAMIC_ZONES } ) {
no warnings ;
create_zone_dyn_chain $ zone , $ frwd_ref if ( %$ source_ref || $ type eq 'ipsec4' ) ;
}
2007-03-27 01:17:46 +02:00
2007-05-15 01:14:30 +02:00
for my $ interface ( keys %$ source_ref ) {
my $ arrayref = $ source_ref - > { $ interface } ;
for my $ hostref ( @ { $ arrayref } ) {
my $ ipsec_match = match_ipsec_in $ zone , $ hostref ;
for my $ net ( @ { $ hostref - > { hosts } } ) {
2007-05-15 22:03:18 +02:00
add_rule (
$ filter_table - > { forward_chain $ interface } ,
join ( '' , match_source_net ( $ net ) , $ ipsec_match , "-j $frwd_ref->{name}" )
) ;
2007-03-15 04:25:08 +01:00
}
}
2007-03-27 01:17:46 +02:00
}
2007-03-15 04:25:08 +01:00
}
}
#
# Main source-zone matrix-generation loop
#
for my $ zone ( grep ( $ zones { $ _ } { type } ne 'firewall' , @ zones ) ) {
my $ zoneref = $ zones { $ zone } ;
my $ source_hosts_ref = $ zoneref - > { hosts } ;
my $ chain1 = rules_target $ firewall_zone , $ zone ;
my $ chain2 = rules_target $ zone , $ firewall_zone ;
2007-04-08 16:42:26 +02:00
my $ complex = $ zoneref - > { options } { complex } || 0 ;
2007-03-15 04:25:08 +01:00
my $ type = $ zoneref - > { type } ;
my $ exclusions = $ zoneref - > { exclusions } ;
2007-04-08 16:42:26 +02:00
my $ frwd_ref = 0 ;
2007-03-15 04:25:08 +01:00
my $ chain = 0 ;
2007-04-22 17:26:47 +02:00
my % needbroadcast ;
2007-03-15 04:25:08 +01:00
if ( $ complex ) {
$ frwd_ref = $ filter_table - > { "${zone}_frwd" } ;
my $ dnat_ref = ensure_chain 'nat' , dnat_chain ( $ zone ) ;
if ( @$ exclusions ) {
insert_exclusions $ dnat_ref , $ exclusions if $ dnat_ref - > { referenced } ;
}
}
2007-03-21 21:08:05 +01:00
if ( $ config { DYNAMIC_ZONES } ) {
push @ rule_chains , [ $ firewall_zone , $ zone , $ chain1 ] ;
push @ rule_chains , [ $ zone , $ firewall_zone , $ chain2 ] ;
}
2007-03-15 04:25:08 +01:00
#
# Take care of PREROUTING, INPUT and OUTPUT jumps
#
for my $ typeref ( values %$ source_hosts_ref ) {
2007-03-19 05:28:47 +01:00
for my $ interface ( keys %$ typeref ) {
my $ arrayref = $ typeref - > { $ interface } ;
2007-03-15 04:25:08 +01:00
for my $ hostref ( @$ arrayref ) {
my $ ipsec_in_match = match_ipsec_in $ zone , $ hostref ;
2007-04-08 16:42:26 +02:00
my $ ipsec_out_match = match_ipsec_out $ zone , $ hostref ;
2007-03-15 04:25:08 +01:00
for my $ net ( @ { $ hostref - > { hosts } } ) {
2007-04-26 18:56:04 +02:00
my $ dest = match_dest_net $ net ;
2007-03-15 04:25:08 +01:00
if ( $ chain1 ) {
if ( @$ exclusions ) {
2007-03-26 02:46:15 +02:00
add_rule $ filter_table - > { output_chain $ interface } , join ( '' , $ dest , $ ipsec_out_match , "-j ${zone}_output" ) ;
2007-03-15 04:25:08 +01:00
add_rule $ filter_table - > { "${zone}_output" } , "-j $chain1" ;
} else {
2007-03-26 02:46:15 +02:00
add_rule $ filter_table - > { output_chain $ interface } , join ( '' , $ dest , $ ipsec_out_match , "-j $chain1" ) ;
2007-03-15 04:25:08 +01:00
}
}
2007-03-27 01:17:46 +02:00
2007-04-25 23:03:40 +02:00
my $ source = match_source_net $ net ;
2007-06-06 02:47:27 +02:00
insertnatjump 'PREROUTING' , dnat_chain $ zone , \ $ prerouting_rule , join ( '' , match_source_dev ( $ interface ) , $ source , $ ipsec_in_match ) ;
2007-03-15 04:25:08 +01:00
if ( $ chain2 ) {
if ( @$ exclusions ) {
2007-03-26 02:46:15 +02:00
add_rule $ filter_table - > { input_chain $ interface } , join ( '' , $ source , $ ipsec_in_match , "-j ${zone}_input" ) ;
2007-03-15 04:25:08 +01:00
add_rule $ filter_table - > { "${zone}_input" } , "-j $chain2" ;
} else {
2007-03-26 02:46:15 +02:00
add_rule $ filter_table - > { input_chain $ interface } , join ( '' , $ source , $ ipsec_in_match , "-j $chain2" ) ;
2007-03-15 04:25:08 +01:00
}
}
2007-03-26 02:46:15 +02:00
add_rule $ filter_table - > { forward_chain $ interface } , join ( '' , $ source , $ ipsec_in_match . "-j $frwd_ref->{name}" )
2007-03-15 04:25:08 +01:00
if $ complex && $ hostref - > { ipsec } ne 'ipsec' ;
2007-04-22 17:26:47 +02:00
$ needbroadcast { $ interface } = 1 if get_interface_option $ interface , 'detectnets' ;
2007-03-15 04:25:08 +01:00
}
}
}
}
2007-04-22 17:26:47 +02:00
if ( $ chain1 ) {
for my $ interface ( keys % needbroadcast ) {
2007-05-06 18:10:00 +02:00
add_rule $ filter_table - > { output_chain $ interface } , "-m addrtype --dst-type BROADCAST -j $chain1" ;
2007-05-15 22:40:15 +02:00
add_rule $ filter_table - > { output_chain $ interface } , "-d 224.0.0.0/4 -j $chain1" ;
2007-04-22 17:26:47 +02:00
}
}
2007-03-15 04:25:08 +01:00
#
# F O R W A R D I N G
#
my @ dest_zones ;
my $ last_chain = '' ;
if ( $ config { OPTIMIZE } > 0 ) {
my @ temp_zones ;
ZONE1:
for my $ zone1 ( grep $ zones { $ _ } { type } ne 'firewall' , @ zones ) {
my $ zone1ref = $ zones { $ zone1 } ;
my $ policy = $ filter_table - > { "${zone}2${zone1}" } - > { policy } ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
next if $ policy eq 'NONE' ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
my $ chain = rules_target $ zone , $ zone1 ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
next unless $ chain ;
if ( $ zone eq $ zone1 ) {
#
# One thing that the Llama fails to mention is that evaluating a hash in a numeric context produces a warning.
#
no warnings ;
2007-05-16 00:42:00 +02:00
next if ( % { $ zoneref - > { interfaces } } < 2 ) && ! ( $ zoneref - > { options } { in_out } { routeback } || @$ exclusions ) ;
2007-03-15 04:25:08 +01:00
}
2007-03-27 01:17:46 +02:00
2007-06-06 17:34:47 +02:00
if ( $ zone1ref - > { type } eq 'bport4' ) {
next unless $ zoneref - > { bridge } eq $ zone1ref - > { bridge } ;
}
2007-03-15 04:25:08 +01:00
if ( $ chain =~ /2all$/ ) {
if ( $ chain ne $ last_chain ) {
$ last_chain = $ chain ;
push @ dest_zones , @ temp_zones ;
@ temp_zones = ( $ zone1 ) ;
} elsif ( $ policy eq 'ACCEPT' ) {
push @ temp_zones , $ zone1 ;
} else {
$ last_chain = $ chain ;
@ temp_zones = ( $ zone1 ) ;
}
} else {
push @ dest_zones , @ temp_zones , $ zone1 ;
@ temp_zones = ( ) ;
$ last_chain = '' ;
}
}
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
if ( $ last_chain && @ temp_zones == 1 ) {
push @ dest_zones , @ temp_zones ;
$ last_chain = '' ;
}
} else {
@ dest_zones = grep $ zones { $ _ } { type } ne 'firewall' , @ zones ;
}
#
# Here it is -- THE BIG UGLY!!!!!!!!!!!!
#
# We now loop through the destination zones creating jumps to the rules chain for each source/dest combination.
# @dest_zones is the list of destination zones that we need to handle from this source zone
#
ZONE1:
for my $ zone1 ( @ dest_zones ) {
my $ zone1ref = $ zones { $ zone1 } ;
my $ policy = $ filter_table - > { "${zone}2${zone1}" } - > { policy } ;
next if $ policy eq 'NONE' ;
my $ chain = rules_target $ zone , $ zone1 ;
2007-05-07 16:28:23 +02:00
next unless $ chain ; # CONTINUE policy with no rules
2007-03-21 21:08:05 +01:00
push @ rule_chains , [ $ zone , $ zone1 , $ chain ] if $ config { DYNAMIC_ZONES } ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
my $ num_ifaces = 0 ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
if ( $ zone eq $ zone1 ) {
#
# One thing that the Llama fails to mention is that evaluating a hash in a numeric context produces a warning.
#
no warnings ;
2007-03-20 15:06:00 +01:00
next ZONE1 if ( $ num_ifaces = % { $ zoneref - > { interfaces } } ) < 2 && ! ( $ zoneref - > { options } { in_out } { routeback } || @$ exclusions ) ;
2007-03-15 04:25:08 +01:00
}
2007-06-06 17:34:47 +02:00
if ( $ zone1ref - > { type } eq 'bport4' ) {
next ZONE1 unless $ zoneref - > { bridge } eq $ zone1ref - > { bridge } ;
}
2007-03-15 04:25:08 +01:00
my $ chainref = $ filter_table - > { $ chain } ;
my $ exclusions1 = $ zone1ref - > { exclusions } ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
my $ dest_hosts_ref = $ zone1ref - > { hosts } ;
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
if ( @$ exclusions1 ) {
if ( $ chain eq "all2$zone1" ) {
unless ( $ chain_exclusions { $ chain } ) {
$ chain_exclusions { $ chain } = 1 ;
insert_exclusions $ chainref , $ exclusions1 ;
}
} elsif ( $ chain =~ /2all$/ ) {
my $ chain1 = $ policy_exclusions { "${chain}_${zone1}" } ;
2007-03-27 01:17:46 +02:00
2007-05-06 17:43:30 +02:00
unless ( $ chain1 ) {
2007-03-15 04:25:08 +01:00
$ chain1 = newexclusionchain ;
$ policy_exclusions { "${chain}_${zone1}" } = $ chain1 ;
my $ chain1ref = ensure_filter_chain $ chain1 , 0 ;
add_exclusions $ chain1ref , $ exclusions1 ;
add_rule $ chain1ref , "-j $chain" ;
}
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
$ chain = $ chain1 ;
2007-06-08 22:10:43 +02:00
} else {
fatal_error "Fatal Error in generate_matrix()" if $ chain eq 'ACCEPT' ;
2007-03-15 04:25:08 +01:00
insert_exclusions $ chainref , $ exclusions1 ;
}
}
2007-03-27 01:17:46 +02:00
2007-03-15 04:25:08 +01:00
if ( $ complex ) {
for my $ typeref ( values %$ dest_hosts_ref ) {
2007-03-19 05:28:47 +01:00
for my $ interface ( keys %$ typeref ) {
my $ arrayref = $ typeref - > { $ interface } ;
2007-03-15 04:25:08 +01:00
for my $ hostref ( @$ arrayref ) {
if ( $ zone ne $ zone1 || $ num_ifaces > 1 || $ hostref - > { options } { routeback } ) {
2007-04-08 16:42:26 +02:00
my $ ipsec_out_match = match_ipsec_out $ zone1 , $ hostref ;
2007-03-15 04:25:08 +01:00
for my $ net ( @ { $ hostref - > { hosts } } ) {
2007-06-06 02:47:27 +02:00
add_rule $ frwd_ref , join ( '' , match_dest_dev ( $ interface ) , match_dest_net ( $ net ) , $ ipsec_out_match , "-j $chain" ) ;
2007-03-15 04:25:08 +01:00
}
}
}
}
}
} else {
for my $ typeref ( values %$ source_hosts_ref ) {
2007-03-19 05:28:47 +01:00
for my $ interface ( keys %$ typeref ) {
my $ arrayref = $ typeref - > { $ interface } ;
2007-03-15 04:25:08 +01:00
my $ chain3ref = $ filter_table - > { forward_chain $ interface } ;
for my $ hostref ( @$ arrayref ) {
for my $ net ( @ { $ hostref - > { hosts } } ) {
for my $ type1ref ( values %$ dest_hosts_ref ) {
2007-03-19 05:28:47 +01:00
for my $ interface1 ( keys %$ type1ref ) {
my $ array1ref = $ type1ref - > { $ interface1 } ;
2007-03-15 04:25:08 +01:00
for my $ host1ref ( @$ array1ref ) {
2007-04-08 16:42:26 +02:00
my $ ipsec_out_match = match_ipsec_out $ zone1 , $ host1ref ;
2007-03-15 04:25:08 +01:00
for my $ net1 ( @ { $ host1ref - > { hosts } } ) {
unless ( $ interface eq $ interface1 && $ net eq $ net1 && ! $ host1ref - > { options } { routeback } ) {
2007-04-26 18:56:04 +02:00
#
2007-05-07 16:28:23 +02:00
# We defer evaluation of the source net match to accomodate systems without $capabilities{KLUDEFREE};
2007-04-26 18:56:04 +02:00
#
2007-05-15 22:03:18 +02:00
add_rule (
$ chain3ref ,
2007-06-06 15:49:30 +02:00
join ( '' , match_dest_dev ( $ interface1 ) , match_source_net ( $ net ) , match_dest_net ( $ net1 ) , $ ipsec_out_match , "-j $chain" )
2007-05-15 22:03:18 +02:00
) ;
2007-03-15 04:25:08 +01:00
}
}
}
}
}
}
}
}
}
}
#
# E N D F O R W A R D I N G
#
# Now add (an) unconditional jump(s) to the last unique policy-only chain determined above, if any
#
if ( $ last_chain ) {
if ( $ complex ) {
add_rule $ frwd_ref , "-j $last_chain" ;
} else {
for my $ typeref ( values %$ source_hosts_ref ) {
2007-03-19 05:28:47 +01:00
for my $ interface ( keys %$ typeref ) {
my $ arrayref = $ typeref - > { $ interface } ;
2007-03-15 04:25:08 +01:00
my $ chain2ref = $ filter_table - > { forward_chain $ interface } ;
for my $ hostref ( @$ arrayref ) {
for my $ net ( @ { $ hostref - > { hosts } } ) {
add_rule $ chain2ref , match_source_net ( $ net ) . "-j $last_chain" ;
}
}
}
}
}
}
}
}
#
# Now add the jumps to the interface chains from FORWARD, INPUT, OUTPUT and POSTROUTING
#
for my $ interface ( @ interfaces ) {
2007-06-06 02:47:27 +02:00
add_rule $ filter_table - > { FORWARD } , match_source_dev ( $ interface ) . "-j " . forward_chain $ interface ;
add_rule $ filter_table - > { INPUT } , match_source_dev ( $ interface ) . "-j " . input_chain $ interface ;
add_rule $ filter_table - > { OUTPUT } , "-o $interface -j " . output_chain $ interface unless $ interfaces { $ interface } { options } { port } ;
addnatjump 'POSTROUTING' , masq_chain ( $ interface ) , match_dest_dev ( $ interface ) ;
2007-03-15 04:25:08 +01:00
}
my $ chainref = $ filter_table - > { "${firewall_zone}2${firewall_zone}" } ;
add_rule $ filter_table - > { OUTPUT } , "-o lo -j " . ( $ chainref - > { referenced } ? "$chainref->{name}" : 'ACCEPT' ) ;
add_rule $ filter_table - > { INPUT } , '-i lo -j ACCEPT' ;
my % builtins = ( mangle = > [ qw/PREROUTING INPUT FORWARD POSTROUTING/ ] ,
nat = > [ qw/PREROUTING OUTPUT POSTROUTING/ ] ,
filter = > [ qw/INPUT FORWARD OUTPUT/ ] ) ;
2007-04-09 19:26:28 +02:00
complete_standard_chain $ filter_table - > { INPUT } , 'all' , $ firewall_zone ;
complete_standard_chain $ filter_table - > { OUTPUT } , $ firewall_zone , 'all' ;
complete_standard_chain $ filter_table - > { FORWARD } , 'all' , 'all' ;
2007-03-15 04:25:08 +01:00
if ( $ config { LOGALLNEW } ) {
for my $ table qw/mangle nat filter/ {
for my $ chain ( @ { $ builtins { $ table } } ) {
2007-04-08 16:42:26 +02:00
log_rule_limit
$ config { LOGALLNEW } ,
2007-03-15 04:25:08 +01:00
$ chain_table { $ table } { $ chain } ,
$ table ,
$ chain ,
'' ,
'' ,
'insert' ,
2007-04-24 02:16:53 +02:00
'-m state --state NEW ' ;
2007-03-15 04:25:08 +01:00
}
}
}
}
2007-03-17 00:57:43 +01:00
sub setup_mss ( $ ) {
my $ clampmss = $ _ [ 0 ] ;
2007-06-14 01:50:26 +02:00
my $ option ;
my $ match = '' ;
2007-03-17 00:57:43 +01:00
2007-06-14 01:50:26 +02:00
if ( "\L$clampmss" eq 'yes' ) {
$ option = '--clamp-mss-to-pmtu' ;
} else {
2007-06-16 16:27:02 +02:00
$ match = "-m tcpmss --mss $clampmss: " if $ capabilities { TCPMSS_MATCH } ;
2007-06-16 16:56:17 +02:00
$ option = "--set-mss $clampmss" ;
2007-06-14 01:50:26 +02:00
}
add_rule $ filter_table - > { FORWARD } , "-p tcp --tcp-flags SYN,RST SYN ${match}-j TCPMSS $option" ;
2007-03-17 00:57:43 +01:00
}
2007-03-21 21:35:40 +01:00
sub dump_rule_chains () {
for my $ arrayref ( @ rule_chains ) {
emit_unindented "@$arrayref" ;
}
2007-03-27 01:17:46 +02:00
}
2007-03-21 21:35:40 +01:00
2007-03-15 04:17:56 +01:00
1 ;