Make TPROXY actually work!

Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
Tom Eastep 2012-05-10 11:19:23 -07:00
parent 4d4fc315e0
commit 69d735ea0a
11 changed files with 144 additions and 132 deletions

View File

@ -4097,7 +4097,7 @@ sub get_configuration( $$$ ) {
fatal_error 'Invalid Packet Mark layout' if $config{ZONE_BITS} + $globals{ZONE_OFFSET} > 30;
$globals{EXCLUSION_MASK} = 1 << ( $globals{ZONE_OFFSET} + $config{ZONE_BITS} );
$globals{TPROXY_MASK} = $globals{EXCLUSION_MASK} << 1;
$globals{TPROXY_MARK} = $globals{EXCLUSION_MASK} << 1;
$globals{PROVIDER_MIN} = 1 << $config{PROVIDER_OFFSET};
$globals{TC_MAX} = make_mask( $config{TC_BITS} );

View File

@ -396,8 +396,8 @@ sub process_a_provider() {
$gateway = '';
}
my ( $loose, $track, $balance , $default, $default_balance, $optional, $mtu, $local , $load ) =
(0, $config{TRACK_PROVIDERS}, 0 , 0, $config{USE_DEFAULT_RT} ? 1 : 0, interface_is_optional( $interface ), '' , 0 , 0 );
my ( $loose, $track, $balance , $default, $default_balance, $optional, $mtu, $tproxy , $load ) =
(0, $config{TRACK_PROVIDERS}, 0 , 0, $config{USE_DEFAULT_RT} ? 1 : 0, interface_is_optional( $interface ), '' , 0 , 0 );
unless ( $options eq '-' ) {
for my $option ( split_list $options, 'option' ) {
@ -434,10 +434,10 @@ sub process_a_provider() {
} elsif ( $option eq 'fallback' ) {
$default = -1;
$default_balance = 0;
} elsif ( $option eq 'local' ) {
$local = 1;
$track = 0 if $config{TRACK_PROVIDERS};
$default_balance = 0 if $config{USE_DEFAULT_RT};
} elsif ( $option eq 'tproxy' ) {
$tproxy = 1;
$track = 0 if $config{TRACK_PROVIDERS};
$default_balance = 0 if $config{USE_DEFAULT_RT};
} elsif ( $option =~ /^load=(0?\.\d{1,8})/ ) {
$load = $1;
require_capability 'STATISTIC_MATCH', "load=$load", 's';
@ -455,11 +455,12 @@ sub process_a_provider() {
$maxload += $load;
}
if ( $local ) {
fatal_error "GATEWAY not valid with 'local' provider" unless $gatewaycase eq 'none';
fatal_error "'track' not valid with 'local'" if $track;
fatal_error "DUPLICATE not valid with 'local'" if $duplicate ne '-';
fatal_error "MARK required with 'local'" unless $mark;
if ( $tproxy ) {
fatal_error "GATEWAY not valid with 'tproxy' provider" unless $gatewaycase eq 'none';
fatal_error "'track' not valid with 'tproxy'" if $track;
fatal_error "DUPLICATE not valid with 'tproxy'" if $duplicate ne '-';
fatal_error "MARK not allowed with 'tproxy'" if $mark ne '-';
$mark = $globals{TPROXY_MARK};
}
my $val = 0;
@ -471,24 +472,29 @@ sub process_a_provider() {
require_capability( 'MANGLE_ENABLED' , 'Provider marks' , '' );
$val = numeric_value $mark;
if ( $tproxy ) {
$val = $globals{TPROXY_MARK};
$pref = 1;
} else {
$val = numeric_value $mark;
fatal_error "Invalid Mark Value ($mark)" unless defined $val && $val;
fatal_error "Invalid Mark Value ($mark)" unless defined $val && $val;
verify_mark $mark;
verify_mark $mark;
fatal_error "Invalid Mark Value ($mark)" unless ( $val & $globals{PROVIDER_MASK} ) == $val;
fatal_error "Invalid Mark Value ($mark)" unless ( $val & $globals{PROVIDER_MASK} ) == $val;
fatal_error "Provider MARK may not be specified when PROVIDER_BITS=0" unless $config{PROVIDER_BITS};
fatal_error "Provider MARK may not be specified when PROVIDER_BITS=0" unless $config{PROVIDER_BITS};
for my $providerref ( values %providers ) {
fatal_error "Duplicate mark value ($mark)" if numeric_value( $providerref->{mark} ) == $val;
for my $providerref ( values %providers ) {
fatal_error "Duplicate mark value ($mark)" if numeric_value( $providerref->{mark} ) == $val;
}
$lastmark = $val;
$pref = 10000 + $number - 1;
}
$pref = 10000 + $number - 1;
$lastmark = $val;
}
unless ( $loose ) {
@ -526,7 +532,7 @@ sub process_a_provider() {
loose => $loose ,
duplicate => $duplicate ,
address => $address ,
local => $local ,
tproxy => $tproxy ,
load => $load ,
rules => [] ,
routes => [] ,
@ -578,7 +584,7 @@ sub add_a_provider( $$ ) {
my $loose = $providerref->{loose};
my $duplicate = $providerref->{duplicate};
my $address = $providerref->{address};
my $local = $providerref->{local};
my $tproxy = $providerref->{tproxy};
my $load = $providerref->{load};
my $dev = chain_base $physical;
@ -600,7 +606,7 @@ sub add_a_provider( $$ ) {
$provider_interfaces{$interface} = $table;
if ( $gatewaycase eq 'none' ) {
if ( $local ) {
if ( $tproxy ) {
emit 'run_ip route add local ' . ALLIP . " dev $physical table $number";
} else {
emit "run_ip route add default dev $physical table $number";
@ -632,12 +638,13 @@ CEOF
setup_interface_proc( $interface );
if ( $mark ne '-' ) {
my $mask = have_capability 'FWMARK_RT_MASK' ? '/' . in_hex $globals{PROVIDER_MASK} : '';
my $hexmark = in_hex( $mark );
my $mask = have_capability 'FWMARK_RT_MASK' ? '/' . in_hex( $globals{ $tproxy ? 'TPROXY_MARK' : 'PROVIDER_MASK' } ) : '';
emit ( "qt \$IP -$family rule del fwmark ${mark}${mask}" ) if $config{DELETE_THEN_ADD};
emit ( "qt \$IP -$family rule del fwmark ${hexmark}${mask}" ) if $config{DELETE_THEN_ADD};
emit ( "run_ip rule add fwmark ${mark}${mask} pref $pref table $number",
"echo \"qt \$IP -$family rule del fwmark ${mark}${mask}\" >> \${VARDIR}/undo_${table}_routing"
emit ( "run_ip rule add fwmark ${hexmark}${mask} pref $pref table $number",
"echo \"qt \$IP -$family rule del fwmark ${hexmark}${mask}\" >> \${VARDIR}/undo_${table}_routing"
);
}
@ -697,7 +704,7 @@ CEOF
qq( qt \$IP -6 rule add from all table ) . DEFAULT_TABLE . qq( prio 32767\n) ,
qq(fi) ) if $family == F_IPV6;
unless ( $local ) {
unless ( $tproxy ) {
emit '';
if ( $loose ) {

View File

@ -163,18 +163,16 @@ my @tcclasses;
my %tcclasses;
my %restrictions = ( tcpre => PREROUTE_RESTRICT ,
tproxy => PREROUTE_RESTRICT ,
tcpost => POSTROUTE_RESTRICT ,
tcfor => NO_RESTRICT ,
tcin => INPUT_RESTRICT ,
tcout => OUTPUT_RESTRICT );
tcout => OUTPUT_RESTRICT ,
);
my $family;
#
# Variables supporting DIVERT
#
my $divert; #Next chain sequence number
my %diversions; #Map of marks -> chains. We use a hash rather than an array because mark values can be huge
my $divertref; # DIVERT chain
#
# Rather than initializing globals in an INIT block or during declaration,
@ -187,18 +185,17 @@ my %diversions; #Map of marks -> chains. We use a hash rather than an array
# able to re-initialize its dependent modules' state.
#
sub initialize( $ ) {
$family = shift;
%classids = ();
@tcdevices = ();
%tcdevices = ();
@tcclasses = ();
%tcclasses = ();
%diversions = ();
@devnums = ();
$devnum = 0;
$sticky = 0;
$ipp2p = 0;
$divert = 0;
$family = shift;
%classids = ();
@tcdevices = ();
%tcdevices = ();
@tcclasses = ();
%tcclasses = ();
@devnums = ();
$devnum = 0;
$sticky = 0;
$ipp2p = 0;
$divertref = 0;
}
sub process_tc_rule( ) {
@ -305,30 +302,18 @@ sub process_tc_rule( ) {
},
DIVERT => sub() {
fatal_error "Invalid DIVERT specification( $cmd/$rest )" if $rest;
fatal_error "DIVERT requires TC_EXPERT=Yes" unless $config{TC_EXPERT};
$chain = 'tcpre';
$chain = 'tproxy';
$cmd =~ /DIVERT\((.+?)\)$/;
$mark = in_hex( $globals{TPROXY_MARK} ) . '/' . in_hex( $globals{TPROXY_MARK} );
$mark = $1;
fatal_error "Invalid DIVERT specification( $cmd )" unless defined $mark;
my $val = numeric_value( $mark );
validate_mark $val . '/' . in_hex( $globals{PROVIDER_MASK} );
my $divertref = $diversions{$val};
unless ( $divertref ) {
$divertref = $diversions{$val} = new_chain( 'mangle', 'DIVERT' . ( $divert ? $divert : '' ) );
$divert++;
add_ijump( $divertref , j => 'MARK', targetopts => '--set-mark ' . in_hex( $val ) . '/' . in_hex( $globals{PROVIDER_MASK} ) );
$divertref = new_chain( 'mangle', 'divert' );
add_ijump( $divertref , j => 'MARK', targetopts => "--set-mark $mark" );
add_ijump( $divertref , j => 'ACCEPT' );
}
$target = $divertref->{name};
$target = 'divert';
$matches = '! --tcp-flags FIN,SYN,RST,ACK SYN -m socket --transparent ';
},
@ -337,7 +322,7 @@ sub process_tc_rule( ) {
fatal_error "Invalid TPROXY specification( $cmd/$rest )" if $rest;
$chain = 'tcpre';
$chain = 'tproxy';
$cmd =~ /TPROXY\((.+?)\)$/;
@ -345,7 +330,7 @@ sub process_tc_rule( ) {
fatal_error "Invalid TPROXY specification( $cmd )" unless defined $params;
( $mark, my $port, my $ip, my $bad ) = split ',', $params;
( my $port, my $ip, my $bad ) = split ',', $params;
fatal_error "Invalid TPROXY specification( $cmd )" if defined $bad;
@ -368,7 +353,7 @@ sub process_tc_rule( ) {
$target .= ' --tproxy-mark';
$mark = "$mark/" . in_hex( $globals{PROVIDER_MASK} );
$mark = in_hex( $globals{TPROXY_MARK} ) . '/' . in_hex( $globals{TPROXY_MARK} );
},
TTL => sub() {
fatal_error "TTL is not supported in IPv6 - use HL instead" if $family == F_IPV6;
@ -1958,6 +1943,7 @@ sub setup_tc() {
ensure_mangle_chain 'tcfor';
ensure_mangle_chain 'tcpost';
ensure_mangle_chain 'tcin';
ensure_mangle_chain 'tproxy';
}
my @mark_part;
@ -1975,6 +1961,7 @@ sub setup_tc() {
}
}
add_ijump $mangle_table->{PREROUTING} , j => 'tproxy' if $mangle_table->{tproxy}{referenced};
add_ijump $mangle_table->{PREROUTING} , j => 'tcpre', @mark_part;
add_ijump $mangle_table->{OUTPUT} , j => 'tcout', @mark_part;

View File

@ -270,6 +270,19 @@
<filename>shorewall.conf</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">tproxy</emphasis></term>
<listitem>
<para>Added in Shorewall 4.5.4. Used for supporting the TPROXY
action in shorewall-tcrules(5). See <ulink
url="http://www.shorewall.net/Shorewall_Squid_Usage.html">http://www.shorewall.net/Shorewall_Squid_Usage.html</ulink>.
When specified, the MARK, DUPLICATE and GATEWAY columns should
be empty, INTERFACE should be set to 'lo' and
<option>tproxy</option> should be the only OPTION.</para>
</listitem>
</varlistentry>
</variablelist>
</listitem>
</varlistentry>

View File

@ -408,36 +408,29 @@ SAME $FW 0.0.0.0/0 tcp 80,443</programlisting>
</listitem>
<listitem>
<para><emphasis
role="bold">DIVERT</emphasis>(<replaceable>mark</replaceable>)</para>
<para><emphasis role="bold">DIVERT</emphasis></para>
<para>Added in Shorewall 4.5.3. A DIVERT rule should preceed
each TPROXY rule and should specify the same
<replaceable>mark</replaceable> value. DIVERT avoids sending
packets to the TPROXY target once a socket connection to Squid3
has been established by TPROXY. DIVERT marks the packet with the
specified <replaceable>mark</replaceable> and exempts it from
any rules that follow.</para>
<para>Added in Shorewall 4.5.3. Two DIVERT rule should preceed
the TPROXY rule and should select DEST PORT tcp 80 and SOURCE
PORT tcp 80 respectively (assuming that tcp port 80 is being
proxied). DIVERT avoids sending packets to the TPROXY target
once a socket connection to Squid3 has been established by
TPROXY. DIVERT marks the packet with a unique mark and exempts
it from any rules that follow.</para>
</listitem>
<listitem>
<para><emphasis
role="bold">TPROXY</emphasis>(<replaceable>mark</replaceable>[,[<replaceable>port</replaceable>][,[<replaceable>address</replaceable>]]])</para>
role="bold">TPROXY</emphasis>([<replaceable>port</replaceable>][,<replaceable>address</replaceable>])</para>
<para>Transparently redirects a packet without altering the IP
header. Requires a local provider to be defined in <ulink
header. Requires a tproxy provider to be defined in <ulink
url="shorewall-providers.html">shorewall-providers</ulink>(5).</para>
<para>There are three parameters to TPROXY - only the first
(mark) is required:</para>
<para>There are three parameters to TPROXY - neither is
required:</para>
<itemizedlist>
<listitem>
<para><replaceable>mark</replaceable> - the MARK value
corresponding to the local provider in <ulink
url="shorewall-providers.html">shorewall-providers</ulink>(5).</para>
</listitem>
<listitem>
<para><replaceable>port</replaceable> - the port on which
the proxy server is listening. If omitted, the original
@ -451,12 +444,6 @@ SAME $FW 0.0.0.0/0 tcp 80,443</programlisting>
request arrives.</para>
</listitem>
</itemizedlist>
<note>
<para>A DIVERT rule specifying the same
<replaceable>mark</replaceable> value and other column values
should preceed each TPROXY rule.</para>
</note>
</listitem>
<listitem>

View File

@ -245,6 +245,19 @@
column is assumed.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">tproxy</emphasis></term>
<listitem>
<para>Added in Shorewall 4.5.4. Used for supporting the TPROXY
action in shorewall-tcrules(5). See <ulink
url="http://www.shorewall.net/Shorewall_Squid_Usage.html">http://www.shorewall.net/Shorewall_Squid_Usage.html</ulink>.
When specified, the MARK, DUPLICATE and GATEWAY columns should
be empty, INTERFACE should be set to 'lo' and
<option>tproxy</option> should be the only OPTION.</para>
</listitem>
</varlistentry>
</variablelist>
</listitem>
</varlistentry>

View File

@ -305,21 +305,20 @@ SAME $FW 0.0.0.0/0 tcp 80,443</programlisting>
</listitem>
<listitem>
<para><emphasis
role="bold">DIVERT</emphasis>(<replaceable>mark</replaceable>)</para>
<para><emphasis role="bold">DIVERT</emphasis></para>
<para>Added in Shorewall 4.5.3. A DIVERT rule should preceed
each TPROXY rule and should specify the same
<replaceable>mark</replaceable> value. DIVERT avoids sending
packets to the TPROXY target once a socket connection to Squid3
has been established by TPROXY. DIVERT marks the packet with the
specified <replaceable>mark</replaceable> and exempts it from
any rules that follow.</para>
<para>Added in Shorewall 4.5.3. Two DIVERT rule should preceed
the TPROXY rule and should select DEST PORT tcp 80 and SOURCE
PORT tcp 80 respectively (assuming that tcp port 80 is being
proxied). DIVERT avoids sending packets to the TPROXY target
once a socket connection to Squid3 has been established by
TPROXY. DIVERT marks the packet with a unique mark and exempts
it from any rules that follow.</para>
</listitem>
<listitem>
<para><emphasis
role="bold">TPROXY</emphasis>(<replaceable>mark</replaceable>][,[<replaceable>port</replaceable>][,[<replaceable>address</replaceable>]]])</para>
role="bold">TPROXY</emphasis>([<replaceable>port</replaceable>][,[<replaceable>address</replaceable>]]])</para>
<para>Transparently redirects a packet without altering the IP
header. Requires a local provider to be defined in <ulink
@ -329,12 +328,6 @@ SAME $FW 0.0.0.0/0 tcp 80,443</programlisting>
(mark) is required:</para>
<itemizedlist>
<listitem>
<para><replaceable>mark</replaceable> - the MARK value
corresponding to the local provider in <ulink
url="shorewall6-providers.html">shorewall6-providers</ulink>(5).</para>
</listitem>
<listitem>
<para><replaceable>port</replaceable> - the port on which
the proxy server is listening. If omitted, the original
@ -348,12 +341,6 @@ SAME $FW 0.0.0.0/0 tcp 80,443</programlisting>
request arrives.</para>
</listitem>
</itemizedlist>
<note>
<para>A DIVERT rule specifying the same
<replaceable>mark</replaceable> value and other column values
should preceed each TPROXY rule.</para>
</note>
</listitem>
<listitem>

View File

@ -398,6 +398,10 @@ tcp 6 19 TIME_WAIT src=206.124.146.176 dst=192.136.34.98 sport=58597 dport=
<command>shorewall update</command> (<command>shorewall6 update</command>)
command will set the above options based on the settings of WIDE_TC_MARKS
and HIGH_ROUTE_MARKS.</para>
<para>In Shorewall 4.5.4, a <firstterm>TPROXY mark</firstterm> was added
for TPROXY support. It is a single bit wide and is to the immediate left
of the exclusion mark.</para>
</section>
<section id="Shorewall">

View File

@ -312,15 +312,25 @@ ACCEPT $FW net tcp 80,443</programlisting></para>
<section id="TPROXY">
<title>Transparent with TPROXY</title>
<para>Shorewall 4.4.7 contains support for TPROXY. TPROXY differs from
REDIRECT in that it does not modify the IP header. Because the IP header
stays intact, TPROXY requires policy routing to direct the packets to the
proxy server running on the firewall. This approach requires TPROXY
support in your kernel and iptables and Squid 3. See <ulink
<para>Shorewall 4.5.4 contains support for TPROXY. TPROXY differs from
REDIRECT in that it does not modify the IP header and requires Squid 3 or
later. Because the IP header stays intact, TPROXY requires policy routing
to direct the packets to the proxy server running on the firewall. This
approach requires TPROXY support in your kernel and iptables and Squid 3.
See <ulink
url="http://wiki.squid-cache.org/Features/Tproxy4">http://wiki.squid-cache.org/Features/Tproxy4</ulink>.</para>
<note>
<para>Support for the TPROXY action in shorewall-tcrules(5) and the
<option>local</option> option in shorewall-providers(5) has been
available since Shoreall 4.4.7. That support required additional rules
to be added in the 'start' extention script to make it work
reliable.</para>
</note>
<para>The following configuration works with Squid running on the firewall
itself (assume that Squid is listening on port 3128).</para>
itself (assume that Squid is listening on port 3129 for TPROXY
connections).</para>
<para><filename>/etc/shorewall/interfaces:</filename></para>
@ -330,21 +340,25 @@ ACCEPT $FW net tcp 80,443</programlisting></para>
<para><filename>/etc/shorewall/providers</filename>:</para>
<programlisting>#NAME NUMBER MARK DUPLICATE INTERFACE GATEWAY OPTIONS COPY
Tproxy 1 1 - lo - local</programlisting>
Tproxy 1 - - lo - tproxy</programlisting>
<note>
<para>Notice that the MARK, DUPLICATE and GATEWAY columns are empty and
that the only option is <option>tproxy</option>.</para>
</note>
<para><filename>/etc/shorewall/tcrules</filename> (assume loc interface is
eth1):</para>
<programlisting>MARK SOURCE DEST PROTO PORT(S)
DIVERT(1) eth1 0.0.0.0/0 tcp 80
TPROXY(1,3128) eth1 0.0.0.0/0 tcp 80</programlisting>
<programlisting>MARK SOURCE DEST PROTO DEST SOURCE
PORT(S) PORT(S)
DIVERT - 0.0.0.0/0 tcp 80
DIVERT - 0.0.0.0/0 tcp - 80
TPROXY(3129) eth1 0.0.0.0/0 tcp 80</programlisting>
<note>
<para>The DIVERT action was added in Shorewall 4.5.3; user's running
earlier versions of Shorewall will need to use the <ulink
url="extension_scripts.htm">start extension script</ulink> to add the
DIVERT logic mentioned in the Squid article linked above.</para>
</note>
<para>The DIVERT rules are used to avoid unnecessary invocation of TPROXY
for request packets after the connection is established and to direct
response packets back to Squid3.</para>
<para>/etc/shorewall/rules:</para>

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB