#!/bin/sh
#
# Usage: createiptab.sh
# 
# Rule are defined as follows.

#    ----------         -------
#    | INPUT  |-------->| TCP |------> Other protocol rules
#    ----------         -------
#        |
#        |              -------
#        -------------->| UDP |------> Other protocol rules
#                       -------
#
# splitting rule processing in two levels makes it faster
# to execute.

export PATH=/fabos/sbin:/fabos/bin:/bin:/usr/bin:/sbin

# update version whenever script is changed

# IPTABLES variable is not defined, hence will be null
# this variable is kept if script is changed to use a 
# path for iptables,  this variable can be set to proper
# location

export debug_on=0

iptab_clear_rules()
{
    iptables -P INPUT ACCEPT
    iptables -P OUTPUT ACCEPT
    iptables -P FORWARD ACCEPT

    iptables -F
    iptables -X
}

if [ $# != 3 ]; then
	echo "ERROR: $0, Incorrect number of parameters."
	iptab_clear_rules;
	exit 1
fi

# third parameter is CP IP, script will differentiate based on CPSTATE
export ACTCP_IP=$1

export CPNAME=$2
export CPSTATE=$3

version=v1.0

iptables_restore=/sbin/iptables-restore

export ERRFILE="/dev/null"
export INTERNAL_IFACE=eth1
export INTERNAL_IFACE_BP=eth2
export INET_IFACE="eth0"
export DCE_IFACE="veth+"
export DCE_VLAN_IFACE="vlan+"
export FC="fc"
export SCRDIR="/fabos/libexec"
export PRIV_NET=10.0.0.0/28 
export PRIV_NET_BP=127.1.0.0/16 
export INTERNAL_IP1=10.0.0.5
export INTERNAL_IP2=10.0.0.6

export rule_file=/tmp/netfilter.rules
export proto_tcp=tcp
export proto_udp=udp
export proto_icmp=icmp

PROTO_TELNET=telnet
PROTO_SSH=ssh
PROTO_HTTP=http
PROTO_APIRPC=apirpc
PROTO_APIPMAP=apipmap
PROTO_TCPCOMM=tcpcommon
PROTO_UDPCOMM=udpcommon
PRIV_NET_RULE=priv_net_rule

rule_chains="tcp udp telnet ssh http apirpc apipmap tcpcommon udpcommon"

CREATECHAINS="createchains"

echo_debug()
{
		echo $* 1>&2
}

iptab_clean_up () 
{
	rm -f $rule_file
}

iptab_create_header()
{
	# Start generating rules file

	echo "# Generated by createiptab $version" > $rule_file
	echo "*filter" >> $rule_file
	echo ":INPUT DROP [0:0]" >> $rule_file
	echo ":FORWARD ACCEPT [0:0]" >> $rule_file
	echo ":OUTPUT ACCEPT [0:0]" >> $rule_file
}

iptab_sw_create_rule_chains()
{
	swinst=$1;

	for rule_item in $rule_chains
	do 
		# need this for both tcp and udp
		if [ "apirpc" = "$rule_item" ]; then
			echo ":$rule_item$swinst$proto_tcp - [0:0]" >> $rule_file
			echo ":$rule_item$swinst$proto_udp - [0:0]" >> $rule_file
			continue;
		fi	

		echo ":$rule_item$swinst - [0:0]" >> $rule_file
	done
}

iptab_priv_net_create_rule_chain()
{
	echo ":$PRIV_NET_RULE - [0:0]" >> $rule_file
}

iptab_cp_create_rule_chains()
{
	cp_code=$1;

	echo ":$proto_tcp$cp_code - [0:0]" >> $rule_file
	echo ":$proto_udp$cp_code - [0:0]" >> $rule_file
}

iptab_get_cp_association()
{

	if [ "$ischassis" = "Yes" ] && [ "$CPSTATE" = "Standby" ]; then
		ACTIVECP=0	# Dual chassis stanby CP
	elif [ "$ischassis" = "Yes" ]; then
		ACTIVECP=1	# Dual chassis active CP 
	else
		ACTIVECP=2	# Single Chassis box 
	fi

	export ACTIVECP
}

iptab_priv_net_put_chain_rules()
{
	echo "$IPTABLES -A OUTPUT -o $INTERNAL_IFACE -j $R_PRIV_NET" >> $rule_file
	echo "$IPTABLES -A OUTPUT -o $INTERNAL_IFACE_BP -j $R_PRIV_NET" >> $rule_file
	echo "$IPTABLES -A $R_PRIV_NET -s $PRIV_NET -d $PRIV_NET -j ACCEPT" >> $rule_file
	echo "$IPTABLES -A $R_PRIV_NET -s $PRIV_NET_BP -d $PRIV_NET_BP -j ACCEPT" >> $rule_file
	echo "$IPTABLES -A $R_PRIV_NET -j REJECT" >> $rule_file
}

iptab_BP_priv_net_put_chain_rules()
{
	echo "$IPTABLES -A OUTPUT -o $INTERNAL_IFACE_BP -j $R_PRIV_NET" >> $rule_file
	echo "$IPTABLES -A $R_PRIV_NET -s $PRIV_NET_BP -d $PRIV_NET_BP -j ACCEPT" >> $rule_file
	echo "$IPTABLES -A $R_PRIV_NET -j REJECT" >> $rule_file
}

iptab_create_common_rules()
{
if [ "$ACTIVECP" = "1" ]; then

	secmode0=`/fabos/libexec/secModeGet 0`
	secmode1=`/fabos/libexec/secModeGet 1`
	if [ "$secmode0" != "0"  -o "$secmode1" != "0" ]; then
		secmode=1
	else
		secmode=0
	fi

#	echo_debug "debug: secmode=$secmode"

	# 600 is arbitrarily chosen to allow mounts from switch
	# it was observed that mount used port above 800 in most cases
	echo "$IPTABLES -A $R_ACTTCP -p $proto_tcp -m $proto_tcp --dport 897 -j REJECT" >> $rule_file
	if [ "$secmode" = "0" ]; then
		echo "$IPTABLES -A $R_ACTTCP -p $proto_tcp -m $proto_tcp --dport 23 -j ACCEPT" >> $rule_file
		echo "$IPTABLES -A $R_ACTTCP -p $proto_tcp -m $proto_tcp --dport 22 -j ACCEPT" >> $rule_file
	fi
	echo "$IPTABLES -A $R_ACTTCP -p $proto_tcp -m $proto_tcp --dport 600: -j ACCEPT" >> $rule_file

	echo "$IPTABLES -A $R_ACTUDP -p $proto_udp -m $proto_udp --dport 8000 -j REJECT" >> $rule_file
	echo "$IPTABLES -A $R_ACTUDP -p $proto_udp -m $proto_udp --dport 9090 -j REJECT" >> $rule_file
	echo "$IPTABLES -A $R_ACTUDP -p $proto_udp -m $proto_udp --dport 123 -j ACCEPT" >> $rule_file
	echo "$IPTABLES -A $R_ACTUDP -p $proto_udp -m $proto_udp --dport 600: -j ACCEPT" >> $rule_file

	echo "$IPTABLES -A INPUT -p $proto_tcp -i $INET_IFACE -d $ACTCP_IP -j $R_ACTTCP" >> $rule_file
	echo "$IPTABLES -A INPUT -p $proto_udp -i $INET_IFACE -d $ACTCP_IP -j $R_ACTUDP" >> $rule_file

fi # end for active CP rules

# default policy for all, eth1 should always be allowed because
# two CPs talk on this. Terminator does not need eth1 rule.

echo "$IPTABLES -A INPUT -i $INET_IFACE -p $proto_icmp -m $proto_icmp -d $INTERNAL_IP1 --icmp-type 8 -j DROP" >> $rule_file
echo "$IPTABLES -A INPUT -i $INET_IFACE -p $proto_icmp -m $proto_icmp -d $INTERNAL_IP2 --icmp-type 8 -j DROP" >> $rule_file
echo "$IPTABLES -A INPUT -p $proto_icmp -m $proto_icmp --icmp-type 0 -j ACCEPT" >> $rule_file
echo "$IPTABLES -A INPUT -p $proto_icmp -m $proto_icmp --icmp-type 8 -j ACCEPT" >> $rule_file

#
# setup BP interface only on systems where it is available
#
/sbin/ifconfig $INTERNAL_IFACE_BP 1>$ERRFILE 2>$ERRFILE
if [ $? -eq 0 ]; then
	echo "$IPTABLES -A INPUT -i $INTERNAL_IFACE_BP -j ACCEPT" >> $rule_file
	echo "$IPTABLES -A FORWARD -o $INTERNAL_IFACE_BP -j REJECT" >> $rule_file
	echo "$IPTABLES -A FORWARD -i $INTERNAL_IFACE_BP -j REJECT" >> $rule_file
fi

#
# setup to always receive anything on DCE interfaces
#
echo "$IPTABLES -A INPUT -i $DCE_IFACE -j ACCEPT" >> $rule_file
echo "$IPTABLES -A INPUT -i $DCE_VLAN_IFACE -j ACCEPT" >> $rule_file

if [ "$ACTIVECP" != "2" ]; then
	# set up forwarding rules, no forwarding from/to eth1
	echo "$IPTABLES -A INPUT -i $INTERNAL_IFACE -j ACCEPT" >> $rule_file
	echo "$IPTABLES -A FORWARD -o $INTERNAL_IFACE -j REJECT" >> $rule_file
	echo "$IPTABLES -A FORWARD -i $INTERNAL_IFACE -j REJECT" >> $rule_file

#	echo "$IPTABLES -A INPUT -s $PRIV_NET -j REJECT" >> $rule_file
#	echo "$IPTABLES -A INPUT -d $PRIV_NET -j REJECT" >> $rule_file
fi

echo "$IPTABLES -A INPUT -i lo -j ACCEPT" >> $rule_file

# this is necessary so clients do not hang waiting for response.
# this was discovered with firmwaredownload
echo "$IPTABLES -A INPUT -j REJECT" >> $rule_file

echo "$IPTABLES -P INPUT DROP" >> $rule_file
} # iptab_create_common_rules

iptab_export_rule_names()
{
	MYSWITCH=$1
	export R_TCP=$proto_tcp$MYSWITCH
	export R_UDP=$proto_udp$MYSWITCH
	export R_TELNET=$PROTO_TELNET$MYSWITCH
	export R_SSH=$PROTO_SSH$MYSWITCH
	export R_HTTP=$PROTO_HTTP$MYSWITCH
	export R_APIRPCUDP=$PROTO_APIRPC$MYSWITCH"udp"
	export R_APIRPCTCP=$PROTO_APIRPC$MYSWITCH"tcp"
	export R_APIPMAP=$PROTO_APIPMAP$MYSWITCH
	export R_TCPCOMMON=$PROTO_TCPCOMM$MYSWITCH
	export R_UDPCOMMON=$PROTO_UDPCOMM$MYSWITCH

	export R_PRIV_NET=$PRIV_NET_RULE

	if [ "$ACTIVECP" = "1" ]; then
		export R_ACTTCP="tcp"A
		export R_ACTUDP="udp"A
	fi
}

#///////////////////////////////
################################
# start of this script
################################
#///////////////////////////////


chassis_info=`getchassisconfig`

export ischassis=`echo $chassis_info | sed -n -e 's/.*Chassis based system: //gp' | \
	sed -n -e 's/ .*//gp'`

export num_switches=`echo $chassis_info | sed -n -e 's/Number of switches: //gp' | \
	sed -n -e 's/ .*//gp'`

iptab_create_header;

# get switch type, term/Ulysses and cp type for Ulysses
iptab_get_cp_association;

# create rule chain names

# standby CP on Ulysses
if [ $ACTIVECP = 0 ]; then
	iptab_sw_create_rule_chains S;
	iptab_priv_net_create_rule_chain;

	iptab_export_rule_names S; 
	$SCRDIR/$CREATECHAINS 0;

	if [ "$?" != "0" ]; then
		iptab_clear_rules;
		exit 1
	fi

	iptab_priv_net_put_chain_rules;
fi

# Dual chassis active CP
if [ $ACTIVECP = 1 ]; then
	number=0
	while [ $number -lt $num_switches ]; do
		iptab_sw_create_rule_chains $number;
		number=$((number + 1))
	done

	iptab_cp_create_rule_chains A;
	iptab_priv_net_create_rule_chain;

	number=0
	while [ $number -lt $num_switches ]; do
		iptab_export_rule_names $number; 
		$SCRDIR/$CREATECHAINS $number;
		if [ "$?" != "0" ]; then
			iptab_clear_rules;
			exit 1
		fi

		number=$((number + 1))
	done

	iptab_priv_net_put_chain_rules;
fi

# Single chassis box
if [ $ACTIVECP = 2 ]; then
	iptab_sw_create_rule_chains 0;

	iptab_priv_net_create_rule_chain;

	iptab_export_rule_names 0; 
	$SCRDIR/$CREATECHAINS 0;

	if [ "$?" != "0" ]; then
		iptab_clear_rules;
		exit 1
	fi

#
# setup BP interface only on systems where it is available
#
	/sbin/ifconfig $INTERNAL_IFACE_BP 1>$ERRFILE 2>$ERRFILE
	if [ $? -eq 0 ]; then
		iptab_BP_priv_net_put_chain_rules;
	fi

fi

iptab_create_common_rules;

echo "COMMIT" >> $rule_file
echo "# Completed" >> $rule_file

$iptables_restore < $rule_file 1> /dev/null 2>&1 
if [ $? != 0 ]; then
    iptab_clear_rules;
    echo "ERROR: Failed to enforce new iptables rules"
    exit 1
fi

# add forwarding rule, let it be commented, can be uncommented when needed
# if uncommented, FORWARD rule also must be changed to ACCEPT
# echo "1" > /proc/sys/net/ipv4/ip_forward

# cleanup  previous state
#iptab_clean_up;
