#!/bin/sh

MY_VERSION="2.0.3"

# Location of the main configuration file for the firewall
##########################################################
CONFIG_FILE=/etc/arno-iptables-firewall/firewall.conf

# ------------------------------------------------------------------------------
#                           -= Arno's iptables firewall =-
#               Single- & multi-homed firewall script with DSL/ADSL support
#
#                           ~ In memory of my dear father ~
#
# (C) Copyright 2001-2018 by Arno van Amersfoort & Lonnie Abelbeck
# Homepage              : http://rocky.eld.leidenuniv.nl/
# Email                 : a r n o v a AT r o c k y DOT e l d DOT l e i d e n u n i v DOT n l
#                         (note: you must remove all spaces and substitute the @ and the .
#                         at the proper locations!)
# ------------------------------------------------------------------------------
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 2 as published by the Free Software Foundation.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
# ------------------------------------------------------------------------------

printf "\033[40m\033[1;32mArno's Iptables Firewall Script v$MY_VERSION\033[0m\n"
echo "-------------------------------------------------------------------------------"

# Check if the main config file exists and if so load it
########################################################
if [ -e "$CONFIG_FILE" ]; then
  . "$CONFIG_FILE"
else
  printf "\033[40m\033[1;31mERROR: Could not read configuration file $CONFIG_FILE!\033[0m\n" >&2
  printf "\033[40m\033[1;31m       Please, check the file's location and (root) rights.\033[0m\n\n" >&2
  exit 2
fi

# Check if the environment file exists and if so, load it
#########################################################
if [ -z "$ENV_FILE" ]; then
  if [ -f /usr/local/share/arno-iptables-firewall/environment ]; then
    ENV_FILE="/usr/local/share/arno-iptables-firewall/environment"
  else
    if [ -f /usr/share/arno-iptables-firewall/environment ]; then
      ENV_FILE="/usr/share/arno-iptables-firewall/environment"
    else
      printf "\033[40m\033[1;31mERROR: The environment file (ENV_FILE) has not been specified\033[0m\n" >&2
      printf "\033[40m\033[1;31m       in the configuration file. Try upgrading your config-file!\033[0m\n\n" >&2
      exit 2
    fi
  fi
fi

if [ -e "$ENV_FILE" ]; then
  . "$ENV_FILE"
else
  printf "\033[40m\033[1;31mERROR: Could not read environment file \"$ENV_FILE\"!\033[0m\n" >&2
  printf "\033[40m\033[1;31m       Please, check the file's location and (root) rights.\033[0m\n\n" >&2
  exit 2
fi


sanity_check()
{
  # Show uname information
  echo "Platform: $(uname -s -r -m)"

  # root check
  if [ "$(id -u)" != "0" ]; then
    printf "\033[40m\033[1;31mERROR: Root check FAILED (you MUST be root to use this script)! Quitting...\033[0m\n\n" >&2
    exit 1
  fi

  # Check whether the required binaries exist and are executable
  ##############################################################
  check_command_error iptables
  if [ "$IPV6_DETECTED" = "1" ]; then
    check_command_error ip6tables
  fi
  check_command_error awk
  check_command_error tr
  check_command_error ip
  check_command_error cut
  check_command_error uname
  check_command_error sed
  check_command_error cat
  check_command_error date
  check_command_error modprobe
  check_command_error sysctl
  check_command_error head
  check_command_error tail
  check_command_error wc
  check_command_error logger

  check_command_warning dig nslookup

  if [ "$IPV6_SUPPORT" = "1" ]; then
    if ! kernel_ver_chk 2 6 24; then
      printf "\033[40m\033[1;31mWARNING: IPv6 support is enabled but your kernel is rather old (<2.6.24)! This *could* cause problems...\033[0m\n" >&2
    fi
  fi
}


ipset_check()
{
  if [ "$IPTABLES_IPSET" != "1" ]; then
    return 1
  fi

  ## Check if userspace 'ipset' command is installed
  if ! check_command ipset; then
    return 1
  fi

  ## Issue a simple command that will fail without kernel support
  if ! ipset list -n >/dev/null 2>&1; then
    return 1
  fi

  return 0
}


config_check()
{
  local retval=0

  # Make sure EXT_IF != ""
  ########################
  if [ -z "$EXT_IF" ]; then
    printf "\033[40m\033[1;31mERROR: The required variable EXT_IF is empty!\033[0m\n" >&2
    printf "\033[40m\033[1;31m       Please, check the configuration file.\033[0m\n\n" >&2
    retval=1
  fi

  # Check whether EXT_IF exists
  #############################
  IFS=' ,'
  for interface in $EXT_IF; do
    if ! check_interface $interface; then
      printf "\033[40m\033[1;31mWARNING: External interface $interface does NOT exist (yet?)\033[0m\n\n" >&2
    fi
  done

  # Check whether INT_IF exists
  #############################
  IFS=' ,'
  for interface in $INT_IF; do
    if ! check_interface $interface; then
      printf "\033[40m\033[1;31mWARNING: Internal interface $interface does NOT exist (yet?)\033[0m\n\n" >&2
    fi
  done

  # Check whether DMZ_IF exists
  #############################
  IFS=' ,'
  for interface in $DMZ_IF; do
    if ! check_interface $interface; then
      printf "\033[40m\033[1;31mWARNING: DMZ interface $interface does NOT exist (yet?)\033[0m\n\n" >&2
    fi
  done

  # Check whether TRUSTED_IF exists
  #################################
  IFS=' ,'
  for interface in $TRUSTED_IF; do
    if ! check_interface $interface; then
      printf "\033[40m\033[1;31mWARNING: Trusted interface $interface does NOT exist (yet?)\033[0m\n\n" >&2
    fi
  done

  # Make sure INT_IF != EXT_IF
  ############################
  IFS=' ,'
  for eif in $EXT_IF; do
    for iif in $INT_IF; do
      if [ "$iif" = "$eif" ]; then
        printf "\033[40m\033[1;31mERROR: One or more interfaces specified in EXT_IF is the same as one in\033[0m\n" >&2
        printf "\033[40m\033[1;31m       INT_IF! Please, check the configuration file.\033[0m\n\n" >&2
        retval=1
        break
      fi
    done
  done

  # Make sure EXT_IF != lo / 127.0.0.1
  ####################################
  IFS=' ,'
  for eif in $EXT_IF; do
    if [ "$eif" = "lo" -o "$eif" = "127.0.0.1" ]; then
      printf "\033[40m\033[1;31mERROR: One or more interfaces specified in EXT_IF has the address or name of the\033[0m\n" >&2
      printf "\033[40m\033[1;31m       local loopback device! Please, check the configuration file.\033[0m\n\n" >&2
      retval=1
      break
    fi
  done

  # Make sure INT_IF != lo / 127.0.0.1
  ####################################
  IFS=' ,'
  for iif in $INT_IF; do
    if [ "$iif" = "lo" -o "$iif" = "127.0.0.1" ]; then
      printf "\033[40m\033[1;31mERROR: At least one of the interfaces specified in INT_IF has the address or\033[0m\n" >&2
      printf "\033[40m\033[1;31m       name of the local loopback device! Please, check the configuration file.\033[0m\n\n" >&2
      retval=1
      break
    fi
  done

  # If support for an DHCP server serving an external net is enabled, we
  # also need to know what the external net is.
  ##########################################################################
  if [ "$EXTERNAL_DHCP_SERVER" = "1" -a -z "$EXTERNAL_NET" ]; then
    printf "\033[40m\033[1;31mERROR: You have enabled external DHCP server support but required variable\033[0m\n" >&2
    printf "\033[40m\033[1;31m       EXTERNAL_NET has NOT been defined!\033[0m\n\n" >&2
    retval=1
  fi

  # We can only perform NAT if NAT_INTERNAL_NET is defined
  if [ "$NAT" = "1" -a -z "$NAT_INTERNAL_NET" ]; then
    printf "\033[40m\033[1;31mERROR: Unable to enable NAT because there's no (NAT_)INTERNAL_NET specified!\033[0m\n\n" >&2
    retval=1
  fi

  # If support the nmb_broadcast_fix is enabled we need the EXTERNAL_NET set
  ##########################################################################
  if [ "$NMB_BROADCAST_FIX" = "1" -a -z "$EXTERNAL_NET" ]; then
    printf "\033[40m\033[1;31mERROR: You have enabled the NMB_BROADCAST_FIX but required variable\033[0m\n" >&2
    printf "\033[40m\033[1;31m       EXTERNAL_NET has NOT been defined!\033[0m\n\n" >&2
    retval=1
  fi

  # Warn if no_broadcast variables are used and external net is NOT defined
  ##########################################################################
  if [ -n "$BROADCAST_TCP_NOLOG" -o -n "$BROADCAST_UDP_NOLOG" ]; then
    if [ -z "$EXTERNAL_NET" -a -z "$EXT_NET_BCAST_ADDRESS" ]; then
      printf "\033[40m\033[1;31mWARNING: You are using the BROADCAST_xxx_NOLOG variables but EXTERNAL_NET (or EXT_NET_BCAST_ADDRESS)\033[0m\n" >&2
      printf "\033[40m\033[1;31m         has NOT been defined!\033[0m\n\n" >&2
    fi
  fi

  # Check whether we know the plugin binary path
  ##############################################
  if [ ! -d "$PLUGIN_BIN_PATH" ]; then
    printf "\033[40m\033[1;31mERROR: The PLUGIN_BIN_PATH ($PLUGIN_BIN_PATH) does not exist!\033[0m\n" >&2
    printf "\033[40m\033[1;31m       Please check your installation and/or configuration file.\033[0m\n\n" >&2
    retval=1
  fi

  # Check whether we know the plugin config path
  ##############################################
  if [ ! -d "$PLUGIN_CONF_PATH" ]; then
    printf "\033[40m\033[1;31mERROR: The PLUGIN_CONF_PATH ($PLUGIN_CONF_PATH) does not exist!\033[0m\n" >&2
    printf "\033[40m\033[1;31m       Please check your installation and/or configuration file.\033[0m\n\n" >&2
    retval=1
  fi

  # Check for errors
  if [ $retval -ne 0 ]; then
    show_failed
    exit $retval
  fi
}


load_modules()
{
  unset IFS
  # Set indent for functions
  INDENT=' '

  echo "Checking/probing Iptables modules:"

  # Required; all IPv4 modules depend on this one
  modprobe ip_tables
  if [ "$IPV6_SUPPORT" = "1" ]; then
    modprobe ip6_tables
  fi

  # Allows connection tracking state match, which allows you to
  # write rules matching the state of a connection
  modprobe_multi nf_conntrack ip_conntrack
  if [ "$IPV6_SUPPORT" = "1" ]; then
    modprobe nf_conntrack_ipv6
  fi

  # Allows tracking for various protocols, placing entries in the conntrack table etc.
  if [ "$IPV6_SUPPORT" = "1" ]; then
    modprobe_multi xt_conntrack "ipt_conntrack,ip6t_conntrack"
  else
    modprobe_multi xt_conntrack ipt_conntrack
  fi

  # Allows log limits
  if [ "$IPV6_SUPPORT" = "1" ]; then
    modprobe_multi xt_limit "ipt_limit,ip6t_limit"
  else
    modprobe_multi xt_limit ipt_limit
  fi

  # Permits packet state checking (SYN, SYN-ACK, ACK, and so on).
  if [ "$IPV6_SUPPORT" = "1" ]; then
    modprobe_multi xt_state "ipt_state,ip6t_state"
  else
    modprobe_multi xt_state ipt_state
  fi

  # Allows packet specifications on multiple ports
  if [ "$IPV6_SUPPORT" = "1" ]; then
    modprobe_multi xt_multiport "ipt_multiport,ip6t_multiport"
  else
    modprobe_multi xt_multiport ipt_multiport
  fi

  # Implement the filter table:
  modprobe iptable_filter
  if [ "$IPV6_SUPPORT" = "1" ]; then
    modprobe ip6table_filter
  fi

  # Implement the mangle table
  modprobe iptable_mangle
  if [ "$IPV6_SUPPORT" = "1" ]; then
    modprobe ip6table_mangle
  fi

  # Implement the raw table
  modprobe iptable_raw
  if [ "$IPV6_SUPPORT" = "1" ]; then
    modprobe ip6table_raw
  fi

  # Implement the REJECT target
  modprobe ipt_REJECT
  if [ "$IPV6_SUPPORT" = "1" ]; then
    modprobe ip6t_REJECT
  fi

  # Implement the LOG target
  if [ "$IPV6_SUPPORT" = "1" ]; then
    modprobe_multi xt_LOG "ipt_LOG,ip6t_LOG"
  else
    modprobe_multi xt_LOG ipt_LOG
  fi

  if [ "$SET_MSS" != "0" ]; then
    # Implement the TCPMSS target
    if [ "$IPV6_SUPPORT" = "1" ]; then
      modprobe_multi xt_TCPMSS "ipt_TCPMSS,ip6t_TCPMSS"
    else
      modprobe_multi xt_TCPMSS ipt_TCPMSS
    fi
  fi

  if [ "$MANGLE_TOS" != "0" ]; then
    # Implement the TOS target
    if [ "$IPV6_SUPPORT" = "1" ]; then
      modprobe_multi xt_DSCP "ipt_DSCP,ip6t_DSCP" "ipt_TOS,ip6t_TOS"
    else
      modprobe_multi xt_DSCP ipt_DSCP ipt_TOS
    fi
  fi

  if [ "$PACKET_TTL" = "1" -o "$TTL_INC" = "1" ]; then
    # Load the TTL target:
    modprobe ipt_TTL
  fi

  # (Currently) unused modules:
  #    modprobe ipt_addrtype            # Allows matching src/dst address type (BROKEN!)
  #    modprobe ipt_pkttype             # Permits checking for packet type (BROADCAST, MULTICAST etc.) (BROKEN!)
  #    modprobe ip_queue                # Allows queuing packets to user space
  #    modprobe ipt_owner               # Permits user/group checking on OUTPUT packets
  #    modprobe ipt_mark                # Allows use of mark match
  #    modprobe ip_conntrack_egg

  # Implement the NAT table
  modprobe iptable_nat

  if [ "$NAT" = "1" -o -n "$NAT_FORWARD_TCP" -o -n "$NAT_FORWARD_UDP" -o -n "$NAT_FORWARD_IP" ]; then
    # Load the module implementing DNAT/SNAT/NAT support
    modprobe_multi nf_nat ip_nat
  fi

  if [ "$NAT" = "1" ]; then
    if [ -z "$NAT_STATIC_IP" ]; then
      # Load the MASQUERADE target:
      modprobe ipt_MASQUERADE
    fi
  fi

  echo " Module check done..."
}


setup_misc()
{
  # Remove any stale plugin restart file
  rm -f "$PLUGIN_LOAD_FILE_RESTART"

  # Remove stale host-cache file
  rm -f "$HOST_CACHE_FILE"

  # Most people don't want to get any firewall logs being spit to the console
  # This option makes the kernel ring buffer only log messages with level "panic"
  if [ "$DMESG_PANIC_ONLY" = "1" ]; then
    echo "Setting the kernel ring buffer to only log panic messages to the console"
#    dmesg -c    # Clear ring buffer
    dmesg -n 1  # Only show panic messages on the console
  fi
}


setup_kernel_settings()
{
  # Set INDENT value for functions
  INDENT='  '

  echo "Configuring general kernel parameters:"

  # Disable conntrack automatic helper assignment, if supported
  #############################################################
  if sysctl_key_match net.netfilter.nf_conntrack_helper; then
    sysctl -w net.netfilter.nf_conntrack_helper=0
  else
    echo " Conntrack legacy automatic helper assignment is ENABLED"
    # Fallback to an older conntrack match method
    NF_CONNTRACK_STATE="-m state --state"
  fi

  # Set the maximum number of connections to track.
  # The kernel "default" depends on the available amount of RAM, 128 MB of RAM -> 8192
  # possible entries, 256 MB of RAM --> 16376 possible entries, etc...
  #######################################################################################
  if [ -n "$CONNTRACK" -a "$CONNTRACK" != "0" ]; then
    echo " Setting the max. amount of simultaneous connections to $CONNTRACK"
    sysctl_multi -w net.nf_conntrack_max=$CONNTRACK \
                    net.ipv4.netfilter.ip_conntrack_max=$CONNTRACK \
                    net.ipv4.ip_conntrack_max=$CONNTRACK
  fi

  # Change some default timings to fix false logs generated by "lost connections"
  # Defaults:
  #          echo 60 > /proc/sys/net/ipv4/netfilter/ip_conntrack_udp_timeout
  #          echo 180 > /proc/sys/net/ipv4/netfilter/ip_conntrack_udp_timeout_stream
  #          echo 10 >/proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_close
  #          echo 300 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_max_retrans
  #          echo 120 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_time_wait
  #          echo 30 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_last_ack
  #          echo 60 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_close_wait
  #          echo 120 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_fin_wait
  #          echo 60 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_syn_recv
  #          echo 120 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_syn_sent
  #          echo 30 > /proc/sys/net/ipv4/netfilter/ip_conntrack_icmp_timeout
  #          echo 1200 > /proc/sys/net/ipv4/netfilter/ip_conntrack_generic_timeout
  ###############################################################################
#  echo " Setting default conntrack timeouts"

  # This is to fix issues with DNS:
  sysctl_multi -w net.netfilter.nf_conntrack_udp_timeout=60 \
                  net.ipv4.netfilter.ip_conntrack_udp_timeout=60

#  sysctl_multi -w net.netfilter.nf_conntrack_udp_timeout_stream=180 \
#                  net.ipv4.netfilter.ip_conntrack_udp_timeout_stream=180

  # Enable Conntrack Accounting (kernel config CONFIG_NF_CT_ACCT)
  # CONFIG_NF_CT_ACCT is deprecated and will be removed sometime after kernel 2.6.27
  sysctl -w net.netfilter.nf_conntrack_acct=1 2>/dev/null

  # Always set IPv4 options for IPv4 or IPv4/IPv6
  ######################################################
  echo "Configuring kernel parameters:"

  # Disable ICMP send_redirect
  ############################
  echo " Disabling send redirects"
  sysctl_set_all "net.ipv4.conf" "send_redirects" 0
  if [ "$IPV6_SUPPORT" = "1" ]; then
    sysctl_set_all "net.ipv6.conf" "send_redirects" 0
  fi

  # Don't accept source routed packets.
  # Attackers can use source routing to generate
  # traffic pretending to be from inside your network, but which is routed back along
  # the path from which it came, namely outside, so attackers can compromise your
  # network. Source routing is rarely used for legitimate purposes.
  ###################################################################################
  if [ "$SOURCE_ROUTE_PROTECTION" = "0" ]; then
    echo " DISABLING protection against source routed packets"
    sysctl_set_all "net.ipv4.conf" "accept_source_route" 1
    if [ "$IPV6_SUPPORT" = "1" ]; then
      sysctl_set_all "net.ipv6.conf" "accept_source_route" 1
    fi
  else
    echo " Enabling protection against source routed packets"
    sysctl_set_all "net.ipv4.conf" "accept_source_route" 0
    if [ "$IPV6_SUPPORT" = "1" ]; then
      sysctl_set_all "net.ipv6.conf" "accept_source_route" 0
    fi
  fi

  # ICMP Broadcasting protection (smurf amplifier protection)
  ###########################################################
  sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1

  # ICMP Dead Error Messages protection
  #####################################
  sysctl -w net.ipv4.icmp_ignore_bogus_error_responses=1

  # IP forwarding (need it to perform for example NAT)
  ####################################################
  if [ "$IP_FORWARDING" = "1" ]; then
    echo " Enabling packet forwarding"
    sysctl_set_all "net.ipv4.conf" "forwarding" 1 || sysctl -w net.ipv4.ip_forward=1 ||
    {
      printf "\033[40m\033[1;31m WARNING: net.ipv4.conf.*.forwarding (or net.ipv4.ip_forward) could not be set! If you're using\033[0m\n" >&2
      printf "\033[40m\033[1;31m          NAT or any other type of forwarding this may be a problem.\033[0m\n" >&2
    }
    if [ "$IPV6_SUPPORT" = "1" ]; then
      sysctl_set_all "net.ipv6.conf" "forwarding" 1
      echo " Disabling Local IPv6 Auto-Configuration"
      sysctl_set_all "net.ipv6.conf" "autoconf" 0
      ## Setting accept_ra=0 is not needed with forwarding=1, don't overwrite any existing accept_ra=2 values
    fi
  elif [ "$IP_FORWARDING" = "0" ]; then
    echo " DISABLING packet forwarding"
    sysctl_set_all "net.ipv4.conf" "forwarding" 0 2>/dev/null || sysctl -w -e net.ipv4.ip_forward=0 2>/dev/null
    if [ "$IPV6_SUPPORT" = "1" ]; then
      sysctl_set_all "net.ipv6.conf" "forwarding" 0 2>/dev/null
      if [ "$IPV6_AUTO_CONFIGURATION" != "0" ]; then
        echo " Enabling IPv6 Auto-Configuration"
        sysctl_set_all "net.ipv6.conf" "autoconf" 1
        sysctl_set_all "net.ipv6.conf" "accept_ra" 1
      else
        echo " DISABLING IPv6 Auto-Configuration"
        sysctl_set_all "net.ipv6.conf" "autoconf" 0
        sysctl_set_all "net.ipv6.conf" "accept_ra" 0
      fi
    fi
  fi

  # Enable some general settings
  ##############################
  echo " Setting some kernel performance options"
  sysctl -w net.ipv4.tcp_window_scaling=1
  sysctl -w net.ipv4.tcp_timestamps=1
  sysctl -w net.ipv4.tcp_sack=1
  sysctl -w net.ipv4.tcp_dsack=1
  sysctl -w net.ipv4.tcp_fack=1
  sysctl -w net.ipv4.tcp_low_latency=0

  # Reduce DoS'ing ability by reducing timeouts
  #############################################################
  if [ "$REDUCE_DOS_ABILITY" = "1" ]; then
    echo " Enabling reduction of the DoS'ing ability"

    sysctl -w net.ipv4.tcp_fin_timeout=30
    sysctl -w net.ipv4.tcp_keepalive_time=1800

    # Set number of times to retry SYN in a new connection
    sysctl -w net.ipv4.tcp_syn_retries=3

    # Set number of times to retry a SYN-ACK in a half-open new connections
    sysctl -w net.ipv4.tcp_synack_retries=2

    # Enable a fix for RFC1337 - time-wait assassination hazards in TCP
    sysctl -w net.ipv4.tcp_rfc1337=1
  elif [ "$REDUCE_DOS_ABILITY" = "0" ]; then
    echo " Disabling reduction of the DoS'ing ability"

    # Defaults:
    sysctl -w net.ipv4.tcp_fin_timeout=60
    sysctl -w net.ipv4.tcp_keepalive_time=7200
    sysctl -w net.ipv4.tcp_syn_retries=5
    sysctl -w net.ipv4.tcp_synack_retries=5
    sysctl -w net.ipv4.tcp_rfc1337=0
  fi

  # Set out local port range. Kernel default = "1024 4999"
  ########################################################
  if [ -z "$LOCAL_PORT_RANGE" ]; then
    LOCAL_PORT_RANGE="32768 61000"
  fi
  sysctl -w net.ipv4.ip_local_port_range="$LOCAL_PORT_RANGE"

  # Now we change the LOCAL_PORT_RANGE for further use by iptables (replace space with :)
  LOCAL_PORT_RANGE="$(echo "$LOCAL_PORT_RANGE" |tr ' ' ':')"

  # Add synflood protection?
  ##########################
  if [ "$SYN_PROT" != "0" ]; then
    echo " Enabling SYN-flood protection via SYN-cookies"
    sysctl -w net.ipv4.tcp_syncookies=1
  else
    echo " Disabling SYN-flood protection via SYN-cookies"
    sysctl -w net.ipv4.tcp_syncookies=0
  fi

  # Use rp_filter to drop connections from non-routable IPs
  ######################################################################
  if [ "$RP_FILTER" = "2" ]; then
    echo " Enabling loose anti-spoof with rp_filter"
    sysctl_set_all "net.ipv4.conf" "rp_filter" 2
  elif [ "$RP_FILTER" = "1" ]; then
    echo " Enabling strict anti-spoof with rp_filter"
    sysctl_set_all "net.ipv4.conf" "rp_filter" 1
  elif [ "$RP_FILTER" = "0" ]; then
    echo " Disabling anti-spoof with rp_filter"
    sysctl_set_all "net.ipv4.conf" "rp_filter" 0
  fi

  # Block ALL ICMP echo requests?
  ###############################
  if [ "$ECHO_IGNORE" = "1" ]; then
    echo " Blocking all ICMP echo-requests"
    sysctl -w net.ipv4.icmp_echo_ignore_all=1
  elif [ "$ECHO_IGNORE" = "0" ]; then
    sysctl -w net.ipv4.icmp_echo_ignore_all=0
  fi

  # Log martians?
  ###############
  if [ "$LOG_MARTIANS" = "1" ]; then
    echo " Enabling the logging of martians"
    sysctl_set_all "net.ipv4.conf" "log_martians" 1
  elif [ "$LOG_MARTIANS" = "0" ]; then
    echo " Disabling the logging of martians"
    sysctl_set_all "net.ipv4.conf" "log_martians" 0
  fi

  # Accept ICMP redirect messages?
  ################################
  if [ "$ICMP_REDIRECT" = "1" ]; then
    echo " Enabling the acception of ICMP-redirect messages"
    sysctl_set_all "net.ipv4.conf" "accept_redirects" 1
    if [ "$IPV6_SUPPORT" = "1" ]; then
      sysctl_set_all "net.ipv6.conf" "accept_redirects" 1
    fi
  elif [ "$ICMP_REDIRECT" = "0" ]; then
    echo " Disabling the acception of ICMP-redirect messages"
    sysctl_set_all "net.ipv4.conf" "accept_redirects" 0
    if [ "$IPV6_SUPPORT" = "1" ]; then
      sysctl_set_all "net.ipv6.conf" "accept_redirects" 0
    fi
  fi

  # Enable ECN? (Explicit Congestion Notification)
  ################################################
  if [ "$ECN" = "1" ]; then
    echo " Enabling ECN (Explicit Congestion Notification)"
    sysctl -w net.ipv4.tcp_ecn=1
  elif [ "$ECN" = "0" ]; then
    echo " Disabling ECN (Explicit Congestion Notification)"
    sysctl -w net.ipv4.tcp_ecn=0
  fi

  # This enables dynamic-address hacking which makes the
  # life with Diald and similar programs much easier.
  ######################################################
  if [ "$EXT_IF_DHCP_IP" = "1" ]; then
    echo " Enabling kernel support for dynamic IPs"
    sysctl -w net.ipv4.ip_dynaddr=1
  elif [ "$EXT_IF_DHCP_IP" = "0" ]; then
    echo " Disabling kernel support for dynamic IPs"
    sysctl -w net.ipv4.ip_dynaddr=0
  fi

  # In most cases pmtu discovery is ok, but in some rare cases (when having problems)
  # you might want to disable it.
  if [ "$NO_PMTU_DISCOVERY" = "1" ]; then
    echo " Disabling PMTU discovery"
    sysctl -w net.ipv4.ip_no_pmtu_disc=1
  elif [ "$NO_PMTU_DISCOVERY" = "0" ]; then
    echo " Enabling PMTU discovery"
    sysctl -w net.ipv4.ip_no_pmtu_disc=0
  fi

  # Time To Live (TTL) is the term for a data field in the internet protocol.
  # TTL is today interpreted to indicate the maximum number of routers a packet may transit.
  # Each router that handles a packet will decrement the TTL field by 1.
  # Raise if you have a huge network.
  # Set the default ttl. (Kernel Default: 64)
  ###########################################################################################
  if [ -n "$DEFAULT_TTL" ]; then
    if [ $DEFAULT_TTL -gt 9 -a $DEFAULT_TTL -lt 256 ]; then
      echo " Setting default TTL=$DEFAULT_TTL"
      sysctl -w net.ipv4.ip_default_ttl=$DEFAULT_TTL
    else
      printf "\033[40m\033[1;31m WARNING: Ingoring invalid value for DEFAULT_TTL ($DEFAULT_TTL), it should be between 10 and 255!\033[0m\n" >&2
    fi
  fi

  # Increase the default queuelength. (Kernel Default: 1024)
  ##########################################################
  # sysctl -w -e net.ipv4.ip_queue_maxlen=2048

  # With eg. open iscsi some systems may have problems under heavy load. Enable tcp_be_liberal to workaround this
  if [ "$TCP_BE_LIBERAL" = "1" ]; then
    sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_be_liberal=1
  fi

  echo " Flushing route table"
  sysctl -w net.ipv4.route.flush=1
  if [ "$IPV6_SUPPORT" = "1" ]; then
    sysctl -w net.ipv6.route.flush=1
  fi

  echo " Kernel setup done..."

  # Return "no error"
  return 0
}


