#!/bin/sh
# Sreen Tallam 08/07/08 

if [ -f /mnt/logs/var/ip6fw_disable ]
then
	echo "Firewall disabled " > /var/run/firewall_status
	exit 0;
else
	echo "Firewall enabled " > /var/run/firewall_status
fi

IPTABLES="/sbin/iptables"
log="/var/log/messages"
interfaces="/mnt/logs/etc/network/interfaces"
breakout=0
date=`date | sed "s/[A-Z]\{3\} [0-9]\{4\}//"`
loginfo="$date (none) firewall[$pid]:"

############################################################################
#  Following is the order of execution
#
#  /etc/init.d/firewall start
#  --------------------------
#
#  1. create_v4_rules   (creates the ipv4 related rules)
#  2.   update_v4_chain (creates rules for INPUT, OUTPUT & POSTROUTING chains)
#  3. create_v6_rules   (creates the ipv6 related rules)
#  4.   update_v6_chain (creates rules for INPUT & OUTPUT chains)
#
#
#  /etc/init.d/firewall restart
#  -----------------------------
#
#  1. modify_v4_rules   (flushes the rules and invokes update_v4_chain)
#  2. update_v4_chain
#  3. modify_v6_rules   (flushes the rules and invokes update_v6_chain)
#  4. update_v6_chain
#
#
#  /etc/init.d/firewall open_telnet_port
#  -------------------------------------
#
#  - Open the TELNET port for GDB connection
#
############################################################################

#
# create loopback rules for the IPv4 adapter
# See burt 421193 

default_loopback() {

    $IPTABLES -A OUTPUT -p ALL -o lo -j output_pkts
    $IPTABLES -A INPUT -p ALL -i lo -j input_pkts
}

update_v4_chain() {

	# for Vespa, we always update the rules for the 16 & 64 adapter
    if [ -e /var/run/Vespa ] || [ -f "/var/run/Helix" ] || [ -f "/var/run/Zuma" ]  ; then
        $IPTABLES -A INPUT -i eth0.16 -j input_eth_16
        $IPTABLES -A INPUT -i eth0.64 -j input_eth_64
        $IPTABLES -A OUTPUT -o eth0.16 -j output_eth_16
        $IPTABLES -A OUTPUT -o eth0.64 -j output_eth_64
    fi


	### create INPUT and OUTPUT chain rules only if v4 is enabled ###
	if [ $ipv4_disable_flag -eq 0 ]
	then		

        ###### INPUT chain ###### 

		$IPTABLES -A INPUT -p ALL -j input_pkts

		$IPTABLES -A INPUT -p ALL -i $LO_IFACE -s $INET_IP -j ACCEPT

		$IPTABLES -A INPUT -p ALL -i $INET_IFACE -d $INET_IP -m state \
			--state ESTABLISHED,RELATED -j ACCEPT

		$IPTABLES -A INPUT -m limit --limit 1/hour \
		     --limit-burst 2 -j LOG --log-level DEBUG \
		     --log-prefix "ipv4: INPUT died"

		# Set up the rule to forward the spcs packets to spcs chain
		$IPTABLES -A INPUT -p tcp -s 0/0 --dport 49152:65535 -j spcs

		# Now execute the spcs firewall script to see if we need to perform
		# any additional actions
		/usr/local/bin/set_spcs_firewall_rules.sh

		###### OUTPUT chain ###### 

		$IPTABLES -A OUTPUT -p ALL -j output_pkts

		$IPTABLES -A OUTPUT -p ALL -o $INET_IFACE -s $INET_IP -j ACCEPT

		$IPTABLES -A OUTPUT -m limit --limit 1/hour --limit-burst 2 \
			-j LOG --log-level DEBUG --log-prefix "ipv4: OUTPUT died"

		## forwarding (not used for the primary eth adatper) ##

		if [ -e /var/run/Vespa ]  || [ -f "/var/run/Helix" ] || [ -f "/var/run/Zuma" ]  ; then 
	            $IPTABLES -A FORWARD -i eth0.16 -j forward_eth_16
		    $IPTABLES -A FORWARD -i eth0.64 -j forward_eth_64		    
		fi

		###### POSTROUTING chain ###### 

		# Enable simple IP Forwarding and Network Address Translation
		$IPTABLES -t nat -A POSTROUTING -o $INET_IFACE \
		     -j SNAT --to-source $INET_IP
	fi

}

