#!/bin/bash

# +-------------------------------------------------+
# |                                                 |
# |     Korseby IP-TABLES FIREWALL   1.2.0-rc4      |
# |                                                 |
# | FORWARDING Configuration a private network      |
# |                                                 |
# +-------------------------------+-----------------+
# | (c) 2001-2004 Kristian Peters |
# +-------------------------------+

# ---------------------------- Preferences ---------------------------

VERSION="1.2.0-rc4"
IPTABLES="/sbin/iptables"
IFCONFIG="/sbin/ifconfig"
SYSCTL="/sbin/sysctl"

# Masquerading or NAT ?   Syntax: "MASQ" | "NAT"
METHOD="NAT"

# Mangle-Config
MANGLE="YES"

# LAN-side iface
LANIF="eth0"
LANIP="10.1.1.1"

# INET-ISDN iface
INETIF="ippp0"
INETIP="$(ifconfig $INETIF | grep inet | cut -d : -f 2 | cut -d \  -f 1)"

# internal addresses LAN=10.1.1.[0-127]
LAN="10.1.1.0/255.255.255.128"
KORSEBY="10.1.1.1"

# erlaubte Verbindungen vom Internet
# Syntax:     IP#fromport#destport
INET_ALLOW_HOST_TCP=""
INET_ALLOW_HOST_UDP=""

# DNAT Port-Forwards für INET setzen
# Syntax:      Dest-IP#sport1:sport2#dport1-dport2
INET_TCP_FW=""
INET_UDP_FW=""

# gesamte Interfaces, die verwaltet werden sollen
INTERFACES="$INETIF"

# Alle Input/Output Chains festlegen
for iface in $INTERFACES ; do
	CHAINS="$CHAINS ${iface}_IN ${iface}_OUT"
done

# spezielle FORWARD-Chains festlegen
CHAINS="$CHAINS ${INETIF}_FORWARD_IN ${INETIF}_FORWARD_OUT"

# bogus addresses
CHAINS="$CHAINS BOGUS_SRC BOGUS_DST LOGREJECT"
BOGONS="0.0.0.0/8 1.0.0.0/8 2.0.0.0/8 5.0.0.0/8 7.0.0.0/8 10.0.0.0/8 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.0.2.0/24 192.168.0.0/16"

# hier alle Ports eintragen, die geloggt werden sollen
LOGPORTS="20:21#FTP 22#SSH 23#TELNET 80#HTTP 111#PORTMAP 113#AUTH 515#printer 1051#MS_HTTP 1080#SOCKS 1433:1434#MS_SQL 3128#PROXY 4000:4001#ICQ 4661:4669#EDONKEY 6000:6101#X-WINDOW 6667#IRC 8080#PROXY 27374#ADDR_SRCH"

# hier alle ICMP-Typen, die explizit geloggt werden sollen
LOGICMP="0#echo-reply 5#redirect 8#echo-requ"

# hier alle Ports eintragen, die nicht geloggt werden sollen (sportrange#dportrange)
DONTLOG="1024:#4662 1024:#44662:44665 4662#32768:"

# Mangle-Optimization (Syntax: Port#TOS) (siehe auch "iptables -j TOS -h")
MANGLE_OPTIMIZE="20#Minimize-Cost 21#Maximize-Throughput 22#Minimize-Delay 23#Minimize-Delay 25#Minimize-Delay 53#Minimize-Delay 80#Maximize-Throughput"

# Zeitliche Limits setzen
LOG_FLOOD="20/m"
SYN_FLOOD="2/s"
PING_FLOOD="2/s"

# Loglevel Definitonen
info="6"
warning="4"
crit="2"



# ------------------------------ error -------------------------------
function error() {
	echo "$@"
	exit 1
}



