#!/bin/sh
#
# firewall   : Open/Close firewall
# 
# chkconfig: 2345 11 89
# description: Start/Stop the firewall
#
# Miguel Armas <kuko@optyma.net>
# $Id: firewall,v 1.2 2002/02/21 14:22:58 cvs Exp $

# iptables command
IPT=/sbin/iptables

# Config file
CFG=/etc/firewall.conf

# Look for connection tracking modules
CONTRACK_MODS=`/sbin/modprobe -l -tipv4/netfilter | grep conntrack \
              | sed "s|^.*/||" | sed "s/.o$//"`

# Look for nat modules
NAT_MODS=`/sbin/modprobe -l -tipv4/netfilter | grep nat \
              | sed "s|^.*/||" | sed "s/.o$//"`

# FUNCTION: validate()
# DESCRIPCION: Validate a port string from the config file
function validate {
   token=$1
   type=$2
   sip="";sport="";dip="";dport="";proto=""
   
   # * means "all", so substitute * with ""
   token=`echo $token | sed 's/*//g'`
   # Do we have src-dst?
   if echo $token | grep -q "-"; then
      src=`echo $token | cut -d- -f1` 
      dst=`echo $token | cut -d- -f2` 
   else
      src=""
      dst="$token"
   fi
   
   if echo $src | grep -q ":"; then
      sip=`echo $src | cut -d: -f1`
      sport=`echo $src | cut -d: -f2`
   else
      sip="$src"
      sport=""
   fi
   if echo $dst | grep -q ":"; then
      dip=`echo $dst | cut -d: -f1`
      dport=`echo $dst | cut -d: -f2`
   else
      dip=""
      dport="$dst"
   fi
   if [ "x$sip" != "x" ]; then
      # In NAT rules, $sip is the original destination ip
      if [ "x$type" == "xNAT" -o "x$type" == "xNPROTO" ]; then
          sip="-d $sip"
      else
          sip="-s $sip"
      fi
   fi
   if [ "x$sport" != "x" ]; then
      # In NAT rules, $sport is the original destination port
      if [ "x$type" == "xNAT" ]; then
         sport="--dport $sport"
      # In proto rules, sport doesn't make sense...
      elif [ "x$type" == "xPROTO" -a "x$type" == "xNPROTO" ]; then
         echo "ERROR: Source port in protocol rule ($token)"
         sport=""
      else
         sport="--sport $sport"
      fi
   fi
   if [ "x$dip" != "x" ]; then
      if [ "x$type" != "xNAT" -a "x$type" != "xNPROTO" ]; then
         dip="-d $dip"
      fi
   else
      if [ "x$type" == "xNAT" -o "x$type" == "xNPROTO" ]; then
         echo "ERROR: dest IP is MANDATORY in NAT rules ($token)"
	 return 1
      fi
   fi
   if [ "x$dport" != "x" ]; then
      if [ "x$type" != "xNAT" -a "x$type" != "xPROTO" -a "x$type" != "xNPROTO" ]; then
	 dport="--dport $dport"
      fi
   else
      if [ "x$type" == "xNAT" ]; then
         echo "ERROR: dest port is MANDATORY in NAT rules ($token)"
	 return 1
      elif [ "x$type" == "xPROTO" -o "x$type" == "xNPROTO" ]; then
         echo "ERROR: proto is MANDATORY in PROTOCOL rules ($token)"
      fi
      
   fi
   return 0
}

# Do we have a config file?
if [ ! -f $CFG ]; then
   echo "Can't read config file: $CFG"
   exit 1
fi

# Read config file
. $CFG

# Load iptables module
#modprobe ip_tables

