#!/bin/sh

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/syno/sbin:/usr/syno/bin
IPTABLES="iptables"
MODULES_BASE="/lib/modules"
RULES_IPTABLES="/etc/firewall_rules.dump"
RULES_6_IPTABLES="/etc/firewall_6_rules.dump"
KERNEL_VERSION=`uname -r`
PROC_IPTABLES_NAMES="/proc/net/ip_tables_names"
PROC_6_IPTABLES_NAMES="/proc/net/ip6_tables_names"
V4ONLY=`get_key_value /etc.defaults/synoinfo.conf ipv4only`
WIRELESS_INFO="/tmp/wireless.info"
WIRELESS_AP="/usr/syno/etc/wireless_ap.conf"
KERNEL_FW_STATUS_FILE="/proc/sys/kernel/syno_firewall_status"
SZD_FW_SECURITY_ROOT="/etc/fw_security"
FW_SECURITY_SCRIPT="$SZD_FW_SECURITY_ROOT/sysconf/iptables_security.sh"
FW="firewall"
NAT="nat"
NATTEST="nattest"
PORTFW="portfw"
PORT_TRIGGER="port_trigger"
URL_FILTER="URL_filter"
CONTENT_FILTER="Content_filter"
HAS_PORTFW_RULE=0
# For guest wifi
RC_NETWORK=/etc/rc.network
LOCK_FILE="/tmp/iptables_scripts.lock"
ADMIN_PORT=`get_key_value /etc/synoinfo.conf admin_port`
ADMIN_PORT_SECURE=`get_key_value /etc/synoinfo.conf secure_admin_port`

## Get module list
source /usr/syno/etc.defaults/iptables_modules_list

success() {
	[ -n $1 ] && echo "success" || echo "$1: success"
}

failure() {
	[ -n $1 ] && echo "failed" || echo "$1: failed"
}

reverse_syno() {
	local modules=$1
	local mod
	local ret=""

	for mod in $modules; do
	    ret="$mod $ret"
	done

	echo $ret
}

dump_rules_clear() {
	if [ -e $1 ]; then
		rm -f $1
	fi
	if [ -e $2 ]; then
		rm -f $2
	fi
}

load_nat_module()
{
	local port_forward="$1"

	/usr/syno/bin/iptablestool --checkap
	local ret_checkap=$?

	if [ "${port_forward}" = "forwarding_test" ]; then
		/usr/syno/bin/iptablestool --insmod $NATTEST  ${KERNEL_MODULES_COMMON}
	fi

	return 0
}

unload_nat_module()
{
	local port_forward="$1"
	local fw_has_rules=0
	local portfw_has_rules=0

	dump_fw_rules "/etc/firewall" "0"
	fw_has_rules=$?

	if [ "${port_forward}" = "forwarding_test" ]; then
		if [ 0 -eq $fw_has_rules -o 0 -eq $portfw_has_rules ]; then
			stop_kernel $NATTEST
		else
			stop $NATTEST 1
		fi
	else
		#Run AP mode
		if [ 0 -eq $fw_has_rules -o 0 -eq $portfw_has_rules ]; then
			stop_kernel $NAT
		else
			stop $NAT 1
		fi
	fi
}

load_port_trigger_module()
{
	/usr/syno/bin/iptablestool --insmod $PORT_TRIGGER  ${KERNEL_MODULES_COMMON} ${PT_MODULES}
}

unload_port_trigger_module()
{
	local modules_reverse=`reverse_syno "${KERNEL_MODULES_COMMON} ${PT_MODULES}"`
	/usr/syno/bin/iptablestool --rmmod $PORT_TRIGGER ${modules_reverse}
}

load_url_filter_module()
{
	/usr/syno/bin/iptablestool --insmod $URL_FILTER ${KERNEL_MODULES_COMMON} ${URL_FILTER_MODULES}
}

unload_url_filter_module()
{
	local modules_reverse=`reverse_syno "${KERNEL_MODULES_COMMON} ${URL_FILTER_MODULES}"`
	/usr/syno/bin/iptablestool --rmmod $URL_FILTER ${modules_reverse}
}

load_content_filter_module()
{
	/usr/syno/bin/iptablestool --insmod $CONTENT_FILTER ${KERNEL_MODULES_COMMON} ${CONTENT_FILTER_MODULES}
}

unload_content_filter_module()
{
	local modules_reverse=`reverse_syno "${KERNEL_MODULES_COMMON} ${CONTENT_FILTER_MODULES}"`
	/usr/syno/bin/iptablestool --rmmod $CONTENT_FILTER ${modules_reverse}
}

dump_fw_rules()
{
	local v6rules="${RULES_6_IPTABLES}"
	local ret=
	[ "yes" = "${V4ONLY}" ] && v6rules="none"

	dump_rules_clear ${RULES_IPTABLES} ${RULES_6_IPTABLES}
	firewalltool -dump_rules $1 ${RULES_IPTABLES} ${v6rules} $2
	ret=$?

	[ -f "${RULES_IPTABLES}" -o -f "${RULES_6_IPTABLES}" ] && return 0
	return 1
}