init_firewall_chains()
{
  echo "Initializing firewall chains"

  # Set INDENT for functions
  INDENT='  '

  # Attempt to flush all IPv4 chains
  ##################################
  ip4tables -F
  ip4tables -X

  # Flush builtin IPv4 chains
  ###########################
  ip4tables -F INPUT
  ip4tables -F OUTPUT
  ip4tables -F FORWARD

  # Flush rules in nat/mangle/raw tables
  ######################################
  ip4tables -t nat -F
  ip4tables -t nat -X
  ip4tables -t mangle -F
  ip4tables -t mangle -X
  try_ip4tables -t raw -F
  try_ip4tables -t raw -X

  if [ "$IPV6_DETECTED" = "1" ]; then
    # Attempt to flush all IPv6 chains
    ##################################
    ip6tables -F
    ip6tables -X

    # Flush builtin IPv6 chains
    ###########################
    ip6tables -F INPUT
    ip6tables -F OUTPUT
    ip6tables -F FORWARD

    # Flush rules in nat/mangle/raw tables
    ######################################
    try_ip6tables -t nat -F
    try_ip6tables -t nat -X
    ip6tables -t mangle -F
    ip6tables -t mangle -X
    try_ip6tables -t raw -F
    try_ip6tables -t raw -X
  fi

  # Create our "base" chains
  ##########################
  iptables -N BASE_INPUT_CHAIN
  iptables -N BASE_FORWARD_CHAIN
  iptables -N BASE_OUTPUT_CHAIN

  # Accept packets of ESTABLISHED connections
  ###########################################
  iptables -A BASE_INPUT_CHAIN $NF_CONNTRACK_STATE ESTABLISHED -j ACCEPT
  iptables -A BASE_FORWARD_CHAIN $NF_CONNTRACK_STATE ESTABLISHED -j ACCEPT
  iptables -A BASE_OUTPUT_CHAIN $NF_CONNTRACK_STATE ESTABLISHED -j ACCEPT

  # Accept packets of RELATED connections
  #######################################
  iptables -A BASE_INPUT_CHAIN $NF_CONNTRACK_STATE RELATED -p icmp -j ACCEPT
  iptables -A BASE_FORWARD_CHAIN $NF_CONNTRACK_STATE RELATED -p icmp -j ACCEPT

  # Apply conntrack helper chain, fallback to RELATED connections
  ###############################################################
  if [ "$(sysctl_get_value net.netfilter.nf_conntrack_helper)" = "0" ]; then
    iptables -N CONNTRACK_HELPER
    iptables -A BASE_INPUT_CHAIN -j CONNTRACK_HELPER
    iptables -A BASE_FORWARD_CHAIN -j CONNTRACK_HELPER
  else
    iptables -A BASE_INPUT_CHAIN $NF_CONNTRACK_STATE RELATED -p tcp --dport 1024: -j ACCEPT
    iptables -A BASE_INPUT_CHAIN $NF_CONNTRACK_STATE RELATED -p udp --dport 1024: -j ACCEPT
    iptables -A BASE_FORWARD_CHAIN $NF_CONNTRACK_STATE RELATED -p tcp --dport 1024: -j ACCEPT
    iptables -A BASE_FORWARD_CHAIN $NF_CONNTRACK_STATE RELATED -p udp --dport 1024: -j ACCEPT
  fi

  # Drop all IPv6 packets with Routing Header Type 0
  ##################################################
  if [ "$IPV6_SUPPORT" = "1" -a "$IPV6_DROP_RH_ZERO" != "0" ]; then
    if try_ip6tables -A BASE_INPUT_CHAIN -m rt --rt-type 0 -j DROP; then
      ip6tables -A BASE_FORWARD_CHAIN -m rt --rt-type 0 -j DROP
      ip6tables -A BASE_OUTPUT_CHAIN -m rt --rt-type 0 -j DROP
    else
      echo " WARNING: IPv6 Routing Header Type 0 matching not supported"
    fi
  fi

  # Accept all packets for the loopback device
  ############################################
  iptables -A BASE_INPUT_CHAIN -i lo -j ACCEPT
  iptables -A BASE_FORWARD_CHAIN -i lo -j ACCEPT
  iptables -A BASE_OUTPUT_CHAIN -o lo -j ACCEPT

  # Insert our base chains
  ########################
  iptables -A INPUT -j BASE_INPUT_CHAIN
  iptables -A FORWARD -j BASE_FORWARD_CHAIN
  iptables -A OUTPUT -j BASE_OUTPUT_CHAIN

  # Create several chains that we will use later on
  #################################################
  create_user_chains

  # Reset the iptables counters
  iptables -Z
  iptables -t mangle -Z
  ip4tables -t nat -Z
}


# Check if the base chains are appropriate for the IPV6_SUPPORT setting
#######################################################################
check_for_base_chains()
{
  local ipv4_rtn ipv6_rtn

  ip4tables -nL BASE_INPUT_CHAIN >/dev/null 2>&1
  ipv4_rtn=$?

  if [ "$IPV6_DETECTED" = "1" ]; then
    ip6tables -nL BASE_INPUT_CHAIN >/dev/null 2>&1
    ipv6_rtn=$?
  else
    # No IPv6 available, therefore no ip6tables call
    # Set a return error result of 1 to specify no IPv6 BASE_INPUT_CHAIN
    ipv6_rtn=1
  fi

  if [ "$IPV6_SUPPORT" = "1" ]; then
    if [ $ipv4_rtn -eq 0 -a $ipv6_rtn -eq 0 ]; then
      echo "yes"
    elif [ $ipv4_rtn -eq 0 ]; then
      echo "other"
    else
      echo "no"
    fi
  else
    if [ $ipv4_rtn -eq 0 -a $ipv6_rtn -eq 0 ]; then
      echo "other"
    elif [ $ipv4_rtn -eq 0 ]; then
      echo "yes"
    else
      echo "no"
    fi
  fi
}


setup_default_policies()
{
  # Set the default policies for the builtin INPUT & FORWARD tables. The
  # default for other chains (eg. OUTPUT) is always set to ACCEPT.
  #######################################################################
  if [ "$DEFAULT_POLICY_DROP" != "0" ]; then
    echo " Setting all default policies to DROP while \"setting up firewall rules\""
    iptables -P INPUT DROP
    iptables -P FORWARD DROP
    iptables -P OUTPUT DROP
  else
    echo " WARNING: Setting all default policies to ACCEPT while \"setting up firewall rules\""
    iptables -P INPUT ACCEPT
    iptables -P FORWARD ACCEPT
    iptables -P OUTPUT ACCEPT
  fi

  if [ "$IPV6_SUPPORT" = "1" ]; then
    echo "IPv4/IPv6 mixed mode selected"
  elif [ "$IPV6_DETECTED" = "1" ]; then
    echo "IPv4 mode selected but IPv6 available, DROP all IPv6 packets"
    ip6tables -P INPUT DROP
    ip6tables -P FORWARD DROP
    ip6tables -P OUTPUT DROP

    # Allow IPv6 traffic from the loopback (localhost)
    ip6tables -A INPUT -i lo -j ACCEPT
    ip6tables -A FORWARD -i lo -j ACCEPT
    ip6tables -A OUTPUT -o lo -j ACCEPT

    # DROP all IPv6 traffic
    ip6tables -A INPUT -j DROP
    ip6tables -A FORWARD -j DROP
    ip6tables -A OUTPUT -j DROP
  else
    echo "IPv4 mode selected, no IPv6 available"
  fi

  # The POST_INPUT_DROP_CHAIN should temporarily DROP for now
  # We'll change this once the rules are in place
  ###########################################################
  iptables -A POST_INPUT_DROP_CHAIN -j DROP

  # The HOST_BLOCK_SRC_DROP chain should always DROP
  ###########################################################
  if [ "$BLOCKED_HOST_LOG" = "1" -o "$BLOCKED_HOST_LOG" = "2" ]; then
    iptables -A HOST_BLOCK_SRC_DROP -m limit --limit 1/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Blocked inbound host: "
  fi
  iptables -A HOST_BLOCK_SRC_DROP -j DROP

  # The HOST_BLOCK_DST_DROP chain should always DROP
  ###########################################################
  if [ "$BLOCKED_HOST_LOG" = "1" -o "$BLOCKED_HOST_LOG" = "3" ]; then
    iptables -A HOST_BLOCK_DST_DROP -m limit --limit 1/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Blocked outbound host: "
  fi
  iptables -A HOST_BLOCK_DST_DROP -j DROP

  # The LINK_LOCAL_DROP chain should always DROP
  ###########################################################
  if [ "$IPV6_SUPPORT" = "1" ]; then
    if [ "$LINK_LOCAL_DROP_LOG" != "0" ]; then
      ip6tables -A LINK_LOCAL_DROP -m limit --limit 1/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Dropped Link-Local: "
    fi
    ip6tables -A LINK_LOCAL_DROP -j DROP
  fi
}


##################################################################################################################
## Chain VALID_CHK - Check packets for invalid flags etc.                                                       ##
##################################################################################################################
setup_valid_chk_chain()
{
  ## Log scanning of nmap etc.
  ############################
  if [ "$SCAN_LOG" != "0" ]; then
    echo "Logging of stealth scans (nmap probes etc.) enabled"

    # (NMAP) FIN/URG/PSH
    ####################
    iptables -A VALID_CHK -p tcp --tcp-flags ALL FIN,URG,PSH \
      -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Stealth XMAS scan: "

    # SYN/RST/ACK/FIN/URG
    #####################
    iptables -A VALID_CHK -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG \
      -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Stealth XMAS-PSH scan: "

    # ALL/ALL
    #########
    iptables -A VALID_CHK -p tcp --tcp-flags ALL ALL \
      -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Stealth XMAS-ALL scan: "

    # NMAP FIN Stealth
    ##################
    iptables -A VALID_CHK -p tcp --tcp-flags ALL FIN \
      -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Stealth FIN scan: "

    # SYN/RST
    #########
    iptables -A VALID_CHK -p tcp --tcp-flags SYN,RST SYN,RST \
      -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Stealth SYN/RST scan: "

    # SYN/FIN (probably)
    ####################
    iptables -A VALID_CHK -p tcp --tcp-flags SYN,FIN SYN,FIN \
      -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Stealth SYN/FIN scan?: "

    # Null scan
    ###########
    iptables -A VALID_CHK -p tcp --tcp-flags ALL NONE \
      -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Stealth Null scan: "

  else
    echo "Logging of stealth scans (nmap probes etc.) disabled"
  fi

  # Drop (NMAP) scan packets:
  ###########################

  # NMAP FIN/URG/PSH
  ##################
  iptables -A VALID_CHK -p tcp --tcp-flags ALL FIN,URG,PSH -j POST_INPUT_DROP_CHAIN

  # SYN/RST/ACK/FIN/URG
  #####################
  iptables -A VALID_CHK -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j POST_INPUT_DROP_CHAIN

  # ALL/ALL Scan
  ##############
  iptables -A VALID_CHK -p tcp --tcp-flags ALL ALL -j POST_INPUT_DROP_CHAIN

  # NMAP FIN Stealth
  ##################
  iptables -A VALID_CHK -p tcp --tcp-flags ALL FIN -j POST_INPUT_DROP_CHAIN

  # SYN/RST
  #########
  iptables -A VALID_CHK -p tcp --tcp-flags SYN,RST SYN,RST -j POST_INPUT_DROP_CHAIN

  # SYN/FIN -- Scan(probably)
  ###########################
  iptables -A VALID_CHK -p tcp --tcp-flags SYN,FIN SYN,FIN -j POST_INPUT_DROP_CHAIN

  # NMAP Null Scan
  ################
  iptables -A VALID_CHK -p tcp --tcp-flags ALL NONE -j POST_INPUT_DROP_CHAIN

  # Log packets with bad flags?
  #############################
  if [ "$BAD_FLAGS_LOG" != "0" ]; then
    echo "Logging of packets with bad TCP-flags enabled"
    iptables -A VALID_CHK -p tcp --tcp-option 64 \
      -m limit --limit 3/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Bad TCP flag(64): "

    iptables -A VALID_CHK -p tcp --tcp-option 128 \
      -m limit --limit 3/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Bad TCP flag(128): "
  else
    echo "Logging of packets with bad TCP-flags disabled"
  fi

  # Drop packets with bad tcp flags
  #################################
  iptables -A VALID_CHK -p tcp --tcp-option 64 -j POST_INPUT_DROP_CHAIN
  iptables -A VALID_CHK -p tcp --tcp-option 128 -j POST_INPUT_DROP_CHAIN

  # These packets are normally from "lost connection" and thus can generate false alarms
  # So we might want to ignore such packets
  ######################################################################################
#  if [ "$LOST_CONNECTION_LOG" != "1" ]; then
#    iptables -A VALID_CHK -p tcp --tcp-flags SYN,ACK,FIN,RST ACK -j POST_INPUT_DROP_CHAIN
#    iptables -A VALID_CHK -p tcp --tcp-flags SYN,ACK,FIN,RST FIN -j POST_INPUT_DROP_CHAIN
#    iptables -A VALID_CHK -p tcp --tcp-flags SYN,ACK,FIN,RST RST -j POST_INPUT_DROP_CHAIN
#    iptables -A VALID_CHK -p tcp --tcp-flags SYN,ACK,FIN,RST ACK,FIN -j POST_INPUT_DROP_CHAIN
#    iptables -A VALID_CHK -p tcp --tcp-flags SYN,ACK,FIN,RST ACK,RST -j POST_INPUT_DROP_CHAIN
#    iptables -A VALID_CHK -p tcp --tcp-flags SYN,ACK,FIN,RST SYN,ACK -j POST_INPUT_DROP_CHAIN
#  fi

  # Here we add some protection from random packets we receive, such as random sweeps from other
  # (possible) hacked computers, or just packets who are invalid, not belonging to ANY connection
  ###############################################################################################
  if [ "$INVALID_TCP_LOG" = "1" ]; then
    echo "Logging of INVALID TCP packets enabled"

    iptables -A VALID_CHK -p tcp $NF_CONNTRACK_STATE INVALID \
      -m limit --limit 1/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INVALID TCP: "
  else
    echo "Logging of INVALID TCP packets disabled"
  fi

  if [ "$INVALID_UDP_LOG" = "1" ]; then
    echo "Logging of INVALID UDP packets enabled"

    iptables -A VALID_CHK -p udp $NF_CONNTRACK_STATE INVALID \
      -m limit --limit 1/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INVALID UDP: "
  else
    echo "Logging of INVALID UDP packets disabled"
  fi

  if [ "$INVALID_ICMP_LOG" = "1" ]; then
    echo "Logging of INVALID ICMP packets enabled"

    # Only log INVALID ICMP-request packets when we also want to log "normal" ICMP-request packets
    if [ "$ICMP_REQUEST_LOG" != "0" ]; then
      iptables -A VALID_CHK -p icmp --icmp-type echo-request $NF_CONNTRACK_STATE INVALID \
        -m limit --limit 1/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INVALID ICMP-request: "
    fi

    # Only log INVALID ICMP-other when enabled in the config
    if [ "$ICMP_OTHER_LOG" != "0" ]; then
      iptables -A VALID_CHK -p icmp ! --icmp-type echo-request $NF_CONNTRACK_STATE INVALID \
        -m limit --limit 1/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INVALID ICMP-other: "
    fi
  else
    echo "Logging of INVALID ICMP packets disabled"
  fi

  # Drop invalid packets
  ######################
  iptables -A VALID_CHK $NF_CONNTRACK_STATE INVALID -j POST_INPUT_DROP_CHAIN

  # Fragmentation cannot happen with IPV6 (and maybe even not with iptables/ipv4?)
  ## Log fragmented packets
  #########################
  if [ "$FRAG_LOG" = "1" ]; then
    echo "Logging of fragmented packets enabled"
    ip4tables -A VALID_CHK -f -m limit --limit 3/m --limit-burst 1 -j LOG --log-prefix "AIF:Fragment packet: "
  else
    echo "Logging of IPv4 fragmented packets disabled"
  fi

  # Drop fragmented packets
  #########################
  ip4tables -A VALID_CHK -f -j DROP
}


################################################################################################################
## Chain RESERVED_NET_CHK - Check if the source addresses of the packets are (in)valid                        ##
################################################################################################################
setup_reserved_net_chk_chain()
{
  # Log access from reserved addresses
  ####################################
  if [ "$RESERVED_NET_LOG" = "1" ]; then
    echo "Logging of access from reserved nets enabled"
    ip4tables -A RESERVED_NET_CHK -s 10.0.0.0/8 \
      -m limit --limit 1/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:IPv4 Private address: "

    ip4tables -A RESERVED_NET_CHK -s 172.16.0.0/12 \
      -m limit --limit 1/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:IPv4 Private address: "

    ip4tables -A RESERVED_NET_CHK -s 192.168.0.0/16 \
      -m limit --limit 1/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:IPv4 Private address: "

    ip4tables -A RESERVED_NET_CHK -s 169.254.0.0/16 \
      -m limit --limit 1/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:IPv4 Link-local address: "

    ip4tables -A RESERVED_NET_CHK -s 224.0.0.0/24 \
      -m limit --limit 1/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:IPv4 Multicast address: "

    ip4tables -A RESERVED_NET_CHK -s 239.0.0.0/24 \
      -m limit --limit 1/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:IPv4 Multicast address: "

    if [ "$IPV6_SUPPORT" = "1" ]; then
      # IPv6 not 2000::/3 is non-Global Unicast
      ip6tables -A RESERVED_NET_CHK ! -s 2000::/3 \
        -m limit --limit 1/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:IPv6 non-Global address: "
    fi
  else
    echo "Logging of access from reserved nets disabled"
  fi

  if [ "$RESERVED_NET_DROP" = "1" ]; then
    # rp_filter drops some of these addresses, but just to be sure :)
    ################################################################
    #echo "Denying access from reserved addresses..."
    ip4tables -A RESERVED_NET_CHK -s 10.0.0.0/8 -j POST_INPUT_DROP_CHAIN
    ip4tables -A RESERVED_NET_CHK -s 172.16.0.0/12 -j POST_INPUT_DROP_CHAIN
    ip4tables -A RESERVED_NET_CHK -s 192.168.0.0/16 -j POST_INPUT_DROP_CHAIN
    ip4tables -A RESERVED_NET_CHK -s 169.254.0.0/16 -j POST_INPUT_DROP_CHAIN
    ip4tables -A RESERVED_NET_CHK -s 224.0.0.0/24 -j POST_INPUT_DROP_CHAIN
    ip4tables -A RESERVED_NET_CHK -s 239.0.0.0/24 -j POST_INPUT_DROP_CHAIN

    if [ "$IPV6_SUPPORT" = "1" ]; then
      # IPv6 not 2000::/3 is non-Global Unicast
      ip6tables -A RESERVED_NET_CHK ! -s 2000::/3 -j POST_INPUT_DROP_CHAIN
    fi
  fi
}


################################################################################################################
## Chain SPOOF_CHK - Check if the source address is not spoofed                                               ##
################################################################################################################
setup_spoof_chk_chain()
{
  # Anti-spoof protection for the internal net
  if [ -n "$INT_IF" -a -n "$INTERNAL_NET" ]; then
    if [ "$INTERNAL_NET_ANTISPOOF" != "0" ]; then
      printf "Setting up antispoof for INTERNAL net(s): "
      IFS=' ,'
      for net in $INTERNAL_NET; do
        printf "$net "
        for interface in $INT_IF; do
          # Any internal net is valid
          iptables -A SPOOF_CHK -i $interface -s $net -j RETURN
        done
        iptables -A SPOOF_CHK -s $net -m limit --limit 3/m -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Spoofed packet: "
        iptables -A SPOOF_CHK -s $net -j POST_INPUT_DROP_CHAIN
      done
      echo ""
    else
      echo "Antispoof for INTERNAL net(s) DISABLED!"
    fi
  fi

  # Anti-spoof protection for the DMZ net
  if [ -n "$DMZ_IF" -a -n "$DMZ_NET" ]; then
    if [ "$DMZ_NET_ANTISPOOF" != "0" ]; then
      printf "Setting up antispoof for DMZ net(s): "
      IFS=' ,'
      for net in $DMZ_NET; do
        printf "$net "
        for interface in $DMZ_IF; do
          # Any dmz net is valid
          iptables -A SPOOF_CHK -i $interface -s $net -j RETURN
        done
        iptables -A SPOOF_CHK -s $net -m limit --limit 3/m -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Spoofed packet: "
        iptables -A SPOOF_CHK -s $net -j POST_INPUT_DROP_CHAIN
      done
      echo ""
    else
      echo "Antispoof for DMZ net(s) DISABLED!"
    fi
  fi

  # Everything else is valid
  iptables -A SPOOF_CHK -j RETURN
}


################################################################
# Setup rules to forward INET IPv6 and non-NAT'ed IPv4 traffic #
################################################################
setup_inet_forward_rules()
{
  # TCP ports to ALLOW for certain INET hosts
  #########################################
  unset IFS
  for rule in $INET_FORWARD_TCP; do
    if parse_rule "$rule" INET_FORWARD_TCP "interfaces:EXT_IF-shosts-dhosts-ports:ANYPORT"; then

      echo "$(show_if_ip "$interfaces")Forwarding(non-NAT) TCP port(s): $ports from $shosts(INET) to $dhosts"

      IFS=' ,'
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            for interface in $interfaces; do
              iptables -A EXT_FORWARD_IN_CHAIN -i $interface ! -o $interface -s $shost -d $dhost -p tcp --dport $port -j ACCEPT
            done
          done
        done
      done
    fi
  done

  # UDP ports to ALLOW for certain INET hosts
  #########################################
  unset IFS
  for rule in $INET_FORWARD_UDP; do
    if parse_rule "$rule" INET_FORWARD_UDP "interfaces:EXT_IF-shosts-dhosts-ports:ANYPORT"; then

      echo "$(show_if_ip "$interfaces")Forwarding(non-NAT) UDP port(s): $ports from $shosts(INET) to $dhosts"

      IFS=' ,'
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            for interface in $interfaces; do
              iptables -A EXT_FORWARD_IN_CHAIN -i $interface ! -o $interface -s $shost -d $dhost -p udp --dport $port -j ACCEPT
            done
          done
        done
      done
    fi
  done

  # (Other) IP protocols to ALLOW for certain INET hosts
  ######################################################
  unset IFS
  for rule in $INET_FORWARD_IP; do
    if parse_rule "$rule" INET_FORWARD_IP "interfaces:EXT_IF-shosts-dhosts-protos"; then

      echo "$(show_if_ip "$interfaces")Forwarding(non-NAT) IP protocol(s): $protos from $shosts(INET) to $dhosts"

      IFS=' ,'
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for proto in $protos; do
            for interface in $interfaces; do
              iptables -A EXT_FORWARD_IN_CHAIN -i $interface ! -o $interface -s $shost -d $dhost -p $proto -j ACCEPT
            done
          done
        done
      done
    fi
  done
}


##################################################
# Setup chain for the DMZ input traffic          #
##################################################
setup_dmz_input_chain()
{
  # Add TCP ports to allow for certain hosts
  ##########################################
  unset IFS
  for rule in $DMZ_HOST_OPEN_TCP; do
    if parse_rule "$rule" DMZ_HOST_OPEN_TCP "hosts-ports"; then

      echo " Allowing $hosts(DMZ) for TCP port(s): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          iptables -A DMZ_INPUT_CHAIN -s $host -p tcp --dport $port -j ACCEPT
        done
      done
    fi
  done

  # Add UDP ports to allow for certain hosts
  ##########################################
  unset IFS
  for rule in $DMZ_HOST_OPEN_UDP; do
    if parse_rule "$rule" DMZ_HOST_OPEN_UDP "hosts-ports"; then

      echo " Allowing $hosts(DMZ) for UDP port(s): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          iptables -A DMZ_INPUT_CHAIN -s $host -p udp --dport $port -j ACCEPT
        done
      done
    fi
  done

  # Add IP protocols to allow for certain hosts
  #############################################
  unset IFS
  for rule in $DMZ_HOST_OPEN_IP; do
    if parse_rule "$rule" DMZ_HOST_OPEN_IP "hosts-protos"; then

      echo " Allowing $hosts(DMZ) for IP protocol(s): $protos"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for proto in $protos; do
          iptables -A DMZ_INPUT_CHAIN -s $host -p $proto -j ACCEPT
        done
      done
    fi
  done

  # Adding TCP ports NOT to be firewalled
  #######################################
  if [ -n "$DMZ_OPEN_TCP" ]; then
    echo " Allowing TCP port(s): $DMZ_OPEN_TCP"
    IFS=' ,'
    for port in $DMZ_OPEN_TCP; do
      iptables -A DMZ_INPUT_CHAIN -p tcp --dport $port -j ACCEPT
    done
  fi

  # Adding UDP ports NOT to be firewalled
  #######################################
  if [ -n "$DMZ_OPEN_UDP" ]; then
    echo " Allowing UDP port(s): $DMZ_OPEN_UDP"
    IFS=' ,'
    for port in $DMZ_OPEN_UDP; do
      iptables -A DMZ_INPUT_CHAIN -p udp --dport $port -j ACCEPT
    done
  fi

  # Adding IP protocols NOT to be firewalled
  ##########################################
  if [ -n "$DMZ_OPEN_IP" ]; then
    echo " Allowing IP protocol(s): $DMZ_OPEN_IP"
    IFS=' ,'
    for proto in $DMZ_OPEN_IP; do
      iptables -A DMZ_INPUT_CHAIN -p $proto -j ACCEPT
    done
  fi

  # Allow to send ICMP packets?
  #############################
  if [ "$DMZ_OPEN_ICMP" != "0" ]; then
    echo " Allowing ICMP-requests(ping)"
    iptables -A DMZ_INPUT_CHAIN -p icmp --icmp-type echo-request -m limit --limit 20/second --limit-burst 100 -j ACCEPT
    if [ "$IPV6_SUPPORT" = "1" ]; then
      unset IFS
      for icmpv6_type in $ICMPV6_SPECIAL_TYPES; do
        ip6tables -A DMZ_INPUT_CHAIN -p icmpv6 --icmpv6-type $icmpv6_type -m hl --hl-eq 255 -j ACCEPT
      done
    fi
  fi

  # Log incoming ICMP-request packets?
  ####################################
  if [ "$ICMP_REQUEST_LOG" != "0" ]; then
    iptables -A DMZ_INPUT_CHAIN -p icmp --icmp-type echo-request \
      -m limit --limit 3/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-request: "
  fi

  # Drop ICMP packets
  iptables -A DMZ_INPUT_CHAIN -p icmp --icmp-type echo-request -j DROP

  # Log everything else
  if [ "$DMZ_INPUT_DENY_LOG" != "0" ]; then
    iptables -A DMZ_INPUT_CHAIN -m limit --limit 3/m -j LOG --log-level $LOGLEVEL --log-prefix "AIF:DMZ-INPUT denied: "
  fi

  # Everything else is denied
  iptables -A DMZ_INPUT_CHAIN -j DROP
}


