mirror of
https://gitlab.com/shorewall/code.git
synced 2024-12-27 00:29:02 +01:00
f1567e6e3d
git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@8492 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb
812 lines
21 KiB
Bash
812 lines
21 KiB
Bash
#!/bin/sh
|
|
#
|
|
# Shorewall 4.2 -- /usr/share/shorewall/lib.nat
|
|
#
|
|
# This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt]
|
|
#
|
|
# (c) 1999,2000,2001,2002,2003,2004,2005,2006,2007 - 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
#
|
|
# This library is loaded by /usr/share/shorewall/compiler when any of the following
|
|
# configuration files are non-empty: masq, nat, netmap; or when there are
|
|
# DNAT/REDIRECT rules in the /etc/shorewall/rules file.
|
|
#
|
|
|
|
#
|
|
# Set up Source NAT (including masquerading)
|
|
#
|
|
setup_masq()
|
|
{
|
|
local comment
|
|
comment=
|
|
|
|
do_ipsec_options() {
|
|
local options
|
|
options="$(separate_list $ipsec)"
|
|
local option
|
|
[ -n "$ORIGINAL_POLICY_MATCH" ] || \
|
|
fatal_error "IPSEC options require policy match support in your kernel and iptables"
|
|
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=$ADD_SNAT_ALIASES
|
|
local pre_nat
|
|
pre_nat=
|
|
local policy
|
|
policy=
|
|
local destnets
|
|
destnets=
|
|
|
|
[ "x$ipsec" = x- ] && ipsec=
|
|
|
|
case $ipsec in
|
|
Yes|yes)
|
|
[ -n "$ORIGINAL_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 "$ORIGINAL_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=
|
|
|
|
[ "x$mark" = x- ] && mark=
|
|
|
|
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
|
|
|
|
if [ -n "$mark" ]; then
|
|
displaymark="($mark)"
|
|
if [ "$mark" = "${mark%!*}" ]; then
|
|
mark="-m mark --mark $mark"
|
|
else
|
|
mark="-m mark ! --mark ${mark#*!}"
|
|
fi
|
|
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 $(separate_list $networks); do
|
|
addnatrule $chain $(source_ip_range $s) $proto $ports $mark $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 $mark $policy -j $newchain
|
|
done
|
|
|
|
__EOF__
|
|
else
|
|
addnatrule $chain -j $newchain
|
|
fi
|
|
|
|
chain=$newchain
|
|
destnets=0.0.0.0/0
|
|
proto=
|
|
ports=
|
|
policy=
|
|
detectinterface=
|
|
|
|
[ -n "$nomasq" ] && source="$source except $nomasq"
|
|
;;
|
|
*)
|
|
if [ -n "$nomasq" ]; then
|
|
build_exclusion_chain newchain nat $nomasq
|
|
|
|
if [ -n "$networks" ]; then
|
|
for s in $(separate_list $networks); do
|
|
for destnet in $(separate_list $destnets); do
|
|
addnatrule $chain $(both_ip_ranges $s $destnet) $proto $ports $mark $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 $ports $mark $policy -j $newchain
|
|
__EOF__
|
|
done
|
|
indent >&3 << __EOF__
|
|
|
|
done
|
|
__EOF__
|
|
else
|
|
for destnet in $(separate_list $destnets); do
|
|
addnatrule $chain $(dest_ip_range $destnet) $proto $ports $mark $policy -j $newchain
|
|
done
|
|
fi
|
|
|
|
chain=$newchain
|
|
networks=
|
|
destnets=0.0.0.0/0
|
|
proto=
|
|
ports=
|
|
policy=
|
|
detectinterface=
|
|
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=
|
|
addresses=\$(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 $(separate_list $networks); do
|
|
for destnet in $(separate_list $destnets); do
|
|
addnatrule $chain $(both_ip_ranges $network $destnet) $proto $ports $mark $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 $mark $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 $mark $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
|
|
|
|
} #setup_one()
|
|
|
|
if [ -s $TMP_DIR/masq ]; then
|
|
progress_message2 "$DOING Masquerading/SNAT"
|
|
save_progress_message "Setting up Masquerading/SNAT..."
|
|
|
|
while read fullinterface networks addresses proto ports ipsec mark; do
|
|
if [ -n "$NAT_ENABLED" ]; then
|
|
if [ "x$fullinterface" = xCOMMENT ]; then
|
|
if [ -n "$COMMENTS" ]; then
|
|
comment=$(echo $networks $addresses $proto $ports $ipsec $mark)
|
|
save_command COMMENT=\"$comment\"
|
|
else
|
|
error_message "COMMENT ignored -- requires comment support in iptables/Netfilter"
|
|
fi
|
|
else
|
|
setup_one
|
|
fi
|
|
else
|
|
error_message "WARNING: NAT disabled; masq rule ignored"
|
|
fi
|
|
done < $TMP_DIR/masq
|
|
#
|
|
# Just in case the file ended with a comment
|
|
#
|
|
if [ -n "$COMMENTS" ]; then
|
|
save_command
|
|
save_command COMMENT=
|
|
save_command
|
|
fi
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Setup Static Network Address Translation (NAT)
|
|
#
|
|
setup_nat() {
|
|
local external
|
|
external=
|
|
local interface
|
|
interface=
|
|
local internal
|
|
internal=
|
|
local allints
|
|
allints=
|
|
local localnat
|
|
localnat=
|
|
local policyin
|
|
policyin=
|
|
local policyout
|
|
policyout=
|
|
local comment
|
|
comment=
|
|
|
|
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=$ADD_IP_ALIASES
|
|
local iface
|
|
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
|
|
|
|
if [ -s $TMP_DIR/nat ]; then
|
|
save_progress_message "Setting up one-to-one NAT..."
|
|
|
|
while read external interface internal allints localnat; do
|
|
|
|
if [ "x$external" = xCOMMENT ]; then
|
|
if [ -n "$COMMENTS" ]; then
|
|
comment=$(echo $interface $internal $allints $localnat)
|
|
save_command COMMENT=\"$comment\"
|
|
else
|
|
error_message "COMMENT ignored -- requires comment support in iptables/Netfilter"
|
|
fi
|
|
else
|
|
do_one_nat
|
|
fi
|
|
progress_message_and_save " Host $internal NAT $external on $interface"
|
|
done < $TMP_DIR/nat
|
|
|
|
if [ -n "$COMMENTS" ]; then
|
|
save_command
|
|
save_command COMMENT=
|
|
save_command
|
|
fi
|
|
fi
|
|
|
|
}
|
|
|
|
#
|
|
# Setup Network Mapping (NETMAP)
|
|
#
|
|
setup_netmap() {
|
|
|
|
while read type net1 interface net2 ; do
|
|
|
|
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
|
|
}
|
|
|
|
#
|
|
# Add a NAT rule - Helper function for the rules file processor
|
|
#
|
|
# The caller has established the following variables:
|
|
# cli = Source IP, interface or MAC Specification
|
|
# serv = Destination IP Specification
|
|
# servport = Port the server is listening on
|
|
# dest_interface = Destination Interface Specification
|
|
# proto = Protocol Specification
|
|
# addr = Original Destination Address
|
|
# dports = Destination Port Specification. 'dports' may be changed
|
|
# by this function
|
|
# cport = Source Port Specification
|
|
# multiport = String to invoke multiport match if appropriate
|
|
# ratelimit = Optional rate limiting clause
|
|
# userandgroup = -m owner match to limit the rule to a particular user and/or group
|
|
# logtag = Log tag
|
|
# excludesource = Source Exclusion List
|
|
#
|
|
add_nat_rule() {
|
|
local chain
|
|
local excludedests
|
|
excludedests=
|
|
|
|
# Be sure we can NAT
|
|
|
|
if [ -z "$NAT_ENABLED" ]; then
|
|
fatal_error "Rule \"$rule\" requires NAT which is disabled"
|
|
fi
|
|
|
|
# Parse SNAT address if any
|
|
|
|
if [ "$addr" != "${addr%:*}" ]; then
|
|
fatal_error "SNAT may no longer be specified in a DNAT rule; use ${CONFDIR}/masq instead"
|
|
fi
|
|
|
|
# Set original destination address
|
|
|
|
case $addr in
|
|
all)
|
|
addr=
|
|
;;
|
|
detect)
|
|
eval interfaces=\$${source}_interfaces
|
|
|
|
if [ -n "$DETECT_DNAT_IPADDRS" -a "$source" != "$FW" ]; then
|
|
|
|
save_command
|
|
if [ $(list_count1 $interfaces) -eq 1 ]; then
|
|
save_command "addr=\$(find_first_interface_address $interfaces)"
|
|
else
|
|
save_command "addr="
|
|
for interface in $interfaces; do
|
|
ident >&3 << __EOF__
|
|
addr="\$addr \$(find_first_interface_address $interface)"
|
|
__EOF__
|
|
done
|
|
fi
|
|
else
|
|
addr=
|
|
fi
|
|
;;
|
|
!*)
|
|
if [ $(list_count $addr) -gt 1 ]; then
|
|
excludedests="${addr#\!}"
|
|
addr=
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
addr=${addr:-0.0.0.0/0}
|
|
|
|
# Select target
|
|
|
|
if [ "$logtarget" = SAME ]; then
|
|
[ -n "$servport" ] && fatal_error "Port mapping not allowed in SAME rules"
|
|
serv1=
|
|
for srv in $(separate_list $serv); do
|
|
serv1="$serv1 --to ${srv}"
|
|
done
|
|
target1="SAME $serv1"
|
|
elif [ -n "$serv" ]; then
|
|
servport="${servport:+:$servport}"
|
|
serv1=
|
|
for srv in $(separate_list $serv); do
|
|
serv1="$serv1 --to-destination ${srv}${servport}"
|
|
done
|
|
target1="DNAT $serv1"
|
|
else
|
|
target1="REDIRECT --to-port $servport"
|
|
fi
|
|
|
|
# Generate nat table rules
|
|
|
|
if [ "$source" = "$FW" ]; then
|
|
if [ -n "${excludesource}${excludedests}" ]; then
|
|
build_exclusion_chain chain nat "$excludesource" $excludedests
|
|
|
|
for adr in $(separate_list $addr); do
|
|
run_iptables2 -t nat -A OUTPUT $cli $proto $userandgroup $multiport $sports $dports $(dest_ip_range $adr) -j $chain
|
|
done
|
|
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule_limit $loglevel $chain OUTPUT $logtarget "$ratelimit" "$logtag" -A -t nat
|
|
fi
|
|
|
|
addnatrule $chain $ratelimit $proto -j $target1 # Protocol is necessary for port redirection
|
|
else
|
|
for adr in $(separate_list $addr); do
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule_limit $loglevel OUTPUT OUTPUT $logtarget "$ratelimit" "$logtag" -A -t nat \
|
|
$(fix_bang $proto $cli $sports $userandgroup $(dest_ip_range $adr) $multiport $dports)
|
|
fi
|
|
|
|
run_iptables2 -t nat -A OUTPUT $ratelimit $proto $sports $userandgroup $(dest_ip_range $adr) $multiport $dports -j $target1
|
|
done
|
|
fi
|
|
else
|
|
if [ -n "${excludesource}${excludedests}" ]; then
|
|
build_exclusion_chain chain nat "$excludesource" $excludedests
|
|
|
|
if [ $addr = detect ]; then
|
|
ensurenatchain $(dnat_chain $source)
|
|
#
|
|
# The 'for loops' begun below are completed in add_a_rule() (in the compiler)
|
|
#
|
|
indent >&3 << __EOF__
|
|
|
|
for adr in \$addr; do
|
|
run_iptables -t nat -A $(fix_bang $(dnat_chain $source) $cli $proto $multiport $sports $dports) -d \$adr -j $chain
|
|
__EOF__
|
|
else
|
|
for adr in $(separate_list $addr); do
|
|
addnatrule $(dnat_chain $source) $cli $proto $multiport $sports $dports $(dest_ip_range $adr) -j $chain
|
|
done
|
|
fi
|
|
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule_limit $loglevel $chain $(dnat_chain $source) $logtarget "$ratelimit" "$logtag" -A -t nat
|
|
fi
|
|
|
|
addnatrule $chain $ratelimit $proto -j $target1 # Protocol is necessary for port redirection
|
|
else
|
|
chain=$(dnat_chain $source)
|
|
|
|
if [ $addr = detect ]; then
|
|
ensurenatchain $chain
|
|
|
|
indent >&3 << __EOF__
|
|
|
|
for adr in \$addr; do
|
|
__EOF__
|
|
if [ -n "$loglevel" ]; then
|
|
indent >&3 << __EOF__
|
|
log_rule_limit $loglevel $chain $chain $logtarget "$ratelimit" "$logtag" -A -t nat $(fix_bang $proto $cli $sports $multiport $dports) -d \$adr
|
|
__EOF__
|
|
fi
|
|
|
|
indent >&3 << __EOF__
|
|
run_iptables -t nat -A $chain $(fix_bang $proto $ratelimit $cli $sports $multiport $dports) -d \$adr -j $target1
|
|
__EOF__
|
|
else
|
|
for adr in $(separate_list $addr); do
|
|
if [ -n "$loglevel" ]; then
|
|
ensurenatchain $chain
|
|
log_rule_limit $loglevel $chain $chain $logtarget "$ratelimit" "$logtag" -A -t nat \
|
|
$(fix_bang $proto $cli $sports $(dest_ip_range $adr) $multiport $dports)
|
|
fi
|
|
|
|
addnatrule $chain $proto $ratelimit $cli $sports \
|
|
-d $adr $multiport $dports -j $target1
|
|
done
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Replace destination port by the new destination port
|
|
|
|
if [ -n "$servport" ]; then
|
|
if [ -z "$multiport" ]; then
|
|
dports="--dport ${servport#*:}"
|
|
else
|
|
dports="--dports ${servport#*:}"
|
|
fi
|
|
fi
|
|
|
|
[ "x$addr" = "x0.0.0.0/0" ] && addr=
|
|
ratelimit=
|
|
}
|