More IPv6 Changes

git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@8939 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb
This commit is contained in:
teastep 2008-12-07 20:54:15 +00:00
parent cfa3212b30
commit 692e83c1c8
5 changed files with 269 additions and 49 deletions

View File

@ -70,6 +70,9 @@ our %EXPORT_TAGS = (
OUTPUT_RESTRICT OUTPUT_RESTRICT
POSTROUTE_RESTRICT POSTROUTE_RESTRICT
ALL_RESTRICT ALL_RESTRICT
use_ipv4_chains
use_ipv6_chains
add_command add_command
add_commands add_commands
@ -261,13 +264,13 @@ use constant { NULL_MODE => 0 , # Generating neither shell commands nor iptabl
our $mode; our $mode;
sub use_ipv4() { sub use_ipv4_chains() {
$nat_table = $chain_table{nat}; $nat_table = $chain_table{nat};
$mangle_table = $chain_table{mangle}; $mangle_table = $chain_table{mangle};
$filter_table = $chain_table{filter}; $filter_table = $chain_table{filter};
} }
sub use_ipv6() { sub use_ipv6_chains() {
$nat_table = undef; $nat_table = undef;
$mangle_table = $chain_table{mangle6}; $mangle_table = $chain_table{mangle6};
$filter_table = $chain_table{filter6}; $filter_table = $chain_table{filter6};
@ -294,11 +297,11 @@ sub initialize() {
$chain_table{mangle}{__NAME__} = 'mangle'; $chain_table{mangle}{__NAME__} = 'mangle';
$chain_table{nat}{__NAME__} = 'nat'; $chain_table{nat}{__NAME__} = 'nat';
$chain_table{filter}{__NAME__} = 'filter'; $chain_table{filter}{__NAME__} = 'filter';
$chain_table{raw6}{__NAME__} = 'raw'; $chain_table{raw6}{__NAME__} = 'raw6';
$chain_table{mangle6}{__NAME__} = 'mangle'; $chain_table{mangle6}{__NAME__} = 'mangle6';
$chain_table{filter6}{__NAME__} = 'filter'; $chain_table{filter6}{__NAME__} = 'filter6';
use_ipv4; use_ipv4_chains;
# #
# These get set to 1 as sections are encountered. # These get set to 1 as sections are encountered.
# #

View File

@ -26,17 +26,25 @@
# #
package Shorewall::IPAddrs; package Shorewall::IPAddrs;
require Exporter; require Exporter;
use Socket6;
use Shorewall::Config qw( :DEFAULT split_list require_capability in_hex8); use Shorewall::Config qw( :DEFAULT split_list require_capability in_hex8);
use strict; use strict;
our @ISA = qw(Exporter); our @ISA = qw(Exporter);
our @EXPORT = qw( ALLIPv4 our @EXPORT = qw( ALLIPv4
ALLIPv6
ALLIP
ALL
TCP TCP
UDP UDP
ICMP ICMP
IPv6_ICMP
SCTP SCTP
F_INET
F_INET6
validate_address validate_address
validate_net validate_net
decompose_net decompose_net
@ -45,33 +53,82 @@ our @EXPORT = qw( ALLIPv4
ip_range_explicit ip_range_explicit
expand_port_range expand_port_range
allipv4 allipv4
allipv6
allip
rfc1918_networks rfc1918_networks
resolve_proto resolve_proto
proto_name proto_name
use_ipv4_addrs
use_ipv6_addrs
using_ipv4_addrs
using_ipv6_addrs
validate_port validate_port
validate_portpair validate_portpair
validate_port_list validate_port_list
validate_icmp validate_icmp
); );
our @EXPORT_OK = qw( ); our @EXPORT_OK = qw( );
our $VERSION = 4.1.5; our $VERSION = 4.3.0;
# #
# Some IPv4 useful stuff # Some IPv4/6 useful stuff
# #
our @allipv4 = ( '0.0.0.0/0' ); our @allipv4 = ( '0.0.0.0/0' );
our @allipv6 = ( '::/0' );
our $family;
use constant { ALLIPv4 => '0.0.0.0/0' , ICMP => 1, TCP => 6, UDP => 17 , SCTP => 132 }; use constant { ALLIPv4 => '0.0.0.0/0' ,
ALLIPv6 => '::/0' ,
F_INET => 1,
F_INET6 => 2,
ICMP => 1,
TCP => 6,
UDP => 17,
ICMPv6_ICMP => 58,
SCTP => 132 };
our @rfc1918_networks = ( "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16" ); our @rfc1918_networks = ( "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16" );
sub use_ipv4() {
$family = F_INET;
}
sub using_ipv4() {
$family == F_INET;
}
sub use_ipv6() {
$family = F_INET6;
}
sub using_ipv6() {
$family == F_INET6;
}
#
# 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() {
use_ipv4;
}
INIT {
initialize;
}
sub vlsm_to_mask( $ ) { sub vlsm_to_mask( $ ) {
my $vlsm = $_[0]; my $vlsm = $_[0];
in_hex8 ( ( 0xFFFFFFFF << ( 32 - $vlsm ) ) && 0xFFFFFFFF ); in_hex8 ( ( 0xFFFFFFFF << ( 32 - $vlsm ) ) && 0xFFFFFFFF );
} }
sub valid_address( $ ) { sub valid_4address( $ ) {
my $address = $_[0]; my $address = $_[0];
my @address = split /\./, $address; my @address = split /\./, $address;
@ -83,20 +140,19 @@ sub valid_address( $ ) {
1; 1;
} }
sub validate_address( $$ ) { sub validate_4address( $$ ) {
my ( $addr, $allow_name ) = @_; my ( $addr, $allow_name ) = @_;
my @addrs = ( $addr ); my @addrs = ( $addr );
unless ( valid_address $addr ) { unless ( valid_4address $addr ) {
fatal_error "Invalid IP Address ($addr)" unless $allow_name; fatal_error "Invalid IP Address ($addr)" unless $allow_name;
fatal_error "Unknown Host ($addr)" unless (@addrs = gethostbyname $addr); fatal_error "Unknown Host ($addr)" unless (@addrs = gethostbyname $addr);
if ( defined wantarray ) { if ( defined wantarray ) {
shift @addrs for (1..4); shift @addrs for (1..4);
for ( @addrs ) { for ( @addrs ) {
my (@a) = unpack('C4',$_); $_ = inet_htoa $_;
$_ = join('.', @a );
} }
} }
} }
@ -130,7 +186,7 @@ sub encodeaddr( $ ) {
$result; $result;
} }
sub validate_net( $$ ) { sub validate_4net( $$ ) {
my ($net, $vlsm, $rest) = split( '/', $_[0], 3 ); my ($net, $vlsm, $rest) = split( '/', $_[0], 3 );
my $allow_name = $_[1]; my $allow_name = $_[1];
@ -142,10 +198,10 @@ sub validate_net( $$ ) {
if ( defined $vlsm ) { if ( defined $vlsm ) {
fatal_error "Invalid VLSM ($vlsm)" unless $vlsm =~ /^\d+$/ && $vlsm <= 32; fatal_error "Invalid VLSM ($vlsm)" unless $vlsm =~ /^\d+$/ && $vlsm <= 32;
fatal_error "Invalid Network address ($_[0])" if defined $rest; fatal_error "Invalid Network address ($_[0])" if defined $rest;
fatal_error "Invalid IP address ($net)" unless valid_address $net; fatal_error "Invalid IP address ($net)" unless valid_4address $net;
} else { } else {
fatal_error "Invalid Network address ($_[0])" if $_[0] =~ '/' || ! defined $net; fatal_error "Invalid Network address ($_[0])" if $_[0] =~ '/' || ! defined $net;
validate_address $net, $_[1]; validate_4address $net, $_[1];
$vlsm = 32; $vlsm = 32;
} }
@ -159,11 +215,11 @@ sub validate_net( $$ ) {
} }
} }
sub validate_range( $$ ) { sub validate_4range( $$ ) {
my ( $low, $high ) = @_; my ( $low, $high ) = @_;
validate_address $low, 0; validate_4address $low, 0;
validate_address $high, 0; validate_4address $high, 0;
my $first = decodeaddr $low; my $first = decodeaddr $low;
my $last = decodeaddr $high; my $last = decodeaddr $high;
@ -171,6 +227,16 @@ sub validate_range( $$ ) {
fatal_error "Invalid IP Range ($low-$high)" unless $first <= $last; fatal_error "Invalid IP Range ($low-$high)" unless $first <= $last;
} }
sub validate_4host( $$ ) {
my ( $host, $allow_name ) = $_[0];
if ( $host =~ /^(\d+\.\d+\.\d+\.\d+)-(\d+\.\d+\.\d+\.\d+)$/ ) {
validate_4ange $1, $2;
} else {
validate_4net( $host, $allow_name );
}
}
sub ip_range_explicit( $ ) { sub ip_range_explicit( $ ) {
my $range = $_[0]; my $range = $_[0];
my @result; my @result;
@ -182,7 +248,7 @@ sub ip_range_explicit( $ ) {
push @result, $low; push @result, $low;
if ( defined $high ) { if ( defined $high ) {
validate_address $high, 0; validate_faddress $high, 0;
my $first = decodeaddr $low; my $first = decodeaddr $low;
my $last = decodeaddr $high; my $last = decodeaddr $high;
@ -209,20 +275,14 @@ sub decompose_net( $ ) {
} }
sub validate_host( $$ ) {
my ( $host, $allow_name ) = $_[0];
if ( $host =~ /^(\d+\.\d+\.\d+\.\d+)-(\d+\.\d+\.\d+\.\d+)$/ ) {
validate_range $1, $2;
} else {
validate_net( $host, $allow_name );
}
}
sub allipv4() { sub allipv4() {
@allipv4; @allipv4;
} }
sub allipv6() {
@allipv6;
}
sub rfc1918_networks() { sub rfc1918_networks() {
@rfc1918_networks @rfc1918_networks
} }
@ -231,7 +291,7 @@ sub rfc1918_networks() {
# Protocol/port validation # Protocol/port validation
# #
our %nametoproto = ( all => 0, ALL => 0, icmp => 1, ICMP => 1, tcp => 6, TCP => 6, udp => 17, UDP => 17 ); our %nametoproto = ( all => 0, ALL => 0, icmp => 1, ICMP => 1, tcp => 6, TCP => 6, udp => 17, UDP => 17 );
our @prototoname = ( 'all', 'icmp', '', '', '', '', 'tcp', '', '', '', '', '', '', '', '', '', '', 'udp' ); our @prototoname = ( 'all', 'icmp', '', '', '', '', 'tcp', '', '', '', '', '', '', '', '', '', '', 'udp' );
# #
@ -345,6 +405,8 @@ my %icmp_types = ( any => 'any',
'address-mask-reply' => 18 ); 'address-mask-reply' => 18 );
sub validate_icmp( $ ) { sub validate_icmp( $ ) {
fatal_error "IPv4 ICMP not allowed in an IPv6 Rule" unless $family == F_INET;
my $type = $_[0]; my $type = $_[0];
my $value = $icmp_types{$type}; my $value = $icmp_types{$type};
@ -419,4 +481,159 @@ sub expand_port_range( $$ ) {
} }
} }
sub valid_6address( $ ) {
my $address = $_[0];
my @address = split /:/, $address;
return 0 if @address > 8;
return 0 if @address < 8 && ! $address =~ /::/;
return 0 if $address =~ /:::/ || $address =~ /::.*::/;
if ( $address =~ /^:/ ) {
unless ( $address eq '::' ) {
return 0 if $address =~ /:$/ || $address =~ /^:.*::/;
}
} elsif ( $address =~ /:$/ ) {
return 0 if $address =~ /::.*:$/;
}
for my $a ( @address ) {
return 0 unless $a eq '' || ( $a =~ /^[a-fA-f\d]+$/ && oct "0x$a" < 65536 );
}
1;
}
sub validate_6address( $$ ) {
my ( $addr, $allow_name ) = @_;
my @addrs = ( $addr );
unless ( valid_6address $addr ) {
fatal_error "Invalid IPv6 Address ($addr)" unless $allow_name;
fatal_error "Unknown Host ($addr)" unless (@addrs = gethostbyname2 $addr, AF_INET6);
if ( defined wantarray ) {
shift @addrs for (1..4);
for ( @addrs ) {
$_ = inet_ntop AF_INET6, $_;
}
}
}
defined wantarray ? wantarray ? @addrs : $addrs[0] : undef;
}
sub validate_6net( $$ ) {
my ($net, $vlsm, $rest) = split( '/', $_[0], 3 );
my $allow_name = $_[1];
fatal_error "An ipset name ($net) is not allowed in this context" if substr( $net, 0, 1 ) eq '+';
if ( defined $vlsm ) {
fatal_error "Invalid VLSM ($vlsm)" unless $vlsm =~ /^\d+$/ && $vlsm <= 64;
fatal_error "Invalid Network address ($_[0])" if defined $rest;
fatal_error "Invalid IPv6 address ($net)" unless valid_6address $net;
} else {
fatal_error "Invalid Network address ($_[0])" if $_[0] =~ '/' || ! defined $net;
validate_6address $net, $allow_name;
}
}
sub validate_6range( $$ ) {
my ( $low, $high ) = @_;
validate_6address $low, 0;
validate_6address $high, 0;
my @low = split ":", $low;
my @high = split ":", $high;
if ( @low == @high ) {
my ( $l, $h) = ( pop @low, pop @high );
return 1 if hex "0x$l" <= hex "0x$h" && join( ":", @low ) eq join( ":", @high );
}
fatal_error "Invalid IPv6 Range ($low-$high)";
}
sub validate_6host( $$ ) {
my ( $host, $allow_name ) = $_[0];
if ( $host =~ /^(.*:.*)-(.*:.*)$/ ) {
validate_6range $1, $2;
} else {
validate_6net( $host, $allow_name );
}
}
my %ipv6_icmp_types = ( any => 'any',
'destination-unreachable' => 1,
'no-route' => '1/0',
'communication-prohibited' => '1/1',
'address-unreachable' => '1/2',
'port-unreachable' => '1/3',
'packet-too-big' => 2,
'time-exceeded' => 3,
'ttl-exceeded' => 3,
'ttl-zero-during-transit' => '3/0',
'ttl-zero-during-reassembly' => '3/1',
'parameter-problem' => 4,
'bad-header' => '4/0',
'unknown-header-type' => '4/1',
'unknown-option' => '4/2',
'echo-request' => 128,
'echo-reply' => 129,
'router-solicitation' => 133,
'router-advertisement' => 134,
'neighbour-solicitation' => 135,
'neighbour-advertisement' => 136,
redirect => 137 );
sub validate_icmp6( $ ) {
fatal_error "IPv6 ICMP not allowed in an IPv4 Rule" unless $family == F_INET6;
my $type = $_[0];
my $value = $ipv6_icmp_types{$type};
return $value if defined $value;
if ( $type =~ /^(\d+)(\/(\d+))?$/ ) {
return $type if $1 < 256 && ( ! $2 || $3 < 256 );
}
fatal_error "Invalid IPv6 ICMP Type ($type)"
}
sub ALLIP() {
$family == F_INET ? ALLIPv4 : ALLIPv6;
}
sub allip() {
$family == F_INET ? ALLIPv4 : ALLIPv6;
}
sub valid_address ( $ ) {
$family == F_INET ? valid_4address( $_[0] ) : valid_6address( $_[0] );
}
sub validate_address ( $$ ) {
$family == F_INET ? validate_4address( $_[0], $_[1] ) : validate_6address( $_[0], $_[1] );
}
sub validate_net ( $$ ) {
$family == F_INET ? validate_4net( $_[0], $_[1] ) : validate_6net( $_[0], $_[1] );
}
sub validate_range ($$ ) {
$family == F_INET ? validate_4range( $_[0], $_[1] ) : validate_6range( $_[0], $_[1] );
}
sub validate_host ($$ ) {
$family == F_INET ? validate_4host( $_[0], $_[1] ) : validate_6host( $_[0], $_[1] );
}
1; 1;

View File

@ -419,14 +419,14 @@ sub add_an_rtrule( $$$$ ) {
fatal_error "You must specify either the source or destination in a route_rules entry" if $source eq '-' && $dest eq '-'; fatal_error "You must specify either the source or destination in a route_rules entry" if $source eq '-' && $dest eq '-';
if ( $dest eq '-' ) { if ( $dest eq '-' ) {
$dest = 'to ' . ALLIPv4; $dest = 'to ' . ALLIP;
} else { } else {
validate_net( $dest, 0 ); validate_net( $dest, 0 );
$dest = "to $dest"; $dest = "to $dest";
} }
if ( $source eq '-' ) { if ( $source eq '-' ) {
$source = 'from ' . ALLIPv4; $source = 'from ' . ALLIP;
} elsif ( $source =~ /:/ ) { } elsif ( $source =~ /:/ ) {
( my $interface, $source , my $remainder ) = split( /:/, $source, 3 ); ( my $interface, $source , my $remainder ) = split( /:/, $source, 3 );
fatal_error "Invalid SOURCE" if defined $remainder; fatal_error "Invalid SOURCE" if defined $remainder;

View File

@ -177,7 +177,7 @@ sub setup_ecn()
$interfaces{$interface} = 1; $interfaces{$interface} = 1;
$hosts = ALLIPv4 if $hosts eq '-'; $hosts = ALLIP if $hosts eq '-';
for my $host( split_list $hosts, 'address' ) { for my $host( split_list $hosts, 'address' ) {
validate_host( $host , 1 ); validate_host( $host , 1 );
@ -361,7 +361,7 @@ sub process_criticalhosts() {
fatal_error "Unknown interface ($interface)" unless known_interface $interface; fatal_error "Unknown interface ($interface)" unless known_interface $interface;
$hosts = ALLIPv4 unless $hosts ne '-'; $hosts = ALLIP unless $hosts ne '-';
my @hosts; my @hosts;
@ -402,7 +402,7 @@ sub process_routestopped() {
fatal_error "Unknown interface ($interface)" unless known_interface $interface; fatal_error "Unknown interface ($interface)" unless known_interface $interface;
$hosts = ALLIPv4 unless $hosts && $hosts ne '-'; $hosts = ALLIP unless $hosts && $hosts ne '-';
my @hosts; my @hosts;
@ -1034,7 +1034,7 @@ sub process_rule1 ( $$$$$$$$$$$$$ ) {
$source = $2; $source = $2;
} else { } else {
$sourcezone = $source; $sourcezone = $source;
$source = ALLIPv4; $source = ALLIP;
} }
if ( $dest =~ /^(.*?):(.*)/ ) { if ( $dest =~ /^(.*?):(.*)/ ) {
@ -1048,7 +1048,7 @@ sub process_rule1 ( $$$$$$$$$$$$$ ) {
$destzone = '-'; $destzone = '-';
} else { } else {
$destzone = $dest; $destzone = $dest;
$dest = ALLIPv4; $dest = ALLIP;
} }
fatal_error "Missing source zone" if $sourcezone eq '-' || $sourcezone =~ /^:/; fatal_error "Missing source zone" if $sourcezone eq '-' || $sourcezone =~ /^:/;
@ -1073,7 +1073,7 @@ sub process_rule1 ( $$$$$$$$$$$$$ ) {
# #
# For compatibility with older Shorewall versions # For compatibility with older Shorewall versions
# #
$origdest = ALLIPv4 if $origdest eq 'all'; $origdest = ALLIP if $origdest eq 'all';
# #
# Take care of chain # Take care of chain
@ -1178,14 +1178,14 @@ sub process_rule1 ( $$$$$$$$$$$$$ ) {
$target = '-j REDIRECT '; $target = '-j REDIRECT ';
$target .= "--to-port $serverport " if $serverport; $target .= "--to-port $serverport " if $serverport;
if ( $origdest eq '' || $origdest eq '-' ) { if ( $origdest eq '' || $origdest eq '-' ) {
$origdest = ALLIPv4; $origdest = ALLIP;
} elsif ( $origdest eq 'detect' ) { } elsif ( $origdest eq 'detect' ) {
if ( $config{DETECT_DNAT_IPADDRS} && $sourcezone ne firewall_zone ) { if ( $config{DETECT_DNAT_IPADDRS} && $sourcezone ne firewall_zone ) {
my $interfacesref = $sourceref->{interfaces}; my $interfacesref = $sourceref->{interfaces};
my @interfaces = keys %$interfacesref; my @interfaces = keys %$interfacesref;
$origdest = @interfaces ? "detect:@interfaces" : ALLIPv4; $origdest = @interfaces ? "detect:@interfaces" : ALLIPv4;
} else { } else {
$origdest = ALLIPv4; $origdest = ALLIP;
} }
} }
} else { } else {
@ -1218,9 +1218,9 @@ sub process_rule1 ( $$$$$$$$$$$$$ ) {
if ( $config{DETECT_DNAT_IPADDRS} && $sourcezone ne firewall_zone ) { if ( $config{DETECT_DNAT_IPADDRS} && $sourcezone ne firewall_zone ) {
my $interfacesref = $sourceref->{interfaces}; my $interfacesref = $sourceref->{interfaces};
my @interfaces = keys %$interfacesref; my @interfaces = keys %$interfacesref;
$origdest = @interfaces ? "detect:@interfaces" : ALLIPv4; $origdest = @interfaces ? "detect:@interfaces" : ALLIP;
} else { } else {
$origdest = ALLIPv4; $origdest = ALLIP;
} }
} }
} }
@ -1265,7 +1265,7 @@ sub process_rule1 ( $$$$$$$$$$$$$ ) {
if ( $origdest eq 'detect' ) { if ( $origdest eq 'detect' ) {
my $interfacesref = $sourceref->{interfaces}; my $interfacesref = $sourceref->{interfaces};
my $interfaces = "@$interfacesref"; my $interfaces = "@$interfacesref";
$origdest = $interfaces ? "detect:$interfaces" : ALLIPv4; $origdest = $interfaces ? "detect:$interfaces" : ALLIP;
} }
expand_rule( ensure_chain ('nat' , $sourceref->{type} eq 'firewall' ? 'OUTPUT' : dnat_chain $sourcezone) , expand_rule( ensure_chain ('nat' , $sourceref->{type} eq 'firewall' ? 'OUTPUT' : dnat_chain $sourcezone) ,

View File

@ -481,7 +481,7 @@ sub add_group_to_zone($$$$$)
unless ( $switched ) { unless ( $switched ) {
if ( $type eq $zonetype ) { if ( $type eq $zonetype ) {
fatal_error "Duplicate Host Group ($interface:$host) in zone $zone" if $ifacezone eq $zone; fatal_error "Duplicate Host Group ($interface:$host) in zone $zone" if $ifacezone eq $zone;
$ifacezone = $zone if $host eq ALLIPv4; $ifacezone = $zone if $host eq ALLIP;
} }
} }
@ -746,7 +746,7 @@ sub validate_interfaces_file( $ )
push @ifaces, $interface; push @ifaces, $interface;
my @networks = allipv4; my @networks = allip;
add_group_to_zone( $zone, $zoneref->{type}, $interface, \@networks, $optionsref ) if $zone; add_group_to_zone( $zone, $zoneref->{type}, $interface, \@networks, $optionsref ) if $zone;
@ -993,7 +993,7 @@ sub validate_hosts_file()
# #
# Take care of case where the hosts list begins with '!' # Take care of case where the hosts list begins with '!'
# #
$hosts = join( '', ALLIPv4 , $hosts ) if substr($hosts, 0, 2 ) eq ',!'; $hosts = join( '', ALLIP , $hosts ) if substr($hosts, 0, 2 ) eq ',!';
add_group_to_zone( $zone, $type , $interface, [ split_list( $hosts, 'host' ) ] , $optionsref); add_group_to_zone( $zone, $type , $interface, [ split_list( $hosts, 'host' ) ] , $optionsref);
@ -1027,7 +1027,7 @@ sub find_hosts_by_option( $ ) {
for my $interface ( @interfaces ) { for my $interface ( @interfaces ) {
if ( ! $interfaces{$interface}{zone4} && $interfaces{$interface}{options}{$option} ) { if ( ! $interfaces{$interface}{zone4} && $interfaces{$interface}{options}{$option} ) {
push @hosts, [ $interface, 'none', ALLIPv4 ]; push @hosts, [ $interface, 'none', ALLIP ];
} }
} }