#!/bin/sh
#
# Usage: createchains.sh
#
# script to enforce iptable rules
# follow these steps. 
# 1. First get the switch instance, Active, Standby CP status
# 2. also find if CP is Active/Standby.
# 	for Active CP IP address block all access
# 	for standby CP should create union of rules for switches

# 3. on Ulysses, for switch in secure mode access is restricted,
# 	if other switch is in non-secure mode, access is open
# 	active CP access is blocked in this case and standby is open.

echo_debug()
{
	if [ "$debug_on" = "1" ]; then
    	echo $* 1>&2
	fi
}

iptab_create_default_rules()
{
	# ordering of rules is important, do not change arbitrarily

	## TCP common rules
	echo "$IPTABLES -A $R_TCPCOMMON -p $proto_tcp -m $proto_tcp --dport 897 -j REJECT" >> $rule_file
	echo "$IPTABLES -A $R_TCPCOMMON -p $proto_tcp -m $proto_tcp --dport 898 -j REJECT" >> $rule_file
	echo "$IPTABLES -A $R_TCPCOMMON -p $proto_tcp -m $proto_tcp --dport 600: -j ACCEPT" >> $rule_file

	# UDP common rules
	echo "$IPTABLES -A $R_UDPCOMMON -p $proto_udp -m $proto_udp --dport 8000 -j REJECT" >> $rule_file
	echo "$IPTABLES -A $R_UDPCOMMON -p $proto_udp -m $proto_udp --dport 9090 -j REJECT" >> $rule_file
	echo "$IPTABLES -A $R_UDPCOMMON -p $proto_udp -m $proto_udp --dport 123 -j ACCEPT" >> $rule_file
	echo "$IPTABLES -A $R_UDPCOMMON -p $proto_udp -m $proto_udp --dport 600: -j ACCEPT" >> $rule_file

}

iptab_create_generic_rules()
{
	if [ "$1" = "" ]; then
		echo "ERROR: IP address is NULL"
		exit 1
	fi

	IP_DDR=$1 #this will be CP ip in case of CP

	# Create tcp/udp rules for ports other than specified in policies
	echo "$IPTABLES -A $R_TCP -p tcp -j $R_TCPCOMMON" >> $rule_file
	echo "$IPTABLES -A $R_UDP -p udp -j $R_UDPCOMMON" >> $rule_file

	echo "$IPTABLES -A INPUT -p $proto_tcp -i $INET_IFACE -d $IP_DDR -j $R_TCP" >> $rule_file
	echo "$IPTABLES -A INPUT -p $proto_udp -i $INET_IFACE -d $IP_DDR -j $R_UDP" >> $rule_file

	if [ "$CPSTATE" = "Active" ]; then

#		detaching FC IP address FC interface.
#		defect # 25011 - because of ARP cache corruption, this association can not be
# 		enforced,. ARP cache is corrupted because of Linux behaviour where it does
#		not support two physical interfaces on same subnet.

#		echo "$IPTABLES -A INPUT -p $proto_tcp -i $FC_IFACE -d $FC_IP -j $R_TCP" >> $rule_file
#		echo "$IPTABLES -A INPUT -p $proto_udp -i $FC_IFACE -d $FC_IP -j $R_UDP" >> $rule_file
		echo "$IPTABLES -A INPUT -p $proto_tcp -d $FC_IP -j $R_TCP" >> $rule_file
		echo "$IPTABLES -A INPUT -p $proto_udp -d $FC_IP -j $R_UDP" >> $rule_file
	fi
}

