#!/bin/sh

test -n "$FAILSAFE" && exit
test -z "$(awk 'sub(":","") {print $1}' /proc/net/wireless)" && exit

. /etc/functions.sh

DEBUG=true	# Dump dropped packets to klog, show with "dmesg -c"

set_masq() {
	# Ignore OLSR packets directly from these stations, e.g.
	# if this connection is too slow, ignore sporadic packets
	IGNIPS=$(nvram get ff_ign)
	IFS=\;
	for IGNIP in $IGNIPS; do
		iptables -I INPUT -i $WIFIDEV -s $IGNIP -p udp --sport 698 -j DROP
	done
	unset IFS

	# Setup alias and dest nat for an ethernet DMZ PC, e.g.
	# 172.31.255.254 on WLAN -> DMZ PC which has 192.168.1.2
	# Also add to olsrd.conf: "HNA4 172.31.255.254 255.255.255.255"
	DEVNUM=0
	DMZS=$(nvram get ff_dmz)
	IFS=\;
	for DMZ in $DMZS; do
		SRCIP=${DMZ%[:,]*}
		DSTIP=${DMZ#*[:,]}
		ip addr add dev $WIFIDEV $SRCIP/32 broadcast $SRCIP label $WIFIDEV:$DEVNUM
		iptables -t nat -A PREROUTING -d $SRCIP -j DNAT --to $DSTIP
		iptables -I FORWARD -s ! $LANNET/$LANPRE -d $DSTIP -j ACCEPT
		iptables -I FORWARD -s $DSTIP -d ! $LANNET/$LANPRE -j ACCEPT
		iptables -I OUTPUT -o lo -s $SRCIP -d $SRCIP -j ACCEPT
		iptables -I INPUT -i lo -s $SRCIP -j ACCEPT
		DEVNUM=$(( $DEVNUM + 1 ))
	done
	unset IFS

	# Accept fragments
	iptables -I INPUT -f -j ACCEPT

	# Mask packets from these WLAN DHCP clients, so they can do inet w/o OLSR
	ENTS=$(nvram get ff_wldhcp)
	IFS=\;
	for ENT in $ENTS; do
		NET=${ENT%[:,]*}
		MSK=${ENT#*[:,]}
		iptables -t nat -A POSTROUTING -s $NET -j MASQUERADE
	done
	unset IFS

	if [ -z "$LANOLSR" ] && [ "$(nvram get ff_nonat)" != "1" ]; then
		# Mask packets from LAN to WIFI
		iptables -t nat -A POSTROUTING -o $WIFIDEV -s $LANNET/$LANPRE -j MASQUERADE
	fi

	if [ -n "$WANDEV" ]; then
		if [ -z "$WANOLSR" ]; then
			# Mask packets to WAN
			iptables -t nat -A POSTROUTING -o $WANDEV -j MASQUERADE
		elif [ -z "$LANOLSR" ] && [ "$(nvram get ff_nonat)" != "1" ]; then
			# Mask packets from LAN to WAN(OLSR)
			iptables -t nat -A POSTROUTING -o $WANDEV -s $LANNET/$LANPRE -j MASQUERADE
		fi
	fi

	if [ -n "$WIFIMTU" ] && [ 1500 -gt $WIFIMTU ]; then
		# Repair braindead websites
		iptables -I FORWARD -o $WIFIDEV -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
	fi
}

set_wlan_fw() {
	iptables -N ipfilter

	# Receive on /dev/lo? Only to/from 127.x.x.x and
	# to local devs because of kernel route tricks
	iptables -A ipfilter -i $LODEV -s $LONET/$LOPRE -d $LONET/$LOPRE -j ACCEPT
	iptables -A ipfilter -i $LODEV -s $LANADR -d $LANADR -j ACCEPT
	test -n "$WIFIADR" && iptables -A ipfilter -i $LODEV -s $WIFIADR -d $WIFIADR -j ACCEPT
	# Receive on /dev/et? Only to/from 192.168.x.x
	iptables -A ipfilter -i $LANDEV -s $LANNET/$LANPRE -d $LANNET/$LANPRE -j ACCEPT
	# Receive on /dev/wl? Only to 172.x.x.x, from inet ok
	test -n "$WIFIADR" && $DEBUG && iptables -A ipfilter -i $WIFIDEV -s $LONET/$LOPRE -j LOG
	test -n "$WIFIADR" && iptables -A ipfilter -i $WIFIDEV -s $LONET/$LOPRE -j DROP
	test -n "$WIFIADR" && $DEBUG && iptables -A ipfilter -i $WIFIDEV -s $LANNET/$LANPRE -j LOG
	test -n "$WIFIADR" && iptables -A ipfilter -i $WIFIDEV -s $LANNET/$LANPRE -j DROP
	test -n "$WIFIADR" && iptables -A ipfilter -i $WIFIDEV -d $WIFINET/$WIFIPRE -j ACCEPT

	# Send to /dev/lo? Only to/from 127.x.x.x and
	# from local devs because of kernel route tricks
	iptables -A ipfilter -o $LODEV -s $LONET/$LOPRE -d $LONET/$LOPRE -j ACCEPT
	iptables -A ipfilter -o $LODEV -s $LANADR -d $LANADR -j ACCEPT
	test -n "$WIFIADR" && iptables -A ipfilter -o $LODEV -s $WIFIADR -d $WIFIADR -j ACCEPT
	# Send to /dev/et? Only to/from 192.168.x.x
	iptables -A ipfilter -o $LANDEV -s $LANNET/$LANPRE -d $LANNET/$LANPRE -j ACCEPT
	# Send to /dev/wl? Only from 172.16.x.x, to inet ok
	test -n "$WIFIADR" && $DEBUG && iptables -A ipfilter -o $WIFIDEV -d $LONET/$LOPRE -j LOG
	test -n "$WIFIADR" && iptables -A ipfilter -o $WIFIDEV -d $LONET/$LOPRE -j DROP
	test -n "$WIFIADR" && $DEBUG && iptables -A ipfilter -o $WIFIDEV -d $LANNET/$LANPRE -j LOG
	test -n "$WIFIADR" && iptables -A ipfilter -o $WIFIDEV -d $LANNET/$LANPRE -j DROP
	test -n "$WIFIADR" && iptables -A ipfilter -o $WIFIDEV -s $WIFINET/$WIFIPRE -j ACCEPT

	# Allow DHCP. Note: Answers do not show up in INPUT|FORWARD
	test -n "$WIFIADR" && iptables -A INPUT -i $WIFIDEV -d 255.255.255.255 -p udp --sport 68 --dport 67 -j ACCEPT
	iptables -A INPUT -i $LANDEV -d 255.255.255.255 -p udp --sport 68 --dport 67 -j ACCEPT

	iptables -A INPUT -j ipfilter

	# Accept all OLSR broadcasts, even to 255.255.255.255
	iptables -A INPUT -i $WIFIDEV -s ! $LANNET/$LANPRE -p udp --sport 698 --dport 698 -j ACCEPT
	test -n "$WIFIADR" && iptables -A INPUT -i $LANDEV -s $LANNET/$LANPRE -d $WIFIADR -j ACCEPT
	test $DEBUG && iptables -A INPUT -j LOG --log-prefix "IN:"
	iptables -A INPUT -j DROP

	iptables -A OUTPUT -j ipfilter
	test -n "$WIFIADR" && iptables -A OUTPUT -o $LANDEV -s $WIFIADR -d $LANNET/$LANPRE -j ACCEPT
	test $DEBUG && iptables -A OUTPUT -j LOG --log-prefix "OUT:"
	iptables -A OUTPUT -j DROP

	iptables -A FORWARD -j ipfilter
	iptables -A FORWARD -i $WIFIDEV -o $WIFIDEV -j ACCEPT
	test -n "$WIFIADR" && iptables -A FORWARD -i $WIFIDEV -d 255.255.255.255 -p udp --sport 68 --dport 67 -j ACCEPT
	iptables -A FORWARD -i $LANDEV -d 255.255.255.255 -p udp --sport 68 --dport 67 -j ACCEPT
	test -n "$WIFIADR" && iptables -A FORWARD -i $WIFIDEV -o $LANDEV -m state --state ESTABLISHED,RELATED -j ACCEPT
	test -n "$WIFIADR" && iptables -A FORWARD -i $LANDEV -o $WIFIDEV -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
	test $DEBUG && iptables -A FORWARD -j LOG --log-prefix "FW:"
	iptables -A FORWARD -j DROP
}

set_wan_fw() {
	if [ -n "$WIFIADR" ] && [ "$(ipcalc -n $WANADR $WIFIMSK|cut -d'=' -f2)" = "$(ipcalc -n $WIFIADR $WIFIMSK|cut -d'=' -f2)" ]; then
		# WAN with OLSR, open firewall
		iptables -I INPUT -i $WANDEV -j ACCEPT
		iptables -I OUTPUT -o $WANDEV -j ACCEPT
		iptables -I FORWARD -o $WANDEV -j ACCEPT
		iptables -I FORWARD -i $WANDEV -j ACCEPT
		return
	fi
	test "$(nvram get ff_wanssh)" = "1" && iptables -A INPUT -i $WANDEV -p tcp --dport 22 -j ACCEPT
	test "$(nvram get ff_wanhttp)" = "1" && iptables -A INPUT -i $WANDEV -p tcp --dport 80 -j ACCEPT
	test "$(nvram get ff_wanicmp)" = "1" && iptables -A INPUT -i $WANDEV -p icmp -j ACCEPT
	iptables -A INPUT -i $WANDEV -m state --state INVALID -j DROP
	iptables -A INPUT -i $WANDEV -m state --state ESTABLISHED,RELATED -j ACCEPT
	test $DEBUG && iptables -A INPUT -i $WANDEV -j LOG --log-prefix "WAN:"
	iptables -A INPUT -i $WANDEV -j DROP

	iptables -A OUTPUT -o $WANDEV -j ACCEPT

	iptables -A FORWARD -i $LANDEV -o $WANDEV -m state --state INVALID -j DROP
	iptables -A FORWARD -i $LANDEV -o $WANDEV -s $LANNET/$LANPRE -d ! $LANNET/$LANPRE -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
	test $DEBUG && iptables -A FORWARD -i $LANDEV -o $WANDEV -j LOG --log-prefix "WAN:"
	iptables -A FORWARD -i $LANDEV -o $WANDEV -j DROP

	iptables -A FORWARD -i $WANDEV -o $LANDEV -m state --state INVALID -j DROP
	iptables -A FORWARD -i $WANDEV -o $LANDEV -s ! $LANNET/$LANPRE -d $LANNET/$LANPRE -m state --state ESTABLISHED,RELATED -j ACCEPT
	test $DEBUG && iptables -A FORWARD -i $WANDEV -o $LANDEV -d $LANNET/$LANPRE -j LOG --log-prefix "WAN:"
	iptables -A FORWARD -i $WANDEV -o $LANDEV -d $LANNET/$LANPRE -j DROP

	if [ -n "$WIFIADR" ]; then
		iptables -A FORWARD -i $WIFIDEV -o $WANDEV -m state --state INVALID -j DROP
		iptables -A FORWARD -i $WIFIDEV -o $WANDEV -s $WIFINET/$WIFIPRE -d ! $WIFINET/$WIFIPRE -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
		test $DEBUG && iptables -A FORWARD -i $WIFIDEV -o $WANDEV -j LOG --log-prefix "WAN:"
		iptables -A FORWARD -i $WIFIDEV -o $WANDEV -j DROP

		iptables -A FORWARD -i $WANDEV -o $WIFIDEV -m state --state INVALID -j DROP
		iptables -A FORWARD -i $WANDEV -o $WIFIDEV -s ! $WIFINET/$WIFIPRE -d $WIFINET/$WIFIPRE -m state --state ESTABLISHED,RELATED -j ACCEPT
		test $DEBUG && iptables -A FORWARD -i $WANDEV -o $WIFIDEV -d $WIFINET/$WIFIPRE -j LOG --log-prefix "WAN:"
		iptables -A FORWARD -i $WANDEV -o $WIFIDEV -d $WIFINET/$WIFIPRE -j DROP
	fi
}

harden_fw() {
	# Drop invalid state packets early
	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

	# Stealth Scans etc. DROP'ed (no flags set)
	iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP
	iptables -A FORWARD -p tcp --tcp-flags ALL NONE -j DROP

	# SYN and FIN set
	iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
	iptables -A FORWARD -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP

	# SYN and RST both set
	iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
	iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN,RST -j DROP

	# FIN w/o RST both set
	iptables -A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP
	iptables -A FORWARD -p tcp --tcp-flags FIN,RST FIN,RST -j DROP

	# FIN w/o ACK
	iptables -A INPUT -p tcp --tcp-flags ACK,FIN FIN -j DROP
	iptables -A FORWARD -p tcp --tcp-flags ACK,FIN FIN -j DROP

	# PSH w/o ACK
	iptables -A INPUT -p tcp --tcp-flags ACK,PSH PSH -j DROP
	iptables -A FORWARD -p tcp --tcp-flags ACK,PSH PSH -j DROP

	# URG w/o ACK
	iptables -A INPUT -p tcp --tcp-flags ACK,URG URG -j DROP
	iptables -A FORWARD -p tcp --tcp-flags ACK,URG URG -j DROP
}

clear_fw() {
	for T in filter nat mangle; do
		iptables -t $T -F 2>/dev/null
		iptables -t $T -X 2>/dev/null
		case $T in
			filter)
				for C in INPUT FORWARD OUTPUT; do
					iptables -t $T -P $C ACCEPT 2>/dev/null
				done
			;;
			nat)
				for C in PREROUTING OUTPUT POSTROUTING; do
					iptables -t $T -P $C ACCEPT 2>/dev/null
				done
			;;
			mangle)
				for C in PREROUTING INPUT FORWARD OUTPUT POSTROUTING; do
					iptables -t $T -P $C ACCEPT 2>/dev/null
				done
			;;
		esac
	done
}

