Implement statistical marking in the tcrules file.

Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
Tom Eastep 2012-10-26 07:10:26 -07:00
parent d0e03bb03a
commit e177916c12
6 changed files with 419 additions and 78 deletions

View File

@ -117,6 +117,7 @@ our %EXPORT_TAGS = (
OPTIMIZE_RULESET_MASK
OPTIMIZE_MASK
state_match
state_imatch
initialize_chain_table
copy_rules
@ -3715,6 +3716,16 @@ sub port_count( $ ) {
#
# Generate a state match
#
sub state_match( $ ) {
my $state = shift;
if ( $state eq 'ALL' ) {
''
} else {
have_capability 'CONNTRACK_MATCH' ? ( "-m conntrack --ctstate $state " ) : ( "-m state --state $state " );
}
}
sub state_imatch( $ ) {
my $state = shift;

View File

@ -174,6 +174,12 @@ my $family;
my $divertref; # DIVERT chain
my %validstates = ( NEW => 0,
RELATED => 0,
ESTABLISHED => 0,
UNTRACKED => 0,
INVALID => 0,
);
#
# Rather than initializing globals in an INIT block or during declaration,
# we initialize them in a function. This is done for two reasons:
@ -199,14 +205,14 @@ sub initialize( $ ) {
}
sub process_tc_rule( ) {
my ( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers, $probability , $dscp );
my ( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers, $probability , $dscp , $state );
if ( $family == F_IPV4 ) {
( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $probability, $dscp ) =
split_line1 'tcrules file', { mark => 0, action => 0, source => 1, dest => 2, proto => 3, dport => 4, sport => 5, user => 6, test => 7, length => 8, tos => 9, connbytes => 10, helper => 11, probability => 12 , dscp => 13 }, { COMMENT => 0, FORMAT => 2 } , 14;
( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $probability, $dscp, $state ) =
split_line1 'tcrules file', { mark => 0, action => 0, source => 1, dest => 2, proto => 3, dport => 4, sport => 5, user => 6, test => 7, length => 8, tos => 9, connbytes => 10, helper => 11, probability => 12 , dscp => 13, state => 14 }, { COMMENT => 0, FORMAT => 2 } , 15;
$headers = '-';
} else {
( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers, $probability, $dscp ) =
split_line1 'tcrules file', { mark => 0, action => 0, source => 1, dest => 2, proto => 3, dport => 4, sport => 5, user => 6, test => 7, length => 8, tos => 9, connbytes => 10, helper => 11, headers => 12, probability => 13 , dscp => 14 }, { COMMENT => 0, FORMAT => 2 }, 15;
( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers, $probability, $dscp, $state ) =
split_line1 'tcrules file', { mark => 0, action => 0, source => 1, dest => 2, proto => 3, dport => 4, sport => 5, user => 6, test => 7, length => 8, tos => 9, connbytes => 10, helper => 11, headers => 12, probability => 13 , dscp => 14 , state => 15 }, { COMMENT => 0, FORMAT => 2 }, 16;
}
our @tccmd;
@ -259,6 +265,7 @@ sub process_tc_rule( ) {
my $cmd;
my $rest;
my $matches = '';
my $mark1;
my %processtcc = ( sticky => sub() {
if ( $chain eq 'tcout' ) {
@ -501,7 +508,7 @@ sub process_tc_rule( ) {
$chain = $tcsref->{chain} if $tcsref->{chain};
$target = $tcsref->{target} if $tcsref->{target};
$mark = "$mark/" . in_hex( $globals{TC_MASK} ) if $connmark = $tcsref->{connmark};
$mark = "$mark/" . in_hex( $globals{TC_MASK} ) if $connmark = $tcsref->{connmark} && $mark !~ m'/';
require_capability ('CONNMARK' , "CONNMARK Rules", '' ) if $connmark;
@ -584,6 +591,13 @@ sub process_tc_rule( ) {
}
}
if ( $mark =~ /-/ ) {
( $mark, $mark1 ) = split /-/, $mark, 2;
validate_mark $mark;
fatal_error "Invalid mark range ($mark-$mark1)" if $mark =~ m'/';
validate_mark $mark1;
require_capability 'STATISTIC_MATCH', 'A mark range', 's';
} else {
validate_mark $mark;
if ( $config{PROVIDER_OFFSET} ) {
@ -597,10 +611,72 @@ sub process_tc_rule( ) {
}
}
}
}
fatal_error "USER/GROUP only allowed in the OUTPUT chain" unless ( $user eq '-' || ( $chain eq 'tcout' || $chain eq 'tcpost' ) );
if ( ( my $result = expand_rule( ensure_chain( 'mangle' , $chain ) ,
if ( $state ne '-' ) {
my @state = split_list( $state, 'state' );
my %state = %validstates;
for ( @state ) {
fatal_error "Invalid STATE ($_)" unless exists $state{$_};
fatal_error "Duplicate STATE ($_)" if $state{$_};
}
} else {
$state = 'ALL';
}
if ( $mark1 ) {
#
# A Mark Range
#
my $chainref = ensure_chain( 'mangle', $chain );
( $mark1, my $mask ) = split( '/', $mark1 );
my ( $markval, $mark1val ) = ( numeric_value $mark, numeric_value $mark1 );
fatal_error "Invalid mark range ($mark-$mark1)" unless $markval < $mark1val;
$mask = $globals{TC_MASK} unless supplied $mask;
$mask = numeric_value $mask;
my $increment = 1;
$increment <<= 1 until $increment & $mask;
$mask = in_hex $mask;
my $marks = $mark1val - $markval + 1;
for ( my $packet = 0; $packet < $marks; $packet++, $markval += $increment ) {
my $match = "-m statistic --mode nth --every $marks --packet $packet ";
expand_rule( $chainref,
$restrictions{$chain} | $restriction,
$match .
do_user( $user ) .
do_test( $testval, $globals{TC_MASK} ) .
do_test( $testval, $globals{TC_MASK} ) .
do_length( $length ) .
do_tos( $tos ) .
do_connbytes( $connbytes ) .
do_helper( $helper ) .
do_headers( $headers ) .
do_probability( $probability ) .
do_dscp( $dscp ) .
state_match( $state ) ,
$source ,
$dest ,
'' ,
"$target " . join( '/', in_hex( $markval ) , $mask ) ,
'',
$target ,
'' );
}
} elsif ( ( my $result = expand_rule( ensure_chain( 'mangle' , $chain ) ,
$restrictions{$chain} | $restriction,
do_proto( $proto, $ports, $sports) . $matches .
do_user( $user ) .
@ -611,7 +687,8 @@ sub process_tc_rule( ) {
do_helper( $helper ) .
do_headers( $headers ) .
do_probability( $probability ) .
do_dscp( $dscp ) ,
do_dscp( $dscp ) .
state_match( $state ) ,
$source ,
$dest ,
'' ,

View File

@ -619,6 +619,29 @@
eth0:+myset[dst] - 206.124.146.177</programlisting>
</listitem>
</varlistentry>
<varlistentry>
<term>Example 7:</term>
<listitem>
<para>SNAT outgoing connections on eth0 from 192.168.1.0/24 in
round-robin fashion between addresses 1.1.1.1, 1.1.1.3, and 1.1.1.9
(Shorewall 4.5.9 and later).</para>
<programlisting>/etc/shorewall/tcrules:
#ACTION SOURCE DEST PROTO PORT(S) SOURCE USER TEST
# PORT(S)
1-3:CF 192.168.1.0/24 eth0 ; state=NEW
/etc/shorewall/masq:
#INTERFACE SOURCE ADDRESS ...
eth0 192.168.1.0/24 1.1.1.1 ; mark=1:C
eth0 192.168.1.0/24 1.1.1.3 ; mark=2:C
eth0 192.168.1.0/24 1.1.1.4 ; mark=3:C</programlisting>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -131,8 +131,12 @@
<para>The mark value may be optionally followed by "/" and a
mask value (used to determine those bits of the connection mark
to actually be set). The mark and optional mask are then
followed by one of:</para>
to actually be set). When a mask is specified, the result of
logically ANDing the mark value with the mask must be the same
as the mark value.</para>
<para>The mark and optional mask are then followed by one
of:</para>
<variablelist>
<varlistentry>
@ -178,26 +182,114 @@
</listitem>
</varlistentry>
</variablelist>
</listitem>
<para><emphasis role="bold">Special considerations for If
HIGH_ROUTE_MARKS=Yes in <ulink
url="shorewall.conf.html">shorewall.conf</ulink>(5</emphasis>).</para>
<listitem>
<para>A mark range which is a pair of integers separated by a
dash ("-"). Added in Shorewall 4.5.9.</para>
<para>If HIGH_ROUTE_MARKS=Yes, then you may also specify a value
in the range 0x0100-0xFF00 with the low-order byte being zero.
Such values may only be used in the PREROUTING chain (value
followed by <emphasis role="bold">:P</emphasis> or you have set
MARK_IN_FORWARD_CHAIN=No in <ulink
url="shorewall.conf.html">shorewall.conf</ulink>(5) and have not
followed the value with <option>:F</option>) or the OUTPUT chain
(SOURCE is <emphasis role="bold">$FW</emphasis>). With
HIGH_ROUTE_MARKS=Yes, non-zero mark values less that 256 are not
permitted. Shorewall prohibits non-zero mark values less that
256 in the OUTPUT chain when HIGH_ROUTE_MARKS=Yes. While earlier
versions allow such values in the OUTPUT chain, it is strongly
recommended that with HIGH_ROUTE_MARKS=Yes, you use the
POSTROUTING chain to apply traffic shaping
marks/classification.</para>
<para>May be optionally followed by a slash ("/") and a mask and
requires the <firstterm>Statistics Match</firstterm> capability
in iptables and kernel. Marks in the specified range are
assigned to packets on a round-robin fashion.</para>
<para>When a mask is specified, the result of logically ANDing
the mark value with the mask must be the same as the mark value.
The least significant bit in the mask is used as an increment.
For example, if '0x200-0x400/0xff00' is specified, then the
assigned mark values are 0x200, 0x300 and 0x400 in equal
proportions. If no mask is specified, then ( 2 ** MASK_BITS ) -
1 is assumed (MASK_BITS is set in <ulink
url="shorewall.conf.html">shorewall.conf</ulink>(5)).</para>
<para>May optionally be followed by <emphasis
role="bold">:P</emphasis>, <emphasis
role="bold">:F</emphasis>,<emphasis role="bold">:T</emphasis> or
<emphasis role="bold">:I</emphasis> where<emphasis role="bold">
:P</emphasis> indicates that marking should occur in the
PREROUTING chain, <emphasis role="bold">:F</emphasis> indicates
that marking should occur in the FORWARD chain, <emphasis
role="bold">:I </emphasis>indicates that marking should occur in
the INPUT chain (added in Shorewall 4.4.13), and <emphasis
role="bold">:T</emphasis> indicates that marking should occur in
the POSTROUTING chain. If neither <emphasis
role="bold">:P</emphasis>, <emphasis role="bold">:F</emphasis>
nor <emphasis role="bold">:T</emphasis> follow the mark value
then the chain is determined as follows:</para>
<para>- If the SOURCE is <emphasis
role="bold">$FW</emphasis>[<emphasis
role="bold">:</emphasis><emphasis>address-or-range</emphasis>[,<emphasis>address-or-range</emphasis>]...],
then the rule is inserted into the OUTPUT chain. When
HIGH_ROUTE_MARKS=Yes, only high mark values may be assigned
there. Packet marking rules for traffic shaping of packets
originating on the firewall must be coded in the POSTROUTING
chain (see below).</para>
<para>- Otherwise, the chain is determined by the setting of
MARK_IN_FORWARD_CHAIN in <ulink
url="shorewall.conf.html">shorewall.conf</ulink>(5).</para>
<para>Please note that <emphasis role="bold">:I</emphasis> is
included for completeness and affects neither traffic shaping
nor policy routing.</para>
<para>If your kernel and iptables include CONNMARK support then
you can also mark the connection rather than the packet.</para>
<para>The mark range may be optionally followed by "/" and a
mask value (used to determine those bits of the connection mark
to actually be set). When a mask is specified, the result of
logically ANDing the mark value with each of the masks must be
the same as the mark value.</para>
<para>The mark and optional mask are then followed by one
of:</para>
<variablelist>
<varlistentry>
<term><emphasis role="bold">C</emphasis></term>
<listitem>
<para>Mark the connection in the chain determined by the
setting of MARK_IN_FORWARD_CHAIN</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">CF</emphasis></term>
<listitem>
<para>Mark the connection in the FORWARD chain</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">CP</emphasis></term>
<listitem>
<para>Mark the connection in the PREROUTING chain.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CT</term>
<listitem>
<para>Mark the connecdtion in the POSTROUTING chain</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CI</term>
<listitem>
<para>Mark the connection in the INPUT chain. This option
is included for completeness and has no applicability to
traffic shaping or policy routing.</para>
</listitem>
</varlistentry>
</variablelist>
</listitem>
<listitem>
@ -530,6 +622,17 @@ SAME $FW 0.0.0.0/0 tcp 80,443</programlisting>
role="bold">:F</emphasis></para>
</listitem>
<listitem>
<para><emphasis role="bold">STATE</emphasis> {<emphasis
role="bold">NEW</emphasis>|<emphasis
role="bold">RELATED</emphasis>|<emphasis
role="bold">ESTABLISHED</emphasis>|<emphasis
role="bold">INVALID</emphasis>} [,...]</para>
<para>Added in Shorewall 4.5.9. The rule will only match if the
packet's connection is in one of the listed states.</para>
</listitem>
<listitem>
<para><emphasis
role="bold">TOS</emphasis>(<replaceable>tos</replaceable>[/<replaceable>mask</replaceable>])</para>
@ -1124,6 +1227,29 @@ Normal-Service =&gt; 0x00</programlisting>
mark has been set, save it to the connection mark.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Example 2:</term>
<listitem>
<para>SNAT outgoing connections on eth0 from 192.168.1.0/24 in
round-robin fashion between addresses 1.1.1.1, 1.1.1.3, and 1.1.1.9
(Shorewall 4.5.9 and later).</para>
<programlisting>/etc/shorewall/tcrules:
#ACTION SOURCE DEST PROTO PORT(S) SOURCE USER TEST
# PORT(S)
1-3:CF 192.168.1.0/24 eth0 ; state=NEW
/etc/shorewall/masq:
#INTERFACE SOURCE ADDRESS ...
eth0 192.168.1.0/24 1.1.1.1 ; mark=1:C
eth0 192.168.1.0/24 1.1.1.3 ; mark=2:C
eth0 192.168.1.0/24 1.1.1.4 ; mark=3:C</programlisting>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -131,8 +131,12 @@
<para>The mark value may be optionally followed by "/" and a
mask value (used to determine those bits of the connection mark
to actually be set). The mark and optional mask are then
followed by one of:+</para>
to actually be set). When a mask is specified, the result of
logically ANDing the mark value with the mask must be the same
as the mark value.</para>
<para>The mark and optional mask are then followed by one
of:+</para>
<variablelist>
<varlistentry>
@ -178,26 +182,114 @@
</listitem>
</varlistentry>
</variablelist>
</listitem>
<para><emphasis role="bold">Special considerations for If
HIGH_ROUTE_MARKS=Yes in <ulink
url="shorewall6.conf.html">shorewall6.conf</ulink>(5</emphasis>).</para>
<listitem>
<para>A mark range which is a pair of integers separated by a
dash ("-"). Added in Shorewall 4.5.9.</para>
<para>If HIGH_ROUTE_MARKS=Yes, then you may also specify a value
in the range 0x0100-0xFF00 with the low-order byte being zero.
Such values may only be used in the PREROUTING chain (value
followed by <emphasis role="bold">:P</emphasis> or you have set
MARK_IN_FORWARD_CHAIN=No in <ulink
url="shorewall6.conf.html">shorewall6.conf</ulink>(5) and have
not followed the value with <option>:F</option>) or the OUTPUT
chain (SOURCE is <emphasis role="bold">$FW</emphasis>). With
HIGH_ROUTE_MARKS=Yes, non-zero mark values less that 256 are not
permitted. Shorewall6 prohibits non-zero mark values less that
256 in the OUTPUT chain when HIGH_ROUTE_MARKS=Yes. While earlier
versions allow such values in the OUTPUT chain, it is strongly
recommended that with HIGH_ROUTE_MARKS=Yes, you use the
POSTROUTING chain to apply traffic shaping
marks/classification.</para>
<para>May be optionally followed by a slash ("/") and a mask and
requires the <firstterm>Statistics Match</firstterm> capability
in iptables and kernel. Marks in the specified range are
assigned to packets on a round-robin fashion.</para>
<para>When a mask is specified, the result of logically ANDing
the mark value with the mask must be the same as the mark value.
The least significant bit in the mask is used as an increment.
For example, if '0x200-0x400/0xff00' is specified, then the
assigned mark values are 0x200, 0x300 and 0x400 in equal
proportions. If no mask is specified, then ( 2 ** MASK_BITS ) -
1 is assumed (MASK_BITS is set in <ulink
url="shorewall6.conf.html">shorewall6.conf</ulink>(5)).</para>
<para>May optionally be followed by <emphasis
role="bold">:P</emphasis>, <emphasis
role="bold">:F</emphasis>,<emphasis role="bold">:T</emphasis> or
<emphasis role="bold">:I</emphasis> where<emphasis role="bold">
:P</emphasis> indicates that marking should occur in the
PREROUTING chain, <emphasis role="bold">:F</emphasis> indicates
that marking should occur in the FORWARD chain, <emphasis
role="bold">:I </emphasis>indicates that marking should occur in
the INPUT chain (added in Shorewall 4.4.13), and <emphasis
role="bold">:T</emphasis> indicates that marking should occur in
the POSTROUTING chain. If neither <emphasis
role="bold">:P</emphasis>, <emphasis role="bold">:F</emphasis>
nor <emphasis role="bold">:T</emphasis> follow the mark value
then the chain is determined as follows:</para>
<para>- If the SOURCE is <emphasis
role="bold">$FW</emphasis>[<emphasis
role="bold">:</emphasis><emphasis>address-or-range</emphasis>[,<emphasis>address-or-range</emphasis>]...],
then the rule is inserted into the OUTPUT chain. When
HIGH_ROUTE_MARKS=Yes, only high mark values may be assigned
there. Packet marking rules for traffic shaping of packets
originating on the firewall must be coded in the POSTROUTING
chain (see below).</para>
<para>- Otherwise, the chain is determined by the setting of
MARK_IN_FORWARD_CHAIN in <ulink
url="shorewall.conf.html">shorewall.conf</ulink>(5).</para>
<para>Please note that <emphasis role="bold">:I</emphasis> is
included for completeness and affects neither traffic shaping
nor policy routing.</para>
<para>If your kernel and iptables include CONNMARK support then
you can also mark the connection rather than the packet.</para>
<para>The mark range may be optionally followed by "/" and a
mask value (used to determine those bits of the connection mark
to actually be set). When a mask is specified, the result of
logically ANDing the mark value with each of the masks must be
the same as the mark value.</para>
<para>The mark and optional mask are then followed by one
of:</para>
<variablelist>
<varlistentry>
<term><emphasis role="bold">C</emphasis></term>
<listitem>
<para>Mark the connection in the chain determined by the
setting of MARK_IN_FORWARD_CHAIN</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">CF</emphasis></term>
<listitem>
<para>Mark the connection in the FORWARD chain</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">CP</emphasis></term>
<listitem>
<para>Mark the connection in the PREROUTING chain.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CT</term>
<listitem>
<para>Mark the connecdtion in the POSTROUTING chain</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CI</term>
<listitem>
<para>Mark the connection in the INPUT chain. This option
is included for completeness and has no applicability to
traffic shaping or policy routing.</para>
</listitem>
</varlistentry>
</variablelist>
</listitem>
<listitem>
@ -451,6 +543,17 @@ SAME $FW 0.0.0.0/0 tcp 80,443</programlisting>
role="bold">:F</emphasis></para>
</listitem>
<listitem>
<para><emphasis role="bold">STATE</emphasis> {<emphasis
role="bold">NEW</emphasis>|<emphasis
role="bold">RELATED</emphasis>|<emphasis
role="bold">ESTABLISHED</emphasis>|<emphasis
role="bold">INVALID</emphasis>} [,...]</para>
<para>Added in Shorewall 4.5.9. The rule will only match if the
packet's connection is in one of the listed states.</para>
</listitem>
<listitem>
<para><emphasis
role="bold">TOS</emphasis>(<replaceable>tos</replaceable>[/<replaceable>mask</replaceable>])</para>

View File

@ -278,8 +278,9 @@ tcp 6 19 TIME_WAIT src=206.124.146.176 dst=192.136.34.98 sport=58597 dport=
<para>Shorewall actually allows you to have complete control over the
layout of the 32-bit mark using the following options in <ulink
url="manpages/shorewall.conf.html">shorewall.conf</ulink> (5) (these
options were documents in the shorewall.conf manpage in Shorewall
4.4.26):</para>
options were documentrf in the <ulink
url="manpages/shorewall.conf.html">shorewall.conf</ulink>(5) manpage in
Shorewall 4.4.26):</para>
<variablelist>
<varlistentry>
@ -341,7 +342,7 @@ tcp 6 19 TIME_WAIT src=206.124.146.176 dst=192.136.34.98 sport=58597 dport=
<graphic align="left" fileref="images/MarkGeometry.png" valign="top" />
<para/>
<para></para>
<para>The default values of these options are determined by the settings
of other options as follows:</para>