#!/bin/sh

. /lib/functions.sh

ZONE_NAME_FILE="/tmp/cwmp_fw_zone"

#created by the icwmp package
log() {
	echo "${@}"|logger -t firewall.cwmp -p info
}

collect_zone_name() {
	local name network

	config_get name "${1}" name ""
	if [ -z "${name}" ]; then
		return
	fi

	config_get network "${1}" network ""
	for i in ${network}; do
		var="${i}_zone"
		echo "${var}=${name}" >> "${ZONE_NAME_FILE}"
	done
}

load_zone_names() {
	rm -f "${ZONE_NAME_FILE}"
	config_load firewall
	config_foreach collect_zone_name zone
}

get_firewall_zone() {
	if [ ! -f "${ZONE_NAME_FILE}" ]; then
		echo ""
		return
	fi

	var="${1}_zone="
	name="$(cat ${ZONE_NAME_FILE} | grep ${var} | head -n 1 | cut -d'=' -f 2)"
	echo "${name}"
}

cleanup_upstream_rules() {
	if [ -z "${1}" ]; then
		log "Rule can not be cleaned without zone name"
		return
	fi

	while iptables -w 1 -nL zone_"${1}"_input --line-numbers 2>/dev/null | grep "Open_ACS_port"; do
		rule_num="$(iptables -w 1 -nL zone_"${1}"_input --line-numbers | grep "Open_ACS_port" | head -1|awk '{print $1}')"
		if [ -n "${rule_num}" ]; then
			iptables -w 1 -D zone_"${1}"_input "${rule_num}";
		fi
	done
	while ip6tables -w 1 -nL zone_"${1}"_input --line-numbers 2>/dev/null | grep "Open_ACS_port"; do
		rule_num="$(ip6tables -w 1 -nL zone_"${1}"_input --line-numbers | grep "Open_ACS_port" | head -1|awk '{print $1}')"
		if [ -n "${rule_num}" ]; then
			ip6tables -w 1 -D zone_"${1}"_input "${rule_num}";
		fi
	done
}

cleanup_downstream_rules() {
	if [ -z "${1}" ]; then
		log "Rule can not be cleaned without zone name"
		return
	fi

	while iptables -w 1 -nL zone_"${1}"_input --line-numbers 2>/dev/null | grep "Close_ACS_port"; do
		rule_num="$(iptables -w 1 -nL zone_"${1}"_input --line-numbers | grep "Close_ACS_port" | head -1|awk '{print $1}')"
		if [ -n "${rule_num}" ]; then
			iptables -w 1 -D zone_"${1}"_input "${rule_num}";
		fi
	done
	while ip6tables -w 1 -nL zone_"${1}"_input --line-numbers 2>/dev/null | grep "Close_ACS_port"; do
		rule_num="$(ip6tables -w 1 -nL zone_"${1}"_input --line-numbers | grep "Close_ACS_port" | head -1|awk '{print $1}')"
		if [ -n "${rule_num}" ]; then
			ip6tables -w 1 -D zone_"${1}"_input "${rule_num}";
		fi
	done
}

close_downstream_acs_port() {
	lan="${1}"
	port="${2}"
	zone_name="$(get_firewall_zone $lan)"

	if [ -z "${zone_name}" ]; then
		log "Rule can not be added without zone name"
		return
	fi

	cmd="iptables -w 1 -I zone_${zone_name}_input -p tcp --dport ${port} -j DROP -m comment --comment=Close_ACS_port"
	${cmd}
	log "Applied [${cmd}]"

	cmd="ip6tables -w 1 -I zone_${zone_name}_input -p tcp --dport ${port} -j DROP -m comment --comment=Close_ACS_port"
	${cmd}
	log "Applied [${cmd}]"
}

add_firewall_rule() {
	version="${1}"
	ipaddr="${2}"
	port="${3}"
	zone_name="${4}"
	cmd=""

	if [ -z "${zone_name}" ]; then
		log "Rule can not be added without zone name"
		return
	fi

	if [ "${version}" = "ipv6" ]; then
		cmd="ip6tables -w 1 -I zone_${zone_name}_input -p tcp"
	else
		cmd="iptables -w 1 -I zone_${zone_name}_input -p tcp"
	fi

	if [ -n "${ipaddr}" ]; then
		cmd="${cmd} -s ${ipaddr}"
	fi

	if [ -n "${port}" ]; then
		cmd="${cmd} --dport ${port}"
	fi

	if echo "${cmd}"|grep -q "\-\-dport \|\-s "; then
		cmd="${cmd} -j ACCEPT -m comment --comment=Open_ACS_port"
		${cmd}
		log "Applied [${cmd}]"
	fi
}