update_v6_chain() {

	if [ $ipv6_disable_flag -eq 1 ]
	then
		$IP6TABLES -A INPUT -p ipv6-icmp -j icmp_packets
		$IP6TABLES -A OUTPUT -p ipv6-icmp -j icmp_packets
	fi

	### create INPUT and OUTPUT chain rules only if v6 is enabled ###
	if [ $ipv6_disable_flag -eq 0 ]
	then

		######################### INPUT  ######################### 

		$IP6TABLES -A INPUT -p ALL -j input_pkts

		$IP6TABLES -A INPUT -p udp \
			     -m limit --limit 20/second --limit-burst 50 -j ACCEPT

		### Add rate limiting rule for protocol 254 (Use for experimentation
		### and testing).  This is needed for USGv6 compliance testing.
		$IP6TABLES -A INPUT -p 254  -s 0/0 -m limit --limit 3/second \
		     --limit-burst 5 -j ACCEPT
                 
		$IP6TABLES -A INPUT -m limit --limit 1/hour --limit-burst 2 \
		     -j LOG --log-level DEBUG --log-prefix "ipv6: INPUT died"

		# Set up the rule to forward the spcs packets to spcs chain
		$IP6TABLES -A INPUT -p tcp -s 0/0 --dport 49152:65535 -j spcs

		# Now execute the spcs firewall script to see if we need to perform
		# any additional actions
		/usr/local/bin/set_spcs_firewall_rules.sh

		######################### OUTPUT  ######################### 

		$IP6TABLES -A OUTPUT -p ipv6-icmp -j icmp_packets

		# Bad TCP packets we don't want.
		$IP6TABLES -A OUTPUT -p tcp ! --syn -m state --state NEW \
		     -j LOG -m limit --limit 1/hour --limit-burst 2 \
		     --log-prefix "ipv6: OUTPUT new not syn"
		$IP6TABLES -A OUTPUT -p tcp ! --syn -m state --state NEW \
		     -j DROP

		$IP6TABLES -A OUTPUT -p ALL -j ACCEPT

		# Log weird packets that don't match the above.
		$IP6TABLES -A OUTPUT -m limit --limit 1/hour \
		     --limit-burst 2 -j LOG --log-level DEBUG \
		     --log-prefix "ipv6: OUTPUT died"

    fi

}