##################################################
# Setup chain for the DMZ-to-LAN forward traffic #
##################################################
setup_dmz_lan_forward_chain()
{
  echo " Setting up DMZ->LAN policy"

  # TCP ports to ALLOW for certain DMZ hosts
  ##########################################
  unset IFS
  for rule in $DMZ_LAN_HOST_OPEN_TCP; do
    if parse_rule "$rule" DMZ_LAN_HOST_OPEN_TCP "shosts:ANYHOST-dhosts-ports:ANYPORT"; then

      echo "  Allowing $shosts(DMZ) to $dhosts(LAN) for TCP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            iptables -A DMZ_LAN_FORWARD_CHAIN -s $shost -d $dhost -p tcp --dport $port -j ACCEPT
          done
        done
      done
    fi
  done

  # UDP ports to ALLOW for certain DMZ hosts
  ##########################################
  unset IFS
  for rule in $DMZ_LAN_HOST_OPEN_UDP; do
    if parse_rule "$rule" DMZ_LAN_HOST_OPEN_UDP "shosts:ANYHOST-dhosts-ports:ANYPORT"; then

      echo "  Allowing $shosts(DMZ) to $dhosts(LAN) for UDP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            iptables -A DMZ_LAN_FORWARD_CHAIN -s $shost -d $dhost -p udp --dport $port -j ACCEPT
          done
        done
      done
    fi
  done

  # IP protocol(s) to ALLOW for certain DMZ hosts
  ###############################################
  unset IFS
  for rule in $DMZ_LAN_HOST_OPEN_IP; do
    if parse_rule "$rule" DMZ_LAN_HOST_OPEN_IP "shosts:ANYHOST-dhosts-protos"; then

      echo "  Allowing $shosts(DMZ) to $dhosts(LAN) for IP protocol(s): $protos"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for proto in $protos; do
            iptables -A DMZ_LAN_FORWARD_CHAIN -s $shost -d $dhost -p $proto -j ACCEPT
          done
        done
      done
    fi
  done

  # Allow ICMP-requests(ping) for DMZ->LAN?
  ##########################################
  if [ "$DMZ_LAN_OPEN_ICMP" = "1" ]; then
    echo "  Allowing ICMP-requests(ping)"
    iptables -A DMZ_LAN_FORWARD_CHAIN -p icmp --icmp-type echo-request \
      -m limit --limit 20/second --limit-burst 100 -j ACCEPT
  fi

  # Log incoming ICMP-request packets?
  ####################################
  if [ "$ICMP_REQUEST_LOG" != "0" ]; then
    iptables -A DMZ_LAN_FORWARD_CHAIN -p icmp --icmp-type echo-request \
      -m limit --limit 3/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-request: "
  fi

  # Drop ICMP packets
  iptables -A DMZ_LAN_FORWARD_CHAIN -p icmp --icmp-type echo-request -j DROP

  # Log everything else
  if [ "$DMZ_OUTPUT_DENY_LOG" != "0" ]; then
    iptables -A DMZ_LAN_FORWARD_CHAIN -m limit --limit 3/m -j LOG --log-level $LOGLEVEL --log-prefix "AIF:DMZ->LAN denied: "
  fi

  # Everything else is denied
  iptables -A DMZ_LAN_FORWARD_CHAIN -j DROP
}


###################################################
# Setup chain for the INET-to-DMZ forward traffic #
###################################################
setup_inet_dmz_forward_chain()
{
  echo " Setting up INET->DMZ policy"

  # TCP ports to ALLOW for certain INET hosts
  #########################################
  unset IFS
  for rule in $INET_DMZ_HOST_OPEN_TCP; do
    if parse_rule "$rule" INET_DMZ_HOST_OPEN_TCP "interfaces-shosts-dhosts-ports:ANYPORT"; then

      echo "  $(show_if_ip "$interfaces")Allowing $shosts(INET) to $dhosts(DMZ) for TCP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            for interface in $interfaces; do
              iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -s $shost -d $dhost -p tcp --dport $port -j ACCEPT
            done
          done
        done
      done
    fi
  done

  # UDP ports to ALLOW for certain INET hosts
  #########################################
  unset IFS
  for rule in $INET_DMZ_HOST_OPEN_UDP; do
    if parse_rule "$rule" INET_DMZ_HOST_OPEN_UDP "interfaces-shosts-dhosts-ports:ANYPORT"; then

      echo "  $(show_if_ip "$interfaces")Allowing $shosts(INET) to $dhosts(DMZ) for UDP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            for interface in $interfaces; do
              iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -s $shost -d $dhost -p udp --dport $port -j ACCEPT
            done
          done
        done
      done
    fi
  done

  # (Other) IP protocols to ALLOW for certain INET hosts
  ######################################################
  unset IFS
  for rule in $INET_DMZ_HOST_OPEN_IP; do
    if parse_rule "$rule" INET_DMZ_HOST_OPEN_IP "interfaces-shosts-dhosts-protos"; then

      echo "  $(show_if_ip "$interfaces")Allowing $shosts(INET) to $dhosts(DMZ) for IP protocol(s): $protos"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for proto in $protos; do
            for interface in $interfaces; do
              iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -s $shost -d $dhost -p $proto -j ACCEPT
            done
          done
        done
      done
    fi
  done


  # TCP ports to DENY for certain INET hosts
  #########################################
  unset IFS
  for rule in $INET_DMZ_HOST_DENY_TCP; do
    if parse_rule "$rule" INET_DMZ_HOST_DENY_TCP "interfaces-shosts:ANYHOST-dhosts-ports:ANYPORT"; then

      echo "  $(show_if_ip "$interfaces")Denying $shosts(INET) to $dhosts(DMZ) for TCP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            for interface in $interfaces; do
              if [ "$DMZ_INPUT_DENY_LOG" != "0" ]; then
                iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -s $shost -d $dhost -p tcp --dport $port \
                  -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host INET->DMZ denied: "
              fi
              iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -s $shost -d $dhost -p tcp --dport $port -j DROP
            done
          done
        done
      done
    fi
  done

  # UDP ports to DENY for certain INET hosts
  #########################################
  unset IFS
  for rule in $INET_DMZ_HOST_DENY_UDP; do
    if parse_rule "$rule" INET_DMZ_HOST_DENY_UDP "interfaces-shosts:ANYHOST-dhosts-ports:ANYPORT"; then

      echo "  $(show_if_ip "$interfaces")Denying $shosts(INET) to $dhosts(DMZ) for UDP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            for interface in $interfaces; do
              if [ "$DMZ_INPUT_DENY_LOG" != "0" ]; then
                iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -s $shost -d $dhost -p udp --dport $port \
                  -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host INET->DMZ denied: "
              fi
              iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -s $shost -d $dhost -p udp --dport $port -j DROP
            done
          done
        done
      done
    fi
  done

  # (Other) IP protocols to DENY for certain INET hosts
  #####################################################
  unset IFS
  for rule in $INET_DMZ_HOST_DENY_IP; do
    if parse_rule "$rule" INET_DMZ_HOST_DENY_IP "interfaces-shosts:ANYHOST-dhosts-protos"; then

      echo "  $(show_if_ip "$interfaces")Denying $shosts(INET) to $dhosts(DMZ) for IP protocol(s): $protos"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for proto in $protos; do
            for interface in $interfaces; do
              if [ "$DMZ_INPUT_DENY_LOG" != "0" ]; then
                iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -s $shost -d $dhost -p $proto \
                  -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host INET->DMZ denied: "
              fi
              iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -s $shost -d $dhost -p $proto -j DROP
            done
          done
        done
      done
    fi
  done

  # Allow only certain TCP ports to be used from the INET->DMZ?
  #############################################################
  unset IFS
  for rule in $INET_DMZ_OPEN_TCP; do
    if parse_rule "$rule" INET_DMZ_OPEN_TCP "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Allowing TCP port(s): $ports"

      IFS=','
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -d $destip -p tcp --dport $port -j ACCEPT
          done
        done
      done
    fi
  done

  # Allow only certain UDP ports to be used from the INET->DMZ?
  #############################################################
  unset IFS
  for rule in $INET_DMZ_OPEN_UDP; do
    if parse_rule "$rule" INET_DMZ_OPEN_UDP "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Allowing UDP port(s): $ports"

      IFS=','
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -d $destip -p udp --dport $port -j ACCEPT
          done
        done
      done
    fi
  done

  # Allow only certain IP protocols to be used from the INET->DMZ?
  ################################################################
  unset IFS
  for rule in $INET_DMZ_OPEN_IP; do
    if parse_rule "$rule" INET_DMZ_OPEN_IP "interfaces-destips-protos"; then

      echo " $(show_if_ip "$interfaces" "$destips")Allowing IP protocol(s): $protos"

      IFS=','
      for proto in $protos; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -d $destip -p $proto -j ACCEPT
          done
        done
      done
    fi
  done

  # Allow ICMP-requests(ping) for INET->DMZ?
  ##########################################
  if [ "$INET_DMZ_OPEN_ICMP" = "1" ]; then
    echo "  Allowing ICMP-requests(ping)"
    iptables -A INET_DMZ_FORWARD_CHAIN -p icmp --icmp-type echo-request \
      -m limit --limit 20/second --limit-burst 100 -j ACCEPT
  fi

  # TCP ports to DENY for INET->DMZ
  #################################
  unset IFS
  for rule in $INET_DMZ_DENY_TCP; do
    if parse_rule "$rule" INET_DMZ_DENY_TCP "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying TCP port(s): $ports"

      IFS=','
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            if [ "$DMZ_INPUT_DENY_LOG" != "0" ]; then
              iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -d $destip -p tcp --dport $port -m limit \
                --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INET->DMZ denied: "
            fi
            iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -d $destip -p tcp --dport $port -j DROP
          done
        done
      done
    fi
  done

  # UDP ports to DENY for INET->DMZ
  #################################
  unset IFS
  for rule in $INET_DMZ_DENY_UDP; do
    if parse_rule "$rule" INET_DMZ_DENY_UDP "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying UDP port(s): $ports"

      IFS=','
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            if [ "$DMZ_INPUT_DENY_LOG" != "0" ]; then
              iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -d $destip -p udp --dport $port -m limit \
                --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INET->DMZ denied: "
            fi
            iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -d $destip -p udp --dport $port -j DROP
          done
        done
      done
    fi
  done

  # IP protocols to DENY for INET->DMZ
  ####################################
  unset IFS
  for rule in $INET_DMZ_DENY_IP; do
    if parse_rule "$rule" INET_DMZ_DENY_IP "interfaces-destips-protos"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying IP protocol(s): $protos"

      IFS=','
      for proto in $protos; do
        for destip in $destips; do
          for interface in $interfaces; do
            if [ "$DMZ_INPUT_DENY_LOG" != "0" ]; then
              iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -d $destip -p $proto -m limit \
                --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INET->DMZ denied: "
            fi
            iptables -A INET_DMZ_FORWARD_CHAIN -i $interface -d $destip -p $proto -j DROP
          done
        done
      done
    fi
  done

  # Log incoming ICMP-request packets?
  ####################################
  if [ "$ICMP_REQUEST_LOG" != "0" ]; then
    iptables -A INET_DMZ_FORWARD_CHAIN -p icmp --icmp-type echo-request \
      -m limit --limit 3/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-request: "
  fi

  echo "  Denying all other INET->DMZ packets"

  # Drop ICMP packets
  iptables -A INET_DMZ_FORWARD_CHAIN -p icmp --icmp-type echo-request -j DROP

  if [ "$DMZ_INPUT_DENY_LOG" != "0" ]; then
    iptables -A INET_DMZ_FORWARD_CHAIN \
      -m limit --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INET->DMZ denied: "
  fi
  iptables -A INET_DMZ_FORWARD_CHAIN -j DROP
}


###################################################
# Setup chain for the DMZ-to-INET forward traffic #
###################################################
setup_dmz_inet_forward_chain()
{
  echo " Setting up DMZ->INET policy"

  # TCP ports to ALLOW for certain DMZ hosts
  #########################################
  unset IFS
  for rule in $DMZ_INET_HOST_OPEN_TCP; do
    if parse_rule "$rule" DMZ_INET_HOST_OPEN_TCP "interfaces-shosts:ANYHOST-dhosts-ports:ANYPORT"; then

      echo "  $(show_if_ip "$interfaces" "$destips")Allowing $shosts(DMZ) to $dhosts(INET) for TCP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            for interface in $interfaces; do
              iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p tcp --dport $port -j ACCEPT
            done
          done
        done
      done
    fi
  done

  # UDP ports to ALLOW for certain DMZ hosts
  #########################################
  unset IFS
  for rule in $DMZ_INET_HOST_OPEN_UDP; do
    if parse_rule "$rule" DMZ_INET_HOST_OPEN_UDP "interfaces-shosts:ANYHOST-dhosts-ports:ANYPORT"; then

      echo "  $(show_if_ip "$interfaces")Allowing $shosts(DMZ) to $dhosts(INET) for UDP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            for interface in $interfaces; do
              iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p udp --dport $port -j ACCEPT
            done
          done
        done
      done
    fi
  done

  # (Other) IP protocols to ALLOW for certain DMZ hosts
  #####################################################
  unset IFS
  for rule in $DMZ_INET_HOST_OPEN_IP; do
    if parse_rule "$rule" DMZ_INET_HOST_OPEN_IP "interfaces-shosts:ANYHOST-dhosts-protos"; then

      echo "  $(show_if_ip "$interfaces")Allowing $shosts(DMZ) to $dhosts(INET) for IP protocol(s): $protos"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for proto in $protos; do
            for interface in $interfaces; do
              iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p $proto -j ACCEPT
            done
          done
        done
      done
    fi
  done


  # TCP ports to DENY for certain DMZ hosts
  #########################################
  unset IFS
  for rule in $DMZ_INET_HOST_DENY_TCP; do
    if parse_rule "$rule" DMZ_INET_HOST_DENY_TCP "interfaces-shosts:ANYHOST-dhosts-ports:ANYPORT"; then

      echo "  $(show_if_ip "$interfaces")Denying $shosts(DMZ) to $dhosts(INET) for TCP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            for interface in $interfaces; do
              if [ "$DMZ_OUTPUT_DENY_LOG" != "0" ]; then
                iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p tcp --dport $port \
                  -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host DMZ->INET denied: "
              fi
              iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p tcp --dport $port -j DROP
            done
          done
        done
      done
    fi
  done

  # UDP ports to DENY for certain DMZ hosts
  #########################################
  unset IFS
  for rule in $DMZ_INET_HOST_DENY_UDP; do
    if parse_rule "$rule" DMZ_INET_HOST_DENY_UDP "interfaces-shosts:ANYHOST-dhosts-ports:ANYPORT"; then

      echo "  $(show_if_ip "$interfaces")Denying $shosts(DMZ) to $dhosts(INET) for UDP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            for interface in $interfaces; do
              if [ "$DMZ_OUTPUT_DENY_LOG" != "0" ]; then
                iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p udp --dport $port \
                  -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host DMZ->INET denied: "
              fi
              iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p udp --dport $port -j DROP
            done
          done
        done
      done
    fi
  done

  # (Other) IP protocols to DENY for certain DMZ hosts
  #####################################################
  unset IFS
  for rule in $DMZ_INET_HOST_DENY_IP; do
    if parse_rule "$rule" DMZ_INET_HOST_DENY_IP "interfaces-shosts:ANYHOST-dhosts-protos"; then

      echo "  $(show_if_ip "$interfaces")Denying $shosts(DMZ) to $dhosts(INET) for IP protocol(s): $protos"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for proto in $protos; do
            for interface in $interfaces; do
              if [ "$DMZ_OUTPUT_DENY_LOG" != "0" ]; then
                iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p $proto \
                  -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host DMZ->INET denied: "
              fi
              iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p $proto -j DROP
            done
          done
        done
      done
    fi
  done

  # Allow only certain TCP ports to be used from the DMZ->INET?
  #############################################################
  unset IFS
  for rule in $DMZ_INET_OPEN_TCP; do
    if parse_rule "$rule" DMZ_INET_OPEN_TCP "interfaces-ports"; then

      echo " $(show_if_ip "$interfaces")Allowing TCP port(s): $ports"

      IFS=','
      for port in $ports; do
        for interface in $interfaces; do
          iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -p tcp --dport $port -j ACCEPT
        done
      done
    fi
  done

  # Allow only certain UDP ports to be used from the DMZ->INET?
  #############################################################
  unset IFS
  for rule in $DMZ_INET_OPEN_UDP; do
    if parse_rule "$rule" DMZ_INET_OPEN_UDP "interfaces-ports"; then

      echo " $(show_if_ip "$interfaces")Allowing UDP port(s): $ports"

      IFS=','
      for port in $ports; do
        for interface in $interfaces; do
          iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -p udp --dport $port -j ACCEPT
        done
      done
    fi
  done

  # Allow only certain IP protocols to be used from the DMZ->INET?
  ################################################################
  unset IFS
  for rule in $DMZ_INET_OPEN_IP; do
    if parse_rule "$rule" DMZ_INET_OPEN_IP "interfaces-protos"; then

      echo " $(show_if_ip "$interfaces")Allowing IP protocol(s): $protos"

      IFS=','
      for proto in $protos; do
        for interface in $interfaces; do
          iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -p $proto -j ACCEPT
        done
      done
    fi
  done

  # Allow ICMP-requests(ping) for DMZ->INET?
  ##########################################
  if [ "$DMZ_INET_OPEN_ICMP" != "0" ]; then
    echo "  Allowing ICMP-requests(ping)"
    iptables -A DMZ_INET_FORWARD_CHAIN -p icmp --icmp-type echo-request \
      -m limit --limit 20/second --limit-burst 100 -j ACCEPT
  fi

  # TCP ports to DENY for DMZ->INET
  #################################
  unset IFS
  for rule in $DMZ_INET_DENY_TCP; do
    if parse_rule "$rule" DMZ_INET_DENY_TCP "interfaces-ports"; then

      echo " $(show_if_ip "$interfaces")Denying TCP port(s): $ports"

      IFS=','
      for port in $ports; do
        for interface in $interfaces; do
          if [ "$DMZ_OUTPUT_DENY_LOG" != "0" ]; then
            iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -p tcp --dport $port -m limit \
              --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:DMZ->INET denied: "
          fi
          iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -p tcp --dport $port -j DROP
        done
      done
    fi
  done

  # UDP ports to DENY for DMZ->INET
  #################################
  unset IFS
  for rule in $DMZ_INET_DENY_UDP; do
    if parse_rule "$rule" DMZ_INET_DENY_UDP "interfaces-ports"; then

      echo " $(show_if_ip "$interfaces")Denying UDP port(s): $ports"

      IFS=','
      for port in $ports; do
        for interface in $interfaces; do
          if [ "$DMZ_OUTPUT_DENY_LOG" != "0" ]; then
            iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -p udp --dport $port -m limit \
              --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:DMZ->INET denied: "
          fi
          iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -p udp --dport $port -j DROP
        done
      done
    fi
  done

  # IP protocols to DENY for DMZ->INET
  ####################################
  unset IFS
  for rule in $DMZ_INET_DENY_IP; do
    if parse_rule "$rule" DMZ_INET_DENY_IP "interfaces-protos"; then

      echo " $(show_if_ip "$interfaces")Denying IP protocol(s): $protos"

      IFS=','
      for proto in $protos; do
        for interface in $interfaces; do
          if [ "$DMZ_OUTPUT_DENY_LOG" != "0" ]; then
            iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -p $proto -m limit \
              --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:DMZ->INET denied: "
          fi
          iptables -A DMZ_INET_FORWARD_CHAIN -o $interface -p $proto -j DROP
        done
      done
    fi
  done

  # Log incoming ICMP-request packets?
  ####################################
  if [ "$ICMP_REQUEST_LOG" != "0" ]; then
    iptables -A DMZ_INET_FORWARD_CHAIN -p icmp --icmp-type echo-request \
      -m limit --limit 3/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-request: "
  fi

  # Drop ICMP packets
  iptables -A DMZ_INET_FORWARD_CHAIN -p icmp --icmp-type echo-request -j DROP

  # Set the default policy (switch to DROP for a protocol when xxx_OPEN_xxx variable is used)
  ###########################################################################################
  if [ -z "$DMZ_INET_OPEN_TCP" -a -z "$DMZ_INET_HOST_OPEN_TCP" -a \
       -z "$DMZ_INET_OPEN_UDP" -a -z "$DMZ_INET_HOST_OPEN_UDP" -a \
       -z "$DMZ_INET_OPEN_IP"  -a -z "$DMZ_INET_HOST_OPEN_IP"  -a -z "$DMZ_INET_DEFAULT_POLICY_DROP" ] \
     || [ "$DMZ_INET_DEFAULT_POLICY_DROP" = "0" ]; then
    echo "  Allowing all (other) ports/protocols"
    iptables -A DMZ_INET_FORWARD_CHAIN -j ACCEPT
  else
    if [ "$DMZ_OUTPUT_DENY_LOG" != "0" ]; then
      iptables -A DMZ_INET_FORWARD_CHAIN -m limit \
        --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:DMZ->INET denied: "
    fi
    echo "  Denying all (other) ports/protocols"
    iptables -A DMZ_INET_FORWARD_CHAIN -j DROP
  fi
}


#########################################
# Setup chain for the LAN input traffic #
#########################################
setup_int_input_chain()
{
  # TCP ports to OPEN for certain LAN hosts
  #########################################
  unset IFS
  for rule in $LAN_HOST_OPEN_TCP; do
    if parse_rule "$rule" LAN_HOST_OPEN_TCP "hosts-ports"; then

      echo " Allowing $hosts(LAN) for TCP port(s): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          iptables -A INT_INPUT_CHAIN -s $host -p tcp --dport $port -j ACCEPT
        done
      done
    fi
  done

  # UDP ports to OPEN for certain LAN hosts
  #########################################
  unset IFS
  for rule in $LAN_HOST_OPEN_UDP; do
    if parse_rule "$rule" LAN_HOST_OPEN_UDP "hosts-ports"; then

      echo " Allowing $hosts(LAN) for UDP port(s): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          iptables -A INT_INPUT_CHAIN -s $host -p udp --dport $port -j ACCEPT
        done
      done
    fi
  done

  # IP protocols to OPEN for certain LAN hosts
  ############################################
  unset IFS
  for rule in $LAN_HOST_OPEN_IP; do
    if parse_rule "$rule" LAN_HOST_OPEN_IP "hosts-protos"; then

      echo " Allowing $hosts(LAN) for IP protocol(s): $protos"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for proto in $protos; do
          iptables -A INT_INPUT_CHAIN -s $host -p $proto -j ACCEPT
        done
      done
    fi
  done

  # TCP ports to DENY for certain LAN hosts
  #########################################
  unset IFS
  for rule in $LAN_HOST_DENY_TCP; do
    if parse_rule "$rule" LAN_HOST_DENY_TCP "hosts:ANYHOST-ports:ANYPORT"; then

      echo " Denying $hosts(LAN) for TCP port(s): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          if [ "$LAN_INPUT_DENY_LOG" != "0" ]; then
            iptables -A INT_INPUT_CHAIN -s $host -p tcp --dport $port \
              -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host LAN denied: "
          fi
          iptables -A INT_INPUT_CHAIN -s $host -p tcp --dport $port -j DROP
        done
      done
    fi
  done

  # UDP ports to DENY for certain LAN hosts
  #########################################
  unset IFS
  for rule in $LAN_HOST_DENY_UDP; do
    if parse_rule "$rule" LAN_HOST_DENY_UDP "hosts:ANYHOST-ports:ANYPORT"; then

      echo " Denying $hosts(LAN) for UDP port(s): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          if [ "$LAN_INPUT_DENY_LOG" != "0" ]; then
            iptables -A INT_INPUT_CHAIN -s $host -p udp --dport $port \
              -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host LAN denied: "
          fi
          iptables -A INT_INPUT_CHAIN -s $host -p udp --dport $port -j DROP
        done
      done
    fi
  done

  # IP protocols to DENY for certain LAN hosts
  ############################################
  unset IFS
  for rule in $LAN_HOST_DENY_IP; do
    if parse_rule "$rule" LAN_HOST_DENY_IP "hosts:ANYHOST-protos"; then

      echo " Denying $hosts(LAN) for IP protocol(s): $protos"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for proto in $protos; do
          if [ "$LAN_INPUT_DENY_LOG" != "0" ]; then
            iptables -A INT_INPUT_CHAIN -s $host -p $proto \
              -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host LAN denied: "
          fi
          iptables -A INT_INPUT_CHAIN -s $host -p $proto -j DROP
        done
      done
    fi
  done

  # Allow only certain TCP ports to be used from the LAN?
  #######################################################
  if [ -n "$LAN_OPEN_TCP" ]; then
    echo " Allowing TCP port(s): $LAN_OPEN_TCP"
    IFS=' ,'
    for port in $LAN_OPEN_TCP; do
      iptables -A INT_INPUT_CHAIN -p tcp --dport $port -j ACCEPT
    done
  fi

  # Allow only certain UDP ports to be used from the LAN?
  #######################################################
  if [ -n "$LAN_OPEN_UDP" ]; then
    echo " Allowing UDP port(s): $LAN_OPEN_UDP"
    IFS=' ,'
    for port in $LAN_OPEN_UDP; do
      iptables -A INT_INPUT_CHAIN -p udp --dport $port -j ACCEPT
    done
  fi

  # Allow only certain IP protocols to be used from the LAN?
  ##########################################################
  if [ -n "$LAN_OPEN_IP" ]; then
    echo " Allowing IP protocol(s): $LAN_OPEN_IP"
    IFS=' ,'
    for proto in $LAN_OPEN_IP; do
      iptables -A INT_INPUT_CHAIN -p $proto -j ACCEPT
    done
  fi

  # Allow world to send ICMP packets?
  ###################################
  if [ "$LAN_OPEN_ICMP" != "0" ]; then
    echo " Allowing ICMP-requests(ping)"
    iptables -A INT_INPUT_CHAIN -p icmp --icmp-type echo-request -m limit --limit 20/second --limit-burst 100 -j ACCEPT
  fi

  # TCP ports to DENY for LAN hosts
  #################################
  if [ -n "$LAN_DENY_TCP" ]; then
    echo " Denying TCP port(s): $LAN_DENY_TCP"
    IFS=' ,'
    for port in $LAN_DENY_TCP; do
      if [ "$LAN_INPUT_DENY_LOG" != "0" ]; then
        iptables -A INT_INPUT_CHAIN -p tcp --dport $port -m limit \
          --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:LAN-INPUT denied: "
      fi
      iptables -A INT_INPUT_CHAIN -p tcp --dport $port -j DROP
    done
  fi

  # UDP ports to DENY for LAN hosts
  #################################
  if [ -n "$LAN_DENY_UDP" ]; then
    echo " Denying UDP port(s): $LAN_DENY_UDP"
    IFS=' ,'
    for port in $LAN_DENY_UDP; do
      if [ "$LAN_INPUT_DENY_LOG" != "0" ]; then
        iptables -A INT_INPUT_CHAIN -p udp --dport $port -m limit \
          --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:LAN-INPUT denied: "
      fi
      iptables -A INT_INPUT_CHAIN -p udp --dport $port -j DROP
    done
  fi

  # IP protocols to DENY for LAN hosts
  ####################################
  if [ -n "$LAN_DENY_IP" ]; then
    echo " Denying IP protocol(s): $LAN_DENY_IP"
    IFS=' ,'
    for proto in $LAN_DENY_IP; do
      if [ "$LAN_INPUT_DENY_LOG" != "0" ]; then
        iptables -A INT_INPUT_CHAIN -p $proto -m limit \
          --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:LAN-INPUT denied: "
      fi
      iptables -A INT_INPUT_CHAIN -p $proto -j DROP
    done
  fi

  # Log incoming ICMP-request packets?
  ####################################
  if [ "$ICMP_REQUEST_LOG" != "0" ]; then
    iptables -A INT_INPUT_CHAIN -p icmp --icmp-type echo-request \
      -m limit --limit 3/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-request: "
  fi

  # Drop ICMP packets
  iptables -A INT_INPUT_CHAIN -p icmp --icmp-type echo-request -j DROP

  # Set the default policy
  ########################
  if [ -z "$LAN_OPEN_TCP" -a -z "$LAN_HOST_OPEN_TCP" -a \
       -z "$LAN_OPEN_UDP" -a -z "$LAN_HOST_OPEN_UDP" -a \
       -z "$LAN_OPEN_IP"  -a -z "$LAN_HOST_OPEN_IP"  -a -z "$LAN_DEFAULT_POLICY_DROP" ] \
     || [ "$LAN_DEFAULT_POLICY_DROP" = "0" ]; then
    echo " Allowing all (other) ports/protocols"
    iptables -A INT_INPUT_CHAIN -j ACCEPT
  else
    echo " Denying all (other) ports/protocols"
    if [ "$LAN_INPUT_DENY_LOG" != "0" ]; then
      iptables -A INT_INPUT_CHAIN -m limit \
        --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:LAN-INPUT denied: "
    fi
    iptables -A INT_INPUT_CHAIN -j DROP
  fi
}


