#!/bin/sh /etc/rc.common

######## Reserved Ports:
#	(<enable/disable> <port number definition>)
#1. (Default Fixed)DHCP Client udp packets on port: 68(IPv4),546(IPV6)
#		uci config:acc_ctrl; script:acc_ctrl
#2. (Default Custom)ipsec ISAKMP, ipsec NAT-t:
#		uci config:ipsec_config; script:ipsec.init
#3. (Toggle Fixed)L2TP: 1701
#		uci config:l2tpd_config; script:xl2tpd.init
#4. (Toggle Fixed)PPTP: 1723
#		uci config:pptpd_config; script:pptpd.init
#5. (Toggle Fixed)SNMP: 161
#		uci config:snmpd; script:snmpd.init
#6. (Toggle Custom)TR069: 
#		uci config:cwmp; script:cwmp.init
#7. (Toggle Custom)Allow remote access to local machine: custom(web_port,telnet_port,ssh_port,https_port)
#		uci config:acc_ctrl; script:acc_ctrl

START=88
ACC_CTRL="ACC_CTRL"
ACC_CTRL_AL="ACC_CTRL_AL" #access list
ACC6_CTRL="ACC6_CTRL"
PRE_NAT_FUNCS="PRE_NAT_FUNCS"
model=$(head -n 1 /etc/version)

append_multiport() {
	[ -z "$multiport" ] && multiport="$1" || multiport="$multiport,$1"
}
append_blockport() {
    [ -z "$multiblockport" ] && multiblockport="$1" || multiblockport="$multiblockport,$1"
}

