Move pic of house to web/images

git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@8853 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb
This commit is contained in:
teastep 2008-11-02 19:35:58 +00:00
parent c2cc895b3a
commit 8b5792052b
11 changed files with 3226 additions and 122 deletions

View File

@ -35,7 +35,7 @@
# #
SHOREWALL_LIBVERSION=40000 SHOREWALL_LIBVERSION=40000
SHOREWALL_CAPVERSION=40200 SHOREWALL_CAPVERSION=40202
[ -n "${VARDIR:=/var/lib/shorewall}" ] [ -n "${VARDIR:=/var/lib/shorewall}" ]
[ -n "${SHAREDIR:=/usr/share/shorewall}" ] [ -n "${SHAREDIR:=/usr/share/shorewall}" ]
@ -1047,6 +1047,7 @@ determine_capabilities() {
CONNTRACK_MATCH= CONNTRACK_MATCH=
NEW_CONNTRACK_MATCH= NEW_CONNTRACK_MATCH=
OLD_CONNTRACK_MATCH=
MULTIPORT= MULTIPORT=
XMULTIPORT= XMULTIPORT=
POLICY_MATCH= POLICY_MATCH=
@ -1105,6 +1106,7 @@ determine_capabilities() {
if [ -n "$CONNTRACK_MATCH" ]; then if [ -n "$CONNTRACK_MATCH" ]; then
qt $IPTABLES -A $chain -m conntrack -p tcp --ctorigdstport 22 -j ACCEPT && NEW_CONNTRACK_MATCH=Yes qt $IPTABLES -A $chain -m conntrack -p tcp --ctorigdstport 22 -j ACCEPT && NEW_CONNTRACK_MATCH=Yes
qt $IPTABLES -A $chain -m conntrack ! --ctorigdst 1.2.3.4 || OLD_CONNTRACK_MATCH=Yes
fi fi
if qt $IPTABLES -A $chain -p tcp -m multiport --dports 21,22 -j ACCEPT; then if qt $IPTABLES -A $chain -p tcp -m multiport --dports 21,22 -j ACCEPT; then
@ -1211,7 +1213,10 @@ report_capabilities() {
report_capability "Multi-port Match" $MULTIPORT report_capability "Multi-port Match" $MULTIPORT
[ -n "$MULTIPORT" ] && report_capability "Extended Multi-port Match" $XMULTIPORT [ -n "$MULTIPORT" ] && report_capability "Extended Multi-port Match" $XMULTIPORT
report_capability "Connection Tracking Match" $CONNTRACK_MATCH report_capability "Connection Tracking Match" $CONNTRACK_MATCH
[ -n "$CONNTRACK_MATCH" ] && report_capability "Extended Connection Tracking Match Support" $NEW_CONNTRACK_MATCH if [ -n "$CONNTRACK_MATCH" ]; then
report_capability "Extended Connection Tracking Match Support" $NEW_CONNTRACK_MATCH
report_capability "Old Connection Tracking Match Syntax" $OLD_CONNTRACK_MATCH
fi
report_capability "Packet Type Match" $USEPKTTYPE report_capability "Packet Type Match" $USEPKTTYPE
report_capability "Policy Match" $POLICY_MATCH report_capability "Policy Match" $POLICY_MATCH
report_capability "Physdev Match" $PHYSDEV_MATCH report_capability "Physdev Match" $PHYSDEV_MATCH
@ -1263,6 +1268,7 @@ report_capabilities1() {
report_capability1 XMULTIPORT report_capability1 XMULTIPORT
report_capability1 CONNTRACK_MATCH report_capability1 CONNTRACK_MATCH
report_capability1 NEW_CONNTRACK_MATCH report_capability1 NEW_CONNTRACK_MATCH
report_capability1 OLD_CONNTRACK_MATCH
report_capability1 USEPKTTYPE report_capability1 USEPKTTYPE
report_capability1 POLICY_MATCH report_capability1 POLICY_MATCH
report_capability1 PHYSDEV_MATCH report_capability1 PHYSDEV_MATCH

View File

@ -43,9 +43,11 @@ our @EXPORT = qw( merge_levels
process_actions3 process_actions3
find_macro find_macro
find_6macro
split_action split_action
substitute_param substitute_param
merge_macro_source_dest merge_macro_source_dest
merge_6macro_source_dest
merge_macro_column merge_macro_column
%usedactions %usedactions
@ -53,6 +55,7 @@ our @EXPORT = qw( merge_levels
%actions %actions
%macros %macros
%macros6
$macro_commands $macro_commands
); );
our @EXPORT_OK = qw( initialize ); our @EXPORT_OK = qw( initialize );
@ -82,6 +85,7 @@ our %actions;
our %logactionchains; our %logactionchains;
our %macros; our %macros;
our %macros6;
# #
# Commands that can be embedded in a macro file and how many total tokens on the line (0 => unlimited). # Commands that can be embedded in a macro file and how many total tokens on the line (0 => unlimited).
@ -105,6 +109,8 @@ sub initialize() {
QUEUE => 'none' ); QUEUE => 'none' );
%actions = (); %actions = ();
%logactionchains = (); %logactionchains = ();
%macros = ();
%macros6 = ();
} }
INIT { INIT {
@ -162,6 +168,19 @@ sub find_macro( $ )
} }
} }
sub find_6macro( $ )
{
my $macro = $_[0];
my $macrofile = find_file "macro.$macro";
if ( -f $macrofile ) {
$macros6{$macro} = $macrofile;
$targets6{$macro} = MACRO;
} else {
0;
}
}
# #
# Return ( action, level[:tag] ) from passed full action # Return ( action, level[:tag] ) from passed full action
# #
@ -212,6 +231,22 @@ sub merge_macro_source_dest( $$ ) {
$body || ''; $body || '';
} }
sub merge_6macro_source_dest( $$ ) {
my ( $body, $invocation ) = @_;
if ( $invocation ) {
if ( $body ) {
return $body if $invocation eq '-';
return "$body;$invocation" if $invocation =~ /:|.*?\.*?\.|^\+|^~|^!~/;
return "$invocation;$body";
}
return $invocation;
}
$body || '';
}
sub merge_macro_column( $$ ) { sub merge_macro_column( $$ ) {
my ( $body, $invocation ) = @_; my ( $body, $invocation ) = @_;

File diff suppressed because it is too large Load Diff

View File

@ -41,7 +41,7 @@ use Shorewall::Proxyarp;
our @ISA = qw(Exporter); our @ISA = qw(Exporter);
our @EXPORT = qw( compiler EXPORT TIMESTAMP DEBUG ); our @EXPORT = qw( compiler EXPORT TIMESTAMP DEBUG );
our @EXPORT_OK = qw( $export ); our @EXPORT_OK = qw( $export );
our $VERSION = 4.1.4; our $VERSION = 4.3.0;
our $export; our $export;
@ -168,6 +168,13 @@ sub generate_script_1() {
emit( 'IPTABLES_RESTORE=${IPTABLES}-restore', emit( 'IPTABLES_RESTORE=${IPTABLES}-restore',
'[ -x "$IPTABLES_RESTORE" ] || startup_error "$IPTABLES_RESTORE does not exist or is not executable"' ); '[ -x "$IPTABLES_RESTORE" ] || startup_error "$IPTABLES_RESTORE does not exist or is not executable"' );
if ( $config{IPV6} eq 'On' ) {
emit( 'IP6TABLES=$(dirname ${IPTABLES})/ip6tables',
'[ -x "$IPTABLES_RESTORE" ] || startup_error "$IP6TABLES_RESTORE does not exist or is not executable"' );
emit( 'IP6TABLES_RESTORE=$(dirname ${IPTABLES})/ip6tables-restore',
'[ -x "$IP6TABLES_RESTORE" ] || startup_error "$IP6TABLES_RESTORE does not exist or is not executable"' );
}
append_file 'params' if $config{EXPORTPARAMS}; append_file 'params' if $config{EXPORTPARAMS};
emit ( '', emit ( '',

View File

@ -114,7 +114,7 @@ our %EXPORT_TAGS = ( internal => [ qw( create_temp_object
Exporter::export_ok_tags('internal'); Exporter::export_ok_tags('internal');
our $VERSION = 4.2.0; our $VERSION = 4.3.0;
# #
# describe the current command, it's present progressive, and it's completion. # describe the current command, it's present progressive, and it's completion.
@ -163,7 +163,7 @@ our %config;
# #
# Config options and global settings that are to be copied to object script # Config options and global settings that are to be copied to object script
# #
our @propagateconfig = qw/ DISABLE_IPV6 MODULESDIR MODULE_SUFFIX LOGFORMAT SUBSYSLOCK LOCKFILE /; our @propagateconfig = qw/ IPV6 MODULESDIR MODULE_SUFFIX LOGFORMAT SUBSYSLOCK LOCKFILE /;
our @propagateenv = qw/ LOGLIMIT LOGTAGONLY LOGRULENUMBERS /; our @propagateenv = qw/ LOGLIMIT LOGTAGONLY LOGRULENUMBERS /;
# #
# From parsing the capabilities file # From parsing the capabilities file
@ -177,6 +177,8 @@ our %capdesc = ( NAT_ENABLED => 'NAT',
MULTIPORT => 'Multi-port Match' , MULTIPORT => 'Multi-port Match' ,
XMULTIPORT => 'Extended Multi-port Match', XMULTIPORT => 'Extended Multi-port Match',
CONNTRACK_MATCH => 'Connection Tracking Match', CONNTRACK_MATCH => 'Connection Tracking Match',
OLD_CONNTRACK_MATCH =>
'Old conntrack match syntax',
NEW_CONNTRACK_MATCH => NEW_CONNTRACK_MATCH =>
'Extended Connection Tracking Match', 'Extended Connection Tracking Match',
USEPKTTYPE => 'Packet Type Match', USEPKTTYPE => 'Packet Type Match',
@ -271,7 +273,7 @@ sub initialize() {
LOGPARMS => '', LOGPARMS => '',
TC_SCRIPT => '', TC_SCRIPT => '',
VERSION => "4.2.1", VERSION => "4.2.1",
CAPVERSION => 40200 , CAPVERSION => 40202 ,
); );
# #
# From shorewall.conf file # From shorewall.conf file
@ -348,6 +350,7 @@ sub initialize() {
DELAYBLACKLISTLOAD => undef, DELAYBLACKLISTLOAD => undef,
MODULE_SUFFIX => undef, MODULE_SUFFIX => undef,
DISABLE_IPV6 => undef, DISABLE_IPV6 => undef,
IPV6 => undef,
DYNAMIC_ZONES => undef, DYNAMIC_ZONES => undef,
PKTTYPE=> undef, PKTTYPE=> undef,
RFC1918_STRICT => undef, RFC1918_STRICT => undef,
@ -389,6 +392,7 @@ sub initialize() {
XMULTIPORT => undef, XMULTIPORT => undef,
CONNTRACK_MATCH => undef, CONNTRACK_MATCH => undef,
NEW_CONNTRACK_MATCH => undef, NEW_CONNTRACK_MATCH => undef,
OLD_CONNTRACK_MATCH => undef,
USEPKTTYPE => undef, USEPKTTYPE => undef,
POLICY_MATCH => undef, POLICY_MATCH => undef,
PHYSDEV_MATCH => undef, PHYSDEV_MATCH => undef,
@ -765,12 +769,14 @@ sub copy( $ ) {
open IF , $file or fatal_error "Unable to open $file: $!"; open IF , $file or fatal_error "Unable to open $file: $!";
while ( <IF> ) { while ( <IF> ) {
chomp;
if ( /^\s*$/ ) { if ( /^\s*$/ ) {
print $object "\n" unless $lastlineblank; print $object "\n" unless $lastlineblank;
$lastlineblank = 1; $lastlineblank = 1;
} else { } else {
s/^/$indent/ if $indent; s/^/$indent/ if $indent;
print $object $_; print $object $_;
print $object "\n";
$lastlineblank = 0; $lastlineblank = 0;
} }
} }
@ -791,6 +797,7 @@ sub copy1( $ ) {
my $do_indent = 1; my $do_indent = 1;
while ( <IF> ) { while ( <IF> ) {
chomp;
if ( /^\s*$/ ) { if ( /^\s*$/ ) {
print $object "\n"; print $object "\n";
$do_indent = 1; $do_indent = 1;
@ -799,6 +806,7 @@ sub copy1( $ ) {
s/^/$indent/ if $indent && $do_indent; s/^/$indent/ if $indent && $do_indent;
print $object $_; print $object $_;
print $object "\n";
$do_indent = ! ( /\\$/ ); $do_indent = ! ( /\\$/ );
} }
@ -1557,8 +1565,9 @@ sub determine_capabilities( $ ) {
$capabilities{CONNTRACK_MATCH} = qt1( "$iptables -A $sillyname -m conntrack --ctorigdst 192.168.1.1 -j ACCEPT" ); $capabilities{CONNTRACK_MATCH} = qt1( "$iptables -A $sillyname -m conntrack --ctorigdst 192.168.1.1 -j ACCEPT" );
if ( $capabilities{CONNTRACL_MATCH} ) { if ( $capabilities{CONNTRACK_MATCH} ) {
$capabilities{NEW_CONNTRACK_MATCH} = qt1( "$iptables -A $sillyname -m conntrack -p tcp --ctorigdstport 22 -j ACCEPT" ); $capabilities{NEW_CONNTRACK_MATCH} = qt1( "$iptables -A $sillyname -m conntrack -p tcp --ctorigdstport 22 -j ACCEPT" );
$capabilities{OLD_CONNTRACK_MATCH} = ! qt1( "$iptables -A $sillyname -m conntrack ! --ctorigdstport 1.2.3.4" );
} }
if ( qt1( "$iptables -A $sillyname -p tcp -m multiport --dports 21,22 -j ACCEPT" ) ) { if ( qt1( "$iptables -A $sillyname -p tcp -m multiport --dports 21,22 -j ACCEPT" ) ) {
@ -1892,7 +1901,22 @@ sub get_configuration( $ ) {
default_yes_no 'ADMINISABSENTMINDED' , ''; default_yes_no 'ADMINISABSENTMINDED' , '';
default_yes_no 'BLACKLISTNEWONLY' , ''; default_yes_no 'BLACKLISTNEWONLY' , '';
default_yes_no 'DISABLE_IPV6' , '';
if ( defined $config{IPV6} ) {
if ( $config{IPV6} =~ /on/i ) {
$config{IPV6} = 'On';
} elsif ( $config{IPV6} =~ /off/i ) {
$config{IPV6} = 'Off';
} elsif ( $config{IPV6} =~ /keep/i ) {
$config{IPV6} = '';
}
}
default_yes_no 'DISABLE_IPV6' , '';
fatal_error "Incompatible settings of IPV6 (On) and DISABLE_IPV6 (Yes)" if $config{IPV6} eq 'On' && $config{DISABLE_IPV6} eq 'Yes';
$config{IPV6} = $config{DISABLE_IPV6} ? 'Off' : '' unless defined $config{IPV6};
unsupported_yes_no 'DYNAMIC_ZONES'; unsupported_yes_no 'DYNAMIC_ZONES';
unsupported_yes_no 'BRIDGING'; unsupported_yes_no 'BRIDGING';

View File

@ -26,25 +26,36 @@
# #
package Shorewall::IPAddrs; package Shorewall::IPAddrs;
require Exporter; require Exporter;
use Socket6;
use Shorewall::Config qw( :DEFAULT split_list require_capability in_hex8); use Shorewall::Config qw( :DEFAULT split_list require_capability in_hex8);
use strict; use strict;
our @ISA = qw(Exporter); our @ISA = qw(Exporter);
our @EXPORT = qw( ALLIPv4 our @EXPORT = qw( ALLIPv4
ALLIPv6
TCP TCP
UDP UDP
ICMP ICMP
IPv6_ICMP
SCTP SCTP
F_INET
F_INET6
validate_address validate_address
validate_6address
validate_net validate_net
validate_6net
decompose_net decompose_net
validate_host validate_host
validate_6host
validate_range validate_range
validate_6range
ip_range_explicit ip_range_explicit
expand_port_range expand_port_range
allipv4 allipv4
allipv6
rfc1918_networks rfc1918_networks
resolve_proto resolve_proto
proto_name proto_name
@ -54,14 +65,23 @@ our @EXPORT = qw( ALLIPv4
validate_icmp validate_icmp
); );
our @EXPORT_OK = qw( ); our @EXPORT_OK = qw( );
our $VERSION = 4.1.5; our $VERSION = 4.3.0;
# #
# Some IPv4 useful stuff # Some IPv4/6 useful stuff
# #
our @allipv4 = ( '0.0.0.0/0' ); our @allipv4 = ( '0.0.0.0/0' );
our @allipv6 = ( '::/0' );
use constant { ALLIPv4 => '0.0.0.0/0' , ICMP => 1, TCP => 6, UDP => 17 , SCTP => 132 }; use constant { ALLIPv4 => '0.0.0.0/0' ,
ALLIPv6 => '::/0' ,
F_INET => 1,
F_INET6 => 2,
ICMP => 1,
TCP => 6,
UDP => 17,
ICMPv6_ICMP => 58,
SCTP => 132 };
our @rfc1918_networks = ( "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16" ); our @rfc1918_networks = ( "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16" );
@ -95,8 +115,7 @@ sub validate_address( $$ ) {
if ( defined wantarray ) { if ( defined wantarray ) {
shift @addrs for (1..4); shift @addrs for (1..4);
for ( @addrs ) { for ( @addrs ) {
my (@a) = unpack('C4',$_); $_ = inet_htoa $_;
$_ = join('.', @a );
} }
} }
} }
@ -223,6 +242,10 @@ sub allipv4() {
@allipv4; @allipv4;
} }
sub allipv6() {
@allipv6;
}
sub rfc1918_networks() { sub rfc1918_networks() {
@rfc1918_networks @rfc1918_networks
} }
@ -231,7 +254,7 @@ sub rfc1918_networks() {
# Protocol/port validation # Protocol/port validation
# #
our %nametoproto = ( all => 0, ALL => 0, icmp => 1, ICMP => 1, tcp => 6, TCP => 6, udp => 17, UDP => 17 ); our %nametoproto = ( all => 0, ALL => 0, icmp => 1, ICMP => 1, tcp => 6, TCP => 6, udp => 17, UDP => 17 );
our @prototoname = ( 'all', 'icmp', '', '', '', '', 'tcp', '', '', '', '', '', '', '', '', '', '', 'udp' ); our @prototoname = ( 'all', 'icmp', '', '', '', '', 'tcp', '', '', '', '', '', '', '', '', '', '', 'udp' );
# #
@ -419,4 +442,121 @@ sub expand_port_range( $$ ) {
} }
} }
sub valid_6address( $ ) {
my $address = $_[0];
my @address = split /:/, $address;
return 0 if @address > 8;
return 0 if @address < 8 && ! $address =~ /::/;
return 0 if $address =~ /:::/ || $address =~ /::.*::/;
if ( $address =~ /^:/ ) {
unless ( $address eq '::' ) {
return 0 if $address =~ /:$/ || $address =~ /^:.*::/;
}
} elsif ( $address =~ /:$/ ) {
return 0 if $address =~ /::.*:$/;
}
for my $a ( @address ) {
return 0 unless $a eq '' || ( $a =~ /^[a-fA-f\d]+$/ && oct "0x$a" < 65536 );
}
1;
}
sub validate_6address( $$ ) {
my ( $addr, $allow_name ) = @_;
my @addrs = ( $addr );
unless ( valid_6address $addr ) {
fatal_error "Invalid IPv6 Address ($addr)" unless $allow_name;
fatal_error "Unknown Host ($addr)" unless (@addrs = gethostbyname2 $addr, AF_INET6);
if ( defined wantarray ) {
shift @addrs for (1..4);
for ( @addrs ) {
$_ = inet_ntop AF_INET6, $_;
}
}
}
defined wantarray ? wantarray ? @addrs : $addrs[0] : undef;
}
}
sub validate_6net( $ ) {
my ($net, $vlsm, $rest) = split( '/', $_[0], 3 );
fatal_error "An ipset name ($net) is not allowed in this context" if substr( $net, 0, 1 ) eq '+';
if ( defined $vlsm ) {
fatal_error "Invalid VLSM ($vlsm)" unless $vlsm =~ /^\d+$/ && $vlsm <= 64;
fatal_error "Invalid Network address ($_[0])" if defined $rest;
fatal_error "Invalid IPv6 address ($net)" unless valid_6address $net;
} else {
fatal_error "Invalid Network address ($_[0])" if $_[0] =~ '/' || ! defined $net;
validate_6address $net;
}
}
sub validate_6range( $$ ) {
my ( $low, $high ) = @_;
validate_6address $low, 0;
validate_6address $high, 0;
my @low = split ":", $low;
my @high = split ":", $high;
if ( @low == @high ) {
my ( $l, $h) = ( pop @low, pop @high );
return 1 if hex "0x$l" <= hex "0x$h" && join( ":", @low ) eq join( ":", @high );
}
fatal_error "Invalid IPv6 Range ($low-$high)";
}
my %ipv6_icmp_types = ( any => 'any',
'destination-unreachable' => 1,
'no-route' => '1/0',
'communication-prohibited' => '1/1',
'address-unreachable' => '1/2',
'port-unreachable' => '1/3',
'packet-too-big' => 2,
'time-exceeded' => 3,
'ttl-exceeded' => 3,
'ttl-zero-during-transit' => '3/0',
'ttl-zero-during-reassembly' => '3/1',
'parameter-problem' => 4
'bad-header' => '4/0',
'unknown-header-type' => '4/1',
'unknown-option' => '4/2',
'echo-request' => 128,
'echo-reply' => 129,
'router-solicitation' => 133,
'router-advertisement' => 134,
'neighbour-solicitation' => 135,
'neighbour-advertisement' => 136,
redirect => 137 );
sub validate_icmp6( $ ) {
my $type = $_[0];
my $value = $ipv6_icmp_types{$type};
return $value if defined $value;
if ( $type =~ /^(\d+)(\/(\d+))?$/ ) {
return $type if $1 < 256 && ( ! $2 || $3 < 256 );
}
fatal_error "Invalid IPv6 ICMP Type ($type)"
}
1; 1;

View File

@ -26,20 +26,33 @@ package Shorewall::Policy;
require Exporter; require Exporter;
use Shorewall::Config qw(:DEFAULT :internal); use Shorewall::Config qw(:DEFAULT :internal);
use Shorewall::Zones; use Shorewall::Zones;
use Shorewall::IPAddrs;
use Shorewall::Chains qw( :DEFAULT :internal) ; use Shorewall::Chains qw( :DEFAULT :internal) ;
use Shorewall::Actions; use Shorewall::Actions;
use strict; use strict;
our @ISA = qw(Exporter); our @ISA = qw(Exporter);
our @EXPORT = qw( validate_policy apply_policy_rules complete_standard_chain sub setup_syn_flood_chains ); our @EXPORT = qw( validate_policy
validate_6policy
apply_policy_rules
apply_6policy_rules
complete_standard_chain
complete_standard_6chain
setup_syn_flood_chains
setup_syn_flood_6chains );
our @EXPORT_OK = qw( ); our @EXPORT_OK = qw( );
our $VERSION = 4.1.1; our $VERSION = 4.3.0;
# @policy_chains is a list of references to policy chains in the filter table # @policy_chains is a list of references to policy chains in the filter table
our @policy_chains; our @policy_chains;
# @policy_6chains is a list of references to policy chains in the filter6 table
our @policy_6chains;
# #
# Initialize globals -- we take this novel approach to globals initialization to allow # Initialize globals -- we take this novel approach to globals initialization to allow
# the compiler to run multiple times in the same process. The # the compiler to run multiple times in the same process. The
@ -51,6 +64,7 @@ our @policy_chains;
sub initialize() { sub initialize() {
@policy_chains = (); @policy_chains = ();
@policy_6chains = ();
} }
INIT { INIT {
@ -85,6 +99,20 @@ sub new_policy_chain($$$$)
$chainref; $chainref;
} }
#
# Create a new policy 6chain and return a reference to it.
#
sub new_policy_6chain($$$$)
{
my ($source, $dest, $policy, $optional) = @_;
my $chainref = new_6chain( 'filter', "${source}2${dest}" );
convert_to_policy_chain( $chainref, $source, $dest, $policy, $optional );
$chainref;
}
# #
# Set the passed chain's policychain and policy to the passed values. # Set the passed chain's policychain and policy to the passed values.
# #
@ -142,6 +170,21 @@ sub add_or_modify_policy_chain( $$ ) {
} }
} }
sub add_or_modify_policy_6chain( $$ ) {
my ( $zone, $zone1 ) = @_;
my $chain = "${zone}2${zone1}";
my $chainref = $filter6_table->{$chain};
if ( $chainref ) {
unless( $chainref->{is_policy} ) {
convert_to_policy_chain( $chainref, $zone, $zone1, 'CONTINUE', OPTIONAL );
push @policy_6chains, $chainref;
}
} else {
push @policy_6chains, ( new_policy_chain $zone, $zone1, 'CONTINUE', OPTIONAL );
}
}
sub print_policy($$$$) { sub print_policy($$$$) {
my ( $source, $dest, $policy , $chain ) = @_; my ( $source, $dest, $policy , $chain ) = @_;
unless ( ( $source eq 'all' ) || ( $dest eq 'all' ) ) { unless ( ( $source eq 'all' ) || ( $dest eq 'all' ) ) {
@ -220,11 +263,17 @@ sub validate_policy()
my $clientwild = ( "\L$client" eq 'all' ); my $clientwild = ( "\L$client" eq 'all' );
fatal_error "Undefined zone ($client)" unless $clientwild || defined_zone( $client ); unless ( $clientwild ) {
fatal_error "Undefined zone ($client)" unless defined_zone( $client );
fatal_error "IPv6 zone ($client) not permitted in policy file" unless zone_family($client) & F_INET;
}
my $serverwild = ( "\L$server" eq 'all' ); my $serverwild = ( "\L$server" eq 'all' );
fatal_error "Undefined zone ($server)" unless $serverwild || defined_zone( $server ); unless ( $serverwild ) {
fatal_error "Undefined zone ($server)" unless defined_zone( $server );
fatal_error "IPv6 zone ($server) not permitted in policy file" unless zone_family($server) & F_INET;
}
my ( $policy, $default, $remainder ) = split( /:/, $originalpolicy, 3 ); my ( $policy, $default, $remainder ) = split( /:/, $originalpolicy, 3 );
@ -344,6 +393,205 @@ sub validate_policy()
} }
} }
sub validate_6policy()
{
my %validpolicies = (
ACCEPT => undef,
REJECT => undef,
DROP => undef,
CONTINUE => undef,
QUEUE => undef,
NFQUEUE => undef,
NONE => undef
);
my %map = ( DROP_DEFAULT => 'DROP' ,
REJECT_DEFAULT => 'REJECT' ,
ACCEPT_DEFAULT => 'ACCEPT' ,
QUEUE_DEFAULT => 'QUEUE' ,
NFQUEUE_DEFAULT => 'NFQUEUE' );
my $zone;
my @zonelist = $config{EXPAND_POLICIES} ? all_6zones : ( all_6zones, 'all' );
for my $option qw/DROP_DEFAULT REJECT_DEFAULT ACCEPT_DEFAULT QUEUE_DEFAULT NFQUEUE_DEFAULT/ {
my $action = $config{$option};
next if $action eq 'none';
my $actiontype = $targets{$action};
if ( defined $actiontype ) {
fatal_error "Invalid setting ($action) for $option" unless $actiontype & ACTION;
} else {
fatal_error "Default Action $option=$action not found";
}
unless ( $usedactions{$action} ) {
$usedactions{$action} = 1;
createactionchain $action;
}
$default_actions{$map{$option}} = $action;
}
for $zone ( all_6zones ) {
push @policy_6chains, ( new_policy_6chain $zone, $zone, 'ACCEPT', OPTIONAL );
if ( $config{IMPLICIT_CONTINUE} && ( @{find_zone( $zone )->{parents}} ) ) {
for my $zone1 ( all_6zones ) {
unless( $zone eq $zone1 ) {
add_or_modify_policy_chain( $zone, $zone1 );
add_or_modify_policy_chain( $zone1, $zone );
}
}
}
}
my $fn = open_file '6policy';
first_entry "$doing $fn...";
while ( read_a_line ) {
my ( $client, $server, $originalpolicy, $loglevel, $synparams, $connlimit ) = split_line 3, 6, '6policy file';
$loglevel = '' if $loglevel eq '-';
$synparams = '' if $synparams eq '-';
$connlimit = '' if $connlimit eq '-';
my $clientwild = ( "\L$client" eq 'all' );
unless ( $clientwild ) {
fatal_error "Undefined zone ($client)" unless defined_zone( $client );
fatal_error "IPv4 zone ($client) not permitted in policy file" unless zone_family($client) & F_INET6;
}
my $serverwild = ( "\L$server" eq 'all' );
unless ( $serverwild ) {
fatal_error "Undefined zone ($server)" unless defined_zone( $server );
fatal_error "IPv4 zone ($server) not permitted in policy file" unless zone_family($server) & F_INET6;
}
my ( $policy, $default, $remainder ) = split( /:/, $originalpolicy, 3 );
fatal_error "Invalid or missing POLICY ($originalpolicy)" unless $policy;
fatal_error "Invalid default action ($default:$remainder)" if defined $remainder;
( $policy , my $queue ) = get_target_param $policy;
if ( $default ) {
if ( "\L$default" eq 'none' ) {
$default = 'none';
} else {
my $defaulttype = $targets6{$default} || 0;
if ( $defaulttype & ACTION ) {
unless ( $usedactions{$default} ) {
$usedactions{$default} = 1;
createactionchain $default;
}
} else {
fatal_error "Unknown Default Action ($default)";
}
}
} else {
$default = $default_actions{$policy} || '';
}
fatal_error "Invalid policy ($policy)" unless exists $validpolicies{$policy};
if ( defined $queue ) {
fatal_error "Invalid policy ($policy($queue))" unless $policy eq 'NFQUEUE';
require_capability( 'NFQUEUE_TARGET', 'An NFQUEUE Policy', 's' );
my $queuenum = numeric_value( $queue );
fatal_error "Invalid NFQUEUE queue number ($queue)" unless defined( $queuenum) && $queuenum <= 65535;
$policy = "NFQUEUE --queue-num $queuenum";
} elsif ( $policy eq 'NONE' ) {
fatal_error "NONE policy not allowed with \"all\""
if $clientwild || $serverwild;
fatal_error "NONE policy not allowed to/from firewall zone"
if ( zone_type( $client ) eq 'firewall' ) || ( zone_type( $server ) eq 'firewall' );
}
unless ( $clientwild || $serverwild ) {
if ( zone_type( $server ) eq 'bport6' ) {
fatal_error "Invalid policy - DEST zone is a Bridge Port zone but the SOURCE zone is not associated with the same bridge"
unless find_zone( $client )->{bridge} eq find_zone( $server)->{bridge} || single_interface( $client ) eq find_zone( $server )->{bridge};
}
}
my $chain = "${client}2${server}";
my $chainref;
if ( defined $filter_table->{$chain} ) {
$chainref = $filter_table->{$chain};
if ( $chainref->{is_policy} ) {
if ( $chainref->{is_optional} ) {
$chainref->{is_optional} = 0;
$chainref->{policy} = $policy;
} else {
fatal_error qq(Policy "$client $server $policy" duplicates earlier policy "@{$chainref->{policypair}} $chainref->{policy}");
}
} elsif ( $chainref->{policy} ) {
fatal_error qq(Policy "$client $server $policy" duplicates earlier policy "@{$chainref->{policypair}} $chainref->{policy}");
} else {
convert_to_policy_chain( $chainref, $client, $server, $policy, 0 );
push @policy_6chains, ( $chainref ) unless $config{EXPAND_POLICIES} && ( $clientwild || $serverwild );
}
} else {
$chainref = new_policy_6chain $client, $server, $policy, 0;
push @policy_6chains, ( $chainref ) unless $config{EXPAND_POLICIES} && ( $clientwild || $serverwild );
}
$chainref->{loglevel} = validate_level( $loglevel ) if defined $loglevel && $loglevel ne '';
if ( $synparams ne '' || $connlimit ne '' ) {
my $value = '';
fatal_error "Invalid CONNLIMIT ($connlimit)" if $connlimit =~ /^!/;
$value = do_ratelimit $synparams, 'ACCEPT' if $synparams ne '';
$value .= do_connlimit $connlimit if $connlimit ne '';
$chainref->{synparams} = $value;
$chainref->{synchain} = $chain
}
$chainref->{default} = $default if $default;
if ( $clientwild ) {
if ( $serverwild ) {
for my $zone ( @zonelist ) {
for my $zone1 ( @zonelist ) {
set_policy_chain $client, $server, "${zone}2${zone1}", $chainref, $policy;
print_policy $zone, $zone1, $policy, $chain;
}
}
} else {
for my $zone ( all_zones ) {
set_policy_chain $client, $server, "${zone}2${server}", $chainref, $policy;
print_policy $zone, $server, $policy, $chain;
}
}
} elsif ( $serverwild ) {
for my $zone ( @zonelist ) {
set_policy_chain $client, $server, "${client}2${zone}", $chainref, $policy;
print_policy $client, $zone, $policy, $chain;
}
} else {
print_policy $client, $server, $policy, $chain;
}
}
for $zone ( all_zones ) {
for my $zone1 ( all_zones ) {
fatal_error "No policy defined from zone $zone to zone $zone1" unless $filter_table->{"${zone}2${zone1}"}{policy};
}
}
}
# #
# Policy Rule application # Policy Rule application
# #
@ -361,6 +609,19 @@ sub policy_rules( $$$$$ ) {
} }
} }
sub policy_6rules( $$$$$ ) {
my ( $chainref , $target, $loglevel, $default, $dropmulticast ) = @_;
unless ( $target eq 'NONE' ) {
add_rule $chainref, "-j $default" if $default && $default ne 'none';
log_rule $loglevel , $chainref , $target , '' if $loglevel ne '';
fatal_error "Null target in policy_rules()" unless $target;
$target = 'reject' if $target eq 'REJECT';
add_jump( $chainref , $target ) unless $target eq 'CONTINUE';
}
}
sub report_syn_flood_protection() { sub report_syn_flood_protection() {
progress_message ' Enabled SYN flood protection'; progress_message ' Enabled SYN flood protection';
} }
@ -400,8 +661,43 @@ sub default_policy( $$$ ) {
} }
sub default_6policy( $$$ ) {
my $chainref = $_[0];
my $policyref = $filter6_table->{$chainref->{policychain}};
my $synparams = $policyref->{synparams};
my $default = $policyref->{default};
my $policy = $policyref->{policy};
my $loglevel = $policyref->{loglevel};
fatal_error "Internal error in default_6policy()" unless $policyref;
if ( $chainref eq $policyref ) {
policy_6rules $chainref , $policy, $loglevel , $default, $config{MULTICAST};
} else {
if ( $policy eq 'ACCEPT' || $policy eq 'QUEUE' || $policy =~ /^NFQUEUE/ ) {
if ( $synparams ) {
report_syn_flood_protection;
policy_6rules $chainref , $policy , $loglevel , $default, $config{MULTICAST};
} else {
add_jump $chainref, $policyref;
$chainref = $policyref;
}
} elsif ( $policy eq 'CONTINUE' ) {
report_syn_flood_protection if $synparams;
policy_6rules $chainref , $policy , $loglevel , $default, $config{MULTICAST};
} else {
report_syn_flood_protection if $synparams;
add_jump $chainref , $policyref;
$chainref = $policyref;
}
}
progress_message " Policy $policy from $_[1] to $_[2] using chain $chainref->{name}";
}
sub apply_policy_rules() { sub apply_policy_rules() {
progress_message2 'Applying Policies...'; progress_message2 'Applying IPv4 Policies...';
for my $chainref ( @policy_chains ) { for my $chainref ( @policy_chains ) {
my $policy = $chainref->{policy}; my $policy = $chainref->{policy};
@ -434,6 +730,40 @@ sub apply_policy_rules() {
} }
} }
sub apply_6policy_rules() {
progress_message2 'Applying IPv6 Policies...';
for my $chainref ( @policy_6chains ) {
my $policy = $chainref->{policy};
my $loglevel = $chainref->{loglevel};
my $optional = $chainref->{is_optional};
my $default = $chainref->{default};
my $name = $chainref->{name};
if ( $policy ne 'NONE' ) {
if ( ! $chainref->{referenced} && ( ! $optional && $policy ne 'CONTINUE' ) ) {
ensure_filter_6chain $name, 1;
}
if ( $name =~ /^all2|2all$/ ) {
run_user_exit $chainref;
policy_6rules $chainref , $policy, $loglevel , $default, $config{MULTICAST};
}
}
}
for my $zone ( all_6zones ) {
for my $zone1 ( all_6zones ) {
my $chainref = $filter6_table->{"${zone}2${zone1}"};
if ( $chainref->{referenced} ) {
run_user_exit $chainref;
default_6policy $chainref, $zone, $zone1;
}
}
}
}
# #
# Complete a standard chain # Complete a standard chain
# #
@ -461,6 +791,33 @@ sub complete_standard_chain ( $$$$ ) {
policy_rules $stdchainref , $policy , $loglevel, $defaultaction, 0; policy_rules $stdchainref , $policy , $loglevel, $defaultaction, 0;
} }
#
# Complete a standard chain
#
# - run any supplied user exit
# - search the policy file for an applicable policy and add rules as
# appropriate
# - If no applicable policy is found, add rules for an assummed
# policy of DROP INFO
#
sub complete_standard_6chain ( $$$$ ) {
my ( $stdchainref, $zone, $zone2, $default ) = @_;
add_rule $stdchainref, '-m state --state ESTABLISHED,RELATED -j ACCEPT' unless $config{FASTACCEPT};
run_user_exit $stdchainref;
my $ruleschainref = $filter6_table->{"${zone}2${zone2}"};
my ( $policy, $loglevel, $defaultaction ) = ( $default , 6, $config{$default . '_DEFAULT'} );
my $policychainref;
$policychainref = $filter6_table->{$ruleschainref->{policychain}} if $ruleschainref;
( $policy, $loglevel, $defaultaction ) = @{$policychainref}{'policy', 'loglevel', 'default' } if $policychainref;
policy_rules $stdchainref , $policy , $loglevel, $defaultaction, 0;
}
# #
# Create and populate the synflood chains corresponding to entries in /etc/shorewall/policy # Create and populate the synflood chains corresponding to entries in /etc/shorewall/policy
# #
@ -478,4 +835,21 @@ sub setup_syn_flood_chains() {
} }
} }
#
# Create and populate the synflood chains corresponding to entries in /etc/shorewall/policy
#
sub setup_syn_flood_6chains() {
for my $chainref ( @policy_6chains ) {
my $limit = $chainref->{synparams};
if ( $limit && ! $filter6_table->{syn_flood_chain $chainref} ) {
my $level = $chainref->{loglevel};
my $synchainref = new_6chain 'filter' , syn_flood_chain $chainref;
add_rule $synchainref , "${limit}-j RETURN";
log_rule_limit $level , $synchainref , $chainref->{name} , 'DROP', '-m limit --limit 5/min --limit-burst 5 ' , '' , 'add' , ''
if $level ne '';
add_rule $synchainref, '-j DROP';
}
}
}
1; 1;

View File

@ -52,6 +52,7 @@ our $VERSION = 4.1.5;
# Set to one if we find a SECTION # Set to one if we find a SECTION
# #
our $sectioned; our $sectioned;
our $sectioned6;
our $macro_nest_level; our $macro_nest_level;
our $current_param; our $current_param;
our @param_stack; our @param_stack;
@ -74,6 +75,7 @@ my %rules_commands = ( COMMENT => 0,
sub initialize() { sub initialize() {
$sectioned = 0; $sectioned = 0;
$sectioned6 = 0;
$macro_nest_level = 0; $macro_nest_level = 0;
$current_param = ''; $current_param = '';
@param_stack = (); @param_stack = ();
@ -386,6 +388,125 @@ sub process_criticalhosts() {
\@critical; \@critical;
} }
sub setup_6blacklist() {
my $hosts = find_hosts_by_option '6blacklist';
my $chainref;
my ( $level, $disposition ) = @config{'BLACKLIST_LOGLEVEL', 'BLACKLIST_DISPOSITION' };
my $target = $disposition eq 'REJECT' ? 'reject' : $disposition;
if ( @$hosts ) {
$chainref = new_standard_6chain 'blacklst';
if ( defined $level && $level ne '' ) {
my $logchainref = new_standard_6chain 'blacklog';
log_rule_limit( $level , $logchainref , 'blacklst' , $disposition , "$globals{LOGLIMIT}" , '', 'add', '' );
add_rule $logchainref, "-j $target" ;
$target = 'blacklog';
}
}
BLACKLIST:
{
if ( my $fn = open_file '6blacklist' ) {
my $first_entry = 1;
first_entry "$doing $fn...";
while ( read_a_line ) {
if ( $first_entry ) {
unless ( @$hosts ) {
warning_message q(The entries in $fn have been ignored because there are no 'blacklist' interfaces);
close_file;
last BLACKLIST;
}
$first_entry = 0;
}
my ( $networks, $protocol, $ports ) = split_line 1, 3, 'blacklist file';
expand_rule(
$chainref ,
NO_RESTRICT ,
do_proto6( $protocol , $ports, '' ) ,
$networks ,
'' ,
'' ,
'' ,
"-j $target" ,
'' ,
$disposition ,
'' );
progress_message " \"$currentline\" added to blacklist";
}
}
my $state = $config{BLACKLISTNEWONLY} ? '-m state --state NEW,INVALID ' : '';
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_6net $network;
for my $chain ( first_chains $interface ) {
add_rule $filter_table->{$chain} , "${source}${state}${policy}-j blacklst";
}
progress_message " Blacklisting enabled on ${interface};${network}";
}
}
}
sub process_critical6hosts() {
my @critical = ();
my $fn = open_file '6routestopped';
first_entry "$doing $fn for critical IPv6 hosts...";
while ( read_a_line ) {
my $routeback = 0;
my ($interface, $hosts, $options ) = split_line 1, 3, 'routestopped file';
fatal_error "Unknown interface ($interface)" unless known_6interface $interface;
$hosts = ALLIPv6 unless $hosts ne '-';
my @hosts;
for my $host ( split_list $hosts, 'host' ) {
validate_host $host, 1;
push @hosts, "$interface;$host";
}
unless ( $options eq '-' ) {
for my $option (split_list $options, 'option' ) {
unless ( $option eq 'routeback' || $option eq 'source' || $option eq 'dest' ) {
if ( $option eq 'critical' ) {
push @critical, @hosts;
} else {
warning_message "Unknown routestopped option ( $option ) ignored";
}
}
}
}
}
\@critical;
}
sub process_routestopped() { sub process_routestopped() {
my ( @allhosts, %source, %dest ); my ( @allhosts, %source, %dest );
@ -480,6 +601,100 @@ sub process_routestopped() {
} }
} }
sub process_6routestopped() {
my ( @allhosts, %source, %dest );
my $fn = open_file '6routestopped';
first_entry "$doing $fn...";
while ( read_a_line ) {
my $routeback = 0;
my ($interface, $hosts, $options ) = split_line 1, 3, '6routestopped file';
fatal_error "Unknown interface ($interface)" unless known_6interface $interface;
$hosts = ALLIPv6 unless $hosts && $hosts ne '-';
my @hosts;
for my $host ( split /,/, $hosts ) {
validate_6host $host, 1;
push @hosts, "$interface;$host";
}
unless ( $options eq '-' ) {
for my $option (split /,/, $options ) {
if ( $option eq 'routeback' ) {
if ( $routeback ) {
warning_message "Duplicate 'routeback' option ignored";
} else {
$routeback = 1;
for my $host ( split /,/, $hosts ) {
my $source = match_source_6net $host;
my $dest = match_dest_6net $host;
emit "run_ip6tables -A FORWARD -i $interface -o $interface $source $dest -j ACCEPT";
clearrule;
}
}
} 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;
}
} else {
warning_message "Unknown 6routestopped option ( $option ) ignored" unless $option eq 'critical';
}
}
}
push @allhosts, @hosts;
}
for my $host ( @allhosts ) {
my ( $interface, $h ) = split /;/, $host;
my $source = match_source_6net $h;
my $dest = match_dest_6net $h;
my $sourcei = match_source_6dev $interface;
my $desti = match_dest_6dev $interface;
emit "\$IP6TABLES -A INPUT $sourcei $source -j ACCEPT";
emit "\$IP6TABLES -A OUTPUT $desti $dest -j ACCEPT" unless $config{ADMINISABSENTMINDED};
my $matched = 0;
if ( $source{$host} ) {
emit "\$IP6TABLES -A FORWARD $sourcei $source -j ACCEPT";
$matched = 1;
}
if ( $dest{$host} ) {
emit "\$IP6TABLES -A FORWARD $desti $dest -j ACCEPT";
$matched = 1;
}
unless ( $matched ) {
for my $host1 ( @allhosts ) {
unless ( $host eq $host1 ) {
my ( $interface1, $h1 ) = split /;/, $host1;
my $dest1 = match_dest_6net $h1;
my $desti1 = match_dest_6dev $interface1;
emit "\$IP6TABLES -A FORWARD $sourcei $desti1 $source $dest1 -j ACCEPT";
clearrule;
}
}
}
}
}
sub setup_mss(); sub setup_mss();
sub add_common_rules() { sub add_common_rules() {
@ -1447,6 +1662,642 @@ sub process_rules() {
$section = 'DONE'; $section = 'DONE';
} }
sub process_6rule1 ( $$$$$$$$$$$$ );
#
# Expand a macro rule from the rules file
#
sub process_6macro ( $$$$$$$$$$$$$$ ) {
my ($macro, $target, $param, $source, $dest, $proto, $ports, $sports, $rate, $user, $mark, $connlimit, $time, $wildcard ) = @_;
my $nocomment = no_comment;
my $format = 1;
macro_comment $macro;
my $macrofile = $macros{$macro};
progress_message "..Expanding Macro $macrofile...";
push_open $macrofile;
while ( read_a_line ) {
my ( $mtarget, $msource, $mdest, $mproto, $mports, $msports, $morigdest, $mrate, $muser );
if ( $format == 1 ) {
( $mtarget, $msource, $mdest, $mproto, $mports, $msports, $mrate, $muser, $morigdest ) = split_line1 1, 9, 'macro file', $macro_commands;
} else {
( $mtarget, $msource, $mdest, $mproto, $mports, $msports, $morigdest, $mrate, $muser ) = split_line1 1, 9, 'macro file', $macro_commands;
}
if ( $mtarget eq 'COMMENT' ) {
process_comment unless $nocomment;
next;
}
if ( $mtarget eq 'FORMAT' ) {
fatal_error "Invalid FORMAT ($msource)" unless $msource =~ /^[12]$/;
$format = $msource;
next;
}
if ( $morigdest ne '-' ) {
fatal_error "Invalid macro file entry (too many columns)" if $format == 1;
fatal_error "A macro with ORIGINAL DEST cannot be used with IPv6";
}
$mtarget = merge_levels $target, $mtarget;
if ( $mtarget =~ /^PARAM(:.*)?$/ ) {
fatal_error 'PARAM requires a parameter to be supplied in macro invocation' unless $param ne '';
$mtarget = substitute_param $param, $mtarget;
}
my $action = isolate_basic_target $mtarget;
fatal_error "Invalid or missing ACTION ($mtarget)" unless defined $action;
my $actiontype = $targets6{$action} || find_6macro( $action );
fatal_error "Invalid Action ($mtarget) in macro" unless $actiontype & ( ACTION + STANDARD + NATRULE + MACRO );
if ( $msource ) {
if ( $msource eq '-' ) {
$msource = $source || '';
} elsif ( $msource =~ s/^DEST:?// ) {
$msource = merge_6macro_source_dest $msource, $dest;
} else {
$msource =~ s/^SOURCE:?//;
$msource = merge_6macro_source_dest $msource, $source;
}
} else {
$msource = '';
}
if ( $mdest ) {
if ( $mdest eq '-' ) {
$mdest = $dest || '';
} elsif ( $mdest =~ s/^SOURCE:?// ) {
$mdest = merge_6macro_source_dest $mdest , $source;
} else {
$mdest =~ s/DEST:?//;
$mdest = merge_6macro_source_dest $mdest, $dest;
}
} else {
$mdest = '';
}
process_6rule1(
$mtarget,
$msource,
$mdest,
merge_macro_column( $mproto, $proto ) ,
merge_macro_column( $mports, $ports ) ,
merge_macro_column( $msports, $sports ) ,
merge_macro_column( $mrate, $rate ) ,
merge_macro_column( $muser, $user ) ,
$mark,
$connlimit,
$time,
$wildcard
);
progress_message " IPv6 Rule \"$currentline\" $done";
}
pop_open;
progress_message "..End Macro $macrofile";
clear_comment unless $nocomment;
}
#
# Once a rule has been expanded via wildcards (source and/or dest zone == 'all'), it is processed by this function. If
# the target is a macro, the macro is expanded and this function is called recursively for each rule in the expansion.
#
sub process_rule1 ( $$$$$$$$$$$$$ ) {
my ( $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark, $connlimit, $time, $wildcard ) = @_;
my ( $action, $loglevel) = split_action $target;
my ( $basictarget, $param ) = get_target_param $action;
my $rule = '';
my $actionchainref;
my $optimize = $wildcard ? ( $basictarget =~ /!$/ ? 0 : $config{OPTIMIZE} ) : 0;
unless ( defined $param ) {
( $basictarget, $param ) = ( $1, $2 ) if $action =~ /^(\w+)[(](.*)[)]$/;
}
$param = '' unless defined $param;
#
# Determine the validity of the action
#
my $actiontype = $targets{$basictarget} || find_macro( $basictarget );
fatal_error "Unknown action ($action)" unless $actiontype;
if ( $actiontype == MACRO ) {
#
# process_macro() will call process_rule1() recursively for each rule in the macro body
#
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;
}
process_macro( $basictarget,
$target ,
$current_param,
$source,
$dest,
$proto,
$ports,
$sports,
$origdest,
$ratelimit,
$user,
$mark,
$connlimit,
$time,
$wildcard );
$macro_nest_level--;
$current_param = pop @param_stack if $param ne '';
return;
} elsif ( $actiontype & NFQ ) {
require_capability( 'NFQUEUE_TARGET', 'NFQUEUE Rules', '' );
my $paramval = $param eq '' ? 0 : numeric_value( $param );
fatal_error "Invalid value ($param) for NFQUEUE queue number" unless defined($paramval) && $paramval <= 65535;
$action = "NFQUEUE --queue-num $paramval";
} else {
fatal_error "The $basictarget TARGET does not accept a parameter" unless $param eq '';
}
#
# We can now dispense with the postfix character
#
$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 ) {
my $z = $actiontype & NATONLY ? '' : firewall_zone;
if ( $dest eq '-' ) {
$dest = join( '', $z, '::' , $ports =~ /[:,]/ ? '' : $ports );
} else {
$dest = join( '', $z, '::', $dest ) unless $dest =~ /:/;
}
} elsif ( $action eq 'REJECT' ) {
$action = 'reject';
} elsif ( $action eq 'CONTINUE' ) {
$action = 'RETURN';
} elsif ( $actiontype & LOGRULE ) {
fatal_error 'LOG requires a log level' unless defined $loglevel and $loglevel ne '';
}
#
# Isolate and validate source and destination zones
#
my $sourcezone;
my $destzone;
my $sourceref;
my $destref;
my $origdstports;
if ( $source =~ /^(.+?):(.*)/ ) {
fatal_error "Missing SOURCE Qualifier ($source)" if $2 eq '';
$sourcezone = $1;
$source = $2;
} else {
$sourcezone = $source;
$source = ALLIPv4;
}
if ( $dest =~ /^(.*?):(.*)/ ) {
fatal_error "Missing DEST Qualifier ($dest)" if $2 eq '';
$destzone = $1;
$dest = $2;
} else {
$destzone = $dest;
$dest = ALLIPv4;
}
fatal_error "Missing source zone" if $sourcezone eq '-' || $sourcezone =~ /^:/;
fatal_error "Unknown source zone ($sourcezone)" unless $sourceref = defined_zone( $sourcezone );
if ( $actiontype & NATONLY ) {
warning_message "Destination zone ($destzone) ignored" unless $destzone eq '-' || $destzone eq '';
} else {
fatal_error "Missing destination zone" if $destzone eq '-' || $destzone eq '';
fatal_error "Unknown destination zone ($destzone)" unless $destref = defined_zone( $destzone );
}
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;
}
my ( $chain, $chainref, $policy );
#
# For compatibility with older Shorewall versions
#
$origdest = ALLIPv4 if $origdest eq 'all';
#
# Take care of chain
#
unless ( $actiontype & NATONLY ) {
#
# Check for illegal bridge port rule
#
if ( $destref->{type} eq 'bport4' ) {
unless ( $sourceref->{bridge} eq $destref->{bridge} || single_interface( $sourcezone ) eq $destref->{bridge} ) {
return 1 if $wildcard;
fatal_error "Rules with a DESTINATION Bridge Port zone must have a SOURCE zone on the same bridge";
}
}
$chain = "${sourcezone}2${destzone}";
$chainref = ensure_chain 'filter', $chain;
$policy = $chainref->{policy};
if ( $policy eq 'NONE' ) {
return 1 if $wildcard;
fatal_error "Rules may not override a NONE policy";
}
#
# Handle Optimization
#
if ( $optimize > 0 ) {
my $loglevel = $filter_table->{$chainref->{policychain}}{loglevel};
if ( $loglevel ne '' ) {
return 1 if $target eq "${policy}:$loglevel}";
} else {
return 1 if $basictarget eq $policy;
}
}
#
# Mark the chain as referenced and add appropriate rules from earlier sections.
#
$chainref = ensure_filter_chain $chain, 1;
}
#
# Generate Fixed part of the rule
#
$rule = join( '', do_proto($proto, $ports, $sports), do_ratelimit( $ratelimit, $basictarget ) , do_user( $user ) , do_test( $mark , 0xFF ) , do_connlimit( $connlimit ), do_time( $time ) );
unless ( $section eq 'NEW' ) {
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 & ( NATRULE | NONAT );
$rule .= "-m state --state $section "
}
#
# Generate NAT rule(s), if any
#
if ( $actiontype & NATRULE ) {
my ( $server, $serverport );
my $randomize = $dest =~ s/:random$// ? '--random ' : '';
require_capability( 'NAT_ENABLED' , "$basictarget rules", '' );
#
# Isolate server port
#
if ( $dest =~ /^(.*)(:(.+))$/ ) {
#
# Server IP and Port
#
$server = $1; # May be empty
$serverport = $3; # Not Empty due to RE
$origdstports = $ports;
if ( $serverport =~ /^(\d+)-(\d+)$/ ) {
#
# Server Port Range
#
fatal_error "Invalid port range ($serverport)" unless $1 < $2;
my @ports = ( $1, $2 );
$_ = validate_port( proto_name( $proto ), $_) for ( @ports );
( $ports = $serverport ) =~ tr/-/:/;
} else {
$serverport = $ports = validate_port( proto_name( $proto ), $serverport );
}
} elsif ( $dest eq ':' ) {
#
# Rule with no server IP or port ( zone:: )
#
$server = $serverport = '';
} else {
#
# Simple server IP address (may be empty or "-")
#
$server = $dest;
$serverport = '';
}
#
# Generate the target
#
my $target = '';
if ( $actiontype & REDIRECT ) {
fatal_error "A server IP address may not be specified in a REDIRECT rule" if $server;
$target = '-j REDIRECT ';
$target .= "--to-port $serverport " if $serverport;
if ( $origdest eq '' || $origdest eq '-' ) {
$origdest = ALLIPv4;
} elsif ( $origdest eq 'detect' ) {
if ( $config{DETECT_DNAT_IPADDRS} && $sourcezone ne firewall_zone ) {
my $interfacesref = $sourceref->{interfaces};
my @interfaces = keys %$interfacesref;
$origdest = @interfaces ? "detect:@interfaces" : ALLIPv4;
} else {
$origdest = ALLIPv4;
}
}
} else {
fatal_error "A server must be specified in the DEST column in $action rules" if $server eq '';
if ( $server =~ /^(.+)-(.+)$/ ) {
validate_range( $1, $2 );
} else {
$server = validate_address $server, 1;
}
if ( $action eq 'SAME' ) {
fatal_error 'Port mapping not allowed in SAME rules' if $serverport;
fatal_error 'SAME not allowed with SOURCE=$FW' if $sourcezone eq firewall_zone;
fatal_error "':random' is not supported by the SAME target" if $randomize;
warning_message 'Netfilter support for SAME is being dropped in early 2008';
$target = '-j SAME ';
for my $serv ( split /,/, $server ) {
$target .= "--to $serv ";
}
} elsif ( $action eq 'DNAT' ) {
$target = '-j DNAT ';
$serverport = ":$serverport" if $serverport;
for my $serv ( split /,/, $server ) {
$target .= "--to-destination ${serv}${serverport} ";
}
}
unless ( $origdest && $origdest ne '-' && $origdest ne 'detect' ) {
if ( $config{DETECT_DNAT_IPADDRS} && $sourcezone ne firewall_zone ) {
my $interfacesref = $sourceref->{interfaces};
my @interfaces = keys %$interfacesref;
$origdest = @interfaces ? "detect:@interfaces" : ALLIPv4;
} else {
$origdest = ALLIPv4;
}
}
}
$target .= $randomize;
#
# And generate the nat table rule(s)
#
expand_rule ( ensure_chain ('nat' , $sourceref->{type} eq 'firewall' ? 'OUTPUT' : dnat_chain $sourcezone ),
PREROUTE_RESTRICT ,
$rule ,
$source ,
$origdest ,
'' ,
'' ,
$target ,
$loglevel ,
$action ,
$serverport ? do_proto( $proto, '', '' ) : '' );
#
# After NAT:
# - the destination port will be the server port ($ports) -- we did that above
# - the destination IP will be the server IP ($dest)
# - there will be no log level (we log NAT rules in the nat table rather than in the filter table).
# - the target will be ACCEPT.
#
unless ( $actiontype & NATONLY ) {
$rule = join( '', do_proto( $proto, $ports, $sports ), do_ratelimit( $ratelimit, 'ACCEPT' ), do_user $user , do_test( $mark , 0xFF ) );
$loglevel = '';
$dest = $server;
$action = 'ACCEPT';
}
} 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 '-';
if ( $origdest eq 'detect' ) {
my $interfacesref = $sourceref->{interfaces};
my $interfaces = "@$interfacesref";
$origdest = $interfaces ? "detect:$interfaces" : ALLIPv4;
}
expand_rule( ensure_chain ('nat' , $sourceref->{type} eq 'firewall' ? 'OUTPUT' : dnat_chain $sourcezone) ,
PREROUTE_RESTRICT ,
$rule ,
$source ,
$dest ,
$origdest ,
'',
'-j RETURN ' ,
$loglevel ,
$action ,
'' );
}
#
# Add filter table rule, unless this is a NATONLY rule type
#
unless ( $actiontype & NATONLY ) {
if ( $actiontype & ACTION ) {
$action = (find_logactionchain $target)->{name};
$loglevel = '';
}
unless ( $origdest eq '-' ) {
require_capability( 'CONNTRACK_MATCH', 'ORIGINAL DEST in a non-NAT rule', 's' ) unless $actiontype & NATRULE;
} else {
$origdest = '';
}
expand_rule( ensure_chain( 'filter', $chain ) ,
$restriction ,
$rule ,
$source ,
$dest ,
$origdest ,
$origdstports ,
"-j $action " ,
$loglevel ,
$action ,
'' );
}
}
#
# Process a Record in the rules file
#
# Deals with the ugliness of wildcard zones ('all' in SOURCE and/or DEST column).
#
sub process_rule ( $$$$$$$$$$$$ ) {
my ( $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark, $connlimit , $time ) = @_;
my $intrazone = 0;
my $includesrcfw = 1;
my $includedstfw = 1;
my $thisline = $currentline;
#
# 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';
$sections{$section = 'NEW'} = 1;
$sectioned = 1;
}
#
# Handle Wildcards
#
if ( $source =~ /^all[-+]/ ) {
if ( $source eq 'all+' ) {
$source = 'all';
$intrazone = 1;
} elsif ( ( $source eq 'all+-' ) || ( $source eq 'all-+' ) ) {
$source = 'all';
$intrazone = 1;
$includesrcfw = 0;
} elsif ( $source eq 'all-' ) {
$source = 'all';
$includesrcfw = 0;
} else {
fatal_error "Invalid SOURCE ($source)";
}
}
if ( $dest =~ /^all[-+]/ ) {
if ( $dest eq 'all+' ) {
$dest = 'all';
$intrazone = 1;
} elsif ( ( $dest eq 'all+-' ) || ( $dest eq 'all-+' ) ) {
$dest = 'all';
$intrazone = 1;
$includedstfw = 0;
} elsif ( $dest eq 'all-' ) {
$dest = 'all';
$includedstfw = 0;
} else {
fatal_error "Invalid DEST ($dest)";
}
}
my $action = isolate_basic_target $target;
fatal_error "Invalid or missing ACTION ($target)" unless defined $action;
if ( $source eq 'all' ) {
for my $zone ( all_zones ) {
if ( $includesrcfw || ( zone_type( $zone ) ne 'firewall' ) ) {
if ( $dest eq 'all' ) {
for my $zone1 ( all_zones ) {
if ( $includedstfw || ( zone_type( $zone1 ) ne 'firewall' ) ) {
if ( $intrazone || ( $zone ne $zone1 ) ) {
process_rule1 $target, $zone, $zone1 , $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark, $connlimit, $time, 1;
}
}
}
} else {
my $destzone = (split( /:/, $dest, 2 ) )[0];
$destzone = firewall_zone unless defined_zone( $destzone ); # We do this to allow 'REDIRECT all ...'; process_rule1 will catch the case where the dest zone is invalid
if ( $intrazone || ( $zone ne $destzone ) ) {
process_rule1 $target, $zone, $dest , $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark, $connlimit, $time, 1;
}
}
}
}
} elsif ( $dest eq 'all' ) {
for my $zone ( all_zones ) {
my $sourcezone = ( split( /:/, $source, 2 ) )[0];
if ( ( $includedstfw || ( zone_type( $zone ) ne 'firewall') ) && ( ( $sourcezone ne $zone ) || $intrazone) ) {
process_rule1 $target, $source, $zone , $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark, $connlimit, $time, 1;
}
}
} else {
process_rule1 $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark, $connlimit, $time, 0;
}
progress_message " Rule \"$thisline\" $done";
}
#
# Process the Rules File
#
sub process_rules() {
my $fn = open_file 'rules';
first_entry "$doing $fn...";
while ( read_a_line ) {
my ( $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark, $connlimit, $time ) = split_line1 1, 12, 'rules file', \%rules_commands;
if ( $target eq 'COMMENT' ) {
process_comment;
} elsif ( $target eq 'SECTION' ) {
#
# read_a_line has already verified that there are exactly two tokens on the line
#
fatal_error "Invalid SECTION ($source)" unless defined $sections{$source};
fatal_error "Duplicate or out of order SECTION $source" if $sections{$source};
$sectioned = 1;
$sections{$source} = 1;
if ( $source eq 'RELATED' ) {
$sections{ESTABLISHED} = 1;
finish_section 'ESTABLISHED';
} elsif ( $source eq 'NEW' ) {
@sections{'ESTABLISHED','RELATED'} = ( 1, 1 );
finish_section ( ( $section eq 'RELATED' ) ? 'RELATED' : 'ESTABLISHED,RELATED' );
}
$section = $source;
} else {
if ( "\L$source" =~ /^none(:.*)?$/ || "\L$dest" =~ /^none(:.*)?$/ ) {
progress_message "Rule \"$currentline\" ignored."
} else {
process_rule $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark, $connlimit, $time;
}
}
}
clear_comment;
$section = 'DONE';
}
# #
# To quote an old comment, "generate_matrix makes a sow's ear out of a silk purse". # To quote an old comment, "generate_matrix makes a sow's ear out of a silk purse".
# #