##################################################
# Setup chain for the LAN-to-LAN forward traffic #
##################################################
setup_lan_lan_forward_chain()
{
  local rtn_val=1

  echo " Setting up LAN->LAN policy"

  # TCP ports to ALLOW for certain Inter-LAN hosts
  ################################################
  unset IFS
  for rule in $LAN_LAN_HOST_OPEN_TCP; do
    if parse_rule "$rule" LAN_LAN_HOST_OPEN_TCP "shosts:ANYHOST-dhosts-ports:ANYPORT"; then

      echo "  Allowing $shosts(LAN) to $dhosts(LAN) for TCP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            iptables -A LAN_LAN_FORWARD_CHAIN -s $shost -d $dhost -p tcp --dport $port -j ACCEPT
            rtn_val=0
          done
        done
      done
    fi
  done

  # UDP ports to ALLOW for certain Inter-LAN hosts
  ################################################
  unset IFS
  for rule in $LAN_LAN_HOST_OPEN_UDP; do
    if parse_rule "$rule" LAN_LAN_HOST_OPEN_UDP "shosts:ANYHOST-dhosts-ports:ANYPORT"; then

      echo "  Allowing $shosts(LAN) to $dhosts(LAN) for UDP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            iptables -A LAN_LAN_FORWARD_CHAIN -s $shost -d $dhost -p udp --dport $port -j ACCEPT
            rtn_val=0
          done
        done
      done
    fi
  done

  # IP protocol(s) to ALLOW for certain Inter-LAN hosts
  #####################################################
  unset IFS
  for rule in $LAN_LAN_HOST_OPEN_IP; do
    if parse_rule "$rule" LAN_LAN_HOST_OPEN_IP "shosts:ANYHOST-dhosts-protos"; then

      echo "  Allowing $shosts(LAN) to $dhosts(LAN) for IP protocol(s): $protos"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for proto in $protos; do
            iptables -A LAN_LAN_FORWARD_CHAIN -s $shost -d $dhost -p $proto -j ACCEPT
            rtn_val=0
          done
        done
      done
    fi
  done

  return $rtn_val
}


###################################################
# Setup chain for the LAN-to-INET forward traffic #
###################################################
setup_lan_inet_forward_chain()
{
  echo " Setting up LAN->INET policy"

  # TCP ports to ALLOW for certain LAN hosts
  #########################################
  unset IFS
  for rule in $LAN_INET_HOST_OPEN_TCP; do
    if parse_rule "$rule" LAN_INET_HOST_OPEN_TCP "interfaces-shosts:ANYHOST-dhosts-ports:ANYPORT"; then

      echo "  $(show_if_ip "$interfaces")Allowing $shosts(LAN) to $dhosts(INET) for TCP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            for interface in $interfaces; do
              iptables -A LAN_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p tcp --dport $port -j ACCEPT
            done
          done
        done
      done
    fi
  done

  # UDP ports to ALLOW for certain LAN hosts
  #########################################
  unset IFS
  for rule in $LAN_INET_HOST_OPEN_UDP; do
    if parse_rule "$rule" LAN_INET_HOST_OPEN_UDP "interfaces-shosts:ANYHOST-dhosts-ports:ANYPORT"; then

      echo "  $(show_if_ip "$interfaces")Allowing $shosts(LAN) to $dhosts(INET) for UDP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            for interface in $interfaces; do
              iptables -A LAN_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p udp --dport $port -j ACCEPT
            done
          done
        done
      done
    fi
  done

  # (Other) IP protocols to ALLOW for certain LAN hosts
  #####################################################
  unset IFS
  for rule in $LAN_INET_HOST_OPEN_IP; do
    if parse_rule "$rule" LAN_INET_HOST_OPEN_IP "interfaces-shosts:ANYHOST-dhosts-protos"; then

      echo "  $(show_if_ip "$interfaces")Allowing $shosts(LAN) to $dhosts(INET) for IP protocol(s): $protos"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for proto in $protos; do
            for interface in $interfaces; do
              iptables -A LAN_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p $proto -j ACCEPT
            done
          done
        done
      done
    fi
  done

  # TCP ports to DENY for certain LAN hosts
  #########################################
  unset IFS
  for rule in $LAN_INET_HOST_DENY_TCP; do
    if parse_rule "$rule" LAN_INET_HOST_DENY_TCP "interfaces-shosts:ANYHOST-dhosts-ports:ANYPORT"; then

      echo "  $(show_if_ip "$interfaces")Denying $shosts(LAN) to $dhosts(INET) for TCP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            for interface in $interfaces; do
              if [ "$LAN_OUTPUT_DENY_LOG" != "0" ]; then
                iptables -A LAN_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p tcp --dport $port \
                  -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host LAN->INET denied: "
              fi
              iptables -A LAN_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p tcp --dport $port -j DROP
            done
          done
        done
      done
    fi
  done

  # UDP ports to DENY for certain LAN hosts
  #########################################
  unset IFS
  for rule in $LAN_INET_HOST_DENY_UDP; do
    if parse_rule "$rule" LAN_INET_HOST_DENY_UDP "interfaces-shosts:ANYHOST-dhosts-ports:ANYPORT"; then

      echo "  $(show_if_ip "$interfaces")Denying $shosts(LAN) to $dhosts(INET) for UDP port(s): $ports"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for port in $ports; do
            for interface in $interfaces; do
              if [ "$LAN_OUTPUT_DENY_LOG" != "0" ]; then
                iptables -A LAN_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p udp --dport $port \
                  -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host LAN->INET denied: "
              fi
              iptables -A LAN_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p udp --dport $port -j DROP
            done
          done
        done
      done
    fi
  done

  # (Other) IP protocols to DENY for certain LAN hosts
  #####################################################
  unset IFS
  for rule in $LAN_INET_HOST_DENY_IP; do
    if parse_rule "$rule" LAN_INET_HOST_DENY_IP "interfaces-shosts:ANYHOST-dhosts-protos"; then

      echo "  $(show_if_ip "$interfaces")Denying $shosts(LAN) to $dhosts(INET) for IP protocol(s): $protos"

      IFS=','
      for shost in `ip_range "$shosts"`; do
        for dhost in `ip_range "$dhosts"`; do
          for proto in $protos; do
            for interface in $interfaces; do
              if [ "$LAN_OUTPUT_DENY_LOG" != "0" ]; then
                iptables -A LAN_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p $proto \
                  -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host LAN->INET denied: "
              fi
              iptables -A LAN_INET_FORWARD_CHAIN -o $interface -s $shost -d $dhost -p $proto -j DROP
            done
          done
        done
      done
    fi
  done

  # Allow only certain udp ports to be used from the LAN->INET?
  #############################################################
  unset IFS
  for rule in $LAN_INET_OPEN_TCP; do
    if parse_rule "$rule" LAN_INET_OPEN_TCP "interfaces-ports"; then

      echo "  $(show_if_ip "$interfaces")Allowing TCP port(s): $ports"

      IFS=','
      for port in $ports; do
        for interface in $interfaces; do
          iptables -A LAN_INET_FORWARD_CHAIN -o $interface -p tcp --dport $port -j ACCEPT
        done
      done
    fi
  done

  # Allow only certain UDP ports to be used from the LAN->INET?
  #############################################################
  unset IFS
  for rule in $LAN_INET_OPEN_UDP; do
    if parse_rule "$rule" LAN_INET_OPEN_UDP "interfaces-ports"; then

      echo "  $(show_if_ip "$interfaces")Allowing UDP port(s): $ports"

      IFS=','
      for port in $ports; do
        for interface in $interfaces; do
          iptables -A LAN_INET_FORWARD_CHAIN -o $interface -p udp --dport $port -j ACCEPT
        done
      done
    fi
  done

  # Allow only certain IP protocols to be used from the LAN->INET?
  ################################################################
  unset IFS
  for rule in $LAN_INET_OPEN_IP; do
    if parse_rule "$rule" LAN_INET_OPEN_IP "interfaces-protos"; then

      echo "  $(show_if_ip "$interfaces")Allowing IP protocol(s): $protos"

      IFS=','
      for proto in $protos; do
        for interface in $interfaces; do
          iptables -A LAN_INET_FORWARD_CHAIN -o $interface -p $proto -j ACCEPT
        done
      done
    fi
  done

  # Allow ICMP-requests(ping) for LAN->INET?
  ##########################################
  if [ "$LAN_INET_OPEN_ICMP" != "0" ]; then
    echo "  Allowing ICMP-requests(ping)"
    iptables -A LAN_INET_FORWARD_CHAIN -p icmp --icmp-type echo-request \
      -m limit --limit 20/second --limit-burst 100 -j ACCEPT
  fi

  # TCP ports to DENY for LAN->INET
  #################################
  unset IFS
  for rule in $LAN_INET_DENY_TCP; do
    if parse_rule "$rule" LAN_INET_DENY_TCP "interfaces-ports"; then

      echo "  $(show_if_ip "$interfaces")Denying TCP port(s): $ports"

      IFS=','
      for port in $ports; do
        for interface in $interfaces; do
          if [ "$LAN_OUTPUT_DENY_LOG" != "0" ]; then
            iptables -A LAN_INET_FORWARD_CHAIN -o $interface -p tcp --dport $port -m limit \
              --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:LAN->INET denied: "
          fi
          iptables -A LAN_INET_FORWARD_CHAIN -o $interface -p tcp --dport $port -j DROP
        done
      done
    fi
  done

  # UDP ports to DENY for LAN->INET
  #################################
  unset IFS
  for rule in $LAN_INET_DENY_UDP; do
    if parse_rule "$rule" LAN_INET_DENY_UDP "interfaces-ports"; then

      echo "  $(show_if_ip "$interfaces")Denying UDP port(s): $ports"

      IFS=','
      for port in $ports; do
        for interface in $interfaces; do
          if [ "$LAN_OUTPUT_DENY_LOG" != "0" ]; then
            iptables -A LAN_INET_FORWARD_CHAIN -o $interface -p udp --dport $port -m limit \
              --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:LAN->INET denied: "
          fi
          iptables -A LAN_INET_FORWARD_CHAIN -o $interface -p udp --dport $port -j DROP
        done
      done
    fi
  done

  # IP protocols to DENY for LAN->INET
  ####################################
  unset IFS
  for rule in $LAN_INET_DENY_IP; do
    if parse_rule "$rule" LAN_INET_DENY_IP "interfaces-protos"; then

      echo "  $(show_if_ip "$interfaces")Denying IP protocol(s): $protos"

      IFS=','
      for proto in $protos; do
        for interface in $interfaces; do
          if [ "$LAN_OUTPUT_DENY_LOG" != "0" ]; then
            iptables -A LAN_INET_FORWARD_CHAIN -o $interface -p $proto -m limit \
              --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:LAN->INET denied: "
          fi
          iptables -A LAN_INET_FORWARD_CHAIN -o $interface -p $proto -j DROP
        done
      done
    fi
  done

  # Log incoming ICMP-request packets?
  ####################################
  if [ "$ICMP_REQUEST_LOG" != "0" ]; then
    iptables -A LAN_INET_FORWARD_CHAIN -p icmp --icmp-type echo-request \
      -m limit --limit 3/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-request: "
  fi

  # Drop ICMP packets
  iptables -A LAN_INET_FORWARD_CHAIN -p icmp --icmp-type echo-request -j DROP

  # Set the default policy (switch to DROP for a protocol when xxx_OPEN_xxx variable is used)
  ###########################################################################################
  if [ -z "$LAN_INET_OPEN_TCP" -a -z "$LAN_INET_HOST_OPEN_TCP" -a \
       -z "$LAN_INET_OPEN_UDP" -a -z "$LAN_INET_HOST_OPEN_UDP" -a \
       -z "$LAN_INET_OPEN_IP"  -a -z "$LAN_INET_HOST_OPEN_IP"  -a -z "$LAN_INET_DEFAULT_POLICY_DROP" ] \
     || [ "$LAN_INET_DEFAULT_POLICY_DROP" = "0" ]; then
    echo "  Allowing all (other) ports/protocols"
    iptables -A LAN_INET_FORWARD_CHAIN -j ACCEPT
  else
    if [ "$LAN_OUTPUT_DENY_LOG" != "0" ]; then
      iptables -A LAN_INET_FORWARD_CHAIN -m limit \
        --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:LAN->INET denied: "
    fi
    echo "  Denying all (other) ports/protocols"
    iptables -A LAN_INET_FORWARD_CHAIN -j DROP
  fi
}


