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

START=90
EXTRA_COMMANDS='l2_isolation_rule_mac'
config_load wireless

load_ebtable() {
	local kernel_version=$(ls /lib/modules/) 
	[ -d /sys/module/ebtables ] || insmod /lib/modules/${kernel_version}/ebtables.ko
	[ -d /sys/module/ebtable_broute ] || insmod /lib/modules/${kernel_version}/ebtable_broute.ko
	[ -d /sys/module/ebtable_filter ] || insmod /lib/modules/${kernel_version}/ebtable_filter.ko
	[ -d /sys/module/ebtable_nat ] || insmod /lib/modules/${kernel_version}/ebtable_nat.ko
	[ -d /sys/module/ebt_ip ] || insmod /lib/modules/${kernel_version}/ebt_ip.ko
	[ -d /sys/module/ebt_snat ] || insmod /lib/modules/${kernel_version}/ebt_snat.ko

}

unload_ebtable() {
	[ -d /sys/module/ebt_snat ] && rmmod ebt_snat
	[ -d /sys/module/ebt_ip ] && rmmod ebt_ip
	[ -d /sys/module/ebtable_nat ] && rmmod ebtable_nat
	[ -d /sys/module/ebtable_filter ] && rmmod ebtable_filter
	[ -d /sys/module/ebtable_broute ] && rmmod ebtable_broute
	[ -d /sys/module/ebtables ] && rmmod ebtables
}

disable_isolation_ebtable()
{
    local ifname="$1"

    ebtables -D FORWARD -i $ifname -j isolation_${ifname}_chain 2>/dev/null
    ebtables -F isolation_${ifname}_chain 2>/dev/null
    ebtables -X isolation_${ifname}_chain 2>/dev/null

    ebtables -D isolation_${ifname}_chain -i $ifname -j isolation_mac_${ifname}_chain 2>/dev/null
    ebtables -F isolation_mac_${ifname}_chain 2>/dev/null
    ebtables -X isolation_mac_${ifname}_chain 2>/dev/null
}

enable_isolation_ebtable()
{
    local vif="$1"
    local ifname="$2"
    local network="$3"
    local only="$4"

    ebtables -I FORWARD -j DROP
    ebtables -N isolation_${ifname}_chain 2>/dev/null
    ebtables -F isolation_${ifname}_chain 2>/dev/null
    if [ "$only" == "0" ]
    then
        l2_isolation_rule_white_list "$vif" "$ifname"
        ## for client isolation in same athx ##
        ## iwpriv athx ap_bridge 0 (0:enable, 1:disable) ##
        ## force enable client isolation when l2 isolation enabled.
        iwpriv "$ifname" ap_bridge 0
        l2_isolation_rule_ip "$ifname" "$network"
        # coova-chilli NAT mode need use this rule to block different radio
        config_foreach ssid_client_isolation wifi-device "$vif" "$ifname"
    else
        config_foreach ssid_client_isolation wifi-device "$vif" "$ifname"
    fi
    ebtables -D FORWARD -i $ifname -j isolation_${ifname}_chain 2>/dev/null
    ebtables -A FORWARD -i $ifname -j isolation_${ifname}_chain
    ebtables -D FORWARD -j DROP
}

clean_rule()
{
    local vifs=$(eval "/usr/sbin/foreach wireless wifi-iface device $1")
    for vif in $vifs; do
	    config_get ifname "$vif" ifname
	    disable_isolation_ebtable "$ifname"
    done
}