acc_ctrl_config()
{
	local web_allow
	local web_port
	local telnet_allow
	local telnet_port
	local ssh_allow
	local ssh_port
	local https_allow
	local https_port
	local server_cert
	local user_define
	local allow_ip
	local wan_ping_allow
	local lan_ping_allow
	local intf
	local block_lan
	multiport=""
	multiblockport=""
	config_get web_allow $1 web_allow
	config_get web_port $1 web_port
	config_get telnet_allow  $1 telnet_allow
	config_get telnet_port $1 telnet_port
	config_get ssh_allow $1 ssh_allow
	config_get ssh_port $1 ssh_port
	config_get https_allow $1 https_allow
	config_get https_port $1 https_port
	config_get server_cert $1 server_cert
	config_get user_define $1 user_define
	config_get allow_ip $1 allow_ip
    config_get wan_ping_allow $1 wan_ping_allow
    config_get lan_ping_allow $1 lan_ping_allow
	config_get block_lan $1 block_lan
	config_get mngt_wan $1 mngt_wan
	config_get wan_pip_only $1 wan_pip_only
	all_wan_pip_set=""
	[ "$wan_pip_only" = "enable" ] && all_wan_pip_set="-m set --set all_wan_pip dst"
    #echo $https_port $block_lan $1> /dev/console

    #restart lighttpd if port is changed
	local last_web_port=$(uci oget acc_ctrl.access_control.web_port)
    local last_https_port=$(uci oget acc_ctrl.access_control.https_port)  	    	
    local last_server_cert=$(uci oget acc_ctrl.access_control.server_cert)
    if [ "$last_web_port" != "$web_port" ] || [ "$last_https_port" != "$https_port" ] || [ "$last_server_cert" != "$server_cert" ];then
		#Reset web portal attributes for lighttpd restart
		/sbin/setup_webportal_attr
    	/etc/init.d/lighttpd restart 1>/dev/null 2>/dev/null
    fi
    
	#restart telnet if port is changed
    local last_telnet_port=$(uci oget acc_ctrl.access_control.telnet_port)    
    if [ "$telnet_port" != "$last_telnet_port" ];then
    	/etc/init.d/telnet restart 1>/dev/null 2>/dev/null
    fi
    
    #restart ssh if port is changed
	local last_ssh_port=$(uci oget acc_ctrl.access_control.ssh_port)
    if [ "$ssh_port" != "$last_ssh_port" ];then
    	/etc/init.d/dropbear restart 1>/dev/null 2>/dev/null
    fi
	
	#collect access ports
	if [ "$ssh_allow" == "enable" ] ;then
		append_multiport $ssh_port	
    else
        append_blockport $ssh_port	
	fi
	if [ "$web_allow" == "enable" ] ;then
		append_multiport $web_port	
    else
        append_blockport $web_port	
	fi
	if [ "$telnet_allow" == "enable" ] ;then
		append_multiport $telnet_port
    else
        append_blockport $telnet_port	
	fi   
	if [ "$https_allow" == "enable" ] ;then  
		append_multiport $https_port
    else
        append_blockport $https_port	
	fi
	
	# G41319:We need to accept udp packets on port 68,
	# see https://dev.openwrt.org/ticket/4108
	iptables -A $ACC_CTRL -i wan+ -p udp --dport 68 -j ACCEPT
	#DHCPv6 has the same issue: G41730
	ip6tables -A $ACC6_CTRL -i wan+ -p udp --dport 546 -j ACCEPT
	
	########Allow PING from WAN/LAN
    if [ "$wan_ping_allow" = "disable" ];then
		if [ "$lan_ping_allow" = "enable" ] ;then
			#allow ping from lan
			iptables -A $ACC_CTRL -i ppp+ -p icmp --icmp-type 8 -j ACCEPT
			iptables -A $ACC_CTRL -i lan+ -p icmp --icmp-type 8 -j ACCEPT
			ip6tables -A $ACC6_CTRL -i wan+ -p icmpv6 --icmpv6-type 128 -j DROP
			ip6tables -A $ACC6_CTRL -i lan+ -p icmpv6 --icmpv6-type 128 -j ACCEPT
		else
			#do not allow any ping
			iptables -A $ACC_CTRL -p icmp --icmp-type 8 -j DROP
			ip6tables -A $ACC6_CTRL -p icmpv6 --icmpv6-type 128 -j DROP
		fi
	else
		if [ "$lan_ping_allow" = "enable" ] ;then
			#allow ping from lan/wan
			iptables -A $ACC_CTRL -p icmp --icmp-type 8 -j ACCEPT
			ip6tables -A $ACC6_CTRL -p icmpv6 --icmpv6-type 128 -j ACCEPT
		else
			#allow ping from wan
			iptables -A $ACC_CTRL -i wan+ -p icmp --icmp-type 8 -m mset ! --set exception_subnet_set src ! --set exception_subnet_gre_set src -j ACCEPT
			iptables -A $ACC_CTRL -i wan+ -p icmp --icmp-type 8 -m mset --set exception_subnet_set src --set exception_subnet_gre_set src -j DROP
			iptables -A $ACC_CTRL -i ppp+ -p icmp --icmp-type 8 -j DROP
			iptables -A $ACC_CTRL -i lan+ -p icmp --icmp-type 8 -j DROP
			ip6tables -A $ACC6_CTRL -i wan+ -p icmpv6 --icmpv6-type 128 -j ACCEPT
			ip6tables -A $ACC6_CTRL -i lan+ -p icmpv6 --icmpv6-type 128 -j DROP
		fi
	fi
	
	######## Allow ICMPv6 from WAN/LAN
	ip6tables -A $ACC6_CTRL -p icmpv6 -j ACCEPT
	
	########User Defined IP: if it's enabled, only defined subnet can access local service ports
    if [ "$user_define" = "enable" -a -n "$multiport" ]; then
		allow_ip_flag=0
		for each_ip in $allow_ip
		do
			if [ "$each_ip" != "0.0.0.0/0" ] ;then
				iptables -A $ACC_CTRL_AL -p tcp -s $each_ip -m multiport --dport $multiport -j ACCEPT
				iptables -t nat -I $PRE_NAT_FUNCS -p tcp -s $each_ip $all_wan_pip_set -m multiport --dport $multiport -j RETURN
				allow_ip_flag=1
			fi
		done
		
		if [ "$allow_ip_flag" = 0 ];then
			logger "(WebUI access control), ERROR: It must have at least one non-zero IP in \"Allowed IP\""
			uci set acc_ctrl.$1.user_define=disable	
            if [ -n "$block_lan" ]; then		       
		        for ifs in $block_lan; do 
	               iptables -A $ACC_CTRL -i lan-$ifs -p tcp -m multiport --dport $multiblockport -j DROP
			       ip6tables -A $ACC6_CTRL -i lan-$ifs -p tcp -m multiport --dport $multiblockport -j DROP
		        done
		    fi		
		fi
	    	
	elif [ -n "$multiport" ]; then
	    if [ -n "$block_lan" ]; then		
		    for ifs in $block_lan; do 
	          iptables -A $ACC_CTRL -i lan-$ifs -p tcp -m multiport --dport $multiblockport -j DROP
			  ip6tables -A $ACC6_CTRL -i lan-$ifs -p tcp -m multiport --dport $multiblockport -j DROP
		    done
		fi
		iptables -A $ACC_CTRL -p tcp -m multiport --dport $multiport -j ACCEPT
		iptables -t nat -I $PRE_NAT_FUNCS -p tcp $all_wan_pip_set -m multiport --dport $multiport -j RETURN
		ip6tables -A $ACC6_CTRL -p tcp -m multiport --dport $multiport -j ACCEPT
	elif [ -n "$multiblockport" ]; then
        if [ -n "$block_lan" ]; then		
		    for ifs in $block_lan; do 
	          iptables -A $ACC_CTRL -i lan-$ifs -p tcp -m multiport --dport $multiblockport -j DROP
			  ip6tables -A $ACC6_CTRL -i lan-$ifs -p tcp -m multiport --dport $multiblockport -j DROP
		    done
		fi
	
	fi
	
	if [ "$model" = "Vigor3900" -o "$model" = "Vigor2960" -o "$model" = "Vigor2960F" ]; then
		########Open global regular service port/protocol:
		ikeport=`uci get ipsec_config.ipsec.ikeport`
		natport=`uci get ipsec_config.ipsec.natport`
		iptables -A ACC_CTRL -i wan+ -p udp -m multiport --dport $ikeport,$natport -j ACCEPT
		iptables -A $ACC_CTRL -i wan+ -p esp -j ACCEPT	#allow ipsec
		iptables -A $ACC_CTRL -i wan+ -p ah -j ACCEPT	#allow ipsec proto to router
		iptables -A $ACC_CTRL -i wan+ -p 47 -j ACCEPT	#allow gre proto to local
	fi
	
	#T17737: open path for manage WAN
	if [ -n "$mngt_wan" ] ;then
		sn2=$(mtd fw_printenv 1 | grep sn2= | cut -d '=' -f 2)
		if [ "$sn2" = "101" ]; then
			iptables -t mangle -A MANAGE_WAN -i wan-$mngt_wan -j ACCEPT
			iptables -t nat -A MANAGE_WAN -p tcp -i wan-$mngt_wan --dport 22 -j REDIRECT --to-ports ${ssh_port:-22}
			iptables -t nat -A MANAGE_WAN -p tcp -i wan-$mngt_wan --dport 23 -j REDIRECT --to-ports ${telnet_port:-23}
			iptables -t nat -A MANAGE_WAN -p tcp -i wan-$mngt_wan --dport 80 -j REDIRECT --to-ports ${web_port:-80}
			iptables -t nat -A MANAGE_WAN -p tcp -i wan-$mngt_wan --dport 443 -j REDIRECT --to-ports ${https_port:-443}
			iptables -t nat -A MANAGE_WAN -i wan-$mngt_wan -j ACCEPT
			iptables -A MANAGE_WAN -i wan-$mngt_wan -j ACCEPT
		else
			uci set acc_ctrl.access_control.mngt_wan=""
		fi
	fi

}
start() {
	config_clear
	config_load acc_ctrl
	config_foreach acc_ctrl_config acc_ctrl
}

