Consolidate Perl Modules

git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@6888 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb
This commit is contained in:
teastep 2007-07-16 22:07:50 +00:00
parent d1d664ed97
commit bddf520436
20 changed files with 945 additions and 1189 deletions

View File

@ -25,11 +25,9 @@
#
package Shorewall::Accounting;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::IPAddrs;
use Shorewall::Zones;
use Shorewall::Interfaces;
use Shorewall::Chains;
use strict;

View File

@ -21,15 +21,13 @@
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
# This module contains the code for dealing with actions (built-in,
# standard and user-defined).
# standard and user-defined) and Macros.
#
package Shorewall::Actions;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::Zones;
use Shorewall::Chains;
use Shorewall::Macros;
use strict;
@ -44,9 +42,17 @@ our @EXPORT = qw( merge_levels
process_actions2
process_actions3
find_macro
split_action
substitute_param
merge_macro_source_dest
merge_macro_column
%usedactions
%default_actions
%actions
%macros
);
our @EXPORT_OK = qw( initialize );
our $VERSION = 4.00;
@ -73,6 +79,9 @@ our %actions;
# Contains an entry for each used <action>:<level>[:<tag>] that maps to the associated chain.
#
our %logactionchains;
our %macros;
#
# Initialize globals -- we take this novel approach to globals initialization to allow
# the compiler to run multiple times in the same process. The
@ -108,6 +117,84 @@ sub merge_levels ($$) {
my $subparts = @subparts;
my $target = $subparts[0];
#
# Try to find a macro file -- RETURNS false if the file doesn't exist or MACRO if it does.
# If the file exists, the macro is entered into the 'targets' table and the fully-qualified
# name of the file is stored in the 'macro' table.
#
sub find_macro( $ )
{
my $macro = $_[0];
my $macrofile = find_file "macro.$macro";
if ( -f $macrofile ) {
$macros{$macro} = $macrofile;
$targets{$macro} = MACRO;
} else {
0;
}
}
#
# Return ( action, level[:tag] ) from passed full action
#
sub split_action ( $ ) {
my $action = $_[0];
my @a = split( /:/ , $action, 4 );
fatal_error "Invalid ACTION ($action)" if ( $action =~ /::/ ) || ( @a > 3 );
( shift @a, join ":", @a );
}
#
# This function substitutes the second argument for the first part of the first argument up to the first colon (":")
#
# Example:
#
# substitute_param DNAT PARAM:info:FTP
#
# produces "DNAT:info:FTP"
#
sub substitute_param( $$ ) {
my ( $param, $action ) = @_;
if ( $action =~ /:/ ) {
my $logpart = (split_action $action)[1];
$logpart =~ s!/$!!;
return "$param:$logpart";
}
$param;
}
#
# Combine fields from a macro body with one from the macro invocation
#
sub merge_macro_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 ) = @_;
if ( defined $invocation && $invocation ne '' && $invocation ne '-' ) {
$invocation;
} else {
$body;
}
}
push @subparts, '' while @subparts < 3; #Avoid undefined values

View File

@ -27,11 +27,9 @@
package Shorewall::Chains;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::Ports;
use Shorewall::Zones;
use Shorewall::Interfaces;
use Shorewall::IPAddrs;
use strict;

View File