# ------------------------------ sysctl ------------------------------
function sysctl() {
        if [ "$1" = "set" ] ; then
		# INETIF has dynamic ip addresses
		$SYSCTL -w net/ipv4/ip_dynaddr=1 1>/dev/null

		# enable FORWARDING
		$SYSCTL -w net/ipv4/ip_forward=2 1>/dev/null

		$SYSCTL -w net/ipv4/conf/all/forwarding=1 1>/dev/null
		$SYSCTL -w net/ipv4/conf/${LANIF}/forwarding=1 1>/dev/null
		$SYSCTL -w net/ipv4/conf/${INETIF}/forwarding=1 1>/dev/null

		# FORWARDING: redirects
		$SYSCTL -w net/ipv4/conf/all/accept_redirects=0 1>/dev/null
		$SYSCTL -w net/ipv4/conf/${LANIF}/accept_redirects=0 1>/dev/null
		$SYSCTL -w net/ipv4/conf/${INETIF}/accept_redirects=0 1>/dev/null

		$SYSCTL -w net/ipv4/conf/all/accept_source_route=1 1>/dev/null
		$SYSCTL -w net/ipv4/conf/${LANIF}/accept_source_route=1 1>/dev/null
		$SYSCTL -w net/ipv4/conf/${INETIF}/accept_source_route=1 1>/dev/null
		
		$SYSCTL -w net/ipv4/conf/all/secure_redirects=1 1>/dev/null
		$SYSCTL -w net/ipv4/conf/${LANIF}/secure_redirects=1 1>/dev/null
		$SYSCTL -w net/ipv4/conf/${INETIF}/secure_redirects=1 1>/dev/null
		
		$SYSCTL -w net/ipv4/conf/all/send_redirects=0 1>/dev/null
		$SYSCTL -w net/ipv4/conf/${LANIF}/send_redirects=0 1>/dev/null
		$SYSCTL -w net/ipv4/conf/${INETIF}/send_redirects=0 1>/dev/null

		# LOG packets with unknown sourceroutes
		$SYSCTL -w net/ipv4/conf/${INETIF}/log_martians=1 1>/dev/null

		# syn & spoofing protection
		$SYSCTL -w net/ipv4/conf/${INETIF}/rp_filter=1 1>/dev/null
	else
		# disable FORWARDING
		$SYSCTL -w net/ipv4/ip_dynaddr=0 1>/dev/null
		$SYSCTL -w net/ipv4/ip_forward=0 1>/dev/null
		$SYSCTL -w net/ipv4/conf/all/forwarding=0 1>/dev/null
		$SYSCTL -w net/ipv4/conf/all/accept_source_route=0 1>/dev/null
	fi
}



# ------------------------------ logdrop -----------------------------
function logdrop() {
	echo "logdrop does nothing more at the moment."
}



# ----------------------------- logreject ----------------------------
function logreject() {
	# freenet SOCKS
	$IPTABLES -A LOGREJECT -p tcp -s 194.97.2.2 --dport 1080 -m limit --limit $LOG_FLOOD -j LOG --log-level $info --log-prefix "freenet SOCKS Rejected "
	$IPTABLES -A LOGREJECT -p tcp -s 194.97.2.2 --dport 1080 -j REJECT --reject-with icmp-port-unreachable

	# freenet AUTH
	$IPTABLES -A LOGREJECT -p tcp -s 194.97.2.2 --dport 113 -m limit --limit $LOG_FLOOD -j LOG --log-level $info --log-prefix "freenet AUTH Rejected "
	$IPTABLES -A LOGREJECT -p tcp -s 194.97.2.2 --dport 113 -j REJECT --reject-with icmp-port-unreachable

	# freenet PROXY
	$IPTABLES -A LOGREJECT -p tcp -s 194.97.2.2 --dport 3128 -m limit --limit $LOG_FLOOD -j LOG --log-level $info --log-prefix "freenet PROXY Rejected "
	$IPTABLES -A LOGREJECT -p tcp -s 194.97.2.2 --dport 3128 -j REJECT --reject-with icmp-port-unreachable

	# freenet PROXY
	$IPTABLES -A LOGREJECT -p tcp -s 194.97.2.2 --dport 8080 -m limit --limit $LOG_FLOOD -j LOG --log-level $info --log-prefix "freenet PROXY Rejected "
	$IPTABLES -A LOGREJECT -p tcp -s 194.97.2.2 --dport 8080 -j REJECT --reject-with icmp-port-unreachable

	# not matched
	$IPTABLES -A LOGREJECT -m limit --limit $LOG_FLOOD -j LOG --log-level $info --log-prefix "other Rejected "
	$IPTABLES -A LOGREJECT -j REJECT --reject-with icmp-port-unreachable
}



