2009-02-22 18:30:14 +01:00
#
2009-06-13 16:07:55 +02:00
# Shorewall 4.4 -- /usr/share/shorewall/Shorewall/Zones.pm
2009-02-22 18:30:14 +01:00
#
# This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt]
#
2010-01-01 21:58:27 +01:00
# (c) 2007,2008,2009,2010 - Tom Eastep (teastep@shorewall.net)
2009-02-22 18:30:14 +01:00
#
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# 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::Config qw( :DEFAULT :internal ) ;
use Shorewall::IPAddrs ;
use strict ;
our @ ISA = qw( Exporter ) ;
our @ EXPORT = qw( NOTHING
NUMERIC
NETWORK
IPSECPROTO
IPSECMODE
2009-03-13 23:59:49 +01:00
FIREWALL
IP
BPORT
IPSEC
2009-02-22 18:30:14 +01:00
determine_zones
zone_report
dump_zone_contents
find_zone
firewall_zone
defined_zone
zone_type
zone_interfaces
all_zones
2009-07-16 00:59:53 +02:00
all_parent_zones
2009-02-22 18:30:14 +01:00
complex_zones
non_firewall_zones
single_interface
validate_interfaces_file
all_interfaces
all_bridges
interface_number
find_interface
known_interface
2009-11-06 16:27:44 +01:00
get_physical
2009-11-08 16:00:43 +01:00
physical_name
2009-02-22 18:30:14 +01:00
have_bridges
port_to_bridge
source_port_to_bridge
interface_is_optional
find_interfaces_by_option
get_interface_option
set_interface_option
validate_hosts_file
find_hosts_by_option
2009-03-06 05:08:07 +01:00
all_ipsets
2009-02-22 18:30:14 +01:00
) ;
our @ EXPORT_OK = qw( initialize ) ;
2010-01-01 21:58:27 +01:00
our $ VERSION = '4.4_6' ;
2009-02-22 18:30:14 +01:00
#
# IPSEC Option types
#
use constant { NOTHING = > 'NOTHING' ,
NUMERIC = > '0x[\da-fA-F]+|\d+' ,
NETWORK = > '\d+.\d+.\d+.\d+(\/\d+)?' ,
IPSECPROTO = > 'ah|esp|ipcomp' ,
IPSECMODE = > 'tunnel|transport'
} ;
#
# Zone Table.
#
# @zones contains the ordered list of zones with sub-zones appearing before their parents.
#
2009-03-13 23:59:49 +01:00
# %zones{<zone1> => {type = > <zone type> FIREWALL, IP, IPSEC, BPORT;
2009-02-22 18:30:14 +01:00
# options => { complex => 0|1
# nested => 0|1
2009-07-29 23:35:27 +02:00
# super => 0|1
2009-11-25 20:51:13 +01:00
# in_out => < policy match string >
# in => < policy match string >
# out => < policy match string >
2009-02-22 18:30:14 +01:00
# }
# parents => [ <parents> ] Parents, Children and interfaces are listed by name
# children => [ <children> ]
# interfaces => { <interfaces1> => 1, ... }
# bridge => <bridge>
# hosts { <type> } => [ { <interface1> => { ipsec => 'ipsec'|'none'
# options => { <option1> => <value1>
# ...
# }
# hosts => [ <net1> , <net2> , ... ]
# exclusions => [ <net1>, <net2>, ... ]
# }
# <interface2> => ...
# }
# ]
# }
# <zone2> => ...
# }
#
# $firewall_zone names the firewall zone.
#
our @ zones ;
our % zones ;
our $ firewall_zone ;
our % reservedName = ( all = > 1 ,
2009-07-26 21:29:37 +02:00
any = > 1 ,
2009-02-22 18:30:14 +01:00
none = > 1 ,
SOURCE = > 1 ,
DEST = > 1 ) ;
#
# Interface Table.
#
# @interfaces lists the interface names in the order that they appear in the interfaces file.
#
# %interfaces { <interface1> => { name => <name of interface>
# root => <name without trailing '+'>
2009-11-07 16:19:52 +01:00
# options => { port => undef|1
# <option1> = <val1> , #See %validinterfaceoptions
2009-02-22 18:30:14 +01:00
# ...
# }
# zone => <zone name>
# nets => <number of nets in interface/hosts records referring to this interface>
# bridge => <bridge>
# broadcasts => 'none', 'detect' or [ <addr1>, <addr2>, ... ]
# number => <ordinal position in the interfaces file>
2009-11-06 17:07:13 +01:00
# physical => <physical interface name>
2009-02-22 18:30:14 +01:00
# }
# }
#
our @ interfaces ;
our % interfaces ;
our @ bport_zones ;
2009-03-06 05:08:07 +01:00
our % ipsets ;
2009-11-10 16:24:14 +01:00
our % physical ;
2009-02-22 18:30:14 +01:00
our $ family ;
2009-03-13 23:59:49 +01:00
use constant { FIREWALL = > 1 ,
IP = > 2 ,
BPORT = > 3 ,
2009-11-28 16:19:10 +01:00
IPSEC = > 4 } ;
2009-03-13 23:59:49 +01:00
2009-05-06 00:43:38 +02:00
use constant { SIMPLE_IF_OPTION = > 1 ,
BINARY_IF_OPTION = > 2 ,
ENUM_IF_OPTION = > 3 ,
NUMERIC_IF_OPTION = > 4 ,
OBSOLETE_IF_OPTION = > 5 ,
IPLIST_IF_OPTION = > 6 ,
2009-11-06 16:27:44 +01:00
STRING_IF_OPTION = > 7 ,
2009-05-06 00:43:38 +02:00
MASK_IF_OPTION = > 7 ,
2009-08-20 23:32:15 +02:00
2009-05-06 00:43:38 +02:00
IF_OPTION_ZONEONLY = > 8 ,
IF_OPTION_HOST = > 16 ,
} ;
our % validinterfaceoptions ;
2009-12-19 22:47:12 +01:00
our % defaultinterfaceoptions = ( routefilter = > 1 ) ;
our % maxoptionvalue = ( routefilter = > 2 , mss = > 100000 ) ;
2009-05-06 16:07:38 +02:00
our % validhostoptions ;
2009-05-06 00:43:38 +02:00
2009-02-22 18:30:14 +01:00
#
2009-08-20 23:32:15 +02:00
# Rather than initializing globals in an INIT block or during declaration,
2009-08-16 18:24:51 +02:00
# we initialize them in a function. This is done for two reasons:
#
2009-08-17 19:45:46 +02:00
# 1. Proper initialization depends on the address family which isn't
2009-08-16 18:24:51 +02:00
# known until the compiler has started.
#
# 2. The compiler can run multiple times in the same process so it has to be
2009-08-17 19:45:46 +02:00
# able to re-initialize its dependent modules' state.
2009-02-22 18:30:14 +01:00
#
sub initialize ( $ ) {
$ family = shift ;
@ zones = ( ) ;
% zones = ( ) ;
$ firewall_zone = '' ;
@ interfaces = ( ) ;
% interfaces = ( ) ;
@ bport_zones = ( ) ;
2009-03-06 05:08:07 +01:00
% ipsets = ( ) ;
2009-11-10 16:24:14 +01:00
% physical = ( ) ;
2009-05-06 00:43:38 +02:00
if ( $ family == F_IPV4 ) {
% validinterfaceoptions = ( arp_filter = > BINARY_IF_OPTION ,
arp_ignore = > ENUM_IF_OPTION ,
blacklist = > SIMPLE_IF_OPTION + IF_OPTION_HOST ,
bridge = > SIMPLE_IF_OPTION ,
detectnets = > OBSOLETE_IF_OPTION ,
dhcp = > SIMPLE_IF_OPTION ,
maclist = > SIMPLE_IF_OPTION + IF_OPTION_HOST ,
logmartians = > BINARY_IF_OPTION ,
nets = > IPLIST_IF_OPTION + IF_OPTION_ZONEONLY ,
2009-06-05 19:51:30 +02:00
norfc1918 = > OBSOLETE_IF_OPTION ,
2009-05-06 00:43:38 +02:00
nosmurfs = > SIMPLE_IF_OPTION + IF_OPTION_HOST ,
optional = > SIMPLE_IF_OPTION ,
proxyarp = > BINARY_IF_OPTION ,
routeback = > SIMPLE_IF_OPTION + IF_OPTION_ZONEONLY + IF_OPTION_HOST ,
2009-12-20 01:57:16 +01:00
routefilter = > NUMERIC_IF_OPTION ,
2009-05-06 00:43:38 +02:00
sourceroute = > BINARY_IF_OPTION ,
tcpflags = > SIMPLE_IF_OPTION + IF_OPTION_HOST ,
upnp = > SIMPLE_IF_OPTION ,
2009-06-15 22:34:35 +02:00
upnpclient = > SIMPLE_IF_OPTION ,
2009-05-06 00:43:38 +02:00
mss = > NUMERIC_IF_OPTION ,
2009-11-06 16:27:44 +01:00
physical = > STRING_IF_OPTION + IF_OPTION_HOST ,
2009-05-06 00:43:38 +02:00
) ;
% validhostoptions = (
blacklist = > 1 ,
maclist = > 1 ,
nosmurfs = > 1 ,
routeback = > 1 ,
tcpflags = > 1 ,
broadcast = > 1 ,
destonly = > 1 ,
sourceonly = > 1 ,
) ;
} else {
% validinterfaceoptions = ( blacklist = > SIMPLE_IF_OPTION + IF_OPTION_HOST ,
bridge = > SIMPLE_IF_OPTION ,
dhcp = > SIMPLE_IF_OPTION ,
maclist = > SIMPLE_IF_OPTION + IF_OPTION_HOST ,
nets = > IPLIST_IF_OPTION + IF_OPTION_ZONEONLY ,
nosmurfs = > SIMPLE_IF_OPTION ,
optional = > SIMPLE_IF_OPTION ,
proxyndp = > BINARY_IF_OPTION ,
routeback = > SIMPLE_IF_OPTION + IF_OPTION_ZONEONLY + IF_OPTION_HOST ,
sourceroute = > BINARY_IF_OPTION ,
tcpflags = > SIMPLE_IF_OPTION + IF_OPTION_HOST ,
mss = > NUMERIC_IF_OPTION ,
2009-12-24 16:46:03 +01:00
forward = > BINARY_IF_OPTION ,
2009-11-06 16:27:44 +01:00
physical = > STRING_IF_OPTION + IF_OPTION_HOST ,
2009-05-06 00:43:38 +02:00
) ;
% validhostoptions = (
blacklist = > 1 ,
maclist = > 1 ,
routeback = > 1 ,
tcpflags = > 1 ,
) ;
}
2009-02-22 18:30:14 +01:00
}
#
# Parse the passed option list and return a reference to a hash as follows:
#
# => mss = <MSS setting>
# => ipsec = <-m policy arguments to match options>
#
sub parse_zone_option_list ($$)
{
my % validoptions = ( mss = > NUMERIC ,
strict = > NOTHING ,
next = > NOTHING ,
reqid = > NUMERIC ,
spi = > NUMERIC ,
proto = > IPSECPROTO ,
mode = > IPSECMODE ,
"tunnel-src" = > NETWORK ,
"tunnel-dst" = > NETWORK ,
) ;
#
# Hash of options that have their own key in the returned hash.
#
2009-11-25 20:51:13 +01:00
my % key = ( mss = > 'mss' ) ;
2009-02-22 18:30:14 +01:00
my ( $ list , $ zonetype ) = @ _ ;
my % h ;
my $ options = '' ;
my $ fmt ;
if ( $ list ne '-' ) {
for my $ e ( split_list $ list , 'option' ) {
my $ val = undef ;
my $ invert = '' ;
if ( $ e =~ /([\w-]+)!=(.+)/ ) {
$ val = $ 2 ;
$ e = $ 1 ;
$ invert = '! ' ;
} elsif ( $ e =~ /([\w-]+)=(.+)/ ) {
$ val = $ 2 ;
$ e = $ 1 ;
}
$ fmt = $ validoptions { $ e } ;
fatal_error "Invalid Option ($e)" unless $ fmt ;
if ( $ fmt eq NOTHING ) {
fatal_error "Option \"$e\" does not take a value" if defined $ val ;
} else {
fatal_error "Missing value for option \"$e\"" unless defined $ val ;
fatal_error "Invalid value ($val) for option \"$e\"" unless $ val =~ /^($fmt)$/ ;
}
if ( $ key { $ e } ) {
2009-11-25 20:51:13 +01:00
$ h { $ e } = $ val ;
2009-02-22 18:30:14 +01:00
} else {
2009-03-13 23:59:49 +01:00
fatal_error "The \"$e\" option may only be specified for ipsec zones" unless $ zonetype == IPSEC ;
2009-02-22 18:30:14 +01:00
$ options . = $ invert ;
$ options . = "--$e " ;
$ options . = "$val " if defined $ val ;
}
}
}
$ h { ipsec } = $ options ? "$options " : '' ;
\ % h ;
}
2009-07-30 00:33:47 +02:00
#
# Set the super option on the passed zoneref and propagate to its parents
#
sub set_super ( $ ) ;
sub set_super ( $ ) {
my $ zoneref = shift ;
2009-08-20 23:32:15 +02:00
2009-07-30 00:33:47 +02:00
unless ( $ zoneref - > { options } { super } ) {
$ zoneref - > { options } { super } = 1 ;
set_super ( $ zones { $ _ } ) for @ { $ zoneref - > { parents } } ;
}
}
2009-05-06 00:43:38 +02:00
#
# Process a record in the zones file
#
sub process_zone ( \$ ) {
my $ ip = $ _ [ 0 ] ;
my @ parents ;
my ( $ zone , $ type , $ options , $ in_options , $ out_options ) = split_line 1 , 5 , 'zones file' ;
if ( $ zone =~ /(\w+):([\w,]+)/ ) {
$ zone = $ 1 ;
@ parents = split_list $ 2 , 'zone' ;
for my $ p ( @ parents ) {
fatal_error "Invalid Parent List ($2)" unless $ p ;
fatal_error "Unknown parent zone ($p)" unless $ zones { $ p } ;
fatal_error 'Subzones of firewall zone not allowed' if $ zones { $ p } { type } == FIREWALL ;
push @ { $ zones { $ p } { children } } , $ zone ;
}
}
2009-05-06 01:00:12 +02:00
fatal_error "Invalid zone name ($zone)" unless $ zone =~ /^[a-z]\w*$/i && length $ zone <= $ globals { MAXZONENAMELENGTH } ;
fatal_error "Invalid zone name ($zone)" if $ reservedName { $ zone } || $ zone =~ /^all2|2all$/ ;
2009-05-06 00:43:38 +02:00
fatal_error ( "Duplicate zone name ($zone)" ) if $ zones { $ zone } ;
2009-08-20 23:32:15 +02:00
2009-08-30 17:05:10 +02:00
if ( $ type =~ /^ip(v([46]))?$/i ) {
fatal_error "Invalid zone type ($type)" if $ 1 && $ 2 != $ family ;
2009-05-06 00:43:38 +02:00
$ type = IP ;
$$ ip = 1 ;
} elsif ( $ type =~ /^ipsec([46])?$/i ) {
2009-07-30 00:33:47 +02:00
fatal_error "Invalid zone type ($type)" if $ 1 && $ 1 != $ family ;
2009-05-06 00:43:38 +02:00
$ type = IPSEC ;
} elsif ( $ type =~ /^bport([46])?$/i ) {
2009-07-30 00:33:47 +02:00
fatal_error "Invalid zone type ($type)" if $ 1 && $ 1 != $ family ;
2009-05-06 00:43:38 +02:00
warning_message "Bridge Port zones should have a parent zone" unless @ parents ;
$ type = BPORT ;
push @ bport_zones , $ zone ;
} elsif ( $ type eq 'firewall' ) {
fatal_error 'Firewall zone may not be nested' if @ parents ;
fatal_error "Only one firewall zone may be defined ($zone)" if $ firewall_zone ;
$ firewall_zone = $ zone ;
$ ENV { FW } = $ zone ;
$ type = FIREWALL ;
} elsif ( $ type eq '-' ) {
$ type = IP ;
$$ ip = 1 ;
} else {
fatal_error "Invalid zone type ($type)" ;
}
2009-07-29 16:49:06 +02:00
2009-11-28 16:19:10 +01:00
if ( $ type eq IPSEC ) {
for ( @ parents ) {
unless ( $ zones { $ _ } { type } == IPSEC ) {
set_super ( $ zones { $ _ } ) ;
}
}
}
2009-05-06 00:43:38 +02:00
for ( $ options , $ in_options , $ out_options ) {
$ _ = '' if $ _ eq '-' ;
}
2009-08-20 23:32:15 +02:00
2009-11-25 20:51:13 +01:00
$ zones { $ zone } = { type = > $ type ,
parents = > \ @ parents ,
bridge = > '' ,
options = > { in_out = > parse_zone_option_list ( $ options || '' , $ type ) ,
in = > parse_zone_option_list ( $ in_options || '' , $ type ) ,
out = > parse_zone_option_list ( $ out_options || '' , $ type ) ,
complex = > ( $ type == IPSEC || $ options || $ in_options || $ out_options ? 1 : 0 ) ,
nested = > @ parents > 0 ,
super = > 0 ,
} ,
interfaces = > { } ,
children = > [] ,
hosts = > { }
} ;
2009-08-20 23:32:15 +02:00
2009-05-06 00:43:38 +02:00
return $ zone ;
2009-08-20 23:32:15 +02:00
2009-05-06 00:43:38 +02:00
}
2009-02-22 18:30:14 +01:00
#
# Parse the zones file.
#
sub determine_zones ()
{
my @ z ;
my $ ip = 0 ;
my $ fn = open_file 'zones' ;
first_entry "$doing $fn..." ;
2009-05-06 00:43:38 +02:00
push @ z , process_zone ( $ ip ) while read_a_line ;
2009-02-22 18:30:14 +01:00
fatal_error "No firewall zone defined" unless $ firewall_zone ;
fatal_error "No IP zones defined" unless $ ip ;
2009-05-06 01:00:12 +02:00
#
# Topological sort to place sub-zones before all of their parents
#
2009-02-22 18:30:14 +01:00
my % ordered ;
PUSHED:
{
ZONE:
for my $ zone ( @ z ) {
unless ( $ ordered { $ zone } ) {
for ( @ { $ zones { $ zone } { children } } ) {
next ZONE unless $ ordered { $ _ } ;
}
$ ordered { $ zone } = 1 ;
push @ zones , $ zone ;
redo PUSHED ;
}
}
}
2009-03-09 21:56:53 +01:00
assert ( scalar @ zones == scalar @ z ) ;
2009-02-22 18:30:14 +01:00
}
#
# Return true of we have any ipsec zones
#
sub haveipseczones () {
for my $ zoneref ( values % zones ) {
2009-03-13 23:59:49 +01:00
return 1 if $ zoneref - > { type } == IPSEC ;
2009-02-22 18:30:14 +01:00
}
0 ;
}
#
# Report about zones.
#
sub zone_report ()
{
progress_message2 "Determining Hosts in Zones..." ;
2009-03-13 23:59:49 +01:00
my @ translate ;
if ( $ family == F_IPV4 ) {
2009-11-28 16:19:10 +01:00
@ translate = ( undef , 'firewall' , 'ipv4' , 'bport4' , 'ipsec4' ) ;
2009-08-20 23:32:15 +02:00
} else {
2009-11-28 16:19:10 +01:00
@ translate = ( undef , 'firewall' , 'ipv6' , 'bport6' , 'ipsec6' ) ;
2009-03-13 23:59:49 +01:00
}
2009-02-22 18:30:14 +01:00
for my $ zone ( @ zones )
{
my $ zoneref = $ zones { $ zone } ;
my $ hostref = $ zoneref - > { hosts } ;
my $ type = $ zoneref - > { type } ;
my $ optionref = $ zoneref - > { options } ;
2009-03-13 23:59:49 +01:00
progress_message_nocompress " $zone ($translate[$type])" ;
2009-02-22 18:30:14 +01:00
my $ printed = 0 ;
if ( $ hostref ) {
for my $ type ( sort keys %$ hostref ) {
my $ interfaceref = $ hostref - > { $ type } ;
for my $ interface ( sort keys %$ interfaceref ) {
2009-11-06 17:40:53 +01:00
my $ iref = $ interfaces { $ interface } ;
2009-02-22 18:30:14 +01:00
my $ arrayref = $ interfaceref - > { $ interface } ;
for my $ groupref ( @$ arrayref ) {
my $ hosts = $ groupref - > { hosts } ;
if ( $ hosts ) {
2009-11-06 17:40:53 +01:00
my $ grouplist = join ',' , ( @$ hosts ) ;
my $ exclusions = join ',' , @ { $ groupref - > { exclusions } } ;
2009-02-22 18:30:14 +01:00
$ grouplist = join '!' , ( $ grouplist , $ exclusions ) if $ exclusions ;
2009-11-05 20:04:14 +01:00
2009-02-22 18:30:14 +01:00
if ( $ family == F_IPV4 ) {
2009-11-06 17:40:53 +01:00
progress_message_nocompress " $iref->{physical}:$grouplist" ;
2009-02-22 18:30:14 +01:00
} else {
2009-11-06 17:40:53 +01:00
progress_message_nocompress " $iref->{physical}:<$grouplist>" ;
2009-02-22 18:30:14 +01:00
}
$ printed = 1 ;
}
}
2009-11-25 20:51:13 +01:00
2009-02-22 18:30:14 +01:00
}
}
}
unless ( $ printed ) {
2009-03-13 23:59:49 +01:00
fatal_error "No bridge has been associated with zone $zone" if $ type == BPORT && ! $ zoneref - > { bridge } ;
2009-11-28 16:23:23 +01:00
warning_message "*** $zone is an EMPTY ZONE ***" unless $ type == FIREWALL ;
2009-02-22 18:30:14 +01:00
}
2009-11-28 16:23:23 +01:00
2009-02-22 18:30:14 +01:00
}
}
2009-11-06 17:11:18 +01:00
#
# This function is called to create the contents of the ${VARDIR}/zones file
#
2009-02-22 18:30:14 +01:00
sub dump_zone_contents ()
{
2009-03-13 23:59:49 +01:00
my @ xlate ;
2009-02-22 18:30:14 +01:00
if ( $ family == F_IPV4 ) {
2009-11-28 16:19:10 +01:00
@ xlate = ( undef , 'firewall' , 'ipv4' , 'bport4' , 'ipsec4' ) ;
2009-08-20 23:32:15 +02:00
} else {
2009-11-28 16:19:10 +01:00
@ xlate = ( undef , 'firewall' , 'ipv6' , 'bport6' , 'ipsec6' ) ;
2009-02-22 18:30:14 +01:00
}
for my $ zone ( @ zones )
{
my $ zoneref = $ zones { $ zone } ;
my $ hostref = $ zoneref - > { hosts } ;
my $ type = $ zoneref - > { type } ;
my $ optionref = $ zoneref - > { options } ;
2009-03-13 23:59:49 +01:00
my $ entry = "$zone $xlate[$type]" ;
2009-02-22 18:30:14 +01:00
2009-03-13 23:59:49 +01:00
$ entry . = ":$zoneref->{bridge}" if $ type == BPORT ;
2009-02-22 18:30:14 +01:00
if ( $ hostref ) {
for my $ type ( sort keys %$ hostref ) {
my $ interfaceref = $ hostref - > { $ type } ;
for my $ interface ( sort keys %$ interfaceref ) {
2009-11-06 17:40:53 +01:00
my $ iref = $ interfaces { $ interface } ;
2009-02-22 18:30:14 +01:00
my $ arrayref = $ interfaceref - > { $ interface } ;
for my $ groupref ( @$ arrayref ) {
my $ hosts = $ groupref - > { hosts } ;
if ( $ hosts ) {
2009-11-06 17:40:53 +01:00
my $ grouplist = join ',' , ( @$ hosts ) ;
my $ exclusions = join ',' , @ { $ groupref - > { exclusions } } ;
2009-02-22 18:30:14 +01:00
$ grouplist = join '!' , ( $ grouplist , $ exclusions ) if $ exclusions ;
if ( $ family == F_IPV4 ) {
2009-11-06 17:40:53 +01:00
$ entry . = " $iref->{physical}:$grouplist" ;
2009-02-22 18:30:14 +01:00
} else {
2009-11-06 17:40:53 +01:00
$ entry . = " $iref->{physical}:<$grouplist>" ;
2009-02-22 18:30:14 +01:00
}
}
}
}
}
}
emit_unindented $ entry ;
}
}
#
# If the passed zone is associated with a single interface, the name of the interface is returned. Otherwise, the funtion returns '';
#
sub single_interface ( $ ) {
my $ zone = $ _ [ 0 ] ;
my $ zoneref = $ zones { $ zone } ;
2009-03-09 21:57:18 +01:00
assert ( $ zoneref ) ;
2009-02-22 18:30:14 +01:00
my @ keys = keys ( % { $ zoneref - > { interfaces } } ) ;
@ keys == 1 ? $ keys [ 0 ] : '' ;
}
sub add_group_to_zone ($$$$$)
{
my ( $ zone , $ type , $ interface , $ networks , $ options ) = @ _ ;
my $ hostsref ;
my $ typeref ;
my $ interfaceref ;
my $ zoneref = $ zones { $ zone } ;
my $ zonetype = $ zoneref - > { type } ;
$ zoneref - > { interfaces } { $ interface } = 1 ;
my @ newnetworks ;
my @ exclusions = ( ) ;
my $ new = \ @ newnetworks ;
my $ switched = 0 ;
2009-08-31 18:09:15 +02:00
my $ allip = 0 ;
2009-02-22 18:30:14 +01:00
for my $ host ( @$ networks ) {
$ interfaces { $ interface } { nets } + + ;
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 ) {
2009-03-13 23:59:49 +01:00
if ( $ type == $ zonetype ) {
2009-08-29 16:20:16 +02:00
fatal_error "Duplicate Host Group ($interface:$host) in zone $zone" if $ interfaces { $ interface } { zone } eq $ zone ;
2009-08-31 18:09:15 +02:00
if ( $ host eq ALLIP ) {
fatal_error "Duplicate Host Group ($interface:$host) in zone $zone" if @ newnetworks ;
$ interfaces { $ interface } { zone } = $ zone ;
$ allip = 1 ;
}
2009-02-22 18:30:14 +01:00
}
}
if ( substr ( $ host , 0 , 1 ) eq '+' ) {
fatal_error "Invalid ipset name ($host)" unless $ host =~ /^\+[a-zA-Z]\w*$/ ;
2009-08-20 23:32:15 +02:00
require_capability ( 'IPSET_MATCH' , 'Ipset names in host lists' , '' ) ;
2009-02-22 18:30:14 +01:00
} else {
validate_host $ host , 0 ;
}
push @$ new , $ host ;
}
$ zoneref - > { options } { in_out } { routeback } = 1 if $ options - > { routeback } ;
2009-03-13 23:59:49 +01:00
my $ gtype = $ type == IPSEC ? 'ipsec' : 'ip' ;
2009-02-22 18:30:14 +01:00
$ hostsref = ( $ zoneref - > { hosts } || ( $ zoneref - > { hosts } = { } ) ) ;
2009-03-13 23:59:49 +01:00
$ typeref = ( $ hostsref - > { $ gtype } || ( $ hostsref - > { $ gtype } = { } ) ) ;
2009-02-22 18:30:14 +01:00
$ interfaceref = ( $ typeref - > { $ interface } || ( $ typeref - > { $ interface } = [] ) ) ;
2009-08-31 18:09:15 +02:00
fatal_error "Duplicate Host Group ($interface:" . ALLIP . ") in zone $zone" if $ allip && @$ interfaceref ;
2009-12-29 17:21:52 +01:00
$ zoneref - > { options } { complex } = 1 if @$ interfaceref || ( @ newnetworks > 1 ) || ( @ exclusions ) || $ options - > { routeback } ;
2009-02-22 18:30:14 +01:00
push @ { $ interfaceref } , { options = > $ options ,
hosts = > \ @ newnetworks ,
2009-03-13 23:59:49 +01:00
ipsec = > $ type == IPSEC ? 'ipsec' : 'none' ,
2009-02-22 18:30:14 +01:00
exclusions = > \ @ exclusions } ;
}
#
# Verify that the passed zone name represents a declared zone. Return a
# reference to its zone table entry.
#
sub find_zone ( $ ) {
my $ zone = $ _ [ 0 ] ;
my $ zoneref = $ zones { $ zone } ;
fatal_error "Unknown zone ($zone)" unless $ zoneref ;
$ zoneref ;
}
sub zone_type ( $ ) {
find_zone ( $ _ [ 0 ] ) - > { type } ;
}
sub zone_interfaces ( $ ) {
find_zone ( $ _ [ 0 ] ) - > { interfaces } ;
}
sub defined_zone ( $ ) {
$ zones { $ _ [ 0 ] } ;
}
sub all_zones () {
@ zones ;
}
sub non_firewall_zones () {
2009-03-21 23:46:16 +01:00
grep ( $ zones { $ _ } { type } != FIREWALL , @ zones ) ;
2009-02-22 18:30:14 +01:00
}
2009-07-16 00:59:53 +02:00
sub all_parent_zones () {
grep ( ! @ { $ zones { $ _ } { parents } } , @ zones ) ;
}
2009-02-22 18:30:14 +01:00
sub complex_zones () {
grep ( $ zones { $ _ } { options } { complex } , @ zones ) ;
}
sub firewall_zone () {
$ firewall_zone ;
}
#
2009-05-06 00:43:38 +02:00
# Process a record in the interfaces file
2009-02-22 18:30:14 +01:00
#
2009-05-06 16:07:38 +02:00
sub process_interface ( $ ) {
my $ nextinum = $ _ [ 0 ] ;
2009-11-09 20:15:08 +01:00
my $ netsref = '' ;
2009-11-09 16:27:14 +01:00
my ( $ zone , $ originalinterface , $ bcasts , $ options ) = split_line 2 , 4 , 'interfaces file' ;
2009-05-06 00:43:38 +02:00
my $ zoneref ;
my $ bridge = '' ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
if ( $ zone eq '-' ) {
$ zone = '' ;
2009-02-22 18:30:14 +01:00
} else {
2009-05-06 00:43:38 +02:00
$ zoneref = $ zones { $ zone } ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
fatal_error "Unknown zone ($zone)" unless $ zoneref ;
fatal_error "Firewall zone not allowed in ZONE column of interface record" if $ zoneref - > { type } == FIREWALL ;
}
2009-02-22 18:30:14 +01:00
2009-11-09 16:27:14 +01:00
$ bcasts = '' if $ bcasts eq '-' ;
2009-05-06 00:43:38 +02:00
$ options = '' if $ options eq '-' ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
my ( $ interface , $ port , $ extra ) = split /:/ , $ originalinterface , 3 ;
2009-02-24 00:39:46 +01:00
2009-05-06 00:43:38 +02:00
fatal_error "Invalid INTERFACE ($originalinterface)" if ! $ interface || defined $ extra ;
2009-02-22 18:30:14 +01:00
2009-11-11 19:52:14 +01:00
if ( defined $ port && $ port ne '' ) {
2009-05-06 00:43:38 +02:00
fatal_error qq( "Virtual" interfaces are not supported -- see http://www.shorewall.net/Shorewall_and_Aliased_Interfaces.html ) if $ port =~ /^\d+$/ ;
require_capability ( 'PHYSDEV_MATCH' , 'Bridge Ports' , '' ) ;
fatal_error "Your iptables is not recent enough to support bridge ports" unless $ capabilities { KLUDGEFREE } ;
2009-11-05 20:04:14 +01:00
2009-11-11 19:52:14 +01:00
fatal_error "Invalid Interface Name ($interface:$port)" unless $ port =~ /^[\w.@%-]+\+?$/ ;
2009-11-06 01:34:41 +01:00
fatal_error "Duplicate Interface ($port)" if $ interfaces { $ port } ;
2009-11-05 20:04:14 +01:00
2009-05-06 00:43:38 +02:00
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 } != BPORT ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
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 ;
}
2009-02-22 18:30:14 +01:00
}
2009-05-06 00:43:38 +02:00
$ 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 } == BPORT ;
$ bridge = $ interface ;
}
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
my $ wildcard = 0 ;
my $ root ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
if ( $ interface =~ /\+$/ ) {
$ wildcard = 1 ;
$ root = substr ( $ interface , 0 , - 1 ) ;
} else {
$ root = $ interface ;
}
2009-02-22 18:30:14 +01:00
2009-11-06 16:27:44 +01:00
my $ physical = $ interface ;
2009-05-06 00:43:38 +02:00
my $ broadcasts ;
2009-02-22 18:30:14 +01:00
2009-11-09 16:27:14 +01:00
unless ( $ bcasts eq '' || $ bcasts eq 'detect' ) {
my @ broadcasts = split_list $ bcasts , 'address' ;
2009-08-20 23:32:15 +02:00
2009-05-06 00:43:38 +02:00
for my $ address ( @ broadcasts ) {
fatal_error 'Invalid BROADCAST address' unless $ address =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ ;
2009-02-22 18:30:14 +01:00
}
2009-05-06 00:43:38 +02:00
if ( $ capabilities { ADDRTYPE } ) {
warning_message 'Shorewall no longer uses broadcast addresses in rule generation when Address Type Match is available' ;
2009-02-22 18:30:14 +01:00
} else {
2009-05-06 00:43:38 +02:00
$ broadcasts = \ @ broadcasts ;
2009-02-22 18:30:14 +01:00
}
2009-05-06 00:43:38 +02:00
}
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
my % options ;
2009-02-25 17:56:48 +01:00
2009-05-06 00:43:38 +02:00
$ options { port } = 1 if $ port ;
2009-03-07 21:22:20 +01:00
2009-05-06 00:43:38 +02:00
my $ hostoptionsref = { } ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
if ( $ options ) {
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
my % hostoptions = ( dynamic = > 0 ) ;
2009-08-20 23:32:15 +02:00
2009-05-06 00:43:38 +02:00
for my $ option ( split_list1 $ options , 'option' ) {
next if $ option eq '-' ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
( $ option , my $ value ) = split /=/ , $ option ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
fatal_error "Invalid Interface option ($option)" unless my $ type = $ validinterfaceoptions { $ option } ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
fatal_error "The \"$option\" option may not be specified on a multi-zone interface" if $ type & IF_OPTION_ZONEONLY && ! $ zone ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
my $ hostopt = $ type & IF_OPTION_HOST ;
2009-02-24 02:11:47 +01:00
2009-05-06 00:43:38 +02:00
fatal_error "The \"$option\" option is not allowed on a bridge port" if $ port && ! $ hostopt ;
2009-03-07 21:22:20 +01:00
2009-05-06 00:43:38 +02:00
$ type & = MASK_IF_OPTION ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
if ( $ type == SIMPLE_IF_OPTION ) {
fatal_error "Option $option does not take a value" if defined $ value ;
$ options { $ option } = 1 ;
$ hostoptions { $ option } = 1 if $ hostopt ;
} elsif ( $ type == BINARY_IF_OPTION ) {
$ value = 1 unless defined $ value ;
2009-11-09 20:15:08 +01:00
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 ;
2009-05-06 00:43:38 +02:00
$ options { $ option } = $ value ;
$ hostoptions { $ option } = $ value if $ hostopt ;
} elsif ( $ type == ENUM_IF_OPTION ) {
2009-11-09 20:15:08 +01:00
fatal_error "The '$option' option may not be used with a wild-card interface name" if $ wildcard ;
2009-05-06 00:43:38 +02:00
if ( $ option eq 'arp_ignore' ) {
if ( defined $ value ) {
if ( $ value =~ /^[1-3,8]$/ ) {
$ options { arp_ignore } = $ value ;
2009-02-22 18:30:14 +01:00
} else {
2009-05-06 00:43:38 +02:00
fatal_error "Invalid value ($value) for arp_ignore" ;
2009-02-22 18:30:14 +01:00
}
} else {
2009-05-06 00:43:38 +02:00
$ options { arp_ignore } = 1 ;
2009-02-22 18:30:14 +01:00
}
} else {
2009-05-06 00:43:38 +02:00
assert ( 0 ) ;
2009-02-22 18:30:14 +01:00
}
2009-05-06 00:43:38 +02:00
} elsif ( $ type == NUMERIC_IF_OPTION ) {
2009-12-19 22:47:12 +01:00
$ value = $ defaultinterfaceoptions { $ option } unless defined $ value ;
2009-11-09 20:15:08 +01:00
fatal_error "The '$option' option requires a value" unless defined $ value ;
2009-05-06 00:43:38 +02:00
my $ numval = numeric_value $ value ;
2009-12-19 22:47:12 +01:00
fatal_error "Invalid value ($value) for option $option" unless defined $ numval && $ numval <= $ maxoptionvalue { $ option } ;
2009-05-06 00:43:38 +02:00
$ options { $ option } = $ numval ;
$ hostoptions { $ option } = $ numval if $ hostopt ;
} elsif ( $ type == IPLIST_IF_OPTION ) {
2009-11-09 20:15:08 +01:00
fatal_error "The '$option' option requires a value" unless defined $ value ;
2009-05-06 00:43:38 +02:00
#
# Remove parentheses from address list if present
#
$ value =~ s/\)$// if $ value =~ s/^\(// ;
#
# Add all IP to the front of a list if the list begins with '!'
#
$ value = join ',' , ALLIP , $ value if $ value =~ /^!/ ;
2009-08-20 23:32:15 +02:00
2009-11-07 00:51:53 +01:00
if ( $ option eq 'nets' ) {
fatal_error q( "nets=" may not be specified for a multi-zone interface ) unless $ zone ;
2009-11-09 20:15:08 +01:00
fatal_error "Duplicate $option option" if $ netsref ;
2009-11-07 00:51:53 +01:00
if ( $ value eq 'dynamic' ) {
require_capability ( 'IPSET_MATCH' , 'Dynamic nets' , '' ) ;
$ hostoptions { dynamic } = 1 ;
2009-11-09 20:15:08 +01:00
#
# Defer remaining processing until we have the final physical interface name
#
$ netsref = 'dynamic' ;
2009-11-07 00:51:53 +01:00
} else {
$ hostoptions { multicast } = 1 ;
2009-11-09 20:15:08 +01:00
#
# Convert into a Perl array reference
#
$ netsref = [ split_list $ value , 'address' ] ;
2009-11-07 00:51:53 +01:00
}
#
# Assume 'broadcast'
#
$ hostoptions { broadcast } = 1 ;
2009-08-29 00:17:10 +02:00
} else {
2009-11-07 00:51:53 +01:00
assert ( 0 ) ;
2009-08-20 23:32:15 +02:00
}
2009-11-06 16:27:44 +01:00
} elsif ( $ type == STRING_IF_OPTION ) {
2009-11-09 20:15:08 +01:00
fatal_error "The '$option' option requires a value" unless defined $ value ;
2009-11-07 00:51:53 +01:00
2009-11-08 17:37:03 +01:00
if ( $ option eq 'physical' ) {
2009-11-07 00:51:53 +01:00
fatal_error "Invalid Physical interface name ($value)" unless $ value =~ /^[\w.@%-]+\+?$/ ;
2009-11-10 16:24:14 +01:00
2009-11-11 19:45:01 +01:00
fatal_error "Duplicate physical interface name ($value)" if ( $ physical { $ value } && ! $ port ) ;
2009-11-10 16:24:14 +01:00
2009-11-10 23:12:55 +01:00
fatal_error "The type of 'physical' name ($value) doesn't match the type of interface name ($interface)" if $ wildcard && ! $ value =~ /\+$/ ;
2009-11-07 00:51:53 +01:00
$ physical = $ value ;
} else {
assert ( 0 ) ;
}
2009-05-06 00:43:38 +02:00
} else {
2009-06-13 16:07:55 +02:00
warning_message "Support for the $option interface option has been removed from Shorewall" ;
2009-02-22 18:30:14 +01:00
}
2009-05-06 00:43:38 +02:00
}
2009-02-22 18:30:14 +01:00
2009-11-09 20:15:08 +01:00
if ( $ netsref eq 'dynamic' ) {
my $ ipset = "${zone}_" . chain_base $ physical ;
2009-11-09 21:38:00 +01:00
$ netsref = [ "+$ipset" ] ;
2009-11-09 20:15:08 +01:00
$ ipsets { $ ipset } = 1 ;
}
2009-05-06 00:43:38 +02:00
$ zoneref - > { options } { in_out } { routeback } = 1 if $ zoneref && $ options { routeback } ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
if ( $ options { bridge } ) {
require_capability ( 'PHYSDEV_MATCH' , 'The "bridge" option' , 's' ) ;
fatal_error "Bridges may not have wildcard names" if $ wildcard ;
}
2009-02-25 17:56:48 +01:00
2009-05-06 00:43:38 +02:00
$ hostoptionsref = \ % hostoptions ;
2009-02-25 17:56:48 +01:00
2009-05-06 00:43:38 +02:00
}
2009-02-22 18:30:14 +01:00
2009-11-11 19:45:01 +01:00
$ physical { $ physical } = $ interfaces { $ interface } = { name = > $ interface ,
bridge = > $ bridge ,
nets = > 0 ,
number = > $ nextinum ,
root = > $ root ,
broadcasts = > $ broadcasts ,
options = > \ % options ,
zone = > '' ,
physical = > $ physical
} ;
2009-02-22 18:30:14 +01:00
2009-08-29 00:17:10 +02:00
if ( $ zone ) {
2009-11-09 21:38:00 +01:00
$ netsref || = [ allip ] ;
add_group_to_zone ( $ zone , $ zoneref - > { type } , $ interface , $ netsref , $ hostoptionsref ) ;
2009-09-01 20:11:57 +02:00
add_group_to_zone ( $ zone ,
$ zoneref - > { type } ,
$ interface ,
[ IPv4_MULTICAST ] ,
{ destonly = > 1 } ) if $ hostoptionsref - > { multicast } && $ interfaces { $ interface } { zone } ne $ zone ;
2009-08-29 00:17:10 +02:00
}
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
progress_message " Interface \"$currentline\" Validated" ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
return $ interface ;
}
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
#
# Parse the interfaces file.
#
sub validate_interfaces_file ( $ ) {
my $ export = shift ;
my $ fn = open_file 'interfaces' ;
my @ ifaces ;
2009-05-06 16:07:38 +02:00
my $ nextinum = 1 ;
2009-05-06 00:43:38 +02:00
first_entry "$doing $fn..." ;
2009-05-06 16:07:38 +02:00
push @ ifaces , process_interface ( $ nextinum + + ) while read_a_line ;
2009-02-22 18:30:14 +01:00
#
# 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 } ;
}
#
# Be sure that we have at least one interface
#
fatal_error "No network interfaces defined" unless @ interfaces ;
}
2009-11-06 16:27:44 +01:00
#
# Map the passed name to the corresponding physical name in the passed interface
#
sub map_physical ( $$ ) {
my ( $ name , $ interfaceref ) = @ _ ;
my $ physical = $ interfaceref - > { physical } ;
return $ physical if $ name eq $ interfaceref - > { name } ;
$ physical =~ s/\+$// ;
$ physical . substr ( $ name , length $ interfaceref - > { root } ) ;
}
2009-02-22 18:30:14 +01:00
#
# 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 ] ;
my $ interfaceref = $ interfaces { $ interface } ;
return $ interfaceref if $ interfaceref ;
for my $ i ( @ interfaces ) {
$ interfaceref = $ interfaces { $ i } ;
2009-11-06 22:10:19 +01:00
my $ root = $ interfaceref - > { root } ;
if ( $ i ne $ root && substr ( $ interface , 0 , length $ root ) eq $ root ) {
2009-02-22 18:30:14 +01:00
#
2009-11-06 22:10:19 +01:00
# Cache this result for future reference. We set the 'name' to the name of the entry that appears in /etc/shorewall/interfaces and we do not set the root;
2009-02-22 18:30:14 +01:00
#
2009-11-06 16:27:44 +01:00
return $ interfaces { $ interface } = { options = > $ interfaceref - > { options } ,
bridge = > $ interfaceref - > { bridge } ,
name = > $ i ,
number = > $ interfaceref - > { number } ,
physical = > map_physical ( $ interface , $ interfaceref )
} ;
2009-02-22 18:30:14 +01:00
}
}
0 ;
}
#
# Return interface number
#
sub interface_number ( $ ) {
$ interfaces { $ _ [ 0 ] } { number } || 256 ;
}
#
# Return the interfaces list
#
sub all_interfaces () {
@ interfaces ;
}
#
# Return a list of bridges
#
sub all_bridges () {
grep ( $ interfaces { $ _ } { options } { bridge } , @ interfaces ) ;
}
#
# Return a reference to the interfaces table entry for an interface
#
sub find_interface ( $ ) {
my $ interface = $ _ [ 0 ] ;
my $ interfaceref = $ interfaces { $ interface } ;
fatal_error "Unknown Interface ($interface)" unless $ interfaceref ;
$ interfaceref ;
}
2009-11-06 16:27:44 +01:00
#
# Returns the physical interface associated with the passed logical name
#
sub get_physical ( $ ) {
2009-11-09 16:27:14 +01:00
$ interfaces { $ _ [ 0 ] } - > { physical } ;
2009-11-06 16:27:44 +01:00
}
2009-11-08 16:00:43 +01:00
#
# This one doesn't insist that the passed name be the name of a configured interface
#
sub physical_name ( $ ) {
my $ device = shift ;
my $ devref = known_interface $ device ;
$ devref ? $ devref - > { physical } : $ device ;
}
2009-02-22 18:30:14 +01:00
#
# Returns true if there are bridge port zones defined in the config
#
sub have_bridges () {
@ bport_zones > 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 ) {
2009-11-06 22:10:19 +01:00
my $ interfaceref = $ interfaces { $ interface } ;
next unless $ interfaceref - > { root } ;
my $ optionsref = $ interfaceref - > { options } ;
2009-02-22 18:30:14 +01:00
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 } ;
}
#
# Set an option for an interface
#
sub set_interface_option ( $$$ ) {
my ( $ interface , $ option , $ value ) = @ _ ;
$ interfaces { $ interface } { options } { $ option } = $ value ;
}
#
2009-05-06 00:43:38 +02:00
# Process a record in the hosts file
2009-02-22 18:30:14 +01:00
#
2009-05-06 00:43:38 +02:00
sub process_host ( ) {
2009-02-22 18:30:14 +01:00
my $ ipsec = 0 ;
2009-05-06 00:43:38 +02:00
my ( $ zone , $ hosts , $ options ) = split_line 2 , 3 , 'hosts file' ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
my $ zoneref = $ zones { $ zone } ;
my $ type = $ zoneref - > { type } ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
fatal_error "Unknown ZONE ($zone)" unless $ type ;
fatal_error 'Firewall zone not allowed in ZONE column of hosts record' if $ type == FIREWALL ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
my $ interface ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
if ( $ family == F_IPV4 ) {
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" ;
}
2010-01-08 22:54:31 +01:00
} elsif ( $ hosts =~ /^([\w.@%-]+\+?):<(.*)>\s*$/ || $ hosts =~ /^([\w.@%-]+\+?):\[(.*)\]\s*$/ ) {
$ interface = $ 1 ;
$ hosts = $ 2 ;
$ zoneref - > { options } { complex } = 1 if $ hosts =~ /^\+/ ;
fatal_error "Unknown interface ($interface)" unless $ interfaces { $ interface } { root } ;
2009-05-06 00:43:38 +02:00
} else {
2010-01-08 22:54:31 +01:00
fatal_error "Invalid HOST(S) column contents: $hosts" ;
2009-05-06 00:43:38 +02:00
}
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
if ( $ type == BPORT ) {
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}" ;
}
}
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
my $ optionsref = { dynamic = > 0 } ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
if ( $ options ne '-' ) {
my @ options = split_list $ options , 'option' ;
my % options = ( dynamic = > 0 ) ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
for my $ option ( @ options ) {
if ( $ option eq 'ipsec' ) {
$ type = IPSEC ;
$ zoneref - > { options } { complex } = 1 ;
$ ipsec = 1 ;
2009-06-07 17:07:56 +02:00
} elsif ( $ option eq 'norfc1918' ) {
2009-06-05 19:51:30 +02:00
warning_message "The 'norfc1918' option is no longer supported"
2009-05-06 00:43:38 +02:00
} elsif ( $ validhostoptions { $ option } ) {
$ options { $ option } = 1 ;
2009-02-22 18:30:14 +01:00
} else {
2009-05-06 00:43:38 +02:00
fatal_error "Invalid option ($option)" ;
2009-02-22 18:30:14 +01:00
}
}
2009-05-06 00:43:38 +02:00
$ optionsref = \ % options ;
}
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
#
# 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 ( '' , ALLIP , $ hosts ) if substr ( $ hosts , 0 , 2 ) eq ',!' ;
if ( $ hosts eq 'dynamic' ) {
require_capability ( 'IPSET_MATCH' , 'Dynamic nets' , '' ) ;
2009-11-08 17:37:03 +01:00
my $ physical = physical_name $ interface ;
$ hosts = "+${zone}_${physical}" ;
2009-05-06 00:43:38 +02:00
$ optionsref - > { dynamic } = 1 ;
2009-11-08 17:37:03 +01:00
$ ipsets { "${zone}_${physical}" } = 1 ;
2009-08-20 23:32:15 +02:00
2009-05-06 00:43:38 +02:00
}
2009-08-20 23:32:15 +02:00
2009-05-06 00:43:38 +02:00
add_group_to_zone ( $ zone , $ type , $ interface , [ split_list ( $ hosts , 'host' ) ] , $ optionsref ) ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
progress_message " Host \"$currentline\" validated" ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
return $ ipsec ;
}
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
#
# Validates the hosts file. Generates entries in %zone{..}{hosts}
#
sub validate_hosts_file ()
{
my $ ipsec = 0 ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
my $ fn = open_file 'hosts' ;
2009-03-06 05:08:07 +01:00
2009-10-17 23:23:11 +02:00
first_entry "$doing $fn..." ;
2009-02-22 18:30:14 +01:00
2009-05-06 00:43:38 +02:00
$ ipsec |= process_host while read_a_line ;
2009-02-22 18:30:14 +01:00
$ 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 , exclusions );
#
sub find_hosts_by_option ( $ ) {
my $ option = $ _ [ 0 ] ;
my @ hosts ;
2009-03-13 23:59:49 +01:00
for my $ zone ( grep $ zones { $ _ } { type } != FIREWALL , @ zones ) {
2009-02-22 18:30:14 +01:00
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 , $ host - > { exclusions } ] ;
}
}
}
}
}
}
for my $ interface ( @ interfaces ) {
if ( ! $ interfaces { $ interface } { zone } && $ interfaces { $ interface } { options } { $ option } ) {
push @ hosts , [ $ interface , 'none' , ALLIP , [] ] ;
}
}
\ @ hosts ;
}
2009-03-06 05:08:07 +01:00
sub all_ipsets () {
sort keys % ipsets ;
}
2009-02-22 18:30:14 +01:00
1 ;