@ -1,367 +0,0 @@
#
# Shorewall-perl 4.0 -- /usr/share/shorewall-perl/Shorewall/Common.pm
#
# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
#
# (c) 2007 - Tom Eastep (teastep@shorewall.net)
#
# Complete documentation is available at http://shorewall.net
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of Version 2 of the GNU General Public License
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
# This is the lowes level Shorewall module. It provides very basic
# services such as creation of temporary 'object' files, writing
# into those files (emitters) and finalizing those files (renaming
# them to their final name and setting their mode appropriately).
#
package Shorewall::Common;
require Exporter;
use File::Basename;
use File::Temp qw/ tempfile tempdir /;
use Cwd 'abs_path';
use strict;
our @ISA = qw(Exporter);
our @EXPORT = qw(
create_temp_object
finalize_object
emit
emitj
emit_unindented
save_progress_message
save_progress_message_short
set_timestamp
set_verbose
progress_message
progress_message2
progress_message3
push_indent
pop_indent
copy
copy1
create_temp_aux_config
finalize_aux_config
$command
$doing
$done
$verbose
);
our @EXPORT_OK = qw( $timestamp initialize );
our $VERSION = 4.00;
our ($command, $doing, $done );
our $verbose;
our $timestamp;
our $object;
our $lastlineblank;
our $indent;
our ( $dir, $file ); # Object's Directory and File
our $tempfile; # Temporary File Name
#
# Initialize globals -- we take this novel approach to globals initialization to allow
# the compiler to run multiple times in the same process. The
# initialize() function does globals initialization for this
# module and is called from an INIT block below. The function is
# also called by Shorewall::Compiler::compiler at the beginning of
# the second and subsequent calls to that function.
#
sub initialize() {
( $command, $doing, $done ) = qw/ compile Compiling Compiled/; #describe the current command, it's present progressive, and it's completion.
$verbose = 0; # Verbosity setting. 0 = almost silent, 1 = major progress messages only, 2 = all progress messages (very noisy)
$timestamp = ''; # If true, we are to timestamp each progress message
$object = 0; # Object (script) file Handle Reference
$lastlineblank = 0; # Avoid extra blank lines in the output
$indent = ''; # Current indentation
( $dir, $file ) = ('',''); # Object's Directory and File
$tempfile = ''; # Temporary File Name
}
INIT {
initialize;
}
#
# Fatal Error
#
sub fatal_error
{
die " ERROR: @_\n";
}
#
# Write the argument to the object file (if any) with the current indentation.
#
# Replaces leading spaces with tabs as appropriate and suppresses consecutive blank lines.
#
sub emit ( $ ) {
if ( $object ) {
#
# 'compile' as opposed to 'check'
#
my $line = $_[0]; # This copy is necessary because the actual arguments are almost always read-only.
unless ( $line =~ /^\s*$/ ) {
$line =~ s/^\n// if $lastlineblank;
$line =~ s/^/$indent/gm if $indent;
$line =~ s/ /\t/gm;
print $object "$line\n";
$lastlineblank = ( substr( $line, -1, 1 ) eq "\n" );
} else {
print $object "\n" unless $lastlineblank;
$lastlineblank = 1;
}
}
}
#
# Version of emit() that accepts an indefinite number of scalar arguments; each argument will be emitted as a separate line
#
sub emitj {
if ( $object ) {
#
# 'compile' as opposed to 'check'
#
for ( @_ ) {
unless ( /^\s*$/ ) {
my $line = $_; # This copy is necessary because the actual arguments are almost always read-only.
$line =~ s/^\n// if $lastlineblank;
$line =~ s/^/$indent/gm if $indent;
$line =~ s/ /\t/gm;
print $object "$line\n";
$lastlineblank = ( substr( $line, -1, 1 ) eq "\n" );
} else {
print $object "\n" unless $lastlineblank;
$lastlineblank = 1;
}
}
}
}
#
# Write passed message to the object with newline but no indentation.
#
sub emit_unindented( $ ) {
print $object "$_[0]\n" if $object;
}
#
# Write a progress_message2 command with surrounding blank lines to the output file.
#
sub save_progress_message( $ ) {
emit "\nprogress_message2 @_\n" if $object;
}
#
# Write a progress_message command to the output file.
#
sub save_progress_message_short( $ ) {
emit "progress_message $_[0]" if $object;
}
#
# Set $timestamp
#
sub set_timestamp( $ ) {
$timestamp = shift;
}
#
# Set $verbose
#
sub set_verbose( $ ) {
$verbose = shift;
}
#
# Print the current TOD to STDOUT.
#
sub timestamp() {
my ($sec, $min, $hr) = ( localtime ) [0,1,2];
printf '%02d:%02d:%02d ', $hr, $min, $sec;
}
#
# Write a message if $verbose >= 2
#
sub progress_message {
if ( $verbose > 1 ) {
timestamp if $timestamp;
#
# We use this function to display messages containing raw config file images which may contains tabs (including multiple tabs in succession).
# The following makes such messages look more readable and uniform
#
my $line = "@_";
$line =~ s/\s+/ /g;
print "$line\n";
}
}
#
# Write a message if $verbose >= 1
#
sub progress_message2 {
if ( $verbose > 0 ) {
timestamp if $timestamp;
print "@_\n";
}
}
#
# Write a message if $verbose >= 0
#
sub progress_message3 {
if ( $verbose >= 0 ) {
timestamp if $timestamp;
print "@_\n";
}
}
#
# Push/Pop Indent
#
sub push_indent() {
$indent = "$indent ";
}
sub pop_indent() {
$indent = substr( $indent , 0 , ( length $indent ) - 4 );
}
#
# Functions for copying files into the object
#
sub copy( $ ) {
if ( $object ) {
my $file = $_[0];
open IF , $file or fatal_error "Unable to open $file: $!";
while ( <IF> ) {
s/^/$indent/ if $indent;
print $object $_;
}
close IF;
}
}
#
# This one handles line continuation.
sub copy1( $ ) {
if ( $object ) {
my $file = $_[0];
open IF , $file or fatal_error "Unable to open $file: $!";
my $do_indent = 1;
while ( <IF> ) {
if ( /^\s*$/ ) {
print $object "\n";
$do_indent = 1;
next;
}
s/^/$indent/ if $indent && $do_indent;
print $object $_;
$do_indent = ! ( /\\$/ );
}
close IF;
}
}
#
# Create the temporary object file -- the passed file name is the name of the final file.
# We create a temporary file in the same directory so that we can use rename to finalize it.
#
sub create_temp_object( $ ) {
my $objectfile = $_[0];
my $suffix;
eval {
( $file, $dir, $suffix ) = fileparse( $objectfile );
};
die if $@;
fatal_error "Directory $dir does not exist" unless -d $dir;
fatal_error "Directory $dir is not writable" unless -w _;
fatal_error "$dir is a Symbolic Link" if -l $dir;
fatal_error "$objectfile is a Directory" if -d $objectfile;
fatal_error "$dir is a Symbolic Link" if -l $objectfile;
fatal_error "$objectfile exists and is not a compiled script" if -e _ && ! -x _;
eval {
$dir = abs_path $dir;
( $object, $tempfile ) = tempfile ( 'tempfileXXXX' , DIR => $dir );
};
fatal_error "Unable to create temporary file in directory $dir" if $@;
$file = "$file.$suffix" if $suffix;
$dir .= '/' unless substr( $dir, -1, 1 ) eq '/';
$file = $dir . $file;
}
#
# Finalize the object file
#
sub finalize_object( $ ) {
my $export = $_[0];
close $object;
$object = 0;
rename $tempfile, $file or fatal_error "Cannot Rename $tempfile to $file: $!";
chmod 0700, $file or fatal_error "Cannot secure $file for execute access";
progress_message3 "Shorewall configuration compiled to $file" unless $export;
}
#
# Create the temporary aux config file.
#
sub create_temp_aux_config() {
eval {
( $object, $tempfile ) = tempfile ( 'tempfileXXXX' , DIR => $dir );
};
die if $@;
}
#
# Finalize the aux config file.
#
sub finalize_aux_config() {
close $object;
$object = 0;
rename $tempfile, "$file.conf" or fatal_error "Cannot Rename $tempfile to $file.conf: $!";
progress_message3 "Shorewall configuration compiled to $file";
}
END {
if ( $object ) {
close $object;
unlink $tempfile;
}
}
1;

View File