######################################################################################################################
## Chain EXT_INPUT_CHAIN - Checks all incoming packets for the EXTERNAL interface(s)                                ##
######################################################################################################################
setup_ext_input_chain()
{
  ## Log scanning of port 0 fingerprinting
  ########################################
  if [ "$SCAN_LOG" != "0" ]; then
    iptables -A EXT_INPUT_CHAIN -p tcp --dport 0 \
      -m limit --limit 6/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Port 0 OS fingerprint: "
    iptables -A EXT_INPUT_CHAIN -p udp --dport 0 \
      -m limit --limit 6/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Port 0 OS fingerprint: "
  fi

  # Drop port 0 scan packets
  ##########################
  iptables -A EXT_INPUT_CHAIN -p tcp --dport 0 -j POST_INPUT_DROP_CHAIN
  iptables -A EXT_INPUT_CHAIN -p udp --dport 0 -j POST_INPUT_DROP_CHAIN

  ## Log scanning of source port 0
  ################################
  if [ "$SCAN_LOG" != "0" ]; then
    iptables -A EXT_INPUT_CHAIN -p tcp --sport 0 \
      -m limit --limit 6/h --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:TCP source port 0: "
    iptables -A EXT_INPUT_CHAIN -p udp --sport 0 \
      -m limit --limit 6/h --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:UDP source port 0: "
  fi

  # Drop source port 0 packets
  ############################
  iptables -A EXT_INPUT_CHAIN -p tcp --sport 0 -j POST_INPUT_DROP_CHAIN
  iptables -A EXT_INPUT_CHAIN -p udp --sport 0 -j POST_INPUT_DROP_CHAIN

  # Here we add support for DHCP assigned IP
  ##########################################
  if [ "$EXT_IF_DHCP_IP" = "1" ]; then
    echo " Enabling support for DHCP-assigned-IP (DHCP client)"
    # Allow this host to be an DHCP client:
    ip4tables -A EXT_INPUT_CHAIN -p udp --sport 67 --dport 68 -j ACCEPT
  fi
  if [ "$EXT_IF_DHCP_IP" = "1" -o "$EXT_IF_DHCPV6_IPV6" = "1" ]; then
    if [ "$IPV6_SUPPORT" = "1" ]; then
      # Allow this host to be an DHCPv6 client:
      ip6tables -A EXT_INPUT_CHAIN -s fe80::/10 -p udp --sport 547 --dport 546 -j ACCEPT
    fi
  fi

  # Support for a DHCP/BootP service on the EXTERNAL interface
  ############################################################
  if [ "$EXTERNAL_DHCP_SERVER" = "1" ]; then
    echo " Enabling support for DHCP/BOOTP (DHCP server) for subnet(s): $EXTERNAL_NET"
    IFS=' ,'
    for net in $EXTERNAL_NET; do
      # Allow this host to be a DHCP/BOOTP-server:
      ip4tables -A EXT_INPUT_CHAIN -d 255.255.255.255 -p udp --dport 67 -j ACCEPT
      ip4tables -A EXT_INPUT_CHAIN -s $net -p udp --dport 67 -j ACCEPT
#      ip4tables -A EXT_INPUT_CHAIN -d 255.255.255.255 -p udp --sport 68 --dport 67 -j ACCEPT
#      ip4tables -A EXT_INPUT_CHAIN -s $net -p udp --sport 68 --dport 67 -j ACCEPT

      # Extra rules to allow packets from other dhcp servers in the same segment
      ip4tables -A EXT_INPUT_CHAIN -s $net -d 255.255.255.255 -p udp --sport 67 --dport 68 -j ACCEPT
    done
  fi

  # Support for a DHCPv6 service on the EXTERNAL interface
  ########################################################
  if [ "$IPV6_SUPPORT" = "1" -a "$EXTERNAL_DHCPV6_SERVER" = "1" ]; then
    echo " Enabling support for DHCPv6 server on external interface(s)"
    # Allow only Link-Local clients
    ip6tables -A EXT_INPUT_CHAIN -s fe80::/10 -p udp --dport 547 -j ACCEPT
  fi

  # This is the fix(hack) for nmb broadcast packets (nmblookup/Samba)
  ###################################################################
  if [ "$NMB_BROADCAST_FIX" = "1" ]; then
    echo " Enabling support for NMB-broadcasts(Samba) for subnet(s): $EXTERNAL_NET"
    IFS=' ,'
    for net in $EXTERNAL_NET; do
      ip4tables -A EXT_INPUT_CHAIN -s $net -p udp --sport 137 --dport "$LOCAL_PORT_RANGE" -j ACCEPT
    done
  fi

  # Enable logging of blocked hosts?
  ##################################
  if [ "$BLOCKED_HOST_LOG" = "1" ]; then
    echo " Logging of explicitly blocked hosts inbound/outbound enabled"
  elif [ "$BLOCKED_HOST_LOG" = "2" ]; then
    echo " Logging of explicitly blocked hosts inbound enabled"
  elif [ "$BLOCKED_HOST_LOG" = "3" ]; then
    echo " Logging of explicitly blocked hosts outbound enabled"
  else
    echo " Logging of explicitly blocked hosts disabled"
  fi

  # Enable logging of denied output connections?
  ##############################################
  if [ "$INET_OUTPUT_DENY_LOG" != "0" ]; then
    echo " Logging of denied local output connections enabled"
  else
    echo " Logging of denied local output connections disabled"
  fi

  # Add TCP ports to allow for certain hosts
  ##########################################
  unset IFS
  for rule in $HOST_OPEN_TCP; do
    if parse_rule "$rule" HOST_OPEN_TCP "interfaces-destips-hosts-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Allowing $hosts for TCP port(s): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p tcp --dport $port -j ACCEPT
            done
          done
        done
      done
    fi
  done


  # Add UDP ports to allow for certain hosts
  ##########################################
  unset IFS
  for rule in $HOST_OPEN_UDP; do
    if parse_rule "$rule" HOST_OPEN_UDP "interfaces-destips-hosts-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Allowing $hosts for UDP port(s): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p udp --dport $port -j ACCEPT
            done
          done
        done
      done
    fi
  done


  # Add IP protocols to allow for certain hosts
  #############################################
  unset IFS
  for rule in $HOST_OPEN_IP; do
    if parse_rule "$rule" HOST_OPEN_IP "interfaces-destips-hosts-protos"; then

      echo " $(show_if_ip "$interfaces" "$destips")Allowing $hosts for IP protocol(s): $protos"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for proto in $protos; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p $proto -j ACCEPT
            done
          done
        done
      done
    fi
  done


  # Add ICMP to allow for certain hosts
  #####################################
  unset IFS
  for rule in $HOST_OPEN_ICMP; do
    if parse_rule "$rule" HOST_OPEN_ICMP "interfaces-destips-hosts"; then

      echo " $(show_if_ip "$interfaces" "$destips")Allowing $hosts for ICMP-requests(ping)"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p icmp --icmp-type echo-request -j ACCEPT
          done
        done
      done
    fi
  done


  # Add TCP ports to REJECT for certain hosts but NOT logged
  ##########################################################
  unset IFS
  for rule in $HOST_REJECT_TCP_NOLOG; do
    if parse_rule "$rule" HOST_REJECT_TCP_NOLOG "interfaces-destips-hosts-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Rejecting $hosts for TCP port(s) (NO LOG): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p tcp --dport $port -j REJECT --reject-with tcp-reset
            done
          done
        done
      done
    fi
  done


  # Add UDP ports to REJECT for certain hosts NOT logged
  ######################################################
  unset IFS
  for rule in $HOST_REJECT_UDP_NOLOG; do
    if parse_rule "$rule" HOST_REJECT_UDP_NOLOG "interfaces-destips-hosts-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Rejecting $hosts for UDP port(s) (NO LOG): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for destip in $destips; do
            for interface in $interfaces; do
              ip4tables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p udp --dport $port -j REJECT --reject-with icmp-host-unreachable
              if [ "$IPV6_SUPPORT" = "1" ]; then
                ip6tables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p udp --dport $port -j REJECT --reject-with icmp6-addr-unreachable
              fi
            done
          done
        done
      done
    fi
  done


  # Add TCP ports to REJECT for certain hosts
  ###########################################
  unset IFS
  for rule in $HOST_REJECT_TCP; do
    if parse_rule "$rule" HOST_REJECT_TCP "interfaces-destips-hosts-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Rejecting $hosts for TCP port(s): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p tcp --dport $port \
                -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Hostwise TCP rejected: "

              iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p tcp --dport $port -j REJECT --reject-with tcp-reset
            done
          done
        done
      done
    fi
  done


  # Add UDP ports to REJECT for certain hosts
  ###########################################
  unset IFS
  for rule in $HOST_REJECT_UDP; do
    if parse_rule "$rule" HOST_REJECT_UDP "interfaces-destips-hosts-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Rejecting $hosts for UDP port(s): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p udp --dport $port \
                -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Hostwise UDP rejected: "

              ip4tables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p udp --dport $port -j REJECT --reject-with icmp-host-unreachable
              if [ "$IPV6_SUPPORT" = "1" ]; then
                ip6tables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p udp --dport $port -j REJECT --reject-with icmp6-addr-unreachable
              fi
            done
          done
        done
      done
    fi
  done


  # Add TCP ports to DENY for certain hosts but NOT logged
  ########################################################
  unset IFS
  for rule in $HOST_DENY_TCP_NOLOG; do
    if parse_rule "$rule" HOST_DENY_TCP_NOLOG "interfaces-destips-hosts-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying $hosts for TCP port(s) (NO LOG): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p tcp --dport $port -j POST_INPUT_DROP_CHAIN
            done
          done
        done
      done
    fi
  done


  # Add UDP ports to DENY for certain hosts but NOT logged
  ########################################################
  unset IFS
  for rule in $HOST_DENY_UDP_NOLOG; do
    if parse_rule "$rule" HOST_DENY_UDP_NOLOG "interfaces-destips-hosts-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying $hosts for UDP port(s) (NO LOG): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p udp --dport $port -j POST_INPUT_DROP_CHAIN
            done
          done
        done
      done
    fi
  done

  # Add IP protocols to DENY for certain hosts but NOT logged
  ###########################################################
  unset IFS
  for rule in $HOST_DENY_IP_NOLOG; do
    if parse_rule "$rule" HOST_DENY_IP_NOLOG "interfaces-destips-hosts-protos"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying $hosts for IP protocol(s) (NO LOG): $protos"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for proto in $protos; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p $proto -j POST_INPUT_DROP_CHAIN
            done
          done
        done
      done
    fi
  done

  # Add ICMP-request to DENY for certain hosts but NOT logged
  ############################################################
  unset IFS
  for rule in $HOST_DENY_ICMP_NOLOG; do
    if parse_rule "$rule" HOST_DENY_ICMP_NOLOG "interfaces-destips-hosts"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying $hosts for ICMP-requests(ping)"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p icmp --icmp-type echo-request -j POST_INPUT_DROP_CHAIN
          done
        done
      done
    fi
  done


  # Add TCP ports to DENY for certain hosts
  #########################################
  unset IFS
  for rule in $HOST_DENY_TCP; do
    if parse_rule "$rule" HOST_DENY_TCP "interfaces-destips-hosts-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying $hosts for TCP port(s): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p tcp --dport $port \
                -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host INET denied: "

              iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p tcp --dport $port -j POST_INPUT_DROP_CHAIN
            done
          done
        done
      done
    fi
  done


  # Add UDP ports to DENY for certain hosts
  #########################################
  unset IFS
  for rule in $HOST_DENY_UDP; do
    if parse_rule "$rule" HOST_DENY_UDP "interfaces-destips-hosts-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying $hosts for UDP port(s): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p udp --dport $port \
                -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host INET denied: "

              iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -p udp --dport $port -j POST_INPUT_DROP_CHAIN
            done
          done
        done
      done
    fi
  done


  # Add IP protocols to DENY for certain hosts
  ############################################
  unset IFS
  for rule in $HOST_DENY_IP; do
    if parse_rule "$rule" HOST_DENY_IP "interfaces-destips-hosts-protos"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying $hosts for IP protocol(s): $protos"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for proto in $protos; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A EXT_INPUT_CHAIN -s $host -p $proto \
                 -m limit --limit 1/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host INET denied: "

              iptables -A EXT_INPUT_CHAIN -s $host -p $proto -j POST_INPUT_DROP_CHAIN
            done
          done
        done
      done
    fi
  done


  # Add ICMP-request to DENY for certain hosts
  ############################################
  unset IFS
  for rule in $HOST_DENY_ICMP; do
    if parse_rule "$rule" HOST_DENY_ICMP "interfaces-destips-hosts"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying $hosts for ICMP-requests(ping)"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for destip in $destips; do
          for interface in $interfaces; do
            if [ "$ICMP_DROP_LOG" != "0" ]; then
              iptables -A EXT_INPUT_CHAIN -s $host -p icmp --icmp-type echo-request -m limit --limit 1/h --limit-burst 1 \
                -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host INET denied: "
            fi
            iptables -A EXT_INPUT_CHAIN -s $host -p icmp --icmp-type echo-request -j POST_INPUT_DROP_CHAIN
          done
        done
      done
    fi
  done

  # Adding TCP ports to be denied for everyone
  ############################################
  unset IFS
  for rule in $DENY_TCP; do
    if parse_rule "$rule" DENY_TCP "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying ANYHOST for TCP port(s): $ports"

      IFS=','
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A EXT_INPUT_CHAIN -i $interface -d $destip -p tcp --dport $port \
              -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INET-INPUT denied: "

            iptables -A EXT_INPUT_CHAIN -i $interface -d $destip -p tcp --dport $port -j POST_INPUT_DROP_CHAIN
          done
        done
      done
    fi
  done

  # Adding UDP ports to be denied for everyone
  ############################################
  unset IFS
  for rule in $DENY_UDP; do
    if parse_rule "$rule" DENY_UDP "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying ANYHOST for UDP port(s): $ports"

      IFS=','
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A EXT_INPUT_CHAIN -i $interface -d $destip -p udp --dport $port \
              -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INET-INPUT denied: "

            iptables -A EXT_INPUT_CHAIN -i $interface -d $destip -p udp --dport $port -j POST_INPUT_DROP_CHAIN
          done
        done
      done
    fi
  done

  # Adding TCP ports to be rejected for everyone
  ##############################################
  unset IFS
  for rule in $REJECT_TCP; do
    if parse_rule "$rule" REJECT_TCP "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Rejecting ANYHOST for TCP port(s): $ports"

      IFS=','
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A EXT_INPUT_CHAIN -i $interface -d $destip -p tcp --dport $port \
              -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Rejected TCP port: "

            iptables -A EXT_INPUT_CHAIN -i $interface -d $destip -p tcp --dport $port -j REJECT --reject-with tcp-reset
          done
        done
      done
    fi
  done

  # Adding UDP ports to be rejected for everyone
  ##############################################
  unset IFS
  for rule in $REJECT_UDP; do
    if parse_rule "$rule" REJECT_UDP "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Rejecting ANYHOST for UDP port(s): $ports"

      IFS=','
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A EXT_INPUT_CHAIN -p udp --dport $port \
              -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Rejected UDP port: "

            ip4tables -A EXT_INPUT_CHAIN -p udp --dport $port -j REJECT --reject-with icmp-host-unreachable
            if [ "$IPV6_SUPPORT" = "1" ]; then
              ip6tables -A EXT_INPUT_CHAIN -p udp --dport $port -j REJECT --reject-with icmp6-addr-unreachable
            fi
          done
        done
      done
    fi
  done

  # Adding the "full access hosts"
  ################################
  unset IFS
  for rule in $FULL_ACCESS_HOSTS; do
    if parse_rule "$rule" FULL_ACCESS_HOSTS "interfaces-destips-hosts"; then

      echo " $(show_if_ip "$interfaces" "$destips")Allowing $hosts full (inbound) access"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A EXT_INPUT_CHAIN -i $interface -s $host -d $destip -j ACCEPT
          done
        done
      done
    fi
  done

  # TCP ports to DENY but NOT to LOG
  ##################################
  unset IFS
  for rule in $DENY_TCP_NOLOG; do
    if parse_rule "$rule" DENY_TCP_NOLOG "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying ANYHOST for TCP port(s) (NO LOG): $ports"

      IFS=','
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A EXT_INPUT_CHAIN -i $interface -d $destip -p tcp --dport $port -j POST_INPUT_DROP_CHAIN
          done
        done
      done
    fi
  done

  # UDP ports to DENY but NOT to LOG
  ##################################
  unset IFS
  for rule in $DENY_UDP_NOLOG; do
    if parse_rule "$rule" DENY_UDP_NOLOG "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Denying ANYHOST for UDP port(s) (NO LOG): $ports"

      IFS=','
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A EXT_INPUT_CHAIN -i $interface -d $destip -p udp --dport $port -j POST_INPUT_DROP_CHAIN
          done
        done
      done
    fi
  done

  # TCP ports to REJECT but NOT to LOG
  ####################################
  unset IFS
  for rule in $REJECT_TCP_NOLOG; do
    if parse_rule "$rule" REJECT_TCP_NOLOG "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Rejecting ANYHOST for TCP port(s) (NO LOG): $ports"

      IFS=','
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A EXT_INPUT_CHAIN -i $interface -d $destip -p tcp --dport $port -j REJECT --reject-with tcp-reset
          done
        done
      done
    fi
  done

  # UDP ports to REJECT but NOT to LOG
  ####################################
  unset IFS
  for rule in $REJECT_UDP_NOLOG; do
    if parse_rule "$rule" REJECT_UDP_NOLOG "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Rejecting ANYHOST for UDP port(s) (NO LOG): $ports"

      IFS=','
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            ip4tables -A EXT_INPUT_CHAIN -i $interface -d $destip -p udp --dport $port -j REJECT --reject-with icmp-host-unreachable
            if [ "$IPV6_SUPPORT" = "1" ]; then
              ip6tables -A EXT_INPUT_CHAIN -i $interface -d $destip -p udp --dport $port -j REJECT --reject-with icmp6-addr-unreachable
            fi
          done
        done
      done
    fi
  done

  # Check the packet source address
  #################################
  if [ "$RESERVED_NET_DROP" = "1" ]; then
    echo " Packets will be checked for reserved source addresses"
  else
    echo " Packets will NOT be checked for reserved source addresses"
  fi

  if [ "$RESERVED_NET_DROP" = "1" -o "$RESERVED_NET_LOG" = "1" ]; then
    iptables -A EXT_INPUT_CHAIN -j RESERVED_NET_CHK
  fi

  # Do NOT allow DRDOS abuse (Distributed Reflection Denial Of Service attack)
  ############################################################################
  if [ "$DRDOS_PROTECT" = "1" ]; then
    echo " Enabling protection against DRDOS-abuse"

    iptables -A EXT_INPUT_CHAIN -p tcp ! --dport 2049 -m multiport --sports 20,21,22,23,80,110,143,443,993,995 \
      -m limit --limit 6/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Possible DRDOS abuse: "
    iptables -A EXT_INPUT_CHAIN -p udp ! --dport 2049 -m multiport --sports 20,21,22,23,80,110,143,443,993,995 \
      -m limit --limit 6/h --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Possible DRDOS abuse: "

    iptables -A EXT_INPUT_CHAIN -p tcp ! --dport 2049 -m multiport --sports 20,21,22,23,80,110,143,443,993,995 -j POST_INPUT_DROP_CHAIN
    iptables -A EXT_INPUT_CHAIN -p udp ! --dport 2049 -m multiport --sports 20,21,22,23,80,110,143,443,993,995 -j POST_INPUT_DROP_CHAIN
  fi

  # Adding TCP ports NOT to be firewalled
  #######################################
  unset IFS
  for rule in $OPEN_TCP; do
    if parse_rule "$rule" OPEN_TCP "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Allowing ANYHOST for TCP port(s): $ports"

      IFS=','
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A EXT_INPUT_CHAIN -i $interface -d $destip -p tcp --dport $port -j ACCEPT
          done
        done
      done
    fi
  done


  # Adding UDP ports NOT to be firewalled
  #######################################
  unset IFS
  for rule in $OPEN_UDP; do
    if parse_rule "$rule" OPEN_UDP "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Allowing ANYHOST for UDP port(s): $ports"

      IFS=','
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A EXT_INPUT_CHAIN -i $interface -d $destip -p udp --dport $port -j ACCEPT
          done
        done
      done
    fi
  done


  # Adding IP protocols NOT to be firewalled
  ##########################################
  unset IFS
  for rule in $OPEN_IP; do
    if parse_rule "$rule" OPEN_IP "interfaces-destips-protos"; then

      echo " $(show_if_ip "$interfaces" "$destips")Allowing ANYHOST for IP protocol(s): $protos"

      IFS=','
      for proto in $protos; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A EXT_INPUT_CHAIN -i $interface -d $destip -p $proto -j ACCEPT
          done
        done
      done
    fi
  done

  # Allow world to send IPv4 ICMP packets?
  ########################################
  if [ "$OPEN_ICMP" = "1" ]; then
    echo " Allowing ANYHOST to send IPv4 ICMP-requests (ping)"
    ip4tables -A EXT_INPUT_CHAIN -p icmp --icmp-type echo-request -m limit --limit 20/second --limit-burst 100 -j ACCEPT
  else
    echo " Denying ANYHOST to send IPv4 ICMP-requests (ping)"
  fi

  # Allow world to send IPv6 ICMPv6 packets?
  ##########################################
  if [ "$IPV6_SUPPORT" = "1" ]; then
    if [ "$OPEN_ICMPV6" != "0" ]; then
      echo " Allowing ANYHOST to send IPv6 ICMPv6-requests"
      ip6tables -A EXT_INPUT_CHAIN -p icmpv6 --icmpv6-type echo-request -m limit --limit 20/second --limit-burst 100 -j ACCEPT
    else
      echo " Denying ANYHOST to send IPv6 ICMPv6-requests"
    fi
  fi

  # Logging of possible stealth scans
  ###################################
  if [ "$POSSIBLE_SCAN_LOG" = "1" ]; then
    echo " Logging of possible stealth scans enabled"
    if [ "$UNPRIV_TCP_LOG" != "0" ]; then
      iptables -A EXT_INPUT_CHAIN -p tcp ! --syn --dport 1024: \
        -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Stealth scan? (UNPRIV): "
    fi

    if [ "$PRIV_TCP_LOG" != "0" ]; then
      iptables -A EXT_INPUT_CHAIN -p tcp ! --syn --dport :1023 \
        -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Stealth scan? (PRIV): "
    fi
  else
    echo " Logging of possible stealth scans disabled"
  fi

  # General stealth scan drop
  ###########################
  iptables -A EXT_INPUT_CHAIN -p tcp ! --syn -j POST_INPUT_DROP_CHAIN

  # Setup IPv4 chain to handle broadcast traffic
  ##############################################
  ip4tables -A EXT_INPUT_CHAIN -d 255.255.255.255 -j EXT_BROADCAST_CHAIN

  # ip4tables -A EXT_INPUT_CHAIN -m pkttype --pkt-type broadcast -j EXT_BROADCAST_CHAIN
  # ip4tables -A EXT_INPUT_CHAIN -m addrtype --dst-type BROADCAST -j EXT_BROADCAST_CHAIN
  if [ -n "$EXT_NET_BCAST_ADDRESS" ]; then
    IFS=' ,'
    for address in $EXT_NET_BCAST_ADDRESS; do
      ip4tables -A EXT_INPUT_CHAIN -d $address -j EXT_BROADCAST_CHAIN
    done
  fi

  # Handle multicast traffic
  ##########################
  ip4tables -A EXT_INPUT_CHAIN -d 224.0.0.0/4 -j EXT_MULTICAST_CHAIN
  if [ "$IPV6_SUPPORT" = "1" ]; then
    ip6tables -A EXT_INPUT_CHAIN -d ff00::/8 -j EXT_MULTICAST_CHAIN
  fi

  # Allow all packets that have been locally redirected
  #####################################################
  if [ "$NAT_LOCAL_REDIRECT" = "1" ]; then
    echo " Enabling support for NAT local redirect"
    ip4tables -A EXT_INPUT_CHAIN -m conntrack --ctstate DNAT -j ACCEPT
  fi

  # Log packets to privileged TCP ports?
  ##################################################
  if [ "$PRIV_TCP_LOG" != "0" ]; then
    echo " Logging of (other) packets to PRIVILEGED TCP ports enabled"
    iptables -A EXT_INPUT_CHAIN -p tcp --dport :1023 \
      -m limit --limit 6/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:PRIV TCP packet: "
    iptables -A EXT_MULTICAST_CHAIN -p tcp --dport :1023 \
      -m limit --limit 6/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:PRIV TCP multicast: "
    iptables -A EXT_BROADCAST_CHAIN -p tcp --dport :1023 \
      -m limit --limit 6/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:PRIV TCP broadcast: "
  else
    echo " Logging of (other) packets to PRIVILEGED TCP ports disabled"
  fi

  # Log packets to privileged UDP ports?
  ##################################################
  if [ "$PRIV_UDP_LOG" != "0" ]; then
    echo " Logging of (other) packets to PRIVILEGED UDP ports enabled"
    iptables -A EXT_INPUT_CHAIN -p udp --dport :1023 \
      -m limit --limit 6/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:PRIV UDP packet: "
    iptables -A EXT_MULTICAST_CHAIN -p udp --dport :1023 \
      -m limit --limit 6/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:PRIV UDP multicast: "
    iptables -A EXT_BROADCAST_CHAIN -p udp --dport :1023 \
      -m limit --limit 6/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:PRIV UDP broadcast: "
  else
    echo " Logging of (other) packets to PRIVILEGED UDP ports disabled"
  fi

  # Log packets to unprivileged TCP ports?
  ####################################################
  if [ "$UNPRIV_TCP_LOG" != "0" ]; then
    echo " Logging of (other) packets to UNPRIVILEGED TCP ports enabled"
    iptables -A EXT_INPUT_CHAIN -p tcp --dport 1024: \
      -m limit --limit 6/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:UNPRIV TCP packet: "
    iptables -A EXT_MULTICAST_CHAIN -p tcp --dport 1024: \
      -m limit --limit 6/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:UNPRIV TCP multicast: "
    iptables -A EXT_BROADCAST_CHAIN -p tcp --dport 1024: \
      -m limit --limit 6/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:UNPRIV TCP broadcast: "
  else
    echo " Logging of (other) packets to UNPRIVILEGED TCP ports disabled"
  fi

  # Log packets to unprivileged UDP ports?
  ####################################################
  if [ "$UNPRIV_UDP_LOG" != "0" ]; then
    echo " Logging of (other) packets to UNPRIVILEGED UDP ports enabled"
    iptables -A EXT_INPUT_CHAIN -p udp --dport 1024: \
      -m limit --limit 6/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:UNPRIV UDP packet: "
    iptables -A EXT_MULTICAST_CHAIN -p udp --dport 1024 \
      -m limit --limit 6/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:UNPRIV UDP multicast: "
    iptables -A EXT_BROADCAST_CHAIN -p udp --dport 1024 \
      -m limit --limit 6/m --limit-burst 2 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:UNPRIV UDP broadcast: "
  else
    echo "Logging of (other) packets to UNPRIVILEGED UDP ports disabled"
  fi

  # Do we want to log igmp packets?
  #############################################
  if [ "$IGMP_LOG" != "0" ]; then
    echo " Logging of IGMP packets enabled"
    ip4tables -A EXT_INPUT_CHAIN -p 2 \
      -m limit --limit 1/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:IGMP packet: "
  else
    echo " Logging of IPv4 IGMP packets disabled"
  fi

  # Finally drop all in the broadcast chain
  iptables -A EXT_BROADCAST_CHAIN -j DROP

  # Jump into the POST_INPUT_CHAIN before we start to DROP
  iptables -A EXT_INPUT_CHAIN -j POST_INPUT_CHAIN

  if [ "$ICMP_REQUEST_LOG" != "0" ]; then
    echo " Logging of dropped ICMP-request(ping) packets enabled"
    iptables -A EXT_INPUT_CHAIN -p icmp --icmp-type echo-request \
      -m limit --limit 3/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-request: "
    iptables -A EXT_MULTICAST_CHAIN -p icmp --icmp-type echo-request \
      -m limit --limit 3/m --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-multicast-request: "
  else
    echo " Logging of dropped ICMP-request(ping) packets disabled"
  fi

  if [ "$ICMP_OTHER_LOG" != "0" ]; then
    echo " Logging of dropped other ICMP packets enabled"
    iptables -A EXT_INPUT_CHAIN -p icmp ! --icmp-type echo-request \
      -m limit --limit 12/hour --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-other: "
    iptables -A EXT_MULTICAST_CHAIN -p icmp ! --icmp-type echo-request \
      -m limit --limit 12/hour --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-multicast-other: "
  else
    echo " Logging of dropped other ICMP packets disabled"
  fi

  # Drop all in the multicast chain
  iptables -A EXT_MULTICAST_CHAIN -j DROP

  # Drop all "standard" IP protocols
  ##################################
  iptables -A EXT_INPUT_CHAIN -p tcp -j POST_INPUT_DROP_CHAIN
  iptables -A EXT_INPUT_CHAIN -p udp -j POST_INPUT_DROP_CHAIN
  ip4tables -A EXT_INPUT_CHAIN -p 2 -j POST_INPUT_DROP_CHAIN
  iptables -A EXT_INPUT_CHAIN -p icmp -j POST_INPUT_DROP_CHAIN

  # Do we want to log non udp/tcp/icmp packets?
  #############################################
  if [ "$OTHER_IP_LOG" != "0" ]; then
    echo " Logging of other IP protocols (non TCP/UDP/ICMP/IGMP) packets enabled"
    iptables -A EXT_INPUT_CHAIN \
      -m limit --limit 1/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Other connect: "
  else
    echo " Logging of other IP protocols (non TCP/UDP/ICMP/IGMP) packets disabled"
  fi

  # Drop all remaining packets
  ############################
  iptables -A EXT_INPUT_CHAIN -j POST_INPUT_DROP_CHAIN
}


######################################################################################################################
## Chain EXT_ICMP_FLOOD_CHAIN - Checks all ICMP (flooded) packets for the EXTERNAL interface(s)                     ##
######################################################################################################################
setup_ext_icmp_flood_chain()
{
  # Log of ICMP flooding
  ######################
  if [ "$ICMP_FLOOD_LOG" != "0" ]; then
    echo " Logging of ICMP flooding enabled"

    iptables -A EXT_ICMP_FLOOD_CHAIN -p icmp --icmp-type destination-unreachable \
      -m limit --limit 12/hour --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-unreachable flood: "
    iptables -A EXT_ICMP_FLOOD_CHAIN -p icmp --icmp-type destination-unreachable -j POST_INPUT_DROP_CHAIN

    iptables -A EXT_ICMP_FLOOD_CHAIN -p icmp --icmp-type time-exceeded \
      -m limit --limit 12/hour --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-time-exceeded fld: "
    iptables -A EXT_ICMP_FLOOD_CHAIN -p icmp --icmp-type time-exceeded -j POST_INPUT_DROP_CHAIN

    iptables -A EXT_ICMP_FLOOD_CHAIN -p icmp --icmp-type parameter-problem \
      -m limit --limit 12/hour --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-param-problem fld: "
    iptables -A EXT_ICMP_FLOOD_CHAIN -p icmp --icmp-type parameter-problem -j POST_INPUT_DROP_CHAIN

    iptables -A EXT_ICMP_FLOOD_CHAIN -p icmp --icmp-type echo-request \
      -m limit --limit 12/hour --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-request(ping) fld: "
    iptables -A EXT_ICMP_FLOOD_CHAIN -p icmp --icmp-type echo-request -j POST_INPUT_DROP_CHAIN

    iptables -A EXT_ICMP_FLOOD_CHAIN -p icmp --icmp-type echo-reply \
      -m limit --limit 12/hour --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-reply(pong) flood: "
    iptables -A EXT_ICMP_FLOOD_CHAIN -p icmp --icmp-type echo-reply -j POST_INPUT_DROP_CHAIN

    ip4tables -A EXT_ICMP_FLOOD_CHAIN -p icmp --icmp-type source-quench \
      -m limit --limit 12/hour --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-source-quench fld: "
    ip4tables -A EXT_ICMP_FLOOD_CHAIN -p icmp --icmp-type source-quench -j POST_INPUT_DROP_CHAIN

    if [ "$IPV6_SUPPORT" = "1" ]; then
      ip6tables -A EXT_ICMP_FLOOD_CHAIN -p icmpv6 --icmpv6-type packet-too-big \
        -m limit --limit 12/hour --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP-packet-too-big fld: "
      ip6tables -A EXT_ICMP_FLOOD_CHAIN -p icmpv6 --icmpv6-type packet-too-big -j POST_INPUT_DROP_CHAIN
    fi

    # All other ICMP into the general log rule
    iptables -A EXT_ICMP_FLOOD_CHAIN -p icmp \
      -m limit --limit 12/hour --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:ICMP(other) flood: "
  else
    echo " Logging of ICMP flooding disabled"
  fi

  # Drop any ICMP packets left
  iptables -A EXT_ICMP_FLOOD_CHAIN -p icmp -j POST_INPUT_DROP_CHAIN
}


######################################################################################################################
## Chain EXT_OUTPUT_CHAIN - Checks all outgoing packets for the EXTERNAL interface(s)                               ##
######################################################################################################################
setup_ext_output_chain()
{
  # This rule is for hostwise OUTPUT TCP blocking
  ###############################################
  unset IFS
  for rule in $HOST_DENY_TCP_OUTPUT; do
    if parse_rule "$rule" HOST_DENY_TCP_OUTPUT "interfaces-srcips-hosts-ports"; then

      echo " $(show_if_ip "$interfaces" "$srcips")Denying $hosts for TCP port(s): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for interface in $interfaces; do
            for srcip in $srcips; do
              if [ "$INET_OUTPUT_DENY_LOG" != "0" ]; then
                iptables -A EXT_OUTPUT_CHAIN -o $interface -s $srcip -d $host -p tcp --dport $port \
                  -m limit --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INET-OUTPUT denied: "
              fi
              iptables -A EXT_OUTPUT_CHAIN -o $interface -s $srcip -d $host -p tcp --dport $port -j DROP
            done
          done
        done
      done
    fi
  done

  # This rule is for hostwise OUTPUT UDP blocking
  ###############################################
  unset IFS
  for rule in $HOST_DENY_UDP_OUTPUT; do
    if parse_rule "$rule" HOST_DENY_UDP_OUTPUT "interfaces-srcips-hosts-ports"; then

      echo " $(show_if_ip "$interfaces" "$srcips")Denying $hosts for UDP port(s): $ports"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for interface in $interfaces; do
            for srcip in $srcips; do
              if [ "$INET_OUTPUT_DENY_LOG" != "0" ]; then
                iptables -A EXT_OUTPUT_CHAIN -o $interface -s $srcip -d $host -p udp --dport $port \
                  -m limit --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INET-OUTPUT denied: "
              fi
              iptables -A EXT_OUTPUT_CHAIN -o $interface -s $srcip -d $host -p udp --dport $port -j DROP
            done
          done
        done
      done
    fi
  done

  # This rule is for hostwise OUTPUT IP blocking
  ##############################################
  unset IFS
  for rule in $HOST_DENY_IP_OUTPUT; do
    if parse_rule "$rule" HOST_DENY_IP_OUTPUT "interfaces-srcips-hosts-protos"; then

      echo " $(show_if_ip "$interfaces" "$srcips")Denying $hosts for IP protocol(s): $protos"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for proto in $protos; do
          for interface in $interfaces; do
            for srcip in $srcips; do
              if [ "$INET_OUTPUT_DENY_LOG" != "0" ]; then
                iptables -A EXT_OUTPUT_CHAIN -o $interface -s $srcip -d $host -p $proto \
                  -m limit --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INET-OUTPUT denied: "
              fi
              iptables -A EXT_OUTPUT_CHAIN -o $interface -s $srcip -d $host -p $proto -j DROP
            done
          done
        done
      done
    fi
  done

  # Adding the "full access hosts"
  ################################
  unset IFS
  for rule in $FULL_ACCESS_HOSTS; do
    if parse_rule "$rule" FULL_ACCESS_HOSTS "interfaces-srcips-hosts"; then

      echo " $(show_if_ip "$interfaces")Allowing $hosts full (outbound) access"

      IFS=','
      for host in `ip_range "$hosts"`; do
        for interface in $interfaces; do
          for srcip in $srcips; do
            iptables -A EXT_OUTPUT_CHAIN -o $interface -s $srcip -d $host -j ACCEPT
          done
        done
      done
    fi
  done

  # This rule is for local OUTPUT TCP blocking
  ############################################
  unset IFS
  for rule in $DENY_TCP_OUTPUT; do
    if parse_rule "$rule" DENY_TCP_OUTPUT "interfaces-srcips-ports"; then

      echo " $(show_if_ip "$interfaces" "$srcips")Denying TCP port(s): $ports"

      IFS=','
      for port in $ports; do
        for interface in $interfaces; do
          for srcip in $srcips; do
            if [ "$INET_OUTPUT_DENY_LOG" != "0" ]; then
              iptables -A EXT_OUTPUT_CHAIN -o $interface -s $srcip -p tcp --dport $port \
                -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INET-OUTPUT denied: "
            fi
            iptables -A EXT_OUTPUT_CHAIN -o $interface -s $srcip -p tcp --dport $port -j DROP
          done
        done
      done
    fi
  done

  # This rule is for local OUTPUT UDP blocking
  ############################################
  unset IFS
  for rule in $DENY_UDP_OUTPUT; do
    if parse_rule "$rule" DENY_UDP_OUTPUT "interfaces-srcips-ports"; then

      echo " $(show_if_ip "$interfaces" "$srcips")Denying UDP port(s): $ports"

      IFS=','
      for port in $ports; do
        for interface in $interfaces; do
          for srcip in $srcips; do
            if [ "$INET_OUTPUT_DENY_LOG" != "0" ]; then
              iptables -A EXT_OUTPUT_CHAIN -o $interface -s $srcip -p udp --dport $port \
                -m limit --limit 3/m --limit-burst 5 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INET-OUTPUT denied: "
            fi
            iptables -A EXT_OUTPUT_CHAIN -o $interface -s $srcip -p udp --dport $port -j DROP
          done
        done
      done
    fi
  done

  # This rule is for local OUTPUT IP blocking
  ############################################
  unset IFS
  for rule in $DENY_IP_OUTPUT; do
    if parse_rule "$rule" DENY_IP_OUTPUT "interfaces-srcips-protos"; then

      echo " $(show_if_ip "$interfaces" "$srcips")Denying IP protocol(s): $protos"

      IFS=','
      for proto in $protos; do
        for interface in $interfaces; do
          for srcip in $srcips; do
            if [ "$INET_OUTPUT_DENY_LOG" != "0" ]; then
              iptables -A EXT_OUTPUT_CHAIN -o $interface -s $srcip -p $proto \
                -m limit --limit 1/s --limit-burst 1 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:INET-OUTPUT denied: "
            fi
            iptables -A EXT_OUTPUT_CHAIN -o $interface -s $srcip -p $proto -j DROP
          done
        done
      done
    fi
  done
}


