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

START=20
STOP=90

USE_PROCD=1

. /lib/functions.sh
. /usr/share/libubox/jshn.sh

SSDK_SH=/usr/sbin/ssdk_sh
ETHINFO_FILE="/tmp/cache/systeminfo/ethinfo.info"
DISFDB_BYVLAN="/tmp/cache/switch/disable_fdb_by_vlan"
DISFDB_BYVLAN_OLD="/tmp/cache/switch/disable_fdb_by_vlan_old"

json_load "$(cat $ETHINFO_FILE)"
json_get_var eth_ifs_maps "eth_ifs_maps"

init_switch() {
	setup_switch() { return 0; }

	include /lib/network
	setup_switch
}

calculate_eth_mac() {
	source_mac=$1
	offset=$2

	if [ "$offset" = "0" ]; then
		ifmac=$source_mac
	else
		oct0=$(echo ${source_mac} | tr ":" " " | awk '{ print $1 }')
		oct1=$(echo ${source_mac} | tr ":" " " | awk '{ print $2 }')
		oct2=$(echo ${source_mac} | tr ":" " " | awk '{ print $3 }')
		oct3=$(echo ${source_mac} | tr ":" " " | awk '{ print $4 }')
		oct4=$(echo ${source_mac} | tr ":" " " | awk '{ print $5 }')
		oct5=$(echo ${source_mac} | tr ":" " " | awk '{ print $6 }')

		oct5=$((0x$oct5+$offset))
		oct5=$(($oct5&0xff))
		oct0=$((0x$oct0+2))
		oct0=$(printf "%.2x" $oct0)
		oct5=$(printf "%.2x" $oct5)
		ifmac=$(echo "$oct0:$oct1:$oct2:$oct3:$oct4:$oct5")
	fi
	echo $ifmac
}

assign_lan_macs() {
	base_mac=$(uci get systeminfo.boarddata.lan_mac)
	uci set network.eth2=device
	uci set network.eth2.name="eth2"
	uci set network.eth2.macaddr="$base_mac"
	i=1
	for lan_if in eth3 eth4; do
		uci set network.$lan_if=device
		uci set network.$lan_if.name="$lan_if"
		uci set network.$lan_if.macaddr=$(calculate_eth_mac $base_mac $i)
		i=$(($i+1))
	done
}
opmode_assign_wan_macs() {
       local opmode=$(uci get opmode.operate.mode)
       [ "$opmode" != "apmode" ] && return 

       local base_mac=$(uci get systeminfo.boarddata.lan_mac)
       uci set network.eth1=device
       uci set network.eth1.name="eth1"
       uci set network.eth1.macaddr=$(calculate_eth_mac $base_mac 3) #the eth2 eth3 eth4 ,then assign the mac to eth1 in AP mode
}


chk_disable_fdb_by_vlan() {
	local eth_map net_name net_type
	local eth_i cur_net

	[ -f $DISFDB_BYVLAN ] && mv $DISFDB_BYVLAN $DISFDB_BYVLAN_OLD
	echo '0' > $DISFDB_BYVLAN

	[ "$(uci get network.vlan_lan)" != "" ] && return #DT serial type no need, only use for AX serial type

	[ "$(uci show network | grep network.v)" = "" ] && return

	for eth_map in $eth_ifs_maps; do
		net_name="$(echo $eth_map | awk -F: '{print $1}')"
		net_type="${net_name:0:3}"

		if [ "$net_type" == "LAN" ]; then
			eth_i="$(echo $eth_map | awk -F: '{print $2}')"
			cur_net="$(uci show network | grep $eth_i | grep ifname | awk -F= '{print $1}')"

			#LAN port belone to a VLAN in vlan-mode
			if [ "$(echo $cur_net | grep 'v')" != "" ]; then
				echo '1' > $DISFDB_BYVLAN #should disable lan/wan port fdb learning to make priority work normal
				return
			fi
		fi
	done
}

switch_port_fdblearing() {
	local change=0
	local vchange=0
	local opmode=$(uci get opmode.operate.mode)
	local opmode_old
	local eth_map portid net_name net_type
	local disfdb_byvlan disfdb_byvlan_old
	local nosupport_flush=0

	# remove disable fdb learning in router mode for vlan bridge(to implement VLAN priority func) temply.
	# as it will cause LAN->WAN communicate error if packets enter NSS acceleration. (QCA aslo have this problem, need check with qca) 
	#chk_disable_fdb_by_vlan
	[ -f $DISFDB_BYVLAN ] && disfdb_byvlan=$(cat $DISFDB_BYVLAN)

	if [ "$1" != "start" ]; then
		[ -f $DISFDB_BYVLAN_OLD ] && disfdb_byvlan_old=$(cat $DISFDB_BYVLAN_OLD)

		if [ "$disfdb_byvlan_old" != "$disfdb_byvlan" ]; then
			change=1
			vchange=1
		else
			opmode_old=$(uci get /etc/config/opmode.operate.mode)

			[ "$opmode" != "$opmode_old" ] && change=1
		fi

		[ $change -eq 0 ] && return
	fi

	#if ssdk not support flush command, use shutdown port instead
	if [ $vchange -eq 1 ]; then
		[ "$($SSDK_SH fdb entry flush 0 | grep 'not support')" != "" ] && nosupport_flush=1
	fi

	# Refer with RAX120 Solution to disable WAN port MAC-Learn to support DNS hijack in AP mode.
        # to make NSS in bridge work, modify to disable both WAN and LAN ports fdb learning in AP mode.
        for eth_map in $eth_ifs_maps; do
                net_name="$(echo $eth_map | awk -F: '{print $1}')"
                net_type="${net_name:0:3}"
                portid="$(echo $eth_map | awk -F: '{print $3}')"

                if [ "$opmode" = "apmode" ]; then
                        $SSDK_SH fdb portLearn set $portid disable
                else
                        if [ "$disfdb_byvlan" = "1" ]; then
                                $SSDK_SH fdb portLearn set $portid disable
                        else
				$SSDK_SH fdb portLearn set $portid enable
                        fi

			[ $nosupport_flush -eq 1 ] && {
				$SSDK_SH port poweroff set $portid
				$SSDK_SH port poweron set $portid
			}
                fi
        done

        [ $nosupport_flush -eq 0 ] && $SSDK_SH fdb entry flush 0
}