create_v4_rules() {

	INET_IP=$IP
	LO_IP="127.0.0.1"
	
	echo "0" > /proc/sys/net/ipv4/ip_forward
	
	$IPTABLES -F 

	# Drop all incoming traffic
	$IPTABLES -P INPUT DROP
	$IPTABLES -P OUTPUT DROP
	$IPTABLES -P FORWARD DROP

	# Clear out nat table
	# if this is not flushed, RLM keeps using the oldest ip address
	# that is present in RLM for the outgoing packets
	$IPTABLES -t nat --flush

	$IPTABLES -N input_pkts
	$IPTABLES -F input_pkts

	$IPTABLES -N ssh_pkts
	$IPTABLES -F ssh_pkts

	$IPTABLES -N output_pkts
	$IPTABLES -F output_pkts

	#Create spcs chain for ipv4
	$IPTABLES -N spcs
	$IPTABLES -F spcs

	$IPTABLES -N blocked_ip
	$IPTABLES -F blocked_ip

	if [ -e /var/run/Vespa ]  || [ -f "/var/run/Helix" ] || [ -f "/var/run/Zuma" ]  ; then 
            for i in 16 64 ; do
		for r in input output forward ; do
                    $IPTABLES -N ${r}_eth_${i}
                    $IPTABLES -F ${r}_eth_${i}
		done
            done
	fi

	###### input_pkts chain ###### 

	# creat the rules for vespa, the default rules will be accept
	if [ -e /var/run/Vespa ]  || [ -f "/var/run/Helix" ] || [ -f "/var/run/Zuma" ]  ; then 
            for i in 16 64 ; do
		for r in input output forward ; do 
		    $IPTABLES -A ${r}_eth_${i} -j ACCEPT
		done
            done
	fi

	$IPTABLES -A input_pkts -p tcp ! --syn -m state --state NEW \
	     -j LOG -m limit --limit 1/hour --limit-burst 2 \
	     --log-prefix "ipv4: INPUT new not syn"

	if [ -e /var/run/Vespa ]  || [ -f "/var/run/Helix" ]  || [ -f "/var/run/Zuma" ]  ; then
	    $IPTABLES -A input_pkts -p ALL -i $LO_IFACE -j ACCEPT
	else
	    $IPTABLES -A input_pkts -p ALL -i $LO_IFACE -s $LO_IP -j ACCEPT
	fi

	$IPTABLES -A input_pkts -p ALL -j ssh_pkts

	$IPTABLES -A input_pkts -p ALL -i $INET_IFACE -j blocked_ip

	$IPTABLES -A input_pkts -p ICMP -i $INET_IFACE -s 0/0 \
	    --icmp-type 8 -m limit --limit 3/second --limit-burst 5 \
	    -j ACCEPT

	$IPTABLES -A input_pkts -p ICMP -i $INET_IFACE -s 0/0 \
	    --icmp-type 11 -m limit --limit 3/second --limit-burst 5 \
	    -j ACCEPT

	###### ssh_pkts chain ######

	#Following 2 rules create a 'recent' list of recently seen IP addresses.
	#If new SSH connection requests are made from the same IP in a short 
	#interval, it is likely a DOS attack. The following rule allows 10 SSH
	#requests from the same IP within a minute and then drops new requests.
	
	$IPTABLES -A ssh_pkts -p TCP -i $INET_IFACE --dport 22 -m state \
		--state NEW -m recent --set

	$IPTABLES -A ssh_pkts -p TCP -i $INET_IFACE --dport 22 -m state \
		--state NEW -m recent --update --seconds 60 --hitcount 10 -j DROP
		
	 #Following rule rate-limits new incoming ssh connection requests to 
	 #an average of 20 per minute, with an initial burst of 20 connections

	$IPTABLES -A ssh_pkts -p TCP -i $INET_IFACE -s 0/0 \
		--dport 22 --syn -m state --state NEW -m limit --limit 20/minute \
		--limit-burst 20 -j ACCEPT

	###### output_pkts chain ###### 

	$IPTABLES -A output_pkts -p tcp ! --syn -m state --state NEW -j LOG \
	     -m limit --limit 1/hour --limit-burst 2 \
	     --log-prefix "ipv4: OUTPUT new not syn"

	$IPTABLES -A output_pkts -p tcp ! --syn -m state --state NEW -j DROP

#    if [ -e /var/run/Vespa ]  || [ -f "/var/run/Helix" ] || [ -f "/var/run/Zuma" ]  ; 
#        $IPTABLES -t mangle -A PREROUTING -p tcp --sport 5141 -j MARK --set-mark 16
#        $IPTABLES -t mangle -A PREROUTING -p tcp --sport 5131 -j MARK --set-mark 16
#    fi

    if [ -e /var/run/Vespa ]  || [ -f "/var/run/Helix" ] || [ -f "/var/run/Zuma" ]  ; then 
        $IPTABLES -A output_pkts -p ALL -o $LO_IFACE -j ACCEPT
    else
	    $IPTABLES -A output_pkts -p ALL -s $LO_IP -j ACCEPT
    fi

	# Default spcs chain rule: DROP everything coming to the spcs chain
	# Only if spcs is enabled and listening would this rule will change
	$IPTABLES -A spcs -p tcp -s 0/0 -j DROP

	###### INPUT, OUTPUT AND POSTROUTING chains ###### 
	update_v4_chain
}


##############################################
#  Initial ipv6 rules creation
##############################################