# Helper chain to catch broadcast traffic
setup_ext_broadcast_chain()
{
  # Disable logging of certain TCP broadcasts on the external interface
  #####################################################################
  unset IFS
  for rule in $BROADCAST_TCP_NOLOG; do
    if parse_rule "$rule" BROADCAST_TCP_NOLOG "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Logging of external subnet broadcasts disabled for TCP port(s): $ports"

      IFS=' ,'
      for port in $ports; do
        for interface in $interfaces; do
          iptables -A EXT_BROADCAST_CHAIN -i $interface -p tcp --dport $port -j DROP
        done
      done
    fi
  done

  # Disable logging of certain UDP broadcasts on the external interface
  ##########################################################################################
  unset IFS
  for rule in $BROADCAST_UDP_NOLOG; do
    if parse_rule "$rule" BROADCAST_UDP_NOLOG "interfaces-destips-ports"; then

      echo " $(show_if_ip "$interfaces" "$destips")Logging of external subnet broadcasts disabled for UDP port(s): $ports"

      IFS=' ,'
      for port in $ports; do
        for interface in $interfaces; do
          iptables -A EXT_BROADCAST_CHAIN -i $interface -p udp --dport $port -j DROP
        done
      done
    fi
  done
}


# This creates the input logging rules
##########################################################
setup_input_log()
{
  # This rule is for local INPUT TCP watching
  ############################################
  unset IFS
  for rule in $LOG_INPUT_TCP; do
    if parse_rule "$rule" LOG_INPUT_TCP "interfaces-destips-ports"; then

      echo "$(show_if_ip "$interfaces" "$destips")Logging incoming TCP port(s): $ports"

      IFS=' ,'
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A INPUT -i $interface -d $destip -p tcp --dport $port $NF_CONNTRACK_STATE NEW -m limit \
              --limit 3/m --limit-burst 15 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:TCP INPUT log: "
          done
        done
      done
    fi
  done


  # This rule is for local INPUT UDP watching
  ###########################################
  unset IFS
  for rule in $LOG_INPUT_UDP; do
    if parse_rule "$rule" LOG_INPUT_UDP "interfaces-destips-ports"; then

      echo "$(show_if_ip "$interfaces" "$destips")Logging incoming UDP port(s): $ports"

      IFS=' ,'
      for port in $ports; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A INPUT -i $interface -d $destip -p udp --dport $port $NF_CONNTRACK_STATE NEW -m limit \
              --limit 3/m --limit-burst 15 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:UDP INPUT log: "
          done
        done
      done
    fi
  done

  # This rule is for local INPUT IP watching
  ##########################################
  unset IFS
  for rule in $LOG_INPUT_IP; do
    if parse_rule "$rule" LOG_INPUT_IP "interfaces-destips-protos"; then

      echo "$(show_if_ip "$interfaces" "$destips")Logging incoming IP protocol(s): $protos"

      IFS=' ,'
      for proto in $protos; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A INPUT -i $interface -d $destip -p $proto $NF_CONNTRACK_STATE NEW -m limit \
              --limit 3/m --limit-burst 15 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:IP INPUT log: "
          done
        done
      done
    fi
  done


  # Hostwise logging of input connection attempts
  ###############################################
  unset IFS
  for rule in $LOG_HOST_INPUT; do
    if parse_rule "$rule" LOG_HOST_INPUT "interfaces-destips-hosts"; then

      echo "$(show_if_ip "$interfaces" "$destips")Logging incoming connections of: $hosts"

      IFS=' ,'
      for host in `ip_range "$hosts"`; do
        for destip in $destips; do
          for interface in $interfaces; do
            iptables -A INPUT -i $interface -s $host -d $destip $NF_CONNTRACK_STATE NEW \
              -m limit --limit 12/m --limit-burst 50 \
              -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Hostwise INPUT log: "
          done
        done
      done
    fi
  done

  # Hostwise logging of certain TCP port connection attempts
  ##########################################################
  unset IFS
  for rule in $LOG_HOST_INPUT_TCP; do
    if parse_rule "$rule" LOG_HOST_INPUT_TCP "interfaces-destips-hosts-ports"; then

      echo "$(show_if_ip "$interfaces" "$destips")Logging incoming connections of $hosts to TCP port(s): $ports"

      IFS=' ,'
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A INPUT -i $interface -s $host -d $destip -p tcp --dport $port $NF_CONNTRACK_STATE NEW \
                -m limit --limit 12/m --limit-burst 5 \
                -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Hostwise TCP log (IN): "
            done
          done
        done
      done
    fi
  done


  # Hostwise logging of certain UDP port connection attempts
  ##########################################################
  unset IFS
  for rule in $LOG_HOST_INPUT_UDP; do
    if parse_rule "$rule" LOG_HOST_INPUT_UDP "interfaces-destips-hosts-ports"; then

      echo "$(show_if_ip "$interfaces" "$destips")Logging incoming connections of $hosts to UDP port(s): $ports"

      IFS=' ,'
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A INPUT -i $interface -s $host -d $destip -p udp --dport $port $NF_CONNTRACK_STATE NEW \
                -m limit --limit 12/m --limit-burst 5 \
                -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Hostwise UDP INPUT log: "
            done
          done
        done
      done
    fi
  done

  # Hostwise logging of certain IP protocols connection attempts
  ##############################################################
  unset IFS
  for rule in $LOG_HOST_INPUT_IP; do
    if parse_rule "$rule" LOG_HOST_INPUT_IP "interfaces-destips-hosts-protos"; then

      echo "$(show_if_ip "$interfaces" "$destips")Logging incoming connections of $hosts to IP protocol(s): $protos"

      IFS=' ,'
      for host in `ip_range "$hosts"`; do
        for proto in $protos; do
          for destip in $destips; do
            for interface in $interfaces; do
              iptables -A INPUT -i $interface -s $host -d $destip -p $proto $NF_CONNTRACK_STATE NEW \
                -m limit --limit 12/m --limit-burst 5 \
                -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Hostwise IP INPUT log: "
            done
          done
        done
      done
    fi
  done
}


# This creates the output logging rules
##########################################################
setup_output_log()
{
  # This rule is for local OUTPUT TCP watching
  ############################################
  unset IFS
  for rule in $LOG_OUTPUT_TCP; do
    if parse_rule "$rule" LOG_OUTPUT_TCP "interfaces-srcips-ports"; then

      echo "$(show_if_ip "$interfaces" "$srcips")Logging outgoing TCP port(s): $ports"

      IFS=' ,'
      for port in $ports; do
        for interface in $interfaces; do
          for srcip in $srcips; do
            iptables -A OUTPUT -o $interface -s $srcip -p tcp --dport $port $NF_CONNTRACK_STATE NEW -m limit \
              --limit 3/m --limit-burst 15 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:TCP OUTPUT log: "
          done
        done
      done
    fi
  done

  # This rule is for local OUTPUT UDP watching
  ############################################
  unset IFS
  for rule in $LOG_OUTPUT_UDP; do
    if parse_rule "$rule" LOG_OUTPUT_UDP "interfaces-srcips-ports"; then

      echo "$(show_if_ip "$interfaces")Logging outgoing UDP port(s): $ports"

      IFS=' ,'
      for port in $ports; do
        for interface in $interfaces; do
          for srcip in $srcips; do
            iptables -A OUTPUT -o $interface -s $srcip -p udp --dport $port $NF_CONNTRACK_STATE NEW -m limit \
              --limit 3/m --limit-burst 15 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:UDP OUTPUT log: "
          done
        done
      done
    fi
  done

  # This rule is for local OUTPUT IP watching
  ###########################################
  unset IFS
  for rule in $LOG_OUTPUT_IP; do
    if parse_rule "$rule" LOG_OUTPUT_IP "interfaces-srcips-protos"; then

      echo "$(show_if_ip "$interfaces" "$srcips")Logging outgoing IP protocol(s): $protos"

      IFS=' ,'
      for proto in $protos; do
        for interface in $interfaces; do
          for srcip in $srcips; do
            iptables -A OUTPUT -o $interface -s $srcip -p $proto $NF_CONNTRACK_STATE NEW -m limit \
              --limit 3/m --limit-burst 15 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:IP OUTPUT log: "
          done
        done
      done
    fi
  done

  # Hostwise logging of output connection attempts
  ################################################
  unset IFS
  for rule in $LOG_HOST_OUTPUT; do
    if parse_rule "$rule" LOG_HOST_OUTPUT "interfaces-srcips-hosts"; then

      echo "$(show_if_ip "$interfaces" "$srcips")Logging outgoing connections to: $hosts"

      IFS=' ,'
      for host in `ip_range "$hosts"`; do
        for interface in $interfaces; do
          for srcip in $srcips; do
            iptables -A OUTPUT -o $interface -s $srcip -d $host $NF_CONNTRACK_STATE NEW \
              -m limit --limit 12/m --limit-burst 50 \
              -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Hostwise OUTPUT log: "
          done
        done
      done
    fi
  done


  # Hostwise logging of certain TCP port connection attempts
  ##########################################################
  unset IFS
  for rule in $LOG_HOST_OUTPUT_TCP; do
    if parse_rule "$rule" LOG_HOST_OUTPUT_TCP "interfaces-srcips-hosts-ports"; then

      echo "$(show_if_ip "$interfaces" "$srcips")Logging outgoing connections of $hosts to TCP port(s): $ports"

      IFS=' ,'
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for interface in $interfaces; do
            for srcip in $srcips; do
              iptables -A OUTPUT -o $interface -s $srcip -d $host -p tcp --dport $port $NF_CONNTRACK_STATE NEW \
                -m limit --limit 12/m --limit-burst 5 \
                -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host TCP log (OUT): "
            done
          done
        done
      done
    fi
  done


  # Hostwise logging of certain UDP port connection attempts
  ##########################################################
  unset IFS
  for rule in $LOG_HOST_OUTPUT_UDP; do
    if parse_rule "$rule" LOG_HOST_OUTPUT_UDP "interfaces-srcips-hosts-ports"; then

      echo "$(show_if_ip "$interfaces" "$srcips")Logging outgoing connections of $hosts to UDP port(s): $ports"

      IFS=' ,'
      for host in `ip_range "$hosts"`; do
        for port in $ports; do
          for interface in $interfaces; do
            for srcip in $srcips; do
              iptables -A OUTPUT -o $interface -s $srcip -d $host -p udp --dport $port $NF_CONNTRACK_STATE NEW \
                -m limit --limit 12/m --limit-burst 5 \
                -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host UDP log (OUT): "
            done
          done
        done
      done
    fi
  done

  # Hostwise logging of certain IP protocols connection attempts
  ##############################################################
  unset IFS
  for rule in $LOG_HOST_OUTPUT_IP; do
    if parse_rule "$rule" LOG_HOST_OUTPUT_IP "interfaces-srcips-hosts-protos"; then

      echo "$(show_if_ip "$interfaces" "$srcips")Logging outgoing connections of $hosts to IP protocol(s): $protos"

      IFS=' ,'
      for host in `ip_range "$hosts"`; do
        for proto in $protos; do
          for interface in $interfaces; do
            for srcip in $srcips; do
              iptables -A OUTPUT -o $interface -s $srcip -d $host -p $proto $NF_CONNTRACK_STATE NEW \
                -m limit --limit 12/m --limit-burst 5 \
                -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Host IP log (OUT): "
            done
          done
        done
      done
    fi
  done
}


# Helper function for setup_hostblock_chain
###########################################
setup_ipset_netset()
{
  local dir="$1" hashsize="$2" maxelem="$3" family netset set_name IFS

  ## Setup Whitelist
  ipset create -exist aif_whitelist hash:net family inet hashsize $hashsize maxelem $maxelem

  ip4tables -A HOST_BLOCK_SRC -m set --match-set aif_whitelist src -j RETURN
  if [ "$BLOCK_HOSTS_BIDIRECTIONAL" != "0" ]; then
    ip4tables -A HOST_BLOCK_DST -m set --match-set aif_whitelist dst -j RETURN
  fi
  if [ "$IPV6_SUPPORT" = "1" ]; then
    ipset create -exist aif_whitelistv6 hash:net family inet6 hashsize $hashsize maxelem $maxelem

    ip6tables -A HOST_BLOCK_SRC -m set --match-set aif_whitelistv6 src -j RETURN
    if [ "$BLOCK_HOSTS_BIDIRECTIONAL" != "0" ]; then
      ip6tables -A HOST_BLOCK_DST -m set --match-set aif_whitelistv6 dst -j RETURN
    fi
  fi

  ## Setup *.netset files
  unset IFS
  for netset in "$dir"/*.netset; do
    set_name="${netset##*/}"
    set_name="${set_name%.netset}"

    ## Kernel limits set names to 31 characters, subtract 4 for _tmp
    if [ ${#set_name} -gt 27 ]; then
      continue
    fi

    ## Naming convention, *v6.netset files for IPv6, all other *.netset files default to IPv4
    case $set_name in
      *v6|*V6) family="inet6" ;;
            *) family="inet" ;;
    esac

    if [ "$IPV6_SUPPORT" != "1" -a "$family" = "inet6" ]; then
      continue
    fi

    ## Whitelist already setup above
    if [ "$set_name" = "whitelist" -o "$set_name" = "whitelistv6" ]; then
      continue
    fi

    ipset create -exist ${set_name} hash:net family $family hashsize $hashsize maxelem $maxelem

    if [ "$family" = "inet" ]; then
      ip4tables -A HOST_BLOCK_SRC -m set --match-set ${set_name} src -j HOST_BLOCK_SRC_DROP
      if [ "$BLOCK_HOSTS_BIDIRECTIONAL" != "0" ]; then
        ip4tables -A HOST_BLOCK_DST -m set --match-set ${set_name} dst -j HOST_BLOCK_DST_DROP
      fi
    else
      ip6tables -A HOST_BLOCK_SRC -m set --match-set ${set_name} src -j HOST_BLOCK_SRC_DROP
      if [ "$BLOCK_HOSTS_BIDIRECTIONAL" != "0" ]; then
        ip6tables -A HOST_BLOCK_DST -m set --match-set ${set_name} dst -j HOST_BLOCK_DST_DROP
      fi
    fi
  done
}


# Helper function for setup_hostblock_chain
###########################################
apply_ipset_netset()
{
  local dir="$1" hashsize="$2" maxelem="$3" family netset set_name swap_err IFS
  local x default_whitelist default_whitelistv6

  default_whitelist="127.0.0.0/8 0.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 169.254.0.0/16 224.0.0.0/3"
  default_whitelistv6="::1 fe80::/10 ff00::/8"

  ## Apply Whitelist
  unset IFS
  for netset in "$dir/whitelist.netset" "$dir/whitelistv6.netset"; do
    set_name="${netset##*/}"
    set_name="aif_${set_name%.netset}"

    case $set_name in
      *v6|*V6) family="inet6" ;;
            *) family="inet" ;;
    esac

    if [ "$IPV6_SUPPORT" != "1" -a "$family" = "inet6" ]; then
      continue
    fi

    ipset create -exist ${set_name}_tmp hash:net family $family hashsize $hashsize maxelem $maxelem
    ipset flush ${set_name}_tmp

    if [ "$family" = "inet" ]; then
      unset IFS
      for x in ${DEFAULT_NETSET_WHITELIST:-$default_whitelist}; do
        ipset add -exist ${set_name}_tmp $x
        if [ $? -ne 0 ]; then
          RULE_WARNING=$((RULE_WARNING + 1))
        fi
      done
      if [ -f "$netset" ]; then
        printf "Loading IPv4 Whitelist Set: whitelist.netset ... "
        sed -n -r -e "s/^([0-9][0-9./]+)([[:space:]].*|)$/add -exist ${set_name}_tmp \1/p" "$netset" | ipset restore
        if [ $? -ne 0 ]; then
          RULE_WARNING=$((RULE_WARNING + 1))
        fi
        echo "Done."
      fi
    else
      unset IFS
      for x in ${DEFAULT_NETSET_WHITELISTV6:-$default_whitelistv6}; do
        ipset add -exist ${set_name}_tmp $x
        if [ $? -ne 0 ]; then
          RULE_WARNING=$((RULE_WARNING + 1))
        fi
      done
      if [ -f "$netset" ]; then
        printf "Loading IPv6 Whitelist Set: whitelistv6.netset ... "
        sed -n -r -e "s/^([0-9a-fA-F][0-9a-fA-F:/]+)([[:space:]].*|)$/add -exist ${set_name}_tmp \1/p" "$netset" | ipset restore
        if [ $? -ne 0 ]; then
          RULE_WARNING=$((RULE_WARNING + 1))
        fi
        echo "Done."
      fi
    fi
    ipset swap ${set_name} ${set_name}_tmp
    ipset destroy ${set_name}_tmp
  done

  ## Apply *.netset files
  unset IFS
  for netset in "$dir"/*.netset; do
    set_name="${netset##*/}"
    set_name="${set_name%.netset}"

    ## Kernel limits set names to 31 characters, subtract 4 for _tmp
    if [ ${#set_name} -gt 27 ]; then
      printf "\033[40m\033[1;31mERROR: The \"${set_name}.netset\" name is too long, filenames are limited to <27-characters>.netstat\033[0m\n" >&2
      RULE_WARNING=$((RULE_WARNING + 1))
      continue
    fi

    ## Naming convention, *v6.netset files for IPv6, all other *.netset files default to IPv4
    case $set_name in
      *v6|*V6) family="inet6" ;;
            *) family="inet" ;;
    esac

    if [ "$IPV6_SUPPORT" != "1" -a "$family" = "inet6" ]; then
      continue
    fi

    ## Whitelist already applied above
    if [ "$set_name" = "whitelist" -o "$set_name" = "whitelistv6" ]; then
      continue
    fi

    ipset create -exist ${set_name}_tmp hash:net family $family hashsize $hashsize maxelem $maxelem
    ipset flush ${set_name}_tmp

    swap_err=0

    if [ "$family" = "inet" ]; then
      printf "Loading IPv4 Blocklist Set: ${set_name}.netset ... "
      sed -n -r -e "s/^([0-9][0-9./]+)([[:space:]].*|)$/add -exist ${set_name}_tmp \1/p" "$netset" | ipset restore
      if [ $? -ne 0 ]; then
        swap_err=1
        RULE_WARNING=$((RULE_WARNING + 1))
      fi
    else
      printf "Loading IPv6 Blocklist Set: ${set_name}.netset ... "
      sed -n -r -e "s/^([0-9a-fA-F][0-9a-fA-F:/]+)([[:space:]].*|)$/add -exist ${set_name}_tmp \1/p" "$netset" | ipset restore
      if [ $? -ne 0 ]; then
        swap_err=1
        RULE_WARNING=$((RULE_WARNING + 1))
      fi
    fi

    if [ $swap_err -eq 0 ]; then
      ipset swap ${set_name} ${set_name}_tmp
      echo "Done."
    else
      echo ""
      printf "\033[40m\033[1;31mERROR: \"ipset swap ${set_name} ...\" not applied.\033[0m\n" >&2
    fi
    ipset destroy ${set_name}_tmp
  done
}