View File

@ -44,28 +44,44 @@ our @EXPORT = qw( NOTHING
firewall_zone firewall_zone
defined_zone defined_zone
zone_type zone_type
zone_family
all_zones all_zones
all_6zones
complex_zones complex_zones
non_firewall_zones non_firewall_zones
non_firewall_6zones
single_interface single_interface
validate_interfaces_file validate_interfaces_file
validate_6interfaces_file
all_interfaces all_interfaces
all_6interfaces
interface_number interface_number
find_interface find_interface
find_6interface
known_interface known_interface
known_6interface
have_bridges have_bridges
have_6bridges
port_to_bridge port_to_bridge
port_to_6bridge
source_port_to_bridge source_port_to_bridge
source_port_to_6bridge
interface_is_optional interface_is_optional
interface6_is_optional
find_interfaces_by_option find_interfaces_by_option
find_6interfaces_by_option
get_interface_option get_interface_option
get_6interface_option
set_interface_option set_interface_option
set_6interface_option
validate_hosts_file validate_hosts_file
validate_6hosts_file
find_hosts_by_option find_hosts_by_option
find_6hosts_by_option
); );
our @EXPORT_OK = qw( initialize ); our @EXPORT_OK = qw( initialize );
our $VERSION = 4.1.5; our $VERSION = 4.3.0;
# #
# IPSEC Option types # IPSEC Option types
@ -82,7 +98,7 @@ use constant { NOTHING => 'NOTHING',
# #
# @zones contains the ordered list of zones with sub-zones appearing before their parents. # @zones contains the ordered list of zones with sub-zones appearing before their parents.
# #
# %zones{<zone1> => {type = > <zone type> 'firewall', 'ipv4', 'ipsec4', 'bport4'; # %zones{<zone1> => {type = > <zone type> 'firewall', 'ipv4', 'ipsec4', 'bport4', 'ipv6', 'ipsec6', 'bport6';
# options => { complex => 0|1 # options => { complex => 0|1
# nested => 0|1 # nested => 0|1
# in_out => < policy match string > # in_out => < policy match string >
@ -93,6 +109,7 @@ use constant { NOTHING => 'NOTHING',
# children => [ <children> ] # children => [ <children> ]
# interfaces => [ <interfaces> ] # interfaces => [ <interfaces> ]
# bridge => <bridge> # bridge => <bridge>
# family => 1 = IPv4, 2 = IPv6, 3 = firewall
# hosts { <type> } => [ { <interface1> => { ipsec => 'ipsec'|'none' # hosts { <type> } => [ { <interface1> => { ipsec => 'ipsec'|'none'
# options => { <option1> => <value1> # options => { <option1> => <value1>
# ... # ...
@ -120,14 +137,15 @@ our %reservedName = ( all => 1,
# #
# Interface Table. # Interface Table.
# #
# @interfaces lists the interface names in the order that they appear in the interfaces file. # @interfaces lists the interface names in the order that they appear in the interfaces file.
# @interfaces6 lists the interface names in the order that they appear in the interfaces6 file.
# #
# %interfaces { <interface1> => { name => <name of interface> # %interfaces { <interface1> => { name => <name of interface>
# root => <name without trailing '+'> # root => <name without trailing '+'>
# options => { <option1> = <val1> , # options => { <option1> = <val1> ,
# ... # ...
# } # }
# zone4 => <zone name> # zone => <zone name>
# nets => <number of nets in interface/hosts records referring to this interface> # nets => <number of nets in interface/hosts records referring to this interface>
# bridge => <bridge> # bridge => <bridge>
# broadcasts => 'none', 'detect' or [ <addr1>, <addr2>, ... ] # broadcasts => 'none', 'detect' or [ <addr1>, <addr2>, ... ]
@ -135,9 +153,25 @@ our %reservedName = ( all => 1,
# } # }
# } # }
# #
# %interfaces6 { <interface1> => { name => <name of interface>
# root => <name without trailing '+'>
# options => { <option1> = <val1> ,
# ...
# }
# zone => <zone name>
# nets => <number of nets in interface/hosts records referring to this interface>
# bridge => <bridge>
# broadcasts => 'none', 'detect' or [ <addr1>, <addr2>, ... ]
# number => <ordinal position in the interfaces file>
# }
# }
#
our @interfaces; our @interfaces;
our %interfaces; our %interfaces;
our @interfaces6;
our %interfaces6;
our @bport_zones; our @bport_zones;
our @bport_6zones;
# #
# Initialize globals -- we take this novel approach to globals initialization to allow # Initialize globals -- we take this novel approach to globals initialization to allow
@ -156,6 +190,9 @@ sub initialize() {
@interfaces = (); @interfaces = ();
%interfaces = (); %interfaces = ();
@bport_zones = (); @bport_zones = ();
@interfaces6 = ();
%interfaces6 = ();
@bport_6zones = ();
} }
INIT { INIT {
@ -219,7 +256,7 @@ sub parse_zone_option_list($$)
if ( $key{$e} ) { if ( $key{$e} ) {
$h{$e} = $val; $h{$e} = $val;
} else { } else {
fatal_error "The \"$e\" option may only be specified for ipsec zones" unless $zonetype eq 'ipsec4'; fatal_error "The \"$e\" option may only be specified for ipsec zones" unless $zonetype =~ /^ipsec/;
$options .= $invert; $options .= $invert;
$options .= "--$e "; $options .= "--$e ";
$options .= "$val "if defined $val; $options .= "$val "if defined $val;
@ -240,6 +277,7 @@ sub determine_zones()
my @z; my @z;
my $ipv4 = 0; my $ipv4 = 0;
my $ipv6 = 0;
my $fn = open_file 'zones'; my $fn = open_file 'zones';
@ -262,28 +300,43 @@ sub determine_zones()
push @{$zones{$p}{children}}, $zone; push @{$zones{$p}{children}}, $zone;
} }
} }
fatal_error "Invalid zone name ($zone)" unless "\L$zone" =~ /^[a-z]\w*$/ && length $zone <= $globals{MAXZONENAMELENGTH}; fatal_error "Invalid zone name ($zone)" unless "\L$zone" =~ /^[a-z]\w*$/ && length $zone <= $globals{MAXZONENAMELENGTH};
fatal_error "Invalid zone name ($zone)" if $reservedName{$zone} || $zone =~ /^all2|2all$/; fatal_error "Invalid zone name ($zone)" if $reservedName{$zone} || $zone =~ /^all2|2all$/;
fatal_error( "Duplicate zone name ($zone)" ) if $zones{$zone}; fatal_error( "Duplicate zone name ($zone)" ) if $zones{$zone};
$type = "ipv4" unless $type; $type = "ipv4" unless $type;
my $family = F_INET;
if ( $type =~ /ipv4/i ) { if ( $type =~ /ipv4/i ) {
$type = 'ipv4'; $type = 'ipv4';
$ipv4 = 1; $ipv4 = 1;
} elsif ( $type =~ /ipv6/i ) {
$type = 'ipv6';
$ipv6 = 1;
$family = F_INET6;
} elsif ( $type =~ /^ipsec4?$/i ) { } elsif ( $type =~ /^ipsec4?$/i ) {
$type = 'ipsec4'; $type = 'ipsec4';
} elsif ( $type =~ /^ipsec6?$/i ) {
$type = 'ipsec6';
$family = F_INET6;
} elsif ( $type =~ /^bport4?$/i ) { } elsif ( $type =~ /^bport4?$/i ) {
warning_message "Bridge Port zones should have a parent zone" unless @parents; warning_message "Bridge Port zones should have a parent zone" unless @parents;
$type = 'bport4'; $type = 'bport4';
push @bport_zones, $zone; push @bport_zones, $zone;
} elsif ( $type =~ /^bport6?$/i ) {
warning_message "Bridge Port zones should have a parent zone" unless @parents;
$type = 'bport6';
$family = F_INET6;
push @bport_6zones, $zone;
} elsif ( $type eq 'firewall' ) { } elsif ( $type eq 'firewall' ) {
fatal_error 'Firewall zone may not be nested' if @parents; fatal_error 'Firewall zone may not be nested' if @parents;
fatal_error "Only one firewall zone may be defined ($zone)" if $firewall_zone; fatal_error "Only one firewall zone may be defined ($zone)" if $firewall_zone;
$firewall_zone = $zone; $firewall_zone = $zone;
$ENV{FW} = $zone; $ENV{FW} = $zone;
$type = "firewall"; $type = "firewall";
$family = F_INET | F_INET6;
} elsif ( $type eq '-' ) { } elsif ( $type eq '-' ) {
$type = 'ipv4'; $type = 'ipv4';
$ipv4 = 1; $ipv4 = 1;
@ -291,6 +344,10 @@ sub determine_zones()
fatal_error "Invalid zone type ($type)" ; fatal_error "Invalid zone type ($type)" ;
} }
for ( @parents ) {
fatal_error "Incompatible Parent/Child Zones Types ($_)" unless $zones{$_}{family} == $family
}
for ( $options, $in_options, $out_options ) { for ( $options, $in_options, $out_options ) {
$_ = '' if $_ eq '-'; $_ = '' if $_ eq '-';
} }
@ -299,10 +356,11 @@ sub determine_zones()
parents => \@parents, parents => \@parents,
exclusions => [], exclusions => [],
bridge => '', bridge => '',
family => $family,
options => { in_out => parse_zone_option_list( $options || '', $type ) , options => { in_out => parse_zone_option_list( $options || '', $type ) ,
in => parse_zone_option_list( $in_options || '', $type ) , in => parse_zone_option_list( $in_options || '', $type ) ,
out => parse_zone_option_list( $out_options || '', $type ) , out => parse_zone_option_list( $out_options || '', $type ) ,
complex => ($type eq 'ipsec4' || $options || $in_options || $out_options ? 1 : 0) , complex => ($type =~ /^ipsec/ || $options || $in_options || $out_options ? 1 : 0) ,
nested => @parents > 0 } , nested => @parents > 0 } ,
interfaces => {} , interfaces => {} ,
children => [] , children => [] ,
@ -311,8 +369,8 @@ sub determine_zones()
push @z, $zone; push @z, $zone;
} }
fatal_error "No firewall zone defined" unless $firewall_zone; fatal_error "No firewall zone defined" unless $firewall_zone;
fatal_error "No IPv4 zones defined" unless $ipv4; fatal_error "No IPv4 or IPv6 zones defined" unless $ipv4 || $ipv6;
my %ordered; my %ordered;
@ -336,7 +394,7 @@ sub determine_zones()
} }
# #
# Return true of we have any ipsec zones # Return true of we have any ipse4c zones
# #
sub haveipseczones() { sub haveipseczones() {
for my $zoneref ( values %zones ) { for my $zoneref ( values %zones ) {
@ -346,6 +404,17 @@ sub haveipseczones() {
0; 0;
} }
#
# Return true of we have any ipse4c zones
#
sub haveipsec6zones() {
for my $zoneref ( values %zones ) {
return 1 if $zoneref->{type} eq 'ipsec6';
}
0;
}
# #
# Report about zones. # Report about zones.
# #
@ -374,7 +443,7 @@ sub zone_report()
my $hosts = $groupref->{hosts}; my $hosts = $groupref->{hosts};
if ( $hosts ) { if ( $hosts ) {
my $grouplist = join ',', ( @$hosts ); my $grouplist = join ',', ( @$hosts );
progress_message " $interface:$grouplist"; progress_message " $interface $grouplist";
$printed = 1; $printed = 1;
} }
} }
@ -384,7 +453,7 @@ sub zone_report()
} }
unless ( $printed ) { unless ( $printed ) {
fatal_error "No bridge has been associated with zone $zone" if $type eq 'bport4' && ! $zoneref->{bridge}; fatal_error "No bridge has been associated with zone $zone" if $type =~ /^bport/ && ! $zoneref->{bridge};
warning_message "*** $zone is an EMPTY ZONE ***" unless $type eq 'firewall'; warning_message "*** $zone is an EMPTY ZONE ***" unless $type eq 'firewall';
} }
@ -402,7 +471,7 @@ sub dump_zone_contents()
my $exclusions = $zoneref->{exclusions}; my $exclusions = $zoneref->{exclusions};
my $entry = "$zone $type"; my $entry = "$zone $type";
$entry .= ":$zoneref->{bridge}" if $type eq 'bport4'; $entry .= ":$zoneref->{bridge}" if $type =~ /^bport/;
if ( $hostref ) { if ( $hostref ) {
for my $type ( sort keys %$hostref ) { for my $type ( sort keys %$hostref ) {
@ -414,7 +483,7 @@ sub dump_zone_contents()
my $hosts = $groupref->{hosts}; my $hosts = $groupref->{hosts};
if ( $hosts ) { if ( $hosts ) {
my $grouplist = join ',', ( @$hosts ); my $grouplist = join ',', ( @$hosts );
$entry .= " $interface:$grouplist"; $entry .= " $interface\($grouplist\)";
} }
} }
} }
@ -455,7 +524,7 @@ sub add_group_to_zone($$$$$)
my $arrayref; my $arrayref;
my $zoneref = $zones{$zone}; my $zoneref = $zones{$zone};
my $zonetype = $zoneref->{type}; my $zonetype = $zoneref->{type};
my $ifacezone = $interfaces{$interface}{zone4}; my $ifacezone = $interfaces{$interface}{zone};
$zoneref->{interfaces}{$interface} = 1; $zoneref->{interfaces}{$interface} = 1;
@ -481,7 +550,7 @@ sub add_group_to_zone($$$$$)
unless ( $switched ) { unless ( $switched ) {
if ( $type eq $zonetype ) { if ( $type eq $zonetype ) {
fatal_error "Duplicate Host Group ($interface:$host) in zone $zone" if $ifacezone eq $zone; fatal_error "Duplicate Host Group ($interface:$host) in zone $zone" if $ifacezone eq $zone;
$ifacezone = $zone if $host eq ALLIPv4; $ifacezone = $zone if $host eq ALLIPv4 || $host eq ALLIPv6;
} }
} }
@ -506,7 +575,7 @@ sub add_group_to_zone($$$$$)
push @{$arrayref}, { options => $options, push @{$arrayref}, { options => $options,
hosts => \@newnetworks, hosts => \@newnetworks,
ipsec => $type eq 'ipsec4' ? 'ipsec' : 'none' }; ipsec => $type =~ /^ipsec/ ? 'ipsec' : 'none' };
} }
# #
@ -527,20 +596,36 @@ sub zone_type( $ ) {
find_zone( $_[0] )->{type}; find_zone( $_[0] )->{type};
} }
sub zone_family( $ ) {
find_zone( $_[0] )->{family};
}
sub defined_zone( $ ) { sub defined_zone( $ ) {
$zones{$_[0]}; $zones{$_[0]};
} }
sub all_zones() { sub all_zones() {
@zones; grep ( ! $zones{$_}{family} & F_INET , @zones );
}
sub all_6zones() {
grep ( ! $zones{$_}{family} & F_INET6 , @zones );
} }
sub non_firewall_zones() { sub non_firewall_zones() {
grep ( $zones{$_}{type} ne 'firewall' , @zones ); grep ( $zones{$_}{family} == F_INET , @zones );
}
sub non_firewall_6zones() {
grep ( $zones{$_}{family} == F_INET6 , @zones );
} }
sub complex_zones() { sub complex_zones() {
grep( $zones{$_}{options}{complex} , @zones ); grep( $zones{$_}{options}{complex} && $zones{$_}{family} == F_INET , @zones );
}
sub complex_6zones() {
grep( $zones{$_}{options}{complex} && $zones{$_}{family} == F_INET6 , @zones );
} }
sub firewall_zone() { sub firewall_zone() {
@ -551,20 +636,19 @@ sub firewall_zone() {
# Parse the interfaces file. # Parse the interfaces file.
# #
use constant { SIMPLE_IF_OPTION => 1,
BINARY_IF_OPTION => 2,
ENUM_IF_OPTION => 3,
NUMERIC_IF_OPTION => 4,
OBSOLETE_IF_OPTION => 5,
MASK_IF_OPTION => 7,
IF_OPTION_ZONEONLY => 8 };
sub validate_interfaces_file( $ ) sub validate_interfaces_file( $ )
{ {
my $export = shift; my $export = shift;
my $num = 0; my $num = 0;
use constant { SIMPLE_IF_OPTION => 1,
BINARY_IF_OPTION => 2,
ENUM_IF_OPTION => 3,
NUMERIC_IF_OPTION => 4,
OBSOLETE_IF_OPTION => 5,
MASK_IF_OPTION => 7,
IF_OPTION_ZONEONLY => 8 };
my %validoptions = (arp_filter => BINARY_IF_OPTION, my %validoptions = (arp_filter => BINARY_IF_OPTION,
arp_ignore => ENUM_IF_OPTION, arp_ignore => ENUM_IF_OPTION,
blacklist => SIMPLE_IF_OPTION, blacklist => SIMPLE_IF_OPTION,
@ -609,6 +693,7 @@ sub validate_interfaces_file( $ )
fatal_error "Unknown zone ($zone)" unless $zoneref; fatal_error "Unknown zone ($zone)" unless $zoneref;
fatal_error "Firewall zone not allowed in ZONE column of interface record" if $zoneref->{type} eq 'firewall'; fatal_error "Firewall zone not allowed in ZONE column of interface record" if $zoneref->{type} eq 'firewall';
fatal_error "IPv6 Zones not allowed in the interfaces file ($zone}" if $zoneref->{type} =~ /6/;
} }
$networks = '' if $networks eq '-'; $networks = '' if $networks eq '-';
@ -752,7 +837,7 @@ sub validate_interfaces_file( $ )
add_group_to_zone( $zone, $zoneref->{type}, $interface, \@networks, $optionsref ) if $zone; add_group_to_zone( $zone, $zoneref->{type}, $interface, \@networks, $optionsref ) if $zone;
$interfaces{$interface}{zone4} = $zone; #Must follow the call to add_group_to_zone() $interfaces{$interface}{zone} = $zone; #Must follow the call to add_group_to_zone()
progress_message " Interface \"$currentline\" Validated"; progress_message " Interface \"$currentline\" Validated";
@ -782,6 +867,225 @@ sub validate_interfaces_file( $ )
fatal_error "No network interfaces defined" unless @interfaces; fatal_error "No network interfaces defined" unless @interfaces;
} }
#
# Parse the interfaces file.
#
sub validate_6interfaces_file( $ )
{
my $export = shift;
my $num = 0;
my %validoptions = (blacklist => SIMPLE_IF_OPTION,
bridge => SIMPLE_IF_OPTION,
maclist => SIMPLE_IF_OPTION,
nosmurfs => SIMPLE_IF_OPTION,
optional => SIMPLE_IF_OPTION,
proxyndp => BINARY_IF_OPTION,
routeback => SIMPLE_IF_OPTION + IF_OPTION_ZONEONLY,
sourceroute => BINARY_IF_OPTION,
tcpflags => SIMPLE_IF_OPTION,
mss => NUMERIC_IF_OPTION,
);
my $fn = open_file '6interfaces';
my $first_entry = 1;
my @ifaces;
while ( read_a_line ) {
if ( $first_entry ) {
progress_message2 "$doing $fn...";
$first_entry = 0;
}
my ($zone, $originalinterface, $networks, $options ) = split_line 2, 4, '6interfaces file';
my $zoneref;
my $bridge = '';
if ( $zone eq '-' ) {
$zone = '';
} else {
$zoneref = $zones{$zone};
fatal_error "Unknown zone ($zone)" unless $zoneref;
fatal_error "Firewall zone not allowed in ZONE column of interface record" if $zoneref->{type} eq 'firewall';
fatal_error "IPv4 Zones not allowed in the 6interfaces file ($zone}" if $zoneref->{type} =~ /4/;
}
$networks = '' if $networks eq '-';
$options = '' if $options eq '-';
my ($interface, $port, $extra) = split /:/ , $originalinterface, 3;
fatal_error "Invalid INTERFACE ($originalinterface)" if ! $interface || defined $extra;
fatal_error "Invalid Interface Name (+)" if $interface eq '+';
if ( defined $port ) {
fatal_error qq("Virtual" interfaces are not supported -- see http://www.shorewall.net/Shorewall_and_Aliased_Interfaces.html) if $port =~ /^\d+$/;
require_capability( 'PHYSDEV_MATCH', 'Bridge Ports', '');
fatal_error "Your iptables is not recent enough to support bridge ports" unless $capabilities{KLUDGEFREE};
fatal_error "Duplicate Interface ($port)" if $interfaces{$port};
fatal_error "$interface is not a defined bridge" unless $interfaces{$interface} && $interfaces{$interface}{options}{bridge};
fatal_error "Bridge Ports may only be associated with 'bport' zones" if $zone && $zoneref->{type} ne 'bport4';
if ( $zone ) {
if ( $zoneref->{bridge} ) {
fatal_error "Bridge Port zones may only be associated with a single bridge" if $zoneref->{bridge} ne $interface;
} else {
$zoneref->{bridge} = $interface;
}
}
fatal_error "Bridge Ports may not have options" if $options && $options ne '-';
next if $port eq '';
fatal_error "Invalid Interface Name ($interface:$port)" unless $port =~ /^[\w.@%-]+\+?$/;
$bridge = $interface;
$interface = $port;
} else {
fatal_error "Duplicate Interface ($interface)" if $interfaces{$interface};
fatal_error "Zones of type 'bport' may only be associated with bridge ports" if $zone && $zoneref->{type} eq 'bport4';
$bridge = $interface;
}
my $wildcard = 0;
my $root;
if ( $interface =~ /\+$/ ) {
$wildcard = 1;
$root = substr( $interface, 0, -1 );
} else {
$root = $interface;
}
my $broadcasts;
unless ( $networks eq '' || $networks eq 'detect' ) {
my @broadcasts = split $networks, 'address';
for my $address ( @broadcasts ) {
fatal_error 'Invalid BROADCAST address' unless $address =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
}
if ( $capabilities{ADDRTYPE} ) {
warning_message 'Shorewall no longer uses broadcast addresses in rule generation when Address Type Match is available';
} else {
$broadcasts = \@broadcasts;
}
}
my $optionsref = {};
my %options;
if ( $options ) {
for my $option (split_list $options, 'option' ) {
next if $option eq '-';
( $option, my $value ) = split /=/, $option;
fatal_error "Invalid Interface option ($option)" unless my $type = $validoptions{$option};
fatal_error "The \"$option\" option may not be specified on a multi-zone interface" if $type & IF_OPTION_ZONEONLY && ! $zone;
$type &= MASK_IF_OPTION;
if ( $type == SIMPLE_IF_OPTION ) {
fatal_error "Option $option does not take a value" if defined $value;
$options{$option} = 1;
} elsif ( $type == BINARY_IF_OPTION ) {
$value = 1 unless defined $value;
fatal_error "Option value for $option must be 0 or 1" unless ( $value eq '0' || $value eq '1' );
fatal_error "The $option option may not be used with a wild-card interface name" if $wildcard;
$options{$option} = $value;
} elsif ( $type == ENUM_IF_OPTION ) {
fatal_error "The $option option may not be used with a wild-card interface name" if $wildcard;
if ( $option eq 'arp_ignore' ) {
if ( defined $value ) {
if ( $value =~ /^[1-3,8]$/ ) {
$options{arp_ignore} = $value;
} else {
fatal_error "Invalid value ($value) for arp_ignore";
}
} else {
$options{arp_ignore} = 1;
}
} else {
fatal_error "Internal Error in validate_interfaces_file";
}
} elsif ( $type == NUMERIC_IF_OPTION ) {
fatal_error "The $option option requires a value" unless defined $value;
my $numval = numeric_value $value;
fatal_error "Invalid value ($value) for option $option" unless defined $numval;
$options{$option} = $numval;
} else {
warning_message "Support for the $option interface option has been removed from Shorewall-perl";
}
}
$zoneref->{options}{in_out}{routeback} = 1 if $zoneref && $options{routeback};
if ( $options{bridge} ) {
require_capability( 'PHYSDEV_MATCH', 'The "bridge" option', 's');
fatal_error "Bridges may not have wildcard names" if $wildcard;
}
} elsif ( $port ) {
$options{port} = 1;
}
$optionsref = \%options;
$interfaces6{$interface} = { name => $interface ,
bridge => $bridge ,
nets => 0 ,
number => ++$num ,
root => $root ,
broadcasts => $broadcasts ,
options => $optionsref };
push @ifaces, $interface;
my @networks = allipv4;
add_group_to_zone( $zone, $zoneref->{type}, $interface, \@networks, $optionsref ) if $zone;
$interfaces6{$interface}{zone6} = $zone; #Must follow the call to add_group_to_zone()
progress_message " Interface \"$currentline\" Validated";
}
#
# We now assemble the @interfaces6 array such that bridge ports immediately precede their associated bridge
#
for my $interface ( @ifaces ) {
my $interfaceref = $interfaces6{$interface};
if ( $interfaceref->{options}{bridge} ) {
my @ports = grep $interfaces{$_}{options}{port} && $interfaces{$_}{bridge} eq $interface, @ifaces;
if ( @ports ) {
push @interfaces, @ports;
} else {
$interfaceref->{options}{routeback} = 1; #so the bridge will work properly
}
}
push @interfaces, $interface unless $interfaceref->{options}{port};
}
#
# Be sure that we have at least one interface
#
fatal_error "No network interfaces defined" unless @interfaces;
}
# #
# Returns true if passed interface matches an entry in /etc/shorewall/interfaces # Returns true if passed interface matches an entry in /etc/shorewall/interfaces
# #
@ -809,6 +1113,33 @@ sub known_interface($)
0; 0;
} }
#
# Returns true if passed interface matches an entry in /etc/shorewall/interfaces
#
# If the passed name matches a wildcard, a entry for the name is added in %interfaces to speed up validation of other references to that name.
#
sub known_6interface($)
{
my $interface = $_[0];
my $interfaceref = $interfaces6{$interface};
return $interfaceref if $interfaceref;
for my $i ( @interfaces6 ) {
$interfaceref = $interfaces6{$i};
my $val = $interfaceref->{root};
next if $val eq $i;
if ( substr( $interface, 0, length $val ) eq $val ) {
#
# Cache this result for future reference. We set the 'name' to the name of the entry that appears in /etc/shorewall/interfaces.
#
return $interfaces6{$interface} = { options => $interfaceref->{options}, bridge => $interfaceref->{bridge} , name => $i , number => $interfaceref->{number} };
}
}
0;
}
# #
# Return interface number # Return interface number
# #
@ -823,12 +1154,38 @@ sub all_interfaces() {
@interfaces; @interfaces;
} }
#
# Return 6interface number
#
sub interface6_number( $ ) {
$interfaces6{$_[0]}{number} || 256;
}
#
# Return the 6interfaces list
#
sub all_interfaces6() {
@interfaces6;
}
# #
# Return a reference to the interfaces table entry for an interface # Return a reference to the interfaces table entry for an interface
# #
sub find_interface( $ ) { sub find_interface( $ ) {
my $interface = $_[0]; my $interface = $_[0];
my $interfaceref = $interfaces{ $interface }; my $interfaceref = $interfaces6{ $interface };
fatal_error "Unknown Interface ($interface)" unless $interfaceref;
$interfaceref;
}
#
# Return a reference to the interfaces6 table entry for an interface
#
sub find_interface6( $ ) {
my $interface = $_[0];
my $interfaceref = $interfaces6{ $interface };
fatal_error "Unknown Interface ($interface)" unless $interfaceref; fatal_error "Unknown Interface ($interface)" unless $interfaceref;
@ -842,6 +1199,13 @@ sub have_bridges() {
@bport_zones > 0; @bport_zones > 0;
} }
#
# Returns true if there are bridge port zones defined in the config
#
sub have_6bridges() {
@bport_6zones > 0;
}
# #
# Return the bridge associated with the passed interface. If the interface is not a bridge port, # Return the bridge associated with the passed interface. If the interface is not a bridge port,
# return '' # return ''
@ -851,6 +1215,15 @@ sub port_to_bridge( $ ) {
return $portref && $portref->{options}{port} ? $portref->{bridge} : ''; return $portref && $portref->{options}{port} ? $portref->{bridge} : '';
} }
#
# Return the bridge associated with the passed interface. If the interface is not a bridge port,
# return ''
#
sub port_to_6bridge( $ ) {
my $portref = $interfaces6{$_[0]};
return $portref && $portref->{options}{port} ? $portref->{bridge} : '';
}
# #
# Return the bridge associated with the passed interface. # Return the bridge associated with the passed interface.
# #
@ -859,6 +1232,14 @@ sub source_port_to_bridge( $ ) {
return $portref ? $portref->{bridge} : ''; return $portref ? $portref->{bridge} : '';
} }
#
# Return the bridge associated with the passed 6interface.
#
sub source_port_to_6bridge( $ ) {
my $portref = $interfaces6{$_[0]};
return $portref ? $portref->{bridge} : '';
}
# #
# Return the 'optional' setting of the passed interface # Return the 'optional' setting of the passed interface
# #
@ -867,6 +1248,14 @@ sub interface_is_optional($) {
$optionsref && $optionsref->{optional}; $optionsref && $optionsref->{optional};
} }
#
# Return the 'optional' setting of the passed interface
#
sub interface6_is_optional($) {
my $optionsref = $interfaces6{$_[0]}{options};
$optionsref && $optionsref->{optional};
}
# #
# Returns reference to array of interfaces with the passed option # Returns reference to array of interfaces with the passed option
# #
@ -884,6 +1273,23 @@ sub find_interfaces_by_option( $ ) {
\@ints; \@ints;
} }
#
# Returns reference to array of interfaces6 with the passed option
#
sub find_interfaces6_by_option( $ ) {
my $option = $_[0];
my @ints = ();
for my $interface ( @interfaces ) {
my $optionsref = $interfaces{$interface}{options};
if ( $optionsref && defined $optionsref->{$option} ) {
push @ints , $interface
}
}
\@ints;
}
# #
# Return the value of an option for an interface # Return the value of an option for an interface
# #
@ -902,6 +1308,24 @@ sub set_interface_option( $$$ ) {
$interfaces{$interface}{options}{$option} = $value; $interfaces{$interface}{options}{$option} = $value;
} }
#
# Return the value of an option for an interface6
#
sub get_interface6_option( $$ ) {
my ( $interface, $option ) = @_;
$interfaces6{$interface}{options}{$option};
}
#
# Set an option for an interface6
#
sub set_interface6_option( $$$ ) {
my ( $interface, $option, $value ) = @_;
$interfaces6{$interface}{options}{$option} = $value;
}
# #
# Validates the hosts file. Generates entries in %zone{..}{hosts} # Validates the hosts file. Generates entries in %zone{..}{hosts}
# #
@ -910,10 +1334,8 @@ sub validate_hosts_file()
my %validoptions = ( my %validoptions = (
blacklist => 1, blacklist => 1,
maclist => 1, maclist => 1,
norfc1918 => 1,
nosmurfs => 1, nosmurfs => 1,
routeback => 1, routeback => 1,
routefilter => 1,
tcpflags => 1, tcpflags => 1,
broadcast => 1, broadcast => 1,
destonly => 1, destonly => 1,
@ -939,6 +1361,7 @@ sub validate_hosts_file()
fatal_error "Unknown ZONE ($zone)" unless $type; fatal_error "Unknown ZONE ($zone)" unless $type;
fatal_error 'Firewall zone not allowed in ZONE column of hosts record' if $type eq 'firewall'; fatal_error 'Firewall zone not allowed in ZONE column of hosts record' if $type eq 'firewall';
fatal_error 'IPv6 zones not allowed in ZONE column of hosts record' if $type =~ /6/;
my $interface; my $interface;
@ -951,7 +1374,7 @@ sub validate_hosts_file()
fatal_error "Invalid HOST(S) column contents: $hosts"; fatal_error "Invalid HOST(S) column contents: $hosts";
} }
if ( $type eq 'bport4' ) { if ( $type eq 'bport6' ) {
if ( $zoneref->{bridge} eq '' ) { if ( $zoneref->{bridge} eq '' ) {
fatal_error 'Bridge Port Zones may only be associated with bridge ports' unless $interfaces{$interface}{options}{port}; fatal_error 'Bridge Port Zones may only be associated with bridge ports' unless $interfaces{$interface}{options}{port};
$zoneref->{bridge} = $interfaces{$interface}{bridge}; $zoneref->{bridge} = $interfaces{$interface}{bridge};
@ -969,7 +1392,7 @@ sub validate_hosts_file()
for my $option ( @options ) for my $option ( @options )
{ {
if ( $option eq 'ipsec' ) { if ( $option eq 'ipsec' ) {
$type = 'ipsec4'; $type = 'ipsec6';
$zoneref->{options}{complex} = 1; $zoneref->{options}{complex} = 1;
$ipsec = 1; $ipsec = 1;
} elsif ( $validoptions{$option}) { } elsif ( $validoptions{$option}) {
@ -995,7 +1418,7 @@ sub validate_hosts_file()
# #
# Take care of case where the hosts list begins with '!' # Take care of case where the hosts list begins with '!'
# #
$hosts = join( '', ALLIPv4 , $hosts ) if substr($hosts, 0, 2 ) eq ',!'; $hosts = join( '', ALLIPv6 , $hosts ) if substr($hosts, 0, 2 ) eq ',!';
add_group_to_zone( $zone, $type , $interface, [ split_list( $hosts, 'host' ) ] , $optionsref); add_group_to_zone( $zone, $type , $interface, [ split_list( $hosts, 'host' ) ] , $optionsref);
@ -1005,6 +1428,108 @@ sub validate_hosts_file()
$capabilities{POLICY_MATCH} = '' unless $ipsec || haveipseczones; $capabilities{POLICY_MATCH} = '' unless $ipsec || haveipseczones;
} }
#
# Validates the 6hosts file. Generates entries in %zone{..}{hosts}
#
sub validate_6hosts_file()
{
my %validoptions = (
blacklist => 1,
maclist => 1,
nosmurfs => 1,
routeback => 1,
tcpflags => 1,
broadcast => 1,
destonly => 1,
sourceonly => 1,
);
my $ipsec = 0;
my $first_entry = 1;
my $fn = open_file '6hosts';
while ( read_a_line ) {
if ( $first_entry ) {
progress_message2 "$doing $fn...";
$first_entry = 0;
}
my ($zone, $hosts, $options ) = split_line 2, 3, '6hosts file';
my $zoneref = $zones{$zone};
my $type = $zoneref->{type};
fatal_error "Unknown ZONE ($zone)" unless $type;
fatal_error 'Firewall zone not allowed in ZONE column of hosts record' if $type eq 'firewall';
fatal_error 'IPv4 zonea not allowed in ZONE column of 6hosts record' if $type =~ /4/;
my $interface;
if ( $hosts =~ /^([\w.@%-]+\+?)\[(.*)\]$/ ) {
$interface = $1;
$hosts = $2;
$zoneref->{options}{complex} = 1 if $hosts =~ /^\+/;
fatal_error "Unknown 6interface ($interface)" unless $interfaces6{$interface}{root};
} else {
fatal_error "Invalid HOST(S) column contents: $hosts";
}
if ( $type eq 'bport6' ) {
if ( $zoneref->{bridge} eq '' ) {
fatal_error 'Bridge Port Zones may only be associated with bridge ports' unless $interfaces6{$interface}{options}{port};
$zoneref->{bridge} = $interfaces6{$interface}{bridge};
} elsif ( $zoneref->{bridge} ne $interfaces6{$interface}{bridge} ) {
fatal_error "Interface $interface is not a port on bridge $zoneref->{bridge}";
}
}
my $optionsref = {};
if ( $options ne '-' ) {
my @options = split_list $options, 'option';
my %options;
for my $option ( @options )
{
if ( $option eq 'ipsec' ) {
$type = 'ipsec6';
$zoneref->{options}{complex} = 1;
$ipsec = 1;
} elsif ( $validoptions{$option}) {
$options{$option} = 1;
} else {
fatal_error "Invalid option ($option)";
}
}
$optionsref = \%options;
}
#
# Looking for the '!' at the beginning of a list element is more straight-foward than looking for it in the middle.
#
# Be sure we don't have a ',!' in the original
#
fatal_error "Invalid hosts list" if $hosts =~ /,!/;
#
# Now add a comma before '!'. Do it globally - add_group_to_zone() correctly checks for multiple exclusions
#
$hosts =~ s/!/,!/g;
#
# Take care of case where the hosts list begins with '!'
#
$hosts = join( '', ALLIPv6 , $hosts ) if substr($hosts, 0, 2 ) eq ',!';
add_group_to_zone( $zone, $type , $interface, [ split_list( $hosts, 'host' ) ] , $optionsref);
progress_message " Host \"$currentline\" validated";
}
$capabilities{POLICY_MATCH} = 'Yes' if $ipsec || haveipseczones;
}
# #
# Returns a reference to a array of host entries. Each entry is a # Returns a reference to a array of host entries. Each entry is a
# reference to an array containing ( interface , polciy match type {ipsec|none} , network ); # reference to an array containing ( interface , polciy match type {ipsec|none} , network );
@ -1013,7 +1538,7 @@ sub find_hosts_by_option( $ ) {
my $option = $_[0]; my $option = $_[0];
my @hosts; my @hosts;
for my $zone ( grep $zones{$_}{type} ne 'firewall' , @zones ) { for my $zone ( grep $zones{$_}{family} == F_INET , @zones ) {
while ( my ($type, $interfaceref) = each %{$zones{$zone}{hosts}} ) { while ( my ($type, $interfaceref) = each %{$zones{$zone}{hosts}} ) {
while ( my ( $interface, $arrayref) = ( each %{$interfaceref} ) ) { while ( my ( $interface, $arrayref) = ( each %{$interfaceref} ) ) {
for my $host ( @{$arrayref} ) { for my $host ( @{$arrayref} ) {
@ -1028,7 +1553,7 @@ sub find_hosts_by_option( $ ) {
} }
for my $interface ( @interfaces ) { for my $interface ( @interfaces ) {
if ( ! $interfaces{$interface}{zone4} && $interfaces{$interface}{options}{$option} ) { if ( ! $interfaces{$interface}{zone} && $interfaces{$interface}{options}{$option} ) {
push @hosts, [ $interface, 'none', ALLIPv4 ]; push @hosts, [ $interface, 'none', ALLIPv4 ];
} }
} }
@ -1036,4 +1561,35 @@ sub find_hosts_by_option( $ ) {
\@hosts; \@hosts;
} }
#
# Returns a reference to a array of host entries. Each entry is a
# reference to an array containing ( interface , polciy match type {ipsec|none} , network );
#
sub find_6hosts_by_option( $ ) {
my $option = $_[0];
my @hosts;
for my $zone ( grep $zones{$_}{family} == F_INET6 , @zones ) {
while ( my ($type, $interfaceref) = each %{$zones{$zone}{hosts}} ) {
while ( my ( $interface, $arrayref) = ( each %{$interfaceref} ) ) {
for my $host ( @{$arrayref} ) {
if ( $host->{options}{$option} ) {
for my $net ( @{$host->{hosts}} ) {
push @hosts, [ $interface, $host->{ipsec} , $net ];
}
}
}
}
}
}
for my $interface ( @interfaces6 ) {
if ( ! $interfaces6{$interface}{zone} && $interfaces6{$interface}{options}{$option} ) {
push @hosts, [ $interface, 'none', ALLIPv6 ];
}
}
\@hosts;
}
1; 1;

View File

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<refentry> <refentry>
<refmeta> <refmeta>
<refentrytitle>shorewall-interfaces</refentrytitle> <refentrytitle>shorewall-interfaces</refentrytitle>
@ -95,9 +97,9 @@ loc eth2 -</programlisting>
<listitem> <listitem>
<para>The broadcast address(es) for the network(s) to which the <para>The broadcast address(es) for the network(s) to which the
interface belongs. For P-T-P interfaces, this column is left interface belongs. For P-T-P interfaces, this column is left blank.
blank. If the interface has multiple addresses on multiple subnets If the interface has multiple addresses on multiple subnets then
then list the broadcast addresses as a comma-separated list.</para> list the broadcast addresses as a comma-separated list.</para>
<para>If you use the special value <emphasis <para>If you use the special value <emphasis
role="bold">detect</emphasis>, Shorewall will detect the broadcast role="bold">detect</emphasis>, Shorewall will detect the broadcast
@ -257,8 +259,8 @@ loc eth2 -</programlisting>
</listitem> </listitem>
<listitem> <listitem>
<para>you have a static IP but are on a LAN segment with <para>the interface has a static IP but is on a LAN
lots of DHCP clients.</para> segment with lots of DHCP clients.</para>
</listitem> </listitem>
<listitem> <listitem>
@ -266,6 +268,9 @@ loc eth2 -</programlisting>
port and DHCP clients on another port.</para> port and DHCP clients on another port.</para>
</listitem> </listitem>
</orderedlist> </orderedlist>
<para>This option allows DHCP datagrams to enter and leave the
interface.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 150 KiB