#!/bin/bash
#
# VERSION=8
# CHANGES="change back sbcfs to appfs"

export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin:
BEROCONF=/usr/fallback/beroconf

IPTABLES_LAST=/tmp/acl.iptables.last
IPTABLES_NEW=/tmp/acl.iptables.new
BERONET_IP_FILE=/tmp/acl.beronet.ip

lanmode=$(${BEROCONF} get root lan-mode)

DISABLE_UDP_CONNTRACK=1
PORTRANGE_START=50000
PORTRANGE_STOP=60000


if [[ "$lanmode" = bonding ]]; then
	ethdevice=eth0
	ethdevice_lan=$ethdevice
	alldevices="$ethdevice"
elif [[ "$lanmode" = lan-wan ]]; then
	ethdevice=`ls /proc/net/vlan|grep eth|tail -1`
	ethdevice_wan=`ls /proc/net/vlan|grep eth|tail -1`
	ethdevice_lan=`ls /proc/net/vlan|grep eth|head -1`
	alldevices="$ethdevice_lan $ethdevice_wan"
else
	ethdevice=`ls /proc/net/vlan|grep eth|head -1`
	ethdevice_lan="$ethdevice"
	alldevices="$ethdevice"
fi

[[ -z "$ethdevice" ]] && { ethdevice=eth0; ethdevice_lan=$ethdevice; alldevices="$ethdevice"; }