create_v6_rules() {

	LO_IP6="::1"
	#LO_IP6="::1/128"

	# Clear out any old rules
	$IP6TABLES -F INPUT
	$IP6TABLES -F OUTPUT
	$IP6TABLES -F FORWARD

	# 4.1.1 Set policies ( Drop all incoming traffic)
	$IP6TABLES -P INPUT DROP
	$IP6TABLES -P OUTPUT DROP
	$IP6TABLES -P FORWARD DROP

	$IP6TABLES -N icmp_packets
	$IP6TABLES -F icmp_packets

	$IP6TABLES -N input_pkts
	$IP6TABLES -F input_pkts

	$IP6TABLES -N ssh_pkts
	$IP6TABLES -F ssh_pkts

	#Create spcs chain for ipv6
	$IP6TABLES -N spcs
	$IP6TABLES -F spcs

	$IP6TABLES -N blocked_ip
	$IP6TABLES -F blocked_ip

	######################### icmp_packets rules ########################

	# Rate limit all ipv6-icmp packet types.  This is needed for USGv6
	# compliance testing.
	$IP6TABLES -A icmp_packets -p ipv6-icmp -s 0/0 \
	     -m limit --limit 10/second --limit-burst 30 -j ACCEPT

	###### input_pkts chain ###### 

    $IP6TABLES -A input_pkts -p ALL -i $INET_IFACE -j blocked_ip

    # allowed chain
	# Only Accept packets with the RST,ACK set and SYN cleared
	# i.e. only connections that are coming back from the internet with
	# ACK,RST flags set and SYN cleared. This is used for all the 
	# HTTP connections intiated from RLM. WGET is one such example
	$IP6TABLES -A input_pkts -p tcp ! --syn -j ACCEPT

    $IP6TABLES -A input_pkts -p ipv6-icmp -j icmp_packets

	# handle ssh packets
	$IP6TABLES -A input_pkts -p ALL -j ssh_pkts

	#$IP6TABLES -A input_pkts -p ICMP -i $INET_IFACE -j icmp_packets

	# Rules for special networks not part of the Internet
	$IP6TABLES -A input_pkts -p ALL -i $LO_IFACE -s $LO_IP6 -j ACCEPT

	###### ssh_pkts chain ######
	$IP6TABLES -A ssh_pkts -p tcp -i $INET_IFACE -s 0/0 --dport 22 \
	     -j ACCEPT

	
	# Default spcs chain rule: DROP everything coming to the spcs chain
	# Only if spcs is enabled and listening would this rule will change

	$IP6TABLES -A spcs -p tcp -s 0/0 -j DROP

	###### INPUT AND OUTPUT chains ###### 
	update_v6_chain

}


####################################################
#  Ipv4 rules modification for the new ipv4 address
####################################################

modify_v4_rules() {

	INET_IP=$IP
	LO_IP="127.0.0.1"

	echo "0" > /proc/sys/net/ipv4/ip_forward
	
	# Clear out nat table
	# if this is not flushed, RLM keeps using the oldest ip address
	# that is present in RLM for the outgoing packets
	$IPTABLES -t nat --flush
	$IPTABLES -F INPUT
	$IPTABLES -F OUTPUT

	###### INPUT, OUTPUT AND POSTROUTING chains ###### 
	update_v4_chain

}

####################################################
#  Ipv6 rules modification for the new ipv6 address
####################################################

modify_v6_rules() {	

	LO_IP6="::1"
	#LO_IP6="::1/128"

	# Clear out any old rules
	$IP6TABLES -F INPUT
	$IP6TABLES -F OUTPUT
	
	###### INPUT AND OUTPUT chains ###### 
	update_v6_chain
}

#set -x