check_admin_ports()
{
	if [ -z $ADMIN_PORT ]; then
		ADMIN_PORT=8000
	fi

	if [ -z $ADMIN_PORT_SECURE ]; then
		ADMIN_PORT_SECURE=8001
	fi
}

get_lbr0_CIDR()
{
	local IPADDR_PREFIX
	local CIDR_PREFIX
	local CIDR_IP
	local CIDR

	if ! IPADDR_PREFIX=$(synonettool --lan --get-ip-mask --ip_type ipv4 --id lbr0); then
		return 1
	fi
	CIDR_PREFIX="$(echo "${IPADDR_PREFIX}" | awk -F"/" '{print $2}')"
	CIDR_IP="$(ipcalc -n "${IPADDR_PREFIX}" | cut -d'=' -f2)"
	CIDR="${CIDR_IP}/${CIDR_PREFIX}"

	echo "${CIDR}"
}

get_lbr0_IP()
{
	local IPADDR_PREFIX=$(synonettool --lan --get-ip-mask --ip_type ipv4 --id lbr0)

	echo ${IPADDR_PREFIX} | awk -F"/" '{print $1}'
}

update_first_install_rules()
{
	local welcome_installer_hide=$(/bin/get_key_value /etc/synoinfo.conf welcome_installer_hide)

	if [ "yes" != "${welcome_installer_hide}" ]; then
		add_first_install_rules
	else
		remove_first_install_rules
	fi
}

add_first_install_rules()
{
	local CIDR
	local lbr0_IP

	remove_first_install_rules

	check_admin_ports

	IPTABLES_RULES="*nat
-A FIRST_INSTALL_PREROUTING -p udp --dport 53 -j REDIRECT --to-port 5450"

	if CIDR="$(get_lbr0_CIDR)" && lbr0_IP="$(get_lbr0_IP)"; then
		IPTABLES_RULES="${IPTABLES_RULES}
-A FIRST_INSTALL_PREROUTING -i lbr0 -s ${CIDR} -p tcp --dport 80 -j DNAT --to-destination ${lbr0_IP}
-A FIRST_INSTALL_PREROUTING -i lbr0 -s ${CIDR} -p tcp -m multiport --dport 443,${ADMIN_PORT_SECURE} -j REDIRECT --to-port ${ADMIN_PORT_SECURE}
-A FIRST_INSTALL_PREROUTING -i lbr0 -s ${CIDR} -p tcp -d 0/0 -j DNAT --to-destination ${lbr0_IP}:${ADMIN_PORT}"
	fi

	# for connection from eth0, but must be private ip
	# for mesh webapi connection from sta0.1 and sta1.1
	# allow port 137,138,9998,9999 for findhostd
	IPTABLES_RULES="${IPTABLES_RULES}
COMMIT
*filter
-I FIRST_INSTALL_INPUT -i eth0 -s 10.0.0.0/8 -p tcp -m multiport --dport 80,443,$ADMIN_PORT,$ADMIN_PORT_SECURE -j ACCEPT
-I FIRST_INSTALL_INPUT -i eth0 -s 172.16.0.0/12 -p tcp -m multiport --dport 80,443,$ADMIN_PORT,$ADMIN_PORT_SECURE -j ACCEPT
-I FIRST_INSTALL_INPUT -i eth0 -s 192.168.0.0/16 -p tcp -m multiport --dport 80,443,$ADMIN_PORT,$ADMIN_PORT_SECURE -j ACCEPT
-I FIRST_INSTALL_INPUT -i sta0.1 -p tcp -m multiport --dport 80,443,$ADMIN_PORT,$ADMIN_PORT_SECURE -j ACCEPT
-I FIRST_INSTALL_INPUT -i sta1.1 -p tcp -m multiport --dport 80,443,$ADMIN_PORT,$ADMIN_PORT_SECURE -j ACCEPT
-I FIRST_INSTALL_INPUT -i sta2.1 -p tcp -m multiport --dport 80,443,$ADMIN_PORT,$ADMIN_PORT_SECURE -j ACCEPT
-I FIRST_INSTALL_INPUT -i eth0 -p udp -m multiport --dport 137,138,9998,9999 -j ACCEPT
COMMIT"

	# allow fe80::/10:7783 for mesh ethernet scan
	IP6TABLES_RULES="*filter
-I FIRST_INSTALL_INPUT -i eth0 -s fe80::/10 -p tcp -m multiport --dport 80,443,$ADMIN_PORT,$ADMIN_PORT_SECURE -j ACCEPT
-I FIRST_INSTALL_INPUT -i sta0.1 -p icmpv6 --icmpv6-type echo-request -j ACCEPT
-I FIRST_INSTALL_INPUT -i sta1.1 -p icmpv6 --icmpv6-type echo-request -j ACCEPT
-I FIRST_INSTALL_INPUT -i sta2.1 -p icmpv6 --icmpv6-type echo-request -j ACCEPT
-I FIRST_INSTALL_INPUT -i sta0.1 -p tcp -m multiport --dport 80,443,$ADMIN_PORT,$ADMIN_PORT_SECURE -j ACCEPT
-I FIRST_INSTALL_INPUT -i sta1.1 -p tcp -m multiport --dport 80,443,$ADMIN_PORT,$ADMIN_PORT_SECURE -j ACCEPT
-I FIRST_INSTALL_INPUT -i sta2.1 -p tcp -m multiport --dport 80,443,$ADMIN_PORT,$ADMIN_PORT_SECURE -j ACCEPT
-I FIRST_INSTALL_INPUT -i eth0 -s fe80::/10 -p udp --dport 7783 -j ACCEPT
COMMIT"

	for i in $(seq 1 3)
	do
		if echo "$IPTABLES_RULES" | iptables-restore; then
			break
		fi
		sleep 1
	done
	for i in $(seq 1 3)
	do
		if echo "$IP6TABLES_RULES" | ip6tables-restore; then
			break
		fi
		sleep 1
	done

	# start redirecting all domain names to lbr0's ip
	if [ ! -z "${lbr0_IP}" ]; then
		/usr/syno/sbin/dnsmasq --user=root --port=5450 --pid-file=/var/run/first_install_dnsmasq.pid --address="/#/${lbr0_IP}" --server=/kaspersky-labs.com/kaspersky.com/digicert.com/phicdn.net/#
	fi
}

