2007-03-15 22:55:22 +01:00
#
2007-12-01 18:45:43 +01:00
# Shorewall-perl 4.1 -- /usr/share/shorewall-perl/Shorewall/Chains.pm
2007-03-15 22:55:22 +01:00
#
2007-09-08 18:09:51 +02:00
# This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt]
2007-03-15 22:55:22 +01:00
#
# (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
2007-09-08 18:09:51 +02:00
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2007-03-15 22:55:22 +01:00
#
2007-07-26 20:36:18 +02:00
# This is the low-level iptables module. It provides the basic services
2007-04-19 01:43:54 +02:00
# of chain and rule creation. It is used by the higher level modules such
# as Rules to create iptables-restore input.
2007-03-15 22:55:22 +01:00
#
2007-03-14 04:12:22 +01:00
package Shorewall::Chains ;
require Exporter ;
2007-11-16 00:24:54 +01:00
use Shorewall::Config qw( :DEFAULT :internal ) ;
2007-03-15 01:34:17 +01:00
use Shorewall::Zones ;
2007-05-08 20:25:16 +02:00
use Shorewall::IPAddrs ;
2007-03-15 01:34:17 +01:00
use strict ;
2007-03-14 04:12:22 +01:00
our @ ISA = qw( Exporter ) ;
2007-11-16 00:24:54 +01:00
our @ EXPORT = qw(
2007-03-15 01:34:17 +01:00
add_rule
2007-03-14 04:12:22 +01:00
insert_rule
2007-03-14 05:06:32 +01:00
new_chain
2007-11-16 00:24:54 +01:00
new_manual_chain
ensure_manual_chain
2007-03-15 01:34:17 +01:00
log_rule_limit
2007-03-27 01:17:46 +02:00
2007-09-11 19:29:41 +02:00
% chain_table
2007-04-08 16:42:26 +02:00
$ nat_table
$ mangle_table
2007-03-14 05:29:14 +01:00
$ filter_table
2007-03-15 01:34:17 +01:00
) ;
2007-11-16 00:24:54 +01:00
our % EXPORT_TAGS = (
internal = > [ qw( STANDARD
NATRULE
BUILTIN
NONAT
NATONLY
REDIRECT
ACTION
MACRO
LOGRULE
NFQ
CHAIN
NO_RESTRICT
PREROUTE_RESTRICT
INPUT_RESTRICT
OUTPUT_RESTRICT
POSTROUTE_RESTRICT
ALL_RESTRICT
add_command
add_commands
process_comment
2007-12-07 00:49:21 +01:00
no_comment
2007-11-16 00:24:54 +01:00
clear_comment
incr_cmd_level
decr_cmd_level
chain_base
forward_chain
input_chain
output_chain
masq_chain
syn_flood_chain
mac_chain
macrecent_target
dynamic_fwd
dynamic_in
dynamic_out
dynamic_chains
dnat_chain
snat_chain
ecn_chain
first_chains
ensure_chain
ensure_mangle_chain
new_standard_chain
new_builtin_chain
2007-12-17 21:53:27 +01:00
new_nat_chain
2007-11-16 00:24:54 +01:00
ensure_filter_chain
initialize_chain_table
finish_section
setup_zone_mss
newexclusionchain
clearrule
validate_port
proto_name
do_proto
mac_match
verify_mark
verify_small_mark
validate_mark
do_test
do_ratelimit
do_user
do_tos
match_source_dev
match_dest_dev
iprange_match
match_source_net
match_dest_net
match_orig_dest
match_ipsec_in
match_ipsec_out
log_rule
expand_rule
addnatjump
insertnatjump
get_interface_address
get_interface_addresses
get_interface_bcasts
2007-11-17 17:48:25 +01:00
get_interface_gateway
2007-11-16 17:44:02 +01:00
get_interface_mac
2007-11-16 00:24:54 +01:00
set_global_variables
create_netfilter_load
create_chainlist_reload
$ section
% sections
% targets
) ] ,
) ;
Exporter:: export_ok_tags ( 'internal' ) ;
2007-12-07 01:01:06 +01:00
our $ VERSION = 4.1 .2 ;
2007-03-14 04:12:22 +01:00
#
# Chain Table
#
2007-09-12 17:03:47 +02:00
# %chain_table { <table> => { <chain1> => { name => <chain name>
# table => <table name>
2007-11-16 00:24:54 +01:00
# is_policy => undef|1 -- if 1, this is a policy chain
# is_optional => undef|1 -- See below.
# referenced => undef|1 -- If 1, will be written to the iptables-restore-input.
# builtin => undef|1 -- If 1, one of Netfilter's built-in chains.
# manual => undef|1 -- If 1, a manual chain.
2007-09-12 17:03:47 +02:00
# log => <logging rule number for use when LOGRULENUMBERS>
# policy => <policy>
# policychain => <name of policy chain> -- self-reference if this is a policy chain
# policypair => [ <policy source>, <policy dest> ] -- Used for reporting duplicated policies
# loglevel => <level>
# synparams => <burst/limit>
# synchain => <name of synparam chain>
# default => <default action>
# cmdlevel => <number of open loops or blocks in runtime commands>
# rules => [ <rule1>
# <rule2>
# ...
# ]
# } ,
# <chain2> => ...
2007-09-11 19:29:41 +02:00
# }
2007-03-30 22:18:48 +02:00
# }
2007-03-14 04:12:22 +01:00
#
# 'is_optional' only applies to policy chains; when true, indicates that this is a provisional policy chain which might be
2007-11-16 00:24:54 +01:00
# replaced. Policy chains created under the IMPLICIT_CONTINUE=Yes option are marked with is_optional == 1.
2007-03-14 04:12:22 +01:00
#
2007-04-16 21:50:29 +02:00
# Only 'referenced' chains get written to the iptables-restore input.
2007-03-14 04:12:22 +01:00
#
2007-07-19 20:36:04 +02:00
# 'loglevel', 'synparams', 'synchain' and 'default' only apply to policy chains.
2007-03-14 04:12:22 +01:00
#
2007-09-11 19:29:41 +02:00
our % chain_table ;
2007-06-15 00:07:45 +02:00
our $ nat_table ;
our $ mangle_table ;
our $ filter_table ;
2007-07-21 17:13:50 +02:00
#
# It is a layer violation to keep information about the rules file sections in this module but in Shorewall, the rules file
# and the filter table are very closely tied. By keeping the information here, we avoid making several other modules dependent
2007-07-24 18:54:13 +02:00
# on Shorewall::Rules.
2007-07-21 17:13:50 +02:00
#
2007-06-14 01:02:39 +02:00
our % sections ;
our $ section ;
2007-07-21 17:13:50 +02:00
2007-06-15 00:07:45 +02:00
our $ comment ;
2007-06-14 01:02:39 +02:00
2007-03-15 01:34:17 +01:00
use constant { STANDARD = > 1 , #defined by Netfilter
2007-05-03 18:30:59 +02:00
NATRULE = > 2 , #Involves NAT
2007-03-15 01:34:17 +01:00
BUILTIN = > 4 , #A built-in action
NONAT = > 8 , #'NONAT' or 'ACCEPT+'
NATONLY = > 16 , #'DNAT-' or 'REDIRECT-'
REDIRECT = > 32 , #'REDIRECT'
2007-05-03 18:30:59 +02:00
ACTION = > 64 , #An action (may be built-in)
2007-03-15 01:34:17 +01:00
MACRO = > 128 , #A Macro
LOGRULE = > 256 , #'LOG'
2007-08-26 17:12:04 +02:00
NFQ = > 512 , #'NFQUEUE'
2007-11-16 00:24:54 +01:00
CHAIN = > 1024 , #Manual Chain
2007-03-15 01:34:17 +01:00
} ;
2007-06-15 00:07:45 +02:00
2007-06-14 01:02:39 +02:00
our % targets ;
2007-03-15 01:34:17 +01:00
#
2007-06-14 01:02:39 +02:00
# expand_rule() restrictions
2007-03-15 01:34:17 +01:00
#
2007-06-14 01:02:39 +02:00
use constant { NO_RESTRICT = > 0 , # FORWARD chain rule - Both -i and -o may be used in the rule
PREROUTE_RESTRICT = > 1 , # PREROUTING chain rule - -o converted to -d <address list> using main routing table
INPUT_RESTRICT = > 4 , # INPUT chain rule - -o not allowed
OUTPUT_RESTRICT = > 8 , # OUTPUT chain rule - -i not allowed
POSTROUTE_RESTRICT = > 16 , # POSTROUTING chain rule - -i converted to -s <address list> using main routing table
ALL_RESTRICT = > 12 # fw->fw rule - neither -i nor -o allowed
} ;
2007-06-15 00:07:45 +02:00
our $ exclseq ;
our $ iprangematch ;
2007-06-14 01:02:39 +02:00
our $ chainseq ;
2007-06-23 18:06:16 +02:00
our % interfaceaddr ;
our % interfaceaddrs ;
our % interfacenets ;
2007-11-17 17:48:25 +01:00
our % interfacemacs ;
2007-07-26 01:15:37 +02:00
our % interfacebcasts ;
2007-11-17 17:48:25 +01:00
our % interfacegateways ;
2007-06-23 18:06:16 +02:00
2007-06-23 18:32:37 +02:00
our @ builtins = qw( PREROUTING INPUT FORWARD OUTPUT POSTROUTING ) ;
#
2007-08-09 17:16:08 +02:00
# Mode of the generator.
2007-06-23 18:32:37 +02:00
#
2007-08-09 17:16:08 +02:00
use constant { NULL_MODE = > 0 , # Generating neither shell commands nor iptables-restore input
CAT_MODE = > 1 , # Generating iptables-restore input
CMD_MODE = > 2 } ; # Generating shell commands.
2007-06-23 18:32:37 +02:00
2007-08-09 17:16:08 +02:00
our $ mode ;
2007-06-23 18:32:37 +02:00
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
2007-07-26 20:36:18 +02:00
# the second and subsequent calls to that function.
2007-06-15 00:07:45 +02:00
#
2007-06-14 01:02:39 +02:00
sub initialize () {
2007-09-12 17:03:47 +02:00
% chain_table = ( raw = > { } ,
mangle = > { } ,
nat = > { } ,
filter = > { } ) ;
2007-09-11 19:29:41 +02:00
$ nat_table = $ chain_table { nat } ;
$ mangle_table = $ chain_table { mangle } ;
$ filter_table = $ chain_table { filter } ;
2007-06-14 01:02:39 +02:00
#
# These get set to 1 as sections are encountered.
#
% sections = ( ESTABLISHED = > 0 ,
RELATED = > 0 ,
NEW = > 0
) ;
#
# Current rules file section.
#
$ section = 'ESTABLISHED' ;
#
# Contents of last COMMENT line.
#
$ comment = '' ;
#
# As new targets (Actions and Macros) are discovered, they are added to the table
#
% targets = ( 'ACCEPT' = > STANDARD ,
2007-03-15 01:34:17 +01:00
'ACCEPT+' = > STANDARD + NONAT ,
'ACCEPT!' = > STANDARD ,
2007-04-19 18:23:40 +02:00
'NONAT' = > STANDARD + NONAT + NATONLY ,
2007-03-15 01:34:17 +01:00
'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 ,
2007-04-30 17:07:51 +02:00
'CONTINUE!' = > STANDARD ,
2007-03-15 01:34:17 +01:00
'QUEUE' = > STANDARD ,
2007-04-30 17:07:51 +02:00
'QUEUE!' = > STANDARD ,
2007-08-26 17:12:04 +02:00
'NFQUEUE' = > STANDARD + NFQ ,
'NFQUEUE!' = > STANDARD + NFQ ,
2007-03-15 01:34:17 +01:00
'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 ,
) ;
2007-06-14 01:02:39 +02:00
#
# Used to sequence 'exclusion' chains with names 'excl0', 'excl1', ...
#
$ exclseq = 0 ;
#
# Used to suppress duplicate match specifications.
#
$ iprangematch = 0 ;
#
# Sequence for naming temporary chains
#
2007-06-19 01:04:17 +02:00
$ chainseq = undef ;
2007-06-23 18:06:16 +02:00
#
2008-01-03 23:18:24 +01:00
# Keep track of which interfaces have active 'address', 'addresses', 'networks', etc. variables
2007-06-23 18:06:16 +02:00
#
2007-11-17 17:48:25 +01:00
% interfaceaddr = ( ) ;
% interfaceaddrs = ( ) ;
% interfacenets = ( ) ;
% interfacemacs = ( ) ;
% interfacebcasts = ( ) ;
% interfacegateways = ( ) ;
2007-06-14 01:02:39 +02:00
}
INIT {
initialize ;
}
2007-03-18 19:50:34 +01:00
2007-03-14 04:12:22 +01:00
#
2007-03-18 22:16:46 +01:00
# Add a run-time command to a chain. Arguments are:
#
# Chain reference , Command
#
2007-03-23 18:30:12 +01:00
2007-05-09 20:22:40 +02:00
#
2007-07-26 20:36:18 +02:00
# Process a COMMENT line (in $currentline)
2007-05-09 20:22:40 +02:00
#
sub process_comment () {
if ( $ capabilities { COMMENTS } ) {
2007-07-07 18:34:38 +02:00
( $ comment = $ currentline ) =~ s/^\s*COMMENT\s*// ;
2007-06-30 18:43:08 +02:00
$ comment =~ s/\s*$// ;
2007-05-09 20:22:40 +02:00
} else {
warning_message "COMMENT ignored -- requires comment support in iptables/Netfilter" ;
}
}
2007-09-10 17:52:57 +02:00
2007-12-07 00:49:21 +01:00
#
# Returns True if there is a current COMMENT or if COMMENTS are not available.
#
sub no_comment () {
$ comment ? 1 : $ capabilities { COMMENTS } ? 0 : 1 ;
}
2007-09-10 17:52:57 +02:00
#
# Clear the $comment variable
#
sub clear_comment () {
$ comment = '' ;
}
2007-04-28 15:48:28 +02:00
#
2007-08-26 17:12:04 +02:00
# Functions to manipulate cmdlevel
2007-04-28 15:48:28 +02:00
#
2007-08-26 17:12:04 +02:00
sub incr_cmd_level ( $ ) {
$ _ [ 0 ] - > { cmdlevel } + + ;
2007-04-28 15:48:28 +02:00
}
2007-08-26 17:12:04 +02:00
sub decr_cmd_level ( $ ) {
fatal_error "Internal error in decr_cmd_level()" if - - $ _ [ 0 ] - > { cmdlevel } < 0 ;
2007-04-28 15:48:28 +02:00
}
2007-03-18 22:16:46 +01:00
sub add_command ($$)
2007-03-14 04:12:22 +01:00
{
2007-03-18 22:16:46 +01:00
my ( $ chainref , $ command ) = @ _ ;
2007-03-27 01:17:46 +02:00
2007-08-26 17:12:04 +02:00
push @ { $ chainref - > { rules } } , join ( '' , ' ' x $ chainref - > { cmdlevel } , $ command ) ;
2007-03-14 04:12:22 +01:00
$ chainref - > { referenced } = 1 ;
}
2007-03-23 20:19:43 +01:00
2007-04-27 18:18:42 +02:00
sub add_commands {
my $ chainref = shift @ _ ;
2007-07-26 20:36:18 +02:00
2007-04-27 18:18:42 +02:00
for my $ command ( @ _ ) {
2007-08-26 17:12:04 +02:00
push @ { $ chainref - > { rules } } , join ( '' , ' ' x $ chainref - > { cmdlevel } , $ command ) ;
2007-04-27 18:18:42 +02:00
}
$ chainref - > { referenced } = 1 ;
}
2007-10-19 21:43:14 +02:00
sub push_rule ( $$ ) {
2007-03-18 22:16:46 +01:00
my ( $ chainref , $ rule ) = @ _ ;
2007-03-27 01:17:46 +02:00
2007-07-28 21:17:00 +02:00
$ rule . = qq( -m comment --comment "$comment" ) if $ comment ;
2007-08-26 17:12:04 +02:00
if ( $ chainref - > { cmdlevel } ) {
2007-07-28 19:23:03 +02:00
$ rule =~ s/"/\\"/g ; #Must preserve quotes in the rule
2007-03-23 18:30:12 +01:00
add_command $ chainref , qq( echo "-A $chainref->{name} $rule" >&3 ) ;
} else {
2007-07-23 21:00:07 +02:00
push @ { $ chainref - > { rules } } , join ( ' ' , '-A' , $ chainref - > { name } , $ rule ) ;
2007-03-23 18:30:12 +01:00
$ chainref - > { referenced } = 1 ;
}
2007-03-18 19:50:34 +01:00
}
2007-10-19 21:43:14 +02:00
#
# Add a rule to a chain. Arguments are:
#
# Chain reference , Rule [, Expand-long-dest-port-lists ]
#
sub add_rule ( $ $ ; $ )
{
my ( $ chainref , $ rule , $ expandports ) = @ _ ;
$ iprangematch = 0 ;
#
# Pre-processing the port lists as was done in Shorewall-shell results in port-list
# processing driving the rest of rule generation.
#
# By post-processing each rule generated by expand_rule(), we avoid all of that
# messiness and replace it with the following localized messiness.
#
# Because source ports are seldom specified and source port lists are rarer still,
# we only worry about the destination ports.
#
if ( $ expandports && $ rule =~ '^(.* --dports\s+)([^ ]+)(.*)$' ) {
my ( $ first , $ ports , $ rest ) = ( $ 1 , $ 2 , $ 3 ) ;
if ( ( $ ports =~ tr /:,/ : , / ) > 15 ) {
my @ ports = split '([,:])' , $ ports ;
while ( @ ports ) {
my $ count = 0 ;
my $ newports = '' ;
while ( @ ports && $ count < 15 ) {
my ( $ port , $ separator ) = ( shift @ ports , shift @ ports ) ;
$ separator || = '' ;
if ( + + $ count == 15 ) {
if ( $ separator eq ':' ) {
unshift @ ports , $ port , ':' ;
2007-11-16 00:24:54 +01:00
chop $ newports ;
2007-10-19 21:43:14 +02:00
last ;
} else {
$ newports . = $ port ;
}
} else {
$ newports . = "${port}${separator}" ;
}
}
push_rule ( $ chainref , join ( '' , $ first , $ newports , $ rest ) ) ;
}
} else {
push_rule ( $ chainref , $ rule ) ;
}
} else {
push_rule ( $ chainref , $ rule ) ;
}
}
2007-03-14 04:12:22 +01:00
#
# Insert a rule into a chain. Arguments are:
#
2007-07-29 02:03:34 +02:00
# Chain reference , Rule Number, Rule
2007-03-14 04:12:22 +01:00
#
sub insert_rule ($$$)
{
my ( $ chainref , $ number , $ rule ) = @ _ ;
2007-03-23 18:30:12 +01:00
2007-08-26 17:12:04 +02:00
fatal_error 'Internal Error in insert_rule()' if $ chainref - > { cmdlevel } ;
2007-03-27 01:17:46 +02:00
2007-03-14 04:12:22 +01:00
$ rule . = "-m comment --comment \"$comment\"" if $ comment ;
2007-07-23 21:00:07 +02:00
splice ( @ { $ chainref - > { rules } } , $ number - 1 , 0 , join ( ' ' , '-A' , $ chainref - > { name } , $ rule ) ) ;
2007-03-14 04:12:22 +01:00
2007-06-20 20:58:44 +02:00
$ iprangematch = 0 ;
2007-03-14 04:12:22 +01:00
$ chainref - > { referenced } = 1 ;
2007-03-27 01:17:46 +02:00
2007-03-14 04:12:22 +01:00
}
#
2007-04-08 16:42:26 +02:00
# Form the name of a chain.
2007-03-14 04:12:22 +01:00
#
sub chain_base ($) {
my $ chain = $ _ [ 0 ] ;
$ chain =~ s/^@/at_/ ;
2007-03-26 17:22:36 +02:00
$ chain =~ tr /[.\-%@]/ _ / ;
2007-03-25 23:04:24 +02:00
$ chain =~ s/\+$// ;
2007-03-14 04:12:22 +01:00
$ chain ;
}
2007-08-26 17:12:04 +02:00
sub chain_base_cond ($) {
$ config { DYNAMIC_ZONES } ? chain_base ( $ _ [ 0 ] ) : $ _ [ 0 ] ;
}
2007-07-29 17:27:08 +02:00
#
# Forward Chain for an interface
#
sub forward_chain ($)
{
2007-08-26 17:12:04 +02:00
chain_base_cond ( $ _ [ 0 ] ) . '_fwd' ;
2007-07-29 17:27:08 +02:00
}
2007-03-14 04:12:22 +01:00
#
# Input Chain for an interface
#
sub input_chain ($)
{
2007-08-26 17:12:04 +02:00
chain_base_cond ( $ _ [ 0 ] ) . '_in' ;
2007-03-14 04:12:22 +01:00
}
#
# Output Chain for an interface
#
sub output_chain ($)
{
2007-08-26 17:12:04 +02:00
chain_base_cond ( $ _ [ 0 ] ) . '_out' ;
2007-03-14 04:12:22 +01:00
}
#
# Masquerade Chain for an interface
#
sub masq_chain ($)
{
2007-08-26 17:12:04 +02:00
chain_base_cond ( $ _ [ 0 ] ) . '_masq' ;
2007-03-14 04:12:22 +01:00
}
#
2007-07-29 17:36:56 +02:00
# Syn_flood_chain -- differs from the other _chain functions in that the argument is a chain table reference
2007-03-14 04:12:22 +01:00
#
2007-07-29 17:27:08 +02:00
sub syn_flood_chain ( $ ) {
'@' . $ _ [ 0 ] - > { synchain } ;
2007-03-14 04:12:22 +01:00
}
2007-07-29 17:27:08 +02:00
2007-03-14 04:12:22 +01:00
#
# MAC Verification Chain for an interface
#
sub mac_chain ( $ )
{
2007-08-26 17:12:04 +02:00
chain_base_cond ( $ _ [ 0 ] ) . '_mac' ;
2007-03-14 04:12:22 +01:00
}
sub macrecent_target ($)
{
2007-08-26 17:12:04 +02:00
$ config { MACLIST_TTL } ? chain_base_cond ( $ _ [ 0 ] ) . '_rec' : 'RETURN' ;
2007-03-14 04:12:22 +01:00
}
#
# Functions for creating dynamic zone rules
#
sub dynamic_fwd ( $ )
{
2007-08-26 17:12:04 +02:00
chain_base_cond ( $ _ [ 0 ] ) . '_dynf' ;
2007-03-14 04:12:22 +01:00
}
sub dynamic_in ( $ )
{
2007-08-26 17:12:04 +02:00
chain_base_cond ( $ _ [ 0 ] ) . '_dyni' ;
2007-03-14 04:12:22 +01:00
}
sub dynamic_out ( $ ) # $ 1 = interface
{
2007-08-26 17:12:04 +02:00
chain_base_cond ( $ _ [ 0 ] ) . '_dyno' ;
2007-03-14 04:12:22 +01:00
}
sub dynamic_chains ( $ ) # $ 1 = interface
{
2007-08-26 17:12:04 +02:00
my $ c = chain_base_cond ( $ _ [ 0 ] ) ;
2007-03-14 04:12:22 +01:00
2007-10-19 21:43:14 +02:00
( $ c . '_dyni' , $ c . '_dynf' , $ c . '_dyno' ) ;
2007-03-14 04:12:22 +01:00
}
#
# DNAT Chain from a zone
#
sub dnat_chain ( $ )
{
2007-08-26 17:12:04 +02:00
chain_base_cond ( $ _ [ 0 ] ) . '_dnat' ;
2007-03-14 04:12:22 +01:00
}
#
# SNAT Chain to an interface
#
sub snat_chain ( $ )
{
2007-08-26 17:12:04 +02:00
chain_base_cond ( $ _ [ 0 ] ) . '_snat' ;
2007-03-14 04:12:22 +01:00
}
#
# ECN Chain to an interface
#
sub ecn_chain ( $ )
{
2007-08-26 17:12:04 +02:00
chain_base_cond ( $ _ [ 0 ] ) . '_ecn' ;
2007-03-14 04:12:22 +01:00
}
#
# First chains for an interface
#
sub first_chains ( $ ) # $ 1 = interface
{
2007-08-26 17:12:04 +02:00
my $ c = chain_base_cond ( $ _ [ 0 ] ) ;
2007-03-14 04:12:22 +01:00
2007-10-19 21:43:14 +02:00
( $ c . '_fwd' , $ c . '_in' ) ;
2007-03-14 04:12:22 +01:00
}
2007-03-14 05:06:32 +01:00
#
# Create a new chain and return a reference to it.
#
2007-09-12 17:03:47 +02:00
sub new_chain ($$)
2007-03-14 05:06:32 +01:00
{
2007-09-12 17:03:47 +02:00
my ( $ table , $ chain ) = @ _ ;
2007-03-27 01:17:46 +02:00
2007-09-12 17:03:47 +02:00
warning_message "Internal error in new_chain()" if $ chain_table { $ table } { $ chain } ;
2007-08-26 17:12:04 +02:00
2007-09-12 17:03:47 +02:00
$ chain_table { $ table } { $ chain } = { name = > $ chain ,
rules = > [] ,
table = > $ table ,
loglevel = > '' ,
log = > 1 ,
cmdlevel = > 0 } ;
}
2007-03-14 05:06:32 +01:00
#
# Create a chain if it doesn't exist already
#
2007-09-12 17:03:47 +02:00
sub ensure_chain ($$)
2007-03-14 05:06:32 +01:00
{
2007-09-12 17:03:47 +02:00
my ( $ table , $ chain ) = @ _ ;
2007-03-14 05:06:32 +01:00
2007-09-12 17:03:47 +02:00
my $ ref = $ chain_table { $ table } { $ chain } ;
2007-03-27 01:17:46 +02:00
2007-03-14 05:06:32 +01:00
return $ ref if $ ref ;
2007-09-12 17:03:47 +02:00
new_chain $ table , $ chain ;
2007-03-14 05:06:32 +01:00
}
sub finish_chain_section ( $ $ ) ;
#
# Create a filter chain if necessary. Optionally populate it with the appropriate ESTABLISHED,RELATED rule(s) and perform SYN rate limiting.
#
2007-09-12 17:03:47 +02:00
sub ensure_filter_chain ( $ $ )
2007-03-14 05:06:32 +01:00
{
2007-09-12 17:03:47 +02:00
my ( $ chain , $ populate ) = @ _ ;
2007-03-14 05:06:32 +01:00
2007-09-12 17:03:47 +02:00
my $ chainref = $ filter_table - > { $ chain } ;
2007-03-14 05:06:32 +01:00
2007-09-12 17:03:47 +02:00
$ chainref = new_chain 'filter' , $ chain unless $ chainref ;
2007-03-14 05:06:32 +01:00
if ( $ populate and ! $ chainref - > { referenced } ) {
if ( $ section eq 'NEW' or $ section eq 'DONE' ) {
finish_chain_section $ chainref , 'ESTABLISHED,RELATED' ;
2007-05-08 02:48:05 +02:00
} elsif ( $ section eq 'RELATED' ) {
2007-03-14 05:06:32 +01:00
finish_chain_section $ chainref , 'ESTABLISHED' ;
}
}
$ chainref - > { referenced } = 1 ;
2007-03-27 01:17:46 +02:00
2007-03-14 05:06:32 +01:00
$ chainref ;
}
2007-09-12 17:03:47 +02:00
sub ensure_mangle_chain ($) {
my $ chain = $ _ [ 0 ] ;
2007-03-21 00:13:17 +01:00
2007-09-12 17:03:47 +02:00
my $ chainref = ensure_chain 'mangle' , $ chain ;
2007-03-27 01:17:46 +02:00
2007-03-21 00:13:17 +01:00
$ chainref - > { referenced } = 1 ;
$ chainref ;
}
2007-03-14 05:06:32 +01:00
#
# Add a builtin chain
#
2007-09-12 17:03:47 +02:00
sub new_builtin_chain ($$$)
2007-03-14 05:06:32 +01:00
{
2007-09-12 17:03:47 +02:00
my ( $ table , $ chain , $ policy ) = @ _ ;
2007-03-27 01:17:46 +02:00
2007-09-12 17:03:47 +02:00
my $ chainref = new_chain $ table , $ chain ;
2007-03-14 05:06:32 +01:00
$ chainref - > { referenced } = 1 ;
2007-03-25 03:03:43 +02:00
$ chainref - > { policy } = $ policy ;
2007-03-14 05:06:32 +01:00
$ chainref - > { builtin } = 1 ;
}
2007-09-12 17:03:47 +02:00
sub new_standard_chain ($) {
my $ chainref = new_chain 'filter' , $ _ [ 0 ] ;
2007-03-14 05:06:32 +01:00
$ chainref - > { referenced } = 1 ;
$ chainref ;
2007-03-27 01:17:46 +02:00
}
2007-03-14 05:06:32 +01:00
2007-12-17 21:53:27 +01:00
sub new_nat_chain ($) {
my $ chainref = new_chain 'nat' , $ _ [ 0 ] ;
$ chainref - > { referenced } = 1 ;
$ chainref ;
}
2007-11-16 00:24:54 +01:00
sub new_manual_chain ($) {
my $ chain = $ _ [ 0 ] ;
fatal_error "Duplicate Chain Name ($chain)" if $ targets { $ chain } || $ filter_table - > { $ chain } ;
$ targets { $ chain } = CHAIN ;
( my $ chainref = ensure_filter_chain ( $ chain , 0 ) ) - > { manual } = 1 ;
$ chainref - > { referenced } = 1 ;
$ chainref ;
}
sub ensure_manual_chain ($) {
my $ chain = $ _ [ 0 ] ;
my $ chainref = $ filter_table - > { $ chain } || new_manual_chain ( $ chain ) ;
fatal_error "$chain exists and is not a manual chain" unless $ chainref - > { manual } ;
$ chainref ;
}
2007-03-14 05:06:32 +01:00
#
# Add all builtin chains to the chain table
#
#
sub initialize_chain_table ()
{
2007-07-21 17:13:50 +02:00
for my $ chain qw( OUTPUT PREROUTING ) {
2007-09-12 17:03:47 +02:00
new_builtin_chain 'raw' , $ chain , 'ACCEPT' ;
2007-03-14 05:06:32 +01:00
}
2007-07-21 17:13:50 +02:00
for my $ chain qw( INPUT OUTPUT FORWARD ) {
2007-09-12 17:03:47 +02:00
new_builtin_chain 'filter' , $ chain , 'DROP' ;
2007-03-14 05:06:32 +01:00
}
2007-07-21 17:13:50 +02:00
for my $ chain qw( PREROUTING POSTROUTING OUTPUT ) {
2007-09-12 17:03:47 +02:00
new_builtin_chain 'nat' , $ chain , 'ACCEPT' ;
2007-03-14 05:06:32 +01:00
}
2007-08-26 17:12:04 +02:00
for my $ chain qw( PREROUTING INPUT OUTPUT ) {
2007-09-12 17:03:47 +02:00
new_builtin_chain 'mangle' , $ chain , 'ACCEPT' ;
2007-03-14 05:06:32 +01:00
}
2007-03-27 01:17:46 +02:00
2007-03-14 05:06:32 +01:00
if ( $ capabilities { MANGLE_FORWARD } ) {
2007-07-21 17:13:50 +02:00
for my $ chain qw( FORWARD POSTROUTING ) {
2007-09-12 17:03:47 +02:00
new_builtin_chain 'mangle' , $ chain , 'ACCEPT' ;
2007-03-14 05:06:32 +01:00
}
}
}
#
2007-04-08 16:42:26 +02:00
# Add ESTABLISHED,RELATED rules and synparam jumps to the passed chain
2007-03-14 05:06:32 +01:00
#
sub finish_chain_section ($$) {
my ( $ chainref , $ state ) = @ _ ;
my $ chain = $ chainref - > { name } ;
2007-12-08 18:37:04 +01:00
my $ savecomment = $ comment ;
$ comment = '' ;
2007-03-14 05:06:32 +01:00
add_rule $ chainref , "-m state --state $state -j ACCEPT" unless $ config { FASTACCEPT } ;
2007-03-27 01:17:46 +02:00
2007-03-14 05:06:32 +01:00
if ( $ sections { RELATED } ) {
if ( $ chainref - > { is_policy } ) {
if ( $ chainref - > { synparams } ) {
2007-09-12 17:03:47 +02:00
my $ synchainref = ensure_chain 'filter' , syn_flood_chain $ chainref ;
2007-03-14 05:06:32 +01:00
if ( $ section eq 'DONE' ) {
2007-08-26 17:12:04 +02:00
if ( $ chainref - > { policy } =~ /^(ACCEPT|CONTINUE|QUEUE|NFQUEUE)/ ) {
2007-03-14 05:06:32 +01:00
add_rule $ chainref , "-p tcp --syn -j $synchainref->{name}" ;
2007-04-08 16:42:26 +02:00
}
2007-03-14 05:06:32 +01:00
} else {
add_rule $ chainref , "-p tcp --syn -j $synchainref->{name}" ;
}
}
} else {
2007-09-12 17:03:47 +02:00
my $ policychainref = $ filter_table - > { $ chainref - > { policychain } } ;
2007-03-14 05:06:32 +01:00
if ( $ policychainref - > { synparams } ) {
2007-09-12 17:03:47 +02:00
my $ synchainref = ensure_chain 'filter' , syn_flood_chain $ policychainref ;
2007-05-26 04:57:27 +02:00
add_rule $ chainref , "-p tcp --syn -j $synchainref->{name}" ;
2007-03-14 05:06:32 +01:00
}
}
}
2007-12-08 18:37:04 +01:00
$ comment = $ savecomment ;
2007-03-27 01:17:46 +02:00
}
2007-03-14 05:06:32 +01:00
#
# Do section-end processing
2007-04-08 16:42:26 +02:00
#
2007-03-14 05:06:32 +01:00
sub finish_section ( $ ) {
my $ sections = $ _ [ 0 ] ;
2007-05-10 17:29:41 +02:00
for my $ section ( split /,/ , $ sections ) {
$ sections { $ section } = 1 ;
}
2007-09-10 17:52:57 +02:00
for my $ zone ( all_zones ) {
for my $ zone1 ( all_zones ) {
2007-09-12 17:03:47 +02:00
my $ chainref = $ chain_table { 'filter' } { "${zone}2${zone1}" } ;
if ( $ chainref - > { referenced } ) {
finish_chain_section $ chainref , $ sections ;
2007-03-14 05:06:32 +01:00
}
}
}
}
2007-05-08 04:08:44 +02:00
#
# Helper for set_mss
#
2007-09-12 17:03:47 +02:00
sub set_mss1 ( $$ ) {
my ( $ chain , $ mss ) = @ _ ;
my $ chainref = ensure_chain 'filter' , $ chain ;
2007-05-08 04:08:44 +02:00
2007-05-08 17:40:27 +02:00
if ( $ chainref - > { policy } ne 'NONE' ) {
2007-06-16 16:27:02 +02:00
my $ match = $ capabilities { TCPMSS_MATCH } ? "-m tcpmss --mss $mss: " : '' ;
insert_rule $ chainref , 1 , "-p tcp --tcp-flags SYN,RST SYN ${match}-j TCPMSS --set-mss $mss"
2007-05-08 04:08:44 +02:00
}
}
#
# Set up rules to set MSS to and/or from zone "$zone"
#
sub set_mss ( $$$ ) {
my ( $ zone , $ mss , $ direction ) = @ _ ;
2007-09-12 17:03:47 +02:00
for my $ z ( all_zones ) {
2007-05-08 04:08:44 +02:00
if ( $ direction eq '_in' ) {
2007-09-12 17:03:47 +02:00
set_mss1 "${zone}2${z}" , $ mss ;
2007-05-08 04:08:44 +02:00
} elsif ( $ direction eq '_out' ) {
2007-09-12 17:03:47 +02:00
set_mss1 "${z}2${zone}" , $ mss ;
2007-05-08 04:08:44 +02:00
} else {
2007-09-12 17:03:47 +02:00
set_mss1 "${z}2${zone}" , $ mss ;
set_mss1 "${zone}2${z}" , $ mss ;
2007-05-08 04:08:44 +02:00
}
}
}
2007-05-08 05:38:18 +02:00
#
2007-08-26 17:12:04 +02:00
# Interate over non-firewall zones and interfaces with 'mss=' setting adding TCPMSS rules as appropriate.
2007-05-08 05:38:18 +02:00
#
2007-05-08 04:08:44 +02:00
sub setup_zone_mss () {
2007-09-12 17:03:47 +02:00
for my $ zone ( all_zones ) {
2007-09-10 17:52:57 +02:00
my $ zoneref = find_zone ( $ zone ) ;
2007-05-08 04:08:44 +02:00
set_mss ( $ zone , $ zoneref - > { options } { in_out } { mss } , '' ) if $ zoneref - > { options } { in_out } { mss } ;
set_mss ( $ zone , $ zoneref - > { options } { in } { mss } , '_in' ) if $ zoneref - > { options } { in } { mss } ;
set_mss ( $ zone , $ zoneref - > { options } { out } { mss } , '_out' ) if $ zoneref - > { options } { out } { mss } ;
}
2007-07-26 20:36:18 +02:00
}
2007-05-08 04:08:44 +02:00
2007-03-15 01:34:17 +01:00
sub newexclusionchain () {
my $ seq = $ exclseq + + ;
"excl${seq}" ;
}
sub clearrule () {
2007-03-24 23:26:32 +01:00
$ iprangematch = 0 ;
2007-03-15 01:34:17 +01:00
}
2007-10-19 21:43:14 +02:00
#
# Resolve the contents of the PROTO column.
#
our % nametoproto = ( all = > 0 , ALL = > 0 , icmp = > 1 , ICMP = > 1 , tcp = > 6 , TCP = > 6 , udp = > 17 , UDP = > 17 ) ;
our @ prototoname = ( 'all' , 'icmp' , '' , '' , '' , '' , 'tcp' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , 'udp' ) ;
#
# Returns the protocol number if the passed argument is a valid protocol number or name. Returns undef otherwise
#
sub resolve_proto ( $ ) {
my $ proto = $ _ [ 0 ] ;
my $ number ;
$ proto =~ /^(\d+)$/ ? $ proto <= 65535 ? $ proto : undef : defined ( $ number = $ nametoproto { $ proto } ) ? $ number : scalar getprotobyname $ proto ;
}
sub proto_name ( $ ) {
2007-04-24 21:53:13 +02:00
my $ proto = $ _ [ 0 ] ;
2007-10-19 21:43:14 +02:00
$ proto =~ /^(\d+)$/ ? $ prototoname [ $ proto ] || scalar getprotobynumber $ proto : $ proto
}
sub validate_port ( $$ ) {
my ( $ proto , $ port ) = @ _ ;
my $ value ;
if ( $ port =~ /^(\d+)$/ ) {
return $ port if $ port <= 65535 ;
} else {
$ proto = getprotobyname $ proto if $ proto =~ /^(\d+)$/ ;
$ value = getservbyname ( $ port , $ proto ) ;
}
fatal_error "Invalid/Unknown $proto port/service ($port)" unless defined $ value ;
$ value ;
2007-04-24 21:53:13 +02:00
}
2007-10-19 21:43:14 +02:00
sub validate_portpair ( $$ ) {
my ( $ proto , $ portpair ) = @ _ ;
2007-04-24 21:53:13 +02:00
fatal_error "Invalid port range ($portpair)" if $ portpair =~ tr /:/ : / > 1 ;
$ portpair = "0$portpair" if substr ( $ portpair , 0 , 1 ) eq ':' ;
$ portpair = "${portpair}65535" if substr ( $ portpair , - 1 , 1 ) eq ':' ;
2007-10-19 21:43:14 +02:00
my @ ports = split /:/ , $ portpair , 2 ;
2007-05-17 16:10:46 +02:00
2007-10-19 21:43:14 +02:00
$ _ = validate_port ( $ proto , $ _ ) for ( @ ports ) ;
2007-07-26 20:36:18 +02:00
2007-10-19 21:43:14 +02:00
if ( @ ports == 2 ) {
fatal_error "Invalid port range ($portpair)" unless $ ports [ 0 ] < $ ports [ 1 ] ;
}
join ':' , @ ports ;
}
2007-07-26 20:36:18 +02:00
2007-10-19 21:43:14 +02:00
sub validate_port_list ( $$ ) {
2007-04-24 21:53:13 +02:00
my $ result = '' ;
2007-10-19 21:43:14 +02:00
my ( $ proto , $ list ) = @ _ ;
2007-08-01 16:58:26 +02:00
my @ list = split /,/ , $ list ;
if ( @ list > 1 && $ list =~ /:/ ) {
require_capability ( 'XMULTIPORT' , 'Port ranges in a port list' , '' ) ;
}
2007-04-24 21:53:13 +02:00
2007-10-19 21:43:14 +02:00
$ proto = proto_name $ proto ;
2007-08-01 17:45:22 +02:00
for ( @ list ) {
2007-10-19 21:43:14 +02:00
my $ value = validate_portpair ( $ proto , $ _ ) ;
2007-04-24 21:53:13 +02:00
$ result = $ result ? join ',' , $ result , $ value : $ value ;
}
$ result ;
}
my % icmp_types = ( any = > 'any' ,
'echo-reply' = > 0 ,
'destination-unreachable' = > 3 ,
'network-unreachable' = > '3/0' ,
'host-unreachable' = > '3/1' ,
'protocol-unreachable' = > '3/2' ,
'port-unreachable' = > '3/3' ,
'fragmentation-needed' = > '3/4' ,
'source-route-failed' = > '3/5' ,
'network-unknown' = > '3/6' ,
'host-unknown' = > '3/7' ,
'network-prohibited' = > '3/9' ,
'host-prohibited' = > '3/10' ,
'TOS-network-unreachable' = > '3/11' ,
'TOS-host-unreachable' = > '3/12' ,
'communication-prohibited' = > '3/13' ,
'host-precedence-violation' = > '3/14' ,
'precedence-cutoff' = > '3/15' ,
'source-quench' = > 4 ,
'redirect' = > 5 ,
'network-redirect' = > '5/0' ,
'host-redirect' = > '5/1' ,
'TOS-network-redirect' = > '5/2' ,
'TOS-host-redirect' = > '5/3' ,
'echo-request' = > '8' ,
'router-advertisement' = > 9 ,
'router-solicitation' = > 10 ,
'time-exceeded' = > 11 ,
'ttl-zero-during-transit' = > '11/0' ,
'ttl-zero-during-reassembly' = > '11/1' ,
'parameter-problem' = > 12 ,
'ip-header-bad' = > '12/0' ,
'required-option-missing' = > '12/1' ,
'timestamp-request' = > 13 ,
'timestamp-reply' = > 14 ,
'address-mask-request' = > 17 ,
'address-mask-reply' = > 18 ) ;
sub validate_icmp ( $ ) {
my $ type = $ _ [ 0 ] ;
my $ value = $ icmp_types { $ type } ;
return $ value if defined $ value ;
2007-06-22 16:47:16 +02:00
if ( $ type =~ /^(\d+)(\/(\d+))?$/ ) {
return $ type if $ 1 < 256 && ( ! $ 2 || $ 3 < 256 ) ;
}
2007-04-24 21:53:13 +02:00
fatal_error "Invalid ICMP Type ($type)"
}
2007-03-15 01:34:17 +01:00
#
# Handle parsing of PROTO, DEST PORT(S) , SOURCE PORTS(S). Returns the appropriate match string.
#
sub do_proto ( $ $ $ )
{
my ( $ proto , $ ports , $ sports ) = @ _ ;
2007-04-18 05:39:25 +02:00
#
# Return the number of ports represented by the passed list
#
2007-04-18 05:24:38 +02:00
sub port_count ( $ ) {
2007-04-18 05:39:25 +02:00
( $ _ [ 0 ] =~ tr /,:/ , : / ) + 1 ;
2007-04-18 05:24:38 +02:00
}
2007-03-15 01:34:17 +01:00
my $ output = '' ;
2007-03-27 01:17:46 +02:00
2007-03-15 01:34:17 +01:00
$ proto = '' if $ proto eq '-' ;
$ ports = '' if $ ports eq '-' ;
$ sports = '' if $ sports eq '-' ;
2007-10-19 21:43:14 +02:00
if ( $ proto ne '' ) {
my $ synonly = ( $ proto =~ s/:syn$//i ) ;
2007-04-19 23:14:18 +02:00
2007-10-19 21:43:14 +02:00
my $ protonum = resolve_proto $ proto ;
2007-04-18 01:14:42 +02:00
2007-10-19 21:43:14 +02:00
if ( defined $ protonum ) {
#
# Protocol is numeric and <= 65535 or is defined in /etc/protocols or NSS equivalent
#
my $ pname = proto_name ( $ proto = $ protonum ) ;
#
# $proto now contains the protocol number and $pname contains the canonical name of the protocol
#
unless ( $ synonly ) {
$ output = "-p $proto " ;
2007-05-14 16:14:38 +02:00
} else {
2007-10-19 21:43:14 +02:00
fatal_error '":syn" is only allowed with tcp' unless $ proto == TCP ;
$ output = "-p $proto --syn " ;
2007-03-15 01:34:17 +01:00
}
2007-03-27 01:17:46 +02:00
2007-10-19 21:43:14 +02:00
PROTO:
{
if ( $ proto == TCP || $ proto == UDP ) {
my $ multiport = 0 ;
if ( $ ports ne '' ) {
if ( $ ports =~ tr /,/ , / > 0 || $sports =~ tr/ , /,/ > 0 ) {
fatal_error "Port lists require Multiport support in your kernel/iptables" unless $ capabilities { MULTIPORT } ;
$ ports = validate_port_list $ pname , $ ports ;
$ output . = "-m multiport --dports $ports " ;
$ multiport = 1 ;
} else {
$ ports = validate_portpair $ pname , $ ports ;
$ output . = "--dport $ports " ;
}
} else {
$ multiport = ( ( $ sports =~ tr /,/ , / ) > 0 ) ;
}
if ( $ sports ne '' ) {
if ( $ multiport ) {
fatal_error "Too many entries in SOURCE PORT(S) list" if port_count ( $ sports ) > 15 ;
$ sports = validate_port_list $ pname , $ sports ;
$ output . = "-m multiport --sports $sports " ;
} else {
$ sports = validate_portpair $ pname , $ sports ;
$ output . = "--sport $sports " ;
}
}
last PROTO ; }
if ( $ proto == ICMP ) {
if ( $ ports ne '' ) {
fatal_error 'Multiple ICMP types are not permitted' if $ ports =~ /,/ ;
$ ports = validate_icmp $ ports ;
$ output . = "--icmp-type $ports " ;
}
fatal_error 'SOURCE PORT(S) not permitted with ICMP' if $ sports ne '' ;
2007-04-24 21:53:13 +02:00
2007-10-19 21:43:14 +02:00
last PROTO ; }
fatal_error "SOURCE/DEST PORT(S) not allowed with PROTO $pname" if $ ports ne '' || $ sports ne '' ;
} # PROTO
2007-04-24 21:53:13 +02:00
2007-03-20 21:06:52 +01:00
} else {
2007-10-19 21:43:14 +02:00
fatal_error '":syn" is only allowed with tcp' if $ synonly ;
if ( $ proto =~ /^(ipp2p(:(tcp|udp|all))?)$/i ) {
my $ p = $ 2 ? lc $ 3 : 'tcp' ;
require_capability ( 'IPP2P_MATCH' , "PROTO = $proto" , 's' ) ;
$ proto = "-p $nametoproto{$p} " ;
$ ports = 'ipp2p' unless $ ports ;
$ output . = "${proto}-m ipp2p --$ports " ;
} else {
fatal_error "Invalid/Unknown protocol ($proto)"
}
2007-03-15 01:34:17 +01:00
}
2007-10-19 21:43:14 +02:00
} else {
#
# No protocol
#
fatal_error "SOURCE/DEST PORT(S) not allowed without PROTO" if $ ports ne '' || $ sports ne '' ;
2007-03-15 01:34:17 +01:00
}
$ output ;
}
sub mac_match ( $ ) {
my $ mac = $ _ [ 0 ] ;
$ mac =~ s/^(!?)~// ;
2007-04-08 16:42:26 +02:00
$ mac =~ s/^!// if my $ invert = ( $ 1 ? '! ' : '' ) ;
2007-04-18 01:30:09 +02:00
$ mac =~ tr /-/ : / ;
2007-03-15 01:34:17 +01:00
"--match mac --mac-source ${invert}$mac " ;
}
#
# Mark validatation functions
#
sub verify_mark ( $ ) {
my $ mark = $ _ [ 0 ] ;
my $ limit = $ config { HIGH_ROUTE_MARKS } ? 0xFFFF : 0xFF ;
2007-09-23 17:21:18 +02:00
my $ value = numeric_value ( $ mark ) ;
2007-03-15 01:34:17 +01:00
2007-06-16 23:08:12 +02:00
fatal_error "Invalid Mark or Mask value ($mark)"
2007-12-07 20:06:01 +01:00
unless defined ( $ value ) && $ value <= $ limit ;
2007-09-23 17:21:18 +02:00
fatal_error "Invalid High Mark or Mask value ($mark)"
if ( $ value > 0xFF && $ value & 0xFF ) ;
2007-03-15 01:34:17 +01:00
}
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 ) = @ _ ;
2007-03-27 01:17:46 +02:00
2007-12-29 17:31:49 +01:00
return '' unless defined $ testval and $ testval ne '-' ;
2007-03-15 01:34:17 +01:00
my $ invert = $ testval =~ s/^!// ? '! ' : '' ;
2007-05-01 17:55:41 +02:00
my $ match = $ testval =~ s/:C$// ? "-m connmark ${invert}--mark" : "-m mark ${invert}--mark" ;
2007-03-27 01:17:46 +02:00
2007-05-03 19:10:07 +02:00
validate_mark $ testval ;
2008-01-01 18:47:58 +01:00
$ testval = join ( '/' , $ testval , in_hex ( $ mask ) ) unless ( $ testval =~ '/' ) ;
2007-03-15 01:34:17 +01:00
2007-05-01 17:55:41 +02:00
"$match $testval " ;
2007-03-15 01:34:17 +01:00
}
2007-03-27 01:17:46 +02:00
2007-05-03 18:33:49 +02:00
my % norate = ( DROP = > 1 , REJECT = > 1 ) ;
2007-03-15 01:34:17 +01:00
#
# Create a "-m limit" match for the passed LIMIT/BURST
#
2007-05-03 18:30:59 +02:00
sub do_ratelimit ( $$ ) {
my ( $ rate , $ action ) = @ _ ;
2007-03-15 01:34:17 +01:00
return '' unless $ rate and $ rate ne '-' ;
2007-03-27 01:17:46 +02:00
2007-05-03 18:33:49 +02:00
fatal_error "Rate Limiting not available with $action" if $ norate { $ action } ;
2007-05-03 18:30:59 +02:00
2007-07-29 20:09:00 +02:00
if ( $ rate =~ /^(\d+(\/(sec|min|hour|day))?):(\d+)$/ ) {
2007-05-03 18:56:05 +02:00
"-m limit --limit $1 --limit-burst $4 " ;
2007-07-29 20:09:00 +02:00
} elsif ( $ rate =~ /^(\d+)(\/(sec|min|hour|day))?$/ ) {
2007-03-15 01:34:17 +01:00
"-m limit --limit $rate " ;
2007-05-03 18:56:05 +02:00
} else {
fatal_error "Invalid rate ($rate)" ;
2007-03-15 01:34:17 +01:00
}
}
#
# Create a "-m owner" match for the passed USER/GROUP
#
sub do_user ( $ ) {
my $ user = $ _ [ 0 ] ;
2007-04-18 17:43:04 +02:00
my $ rule = '-m owner ' ;
2007-03-15 01:34:17 +01:00
2007-04-18 17:49:07 +02:00
return '' unless defined $ user and $ user ne '-' ;
2007-03-15 01:34:17 +01:00
if ( $ user =~ /^(!)?(.*)\+(.*)$/ ) {
2007-05-02 02:33:30 +02:00
$ rule . = "! --cmd-owner $2 " if defined $ 2 && $ 2 ne '' ;
2007-03-15 01:34:17 +01:00
$ user = "!$1" ;
} elsif ( $ user =~ /^(.*)\+(.*)$/ ) {
2007-05-02 02:33:30 +02:00
$ rule . = "--cmd-owner $2 " if defined $ 2 && $ 2 ne '' ;
2007-03-15 01:34:17 +01:00
$ user = $ 1 ;
}
2007-03-27 01:17:46 +02:00
2007-03-15 01:34:17 +01:00
if ( $ user =~ /^!(.*):(.*)$/ ) {
2007-05-02 02:33:30 +02:00
$ rule . = "! --uid-owner $1 " if defined $ 1 && $ 1 ne '' ;
$ rule . = "! --gid-owner $2 " if defined $ 2 && $ 2 ne '' ;
2007-03-15 01:34:17 +01:00
} elsif ( $ user =~ /^(.*):(.*)$/ ) {
2007-05-02 02:33:30 +02:00
$ rule . = "--uid-owner $1 " if defined $ 1 && $ 1 ne '' ;
$ rule . = "--gid-owner $2 " if defined $ 2 && $ 2 ne '' ;
2007-03-15 01:34:17 +01:00
} elsif ( $ user =~ /^!/ ) {
$ rule . = "! --uid-owner $user " ;
} else {
$ rule . = "--uid-owner $user " ;
}
$ rule ;
}
2007-03-21 17:43:42 +01:00
#
2007-03-21 18:09:52 +01:00
# Create a "-m tos" match for the passed TOS
2007-03-21 17:43:42 +01:00
#
sub do_tos ( $ ) {
my $ tos = $ _ [ 0 ] ;
2007-03-27 01:17:46 +02:00
2007-03-21 18:09:52 +01:00
$ tos ne '-' ? "-m tos --tos $tos " : '' ;
2007-03-27 01:17:46 +02:00
}
2007-06-06 17:34:47 +02:00
#
# Match Source Interface
#
sub match_source_dev ( $ ) {
my $ interface = shift ;
2007-09-10 17:52:57 +02:00
my $ interfaceref = find_interface ( $ interface ) ;
2007-06-06 22:06:16 +02:00
if ( $ interfaceref && $ interfaceref - > { options } { port } ) {
2007-06-06 17:34:47 +02:00
"-i $interfaceref->{bridge} -m physdev --physdev-in $interface " ;
} else {
"-i $interface " ;
}
2007-07-26 20:36:18 +02:00
}
2007-06-06 17:34:47 +02:00
#
# Match Dest device
#
sub match_dest_dev ( $ ) {
my $ interface = shift ;
2007-09-10 17:52:57 +02:00
my $ interfaceref = find_interface ( $ interface ) ;
2007-06-06 22:06:16 +02:00
if ( $ interfaceref && $ interfaceref - > { options } { port } ) {
2007-11-16 00:24:54 +01:00
if ( $ capabilities { PHYSDEV_BRIDGE } ) {
"-o $interfaceref->{bridge} -m physdev --physdev-is-bridged --physdev-out $interface " ;
} else {
"-o $interfaceref->{bridge} -m physdev --physdev-out $interface " ;
}
2007-06-06 17:34:47 +02:00
} else {
"-o $interface " ;
}
2007-07-26 20:36:18 +02:00
}
2007-06-06 17:34:47 +02:00
2007-03-15 01:34:17 +01:00
#
# Avoid generating a second '-m iprange' in a single rule.
#
sub iprange_match () {
my $ match = '' ;
2007-03-21 18:09:52 +01:00
2007-05-04 17:12:29 +02:00
require_capability ( 'IPRANGE_MATCH' , 'Address Ranges' , '' ) ;
2007-03-15 01:34:17 +01:00
unless ( $ iprangematch ) {
$ match = '-m iprange ' ;
2007-04-25 23:03:40 +02:00
$ iprangematch = 1 unless $ capabilities { KLUDGEFREE } ;
2007-03-15 01:34:17 +01:00
}
$ match ;
}
2007-03-24 16:59:17 +01:00
#
# Get set flags (ipsets).
#
sub get_set_flags ( $$ ) {
my ( $ setname , $ option ) = @ _ ;
my $ options = $ option ;
2007-07-21 17:35:45 +02:00
if ( $ setname =~ /^(.*)\[([1-6])\]$/ ) {
2007-03-24 16:59:17 +01:00
$ setname = $ 1 ;
my $ count = $ 2 ;
$ options . = ",$option" while - - $ count > 0 ;
2007-07-21 17:35:45 +02:00
} elsif ( $ setname =~ /^(.*)\[(.*)\]$/ ) {
2007-03-24 16:59:17 +01:00
$ setname = $ 1 ;
$ options = $ 2 ;
}
2007-03-27 01:17:46 +02:00
2007-03-24 16:59:17 +01:00
$ setname =~ s/^\+// ;
2007-07-21 17:35:45 +02:00
fatal_error "Invalid ipset name ($setname)" unless $ setname =~ /^[a-zA-Z]\w*/ ;
2007-05-01 18:39:41 +02:00
"--set $setname $options "
2007-03-24 16:59:17 +01:00
}
2007-03-15 01:34:17 +01:00
#
2007-09-21 18:55:28 +02:00
# Match a Source. Handles IP addresses and ranges and MAC addresses
2007-03-15 01:34:17 +01:00
#
sub match_source_net ( $ ) {
my $ net = $ _ [ 0 ] ;
2007-04-08 16:42:26 +02:00
2007-05-08 20:25:16 +02:00
if ( $ net =~ /^(!?)(\d+\.\d+\.\d+\.\d+)-(\d+\.\d+\.\d+\.\d+)$/ ) {
my ( $ addr1 , $ addr2 ) = ( $ 2 , $ 3 ) ;
2007-03-15 01:34:17 +01:00
$ net =~ s/!// if my $ invert = $ 1 ? '! ' : '' ;
2007-05-08 20:25:16 +02:00
validate_range $ addr1 , $ addr2 ;
2007-03-15 01:34:17 +01:00
iprange_match . "${invert}--src-range $net " ;
} elsif ( $ net =~ /^(!?)~(.*)$/ ) {
2007-04-19 00:28:01 +02:00
( $ net = $ 2 ) =~ tr /-/ : / ;
my $ invert = $ 1 ? '! ' : '' ;
"-m mac --mac-source ${invert}$net " ;
2007-03-24 16:59:17 +01:00
} elsif ( $ net =~ /^(!?)\+/ ) {
2007-05-04 17:12:29 +02:00
require_capability ( 'IPSET_MATCH' , 'ipset names in Shorewall configuration files' , '' ) ;
2007-03-26 02:46:15 +02:00
join ( '' , '-m set ' , $ 1 ? '! ' : '' , get_set_flags ( $ net , 'src' ) ) ;
2007-03-15 01:34:17 +01:00
} elsif ( $ net =~ /^!/ ) {
$ net =~ s/!// ;
2007-09-21 18:55:28 +02:00
validate_net $ net , 1 ;
2007-03-15 01:34:17 +01:00
"-s ! $net " ;
} else {
2007-09-21 18:55:28 +02:00
validate_net $ net , 1 ;
2007-03-15 01:34:17 +01:00
$ net eq ALLIPv4 ? '' : "-s $net " ;
}
}
#
# Match a Source. Currently only handles IP addresses and ranges
#
sub match_dest_net ( $ ) {
my $ net = $ _ [ 0 ] ;
2007-03-27 01:17:46 +02:00
2007-05-08 20:25:16 +02:00
if ( $ net =~ /^(!?)(\d+\.\d+\.\d+\.\d+)-(\d+\.\d+\.\d+\.\d+)$/ ) {
my ( $ addr1 , $ addr2 ) = ( $ 2 , $ 3 ) ;
2007-03-15 01:34:17 +01:00
$ net =~ s/!// if my $ invert = $ 1 ? '! ' : '' ;
2007-05-08 20:25:16 +02:00
validate_range $ addr1 , $ addr2 ;
2007-03-21 18:09:52 +01:00
iprange_match . "${invert}--dst-range $net " ;
2007-03-24 16:59:17 +01:00
} elsif ( $ net =~ /^(!?)\+/ ) {
2007-05-04 17:12:29 +02:00
require_capability ( 'IPSET_MATCH' , 'ipset names in Shorewall configuration files' , '' ) ;
2007-03-26 02:46:15 +02:00
join ( '' , '-m set ' , $ 1 ? '! ' : '' , get_set_flags ( $ net , 'dst' ) ) ;
2007-03-15 01:34:17 +01:00
} elsif ( $ net =~ /^!/ ) {
$ net =~ s/!// ;
2007-09-21 18:55:28 +02:00
validate_net $ net , 1 ;
2007-03-15 01:34:17 +01:00
"-d ! $net " ;
} else {
2007-09-21 18:55:28 +02:00
validate_net $ net , 1 ;
2007-03-15 01:34:17 +01:00
$ net eq ALLIPv4 ? '' : "-d $net " ;
}
}
#
# Match original destination
#
sub match_orig_dest ( $ ) {
my $ net = $ _ [ 0 ] ;
return '' if $ net eq ALLIPv4 ;
2007-03-29 01:21:37 +02:00
return '' unless $ capabilities { CONNTRACK_MATCH } ;
2007-04-08 16:42:26 +02:00
2007-03-15 01:34:17 +01:00
if ( $ net =~ /^!/ ) {
$ net =~ s/!// ;
2007-09-21 18:55:28 +02:00
validate_net $ net , 1 ;
2007-03-15 01:34:17 +01:00
"-m conntrack --ctorigdst ! $net " ;
} else {
2007-09-21 18:55:28 +02:00
validate_net $ net , 1 ;
2007-03-15 01:34:17 +01:00
$ net eq ALLIPv4 ? '' : "-m conntrack --ctorigdst $net " ;
}
}
#
# Match Source IPSEC
#
sub match_ipsec_in ( $$ ) {
my ( $ zone , $ hostref ) = @ _ ;
my $ match = '-m policy --dir in --pol ' ;
2007-09-10 17:52:57 +02:00
my $ zoneref = find_zone ( $ zone ) ;
2007-03-15 01:34:17 +01:00
my $ optionsref = $ zoneref - > { options } ;
2007-09-12 17:03:47 +02:00
if ( $ zoneref - > { type } eq 'ipsec4' ) {
2007-05-07 01:57:47 +02:00
$ match . = "ipsec $optionsref->{in_out}{ipsec}$optionsref->{in}{ipsec}" ;
2007-04-08 16:42:26 +02:00
} elsif ( $ capabilities { POLICY_MATCH } ) {
2007-05-07 01:57:47 +02:00
$ match . = "$hostref->{ipsec} $optionsref->{in_out}{ipsec}$optionsref->{in}{ipsec}" ;
2007-03-15 01:34:17 +01:00
} else {
'' ;
}
}
2007-04-08 16:42:26 +02:00
2007-03-15 01:34:17 +01:00
#
# Match Dest IPSEC
#
sub match_ipsec_out ( $$ ) {
my ( $ zone , $ hostref ) = @ _ ;
my $ match = '-m policy --dir out --pol ' ;
2007-09-10 17:52:57 +02:00
my $ zoneref = find_zone ( $ zone ) ;
2007-03-15 01:34:17 +01:00
my $ optionsref = $ zoneref - > { options } ;
2007-09-12 17:03:47 +02:00
if ( $ zoneref - > { type } eq 'ipsec4' ) {
2007-05-07 01:57:47 +02:00
$ match . = "ipsec $optionsref->{in_out}{ipsec}$optionsref->{out}{ipsec}" ;
2007-04-08 16:42:26 +02:00
} elsif ( $ capabilities { POLICY_MATCH } ) {
2007-05-07 01:57:47 +02:00
$ match . = "$hostref->{ipsec} $optionsref->{in_out}{ipsec}$optionsref->{out}{ipsec}"
2007-03-15 01:34:17 +01:00
} else {
'' ;
}
}
2007-03-27 01:17:46 +02:00
2007-03-15 01:34:17 +01:00
#
# Generate a log message
#
sub log_rule_limit ( $$$$$$$$ ) {
my ( $ level , $ chainref , $ chain , $ disposition , $ limit , $ tag , $ command , $ predicates ) = @ _ ;
my $ prefix ;
2007-07-28 18:37:14 +02:00
$ level = validate_level $ level ; # Do this here again because this function can be called directly from user exits.
2007-07-28 16:56:39 +02:00
return 1 if $ level eq '' ;
2007-10-19 21:43:14 +02:00
$ predicates . = ' ' if $ predicates && substr ( $ predicates , - 1 , 1 ) ne ' ' ;
2007-05-08 16:16:20 +02:00
unless ( $ predicates =~ /-m limit / ) {
$ limit = $ globals { LOGLIMIT } unless $ limit && $ limit ne '-' ;
2007-05-08 20:25:16 +02:00
$ predicates . = $ limit if $ limit ;
2007-05-08 16:16:20 +02:00
}
2007-03-15 01:34:17 +01:00
if ( $ tag ) {
if ( $ config { LOGTAGONLY } ) {
$ chain = $ tag ;
$ tag = '' ;
} else {
$ tag . = ' ' ;
}
} else {
$ tag = '' unless defined $ tag ;
}
2007-11-20 21:47:19 +01:00
$ disposition =~ s/\s+.*// ;
2007-03-31 19:44:16 +02:00
if ( $ globals { LOGRULENUMBERS } ) {
2007-03-15 01:34:17 +01:00
$ prefix = ( sprintf $ config { LOGFORMAT } , $ chain , $ chainref - > { log } + + , $ disposition ) . $ tag ;
} else {
$ prefix = ( sprintf $ config { LOGFORMAT } , $ chain , $ disposition ) . $ tag ;
}
if ( length $ prefix > 29 ) {
2007-08-26 17:12:04 +02:00
$ prefix = substr ( $ prefix , 0 , 28 ) . ' ' ;
2007-03-15 01:34:17 +01:00
warning_message "Log Prefix shortened to \"$prefix\"" ;
}
2007-11-18 19:05:08 +01:00
if ( $ level =~ '^ULOG' ) {
$ prefix = "-j $level --ulog-prefix \"$prefix\" " ;
} elsif ( $ level =~ /^NFLOG/ ) {
$ prefix = "-j $level --nflog-prefix \"$prefix\" " ;
2007-03-15 01:34:17 +01:00
} else {
2007-07-28 19:23:03 +02:00
$ prefix = "-j LOG $globals{LOGPARMS}--log-level $level --log-prefix \"$prefix\" " ;
2007-03-15 01:34:17 +01:00
}
2007-07-28 03:44:52 +02:00
2007-03-15 01:34:17 +01:00
if ( $ command eq 'add' ) {
2007-10-19 21:43:14 +02:00
add_rule ( $ chainref , $ predicates . $ prefix , 1 ) ;
2007-03-15 01:34:17 +01:00
} else {
insert_rule ( $ chainref , 1 , $ predicates . $ prefix ) ;
}
}
sub log_rule ( $$$$ ) {
my ( $ level , $ chainref , $ disposition , $ predicates ) = @ _ ;
2007-03-31 19:44:16 +02:00
log_rule_limit $ level , $ chainref , $ chainref - > { name } , $ disposition , $ globals { LOGLIMIT } , '' , 'add' , $ predicates ;
2007-03-15 01:34:17 +01:00
}
2007-03-23 23:47:21 +01:00
2007-03-25 18:38:00 +02:00
#
# Split a comma-separated source or destination host list but keep [...] together.
#
sub mysplit ( $ ) {
my @ input = split /,/ , $ _ [ 0 ] ;
return @ input unless $ _ [ 0 ] =~ /\[/ ;
my @ result ;
while ( @ input ) {
my $ element = shift @ input ;
if ( $ element =~ /\[/ ) {
2007-04-24 21:53:13 +02:00
while ( substr ( $ element , - 1 , 1 ) ne ']' ) {
2007-03-25 18:38:00 +02:00
last unless @ input ;
$ element . = ( ',' . shift @ input ) ;
}
fatal_error "Invalid Host List ($_[0])" unless substr ( $ element , - 1 , 1 ) eq ']' ;
}
2007-03-27 01:17:46 +02:00
2007-03-25 18:38:00 +02:00
push @ result , $ element ;
}
@ result ;
}
2007-03-23 23:47:21 +01:00
#
# Returns the name of the shell variable holding the first address of the passed interface
#
sub interface_address ( $ ) {
2007-08-03 16:08:40 +02:00
my $ variable = chain_base ( $ _ [ 0 ] ) . '_address' ;
2007-11-17 17:48:25 +01:00
uc $ variable ;
2007-03-23 23:47:21 +01:00
}
#
2007-04-08 16:42:26 +02:00
# Record that the ruleset requires the first IP address on the passed interface
2007-03-23 23:47:21 +01:00
#
2007-03-26 21:01:38 +02:00
sub get_interface_address ( $ ) {
my ( $ interface ) = $ _ [ 0 ] ;
2007-04-08 16:42:26 +02:00
2007-03-27 20:41:55 +02:00
my $ variable = interface_address ( $ interface ) ;
2007-04-18 17:43:04 +02:00
my $ function = interface_is_optional ( $ interface ) ? 'find_first_interface_address_if_any' : 'find_first_interface_address' ;
2007-03-23 23:47:21 +01:00
2007-11-17 17:48:25 +01:00
$ interfaceaddr { $ interface } = "$variable=\$($function $interface)\n" ;
2007-03-27 20:41:55 +02:00
"\$$variable" ;
}
2007-07-26 01:15:37 +02:00
#
# Returns the name of the shell variable holding the broadcast addresses of the passed interface
#
sub interface_bcasts ( $ ) {
2007-08-03 16:08:40 +02:00
my $ variable = chain_base ( $ _ [ 0 ] ) . '_bcasts' ;
2007-11-17 17:48:25 +01:00
uc $ variable ;
2007-07-26 01:15:37 +02:00
}
#
# Record that the ruleset requires the broadcast addresses on the passed interface
#
sub get_interface_bcasts ( $ ) {
my ( $ interface ) = $ _ [ 0 ] ;
my $ variable = interface_bcasts ( $ interface ) ;
$ interfacebcasts { $ interface } = qq( $variable="\$ ( get_interface_bcasts $interface ) 255.255.255.255" ) ;
"\$$variable" ;
}
2007-11-17 17:48:25 +01:00
#
# Returns the name of the shell variable holding the gateway through the passed interface
#
sub interface_gateway ( $ ) {
my $ variable = chain_base ( $ _ [ 0 ] ) . '_gateway' ;
uc $ variable ;
}
#
# Record that the ruleset requires the gateway address on the passed interface
#
sub get_interface_gateway ( $ ) {
my ( $ interface ) = $ _ [ 0 ] ;
my $ variable = interface_gateway ( $ interface ) ;
if ( interface_is_optional $ interface ) {
$ interfacegateways { $ interface } = qq( $variable=\$ ( detect_gateway $interface ) \ n ) ;
} else {
$ interfacegateways { $ interface } = qq( $variable=\$ ( detect_gateway $interface )
[ - n "\$$variable" ] || fatal_error "Unable to detect the gateway through interface $interface"
) ;
}
"\$$variable" ;
}
2007-03-27 20:41:55 +02:00
#
# Returns the name of the shell variable holding the addresses of the passed interface
#
sub interface_addresses ( $ ) {
2007-08-03 16:08:40 +02:00
my $ variable = chain_base ( $ _ [ 0 ] ) . '_addresses' ;
2007-11-17 17:48:25 +01:00
uc $ variable ;
2007-03-27 20:41:55 +02:00
}
#
2007-04-08 16:42:26 +02:00
# Record that the ruleset requires the IP addresses on the passed interface
2007-03-27 20:41:55 +02:00
#
sub get_interface_addresses ( $ ) {
my ( $ interface ) = $ _ [ 0 ] ;
2007-04-08 16:42:26 +02:00
2007-03-27 20:41:55 +02:00
my $ variable = interface_addresses ( $ interface ) ;
2007-04-18 17:43:04 +02:00
if ( interface_is_optional $ interface ) {
2007-04-23 20:20:54 +02:00
$ interfaceaddrs { $ interface } = qq( $variable=\$ ( find_interface_addresses $interface ) \ n ) ;
2007-04-18 17:43:04 +02:00
} else {
2007-04-23 20:20:54 +02:00
$ interfaceaddrs { $ interface } = qq( $variable=\$ ( find_interface_addresses $interface )
2007-03-27 20:41:55 +02:00
[ - n "\$$variable" ] || fatal_error "Unable to determine the IP address(es) of $interface"
) ;
2007-04-18 17:43:04 +02:00
}
2007-07-26 20:36:18 +02:00
2007-03-27 20:41:55 +02:00
"\$$variable" ;
}
#
# Returns the name of the shell variable holding the networks routed out of the passed interface
#
sub interface_nets ( $ ) {
2007-08-03 16:08:40 +02:00
my $ variable = chain_base ( $ _ [ 0 ] ) . '_networks' ;
2007-11-17 17:48:25 +01:00
uc $ variable ;
2007-03-27 20:41:55 +02:00
}
#
2007-12-10 23:53:20 +01:00
# Record that the ruleset requires the networks routed out of the passed interface
2007-03-27 20:41:55 +02:00
#
sub get_interface_nets ( $ ) {
my ( $ interface ) = $ _ [ 0 ] ;
my $ variable = interface_nets ( $ interface ) ;
2007-04-18 17:43:04 +02:00
if ( interface_is_optional $ interface ) {
2007-04-18 18:02:09 +02:00
$ interfacenets { $ interface } = qq( $variable=\$ ( get_routed_networks $interface ) \ n ) ;
2007-04-18 17:43:04 +02:00
} else {
2007-04-18 18:02:09 +02:00
$ interfacenets { $ interface } = qq( $variable=\$ ( get_routed_networks $interface )
2007-03-27 20:41:55 +02:00
[ - n "\$$variable" ] || fatal_error "Unable to determine the routes through interface \\" $ interface \ \ ""
) ;
2007-04-18 17:43:04 +02:00
}
2007-03-27 20:41:55 +02:00
"\$$variable" ;
2007-04-08 16:42:26 +02:00
2007-03-23 23:47:21 +01:00
}
2007-03-27 01:17:46 +02:00
2007-11-16 17:44:02 +01:00
#
# Returns the name of the shell variable holding the MAC address of the gateway for the passed provider out of the passed interface
#
sub interface_mac ( $$ ) {
2007-11-17 17:48:25 +01:00
my $ variable = join ( '_' , chain_base ( $ _ [ 0 ] ) , chain_base ( $ _ [ 1 ] ) , 'mac' ) ;
2007-11-16 17:44:02 +01:00
uc $ variable ;
}
#
2007-11-17 17:48:25 +01:00
# Record the fact that the ruleset requires MAC address of the passed gateway IP routed out of the passed interface for the passed provider number
2007-11-16 17:44:02 +01:00
#
sub get_interface_mac ( $$$ ) {
my ( $ ipaddr , $ interface , $ table ) = @ _ ;
my $ variable = interface_mac ( $ interface , $ table ) ;
2007-11-17 17:48:25 +01:00
if ( interface_is_optional $ interface ) {
$ interfacemacs { $ table } = qq( $variable=\$ ( find_mac $ipaddr $interface ) \ n ) ;
} else {
$ interfacemacs { $ table } = qq( $variable=\$ ( find_mac $ipaddr $interface )
[ - n "\$$variable" ] || fatal_error "Unable to determine the MAC address of $ipaddr through interface \\" $ interface \ \ ""
) ;
}
2007-11-16 17:44:02 +01:00
"\$$variable" ;
}
2007-03-15 01:34:17 +01:00
#
# This function provides a uniform way to generate rules (something the original Shorewall sorely needed).
2007-04-08 16:42:26 +02:00
#
2007-06-13 20:56:27 +02:00
# Returns the destination interface specified in the rule, if any.
#
2007-03-18 19:50:34 +01:00
sub expand_rule ( $ $ $ $ $ $ $ $ $ $ )
2007-03-15 01:34:17 +01:00
{
2007-07-26 20:36:18 +02:00
my ( $ chainref , # Chain
2007-06-03 23:44:17 +02:00
$ restriction , # Determines what to do with interface names in the SOURCE or DEST
2007-07-26 20:36:18 +02:00
$ rule , # Caller's matches that don't depend on the SOURCE, DEST and ORIGINAL DEST
$ source , # SOURCE
$ dest , # DEST
$ origdest , # ORIGINAL DEST
$ target , # Target ('-j' part of the rule)
$ loglevel , # Log level (and tag)
$ disposition , # Primative part of the target (RETURN, ACCEPT, ...)
2007-06-03 23:44:17 +02:00
$ exceptionrule # Caller's matches used in exclusion case
) = @ _ ;
2007-06-11 20:07:34 +02:00
2007-03-15 01:34:17 +01:00
my ( $ iiface , $ diface , $ inets , $ dnets , $ iexcl , $ dexcl , $ onets , $ oexcl ) ;
2007-03-18 22:16:46 +01:00
my $ chain = $ chainref - > { name } ;
2007-12-21 20:14:27 +01:00
our @ ends = ( ) ;
#
# In the generated rules, we sometimes need run-time loops or conditional blocks. This function is used
# to define such a loop or block.
#
# $chainref = Reference to the chain
# $command = The shell command that begins the loop or conditional
# $end = The shell keyword ('done' or 'fi') that ends the loop or conditional
#
# All open loops and conditionals are closed just before expand_rule() exits
#
sub push_command ( $$$ ) {
my ( $ chainref , $ command , $ end ) = @ _ ;
add_command $ chainref , $ command ;
incr_cmd_level $ chainref ;
push @ ends , $ end ;
}
2007-03-23 17:12:36 +01:00
#
# Handle Log Level
#
my $ logtag ;
2007-04-24 16:53:45 +02:00
if ( $ loglevel ne '' ) {
2007-05-17 16:10:46 +02:00
( $ loglevel , $ logtag , my $ remainder ) = split ( /:/ , $ loglevel , 3 ) ;
fatal_error "Invalid log tag" if defined $ remainder ;
2007-03-27 01:17:46 +02:00
2007-03-23 17:12:36 +01:00
if ( $ loglevel =~ /^none!?$/i ) {
2007-03-23 20:19:43 +01:00
return if $ disposition eq 'LOG' ;
2007-03-23 17:12:36 +01:00
$ loglevel = $ logtag = '' ;
2007-05-15 22:04:34 +02:00
} else {
$ loglevel = validate_level ( $ loglevel ) ;
$ logtag = '' unless defined $ logtag ;
2007-03-23 17:12:36 +01:00
}
2007-05-03 04:07:04 +02:00
} elsif ( $ disposition eq 'LOG' ) {
fatal_error "LOG requires a level" ;
2007-03-23 17:12:36 +01:00
}
2007-12-17 21:53:27 +01:00
#
# Mark Target as referenced, if it's a chain
#
if ( $ disposition ) {
my $ targetref = $ chain_table { $ chainref - > { table } } { $ disposition } ;
$ targetref - > { referenced } = 1 if $ targetref ;
}
2007-03-15 01:34:17 +01:00
#
# 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 = '' ;
}
2007-03-23 17:12:36 +01:00
2007-03-18 22:16:46 +01:00
#
2007-05-15 22:04:34 +02:00
# Verify Interface, if any
2007-03-15 01:34:17 +01:00
#
if ( $ iiface ) {
2007-05-15 22:04:34 +02:00
fatal_error "Unknown Interface ($iiface)" unless known_interface $ iiface ;
2007-03-18 22:57:39 +01:00
2007-03-25 21:27:25 +02:00
if ( $ restriction & POSTROUTE_RESTRICT ) {
2007-03-23 23:47:21 +01:00
#
# An interface in the SOURCE column of a masq file
#
2007-06-06 17:34:47 +02:00
fatal_error "Bridge ports may not appear in the SOURCE column of this file" if port_to_bridge ( $ iiface ) ;
2007-03-27 20:41:55 +02:00
my $ networks = get_interface_nets ( $ iiface ) ;
2007-12-21 20:14:27 +01:00
push_command $ chainref , join ( '' , 'for source in ' , $ networks , '; do' ) , 'done' ;
2007-03-27 20:41:55 +02:00
2007-03-23 23:47:21 +01:00
$ rule . = '-s $source ' ;
2007-07-28 19:31:31 +02:00
2007-03-18 22:16:46 +01:00
} else {
2007-09-10 17:52:57 +02:00
fatal_error "Source Interface ($iiface) not allowed when the source zone is the firewall zone" if $ restriction & OUTPUT_RESTRICT ;
2007-06-06 17:34:47 +02:00
$ rule . = match_source_dev ( $ iiface ) ;
2007-03-18 22:16:46 +01:00
}
2007-03-15 01:34:17 +01:00
}
#
# Isolate Destination Interface, if any
#
if ( $ dest ) {
if ( $ dest eq '-' ) {
$ dest = '' ;
2007-03-25 21:27:25 +02:00
} elsif ( ( $ restriction & PREROUTE_RESTRICT ) && $ dest =~ /^detect:(.*)$/ ) {
2007-03-23 17:12:36 +01:00
#
# DETECT_DNAT_IPADDRS=Yes and we're generating the nat rule
#
my @ interfaces = split /\s+/ , $ 1 ;
if ( @ interfaces > 1 ) {
2007-03-27 20:41:55 +02:00
my $ list = "" ;
2007-12-21 20:14:27 +01:00
my $ optional ;
2007-03-27 01:17:46 +02:00
2007-03-23 17:12:36 +01:00
for my $ interface ( @ interfaces ) {
2007-12-21 20:14:27 +01:00
$ optional + + if interface_is_optional $ interface ;
2007-03-27 20:41:55 +02:00
$ list = join ( ' ' , $ list , get_interface_address ( $ interface ) ) ;
2007-03-23 17:12:36 +01:00
}
2007-03-27 20:41:55 +02:00
2007-12-21 20:14:27 +01:00
push_command ( $ chainref , "for address in $list; do" , 'done' ) ;
push_command ( $ chainref , 'if [ $address != 0.0.0.0 ]; then' , 'fi' ) if $ optional ;
2007-03-27 20:41:55 +02:00
2007-03-23 23:47:21 +01:00
$ rule . = '-d $address ' ;
2007-03-23 17:12:36 +01:00
} else {
2007-12-21 20:14:27 +01:00
my $ interface = $ interfaces [ 0 ] ;
my $ variable = get_interface_address ( $ interface ) ;
push_command ( $ chainref , "if [ $variable != 0.0.0.0 ]; then" , 'fi' ) if interface_is_optional ( $ interface ) ;
$ rule . = "-d $variable " ;
2007-03-23 17:12:36 +01:00
}
$ dest = '' ;
2007-03-15 01:34:17 +01:00
} elsif ( $ dest =~ /^([^:]+):([^:]+)$/ ) {
$ diface = $ 1 ;
$ dnets = $ 2 ;
} elsif ( $ dest =~ /\+|~|\..*\./ ) {
$ dnets = $ dest ;
} else {
$ diface = $ dest ;
}
} else {
$ dest = '' ;
}
2007-03-23 23:47:21 +01:00
2007-03-15 01:34:17 +01:00
#
# Verify Destination Interface, if any
#
if ( $ diface ) {
2007-05-15 22:04:34 +02:00
fatal_error "Unknown Interface ($diface)" unless known_interface $ diface ;
2007-03-18 22:57:39 +01:00
2007-03-25 21:27:25 +02:00
if ( $ restriction & PREROUTE_RESTRICT ) {
2007-03-23 23:47:21 +01:00
#
# ADDRESS 'detect' in the masq file.
#
2007-06-16 23:08:12 +02:00
fatal_error "Bridge port ($diface) not allowed" if port_to_bridge ( $ diface ) ;
2007-04-27 18:18:42 +02:00
add_command ( $ chainref , 'for dest in ' . get_interface_addresses ( $ diface ) . '; do' ) ;
2007-06-10 19:09:09 +02:00
$ rule . = '-d $dest ' ;
2007-08-26 17:12:04 +02:00
incr_cmd_level $ chainref ;
2007-03-18 22:16:46 +01:00
} else {
2007-06-16 23:08:12 +02:00
fatal_error "Bridge Port ($diface) not allowed in OUTPUT or POSTROUTING rules" if ( $ restriction & ( POSTROUTE_RESTRICT + OUTPUT_RESTRICT ) ) && port_to_bridge ( $ diface ) ;
2007-09-10 17:52:57 +02:00
fatal_error "Destination Interface ($diface) not allowed when the destination zone is the firewall zone" if $ restriction & INPUT_RESTRICT ;
2007-06-06 17:34:47 +02:00
if ( $ iiface ) {
my $ bridge = port_to_bridge ( $ diface ) ;
2007-06-16 23:08:12 +02:00
fatal_error "Source interface ($iiface) is not a port on the same bridge as the destination interface ( $diface )" if $ bridge && $ bridge ne source_port_to_bridge ( $ iiface ) ;
2007-06-06 17:34:47 +02:00
}
$ rule . = match_dest_dev ( $ diface ) ;
2007-03-18 22:16:46 +01:00
}
2007-06-13 20:56:27 +02:00
} else {
$ diface = '' ;
2007-03-27 01:17:46 +02:00
}
2007-03-18 22:16:46 +01:00
2007-03-23 17:12:36 +01:00
if ( $ origdest ) {
2007-03-29 01:21:37 +02:00
if ( $ origdest eq '-' || ! $ capabilities { CONNTRACK_MATCH } ) {
2007-03-27 01:17:46 +02:00
$ origdest = '' ;
2007-03-23 17:12:36 +01:00
} elsif ( $ origdest =~ /^detect:(.*)$/ ) {
2007-03-23 23:47:21 +01:00
#
# Either the filter part of a DNAT rule or 'detect' was given in the ORIG DEST column
#
2007-03-23 17:12:36 +01:00
my @ interfaces = split /\s+/ , $ 1 ;
2007-03-18 22:16:46 +01:00
2007-03-23 17:12:36 +01:00
if ( @ interfaces > 1 ) {
2007-03-27 20:41:55 +02:00
my $ list = "" ;
2007-12-21 20:14:27 +01:00
my $ optional ;
2007-03-18 22:16:46 +01:00
2007-03-23 20:19:43 +01:00
for my $ interface ( @ interfaces ) {
2007-12-21 20:14:27 +01:00
$ optional + + if interface_is_optional $ interface ;
2007-03-27 20:41:55 +02:00
$ list = join ( ' ' , $ list , get_interface_address ( $ interface ) ) ;
2007-03-23 17:12:36 +01:00
}
2007-03-23 23:47:21 +01:00
2007-12-21 20:14:27 +01:00
push_command ( $ chainref , "for address in $list; do" , 'done' ) ;
push_command ( $ chainref , 'if [ $address != 0.0.0.0 ]; then' , 'fi' ) if $ optional ;
2007-03-23 23:47:21 +01:00
$ rule . = '-m conntrack --ctorigdst $address ' ;
2007-03-23 17:12:36 +01:00
} else {
2007-12-21 20:14:27 +01:00
my $ interface = $ interfaces [ 0 ] ;
my $ variable = get_interface_address ( $ interface ) ;
push_command ( $ chainref , "if [ $variable != 0.0.0.0 ]; then" , 'fi' ) if interface_is_optional ( $ interface ) ;
$ rule . = "-m conntrack --ctorigdst $variable " ;
2007-04-08 16:42:26 +02:00
}
2007-03-15 01:34:17 +01:00
2007-03-23 17:12:36 +01:00
$ origdest = '' ;
} else {
2007-04-19 03:01:03 +02:00
fatal_error "Invalid ORIGINAL DEST" if $ origdest =~ /^([^!]+)?,!([^!]+)$/ || $ origdest =~ /.*!.*!/ ;
2007-04-19 02:49:38 +02:00
2007-03-23 23:47:21 +01:00
if ( $ origdest =~ /^([^!]+)?!([^!]+)$/ ) {
#
# Exclusion
#
$ onets = $ 1 ;
$ oexcl = $ 2 ;
} else {
2007-03-23 17:12:36 +01:00
$ oexcl = '' ;
2007-03-29 05:07:48 +02:00
$ onets = $ origdest ;
2007-03-23 17:12:36 +01:00
}
2007-03-23 23:47:21 +01:00
2007-03-24 16:59:17 +01:00
unless ( $ onets ) {
2007-03-24 04:56:16 +01:00
my @ oexcl = mysplit $ oexcl ;
2007-03-23 23:47:21 +01:00
if ( @ oexcl == 1 ) {
2007-08-26 17:12:04 +02:00
$ rule . = match_orig_dest ( "!$oexcl" ) ;
2007-03-23 23:47:21 +01:00
$ oexcl = '' ;
}
}
2007-03-15 01:34:17 +01:00
}
2007-03-23 17:12:36 +01:00
} else {
$ oexcl = '' ;
2007-03-15 01:34:17 +01:00
}
#
# Determine if there is Source Exclusion
#
if ( $ inets ) {
2007-04-19 03:01:03 +02:00
fatal_error "Invalid SOURCE" if $ inets =~ /^([^!]+)?,!([^!]+)$/ || $ inets =~ /.*!.*!/ ;
2007-07-26 20:36:18 +02:00
2007-03-15 01:34:17 +01:00
if ( $ inets =~ /^([^!]+)?!([^!]+)$/ ) {
$ inets = $ 1 ;
$ iexcl = $ 2 ;
} else {
$ iexcl = '' ;
}
2007-04-28 16:59:18 +02:00
unless ( $ inets || ( $ iiface && $ restriction & POSTROUTE_RESTRICT ) ) {
2007-03-24 04:56:16 +01:00
my @ iexcl = mysplit $ iexcl ;
2007-03-15 01:34:17 +01:00
if ( @ iexcl == 1 ) {
2007-05-08 20:25:16 +02:00
$ rule . = match_source_net "!$iexcl" ;
2007-03-15 01:34:17 +01:00
$ iexcl = '' ;
}
2007-07-26 20:36:18 +02:00
2007-03-15 01:34:17 +01:00
}
} else {
$ iexcl = '' ;
}
#
# Determine if there is Destination Exclusion
2007-03-18 22:16:46 +01:00
#
2007-03-15 01:34:17 +01:00
if ( $ dnets ) {
2007-04-19 03:01:03 +02:00
fatal_error "Invalid DEST" if $ dnets =~ /^([^!]+)?,!([^!]+)$/ || $ dnets =~ /.*!.*!/ ;
2007-07-26 20:36:18 +02:00
2007-03-15 01:34:17 +01:00
if ( $ dnets =~ /^([^!]+)?!([^!]+)$/ ) {
$ dnets = $ 1 ;
$ dexcl = $ 2 ;
} else {
$ dexcl = '' ;
}
2007-03-24 16:59:17 +01:00
unless ( $ dnets ) {
2007-03-24 04:56:16 +01:00
my @ dexcl = mysplit $ dexcl ;
2007-03-15 01:34:17 +01:00
if ( @ dexcl == 1 ) {
2007-05-08 20:25:16 +02:00
$ rule . = match_dest_net "!$dexcl" ;
2007-03-15 01:34:17 +01:00
$ dexcl = '' ;
}
}
} else {
$ dexcl = '' ;
}
$ 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
#
2007-05-09 00:42:35 +02:00
fatal_error "Exclusion is not possible in ACCEPT+/CONTINUE/NONAT rules" if $ disposition eq 'RETURN' ;
2007-04-19 02:49:38 +02:00
2007-03-15 01:34:17 +01:00
my $ echain = newexclusionchain ;
2007-03-27 01:17:46 +02:00
2007-03-15 01:34:17 +01:00
#
# Use the current rule and sent all possible matches to the exclusion chain
#
2007-03-24 04:56:16 +01:00
for my $ onet ( mysplit $ onets ) {
2007-03-15 01:34:17 +01:00
$ onet = match_orig_dest $ onet ;
2007-03-24 04:56:16 +01:00
for my $ inet ( mysplit $ inets ) {
for my $ dnet ( mysplit $ dnets ) {
2007-05-15 22:04:34 +02:00
#
2007-08-26 17:12:04 +02:00
# We evaluate the source net match in the inner loop to accomodate systems without $capabilities{KLUDGEFREE}
2007-05-15 22:04:34 +02:00
#
2007-10-19 21:43:14 +02:00
add_rule ( $ chainref , join ( '' , $ rule , match_source_net ( $ inet ) , match_dest_net ( $ dnet ) , $ onet , "-j $echain" ) , 1 ) ;
2007-03-15 01:34:17 +01:00
}
}
}
2007-03-23 17:12:36 +01:00
2007-03-15 01:34:17 +01:00
#
# Create the Exclusion Chain
#
2007-09-12 17:03:47 +02:00
my $ echainref = new_chain $ chainref - > { table } , $ echain ;
2007-03-15 01:34:17 +01:00
#
# Generate RETURNs for each exclusion
#
2007-08-26 17:12:04 +02:00
add_rule $ echainref , ( match_source_net $ _ ) . '-j RETURN' for ( mysplit $ iexcl ) ;
add_rule $ echainref , ( match_dest_net $ _ ) . '-j RETURN' for ( mysplit $ dexcl ) ;
add_rule $ echainref , ( match_orig_dest $ _ ) . '-j RETURN' for ( mysplit $ oexcl ) ;
2007-03-23 18:30:12 +01:00
#
# Log rule
#
log_rule_limit $ loglevel , $ echainref , $ chain , $ disposition , '' , $ logtag , 'add' , '' if $ loglevel ;
#
# Generate Final Rule
2007-04-08 16:42:26 +02:00
#
2007-10-19 21:43:14 +02:00
add_rule ( $ echainref , $ exceptionrule . $ target , 1 ) unless $ disposition eq 'LOG' ;
2007-03-15 01:34:17 +01:00
} else {
#
# No exclusions
#
2007-03-24 04:56:16 +01:00
for my $ onet ( mysplit $ onets ) {
2007-03-15 01:34:17 +01:00
$ onet = match_orig_dest $ onet ;
2007-03-24 04:56:16 +01:00
for my $ inet ( mysplit $ inets ) {
2007-05-15 22:04:34 +02:00
#
# We defer evaluating the source net match to accomodate system without $capabilities{KLUDGEFREE}
#
2007-03-24 04:56:16 +01:00
for my $ dnet ( mysplit $ dnets ) {
2007-05-02 17:39:36 +02:00
if ( $ loglevel ne '' ) {
2007-04-08 16:42:26 +02:00
log_rule_limit
$ loglevel ,
$ chainref ,
$ chain ,
$ disposition ,
'' ,
$ logtag ,
'add' ,
2007-05-15 22:04:34 +02:00
join ( '' , $ rule , match_source_net ( $ inet ) , match_dest_net ( $ dnet ) , $ onet ) ;
2007-03-26 02:46:15 +02:00
}
unless ( $ disposition eq 'LOG' ) {
2007-10-19 21:43:14 +02:00
add_rule (
$ chainref ,
join ( '' , $ rule , match_source_net ( $ inet ) , match_dest_net ( $ dnet ) , $ onet , $ target ) ,
1 ) ;
2007-03-26 02:46:15 +02:00
}
2007-03-15 01:34:17 +01:00
}
}
}
2007-03-23 17:12:36 +01:00
}
2007-12-21 20:14:27 +01:00
while ( @ ends ) {
2007-08-26 17:12:04 +02:00
decr_cmd_level $ chainref ;
2007-12-21 20:14:27 +01:00
add_command $ chainref , pop @ ends ;
2007-03-23 20:54:08 +01:00
}
2007-06-13 20:56:27 +02:00
$ diface ;
2007-03-15 01:34:17 +01:00
}
#
# 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 ) = @ _ ;
2007-09-12 17:03:47 +02:00
my $ destref = $ nat_table - > { $ dest } || { } ;
2007-03-27 01:17:46 +02:00
2007-03-15 01:34:17 +01:00
if ( $ destref - > { referenced } ) {
2007-09-12 17:03:47 +02:00
add_rule $ nat_table - > { $ source } , $ predicates . "-j $dest" ;
2007-03-15 01:34:17 +01:00
} else {
clearrule ;
}
}
2007-03-28 21:09:27 +02:00
2007-03-15 01:34:17 +01:00
#
# 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 ) = @ _ ;
2007-03-27 01:17:46 +02:00
2007-09-12 17:03:47 +02:00
my $ destref = $ nat_table - > { $ dest } || { } ;
2007-03-27 01:17:46 +02:00
2007-03-15 01:34:17 +01:00
if ( $ destref - > { referenced } ) {
2007-09-12 17:03:47 +02:00
insert_rule $ nat_table - > { $ source } , ( $$ countref ) + + , $ predicates . "-j $dest" ;
2007-03-15 01:34:17 +01:00
} else {
clearrule ;
}
}
2007-08-09 17:16:08 +02:00
sub emit_comment () {
2007-08-03 16:26:19 +02:00
emit ( '#' ,
'# Establish the values of shell variables used in the following function calls' ,
'#' ) ;
2007-08-09 17:16:08 +02:00
our $ emitted_comment = 1 ;
2007-05-09 00:28:48 +02:00
}
2007-08-09 17:16:08 +02:00
sub emit_test () {
2007-08-03 16:26:19 +02:00
emit ( 'if [ "$COMMAND" != restore ]; then' ,
'' ) ;
push_indent ;
2007-08-09 17:16:08 +02:00
our $ emitted_test = 1 ;
2007-08-03 16:08:40 +02:00
}
2007-04-16 21:50:29 +02:00
#
2007-05-09 00:28:48 +02:00
# Generate setting of global variables
2007-04-16 21:50:29 +02:00
#
2007-05-08 22:36:57 +02:00
sub set_global_variables () {
2007-03-27 01:17:46 +02:00
2007-08-09 17:16:08 +02:00
our ( $ emitted_comment , $ emitted_test ) = ( 0 , 0 ) ;
2007-08-03 16:26:19 +02:00
2007-03-27 20:41:55 +02:00
for ( values % interfaceaddr ) {
2007-08-09 17:16:08 +02:00
emit_comment unless $ emitted_comment ;
2007-03-27 20:41:55 +02:00
emit $ _ ;
}
2007-11-17 17:48:25 +01:00
for ( values % interfacegateways ) {
emit_comment unless $ emitted_comment ;
emit $ _ ;
}
for ( values % interfacemacs ) {
emit_comment unless $ emitted_comment ;
emit $ _ ;
}
2007-03-26 21:01:38 +02:00
for ( values % interfaceaddrs ) {
2007-08-09 17:16:08 +02:00
emit_comment unless $ emitted_comment ;
emit_test unless $ emitted_test ;
2007-03-26 21:01:38 +02:00
emit $ _ ;
}
2007-03-27 20:41:55 +02:00
for ( values % interfacenets ) {
2007-08-09 17:16:08 +02:00
emit_comment unless $ emitted_comment ;
emit_test unless $ emitted_test ;
2007-03-27 20:41:55 +02:00
emit $ _ ;
}
2007-07-26 01:15:37 +02:00
unless ( $ capabilities { ADDRTYPE } ) {
2007-08-09 17:16:08 +02:00
emit_comment unless $ emitted_comment ;
emit_test unless $ emitted_test ;
2007-07-26 18:57:47 +02:00
emit 'ALL_BCASTS="$(get_all_bcasts) 255.255.255.255"' ;
2007-07-26 01:15:37 +02:00
for ( values % interfacebcasts ) {
emit $ _ ;
}
}
2007-08-03 16:08:40 +02:00
pop_indent , emit "fi\n" if $ emitted_test ;
2007-05-08 22:36:57 +02:00
}
2007-07-24 16:49:50 +02:00
#
# What follows is the code that generates the input to iptables-restore
#
# We always write the iptables-restore input into a file then pass the
# file to iptables-restore. That way, if things go wrong, the user (and Shorewall support)
# has (have) something to look at to determine the error
#
2007-07-26 20:36:18 +02:00
# We may have to generate part of the input at run-time. The rules array in each chain
2007-07-24 16:49:50 +02:00
# table entry may contain rules (begin with '-A') or shell source. We alternate between
# writing the rules ('-A') into the temporary file to be bassed to iptables-restore
2007-08-09 17:16:08 +02:00
# (CAT_MODE) and and writing shell source into the generated script (CMD_MODE).
2007-07-24 16:49:50 +02:00
#
2007-08-09 17:16:08 +02:00
# The following two functions are responsible for the mode transitions.
2007-07-24 16:49:50 +02:00
#
2007-08-09 17:16:08 +02:00
sub enter_cat_mode () {
2007-07-24 16:49:50 +02:00
emit '' ;
emit 'cat >&3 << __EOF__' ;
2007-08-09 17:16:08 +02:00
$ mode = CAT_MODE ;
2007-07-24 16:49:50 +02:00
}
2007-08-09 17:16:08 +02:00
sub enter_cmd_mode () {
emit_unindented "__EOF__\n" if $ mode == CAT_MODE ;
$ mode = CMD_MODE ;
2007-07-24 16:49:50 +02:00
}
#
# Emits the passed rule (input to iptables-restore) or command
#
sub emitr ( $ ) {
my $ rule = $ _ [ 0 ] ;
if ( $ rule && substr ( $ rule , 0 , 2 ) eq '-A' ) {
#
# A rule
#
2007-08-09 17:16:08 +02:00
enter_cat_mode unless $ mode == CAT_MODE ;
2007-07-24 16:49:50 +02:00
emit_unindented $ rule ;
} else {
#
# A command
#
2007-08-09 17:16:08 +02:00
enter_cmd_mode unless $ mode == CMD_MODE ;
2007-07-24 16:49:50 +02:00
emit $ rule ;
}
}
2007-05-08 22:36:57 +02:00
#
# Generate the netfilter input
#
sub create_netfilter_load () {
2007-07-24 16:49:50 +02:00
my @ table_list ;
push @ table_list , 'raw' if $ capabilities { RAW_TABLE } ;
push @ table_list , 'nat' if $ capabilities { NAT_ENABLED } ;
push @ table_list , 'mangle' if $ capabilities { MANGLE_ENABLED } ;
push @ table_list , 'filter' ;
2007-08-09 17:16:08 +02:00
$ mode = NULL_MODE ;
2007-07-26 20:36:18 +02:00
2007-07-23 21:00:07 +02:00
emit ( 'setup_netfilter()' ,
2007-05-08 22:36:57 +02:00
'{'
) ;
push_indent ;
save_progress_message "Preparing iptables-restore input..." ;
2007-03-26 21:01:38 +02:00
emit '' ;
2007-06-08 02:27:43 +02:00
2007-07-24 16:49:50 +02:00
emit 'exec 3>${VARDIR}/.iptables-restore-input' ;
2007-06-08 02:27:43 +02:00
2007-08-09 17:16:08 +02:00
enter_cat_mode ;
2007-07-26 20:36:18 +02:00
2007-06-08 02:27:43 +02:00
for my $ table ( @ table_list ) {
2007-07-23 21:07:45 +02:00
emit_unindented "*$table" ;
2007-03-27 01:17:46 +02:00
2007-03-15 02:18:29 +01:00
my @ chains ;
2007-04-16 21:50:29 +02:00
#
# iptables-restore seems to be quite picky about the order of the builtin chains
#
2007-03-21 00:44:12 +01:00
for my $ chain ( @ builtins ) {
2007-09-12 17:03:47 +02:00
my $ chainref = $ chain_table { $ table } { $ chain } ;
2007-03-21 00:44:12 +01:00
if ( $ chainref ) {
2007-08-26 17:12:04 +02:00
fatal_error "Internal error in create_netfilter_load()" if $ chainref - > { cmdlevel } ;
2007-07-23 21:07:45 +02:00
emit_unindented ":$chain $chainref->{policy} [0:0]" ;
2007-03-21 00:44:12 +01:00
push @ chains , $ chainref ;
}
}
2007-04-16 21:50:29 +02:00
#
# First create the chains in the current table
#
2007-09-12 17:03:47 +02:00
for my $ chain ( grep $ chain_table { $ table } { $ _ } - > { referenced } , ( sort keys % { $ chain_table { $ table } } ) ) {
my $ chainref = $ chain_table { $ table } { $ chain } ;
2007-03-21 00:44:12 +01:00
unless ( $ chainref - > { builtin } ) {
2007-08-26 17:12:04 +02:00
fatal_error "Internal error in create_netfilter_load()" if $ chainref - > { cmdlevel } ;
2007-07-23 21:07:45 +02:00
emit_unindented ":$chainref->{name} - [0:0]" ;
2007-03-21 00:44:12 +01:00
push @ chains , $ chainref ;
2007-03-15 02:18:29 +01:00
}
}
2007-04-16 21:50:29 +02:00
#
2007-08-09 17:16:08 +02:00
# Then emit the rules
2007-04-16 21:50:29 +02:00
#
2007-03-15 02:18:29 +01:00
for my $ chainref ( @ chains ) {
2007-08-09 17:16:08 +02:00
emitr $ _ for ( @ { $ chainref - > { rules } } ) ;
2007-03-15 02:18:29 +01:00
}
2007-04-16 21:50:29 +02:00
#
# Commit the changes to the table
#
2007-08-09 17:16:08 +02:00
enter_cat_mode unless $ mode == CAT_MODE ;
2007-07-23 21:07:45 +02:00
emit_unindented 'COMMIT' ;
2007-03-15 02:18:29 +01:00
}
2007-08-09 17:16:08 +02:00
enter_cmd_mode ;
2007-04-16 21:50:29 +02:00
#
2007-07-03 19:13:14 +02:00
# Now generate the actual iptables-restore command
2007-04-16 21:50:29 +02:00
#
2007-07-23 20:14:12 +02:00
emit ( 'exec 3>&-' ,
2007-05-03 20:56:56 +02:00
'' ,
2007-10-19 21:43:14 +02:00
'[ -n "$DEBUG" ] && command=debug_restore_input || command=$IPTABLES_RESTORE' ,
'' ,
'progress_message2 "Running $command..."' ,
2007-03-29 21:45:53 +02:00
'' ,
2007-10-19 21:43:14 +02:00
'cat ${VARDIR}/.iptables-restore-input | $command # Use this nonsensical form to appease SELinux' ,
2007-07-23 21:00:07 +02:00
'if [ $? != 0 ]; then' ,
2007-03-29 21:45:53 +02:00
' fatal_error "iptables-restore Failed. Input is in ${VARDIR}/.iptables-restore-input"' ,
2007-03-27 06:02:58 +02:00
"fi\n"
) ;
2007-03-21 18:54:17 +01:00
2007-03-22 19:24:33 +01:00
pop_indent ;
2007-03-15 02:18:29 +01:00
emit "}\n" ;
}
2007-03-27 01:17:46 +02:00
2007-07-03 20:59:42 +02:00
#
2007-09-10 17:52:57 +02:00
# Generate the netfilter input for refreshing a list of chains
2007-07-03 20:59:42 +02:00
#
2007-09-10 17:52:57 +02:00
sub create_chainlist_reload ($) {
my $ chains = $ _ [ 0 ] ;
my @ chains = split ',' , $ chains ;
unless ( @ chains ) {
2007-09-12 17:03:47 +02:00
@ chains = qw( blacklst ) if $ filter_table - > { blacklst } ;
2007-11-18 02:49:50 +01:00
push @ chains , 'mangle:' if $ capabilities { MANGLE_ENABLED } ;
$ chains = join ( ',' , @ chains ) if @ chains ;
2007-09-10 17:52:57 +02:00
}
2007-07-03 20:59:42 +02:00
2007-08-09 17:16:08 +02:00
$ mode = NULL_MODE ;
2007-07-26 20:36:18 +02:00
2007-09-10 17:52:57 +02:00
emit ( 'chainlist_reload()' ,
2007-07-03 20:59:42 +02:00
'{'
) ;
push_indent ;
2007-09-10 17:52:57 +02:00
if ( @ chains ) {
if ( @ chains == 1 ) {
progress_message2 "Compiling iptables-restore input for chain @chains..." ;
save_progress_message "Preparing iptables-restore input for chain @chains..." ;
} else {
2007-11-18 02:49:50 +01:00
progress_message2 "Compiling iptables-restore input for chains $chains..." ;
2007-09-10 17:52:57 +02:00
save_progress_message "Preparing iptables-restore input for chains $chains..." ;
}
2007-07-03 20:59:42 +02:00
2007-09-10 17:52:57 +02:00
emit '' ;
2007-07-24 16:49:50 +02:00
2007-09-10 17:52:57 +02:00
my $ table = 'filter' ;
my % chains ;
for my $ chain ( @ chains ) {
( $ table , $ chain ) = split ':' , $ chain if $ chain =~ /:/ ;
fatal_error "Invalid table ( $table )" unless $ table =~ /^(nat|mangle|filter)$/ ;
2007-11-18 02:49:50 +01:00
2007-09-10 17:52:57 +02:00
$ chains { $ table } = [] unless $ chains { $ table } ;
2007-11-18 02:49:50 +01:00
if ( $ chain ) {
fatal_error "No $table chain found with name $chain" unless $ chain_table { $ table } { $ chain } ;
fatal_error "Built-in chains may not be refreshed" if $ chain_table { table } { $ chain } { builtin } ;
push @ { $ chains { $ table } } , $ chain ;
} else {
while ( my ( $ chain , $ chainref ) = each % { $ chain_table { $ table } } ) {
push @ { $ chains { $ table } } , $ chain if $ chainref - > { referenced } && ! $ chainref - > { builtin } ;
}
}
2007-09-10 17:52:57 +02:00
}
2007-07-23 21:07:45 +02:00
2007-11-18 02:49:50 +01:00
emit 'exec 3>${VARDIR}/.iptables-restore-input' ;
enter_cat_mode ;
2007-09-10 17:52:57 +02:00
for $ table qw( nat mangle filter ) {
next unless $ chains { $ table } ;
2007-07-26 20:36:18 +02:00
2007-09-10 17:52:57 +02:00
emit_unindented "*$table" ;
2007-07-03 20:59:42 +02:00
2007-09-12 17:03:47 +02:00
my $ tableref = $ chain_table { $ table } ;
2007-09-10 17:52:57 +02:00
@ chains = sort @ { $ chains { $ table } } ;
for my $ chain ( @ chains ) {
my $ chainref = $ tableref - > { $ chain } ;
2007-11-18 19:09:18 +01:00
emit_unindented ":$chainref->{name} - [0:0]" ;
2007-09-10 17:52:57 +02:00
}
for my $ chain ( @ chains ) {
my $ chainref = $ tableref - > { $ chain } ;
my @ rules = @ { $ chainref - > { rules } } ;
@ rules = ( ) unless @ rules ;
#
# Emit the chain rules
#
emitr $ _ for ( @ rules ) ;
}
#
# Commit the changes to the table
#
enter_cat_mode unless $ mode == CAT_MODE ;
emit_unindented 'COMMIT' ;
}
enter_cmd_mode ;
2007-11-18 02:49:50 +01:00
2007-09-10 17:52:57 +02:00
#
# Now generate the actual iptables-restore command
#
emit ( 'exec 3>&-' ,
'' ,
'progress_message2 "Running iptables-restore..."' ,
'' ,
'cat ${VARDIR}/.iptables-restore-input | $IPTABLES_RESTORE -n # Use this nonsensical form to appease SELinux' ,
'if [ $? != 0 ]; then' ,
' fatal_error "iptables-restore Failed. Input is in ${VARDIR}/.iptables-restore-input"' ,
"fi\n"
) ;
} else {
emit ( 'true' ) ;
}
2007-07-03 20:59:42 +02:00
pop_indent ;
emit "}\n" ;
}
2007-03-14 04:12:22 +01:00
1 ;