# This creates the separate host block
######################################
setup_hostblock_chain()
{
  local hashsize maxelem swap4_err swap6_err

  if iptables -F HOST_BLOCK_SRC 2>&1 |grep -q "No chain" || \
     iptables -F HOST_BLOCK_DST 2>&1 |grep -q "No chain"; then
    printf "\033[40m\033[1;31mERROR: The firewall isn't running!\033[0m\n" >&2
    printf "\033[40m\033[1;31m       You should first run this script with the \"start\" command.\033[0m\n" >&2
    return 5
  fi

  # Return if no Blocked Hosts are defined
  if [ -z "$BLOCK_HOSTS" -a -z "$BLOCK_HOSTS_FILE" -a -z "$BLOCK_NETSET_DIR" ]; then
    return
  fi

  if [ "$BLOCK_HOSTS_BIDIRECTIONAL" != "0" ]; then
    echo "Blocking (blackhole) direction: Inbound and Outbound"
  else
    echo "Blocking (blackhole) direction: Inbound Only"
  fi

## Use 'ipset' if enabled and available, else fallback to discrete iptables rules
##
if ipset_check; then
  hashsize="${IPTABLES_IPSET_HASHSIZE:-2048}"
  maxelem="${IPTABLES_IPSET_MAXELEM:-131072}"

  if [ -n "$BLOCK_HOSTS" -o -n "$BLOCK_HOSTS_FILE" ]; then
    ipset create -exist aif_blocklist hash:net family inet hashsize $hashsize maxelem $maxelem

    ip4tables -A HOST_BLOCK_SRC -m set --match-set aif_blocklist src -j HOST_BLOCK_SRC_DROP
    if [ "$BLOCK_HOSTS_BIDIRECTIONAL" != "0" ]; then
      ip4tables -A HOST_BLOCK_DST -m set --match-set aif_blocklist dst -j HOST_BLOCK_DST_DROP
    fi
    if [ "$IPV6_SUPPORT" = "1" ]; then
      ipset create -exist aif_blocklistv6 hash:net family inet6 hashsize $hashsize maxelem $maxelem

      ip6tables -A HOST_BLOCK_SRC -m set --match-set aif_blocklistv6 src -j HOST_BLOCK_SRC_DROP
      if [ "$BLOCK_HOSTS_BIDIRECTIONAL" != "0" ]; then
        ip6tables -A HOST_BLOCK_DST -m set --match-set aif_blocklistv6 dst -j HOST_BLOCK_DST_DROP
      fi
    fi
  fi

  if [ -n "$BLOCK_NETSET_DIR" ] && [ -d "$BLOCK_NETSET_DIR" ] && ls "$BLOCK_NETSET_DIR"/*.netset >/dev/null 2>&1; then
    setup_ipset_netset "$BLOCK_NETSET_DIR" $hashsize $maxelem

    ## Optimization, add ipsets to iptables (above) first, then apply ipset contents (below) which takes time

    apply_ipset_netset "$BLOCK_NETSET_DIR" $hashsize $maxelem
  fi

  if [ -z "$BLOCK_HOSTS" -a -z "$BLOCK_HOSTS_FILE" ]; then
    return
  fi

  ipset create -exist aif_blocklist_tmp hash:net family inet hashsize $hashsize maxelem $maxelem
  ipset flush aif_blocklist_tmp
  if [ "$IPV6_SUPPORT" = "1" ]; then
    ipset create -exist aif_blocklistv6_tmp hash:net family inet6 hashsize $hashsize maxelem $maxelem
    ipset flush aif_blocklistv6_tmp
  fi

  swap4_err=0
  swap6_err=0

  if [ -n "$BLOCK_HOSTS" ]; then
    printf "Blocking (blackhole) host(s): "

    IFS=' ,'
    for hosts in $BLOCK_HOSTS; do
      printf "$hosts "

      for host in `ip_range "$hosts"`; do
        get_numeric_ip_version "$host"
        case $? in
        4)
          ipset add -exist aif_blocklist_tmp $host
          if [ $? -ne 0 ]; then
            swap4_err=1
            RULE_WARNING=$((RULE_WARNING + 1))
          fi
          ;;
        6)
          if [ "$IPV6_SUPPORT" = "1" ]; then
            ipset add -exist aif_blocklistv6_tmp $host
            if [ $? -ne 0 ]; then
              swap6_err=1
              RULE_WARNING=$((RULE_WARNING + 1))
            fi
          fi
          ;;
        esac
      done
    done
    echo ""
  fi

  # Setup the blocked hosts from our file
  if [ -n "$BLOCK_HOSTS_FILE" ]; then
    if [ -e "$BLOCK_HOSTS_FILE" ]; then
      local cur_cnt=0 total_cnt
      total_cnt=$(( $(cat "$BLOCK_HOSTS_FILE" |sed -e 's|[#;].*||' -e 's| *$||' -e '/^$/d' |wc -l) ))

      : > "$IP4TABLES_BATCH_FILE"
      if [ "$IPV6_SUPPORT" = "1" ]; then
        : > "$IP6TABLES_BATCH_FILE"
      fi

      echo "(Re)loading list of BLOCKED hosts from $BLOCK_HOSTS_FILE..."
      if [ $total_cnt -gt 0 ]; then
        progress_bar $cur_cnt $total_cnt

        # Support both a '#' and a ';' as a comment delimiter in BLOCK_HOSTS_FILE file
        unset IFS
        cat "$BLOCK_HOSTS_FILE" |sed -e 's|[#;].*||' -e 's| *$||' -e '/^$/d' |while read hosts; do
          cur_cnt=$((cur_cnt + 100))
          progress_bar $cur_cnt $total_cnt

          if [ -n "$hosts" ]; then
            IFS=','
            for host in `ip_range "$hosts"`; do
              get_numeric_ip_version "$host"
              case $? in
              4)
                echo "add -exist aif_blocklist_tmp $host" >> "$IP4TABLES_BATCH_FILE"
                ;;
              6)
                if [ "$IPV6_SUPPORT" = "1" ]; then
                  echo "add -exist aif_blocklistv6_tmp $host" >> "$IP6TABLES_BATCH_FILE"
                fi
                ;;
              esac
            done
            unset IFS
          fi
        done
        printf "........."
      fi

      echo "$total_cnt host line(s) read"

      ipset restore < "$IP4TABLES_BATCH_FILE"
      if [ $? -ne 0 ]; then
        swap4_err=1
        RULE_WARNING=$((RULE_WARNING + 1))
      fi
      rm -f "$IP4TABLES_BATCH_FILE"
      if [ "$IPV6_SUPPORT" = "1" ]; then
        ipset restore < "$IP6TABLES_BATCH_FILE"
        if [ $? -ne 0 ]; then
          swap6_err=1
          RULE_WARNING=$((RULE_WARNING + 1))
        fi
        rm -f "$IP6TABLES_BATCH_FILE"
      fi
    else
      printf "\033[40m\033[1;31mNOTE: Cannot read the blocked hosts file \"$BLOCK_HOSTS_FILE\".\033[0m\n"
    fi
  fi

  if [ $swap4_err -eq 0 ]; then
    ipset swap aif_blocklist aif_blocklist_tmp
  else
    printf "\033[40m\033[1;31mERROR: IPv4 \"ipset swap ...\" not applied.\033[0m\n" >&2
  fi
  ipset destroy aif_blocklist_tmp
  if [ "$IPV6_SUPPORT" = "1" ]; then
    if [ $swap6_err -eq 0 ]; then
      ipset swap aif_blocklistv6 aif_blocklistv6_tmp
    else
      printf "\033[40m\033[1;31mERROR: IPv6 \"ipset swap ...\" not applied.\033[0m\n" >&2
    fi
    ipset destroy aif_blocklistv6_tmp
  fi
else
  if [ -n "$BLOCK_NETSET_DIR" ]; then
    printf "\033[40m\033[1;31mNOTE: Blocking using .netset files in BLOCK_NETSET_DIR requires IPTABLES_IPSET to be enabled.\033[0m\n"
  fi

  if [ -z "$BLOCK_HOSTS" -a -z "$BLOCK_HOSTS_FILE" ]; then
    return
  fi

  # Set default to DROP all while rules are added
  iptables -A HOST_BLOCK_SRC -j DROP
  iptables -A HOST_BLOCK_DST -j DROP

  iptables_batch start
  iptables_batch init HOST_BLOCK_SRC
  iptables_batch init HOST_BLOCK_DST

  if [ -n "$BLOCK_HOSTS" ]; then
    printf "Blocking (blackhole) host(s): "

    IFS=' ,'
    for hosts in $BLOCK_HOSTS; do
      printf "$hosts "

      for host in `ip_range "$hosts"`; do
        get_numeric_ip_version "$host"
        case $? in
        4)
          ip4tables_batch -A HOST_BLOCK_SRC -s $host -j HOST_BLOCK_SRC_DROP
          if [ "$BLOCK_HOSTS_BIDIRECTIONAL" != "0" ]; then
            ip4tables_batch -A HOST_BLOCK_DST -d $host -j HOST_BLOCK_DST_DROP
          fi
          ;;
        6)
          if [ "$IPV6_SUPPORT" = "1" ]; then
            ip6tables_batch -A HOST_BLOCK_SRC -s $host -j HOST_BLOCK_SRC_DROP
            if [ "$BLOCK_HOSTS_BIDIRECTIONAL" != "0" ]; then
              ip6tables_batch -A HOST_BLOCK_DST -d $host -j HOST_BLOCK_DST_DROP
            fi
          fi
          ;;
        esac
      done
    done
    echo ""
  fi


  # Setup the blocked hosts from our file
  if [ -n "$BLOCK_HOSTS_FILE" ]; then
    if [ -e "$BLOCK_HOSTS_FILE" ]; then
      local cur_cnt=0 total_cnt
      total_cnt=$(( $(cat "$BLOCK_HOSTS_FILE" |sed -e 's|[#;].*||' -e 's| *$||' -e '/^$/d' |wc -l) ))

      echo "(Re)loading list of BLOCKED hosts from $BLOCK_HOSTS_FILE..."
      if [ $total_cnt -gt 0 ]; then
        progress_bar $cur_cnt $total_cnt

        # Support both a '#' and a ';' as a comment delimiter in BLOCK_HOSTS_FILE file
        unset IFS
        cat "$BLOCK_HOSTS_FILE" |sed -e 's|[#;].*||' -e 's| *$||' -e '/^$/d' |while read hosts; do
          cur_cnt=$((cur_cnt + 100))
          progress_bar $cur_cnt $total_cnt

          if [ -n "$hosts" ]; then
            IFS=','
            for host in `ip_range "$hosts"`; do
              get_numeric_ip_version "$host"
              case $? in
              4)
                ip4tables_batch -A HOST_BLOCK_SRC -s $host -j HOST_BLOCK_SRC_DROP
                if [ "$BLOCK_HOSTS_BIDIRECTIONAL" != "0" ]; then
                  ip4tables_batch -A HOST_BLOCK_DST -d $host -j HOST_BLOCK_DST_DROP
                fi
                ;;
              6)
                if [ "$IPV6_SUPPORT" = "1" ]; then
                  ip6tables_batch -A HOST_BLOCK_SRC -s $host -j HOST_BLOCK_SRC_DROP
                  if [ "$BLOCK_HOSTS_BIDIRECTIONAL" != "0" ]; then
                    ip6tables_batch -A HOST_BLOCK_DST -d $host -j HOST_BLOCK_DST_DROP
                  fi
                fi
                ;;
              esac
            done
            unset IFS
          fi
        done
        printf "........."
      fi

      echo "$total_cnt host line(s) read"
    else
      printf "\033[40m\033[1;31mNOTE: Cannot read the blocked hosts file \"$BLOCK_HOSTS_FILE\".\033[0m\n"
    fi
  fi

  iptables_batch apply HOST_BLOCK_SRC
  iptables_batch apply HOST_BLOCK_DST
  iptables_batch stop

  # All Blocked Host rules are applied
  # Remove default rule at the beginning of the HOST_BLOCK_SRC and HOST_BLOCK_DST chains
  iptables -D HOST_BLOCK_SRC 1
  iptables -D HOST_BLOCK_DST 1
fi
}


setup_mangle_tables()
{
  # The following line is intended to hide the firewall during a traceroute.
  ##########################################################################
  if [ "$TTL_INC" = "1" ]; then
    echo "Enabling TTL-increase for the PREROUTING chain"
    IFS=' ,'
    for interface in $EXT_IF; do
      ip4tables -t mangle -A PREROUTING -i $interface -j TTL --ttl-inc 1
    done
  fi

  # Set TTL on outgoing & forwarded packets:
  ##########################################
  if [ -n "$PACKET_TTL" ]; then
    if [ $PACKET_TTL -gt 9 -a $PACKET_TTL -lt 256 ]; then
      echo "Setting TTL=$PACKET_TTL for the FORWARD & OUTPUT chains"
      IFS=' ,'
      for interface in $EXT_IF; do
        ip4tables -t mangle -A FORWARD -o $interface -j TTL --ttl-set $PACKET_TTL
        ip4tables -t mangle -A OUTPUT -o $interface -j TTL --ttl-set $PACKET_TTL
      done
    else
      printf "\033[40m\033[1;31m WARNING: Ingoring invalid value for PACKET_TTL ($PACKET_TTL), it should be between 10 and 255!\033[0m\n" >&2
    fi
  fi

  # Mangles the TOS on standard ports so they get priority in routers
  ###################################################################
  # TOS table
  # Options:
  #               Normal-Service = 0 (0x00)
  #               Minimize-Cost = 2 (0x02)
  #               Maximize-Reliability = 4 (0x04)
  #               Maximize-Throughput = 8 (0x08)
  #               Minimize-Delay = 16 (0x10)
  if [ "$MANGLE_TOS" = "1" ]; then
    echo "Enabling mangling TOS"
    # ToS: Client Applications; data => tos_client
    # Most of these are the RFC 1060/1349 suggested TOS values, yours might vary.
    # To view mangle table, type: iptables -L -t mangle
    IFS=' ,'
    for interface in $EXT_IF; do
      # Mangle values of packets created locally.
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 20 -j TOS --set-tos Maximize-Throughput
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 21 -j TOS --set-tos Minimize-Delay
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 22 -j TOS --set-tos Minimize-Delay
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 23 -j TOS --set-tos Minimize-Delay
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 25 -j TOS --set-tos Minimize-Delay
      iptables -t mangle -A OUTPUT -o $interface -p udp --dport 53 -j TOS --set-tos Maximize-Throughput
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 67 -j TOS --set-tos Minimize-Delay
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 80 -j TOS --set-tos Maximize-Throughput
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 110 -j TOS --set-tos Maximize-Throughput
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 113 -j TOS --set-tos Minimize-Delay
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 123 -j TOS --set-tos Minimize-Delay
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 143 -j TOS --set-tos Maximize-Throughput
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 443 -j TOS --set-tos Maximize-Throughput
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 993 -j TOS --set-tos Maximize-Throughput
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 995 -j TOS --set-tos Maximize-Throughput
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 1080 -j TOS --set-tos Minimize-Delay
      iptables -t mangle -A OUTPUT -o $interface -p tcp --dport 6000:6063 -j TOS --set-tos Maximize-Throughput
    done

    # Rules to mangle TOS values of packets routed through the firewall
    iptables -t mangle -A PREROUTING -p tcp --dport 20 -j TOS --set-tos Maximize-Throughput
    iptables -t mangle -A PREROUTING -p tcp --dport 21 -j TOS --set-tos Minimize-Delay
    iptables -t mangle -A PREROUTING -p tcp --dport 22 -j TOS --set-tos Minimize-Delay
    iptables -t mangle -A PREROUTING -p tcp --dport 23 -j TOS --set-tos Minimize-Delay
    iptables -t mangle -A PREROUTING -p tcp --dport 25 -j TOS --set-tos Minimize-Delay
    iptables -t mangle -A PREROUTING -p udp --dport 53 -j TOS --set-tos Minimize-Delay
    iptables -t mangle -A PREROUTING -p tcp --dport 67 -j TOS --set-tos Minimize-Delay
    iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TOS --set-tos Maximize-Throughput
    iptables -t mangle -A PREROUTING -p tcp --dport 110 -j TOS --set-tos Maximize-Throughput
    iptables -t mangle -A PREROUTING -p tcp --dport 113 -j TOS --set-tos Minimize-Delay
    iptables -t mangle -A PREROUTING -p tcp --dport 123 -j TOS --set-tos Minimize-Delay
    iptables -t mangle -A PREROUTING -p tcp --dport 143 -j TOS --set-tos Maximize-Throughput
    iptables -t mangle -A PREROUTING -p tcp --dport 443 -j TOS --set-tos Maximize-Throughput
    iptables -t mangle -A PREROUTING -p tcp --dport 993 -j TOS --set-tos Maximize-Throughput
    iptables -t mangle -A PREROUTING -p tcp --dport 995 -j TOS --set-tos Maximize-Throughput
    iptables -t mangle -A PREROUTING -p tcp --dport 1080 -j TOS --set-tos Minimize-Delay
    iptables -t mangle -A PREROUTING -p tcp --dport 6000:6063 -j TOS --set-tos Maximize-Throughput
  fi
}


setup_tcp_mss()
{
  # Besides MTU, there is yet another way to set the maximum packet size, the so called Maximum Segment Size.
  # This is a field in the TCP Options part of a SYN packet.
  # The good thing about this is that by setting the MSS value, you are telling the remote side unequivocally
  # 'do not ever try to send me packets bigger than this value'. No ICMP traffic is needed to get this to work.
  # In order for this to work you need at least iptables-1.2.1a and Linux 2.4.3 or higher.
  ##################################################################################################################
  if [ "$SET_MSS" = "1" ]; then
    echo "Enabling setting the maximum packet size via MSS"
    IFS=' ,'
    for interface in $EXT_IF; do
      iptables -A FORWARD -o $interface -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
      iptables -A OUTPUT -o $interface -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

      if [ "$NAT" = "1" ]; then
        ip4tables -t nat -A POSTROUTING -o $interface -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
      fi
    done
  fi
}


plugins_start()
{
  local plugin_count=0

  # Truncate/create file
  : > "$PLUGIN_LOAD_FILE"

  printf "Checking for (user) plugins in $PLUGIN_BIN_PATH..."

  PLUGIN_ERRORS=0

  # Check for plugins in our plugins binary path:
  if ls "$PLUGIN_BIN_PATH"/*.plugin >/dev/null 2>&1; then
    echo ""

    unset IFS
    for plugin in "$PLUGIN_BIN_PATH"/*.plugin; do
      PLUGIN_CMD=start
      if [ -e "$PLUGIN_LOAD_FILE_RESTART" ]; then
        IFS=$EOL
        for plugin_restart in `cat "$PLUGIN_LOAD_FILE_RESTART"`; do
          if [ "$plugin_restart" = "$plugin" ]; then
            if grep -q "^plugin_restart\(\)" "$plugin"; then
              PLUGIN_CMD=restart
            fi
            break
          fi
        done
      fi

      # Preset ENABLED=0 to make sure the plugin only
      # gets loaded if the config has an explicit ENABLED=1:
      ENABLED=0

      # Preinit to 0, just in case
      PLUGIN_RET_VAL=0

      # Store current amount of iptables rule warnings
      STORE_RULE_WARNING=$RULE_WARNING

      # Set indent
      INDENT=' '

      # Explicit unset IFS, just in case
      unset IFS

      # Source the plugin:
      . "$plugin"

      if [ "$ENABLED" = "1" ]; then
        # By checking the ENABLED variable, we know whether the plugin
        # was actually loaded. If so increase the plugin count:
        echo "$plugin" >> "$PLUGIN_LOAD_FILE"
        plugin_count=$((plugin_count + 1))

        # Check result
        if [ "$PLUGIN_RET_VAL" != "0" -o $STORE_RULE_WARNING -ne $RULE_WARNING ]; then
          PLUGIN_ERRORS=$((PLUGIN_ERRORS + 1))
        fi
      fi
    done
    rm -f "$PLUGIN_LOAD_FILE_RESTART"

    echo " Loaded $plugin_count plugin(s)..."
  else
    echo "None found"
  fi
}


plugins_stop()
{
  local restart="$1"
  local plugin_count=0

  # Remove any stale plugin restart file
  rm -f "$PLUGIN_LOAD_FILE_RESTART"

  PLUGIN_ERRORS=0

  if [ -e "$PLUGIN_LOAD_FILE" ]; then
    echo "Stopping (user) plugins..."

    IFS=$EOL
    for plugin in `cat "$PLUGIN_LOAD_FILE"`; do
      plugin_name="$(basename "$plugin" |sed 's/^[0-9]*//')"

      if [ -e "$plugin" ]; then
        plugin_file="$plugin"
      else
        # If we can't find it, ignore the priority number in front of the plugin-filename
        plugin_file="$(ls "$PLUGIN_BIN_PATH"/*.plugin |grep "[0-9]*$plugin_name$" |head -n1)"
      fi

      if [ -e "$plugin_file" ]; then
        # Only issue the stop command for plugins that support it:
        if grep -q "^plugin_stop\(\)" "$plugin_file"; then
          # Preset ENABLED=0 to make sure the plugin only
          # gets loaded if the config has an explicit ENABLED=1:
          ENABLED=0

          # Preinit to 0, just in case
          PLUGIN_RET_VAL=0

          # Store current amount of iptables rule warnings
          STORE_RULE_WARNING=$RULE_WARNING

          # Set indent
          INDENT=' '

          PLUGIN_CMD=stop
          if [ "$restart" = "restart" ] && grep -q "^plugin_restart\(\)" "$plugin_file"; then
            echo "$plugin" >> "$PLUGIN_LOAD_FILE_RESTART"
            PLUGIN_CMD=stop-restart
          fi

          # Explicit unset IFS, just in case
          unset IFS

          # Source the plugin:
          . "$plugin_file"

          # Check result
          if [ "$PLUGIN_RET_VAL" != "0" -o $STORE_RULE_WARNING -ne $RULE_WARNING ]; then
            PLUGIN_ERRORS=$((PLUGIN_ERRORS + 1))
          fi

          plugin_count=$((plugin_count + 1))
        fi
      else
        printf "\033[40m\033[1;31mERROR: Could not stop plugin \"$plugin_name\" as it does no exist!\033[0m\n" >&2
      fi
    done

    echo " Unloaded $plugin_count plugin(s)..."

    rm -f "$PLUGIN_LOAD_FILE"
  fi
}


plugins_status()
{
  local match="$1"

  # Load/insert user plugins
  if [ -e "$PLUGIN_LOAD_FILE" ]; then
    printf "\nShowing status of (user) plugins:${match:+ $match}\n"
    echo "---------------------------------"

    IFS=$EOL
    for plugin in `cat "$PLUGIN_LOAD_FILE"`; do
      # Only issue the status command for plugins that support the PLUGIN_CMD-variable:
      if grep -q "^plugin_status\(\)" "$plugin"; then
        if [ "${plugin%$match.plugin}" != "${plugin}" ]; then
          # Preset ENABLED=0 to make sure the plugin only
          # gets loaded if the config has an explicit ENABLED=1:
          ENABLED=0

          # Set indent
          INDENT=' '

          PLUGIN_CMD=status

          # Source the plugin:
          printf "=>"
          . "$plugin"
          echo ""
        fi
      fi
    done
  fi
}


# Here the actual iptables rules are loaded
###########################################
setup_firewall_rules()
{
  # Set indent for functions
  INDENT=' '

  echo "Using loglevel \"$LOGLEVEL\" for syslogd"
  echo ""

  echo "Setting up firewall rules:"
  echo "-------------------------------------------------------------------------------"

  # Assign conntrack helper modules
  #################################
  echo "Enabling assignment of selected conntrack helpers"
  load_conntrack_helper_module ftp tcp 21
  if [ "$USE_IRC" = "1" ]; then
    load_conntrack_helper_module irc tcp 6667:7001
  fi

  # Setup all TCP MSS stuff
  #########################
  setup_tcp_mss

  # Setup all mangle stuff
  ########################
  setup_mangle_tables

  # Setup basic input/forward/output/... chains
  #############################################
  iptables -A INPUT -j INPUT_CHAIN
  iptables -A FORWARD -j FORWARD_CHAIN
  iptables -A OUTPUT -j OUTPUT_CHAIN
  ip4tables -t nat -A PREROUTING -j NAT_PREROUTING_CHAIN
  ip4tables -t nat -A POSTROUTING -j NAT_POSTROUTING_CHAIN

  # Block all hosts in the custom blocked hosts file
  ##################################################
  iptables -A INPUT -j HOST_BLOCK_SRC
  iptables -A FORWARD -j HOST_BLOCK_SRC
  iptables -A FORWARD -j HOST_BLOCK_DST
  iptables -A OUTPUT -j HOST_BLOCK_DST

  # Allow DNS out for plugins and iptables while setting up rules
  ###############################################################
  iptables -A OUTPUT_CHAIN -p udp --dport 53 -j ACCEPT
  iptables -A OUTPUT_CHAIN -p tcp --dport 53 -j ACCEPT

  # Setup global helper chains
  ############################
  setup_valid_chk_chain
  setup_reserved_net_chk_chain
  setup_spoof_chk_chain

  # Check if source address is spoofed
  ####################################
  iptables -A INPUT -j SPOOF_CHK

  # Setup rules for input/output logging
  ######################################
  setup_input_log
  setup_output_log

  # Explicit unset IFS, just in case
  unset IFS

  # Insert the custom rules
  #########################
  if [ -e "$CUSTOM_RULES" ]; then
    echo "Reading custom rules from $CUSTOM_RULES"
    . $CUSTOM_RULES
  fi

  # Start (user) plugins
  ######################
  plugins_start

  # Drop outgoing fragmented packets (this should in principle never happen because of netfilter's
  # packet defragmentation
  ################################################################################################
  ip4tables -A OUTPUT -f \
    -m limit --limit 3/m -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Fragment packet: "
  ip4tables -A OUTPUT -f -j DROP

  # Setup helper chains for EXTERNAL input traffic:
  echo "Setting up external(INET) INPUT policy"
  setup_ext_broadcast_chain
  setup_ext_icmp_flood_chain
  setup_ext_input_chain

  # Setup helper chains for EXTERNAL output traffic:
  echo "Setting up external(INET) OUTPUT policy"
  setup_ext_output_chain

  # This is used for your external (untrusted) interfaces
  #######################################################
  IFS=' ,'
  for interface in $EXT_IF; do
    echo "Applying external(INET) policy to interface: $interface"

    # Apply external (internet) interface policy for the output chain:
    ##################################################################
    iptables -A OUTPUT -o $interface -j EXT_OUTPUT_CHAIN

    # We must allow special icmpv6 packets since CONNTRACK doesn't handle all icmpv6 types:
    #######################################################################################
    if [ "$IPV6_SUPPORT" = "1" -a "$OPEN_ICMPV6" != "0" ]; then
      for icmpv6_type in $ICMPV6_SPECIAL_TYPES; do
        ip6tables -A INPUT -i $interface -p icmpv6 --icmpv6-type $icmpv6_type -m hl --hl-eq 255 -j ACCEPT
      done
      if [ "$OPEN_ICMPV6_MLD" = "1" ]; then
        for icmpv6_type in $ICMPV6_MLD_TYPES; do
          ip6tables -A INPUT -i $interface -p icmpv6 --icmpv6-type $icmpv6_type -s fe80::/10 -m hl --hl-eq 1 -j ACCEPT
        done
      fi
    fi

    # Apply external (internet) interface policy for the input chain:
    #################################################################
    # Check packets for invalid flags:
    iptables -A INPUT -i $interface -j VALID_CHK

    # Perform check:
    ################
    # Non-ICMP traffic:
    iptables -A INPUT -i $interface ! -p icmp $NF_CONNTRACK_STATE NEW -j EXT_INPUT_CHAIN

    # ICMP traffic (rate limited):
    iptables -A INPUT -i $interface -p icmp $NF_CONNTRACK_STATE NEW \
      -m limit --limit 60/second --limit-burst 100 -j EXT_INPUT_CHAIN

    # ICMP traffic (flood)
    iptables -A INPUT -i $interface -p icmp $NF_CONNTRACK_STATE NEW -j EXT_ICMP_FLOOD_CHAIN

    # Drop any remaining ICMPv6 traffic
    if [ "$IPV6_SUPPORT" = "1" ]; then
      ip6tables -A INPUT -i $interface -p icmpv6 -j POST_INPUT_DROP_CHAIN
    fi
  done


  # Setup input rules for your internal net
  #########################################
  if [ -n "$INT_IF" ]; then
    # Setup helper chain for the LAN:
    echo "Setting up internal(LAN) INPUT policy"
    setup_int_input_chain

    IFS=' ,'
    for interface in $INT_IF; do
      echo "Applying internal(LAN) policy to interface: $interface"
      iptables -A INPUT -i $interface -j INT_INPUT_CHAIN
      iptables -A OUTPUT -o $interface -j INT_OUTPUT_CHAIN
    done
  fi

  # Setup input rules for your DMZ net
  ####################################
  if [ -n "$DMZ_IF" ]; then
    # Setup helper chain for the DMZ:
    echo "Setting up DMZ INPUT policy"
    setup_dmz_input_chain

    IFS=' ,'
    for interface in $DMZ_IF; do
      echo "Applying DMZ policy to interface: $interface"
      iptables -A INPUT -i $interface -j DMZ_INPUT_CHAIN
      iptables -A OUTPUT -o $interface -j DMZ_OUTPUT_CHAIN
    done
  fi

  # Accept all packets on "other" interfaces
  ##########################################
  if [ -n "$TRUSTED_IF" ]; then
    echo "Accepting ALL INPUT packets from trusted interface(s): $TRUSTED_IF"
    IFS=' ,'
    for interface in $TRUSTED_IF; do
      iptables -A INPUT -i $interface -j ACCEPT
    done
  fi

  # Jump to the POST INPUT chain
  ##############################
  iptables -A INPUT -j POST_INPUT_CHAIN

  # Everything else is logged & dropped in INPUT (just in case)...
  ################################################################
  iptables -A INPUT -m limit --limit 1/s -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Dropped INPUT packet: "
  iptables -A INPUT -j DROP

  # Drop all packets at the end of the POST_INPUT_DROP_CHAIN
  iptables -A POST_INPUT_DROP_CHAIN -j DROP

  # Remove the temp rule at the beginning of the POST_INPUT_DROP_CHAIN
  iptables -D POST_INPUT_DROP_CHAIN 1

  ###############################################################################################
  # FORWARD rules                                                                               #
  ###############################################################################################

  # Drop forward traffic to/from IPv6 Link Local Addresses
  ########################################################
  if [ "$IPV6_SUPPORT" = "1" ]; then
    if [ "$FORWARD_LINK_LOCAL" != "1" ]; then
      ip6tables -A FORWARD -s fe80::/10 -j LINK_LOCAL_DROP
      ip6tables -A FORWARD -d fe80::/10 -j LINK_LOCAL_DROP
    else
      echo "IPv6 Link-Local Addresses are forwarded!"
    fi
  fi

  # Allow forward traffic from "trusted-if"
  #########################################
  if [ -n "$TRUSTED_IF" ]; then
    echo "Accepting ALL FORWARD packets for trusted interface(s): $TRUSTED_IF"
    IFS=' ,'
    for interface in $TRUSTED_IF; do
      # Allow forward traffic in:
      iptables -A FORWARD -i $interface -j ACCEPT
      # Allow forward traffic out:
      iptables -A FORWARD -o $interface -j ACCEPT
    done
  fi

  # Source the IN/OUT chains for the external interface(s)
  ########################################################
  IFS=' ,'
  for eif in $EXT_IF; do
    iptables -A FORWARD -i $eif -j EXT_FORWARD_IN_CHAIN
    iptables -A FORWARD -o $eif -j EXT_FORWARD_OUT_CHAIN
  done

  # Source the IN/OUT chains for the dmz interface(s)
  ########################################################
  IFS=' ,'
  for dif in $DMZ_IF; do
    iptables -A FORWARD -i $dif -j DMZ_FORWARD_IN_CHAIN
    iptables -A FORWARD -o $dif -j DMZ_FORWARD_OUT_CHAIN
  done

  # Check source address for spoofing
  ###################################
  iptables -A FORWARD -j SPOOF_CHK

  # Check if the incoming packet is (in)valid
  ###########################################
  iptables -A EXT_FORWARD_IN_CHAIN -j VALID_CHK

  # Check if incoming packet sources are (in)valid
  ################################################
  if [ "$RESERVED_NET_DROP" = "1" -o "$RESERVED_NET_LOG" = "1" ]; then
    iptables -A EXT_FORWARD_IN_CHAIN -j RESERVED_NET_CHK
  fi

  # Setup forward policy for the DMZ
  ##################################
  if [ -n "$DMZ_IF" ]; then

    echo "Setting up DMZ FORWARD policy"

    if [ "$DMZ_OUTPUT_DENY_LOG" != "0" ]; then
      echo " Logging of denied DMZ (forward) output connections enabled"
    else
      echo " Logging of denied DMZ (forward) output connections disabled"
    fi

    if [ "$DMZ_INPUT_DENY_LOG" != "0" ]; then
      echo " Logging of denied DMZ (forward) input connections enabled"
    else
      echo " Logging of denied DMZ (forward) input connections disabled"
    fi

    # Setup helper chains for the DMZ:
    setup_inet_dmz_forward_chain
    setup_dmz_inet_forward_chain
    setup_dmz_lan_forward_chain

    echo " Allowing LAN->DMZ for LAN interface(s): ${LAN_DMZ_ALLOW_IF:-$INT_IF}"

    IFS=' ,'
    for dif in $DMZ_IF; do
      echo "Applying DMZ FORWARD policy to interface: $dif"

      # Always make subnets on the SAME interface trust each other
      iptables -A FORWARD -i $dif -o $dif -j ACCEPT

      for eif in $EXT_IF; do
        # Apply policy for inet->DMZ traffic
        iptables -A FORWARD -i $eif -o $dif -j INET_DMZ_FORWARD_CHAIN

        # Apply policy for DMZ->inet traffic
        iptables -A FORWARD -i $dif -o $eif -j DMZ_INET_FORWARD_CHAIN
      done

      # Apply policy for DMZ->LAN traffic & LAN->DMZ traffic
      for iif in $INT_IF; do
        # Apply policy for DMZ->LAN
        iptables -A FORWARD -i $dif -o $iif -j DMZ_LAN_FORWARD_CHAIN

        # Apply policy for LAN->DMZ (allow all INT_IF when LAN_DMZ_ALLOW_IF is not defined)
        for interface in ${LAN_DMZ_ALLOW_IF:-$INT_IF}; do
          if [ "$iif" = "$interface" ]; then
            iptables -A FORWARD -i $iif -o $dif -j ACCEPT
            break
          fi
        done
      done
    done
  fi

  # Forward traffic for LAN interface(s) that trust each other
  #######################################################################
  IFS=$SEP3
  for if_group in $IF_TRUSTS; do
    echo "Setting up trust FORWARD policy for interface(s): $if_group"
    IFS=' ,'
    for input_if in $if_group; do
      for output_if in $if_group; do
        if [ "$input_if" != "$output_if" ]; then
          iptables -A FORWARD -i $input_if -o $output_if -j ACCEPT
        fi
      done
    done
  done

  # Additional rules for the internal subnet(s)
  #############################################
  if [ -n "$INT_IF" ]; then
    echo "Setting up internal(LAN) FORWARD policy"

    # Enable logging of denied LAN output connections?
    ##################################################
    if [ "$LAN_OUTPUT_DENY_LOG" != "0" ]; then
      echo " Logging of denied LAN->INET FORWARD connections enabled"
    else
      echo " Logging of denied LAN->INET FORWARD connections disabled"
    fi

    # Setup helper chains for the LAN:
    setup_lan_lan_forward_chain
    lan_lan_forward_result=$?

    setup_lan_inet_forward_chain

    IFS=' ,'
    for iif in $INT_IF; do
      echo "Applying internal(LAN) FORWARD policy to interface: $iif"

      # Always make subnets on the SAME interface trust each other
      iptables -A FORWARD -i $iif -o $iif -j ACCEPT

      # Optimize by only adding to FORWARD if LAN_LAN_FORWARD_CHAIN contains rules
      if [ $lan_lan_forward_result -eq 0 ]; then
        for output_if in $INT_IF; do
          if [ "$iif" != "$output_if" ]; then
            iptables -A FORWARD -i $iif -o $output_if -j LAN_LAN_FORWARD_CHAIN
          fi
        done
      fi

      for eif in $EXT_IF; do
        iptables -A FORWARD -i $iif -o $eif -j LAN_INET_FORWARD_CHAIN
      done
    done
    unset lan_lan_forward_result
  fi


  ##############################################################################
  # Masquerade (NAT) or SNAT. Share the gateway's internet connection with     #
  # the internal network                                                       #
  ##############################################################################
  if [ "$NAT" = "1" ]; then
    if [ -n "$NAT_STATIC_IP" ]; then
      echo "Enabling SNAT via (external) IP(s): $NAT_STATIC_IP"

      printf " Adding (internal) host(s): "

      IFS=' ,'
      for net in $NAT_INTERNAL_NET; do
        printf "$net "

        COUNT=0
        for interface in $NAT_IF; do
          COUNT=$((COUNT + 1))
          STATIC_IP=$(echo "$NAT_STATIC_IP" |awk "{ print \$$COUNT }")

          if [ -z "$STATIC_IP" ]; then
            # No more IPs specified for the remaining interfaces
            break
          fi
          ip4tables -t nat -A POSTROUTING -o $interface -s $net ! -d $net -j SNAT --to-source $STATIC_IP
        done
      done
      echo ""
    else
      echo "Enabling masquerading(NAT) via (external) interface(s): $NAT_IF"

      printf " Adding (internal) host(s): "

      IFS=' ,'
      for net in $NAT_INTERNAL_NET; do
        printf "$net "

        for interface in $NAT_IF; do
          # The "! -d $net" is to avoid problems with Freeswan etc.
          ip4tables -t nat -A POSTROUTING -o $interface -s $net ! -d $net -j MASQUERADE
        done
      done
      echo ""
    fi
  fi

  # Port / protocol forwarding. Forward ports or protocols on the internet-gateway to machines in our LAN
  #######################################################################################################

  # NAT TCP port forwards
  #######################
  unset IFS
  for rule in $NAT_FORWARD_TCP; do
    if parse_rule "$rule" NAT_FORWARD_TCP "interfaces:NAT_IF-destips-shosts-ports-dhost_dport"; then

      echo "$(show_if_ip "$interfaces" "$destips")Forwarding(NAT) TCP port(s) $(show_hosts_ports "$shosts" "$ports") to $(echo "$dhost_dport" |tr "$SEP-" '::')"

      IFS=' ,'
      for shost in `ip_range "$shosts"`; do
        for port in $ports; do
          for destip in $destips; do
            # Portforward for all specified interfaces
            for eif in $interfaces; do
              # This code makes it possible to forward to a different port on the internal host
              dport=`get_ports_hp "$dhost_dport" "$port"`

              ip4tables -t nat -A PREROUTING -i $eif -s $shost -d $destip -p tcp --dport $port -j DNAT --to-destination `echo "$dhost_dport" |tr "$SEP-" '::'`
              dhost=`get_hosts_hp "$dhost_dport"`
              if [ -n "$dhost" ]; then
                ip4tables -A EXT_FORWARD_IN_CHAIN -i $eif ! -o $eif -s $shost -d $dhost -p tcp --dport $dport -j ACCEPT
              fi
            done
          done
        done
      done
    fi
  done


  # NAT UDP port forwards
  #######################
  unset IFS
  for rule in $NAT_FORWARD_UDP; do
    if parse_rule "$rule" NAT_FORWARD_UDP "interfaces:NAT_IF-destips-shosts-ports-dhost_dport"; then

      echo "$(show_if_ip "$interfaces" "$destips")Forwarding(NAT) UDP port(s) $(show_hosts_ports "$shosts" "$ports") to $(echo "$dhost_dport" |tr "$SEP-" '::')"

      IFS=' ,'
      for shost in `ip_range "$shosts"`; do
        for port in $ports; do
          for destip in $destips; do
            # Portforward for all specified interfaces
            for eif in $interfaces; do
              # This code makes it possible to forward to a different port on the internal host
              dport=`get_ports_hp "$dhost_dport" "$port"`

              ip4tables -t nat -A PREROUTING -i $eif -s $shost -d $destip -p udp --dport $port -j DNAT --to-destination `echo "$dhost_dport" |tr "$SEP-" '::'`
              dhost=`get_hosts_hp "$dhost_dport"`
              if [ -n "$dhost" ]; then
                ip4tables -A EXT_FORWARD_IN_CHAIN -i $eif ! -o $eif -s $shost -d $dhost -p udp --dport $dport -j ACCEPT
              fi
            done
          done
        done
      done
    fi
  done


  # Non-TCP/UDP port forwards (protocol based)
  ############################################
  unset IFS
  for rule in $NAT_FORWARD_IP; do
    if parse_rule "$rule" NAT_FORWARD_IP "interfaces:NAT_IF-destips-shosts-protos-dhost"; then

      echo "$(show_if_ip "$interfaces" "$destips")Forwarding(NAT) IP protocol(s) $(show_hosts_ports "$shosts" "$protos") to $dhost"

      IFS=' ,'
      for shost in `ip_range "$shosts"`; do
        for proto in $protos; do
          for destip in $destips; do
            for eif in $interfaces; do
              ip4tables -t nat -A PREROUTING -i $eif -s $shost -d $destip -p $proto -j DNAT --to-destination $dhost
              if [ -n "$dhost" ]; then
                ip4tables -A EXT_FORWARD_IN_CHAIN -i $eif ! -o $eif -s $shost -d $dhost -p $proto -j ACCEPT
              fi
            done
          done
        done
      done
    fi
  done

  # Setup rules to allow INET IPv6 and non-NAT'ed IPv4 traffic in FORWARD chain
  setup_inet_forward_rules

  # Do we want to loose the forward chain to make use of protocols like UPnP possible?
  ####################################################################################
  if [ "$LOOSE_FORWARD" = "1" ]; then
    echo "Security is LOOSENED for external interface(s) in the FORWARD chain!"
    # Loose the forward chain
    IFS=' ,'
    for eif in $EXT_IF; do
      iptables -A FORWARD -i $eif ! -o $eif -j ACCEPT
    done
  else
    echo "Security is ENFORCED for external interface(s) in the FORWARD chain"
  fi

  # Jump to the post forward chain
  iptables -A FORWARD -j POST_FORWARD_CHAIN

  # Everything else is logged & dropped in FORWARD
  if [ "$FORWARD_DROP_LOG" != "0" ]; then
    echo " Logging of dropped FORWARD packets enabled"
    iptables -A FORWARD -m limit --limit 1/m  --limit-burst 3 -j LOG --log-level $LOGLEVEL --log-prefix "AIF:Dropped FORWARD packet: "
  else
    echo " Logging of dropped FORWARD packets disabled"
  fi
  iptables -A FORWARD -j DROP

  # Post/pre routing post chains
  ip4tables -t nat -A PREROUTING -j POST_NAT_PREROUTING_CHAIN
  ip4tables -t nat -A POSTROUTING -j POST_NAT_POSTROUTING_CHAIN

  # Mark outgoing packets for traffic shaping?
  ############################################
  #if [ "$TRAFFIC_SHAPING" = "1" ]; then
  #  iptables -t mangle -I OUTPUT -m length --length 0:500 -j MARK --set-mark 1
  #  iptables -t mangle -I OUTPUT -m length --length 500:1500 -j MARK --set-mark 2
  #fi

  # Jump to the post output chain
  iptables -A OUTPUT -j POST_OUTPUT_CHAIN

  # Everything else is accepted in OUTPUT
  iptables -A OUTPUT -j ACCEPT

  # Clean-up DNS output rules
  iptables -D OUTPUT_CHAIN 2
  iptables -D OUTPUT_CHAIN 1
}


