From b4847d6a01980a7d62b5005d1a1351a4f5bc68d7 Mon Sep 17 00:00:00 2001 From: Tom Eastep Date: Thu, 2 Jan 2014 09:36:35 -0800 Subject: [PATCH] New IPSET MATCH extensions Signed-off-by: Tom Eastep --- Shorewall/Perl/Shorewall/Chains.pm | 88 +++++++++++++++++++++++------- Shorewall/Perl/Shorewall/Config.pm | 20 +++++++ 2 files changed, 88 insertions(+), 20 deletions(-) diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index 3ad0400de..bf2ed829a 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -448,7 +448,22 @@ use constant { NO_RESTRICT => 0, # FORWARD chain rule - Both -i an ALL_RESTRICT => 12, # fw->fw rule - neither -i nor -o allowed DESTIFACE_DISALLOW => 32, # Don't allow dest interface. Similar to INPUT_RESTRICT but generates a more relevant error message }; - +# +# Possible IPSET options +# +our %ipset_extensions = ( + 'nomatch' => '--return-nomatch ', + 'no-update-counters' => '! --update-counters ', + 'no-update-subcounters' => '! --update-subcounters ', + 'pkts-eq' => '--packets-eq = ', + 'pkts-neq' => '! --packets-eq = ', + 'pkts-lt' => '--packets-lt = ', + 'pkts-gt' => '--packets-gt = ', + 'bytes-eq' => '--bytes-eq = ', + 'bytes-neq' => '! --bytes-eq = ', + 'bytes-lt' => '--bytes-lt = ', + 'bytes-gt' => '--bytes-gt = ', + ); # # See initialize() below for additional comments on these variables # @@ -4403,26 +4418,32 @@ sub do_proto( $$$;$ ) if ( $ports ne '' ) { $invert = $ports =~ s/^!// ? '! ' : ''; - $sports = '', require_capability( 'MULTIPORT', "'=' in the SOURCE PORT(S) column", 's' ) if ( $srcndst = $sports eq '=' ); - if ( $multiport || $ports =~ tr/,/,/ > 0 || $sports =~ tr/,/,/ > 0 ) { - fatal_error "Port lists require Multiport support in your kernel/iptables" unless have_capability( 'MULTIPORT',1 ); + if ( $ports =~ /^\+/ ) { + $output .= $invert; + $output .= get_set_flags( $ports, 'dst' ); + } else { + $sports = '', require_capability( 'MULTIPORT', "'=' in the SOURCE PORT(S) column", 's' ) if ( $srcndst = $sports eq '=' ); - if ( port_count ( $ports ) > 15 ) { - if ( $restricted ) { - fatal_error "A port list in this file may only have up to 15 ports"; - } elsif ( $invert ) { - fatal_error "An inverted port list may only have up to 15 ports"; + if ( $multiport || $ports =~ tr/,/,/ > 0 || $sports =~ tr/,/,/ > 0 ) { + fatal_error "Port lists require Multiport support in your kernel/iptables" unless have_capability( 'MULTIPORT',1 ); + + if ( port_count ( $ports ) > 15 ) { + if ( $restricted ) { + fatal_error "A port list in this file may only have up to 15 ports"; + } elsif ( $invert ) { + fatal_error "An inverted port list may only have up to 15 ports"; + } } - } - $ports = validate_port_list $pname , $ports; - $output .= ( $srcndst ? "-m multiport ${invert}--ports ${ports} " : "-m multiport ${invert}--dports ${ports} " ); - $multiport = 1; - } else { - fatal_error "Missing DEST PORT" unless supplied $ports; - $ports = validate_portpair $pname , $ports; - $output .= ( $srcndst ? "-m multiport ${invert}--ports ${ports} " : "${invert}--dport ${ports} " ); + $ports = validate_port_list $pname , $ports; + $output .= ( $srcndst ? "-m multiport ${invert}--ports ${ports} " : "-m multiport ${invert}--dports ${ports} " ); + $multiport = 1; + } else { + fatal_error "Missing DEST PORT" unless supplied $ports; + $ports = validate_portpair $pname , $ports; + $output .= ( $srcndst ? "-m multiport ${invert}--ports ${ports} " : "${invert}--dport ${ports} " ); + } } } else { $multiport ||= ( $sports =~ tr/,/,/ ) > 0 ;; @@ -4434,9 +4455,13 @@ sub do_proto( $$$;$ ) if ( $sports ne '' ) { fatal_error "'=' in the SOURCE PORT(S) column requires one or more ports in the DEST PORT(S) column" if $sports eq '='; - $invert = $sports =~ s/^!// ? '! ' : ''; - if ( $multiport ) { + $invert = $sports =~ s/^!// ? '! ' : ''; + + if ( $ports =~ /^\+/ ) { + $output .= $invert; + $output .= get_set_flags( $ports, 'dst' ); + } elsif ( $multiport ) { if ( port_count( $sports ) > 15 ) { if ( $restricted ) { fatal_error "A port list in this file may only have up to 15 ports"; @@ -5424,6 +5449,7 @@ sub iprange_match() { sub get_set_flags( $$ ) { my ( $setname, $option ) = @_; my $options = $option; + my $extensions = ''; require_capability( 'IPSET_MATCH' , 'ipset names in Shorewall configuration files' , '' ); @@ -5431,6 +5457,28 @@ sub get_set_flags( $$ ) { $setname =~ s/^!//; # Caller has already taken care of leading ! + if ( $setname =~ s/\((.+)\)$// ) { + fatal_error "Your iptables and/or kernel are too old to support an IPSET option list" if have_capability 'OLD_IPSET_MATCH'; + my @extensions = split_list($1, 'ipset option'); + + for ( @extensions ) { + my ($extension, $value) = split ' ', $_; + my $match = $ipset_extensions{$extension}; + fatal_error "Unknown ipset option ($extension)" unless $match; + + if ( $match =~ /= $/ ) { + my $val; + fatal_error "The $extension option requires a value" unless supplied $value; + fatal_error "Invalid number ($value)" unless defined ( $val = numeric_value($value) ); + $match =~ s/= $/$value /; + } else { + fatal_error "The $extension option does not require a value" if supplied $value; + } + + $extensions = join( '', $extensions, $match ); + } + } + if ( $setname =~ /^(.*)\[([1-6])\]$/ ) { $setname = $1; my $count = $2; @@ -5463,7 +5511,7 @@ sub get_set_flags( $$ ) { fatal_error "Invalid ipset name ($setname)" unless $setname =~ /^(6_)?[a-zA-Z][-\w]*/; - have_capability( 'OLD_IPSET_MATCH' ) ? "--set $setname $options " : "--match-set $setname $options "; + have_capability( 'OLD_IPSET_MATCH' ) ? "--set $setname $options " : "--match-set $setname $options $extensions"; } diff --git a/Shorewall/Perl/Shorewall/Config.pm b/Shorewall/Perl/Shorewall/Config.pm index 5d7159289..670a32878 100644 --- a/Shorewall/Perl/Shorewall/Config.pm +++ b/Shorewall/Perl/Shorewall/Config.pm @@ -327,6 +327,10 @@ our %capdesc = ( NAT_ENABLED => 'NAT', => 'Owner Name Match', IPSET_MATCH => 'Ipset Match', OLD_IPSET_MATCH => 'Old Ipset Match', + IPSET_MATCH_NOMATCH + => 'Ipset Match nomatch', + IPSET_MATCH_COUNTERS + => 'Ipset Match counters', IPSET_V5 => 'Version 5 ipsets', CONNMARK => 'CONNMARK Target', XCONNMARK => 'Extended CONNMARK Target', @@ -904,6 +908,8 @@ sub initialize( $;$$) { OWNER_NAME_MATCH => undef, IPSET_MATCH => undef, OLD_IPSET_MATCH => undef, + IPSET_MATCH_NOMATCH => undef, + IPSET_MATCH_COUNTERS => undef, IPSET_V5 => undef, CONNMARK => undef, XCONNMARK => undef, @@ -4077,11 +4083,15 @@ sub IPSet_Match() { $ipset = which $ipset unless $ipset =~ '/'; + $capabilities{IPSET_MATCH_NOMATCH} = $capabilities{IPSET_MATCH_COUNTERS} = 0; + if ( $ipset && -x $ipset ) { qt( "$ipset -X $sillyname" ); if ( qt( "$ipset -N $sillyname iphash" ) || qt( "$ipset -N $sillyname hash:ip family $fam") ) { if ( qt1( "$iptables $iptablesw -A $sillyname -m set --match-set $sillyname src -j ACCEPT" ) ) { + $capabilities{IPSET_MATCH_NOMATCH} = qt1( "$iptables $iptablesw -A $sillyname -m set --match-set $sillyname src --return-nomatch -j ACCEPT" ); + $capabilities{IPSET_MATCH_COUNTERS} = qt1( "$iptables $iptablesw -A $sillyname -m set --match-set $sillyname src --packets-lt 100 -j ACCEPT" ); qt1( "$iptables $iptablesw -F $sillyname" ); $result = ! ( $capabilities{OLD_IPSET_MATCH} = 0 ); } else { @@ -4095,6 +4105,14 @@ sub IPSet_Match() { $result; } +sub IPSet_Match_Nomatch() { + have_capability 'IPSET_MATCH' && $capabilities{IPSET_MATCH_NOMATCH}; +} + +sub IPSet_Match_Counters() { + have_capability 'IPSET_MATCH' && $capabilities{IPSET_MATCH_COUNTGERS}; +} + sub IPSET_V5() { my $ipset = $config{IPSET} || 'ipset'; my $result = 0; @@ -4383,6 +4401,8 @@ our %detect_capability = IPP2P_MATCH => \&Ipp2p_Match, IPRANGE_MATCH => \&IPRange_Match, IPSET_MATCH => \&IPSet_Match, + IPSET_MATCH_NOMATCH => \&IPSet_Match_Nomatch, + IPSET_MATCH_COUNTERS => \&IPSet_Match_Counters, IRC_HELPER => \&IRC_Helper, IRC0_HELPER => \&IRC0_Helper, OLD_IPSET_MATCH => \&Old_IPSet_Match,