#!/bin/sh
# Sreen Tallam 08/07/08 
# Copyright (c) 2008-2012 NetApp, Inc
# All rights reserved.


IPTABLES="/sbin/iptables"
log="/var/log/messages"
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
#
############################################################################

update_v4_chain() {

	### 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 -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"

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

		$IPTABLES -A OUTPUT -p ALL -j output_pkts

		$IPTABLES -A OUTPUT -p ALL -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"


		###### 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"


		######################### 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

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

	$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"

	$IPTABLES -A input_pkts -p ALL -i $LO_IFACE -s $LO_IP -j ACCEPT

	$IPTABLES -A input_pkts -p ALL -j ssh_pkts

	$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

	$IPTABLES -A output_pkts -p ALL -s $LO_IP -j ACCEPT

	###### 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

	######################### 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 ###### 

	# 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
	
	###### 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	

	if [ $1 == "start" ]
	then
		# checking if the link is present
		link_status=`/usr/local/bin/miitool eth0 | grep -c "link status: up"`;

		if [ $link_status -eq 0 ]; then {

			# Sometimes in a 100/FDX forced configuration, Ethernet link does not come up
			# Resetting the PHY in those scenarios brings up the link
			echo "No Ethernet link, resetting the PHY" >> /var/log/messages
			LIMIT=3
			counter=1
			while [ $counter -ne $LIMIT ]
			do
				/usr/local/bin/reset_phy
				if [ $? -eq 0 ]; then {
					break
				}
				fi
				counter=`expr $counter + 1`
			done

			# after resetting the PHY, the link doesn't come up immediately,
			# a delay is required before checking the link_status again
			/bin/sleep 2
		}
		fi

		# d-patch for t-systems
		# refer burt 356991 for details
		# checking if the link is present
		link_status=`/usr/local/bin/miitool eth0 | grep -c "link status: up"`;

		if [ $link_status -eq 1 ]; then {
			echo "Checking and applying media-type configuration" >> /var/log/messages
			/usr/local/bin/rlmport_config.sh

		}
		else
			echo "firewall: no Ethernet link" >> /var/log/messages
		fi
	fi

	IP6TABLES="/sbin/ip6tables"
	INET_IFACE="eth0"
	LO_IFACE="lo"
	tmp_ifconfig_eth0="/tmp/ifconfig_file_eth0"
	tmp_ip_6_addr="/tmp/ip_6_addr"
	IFCONFIG="/sbin/ifconfig eth0"	
	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 [ -e $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`
	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 eth0
	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 eth0
			fi
		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}'`
			`$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