iptab_create_stdby_ruleset()
{
	# for one switch, no merge is needed
	if [ $num_switches = 1 ]; then
		cat $1 > $3
		return;
	fi

    # sw0 policy file policy.0.txt does not exist, exit script
	if [ ! -f $1 ]; then
	    echo "INFO: sw0: security policy file not initialized."
	    exit 1
	fi

    # sw1 policy file policy.1.txt does not exist, exit script
	if [ ! -f $2 ]; then
	    echo "INFO: sw1: security policy file not initialized."
	    exit 1
	fi

	TEMPCMD=/tmp/__sec.cmd
	tmp_data_file=/tmp/__sec.data
	IFS='
'

	for line in `cat $1`
	do
		# get protocol
		PROTO=${line%% *}
		rem_line=${line#${PROTO} }

		# get port number
		PORT=${rem_line%% *}

		rem_line=${rem_line#${PORT} }

		echo_debug "debug: rem_line=$rem_line"

    	echo "grep \"$PROTO* $PORT\" $2 | sed -e 's/$PROTO* $PORT //g'" > $TEMPCMD

    	chmod 755 $TEMPCMD

    	iplist=`$TEMPCMD`

		if [ "$rem_line" = "ACCEPT" -o "$iplist" = "ACCEPT" ]; then
			echo "$PROTO $PORT ACCEPT" >> $3
		elif [ "$rem_line" = "REJECT" -a "$iplist" = "REJECT" ]; then
			echo "$PROTO $PORT REJECT" >> $3
		else
# this command does not work for some reason, so using above workaround
# writing to a file and then executing it
#		iplist=`grep "$PROTO *$PORT" $2 | sed -e 's/$PROTO *$PORT //g'`

			IFS=' '
			for field in $rem_line
			do
				echo $field >> $tmp_data_file
			done
			for field in $iplist
			do
				echo $field >> $tmp_data_file
			done

			IFS='
'
			iplist=""
			for field in `sort $tmp_data_file | uniq`
			do
				iplist="${iplist} ${field}"
			done
    		echo $PROTO $PORT $iplist >> $3
		fi
	done

	rm -f $TEMPCMD
	rm -f $tmp_data_file
}

iptab_create_policy_rules()
{
	if [ "$2" = "" ]; then
		echo "ERROR: IP Address is NULL"
		exit 1
	fi

	echo_debug
	echo_debug "debug: entering iptab_create_policy_rules"
	echo_debug

	POLICYFILE=$1
	echo_debug "debug: policyfile=$POLICYFILE"
	DEST_IP=$2
	LIMIT_RATE=20/minute
	BURST_RATE=20

	# check if text policy file exists. If not, exit.
	# this can happen for first time when sw0 comes up before sw1 or vice versa.
	if [ ! -f $POLICYFILE ]; then
		exit 1
	fi

	OLD_IFS=$IFS
	# make the separator as newline
	IFS='
'
	for line in `cat $POLICYFILE`
	do      
	# get protocol
		PROTO=${line%% *}
		echo_debug debug proto: $PROTO
		rem_line=${line#${PROTO} }
		echo_debug debug: rem_line=$rem_line

	# get port number
		PORT=${rem_line%% *}
		echo_debug debug port: $PORT

		rem_line=${rem_line#${PORT} }

		echo_debug debug: rem_line=$rem_line

		case "$PORT" in
			22)
		    	RULE=$R_SSH
		    	PROTOCHAIN=$R_TCP
				VIOLATED_APP="SSH"
		    	;;

			23)
		    	RULE=$R_TELNET
		    	PROTOCHAIN=$R_TCP
				VIOLATED_APP="TELNET"
		    	;;

			80 | 443)
		    	RULE=$R_HTTP
		    	PROTOCHAIN=$R_TCP
				VIOLATED_APP="HTTP"
		    	;;

			111)
		    	if [ "$PROTO"  = "tcp" ]; then
				RULE=$R_APIRPCTCP
				PROTOCHAIN=$R_TCP
		    	else
				RULE=$R_APIRPCUDP
				PROTOCHAIN=$R_UDP
		    	fi
				VIOLATED_APP="API"
		    	;;
			897 | 898)
		    	RULE=$R_APIPMAP
		    	PROTOCHAIN=$R_TCP
				VIOLATED_APP="API"
		    	;;

	# need to add ports for API

			*) 
			if [ "$PROTO"  = "tcp" ]; then
		    	RULE=$R_TCPCOMMON
			else    
		    	RULE=$R_UDPCOMMON
			fi
			;; 
		esac # end of case

		echo "$IPTABLES -A $PROTOCHAIN -p $PROTO -m $PROTO --dport $PORT -j $RULE" >> $rule_file

		IFS=' '
		for IPADDR in $rem_line
		do      
			if [ "$IPADDR" = "REJECT" ]; then
				break;
			elif [ "$PORT" = "443" -o "$PORT" = "898" ]; then
			# skip, 443 enforced by 80 and 898 enforced by 897
				break;
			elif [ "$IPADDR" = "ACCEPT" ]; then
				echo "$IPTABLES -A $RULE -p $PROTO -j ACCEPT" >> $rule_file
				break;
			else
				echo "$IPTABLES -A $RULE -p $PROTO -s $IPADDR -j ACCEPT" >> $rule_file
			fi
		done # while

		IFS='