case $1 in
	start)
		echo "Starting firewall..."
		export NVRAM_wan_ifname
		export NVRAM_lan_ifname
		export NVRAM_lan_ifnames
		eval $(/usr/bin/netparam)
		if [ "$(nvram get fw_disable)" != "1" ]; then
			test -n "$WANDEV" && set_wan_fw
			if [ -z "$WIFIADR" ] || [ "$(ipcalc -n $LANADR $WIFIMSK|cut -d'=' -f2)" != "$(ipcalc -n $WIFIADR $WIFIMSK|cut -d'=' -f2)" ]; then
				set_wlan_fw
			fi
		fi
		set_masq
		test -e /etc/local.fw && . /etc/local.fw start
	;;
	stop)
		echo "Stopping firewall..."
		test -e /etc/local.fw && . /etc/local.fw stop
		clear_fw
	;;
	masq)
		echo "Masquerading setup..."
		export NVRAM_wan_ifname
		export NVRAM_lan_ifname
		export NVRAM_lan_ifnames
		eval $(/usr/bin/netparam)
		set_masq
	;;
	reload)
		echo "Reload firewall..."
		# No clear_fw to preserve -t nat
		iptables -F
		iptables -X ipfilter
		export NVRAM_wan_ifname
		export NVRAM_lan_ifname
		export NVRAM_lan_ifnames
		eval $(/usr/bin/netparam)
		if [ "$(nvram get fw_disable)" != "1" ]; then
			harden_fw
			test -n "$WANDEV" && set_wan_fw
			if [ -z "$WIFIADR" ] || [ "$(ipcalc -n $LANADR $WIFIMSK|cut -d'=' -f2)" != "$(ipcalc -n $WIFIADR $WIFIMSK|cut -d'=' -f2)" ]; then
				set_wlan_fw
			fi
		fi
	;;
	restart)
		$0 stop
		$0 start
	;;
	status)
		iptables -v -n -L
	;;
	*)
		echo "Usage: $0 start|stop|masq|reload|restart|status"
	;;
esac