case "$1" in
  start)
  
    # Source Address Verification (spoof protection)
    if [ -e /proc/sys/net/ipv4/conf/all/rp_filter ]; then
      echo -n "Activating IP Spoof Protection..."
      for f in /proc/sys/net/ipv4/conf/*/rp_filter; do
         echo 1 > $f
      done
      echo "done."
    else
      echo "WARNING! Couldn't activate IP Spoof Protection!!!"
      echo "*************************************************"
    fi
    
    # Enable ip_forward
    echo 1 > /proc/sys/net/ipv4/ip_forward
    
    # Load connection tracking modules
    for mod in $CONTRACK_MODS; do
       echo "Loading $mod"
       modprobe $mod
    done
 
    # If we are doing NAT, load aditional nat modules
    if [ "x$NAT" != "xn" ]; then
       for mod in $NAT_MODS; do
          echo "Loading $mod"
	  modprobe $mod
       done
    fi

    # Set policies to reject all while we are configuring the filters
    $IPT -P INPUT  DROP
    $IPT -P OUTPUT DROP
    $IPT -P FORWARD DROP
    
    # Flush the basic tables
    $IPT -F INPUT
    $IPT -F OUTPUT
    $IPT -F FORWARD
    
    # Flush the NAT tables
    $IPT -t nat -F PREROUTING
    $IPT -t nat -F POSTROUTING
    $IPT -t nat -F OUTPUT
    
    ############################################
    ## INTERNAL INTERFACES (Allow everything) ##
    ############################################
    echo "Configuring filters in internal interfaces"
    # Allow everything 
    for dev in $INTERNDEV; do
        echo "       - Accept trafic from/to $dev"
	$IPT -A INPUT -i $dev -j ACCEPT
	$IPT -A OUTPUT -o $dev -j ACCEPT
	# Allow forwarding from any internat interface
	for idev in $INTERNDEV; do
	    $IPT -A FORWARD -i $idev -o $dev -j ACCEPT
        done
    done
    
    ##################################
    ## EXTERNAL INTERFACES (filter) ##
    ##################################
    for dev in $EXTERNDEV; do
       echo "Configuring filters in interface: $dev"
       
       #####################################
       ## LOCAL TRAFFIC (in the firewall) ##
       #####################################
       ##
       ## LOCAL_PROTO_IN
       ##
       if [ "x$LOCAL_PROTO_IN" != "x" ]; then
          echo "    - Applying LOCAL_PROTO_IN to $dev"
	  for proto in $LOCAL_PROTO_IN; do
	      if validate $proto PROTO; then
		 $IPT -A INPUT -i $dev -p $dport $sip $dip -j ACCEPT
	         $IPT -A OUTPUT -o $dev -p $dport $sip $dip -j ACCEPT
	      fi
	  done
       fi
       ##
       ## LOCAL_UDP_LOG
       ##
       # Log access to local (firewall) UDP ports
       if [ "x$LOCAL_UDP_LOG" != "x" ]; then
          echo "    - Applying LOCAL_UDP_LOG to $dev"
	  for port in $LOCAL_UDP_LOG; do
	      if validate $port; then
	         echo "UDP: $sip $sport $dip $dport"
	         $IPT -A INPUT -i $dev -p udp $sip $sport $dip $dport \
		      -j LOG --log-prefix "LOCAL UDP($port) IN: "
	      fi
	  done
       fi
       ##
       ## LOCAL_UDP_IN
       ##
       # Allow incoming local UDP connections to the given ports (in 
       # the firewall)
       if [ "x$LOCAL_UDP_IN" != "x" ]; then
          echo "    - Applying LOCAL_UDP_IN to $dev"
	  for port in $LOCAL_UDP_IN; do
	      if validate $port; then
	         $IPT -A INPUT -i $dev -p udp $sip $sport $dip $dport \
		      -j ACCEPT
	      fi
	  done
       fi
       # Allow incoming traffic for established sessions
       $IPT -A INPUT -i $dev -p udp -m state --state ESTABLISHED,RELATED -j ACCEPT
       # Block any other incoming traffic
       if [ "x$LOG" != "x" ]; then
          $IPT -A INPUT -i $dev -p udp -j LOG --log-prefix "LOCAL UDP IN: "
       fi
       $IPT -A INPUT -i $dev -p udp -j DROP
       ##
       ## LOCAL_UDP_OUT
       ##
       # If we have defined LOCAL_UDP_OUT allow outgoing connections 
       # only to those ports
       if [ "x$LOCAL_UDP_OUT" != "x" ]; then
          echo "    - Applying LOCAL_UDP_OUT to $dev"
          for port in $LOCAL_UDP_OUT; do
	      if validate $port; then
	         $IPT -A OUTPUT -o $dev -p udp $sip $sport $dip $dport \
		      -j ACCEPT
	      fi
	  done
       else
          # If we didn't define EXT_LOC_UDP_SRV, allow everything
          $IPT -A OUTPUT -o $dev -p udp -j ACCEPT
       fi
       # Allow outgoing related packets
       $IPT -A OUTPUT -o $dev -p udp -m state --state ESTABLISHED,RELATED -j ACCEPT
       # Block any other UDP outgoing traffic
       if [ "x$LOG" != "x" ]; then
          $IPT -A OUTPUT -o $dev -p udp -j LOG --log-prefix "LOCAL UDP OUT: "
       fi
       $IPT -A OUTPUT -o $dev -p udp -j DROP
       
       ##
       ## LOCAL_TCP_LOG
       ##
       # Log access to local (firewall) TCP ports
       if [ "x$LOCAL_TCP_LOG" != "x" ]; then
          echo "    - Applying LOCAL_TCP_LOG to $dev"
	  for port in $LOCAL_TCP_LOG; do
	      if validate $port; then
	         $IPT -A INPUT -i $dev -p tcp --syn $sip $sport $dip $dport \
		      -j LOG --log-prefix "LOCAL TCP($port) IN: " 
	      fi
	  done
       fi
       ##
       ## LOCAL_TCP_IN
       ##
       # Allow incoming local TCP connections to the given ports (in 
       # the firewall)
       if [ "x$LOCAL_TCP_IN" != "x" ]; then
          echo "    - Applying LOCAL_TCP_IN to $dev"
	  for port in $LOCAL_TCP_IN; do
	      if validate $port; then
	         $IPT -A INPUT -i $dev -p tcp --syn $sip $sport $dip $dport \
	              -j ACCEPT
	      fi
	  done
       fi
       # Allow incoming traffic for established sessions
       $IPT -A INPUT -i $dev -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT
       # Block any other incoming traffic
       if [ "x$LOG" != "x" ]; then
          $IPT -A INPUT -i $dev -p tcp -j LOG --log-prefix "LOCAL TCP IN: "
       fi
       $IPT -A INPUT -i $dev -p tcp -j DROP

       ##
       ## LOCAL_TCP_OUT
       ##
       # If we have defined LOCAL_TCP_OUT allow outgoing connections 
       # only to those ports
       if [ "x$LOCAL_TCP_OUT" != "x" ]; then
          echo "    - Applying LOCAL_TCP_OUT to $dev"
          for port in $LOCAL_TCP_OUT; do
	      if validate $port; then
	         $IPT -A OUTPUT -o $dev -p tcp --syn $sip $sport $dip $dport \
		      -j ACCEPT
	      fi
	  done
       else
          # If we didn't define EXT_LOC_TCP_SRV, allow everything
          $IPT -A OUTPUT -o $dev -p tcp --syn -j ACCEPT
       fi
       # Allow outgoing traffic for established sessions
       $IPT -A OUTPUT -o $dev -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT
       # Block any other TCP outgoing traffic
       if [ "x$LOG" != "x" ]; then
          $IPT -A OUTPUT -o $dev -p tcp -j LOG --log-prefix "LOCAL TCP OUT: "
       fi
       $IPT -A OUTPUT -o $dev -p tcp -j DROP


       ####################################
       ## NETWORK TRAFFIC (not firewall) ##
       ####################################
       ##
       ## NETWORK_PROTO_IN
       ##
       if [ "x$NETWORK_PROTO_IN" != "x" ]; then
          echo "    - Applying NETWORK_PROTO_IN to $dev"
	  for proto in $NETWORK_PROTO_IN; do
	      if validate $proto PROTO; then
		 $IPT -A FORWARD -i $dev -p $dport $sip $dip -j ACCEPT
	         $IPT -A FORWARD -o $dev -p $dport $sip $dip -j ACCEPT
	      fi
	  done
       fi
       ##
       ## NETWORK_UDP_LOG
       ##
       # Log access to internal TCP ports
       if [ "x$NETWORK_UDP_LOG" != "x" ]; then
          echo "    - Applying NETWORK_UDP_LOG to $dev"
	  for port in $NETWORK_UDP_LOG; do
	      if validate $port; then
	         $IPT -A FORWARD -i $dev -p udp $sip $sport $dip $dport \
		      -j LOG --log-prefix "NETWORK UDP($port) IN: "
	      fi
	  done
       fi
       ##
       ## NETWORK_UDP_IN
       ##
       # Allow incoming UDP packets to the given ports
       if [ "x$NETWORK_UDP_IN" != "x" ]; then
          echo "    - Applying NETWORK_UDP_IN to $dev"
	  for port in $NETWORK_UDP_IN; do
	      if validate $port; then
	         $IPT -A FORWARD -i $dev -p udp $sip $sport $dip $dport \
		      -j ACCEPT
	      fi
	  done
       fi
       # Allow incoming traffic for established sessions
       $IPT -A FORWARD -i $dev -p udp -m state --state ESTABLISHED,RELATED -j ACCEPT
       # Block any other incoming traffic
       if [ "x$LOG" != "x" ]; then
          $IPT -A FORWARD -i $dev -p udp -j LOG --log-prefix "NETWORK UDP INT: "
       fi
       $IPT -A FORWARD -i $dev -p udp -j DROP
       ##
       ## NETWORK_UDP_OUT
       ##
       # If we have defined NETWORK_UDP_OUT allow outgoing connections 
       # only to those ports
       if [ "x$NETWORK_UDP_OUT" != "x" ]; then
          echo "    - Applying NETWORK_UDP_OUT to $dev"
          for port in $NETWORK_UDP_OUT; do
	      if validate $port; then
	         $IPT -A FORWARD -o $dev -p udp $sip $sport $dip $dport \
		      -j ACCEPT
	      fi
	  done
       else
          # If we didn't define EXT_TCP_SRV, allow everything
          $IPT -A FORWARD -o $dev -p udp -j ACCEPT
       fi
       # Allow outgoing related packets
       $IPT -A FORWARD -o $dev -p udp -m state --state ESTABLISHED,RELATED -j ACCEPT
       # Block any other UDP outgoing traffic
       if [ "x$LOG" != "x" ]; then
          $IPT -A FORWARD -o $dev -p udp -j LOG --log-prefix "NETWORK UDP OUT: "
       fi
       $IPT -A FORWARD -o $dev -p udp -j DROP
       
       ##
       ## NETWORK_TCP_LOG
       ##
       # Log access to internal TCP ports
       if [ "x$NETWORK_TCP_LOG" != "x" ]; then
          echo "    - Applying NETWORK_TCP_LOG to $dev"       
	  for port in $NETWORK_TCP_LOG; do
	      if validate $port; then
	         $IPT -A FORWARD -i $dev -p tcp --syn $sip $sport $dip $dport \
		      -j LOG --log-prefix "NETWORK TCP($port) IN: " 
	      fi
	  done
       fi
       ##
       ## NETWORK_TCP_IN
       ##
       # Allow incoming TCP connections to the given ports
       if [ "x$NETWORK_TCP_IN" != "x" ]; then
          echo "    - Applying NETWORK_TCP_IN to $dev"       
	  for port in $NETWORK_TCP_IN; do
	      if validate $port; then
	         $IPT -A FORWARD -i $dev -p tcp --syn $sip $sport $dip $dport \
		      -j ACCEPT
	      fi
	  done
       fi
       # Allow incoming traffic for established sessions
       $IPT -A FORWARD -i $dev -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT
       # Block any other incoming traffic
       if [ "x$LOG" != "x" ]; then
          $IPT -A FORWARD -i $dev -p tcp -j LOG --log-prefix "NETWORK TCP IN: "
       fi
       $IPT -A FORWARD -i $dev -p tcp -j DROP
       ##
       ## NETWORK_TCP_OUT
       ##
       # If we have defined NETWORK_TCP_OUT allow outgoing connections 
       # only to those ports
       if [ "x$NETWORK_TCP_OUT" != "x" ]; then
          echo "    - Applying NETWORK_TCP_OUT to $dev"       
          for port in $NETWORK_TCP_OUT; do
	      if validate $port; then
	         $IPT -A FORWARD -o $dev -p tcp --syn $sip $sport $dip $dport \
		      -j ACCEPT
	      fi
	  done
       else
          # If we didn't define EXT_TCP_SRV, allow everything
          $IPT -A FORWARD -o $dev -p tcp --syn -j ACCEPT
       fi
       # Allow outgoing traffic for established sessions
       $IPT -A FORWARD -o $dev -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT
       # Block any other TCP outgoing traffic
       if [ "x$LOG" != "x" ]; then
          $IPT -A FORWARD -o $dev -p tcp -j LOG --log-prefix "NETWORK TCP OUT: "
       fi
       $IPT -A FORWARD -o $dev -p tcp -j DROP
       
       ## FIXME: 
       ############################
       ## Rules for ICMP traffic ##
       ############################
       echo "    - Filtering ICMP traffic in $dev"
       
       # Specific ICMP types
       for type in $ICMP_LOG_TYPES; do
	   $IPT -A INPUT -i $dev -p icmp --icmp-type $type -j LOG
	   $IPT -A FORWARD -i $dev -p icmp --icmp-type $type -j LOG
       done
       for type in $ICMP_INPUT_TYPES; do
           $IPT -A INPUT -i $dev -p icmp --icmp-type $type -j ACCEPT
           $IPT -A FORWARD -i $dev -p icmp --icmp-type $type -j ACCEPT
       done
       for type in $ICMP_OUTPUT_TYPES; do
           $IPT -A OUTPUT -o $dev -p icmp --icmp-type $type -j ACCEPT   
           $IPT -A FORWARD -o $dev -p icmp --icmp-type $type -j ACCEPT   
       done
       
       # Remaining ICMP types: default action
       $IPT -A INPUT -i $dev -p icmp -j $ICMP_DEFAULT
       $IPT -A OUTPUT -o $dev -p icmp -j $ICMP_DEFAULT
       
       ###############
       ## NAT RULES ##
       ###############
       # Are we doing dynamic SNAT (MASQ)?
       if [ "x$MASQ" != "xn" ]; then
          echo "    - Activating dynamic SNAT (MASQ) in $dev"
          $IPT -t nat -A POSTROUTING -o $dev -j MASQUERADE
       fi
       ##
       ## NAT_PROTO_IN
       ##
       if [ "x$NAT_PROTO_IN" != "x" ]; then
          echo "    - Applying NAT_PROTO_IN to $dev"
	  for proto in $NAT_PROTO_IN; do	
	      if validate $proto NPROTO; then
	         $IPT -t nat -A PREROUTING -i $dev -p $dport $sip \
		       -j DNAT --to-destination $dip
	      fi
	  done
       fi       
       ##
       ## NAT_TCP_IN
       ##
       if [ "x$NAT_TCP_IN" != "x" ]; then
          echo "    - Applying NAT_TCP_IN to $dev"       
	  for port in $NAT_TCP_IN; do
	      if validate $port NAT; then
	         $IPT -t nat -A PREROUTING -i $dev -p tcp $sip $sport \
		       -j DNAT --to-destination $dip:$dport
	      fi
	  done
       fi
       ##
       ## NAT_UDP_IN
       ##
       if [ "x$NAT_UDP_IN" != "x" ]; then
          echo "    - Applying NAT_UDP_IN to $dev"       
	  for port in $NAT_UDP_IN; do
	      if validate $port NAT; then
	         $IPT -t nat -A PREROUTING -i $dev -p udp $sip $sport \
		       -j DNAT --to-destination $dip:$dport
	      fi
	  done
       fi    
    done
    
    ## BLOCK ANYTHING STILL NOT CATCHED
    if [ "x$LOG" != "x" ]; then
       $IPT -A INPUT -j LOG --log-prefix="UNMATCHED IN: "
       $IPT -A OUTPUT -j LOG --log-prefix="UNMATCHED OUT: "
       $IPT -A FORWARD -j LOG --log-prefix="UNMATCHED FWD: "
    fi    
    $IPT -A OUTPUT -j DROP
    $IPT -A INPUT -j DROP
    $IPT -A FORWARD -j DROP
    
    $IPT -P OUTPUT DROP
    $IPT -P INPUT DROP
    $IPT -P FORWARD DROP
    ;;
    
  stop)
    # Flush tables
    $IPT -F INPUT
    $IPT -F OUTPUT
    $IPT -F FORWARD
    
    # Set policies to accept
    $IPT -P INPUT ACCEPT
    $IPT -P OUTPUT ACCEPT
    $IPT -P FORWARD ACCEPT

    # Flush the NAT tables
    $IPT -t nat -F PREROUTING
    $IPT -t nat -F POSTROUTING
    $IPT -t nat -F OUTPUT

    if [ -e /proc/sys/net/ipv4/conf/all/rp_filter ]; then
      echo -n "Deactivating IP Spoof Protection..."
      for f in /proc/sys/net/ipv4/conf/*/rp_filter; do
         echo 0 > $f
      done
      echo "done."
    fi
    ;;
  
  status)
    echo "###############################################"
    echo "############### FILTERING RULES ###############"
    echo "###############################################"
    $IPT -L
    echo
    echo "###############################################"
    echo "###############    NAT RULES    ###############"
    echo "###############################################"
    $IPT -L -t nat
    
    ;;
  restart)
    $0 stop
    $0 start
    ;;
  *)
    echo "usage: $0 {start|stop|status|block|unblock}"
    exit 1
    
esac

exit 0