'

	# if ACCEPT, do not need a violation log rule
		if [ "$IPADDR" != ACCEPT ]; then
			echo "$IPTABLES -A $RULE -p $PROTO -m limit --limit $LIMIT_RATE \
				--limit-burst $BURST_RATE -j ULOG --ulog-prefix \
				"\"$VIOLATED_APP violation\"" --ulog-cprange 20 --ulog-nlgroup $NLGROUP" >> $rule_file
		fi

    	if [ "$IPADDR" = "REJECT" ]; then
        	echo "$IPTABLES -A $RULE -p $PROTO -j REJECT" >> $rule_file
    	fi

	done   # for

	IFS=$OLD_IFS

	echo_debug
	echo_debug "debug: exiting iptab_create_policy_rules"
	echo_debug
} # iptab_create_policy_rules

#////////////////////////////////////////
########################################
# This is the start of this script
########################################
#////////////////////////////////////////

export SWITCH_NO=$1;

echo_debug "debug: sw=$SWITCH_NO"

export NLGROUP=`expr $SWITCH_NO + 1`

STDBYPOLICYFILE="/tmp/stdbypolicyfile.txt"

POLICYFILE="/etc/fabos/policy."$SWITCH_NO".txt"

export FC_IFACE=$FC$SWITCH_NO

# take union of both switches for standby

# if one switch is in secure mode or policies
# have not been created for it, then all access is
# open on standby

if [ "$CPSTATE" = "Standby" ]; then

	# standby rules are enforced in two cases,
	# 1. When CP is standby
	# 2. When CP status can not be determined and default
	#	is to enforce standby rules.

	SW0_PFILE="/etc/fabos/policy.0.txt"
	SW1_PFILE="/etc/fabos/policy.1.txt"

	iptab_create_stdby_ruleset $SW0_PFILE $SW1_PFILE $STDBYPOLICYFILE;

	# get standby IP address
	# this is a must because on the standby side, there is no SCN
	# mechanism to notify IP address change, hence security IP store
	# does not get updated
	STDBY_IP=`ifconfig | grep "eth0" -A 1 | \
		grep "inet addr" | sed -e 's/inet addr://g' | \
		cut -d B -f 1 | sed -e 's/ //g'`

	iptab_create_default_rules;

	iptab_create_policy_rules $STDBYPOLICYFILE $STDBY_IP
	iptab_create_generic_rules $STDBY_IP;

	rm -f $STDBYPOLICYFILE
	exit 0;
	
else	# Active CP

	# switch IP and FC ip should not be passed in as parameter
	# as script gets address for both sw0 and sw1 and parameter
	# can be passed only for your switch instance. 

	if [ $SWITCH_NO = 0 ]; then
		SWITCH_IP=`sed -n -e 's/ .*#sw0.*//gp' /etc/hosts`
		FC_IP=`sed -n -e 's/ .*#fc0.*//gp' /etc/hosts`
	fi

	if [ $SWITCH_NO = 1 ]; then
		SWITCH_IP=`sed -n -e 's/ .*#sw1.*//gp' /etc/hosts`
		FC_IP=`sed -n -e 's/ .*#fc1.*//gp' /etc/hosts`
	fi

	iptab_create_default_rules;

	# Allow SNMP port, only on active, so can not keep in common function call
	echo "$IPTABLES -A $R_UDP -p $proto_udp -m $proto_udp --dport 161 -j ACCEPT" >> $rule_file

	echo_debug "debug createtules time"
	iptab_create_policy_rules $POLICYFILE $SWITCH_IP;

	iptab_create_generic_rules $SWITCH_IP;

	exit 0

fi
