diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index 21382347c..9f30d3bd3 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -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; @@ -491,16 +496,18 @@ my @unique_options = ( qw/p dport sport s d i o/ ); sub initialize( $$$ ) { ( $family, my $hard, $export ) = @_; - %chain_table = ( raw => {}, - mangle => {}, - nat => {}, - filter => {} ); + %chain_table = ( raw => {}, + rawpost => {}, + mangle => {}, + nat => {}, + filter => {} ); - $raw_table = $chain_table{raw}; - $nat_table = $chain_table{nat}; - $mangle_table = $chain_table{mangle}; - $filter_table = $chain_table{filter}; - %renamed = (); + $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}; + %renamed = (); # # Contents of last COMMENT line. # @@ -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'; @@ -5432,9 +5467,10 @@ sub create_netfilter_load( $ ) { my @table_list; - push @table_list, 'raw' if have_capability( 'RAW_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, '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'; $mode = NULL_MODE; @@ -5534,9 +5570,10 @@ sub preview_netfilter_load() { my @table_list; - push @table_list, 'raw' if have_capability( 'RAW_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, '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'; $mode = NULL_MODE; @@ -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; @@ -5748,9 +5785,10 @@ sub create_stop_load( $ ) { my @table_list; - push @table_list, 'raw' if have_capability( 'RAW_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, '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'; my $utility = $family == F_IPV4 ? 'iptables-restore' : 'ip6tables-restore'; diff --git a/Shorewall/Perl/Shorewall/Config.pm b/Shorewall/Perl/Shorewall/Config.pm index 7da070f39..968007fc2 100644 --- a/Shorewall/Perl/Shorewall/Config.pm +++ b/Shorewall/Perl/Shorewall/Config.pm @@ -307,6 +307,7 @@ our %config_files = ( #accounting => 1, refresh => 1, refreshed => 1, restored => 1, + rawnat => 1, route_rules => 1, routes => 1, routestopped => 1, diff --git a/Shorewall/Perl/Shorewall/Misc.pm b/Shorewall/Perl/Shorewall/Misc.pm index 3454ed9e8..363931b8a 100644 --- a/Shorewall/Perl/Shorewall/Misc.pm +++ b/Shorewall/Perl/Shorewall/Misc.pm @@ -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 diff --git a/Shorewall/Perl/Shorewall/Nat.pm b/Shorewall/Perl/Shorewall/Nat.pm index 9ef690722..f361de34d 100644 --- a/Shorewall/Perl/Shorewall/Nat.pm +++ b/Shorewall/Perl/Shorewall/Nat.pm @@ -414,6 +414,95 @@ sub setup_netmap() { $net3 = ALLIP if $net3 eq '-'; + for my $interface ( split_list $interfacelist, 'interface' ) { + + 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 ); + $interface = $interfaceref->{name}; + } + + if ( $type eq 'DNAT' ) { + add_ijump ensure_chain( 'nat' , input_chain $interface ) , j => "NETMAP --to $net2", @rulein , imatch_source_net( $net3 ), d => $net1; + } elsif ( $type eq 'SNAT' ) { + add_ijump ensure_chain( 'nat' , output_chain $interface ) , j => "NETMAP --to $net2", @ruleout , imatch_dest_net( $net3 ) , s => $net1; + } 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)"; + } + } + + clear_comment; + } + +} + +# +# 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; @@ -428,15 +517,43 @@ sub setup_netmap() { $interface = $interfaceref->{name}; } - if ( $type eq 'DNAT' ) { - add_ijump ensure_chain( 'nat' , input_chain $interface ) , j => "NETMAP --to $net2", @rulein , imatch_source_net( $net3 ), d => $net1; - } elsif ( $type eq 'SNAT' ) { - add_ijump ensure_chain( 'nat' , output_chain $interface ) , j => "NETMAP --to $net2", @ruleout , imatch_dest_net( $net3 ) , s => $net1; + 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 " Network $net1 on $iface mapped to $net2 ($type)"; + progress_message " $net1 on $iface mapped to $net2 ($type)"; } } diff --git a/Shorewall/lib.cli b/Shorewall/lib.cli index bbe2f1440..47896501c 100644 --- a/Shorewall/lib.cli +++ b/Shorewall/lib.cli @@ -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)" diff --git a/Shorewall/shorewall b/Shorewall/shorewall index cf5fbe5c8..f41a30def 100755 --- a/Shorewall/shorewall +++ b/Shorewall/shorewall @@ -1435,7 +1435,7 @@ usage() # $1 = exit status echo " restart [ -n ] [ -p ] [-d] [ -f ] [ -c ][ ]" echo " restore [ -n ] [ ]" echo " save [ ]" - echo " show [ -x ] [ -t {filter|mangle|nat} ] [ {chain [ [ ... ]" + echo " show [ -x ] [ -t {filter|mangle|nat|raw|rawpost} ] [ {chain [ [ ... ]" echo " show actions" echo " show [ -f ] capabilities" echo " show classifiers" @@ -1448,7 +1448,7 @@ usage() # $1 = exit status echo " show [ -m ] log []" echo " show 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" diff --git a/Shorewall6/lib.cli b/Shorewall6/lib.cli index cba53a1af..72bfa301b 100644 --- a/Shorewall6/lib.cli +++ b/Shorewall6/lib.cli @@ -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 ;; diff --git a/docs/netmap.xml b/docs/netmap.xml index fa8deedee..7d92e7b0a 100644 --- a/docs/netmap.xml +++ b/docs/netmap.xml @@ -165,123 +165,157 @@ firewall 1. - The entries in - /etc/shorewall/netmap in - firewall1 would be as follows: + - #TYPE NET1 INTERFACE NET2 +
+ If you are running Shorewall 4.4.22 or Earlier + + The entries in + /etc/shorewall/netmap in + firewall1 would be as follows: + + #TYPE NET1 INTERFACE NET2 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 - The entry in /etc/shorewall/netmap in firewall2 - would be: + The entry in /etc/shorewall/netmap in + firewall2 would be: - #TYPE NET1 INTERFACE NET2 + #TYPE NET1 INTERFACE NET2 DNAT 10.10.10.0/24 vpn 192.168.1.0/24 #RULE 2A SNAT 192.168.1.0/24 vpn 10.10.10.0/24 #RULE 2B - - 192.168.1.4 in the top cloud connects to 192.168.1.27 in the - bottom cloud + + 192.168.1.4 in the top cloud connects to 192.168.1.27 in the + bottom cloud - 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 - /etc/shorewall/netmap entries and gives the rule - which transforms the source and destination IP addresses to those shown - on the next line. - - - - FROM + 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 + /etc/shorewall/netmap entries and gives the rule + which transforms the source and destination IP addresses to those + shown on the next line. + + + + FROM - TO + TO - SOURCE IP ADDRESS + SOURCE IP ADDRESS - DESTINATION IP ADDRESS + DESTINATION IP ADDRESS - RULE - - + RULE + + - - - 192.168.1.4 in upper cloud + + + 192.168.1.4 in upper cloud - Firewall 1 + Firewall 1 - 192.168.1.4 + 192.168.1.4 - 10.10.10.27 + 10.10.10.27 - 1A - + 1A + - - Firewall 1 + + Firewall 1 - Firewall 2 + Firewall 2 - 10.10.11.4 + 10.10.11.4 - 10.10.10.27 + 10.10.10.27 - 2A - + 2A + - - Firewall 2 + + Firewall 2 - 192.168.1.27 in lower cloud + 192.168.1.27 in lower cloud - 10.10.11.4 + 10.10.11.4 - 192.168.1.27 + 192.168.1.27 - - + + - - 192.168.1.27 in the lower cloud + + 192.168.1.27 in the lower cloud - Firewall 2 + Firewall 2 - 192.168.1.27 + 192.168.1.27 - 10.10.11.4 + 10.10.11.4 - 2B - + 2B + - - Firewall 2 + + Firewall 2 - Firewall 1 + Firewall 1 - 10.10.10.27 + 10.10.10.27 - 10.10.11.4 + 10.10.11.4 - 1B - + 1B + - - Firewall 1 + + Firewall 1 - 192.168.1.4 in upper cloud + 192.168.1.4 in upper cloud - 10.10.10.27 + 10.10.10.27 - 192.168.1.4 + 192.168.1.4 - - - - - - + + + + + + + See the OpenVPN documentation + for a solution contributed by Nicola Moretti for resolving duplicate + networks in a roadwarrior VPN environment. + +
+ +
+ If you are running Shorewall 4.4.23 or Later + + Beginning with Shorewall 4.4.23, you can + bridge two duplicate networks with one router, provided that your kernel + and iptables include Rawpost Table Support. 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 /etc/shorewall/netmap. Two + additional entries are added. + + #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 +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 + + The last two entries define Stateless NAT by specifying a chain + designator (:P for PREROUTING and :T for POSTROUTING respectively). See + shorewall-netmap + (5) for details. +
@@ -301,19 +335,4 @@ SNAT 192.168.1.0/24 vpn 10.10.10.0/24 #RULE 2B
- -
- Can't I do this with one router? Why do I need two? - - I wrote this article before Shorewall included multiple provider support. You should be able - to accomplish the same thing with just one router through careful use of - /etc/shorewall/netmap and multiple - providers. If you try it and get it working, please contribute an - update to this article. - - See the OpenVPN documentation for - a solution contributed by Nicola Moretti for resolving duplicate networks - in a roadwarrior VPN environment. -
diff --git a/manpages/shorewall-netmap.xml b/manpages/shorewall-netmap.xml index 9399285db..ce4b6166e 100644 --- a/manpages/shorewall-netmap.xml +++ b/manpages/shorewall-netmap.xml @@ -36,19 +36,39 @@ TYPE - DNAT|SNAT + role="bold">{DNAT|SNAT}[:{P|O|T}] - Must be DNAT or SNAT. + Must be DNAT or SNAT; beginning with Shorewall 4.4.23, may be + optionally followed by :P, :O or :T to perform stateless + NAT. Stateless NAT requires Rawpost Table + support in your kernel and iptables (see the output of + shorewall show capabilities). - If DNAT, traffic entering INTERFACE and addressed to NET1 has - its destination address rewritten to the corresponding address in - NET2. + If DNAT or DNAT:P, traffic entering INTERFACE and addressed to + NET1 has its destination address rewritten to the corresponding + address in NET2. - If SNAT, traffic leaving INTERFACE with a source address in - NET1 has it's source address rewritten to the corresponding address - in NET2. + 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. + + 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. + + If DNAT:P, traffic entering via INTERFACE and addressed to + NET1 has its destination address rewritten to the corresponding + address in NET2. + + 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. + + 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. @@ -114,12 +134,13 @@ url="http://shorewall.net/netmap.html">http://shorewall.net/netmap.html 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) + 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) diff --git a/manpages/shorewall.xml b/manpages/shorewall.xml index 8a3dba6b0..581504288 100644 --- a/manpages/shorewall.xml +++ b/manpages/shorewall.xml @@ -463,7 +463,7 @@ - {|||} + {|||} chain @@ -520,7 +520,7 @@ - +