@ -24,18 +24,14 @@
package Shorewall::Compiler;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::Chains;
use Shorewall::Zones;
use Shorewall::Interfaces;
use Shorewall::Hosts;
use Shorewall::Policy;
use Shorewall::Nat;
use Shorewall::Providers;
use Shorewall::Tc;
use Shorewall::Tunnels;
use Shorewall::Macros;
use Shorewall::Actions;
use Shorewall::Accounting;
use Shorewall::Rules;
@ -59,15 +55,12 @@ use constant { EXPORT => 0x01 ,
# Reinitilize the package-globals in the other modules
#
sub reinitialize() {
Shorewall::Common::initialize;
Shorewall::Config::initialize;
Shorewall::Chains::initialize;
Shorewall::Zones::initialize;
Shorewall::Interfaces::initialize;
Shorewall::Nat::initialize;
Shorewall::Providers::initialize;
Shorewall::Tc::initialize;
Shorewall::Macros::initialize;
Shorewall::Actions::initialize;
Shorewall::Accounting::initialize;
Shorewall::Rules::initialize;

View File

@ -23,18 +23,40 @@
# This module is responsible for lower level configuration file handling.
# It also exports functions for generating warning and error messages.
# The get_configuration function parses the shorewall.conf, capabilities and
# modules files during compiler startup.
# modules files during compiler startup. The module also provides very basic
# services such as creation of temporary 'object' files, writing
# into those files (emitters) and finalizing those files (renaming
# them to their final name and setting their mode appropriately).
#
package Shorewall::Config;
use strict;
use warnings;
use Shorewall::Common;
use File::Basename;
use File::Temp qw/ tempfile tempdir /;
use Cwd 'abs_path';
use autouse 'Carp' => qw(longmess confess);
our @ISA = qw(Exporter);
our @EXPORT = qw(
create_temp_object
finalize_object
emit
emitj
emit_unindented
save_progress_message
save_progress_message_short
set_timestamp
set_verbose
progress_message
progress_message2
progress_message3
push_indent
pop_indent
copy
create_temp_aux_config
finalize_aux_config
warning_message
fatal_error
set_shorewall_dir
@ -61,6 +83,11 @@ our @EXPORT = qw(
run_user_exit2
generate_aux_config
$command
$doing
$done
$verbose
$currentline
%config
%globals
@ -69,6 +96,15 @@ our @EXPORT = qw(
our @EXPORT_OK = qw( $shorewall_dir initialize read_a_line1 set_config_path );
our $VERSION = 4.00;
our ($command, $doing, $done );
our $verbose;
our $timestamp;
our $object;
our $lastlineblank;
our $indent;
our ( $dir, $file ); # Object's Directory and File
our $tempfile; # Temporary File Name
#
# Misc Globals
#
@ -121,6 +157,16 @@ our $debug;
# the second and subsequent calls to that function.
#
sub initialize() {
( $command, $doing, $done ) = qw/ compile Compiling Compiled/; #describe the current command, it's present progressive, and it's completion.
$verbose = 0; # Verbosity setting. 0 = almost silent, 1 = major progress messages only, 2 = all progress messages (very noisy)
$timestamp = ''; # If true, we are to timestamp each progress message
$object = 0; # Object (script) file Handle Reference
$lastlineblank = 0; # Avoid extra blank lines in the output
$indent = ''; # Current indentation
( $dir, $file ) = ('',''); # Object's Directory and File
$tempfile = ''; # Temporary File Name
#
# Misc Globals
#
@ -343,6 +389,259 @@ sub fatal_error {
die " ERROR: @_$currentlineinfo\n";
}
#
# Write the argument to the object file (if any) with the current indentation.
#
# Replaces leading spaces with tabs as appropriate and suppresses consecutive blank lines.
#
sub emit ( $ ) {
if ( $object ) {
#
# 'compile' as opposed to 'check'
#
my $line = $_[0]; # This copy is necessary because the actual arguments are almost always read-only.
unless ( $line =~ /^\s*$/ ) {
$line =~ s/^\n// if $lastlineblank;
$line =~ s/^/$indent/gm if $indent;
$line =~ s/ /\t/gm;
print $object "$line\n";
$lastlineblank = ( substr( $line, -1, 1 ) eq "\n" );
} else {
print $object "\n" unless $lastlineblank;
$lastlineblank = 1;
}
}
}
#
# Version of emit() that accepts an indefinite number of scalar arguments; each argument will be emitted as a separate line
#
sub emitj {
if ( $object ) {
#
# 'compile' as opposed to 'check'
#
for ( @_ ) {
unless ( /^\s*$/ ) {
my $line = $_; # This copy is necessary because the actual arguments are almost always read-only.
$line =~ s/^\n// if $lastlineblank;
$line =~ s/^/$indent/gm if $indent;
$line =~ s/ /\t/gm;
print $object "$line\n";
$lastlineblank = ( substr( $line, -1, 1 ) eq "\n" );
} else {
print $object "\n" unless $lastlineblank;
$lastlineblank = 1;
}
}
}
}
#
# Write passed message to the object with newline but no indentation.
#
sub emit_unindented( $ ) {
print $object "$_[0]\n" if $object;
}
#
# Write a progress_message2 command with surrounding blank lines to the output file.
#
sub save_progress_message( $ ) {
emit "\nprogress_message2 @_\n" if $object;
}
#
# Write a progress_message command to the output file.
#
sub save_progress_message_short( $ ) {
emit "progress_message $_[0]" if $object;
}
#
# Set $timestamp
#
sub set_timestamp( $ ) {
$timestamp = shift;
}
#
# Set $verbose
#
sub set_verbose( $ ) {
$verbose = shift;
}
#
# Print the current TOD to STDOUT.
#
sub timestamp() {
my ($sec, $min, $hr) = ( localtime ) [0,1,2];
printf '%02d:%02d:%02d ', $hr, $min, $sec;
}
#
# Write a message if $verbose >= 2
#
sub progress_message {
if ( $verbose > 1 ) {
timestamp if $timestamp;
#
# We use this function to display messages containing raw config file images which may contains tabs (including multiple tabs in succession).
# The following makes such messages look more readable and uniform
#
my $line = "@_";
$line =~ s/\s+/ /g;
print "$line\n";
}
}
#
# Write a message if $verbose >= 1
#
sub progress_message2 {
if ( $verbose > 0 ) {
timestamp if $timestamp;
print "@_\n";
}
}
#
# Write a message if $verbose >= 0
#
sub progress_message3 {
if ( $verbose >= 0 ) {
timestamp if $timestamp;
print "@_\n";
}
}
#
# Push/Pop Indent
#
sub push_indent() {
$indent = "$indent ";
}
sub pop_indent() {
$indent = substr( $indent , 0 , ( length $indent ) - 4 );
}
#
# Functions for copying files into the object
#
sub copy( $ ) {
if ( $object ) {
my $file = $_[0];
open IF , $file or fatal_error "Unable to open $file: $!";
while ( <IF> ) {
s/^/$indent/ if $indent;
print $object $_;
}
close IF;
}
}
#
# This one handles line continuation.
sub copy1( $ ) {
if ( $object ) {
my $file = $_[0];
open IF , $file or fatal_error "Unable to open $file: $!";
my $do_indent = 1;
while ( <IF> ) {
if ( /^\s*$/ ) {
print $object "\n";
$do_indent = 1;
next;
}
s/^/$indent/ if $indent && $do_indent;
print $object $_;
$do_indent = ! ( /\\$/ );
}
close IF;
}
}
#
# Create the temporary object file -- the passed file name is the name of the final file.
# We create a temporary file in the same directory so that we can use rename to finalize it.
#
sub create_temp_object( $ ) {
my $objectfile = $_[0];
my $suffix;
eval {
( $file, $dir, $suffix ) = fileparse( $objectfile );
};
die if $@;
fatal_error "Directory $dir does not exist" unless -d $dir;
fatal_error "Directory $dir is not writable" unless -w _;
fatal_error "$dir is a Symbolic Link" if -l $dir;
fatal_error "$objectfile is a Directory" if -d $objectfile;
fatal_error "$dir is a Symbolic Link" if -l $objectfile;
fatal_error "$objectfile exists and is not a compiled script" if -e _ && ! -x _;
eval {
$dir = abs_path $dir;
( $object, $tempfile ) = tempfile ( 'tempfileXXXX' , DIR => $dir );
};
fatal_error "Unable to create temporary file in directory $dir" if $@;
$file = "$file.$suffix" if $suffix;
$dir .= '/' unless substr( $dir, -1, 1 ) eq '/';
$file = $dir . $file;
}
#
# Finalize the object file
#
sub finalize_object( $ ) {
my $export = $_[0];
close $object;
$object = 0;
rename $tempfile, $file or fatal_error "Cannot Rename $tempfile to $file: $!";
chmod 0700, $file or fatal_error "Cannot secure $file for execute access";
progress_message3 "Shorewall configuration compiled to $file" unless $export;
}
#
# Create the temporary aux config file.
#
sub create_temp_aux_config() {
eval {
( $object, $tempfile ) = tempfile ( 'tempfileXXXX' , DIR => $dir );
};
die if $@;
}
#
# Finalize the aux config file.
#
sub finalize_aux_config() {
close $object;
$object = 0;
rename $tempfile, "$file.conf" or fatal_error "Cannot Rename $tempfile to $file.conf: $!";
progress_message3 "Shorewall configuration compiled to $file";
}
#
# Set $globals{CONFIG_PATH}
#
@ -1368,4 +1667,11 @@ sub generate_aux_config() {
}
END {
if ( $object ) {
close $object;
unlink $tempfile;
}
}
1;

View File

@ -1,171 +0,0 @@
#
# Shorewall-perl 4.0 -- /usr/share/shorewall-perl/Shorewall/Hosts.pm
#
# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
#
# (c) 2007 - Tom Eastep (teastep@shorewall.net)
#
# Complete documentation is available at http://shorewall.net
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of Version 2 of the GNU General Public License
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
# This module contains the code for dealing with the /etc/shorewall/hosts
# file.
#
package Shorewall::Hosts;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::IPAddrs;
use Shorewall::Zones;
use Shorewall::Interfaces;
use strict;
our @ISA = qw(Exporter);
our @EXPORT = qw( validate_hosts_file find_hosts_by_option );
our @EXPORT_OK = ();
our $VERSION = 4.00;
#
# Validates the hosts file. Generates entries in %zone{..}{hosts}
#
sub validate_hosts_file()
{
my %validoptions = (
blacklist => 1,
maclist => 1,
norfc1918 => 1,
nosmurfs => 1,
routeback => 1,
routefilter => 1,
tcpflags => 1,
);
my $ipsec = 0;
my $first_entry = 1;
my $fn = open_file 'hosts';
while ( read_a_line ) {
if ( $first_entry ) {
progress_message2 "$doing $fn...";
$first_entry = 0;
}
my ($zone, $hosts, $options ) = split_line 2, 3, 'hosts 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';
my $interface;
if ( $hosts =~ /^([\w.@%-]+\+?):(.*)$/ ) {
$interface = $1;
$hosts = $2;
$zoneref->{options}{complex} = 1 if $hosts =~ /^\+/;
fatal_error "Unknown interface ($interface)" unless $interfaces{$interface}{root};
} else {
fatal_error "Invalid HOST(S) column contents: $hosts";
}
if ( $type eq 'bport4' ) {
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};
} elsif ( $zoneref->{bridge} ne $interfaces{$interface}{bridge} ) {
fatal_error "Interface $interface is not a port on bridge $zoneref->{bridge}";
}
}
my $optionsref = {};
if ( $options ne '-' ) {
my @options = split ',', $options;
my %options;
for my $option ( @options )
{
if ( $option eq 'ipsec' ) {
$type = 'ipsec';
$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( '', ALLIPv4 , $hosts ) if substr($hosts, 0, 2 ) eq ',!';
add_group_to_zone( $zone, $type , $interface, [ split( ',', $hosts ) ] , $optionsref);
progress_message " Host \"$currentline\" validated";
}
$capabilities{POLICY_MATCH} = '' unless $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 );
#
sub find_hosts_by_option( $ ) {
my $option = $_[0];
my @hosts;
for my $zone ( grep $zones{$_}{type} ne 'firewall' , @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 ( @interfaces ) {
if ( ! $interfaces{$interface}{zone} && $interfaces{$interface}{options}{$option} ) {
push @hosts, [ $interface, 'none', ALLIPv4 ];
}
}
\@hosts;
}
1;

View File

@ -24,7 +24,6 @@
#
package Shorewall::IPAddrs;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use strict;

View File

@ -1,469 +0,0 @@
#
# Shorewall-perl 4.0 -- /usr/share/shorewall-perl/Shorewall/Interfaces.pm
#
# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
#
# (c) 2007 - Tom Eastep (teastep@shorewall.net)
#
# Complete documentation is available at http://shorewall.net
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of Version 2 of the GNU General Public License
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
# This Module contains the code for processing the /etc/shorewall/interfaces
# file. It also exports 'add_group_to_zone()' which other modules call to
# alter zone membership.
#
package Shorewall::Interfaces;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::IPAddrs;
use Shorewall::Zones;
use strict;
our @ISA = qw(Exporter);
our @EXPORT = qw( add_group_to_zone
validate_interfaces_file
known_interface
port_to_bridge
source_port_to_bridge
interface_is_optional
find_interfaces_by_option
get_interface_option
@interfaces
@bridges );
our @EXPORT_OK = qw( initialize );
our $VERSION = 4.00;
#
# Interface Table.
#
# @interfaces lists the interface names in the order that they appear in the interfaces file.
#
# %interfaces { <interface1> => { root => <name without trailing '+'>
# options => { <option1> = <val1> ,
# ...
# }
# zone => <zone name>
# bridge => <bridge>
# }
# }
#
our @interfaces;
our %interfaces;
our @bridges;
#
# Initialize globals -- we take this novel approach to globals initialization to allow
# the compiler to run multiple times in the same process. The
# initialize() function does globals initialization for this
# module and is called from an INIT block below. The function is
# also called by Shorewall::Compiler::compiler at the beginning of
# the second and subsequent calls to that function.
#
sub initialize() {
@interfaces = ();
%interfaces = ();
@bridges = ();
}
INIT {
initialize;
}
sub add_group_to_zone($$$$$)
{
my ($zone, $type, $interface, $networks, $options) = @_;
my $typeref;
my $interfaceref;
my $arrayref;
my $zoneref = $zones{$zone};
my $zonetype = $zoneref->{type};
my $ifacezone = $interfaces{$interface}{zone};
$zoneref->{interfaces}{$interface} = 1;
my @newnetworks;
my @exclusions;
my $new = \@newnetworks;
my $switched = 0;
$ifacezone = '' unless defined $ifacezone;
for my $host ( @$networks ) {
fatal_error "Invalid Host List" unless defined $host and $host ne '';
if ( substr( $host, 0, 1 ) eq '!' ) {
fatal_error "Only one exclusion allowed in a host list" if $switched;
$switched = 1;
$host = substr( $host, 1 );
$new = \@exclusions;
}
unless ( $switched ) {
if ( $type eq $zonetype ) {
fatal_error "Duplicate Host Group ($interface:$host) in zone $zone" if $ifacezone eq $zone;
$ifacezone = $zone if $host eq ALLIPv4;
}
}
if ( substr( $host, 0, 1 ) eq '+' ) {
fatal_error "Invalid ipset name ($host)" unless $host =~ /^\+[a-zA-Z]\w*$/;
} else {
validate_host $host;
}
push @$new, $switched ? "$interface:$host" : $host;
}
$zoneref->{options}{in_out}{routeback} = 1 if $options->{routeback};
$typeref = ( $zoneref->{hosts} || ( $zoneref->{hosts} = {} ) );
$interfaceref = ( $typeref->{$type} || ( $interfaceref = $typeref->{$type} = {} ) );
$arrayref = ( $interfaceref->{$interface} || ( $interfaceref->{$interface} = [] ) );
$zoneref->{options}{complex} = 1 if @$arrayref || ( @newnetworks > 1 ) || ( @exclusions );
push @{$zoneref->{exclusions}}, @exclusions;
push @{$arrayref}, { options => $options,
hosts => \@newnetworks,
ipsec => $type eq 'ipsec4' ? 'ipsec' : 'none' };
}
#
# Return a list of networks routed out of the passed interface
#
sub get_routed_networks ( $$ ) {
my ( $interface , $error_message ) = @_;
my @networks;
if ( open IP , '-|' , "/sbin/ip route show dev $interface 2> /dev/null" ) {
while ( my $route = <IP> ) {
$route =~ s/^\s+//;
my $network = ( split /\s+/, $route )[0];
if ( $network eq 'default' ) {
fatal_error $error_message if $error_message;
warning_message "default route ignored on interface $interface";
} else {
my ( $address, $vlsm ) = split '/', $network;
$vlsm = 32 unless defined $vlsm;
push @networks, "$address/$vlsm";
}
}
close IP
}
@networks;
}
#
# Parse the interfaces file.
#
sub validate_interfaces_file( $ )
{
my $export = shift;
use constant { SIMPLE_IF_OPTION => 1,
BINARY_IF_OPTION => 2,
ENUM_IF_OPTION => 3,
MASK_IF_OPTION => 3,
IF_OPTION_ZONEONLY => 4 };
my %validoptions = (arp_filter => BINARY_IF_OPTION,
arp_ignore => ENUM_IF_OPTION,
blacklist => SIMPLE_IF_OPTION,
bridge => SIMPLE_IF_OPTION,
detectnets => SIMPLE_IF_OPTION,
dhcp => SIMPLE_IF_OPTION,
maclist => SIMPLE_IF_OPTION,
logmartians => BINARY_IF_OPTION,
norfc1918 => SIMPLE_IF_OPTION,
nosmurfs => SIMPLE_IF_OPTION,
optional => SIMPLE_IF_OPTION,
proxyarp => BINARY_IF_OPTION,
routeback => SIMPLE_IF_OPTION + IF_OPTION_ZONEONLY,
routefilter => BINARY_IF_OPTION,
sourceroute => BINARY_IF_OPTION,
tcpflags => SIMPLE_IF_OPTION,
upnp => SIMPLE_IF_OPTION,
);
my $fn = open_file 'interfaces';
my $first_entry = 1;
my @ifaces;
while ( read_a_line ) {
if ( $first_entry ) {
progress_message2 "$doing $fn...";
$first_entry = 0;
}
my ($zone, $interface, $networks, $options ) = split_line 2, 4, 'interfaces 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';
}
$networks = '' if $networks eq '-';
$options = '' if $options eq '-';
( $interface, my ($port, $extra) ) = split /:/ , $interface, 3;
fatal_error "Invalid INTERFACE" if defined $extra || ! $interface;
fatal_error "Invalid Interface Name ($interface)" if $interface eq '+';
if ( defined $port ) {
require_capability( 'PHYSDEV_MATCH', 'Bridge Ports', '');
require_capability( 'KLUDGEFREE', 'Bridge Ports', '');
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.@%-]+\+?$/;
$interfaces{$port}{bridge} = $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';
$interfaces{$interface}{bridge} = $interface;
}
my $wildcard = 0;
if ( $interface =~ /\+$/ ) {
$wildcard = 1;
$interfaces{$interface}{root} = substr( $interface, 0, -1 );
} else {
$interfaces{$interface}{root} = $interface;
}
unless ( $networks eq '' || $networks eq 'detect' ) {
for my $address ( split /,/, $networks ) {
fatal_error 'Invalid BROADCAST address' unless $address =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
}
warning_message 'Shorewall no longer uses broadcast addresses in rule generation';
}
my $optionsref = {};
my %options;
if ( $options ) {
for my $option (split ',', $options ) {
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";
}
}
}
$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;
push @bridges, $interface;
}
} elsif ( $port ) {
$options{port} = 1;
}
$interfaces{$interface}{options} = $optionsref = \%options;
push @ifaces, $interface;
my @networks;
if ( $options{detectnets} ) {
fatal_error "The 'detectnets' option is not allowed on a multi-zone interface" unless $zone;
fatal_error "The 'detectnets' option may not be used with a wild-card interface name" if $wildcard;
fatal_error "The 'detectnets' option may not be used with the '-e' compiler option" if $export;
@networks = get_routed_networks( $interface, 'detectnets not allowed on interface with default route' );
fatal_error "No routes found through 'detectnets' interface $interface" unless @networks || $options{optional};
delete $options{maclist} unless @networks;
} else {
@networks = @allipv4;
}
add_group_to_zone( $zone, $zoneref->{type}, $interface, \@networks, $optionsref ) if $zone && @networks;
$interfaces{$interface}{zone} = $zone; #Must follow the call to add_group_to_zone()
progress_message " Interface \"$currentline\" Validated";
}
#
# We now assemble the @interfaces array such that bridge ports immediately precede their associated bridge
#
for my $interface ( @ifaces ) {
my $interfaceref = $interfaces{$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};
}
}
#
# 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_interface($)
{
my $interface = $_[0];
return 1 if $interfaces{$interface};
for my $i ( @interfaces ) {
my $interfaceref = $interfaces{$i};
my $val = $interfaceref->{root};
next if $val eq $i;
if ( substr( $interface, 0, length $val ) eq $val ) {
#
# Cache this result for future reference
#
$interfaces{$interface} = { options => $interfaceref->{options}, bridge => $interfaceref->{bridge} };
return 1;
}
}
0;
}
#
# Return the bridge associated with the passed interface. If the interface is not a bridge port,
# return ''
#
sub port_to_bridge( $ ) {
my $portref = $interfaces{$_[0]};
return $portref && $portref->{options}{port} ? $portref->{bridge} : '';
}
#
# Return the bridge associated with the passed interface.
#
sub source_port_to_bridge( $ ) {
my $portref = $interfaces{$_[0]};
return $portref ? $portref->{bridge} : '';
}
#
# Return the 'optional' setting of the passed interface
#
sub interface_is_optional($) {
my $optionsref = $interfaces{$_[0]}{options};
$optionsref && $optionsref->{optional};
}
#
# Returns reference to array of interfaces with the passed option
#
sub find_interfaces_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
#
sub get_interface_option( $$ ) {
my ( $interface, $option ) = @_;
$interfaces{$interface}{options}{$option};
}
1;

View File

@ -1,143 +0,0 @@
#
# Shorewall-perl 4.0 -- /usr/share/shorewall-perl/Shorewall/Macros.pm
#
# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
#
# (c) 2007 - Tom Eastep (teastep@shorewall.net)
#
# Complete documentation is available at http://shorewall.net
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of Version 2 of the GNU General Public License
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
# This module exports some low-level module-oriented functions.
#
package Shorewall::Macros;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::Zones;
use Shorewall::Chains;
use strict;
our @ISA = qw(Exporter);
our @EXPORT = qw( find_macro
split_action
substitute_param
merge_macro_source_dest
merge_macro_column
%macros );
our @EXPORT_OK = qw( initialize );
our $VERSION = 4.00;
our %macros;
#
# Initialize globals -- we take this novel approach to globals initialization to allow
# the compiler to run multiple times in the same process. The
# initialize() function does globals initialization for this
# module and is called from an INIT block below. The function is
# also called by Shorewall::Compiler::compiler at the beginning of
# the second and subsequent calls to that function.
#
sub initialize() {
%macros = ();
}
INIT {
initialize;
}
#
# Try to find a macro file -- RETURNS false if the file doesn't exist or MACRO if it does.
# If the file exists, the macro is entered into the 'targets' table and the fully-qualified
# name of the file is stored in the 'macro' table.
#
sub find_macro( $ )
{
my $macro = $_[0];
my $macrofile = find_file "macro.$macro";
if ( -f $macrofile ) {
$macros{$macro} = $macrofile;
$targets{$macro} = MACRO;
} else {
0;
}
}
#
# Return ( action, level[:tag] ) from passed full action
#
sub split_action ( $ ) {
my $action = $_[0];
my @a = split( /:/ , $action, 4 );
fatal_error "Invalid ACTION ($action)" if ( $action =~ /::/ ) || ( @a > 3 );
( shift @a, join ":", @a );
}
#
# This function substitutes the second argument for the first part of the first argument up to the first colon (":")
#
# Example:
#
# substitute_param DNAT PARAM:info:FTP
#
# produces "DNAT:info:FTP"
#
sub substitute_param( $$ ) {
my ( $param, $action ) = @_;
if ( $action =~ /:/ ) {
my $logpart = (split_action $action)[1];
$logpart =~ s!/$!!;
return "$param:$logpart";
}
$param;
}
#
# Combine fields from a macro body with one from the macro invocation
#
sub merge_macro_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 ) = @_;
if ( defined $invocation && $invocation ne '' && $invocation ne '-' ) {
$invocation;
} else {
$body;
}
}
1;

View File

@ -25,11 +25,9 @@
#
package Shorewall::Nat;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::IPAddrs;
use Shorewall::Zones;
use Shorewall::Interfaces;
use Shorewall::Chains;
use Shorewall::IPAddrs;

View File

@ -24,7 +24,6 @@
#
package Shorewall::Policy;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::Zones;
use Shorewall::Chains;

View File

@ -27,11 +27,9 @@
#
package Shorewall::Proc;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::Zones;
use Shorewall::Chains;
use Shorewall::Interfaces;
use strict;

View File

@ -25,7 +25,6 @@
#
package Shorewall::Providers;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::IPAddrs;
use Shorewall::Zones;

View File

@ -23,9 +23,8 @@
#
package Shorewall::Proxyarp;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::Interfaces;
use Shorewall::Zones;
use strict;

View File

@ -24,15 +24,11 @@
#
package Shorewall::Rules;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::IPAddrs;
use Shorewall::Zones;
use Shorewall::Interfaces;
use Shorewall::Chains;
use Shorewall::Hosts;
use Shorewall::Actions;
use Shorewall::Macros;
use Shorewall::Policy;
use Shorewall::Proc;

View File

@ -29,11 +29,9 @@
#
package Shorewall::Tc;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::Zones;
use Shorewall::Chains;
use Shorewall::Interfaces;
use Shorewall::Providers;
use strict;

View File

@ -24,7 +24,6 @@
#
package Shorewall::Tunnels;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::Zones;
use Shorewall::Chains;

View File

@ -20,12 +20,13 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
# This module contains the code which deals with /etc/shorewall/zones.
# This module contains the code which deals with /etc/shorewall/zones,
# /etc/shorewall/interfaces and /etc/shorewall/hosts.
#
package Shorewall::Zones;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::IPAddrs;
use strict;
@ -39,13 +40,23 @@ our @EXPORT = qw( NOTHING
determine_zones
zone_report
dump_zone_contents
haveipseczones
single_interface
validate_interfaces_file
known_interface
port_to_bridge
source_port_to_bridge
interface_is_optional
find_interfaces_by_option
get_interface_option
validate_hosts_file
find_hosts_by_option
@zones
%zones
$firewall_zone
%interfaces );
%interfaces
@interfaces
@bridges );
our @EXPORT_OK = qw( initialize );
our $VERSION = 4.00;
@ -99,6 +110,24 @@ our %reservedName = ( all => 1,
SOURCE => 1,
DEST => 1 );
#
# Interface Table.
#
# @interfaces lists the interface names in the order that they appear in the interfaces file.
#
# %interfaces { <interface1> => { root => <name without trailing '+'>
# options => { <option1> = <val1> ,
# ...
# }
# zone => <zone name>
# bridge => <bridge>
# }
# }
#
our @interfaces;
our %interfaces;
our @bridges;
#
# Initialize globals -- we take this novel approach to globals initialization to allow
# the compiler to run multiple times in the same process. The
@ -112,6 +141,10 @@ sub initialize() {
@zones = ();
%zones = ();
$firewall_zone = '';
@interfaces = ();
%interfaces = ();
@bridges = ();
}
INIT {
@ -404,4 +437,513 @@ sub single_interface( $ ) {
}
}
sub add_group_to_zone($$$$$)
{
my ($zone, $type, $interface, $networks, $options) = @_;
my $typeref;
my $interfaceref;
my $arrayref;
my $zoneref = $zones{$zone};
my $zonetype = $zoneref->{type};
my $ifacezone = $interfaces{$interface}{zone};
$zoneref->{interfaces}{$interface} = 1;
my @newnetworks;
my @exclusions;
my $new = \@newnetworks;
my $switched = 0;
$ifacezone = '' unless defined $ifacezone;
for my $host ( @$networks ) {
fatal_error "Invalid Host List" unless defined $host and $host ne '';
if ( substr( $host, 0, 1 ) eq '!' ) {
fatal_error "Only one exclusion allowed in a host list" if $switched;
$switched = 1;
$host = substr( $host, 1 );
$new = \@exclusions;
}
unless ( $switched ) {
if ( $type eq $zonetype ) {
fatal_error "Duplicate Host Group ($interface:$host) in zone $zone" if $ifacezone eq $zone;
$ifacezone = $zone if $host eq ALLIPv4;
}
}
if ( substr( $host, 0, 1 ) eq '+' ) {
fatal_error "Invalid ipset name ($host)" unless $host =~ /^\+[a-zA-Z]\w*$/;
} else {
validate_host $host;
}
push @$new, $switched ? "$interface:$host" : $host;
}
$zoneref->{options}{in_out}{routeback} = 1 if $options->{routeback};
$typeref = ( $zoneref->{hosts} || ( $zoneref->{hosts} = {} ) );
$interfaceref = ( $typeref->{$type} || ( $interfaceref = $typeref->{$type} = {} ) );
$arrayref = ( $interfaceref->{$interface} || ( $interfaceref->{$interface} = [] ) );
$zoneref->{options}{complex} = 1 if @$arrayref || ( @newnetworks > 1 ) || ( @exclusions );
push @{$zoneref->{exclusions}}, @exclusions;
push @{$arrayref}, { options => $options,
hosts => \@newnetworks,
ipsec => $type eq 'ipsec4' ? 'ipsec' : 'none' };
}
#
# Return a list of networks routed out of the passed interface
#
sub get_routed_networks ( $$ ) {
my ( $interface , $error_message ) = @_;
my @networks;
if ( open IP , '-|' , "/sbin/ip route show dev $interface 2> /dev/null" ) {
while ( my $route = <IP> ) {
$route =~ s/^\s+//;
my $network = ( split /\s+/, $route )[0];
if ( $network eq 'default' ) {
fatal_error $error_message if $error_message;
warning_message "default route ignored on interface $interface";
} else {
my ( $address, $vlsm ) = split '/', $network;
$vlsm = 32 unless defined $vlsm;
push @networks, "$address/$vlsm";
}
}
close IP
}
@networks;
}
#
# Parse the interfaces file.
#
sub validate_interfaces_file( $ )
{
my $export = shift;
use constant { SIMPLE_IF_OPTION => 1,
BINARY_IF_OPTION => 2,
ENUM_IF_OPTION => 3,
MASK_IF_OPTION => 3,
IF_OPTION_ZONEONLY => 4 };
my %validoptions = (arp_filter => BINARY_IF_OPTION,
arp_ignore => ENUM_IF_OPTION,
blacklist => SIMPLE_IF_OPTION,
bridge => SIMPLE_IF_OPTION,
detectnets => SIMPLE_IF_OPTION,
dhcp => SIMPLE_IF_OPTION,
maclist => SIMPLE_IF_OPTION,
logmartians => BINARY_IF_OPTION,
norfc1918 => SIMPLE_IF_OPTION,
nosmurfs => SIMPLE_IF_OPTION,
optional => SIMPLE_IF_OPTION,
proxyarp => BINARY_IF_OPTION,
routeback => SIMPLE_IF_OPTION + IF_OPTION_ZONEONLY,
routefilter => BINARY_IF_OPTION,
sourceroute => BINARY_IF_OPTION,
tcpflags => SIMPLE_IF_OPTION,
upnp => SIMPLE_IF_OPTION,
);
my $fn = open_file 'interfaces';
my $first_entry = 1;
my @ifaces;
while ( read_a_line ) {
if ( $first_entry ) {
progress_message2 "$doing $fn...";
$first_entry = 0;
}
my ($zone, $interface, $networks, $options ) = split_line 2, 4, 'interfaces 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';
}
$networks = '' if $networks eq '-';
$options = '' if $options eq '-';
( $interface, my ($port, $extra) ) = split /:/ , $interface, 3;
fatal_error "Invalid INTERFACE" if defined $extra || ! $interface;
fatal_error "Invalid Interface Name ($interface)" if $interface eq '+';
if ( defined $port ) {
require_capability( 'PHYSDEV_MATCH', 'Bridge Ports', '');
require_capability( 'KLUDGEFREE', 'Bridge Ports', '');
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.@%-]+\+?$/;
$interfaces{$port}{bridge} = $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';
$interfaces{$interface}{bridge} = $interface;
}
my $wildcard = 0;
if ( $interface =~ /\+$/ ) {
$wildcard = 1;
$interfaces{$interface}{root} = substr( $interface, 0, -1 );
} else {
$interfaces{$interface}{root} = $interface;
}
unless ( $networks eq '' || $networks eq 'detect' ) {
for my $address ( split /,/, $networks ) {
fatal_error 'Invalid BROADCAST address' unless $address =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
}
warning_message 'Shorewall no longer uses broadcast addresses in rule generation';
}
my $optionsref = {};
my %options;
if ( $options ) {
for my $option (split ',', $options ) {
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";
}
}
}
$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;
push @bridges, $interface;
}
} elsif ( $port ) {
$options{port} = 1;
}
$interfaces{$interface}{options} = $optionsref = \%options;
push @ifaces, $interface;
my @networks;
if ( $options{detectnets} ) {
fatal_error "The 'detectnets' option is not allowed on a multi-zone interface" unless $zone;
fatal_error "The 'detectnets' option may not be used with a wild-card interface name" if $wildcard;
fatal_error "The 'detectnets' option may not be used with the '-e' compiler option" if $export;
@networks = get_routed_networks( $interface, 'detectnets not allowed on interface with default route' );
fatal_error "No routes found through 'detectnets' interface $interface" unless @networks || $options{optional};
delete $options{maclist} unless @networks;
} else {
@networks = @allipv4;
}
add_group_to_zone( $zone, $zoneref->{type}, $interface, \@networks, $optionsref ) if $zone && @networks;
$interfaces{$interface}{zone} = $zone; #Must follow the call to add_group_to_zone()
progress_message " Interface \"$currentline\" Validated";
}
#
# We now assemble the @interfaces array such that bridge ports immediately precede their associated bridge
#
for my $interface ( @ifaces ) {
my $interfaceref = $interfaces{$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};
}
}
#
# 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_interface($)
{
my $interface = $_[0];
return 1 if $interfaces{$interface};
for my $i ( @interfaces ) {
my $interfaceref = $interfaces{$i};
my $val = $interfaceref->{root};
next if $val eq $i;
if ( substr( $interface, 0, length $val ) eq $val ) {
#
# Cache this result for future reference
#
$interfaces{$interface} = { options => $interfaceref->{options}, bridge => $interfaceref->{bridge} };
return 1;
}
}
0;
}
#
# Return the bridge associated with the passed interface. If the interface is not a bridge port,
# return ''
#
sub port_to_bridge( $ ) {
my $portref = $interfaces{$_[0]};
return $portref && $portref->{options}{port} ? $portref->{bridge} : '';
}
#
# Return the bridge associated with the passed interface.
#
sub source_port_to_bridge( $ ) {
my $portref = $interfaces{$_[0]};
return $portref ? $portref->{bridge} : '';
}
#
# Return the 'optional' setting of the passed interface
#
sub interface_is_optional($) {
my $optionsref = $interfaces{$_[0]}{options};
$optionsref && $optionsref->{optional};
}
#
# Returns reference to array of interfaces with the passed option
#
sub find_interfaces_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
#
sub get_interface_option( $$ ) {
my ( $interface, $option ) = @_;
$interfaces{$interface}{options}{$option};
}
#
# Validates the hosts file. Generates entries in %zone{..}{hosts}
#
sub validate_hosts_file()
{
my %validoptions = (
blacklist => 1,
maclist => 1,
norfc1918 => 1,
nosmurfs => 1,
routeback => 1,
routefilter => 1,
tcpflags => 1,
);
my $ipsec = 0;
my $first_entry = 1;
my $fn = open_file 'hosts';
while ( read_a_line ) {
if ( $first_entry ) {
progress_message2 "$doing $fn...";
$first_entry = 0;
}
my ($zone, $hosts, $options ) = split_line 2, 3, 'hosts 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';
my $interface;
if ( $hosts =~ /^([\w.@%-]+\+?):(.*)$/ ) {
$interface = $1;
$hosts = $2;
$zoneref->{options}{complex} = 1 if $hosts =~ /^\+/;
fatal_error "Unknown interface ($interface)" unless $interfaces{$interface}{root};
} else {
fatal_error "Invalid HOST(S) column contents: $hosts";
}
if ( $type eq 'bport4' ) {
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};
} elsif ( $zoneref->{bridge} ne $interfaces{$interface}{bridge} ) {
fatal_error "Interface $interface is not a port on bridge $zoneref->{bridge}";
}
}
my $optionsref = {};
if ( $options ne '-' ) {
my @options = split ',', $options;
my %options;
for my $option ( @options )
{
if ( $option eq 'ipsec' ) {
$type = 'ipsec';
$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( '', ALLIPv4 , $hosts ) if substr($hosts, 0, 2 ) eq ',!';
add_group_to_zone( $zone, $type , $interface, [ split( ',', $hosts ) ] , $optionsref);
progress_message " Host \"$currentline\" validated";
}
$capabilities{POLICY_MATCH} = '' unless $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 );
#
sub find_hosts_by_option( $ ) {
my $option = $_[0];
my @hosts;
for my $zone ( grep $zones{$_}{type} ne 'firewall' , @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 ( @interfaces ) {
if ( ! $interfaces{$interface}{zone} && $interfaces{$interface}{options}{$option} ) {
push @hosts, [ $interface, 'none', ALLIPv4 ];
}
}
\@hosts;
}
1;

View File

@ -70,12 +70,9 @@ fi
%attr(0644,root,root) /usr/share/shorewall-perl/Shorewall/Accounting.pm
%attr(0644,root,root) /usr/share/shorewall-perl/Shorewall/Actions.pm
%attr(0644,root,root) /usr/share/shorewall-perl/Shorewall/Chains.pm
%attr(0644,root,root) /usr/share/shorewall-perl/Shorewall/Common.pm
%attr(0644,root,root) /usr/share/shorewall-perl/Shorewall/Compiler.pm
%attr(0644,root,root) /usr/share/shorewall-perl/Shorewall/Config.pm
%attr(0644,root,root) /usr/share/shorewall-perl/Shorewall/FallbackPorts.pm
%attr(0644,root,root) /usr/share/shorewall-perl/Shorewall/Hosts.pm
%attr(0644,root,root) /usr/share/shorewall-perl/Shorewall/Interfaces.pm
%attr(0644,root,root) /usr/share/shorewall-perl/Shorewall/IPAddrs.pm
%attr(0644,root,root) /usr/share/shorewall-perl/Shorewall/Macros.pm
%attr(0644,root,root) /usr/share/shorewall-perl/Shorewall/Nat.pm