Implement Stateless NAT support.

Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
Tom Eastep 2011-08-14 12:01:17 -07:00
parent 71480ff647
commit 0b2a8b12c7
10 changed files with 346 additions and 137 deletions

View File

@ -63,6 +63,7 @@ our @EXPORT = qw(
%chain_table
$raw_table
$rawpost_table
$nat_table
$mangle_table
$filter_table
@ -113,6 +114,8 @@ our %EXPORT_TAGS = (
zone_input_chain
use_input_chain
output_chain
prerouting_chain
postrouting_chain
zone_output_chain
use_output_chain
masq_chain
@ -132,6 +135,7 @@ our %EXPORT_TAGS = (
ensure_mangle_chain
ensure_nat_chain
ensure_raw_chain
ensure_rawpost_chain
new_standard_chain
new_builtin_chain
new_nat_chain
@ -262,6 +266,7 @@ our $VERSION = 'MODULEVERSION';
#
our %chain_table;
our $raw_table;
our $rawpost_table;
our $nat_table;
our $mangle_table;
our $filter_table;
@ -492,11 +497,13 @@ sub initialize( $$$ ) {
( $family, my $hard, $export ) = @_;
%chain_table = ( raw => {},
rawpost => {},
mangle => {},
nat => {},
filter => {} );
$raw_table = $chain_table{raw};
$rawpost_table = $chain_table{rawpost};
$nat_table = $chain_table{nat};
$mangle_table = $chain_table{mangle};
$filter_table = $chain_table{filter};
@ -1582,6 +1589,22 @@ sub output_chain($)
$_[0] . '_out';
}
#
# Prerouting Chain for an interface
#
sub prerouting_chain($)
{
$_[0] . '_pre';
}
#
# Prerouting Chain for an interface
#
sub postrouting_chain($)
{
$_[0] . '_post';
}
#
# Output Chain for a zone
#
@ -2044,6 +2067,14 @@ sub ensure_raw_chain($) {
$chainref;
}
sub ensure_rawpost_chain($) {
my $chain = $_[0];
my $chainref = ensure_chain 'rawpost', $chain;
$chainref->{referenced} = 1;
$chainref;
}
#
# Add a builtin chain
#
@ -2200,6 +2231,8 @@ sub initialize_chain_table($) {
new_builtin_chain 'raw', $chain, 'ACCEPT';
}
new_builtin_chain 'rawpost', 'POSTROUTING', 'ACCEPT';
for my $chain ( qw(INPUT OUTPUT FORWARD) ) {
new_builtin_chain 'filter', $chain, 'DROP';
}
@ -2243,6 +2276,8 @@ sub initialize_chain_table($) {
new_builtin_chain 'raw', $chain, 'ACCEPT';
}
new_builtin_chain 'rawpost', 'POSTROUTING', 'ACCEPT';
for my $chain ( qw(INPUT OUTPUT FORWARD) ) {
new_builtin_chain 'filter', $chain, 'DROP';
}
@ -2718,7 +2753,7 @@ sub optimize_level8( $$$ ) {
}
sub optimize_ruleset() {
for my $table ( qw/raw mangle nat filter/ ) {
for my $table ( qw/raw rawpost mangle nat filter/ ) {
next if $family == F_IPV6 && $table eq 'nat';
@ -5433,6 +5468,7 @@ sub create_netfilter_load( $ ) {
my @table_list;
push @table_list, 'raw' if have_capability( 'RAW_TABLE' );
push @table_list, 'rawpost' if have_capability( 'RAWPOST_TABLE' );
push @table_list, 'nat' if have_capability( 'NAT_ENABLED' );
push @table_list, 'mangle' if have_capability( 'MANGLE_ENABLED' ) && $config{MANGLE_ENABLED};
push @table_list, 'filter';
@ -5535,6 +5571,7 @@ sub preview_netfilter_load() {
my @table_list;
push @table_list, 'raw' if have_capability( 'RAW_TABLE' );
push @table_list, 'rawpost' if have_capability( 'RAWPOST_TABLE' );
push @table_list, 'nat' if have_capability( 'NAT_ENABLED' );
push @table_list, 'mangle' if have_capability( 'MANGLE_ENABLED' ) && $config{MANGLE_ENABLED};
push @table_list, 'filter';
@ -5644,7 +5681,7 @@ sub create_chainlist_reload($) {
for my $chain ( @chains ) {
( $table , $chain ) = split ':', $chain if $chain =~ /:/;
fatal_error "Invalid table ( $table )" unless $table =~ /^(nat|mangle|filter|raw)$/;
fatal_error "Invalid table ( $table )" unless $table =~ /^(nat|mangle|filter|raw|rawpost)$/;
$chains{$table} = {} unless $chains{$table};
@ -5673,7 +5710,7 @@ sub create_chainlist_reload($) {
enter_cat_mode;
for $table ( qw(raw nat mangle filter) ) {
for $table ( qw(raw rawpost nat mangle filter) ) {
my $tableref=$chains{$table};
next unless $tableref;
@ -5749,6 +5786,7 @@ sub create_stop_load( $ ) {
my @table_list;
push @table_list, 'raw' if have_capability( 'RAW_TABLE' );
push @table_list, 'rawpost' if have_capability( 'RAWPOST_TABLE' );
push @table_list, 'nat' if have_capability( 'NAT_ENABLED' );
push @table_list, 'mangle' if have_capability( 'MANGLE_ENABLED' ) && $config{MANGLE_ENABLED};
push @table_list, 'filter';

View File

@ -307,6 +307,7 @@ our %config_files = ( #accounting => 1,
refresh => 1,
refreshed => 1,
restored => 1,
rawnat => 1,
route_rules => 1,
routes => 1,
routestopped => 1,

View File

@ -1166,6 +1166,12 @@ sub add_interface_jumps {
addnatjump 'PREROUTING' , input_chain( $interface ) , imatch_source_dev( $interface );
addnatjump 'POSTROUTING' , output_chain( $interface ) , imatch_dest_dev( $interface );
addnatjump 'POSTROUTING' , masq_chain( $interface ) , imatch_dest_dev( $interface );
if ( have_capability 'RAWPOST_TABLE' ) {
insert_ijump ( $rawpost_table->{POSTROUTING}, j => postrouting_chain( $interface ), 0, imatch_dest_dev( $interface) ) if $rawpost_table->{postrouting_chain $interface};
insert_ijump ( $raw_table->{PREROUTING}, j => prerouting_chain( $interface ), 0, imatch_source_dev( $interface) ) if $raw_table->{prerouting_chain $interface};
insert_ijump ( $raw_table->{OUTPUT}, j => output_chain( $interface ), 0, imatch_dest_dev( $interface) ) if $raw_table->{output_chain $interface};
}
}
#
# Add the jumps to the interface chains from filter FORWARD, INPUT, OUTPUT

View File

@ -416,12 +416,14 @@ sub setup_netmap() {
for my $interface ( split_list $interfacelist, 'interface' ) {
my @rulein;
my @ruleout;
my $iface = $interface;
fatal_error "Unknown interface ($interface)" unless my $interfaceref = known_interface( $interface );
unless ( $type =~ /:/ ) {
my @rulein;
my @ruleout;
unless ( $interfaceref->{root} ) {
@rulein = imatch_source_dev( $interface );
@ruleout = imatch_dest_dev( $interface );
@ -435,6 +437,44 @@ sub setup_netmap() {
} else {
fatal_error "Invalid type ($type)";
}
} elsif ( $type =~ /^(DNAT|SNAT):([POT])$/ ) {
my ( $target , $chain ) = ( $1, $2 );
my $table = 'raw';
my @match = ();
unless ( $interfaceref->{root} ) {
@match = imatch_dest_dev( $interface );
$interface = $interfaceref->{name};
}
if ( $chain eq 'P' ) {
$chain = prerouting_chain $interface;
@match = imatch_source_dev( $iface ) unless $iface eq $interface;
} elsif ( $chain eq 'O' ) {
$chain = output_chain $interface;
} else {
$chain = postrouting_chain $interface;
$table = 'rawpost';
}
if ( $target eq 'DNAT' ) {
add_ijump( ensure_chain( $table, $chain ) ,
j => 'RAWDNAT',
targetopts => "--to-dest $net2",
imatch_source_net( $net3 ) ,
imatch_dest_net( $net1 ) ,
@match );
} else {
add_ijump( ensure_chain( $table, $chain ) ,
j => 'RAWSNAT',
targetopts => "--to-source $net2",
imatch_dest_net( $net3 ) ,
imatch_source_net( $net1 ) ,
@match );
}
} else {
fatal_error "Invalid type ($type)";
}
progress_message " Network $net1 on $iface mapped to $net2 ($type)";
}
@ -445,6 +485,83 @@ sub setup_netmap() {
}
#
# Setup Raw NAT
#
sub setup_rawnat() {
if ( my $fn = open_file 'rawnat' ) {
first_entry( sub { progress_message2 "$doing $fn..."; require_capability 'RAWPOST_TABLE' , 'a non-empty rawnat file' , 's'; } );
while ( read_a_line ) {
my ( $type, $net1, $interfacelist, $net2, $net3 ) = split_line 4, 5, 'rawnat file';
validate_net $net1, 0;
validate_net $net2, 0;
$net3 = ALLIP if $net3 eq '-';
for my $interface ( split_list $interfacelist, 'interface' ) {
my @rulein;
my @ruleout;
my $iface = $interface;
fatal_error "Unknown interface ($interface)" unless my $interfaceref = known_interface( $interface );
unless ( $interfaceref->{root} ) {
@rulein = imatch_source_dev( $interface );
@ruleout = imatch_dest_dev( $interface );
$interface = $interfaceref->{name};
}
if ( $type =~ /^(DNAT|SNAT):([POT])$/ ) {
my ( $target , $chain ) = ( $1, $2 );
my $table = 'raw';
my $match = 'o';
if ( $chain eq 'P' ) {
$chain = prerouting_chain $interface;
$match = 'i';
} elsif ( $chain eq 'O' ) {
$chain = output_chain $interface;
} else {
$chain = postrouting_chain $interface;
$table = 'rawpost';
}
if ( $target eq 'DNAT' ) {
add_ijump( ensure_chain( $table, $chain ) ,
j => 'RAWDNAT',
targetopts => "--to-dest $net2",
imatch_source_net( $net3 ) ,
imatch_dest_net( $net1 ) ,
$match => $interface,
$interfaceref->{root} ? () : @rulein );
} else {
add_ijump( ensure_chain( $table, $chain ) ,
j => 'RAWSNAT',
targetopts => "--to-source $net2",
imatch_dest_net( $net3 ) ,
imatch_source_net( $net1 ) ,
$match => $interface,
$interfaceref->{root} ? () : @ruleout );
}
} else {
fatal_error "Invalid type ($type)";
}
progress_message " $net1 on $iface mapped to $net2 ($type)";
}
}
clear_comment;
}
}
sub add_addresses () {
if ( @addresses_to_add ) {
my @addrs = @addresses_to_add;

View File

@ -525,7 +525,7 @@ show_command() {
[ $# -eq 1 ] && usage 1
case $2 in
mangle|nat|filter|raw)
mangle|nat|filter|raw|rawpost)
table=$2
table_given=Yes
;;
@ -602,6 +602,13 @@ show_command() {
show_reset
$IPTABLES -t raw -L $g_ipt_options
;;
rawpost)
[ $# -gt 1 ] && usage 1
echo "$g_product $SHOREWALL_VERSION RAWPOST Table at $g_hostname - $(date)"
echo
show_reset
$IPTABLES -t rawpost -L $g_ipt_options
;;
tos|mangle)
[ $# -gt 1 ] && usage 1
echo "$g_product $SHOREWALL_VERSION Mangle Table at $g_hostname - $(date)"

View File

@ -1435,7 +1435,7 @@ usage() # $1 = exit status
echo " restart [ -n ] [ -p ] [-d] [ -f ] [ -c ][ <directory> ]"
echo " restore [ -n ] [ <file name> ]"
echo " save [ <file name> ]"
echo " show [ -x ] [ -t {filter|mangle|nat} ] [ {chain [<chain> [ <chain> ... ]"
echo " show [ -x ] [ -t {filter|mangle|nat|raw|rawpost} ] [ {chain [<chain> [ <chain> ... ]"
echo " show actions"
echo " show [ -f ] capabilities"
echo " show classifiers"
@ -1448,7 +1448,7 @@ usage() # $1 = exit status
echo " show [ -m ] log [<regex>]"
echo " show macro <macro>"
echo " show macros"
echo " show [ -x ] mangle|nat|raw|routing"
echo " show [ -x ] mangle|nat|raw|rawpost|routing"
echo " show policies"
echo " show tc [ device ]"
echo " show vardir"

View File

@ -510,7 +510,7 @@ show_command() {
[ $# -eq 1 ] && usage 1
case $2 in
mangle|nat|filter|raw)
mangle|nat|filter|raw|rawpost)
table=$2
table_given=Yes
;;

View File

@ -165,7 +165,12 @@
firewall 1.</para>
</listitem>
</itemizedlist>
</important> The entries in
</important></para>
<section id="Solution">
<title>If you are running Shorewall 4.4.22 or Earlier</title>
<para>The entries in
<filename><filename>/etc/shorewall/netmap</filename></filename> in
firewall1 would be as follows:</para>
@ -173,8 +178,8 @@
SNAT 192.168.1.0/24 vpn 10.10.11.0/24 #RULE 1A
DNAT 10.10.11.0/24 vpn 192.168.1.0/24 #RULE 1B</programlisting>
<para>The entry in <filename>/etc/shorewall/netmap</filename> in firewall2
would be:</para>
<para>The entry in <filename>/etc/shorewall/netmap</filename> in
firewall2 would be:</para>
<programlisting>#TYPE NET1 INTERFACE NET2
DNAT 10.10.10.0/24 vpn 192.168.1.0/24 #RULE 2A
@ -184,13 +189,13 @@ SNAT 192.168.1.0/24 vpn 10.10.10.0/24 #RULE 2B</programlist
<title>192.168.1.4 in the top cloud connects to 192.168.1.27 in the
bottom cloud</title>
<para>In order to make this connection, the client attempts a connection
to 10.10.10.27. The following table shows how the source and destination
IP addresses are modified as requests are sent and replies are returned.
The RULE column refers to the above
<para>In order to make this connection, the client attempts a
connection to 10.10.10.27. The following table shows how the source
and destination IP addresses are modified as requests are sent and
replies are returned. The RULE column refers to the above
<filename>/etc/shorewall/netmap</filename> entries and gives the rule
which transforms the source and destination IP addresses to those shown
on the next line. <informaltable>
which transforms the source and destination IP addresses to those
shown on the next line. <informaltable>
<tgroup cols="5">
<thead>
<row>
@ -281,9 +286,38 @@ SNAT 192.168.1.0/24 vpn 10.10.10.0/24 #RULE 2B</programlist
</tbody>
</tgroup>
</informaltable></para>
<para>See the<ulink url="OPENVPN.html"> OpenVPN documentation</ulink>
for a solution contributed by Nicola Moretti for resolving duplicate
networks in a roadwarrior VPN environment.</para>
</example>
</section>
<section>
<title>If you are running Shorewall 4.4.23 or Later</title>
<para>Beginning with Shorewall 4.4.23, you <emphasis>can</emphasis>
bridge two duplicate networks with one router, provided that your kernel
and iptables include <emphasis>Rawpost Table Support</emphasis>. That
support is used to implement Stateless NAT which allows for performing
DNAT in the rawpost table POSTROUTING and OUTPUT chains and for
performing SNAT in the raw table PREROUTING chain. Using this support,
only firewall1 requires <filename>/etc/shorewall/netmap</filename>. Two
additional entries are added.</para>
<programlisting>#TYPE NET1 INTERFACE NET2
SNAT 192.168.1.0/24 vpn 10.10.11.0/24
DNAT 10.10.11.0/24 vpn 192.168.1.0/24
<emphasis role="bold">SNAT:P 192.168.1.0/24 vpn 10.10.10.0/24
DNAT:T 10.10.10.0/24 vpn 192.168.1.0/24</emphasis></programlisting>
<para>The last two entries define Stateless NAT by specifying a chain
designator (:P for PREROUTING and :T for POSTROUTING respectively). See
<ulink url="manpages/shorewall-netlink.html">shorewall-netmap</ulink>
(5) for details.</para>
</section>
</section>
<section id="Notes">
<title>Author's Notes</title>
@ -301,19 +335,4 @@ SNAT 192.168.1.0/24 vpn 10.10.10.0/24 #RULE 2B</programlist
bottom cloud to be able to establish connections with the 192.168.1.0/24
network in the top cloud.</para>
</section>
<section id="WhyTwo">
<title>Can't I do this with one router? Why do I need two?</title>
<para>I wrote this article before Shorewall included <ulink
url="MultiISP.html">multiple provider support</ulink>. You should be able
to accomplish the same thing with just one router through careful use of
/etc/shorewall/netmap and <ulink url="MultiISP.html">multiple
providers</ulink>. If you try it and get it working, please contribute an
update to this article.</para>
<para>See the<ulink url="OPENVPN.html"> OpenVPN documentation</ulink> for
a solution contributed by Nicola Moretti for resolving duplicate networks
in a roadwarrior VPN environment.</para>
</section>
</article>

View File

@ -36,19 +36,39 @@
<variablelist>
<varlistentry>
<term><emphasis role="bold">TYPE</emphasis> - <emphasis
role="bold">DNAT</emphasis>|<emphasis
role="bold">SNAT</emphasis></term>
role="bold">{DNAT</emphasis>|<emphasis
role="bold">SNAT}[:{P|O|T}</emphasis>]</term>
<listitem>
<para>Must be DNAT or SNAT.</para>
<para>Must be DNAT or SNAT; beginning with Shorewall 4.4.23, may be
optionally followed by :P, :O or :T to perform <firstterm>stateless
NAT</firstterm>. Stateless NAT requires <firstterm>Rawpost Table
support</firstterm> in your kernel and iptables (see the output of
<command>shorewall show capabilities</command>).</para>
<para>If DNAT, traffic entering INTERFACE and addressed to NET1 has
its destination address rewritten to the corresponding address in
NET2.</para>
<para>If DNAT or DNAT:P, traffic entering INTERFACE and addressed to
NET1 has its destination address rewritten to the corresponding
address in NET2.</para>
<para>If SNAT, traffic leaving INTERFACE with a source address in
NET1 has it's source address rewritten to the corresponding address
in NET2.</para>
<para>If SNAT or SNAT:T, traffic leaving INTERFACE with a source
address in NET1 has it's source address rewritten to the
corresponding address in NET2.</para>
<para>If DNAT:O, traffic originating on the firewall and leaving via
INTERFACE and addressed to NET1 has its destination address
rewritten to the corresponding address in NET2.</para>
<para>If DNAT:P, traffic entering via INTERFACE and addressed to
NET1 has its destination address rewritten to the corresponding
address in NET2.</para>
<para>If SNAT:T, traffic leaving via INTERFACE with a source address
in NET1 has it's source address rewritten to the corresponding
address in NET2.</para>
<para>If SNAT:O, traffic originating on the firewall and leaving via
INTERFACE with a source address in NET1 has it's source address
rewritten to the corresponding address in NET2.</para>
</listitem>
</varlistentry>
@ -114,12 +134,13 @@
url="http://shorewall.net/netmap.html">http://shorewall.net/netmap.html</ulink></para>
<para>shorewall(8), shorewall-accounting(5), shorewall-actions(5),
shorewall-blacklist(5), shorewall-hosts(5), shorewall_interfaces(5), shorewall-ipsets(5),
shorewall-maclist(5), shorewall-masq(5), shorewall-nat(5),
shorewall-params(5), shorewall-policy(5), shorewall-providers(5),
shorewall-proxyarp(5), shorewall-route_rules(5),
shorewall-routestopped(5), shorewall-rules(5), shorewall.conf(5), shorewall-secmarks(5),
shorewall-tcclasses(5), shorewall-tcdevices(5), shorewall-tcrules(5),
shorewall-tos(5), shorewall-tunnels(5), shorewall-zones(5)</para>
shorewall-blacklist(5), shorewall-hosts(5), shorewall_interfaces(5),
shorewall-ipsets(5), shorewall-maclist(5), shorewall-masq(5),
shorewall-nat(5), shorewall-params(5), shorewall-policy(5),
shorewall-providers(5), shorewall-proxyarp(5), shorewall-route_rules(5),
shorewall-routestopped(5), shorewall-rules(5), shorewall.conf(5),
shorewall-secmarks(5), shorewall-tcclasses(5), shorewall-tcdevices(5),
shorewall-tcrules(5), shorewall-tos(5), shorewall-tunnels(5),
shorewall-zones(5)</para>
</refsect1>
</refentry>

View File

@ -463,7 +463,7 @@
<arg><option>-l</option></arg>
<arg><option>-t</option>
{<option>filter</option>|<option>mangle</option>|<option>nat</option>|<option>raw</option>}</arg>
{<option>filter</option>|<option>mangle</option>|<option>nat</option>|<option>raw|rawpost</option>}</arg>
<arg><arg><option>chain</option></arg><arg choice="plain"
rep="repeat"><replaceable>chain</replaceable></arg></arg>
@ -520,7 +520,7 @@
<arg><option>-x</option></arg>
<arg choice="req"><option>mangle|nat|raw</option></arg>
<arg choice="req"><option>mangle|nat|routing|raw|rawpost</option></arg>
</cmdsynopsis>
<cmdsynopsis>