stop() {
	apply
}

apply() 
{
	iptables -F $ACC_CTRL 2>/dev/null
	iptables -F $ACC_CTRL_AL 2>/dev/null
	ip6tables -F $ACC6_CTRL 2>/dev/null
	
	########Handle ip6tables
	local web_allow
	local web_port
	local telnet_allow
	local telnet_port
	local ssh_allow
	local ssh_port
	local https_allow
	local https_port
	multiport=""
	web_allow=$(uci oget acc_ctrl.access_control.web_allow)
	web_port=$(uci oget acc_ctrl.access_control.web_port)
	telnet_allow=$(uci oget acc_ctrl.access_control.telnet_allow)
	telnet_port=$(uci oget acc_ctrl.access_control.telnet_port)
	ssh_allow=$(uci oget acc_ctrl.access_control.ssh_allow)
	ssh_port=$(uci oget acc_ctrl.access_control.ssh_port)
	https_allow=$(uci oget acc_ctrl.access_control.https_allow)
	https_port=$(uci oget acc_ctrl.access_control.https_port)
	user_define=$(uci oget acc_ctrl.access_control.user_define)
	allow_ip=$(uci oget acc_ctrl.access_control.allow_ip)
	wan_pip_only=$(uci oget acc_ctrl.access_control.wan_pip_only)

	#collect access ports
	if [ "$ssh_allow" == "enable" ] ;then
		append_multiport $ssh_port
	fi
	if [ "$web_allow" == "enable" ] ;then
		append_multiport $web_port
	fi
	if [ "$telnet_allow" == "enable" ] ;then
		append_multiport $telnet_port
	fi   
	if [ "$https_allow" == "enable" ] ;then  
		append_multiport $https_port
	fi
	
	all_wan_pip_set=""
	[ "$wan_pip_only" = "enable" ] && all_wan_pip_set="-m set --set all_wan_pip dst"
	
	if [ "$user_define" == "enable" -a -n "$multiport" ]; then
		for each_ip in $allow_ip
		do
			if [ "$each_ip" != "0.0.0.0/0" ];then
				iptables -t nat -D $PRE_NAT_FUNCS -p tcp -s $each_ip $all_wan_pip_set -m multiport --dport $multiport -j RETURN 2>/dev/null
			fi
		done
	elif [ -n "$multiport" ]; then
		iptables -t nat -D $PRE_NAT_FUNCS -p tcp $all_wan_pip_set -m multiport --dport $multiport -j RETURN 2>/dev/null
	fi
	
	#T17737: reset manage WAN rules
	iptables -t mangle -F MANAGE_WAN
	iptables -t nat -F MANAGE_WAN
	iptables -F MANAGE_WAN
	
	start
	uci commit acc_ctrl
}