# --------------------------- inetif_input ---------------------------
function inetif_input() {
	# DROP bad packets (NULL,ALL,fragments,invalid)
	$IPTABLES -A ${INETIF}_IN -p tcp --tcp-flags ALL NONE -j LOGDROP
	$IPTABLES -A ${INETIF}_IN -p tcp --tcp-flags ALL FIN,URG,PSH -j LOGDROP
	$IPTABLES -A ${INETIF}_IN -f -j LOGDROP
	$IPTABLES -A ${INETIF}_IN -m state --state INVALID -j DROP

	# filter out bogus addresses
	$IPTABLES -A ${INETIF}_IN -j BOGUS_SRC
	$IPTABLES -A ${INETIF}_IN -j BOGUS_DST

	# allow establ./related connections on ports>=32768 only (linux>=2.4 needed)
	$IPTABLES -A ${INETIF}_IN -p tcp ! --syn --dport 32768: -m state --state ESTABLISHED,RELATED -j ACCEPT
	$IPTABLES -A ${INETIF}_IN -p udp --dport 32768: -m state --state ESTABLISHED,RELATED -j ACCEPT

	# allow specific ICMP
	$IPTABLES -A ${INETIF}_IN -p icmp --icmp-type echo-reply -j ACCEPT
	$IPTABLES -A ${INETIF}_IN -p icmp --icmp-type destination-unreachable -j ACCEPT

	# allow possible TCP connections
	if [ "$INET_ALLOW_HOST_TCP" != "" ] ; then
		for rule in $INET_ALLOW_HOST_TCP ; do
			echo "$rule" | {
				IFS='#' read host sport dport
				$IPTABLES -A ${INETIF}_IN -p tcp -s $host --sport $sport --dport $dport -j ACCEPT
			}
		done
	fi

	# allow possible UDP connections
	if [ "$INET_ALLOW_HOST_UDP" != "" ] ; then
		for rule in $INET_ALLOW_HOST_UDP ; do
			echo "$rule" | {
				IFS='#' read host sport dport
				$IPTABLES -A ${INETIF}_IN -p udp -s $host --sport $sport --dport $dport -j ACCEPT
			}
		done
	fi

	# Reject freenet Queries
	$IPTABLES -A ${INETIF}_IN -p tcp -s 194.97.2.2 -j LOGREJECT

	# DROP not matched INETIF-INPUT
	$IPTABLES -A ${INETIF}_IN -j LOGDROP
}



# --------------------------- inetif_output ---------------------------
function inetif_output() {
	# DROP bad packets (NULL,ALL,fragments,invalid)
	$IPTABLES -A ${INETIF}_OUT -p tcp --tcp-flags ALL NONE -j LOGDROP
	$IPTABLES -A ${INETIF}_OUT -p tcp --tcp-flags ALL FIN,URG,PSH -j LOGDROP
	$IPTABLES -A ${INETIF}_OUT -f -j LOGDROP
	$IPTABLES -A ${INETIF}_OUT -m state --state INVALID -j DROP

# TODO: this line should be depricated !!
##	# allow DNS
##	$IPTABLES -A ${INETIF}_OUT -p udp --sport 53 --dport 53 -j ACCEPT

	# filter out bogus addresses
	$IPTABLES -A ${INETIF}_OUT -j BOGUS_SRC
	$IPTABLES -A ${INETIF}_OUT -j BOGUS_DST

	# allow connections from ports>=32768 only (linux>=2.4 needed)
	$IPTABLES -A ${INETIF}_OUT -p tcp --sport 32768: -j ACCEPT
	$IPTABLES -A ${INETIF}_OUT -p udp --sport 32768: -j ACCEPT

	# allow specific ICMP
	$IPTABLES -A ${INETIF}_OUT -p icmp -j ACCEPT

	# DROP not matched INETIF-OUTPUT
	$IPTABLES -A ${INETIF}_OUT -j LOGDROP
}