# Create our custom chains
create_user_chains()
{
  # General chains
  iptables -N HOST_BLOCK_SRC
  iptables -N HOST_BLOCK_DST
  iptables -N HOST_BLOCK_SRC_DROP
  iptables -N HOST_BLOCK_DST_DROP
  iptables -N VALID_CHK
  iptables -N RESERVED_NET_CHK
  iptables -N SPOOF_CHK
  iptables -N INPUT_CHAIN
  iptables -N FORWARD_CHAIN
  iptables -N OUTPUT_CHAIN
  ip4tables -t nat -N NAT_PREROUTING_CHAIN
  ip4tables -t nat -N NAT_POSTROUTING_CHAIN
  iptables -N POST_INPUT_DROP_CHAIN
  iptables -N POST_INPUT_CHAIN
  iptables -N POST_FORWARD_CHAIN
  iptables -N POST_OUTPUT_CHAIN
  ip4tables -t nat -N POST_NAT_PREROUTING_CHAIN
  ip4tables -t nat -N POST_NAT_POSTROUTING_CHAIN

  if [ "$IPV6_SUPPORT" = "1" ]; then
    ip6tables -N LINK_LOCAL_DROP
  fi

  # Interface-2-interface chains
  iptables -N DMZ_LAN_FORWARD_CHAIN
  iptables -N INET_DMZ_FORWARD_CHAIN
  iptables -N DMZ_INET_FORWARD_CHAIN
  iptables -N LAN_LAN_FORWARD_CHAIN
  iptables -N LAN_INET_FORWARD_CHAIN

  # Chains for the external interface
  iptables -N EXT_MULTICAST_CHAIN
  iptables -N EXT_BROADCAST_CHAIN
  iptables -N EXT_ICMP_FLOOD_CHAIN
  iptables -N EXT_INPUT_CHAIN
  iptables -N EXT_FORWARD_IN_CHAIN
  iptables -N EXT_FORWARD_OUT_CHAIN
  iptables -N EXT_OUTPUT_CHAIN

  # INT(LAN) chains
  iptables -N INT_INPUT_CHAIN
  iptables -N INT_OUTPUT_CHAIN

  # DMZ chains
  iptables -N DMZ_INPUT_CHAIN
  iptables -N DMZ_FORWARD_IN_CHAIN
  iptables -N DMZ_FORWARD_OUT_CHAIN
  iptables -N DMZ_OUTPUT_CHAIN
}


# Flush our custom chains
flush_user_chains()
{
  # General chains
  iptables -F HOST_BLOCK_SRC
  iptables -F HOST_BLOCK_DST
  iptables -F HOST_BLOCK_SRC_DROP
  iptables -F HOST_BLOCK_DST_DROP
  iptables -F VALID_CHK
  iptables -F RESERVED_NET_CHK
  iptables -F SPOOF_CHK
  iptables -F INPUT_CHAIN
  iptables -F FORWARD_CHAIN
  iptables -F OUTPUT_CHAIN
  iptables -F POST_INPUT_DROP_CHAIN
  iptables -F POST_INPUT_CHAIN
  iptables -F POST_FORWARD_CHAIN
  iptables -F POST_OUTPUT_CHAIN

  ip4tables -t nat -F NAT_PREROUTING_CHAIN
  ip4tables -t nat -F NAT_POSTROUTING_CHAIN
  ip4tables -t nat -F POST_NAT_PREROUTING_CHAIN
  ip4tables -t nat -F POST_NAT_POSTROUTING_CHAIN

  if [ "$IPV6_SUPPORT" = "1" ]; then
    ip6tables -F LINK_LOCAL_DROP
  fi

  # Interface-2-interface chains
  iptables -F DMZ_LAN_FORWARD_CHAIN
  iptables -F INET_DMZ_FORWARD_CHAIN
  iptables -F DMZ_INET_FORWARD_CHAIN
  iptables -F LAN_LAN_FORWARD_CHAIN
  iptables -F LAN_INET_FORWARD_CHAIN

  # Chains for the external interface
  iptables -F EXT_MULTICAST_CHAIN
  iptables -F EXT_BROADCAST_CHAIN
  iptables -F EXT_ICMP_FLOOD_CHAIN
  iptables -F EXT_INPUT_CHAIN
  iptables -F EXT_FORWARD_IN_CHAIN
  iptables -F EXT_FORWARD_OUT_CHAIN
  iptables -F EXT_OUTPUT_CHAIN

  # INT(LAN) chains
  iptables -F INT_INPUT_CHAIN
  iptables -F INT_OUTPUT_CHAIN

  # DMZ chains
  iptables -F DMZ_INPUT_CHAIN
  iptables -F DMZ_FORWARD_IN_CHAIN
  iptables -F DMZ_FORWARD_OUT_CHAIN
  iptables -F DMZ_OUTPUT_CHAIN
}


stop_firewall()
{
  # Set default IPv4 policies
  ip4tables -P INPUT ACCEPT
  ip4tables -P FORWARD DROP
  ip4tables -P OUTPUT ACCEPT

  # Flush builtin IPv4 chains
  ip4tables -F INPUT
  ip4tables -F OUTPUT
  ip4tables -F FORWARD

  # Flush rules in the nat/mangle/raw tables
  ip4tables -t nat -F
  ip4tables -t nat -X
  ip4tables -t mangle -F
  ip4tables -t mangle -X
  try_ip4tables -t raw -F
  try_ip4tables -t raw -X

  # Attempt to flush all IPv4 chains
  ip4tables -F
  ip4tables -X

  if [ "$IPV6_DETECTED" = "1" ]; then
    # Set default IPv6 policies
    ip6tables -P INPUT ACCEPT
    ip6tables -P FORWARD DROP
    ip6tables -P OUTPUT ACCEPT

    # Flush builtin IPv6 chains
    ip6tables -F INPUT
    ip6tables -F OUTPUT
    ip6tables -F FORWARD

    # Flush rules in the nat/mangle/raw tables
    try_ip6tables -t nat -F
    try_ip6tables -t nat -X
    ip6tables -t mangle -F
    ip6tables -t mangle -X
    try_ip6tables -t raw -F
    try_ip6tables -t raw -X

    # Attempt to flush all IPv6 chains
    ip6tables -F
    ip6tables -X
  fi
}


reinit_firewall_chains()
{
  echo "Reinitializing firewall chains"

  # Set INDENT for functions
  INDENT='  '

  # Create chains, just in case
  create_user_chains 2>/dev/null

  # Flush our user chains
  flush_user_chains

  # Temporarily set OUTPUT default policy to ACCEPT
  # Without it, active states will be flushed.
  iptables -P OUTPUT ACCEPT

  # Flush builtin IPv4 chains
  ip4tables -F INPUT
  ip4tables -F OUTPUT
  ip4tables -F FORWARD

  # Flush builtin IPv4 nat chains
  ip4tables -t nat -F PREROUTING
  ip4tables -t nat -F OUTPUT
  ip4tables -t nat -F POSTROUTING

  # Flush builtin IPv4 mangle chains
  #   We don't have to be selective by chain since we don't
  #   expect any external, dynamically managed mangle chains
  ip4tables -t mangle -F

  # Flush builtin IPv4 raw chains
  try_ip4tables -t raw -F

  # Flush conntrack helper IPv4 chain, may not exist so "try"
  try_ip4tables -F CONNTRACK_HELPER

  if [ "$IPV6_DETECTED" = "1" ]; then
    # Flush builtin IPv6 chains
    ip6tables -F INPUT
    ip6tables -F OUTPUT
    ip6tables -F FORWARD

    # Flush builtin IPv6 nat chains
    try_ip6tables -t nat -F PREROUTING
    try_ip6tables -t nat -F OUTPUT
    try_ip6tables -t nat -F POSTROUTING

    # Flush builtin IPv6 mangle chains
    #   We don't have to be selective by chain since we don't
    #   expect any external, dynamically managed mangle chains
    ip6tables -t mangle -F

    # Flush builtin IPv6 raw chains
    try_ip6tables -t raw -F

    # Flush conntrack helper IPv6 chain, may not exist so "try"
    try_ip6tables -F CONNTRACK_HELPER
  fi

  # Restore our base chains (which weren't flushed so any
  # running connections should be maintained
  #######################################################
  iptables -A INPUT -j BASE_INPUT_CHAIN
  iptables -A FORWARD -j BASE_FORWARD_CHAIN
  iptables -A OUTPUT -j BASE_OUTPUT_CHAIN
}


stop_block_firewall()
{
  # Temporarely set default IPv4 policies to DROP,
  # to not even have a small window of opportunity
  ################################################
  ip4tables -P INPUT DROP
  ip4tables -P FORWARD DROP
  ip4tables -P OUTPUT DROP

  # Flush builtin IPv4 chains
  ip4tables -F INPUT
  ip4tables -F OUTPUT
  ip4tables -F FORWARD

  # Flush nat/mangle/raw table rules
  ip4tables -t nat -F
  ip4tables -t nat -X
  ip4tables -t mangle -F
  ip4tables -t mangle -X
  try_ip4tables -t raw -F
  try_ip4tables -t raw -X

  # Attempt to flush all IPv4 chains
  ip4tables -F
  ip4tables -X

  if [ "$IPV6_DETECTED" = "1" ]; then
    # Temporarely set default IPv6 policies to DROP,
    # to not even have a small window of opportunity
    ################################################
    ip6tables -P INPUT DROP
    ip6tables -P FORWARD DROP
    ip6tables -P OUTPUT DROP

    # Flush builtin IPv6 chains
    ip6tables -F INPUT
    ip6tables -F OUTPUT
    ip6tables -F FORWARD

    # Flush nat/mangle/raw table rules
    try_ip6tables -t nat -F
    try_ip6tables -t nat -X
    ip6tables -t mangle -F
    ip6tables -t mangle -X
    try_ip6tables -t raw -F
    try_ip6tables -t raw -X

    # Attempt to flush all IPv6 chains
    ip6tables -F
    ip6tables -X
  fi

  # Deny traffic from our internet interfaces
  IFS=' ,'
  for interface in $EXT_IF; do
    ip4tables -A INPUT -i $interface -j DROP

    if [ "$IPV6_DETECTED" = "1" ]; then
      ip6tables -A INPUT -i $interface -j DROP
    fi
  done

  # Allow IPv4 traffic from the loopback (localhost)
  ip4tables -A INPUT -i lo -j ACCEPT
  ip4tables -A FORWARD -i lo -j ACCEPT
  ip4tables -A OUTPUT -o lo -j ACCEPT

  # Set default IPv4 policies
  ip4tables -P INPUT ACCEPT
  ip4tables -P FORWARD DROP
  ip4tables -P OUTPUT ACCEPT

  if [ "$IPV6_DETECTED" = "1" ]; then
    # Allow IPv6 traffic from the loopback (localhost)
    ip6tables -A INPUT -i lo -j ACCEPT
    ip6tables -A FORWARD -i lo -j ACCEPT
    ip6tables -A OUTPUT -o lo -j ACCEPT

    # Set default IPv6 policies
    ip6tables -P INPUT ACCEPT
    ip6tables -P FORWARD DROP
    ip6tables -P OUTPUT ACCEPT
  fi
}


show_status()
{
  # Check if the user gave any parameters
  if [ -z "$1" ]; then
    iptables -xnvL

    if [ "$NAT" = "1" ]; then
      ip4tables -t nat -xnvL
    fi

    if [ "$MANGLE_TOS" = "1" ]; then
      iptables -t mangle -nvL
    fi

    # Show plugin status
    plugins_status
  else
    #iptables -nvL $2 $3 $4 $5
    iptables -xnvL $@
  fi
}


show_start()
{
  DATE=`LC_ALL=C date +'%b %d %H:%M:%S'`
  echo "$DATE ** Starting Arno's Iptables Firewall v$MY_VERSION **" >> $FIREWALL_LOG
  echo "** Starting Arno's Iptables Firewall v$MY_VERSION **" |logger -t firewall -p kern.info
}


show_restart()
{
  DATE=`LC_ALL=C date +'%b %d %H:%M:%S'`
  echo "$DATE ** Restarting Arno's Iptables Firewall v$MY_VERSION **" >> $FIREWALL_LOG
  echo "** Restarting Arno's Iptables Firewall v$MY_VERSION **" |logger -t firewall -p kern.info
}


show_failed()
{
  DATE=`LC_ALL=C date +'%b %d %H:%M:%S'`
  echo "$DATE ** ERROR: Firewall failed to start! **" >> $FIREWALL_LOG
  echo "** ERROR: Firewall failed to start! **" |logger -t firewall -p kern.info
}


show_stop()
{
  DATE=`LC_ALL=C date +'%b %d %H:%M:%S'`
  printf "$DATE \033[40m\033[1;32mStopping Arno's Iptables Firewall v$MY_VERSION\033[0m\n"
  echo "$DATE ** Stopping Arno's Iptables Firewall v$MY_VERSION **" >> $FIREWALL_LOG
  echo "** Stopping Arno's Iptables Firewall v$MY_VERSION **" |logger -t firewall -p kern.info
}


show_stop_blocked()
{
  DATE=`LC_ALL=C date +'%b %d %H:%M:%S'`
  printf "$DATE \033[40m\033[1;31mFIREWALL DISABLED & BLOCKING ALL INTERNET TRAFFIC!\033[0m\n"
}


# Now show the final message
show_applied()
{
  echo ""
  DATE=`LC_ALL=C date +'%b %d %H:%M:%S'`

  if [ $RULE_WARNING -ne 0 ]; then
    printf "$DATE \033[40m\033[1;31mWARNING: $RULE_WARNING firewall rules failed to apply!\n\033[0m" >&2
    echo "$DATE ** WARNING: $RULE_WARNING firewall rules failed to apply! **" >> $FIREWALL_LOG
    echo "** WARNING: $RULE_WARNING firewall rules failed to apply! **" |logger -t firewall -p kern.info
  else
    printf "$DATE \033[40m\033[1;32mAll firewall rules applied.\033[0m\n"
    echo "$DATE ** All firewall rules applied **" >> $FIREWALL_LOG
    echo "** All firewall rules applied **" |logger -t firewall -p kern.info
  fi
}


show_disabled()
{
  DATE=`LC_ALL=C date +'%b %d %H:%M:%S'`
  printf "$DATE \033[40m\033[1;31mFIREWALL DISABLED!\033[0m\n"
}


show_help()
{
  echo "Usage: `basename $0` { start | stop | restart | force-reload | status }" >&2
  echo "start                       = Start firewall" >&2
  echo "stop                        = Stop firewall" >&2
  echo "stop-block                  = Stop firewall & block all internet packets" >&2
  echo "restart                     = Restart firewall" >&2
  echo "force-reload                = Reload blocked hosts (blackhole) file" >&2
  echo "check-conf                  = Check configuration file (only)" >&2
  echo "status [-t {table}] [chain] = View firewall ruleset ([chain] & [-t {table}] are optional)" >&2
  echo "status-plugins [name]       = View plugins status (view only plugin [name] is optional)" >&2
  echo "" >&2
}


main_stop()
{
  plugins_stop
  if [ "$1" = "block" ]; then
    stop_block_firewall
  else
    stop_firewall
  fi

  if [ "$PLUGIN_ERRORS" != "0" ]; then
    printf "\033[40m\033[1;31m\nWARNING: $PLUGIN_ERRORS Plugin(s) reported errors while stopping!\n\033[0m" >&2
  fi
}


main_start()
{
  load_modules
  setup_misc
  setup_kernel_settings
  init_firewall_chains
  setup_default_policies
  setup_hostblock_chain
  setup_firewall_rules
}


main_restart()
{
  plugins_stop restart
  load_modules
#  setup_misc
  setup_kernel_settings
  reinit_firewall_chains
  setup_default_policies
  setup_hostblock_chain
  setup_firewall_rules
}


start_restart()
{
  # Check whether we are actually stopped
  case $(check_for_base_chains) in
    no)    main_start;;
    yes)   main_restart;;
    other) main_stop; main_start;;
  esac

  if [ "$PLUGIN_ERRORS" != "0" ]; then
    printf "\033[40m\033[1;31m\nWARNING: $PLUGIN_ERRORS Plugin(s) reported errors while starting!\n\033[0m" >&2
  fi

  show_applied
}


# int main (char *argv)
#######################

# Check commandline supplied argument:
case "$1" in
  'start' )             sanity_check
                        show_start
                        config_check
                        start_restart;;
  'restart' )           sanity_check
                        show_restart
                        config_check
                        start_restart;;
  'force-reload' )      sanity_check
                        config_check
                        setup_hostblock_chain
                        show_applied;;
  'stop' )              sanity_check
                        show_stop
                        main_stop
                        show_disabled;;
  'stop-block' )        sanity_check
                        show_stop
                        main_stop block
                        show_stop_blocked;;
  'status' )            shift
                        show_status $@;;
  'status-plugins' )    shift
                        plugins_status $@;;
  'check-conf' )        config_check;;
  * )                   printf "\033[40m\033[1;31mERROR: Bad or missing parameter(s)\033[0m\n" >&2
                        show_help;;
esac

if [ $RULE_WARNING -ne 0 ]; then
  exit 1
fi

exit 0