case "$1" in
  stop)
 	echo "."
	;;

  start|reload|force-reload|restart)

	# do not setup the firewall if in a test fixture. 
	if [ -f /var/run/InTestFixture ]; then
	  exit 0;
	fi
		
	IP6TABLES="/sbin/ip6tables"
    if [ -f "/var/run/Vespa" ]  || [ -f "/var/run/Helix" ] || [ -f "/var/run/Zuma" ]  ; then
    	INET_IFACE="eth0.32"
    	IFCONFIG="/sbin/ifconfig eth0.32"	
    else
    	INET_IFACE="eth0"
	    IFCONFIG="/sbin/ifconfig eth0"	
    fi
    LO_IFACE="lo"

	tmp_ifconfig_eth0="/tmp/ifconfig_file_eth0"
	tmp_ip_6_addr="/tmp/ip_6_addr"
	IP6ADDR="/sbin/ip -6 addr"
	tuples_filer="/mnt/logs/etc/rlm_config_from_filer"
	IPv4="/sbin/ip"
	got_router_assigned_addr=0

	##################################################################
	#  Get IPv4 and IPv6 disable/enable status
	##################################################################
	if [ -s $tuples_filer ]
	then
		ipv6_disable_flag=`awk '/^ipv6_disable/ {print $2}' $tuples_filer`
		ipv4_disable_flag=`awk '/^ipv4_disable/ {print $2}' $tuples_filer`
		is_ipv6_ra_disabled=`awk '/^ra_disable/ {print $2}' $tuples_filer`
	else
		ipv4_disable_flag=0
		ipv6_disable_flag=1
	fi

	if [ -z "$ipv4_disable_flag" ]
	then
		ipv4_disable_flag=0
	fi

	if [ -z "$ipv6_disable_flag" ]
	then
		ipv6_disable_flag=1		
	fi

	if [ -z "$is_ipv6_ra_disabled" ]
	then
		is_ipv6_ra_disabled=0
	fi

	##################################################################
	#  Get IPv4 address.  There are two cases:
	#  1. if [ IPv4 flag == " " ] ; Older OnTap -> get v4 address
	#  2. if [ IPv4 flag == "1"/"0" ] ; Newer Ontap
	#	a . if [ IPv4 flag == "1" ] ; disable v4 address
	#	b . if [ IPv4 flag == "0" ] ; enable v4 address
	##################################################################

	if [ $ipv4_disable_flag -eq 1 ]
	then
		IP=0
	else
                IP=$(/usr/local/bin/ipaddr.sh)
                while [ -z $IP ] && [ "$count" -lt 30 ]
                do
                        if [ "$count" -gt 0 ]
                        then
                                sleep 1
                        fi
                        count=$((count+1))
                        IP=$(/usr/local/bin/ipaddr.sh)
                done

                # If we've run out of retries, we try to roll back the interfaces file
                if [ "$count" -ge 30 ]
                then
                        num_prev_interfaces=$(ls -la $interfaces.*[0-9] | wc -l)

                        # Use default interfaces file if none are available otherwise use
                        # the last known healthy config (see rlm_conf_sp.sh)
                        if [ "$num_prev_interfaces" -le "0" ]
                        then
                                def_interfaces="/etc/defconfig/etc/network/interfaces"

                                # If default config is the same as what is loaded
                                # breakout to prevent an infinite loop
                                if diff $interfaces $def_interfaces &> /dev/null ; then
                                        breakout=1
                                else
                                        logger "Networking configuration may be corrupt. Copying to logger (below) and rolling back to default."
                                        logger < $interfaces
                                        cp $def_interfaces $interfaces
                                fi
                        else
                                rollback_file=$(ls -c $interfaces.*[0-9] | head -n 1)
                                logger "Networking configuration may be corrupt. Copying to logger (below) and rolling back to previous config."
                                logger < $interfaces
                                cp $rollback_file $interfaces
                                rm -f $rollback_file
                        fi

                        if [ "$breakout" -ne 1 ]
                        then
                                #restart networking and then restart self (firewall)
                                /etc/init.d/networking restart
                                sleep 10
                                exec $(readlink -f $0) restart
                                logger "exec to restart the firewall failed...exiting..."
                                exit 1
                        fi
                fi
        fi
	
	##################################################################
	#  Get router assigned address
	#  The execution of the v6 firewall happens much faster than 
	#  the time taken for the Router Advertised v6 address to reach
	#  RLM. So the script needs to wait until the RA v6 address
	#  is assigned to the RLM.
	##################################################################

	if [ $ipv6_disable_flag -eq 0 ]
	then 
		LIMIT=20
		counter=1
		while [ $counter -ne $LIMIT ]
		do	
			$IP6ADDR >$tmp_ip_6_addr
			counter=`expr $counter + 1`
			ip6_router_assigned_addr=`/usr/local/bin/get_ip6_router_assigned_addr.sh $tmp_ip_6_addr`					
			if [ -n "$ip6_router_assigned_addr" ]
			then
				got_router_assigned_addr=1
				break
			fi

			/bin/usleep 250000
		done

		$IP6ADDR >$tmp_ip_6_addr
		ip6_global_addr="`/usr/local/bin/get_ip6_global_addr.sh \
			     $tmp_ip_6_addr`"

		# If there is no Ipv6 address to assing, then, please DO NOT
		# run the IPv6 firewall .. Save time!!
		if [ -z "$ip6_router_assigned_addr" -a -z "$ip6_global_addr" -a	"$is_ipv6_ra_disabled" -eq "0" ]
		then
			echo "$loginfo No Global IPv6 or Router Assigned Address's were found by RLM " 1>>$log	
		fi
	fi
	
	#############################################
	#  create/reconfigure v4 and v6 firewall rules
	#############################################

	if [ $1 == "start" ]
	then
		create_v4_rules	
		create_v6_rules
    fi

	if [ $1 == "restart" ]
	then
		modify_v4_rules	
		modify_v6_rules 
	fi
	

	if [ $1 == "start" ]
	then
		# set SSH access control
		echo "Setting SSH access control rules " >> /var/log/messages
		/usr/local/bin/set_ssh_firewall_rules.sh
	fi

	# Bring the interface down if both these flags are set to TRUE
	# This will help bring the interface next time around when there 
	# is some actual IP configuration.
	# Burt reference 361543
	if [ $ipv4_disable_flag -eq 1  -a  $ipv6_disable_flag -eq 1 ]; 	then
   		$IFCONFIG down
    	/sbin/ifdown $INET_IFACE
        # allow packets to cross lo adapter
        default_loopback
    else	
		#############################################
		#  delete v4 address if v4 is disabled
		#  delete v6 address(s) if v6 is disabled
		#############################################

		$IFCONFIG >$tmp_ifconfig_eth0

		if [ $ipv4_disable_flag -eq 1  ]
		then
			IP=`cat $tmp_ifconfig_eth0 | grep "inet addr:" | \
			     sed 's/.*inet addr:\([^ ]*\).*/\1/'`		
			if [ -n "$IP" ]
			then
				$IPv4 addr del $IP dev $INET_IFACE 
			fi

            # allow packets to cross lo adapter
            default_loopback
		fi
	
		if [ $ipv6_disable_flag -eq 1  ]	
		then
			IP6=`cat $tmp_ifconfig_eth0 | awk '/inet6 addr:/ {print $3}'`
			for address_to_del in $IP6
			do
				`$IFCONFIG del "$address_to_del"`
			done
		elif [ "$is_ipv6_ra_disabled" -eq 1 ]
		then
			ip6_router_assigned_addr="$($IP6ADDR | awk '/scope global dynamic $/ {print $2}')"
            [ -n "${ip6_router_assigned_addr}" ] && $IFCONFIG del $ip6_router_assigned_addr
		fi
	fi

	;;

	open_telnet_port)
		# Enable the TELNET port of RLM when GDB Telnet mode is turned ON.
		gdb_console_telnet_port="6466"
		
		$IPTABLES -A INPUT -p TCP -s 0/0 --dport $gdb_console_telnet_port -j ACCEPT		
		echo "$loginfo firewall set to accept telnet connection for GDB" 1>>$log
		echo "."
	;;

		
  *)
 	echo "Usage: /etc/init.d/ipchains {start|stop|reload|force-reload|restart}"
	exit 1
esac

exit 0

