From 6351a51255125f717fae33ff0b2852b0ba3dd551 Mon Sep 17 00:00:00 2001
From: jow <jow@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Date: Mon, 4 Feb 2013 14:38:33 +0000
Subject: firewall: various enhancements

	- reduce mssfix related log spam (#10681)
	- separate src and dest terminal chains (#11453, #12945)
	- disable per-zone custom chains by default, they're rarely used

Additionally introduce options "device", "subnet", "extra", "extra_src" and "extra_dest"
to allow defining zones not related to uci interfaces, e.g. to match "ppp+" or any tcp
traffic to and from a specific port.

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@35484 3c298f89-4303-0410-b956-a3cf2f4a3e73
---
 package/network/config/firewall/Makefile           |   2 +-
 .../config/firewall/files/lib/core_forwarding.sh   |   4 +-
 .../network/config/firewall/files/lib/core_init.sh |  67 +++++++----
 .../config/firewall/files/lib/core_interface.sh    | 134 +++++++++++----------
 .../config/firewall/files/lib/core_redirect.sh     |   4 +-
 .../network/config/firewall/files/lib/core_rule.sh |  17 ++-
 package/network/config/firewall/files/lib/fw.sh    |   2 +-
 7 files changed, 133 insertions(+), 97 deletions(-)

(limited to 'package/network/config')

diff --git a/package/network/config/firewall/Makefile b/package/network/config/firewall/Makefile
index fce0a808c..f97ac8d27 100644
--- a/package/network/config/firewall/Makefile
+++ b/package/network/config/firewall/Makefile
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
 PKG_NAME:=firewall
 
 PKG_VERSION:=2
-PKG_RELEASE:=57
+PKG_RELEASE:=58
 
 include $(INCLUDE_DIR)/package.mk
 
diff --git a/package/network/config/firewall/files/lib/core_forwarding.sh b/package/network/config/firewall/files/lib/core_forwarding.sh
index c4a968143..2ea03f0eb 100644
--- a/package/network/config/firewall/files/lib/core_forwarding.sh
+++ b/package/network/config/firewall/files/lib/core_forwarding.sh
@@ -17,14 +17,14 @@ fw_load_forwarding() {
 
 	fw_callback pre forwarding
 
-	local chain=forward
+	local chain=delegate_forward
 	[ -n "$forwarding_src" ] && {
 		chain=zone_${forwarding_src}_forward 
 	}
 
 	local target=ACCEPT
 	[ -n "$forwarding_dest" ] && {
-		target=zone_${forwarding_dest}_ACCEPT
+		target=zone_${forwarding_dest}_dest_ACCEPT
 	}
 
 	local mode
diff --git a/package/network/config/firewall/files/lib/core_init.sh b/package/network/config/firewall/files/lib/core_init.sh
index 035647998..d1cad711c 100644
--- a/package/network/config/firewall/files/lib/core_init.sh
+++ b/package/network/config/firewall/files/lib/core_init.sh
@@ -115,13 +115,13 @@ fw_load_defaults() {
 		fw add i n POSTROUTING postrouting_rule
 	}
 
-	fw add i f input
-	fw add i f output
-	fw add i f forward
+	fw add i f delegate_input
+	fw add i f delegate_output
+	fw add i f delegate_forward
 
-	fw add i f INPUT   input
-	fw add i f OUTPUT  output
-	fw add i f FORWARD forward
+	fw add i f INPUT   delegate_input
+	fw add i f OUTPUT  delegate_output
+	fw add i f FORWARD delegate_forward
 
 	fw add i f reject
 	fw add i f reject REJECT { --reject-with tcp-reset -p tcp }
@@ -138,21 +138,28 @@ fw_config_get_zone() {
 	fw_config_get_section "$1" zone { \
 		string name "$1" \
 		string network "" \
+		string device "" \
+		string subnet "" \
 		string input "$FW_DEFAULT_INPUT_POLICY" \
 		string output "$FW_DEFAULT_OUTPUT_POLICY" \
 		string forward "$FW_DEFAULT_FORWARD_POLICY" \
 		boolean masq 0 \
 		string masq_src "" \
 		string masq_dest "" \
+		string extra "" \
+		string extra_src "" \
+		string extra_dest "" \
 		boolean conntrack 0 \
 		boolean mtu_fix 0 \
-		boolean custom_chains "$FW_ADD_CUSTOM_CHAINS" \
+		boolean custom_chains 0 \
 		boolean log 0 \
 		string log_limit 10 \
 		string family "" \
 	} || return
 	[ -n "$zone_name" ] || zone_name=$zone_NAME
-	[ -n "$zone_network" ] || zone_network=$zone_name
+	[ -n "$zone_extra_src" ] || zone_extra_src="$zone_extra"
+	[ -n "$zone_extra_dest" ] || zone_extra_dest="$zone_extra"
+	[ -n "$zone_network$zone_subnet$zone_device$zone_extra_src$zone_extra_dest" ] || zone_network=$zone_name
 }
 
 fw_load_zone() {
@@ -192,19 +199,22 @@ fw_load_zone() {
 
 	local chain=zone_${zone_name}
 
-	fw add $mode f ${chain}_ACCEPT
-	fw add $mode f ${chain}_DROP
-	fw add $mode f ${chain}_REJECT
+	fw add $mode f ${chain}_src_ACCEPT
+	fw add $mode f ${chain}_src_DROP
+	fw add $mode f ${chain}_src_REJECT
 
-	# TODO: Rename to ${chain}_input
-	fw add $mode f ${chain}
-	fw add $mode f ${chain} ${chain}_${zone_input} $
+	fw add $mode f ${chain}_dest_ACCEPT
+	fw add $mode f ${chain}_dest_DROP
+	fw add $mode f ${chain}_dest_REJECT
+
+	fw add $mode f ${chain}_input
+	fw add $mode f ${chain}_input ${chain}_src_${zone_input} $
 
 	fw add $mode f ${chain}_forward
-	fw add $mode f ${chain}_forward ${chain}_${zone_forward} $
+	fw add $mode f ${chain}_forward ${chain}_dest_${zone_forward} $
 
-	# TODO: add ${chain}_output
-	fw add $mode f output ${chain}_${zone_output} $
+	fw add $mode f ${chain}_output
+	fw add $mode f ${chain}_output ${chain}_dest_${zone_output} $
 
 	# TODO: Rename to ${chain}_MASQUERADE
 	fw add $mode n ${chain}_nat
@@ -223,7 +233,7 @@ fw_load_zone() {
 			fw_die "zone ${zone_name}: custom_chains globally disabled"
 
 		fw add $mode f input_${zone_name}
-		fw add $mode f ${chain} input_${zone_name} ^
+		fw add $mode f ${chain}_input input_${zone_name} ^
 
 		fw add $mode f forwarding_${zone_name}
 		fw add $mode f ${chain}_forward forwarding_${zone_name} ^
@@ -238,13 +248,16 @@ fw_load_zone() {
 
 		local t
 		for t in REJECT DROP; do
-			fw add $mode f ${chain}_${t} LOG ^ \
-				{ -m limit --limit $zone_log_limit --log-prefix "$t($zone_name): " }
+			local d
+			for d in src dest; do
+				fw add $mode f ${chain}_${d}_${t} LOG ^ \
+					{ -m limit --limit $zone_log_limit --log-prefix "$t($d $zone_name): " }
+			done
 		done
 
 		[ $zone_mtu_fix == 1 ] && \
 			fw add $mode m ${chain}_MSSFIX LOG ^ \
-				{ -m limit --limit $zone_log_limit --log-prefix "MSSFIX($zone_name): " }
+				{ -p tcp --tcp-flags SYN,RST SYN -m limit --limit $zone_log_limit --log-prefix "MSSFIX($zone_name): " }
 	}
 
 	# NB: if MASQUERADING for IPv6 becomes available we'll need a family check here
@@ -267,6 +280,16 @@ fw_load_zone() {
 		done
 	fi
 
+	local dev
+	for dev in ${zone_device:-""}; do
+		local net
+		for net in ${zone_subnet:-""}; do
+			[ -n "$dev" ] || [ -n "$net" ] || continue
+			fw_do_interface_rules add "${zone_name}" "$dev" "$net" \
+                "${zone_extra_src}" "${zone_extra_dest}"
+		done
+	done
+
 	fw_callback post zone
 }
 
@@ -293,7 +316,7 @@ fw_load_include() {
 			fw_log error "You cannot use UCI in firewall includes!" >&2
 			exit 1
 		}
-		. $path 
+		. $path
 	)
 }
 
diff --git a/package/network/config/firewall/files/lib/core_interface.sh b/package/network/config/firewall/files/lib/core_interface.sh
index 7400e2d35..9138cbf0c 100644
--- a/package/network/config/firewall/files/lib/core_interface.sh
+++ b/package/network/config/firewall/files/lib/core_interface.sh
@@ -1,4 +1,4 @@
-# Copyright (C) 2009-2012 OpenWrt.org
+# Copyright (C) 2009-2013 OpenWrt.org
 
 fw__uci_state_add() {
 	local var="$1"
@@ -31,6 +31,73 @@ fw__uci_state_del() {
 	uci_toggle_state firewall core $var "$rest"
 }
 
+fw_do_interface_rules() {
+    local action=$1
+    local zone=$2
+    local chain=zone_${zone}
+    local ifname=$3
+    local subnet=$4
+    local extra_src="$5"
+    local extra_dest="$6"
+
+    local idev odev inet onet mode
+    fw_get_family_mode mode x $zone i
+
+    fw_get_negation idev '-i' "$ifname"
+    fw_get_negation odev '-o' "$ifname"
+
+    case "$mode/$subnet" in
+        # Zone supports v6 only or dual, need v6
+        G6/*:*|i/*:*)
+            fw_get_negation inet '-s' "$subnet"
+            fw_get_negation onet '-d' "$subnet"
+            mode=6
+        ;;
+
+        # Zone supports v4 only or dual, need v4
+        G4/*.*.*.*|i/*.*.*.*)
+            fw_get_negation inet '-s' "$subnet"
+            fw_get_negation onet '-d' "$subnet"
+            mode=4
+        ;;
+
+        # Need v6 while zone is v4
+        */*:*) fw_log info "zone $zone does not support IPv6 address family, skipping"; return ;;
+
+        # Need v4 while zone is v6
+        */*.*) fw_log info "zone $zone does not support IPv4 address family, skipping"; return ;;
+
+        # Strip prefix
+        *) mode="${mode#G}" ;;
+    esac
+
+    lock /var/run/firewall-interface.lock
+
+    fw $action $mode f ${chain}_dest_ACCEPT ACCEPT $ { $odev $onet $extra_dest }
+    fw $action $mode f ${chain}_src_ACCEPT  ACCEPT $ { $idev $inet $extra_src  }
+    fw $action $mode f ${chain}_dest_DROP   DROP   $ { $odev $onet $extra_dest }
+    fw $action $mode f ${chain}_src_DROP    DROP   $ { $idev $inet $extra_src  }
+    fw $action $mode f ${chain}_dest_REJECT reject $ { $odev $onet $extra_dest }
+    fw $action $mode f ${chain}_src_REJECT  reject $ { $idev $inet $extra_src  }
+
+    [ "$(uci_get_state firewall core "${zone}_tcpmss")" == 1 ] && \
+        fw $action $mode m ${chain}_MSSFIX TCPMSS $ \
+            { $odev -p tcp --tcp-flags SYN,RST SYN --clamp-mss-to-pmtu $onet $extra_dest }
+
+    fw $action $mode f delegate_input   ${chain}_input   $ { $idev $inet $extra_src  }
+    fw $action $mode f delegate_forward ${chain}_forward $ { $idev $inet $extra_src  }
+    fw $action $mode f delegate_output  ${chain}_output  $ { $odev $onet $extra_dest }
+
+    fw $action $mode n PREROUTING ${chain}_prerouting $ { $idev $inet $extra_src  }
+    fw $action $mode r PREROUTING ${chain}_notrack    $ { $idev $inet $extra_src  }
+    fw $action $mode n POSTROUTING ${chain}_nat       $ { $odev $onet $extra_dest }
+
+    # Flush conntrack table
+    echo f >/proc/net/nf_conntrack 2>/dev/null
+
+    lock -u /var/run/firewall-interface.lock
+}
+
 fw_configure_interface() {
 	local iface=$1
 	local action=$2
@@ -52,66 +119,6 @@ fw_configure_interface() {
 
 	fw_callback pre interface
 
-	fw__do_rules() {
-		local action=$1
-		local zone=$2
-		local chain=zone_${zone}
-		local ifname=$3
-		local subnet=$4
-
-		local inet onet mode
-		fw_get_family_mode mode x $zone i
-
-		case "$mode/$subnet" in
-			# Zone supports v6 only or dual, need v6
-			G6/*:*|i/*:*)
-				inet="-s $subnet -d ::/0"
-				onet="-s ::/0 -d $subnet"
-				mode=6
-			;;
-
-			# Zone supports v4 only or dual, need v4
-			G4/*.*.*.*|i/*.*.*.*)
-				inet="-s $subnet -d 0.0.0.0/0"
-				onet="-s 0.0.0.0/0 -d $subnet"
-				mode=4
-			;;
-
-			# Need v6 while zone is v4
-			*/*:*) fw_log info "zone $zone does not support IPv6 address family, skipping"; return ;;
-
-			# Need v4 while zone is v6
-			*/*.*) fw_log info "zone $zone does not support IPv4 address family, skipping"; return ;;
-
-			# Strip prefix
-			*) mode="${mode#G}" ;;
-		esac
-
-		lock /var/run/firewall-interface.lock
-
-		fw $action $mode f ${chain}_ACCEPT ACCEPT $ { -o "$ifname" $onet }
-		fw $action $mode f ${chain}_ACCEPT ACCEPT $ { -i "$ifname" $inet }
-		fw $action $mode f ${chain}_DROP   DROP   $ { -o "$ifname" $onet }
-		fw $action $mode f ${chain}_DROP   DROP   $ { -i "$ifname" $inet }
-		fw $action $mode f ${chain}_REJECT reject $ { -o "$ifname" $onet }
-		fw $action $mode f ${chain}_REJECT reject $ { -i "$ifname" $inet }
-
-		[ "$(uci_get_state firewall core "${zone}_tcpmss")" == 1 ] && \
-			fw $action $mode m ${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 forward ${chain}_forward $ { -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 n POSTROUTING ${chain}_nat       $ { -o "$ifname" $onet }
-
-		# Flush conntrack table
-		echo f >/proc/net/nf_conntrack 2>/dev/null
-
-		lock -u /var/run/firewall-interface.lock
-	}
-
 	local old_zones old_ifname old_subnets
 	config_get old_zones core "${iface}_zone"
 	[ -n "$old_zones" ] && {
@@ -123,7 +130,7 @@ fw_configure_interface() {
 			local n
 			for n in ${old_subnets:-""}; do
 				fw_log info "removing $iface ($old_ifname${n:+ alias $n}) from zone $z"
-				fw__do_rules del $z $old_ifname $n
+				fw_do_interface_rules del $z $old_ifname $n
 			done
 
 			[ -n "$old_subnets" ] || {
@@ -182,7 +189,7 @@ fw_configure_interface() {
 		list_contains zone_network "$iface" || return
 
 		fw_log info "adding $iface ($ifname${aliasnet:+ alias $aliasnet}) to zone $zone_name"
-		fw__do_rules add ${zone_name} "$ifname" "$aliasnet"
+		fw_do_interface_rules add ${zone_name} "$ifname" "$aliasnet"
 		append new_zones $zone_name
 
 		[ -n "$aliasnet" ] || {
@@ -205,4 +212,3 @@ fw_sysctl_interface() {
 		sysctl -w net.ipv6.conf.${ifname}.accept_source_route=$FW_ACCEPT_SRC_ROUTE
 	} >/dev/null 2>/dev/null
 }
-
diff --git a/package/network/config/firewall/files/lib/core_redirect.sh b/package/network/config/firewall/files/lib/core_redirect.sh
index fe396c1c1..9493bc6ae 100644
--- a/package/network/config/firewall/files/lib/core_redirect.sh
+++ b/package/network/config/firewall/files/lib/core_redirect.sh
@@ -41,7 +41,7 @@ fw_load_redirect() {
 		# in this case match only DNATed traffic and allow it on input, not forward
 		if [ -z "$redirect_dest_ip" ] || /sbin/ifconfig | grep -qE "addr:${redirect_dest_ip//./\\.}\b"; then
 			fwdopt="-m conntrack --ctstate DNAT"
-			fwdchain="zone_${redirect_src}"
+			fwdchain="zone_${redirect_src}_input"
 		else
 			fwdchain="zone_${redirect_src}_forward"
 		fi
@@ -114,7 +114,7 @@ fw_load_redirect() {
 				$redirect_options \
 			}
 
-			fw add $mode f ${fwdchain:-forward} ACCEPT + \
+			fw add $mode f ${fwdchain:-delegate_forward} ACCEPT + \
 				{ $redirect_src_ip $redirect_dest_ip } { \
 				$srcaddr $destaddr \
 				$pr \
diff --git a/package/network/config/firewall/files/lib/core_rule.sh b/package/network/config/firewall/files/lib/core_rule.sh
index f49c42af5..0ce2122be 100644
--- a/package/network/config/firewall/files/lib/core_rule.sh
+++ b/package/network/config/firewall/files/lib/core_rule.sh
@@ -34,7 +34,7 @@ fw_load_rule() {
 	fw_callback pre rule
 
 	local table=f
-	local chain=input
+	local chain=delegate_output
 	local target="${rule_target:-REJECT}"
 	if [ "$target" == "NOTRACK" ]; then
 		table=r
@@ -42,16 +42,23 @@ fw_load_rule() {
 	else
 		if [ -n "$rule_src" ]; then
 			if [ "$rule_src" != "*" ]; then
-				chain="zone_${rule_src}${rule_dest:+_forward}"
+				if [ -n "$rule_dest" ]; then
+					chain="zone_${rule_src}_forward"
+				else
+					chain="zone_${rule_src}_input"
+				fi
 			else
-				chain="${rule_dest:+forward}"
-				chain="${chain:-input}"
+				chain="${rule_dest:+delegate_forward}"
+				chain="${chain:-delegate_input}"
 			fi
 		fi
 
 		if [ -n "$rule_dest" ]; then
 			if [ "$rule_dest" != "*" ]; then
-				target="zone_${rule_dest}_${target}"
+				target="zone_${rule_dest}_dest_${target}"
+				if [ -z "$rule_src" ]; then
+					chain="zone_${rule_dest}_output"
+				fi
 			elif [ "$target" = REJECT ]; then
 				target=reject
 			fi
diff --git a/package/network/config/firewall/files/lib/fw.sh b/package/network/config/firewall/files/lib/fw.sh
index 76e294f56..ca851e81c 100644
--- a/package/network/config/firewall/files/lib/fw.sh
+++ b/package/network/config/firewall/files/lib/fw.sh
@@ -170,7 +170,7 @@ fw__exec() { # <action> <family> <table> <chain> <target> <position> { <rules> }
 					fw_log info "ICMP type '$2' is not valid for $fam address family, skipping rule"
 					return 1
 				fi
-				shift	
+				shift
 			;;
 			*) cmdline="$cmdline $1" ;;
 		esac
-- 
cgit v1.2.3