boot()
{
	##version upgrade:
	#G45355:(default) make router WAN alias's priority behind NAT functions
	wan_pip_only=`uci get acc_ctrl.access_control.wan_pip_only`
	[ -z "$wan_pip_only" ] && {
		uci set acc_ctrl.access_control.wan_pip_only=enable
		uci commit acc_ctrl
	}
	
	#First policy:
	iptables -I INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
	ip6tables -I INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
	#Final policy: Accept rest packets which are coming through lan+, ppp+(remote dial-in, pppoe server), lan-to-lan vpn
	iptables -A INPUT -i wan+ -m mset ! --set exception_subnet_set src ! --set exception_subnet_gre_set src -j DROP
	ip6tables -A INPUT -i wan+ -j DROP

	if [ "$model" = "Vigor3900" -o "$model" = "Vigor2960" -o "$model" = "Vigor2960F" ]; then
		##Prevent forwarded by NAT functions
		iptables -t nat -I $PRE_NAT_FUNCS -i wan+ -p esp -j RETURN
		iptables -t nat -I $PRE_NAT_FUNCS -i wan+ -p ah -j RETURN
		iptables -t nat -I $PRE_NAT_FUNCS -i wan+ -p 47 -j RETURN
	fi
	iptables -t nat -I $PRE_NAT_FUNCS -p udp -m multiport --dport 68,546 -j RETURN
	#G36963: skip these ports from ddos check
	ddos_status=$(uci get ddos.ddos_config.status)
	if [ "$ddos_status" = "enable" ] ;then
		echo -n $web_port > /proc/sys/net/ipv4/ddos_bypass_web_port
		echo -n $telnet_port > /proc/sys/net/ipv4/ddos_bypass_telnet_port
		echo -n $ssh_port > /proc/sys/net/ipv4/ddos_bypass_ssh_port
		echo -n $https_port > /proc/sys/net/ipv4/ddos_bypass_https_port
    fi
	
	#replace old config (allow_ip1,2,3 -> list allow_ip)
	old_config=$(grep 'allow_ip[123]' /etc/persistence/config/acc_ctrl)
	if [ "$old_config" != "" ];then
		sed -i "s/option 'allow_ip1'/list 'allow_ip'/g" /etc/persistence/config/acc_ctrl
		sed -i "s/option 'allow_ip2'/list 'allow_ip'/g" /etc/persistence/config/acc_ctrl
		sed -i "s/option 'allow_ip3'/list 'allow_ip'/g" /etc/persistence/config/acc_ctrl
	fi
	start
}
