firewall: - simplify masquerade rule setup - remove various subshell invocations - speedup fw() by not relying on xargs and pipes - rework SNAT support - attach to dest zone, use src_dip/src_dport as snat source

SVN-Revision: 23024
This commit is contained in:
Jo-Philipp Wich 2010-09-11 20:04:34 +00:00
parent 9499018b9a
commit f3dd8278bb
7 changed files with 114 additions and 94 deletions

View file

@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=firewall PKG_NAME:=firewall
PKG_VERSION:=2 PKG_VERSION:=2
PKG_RELEASE:=12 PKG_RELEASE:=13
include $(INCLUDE_DIR)/package.mk include $(INCLUDE_DIR)/package.mk

View file

@ -27,7 +27,8 @@ fw_load_forwarding() {
target=zone_${forwarding_dest}_ACCEPT target=zone_${forwarding_dest}_ACCEPT
} }
local mode=$(fw_get_family_mode ${forwarding_family:-x} ${forwarding_dest:-${forwarding_src:--}} i) local mode
fw_get_family_mode mode ${forwarding_family:-x} ${forwarding_dest:-${forwarding_src:--}} i
fw add $mode f $chain $target ^ fw add $mode f $chain $target ^

View file

@ -212,9 +212,6 @@ fw_load_zone() {
fw add $mode r ${chain}_notrack fw add $mode r ${chain}_notrack
[ $zone_masq == 1 ] && \
fw add $mode n POSTROUTING ${chain}_nat $
[ $zone_mtu_fix == 1 ] && \ [ $zone_mtu_fix == 1 ] && \
fw add $mode f FORWARD ${chain}_MSSFIX ^ fw add $mode f FORWARD ${chain}_MSSFIX ^
@ -243,6 +240,18 @@ fw_load_zone() {
done done
} }
# NB: if MASQUERADING for IPv6 becomes available we'll need a family check here
if [ "$zone_masq" == 1 ]; then
local msrc mdst
for msrc in ${zone_masq_src:-0.0.0.0/0}; do
[ "${msrc#!}" != "$msrc" ] && msrc="! -s ${msrc#!}" || msrc="-s $msrc"
for mdst in ${zone_masq_dest:-0.0.0.0/0}; do
[ "${mdst#!}" != "$mdst" ] && mdst="! -d ${mdst#!}" || mdst="-d $mdst"
fw add $mode n ${chain}_nat MASQUERADE $ { $msrc $mdst }
done
done
fi
fw_callback post zone fw_callback post zone
} }

View file

