forked from extern/shorewall_code
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:
parent
d1d664ed97
commit
bddf520436
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
@ -24,7 +24,6 @@
|
||||
#
|
||||
package Shorewall::IPAddrs;
|
||||
require Exporter;
|
||||
use Shorewall::Common;
|
||||
use Shorewall::Config;
|
||||
|
||||
use strict;
|
||||
|
@ -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;
|
@ -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;
|
@ -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;
|
||||
|
||||
|
@ -24,7 +24,6 @@
|
||||
#
|
||||
package Shorewall::Policy;
|
||||
require Exporter;
|
||||
use Shorewall::Common;
|
||||
use Shorewall::Config;
|
||||
use Shorewall::Zones;
|
||||
use Shorewall::Chains;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -25,7 +25,6 @@
|
||||
#
|
||||
package Shorewall::Providers;
|
||||
require Exporter;
|
||||
use Shorewall::Common;
|
||||
use Shorewall::Config;
|
||||
use Shorewall::IPAddrs;
|
||||
use Shorewall::Zones;
|
||||
|
@ -23,9 +23,8 @@
|
||||
#
|
||||
package Shorewall::Proxyarp;
|
||||
require Exporter;
|
||||
use Shorewall::Common;
|
||||
use Shorewall::Config;
|
||||
use Shorewall::Interfaces;
|
||||
use Shorewall::Zones;
|
||||
|
||||
use strict;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -24,7 +24,6 @@
|
||||
#
|
||||
package Shorewall::Tunnels;
|
||||
require Exporter;
|
||||
use Shorewall::Common;
|
||||
use Shorewall::Config;
|
||||
use Shorewall::Zones;
|
||||
use Shorewall::Chains;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user