## In ssid based environment 2.4GHz, 5GHz, 5G-2Hz are in same ssid configuration,
## client isolation = cients in same ssid can't send packet to each other.
## use SUPPORT_COMBINED_SSID_SETTING define to check, if any better define in the future, this define can be substitued.
func_ssid_client_isolation=$(uci get functionlist.functionlist.SUPPORT_COMBINED_SSID_SETTING)
ssid_client_isolation() {
    local radio_name="$1"
    local section_name="$2"
    local in_ifname="$3"
    local out_ifname
    #find athx in the same ssid group
    #sed -e /wifi0_ssid_1/wifi1/ -> wifi1_ssid_1
    section_name=$(echo $section_name | sed -e "s/wifi[0-9]/$radio_name/")
    config_get out_ifname "$section_name" ifname "not_exist"
    if [ "$out_ifname" != "not_exist" -a "$out_ifname" != "$in_ifname" ]; then
        ssid_client_isolation_rule $in_ifname $out_ifname
    fi
}

ssid_client_isolation_rule() {
    local in_ifname="$1"
    local out_ifname="$2"
    ebtables -A isolation_${ifname}_chain -i $in_ifname -o $out_ifname -j DROP
}



## 1. send dhcp query and get offer ip, and then we can get gateway ip ##
## 2. get the gateway mac and then only allow packtes which are dhcp or dns query or to gateway pass##
l2_isolation_rule_mac() {
#    echo "====[$1]===[$2]===[$3]==" > /dev/console
    local ifname="$1"
    local new_dhcp_gateway_mac="$2"
    local old_dhcp_gateway_mac="$3"

    if [ "$new_dhcp_gateway_mac" == "$old_dhcp_gateway_mac" ]; then
        return
    fi

    ebtables -I isolation_${ifname}_chain -j DROP
    ebtables -N isolation_mac_${ifname}_chain 2>/dev/null
    ebtables -F isolation_mac_${ifname}_chain 2>/dev/null

    ## only allow packet to gateway##
    if [ "$new_dhcp_gateway_mac" != "" ]; then
        ebtables -A isolation_mac_${ifname}_chain -p ipv4 -i "$ifname"  -d ! ${new_dhcp_gateway_mac} -j DROP
    fi
    ebtables -D isolation_${ifname}_chain -i $ifname -j isolation_mac_${ifname}_chain 2>/dev/null
    ebtables -A isolation_${ifname}_chain -i $ifname -j isolation_mac_${ifname}_chain
    ebtables -D isolation_${ifname}_chain -j DROP
}

l2_isolation_rule_white_list() {
   local section_name=$1
   local ifname=$2
    white_list_section_name=$(echo $section_name | sed -e "s/wifi[0-9]/wifi/")
    white_list_mac_list=$(uci get l2_isolation.${white_list_section_name}.mac_list)
    for white_list_mac in $white_list_mac_list; do
        ebtables -A isolation_${ifname}_chain -p ipv4 -i "$ifname" -d ${white_list_mac} -j ACCEPT
    done
}

l2_isolation_rule_ip() {
    local ifname="$1"
    local network="$2"
    ## for DNS query packet ##
    ebtables -A isolation_${ifname}_chain -p ipv4 -i "$ifname" --ip-proto udp --ip-destination-port 53 -j ACCEPT
    ebtables -A isolation_${ifname}_chain -p ipv4 -i "$ifname" --ip-proto udp --ip-source-port 53 -j ACCEPT
    ebtables -A isolation_${ifname}_chain -p ipv4 -i "$ifname" --ip-proto tcp --ip-destination-port 53 -j ACCEPT
    ebtables -A isolation_${ifname}_chain -p ipv4 -i "$ifname" --ip-proto tcp --ip-source-port 53 -j ACCEPT
    ## for dhcp packtet ##
    ebtables -A isolation_${ifname}_chain -p ipv4 -i "$ifname" --ip-proto udp --ip-destination-port 67 -j ACCEPT
    ebtables -A isolation_${ifname}_chain -p ipv4 -i "$ifname" --ip-proto udp --ip-source-port 67 -j ACCEPT
    ebtables -A isolation_${ifname}_chain -p ipv4 -i "$ifname" --ip-proto udp --ip-destination-port 68 -j ACCEPT
    ebtables -A isolation_${ifname}_chain -p ipv4 -i "$ifname" --ip-proto udp --ip-source-port 68 -j ACCEPT
    ########## Set up ebtables rules when L2 isolation is enabled on static ip
    if [ "$brlanIP" != "0.0.0.0" ]; then
        if [ "$network" = "lan" ]; then
            ### drop packet from br-lan subnet for br-lan bridge ###
            ebtables -A isolation_${ifname}_chain -p ipv4 -i "$ifname" --ip-dst "$brlanIP"/"$brlanPrefix" -j DROP
        else
            ### drop all packet for vlan bridge in no dhcp server environment ###
            ebtables -A isolation_${ifname}_chain -p ipv4 -i "$ifname" -j DROP
        fi
    fi
}

