From 44b8b78bf1498ab2e421420fcd7909e1fbd09716 Mon Sep 17 00:00:00 2001 From: teastep Date: Sun, 27 Aug 2006 19:42:06 +0000 Subject: [PATCH] Modularize NAT git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@4454 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb --- Shorewall/compiler | 800 +-------------------------------------- Shorewall/lib.nat | 540 ++++++++++++++++++++++++++ Shorewall/shorewall.spec | 2 + 3 files changed, 558 insertions(+), 784 deletions(-) create mode 100644 Shorewall/lib.nat diff --git a/Shorewall/compiler b/Shorewall/compiler index 894b44afc..3f833d0bc 100755 --- a/Shorewall/compiler +++ b/Shorewall/compiler @@ -641,262 +641,6 @@ disable_critical_hosts() done } -# -# Set up ipsec tunnels -# -setup_tunnels() # $1 = name of tunnels file -{ - local inchain - local outchain - local source - local dest - - setup_one_ipsec() # $1 = Tunnel Kind $2 = gateway zones - { - local kind=$1 noah= - - case $kind in - *:*) - noah=${kind#*:} - [ $noah = noah -o $noah = NOAH ] || fatal_error "Invalid IPSEC modifier $noah in tunnel \"$tunnel\"" - kind=${kind%:*} - ;; - esac - - [ $kind = IPSEC ] && kind=ipsec - - options="-m state --state NEW -j ACCEPT" - addrule2 $inchain -p 50 $source -j ACCEPT - addrule2 $outchain -p 50 $dest -j ACCEPT - - if [ -z "$noah" ]; then - run_iptables -A $inchain -p 51 $source -j ACCEPT - run_iptables -A $outchain -p 51 $dest -j ACCEPT - fi - - run_iptables -A $outchain -p udp $dest --dport 500 $options - - if [ $kind = ipsec ]; then - run_iptables -A $inchain -p udp $source --dport 500 $options - else - run_iptables -A $inchain -p udp $source --dport 500 $options - run_iptables -A $inchain -p udp $source --dport 4500 $options - fi - - for z in $(separate_list $2); do - if validate_zone $z; then - if [ -z "$POLICY_MATCH" ]; then - addrule ${z}2${FW} -p 50 $source -j ACCEPT - addrule ${FW}2${z} -p 50 $dest -j ACCEPT - if [ -z "$noah" ]; then - addrule ${z}2${FW} -p 51 $source -j ACCEPT - addrule ${FW}2${z} -p 51 $dest -j ACCEPT - fi - fi - if [ $kind = ipsec ]; then - addrule ${z}2${FW} -p udp $source --dport 500 $options - addrule ${FW}2${z} -p udp $dest --dport 500 $options - else - addrule ${z}2${FW} -p udp $source --dport 500 $options - addrule ${FW}2${z} -p udp $dest --dport 500 $options - addrule ${z}2${FW} -p udp $source --dport 4500 $options - addrule ${FW}2${z} -p udp $dest --dport 4500 $options - fi - else - fatal_error "Invalid gateway zone ($z) -- Tunnel \"$tunnel\"" - fi - done - - progress_message_and_save " IPSEC tunnel to $gateway defined." - } - - setup_one_other() # $1 = TYPE, $2 = protocol - { - addrule2 $inchain -p $2 $source -j ACCEPT - addrule2 $outchain -p $2 $dest -j ACCEPT - - progress_message_and_save " $1 tunnel to $gateway compiled." - } - - setup_pptp_client() - { - addrule2 $outchain -p 47 $dest -j ACCEPT - addrule2 $inchain -p 47 $source -j ACCEPT - addrule2 $outchain -p tcp --dport 1723 $dest -j ACCEPT - - progress_message_and_save " PPTP tunnel to $gateway defined." - } - - setup_pptp_server() - { - addrule2 $inchain -p 47 $source -j ACCEPT - addrule2 $outchain -p 47 $dest -j ACCEPT - addrule2 $inchain -p tcp --dport 1723 $source -j ACCEPT - - progress_message_and_save " PPTP server defined." - } - - setup_one_openvpn() # $1 = kind[:port] - { - local protocol=udp - local p=1194 - - case $1 in - *:*:*) - protocol=${1%:*} - protocol=${protocol#*:} - p=${1##*:} - ;; - *:tcp|*:udp|*:TCP|*:UDP) - protocol=${1#*:} - ;; - *:*) - p=${1#*:} - ;; - esac - - addrule2 $inchain -p $protocol $source --dport $p -j ACCEPT - addrule2 $outchain -p $protocol $dest --dport $p -j ACCEPT - - progress_message_and_save " OPENVPN tunnel to $gateway:$protocol:$p defined." - } - - setup_one_openvpn_server() # $1 = kind[:port] - { - local protocol=udp - local p=1194 - - case $1 in - *:*:*) - protocol=${1%:*} - protocol=${protocol#*:} - p=${1##*:} - ;; - *:tcp|*:udp|*:TCP|*:UDP) - protocol=${1#*:} - ;; - *:*) - p=${1#*:} - ;; - esac - - addrule2 $inchain -p $protocol $source --dport $p -j ACCEPT - addrule2 $outchain -p $protocol $dest --sport $p -j ACCEPT - - progress_message_and_save " OPENVPN server tunnel from $gateway:$protocol:$p defined." - } - - setup_one_openvpn_client() # $1 = kind[:port] - { - local protocol=udp - local p=1194 - - case $1 in - *:*:*) - protocol=${1%:*} - protocol=${protocol#*:} - p=${1##*:} - ;; - *:tcp|*:udp|*:TCP|*:UDP) - protocol=${1#*:} - ;; - *:*) - p=${1#*:} - ;; - esac - - addrule2 $inchain -p $protocol $source --sport $p -j ACCEPT - addrule2 $outchain -p $protocol $dest --dport $p -j ACCEPT - - progress_message_and_save " OPENVPN client tunnel to $gateway:$protocol:$p defined." - } - - setup_one_generic() # $1 = kind:protocol[:port] - { - local protocol - local p= - - case $1 in - *:*:*) - p=${1##*:} - protocol=${1%:*} - protocol=${protocol#*:} - ;; - *:*) - protocol=${1#*:} - ;; - *) - protocol=udp - p=5000 - ;; - esac - - p=${p:+--dport $p} - - addrule2 $inchain -p $protocol $source $p -j ACCEPT - addrule2 $outchain -p $protocol $dest $p -j ACCEPT - - progress_message_and_save " GENERIC tunnel to $1:$p defined." - } - - strip_file tunnels $1 - - while read kind z gateway z1; do - expandv kind z gateway z1 - tunnel="$(echo $kind $z $gateway $z1)" - if validate_zone $z; then - inchain=${z}2${FW} - outchain=${FW}2${z} - gateway=${gateway:-0.0.0.0/0} - source=$(source_ip_range $gateway) - dest=$(dest_ip_range $gateway) - - case $kind in - ipsec|IPSEC|ipsec:*|IPSEC:*) - setup_one_ipsec $kind $z1 - ;; - ipsecnat|IPSECNAT|ipsecnat:*|IPSECNAT:*) - setup_one_ipsec $kind $z1 - ;; - ipip|IPIP) - setup_one_other IPIP 4 - ;; - gre|GRE) - setup_one_other GRE 47 - ;; - 6to4|6TO4) - setup_one_other 6to4 41 - ;; - pptpclient|PPTPCLIENT) - setup_pptp_client - ;; - pptpserver|PPTPSERVER) - setup_pptp_server - ;; - openvpn|OPENVPN|openvpn:*|OPENVPN:*) - setup_one_openvpn $kind - ;; - openvpnclient|OPENVPNCLIENT|openvpnclient:*|OPENVPNCLIENT:*) - setup_one_openvpn_client $kind - ;; - openvpnserver|OPENVPNSERVER|openvpnserver:*|OPENVPNSERVER:*) - setup_one_openvpn_server $kind - ;; - generic:*|GENERIC:*) - setup_one_generic $kind - ;; - *) - error_message "WARNING: Tunnels of type $kind are not supported:" \ - "Tunnel \"$tunnel\" Ignored" - ;; - esac - else - error_message "ERROR: Invalid gateway zone ($z)" \ - " -- Tunnel \"$tunnel\" Ignored" - fi - done < $TMP_DIR/tunnels -} - # # Setup Proxy ARP # @@ -1290,84 +1034,6 @@ __EOF__ } -# -# Setup Static Network Address Translation (NAT) -# -setup_nat() { - local external= interface= internal= allints= localnat= policyin= policyout= - - validate_one() #1 = Variable Name, $2 = Column name, $3 = value - { - case $3 in - Yes|yes) - ;; - No|no) - eval ${1}= - ;; - *) - [ -n "$3" ] && \ - fatal_error "Invalid value ($3) for $2 in entry \"$external $interface $internal $allints $localnat\"" - ;; - esac - } - - do_one_nat() { - local add_ip_aliases=$ADD_IP_ALIASES iface=${interface%:*} - - if [ -n "$add_ip_aliases" ]; then - case $interface in - *:) - interface=${interface%:} - add_ip_aliases= - ;; - *) - [ -n "$RETAIN_ALIASES" ] || save_command del_ip_addr $external $iface - ;; - esac - else - interface=${interface%:} - fi - - validate_one allints "ALL INTERFACES" $allints - validate_one localnat "LOCAL" $localnat - - if [ -n "$allints" ]; then - addnatrule nat_in -d $external $policyin -j DNAT --to-destination $internal - addnatrule nat_out -s $internal $policyout -j SNAT --to-source $external - else - addnatrule $(input_chain $iface) -d $external $policyin -j DNAT --to-destination $internal - addnatrule $(output_chain $iface) -s $internal $policyout -j SNAT --to-source $external - fi - - [ -n "$localnat" ] && \ - run_iptables2 -t nat -A OUTPUT -d $external $policyout -j DNAT --to-destination $internal - - if [ -n "$add_ip_aliases" ]; then - list_search $external $ALIASES_TO_ADD || \ - ALIASES_TO_ADD="$ALIASES_TO_ADD $external $interface" - fi - } - # - # At this point, we're just interested in the network translation - # - > $STATEDIR/nat - - if [ -n "$POLICY_MATCH" ]; then - policyin="-m policy --pol none --dir in" - policyout="-m policy --pol none --dir out" - fi - - [ -n "$RETAIN_ALIASES" ] || save_progress_message "Setting up one-to-one NAT..." - - while read external interface internal allints localnat; do - expandv external interface internal allints localnat - - do_one_nat - - progress_message_and_save " Host $internal NAT $external on $interface" - done < $TMP_DIR/nat -} - # # Delete existing Static NAT # @@ -1390,34 +1056,6 @@ fi __EOF__ } -# -# Setup Network Mapping (NETMAP) -# -setup_netmap() { - - while read type net1 interface net2 ; do - expandv type net1 interface net2 - - list_search $interface $ALL_INTERFACES || \ - fatal_error "Unknown interface $interface in entry \"$type $net1 $interface $net2\"" - - case $type in - DNAT) - addnatrule $(input_chain $interface) -d $net1 -j NETMAP --to $net2 - ;; - SNAT) - addnatrule $(output_chain $interface) -s $net1 -j NETMAP --to $net2 - ;; - *) - fatal_error "Invalid type $type in entry \"$type $net1 $interface $net2\"" - ;; - esac - - progress_message_and_save " Network $net1 on $interface mapped to $net2 ($type)" - - done < $TMP_DIR/netmap -} - # # Setup ECN disabling rules # @@ -4541,420 +4179,6 @@ rules_chain() # $1 = source zone, $2 = destination zone fi } -# -# Set up Source NAT (including masquerading) -# -setup_masq() -{ - do_ipsec_options() { - local options="$(separate_list $ipsec)" option - policy="-m policy --pol ipsec --dir out" - - for option in $options; do - case $option in - [Yy]es) ;; - strict) policy="$policy --strict" ;; - next) policy="$policy --next" ;; - reqid=*) policy="$policy --reqid ${option#*=}" ;; - spi=*) policy="$policy --spi ${option#*=}" ;; - proto=*) policy="$policy --proto ${option#*=}" ;; - mode=*) policy="$policy --mode ${option#*=}" ;; - tunnel-src=*) policy="$policy --tunnel-src ${option#*=}" ;; - tunnel-dst=*) policy="$policy --tunnel-dst ${option#*=}" ;; - reqid!=*) policy="$policy ! --reqid ${option#*=}" ;; - spi!=*) policy="$policy ! --spi ${option#*=}" ;; - proto!=*) policy="$policy ! --proto ${option#*=}" ;; - mode!=*) policy="$policy ! --mode ${option#*=}" ;; - tunnel-src!=*) policy="$policy ! --tunnel-src ${option#*=}" ;; - tunnel-dst!=*) policy="$policy ! --tunnel-dst ${option#*=}" ;; - *) fatal_error "Invalid IPSEC option \"$option\"" ;; - esac - done - } - - setup_one() { - local add_snat_aliases=$ADD_SNAT_ALIASES pre_nat= policy= destnets= - - [ "x$ipsec" = x- ] && ipsec= - - case $ipsec in - Yes|yes) - [ -n "$POLICY_MATCH" ] || \ - fatal_error "IPSEC=Yes requires policy match support in your kernel and iptables" - policy="-m policy --pol ipsec --dir out" - ;; - No|no) - [ -n "$POLICY_MATCH" ] || \ - fatal_error "IPSEC=No requires policy match support in your kernel and iptables" - policy="-m policy --pol none --dir out" - ;; - *) - if [ -n "$ipsec" ]; then - do_ipsec_options - elif [ -n "$POLICY_MATCH" ]; then - policy="-m policy --pol none --dir out" - fi - ;; - esac - - case $fullinterface in - +*) - pre_nat=Yes - fullinterface=${fullinterface#+} - ;; - esac - - case $fullinterface in - *::*) - add_snat_aliases= - destnets="${fullinterface##*:}" - fullinterface="${fullinterface%:*}" - ;; - *:*:*) - # Both alias name and networks - destnets="${fullinterface##*:}" - fullinterface="${fullinterface%:*}" - ;; - *:) - add_snat_aliases= - fullinterface=${fullinterface%:} - ;; - *:*) - # Alias name OR networks - case ${fullinterface#*:} in - *.*) - # It's a networks - destnets="${fullinterface#*:}" - fullinterface="${fullinterface%:*}" - ;; - *) - #it's an alias name - ;; - esac - ;; - *) - ;; - esac - - interface=${fullinterface%:*} - - if ! list_search $interface $ALL_INTERFACES; then - fatal_error "Unknown interface $interface" - fi - - if [ "$networks" = "${networks%!*}" ]; then - nomasq= - else - nomasq="${networks#*!}" - networks="${networks%!*}" - fi - - source="${networks:=0.0.0.0/0}" - - detectinterface= - - case $source in - *.*.*|+*|!+*) - ;; - *) - detectinterface=$networks - networks= - ;; - esac - - [ "x$proto" = x- ] && proto= - [ "x$ports" = x- ] && ports= - - if [ -n "$proto" ]; then - - displayproto="($proto)" - - case $proto in - tcp|TCP|udp|UDP|6|17) - if [ -n "$ports" ]; then - displayproto="($proto $ports)" - - listcount=$(list_count $ports) - - if [ $listcount -gt 1 ]; then - case $ports in - *:*) - if [ -n "$XMULTIPORT" ]; then - if [ $(($listcount + $(list_count1 $(split $ports) ) )) -le 16 ]; then - ports="-m multiport --dports $ports" - else - fatal_error "More than 15 entries in port list ($ports)" - fi - else - fatal_error "Port Range not allowed in list ($ports)" - fi - ;; - *) - if [ -n "$MULTIPORT" ]; then - [ $listcount -le 15 ] || fatal_error "More than 15 entries in port list ($ports)" - ports="-m multiport --dports $ports" - else - fatal_error "Port Ranges require multiport match support in your kernel ($ports)" - fi - ;; - esac - else - ports="--dport $ports" - fi - fi - ;; - *) - [ -n "$ports" ] && fatal_error "Ports only allowed with UDP or TCP ($ports)" - ;; - esac - - proto="-p $proto" - else - displayproto="(all)" - [ -n "$ports" ] && fatal_error "Ports only allowed with UDP or TCP ($ports)" - fi - - destination=${destnets:=0.0.0.0/0} - - [ -z "$pre_nat" ] && chain=$(masq_chain $interface) || chain=$(snat_chain $interface) - - ensurenatchain $chain - - case $destnets in - !*) - destnets=${destnets#!} - - build_exclusion_chain newchain nat "$nomasq" "$destnets" - - if [ -n "$networks" ]; then - for s in $networks; do - addnatrule $chain $(source_ip_range $s) $proto $ports $policy -j $newchain - done - networks= - elif [ -n "$detectinterface" ]; then - indent >&3 << __EOF__ - -networks="\$(get_routed_networks $detectinterface)" - -[ -z "\$networks" ] && fatal_error "Unable to determine the routes through interface \"$detectinterface\"" - -for network in \$networks; do - run_iptables -t nat -A $chain -s \$network $proto $ports $policy -j $newchain -done - -__EOF__ - else - addnatrule $chain -j $newchain - fi - - chain=$newchain - destnets=0.0.0.0/0 - proto= - ports= - policy= - - [ -n "$nomasq" ] && source="$source except $nomasq" - ;; - *) - if [ -n "$nomasq" ]; then - build_exclusion_chain newchain nat $nomasq - - if [ -n "$networks" ]; then - for s in $networks; do - for destnet in $(separate_list $destnets); do - addnatrule $chain $(both_ip_ranges $s $destnet) $proto $ports $policy -j $newchain - done - done - elif [ -n "$detectinterface" ]; then - indent >&3 << __EOF__ - -networks="\$(get_routed_networks $detectinterface)" - -[ -z "\$networks" ] && fatal_error "Unable to determine the routes through interface \"$detectinterface\"" - -for network in \$networks; do -__EOF__ - for destnet in $(separate_list $destnets); do - indent >&3 << __EOF__ - run_iptables -t nat -A $chain -s \$network $(dest_ip_range $destnet) $proto $sports $policy -j $netchain -__EOF__ - done - indent >&3 << __EOF__ - -done -__EOF__ - else - for destnet in $(separate_list $destnets); do - addnatrule $chain $(dest_ip_range $destnet) $proto $ports $policy -j $newchain - done - fi - - chain=$newchain - networks= - destnets=0.0.0.0/0 - proto= - ports= - policy= - source="$source except $nomasq" - fi - - ;; - esac - - addrlist= - target=MASQUERADE - - [ "x$addresses" = x- ] && addresses= - - if [ -n "$addresses" ]; then - case "$addresses" in - SAME:nodst:*) - target="SAME --nodst" - addresses=${addresses#SAME:nodst:} - if [ "$addresses" = detect ]; then - addrlist='$addrlist' - else - for address in $(separate_list $addresses); do - addrlist="$addrlist --to $address"; - done - fi - ;; - SAME:*) - target="SAME" - addresses=${addresses#SAME:} - if [ "$addresses" = detect ]; then - addrlist='$addrlist' - else - for address in $(separate_list $addresses); do - addrlist="$addrlist --to $address"; - done - fi - ;; - detect) - target=SNAT - addrlist='$addrlist' - ;; - *) - for address in $(separate_list $addresses); do - case $address in - *.*.*.*) - target=SNAT - addrlist="$addrlist --to-source $address" - ;; - *) - addrlist="$addrlist --to-ports ${address#:}" - ;; - esac - done - ;; - esac - - if [ "$addrlist" = '$addrlist' ]; then - addresses='$(combine_list $addresses)' - indent >&3 << __EOF__ - -addrlist= -addressses=\$(find_interface_addresses $interface) - -if [ -n "\$addresses" ]; then - for address in \$addresses; do - addrlist="$addrlist --to-source $address" - done -else - fatal_error "Unable to determine the IP address(es) of $interface" -fi - -__EOF__ - elif [ -n "$add_snat_aliases" ]; then - for address in $(separate_list $addresses); do - address=${address%:)} - if [ -n "$address" ]; then - for addr in $(ip_range_explicit ${address%:*}) ; do - if ! list_search $addr $ALIASES_TO_ADD; then - [ -n "$RETAIN_ALIASES" ] || save_command del_ip_addr $addr $interface - ALIASES_TO_ADD="$ALIASES_TO_ADD $addr $fullinterface" - case $fullinterface in - *:*) - fullinterface=${fullinterface%:*}:$((${fullinterface#*:} + 1 )) - ;; - esac - fi - done - fi - done - fi - fi - - if [ -n "$networks" ]; then - for network in $networks; do - for destnet in $(separate_list $destnets); do - addnatrule $chain $(both_ip_ranges $network $destnet) $proto $ports $policy -j $target $addrlist - done - - if [ -n "$addresses" ]; then - progress_message_and_save " To $destination $displayproto from $network through ${interface} using $addresses" - else - progress_message_and_save " To $destination $displayproto from $network through ${interface}" - fi - done - elif [ -n "$detectinterface" ]; then - indent >&3 << __EOF__ - -networks="\$(get_routed_networks $detectinterface)" - -[ -z "\$networks" ] && fatal_error "Unable to determine the routes through interface \"$detectinterface\"" - -for network in \$networks; do -__EOF__ - for destnet in $(separate_list $destnets); do - indent >&3 << __EOF__ - run_iptables -t nat -A $chain -s \$network $(dest_ip_range $destnet) $proto $ports $policy -j $target $addrlist -__EOF__ - done - - if [ -n "$addresses" ]; then - message=" To $destination $displayproto from \$network through ${interface} using $addresses" - else - message=" To $destination $displayproto from \$network through ${interface}" - fi - - indent >&3 << __EOF__ - progress_message "$message" -done - -__EOF__ - - else - for destnet in $(separate_list $destnets); do - addnatrule $chain $(dest_ip_range $destnet) $proto $ports $policy -j $target $addrlist - done - - if [ -n "$addresses" ]; then - progress_message_and_save " To $destination $displayproto from $source through ${interface} using $addresses" - else - progress_message_and_save " To $destination $displayproto from $source through ${interface}" - fi - fi - - } - - strip_file masq $1 - - if [ -n "$NAT_ENABLED" ]; then - progress_message2 "$DOING Masquerading/SNAT" - save_progress_message "Setting up Masquerading/SNAT..." - fi - - while read fullinterface networks addresses proto ports ipsec; do - expandv fullinterface networks addresses proto ports ipsec - if [ -n "$NAT_ENABLED" ]; then - setup_one - else - error_message "WARNING: NAT disabled; masq rule ignored" - fi - done < $TMP_DIR/masq -} - # # Add a record to the blacklst chain # @@ -5220,8 +4444,6 @@ initialize_netfilter () { strip_file rules strip_file proxyarp strip_file maclist - strip_file nat - strip_file netmap progress_message2 "Pre-processing Actions..." process_actions1 @@ -6841,8 +6063,18 @@ __EOF__ [ -n "$ROUTEMARK_INTERFACES" ] && setup_routes fi - progress_message2 "$DOING NAT..."; setup_nat - progress_message2 "$DOING NETMAP..."; setup_netmap + if strip_file_and_lib_load nat nat; then + progress_message2 "$DOING NAT..." + setup_nat + else + > $STATEDIR/nat + fi + + if strip_file_and_lib_load netmap nat; then + progress_message2 "$DOING NETMAP..." + setup_netmap + fi + progress_message2 "$DOING Common Rules"; add_common_rules save_progress_message "Setting up SYN Flood Protection..." @@ -6864,8 +6096,8 @@ __EOF__ save_progress_message "Setting up Rules..." process_rules - tunnels=$(find_file tunnels) - if [ -f $tunnels ]; then + if strip_file_and_lib_load tunnels tunnels; then + tunnels=$(find_file tunnels) progress_message2 "$DOING $tunnels..." save_progress_message "Setting up Tunnels..." setup_tunnels $tunnels @@ -6880,8 +6112,8 @@ __EOF__ progress_message2 "$DOING $(find_file policy)..."; apply_policy_rules - masq=$(find_file masq) - if [ -f $masq ]; then + if strip_file_and_lib_load masq nat; then + masq=$(find_file masq) setup_masq $masq fi diff --git a/Shorewall/lib.nat b/Shorewall/lib.nat new file mode 100644 index 000000000..6dd20b7d0 --- /dev/null +++ b/Shorewall/lib.nat @@ -0,0 +1,540 @@ +#!/bin/sh +# +# Shorewall 3.2 -- /usr/share/shorewall/lib.nat +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 1999,2000,2001,2002,2003,2004,2005,2006 - Tom Eastep (teastep@shorewall.net) +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + +# +# Set up Source NAT (including masquerading) +# +setup_masq() +{ + do_ipsec_options() { + local options="$(separate_list $ipsec)" option + policy="-m policy --pol ipsec --dir out" + + for option in $options; do + case $option in + [Yy]es) ;; + strict) policy="$policy --strict" ;; + next) policy="$policy --next" ;; + reqid=*) policy="$policy --reqid ${option#*=}" ;; + spi=*) policy="$policy --spi ${option#*=}" ;; + proto=*) policy="$policy --proto ${option#*=}" ;; + mode=*) policy="$policy --mode ${option#*=}" ;; + tunnel-src=*) policy="$policy --tunnel-src ${option#*=}" ;; + tunnel-dst=*) policy="$policy --tunnel-dst ${option#*=}" ;; + reqid!=*) policy="$policy ! --reqid ${option#*=}" ;; + spi!=*) policy="$policy ! --spi ${option#*=}" ;; + proto!=*) policy="$policy ! --proto ${option#*=}" ;; + mode!=*) policy="$policy ! --mode ${option#*=}" ;; + tunnel-src!=*) policy="$policy ! --tunnel-src ${option#*=}" ;; + tunnel-dst!=*) policy="$policy ! --tunnel-dst ${option#*=}" ;; + *) fatal_error "Invalid IPSEC option \"$option\"" ;; + esac + done + } + + setup_one() { + local add_snat_aliases=$ADD_SNAT_ALIASES pre_nat= policy= destnets= + + [ "x$ipsec" = x- ] && ipsec= + + case $ipsec in + Yes|yes) + [ -n "$POLICY_MATCH" ] || \ + fatal_error "IPSEC=Yes requires policy match support in your kernel and iptables" + policy="-m policy --pol ipsec --dir out" + ;; + No|no) + [ -n "$POLICY_MATCH" ] || \ + fatal_error "IPSEC=No requires policy match support in your kernel and iptables" + policy="-m policy --pol none --dir out" + ;; + *) + if [ -n "$ipsec" ]; then + do_ipsec_options + elif [ -n "$POLICY_MATCH" ]; then + policy="-m policy --pol none --dir out" + fi + ;; + esac + + case $fullinterface in + +*) + pre_nat=Yes + fullinterface=${fullinterface#+} + ;; + esac + + case $fullinterface in + *::*) + add_snat_aliases= + destnets="${fullinterface##*:}" + fullinterface="${fullinterface%:*}" + ;; + *:*:*) + # Both alias name and networks + destnets="${fullinterface##*:}" + fullinterface="${fullinterface%:*}" + ;; + *:) + add_snat_aliases= + fullinterface=${fullinterface%:} + ;; + *:*) + # Alias name OR networks + case ${fullinterface#*:} in + *.*) + # It's a networks + destnets="${fullinterface#*:}" + fullinterface="${fullinterface%:*}" + ;; + *) + #it's an alias name + ;; + esac + ;; + *) + ;; + esac + + interface=${fullinterface%:*} + + if ! list_search $interface $ALL_INTERFACES; then + fatal_error "Unknown interface $interface" + fi + + if [ "$networks" = "${networks%!*}" ]; then + nomasq= + else + nomasq="${networks#*!}" + networks="${networks%!*}" + fi + + source="${networks:=0.0.0.0/0}" + + detectinterface= + + case $source in + *.*.*|+*|!+*) + ;; + *) + detectinterface=$networks + networks= + ;; + esac + + [ "x$proto" = x- ] && proto= + [ "x$ports" = x- ] && ports= + + if [ -n "$proto" ]; then + + displayproto="($proto)" + + case $proto in + tcp|TCP|udp|UDP|6|17) + if [ -n "$ports" ]; then + displayproto="($proto $ports)" + + listcount=$(list_count $ports) + + if [ $listcount -gt 1 ]; then + case $ports in + *:*) + if [ -n "$XMULTIPORT" ]; then + if [ $(($listcount + $(list_count1 $(split $ports) ) )) -le 16 ]; then + ports="-m multiport --dports $ports" + else + fatal_error "More than 15 entries in port list ($ports)" + fi + else + fatal_error "Port Range not allowed in list ($ports)" + fi + ;; + *) + if [ -n "$MULTIPORT" ]; then + [ $listcount -le 15 ] || fatal_error "More than 15 entries in port list ($ports)" + ports="-m multiport --dports $ports" + else + fatal_error "Port Ranges require multiport match support in your kernel ($ports)" + fi + ;; + esac + else + ports="--dport $ports" + fi + fi + ;; + *) + [ -n "$ports" ] && fatal_error "Ports only allowed with UDP or TCP ($ports)" + ;; + esac + + proto="-p $proto" + else + displayproto="(all)" + [ -n "$ports" ] && fatal_error "Ports only allowed with UDP or TCP ($ports)" + fi + + destination=${destnets:=0.0.0.0/0} + + [ -z "$pre_nat" ] && chain=$(masq_chain $interface) || chain=$(snat_chain $interface) + + ensurenatchain $chain + + case $destnets in + !*) + destnets=${destnets#!} + + build_exclusion_chain newchain nat "$nomasq" "$destnets" + + if [ -n "$networks" ]; then + for s in $networks; do + addnatrule $chain $(source_ip_range $s) $proto $ports $policy -j $newchain + done + networks= + elif [ -n "$detectinterface" ]; then + indent >&3 << __EOF__ + +networks="\$(get_routed_networks $detectinterface)" + +[ -z "\$networks" ] && fatal_error "Unable to determine the routes through interface \"$detectinterface\"" + +for network in \$networks; do + run_iptables -t nat -A $chain -s \$network $proto $ports $policy -j $newchain +done + +__EOF__ + else + addnatrule $chain -j $newchain + fi + + chain=$newchain + destnets=0.0.0.0/0 + proto= + ports= + policy= + + [ -n "$nomasq" ] && source="$source except $nomasq" + ;; + *) + if [ -n "$nomasq" ]; then + build_exclusion_chain newchain nat $nomasq + + if [ -n "$networks" ]; then + for s in $networks; do + for destnet in $(separate_list $destnets); do + addnatrule $chain $(both_ip_ranges $s $destnet) $proto $ports $policy -j $newchain + done + done + elif [ -n "$detectinterface" ]; then + indent >&3 << __EOF__ + +networks="\$(get_routed_networks $detectinterface)" + +[ -z "\$networks" ] && fatal_error "Unable to determine the routes through interface \"$detectinterface\"" + +for network in \$networks; do +__EOF__ + for destnet in $(separate_list $destnets); do + indent >&3 << __EOF__ + run_iptables -t nat -A $chain -s \$network $(dest_ip_range $destnet) $proto $sports $policy -j $netchain +__EOF__ + done + indent >&3 << __EOF__ + +done +__EOF__ + else + for destnet in $(separate_list $destnets); do + addnatrule $chain $(dest_ip_range $destnet) $proto $ports $policy -j $newchain + done + fi + + chain=$newchain + networks= + destnets=0.0.0.0/0 + proto= + ports= + policy= + source="$source except $nomasq" + fi + + ;; + esac + + addrlist= + target=MASQUERADE + + [ "x$addresses" = x- ] && addresses= + + if [ -n "$addresses" ]; then + case "$addresses" in + SAME:nodst:*) + target="SAME --nodst" + addresses=${addresses#SAME:nodst:} + if [ "$addresses" = detect ]; then + addrlist='$addrlist' + else + for address in $(separate_list $addresses); do + addrlist="$addrlist --to $address"; + done + fi + ;; + SAME:*) + target="SAME" + addresses=${addresses#SAME:} + if [ "$addresses" = detect ]; then + addrlist='$addrlist' + else + for address in $(separate_list $addresses); do + addrlist="$addrlist --to $address"; + done + fi + ;; + detect) + target=SNAT + addrlist='$addrlist' + ;; + *) + for address in $(separate_list $addresses); do + case $address in + *.*.*.*) + target=SNAT + addrlist="$addrlist --to-source $address" + ;; + *) + addrlist="$addrlist --to-ports ${address#:}" + ;; + esac + done + ;; + esac + + if [ "$addrlist" = '$addrlist' ]; then + addresses='$(combine_list $addresses)' + indent >&3 << __EOF__ + +addrlist= +addressses=\$(find_interface_addresses $interface) + +if [ -n "\$addresses" ]; then + for address in \$addresses; do + addrlist="$addrlist --to-source $address" + done +else + fatal_error "Unable to determine the IP address(es) of $interface" +fi + +__EOF__ + elif [ -n "$add_snat_aliases" ]; then + for address in $(separate_list $addresses); do + address=${address%:)} + if [ -n "$address" ]; then + for addr in $(ip_range_explicit ${address%:*}) ; do + if ! list_search $addr $ALIASES_TO_ADD; then + [ -n "$RETAIN_ALIASES" ] || save_command del_ip_addr $addr $interface + ALIASES_TO_ADD="$ALIASES_TO_ADD $addr $fullinterface" + case $fullinterface in + *:*) + fullinterface=${fullinterface%:*}:$((${fullinterface#*:} + 1 )) + ;; + esac + fi + done + fi + done + fi + fi + + if [ -n "$networks" ]; then + for network in $networks; do + for destnet in $(separate_list $destnets); do + addnatrule $chain $(both_ip_ranges $network $destnet) $proto $ports $policy -j $target $addrlist + done + + if [ -n "$addresses" ]; then + progress_message_and_save " To $destination $displayproto from $network through ${interface} using $addresses" + else + progress_message_and_save " To $destination $displayproto from $network through ${interface}" + fi + done + elif [ -n "$detectinterface" ]; then + indent >&3 << __EOF__ + +networks="\$(get_routed_networks $detectinterface)" + +[ -z "\$networks" ] && fatal_error "Unable to determine the routes through interface \"$detectinterface\"" + +for network in \$networks; do +__EOF__ + for destnet in $(separate_list $destnets); do + indent >&3 << __EOF__ + run_iptables -t nat -A $chain -s \$network $(dest_ip_range $destnet) $proto $ports $policy -j $target $addrlist +__EOF__ + done + + if [ -n "$addresses" ]; then + message=" To $destination $displayproto from \$network through ${interface} using $addresses" + else + message=" To $destination $displayproto from \$network through ${interface}" + fi + + indent >&3 << __EOF__ + progress_message "$message" +done + +__EOF__ + + else + for destnet in $(separate_list $destnets); do + addnatrule $chain $(dest_ip_range $destnet) $proto $ports $policy -j $target $addrlist + done + + if [ -n "$addresses" ]; then + progress_message_and_save " To $destination $displayproto from $source through ${interface} using $addresses" + else + progress_message_and_save " To $destination $displayproto from $source through ${interface}" + fi + fi + + } + + if [ -n "$NAT_ENABLED" ]; then + progress_message2 "$DOING Masquerading/SNAT" + save_progress_message "Setting up Masquerading/SNAT..." + fi + + while read fullinterface networks addresses proto ports ipsec; do + expandv fullinterface networks addresses proto ports ipsec + if [ -n "$NAT_ENABLED" ]; then + setup_one + else + error_message "WARNING: NAT disabled; masq rule ignored" + fi + done < $TMP_DIR/masq +} + +# +# Setup Static Network Address Translation (NAT) +# +setup_nat() { + local external= interface= internal= allints= localnat= policyin= policyout= + + validate_one() #1 = Variable Name, $2 = Column name, $3 = value + { + case $3 in + Yes|yes) + ;; + No|no) + eval ${1}= + ;; + *) + [ -n "$3" ] && \ + fatal_error "Invalid value ($3) for $2 in entry \"$external $interface $internal $allints $localnat\"" + ;; + esac + } + + do_one_nat() { + local add_ip_aliases=$ADD_IP_ALIASES iface=${interface%:*} + + if [ -n "$add_ip_aliases" ]; then + case $interface in + *:) + interface=${interface%:} + add_ip_aliases= + ;; + *) + [ -n "$RETAIN_ALIASES" ] || save_command del_ip_addr $external $iface + ;; + esac + else + interface=${interface%:} + fi + + validate_one allints "ALL INTERFACES" $allints + validate_one localnat "LOCAL" $localnat + + if [ -n "$allints" ]; then + addnatrule nat_in -d $external $policyin -j DNAT --to-destination $internal + addnatrule nat_out -s $internal $policyout -j SNAT --to-source $external + else + addnatrule $(input_chain $iface) -d $external $policyin -j DNAT --to-destination $internal + addnatrule $(output_chain $iface) -s $internal $policyout -j SNAT --to-source $external + fi + + [ -n "$localnat" ] && \ + run_iptables2 -t nat -A OUTPUT -d $external $policyout -j DNAT --to-destination $internal + + if [ -n "$add_ip_aliases" ]; then + list_search $external $ALIASES_TO_ADD || \ + ALIASES_TO_ADD="$ALIASES_TO_ADD $external $interface" + fi + } + # + # At this point, we're just interested in the network translation + # + > $STATEDIR/nat + + if [ -n "$POLICY_MATCH" ]; then + policyin="-m policy --pol none --dir in" + policyout="-m policy --pol none --dir out" + fi + + [ -n "$RETAIN_ALIASES" ] || save_progress_message "Setting up one-to-one NAT..." + + while read external interface internal allints localnat; do + expandv external interface internal allints localnat + + do_one_nat + + progress_message_and_save " Host $internal NAT $external on $interface" + done < $TMP_DIR/nat +} + +# +# Setup Network Mapping (NETMAP) +# +setup_netmap() { + + while read type net1 interface net2 ; do + expandv type net1 interface net2 + + list_search $interface $ALL_INTERFACES || \ + fatal_error "Unknown interface $interface in entry \"$type $net1 $interface $net2\"" + + case $type in + DNAT) + addnatrule $(input_chain $interface) -d $net1 -j NETMAP --to $net2 + ;; + SNAT) + addnatrule $(output_chain $interface) -s $net1 -j NETMAP --to $net2 + ;; + *) + fatal_error "Invalid type $type in entry \"$type $net1 $interface $net2\"" + ;; + esac + + progress_message_and_save " Network $net1 on $interface mapped to $net2 ($type)" + + done < $TMP_DIR/netmap +} diff --git a/Shorewall/shorewall.spec b/Shorewall/shorewall.spec index 3027ca86c..a3e170554 100644 --- a/Shorewall/shorewall.spec +++ b/Shorewall/shorewall.spec @@ -116,8 +116,10 @@ fi %attr(0555,root,root) /usr/share/shorewall/help %attr(0644,root,root) /usr/share/shorewall/Limit %attr(0444,root,root) /usr/share/shorewall/lib.dynamiczones +%attr(0444,root,root) /usr/share/shorewall/lib.nat %attr(0444,root,root) /usr/share/shorewall/lib.tc %attr(0444,root,root) /usr/share/shorewall/lib.tcrules +%attr(0444,root,root) /usr/share/shorewall/lib.tunnels %attr(0644,root,root) /usr/share/shorewall/macro.AllowICMPs %attr(0644,root,root) /usr/share/shorewall/macro.Amanda %attr(0644,root,root) /usr/share/shorewall/macro.Auth