From 1771bb75cf9f771e8024f6525e297b2d1f621b97 Mon Sep 17 00:00:00 2001 From: Tom Eastep Date: Thu, 2 Jan 2014 14:43:55 -0800 Subject: [PATCH] Finish ipset match option implementation. Signed-off-by: Tom Eastep --- Shorewall-core/lib.cli | 12 +- Shorewall/Perl/Shorewall/Chains.pm | 105 ++++++++++------- Shorewall/Perl/Shorewall/Config.pm | 2 +- Shorewall/Perl/Shorewall/IPAddrs.pm | 2 +- Shorewall/manpages/shorewall-ipsets.xml | 135 +++++++++++++++++++++- Shorewall6/manpages/shorewall6-ipsets.xml | 123 ++++++++++++++++++++ 6 files changed, 333 insertions(+), 46 deletions(-) diff --git a/Shorewall-core/lib.cli b/Shorewall-core/lib.cli index 626c803c7..799e6382c 100644 --- a/Shorewall-core/lib.cli +++ b/Shorewall-core/lib.cli @@ -25,7 +25,7 @@ # loaded after this one and replaces some of the functions declared here. # -SHOREWALL_CAPVERSION=40515 +SHOREWALL_CAPVERSION=40600 [ -n "${g_program:=shorewall}" ] @@ -2265,6 +2265,8 @@ determine_capabilities() { OWNER_NAME_MATCH= IPSET_MATCH= OLD_IPSET_MATCH= + IPSET_MATCH_NOMATCH= + IPSET_MATCH_COUNTERS= IPSET_V5= CONNMARK= XCONNMARK= @@ -2552,6 +2554,8 @@ determine_capabilities() { if [ -n "$have_ipset" ]; then if qt $g_tool -A $chain -m set --match-set $chain src -j ACCEPT; then + qt $g_tool -A $chain -m set --match-set $chain src --return-nomatch -j ACCEPT && IPSET_MATCH_NOMATCH=Yes + qt $g_tool -A $chain -m set --match-set $chain src --packets-lt 100 -j ACCEPT && IPSET_MATCH_COUNTERS=Yes qt $g_tool -F $chain IPSET_MATCH=Yes elif qt $g_tool -A $chain -m set --set $chain src -j ACCEPT; then @@ -2686,7 +2690,9 @@ report_capabilities_unsorted() { report_capability "Owner Name Match (OWNER_NAME_MATCH)" $OWNER_NAME_MATCH if [ -n "$IPSET_MATCH" ]; then report_capability "Ipset Match (IPSET_MATCH)" $IPSET_MATCH - [ -n "$OLD_IPSET_MATCH" ] && report_capability "OLD_Ipset Match (OLD_IPSET_MATCH)" $OLD_IPSET_MATCH + [ -n "$OLD_IPSET_MATCH" ] && report_capability "OLD_Ipset Match (OLD_IPSET_MATCH)" $OLD_IPSET_MATCH + [ -n "$IPSET_MATCH_NOMATCH" ] && report_capability "Ipset Match Nomatch (IPSET_MATCH_NOMATCH)" $IPSET_MATCH_NOMATCH + [ -n "$IPSET_MATCH_NOMATCH" ] && report_capability "Ipset Match Counters (IPSET_MATCH_COUNTERS)" $IPSET_MATCH_COUNTERS fi report_capability "CONNMARK Target (CONNMARK)" $CONNMARK [ -n "$CONNMARK" ] && report_capability "Extended CONNMARK Target (XCONNMARK)" $XCONNMARK @@ -2808,6 +2814,8 @@ report_capabilities_unsorted1() { report_capability1 OWNER_NAME_MATCH report_capability1 IPSET_MATCH report_capability1 OLD_IPSET_MATCH + report_capability1 IPSET_MATCH_NOMATCH + report_capability1 IPSET_MATCH_COUNTERS report_capability1 CONNMARK report_capability1 XCONNMARK report_capability1 CONNMARK_MATCH diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index bf2ed829a..a4248e630 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -455,14 +455,8 @@ 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 = ', + 'packets' => '', + 'bytes' => '', ); # # See initialize() below for additional comments on these variables @@ -5457,35 +5451,24 @@ 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'); + my $rest = ''; - 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])\]$/ ) { + if ( $setname =~ /^(.*)\[([1-6])(?:,(.*))\]$/ ) { $setname = $1; my $count = $2; + $rest = $3; + $options .= ",$option" while --$count > 0; - } elsif ( $setname =~ /^(.*)\[((src|dst)(,(src|dst))*)\]$/ ) { + } elsif ( $setname =~ /^(.*)\[((?:src|dst)(?:,(?:src|dst))*)*(,?.+)?\]$/ ) { $setname = $1; - $options = $2; + $rest = $3; + + if ( supplied $2 ) { + $options = $2; + if ( supplied $rest ) { + fatal_error "Invalid Option List (${options}${rest})" unless $rest =~ s/^,//; + } + } my @options = split /,/, $options; my %typemap = ( src => 'Source', dst => 'Destination' ); @@ -5495,6 +5478,50 @@ sub get_set_flags( $$ ) { warning_message( "The '$_' ipset flag is used in a $typemap{$option} column" ), last unless $_ eq $option; } } + + } + + if ( $rest ) { + my @extensions = split_list($rest, 'ipset option'); + + for ( @extensions ) { + my ($extension, $relop, $value) = split /(!=|=|<|>)/, $_; + + my $match = $ipset_extensions{$extension}; + + fatal_error "Unknown ipset option ($extension)" unless defined $match; + + require_capability ( ( $extension eq 'nomatch' ? + 'IPSET_MATCH_NOMATCH' : + 'IPSET_MATCH_COUNTERS' ), + "The '$extension' option", + 's' ); + if ( $match ) { + fatal_error "The $extension option does not require a value" if supplied $relop || supplied $value; + $extensions .= "$match "; + } else { + my $val; + fatal_error "The $extension option requires a value" unless supplied $value; + fatal_error "Invalid number ($value)" unless defined ( $val = numeric_value($value) ); + $extension = "--$extension"; + + if ( $relop =~ s/!// ) { + $extension = join( ' ', '!', $extension ); + } + + if ( $relop eq '<' ) { + $extension .= '-lt'; + } elsif ( $relop eq '>' ) { + $extension .= '-gt'; + } else { + $extension .= '-eq'; + } + + $extension = join( ' ', $extension, $value ); + + $extensions .= "$extension "; + } + } } $setname =~ s/^\+//; @@ -5651,7 +5678,7 @@ sub match_source_net( $;$\$ ) { fatal_error "Multiple ipset matches require the Repeat Match capability in your kernel and iptables" unless $globals{KLUDGEFREE}; for $net ( @sets ) { - fatal_error "Expected ipset name ($net)" unless $net =~ /^(!?)(?:\+?)((?:6_)?[a-zA-Z][-\w]*(?:\[.*\])?)(?:\((.+)\))?$/; + fatal_error "Expected ipset name ($net)" unless $net =~ /^(!?)(?:\+?)((?:6_)?[a-zA-Z][-\w]*(?:\[.*\]))?(?:\((.+)\))?$/; $result .= join( '', '-m set ', $1 ? '! ' : '', get_set_flags( $2, 'src' ) ); if ( $3 ) { require_capability 'NFACCT_MATCH', "An nfacct object list ($3)", 's'; @@ -5720,7 +5747,7 @@ sub imatch_source_net( $;$\$ ) { return do_imac $net; } - if ( $net =~ /^(!?)(?:\+?)((?:6_)?[a-zA-Z][-\w]*(?:\[.*\])?)(?:\((.+)\))?$/ ) { + if ( $net =~ /^(!?)(?:\+?)((?:6_)?[a-zA-Z][-\w]*(?:\[.*\]))?(?:\((.+)\))?$/ ) { my @result = ( set => join( '', $1 ? '! ' : '', get_set_flags( $2, 'src' ) ) ); if ( $3 ) { require_capability 'NFACCT_MATCH', "An nfacct object list ($3)", 's'; @@ -5740,7 +5767,7 @@ sub imatch_source_net( $;$\$ ) { fatal_error "Multiple ipset matches requires the Repeat Match capability in your kernel and iptables" unless $globals{KLUDGEFREE}; for $net ( @sets ) { - fatal_error "Expected ipset name ($net)" unless $net =~ /^(!?)(?:\+?)((?:6_)?[a-zA-Z][-\w]*(?:\[.*\])?)(?:\((.+)\))?$/; + fatal_error "Expected ipset name ($net)" unless $net =~ /^(!?)(?:\+?)((?:6_)?[a-zA-Z][-\w]*(?:\[.*\]))?(?:\((.+)\))?$/; push @result , ( set => join( '', $1 ? '! ' : '', get_set_flags( $2, 'src' ) ) ); if ( $3 ) { require_capability 'NFACCT_MATCH', "An nfacct object list ($3)", 's'; @@ -5805,7 +5832,7 @@ sub match_dest_net( $;$ ) { return iprange_match . "${invert}--dst-range $net "; } - if ( $net =~ /^(!?)(?:\+?)((?:6_)?[a-zA-Z][-\w]*(?:\[.*\])?)(?:\((.+)\))?$/ ) { + if ( $net =~ /^(!?)(?:\+?)((?:6_)?[a-zA-Z][-\w]*(?:\[.*\]))?(?:\((.+)\))?$/ ) { my $result = join( '', '-m set ', $1 ? '! ' : '', get_set_flags( $2, 'dst' ) ); if ( $3 ) { require_capability 'NFACCT_MATCH', "An nfacct object list ($3)", 's'; @@ -5825,7 +5852,7 @@ sub match_dest_net( $;$ ) { fatal_error "Multiple ipset matches requires the Repeat Match capability in your kernel and iptables" unless $globals{KLUDGEFREE}; for $net ( @sets ) { - fatal_error "Expected ipset name ($net)" unless $net =~ /^(!?)(?:\+?)((?:6_)?[a-zA-Z][-\w]*(?:\[.*\])?)(?:\((.+)\))?$/; + fatal_error "Expected ipset name ($net)" unless $net =~ /^(!?)(?:\+?)((?:6_)?[a-zA-Z][-\w]*(?:\[.*\]))?(?:\((.+)\))?$/; $result .= join( '', '-m set ', $1 ? '! ' : '', get_set_flags( $2, 'dst' ) ); } @@ -5889,7 +5916,7 @@ sub imatch_dest_net( $;$ ) { return ( iprange => "${invert}--dst-range $net" ); } - if ( $net =~ /^(!?)(?:\+?)((?:6_)?[a-zA-Z][-\w]*(?:\[.*\])?)(?:\((.+)\))?$/ ) { + if ( $net =~ /^(!?)(?:\+?)((?:6_)?[a-zA-Z][-\w]*(?:\[.*\]))?(?:\((.+)\))?$/ ) { my @result = ( set => join( '', $1 ? '! ' : '', get_set_flags( $2, 'dst' ) ) ); if ( $3 ) { require_capability 'NFACCT_MATCH', "An nfacct object list ($3)", 's'; @@ -5909,7 +5936,7 @@ sub imatch_dest_net( $;$ ) { fatal_error "Multiple ipset matches requires the Repeat Match capability in your kernel and iptables" unless $globals{KLUDGEFREE}; for $net ( @sets ) { - fatal_error "Expected ipset name ($net)" unless $net =~ /^(!?)(?:\+?)((?:6_)?[a-zA-Z][-\w]*(?:\[.*\])?)(?:\((.+)\))?$/; + fatal_error "Expected ipset name ($net)" unless $net =~ /^(!?)(?:\+?)((?:6_)?[a-zA-Z][-\w]*(?:\[.*\]))?(?:\((.+)\))?$/; push @result , ( set => join( '', $1 ? '! ' : '', get_set_flags( $2, 'dst' ) ) ); if ( $3 ) { require_capability 'NFACCT_MATCH', "An nfacct object list ($3)", 's'; diff --git a/Shorewall/Perl/Shorewall/Config.pm b/Shorewall/Perl/Shorewall/Config.pm index 670a32878..4e818efb6 100644 --- a/Shorewall/Perl/Shorewall/Config.pm +++ b/Shorewall/Perl/Shorewall/Config.pm @@ -703,7 +703,7 @@ sub initialize( $;$$) { EXPORT => 0, KLUDGEFREE => '', VERSION => "4.5.19-Beta1", - CAPVERSION => 40515 , + CAPVERSION => 40600 , ); # # From shorewall.conf file diff --git a/Shorewall/Perl/Shorewall/IPAddrs.pm b/Shorewall/Perl/Shorewall/IPAddrs.pm index 48bb79c48..07d95f397 100644 --- a/Shorewall/Perl/Shorewall/IPAddrs.pm +++ b/Shorewall/Perl/Shorewall/IPAddrs.pm @@ -649,7 +649,7 @@ sub resolve_6dnsname( $ ) { } @addrs; -} +} sub validate_6net( $$ ) { my ( $net, $allow_name ) = @_; diff --git a/Shorewall/manpages/shorewall-ipsets.xml b/Shorewall/manpages/shorewall-ipsets.xml index 59a071a13..2c3b0eac0 100644 --- a/Shorewall/manpages/shorewall-ipsets.xml +++ b/Shorewall/manpages/shorewall-ipsets.xml @@ -47,8 +47,8 @@ [number] - Indicates that 'src' or - 'dst' should be repeated number times. - Example: myset[2]. + 'dst' should be repeated number times. + Example: myset[2]. [flag,...] where flag is or @@ -92,11 +92,134 @@ In that example, when the source address of a packet matches the myset ipset, the myobject nfacct counter will be incremented. + + Beginning with Shorewall 4.6.0, an ipset name (and src/dst list, if + any) can be immediately be followed by a list of match options. Available + options are: + + + + nomatch + + + If the set type supports the nomatch flag, then the matching + is reversed: a match with an element flagged with nomatch returns + true, while a match with a plain element returns false. This option + requires the 'Ipset Match nomatch' capability in your kernel and + ip[6]tables. + + + + + no-update-counters + + + The packet and byte counters of the matching element in the + set won't be updated. By default, the packet and byte counters are + updated. This option and those that follow require the 'Ipset Match + counters' capability in your kernel and ip[6]tables. + + + + + no-update-subcounters + + + The packet and byte counters of the matching element in the + member set of a list type of set won't be updated. Default the + packet and byte counters are updated. + + + + + packets=value + + + If the packet is matched an element in the set, match only if + the packet counter of the element matches the given + value too. + + + + + packets<value + + + If the packet is matched an element in the set, match only if + the packet counter of the element is less than the given + value as well. + + + + + packets>value + + + If the packet is matched an element in the set, match only if + the packet counter of the element is greater than the given + value as well. + + + + + packets!=value + + + If the packet is matched an element in the set, match only if + the packet counter of the element does not match the given + value too. + + + + + bytes=value + + + If the packet is matched an element in the set, match only if + the byte counter of the element matches the given + value too. + + + + + bytes<value + + + If the packet is matched an element in the set, match only if + the byte counter of the element is less than the given + value as well. + + + + + bytes>value + + + If the packet is matched an element in the set, match only if + the byte counter of the element is greater than the given + value as well. + + + + + bytes!=value + + + If the packet is matched an element in the set, match only if + the byte counter of the element does not match the given + value too. + + + Examples + In the examples that follow, myset, + myset1 and myset2 are ipsets + and myObject is an NFacct object name. + +myset +myset[src] @@ -104,6 +227,12 @@ +myset[2] +[myset1,myset2[dst]] + + +myset[src](myObject) + + +myset[src,nomatch,packets>100] + + +myset[nomatch,no-update-counters](myObject) @@ -125,7 +254,7 @@ /etc/shorewall/secmarks - /etc/shorewall/tcrules + /etc/shorewall/mangle diff --git a/Shorewall6/manpages/shorewall6-ipsets.xml b/Shorewall6/manpages/shorewall6-ipsets.xml index e17c00173..48342b67f 100644 --- a/Shorewall6/manpages/shorewall6-ipsets.xml +++ b/Shorewall6/manpages/shorewall6-ipsets.xml @@ -91,6 +91,125 @@ In that example, when the source address of a packet matches the myset ipset, the myobject nfacct counter will be incremented. + + Beginning with Shorewall 4.6.0, an ipset name (and src/dst list, if + any) can be immediately be followed by a list of match options. Available + options are: + + + + nomatch + + + If the set type supports the nomatch flag, then the matching + is reversed: a match with an element flagged with nomatch returns + true, while a match with a plain element returns false. This option + requires the 'Ipset Match nomatch' capability in your kernel and + ip[6]tables. + + + + + no-update-counters + + + The packet and byte counters of the matching element in the + set won't be updated. By default, the packet and byte counters are + updated. This option and those that follow require the 'Ipset Match + counters' capability in your kernel and ip[6]tables. + + + + + no-update-subcounters + + + The packet and byte counters of the matching element in the + member set of a list type of set won't be updated. Default the + packet and byte counters are updated. + + + + + packets=value + + + If the packet is matched an element in the set, match only if + the packet counter of the element matches the given + value too. + + + + + packets<value + + + If the packet is matched an element in the set, match only if + the packet counter of the element is less than the given + value as well. + + + + + packets>value + + + If the packet is matched an element in the set, match only if + the packet counter of the element is greater than the given + value as well. + + + + + packets!=value + + + If the packet is matched an element in the set, match only if + the packet counter of the element does not match the given + value too. + + + + + bytes=value + + + If the packet is matched an element in the set, match only if + the byte counter of the element matches the given + value too. + + + + + bytes<value + + + If the packet is matched an element in the set, match only if + the byte counter of the element is less than the given + value as well. + + + + + bytes>value + + + If the packet is matched an element in the set, match only if + the byte counter of the element is greater than the given + value as well. + + + + + bytes!=value + + + If the packet is matched an element in the set, match only if + the byte counter of the element does not match the given + value too. + + + @@ -103,6 +222,10 @@ +myset[2] +[myset1,myset2[dst]] + + +myset[src,nomatch,packets>100] + + +myset[nomatch,no-update-counters](myObject)