diff --git a/package/firewall/Makefile b/package/firewall/Makefile
index abc6020434a9009fea0cad8178bebacb90be0cda..22d359939f2b7b910377d3d061f42904a806f9f8 100644
--- a/package/firewall/Makefile
+++ b/package/firewall/Makefile
@@ -1,5 +1,5 @@
 #
-# Copyright (C) 2008-2009 OpenWrt.org
+# Copyright (C) 2008-2010 OpenWrt.org
 #
 # This is free software, licensed under the GNU General Public License v2.
 # See /LICENSE for more information.
@@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=firewall
 
-PKG_VERSION:=1
-PKG_RELEASE:=10
+PKG_VERSION:=2
+PKG_RELEASE:=1
 
 include $(INCLUDE_DIR)/package.mk
 
@@ -36,13 +36,15 @@ endef
 
 define Package/firewall/install
 	$(INSTALL_DIR) $(1)/lib/firewall
-	$(INSTALL_DATA) ./files/uci_firewall.sh $(1)/lib/firewall
+	$(INSTALL_DATA) ./files/lib/*.sh $(1)/lib/firewall
+	$(INSTALL_DIR) $(1)/sbin
+	$(INSTALL_BIN) ./files/bin/fw $(1)/sbin
 	$(INSTALL_DIR) $(1)/etc/config
 	$(INSTALL_DATA) ./files/firewall.config $(1)/etc/config/firewall
 	$(INSTALL_DIR) $(1)/etc/init.d/
 	$(INSTALL_BIN) ./files/firewall.init $(1)/etc/init.d/firewall
 	$(INSTALL_DIR) $(1)/etc/hotplug.d/iface
-	$(INSTALL_DATA) ./files/20-firewall $(1)/etc/hotplug.d/iface
+	$(INSTALL_DATA) ./files/firewall.hotplug $(1)/etc/hotplug.d/iface/20-firewall
 	$(INSTALL_DIR) $(1)/etc
 	$(INSTALL_DATA) ./files/firewall.user $(1)/etc
 endef
diff --git a/package/firewall/files/20-firewall b/package/firewall/files/20-firewall
deleted file mode 100644
index 4b89326b515d9806f7ab88b04fc989352d24c736..0000000000000000000000000000000000000000
--- a/package/firewall/files/20-firewall
+++ /dev/null
@@ -1,5 +0,0 @@
-. /lib/firewall/uci_firewall.sh
-unset ZONE
-config_get ifname $INTERFACE ifname
-[ "$ifname" == "lo" ] && exit 0
-fw_event "$ACTION" "$INTERFACE"
diff --git a/package/firewall/files/bin/fw b/package/firewall/files/bin/fw
new file mode 100644
index 0000000000000000000000000000000000000000..0f83b8eed84fb82b028ccd10d6a3fb0e934c3b17
--- /dev/null
+++ b/package/firewall/files/bin/fw
@@ -0,0 +1,49 @@
+#!/bin/sh
+FW_LIBDIR=/lib/firewall
+
+. /etc/functions.sh
+. ${FW_LIBDIR}/fw.sh
+
+case "$(type fw)" in
+	*function) ;;
+	*) exit 255;;
+esac
+
+usage() {
+	echo $0 "<command>" "<family>" "<table>" "<chain>" "<target>" "{" "<rules>" "}"
+	exit 0
+}
+
+cmd=$1
+shift
+case "$cmd" in
+	--help|help) usage ;;
+	start|stop|reload|restart)
+		. ${FW_LIBDIR}/core.sh
+		fw_$cmd
+		exit $?
+	;;
+esac
+
+fam=$1
+shift
+case "$fam" in
+	ip)
+		fam=i
+		if [ $# -gt 2 ]; then
+			for p in $(seq 2 $(($# - 1))); do
+				if eval "[ \$$p == '}' ]"; then
+					fam=I
+					break
+				fi
+			done
+		fi ;;
+	ip4) fam=4 ;;
+	ip6) fam=6 ;;
+	arp) fam=a ;;
+	eth) fam=e ;;
+	-*) exec $0 $cmd ${fam##*-} "$@" ;;
+esac
+
+fw "$cmd" "$fam" "$@"
+exit $?
diff --git a/package/firewall/files/firewall.hotplug b/package/firewall/files/firewall.hotplug
new file mode 100644
index 0000000000000000000000000000000000000000..fa5643a2bf39f450e2ba3a9495a5d081134a2528
--- /dev/null
+++ b/package/firewall/files/firewall.hotplug
@@ -0,0 +1,19 @@
+#!/bin/sh
+# This script is executed as part of the hotplug event with
+# HOTPLUG_TYPE=iface, triggered by various scripts when an interface
+# is configured (ACTION=ifup) or deconfigured (ACTION=ifdown).  The
+# interface is available as INTERFACE, the real device as DEVICE.
+. /etc/functions.sh
+
+[ "$DEVICE" == "lo" ] && exit 0
+
+. /lib/firewall/core.sh
+fw_is_loaded || exit 0
+fw_init
+
+case "$ACTION" in
+	ifup)
+		fw_configure_interface "$INTERFACE" add "$DEVICE" ;;
+	ifdown)
+		fw_configure_interface "$INTERFACE" del "$DEVICE" ;;
+esac
diff --git a/package/firewall/files/firewall.init b/package/firewall/files/firewall.init
index 26855f39ada9e0511e17442fbb7efd8b8642db14..54742488e800f52baa5d9b4678faff44f3d0a97f 100755
--- a/package/firewall/files/firewall.init
+++ b/package/firewall/files/firewall.init
@@ -3,12 +3,25 @@
 
 START=45
 
+FW_LIBDIR=/lib/firewall
+
+fw() {
+	. $FW_LIBDIR/core.sh
+	fw_$1
+}
+
 start() {
-	. /lib/firewall/uci_firewall.sh
-	fw_init
+	fw start
 }
 
 stop() {
-	. /lib/firewall/uci_firewall.sh
-	fw_stop	
+	fw stop
+}
+
+restart() {
+	fw restart
+}
+
+reload() {
+	fw reload
 }
diff --git a/package/firewall/files/lib/config.sh b/package/firewall/files/lib/config.sh
new file mode 100644
index 0000000000000000000000000000000000000000..1c5e030961093e9d0b9b7ab115b4d98f617f2b2a
--- /dev/null
+++ b/package/firewall/files/lib/config.sh
@@ -0,0 +1,97 @@
+# Copyright (C) 2009-2010 OpenWrt.org
+# Copyright (C) 2009 Malte S. Stretz <http://msquadrat.de>
+#
+# This is a temporary file, I hope to have some of this stuff merged into
+# /lib/functions.sh (without the fw_ prefix of course) one day.
+
+fw_config_append() { # <package>
+	CONFIG_APPEND=1 config_load "$@"
+	unset CONFIG_APPEND
+}
+
+fw_config_once() { # <function> <type>
+	local func=$1
+	local type=$2
+	shift 2
+
+	local config=cfg00nil
+	fw_config__once() {
+		config=$1
+	}
+	config_foreach fw_config__once "$type"
+
+	$func $config "$@"
+}
+
+fw_config_get_section() { # <config> <prefix> <type> <name> <default> ...
+	local config=$1
+	local prefix=$2
+	shift 2
+
+	[ -n "$config" ] || return 1
+	[ -n "$prefix" ] && {
+		prefix="${prefix}_"
+		export ${NO_EXPORT:+-n} -- "${prefix}NAME"="${config}"
+		config_get "${prefix}TYPE" "$config" TYPE
+	}
+	
+	[ "$1" == '{' ] && shift
+	while [ $# -ge 3 ]; do
+		local type=$1
+		local name=$2
+		local dflt=$3
+		shift 3
+		# TODO: Move handling of defaults to /lib/functions.sh
+		# and get replace the case block with the following 
+		# two lines:
+		# type=${type#string}
+		# config_get${type:+_${type}} "${prefix}${name}" "$config" "$name" "$dflt" || return
+		case "$type" in
+			string)
+				local tmp
+				config_get tmp "$config" "$name" || return
+				[ -z "$tmp" ] && tmp=$dflt
+				export ${NO_EXPORT:+-n} -- "${prefix}${name}=${tmp}"
+				continue
+			;;
+			boolean)
+				type=bool
+			;;
+		esac;
+		
+		local cmd=${prefix}config_get_${type}
+		type $cmd > /dev/null || {
+			cmd=config_get_${type} 
+		}
+		type $cmd > /dev/null || {
+			echo "config type $type (for $name) not supported" >&2
+			return 1
+		}
+		$cmd "${prefix}${name}" "$config" "$name" "$dflt" || return
+	done
+}
+
+config_get_ipaddr() {
+	local varn=$1
+	local conf=$2
+	local name=$3
+	local dflt=$4
+
+	local addr
+	config_get addr "$conf" "$name" || return
+	[ -n "$addr" ] || addr=$dflt
+
+	local mask=${addr#*/}
+	[ "$mask" != "$addr" ] || mask=
+	addr=${addr%/*}
+
+	local vers=
+	case "$addr" in
+		*.*) vers=4 ;;
+		*:*) vers=6 ;;
+	esac
+	
+	export ${NO_EXPORT:+-n} -- "${varn}=${addr}"
+	export ${NO_EXPORT:+-n} -- "${varn}_prefixlen=${mask}"
+	export ${NO_EXPORT:+-n} -- "${varn}_version=${vers}"
+}
diff --git a/package/firewall/files/lib/core.sh b/package/firewall/files/lib/core.sh
new file mode 100644
index 0000000000000000000000000000000000000000..3fd98d1602ecac2aa986ea80979dc77022d43095
--- /dev/null
+++ b/package/firewall/files/lib/core.sh
@@ -0,0 +1,136 @@
+# Copyright (C) 2009-2010 OpenWrt.org
+
+FW_LIBDIR=${FW_LIBDIR:-/lib/firewall}
+
+. $FW_LIBDIR/fw.sh
+include /lib/network
+
+fw_start() {
+	fw_init
+
+	FW_DEFAULTS_APPLIED=
+
+	fw_is_loaded && {
+		echo "firewall already loaded" >&2
+		exit 1
+	}
+	uci_set_state firewall core "" firewall_state
+
+	fw_clear DROP
+
+	fw_callback pre core
+
+	echo "Loading defaults"
+	fw_config_once fw_load_defaults defaults
+
+	echo "Loading zones"
+	config_foreach fw_load_zone zone
+
+	echo "Loading forwardings"
+	config_foreach fw_load_forwarding forwarding
+
+	echo "Loading redirects"
+	config_foreach fw_load_redirect redirect
+
+	echo "Loading rules"
+	config_foreach fw_load_rule rule
+
+	echo "Loading includes"
+	config_foreach fw_load_include include
+
+	[ -n "$FW_NOTRACK_DISABLED" ] && {
+		echo "Optimizing conntrack"
+		config_foreach fw_load_notrack_zone zone
+	}
+
+	echo "Loading interfaces"
+	config_foreach fw_configure_interface interface add
+
+	fw_callback post core
+
+	uci_set_state firewall core loaded 1
+}
+
+fw_stop() {
+	fw_init
+
+	fw_callback pre stop
+
+	fw_clear ACCEPT
+
+	fw_callback post stop
+
+	uci_revert_state firewall
+	config_clear
+	unset FW_INITIALIZED
+}
+
+fw_restart() {
+	fw_stop
+	fw_start
+}
+
+fw_reload() {
+	fw_restart
+}
+
+fw_is_loaded() {
+	local bool
+	config_get_bool bool core loaded 0
+	return $((! $bool))
+}
+
+
+fw_die() {
+	echo "Error:" "$@" >&2
+	fw_log error "$@"
+	fw_stop
+	exit 1
+}
+
+fw_log() {
+	local level="$1"
+	[ -n "$2" ] || {
+		shift
+		level=notice
+	}
+	logger -t firewall -p user.$level "$@"
+}
+
+
+fw_init() {
+	[ -z "$FW_INITIALIZED" ] || return 0
+
+	. $FW_LIBDIR/config.sh
+
+	scan_interfaces
+	fw_config_append firewall
+
+	local hooks="core stop defaults zone notrack synflood"
+	local file lib hk pp
+	for file in $FW_LIBDIR/core_*.sh; do
+		. $file
+		hk=$(basename $file .sh)
+		hk=${hk#core_}
+		append hooks $hk
+	done
+	for file in $FW_LIBDIR/*.sh; do
+		lib=$(basename $file .sh)
+		lib=${lib##[0-9][0-9]_}
+		case $lib in
+			core*|fw|config|uci_firewall) continue ;;
+		esac
+		. $file
+		for hk in $hooks; do
+			for pp in pre post; do
+				type ${lib}_${pp}_${hk}_cb >/dev/null &&
+					append FW_CB_${pp}_${hk} ${lib}
+			done
+		done
+	done
+
+	fw_callback post init
+
+	FW_INITIALIZED=1
+	return 0
+}
diff --git a/package/firewall/files/lib/core_forwarding.sh b/package/firewall/files/lib/core_forwarding.sh
new file mode 100644
index 0000000000000000000000000000000000000000..766e48e38ec95df11cc4e2e01e3ba5bc72088e2c
--- /dev/null
+++ b/package/firewall/files/lib/core_forwarding.sh
@@ -0,0 +1,40 @@
+# Copyright (C) 2009-2010 OpenWrt.org
+
+fw_config_get_forwarding() {
+	[ "${forwarding_NAME}" != "$1" ] || return
+	fw_config_get_section "$1" forwarding { \
+		string _name "$1" \
+		string name "" \
+		string src "" \
+		string dest "" \
+	} || return
+	[ -n "$forwarding_name" ] || forwarding_name=$forwarding__name
+}
+
+fw_load_forwarding() {
+	fw_config_get_forwarding "$1"
+
+	fw_callback pre forwarding
+
+	local chain=forward
+	[ -n "$forwarding_src" ] && {
+		chain=zone_${forwarding_src}_forward 
+	}
+
+	local target=ACCEPT
+	[ -n "$forwarding_dest" ] && {
+		target=zone_${forwarding_dest}_ACCEPT
+	}
+
+	fw add i f $chain $target ^
+
+	# propagate masq zone flag
+	[ -n "$forwarding_src" ] && list_contains CONNTRACK_ZONES $forwarding_src && {
+		append CONNTRACK_ZONES $forwarding_dest
+	}
+	[ -n "$forwarding_dest" ] && list_contains CONNTRACK_ZONES $forwarding_dest && {
+		append CONNTRACK_ZONES $forwarding_src
+	}
+
+	fw_callback post forwarding
+}
diff --git a/package/firewall/files/lib/core_init.sh b/package/firewall/files/lib/core_init.sh
new file mode 100644
index 0000000000000000000000000000000000000000..82939b941661c20e4d2b918771ab404e6970999c
--- /dev/null
+++ b/package/firewall/files/lib/core_init.sh
@@ -0,0 +1,258 @@
+# Copyright (C) 2009-2010 OpenWrt.org
+# Copyright (C) 2008 John Crispin <blogic@openwrt.org>
+
+FW_INITIALIZED=
+
+FW_ZONES=
+FW_CONNTRACK_ZONES=
+FW_NOTRACK_DISABLED=
+
+FW_DEFAULTS_APPLIED=
+FW_ADD_CUSTOM_CHAINS=
+FW_ACCEPT_REDIRECTS=
+FW_ACCEPT_SRC_ROUTE=
+
+FW_DEFAULT_INPUT_POLICY=REJECT
+FW_DEFAULT_OUTPUT_POLICY=REJECT
+FW_DEFAULT_FORWARD_POLICY=REJECT
+
+
+fw_load_defaults() {
+	fw_config_get_section "$1" defaults { \
+		string input $FW_DEFAULT_INPUT_POLICY \
+		string output $FW_DEFAULT_OUTPUT_POLICY \
+		string forward $FW_DEFAULT_FORWARD_POLICY \
+		boolean drop_invalid 0 \
+		boolean syn_flood 0 \
+		boolean synflood_protect 0 \
+		string synflood_rate 25 \
+		string synflood_burst 50 \
+		boolean tcp_syncookies 1 \
+		boolean tcp_ecn 0 \
+		boolean tcp_westwood 0 \
+		boolean tcp_window_scaling 1 \
+		boolean accept_redirects 0 \
+		boolean accept_source_route 0 \
+		boolean custom_chains 1 \
+	} || return
+	[ -n "$FW_DEFAULTS_APPLIED" ] && {
+		echo "Error: multiple defaults sections detected"
+		return 1
+	}
+	FW_DEFAULTS_APPLIED=1
+
+	FW_DEFAULT_INPUT_POLICY=$defaults_input
+	FW_DEFAULT_OUTPUT_POLICY=$defaults_output
+	FW_DEFAULT_FORWARD_POLICY=$defaults_forward
+
+	FW_ADD_CUSTOM_CHAINS=$defaults_custom_chains
+
+	FW_ACCEPT_REDIRECTS=$defaults_accept_redirects
+	FW_ACCEPT_SRC_ROUTE=$defaults_accept_source_route
+
+	fw_callback pre defaults
+
+	# Seems like there are only one sysctl for both IP versions.
+	for s in syncookies ecn westwood window_scaling; do
+		eval "sysctl -e -w net.ipv4.tcp_${s}=\$defaults_tcp_${s}" >/dev/null
+	done
+	fw_sysctl_interface all
+
+	[ $defaults_drop_invalid == 1 ] && {
+		fw add i f INPUT   DROP { -m state --state INVALID }
+		fw add i f OUTPUT  DROP { -m state --state INVALID }
+		fw add i f FORWARD DROP { -m state --state INVALID }
+		FW_NOTRACK_DISABLED=1
+	}
+
+	fw add i f INPUT   ACCEPT { -m state --state RELATED,ESTABLISHED }
+	fw add i f OUTPUT  ACCEPT { -m state --state RELATED,ESTABLISHED }
+	fw add i f FORWARD ACCEPT { -m state --state RELATED,ESTABLISHED }
+
+	fw add i f INPUT  ACCEPT { -i lo }
+	fw add i f OUTPUT ACCEPT { -o lo }
+
+	# Compatibility to old 'syn_flood' parameter
+	[ $defaults_syn_flood == 1 ] && \
+		defaults_synflood_protect=1
+
+	[ $defaults_synflood_protect == 1 ] && {
+		echo "Loading synflood protection"
+		fw_callback pre synflood
+		fw add i f syn_flood
+		fw add i f syn_flood RETURN { \
+			-p tcp --syn \
+			-m limit --limit "${defaults_synflood_rate}/second" --limit-burst "${defaults_synflood_burst}" \
+		}
+		fw add i f syn_flood DROP
+		fw add i f INPUT syn_flood { -p tcp --syn }
+		fw_callback post synflood
+	}
+
+	[ $defaults_custom_chains == 1 ] && {
+		echo "Adding custom chains"
+		fw add i f input_rule
+		fw add i f output_rule
+		fw add i f forwarding_rule
+		fw add i n prerouting_rule
+		fw add i n postrouting_rule
+			
+		fw add i f INPUT       input_rule
+		fw add i f OUTPUT      output_rule
+		fw add i f FORWARD     forwarding_rule
+		fw add i n PREROUTING  prerouting_rule
+		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 INPUT   input
+	fw add i f OUTPUT  output
+	fw add i f FORWARD forward
+
+	fw add i f reject
+	fw add i f reject REJECT { --reject-with tcp-reset -p tcp }
+	fw add i f reject REJECT { --reject-with port-unreach }
+
+	fw_set_filter_policy
+
+	fw_callback post defaults
+}
+
+
+fw_config_get_zone() {
+	[ "${zone_NAME}" != "$1" ] || return
+	fw_config_get_section "$1" zone { \
+		string name "$1" \
+		string network "" \
+		string input "$FW_DEFAULT_INPUT_POLICY" \
+		string output "$FW_DEFAULT_OUTPUT_POLICY" \
+		string forward "$FW_DEFAULT_FORWARD_POLICY" \
+		boolean masq 0 \
+		boolean conntrack 0 \
+		boolean mtu_fix 0 \
+		boolean custom_chains "$FW_ADD_CUSTOM_CHAINS" \
+	} || return
+	[ -n "$zone_name" ] || zone_name=$zone_NAME
+	[ -n "$zone_network" ] || zone_network=$zone_name
+}
+
+fw_load_zone() {
+	fw_config_get_zone "$1"
+
+	list_contains FW_ZONES $zone_name && {
+		fw_die "zone ${zone_name}: duplicated zone"
+	}
+	append FW_ZONES $zone_name
+
+	fw_callback pre zone
+
+	[ $zone_conntrack = 1 -o $zone_masq = 1 ] && \
+		append FW_CONNTRACK_ZONES "$zone_NAME"
+
+	local chain=zone_${zone_name}
+
+	fw add i f ${chain}_ACCEPT
+	fw add i f ${chain}_DROP
+	fw add i f ${chain}_REJECT
+	fw add i f ${chain}_MSSFIX
+
+	# TODO: Rename to ${chain}_input
+	fw add i f ${chain}
+	fw add i f ${chain} ${chain}_${zone_input} $
+
+	fw add i f ${chain}_forward
+	fw add i f ${chain}_forward ${chain}_${zone_forward} $
+
+	# TODO: add ${chain}_output
+	fw add i f output ${chain}_${zone_output} $
+
+	# TODO: Rename to ${chain}_MASQUERADE
+	fw add i n ${chain}_nat
+	fw add i n ${chain}_prerouting
+
+	fw add i r ${chain}_notrack
+	[ $zone_masq == 1 ] && \
+		fw add i n POSTROUTING ${chain}_nat $
+
+	[ $zone_mtu_fix == 1 ] && \
+		fw add i f FORWARD ${chain}_MSSFIX ^
+
+	[ $zone_custom_chains == 1 ] && {
+		[ $FW_ADD_CUSTOM_CHAINS == 1 ] || \
+			fw_die "zone ${zone_name}: custom_chains globally disabled"
+
+		fw add i f input_${zone_name}
+		fw add i f ${chain} input_${zone_name} ^
+
+		fw add i f forwarding_${zone_name}
+		fw add i f ${chain}_forward forwarding_${zone_name} ^
+
+		fw add i n prerouting_${zone_name}
+		fw add i n ${chain}_prerouting prerouting_${zone_name} ^
+	}
+
+	fw_callback post zone
+}
+
+fw_load_notrack_zone() {
+	list_contains FW_CONNTRACK_ZONES "$1" && return
+
+	fw_config_get_zone "$1"
+
+	fw_callback pre notrack
+
+	fw add i f zone_${zone_name}_notrack NOTRACK $
+
+	fw_callback post notrack
+}
+
+
+fw_load_include() {
+	local name="$1"
+
+	local path; config_get path ${name} path
+	[ -e $path ] && . $path
+}
+
+
+fw_clear() {
+	local policy=$1
+
+	fw_set_filter_policy $policy
+
+	local tab
+	for tab in f n r; do
+		fw del i $tab
+	done
+}
+
+fw_set_filter_policy() {
+	local policy=$1
+
+	local chn tgt
+	for chn in INPUT OUTPUT FORWARD; do
+		eval "tgt=\${policy:-\${FW_DEFAULT_${chn}_POLICY}}"
+		[ $tgt == "REJECT" ] && tgt=reject
+		[ $tgt == "ACCEPT" -o $tgt == "DROP" ] || {
+			fw add i f $chn $tgt $
+			tgt=DROP
+		}
+		fw policy i f $chn $tgt
+	done
+}
+
+
+fw_callback() {
+	local pp=$1
+	local hk=$2
+
+	local libs lib
+	eval "libs=\$FW_CB_${pp}_${hk}"
+	[ -n "$libs" ] || return
+	for lib in $libs; do
+		${lib}_${pp}_${hk}_cb
+	done
+}
diff --git a/package/firewall/files/lib/core_interface.sh b/package/firewall/files/lib/core_interface.sh
new file mode 100644
index 0000000000000000000000000000000000000000..9da6739f0e8de948c2d93e89539899beceab8f29
--- /dev/null
+++ b/package/firewall/files/lib/core_interface.sh
@@ -0,0 +1,86 @@
+# Copyright (C) 2009-2010 OpenWrt.org
+
+fw_configure_interface() {
+	local iface=$1
+	local action=$2
+	local ifname=$3
+
+	local status;
+	config_get_bool status "$iface" up "0"
+	[ "$status" == 1 ] || return 0
+
+	[ -n "$ifname" ] || {
+		config_get ifname "$iface" ifname
+		ifname=${ifname:-$iface}
+	}
+	[ "$ifname" == "lo" ] && return 0
+
+	fw_callback pre interface
+
+	fw__do_rules() {
+		local action=$1
+		local chain=$2
+		local ifname=$3
+
+		fw $action i f ${chain}_ACCEPT ACCEPT ^ { -o "$ifname" }
+		fw $action i f ${chain}_ACCEPT ACCEPT ^ { -i "$ifname" }
+		fw $action i f ${chain}_DROP   DROP   ^ { -o "$ifname" }
+		fw $action i f ${chain}_DROP   DROP   ^ { -i "$ifname" }
+		fw $action i f ${chain}_REJECT reject ^ { -o "$ifname" }
+		fw $action i f ${chain}_REJECT reject ^ { -i "$ifname" }
+
+		fw $action i n ${chain}_nat MASQUERADE ^ { -o "$ifname" }
+		fw $action i f ${chain}_MSSFIX TCPMSS  ^ { -o "$ifname" -p tcp --tcp-flags SYN,RST SYN --clamp-mss-to-pmtu }
+
+		fw $action i f input   ${chain}         $ { -i "$ifname" }
+		fw $action i f forward ${chain}_forward $ { -i "$ifname" }
+		fw $action i n PREROUTING ${chain}_prerouting ^ { -i "$ifname" }
+		fw $action i r PREROUTING ${chain}_notrack    ^ { -i "$ifname" }
+	}
+
+	local old_zones old_ifname
+	config_get old_zones core "${iface}_zone"
+	[ -n "$old_zones" ] && {
+		config_get old_ifname core "${iface}_ifname"
+		for z in $old_zones; do
+			fw_log info "removing $iface ($old_ifname) from zone $z"
+			fw__do_rules del zone_$z $old_ifname
+
+			ACTION=remove ZONE="$z" INTERFACE="$iface" DEVICE="$ifname" /sbin/hotplug-call firewall
+		done
+		uci_revert_state firewall core "${iface}_zone"
+		uci_revert_state firewall core "${iface}_ifname"
+	}
+	[ "$action" == del ] && return
+
+	local new_zones
+	load_zone() {
+		fw_config_get_zone "$1"
+		list_contains zone_network "$iface" || return
+
+		fw_log info "adding $iface ($ifname) to zone $zone_name"
+		fw__do_rules add zone_${zone_name} "$ifname"
+		append new_zones $zone_name
+
+		ACTION=add ZONE="$zone_name" INTERFACE="$iface" DEVICE="$ifname" /sbin/hotplug-call firewall
+	}
+	config_foreach load_zone zone
+
+	uci_set_state firewall core "${iface}_zone" "$new_zones"
+	uci_set_state firewall core "${iface}_ifname" "$ifname"
+
+	fw_sysctl_interface $ifname
+
+	fw_callback post interface
+}
+
+fw_sysctl_interface() {
+	local ifname=$1
+	{
+		sysctl -w net.ipv4.conf.${ifname}.accept_redirects=$FW_ACCEPT_REDIRECTS
+		sysctl -w net.ipv6.conf.${ifname}.accept_redirects=$FW_ACCEPT_REDIRECTS
+		sysctl -w net.ipv4.conf.${ifname}.accept_source_route=$FW_ACCEPT_SRC_ROUTE
+		sysctl -w net.ipv6.conf.${ifname}.accept_source_route=$FW_ACCEPT_SRC_ROUTE
+	} >/dev/null 2>/dev/null
+}
+
diff --git a/package/firewall/files/lib/core_redirect.sh b/package/firewall/files/lib/core_redirect.sh
new file mode 100644
index 0000000000000000000000000000000000000000..0f0ccffe0000bbb443f7cb08a50345ff05300a41
--- /dev/null
+++ b/package/firewall/files/lib/core_redirect.sh
@@ -0,0 +1,61 @@
+# Copyright (C) 2009-2010 OpenWrt.org
+
+fw_config_get_redirect() {
+	[ "${redirect_NAME}" != "$1" ] || return
+	fw_config_get_section "$1" redirect { \
+		string _name "$1" \
+		string name "" \
+		string src "" \
+		ipaddr src_ip "" \
+		ipaddr src_dip "" \
+		string src_mac "" \
+		string src_port "" \
+		string src_dport "" \
+		string dest "" \
+		ipaddr dest_ip "" \
+		string dest_mac "" \
+		string dest_port "" \
+		string proto "tcpudp" \
+	} || return
+	[ -n "$redirect_name" ] || redirect_name=$redirect__name
+}
+
+fw_load_redirect() {
+	fw_config_get_redirect "$1"
+
+	fw_callback pre redirect
+
+	[ -n "$redirect_src" -a -n "$redirect_dest_ip" ] || {
+		fw_die "redirect ${redirect_name}: needs src and dest_ip"
+	}
+
+	local nat_dest_port=$redirect_dest_port
+	redirect_dest_port=$(fw_get_port_range $redirect_dest_port)
+	redirect_src_port=$(fw_get_port_range $redirect_src_port)
+	redirect_src_dport=$(fw_get_port_range $redirect_src_dport)
+	local fwd_dest_port=${redirect_dest_port:-$redirect_src_dport}
+
+	[ "$redirect_proto" == "tcpudp" ] && redirect_proto="tcp udp"
+	for redirect_proto in $redirect_proto; do
+		fw add I n zone_${redirect_src}_prerouting DNAT $ { $redirect_src_ip $redirect_dest_ip } { \
+			${redirect_proto:+-p $redirect_proto} \
+			${redirect_src_ip:+-s $redirect_src_ip} \
+			${redirect_src_dip:+-d $redirect_src_dip} \
+			${redirect_src_port:+--sport $redirect_src_port} \
+			${redirect_src_dport:+--dport $redirect_src_dport} \
+			${redirect_src_mac:+-m mac --mac-source $redirect_src_mac} \
+			--to-destination ${redirect_dest_ip}${redirect_dest_port:+:$nat_dest_port} \
+		}
+
+		fw add I f zone_${redirect_src}_forward ACCEPT ^ { $redirect_src_ip $redirect_dest_ip } { \
+			-d $redirect_dest_ip \
+			${redirect_proto:+-p $redirect_proto} \
+			${redirect_src_ip:+-s $redirect_src_ip} \
+			${redirect_src_port:+--sport $redirect_src_port} \
+			${fwd_dest_port:+--dport $fwd_dest_port} \
+			${redirect_src_mac:+-m mac --mac-source $redirect_src_mac} \
+		}
+	done
+
+	fw_callback post redirect
+}
diff --git a/package/firewall/files/lib/core_rule.sh b/package/firewall/files/lib/core_rule.sh
new file mode 100644
index 0000000000000000000000000000000000000000..e6a276e5f3a31716c4af69e468d8e5e29bc19fe5
--- /dev/null
+++ b/package/firewall/files/lib/core_rule.sh
@@ -0,0 +1,66 @@
+# Copyright (C) 2009-2010 OpenWrt.org
+
+fw_config_get_rule() {
+	[ "${rule_NAME}" != "$1" ] || return
+	fw_config_get_section "$1" rule { \
+		string _name "$1" \
+		string name "" \
+		string src "" \
+		ipaddr src_ip "" \
+		string src_mac "" \
+		string src_port "" \
+		string dest "" \
+		ipaddr dest_ip "" \
+		string dest_mac "" \
+		string dest_port "" \
+		string icmp_type "" \
+		string proto "tcpudp" \
+		string target "" \
+	} || return
+	[ -n "$rule_name" ] || rule_name=$rule__name
+	[ "$rule_proto" == "icmp" ] || rule_icmp_type=
+}
+
+fw_load_rule() {
+	fw_config_get_rule "$1"
+
+	fw_callback pre rule
+
+	rule_src_port=$(fw_get_port_range $rule_src_port)
+	rule_dest_port=$(fw_get_port_range $rule_dest_port)
+	
+	local chain=input
+	[ -n "$rule_src" ] && {
+		[ -z "$rule_dest" ] && {
+			chain=zone_${rule_src}
+		} || {
+			chain=zone_${rule_src}_forward
+		}
+	}
+
+	local target=$rule_target
+	[ -z "$target" ] && {
+		target=REJECT
+	}
+	[ -n "$dest" ] && {
+		target=zone_${rule_dest}_${target}
+	}
+
+	local rule_pos
+	eval 'rule_pos=$((++FW__RULE_COUNT_'$chain'))'
+
+	[ "$rule_proto" == "tcpudp" ] && rule_proto="tcp udp"
+	for rule_proto in $rule_proto; do
+		fw add I f $chain $target $rule_pos { $rule_src_ip $rule_dest_ip } { \
+			${rule_proto:+-p $rule_proto} \
+			${rule_src_ip:+-s $rule_src_ip} \
+			${rule_src_port:+--sport $rule_src_port} \
+			${rule_src_mac:+-m mac --mac-source $rule_src_mac} \
+			${rule_dest_ip:+-d $rule_dest_ip} \
+			${rule_dest_port:+--dport $rule_dest_port} \
+			${rule_icmp_type:+--icmp-type $rule_icmp_type} \
+		}
+	done
+
+	fw_callback post rule
+}
diff --git a/package/firewall/files/lib/fw.sh b/package/firewall/files/lib/fw.sh
new file mode 100644
index 0000000000000000000000000000000000000000..c06e8642364223d529f9ad8f15d4bd442113a12c
--- /dev/null
+++ b/package/firewall/files/lib/fw.sh
@@ -0,0 +1,182 @@
+# Copyright (C) 2009-2010 OpenWrt.org
+# Copyright (C) 2009 Malte S. Stretz
+
+export FW_4_ERROR=0
+export FW_6_ERROR=0
+export FW_i_ERROR=0
+export FW_e_ERROR=0
+export FW_a_ERROR=0
+
+#TODO: remove this
+[ "${-#*x}" == "$-" ] && {
+	fw() {
+		fw__exec "$@"
+	}
+} || {
+	fw() {
+		local os=$-
+		set +x
+		fw__exec "$@"
+		local rc=$?
+		set -$os
+		return $rc
+	}
+}
+
+fw__exec() { # <action> <family> <table> <chain> <target> <position> { <rules> }
+	local cmd fam tab chn tgt pos
+	local i
+	for i in cmd fam tab chn tgt pos; do
+		if [ "$1" -a "$1" != '{' ]; then
+			eval "$i='$1'"
+			shift
+		else
+			eval "$i=-"
+		fi
+	done
+
+	fw__rc() {
+		export FW_${fam}_ERROR=$1
+		return $1
+	}
+
+	fw__dualip() {
+		fw $cmd 4 $tab $chn $tgt $pos "$@"
+		fw $cmd 6 $tab $chn $tgt $pos "$@"
+		fw__rc $((FW_4_ERROR | FW_6_ERROR))
+	}
+
+	fw__autoip() {
+		local ip4 ip6
+		shift
+		while [ "$1" != '}' ]; do
+			case "$1" in
+				*.*.*.*) ip4=1 ;;
+				*:*) ip6=1 ;;
+			esac
+			shift
+		done
+		shift
+		if [ "${ip4:-4}" == "${ip6:-6}" ]; then
+			echo "fw: can't mix ip4 and ip6" >&2
+ 			return 1
+		fi
+		local ver=${ip4:+4}${ip6:+6}
+		fam=i
+		fw $cmd ${ver:-i} $tab $chn $tgt $pos "$@"
+		fw__rc $?
+	}
+
+	fw__has() {
+		local tab=${1:-$tab}
+		if [ $tab == '-' ]; then
+			type $app > /dev/null 2> /dev/null
+			fw__rc $(($? & 1))
+			return 
+		fi
+		local mod
+		eval "mod=\$FW_${fam}_${tab}"
+		if [ "$mod" ]; then
+			fw__rc $mod
+			return
+		fi
+		case "$fam" in
+			4) mod=iptable_${tab} ;;
+			6) mod=ip6table_${tab} ;;
+			*) mod=. ;;
+		esac
+		grep "^${mod} " /proc/modules > /dev/null
+		mod=$?
+		export FW_${fam}_${tab}=$mod
+		fw__rc $mod
+	}
+
+	fw__err() {
+		local err
+		eval "err=\$FW_${fam}_ERROR"
+		fw__rc $err
+	}
+
+	local app=
+	local pol=
+	case "$fam" in
+		4) app=iptables ;;
+		6) app=ip6tables ;;
+		i) fw__dualip "$@"; return ;;
+		I) fw__autoip "$@"; return ;;
+		e) app=ebtables ;;
+		a) app=arptables ;;
+		-) fw $cmd i $tab $chn $tgt $pos "$@"; return ;;
+		*) return 254 ;;
+	esac
+	case "$tab" in
+		f) tab=filter ;;
+		m) tab=mangle ;;
+		n) tab=nat ;;
+		r) tab=raw ;;
+		-) tab=filter ;;
+	esac
+	case "$cmd:$chn:$tgt:$pos" in
+		add:*:-:*) cmd=new-chain ;;
+		add:*:*:-) cmd=append ;;
+		add:*:*:$) cmd=append ;;
+		add:*:*:*) cmd=insert ;;
+		del:-:*:*) cmd=delete-chain; fw flush $fam $tab ;;
+		del:*:-:*) cmd=delete-chain; fw flush $fam $tab $chn ;;
+		del:*:*:*) cmd=delete ;;
+		flush:*) ;;
+		policy:*) pol=$tgt; tgt=- ;;
+		has:*) fw__has; return ;;
+		err:*) fw__err; return ;;
+		list:*) cmd="numeric --verbose --$cmd" ;;
+		*) return 254 ;;
+	esac
+	case "$chn" in
+		-) chn= ;;
+	esac
+	case "$tgt" in
+		-) tgt= ;;
+	esac
+	case "$pos" in
+		^) pos=1 ;;
+		$) pos= ;;
+		-) pos= ;;
+	esac
+
+	if ! fw__has - family || ! fw__has $tab ; then
+		export FW_${fam}_ERROR=0
+		return 0
+	fi
+
+	if [ $# -gt 0 ]; then
+		shift
+		if [ $cmd == del ]; then
+			pos=-
+		fi
+	fi
+	while [ $# -gt 1 ]; do
+		echo -n  "$1"
+		echo -ne "\0"
+		shift
+	done | xargs -0 ${FW_TRACE:+-t} \
+		$app --table ${tab} --${cmd} ${chn} ${pol} ${pos} ${tgt:+--jump "$tgt"}
+	fw__rc $?
+}
+
+fw_get_port_range() {
+	local ports=$1
+	local delim=${2:-:}
+	if [ "$3" ]; then
+		fw_get_port_range "${ports}-${3}" $delim
+		return
+	fi
+
+	local first=${ports%-*}
+	local last=${ports#*-}
+	if [ "$first" != "$last" ]; then
+		echo "$first$delim$last"
+	else
+		echo "$first"
+	fi
+}
+
diff --git a/package/firewall/files/lib/uci_firewall.sh b/package/firewall/files/lib/uci_firewall.sh
new file mode 100644
index 0000000000000000000000000000000000000000..7c95a7a93923e25cf6ee69384d24da30adbc6dba
--- /dev/null
+++ b/package/firewall/files/lib/uci_firewall.sh
@@ -0,0 +1,5 @@
+# This file is here for backwards compatibility and to override the
+# uci_firewall.sh from an earlier version.
+type fw_is_loaded >/dev/null || {
+	. /lib/firewall/core.sh
+}
diff --git a/package/firewall/files/uci_firewall.sh b/package/firewall/files/uci_firewall.sh
deleted file mode 100755
index 8d7538201b97882d9236b02a66c4a3c0ec41651a..0000000000000000000000000000000000000000
--- a/package/firewall/files/uci_firewall.sh
+++ /dev/null
@@ -1,530 +0,0 @@
-#!/bin/sh
-# Copyright (C) 2008 John Crispin <blogic@openwrt.org>
-
-. /etc/functions.sh
-
-IPTABLES="echo iptables"
-IPTABLES=iptables
-
-config_clear
-include /lib/network
-scan_interfaces
-
-CONFIG_APPEND=1
-config_load firewall
-
-config fw_zones
-ZONE_LIST=$CONFIG_SECTION
-
-CUSTOM_CHAINS=1
-DEF_INPUT=DROP
-DEF_OUTPUT=DROP
-DEF_FORWARD=DROP
-CONNTRACK_ZONES=
-NOTRACK_DISABLED=
-
-find_item() {
-	local item="$1"; shift
-	for i in "$@"; do
-		[ "$i" = "$item" ] && return 0
-	done
-	return 1
-}
-
-load_policy() {
-	config_get input $1 input
-	config_get output $1 output
-	config_get forward $1 forward
-
-	DEF_INPUT="${input:-$DEF_INPUT}"
-	DEF_OUTPUT="${output:-$DEF_OUTPUT}"
-	DEF_FORWARD="${forward:-$DEF_FORWARD}"
-}
-
-create_zone() {
-	local exists
-
-	[ "$1" == "loopback" ] && return
-
-	config_get exists $ZONE_LIST $1
-	[ -n "$exists" ] && return
-	config_set $ZONE_LIST $1 1
-
-	$IPTABLES -N zone_$1
-	$IPTABLES -N zone_$1_MSSFIX
-	$IPTABLES -N zone_$1_ACCEPT
-	$IPTABLES -N zone_$1_DROP
-	$IPTABLES -N zone_$1_REJECT
-	$IPTABLES -N zone_$1_forward
-	[ "$4" ] && $IPTABLES -A output -j zone_$1_$4
-	$IPTABLES -N zone_$1_nat -t nat
-	$IPTABLES -N zone_$1_prerouting -t nat
-	$IPTABLES -t raw -N zone_$1_notrack
-	[ "$6" == "1" ] && $IPTABLES -t nat -A POSTROUTING -j zone_$1_nat
-	[ "$7" == "1" ] && $IPTABLES -I FORWARD 1 -j zone_$1_MSSFIX
-}
-
-
-addif() {
-	local network="$1"
-	local ifname="$2"
-	local zone="$3"
-
-	local n_if n_zone
-	config_get n_if core "${network}_ifname"
-	config_get n_zone core "${network}_zone"
-	[ -n "$n_zone" ] && {
-		if [ "$n_zone" != "$zone" ]; then
-			delif "$network" "$n_if" "$n_zone"
-		else
-			return
-		fi
-	}
-
-	logger "adding $network ($ifname) to firewall zone $zone"
-	$IPTABLES -A input -i "$ifname" -j zone_${zone}
-	$IPTABLES -I zone_${zone}_MSSFIX 1 -o "$ifname" -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
-	$IPTABLES -I zone_${zone}_ACCEPT 1 -o "$ifname" -j ACCEPT
-	$IPTABLES -I zone_${zone}_DROP 1 -o "$ifname" -j DROP
-	$IPTABLES -I zone_${zone}_REJECT 1 -o "$ifname" -j reject
-	$IPTABLES -I zone_${zone}_ACCEPT 1 -i "$ifname" -j ACCEPT
-	$IPTABLES -I zone_${zone}_DROP 1 -i "$ifname" -j DROP
-	$IPTABLES -I zone_${zone}_REJECT 1 -i "$ifname" -j reject
-	$IPTABLES -I zone_${zone}_nat 1 -t nat -o "$ifname" -j MASQUERADE
-	$IPTABLES -I PREROUTING 1 -t nat -i "$ifname" -j zone_${zone}_prerouting
-	$IPTABLES -A forward -i "$ifname" -j zone_${zone}_forward
-	$IPTABLES -t raw -I PREROUTING 1 -i "$ifname" -j zone_${zone}_notrack
-	uci_set_state firewall core "${network}_ifname" "$ifname"
-	uci_set_state firewall core "${network}_zone" "$zone"
-	ACTION=add ZONE="$zone" INTERFACE="$network" DEVICE="$ifname" /sbin/hotplug-call firewall
-}
-
-delif() {
-	local network="$1"
-	local ifname="$2"
-	local zone="$3"
-
-	logger "removing $network ($ifname) from firewall zone $zone"
-	$IPTABLES -D input -i "$ifname" -j zone_$zone
-	$IPTABLES -D zone_${zone}_MSSFIX -o "$ifname" -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
-	$IPTABLES -D zone_${zone}_ACCEPT -o "$ifname" -j ACCEPT
-	$IPTABLES -D zone_${zone}_DROP -o "$ifname" -j DROP
-	$IPTABLES -D zone_${zone}_REJECT -o "$ifname" -j reject
-	$IPTABLES -D zone_${zone}_ACCEPT -i "$ifname" -j ACCEPT
-	$IPTABLES -D zone_${zone}_DROP -i "$ifname" -j DROP
-	$IPTABLES -D zone_${zone}_REJECT -i "$ifname" -j reject
-	$IPTABLES -D zone_${zone}_nat -t nat -o "$ifname" -j MASQUERADE
-	$IPTABLES -D PREROUTING -t nat -i "$ifname" -j zone_${zone}_prerouting
-	$IPTABLES -D forward -i "$ifname" -j zone_${zone}_forward
-	uci_revert_state firewall core "${network}_ifname"
-	uci_revert_state firewall core "${network}_zone"
-	ACTION=remove ZONE="$zone" INTERFACE="$network" DEVICE="$ifname" /sbin/hotplug-call firewall
-}
-
-load_synflood() {
-	local rate=${1:-25}
-	local burst=${2:-50}
-	echo "Loading synflood protection"
-	$IPTABLES -N syn_flood
-	$IPTABLES -A syn_flood -p tcp --syn -m limit --limit $rate/second --limit-burst $burst -j RETURN
-	$IPTABLES -A syn_flood -j DROP
-	$IPTABLES -A INPUT -p tcp --syn -j syn_flood
-}
-
-fw_set_chain_policy() {
-	local chain=$1
-	local target=$2
-	[ "$target" == "REJECT" ] && {
-		$IPTABLES -A $chain -j reject
-		target=DROP
-	}
-	$IPTABLES -P $chain $target
-}
-
-fw_clear() {
-	$IPTABLES -F
-	$IPTABLES -t nat -F
-	$IPTABLES -t nat -X
-	$IPTABLES -t raw -F
-	$IPTABLES -t raw -X
-	$IPTABLES -X
-}
-
-fw_defaults() {
-	[ -n "$DEFAULTS_APPLIED" ] && {
-		echo "Error: multiple defaults sections detected"
-		return;
-	}
-	DEFAULTS_APPLIED=1
-
-	load_policy "$1"
-
-	echo 1 > /proc/sys/net/ipv4/tcp_syncookies
-	for f in /proc/sys/net/ipv4/conf/*/accept_redirects
-	do
-		echo 0 > $f
-	done
-	for f in /proc/sys/net/ipv4/conf/*/accept_source_route
-	do
-		echo 0 > $f
-	done
-
-	uci_revert_state firewall core
-	uci_set_state firewall core "" firewall_state
-
-	$IPTABLES -P INPUT DROP
-	$IPTABLES -P OUTPUT DROP
-	$IPTABLES -P FORWARD DROP
-
-	fw_clear
-	config_get_bool drop_invalid $1 drop_invalid 0
-
-	[ "$drop_invalid" -gt 0 ] && {
-		$IPTABLES -A INPUT -m state --state INVALID -j DROP
-		$IPTABLES -A OUTPUT -m state --state INVALID -j DROP
-		$IPTABLES -A FORWARD -m state --state INVALID -j DROP
-		NOTRACK_DISABLED=1
-	}
-
-	$IPTABLES -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-	$IPTABLES -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-	$IPTABLES -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
-
-	$IPTABLES -A INPUT -i lo -j ACCEPT
-	$IPTABLES -A OUTPUT -o lo -j ACCEPT
-
-	config_get syn_flood $1 syn_flood
-	config_get syn_rate $1 syn_rate
-	config_get syn_burst $1 syn_burst
-	[ "$syn_flood" == "1" ] && load_synflood $syn_rate $syn_burst
-
-	echo "Adding custom chains"
-	fw_custom_chains
-
-	$IPTABLES -N input
-	$IPTABLES -N output
-	$IPTABLES -N forward
-
-	$IPTABLES -A INPUT -j input
-	$IPTABLES -A OUTPUT -j output
-	$IPTABLES -A FORWARD -j forward
-
-	$IPTABLES -N reject
-	$IPTABLES -A reject -p tcp -j REJECT --reject-with tcp-reset
-	$IPTABLES -A reject -j REJECT --reject-with icmp-port-unreachable
-
-	fw_set_chain_policy INPUT "$DEF_INPUT"
-	fw_set_chain_policy OUTPUT "$DEF_OUTPUT"
-	fw_set_chain_policy FORWARD "$DEF_FORWARD"
-}
-
-fw_zone_defaults() {
-	local name
-	local network
-	local masq
-
-	config_get name $1 name
-	config_get network $1 network
-	config_get_bool masq $1 masq "0"
-	config_get_bool conntrack $1 conntrack "0"
-	config_get_bool mtu_fix $1 mtu_fix 0
-
-	load_policy $1
-	[ "$forward" ] && $IPTABLES -A zone_${name}_forward -j zone_${name}_${forward}
-	[ "$input" ] && $IPTABLES -A zone_${name} -j zone_${name}_${input}
-}
-
-fw_zone() {
-	local name
-	local network
-	local masq
-
-	config_get name $1 name
-	config_get network $1 network
-	config_get_bool masq $1 masq "0"
-	config_get_bool conntrack $1 conntrack "0"
-	config_get_bool mtu_fix $1 mtu_fix 0
-
-	load_policy $1
-	[ "$conntrack" = "1" -o "$masq" = "1" ] && append CONNTRACK_ZONES "$name"
-	[ -z "$network" ] && network=$name
-	create_zone "$name" "$network" "$input" "$output" "$forward" "$masq" "$mtu_fix"
-	fw_custom_chains_zone "$name"
-}
-
-fw_rule() {
-	local src
-	local src_ip
-	local src_mac
-	local src_port
-	local src_mac
-	local dest
-	local dest_ip
-	local dest_port
-	local proto
-	local icmp_type
-	local target
-	local ruleset
-
-	config_get src $1 src
-	config_get src_ip $1 src_ip
-	config_get src_mac $1 src_mac
-	config_get src_port $1 src_port
-	config_get dest $1 dest
-	config_get dest_ip $1 dest_ip
-	config_get dest_port $1 dest_port
-	config_get proto $1 proto
-	config_get icmp_type $1 icmp_type
-	config_get target $1 target
-	config_get ruleset $1 ruleset
-
-	src_port_first=${src_port%-*}
-	src_port_last=${src_port#*-}
-	[ "$src_port_first" -ne "$src_port_last" ] && { \
-		src_port="$src_port_first:$src_port_last"; }
-
-	dest_port_first=${dest_port%-*}
-	dest_port_last=${dest_port#*-}
-	[ "$dest_port_first" -ne "$dest_port_last" ] && { \
-		dest_port="$dest_port_first:$dest_port_last"; }
-
-	ZONE=input
-	TARGET=$target
-	[ -z "$target" ] && target=DROP
-	[ -n "$src" -a -z "$dest" ] && ZONE=zone_$src
-	[ -n "$src" -a -n "$dest" ] && ZONE=zone_${src}_forward
-	[ -n "$dest" ] && TARGET=zone_${dest}_$target
-
-	eval 'RULE_COUNT=$((++RULE_COUNT_'$ZONE'))'
-
-	add_rule() {
-		$IPTABLES -I $ZONE $RULE_COUNT \
-			${proto:+-p $proto} \
-			${icmp_type:+--icmp-type $icmp_type} \
-			${src_ip:+-s $src_ip} \
-			${src_port:+--sport $src_port} \
-			${src_mac:+-m mac --mac-source $src_mac} \
-			${dest_ip:+-d $dest_ip} \
-			${dest_port:+--dport $dest_port} \
-			-j $TARGET
-	}
-	[ "$proto" == "tcpudp" -o -z "$proto" ] && {
-		proto=tcp
-		add_rule
-		proto=udp
-		add_rule
-		return
-	}
-	add_rule
-}
-
-fw_forwarding() {
-	local src
-	local dest
-	local masq
-
-	config_get src $1 src
-	config_get dest $1 dest
-	[ -n "$src" ] && z_src=zone_${src}_forward || z_src=forward
-	[ -n "$dest" ] && z_dest=zone_${dest}_ACCEPT || z_dest=ACCEPT
-	$IPTABLES -I $z_src 1 -j $z_dest
-
-	# propagate masq zone flag
-	find_item "$src" $CONNTRACK_ZONES && append CONNTRACK_ZONES $dest
-	find_item "$dest" $CONNTRACK_ZONES && append CONNTRACK_ZONES $src
-}
-
-fw_redirect() {
-	local src
-	local src_ip
-	local src_port
-	local src_dport
-	local src_mac
-	local dest_ip
-	local dest_port dest_port2
-	local proto
-
-	config_get src $1 src
-	config_get src_ip $1 src_ip
-	config_get src_dip $1 src_dip
-	config_get src_port $1 src_port
-	config_get src_dport $1 src_dport
-	config_get src_mac $1 src_mac
-	config_get dest_ip $1 dest_ip
-	config_get dest_port $1 dest_port
-	config_get proto $1 proto
-	[ -z "$src" -o -z "$dest_ip" ] && { \
-		echo "redirect needs src and dest_ip"; return ; }
-
-	src_port_first=${src_port%-*}
-	src_port_last=${src_port#*-}
-	[ "$src_port_first" != "$src_port_last" ] && { \
-		src_port="$src_port_first:$src_port_last"; }
-
-	src_dport_first=${src_dport%-*}
-	src_dport_last=${src_dport#*-}
-	[ "$src_dport_first" != "$src_dport_last" ] && { \
-		src_dport="$src_dport_first:$src_dport_last"; }
-
-	dest_port2=${dest_port:-$src_dport}
-	dest_port_first=${dest_port2%-*}
-	dest_port_last=${dest_port2#*-}
-	[ "$dest_port_first" != "$dest_port_last" ] && { \
-		dest_port2="$dest_port_first:$dest_port_last"; }
-
-	add_rule() {
-		$IPTABLES -A zone_${src}_prerouting -t nat \
-			${proto:+-p $proto} \
-			${src_ip:+-s $src_ip} \
-			${src_dip:+-d $src_dip} \
-			${src_port:+--sport $src_port} \
-			${src_dport:+--dport $src_dport} \
-			${src_mac:+-m mac --mac-source $src_mac} \
-			-j DNAT --to-destination $dest_ip${dest_port:+:$dest_port}
-
-		$IPTABLES -I zone_${src}_forward 1 \
-			${proto:+-p $proto} \
-			-d $dest_ip \
-			${src_ip:+-s $src_ip} \
-			${src_port:+--sport $src_port} \
-			${dest_port2:+--dport $dest_port2} \
-			${src_mac:+-m mac --mac-source $src_mac} \
-			-j ACCEPT
-	}
-	[ "$proto" == "tcpudp" -o -z "$proto" ] && {
-		proto=tcp
-		add_rule
-		proto=udp
-		add_rule
-		return
-	}
-	add_rule
-}
-
-fw_include() {
-	local path
-	config_get path $1 path
-	[ -e $path ] && . $path
-}
-
-get_interface_zones() {
-	local interface="$2"
-	local name
-	local network
-	config_get name $1 name
-	config_get network $1 network
-	[ -z "$network" ] && network=$name 
-	for n in $network; do
-		[ "$n" = "$interface" ] && append add_zone "$name"
-	done
-}
-
-fw_event() {
-	local action="$1"
-	local interface="$2"
-	local ifname="$(sh -c ". /etc/functions.sh; include /lib/network; scan_interfaces; config_get "$interface" ifname")"
-	add_zone=
-	local up
-
-	[ -z "$ifname" ] && return 0
-	config_foreach get_interface_zones zone "$interface"
-	[ -z "$add_zone" ] && return 0
-
-	case "$action" in
-		ifup)
-			for z in $add_zone; do 
-				local loaded
-				config_get loaded core loaded
-				[ -n "$loaded" ] && addif "$interface" "$ifname" "$z"
-			done
-		;;
-		ifdown)
-			config_get up "$interface" up
-
-			for z in $ZONE; do 
-				[ "$up" == "1" ] && delif "$interface" "$ifname" "$z"
-			done
-		;;
-	esac
-}
-
-fw_addif() {
-	local up
-	local ifname
-	config_get up $1 up
-	[ -n "$up" ] || return 0
-	fw_event ifup "$1"
-}
-
-fw_custom_chains() {
-	[ -n "$CUSTOM_CHAINS" ] || return 0
-	$IPTABLES -N input_rule
-	$IPTABLES -N output_rule
-	$IPTABLES -N forwarding_rule
-	$IPTABLES -N prerouting_rule -t nat
-	$IPTABLES -N postrouting_rule -t nat
-
-	$IPTABLES -A INPUT -j input_rule
-	$IPTABLES -A OUTPUT -j output_rule
-	$IPTABLES -A FORWARD -j forwarding_rule
-	$IPTABLES -A PREROUTING -t nat -j prerouting_rule
-	$IPTABLES -A POSTROUTING -t nat -j postrouting_rule
-}
-
-fw_custom_chains_zone() {
-	local zone="$1"
-
-	[ -n "$CUSTOM_CHAINS" ] || return 0
-	$IPTABLES -N input_${zone}
-	$IPTABLES -N forwarding_${zone}
-	$IPTABLES -N prerouting_${zone} -t nat
-	$IPTABLES -I zone_${zone} 1 -j input_${zone}
-	$IPTABLES -I zone_${zone}_forward 1 -j forwarding_${zone}
-	$IPTABLES -I zone_${zone}_prerouting 1 -t nat -j prerouting_${zone}
-}
-
-fw_check_notrack() {
-	local zone="$1"
-	config_get name "$zone" name
-	[ -n "$NOTRACK_DISABLED" ] || \
-		find_item "$name" $CONNTRACK_ZONES || \
-		$IPTABLES -t raw -A zone_${name}_notrack -j NOTRACK
-}
-
-fw_init() {
-	DEFAULTS_APPLIED=
-
-	echo "Loading defaults"
-	config_foreach fw_defaults defaults
-	echo "Loading zones"
-	config_foreach fw_zone zone
-	echo "Loading forwarding"
-	config_foreach fw_forwarding forwarding
-	echo "Loading redirects"
-	config_foreach fw_redirect redirect
-	echo "Loading rules"
-	config_foreach fw_rule rule
-	echo "Loading includes"
-	config_foreach fw_include include
-	echo "Loading zone defaults"
-	config_foreach fw_zone_defaults zone
-	uci_set_state firewall core loaded 1
-	config_set core loaded 1
-	config_foreach fw_check_notrack zone
-	INTERFACES="$(sh -c '
-		. /etc/functions.sh; config_load network
-		echo_up() { local up; config_get_bool up "$1" up 0; [ $up = 1 ] && echo "$1"; }
-		config_foreach echo_up interface
-	')"
-	for interface in $INTERFACES; do
-		fw_event ifup "$interface"
-	done
-}
-
-fw_stop() {
-	fw_clear
-	$IPTABLES -P INPUT ACCEPT
-	$IPTABLES -P OUTPUT ACCEPT
-	$IPTABLES -P FORWARD ACCEPT
-	uci_revert_state firewall
-}