mirror of
https://gitlab.com/shorewall/code.git
synced 2024-11-23 16:13:18 +01:00
Initial basic filter implementation.
Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
parent
44e0d48fc5
commit
9c4089fc99
@ -58,6 +58,7 @@ our @EXPORT = ( qw( ALLIPv4
|
|||||||
validate_address
|
validate_address
|
||||||
validate_net
|
validate_net
|
||||||
decompose_net
|
decompose_net
|
||||||
|
decompose_net_u32
|
||||||
compare_nets
|
compare_nets
|
||||||
validate_host
|
validate_host
|
||||||
validate_range
|
validate_range
|
||||||
@ -303,6 +304,48 @@ sub decompose_net( $ ) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# This function returns an array of (mask, address) pairs. For IPv4, only one
|
||||||
|
# pair is returned. For IPv6, one pair is returned for every 32 bits of the VLSM.
|
||||||
|
#
|
||||||
|
# For example, a /64 network will return two pairs; a /80 would return 3.
|
||||||
|
#
|
||||||
|
sub decompose_net_u32( $ ) {
|
||||||
|
my ( $net, $vlsm ) = decompose_net( $_[0] );
|
||||||
|
|
||||||
|
assert( wantarray );
|
||||||
|
|
||||||
|
if ( $family == F_IPV4 ) {
|
||||||
|
$vlsm = ( 0xffffffff << ( $vlsm-32 ) ) & 0xffffffff;
|
||||||
|
return ( in_hex8( $vlsm ) , in_hex8( $net ) );
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# Split the address into 16-bit hex numbers
|
||||||
|
#
|
||||||
|
my @addr = split( ':', $net );
|
||||||
|
#
|
||||||
|
# Replace each element by its numeric value
|
||||||
|
#
|
||||||
|
no warnings;
|
||||||
|
$_ = oct( '0x' . $_ ) for @addr;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
my @result;
|
||||||
|
|
||||||
|
while ( $vlsm >= 32 ) {
|
||||||
|
push @result, 0xffffff;
|
||||||
|
push @result, ( shift( @addr ) << 16 ) | shift( @addr );
|
||||||
|
$vlsm -= 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $vlsm ) {
|
||||||
|
push @result, ( ( 0xffffffff << ( $vlsm-32 ) ) & 0xffffffff );
|
||||||
|
push @result, ( ( $addr[0] << 16) | $addr[1] );
|
||||||
|
}
|
||||||
|
|
||||||
|
@result;
|
||||||
|
}
|
||||||
|
|
||||||
sub compare_nets( $$ ) {
|
sub compare_nets( $$ ) {
|
||||||
my ( @net1, @net2 );
|
my ( @net1, @net2 );
|
||||||
|
|
||||||
|
@ -2188,6 +2188,300 @@ sub process_tc_filter1( $$$$$$$$$ ) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Handle an ipset name in the SOURCE or DEST columns of a filter
|
||||||
|
#
|
||||||
|
sub handle_ematch( $$ ) {
|
||||||
|
my ( $setname, $option ) = @_;
|
||||||
|
|
||||||
|
my $options = $option;
|
||||||
|
|
||||||
|
require_capability 'BASIC_EMATCH', 'IPSets', '';
|
||||||
|
|
||||||
|
if ( $setname =~ /^(.*)\[([1-6])\]$/ ) {
|
||||||
|
$setname = $1;
|
||||||
|
my $count = $2;
|
||||||
|
|
||||||
|
$options .= ",$option" while --$count > 0;
|
||||||
|
} elsif ( $setname =~ /^(.*)\[((?:src|dst)(?:,(?:src|dst))){0,5}\]$/ ) {
|
||||||
|
$setname = $1;
|
||||||
|
$options = $2 if supplied $2;
|
||||||
|
|
||||||
|
my @options = split /,/, $options;
|
||||||
|
|
||||||
|
if ( $config{IPSET_WARNINGS} ) {
|
||||||
|
my %typemap = ( src => 'Source', dst => 'Destination' );
|
||||||
|
warning_message( "The '$options[0]' ipset flag is used in a $typemap{$option} column" ), unless $options[0] eq $option;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$setname =~ s/\+//;
|
||||||
|
|
||||||
|
return "\\\n ipset\\($setname $options\\)";
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Process a TC filter and generate a 'basic' filter -- allows ipsets.
|
||||||
|
#
|
||||||
|
sub process_tc_filter2( $$$$$$$$$ ) {
|
||||||
|
|
||||||
|
my ( $devclass, $source, $dest , $proto, $portlist , $sportlist, $tos, $length, $priority ) = @_;
|
||||||
|
|
||||||
|
my ($device, $class, $rest ) = split /:/, $devclass, 3;
|
||||||
|
|
||||||
|
our $lastdevice;
|
||||||
|
|
||||||
|
fatal_error "Invalid INTERFACE:CLASS ($devclass)" if defined $rest || ! ($device && $class );
|
||||||
|
|
||||||
|
my ( $ip, $ip32, $lo ) = $family == F_IPV4 ? ('ip', 'ip', 2 ) : ('ipv6', 'ip6', 4 );
|
||||||
|
|
||||||
|
my $devref;
|
||||||
|
|
||||||
|
if ( $device =~ /^[\da-fA-F]+$/ && ! $tcdevices{$device} ) {
|
||||||
|
( $device, $devref ) = dev_by_number( hex_value( $device ) );
|
||||||
|
} else {
|
||||||
|
( $device , $devref ) = dev_by_number( $device );
|
||||||
|
}
|
||||||
|
|
||||||
|
my ( $prio, $filterpri ) = ( undef, $devref->{filterpri} );
|
||||||
|
|
||||||
|
if ( $priority eq '-' ) {
|
||||||
|
$prio = ++$filterpri;
|
||||||
|
fatal_error "Filter priority overflow" if $prio > 65535;
|
||||||
|
} else {
|
||||||
|
$prio = validate_filter_priority( $priority, 'filter' );
|
||||||
|
$filterpri = $prio if $prio > $filterpri;
|
||||||
|
}
|
||||||
|
|
||||||
|
$devref->{filterpri} = $filterpri;
|
||||||
|
|
||||||
|
my $devnum = in_hexp $devref->{number};
|
||||||
|
|
||||||
|
my $tcref = $tcclasses{$device};
|
||||||
|
|
||||||
|
my $filtersref = $devref->{filters};
|
||||||
|
|
||||||
|
fatal_error "No Classes were defined for INTERFACE $device" unless $tcref;
|
||||||
|
|
||||||
|
my $classnum = hex_value $class;
|
||||||
|
|
||||||
|
fatal_error "Invalid CLASS ($class)" unless defined $classnum;
|
||||||
|
|
||||||
|
$tcref = $tcref->{$classnum};
|
||||||
|
|
||||||
|
fatal_error "Unknown CLASS ($devclass)" unless $tcref && $tcref->{occurs};
|
||||||
|
fatal_error "Filters may not specify an occurring CLASS" if $tcref->{occurs} > 1;
|
||||||
|
|
||||||
|
unless ( $tcref->{leaf} ) {
|
||||||
|
warning_message "Filter specifying a non-leaf CLASS ($devnum:$class) ignored";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $have_rule = 0;
|
||||||
|
|
||||||
|
my $rule = "filter add dev $devref->{physical} protocol $ip parent $devnum:0 prio $prio basic match";
|
||||||
|
|
||||||
|
if ( $tos ne '-' ) {
|
||||||
|
my $tosval = $tosoptions{$tos};
|
||||||
|
my $mask;
|
||||||
|
|
||||||
|
$tosval = $tos unless $tosval;
|
||||||
|
|
||||||
|
if ( $tosval =~ /^0x[0-9a-f]{2}$/ ) {
|
||||||
|
$mask = '0xfc';
|
||||||
|
} elsif ( $tosval =~ /^(0x[0-9a-f]{2})\/(0x[0-9a-f]{2})$/ ) {
|
||||||
|
$tosval = $1;
|
||||||
|
$mask = $2;
|
||||||
|
} else {
|
||||||
|
fatal_error "Invalid TOS ($tos)";
|
||||||
|
}
|
||||||
|
|
||||||
|
$rule .= ' and' if $have_rule;
|
||||||
|
$rule .= "\\\n cmp\\( u16 at 1 mask $mask eq $tosval \\)";
|
||||||
|
|
||||||
|
$have_rule = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $length ne '-' ) {
|
||||||
|
my $len = numeric_value( $length ) || 0;
|
||||||
|
my $mask = $validlengths{$len};
|
||||||
|
fatal_error "Invalid LENGTH ($length)" unless $mask;
|
||||||
|
$rule .= ' and' if $have_rule;
|
||||||
|
$rule .="\\\n cmp\\(u16 at $lo mask $mask eq $len\\)";
|
||||||
|
$have_rule = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $protonumber = 0;
|
||||||
|
|
||||||
|
unless ( $proto eq '-' ) {
|
||||||
|
$protonumber = resolve_proto $proto;
|
||||||
|
fatal_error "Unknown PROTO ($proto)" unless defined $protonumber;
|
||||||
|
if ( $protonumber ) {
|
||||||
|
$rule .= 'and ' if $have_rule;
|
||||||
|
$rule .= "\\\n match cmp\\( u8 at 6 mask 0xff eq $protonumber \\)";
|
||||||
|
$have_rule = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $portlist ne '-' || $sportlist ne '-' ) {
|
||||||
|
fatal_error "Ports may not be specified without a PROTO" unless $protonumber;
|
||||||
|
|
||||||
|
$rule .= ' and';
|
||||||
|
|
||||||
|
if ( $portlist eq '-' ) {
|
||||||
|
fatal_error "Only TCP, UDP and SCTP may specify SOURCE PORT"
|
||||||
|
unless $protonumber == TCP || $protonumber == UDP || $protonumber == SCTP;
|
||||||
|
|
||||||
|
my @sportlist;
|
||||||
|
my $multiple;
|
||||||
|
|
||||||
|
push @sportlist, expand_port_range( $protonumber, $_ ) for split_list( $sportlist, 'port list' );
|
||||||
|
|
||||||
|
$rule .= "\\\n (" if $multiple = ( @sportlist > 1 );
|
||||||
|
|
||||||
|
while ( @sportlist ) {
|
||||||
|
my ( $sport, $smask ) = ( shift @sportlist, shift @sportlist );
|
||||||
|
$rule .= "\\\n cmp( u16 at 0 layer 2 mask $smask eq $sport \\)";
|
||||||
|
$rule .= ' or' if @sportlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rule .= "\\\n \\)" if $multiple;
|
||||||
|
} else {
|
||||||
|
fatal_error "Only TCP, UDP, SCTP and ICMP may specify DEST PORT"
|
||||||
|
unless $protonumber == TCP || $protonumber == UDP || $protonumber == SCTP || $protonumber == ICMP;
|
||||||
|
|
||||||
|
if ( $protonumber == ICMP ) {
|
||||||
|
fatal_error "ICMP not allowed with IPv6" unless $family == F_IPV4;
|
||||||
|
fatal_error "SOURCE PORT(S) are not allowed with ICMP" if $sportlist ne '-';
|
||||||
|
|
||||||
|
my @typelist = split_list( $portlist, 'icmp type' );
|
||||||
|
|
||||||
|
$rule .= "\\\n (" if @typelist > 1;
|
||||||
|
|
||||||
|
for my $type ( @typelist ) {
|
||||||
|
my ( $icmptype , $icmpcode ) = split '/', validate_icmp( $type );
|
||||||
|
|
||||||
|
$rule .= "\\\n cmp( u16 at 0 layer 2 mask 0xffff eq " . in_hex4( ( $icmptype << 8 ) | $icmpcode );
|
||||||
|
$rule .= ' or' if @typelist > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rule .= "\\\n(" if @typelist > 1;
|
||||||
|
|
||||||
|
} elsif ( $protonumber == IPv6_ICMP ) {
|
||||||
|
fatal_error "IPv6 ICMP not allowed with IPv4" unless $family == F_IPV4;
|
||||||
|
fatal_error "SOURCE PORT(S) are not allowed with IPv6 ICMP" if $sportlist ne '-';
|
||||||
|
|
||||||
|
my @typelist = split_list( $portlist, 'icmp type' );
|
||||||
|
|
||||||
|
$rule .= "\\\n (" if @typelist > 1;
|
||||||
|
|
||||||
|
for my $type ( @typelist ) {
|
||||||
|
|
||||||
|
my ( $icmptype , $icmpcode ) = split '/', validate_icmp6( $type );
|
||||||
|
|
||||||
|
$rule .= "\\\n cmp( u16 at 0 layer 2 mask 0xffff eq " . in_hex4( ( $icmptype << 8 ) | $icmpcode );
|
||||||
|
$rule .= ' or' if @typelist > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rule .= "\\\n(" if @typelist > 1;
|
||||||
|
} else {
|
||||||
|
my @portlist;
|
||||||
|
my $multiple;
|
||||||
|
|
||||||
|
push @portlist, expand_port_range( $protonumber, $_ ) for split_list( $portlist, 'port list' );
|
||||||
|
|
||||||
|
$rule .= "\\\n (" if $multiple = ( @portlist > 1 );
|
||||||
|
|
||||||
|
while ( @portlist ) {
|
||||||
|
my ( $port, $mask ) = ( shift @portlist, shift @portlist );
|
||||||
|
$rule .= "\\\n cmp( u16 at 2 layer 2 mask $mask eq $port \\)";
|
||||||
|
$rule .= ' or' if @portlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rule .= "\\\n \\)" if $multiple;
|
||||||
|
$rule .= ' and';
|
||||||
|
|
||||||
|
push @portlist, expand_port_range( $protonumber, $_ ) for split_list( $sportlist, 'port list' );
|
||||||
|
|
||||||
|
$rule .= "\\\n (" if $multiple = ( @portlist > 1 );
|
||||||
|
|
||||||
|
while ( @portlist ) {
|
||||||
|
my ( $sport, $smask ) = ( shift @portlist, shift @portlist );
|
||||||
|
$rule .= "\\\n cmp( u16 at 0 layer 2 mask $smask eq $sport \\)";
|
||||||
|
$rule .= ' or' if @portlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rule .= "\\\n \\)" if $multiple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $source ne '-' ) {
|
||||||
|
$rule .= ' and' if $have_rule;
|
||||||
|
|
||||||
|
if ( $source =~ /^\+/ ) {
|
||||||
|
$rule = join( ' ', "\\\n ", handle_ematch( $source, 'src' ) );
|
||||||
|
} else {
|
||||||
|
my @parts = decompose_net_u32( $source );
|
||||||
|
|
||||||
|
if ( $family == F_IPV4 ) {
|
||||||
|
$rule .= join( ' ', "\\\n cmp\\( u32 at 12 mask ", in_hex8( $parts[0] ), 'eq' , in_hex8( $parts[1] ), "\\)" );
|
||||||
|
} else {
|
||||||
|
my $offset = 8;
|
||||||
|
|
||||||
|
while ( @parts ) {
|
||||||
|
$rule .= join( ' ', "\\\n cmp\\( u32 at $offset mask ", in_hex8( shift @parts ) , 'eq' , in_hex8( shift @parts ), "\\)" );
|
||||||
|
$offset += 4;
|
||||||
|
$rule .= ' and' if @parts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$have_rule = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $dest ne '-' ) {
|
||||||
|
$rule .= ' and' if $have_rule;
|
||||||
|
|
||||||
|
if ( $dest =~ /^\+/ ) {
|
||||||
|
$rule .= join( ' ', "\\\n ", handle_ematch( $dest, 'dst' ) );
|
||||||
|
} else {
|
||||||
|
$rule .= 'and ' if $have_rule;
|
||||||
|
|
||||||
|
my @parts = decompose_net_u32( $dest );
|
||||||
|
|
||||||
|
if ( $family == F_IPV4 ) {
|
||||||
|
$rule .= join( ' ', "\\\n cmp\\( u32 at 12 mask ", in_hex8( $parts[0] ), 'eq' , in_hex8( $parts[1] ), "\\)" );
|
||||||
|
} else {
|
||||||
|
my $offset = 8;
|
||||||
|
|
||||||
|
while ( @parts ) {
|
||||||
|
$rule .= join( ' ', "\\\n cmp\\( u32 at $offset", in_hex8( shift @parts ), 'eq' , in_hex8( shift @parts ), "\\)" );
|
||||||
|
$offset += 4;
|
||||||
|
$rule .= ' and' if @parts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$have_rule = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $have_rule ) {
|
||||||
|
push @$filtersref, ( "\nrun_tc $rule\\" ,
|
||||||
|
" flowid $devnum:$class" );
|
||||||
|
|
||||||
|
emit '';
|
||||||
|
|
||||||
|
if ( $family == F_IPV4 ) {
|
||||||
|
progress_message " IPv4 TC Filter \"$currentline\" $done";
|
||||||
|
} else {
|
||||||
|
progress_message " IPv6 TC Filter \"$currentline\" $done";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warning_message "Degenerate filter ignored";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub process_tc_filter() {
|
sub process_tc_filter() {
|
||||||
|
|
||||||
my ( $devclass, $source, $dest , $protos, $portlist , $sportlist, $tos, $length, $priority )
|
my ( $devclass, $source, $dest , $protos, $portlist , $sportlist, $tos, $length, $priority )
|
||||||
@ -2196,10 +2490,16 @@ sub process_tc_filter() {
|
|||||||
|
|
||||||
fatal_error 'CLASS must be specified' if $devclass eq '-';
|
fatal_error 'CLASS must be specified' if $devclass eq '-';
|
||||||
|
|
||||||
|
if ( have_capability 'BASIC_EMATCH' ) {
|
||||||
|
for my $proto ( split_list $protos, 'Protocol' ) {
|
||||||
|
process_tc_filter2( $devclass, $source, $dest , $proto, $portlist , $sportlist, $tos, $length, $priority );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for my $proto ( split_list $protos, 'Protocol' ) {
|
for my $proto ( split_list $protos, 'Protocol' ) {
|
||||||
process_tc_filter1( $devclass, $source, $dest , $proto, $portlist , $sportlist, $tos, $length, $priority );
|
process_tc_filter1( $devclass, $source, $dest , $proto, $portlist , $sportlist, $tos, $length, $priority );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Process the tcfilter file storing the compiled filters in the %tcdevices table
|
# Process the tcfilter file storing the compiled filters in the %tcdevices table
|
||||||
|
Loading…
Reference in New Issue
Block a user