# ------------------------ inetif_forward_in --------------------------
function inetif_forward_in() {
	# DROP bad packets (NULL,ALL,fragments,invalid)
        $IPTABLES -A ${INETIF}_FORWARD_IN -p tcp --tcp-flags ALL NONE -j LOGDROP
	$IPTABLES -A ${INETIF}_FORWARD_IN -p tcp --tcp-flags ALL FIN,URG,PSH -j LOGDROP
	$IPTABLES -A ${INETIF}_FORWARD_IN -f -j LOGDROP
	$IPTABLES -A ${INETIF}_FORWARD_IN -m state --state INVALID -j DROP

	# filter out bogus addresses
	$IPTABLES -A ${INETIF}_FORWARD_IN -j BOGUS_SRC

	# allow stabl./related forwarding to ports>=32768 only (linux>=2.4 needed)
	$IPTABLES -A ${INETIF}_FORWARD_IN -p tcp ! --syn -d $LAN --dport 32768: -m state --state ESTABLISHED,RELATED -j ACCEPT
	$IPTABLES -A ${INETIF}_FORWARD_IN -p udp -d $LAN --dport 32768: -m state --state ESTABLISHED,RELATED -j ACCEPT

	# DROP not matched forwarding
	$IPTABLES -A ${INETIF}_FORWARD_IN -j LOGDROP
}



# ------------------------ inetif_forward_out -------------------------
function inetif_forward_out() {
	# DROP bad packets (NULL,ALL,fragments,invalid)
	$IPTABLES -A ${INETIF}_FORWARD_OUT -p tcp --tcp-flags ALL NONE -j LOGDROP
	$IPTABLES -A ${INETIF}_FORWARD_OUT -p tcp --tcp-flags ALL FIN,URG,PSH -j LOGDROP
	$IPTABLES -A ${INETIF}_FORWARD_OUT -f -j LOGDROP
	$IPTABLES -A ${INETIF}_FORWARD_OUT -m state --state INVALID -j DROP
	
	# filter out bogus addresses
	$IPTABLES -A ${INETIF}_FORWARD_OUT -j BOGUS_DST

	# allow forwarding from ports>=32768 only (linux>=2.4 needed)
	$IPTABLES -A ${INETIF}_FORWARD_OUT -p tcp -s $LAN --sport 32768: -j ACCEPT
	$IPTABLES -A ${INETIF}_FORWARD_OUT -p udp -s $LAN --sport 32768: -j ACCEPT

	# DROP not matched forwarding
	$IPTABLES -A ${INETIF}_FORWARD_OUT -j LOGDROP
}



# ------------------------------- start ------------------------------
function inout() {
	# check whether to add or delete rules from INPUT or OUTPUT
	if [ "$1" != "ADD" ] ; then
		method="-D"
		rulenum=""
	else
		method="-I"
		rulenum="6 "
	fi

	# ADD or DELETE rules in chains INPUT, OUTPUT
	if [ "$METHOD" != "MASQ" ] ; then
		# redirect $INETIF input to chain ${INETIF}_IN
		$IPTABLES -t filter $method INPUT $rulenum -p icmp  -i $INETIF -d $INETIP -j ${INETIF}_IN
		$IPTABLES -t filter $method INPUT $rulenum -p udp   -i $INETIF -d $INETIP -j ${INETIF}_IN
		$IPTABLES -t filter $method INPUT $rulenum -p tcp   -i $INETIF -d $INETIP -j ${INETIF}_IN

		# redirect $INETIF output to chain ${INETIF}OUT
		$IPTABLES -t filter $method OUTPUT $rulenum -p icmp -o $INETIF -s $INETIP -j ${INETIF}_OUT
		$IPTABLES -t filter $method OUTPUT $rulenum -p udp  -o $INETIF -s $INETIP -j ${INETIF}_OUT
		$IPTABLES -t filter $method OUTPUT $rulenum -p tcp  -o $INETIF -s $INETIP -j ${INETIF}_OUT
	else
		# redirect $INETIF input to chain ${INETIF}_IN
		$IPTABLES -t filter $method INPUT $rulenum -p icmp  -i $INETIF -j ${INETIF}_IN
		$IPTABLES -t filter $method INPUT $rulenum -p udp   -i $INETIF -j ${INETIF}_IN
		$IPTABLES -t filter $method INPUT $rulenum -p tcp   -i $INETIF -j ${INETIF}_IN

		# redirect $INETIF output to chain ${INETIF}_OUT
		$IPTABLES -t filter $method OUTPUT $rulenum -p icmp -o $INETIF -j ${INETIF}_OUT
		$IPTABLES -t filter $method OUTPUT $rulenum -p udp  -o $INETIF -j ${INETIF}_OUT
		$IPTABLES -t filter $method OUTPUT $rulenum -p tcp  -o $INETIF -j ${INETIF}_OUT
	fi
}