l2_isolation_flag=0
ssid_client_isolation_flag=0
brlanIP=$(ifconfig br-lan | grep Bcast | awk -F " " '{print $2}' | awk -F ":" '{print $2}')
brlanMask=$(ifconfig br-lan | grep Bcast | awk -F " " '{print $4}' | awk -F ":" '{print $2}')
brlanPrefix=$(ipcalc.sh $brlanIP $brlanMask | grep PREFIX | awk -F "PREFIX=" '{print $2}' | awk -F " " '{print $1}')

initial() {
	local device="$1"
	#config_get vifs "$device" vifs	

	config_get opmode "$device" opmode
	case "$opmode" in
	ap|wds_ap)

		local vifs=$(eval "/usr/sbin/foreach wireless wifi-iface device $1")	
		for vif in $vifs; do
			config_get ifname "$vif" ifname
			config_get l2_isolatior "$vif" l2_isolatior
			config_get isolate "$vif" isolate #client isolation
			config_get disabled "$vif" disabled
			config_get mode_display "$vif" mode_display
			config_get mode_display_2 "$vif" mode_display_2
			config_get network "$vif" network
			[ "$opmode" != "$mode_display" ] && [ "$opmode" != "$mode_display_2" ] &&  continue

			case "$func_ssid_client_isolation:$isolate:$l2_isolatior:$disabled" in
			*:1:0)#enable l2 isolation
			l2_isolation_flag="1"
			load_ebtable
			if [ -e /etc/init.d/syskey ]; then
				/etc/init.d/syskey setValue "ebtables" "l2_isolation" "1"
			fi
			enable_isolation_ebtable "$vif" "$ifname" "$network" "0"

			## for clock send period dhcp discovery ##
			echo  "br-$network" "$ifname" >> /tmp/l2_isolation.conf
			;;
			1:1:0:0)#enable client isolation only
			ssid_client_isolation_flag="1"
			load_ebtable
			if [ -e /etc/init.d/syskey ]; then
				/etc/init.d/syskey setValue "ebtables" "l2_isolation" "1"
			fi
			enable_isolation_ebtable "$vif" "$ifname" "$network" "1"
			;;
			esac
		done
		;;
	*)
	;;
	esac
}

start (){
    config_foreach initial wifi-device

    dhtest_timer &

    if [ "$l2_isolation_flag" = "0" -a "$ssid_client_isolation_flag" = "0" ];then
        if [ -e /etc/init.d/syskey ]; then
            /etc/init.d/syskey setValue "ebtables" "l2_isolation" "0"
        else
            unload_ebtable
        fi
    fi
}

stop() {
    config_foreach clean_rule wifi-device

    if [ -e /etc/init.d/syskey ]; then
        /etc/init.d/syskey setValue "ebtables" "l2_isolation" "0"
    else
        unload_ebtable
    fi

    if [ -e /usr/sbin/dhcrelay_l2 ]; then
        killall -9 dhcrelay_l2
    else
        ln -sf /usr/sbin/dhcrelay /usr/sbin/dhcrelay_l2
    fi

    kill -SIGTERM `pidof dhtest_timer`

    rm /tmp/l2_isolation.conf
}


