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_CAPVERSION=40200
SHOREWALL_CAPVERSION=40202
[ -n "${VARDIR:=/var/lib/shorewall}" ]
[ -n "${SHAREDIR:=/usr/share/shorewall}" ]
@ -1047,6 +1047,7 @@ determine_capabilities() {
CONNTRACK_MATCH=
NEW_CONNTRACK_MATCH=
OLD_CONNTRACK_MATCH=
MULTIPORT=
XMULTIPORT=
POLICY_MATCH=
@ -1105,6 +1106,7 @@ determine_capabilities() {
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 ! --ctorigdst 1.2.3.4 || OLD_CONNTRACK_MATCH=Yes
fi
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
[ -n "$MULTIPORT" ] && report_capability "Extended Multi-port Match" $XMULTIPORT
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 "Policy Match" $POLICY_MATCH
report_capability "Physdev Match" $PHYSDEV_MATCH
@ -1263,6 +1268,7 @@ report_capabilities1() {
report_capability1 XMULTIPORT
report_capability1 CONNTRACK_MATCH
report_capability1 NEW_CONNTRACK_MATCH
report_capability1 OLD_CONNTRACK_MATCH
report_capability1 USEPKTTYPE
report_capability1 POLICY_MATCH
report_capability1 PHYSDEV_MATCH

View File

@ -43,9 +43,11 @@ our @EXPORT = qw( merge_levels
process_actions3
find_macro
find_6macro
split_action
substitute_param
merge_macro_source_dest
merge_6macro_source_dest
merge_macro_column
%usedactions
@ -53,6 +55,7 @@ our @EXPORT = qw( merge_levels
%actions
%macros
%macros6
$macro_commands
);
our @EXPORT_OK = qw( initialize );
@ -82,6 +85,7 @@ our %actions;
our %logactionchains;
our %macros;
our %macros6;
#
# 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' );
%actions = ();
%logactionchains = ();
%macros = ();
%macros6 = ();
}
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
#
@ -212,6 +231,22 @@ sub merge_macro_source_dest( $$ ) {
$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( $$ ) {
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 @EXPORT = qw( compiler EXPORT TIMESTAMP DEBUG );
our @EXPORT_OK = qw( $export );
our $VERSION = 4.1.4;
our $VERSION = 4.3.0;
our $export;
@ -168,6 +168,13 @@ sub generate_script_1() {
emit( 'IPTABLES_RESTORE=${IPTABLES}-restore',
'[ -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};
emit ( '',

View File

@ -114,7 +114,7 @@ our %EXPORT_TAGS = ( internal => [ qw( create_temp_object
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.
@ -163,7 +163,7 @@ our %config;
#
# 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 /;
#
# From parsing the capabilities file
@ -177,6 +177,8 @@ our %capdesc = ( NAT_ENABLED => 'NAT',
MULTIPORT => 'Multi-port Match' ,
XMULTIPORT => 'Extended Multi-port Match',
CONNTRACK_MATCH => 'Connection Tracking Match',
OLD_CONNTRACK_MATCH =>
'Old conntrack match syntax',
NEW_CONNTRACK_MATCH =>
'Extended Connection Tracking Match',
USEPKTTYPE => 'Packet Type Match',
@ -271,7 +273,7 @@ sub initialize() {
LOGPARMS => '',
TC_SCRIPT => '',
VERSION => "4.2.1",
CAPVERSION => 40200 ,
CAPVERSION => 40202 ,
);
#
# From shorewall.conf file
@ -348,6 +350,7 @@ sub initialize() {
DELAYBLACKLISTLOAD => undef,
MODULE_SUFFIX => undef,
DISABLE_IPV6 => undef,
IPV6 => undef,
DYNAMIC_ZONES => undef,
PKTTYPE=> undef,
RFC1918_STRICT => undef,
@ -389,6 +392,7 @@ sub initialize() {
XMULTIPORT => undef,
CONNTRACK_MATCH => undef,
NEW_CONNTRACK_MATCH => undef,
OLD_CONNTRACK_MATCH => undef,
USEPKTTYPE => undef,
POLICY_MATCH => undef,
PHYSDEV_MATCH => undef,
@ -765,12 +769,14 @@ sub copy( $ ) {
open IF , $file or fatal_error "Unable to open $file: $!";
while ( <IF> ) {
chomp;
if ( /^\s*$/ ) {
print $object "\n" unless $lastlineblank;
$lastlineblank = 1;
} else {
s/^/$indent/ if $indent;
print $object $_;
print $object "\n";
$lastlineblank = 0;
}
}
@ -791,6 +797,7 @@ sub copy1( $ ) {
my $do_indent = 1;
while ( <IF> ) {
chomp;
if ( /^\s*$/ ) {
print $object "\n";
$do_indent = 1;
@ -799,6 +806,7 @@ sub copy1( $ ) {
s/^/$indent/ if $indent && $do_indent;
print $object $_;
print $object "\n";
$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" );
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{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" ) ) {
@ -1892,7 +1901,22 @@ sub get_configuration( $ ) {
default_yes_no 'ADMINISABSENTMINDED' , '';
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 'BRIDGING';

View File

@ -26,25 +26,36 @@
#
package Shorewall::IPAddrs;
require Exporter;
use Socket6;
use Shorewall::Config qw( :DEFAULT split_list require_capability in_hex8);
use strict;
our @ISA = qw(Exporter);
our @EXPORT = qw( ALLIPv4
ALLIPv6
TCP
UDP
ICMP
IPv6_ICMP
SCTP
F_INET
F_INET6
validate_address
validate_6address
validate_net
validate_6net
decompose_net
validate_host
validate_6host
validate_range
validate_6range
ip_range_explicit
expand_port_range
allipv4
allipv6
rfc1918_networks
resolve_proto
proto_name
@ -54,14 +65,23 @@ our @EXPORT = qw( ALLIPv4
validate_icmp
);
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 @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" );
@ -95,8 +115,7 @@ sub validate_address( $$ ) {
if ( defined wantarray ) {
shift @addrs for (1..4);
for ( @addrs ) {
my (@a) = unpack('C4',$_);
$_ = join('.', @a );
$_ = inet_htoa $_;
}
}
}
@ -223,6 +242,10 @@ sub allipv4() {
@allipv4;
}
sub allipv6() {
@allipv6;
}
sub rfc1918_networks() {
@rfc1918_networks
}
@ -231,7 +254,7 @@ sub rfc1918_networks() {
# 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' );
#
@ -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;

View File

@ -26,20 +26,33 @@ package Shorewall::Policy;
require Exporter;
use Shorewall::Config qw(:DEFAULT :internal);
use Shorewall::Zones;
use Shorewall::IPAddrs;
use Shorewall::Chains qw( :DEFAULT :internal) ;
use Shorewall::Actions;
use strict;
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 $VERSION = 4.1.1;
our $VERSION = 4.3.0;
# @policy_chains is a list of references to policy chains in the filter table
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
# the compiler to run multiple times in the same process. The
@ -51,6 +64,7 @@ our @policy_chains;
sub initialize() {
@policy_chains = ();
@policy_6chains = ();
}
INIT {
@ -85,6 +99,20 @@ sub new_policy_chain($$$$)
$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.
#
@ -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($$$$) {
my ( $source, $dest, $policy , $chain ) = @_;
unless ( ( $source eq 'all' ) || ( $dest eq 'all' ) ) {
@ -220,11 +263,17 @@ sub validate_policy()
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' );
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 );
@ -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
#
@ -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() {
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() {
progress_message2 'Applying Policies...';
progress_message2 'Applying IPv4 Policies...';
for my $chainref ( @policy_chains ) {
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
#
@ -461,6 +791,33 @@ sub complete_standard_chain ( $$$$ ) {
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
#
@ -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;

View File

@ -52,6 +52,7 @@ our $VERSION = 4.1.5;
# Set to one if we find a SECTION
#
our $sectioned;
our $sectioned6;
our $macro_nest_level;
our $current_param;
our @param_stack;
@ -74,6 +75,7 @@ my %rules_commands = ( COMMENT => 0,
sub initialize() {
$sectioned = 0;
$sectioned6 = 0;
$macro_nest_level = 0;
$current_param = '';
@param_stack = ();
@ -386,6 +388,125 @@ sub process_criticalhosts() {
\@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() {
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 add_common_rules() {
@ -1447,6 +1662,642 @@ sub process_rules() {
$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".
#

View File

@ -44,28 +44,44 @@ our @EXPORT = qw( NOTHING
firewall_zone
defined_zone
zone_type
zone_family
all_zones
all_6zones
complex_zones
non_firewall_zones
non_firewall_6zones
single_interface
validate_interfaces_file
validate_6interfaces_file
all_interfaces
all_6interfaces
interface_number
find_interface
find_6interface
known_interface
known_6interface
have_bridges
have_6bridges
port_to_bridge
port_to_6bridge
source_port_to_bridge
source_port_to_6bridge
interface_is_optional
interface6_is_optional
find_interfaces_by_option
find_6interfaces_by_option
get_interface_option
get_6interface_option
set_interface_option
set_6interface_option
validate_hosts_file
validate_6hosts_file
find_hosts_by_option
find_6hosts_by_option
);
our @EXPORT_OK = qw( initialize );
our $VERSION = 4.1.5;
our $VERSION = 4.3.0;
#
# 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{<zone1> => {type = > <zone type> 'firewall', 'ipv4', 'ipsec4', 'bport4';
# %zones{<zone1> => {type = > <zone type> 'firewall', 'ipv4', 'ipsec4', 'bport4', 'ipv6', 'ipsec6', 'bport6';
# options => { complex => 0|1
# nested => 0|1
# in_out => < policy match string >
@ -93,6 +109,7 @@ use constant { NOTHING => 'NOTHING',
# children => [ <children> ]
# interfaces => [ <interfaces> ]
# bridge => <bridge>
# family => 1 = IPv4, 2 = IPv6, 3 = firewall
# hosts { <type> } => [ { <interface1> => { ipsec => 'ipsec'|'none'
# options => { <option1> => <value1>
# ...
@ -120,14 +137,15 @@ our %reservedName = ( all => 1,
#
# 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>
# root => <name without trailing '+'>
# options => { <option1> = <val1> ,
# ...
# }
# zone4 => <zone name>
# zone => <zone name>
# nets => <number of nets in interface/hosts records referring to this interface>
# bridge => <bridge>
# 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 @interfaces6;
our %interfaces6;
our @bport_zones;
our @bport_6zones;
#
# Initialize globals -- we take this novel approach to globals initialization to allow
@ -156,6 +190,9 @@ sub initialize() {
@interfaces = ();
%interfaces = ();
@bport_zones = ();
@interfaces6 = ();
%interfaces6 = ();
@bport_6zones = ();
}
INIT {
@ -219,7 +256,7 @@ sub parse_zone_option_list($$)
if ( $key{$e} ) {
$h{$e} = $val;
} 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 .= "--$e ";
$options .= "$val "if defined $val;
@ -240,6 +277,7 @@ sub determine_zones()
my @z;
my $ipv4 = 0;
my $ipv6 = 0;
my $fn = open_file 'zones';
@ -262,28 +300,43 @@ sub determine_zones()
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)" if $reservedName{$zone} || $zone =~ /^all2|2all$/;
fatal_error( "Duplicate zone name ($zone)" ) if $zones{$zone};
$type = "ipv4" unless $type;
my $family = F_INET;
if ( $type =~ /ipv4/i ) {
$type = 'ipv4';
$ipv4 = 1;
} elsif ( $type =~ /ipv6/i ) {
$type = 'ipv6';
$ipv6 = 1;
$family = F_INET6;
} elsif ( $type =~ /^ipsec4?$/i ) {
$type = 'ipsec4';
} elsif ( $type =~ /^ipsec6?$/i ) {
$type = 'ipsec6';
$family = F_INET6;
} elsif ( $type =~ /^bport4?$/i ) {
warning_message "Bridge Port zones should have a parent zone" unless @parents;
$type = 'bport4';
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' ) {
fatal_error 'Firewall zone may not be nested' if @parents;
fatal_error "Only one firewall zone may be defined ($zone)" if $firewall_zone;
$firewall_zone = $zone;
$ENV{FW} = $zone;
$type = "firewall";
$family = F_INET | F_INET6;
} elsif ( $type eq '-' ) {
$type = 'ipv4';
$ipv4 = 1;
@ -291,6 +344,10 @@ sub determine_zones()
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 ) {
$_ = '' if $_ eq '-';
}
@ -299,10 +356,11 @@ sub determine_zones()
parents => \@parents,
exclusions => [],
bridge => '',
family => $family,
options => { in_out => parse_zone_option_list( $options || '', $type ) ,
in => parse_zone_option_list( $in_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 } ,
interfaces => {} ,
children => [] ,
@ -311,8 +369,8 @@ sub determine_zones()
push @z, $zone;
}
fatal_error "No firewall zone defined" unless $firewall_zone;
fatal_error "No IPv4 zones defined" unless $ipv4;
fatal_error "No firewall zone defined" unless $firewall_zone;
fatal_error "No IPv4 or IPv6 zones defined" unless $ipv4 || $ipv6;
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() {
for my $zoneref ( values %zones ) {
@ -346,6 +404,17 @@ sub haveipseczones() {
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.
#
@ -374,7 +443,7 @@ sub zone_report()
my $hosts = $groupref->{hosts};
if ( $hosts ) {
my $grouplist = join ',', ( @$hosts );
progress_message " $interface:$grouplist";
progress_message " $interface $grouplist";
$printed = 1;
}
}
@ -384,7 +453,7 @@ sub zone_report()
}
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';
}
@ -402,7 +471,7 @@ sub dump_zone_contents()
my $exclusions = $zoneref->{exclusions};
my $entry = "$zone $type";
$entry .= ":$zoneref->{bridge}" if $type eq 'bport4';
$entry .= ":$zoneref->{bridge}" if $type =~ /^bport/;
if ( $hostref ) {
for my $type ( sort keys %$hostref ) {
@ -414,7 +483,7 @@ sub dump_zone_contents()
my $hosts = $groupref->{hosts};
if ( $hosts ) {
my $grouplist = join ',', ( @$hosts );
$entry .= " $interface:$grouplist";
$entry .= " $interface\($grouplist\)";
}
}
}
@ -455,7 +524,7 @@ sub add_group_to_zone($$$$$)
my $arrayref;
my $zoneref = $zones{$zone};
my $zonetype = $zoneref->{type};
my $ifacezone = $interfaces{$interface}{zone4};
my $ifacezone = $interfaces{$interface}{zone};
$zoneref->{interfaces}{$interface} = 1;
@ -481,7 +550,7 @@ sub add_group_to_zone($$$$$)
unless ( $switched ) {
if ( $type eq $zonetype ) {
fatal_error "Duplicate Host Group ($interface:$host) in zone $zone" if $ifacezone eq $zone;
$ifacezone = $zone if $host eq ALLIPv4;
$ifacezone = $zone if $host eq ALLIPv4 || $host eq ALLIPv6;
}
}
@ -506,7 +575,7 @@ sub add_group_to_zone($$$$$)
push @{$arrayref}, { options => $options,
hosts => \@newnetworks,
ipsec => $type eq 'ipsec4' ? 'ipsec' : 'none' };
ipsec => $type =~ /^ipsec/ ? 'ipsec' : 'none' };
}
#
@ -527,20 +596,36 @@ sub zone_type( $ ) {
find_zone( $_[0] )->{type};
}
sub zone_family( $ ) {
find_zone( $_[0] )->{family};
}
sub defined_zone( $ ) {
$zones{$_[0]};
}
sub all_zones() {
@zones;
grep ( ! $zones{$_}{family} & F_INET , @zones );
}
sub all_6zones() {
grep ( ! $zones{$_}{family} & F_INET6 , @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() {
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() {
@ -551,20 +636,19 @@ sub firewall_zone() {
# 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( $ )
{
my $export = shift;
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,
arp_ignore => ENUM_IF_OPTION,
blacklist => SIMPLE_IF_OPTION,
@ -609,6 +693,7 @@ sub validate_interfaces_file( $ )
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 "IPv6 Zones not allowed in the interfaces file ($zone}" if $zoneref->{type} =~ /6/;
}
$networks = '' if $networks eq '-';
@ -752,7 +837,7 @@ sub validate_interfaces_file( $ )
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";
@ -782,6 +867,225 @@ sub validate_interfaces_file( $ )
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
#
@ -809,6 +1113,33 @@ sub known_interface($)
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
#
@ -823,12 +1154,38 @@ sub all_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
#
sub find_interface( $ ) {
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;
@ -842,6 +1199,13 @@ sub have_bridges() {
@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 ''
@ -851,6 +1215,15 @@ sub port_to_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.
#
@ -859,6 +1232,14 @@ sub source_port_to_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
#
@ -867,6 +1248,14 @@ sub interface_is_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
#
@ -884,6 +1273,23 @@ sub find_interfaces_by_option( $ ) {
\@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
#
@ -902,6 +1308,24 @@ sub set_interface_option( $$$ ) {
$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}
#
@ -910,10 +1334,8 @@ sub validate_hosts_file()
my %validoptions = (
blacklist => 1,
maclist => 1,
norfc1918 => 1,
nosmurfs => 1,
routeback => 1,
routefilter => 1,
tcpflags => 1,
broadcast => 1,
destonly => 1,
@ -939,6 +1361,7 @@ sub validate_hosts_file()
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 'IPv6 zones not allowed in ZONE column of hosts record' if $type =~ /6/;
my $interface;
@ -951,7 +1374,7 @@ sub validate_hosts_file()
fatal_error "Invalid HOST(S) column contents: $hosts";
}
if ( $type eq 'bport4' ) {
if ( $type eq 'bport6' ) {
if ( $zoneref->{bridge} eq '' ) {
fatal_error 'Bridge Port Zones may only be associated with bridge ports' unless $interfaces{$interface}{options}{port};
$zoneref->{bridge} = $interfaces{$interface}{bridge};
@ -969,7 +1392,7 @@ sub validate_hosts_file()
for my $option ( @options )
{
if ( $option eq 'ipsec' ) {
$type = 'ipsec4';
$type = 'ipsec6';
$zoneref->{options}{complex} = 1;
$ipsec = 1;
} elsif ( $validoptions{$option}) {
@ -995,7 +1418,7 @@ sub validate_hosts_file()
#
# 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);
@ -1005,6 +1428,108 @@ sub validate_hosts_file()
$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
# 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 @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 ( $interface, $arrayref) = ( each %{$interfaceref} ) ) {
for my $host ( @{$arrayref} ) {
@ -1028,7 +1553,7 @@ sub find_hosts_by_option( $ ) {
}
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 ];
}
}
@ -1036,4 +1561,35 @@ sub find_hosts_by_option( $ ) {
\@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;

View File

@ -1,4 +1,6 @@
<?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>
<refmeta>
<refentrytitle>shorewall-interfaces</refentrytitle>
@ -95,9 +97,9 @@ loc eth2 -</programlisting>
<listitem>
<para>The broadcast address(es) for the network(s) to which the
interface belongs. For P-T-P interfaces, this column is left
blank. If the interface has multiple addresses on multiple subnets
then list the broadcast addresses as a comma-separated list.</para>
interface belongs. For P-T-P interfaces, this column is left blank.
If the interface has multiple addresses on multiple subnets then
list the broadcast addresses as a comma-separated list.</para>
<para>If you use the special value <emphasis
role="bold">detect</emphasis>, Shorewall will detect the broadcast
@ -257,8 +259,8 @@ loc eth2 -</programlisting>
</listitem>
<listitem>
<para>you have a static IP but are on a LAN segment with
lots of DHCP clients.</para>
<para>the interface has a static IP but is on a LAN
segment with lots of DHCP clients.</para>
</listitem>
<listitem>
@ -266,6 +268,9 @@ loc eth2 -</programlisting>
port and DHCP clients on another port.</para>
</listitem>
</orderedlist>
<para>This option allows DHCP datagrams to enter and leave the
interface.</para>
</listitem>
</varlistentry>

View File

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 150 KiB