@ -27,11 +27,9 @@ fw_configure_interface() {
local chain=zone_${zone} local chain=zone_${zone}
local ifname=$3 local ifname=$3
local subnet=$4 local subnet=$4
local masq_src=$5
local masq_dest=$6
local inet onet local inet onet mode
local mode=$(fw_get_family_mode x $zone i) fw_get_family_mode mode x $zone i
case "$mode/$subnet" in case "$mode/$subnet" in
# Zone supports v6 only or dual, need v6 # Zone supports v6 only or dual, need v6
@ -62,38 +60,27 @@ fw_configure_interface() {
fw $action $mode f ${chain}_REJECT reject $ { -o "$ifname" $onet } fw $action $mode f ${chain}_REJECT reject $ { -o "$ifname" $onet }
fw $action $mode f ${chain}_REJECT reject $ { -i "$ifname" $inet } fw $action $mode f ${chain}_REJECT reject $ { -i "$ifname" $inet }
# NB: if MASQUERADING for IPv6 becomes available we'll need a family check here
local msrc mdst
for msrc in ${masq_src:-0.0.0.0/0}; do
[ "${msrc#!}" != "$msrc" ] && msrc="! -s ${msrc#!}" || msrc="-s $msrc"
for mdst in ${subnet:-${masq_dest:-0.0.0.0/0}}; do
[ "${mdst#!}" != "$mdst" ] && mdst="! -d ${mdst#!}" || mdst="-d $mdst"
fw $action $mode n ${chain}_nat MASQUERADE $ { -o "$ifname" $msrc $mdst }
done
done
fw $action $mode f ${chain}_MSSFIX TCPMSS $ { -o "$ifname" -p tcp --tcp-flags SYN,RST SYN --clamp-mss-to-pmtu $onet } fw $action $mode f ${chain}_MSSFIX TCPMSS $ { -o "$ifname" -p tcp --tcp-flags SYN,RST SYN --clamp-mss-to-pmtu $onet }
fw $action $mode f input ${chain} $ { -i "$ifname" $inet } fw $action $mode f input ${chain} $ { -i "$ifname" $inet }
fw $action $mode f forward ${chain}_forward $ { -i "$ifname" $inet } fw $action $mode f forward ${chain}_forward $ { -i "$ifname" $inet }
fw $action $mode n PREROUTING ${chain}_prerouting $ { -i "$ifname" $inet } fw $action $mode n PREROUTING ${chain}_prerouting $ { -i "$ifname" $inet }
fw $action $mode r PREROUTING ${chain}_notrack $ { -i "$ifname" $inet } fw $action $mode r PREROUTING ${chain}_notrack $ { -i "$ifname" $inet }
fw $action $mode n POSTROUTING ${chain}_nat $ { -o "$ifname" $onet }
} }
local old_zones old_ifname old_subnets old_masq_src old_masq_dest local old_zones old_ifname old_subnets
config_get old_zones core "${iface}_zone" config_get old_zones core "${iface}_zone"
[ -n "$old_zones" ] && { [ -n "$old_zones" ] && {
config_get old_ifname core "${iface}_ifname" config_get old_ifname core "${iface}_ifname"
config_get old_subnets core "${iface}_subnets" config_get old_subnets core "${iface}_subnets"
config_get old_masq_src core "${iface}_masq_src"
config_get old_masq_dest core "${iface}_masq_dest"
local z local z
for z in $old_zones; do for z in $old_zones; do
local n local n
for n in ${old_subnets:-""}; do for n in ${old_subnets:-""}; do
fw_log info "removing $iface ($old_ifname${n:+ alias $n}) from zone $z" fw_log info "removing $iface ($old_ifname${n:+ alias $n}) from zone $z"
fw__do_rules del $z $old_ifname $n "$old_masq_src" "$old_masq_dest" fw__do_rules del $z $old_ifname $n
done done
[ -n "$old_subnets" ] || ACTION=remove ZONE="$z" INTERFACE="$iface" DEVICE="$ifname" /sbin/hotplug-call firewall [ -n "$old_subnets" ] || ACTION=remove ZONE="$z" INTERFACE="$iface" DEVICE="$ifname" /sbin/hotplug-call firewall
@ -111,8 +98,6 @@ fw_configure_interface() {
uci_revert_state firewall core "${iface}_ifname" uci_revert_state firewall core "${iface}_ifname"
uci_revert_state firewall core "${iface}_subnets" uci_revert_state firewall core "${iface}_subnets"
uci_revert_state firewall core "${iface}_aliases" uci_revert_state firewall core "${iface}_aliases"
uci_revert_state firewall core "${iface}_masq_src"
uci_revert_state firewall core "${iface}_masq_dest"
} }
[ "$action" == del ] && return [ "$action" == del ] && return
@ -146,17 +131,13 @@ fw_configure_interface() {
} }
local new_zones= local new_zones=
local new_masq_src=
local new_masq_dest=
load_zone() { load_zone() {
fw_config_get_zone "$1" fw_config_get_zone "$1"
list_contains zone_network "$iface" || return list_contains zone_network "$iface" || return
fw_log info "adding $iface ($ifname${aliasnet:+ alias $aliasnet}) to zone $zone_name" fw_log info "adding $iface ($ifname${aliasnet:+ alias $aliasnet}) to zone $zone_name"
fw__do_rules add ${zone_name} "$ifname" "$aliasnet" "$zone_masq_src" "$zone_masq_dest" fw__do_rules add ${zone_name} "$ifname" "$aliasnet"
append new_zones $zone_name append new_zones $zone_name
append new_masq_src "$zone_masq_src"
append new_masq_dest "$zone_masq_dest"
[ -n "$aliasnet" ] || ACTION=add ZONE="$zone_name" INTERFACE="$iface" DEVICE="$ifname" /sbin/hotplug-call firewall [ -n "$aliasnet" ] || ACTION=add ZONE="$zone_name" INTERFACE="$iface" DEVICE="$ifname" /sbin/hotplug-call firewall
} }
@ -164,8 +145,6 @@ fw_configure_interface() {
uci_set_state firewall core "${iface}_zone" "$new_zones" uci_set_state firewall core "${iface}_zone" "$new_zones"
uci_set_state firewall core "${iface}_ifname" "$ifname" uci_set_state firewall core "${iface}_ifname" "$ifname"
uci_set_state firewall core "${iface}_masq_src" "$new_masq_src"
uci_set_state firewall core "${iface}_masq_dest" "$new_masq_dest"
} }
fw_sysctl_interface() { fw_sysctl_interface() {

View file

@ -27,53 +27,77 @@ fw_load_redirect() {
fw_callback pre redirect fw_callback pre redirect
local fwdchain natchain natopt nataddr natports srcdaddr srcdports
if [ "$redirect_target" == "DNAT" ]; then
[ -n "$redirect_src" -a -n "$redirect_dest_ip$redirect_dest_port" ] || { [ -n "$redirect_src" -a -n "$redirect_dest_ip$redirect_dest_port" ] || {
fw_die "redirect ${redirect_name}: needs src and dest_ip or dest_port" fw_die "DNAT redirect ${redirect_name}: needs src and dest_ip or dest_port"
} }
local chain destopt destaddr fwdchain="zone_${redirect_src}_forward"
if [ "$redirect_target" == "DNAT" ]; then
chain="zone_${redirect_src}_prerouting" natopt="--to-destination"
destopt="--to-destination" natchain="zone_${redirect_src}_prerouting"
destaddr="$redirect_dest_ip" nataddr="$redirect_dest_ip"
elif [ "$redirect_target" == "SNAT" ]; then fw_get_port_range natports "$redirect_dest_port" "-"
chain="zone_${redirect_src}_nat"
destopt="--to-source" srcdaddr="${redirect_src_dip:+$redirect_src_dip/$redirect_src_dip_prefixlen}"
destaddr="$redirect_src_dip" fw_get_port_range srcdports "$redirect_src_dport" ":"
else
fw_die "redirect ${redirect_name}: target must be either DNAT or SNAT"
fi
list_contains FW_CONNTRACK_ZONES $redirect_src || \ list_contains FW_CONNTRACK_ZONES $redirect_src || \
append FW_CONNTRACK_ZONES $redirect_src append FW_CONNTRACK_ZONES $redirect_src
local mode=$(fw_get_family_mode ${redirect_family:-x} $redirect_src I) elif [ "$redirect_target" == "SNAT" ]; then
[ -n "$redirect_dest" -a -n "$redirect_src_dip" ] || {
fw_die "SNAT redirect ${redirect_name}: needs dest and src_dip"
}
local nat_dest_port=$redirect_dest_port fwdchain="${redirect_src:+zone_${redirect_src}_forward}"
redirect_dest_port=$(fw_get_port_range $redirect_dest_port)
redirect_src_port=$(fw_get_port_range $redirect_src_port) natopt="--to-source"
redirect_src_dport=$(fw_get_port_range $redirect_src_dport) natchain="zone_${redirect_dest}_nat"
local fwd_dest_port=${redirect_dest_port:-$redirect_src_dport} nataddr="$redirect_src_dip"
fw_get_port_range natports "$redirect_src_dport" "-"
srcdaddr="${redirect_dest_ip:+$redirect_dest_ip/$redirect_dest_ip_prefixlen}"
fw_get_port_range srcdports "$redirect_dest_port" ":"
list_contains FW_CONNTRACK_ZONES $redirect_dest || \
append FW_CONNTRACK_ZONES $redirect_dest
else
fw_die "redirect ${redirect_name}: target must be either DNAT or SNAT"
fi
local mode
fw_get_family_mode mode ${redirect_family:-x} ${redirect_src:-$redirect_dest} I
local srcaddr="${redirect_src_ip:+$redirect_src_ip/$redirect_src_ip_prefixlen}"
local srcports
fw_get_port_range srcports "$redirect_src_port" ":"
local destaddr="${redirect_dest_ip:+$redirect_dest_ip/$redirect_dest_ip_prefixlen}"
local destports
fw_get_port_range destports "${redirect_dest_port:-$redirect_src_dport}" ":"
[ "$redirect_proto" == "tcpudp" ] && redirect_proto="tcp udp" [ "$redirect_proto" == "tcpudp" ] && redirect_proto="tcp udp"
for redirect_proto in $redirect_proto; do for redirect_proto in $redirect_proto; do
fw add $mode n $chain $redirect_target $ { $redirect_src_ip $redirect_dest_ip } { \ fw add $mode n $natchain $redirect_target ^ { $redirect_src_ip $redirect_dest_ip } { \
${redirect_proto:+-p $redirect_proto} \ ${redirect_proto:+-p $redirect_proto} \
${redirect_src_ip:+-s $redirect_src_ip/$redirect_src_ip_prefixlen} \ ${srcaddr:+-s $srcaddr} \
${redirect_src_dip:+-d $redirect_src_dip/$redirect_src_dip_prefixlen} \ ${srcports:+--sport $srcports} \
${redirect_src_port:+--sport $redirect_src_port} \ ${srcdaddr:+-d $srcdaddr} \
${redirect_src_dport:+--dport $redirect_src_dport} \ ${srcdports:+--dport $srcdports} \
${redirect_src_mac:+-m mac --mac-source $redirect_src_mac} \ ${redirect_src_mac:+-m mac --mac-source $redirect_src_mac} \
$destopt ${redirect_dest_ip}${redirect_dest_port:+:$nat_dest_port} \ $natopt $nataddr${natports:+:$natports} \
} }
[ -n "$destaddr" ] && \ [ -n "$destaddr" ] && \
fw add $mode f zone_${redirect_src}_forward ACCEPT ^ { $redirect_src_ip $redirect_dest_ip } { \ fw add $mode f ${fwdchain:-forward} ACCEPT ^ { $redirect_src_ip $redirect_dest_ip } { \
-d $destaddr \
${redirect_proto:+-p $redirect_proto} \ ${redirect_proto:+-p $redirect_proto} \
${redirect_src_ip:+-s $redirect_src_ip/$redirect_src_ip_prefixlen} \ ${srcaddr:+-s $srcaddr} \
${redirect_src_port:+--sport $redirect_src_port} \ ${srcports:+--sport $srcports} \
${fwd_dest_port:+--dport $fwd_dest_port} \ ${destaddr:+-d $destaddr} \
${destports:+--dport $destports} \
${redirect_src_mac:+-m mac --mac-source $redirect_src_mac} \ ${redirect_src_mac:+-m mac --mac-source $redirect_src_mac} \
} }
done done

View file

@ -26,8 +26,8 @@ fw_load_rule() {
fw_callback pre rule fw_callback pre rule
rule_src_port=$(fw_get_port_range $rule_src_port) fw_get_port_range rule_src_port $rule_src_port
rule_dest_port=$(fw_get_port_range $rule_dest_port) fw_get_port_range rule_dest_port $rule_dest_port
local chain=input local chain=input
[ -n "$rule_src" ] && { [ -n "$rule_src" ] && {
@ -46,7 +46,8 @@ fw_load_rule() {
target=zone_${rule_dest}_${target} target=zone_${rule_dest}_${target}
} }
local mode=$(fw_get_family_mode ${rule_family:-x} $rule_src I) local mode
fw_get_family_mode mode ${rule_family:-x} $rule_src I
local rule_pos local rule_pos
eval 'rule_pos=$((++FW__RULE_COUNT_'$mode'_'$chain'))' eval 'rule_pos=$((++FW__RULE_COUNT_'$mode'_'$chain'))'

View file

@ -159,56 +159,62 @@ fw__exec() { # <action> <family> <table> <chain> <target> <position> { <rules> }
fi fi
fi fi
local cmdline="$app --table ${tab} --${cmd} ${chn} ${pol} ${pos} ${tgt:+--jump "$tgt"}"
while [ $# -gt 1 ]; do while [ $# -gt 1 ]; do
case "$app:$1" in case "$app:$1" in
ip6tables:--icmp-type) echo -n "--icmpv6-type" ;; ip6tables:--icmp-type) cmdline="$cmdline --icmpv6-type" ;;
ip6tables:icmp|ip6tables:ICMP) echo -n "icmpv6" ;; ip6tables:icmp|ip6tables:ICMP) cmdline="$cmdline icmpv6" ;;
iptables:--icmpv6-type) echo -n "--icmp-type" ;; iptables:--icmpv6-type) cmdline="$cmdline --icmp-type" ;;
iptables:icmpv6) echo -n "icmp" ;; iptables:icmpv6) cmdline="$cmdline icmp" ;;
*) echo -n "$1" ;; *) cmdline="$cmdline $1" ;;
esac esac
echo -ne "\0"
shift shift
done | xargs -0 ${FW_TRACE:+-t} \ done
$app --table ${tab} --${cmd} ${chn} ${pol} ${pos} ${tgt:+--jump "$tgt"}
[ -n "$FW_TRACE" ] && echo $cmdline >&2
$cmdline
fw__rc $? fw__rc $?
} }
fw_get_port_range() { fw_get_port_range() {
local ports=$1 local _var=$1
local delim=${2:-:} local _ports=$2
if [ "$3" ]; then local _delim=${3:-:}
fw_get_port_range "${ports}-${3}" $delim if [ "$4" ]; then
fw_get_port_range $_var "${_ports}-${4}" $_delim
return return
fi fi
local first=${ports%-*} local _first=${_ports%-*}
local last=${ports#*-} local _last=${_ports#*-}
if [ "$first" != "$last" ]; then if [ "$_first" != "$_last" ]; then
echo "$first$delim$last" export -- "$_var=$_first$_delim$_last"
else else
echo "$first" export -- "$_var=$_first"
fi fi
} }
fw_get_family_mode() { fw_get_family_mode() {
local hint="$1" local _var="$1"
local zone="$2" local _hint="$2"
local mode="$3" local _zone="$3"
local _mode="$4"
local ipv4 ipv6 local _ipv4 _ipv6
[ -n "$FW_ZONES4$FW_ZONES6" ] && { [ -n "$FW_ZONES4$FW_ZONES6" ] && {
list_contains FW_ZONES4 $zone && ipv4=1 || ipv4=0 list_contains FW_ZONES4 $_zone && _ipv4=1 || _ipv4=0
list_contains FW_ZONES6 $zone && ipv6=1 || ipv6=0 list_contains FW_ZONES6 $_zone && _ipv6=1 || _ipv6=0
} || { } || {
ipv4=$(uci_get_state firewall core ${zone}_ipv4 0) _ipv4=$(uci_get_state firewall core ${_zone}_ipv4 0)
ipv6=$(uci_get_state firewall core ${zone}_ipv6 0) _ipv6=$(uci_get_state firewall core ${_zone}_ipv6 0)
} }
case "$hint:$ipv4:$ipv6" in case "$_hint:$_ipv4:$_ipv6" in
*4:1:*|*:1:0) echo G4 ;; *4:1:*|*:1:0) export -n -- "$_var=G4" ;;
*6:*:1|*:0:1) echo G6 ;; *6:*:1|*:0:1) export -n -- "$_var=G6" ;;
*) echo $mode ;; *) export -n -- "$_var=$_mode" ;;
esac esac
} }