# ------------------------------- start ------------------------------
function start() {
	# ---------- create chains ----------

	# delete all rules
	$IPTABLES -F FORWARD

	$IPTABLES -t nat -F PREROUTING
	$IPTABLES -t nat -F POSTROUTING

	if [ "$MANGLE" != "NO" ] ; then
		$IPTABLES -t mangle -F PREROUTING
		$IPTABLES -t mangle -F OUTPUT
	fi

	# create new chains (after deleting them first)
	for chain in $CHAINS ; do
		$IPTABLES -F $chain 2>/dev/null
		$IPTABLES -X $chain 2>/dev/null
		$IPTABLES -t filter -N $chain
	done

	# ---------- default things ----------

	# default policies
	$IPTABLES -P FORWARD DROP

	# add $INETIF rules to INPUT, OUTPUT
	inout ADD

	# Redirect FORWARD OUTPUT from ${INETIF} -> ${LAN} via ${INETIF}_FORWARD_IN
	$IPTABLES -t filter -A FORWARD -p tcp  -i $INETIF -o $LANIF -d $LAN -j ${INETIF}_FORWARD_IN
	$IPTABLES -t filter -A FORWARD -p udp  -i $INETIF -o $LANIF -d $LAN -j ${INETIF}_FORWARD_IN
	$IPTABLES -t filter -A FORWARD -p icmp -i $INETIF -o $LANIF -d $LAN -j ${INETIF}_FORWARD_IN

	# Redirect FORWARD INPUT from ${LAN} -> ${INETIF} via ${INETIF}_FORWARD_OUT
	$IPTABLES -t filter -A FORWARD -p tcp  -i $LANIF -s $LAN -o $INETIF -j ${INETIF}_FORWARD_OUT
	$IPTABLES -t filter -A FORWARD -p udp  -i $LANIF -s $LAN -o $INETIF -j ${INETIF}_FORWARD_OUT
	$IPTABLES -t filter -A FORWARD -p icmp -i $LANIF -s $LAN -o $INETIF -j ${INETIF}_FORWARD_OUT

	# LOGGING chains
	logdrop
	logreject

	# ---------- INPUT ----------

	# chain ${INETIF}_IN
	inetif_input

	# ---------- OUTPUT ----------

	# chain ${INETIF}_OUT
	inetif_output

	# ---------- FORWARDING ----------

	# chains ${INETIF}_FORWARD_(IN|OUT)
	inetif_forward_in
	inetif_forward_out
	
	# DROP not matched FORWARDING
	$IPTABLES -A FORWARD -m limit --limit $LOG_FLOOD -j LOG --log-level $crit --log-prefix "UNKNOWN FORWARD DROPPED "
	$IPTABLES -A FORWARD -j DROP

	# ---------- NAT / Masquerading ----------

	if [ "$METHOD" != "MASQ" ] ; then $IPTABLES -t nat -A POSTROUTING -o $INETIF -j SNAT --to $INETIP
				     else $IPTABLES -t nat -A POSTROUTING -o $INETIF -j MASQUERADE
	fi

	# TCP Port-Forwards
	# Note: Masquerading-Port-Forwarding could be insecure. Spoofed addresses would be welcome.
	if [ "$INET_TCP_FW" != "" ] ; then
		for rule in ${INET_TCP_FW} ; do
			echo "$rule" | {
				IFS='#' read host sport dport
				if [ "$METHOD" != "MASQ" ] ; then $IPTABLES -t nat -A PREROUTING -p tcp -i $INETIF -d $INETIP --dport $sport -j DNAT --to-destination ${host}:${dport}
							     else $IPTABLES -t nat -A PREROUTING -p tcp -i $INETIF --dport $sport -j DNAT --to-destination ${host}:${dport}
				fi
				# ACCEPT & insert as 6th rule into ${INETIF}_FORWARD_IN
				$IPTABLES -I ${INETIF}_FORWARD_IN 6 -p tcp -i $INETIF -d ${host} --dport $dport -j ACCEPT
			}
		done
	fi

	# UDP Port-Forwards
	# Note: MASQ-Port-Forwarding could be insecure. Spoofed addresses would be welcome.
	if [ "$INET_UDP_FW" != "" ] ; then
		for rule in ${INET_UDP_FW} ; do
			echo "$rule" | {
				IFS='#' read host sport dport
				if [ "$METHOD" != "MASQ" ] ; then $IPTABLES -t nat -A PREROUTING -p udp -i $INETIF -d $INETIP --dport $sport -j DNAT --to-destination ${host}:${dport}
							     else $IPTABLES -t nat -A PREROUTING -p udp -i $INETIF --dport $sport -j DNAT --to-destination ${host}:${dport}
				fi
				# ACCEPT & insert as 6th rule into ${INETIF}_FORWARD_IN
				$IPTABLES -I ${INETIF}_FORWARD_IN 6 -p udp -i $INETIF -d ${host} --dport $dport -j ACCEPT
			}
		done
	fi

	# ---------- Mangling ----------

	if [ "$MANGLE" != "NO" ] ; then
		for rule in $MANGLE_OPTIMIZE ; do
			echo "$rule" | {
				IFS='#' read port tos
				protocol="tcp udp"
				for proto in $protocol ; do
					$IPTABLES -t mangle -A OUTPUT -p ${proto} --dport $port -j TOS --set-tos $tos
					$IPTABLES -t mangle -A PREROUTING -p ${proto} --dport $port -j TOS --set-tos $tos
				done
			}
		done
	fi

	# ---------- create BOGONS ----------

	for address in $BOGONS ; do
		$IPTABLES -A BOGUS_SRC -s $address -j LOG --log-level $warning --log-prefix 'BOGUS_SRC Dropped '
		$IPTABLES -A BOGUS_SRC -s $address -j DROP
		$IPTABLES -A BOGUS_DST -d $address -j LOG --log-level $warning --log-prefix 'BOGUS_DST Dropped '
		$IPTABLES -A BOGUS_DST -d $address -j DROP
	done
}