# calculate network address from ip and netmask
function get_network_addr () {

	ip=${1}
	nm=${2}

	# split ip into array
	i=0
	for elem in $(echo ${ip//./ }); do
		ip_arr[${i}]=${elem}
		let "i=${i}+1"
	done

	# split netmask into array
	i=0
	for elem in $(echo ${nm//./ }); do
		nm_arr[${i}]=${elem}
		let "i=${i}+1"
	done

	# build network address
	for i in {0..3}; do
		let "ip_net_arr[${i}]=${ip_arr[${i}]} & ${nm_arr[${i}]}"
	done

	echo "${ip_net_arr[0]}.${ip_net_arr[1]}.${ip_net_arr[2]}.${ip_net_arr[3]}"
}

# allow local network
function allowlan () {

	# get ip configuration of ethdevice
	eth0_cfg=$(ifconfig $ethdevice_lan)

	# get ip and netmask
	ip=$(expr match "${eth0_cfg}" ".*inet addr:\([0-9\.]*\)")
	nm=$(expr match "${eth0_cfg}" ".*Mask:\([0-9\.]*\)")

	ip_net=$(get_network_addr ${ip} ${nm})

	# allow all traffic from our network
	if [ ! -z "${ip_net}" ]; then
		echo iptables -i "$ethdevice_lan" -A INPUT -s ${ip_net}/${nm} -j ACCEPT >> $IPTABLES_NEW
	fi
}

# table;protocol;dport;saddr;jump
function dynamic_rules_read () {

	if [ ! -f "${1}" ]; then
		return
	fi
	ruletype=$2
	
	while read line; do
		
		#if ruletype DROP has been given, only rules with DROP are used, otherwise all non-DROP rules are applied
		hasdropdirective=0
		echo "$line"|grep "DROP;" &> /dev/null
		[[ $? == 0 ]] && hasdropdirective=1
		if [[  "$ruletype" == DROP && $hasdropdirective == 0 ]]; then
			continue
		elif [[  "$ruletype" != DROP && $hasdropdirective == 1 ]]; then
			continue
		fi
		
		if [ -z "${line}" ] || [ "${line:0:1}" = "#" ] ; then
			continue
		fi
		
		IFS=';' read -ra array <<< "${line}"

		if [ -z "${array[0]}" ]; then
			continue
		fi

		if [ ! -z "${array[2]}" ]; then
			if [ "${array[2]}" == all ]; then
				dst_port=""
				val_dst_port=""
			else
				dst_port="--dport ${array[2]}"
				val_dst_port=${array[2]}
			fi
		fi

		if [ ! -z "${array[3]}" ]; then
			src_addr="${array[3]}"
		fi

		if [ -z "${array[5]}" ]; then
			stack="ANY"
		else
			stack="${array[5]}"
		fi
		
# 		echo "##############" NOW $line
# 		echo STACK $stack
		if [[ "$stack" == WAN ]]; then
			[[ "$lanmode" != "lan-wan" ]] && continue
			using_devices=$ethdevice_wan
		elif [[ "$stack" == LAN ]]; then
			using_devices=$ethdevice_lan
		else
			using_devices=$alldevices
		fi
		
		
		#check if we have a netmask
		expr "$src_addr" : '.*/.*' &> /dev/null
		
		if [ $? = 0 ]; then
			addrnet=`echo $src_addr|sed 's/\/.*//'`
			addrmask=/`echo $src_addr|sed 's/.*\///'`
		else
			addrnet=$src_addr
			addrmask=""
		fi

		IFSBUF=$IFS
		IFS='|'

		#check if it is a normal ip
		expr ${addrnet} : '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}$' &> /dev/null

		if [ $? = 0 ]; then
			addresses=${addrnet}
		else
			addresses=`/usr/local/bin/isgw -R ${addrnet}`
		fi

		unset IFS
		
		if [ ! -z "${array[1]}" ]; then
			uprotocols="${array[1]}"
		else
			uprotocols="tcp udp"
		fi
		
		for address in `echo $addresses|sed 's/|/\n/g'|sort -n`; do
			#port -1 defines the sip port, port -2 the ssl port of the associated stack
			if [[ "$val_dst_port" == -1 || "$val_dst_port" == -2 ]]; then
				let ssl_offset=("$val_dst_port"+1)*-1
				
				#loop over all configured stacks and add the corresponding sip port rule
				for stackactive in "stack_active_lan" "stack_active_wan"; do
					
					active=`${BEROCONF} get ari:key_val $stackactive|grep -v get|grep '^[0-9]'|head -1`
					if [ "$active" != 1 ]; then
						continue;
					fi
					
					if [ $stackactive = "stack_active_lan" ]; then
						ndst_port=`${BEROCONF} get ari:key_val "sip_bindport_lan"|grep -v get|grep '^[0-9]'|head -1`
						device=$ethdevice_lan
					elif [ $stackactive = "stack_active_wan" ]; then
						#check if we are in lan wan mode, this value can be still set to 1, if in single mode
						[[ "$lanmode" != "lan-wan" ]] && continue
						ndst_port=`${BEROCONF} get ari:key_val "sip_bindport_wan"|grep -v get|grep '^[0-9]'|head -1`
						device=$ethdevice_wan
					fi
					
					[[ -z $ndst_port ]] && continue
					let ndst_port+=$ssl_offset

					for proto in $uprotocols; do
# 						echo INFO1: iptables -i "$device" -A ${array[0]} -p ${proto} --dport ${ndst_port} -s ${address}${addrmask} -j ${array[4]}
						echo iptables -i "$device" -A ${array[0]} -p ${proto} --dport ${ndst_port} -s ${address}${addrmask} -j ${array[4]} >> $IPTABLES_NEW
					done

				done
			else
				for device in $using_devices; do

					
					for proto in $uprotocols; do
# 						echo INFO2: iptables -i "$device" -A ${array[0]} -p ${proto} ${dst_port} -s ${address}${addrmask} -j ${array[4]}
						echo iptables -i "$device" -A ${array[0]} -p ${proto} ${dst_port} -s ${address}${addrmask} -j ${array[4]} >> $IPTABLES_NEW
					done

				done
			fi
		done
		IFS=$IFSBUF

		unset src_addr
		unset dst_port
		unset protocol
	done < ${1}

}

# applies rules for appfs and userappfs
function dynamic_rules_apply () {

	#read first the DROP rules, then the other ones
	dynamic_rules_read /usr/conf/network.acl DROP
	dynamic_rules_read /usr/conf/network.acl

	for f in /apps/??*/etc/network.acl; do
		dynamic_rules_read ${f} DROP
		dynamic_rules_read ${f}
	done

}

# rules for berofix Configuration Access
function berofix_config_access_rules () {

	#try to resolve the address, if a nameserver is configured, otherwise use the ip
	office_beronet_com_dns=office.beronet.com
	office_beronet_com_ip=85.183.36.56

	#if we resolved it once, reuse the value from last time
	if [ -f $BERONET_IP_FILE ]; then
		office_beronet_com_ip=`head -1 $BERONET_IP_FILE`
	else
		cat /etc/resolv.conf | grep ^nameserver &> /dev/null
		if [ $? = 0 ]; then
			xip=`isgw -R "$office_beronet_com_dns"|head -1`
			if [ -n "$xip" ]; then
				office_beronet_com_ip="$xip"
				echo $office_beronet_com_ip > $BERONET_IP_FILE
			fi
		fi
	fi

	for dest_port in 22 80 443; do
		for device in $alldevices; do
			echo iptables -i "$device" -A INPUT -p tcp --dport ${dest_port} -s ${office_beronet_com_ip} -j ACCEPT >> $IPTABLES_NEW
			echo iptables -i "$device" -A INPUT -p tcp --dport ${dest_port} -s 80.244.243.31/28 -j ACCEPT >> $IPTABLES_NEW
			echo iptables -i "$device" -A INPUT -p tcp --dport ${dest_port} -s 80.244.247.204/30 -j ACCEPT >> $IPTABLES_NEW
		done
	done
	cloudAddress=berocloud.beronet.com
	cloud=`cat /usr/conf/misc.conf |grep ^cloudAddress|head -1|sed 's/.*=//'`
# 	cloud=$(${BEROCONF} get ari:key_val cloudAddress|grep -v failed)


	if [ -n "$cloud" ]; then
		cloudip=`isgw -R "$cloud"|head -1`
		if [ -n "$cloudip" ]; then
			for device in $alldevices; do
				echo iptables -i "$device" -A INPUT -p tcp -s "$cloudip" -j ACCEPT >> $IPTABLES_NEW
			done
		fi
	fi
}



sip_port=$(echo $sip_address | cut -d ":" -f 3)
ip=$(ifconfig $ethdevice  | grep inet | cut -d ":" -f 2 | cut -d " " -f 1)

routing=$(route -n | grep $ethdevice  | grep -v "UG")
network=$(echo $routing | cut -d " " -f 1)
netmask=$(echo $routing | cut -d " " -f 3)

function allow_configured_sip_peers {
	#isgw now adds the rules as needed
	echo iptables -N configured_sip_peers >> $IPTABLES_NEW
	echo iptables -A INPUT -j configured_sip_peers >> $IPTABLES_NEW
}


function allow_mindspeed_rtp {
	#get address of remote eth1 mindspeed
	mspip=`ifconfig | grep -A 2 ^eth1 | grep addr: | sed 's/.*addr://;s/ .*//;s/1$/2/'`
	echo iptables -A INPUT -p udp -s $mspip -j ACCEPT >> $IPTABLES_NEW
}


function create_all_rules_file {

	\rm -f $IPTABLES_NEW
	echo iptables -F >> $IPTABLES_NEW
	echo iptables -X >> $IPTABLES_NEW

	#conntrack
	if [ "$DISABLE_UDP_CONNTRACK" = 1 ]; then
		echo iptables -A INPUT -p tcp --dport $PORTRANGE_START:$PORTRANGE_STOP -j ACCEPT >> $IPTABLES_NEW
		echo iptables -A INPUT -p udp --dport $PORTRANGE_START:$PORTRANGE_STOP -j ACCEPT >> $IPTABLES_NEW
		:
	else
		echo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT >> $IPTABLES_NEW
	fi

	#allow all from localhost
	echo iptables -A INPUT -p tcp -s 127.0.0.1/16 -j ACCEPT >> $IPTABLES_NEW
	echo iptables -A INPUT -p udp -s 127.0.0.1/16 -j ACCEPT >> $IPTABLES_NEW


	# standard rules for beronet configuration access (SSH, HTTP & HTTPS)
	
	
	# allow access from beronet IPs
	excludeberonet_enabled=$(${BEROCONF} get root excludeberonet | grep -v failed)
	if [ "${excludeberonet_enabled}" = "yes" ] || [ "${excludeberonet_enabled}" = "1" ]; then
		#blocking beronet
		:
	else
		berofix_config_access_rules
	fi
	

	# check if excludelan is not disabled
	excludelan_enabled=$(${BEROCONF} get root excludelan | grep -v failed)
	# allow full access from local LAN
	if [ "${excludelan_enabled}" = "yes" ] || [ "${excludelan_enabled}" = "1" ]; then
		#blocking lan
		:
	else
		allowlan
	fi

	# new dynamic rules
	dynamic_rules_apply

	allow_configured_sip_peers
	allow_mindspeed_rtp


	#drop everything not explicitly allowed
	#but this doesn't work without conntrack
	for device in $alldevices; do
		echo iptables -i "$device" -A INPUT -p tcp -j DROP >> $IPTABLES_NEW
		echo iptables -i "$device" -A INPUT -p udp -j DROP >> $IPTABLES_NEW
	done
	
}

function ipt_modules_loaded {

	if [ -z "$(lsmod | grep ipt_state)" ]; then
		echo 0
		return
	fi

	if [ -z "$(lsmod | grep ipt_conntrack)" ]; then
		echo 0
		return
	fi

	echo 1
}

function ipt_modules_load {
	/sbin/insmod /usr/local/modules/ipt_state.ko
	/sbin/insmod /usr/local/modules/ipt_conntrack.ko
}

# iptables -F

case "${1}" in
	start)
		if [ "$DISABLE_UDP_CONNTRACK" = 1 ]; then
			echo 0 > /proc/sys/net/ipv4/netfilter/ip_conntrack_udp_timeout
			echo 0 > /proc/sys/net/ipv4/netfilter/ip_conntrack_udp_timeout_stream
			echo $PORTRANGE_START $PORTRANGE_STOP > /proc/sys/net/ipv4/ip_local_port_range
		fi
		
		iptables -F
		iptables -X
		if [ $(ipt_modules_loaded) -eq 0 ]; then
			ipt_modules_load
		fi
		create_all_rules_file
		source $IPTABLES_NEW
		\cp -f $IPTABLES_NEW $IPTABLES_LAST
		echo "rsi;q;"|nc -q 1 localhost 54322
		;;

	stop)
		iptables -F
		iptables -X
		;;

	restart)
		if [ $(ipt_modules_loaded) -eq 0 ]; then
			ipt_modules_load
		fi

		if [[ ! -f "$IPTABLES_LAST" ]]; then
			$0 start
			exit 0
		fi
		create_all_rules_file
		
		val1=`md5sum $IPTABLES_NEW|awk '{print $1}'`
		val2=`md5sum $IPTABLES_LAST|awk '{print $1}'`

		if [[ $val1 != $val2 ]]; then
			iptables -F
			iptables -X
			source $IPTABLES_NEW
			\cp -f $IPTABLES_NEW $IPTABLES_LAST
		else
			echo no change, keeping old rules
		fi
		#tell isgw to reload the rules
		echo "rsi;q;"|nc -q 1 localhost 54322
		;;

	*)
		echo "Usage: $0 {start|stop}" >&2
		exit 1
		;;
esac