remove_first_install_rules()
{
	/sbin/iptables -t nat -F FIRST_INSTALL_PREROUTING
	/sbin/iptables -t filter -F FIRST_INSTALL_INPUT

	# stop redirecting domain name "synology.router" to lbr0's ip
	if [ -f /var/run/first_install_dnsmasq.pid ]; then
		kill `cat /var/run/first_install_dnsmasq.pid` 2>/dev/null
	fi
}

start() {
	local welcome_installer_hide=`/bin/get_key_value /etc/synoinfo.conf welcome_installer_hide`
	local topology=$(synonettool --topology --get)

	loadFwKernelModules

	if [ "bridge" = "${topology}" ] || [ "repeater" = "${topology}" ]; then
		/usr/syno/sbin/synofirewalltool --disable
	else
		/usr/syno/sbin/synofirewalltool --reload
	fi

	return 0
}

loadFwKernelModules() {
	/usr/syno/bin/iptablestool --insmod synofirewall ${KERNEL_MODULES_COMMON} ${GEOIP_MODULES}
}

removeFwKernelModules() {
	modules_reverse=`reverse_syno "$KERNEL_MODULES_COMMON ${GEOIP_MODULES}"`
	/usr/syno/bin/iptablestool --rmmod synofirewall $modules_reverse
}

stop() {
	removeFwKernelModules
	/usr/syno/sbin/synofirewalltool --disable
}

help() {
	echo $"Usage: ${0##*/} {start|stop|restart|force-reload|load_nat_mod|unload_nat_mod|load_pt_mod|unload_pt_mod|load_url_filter_mod|unload_url_filter_mod|load_content_filter_mod|unload_content_filter_mod}"
}

get_lock()
{
	local timeout=30
	local i=0

	exec 100>$LOCK_FILE
	for i in `seq 1 $timeout`; do
		flock -x -n 100
		if [ 0 -eq $? ]; then
			return 0
		fi
		sleep 1
	done
	return 1
}

free_lock()
{
	flock -u 100
}

get_lock
if [ 0 -ne $? ]; then
	echo "$0 $*: failed to acquire file lock"
	logger -p err "$0 $*: failed to acquire file lock"
	exit 1
fi

case "$1" in
	start)
		start 0 $2
		RETVAL=$?
		$FW_SECURITY_SCRIPT start $HAS_PORTFW_RULE
		;;
	force-reload)
		start $2 $3
		RETVAL=$?
		$FW_SECURITY_SCRIPT start $HAS_PORTFW_RULE
		;;
	stop)
		stop
		RETVAL=$?
		$FW_SECURITY_SCRIPT stop
		;;
	restart)
		stop
		$FW_SECURITY_SCRIPT stop
		start 0 $2
		RETVAL=$?
		$FW_SECURITY_SCRIPT start $HAS_PORTFW_RULE
		;;
	load_nat_mod)
		load_nat_module $2
		RETVAL=$?
		;;
	unload_nat_mod)
		unload_nat_module $2
		;;
	load_pt_mod)
		load_port_trigger_module
		;;
	unload_pt_mod)
		unload_port_trigger_module
		;;
	load_url_filter_mod)
		load_url_filter_module
		;;
	unload_url_filter_mod)
		unload_url_filter_module
		;;
	load_content_filter_mod)
		load_content_filter_module
		;;
	unload_content_filter_mod)
		unload_content_filter_module
		;;
	update_first_install_rules)
		update_first_install_rules
		;;
	remove_first_install_rules)
		remove_first_install_rules
		;;
	add_first_install_rules)
		add_first_install_rules
		;;
	*)
		help
		exit 1
		;;
esac

free_lock

exit $RETVAL