# ----------------------------- main part ----------------------------
case "$1" in

  start)
	echo "Starting iptables firewall..."

	# check for iptables
	[ -x $IPTABLES ] || error "iptables ($IPTABLES) not found !"

	# check if interfaces exist
	for iface in $INTERFACES; do
		ifconfig $iface &>/dev/null || error "No Interface $iface found."
	done

	# check for sysctl
	[ -x $SYSCTL ] || error "sysctl ($SYSCTL) not found !"

	# load modules
	if [ "$MANGLE" != "NO" ] ; then
		modprobe iptable_mangle
	fi
	if [ "$METHOD" != "MASQ" ] ; then
		modprobe iptable_nat
	else
		modprobe ipt_MASQUERADE
	fi
	modprobe ipt_REJECT
	modprobe ipt_TOS

	# sysctl
	sysctl set

	# start
	start
	;;

  stop)
	echo "Stopping iptables firewall..."

	# flush built-in chains
	$IPTABLES -F FORWARD
	$IPTABLES -t nat -F PREROUTING
	$IPTABLES -t nat -F POSTROUTING
	$IPTABLES -t mangle -F OUTPUT
	$IPTABLES -t mangle -F PREROUTING

	# delete $INETIF references from INPUT, OUTPUT
	inout DELETE

	# flush,delete chains
	for chain in $CHAINS ; do
		$IPTABLES -F $chain
		$IPTABLES -X $chain
	done

	# default policy
	$IPTABLES -P FORWARD DROP

	# unload modules
	rmmod ipt_TOS
	if [ "$METHOD" != "MASQ" ] ; then
		rmmod iptable_nat
	else
		rmmod ipt_MASQUERADE
	fi
	rmmod iptable_mangle
	rmmod ipt_REJECT

	# sysctl
	sysctl unset
	;;

  restart)
	$0 stop
	$0 start
	;;

  status)
	$IPTABLES -L -n -v
	;;

  test)
	$0 start
	$0 stop
	;;

  *)

	echo "Usage: $0 {start|stop|restart|status}"
	exit 1

esac

exit $?