network_cleanup() {
	killall netifd 2&> /dev/null
}

start_service() {
	assign_lan_macs
	init_switch
	switch_port_fdblearing "start"

	[ -e "usr/sbin/fst.sh" ] && /usr/sbin/fst.sh configure

	procd_open_instance
	procd_set_param command /sbin/netifd
	#procd_set_param limits core="unlimited"
	procd_set_param respawn
	procd_set_param watch network.interface
	[ -e /proc/sys/kernel/core_pattern ] && {
		procd_set_param limits core="unlimited"
		# CONFIG_NETGEAR_ORBI change core_pattern only if it is default
		# NETGEAR uses different core_pattern
		if [ "$(cat /proc/sys/kernel/core_pattern)" = "core" ]; then
			echo '/tmp/%e.%p.%s.%t.core' > /proc/sys/kernel/core_pattern
		fi
	}
	procd_close_instance
	[ -f /usr/bin/lte-cm ] && {
		/usr/bin/lte-cm start
	}
}

reload_service() {
	init_switch
	switch_port_fdblearing
    opmode_assign_wan_macs
	ubus call network reload
	if [ "$1" != "wan" ]; then
		echo network reload lan-wan wifi >/dev/console
		/sbin/wifi reload_legacy
	else
		dbtool -m wanTestStatus -w done
		echo network reload wan only >/dev/console
	fi
}

stop() {
	/sbin/wifi down
	[ -f /usr/bin/lte-cm ] && {
		/usr/bin/lte-cm stop
	}
	procd_kill network ''
	network_cleanup
}

service_running() {
	ubus -t 30 wait_for network.interface
	if [ "x$(/sbin/uci get opmode.factory.mode)" != "xenable" ]; then	
		[ ! -f "/tmp/boot_status" ] && /sbin/wifi reload_legacy
	fi
}

validate_atm_bridge_section()
{
	uci_validate_section network "atm-bridge" "${1}" \
		'unit:uinteger:0' \
		'vci:range(32, 65535):35' \
		'vpi:range(0, 255):8' \
		'atmdev:uinteger:0' \
		'encaps:or("llc", "vc"):llc' \
		'payload:or("bridged", "routed"):bridged'
}

validate_route_section()
{
	uci_validate_section network route "${1}" \
		'interface:string' \
		'target:cidr4' \
		'netmask:netmask4' \
		'gateway:ip4addr' \
		'metric:uinteger' \
		'mtu:uinteger' \
		'table:or(range(0,65535),string)'
}

validate_route6_section()
{
	uci_validate_section network route6 "${1}" \
		'interface:string' \
		'target:cidr6' \
		'gateway:ip6addr' \
		'metric:uinteger' \
		'mtu:uinteger' \
		'table:or(range(0,65535),string)'
}

validate_rule_section()
{
	uci_validate_section network rule "${1}" \
		'in:string' \
		'out:string' \
		'src:cidr4' \
		'dest:cidr4' \
		'tos:range(0,31)' \
		'mark:string' \
		'invert:bool' \
		'lookup:or(range(0,65535),string)' \
		'goto:range(0,65535)' \
		'action:or("prohibit", "unreachable", "blackhole", "throw")'
}

validate_rule6_section()
{
	uci_validate_section network rule6 "${1}" \
		'in:string' \
		'out:string' \
		'src:cidr6' \
		'dest:cidr6' \
		'tos:range(0,31)' \
		'mark:string' \
		'invert:bool' \
		'lookup:or(range(0,65535),string)' \
		'goto:range(0,65535)' \
		'action:or("prohibit", "unreachable", "blackhole", "throw")'
}

validate_switch_section()
{
	uci_validate_section network switch "${1}" \
		'name:string' \
		'enable:bool' \
		'enable_vlan:bool' \
		'reset:bool'
}

validate_switch_vlan()
{
	uci_validate_section network switch_vlan "${1}" \
		'device:string' \
		'vlan:uinteger' \
		'ports:list(ports)'
}

service_triggers()
{
	procd_add_reload_trigger network wireless

	procd_open_validate
	validate_atm_bridge_section
	validate_route_section
	validate_route6_section
	validate_rule_section
	validate_rule6_section
	validate_switch_section
	validate_switch_vlan
	procd_close_validate
}

restart() {
	ifdown -a
	sleep 1
	trap '' TERM
	stop "$@"
	network_cleanup
	start "$@"
}

shutdown() {
	ifdown -a
	sleep 1
	stop
	network_cleanup
}