remove_port_protection() {
	local enabled chain rule rule_num

	config_get enabled "${1}" "${2}"

	if [ "${enabled}" -eq 1 ]; then
		config_get zonename "$1" name
		[ -n "$zonename" ] || return 0

		chain='prerouting_'$zonename'_rule'

		while rule=$(iptables -w -t nat -nL "$chain" --line-numbers | grep -m 1 -w CWMP_Port_protection); do
			rule_num=${rule%%[$' \t']*}
			iptables -w -t nat -D "$chain" "$rule_num"
		done
	fi
}

cleanup_port_protection() {
	config_load firewall
	config_foreach remove_port_protection zone masq
}

install_port_protection() {
	local PORT="${3}"
	local enabled zonename chain

	config_get enabled "${1}" "${2}"

	if [ "${enabled}" -eq 1 ]; then
		config_get zonename "${1}" name
		[ -n "$zonename" ] || return 0

		chain='prerouting_'$zonename'_rule'

		iptables -w -t nat -I "$chain" -p tcp --dport "$PORT" -j ACCEPT -m comment --comment=CWMP_Port_protection
		iptables -w -t nat -I "$chain" -p udp --dport "$PORT" -j ACCEPT -m comment --comment=CWMP_Port_protection
	fi
}

add_port_protection() {
	config_load firewall
	config_foreach install_port_protection zone masq "${1}"
}

configure_connection_req_rules() {
	app="${1}"

	cleanup_port_protection

	wan="$(uci -q get cwmp.cpe.default_wan_interface)"
	wan="${wan:-wan}"

	wan_zone_name="$(get_firewall_zone $wan)"

	cleanup_upstream_rules "${wan_zone_name}"

	lan="$(uci -q get cwmp.cpe.default_lan_interface)"
	lan="${lan:-lan}"

	if [ "${lan}" != "${wan}" ]; then
		lan_zone_name="$(get_firewall_zone $lan)"
		cleanup_downstream_rules "${lan_zone_name}"
	fi

	enable="$(uci -q get cwmp.cpe.enable)"
	enable="${enable:-1}"

	if [ "$enable" -eq 0 ]; then
		exit 0
	fi

	url="$(uci -q get cwmp.acs.url)"
	if [ -z "${url}" ]; then
		url="$(uci -q get cwmp.acs.dhcp_url)"
	fi

	# no need to apply firewall rule, acs url not configured
	if [ -z "${url}" ]; then
		exit 0
	fi

	if [ -z "${app}" ]; then
		if ! ubus -t 1 list tr069 2>/dev/null; then
			log "cwmp client not running"
			exit 0
		fi
	fi

	port=$(uci -q -c /var/state get icwmp.cpe.port)
	if [ -z "${port}" ]; then
		log "cwmp cpe port not configured"
		exit 0
	fi

	ipaddr=$(uci -q get cwmp.cpe.allowed_cr_ip)
	if [ -n "${ipaddr}" ]; then
		for ip in $ipaddr; do
			if echo "${ip}" | grep -q ":"; then
				add_firewall_rule "ipv6" "${ip}" "${port}" "${wan_zone_name}"
			else
				add_firewall_rule "ipv4" "${ip}" "${port}" "${wan_zone_name}"
			fi
		done
	else
		# Port-only
		add_firewall_rule "ipv6" "" "${port}" "${wan_zone_name}"
		add_firewall_rule "ipv4" "" "${port}" "${wan_zone_name}"
	fi

	if [ "${lan}" != "${wan}" ]; then
		# Close the ACS port at Lan side
		close_downstream_acs_port "${lan}" "${port}"
	fi

	add_port_protection "${port}"
}

load_zone_names
configure_connection_req_rules "$@"
rm -f "${ZONE_NAME_FILE}"
