#!/bin/sh
: '20200310c PeterG <pg_scr {at} scr.for.sabi.co.UK>'

ME="`basename "$0"`"

FIRERC='/etc/sabifirerc'

: 'Documentations comments at the end'

PATH="/sbin:/usr/sbin:$PATH"
HOSTNAME="`hostname`"

: '---------------------------------------------------------------------'

: 'IPv4 CONSTANTS'

NF4_TABLE='/proc/net/ip_tables_names'
NF4_MATCH='/proc/net/ip_tables_matches'
NF4_TARGET='/proc/net/ip_tables_targets'
NF4_TRACK='/proc/sys/net/netfilter/nf_conntrack_icmp_timeout /proc/net/ip_conntrack /proc/net/nf_conntrack'

ICMP4='icmp'

ANY4=0/0
UCB4='127.0.0.0/8'
MSBC4='255.255.255.255/32'

UNI4_LINK='169.154.0.0/16'
MUL4_GLOB='224.0.0.0/8'
: '
  IPv4 invalid addresses, a strange story. There is a regularly updated
  list at: <http://WWW.Cymru.com/Documents/bogon-list.html>
  Most are documented in RFC 3330 <http://WWW.RFC-editor.org/rfc/rfc3330.txt>
  Check this for gaps too: <http://WWW.IANA.org/assignments/ipv4-address-space>

  There are lots of smallish address ranges that cannot be validly used,
  but here for speed we only check the most obvious ones.

  We need to express them in both IPv4 subrange form, and in IPv6/IPv4
  compatible, mapped and 6to4 modes, which will be done later.
'
if false
then IP4RESV4:='10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 240.0.0.0/8'
fi

DFINET4="$ANY4"


: ---------------------------------------------------------------------

: 'IPv6 CONSTANTS'

NF6_TABLE='/proc/net/ip6_tables_names'
NF6_MATCH='/proc/net/ip6_tables_matches'
NF6_TARGET='/proc/net/ip6_tables_targets'
NF6_TRACK='/proc/sys/net/netfilter/nf_conntrack_icmpv6_timeout /proc/net/ip6_conntrack /proc/net/nf_conntrack'

ANY6=::/0
UCB6=::1
MSBC6=

UNI6_4CPT='::0000:0000:0000/96'
UNI6_4MAP='::ffff:0000:0000/96'
UNI6_6TO4='2002::/16'

UNI6_4IN6="$UNI6_6TO4 $UNI6_4MAP $UNI6_4CPT"

: '
  As major policy decision, we ignore the NSAP prefix
  and the fec0::/10 site local address prefix.
'

UNI6_GLOB='2001::/3'
UNI6_SITE='fec0::/10'
UNI6_LINK='fe80::/10'
MUL6_WELL='ff00::/12'
MUL6_LINK='ff02::/16'
MUL6_SITE='ff05::/16'
MUL6_TRAN='ff10::/12'

DFINET6="$UNI6_GLOB $UNI6_4IN6"

case "$SHELL_VERSION" in
'bash'*|'zsh'*|'ksh'*)
  ipaddr4to6()		{ printf '%02x%02x:%02x%02x' ${1+"$@"}; };;
*)
  if test -x '/usr/bin/printf'
  then ipaddr4to6()	{ /usr/bin/printf '%02x%02x:%02x%02x' ${1+"$@"}; }
  elif test -x '/usr/bin/perl'
  then ipaddr4to6()	{ /usr/bin/perl -e \
			    'printf "%02x%02x:%02x%02x",split " ","'"$*"'"'; }
  else IP4RESV6='-'
  fi;;
esac

: '
  We do not allow IPv4 local, link local or class D and E addresses
  in IPv6 compatible form.
  IPv6-IPv4 address ranges are ::0000:0:0/96/ ::ffff:0:0/96/ 2002::0/16.
'

case "$IP4RESV6" in
'-')
  IP4RESV6='';;
'')
  for N6 in `echo "$UNI6_4IN6" | sed 's|::*/|/|g'`
  do
    P6="`expr "$N6" : '\([^/]*\)/'`"
    M6="`expr "$N6" : '[^/]*/\([0-9]*\)'`"

    for N4 in $IP4RESV4 $UNI4_LINK $UCB4
    do
      P4="`expr "$N4" : '\([^/]*\)/' | sed 's/\./ /g'`"
      M4="`expr "$N4" : '[^/]*/\([0-9]*\)'`"

      P4="`ipaddr4to6 $P4`"

      M="`expr "$M6" + "$M4"`"

      if expr "$M6" '<' 96 >/dev/null
      then P="$P6:$P4::"
      else P="$P6:$P4"
      fi

      IP4RESV6="$IP4RESV6 $P/$M"
    done
  done;;
esac

LNDV_LNET4_ONLY()	{ true; }
LNDV_LNET6_ONLY()	{ true; }

LNDV_LNIP4_ONLY()	{ true; }
EXDV_EXIP4_ONLY()	{ true; }
EXDV_EXIP4_DYNAMIC()	{ true; }

LNDV_LNIP6_ONLY()	{ true; }
EXDV_EXIP6_ONLY()	{ false; }; : 'always false!'
EXDV_EXIP6_DYNAMIC()	{ false; }; : 'always false!'

STATELESS()		{ false; }
MASQUERADING()		{ true; }

TRACK_FTP()		{ true; }
TRACK_FTP_CANT()	{ true; }

PRINT_CMDS()		{ false; }
NO_ACTION()		{ false; }

ALLOW_OUTGOING()	{ false; }
ALLOW_OUTGOING_TCP()	{ false; }
ALLOW_OUTGOING_UDP()	{ false; }

ALLOW_GAMES()		{ false; }
ALLOW_H323()		{ false; }

: ---------------------------------------------------------------------

: '
  Defaults (that can be overriden in "sabifirerc") for allowed ports.
  These are blank separated lists of comma separated sublists. Sublists
  are "-m multiport" groups, and can contain at most 15 port numbers.
  Since they are expanded without quoting, port numbers/names cannot
  contain blanks, but then they wouldnt.
'

: ${PORTS_CLIENT='1024:'}

: 'ESP is essential as otherwise encrypted packets get dropped regardless'
: ${IP4_PROTO='50'}
: ${IP6_PROTO='50'}

: ${SMB_P:='loc-srv,netbios-ns,netbios-dgm,netbios-ssn,microsoft-ds'}

: 'External incoming and outgoing, logged and unlogged'
: ${TCP_P_BX_L:='echo,discard,daytime,telnets,ssh'}
: ${UDP_P_BX_L:='echo,discard,daytime'}
: ${TCP_P_BX:='auth'}
: ${UDP_P_BX:='isakmp 4500'}

: 'Local incoming and outgoing'
: ${TCP_P_BL:="bootpc,bootps,ntp auth,finger,font-service,$SMB_P"}
: ${UDP_P_BL:="bootpc,bootps,ntp $SMB_P"}

: 'External outgoing, logged and unlogged'
: ${TCP_P_OX_L:='ftp,ftp-data,finger,ldap,irc,ircd,cvspserver'}
: ${UDP_P_OX_L:=''}
: ${TCP_P_OX:='domain,sunrpc,icp,ntp,smtp,ssmtp,submission,auth,whois
	      ftps,ftps-data,http,https,nntp,nntps,pop3,pop3s,imap,imaps,rsync'}
: ${UDP_P_OX:='domain,sunrpc,icp,ntp'}

: 'Local outgoing'
: ${TCP_P_OL:="ipp,printer,sunrpc"}
: ${UDP_P_OL:="syslog,sunrpc"}

: '
  Defaults (that can be overriden in "sabifirerc") for mangled ports.

  Common ports:
    SIP:	5060T in+out, 5082T out, 7701U-7706U in+out (convention)
    IAX2:	4569U in+out, 7078-7081U in+out (convention)
    RTSP:	564 10000-10009
    aMule:	4662T,4665U,4672U in+out, 4242T,4661T,5306T,5661T out
    Gnutella:	6346T in+out
    BitTorrent:	6881-6889T in+out
'

: ${TCP_P_MIN_DEL='domain,ntp,telnet,telnets,ftp,ftps,irc,rtsp,5060,5082,6000,6001,6002,6003'}
: ${TCP_P_MAX_THR='ftp-data,ftps-data,rsync,3128,cvspserver,imaps
		   4662,4672,4242,4661,5306,5661,6346,6881,6882,6883,6884'}
: ${TCP_P_MAX_REL=''}

: ${UDP_P_MIN_DEL='domain,ntp,syslog,4569,10000,10001,10002,10003,10004,10005,10006,10007,10008,10009'}
: ${UDP_P_MAX_THR='4662,4665,4672'}

: 'By default we max reliability for all ICMP packets.'

: ${ICMP_T_MIN_DEL='echo-request echo-reply time-exceeded'}
: ${ICMP_T_MAX_THR=}

: ' http://www.iana.org/assignments/icmp-parameters.txt
  ICMP types: ("iptables -p icmp -h")

    destination-unreachable source-quench redirect
    time-exceeded parameter-problem
    echo-request echo-reply
    router-advertisement router-solicitation
    timestamp-request timestamp-reply
    address-mask-request address-mask-reply
'

: ${ICMP_T_BLS4='router-advertisement router-solicitation'}
: ${ICMP_T_OLS4=''}
: ${ICMP_T_BXS4='fragmentation-needed time-exceeded
    parameter-problem source-quench'}
: ${ICMP_T_OXS4=''}

: ${ICMP_T_BX4='echo-request echo-reply'}
: ${ICMP_T_OX4=''}

: ' http://www.iana.org/assignments/icmpv6-parameters
  ICMPv6 types: ("ip6tables -p icmpv6 -h")

    destination-unreachable packet-too-big
    time-exceeded parameter-problem
    echo-request echo-reply
    router-solicitation router-advertisement
    neighbour-solicitation neighbour-advertisement
'

: ${ICMP_T_BLS6='neighbour-solicitation neighbour-advertisement
    router-solicitation router-advertisement'}
: ${ICMP_T_OLS6=''}
: ${ICMP_T_BXS6='destination-unreachable time-exceeded
    parameter-problem packet-too-big 130 131 132'}
: ${ICMP_T_OXS6=''}

: ${ICMP_T_BX6='echo-request echo-reply'}
: ${ICMP_T_OX6=''}

test -r "$FIRERC" && . "$FIRERC" ${1+"$@"}

: ######################################################################

setupIPv4() \
{
  if test -r "$NF4_TABLE"
  then : 'v4 OK.'
  else
    echo 1>&2 "$0: cannot read '$NF4_TABLE'"
    exit 1
  fi

  IPT='iptables'
  ICMP='icmp'

  NF_TABLE="$NF4_TABLE"
  NF_TRACK="$NF4_TRACK"
  NF_MATCH="$NF4_MATCH"
  NF_TARGET="$NF4_TARGET"

  ANY="$ANY4"
  UCB="$UCB4"
  MSBC="$MSBC4"

  UNI_GLOB="$UNI4_GLOB"

  UNI_SITE="$UNI4_SITE"
  UNI_LINK="$UNI4_LINK"

  MUL_GLOB="$MUL4_GLOB"
  MUL_SITE="$MUL4_SITE"
  MUL_LINK="$MUL4_LINK"

  ICMP_T_BLS="$ICMP_T_BLS4"
  ICMP_T_OLS="$ICMP_T_OLS4"

  ICMP_T_BX="$ICMP_T_BX4"
  ICMP_T_OX="$ICMP_T_OX4"
  ICMP_T_BXS="$ICMP_T_BXS4"
  ICMP_T_OXS="$ICMP_T_OXS4"

  INET="$DFINET4"

  : ${LNDV:="$DFLNDV4"}
  : ${LNET:="$DFLNET4"}
  : ${LNBC:="$DFLNBC4"}
  : ${LNIP:="$DFLNIP4"}

  : ${EXDV:="$DFEXDV4"}
  : ${ENET:="$DFENET4"}
  : ${EXBC:="$DFEXBC4"}
  : ${EXIP:="$DFEXIP4"}

  ifaceRouteToNET() \
  {
    case "$1" in ?*)
      expr "`ip route show dev "$1"`" \
	: '^\([0-9.]*/[0-9]*\)';;
    esac
  }

  ifaceDescr() \
  {
    case "$1" in ?*)
      expr "`ip addr show dev "$1"`" \
	: '.*\(\<inet [0-9./]* [a-z]* [0-9.]*\).*';;
    esac
  }

  ifaceDescrToBC() \
    { case "$1" in ?*) expr "$1" : '\<brd \([0-9.]*\)';; esac; }

  ifaceDescrToIP() \
    { case "$1" in ?*) expr "$1" : '\<inet \([0-9.]*\)';; esac; }

  ifaceDescrToNET() \
    { case "$1" in ?*) expr "$1" \
	: '\<inet \([0-9.]*/[0-9]*\)';; esac; }
}

setupIPv6() \
{
  if test -r "$NF6_TABLE"
  then : 'v6 OK.'
  else
    echo 1>&2 "$0: cannot read '$NF6_TABLE'"
    exit 1
  fi

  IPT='ip6tables'
  ICMP='icmpv6'

  NF_TABLE="$NF6_TABLE"
  NF_TRACK="$NF6_TRACK"
  NF_MATCH="$NF6_MATCH"
  NF_TARGET="$NF6_TARGET"

  ANY="$ANY6"
  UCB="$UCB6"
  MSBC="$MSBC6"

  UNI_GLOB="$UNI6_GLOB"

  UNI_SITE="$UNI6_SITE"
  UNI_LINK="$UNI6_LINK"

  MUL_GLOB="$MUL6_GLOB"
  MUL_SITE="$MUL6_SITE"
  MUL_LINK="$MUL6_LINK"

  INET="$DFINET6"

  : ${LNDV:="$DFLNDV6"}
  : ${LNET:="$DFLNET6"}
  : ${LNBC:="$DFLNBC6"}
  : ${LNIP:="$DFLNIP6"}

  : ${EXDV:="$DFEXDV6"}
  : ${ENET:="$DFENET6"}
  : ${EXBC:="$DFEXBC6"}
  : ${EXIP:="$DFEXIP6"}

  ICMP_T_BLS="$ICMP_T_BLS6"
  ICMP_T_OLS="$ICMP_T_OLS6"

  ICMP_T_BX="$ICMP_T_BX6"
  ICMP_T_OX="$ICMP_T_OX6"
  ICMP_T_BXS="$ICMP_T_BXS6"
  ICMP_T_OXS="$ICMP_T_OXS6"

  ifaceRouteToNET() \
  {
    case "$1" in ?*)
      expr "`ip -6 route show dev "$1"`" \
	: '^\([0-9.]*/[0-9]*\)';;
    esac
  }

  ifaceDescr() \
  {
    case "$1" in ?*)
      expr "`ip -6 addr show dev "$1"`" \
	: '.*\(\<inet [0-9./]* [a-z]* [0-9.]*\).*';;
    esac
  }

  ifaceDescrToBC() \
    { case "$1" in ?*) echo '';; esac; }

  ifaceDescrToIP() \
    { case "$1" in ?*) expr "$1" : '\<inet6 addr: \([0-9A-Fa-f:.]*\)';; esac; }

  ifaceDescrToNET() \
    { case "$1" in ?*) expr "$1" \
	: '\<inet6 addr: \([0-9A-Fa-f:.]*/[0-9]*\)';; esac; }
}

iptablesPolicy() \
{
  case "$1" in
  '') POLICY='DROP';;
  ?*) POLICY="$1";;
  esac

  case "$IP" in 'v4')
    for C in PREROUTING OUTPUT POSTROUTING
    do nat -P "$C" ACCEPT
    done;;
  esac

  for C in PREROUTING OUTPUT
  do mangle -P "$C" ACCEPT
  done

  for C in INPUT OUTPUT FORWARD
  do filter -P "$C" "$POLICY"
  done
}

iptablesReset() \
{
  : 'Flush all subchains'

  for T in nat mangle filter
  do case "$IP;$T" in 'v4;nat'|*';mangle'|*';filter') "$T" -F; "$T" -X;; esac
  done

  : 'JUST TRUST THE LOCAL INTERFACE'

  filter -A OUTPUT	-j ACCEPT	-o lo # -d "$I"
  filter -A INPUT	-j ACCEPT	-i lo # -s "$I"

  : 'We need to allow DHCP/BOOTP on any interface though'

  filter -A OUTPUT	-j ACCEPT	-p udp --dport 67 --sport 68
  filter -A INPUT	-j ACCEPT	-p udp --dport 68 --sport 67
}

IP=''

: ---------------------------------------------------------------------

: 'ARGUMENT PROCESSING'

HELP="
    -p|--print			print 'iptables' commands
    -n|--no-actions		don't execute 'iptables' commands

    -4|--ipv4|--ip4		set up IPv4 firewall [default]
    -6|--ipv6|--ip6		set up IPv6 firewall

    -e|--external NIC1		external traffic interface
      -E|--ip-external (IP1|@)	our source IP address for NIC1 traffic

    -l|--local NIC2		local traffic interface
      -L|--ip-local (IP2|@)	our source IP address for NIC2 traffic
      -N|--net-local SN2	local subnet range
      -B|--bc-local IP2		local broadcast address

    -o|--outgoing		allow all outgoing connections

    -h|--help			print this help

    [start|status|close|stop]
"

OPTSO='hpno46'
OPTSL='help,print,no-actions,outgoing,ipv4,ip4,ipv6,ip6'
OPTSO="$OPTSO"'e:E:l:L:N:B: '
OPTSL="$OPTSL"',external:,ip-external: '
OPTSL="$OPTSL"',local:,ip-local:,net-local:,bc-local: '

if ARGS="`getopt -n "$0" -o "$OPTSO" -l "$OPTSL" -- "$@"`"
then eval set -- "$ARGS"
else
  echo 1>&2 "$ME: argument '$1' is not valid."
  echo 1>&2 "  $0$HELP"
  exit 1
fi

while true
do
  case "$1" in

  '-4'|'--ipv4'|'ip4')		IP='v4'; shift;;
  '-6'|'--ipv6'|'ip6')		IP='v6'; shift;;
  '-o'|'--outgoing')		ALLOW_OUTGOING() { true; }; shift;;
  '-p'|'--print')		PRINT_CMDS() { true; }; shift;;
  '-n'|'--no-action')		NO_ACTION() { true; }; shift;;

  '-e'|'--external')		EXDV="$2"; shift 2;;
  '-E'|'--ip-external')		EXIP="$2"; shift 2;;

  '-l'|'--local')		LNDV="$2"; shift 2;;
  '-L'|'--ip-local')		LNIP="$2"; shift 2;;
  '-N'|'--net-local')		LNET="$2"; shift 2;;
  '-B'|'--bc-local')		LNBC="$2"; shift 2;;

  '--')				shift; break;;

  *|'-h'|'--help')
    echo 1>&2 "  $ME$HELP"
    exit 1;;
  esac
done

case "$IP" in '') IP='v4';; esac

case "$IP" in
'v4')	setupIPv4;;
'v6')	setupIPv6;;
esac

case "$EXDV" in ?*) : ${EXIP:='@'};; esac

case "$EXDV;$EXIP" in
';'?*)
  echo 1>&2 "$ME: external IP '$EXIP' but no external NIC specified.";;

?*';@')
  : ${ENET:="`ifaceRouteToNET "$EXDV"`"}
  if DESCR="`ifaceDescr "$EXDV"`"
  then
    EXIP="`ifaceDescrToIP "$DESCR"`"
    : EXBC="`ifaceDescrToBC "$DESCR"`"
  fi;;
esac

case "$LNDV" in ?*) : ${LNIP:='@'};; esac

case "$LNDV;$LNIP" in
';'?*)
  echo 1>&2 "$ME: local IP '$LNIP' but no local NIC specified.";;

?*';@')
  : ${LNET:="`ifaceRouteToNET "$LNDV"`"}
  if DESCR="`ifaceDescr "$LNDV"`"
  then
    LNIP="`ifaceDescrToIP "$DESCR"`"
    LNBC="`ifaceDescrToBC "$DESCR"`"
  fi;;
esac

: ######################################################################

if grep -q -w '^LOG' "$NF_TARGET" 
then USE_LOG()		{ true; }
else USE_LOG()		{ false; }
  echo 1>&2 "$0: we really should have the 'LOG' target"
fi

if grep -q -w '^REJECT' "$NF_TARGET"
then USE_REJECT()	{ true; }
else USE_REJECT()	{ false; }
fi

if STATELESS
then : 'Whether we have connection tracking irrelevant'
else
  NF_TRACKLESS()	{ true; }
  for NF_T in $NF_TRACK
  do
    if test -n "$NF_T" -a -r "$NF_T"
    then NF_TRACKLESS()	{ false; }; break
    fi
  done
  if NF_TRACKLESS
  then
    echo 1>&2 "$0: requested non STATELESS but none of '$NF_TRACK' exists"
    exit 1
  fi
fi

if ! grep -q -w '^nat' "$NF_TABLE" 
then MASQUERADING()	{ false; }
fi

if PRINT_CMDS && NO_ACTION
then
  nat()			{ echo "$IPT" -t nat "$@"; }
  mangle()		{ echo "$IPT" -t mangle "$@"; }
  filter()		{ echo "$IPT" -t filter "$@"; }

  if STATELESS
  then
    filNEW()		{ echo "$IPT" -t filter "$@"; }
    filREL()		{ echo "$IPT" -t filter "$@"; }
  else
    filNEW()		{ echo "$IPT" -t filter "$@" -m state --state NEW; }
    filREL()		{ echo "$IPT" -t filter "$@" -m state --state RELATED; }
  fi
elif PRINT_CMDS
then
  nat()			{ echo "$IPT" -t nat "$@";	"$IPT" -t nat "$@"; }
  mangle()		{ echo "$IPT" -t mangle "$@";	"$IPT" -t mangle "$@"; }
  filter()		{ echo "$IPT" -t filter "$@";	"$IPT" "$@"; }

  if STATELESS
  then
    filNEW()		{ echo "$IPT" -t filter "$@"; }
    filREL()		{ echo "$IPT" -t filter "$@"; }
  else
    filNEW()		{ echo "$IPT" -t filter "$@" -m state --state NEW;
			  "$IPT" -t filter "$@" -m state --state NEW; }
    filREL()		{ echo "$IPT" -t filter "$@" -m state --state RELATED;
			  "$IPT" -t filter "$@" -m state --state RELATED; }
  fi
elif NO_ACTION
then
  nat()			{ : "$IPT" -t nat "$@"; }
  mangle()		{ : "$IPT" -t mangle "$@"; }
  filter()		{ : "$IPT" -t filter "$@"; }

  if STATELESS
  then
    filNEW()		{ : "$IPT" -t filter "$@"; }
    filREL()		{ : "$IPT" -t filter "$@"; }
  else
    filNEW()		{ : "$IPT" -t filter "$@" -m state --state NEW; }
    filREL()		{ : "$IPT" -t filter "$@" -m state --state RELATED; }
  fi
else
  nat()			{ "$IPT" -t nat "$@"; }
  mangle()		{ "$IPT" -t mangle "$@"; }
  filter()		{ "$IPT" -t filter "$@"; }

  if STATELESS
  then
    filNEW()		{ "$IPT" -t filter "$@"; }
    filREL()		{ "$IPT" -t filter "$@"; }
  else
    filNEW()		{ "$IPT" -t filter "$@" -m state --state NEW; }
    filREL()		{ "$IPT" -t filter "$@" -m state --state RELATED; }
  fi
fi

: '
  COMMAND PROCESSING
  ##################
'

case "$1" in
'status')	"$IPT" -L -v -n; exit $?;;

'stop'|'open')	iptablesPolicy 'ACCEPT'
		iptablesReset
		exit $?;;

'shut'|'close')	iptablesPolicy 'DROP'
		iptablesReset
		exit $?;;

'start')	: 'The rest of this script';;

*)
  echo 1>&2 "$ME: command '$1' invalid"
  echo 1>&2 "  $ME$HELP"
  exit 1;;
esac

iptablesPolicy 'ACCEPT'
iptablesReset

: '---------------------------------------------------------------------'

if ALLOW_H323
then
  TCP_P_BX_L="$TCP_P_BX_L 1719,1720"
  UDP_P_BX="$UDP_P_BX 1718,1719"

  : 'UDP_P_BX="5000:5020 $UDP_P_BX"'

  : 'UDP_P_MIN_DEL="5000:5020 $UDP_P_MIN_DEL"'
fi

: '
  Extra source, destination and forward address ranges accepted on the
  local and external interfaces in addition to the canonical address.
  Which is "$LNET"/"$LNIP" for "$LNDV" or "$ANY"/"$EXIP" for "$EXDV")
'

case "$IP" in
'v4')
  LNSR_X="$UNI4_LINK $MUL4_GLOB"
  LNDS_X="$UNI4_LINK $MUL4_GLOB $LNBC $MSBC"

  LNSR_L="$LNSR_X"
  LNDS_L="$LNDS_X"

  if LNDV_LNIP4_ONLY
  then LNLI="$LNIP"
  else LNLI="$LNET"
  fi

  if ! LNDV_LNET4_ONLY
  then
    LNSR_X="$INET $LNSR_X"
    LNDS_X="$INET $LNDS_X"
  fi

  EXSR_L="$MUL4_GLOB"
  EXDS_L="$MUL4_GLOB"

  EXSR_X="$EXSR_L"
  EXDS_X="$EXDS_L"

  if ! EXDV_EXIP4_ONLY || MASQUERADING
  then
    EXSR_L="$LNET $EXSR_L"
    EXDS_L="$LNET $EXDS_L"
  fi

  FWSR=""
  FWDS=""
  ;;

'v6')
  LNSR_X="$UNI6_LINK"
  LNDS_X="$UNI6_LINK $MUL6_LINK $MUL6_SITE"

  LNSR_L="$LNSR_X"
  LNDS_L="$LNDS_X"

  if LNDV_LNIP6_ONLY
  then LNLI="$LNIP"
  else LNLI="$LNET"
  fi

  if ! LNDV_LNET6_ONLY
  then
    LNSR_X="$INET $LNSR_X"
    LNDS_X="$INET $LNDS_X"
  fi

  EXSR_L="$UNI6_LINK"
  EXDS_L="$UNI6_LINK $MUL6_LINK"

  EXSR_X="$EXSR_L"
  EXDS_X="$EXDS_L"

  if ! EXDV_EXIP6_ONLY
  then
    EXSR_L="$LNET $EXSR_L"
    EXDS_L="$LNET $EXDS_L"
  fi

  FWSR=""
  FWDS=""
  ;;
esac

: ######################################################################

: '
  TABLE AND CHAIN INITIALIZATION
  ##############################
'

: ----------------------------------------------------------------------

for C in \
  OPEN_L REJECT_L RESET_L DROP_L_A DROP_L_P \
  DROP_MANY DROP_FRAG DROP_FLAGS \
  \
  IP_H_CHK IP_H_NSYN IP_A_CHK PTYPE_I PTYPE_O \
  \
  TCP_P_B TCP_P_O \
  TCP_P_BL_S TCP_P_OL_S \
  TCP_P_BX_L TCP_P_OX_L \
  TCP_P_BX TCP_P_OX \
  \
  UDP_P_B UDP_P_O \
  UDP_P_BL_S UDP_P_OL_S \
  UDP_P_BX_L UDP_P_OX_L \
  UDP_P_BX UDP_P_OX \
  \
  ICMP_T_B ICMP_T_O \
  ICMP_T_BL ICMP_T_OL \
  ICMP_T_BX ICMP_T_OX
do filter -N "$C"
done

if ALLOW_H323
then
for C in \
  TCP_P_BX3 UDP_P_BX3
  do filter -N "$C"
  done
fi

if ALLOW_GAMES
then
for C in \
  TCP_P_OXG UDP_P_BXG UDP_P_OXG
  do filter -N "$C"
  done
fi

if STATELESS
then
  for C in \
    TCP_P_IN TCP_P_ON \
    UDP_P_IN UDP_P_ON
  do filter -N "$C"
  done
fi

: '
  These chains must be created regardless of whether
  there is a local or external device because they
  are referenced anyhow, even if empty, and it is
  too complicated to change that.
'

for C in \
  LNDV_I LNDV_O \
  \
  IP_A_IL_S IP_A_IL_D \
  IP_A_OL_S IP_A_OL_D \
  \
  TCP_P_BL TCP_P_OL \
  UDP_P_BL UDP_P_OL
do filter -N "$C"
done

for C in \
  EXDV_I EXDV_O \
  \
  IP_A_IX_S IP_A_IX_D \
  IP_A_OX_S IP_A_OX_D
do filter -N "$C"
done

for C in \
  LNDV_EX EXDV_LN \
  \
  IP_A_LX_S IP_A_LX_D \
  IP_A_XL_S IP_A_XL_D
do filter -N "$C"
done

: ######################################################################

: '
  CHAINS FOR LOGGING
  ##################
'

if USE_LOG
then filter -A OPEN_L	-j LOG		-m limit --limit 30/h \
					  --log-level debug \
					  --log-prefix "OPEN_L: "
fi

: ---------------------------------------------------------------------

if USE_LOG
then filter -A REJECT_L	-j LOG		-m limit --limit 30/h \
					  --log-level info \
					  --log-prefix "REJECT_L: "
fi
if USE_REJECT
then filter -A REJECT_L	-j REJECT
fi
filter -A REJECT_L	-j DROP

: ---------------------------------------------------------------------

if USE_LOG
then filter -A RESET_L	-j LOG		-m limit --limit 30/h \
					  --log-level info \
					  --log-prefix "RESET_L: "
fi
if USE_REJECT
then filter -A RESET_L	-j REJECT	-p tcp --reject-with tcp-reset
fi
filter -A RESET_L	-j DROP

: ----------------------------------------------------------------------

if USE_LOG
then filter -A DROP_L_A -j LOG		-m limit --limit 60/h \
					  --log-level info \
					  --log-prefix "DROP_L_A: "
fi
filter -A DROP_L_A	-j DROP

: ----------------------------------------------------------------------

if USE_LOG
then filter -A DROP_L_P -j LOG		-m limit --limit 60/h \
					  --log-level warning \
					  --log-prefix "DROP_L_P: "
fi
filter -A DROP_L_P	-j DROP

: ----------------------------------------------------------------------

if USE_LOG
then filter -A DROP_FRAG -j LOG		-m limit --limit 60/h \
					  --log-level warning \
					  --log-prefix "DROP_FRAG: "
fi
filter -A DROP_FRAG	-j DROP

: ---------------------------------------------------------------------

if USE_LOG
then filter -A DROP_FLAGS -j LOG	-m limit --limit 60/h \
					  --log-level error \
					  --log-prefix "DROP_FLAGS: "
fi
filter -A DROP_FLAGS	-j DROP

: ---------------------------------------------------------------------

if USE_LOG
then filter -A DROP_MANY -j LOG		-m limit --limit 60/h \
					  --log-level error \
					  --log-prefix "DROP_MANY: "
fi
filter -A DROP_MANY	-j DROP

: ######################################################################

: '
  CHAINS CHECKING FOR MALFORMED PACKET HEADERS
  ############################################
'

: 'IP_H_NSYN for NEW packets that have no SYN'

filter -A IP_H_NSYN	-j RETURN	-p tcp --tcp-flags ACK,FIN ACK,FIN
if USE_LOG
then filter -A IP_H_NSYN -j LOG		-m limit --limit 60/h \
					  --log-level warning \
					  --log-prefix "IP_H_NSYN: "
fi
filter -A IP_H_NSYN	-j DROP

: ---------------------------------------------------------------------

: '
  Note that "IP_H_CHK" is applied, with equanimity, to _all_
  "filter" chains, including the output ones.

  <URL:http://WWW.CS.Princeton.edu/~jns/security/filter/>
'

case "$IP" in
v4)
  filter -A IP_H_CHK	-j DROP_FRAG	-f ;;
v6)
  if grep -q -w '^frag' "$NF_MATCH"
  then filter -A IP_H_CHK -j DROP_FRAG	-m frag
  fi
esac

: '
  <URL:https://www.cyberciti.biz/tips/linux-iptables-10-how-to-block-common-attack.html>
  <URL:http://WWW.ValtelLinux.IT/documenti/pedro/>
  <URL:http://WWW.CS.Princeton.edu/~jns/security/filter/>
  <URL:http://WWW.IAE.NL/users/guido/papers/tcp_filtering.ps.gz>
'
if ! STATELESS
then filNEW -A IP_H_CHK	-j IP_H_NSYN	-p tcp \! --tcp-flags SYN SYN
fi

: 'Xmas tree scans'
filter -A IP_H_CHK	-j DROP_FLAGS	-p tcp --tcp-flags ALL ALL
filter -A IP_H_CHK	-j DROP_FLAGS	-p tcp --tcp-flags ALL NONE

: 'Other bad flags'
filter -A IP_H_CHK	-j DROP_FLAGS	-p tcp --tcp-flags ALL PSH,URG,FIN
filter -A IP_H_CHK	-j DROP_FLAGS	-p tcp --tcp-flags SYN,RST SYN,RST
filter -A IP_H_CHK	-j DROP_FLAGS	-p tcp --tcp-flags SYN,FIN SYN,FIN

: 'Check for too many packets of a few critical types'

: '
  Ping of death, SYN flood, furtive port scanner
  <URL:http://WWW.CS.Princeton.edu/~jns/security/filter/>
'

if false
then
  filter -A IP_H_CHK	-j DROP_MANY 	-p "$ICMP" --"$ICMP"-type echo-request \
					-m limit \! --limit 10/s

  : '
    Some web pages have lots of elements, and some browsers rapidly
    create lots of parallel HTTP connections to retrieve those.
  '
  filter -A IP_H_CHK	-j DROP_MANY	-p tcp --tcp-flags SYN,RST,ACK SYN \
					-m limit \! --limit 300/s \
					  \! --limit-burst 120

  filter -A IP_H_CHK	-j DROP_MANY 	-p tcp --tcp-flags SYN,RST,ACK,FIN RST \
					-m limit \! --limit 10/s
fi

: ########################################################################

: '
  CHAINS CHECKING FOR INVALID SOURCE OR DESTINATION ADDRESSES
  ###########################################################

  After cheacking the IP header for well formedness, we want to check
  the IP addresses for validity. This is done for both standards
  conformance (some addresses should not be leaked to the Internet)
  and for security, as often invalid addresses are used in attacks.
  The security is a bit pointless, because it is perfectly possible
  for malicious packets to have valid addresses which are simply
  forged or unallocated, but just in case...

  Note that "IP_BXDDR" is applied, with equanimity, to
  _all_ "filter" chains, including the output ones.

  Note that this is done *after* the check for traffic on the "lo"
  interface, so we might get (invalid) ``local"" addresses still.

  CHECK FOR GENERALLY INVALID INVALID ADDRESSES
  ---------------------------------------------

  We first check for IP packets independently of any host or site
  specific ranges, whether validity is determined by the relevant
  standards or the IANA practice. Only in special cases we associate the
  test with an interface, because the standards specifically forbid or
  allow some addresses only on the Internet (external interface) or
  on site (local interface).

  First we drop packets where either address is forbidden,  and then
  we continue processing where both are allowed.
'

case "$IP" in
'v4')
  : '
    Well, famously most of IPv4 is allocated, so the default here
    is to continue processing, and we just drop packets with an
    obviously invalid address, except for those valid addresses which may
    belong to an otherwise invalid range, and we need to check these first.

    We don"t really need "IP_A_D", but it"s mentioned later...

    It may be that the local subnet and host address are reserved,
    so before dropping them, continue processing packets with those
    addresses, just in case.
  '
  case "$LNDV" in ?*)
    case "$LNET" in ?*)
      filter -A IP_A_CHK	-j RETURN	-i "$LNDV" -s "$LNET"
      filter -A IP_A_CHK	-j RETURN	-o "$LNDV" -d "$LNET";;
    esac
    case "$LNIP" in ?*)
      filter -A IP_A_CHK	-j RETURN	-o "$LNDV" -s "$LNIP"
      filter -A IP_A_CHK	-j RETURN	-i "$LNDV" -d "$LNIP";;
    esac
    case "$LNBC" in ?*)
      filter -A IP_A_CHK	-j RETURN	-i "$LNDV" -d "$LNBC"
      filter -A IP_A_CHK	-j RETURN	-o "$LNDV" -d "$LNBC";;
    esac
  esac

  : '
    It may be that the *external* address is a private address, so
    so before dropping them, continue processing packets with that
    address, just in case.
  '
  case "$EXDV" in ?*)
    case "$EXIP" in ?*)
      filter -A IP_A_CHK	-j RETURN	-i "$EXDV" -d "$EXIP"
      filter -A IP_A_CHK	-j RETURN	-o "$EXDV" -s "$EXIP";;
    esac
  esac

  for PREF in $IP4RESV4
  do case "$PREF" in '') continue;; esac
    filter -A IP_A_CHK	-j DROP_L_A	-d "$PREF"
  done;;

'v6')
  : '
    Well, 85% of the IPv6 is not yet (as of end 2003) allocated, so the
    default is to drop, and we continue processing only if we have an
    obviously valid address, except for those invalid addresses which may
    belong to an otherwise valid range, and we need to check these first.
  '

  for PREF in $UNI_GLOB "$UNI_LINK" "$MUL_LINK" "$MUL_SITE" "$UNI_SITE"
  do case "$PREF" in '') continue;; esac
    filter -A IP_A_CHK	-j RETURN	-s "$PREF"
    filter -A IP_A_CHK	-j RETURN	-d "$PREF"
  done

  : '
    If one of the addresses are part of the collection of invalid IPv4
    addresses, and has in one of the IPv4-in-IPv6 prefixes, we need to
    drop it before allowing the IP4-in-IPv6 address.
  '
  for PREF in $IP4RESV6
  do case "$PREF" in '') continue;; esac
    filter -A IP_A_CHK	-j DROP_L_A	-s "$PREF"
    filter -A IP_A_CHK	-j DROP_L_A	-d "$PREF"
  done

  for PREF in "$UNI6_6TO4" "$UNI6_4MAP" "$UNI6_4CPT"
  do case "$PREF" in '') continue;; esac
    filter -A IP_A_CHK	-j RETURN	-s "$PREF"
    filter -A IP_A_CHK	-j RETURN	-d "$PREF"
  done

  filter -A IP_A_CHK	-j DROP_L_A;;
esac

: '
  CHAINS FOR SPECIFICALLY INVALID ADDRESSES
  -----------------------------------------

  When we look at addresses in the contect of the current site,
  we need only to check for addresses known valid for it, and
  only valid for the direction of traffic they are valid for,
  because not all combinations are valid.
'

jumpIfSD() \
{
  TARGET="$1"
  CH_S="$2"
  IP_S="$3"
  CH_D="$4"
  IP_D="$5"

  for S in $IP_S
  do case "$S" in '') continue;; esac
    filter -A "$CH_S"	-j RETURN	-s "$S"
  done

  for D in $IP_D
  do case "$D" in '') continue;; esac
    filter -A "$CH_D"	-j RETURN	-d "$D"
  done
}

case "$LNDV" in ?*)
  case "$LNET;$LNLI" in ?*';'?*)
    jumpIfSD 'RETURN' 'IP_A_IL_S' "$LNET $LNSR_X" 'IP_A_IL_D' "$LNLI $LNDS_L"
    jumpIfSD 'RETURN' 'IP_A_OL_S' "$LNLI $LNSR_L" 'IP_A_OL_D' "$LNET $LNDS_X";;
  esac

  for C in IP_A_IL_S IP_A_IL_D IP_A_OL_S IP_A_OL_D
  do filter -A "$C"	-j DROP_L_A
  done;;
esac

case "$EXDV" in ?*)
  case "$INET;$EXIP" in ?*';'?*)
    jumpIfSD 'RETURN' 'IP_A_IX_S' "$INET $EXSR_X" 'IP_A_IX_D' "$EXIP $EXDS_L"
    jumpIfSD 'RETURN' 'IP_A_OX_S' "$EXIP $EXSR_L" 'IP_A_OX_D' "$INET $EXDS_X";;
  esac

  : '
    If we have a dynamic external address we want any TCP output packets
    on the external interface to an old external address to trigger a reset,
    instead of dropped.
  '
  if EXDV_EXIP4_DYNAMIC || EXDV_EXIP6_DYNAMIC
  then filter -A IP_A_OX_D -j RESET_L	-o "$EXDV" -p tcp
  fi

  for C in IP_A_IX_S IP_A_IX_D IP_A_OX_S IP_A_OX_D
  do filter -A "$C"	-j DROP_L_A
  done;;
esac

case "$LNDV;$EXDV" in ?*';'?*)
  case "$INET;$LNET" in ?*';'?*)
    jumpIfSD 'RETURN' 'IP_A_LX_S' "$LNET $FWSR" 'IP_A_LX_D' "$INET $FWDS"
    jumpIfSD 'RETURN' 'IP_A_XL_S' "$INET $FWSR" 'IP_A_XL_D' "$LNET $FWDS";;
  esac

  for C in IP_A_LX_S IP_A_LX_D IP_A_XL_S IP_A_XL_D
  do filter -A "$C"	-j DROP_L_A
  done;;
esac

: #######################################################################

: '
  CHAINS FOR CHECKING PORTS
  #########################


  PORT CHAINS: INPUT TCP SESSIONS
  -------------------------------

  We allow incoming session opening packets to the either way ports, plus
  to either way local only ports if we are on the local interface, on a
  separate chain.

  Incoming packets that are not session opening and that:
  * go to a client port from a server port because they are the reply
    in an outgoing session to a remote server;
  * go to a server port from any port because they follow a session
    open packet and thus are the request in an incoming session to server.
  go to the stateless chain. But we allow in both cases _any_ originating
  port, as this simplifies things considerably and is not that terrible a risk.

  For input from the local interface we allow some local only services
  plus the others.
'
: 'for S in "$LNET" $LNSR_L'
for L in "$LNET"
do case "$L" in '') continue;; esac
  filter -A TCP_P_BL_S	-j TCP_P_BL	-s "$L"
done

for P in $TCP_P_BL
do case "$P" in '') continue;; esac
  filter -A TCP_P_BL	-j ACCEPT 	-p tcp -m multiport --dp "$P"
done

for P in $TCP_P_BX_L
do case "$P" in '') continue;; esac
  filter -A TCP_P_BX_L	-j OPEN_L 	-p tcp -m multiport --dp "$P"
done
for P in $TCP_P_BX $TCP_P_BX_L
do case "$P" in '') continue;; esac
  filter -A TCP_P_BX	-j ACCEPT	-p tcp -m multiport --dp "$P"
done
if ALLOW_H323
then filter -A TCP_P_BX	-j TCP_P_BX3
fi

if STATELESS
then
  : 'Packets on non-new connections.'
  filter -A TCP_P_IN	-j TCP_P_BL_S
  filter -A TCP_P_IN	-j TCP_P_BX

  filter -A TCP_P_IN	-j ACCEPT	-p tcp --dp "$PORTS_CLIENT"
fi

for L in "$LNET"
do case "$L" in '') continue;; esac
  filter -A TCP_P_B	-j TCP_P_BL	-s "$L"
done
filter -A TCP_P_B	-j TCP_P_BX_L
filter -A TCP_P_B	-j TCP_P_BX

: '
  PORT CHAINS: OUTPUT TCP SESSIONS
  --------------------------------

  Above in chain "TCP_P_OX" we allow outgoing sessions open packets to
  servers ports. Here we allow for outgoing packets that are not session
  opening and that:
  * go to a client port from a server port because they are the reply
    in an incoming session to a server;
  * follow a session open packet and go to a server port from a client
    port or server port because they are the request in an outgoing
    session to a server.
  But we allow in both cases _any_ originating port, as this simplifies
  things considerably and is not that terrible a risk.
'

for P in $TCP_P_OX_L
do case "$P" in '') continue;; esac
  filter -A TCP_P_OX_L	-j OPEN_L	-p tcp -m multiport --dp "$P"
done
for P in $TCP_P_OX $TCP_P_OX_L
do case "$P" in '') continue;; esac
  filter -A TCP_P_OX	-j ACCEPT	-p tcp -m multiport --dp "$P"
done
if ALLOW_OUTGOING || ALLOW_OUTGOING_TCP
then filter -A TCP_P_OX	-j ACCEPT	-p tcp -m state --state NEW
fi
if ALLOW_H323
then filter -A TCP_P_OX	-j TCP_P_BX3
fi
if ALLOW_GAMES
then filter -A TCP_P_OX	-j TCP_P_OXG
fi

for P in $TCP_P_OL
do case "$P" in '') continue;; esac
  filter -A TCP_P_OL	-j ACCEPT 	-p tcp -m multiport --dp "$P"
done

: 'for L in "$LNET" $LNDS_L'
for L in "$LNET"
do case "$L" in '') continue;; esac
  filter -A TCP_P_OL_S	-j TCP_P_OL	-p tcp -d "$L"
done
if STATELESS
then
  filter -A TCP_P_ON	-j TCP_P_OL_S
  filter -A TCP_P_ON	-j TCP_P_BL_S
  filter -A TCP_P_ON	-j TCP_P_OX
  filter -A TCP_P_ON	-j TCP_P_BX

  filter -A TCP_P_ON	-j ACCEPT	-p tcp --dp "$PORTS_CLIENT"
fi

for L in "$LNET"
do case "$L" in '') continue;; esac
  filter -A TCP_P_O	-j TCP_P_OL	-d "$L"
  filter -A TCP_P_O	-j TCP_P_BL	-d "$L"
done
filter -A TCP_P_O	-j TCP_P_OX_L
filter -A TCP_P_O	-j TCP_P_OX
filter -A TCP_P_O	-j TCP_P_BX_L
filter -A TCP_P_O	-j TCP_P_BX

if ! TRACK_FTP || TRACK_FTP_CANT
then
  : '
    "NEW" for everything should not be needed, but "ip_conntrack_ftp"
    can"t track FTP data connections if the FTP client (like "lftp"
    by default, does asynchronous pipelining of FTP commands.

    Enabling all outgoing sessions from client ports allows passive FTP
    to work regardless, but I prefer to disable async FTP in the client.
  '

  filter -A TCP_P_O	-j ACCEPT	-p tcp --sp "$PORTS_CLIENT"
fi

: ---------------------------------------------------------------------

: '
  PORT CHAINS: INPUT UDP SESSIONS
  -------------------------------
'
for P in $UDP_P_BX_L
do case "$P" in '') continue;; esac
  filter -A UDP_P_BX_L	-j OPEN_L 	-p udp -m multiport --dp "$P"
done

for P in $UDP_P_BX $UDP_P_BX_L
do case "$P" in '') continue;; esac
  filter -A UDP_P_BX	-j ACCEPT 	-p udp -m multiport --dp "$P"
done
if ALLOW_H323
then filter -A UDP_P_BX	-j UDP_P_BX3
fi
if ALLOW_GAMES
then filter -A UDP_P_BX	-j UDP_P_BXG
fi

for P in $UDP_P_BL
do case "$P" in '') continue;; esac
  filter -A UDP_P_BL	-j ACCEPT 	-p udp -m multiport --dp "$P"
done

: 'for L in "$LNET" $LNSR_L'
for L in "$LNET"
do case "$L" in '') continue;; esac
  filter -A UDP_P_BL_S	-j UDP_P_BL 	-s "$L"
done
if STATELESS
then
  : 'Packets on non-new connections.'
  filter -A UDP_P_IN	-j UDP_P_BL_S
  filter -A UDP_P_IN	-j UDP_P_BX

  filter -A UDP_P_IN	-j ACCEPT	-p udp --dp "$PORTS_CLIENT"
else
  : 'Special case as sometimes "RELATED" does not work with DNS datagrams.'
  filter -A UDP_P_B	-j ACCEPT	-p udp --sp domain
fi

for L in "$LNET"
do case "$L" in '') continue;; esac
  filter -A UDP_P_B	-j UDP_P_BL	-s "$L"
done
filter -A UDP_P_B	-j UDP_P_BX_L
filter -A UDP_P_B	-j UDP_P_BX

: '
  PORT CHAINS: OUTPUT UDP SESSIONS
  --------------------------------
'

for P in  $UDP_P_OX_L
do case "$P" in '') continue;; esac
  filter -A UDP_P_OX_L	-j OPEN_L	-p udp -m multiport --dp "$P"
done
for P in $UDP_P_OX $UDP_P_OX_L
do case "$P" in '') continue;; esac
  filter -A UDP_P_OX	-j ACCEPT	-p udp -m multiport --dp "$P"
done
if ALLOW_OUTGOING || ALLOW_OUTGOING_UDP
then filter -A UDP_P_OX	-j ACCEPT	-p udp -m state --state NEW
fi
if ALLOW_H323
then filter -A UDP_P_OX	-j UDP_P_BX3
fi
if ALLOW_GAMES
then filter -A UDP_P_OX	-j UDP_P_OXG
fi

for P in $UDP_P_OL
do case "$P" in '') continue;; esac
  filter -A UDP_P_OL	-j ACCEPT	-p udp -m multiport --dp "$P"
done

: 'for L in "$LNET" $LNSR_L'
for L in "$LNET"
do case "$L" in '') continue;; esac
  filter -A UDP_P_OL_S	-j UDP_P_OL 	-s "$L"
done
if STATELESS
then
  filter -A UDP_P_ON	-j UDP_P_OL_S
  filter -A UDP_P_ON	-j UDP_P_OX
  filter -A UDP_P_ON	-j UDP_P_BL_S
  filter -A UDP_P_ON	-j UDP_P_BX

  filter -A UDP_P_ON	-j ACCEPT	-p udp --dp "$PORTS_CLIENT"
else
  : 'Special case as sometimes "RELATED" does not work with DNS datagrams.'
  filter -A UDP_P_OX	-j ACCEPT	-p udp --sp domain
fi

for L in "$LNET"
do case "$L" in '') continue;; esac
  filter -A UDP_P_O	-j UDP_P_BL	-d "$L"
  filter -A UDP_P_O	-j UDP_P_OL	-d "$L"
done
filter -A UDP_P_O	-j UDP_P_OX_L
filter -A UDP_P_O	-j UDP_P_OX
filter -A UDP_P_O	-j UDP_P_BX_L
filter -A UDP_P_O	-j UDP_P_BX

: ---------------------------------------------------------------------

: '
  "NEW" should work for "echo-request" (e.g. PING), but doesn"t...
  "RELATED" should work for "time-exceeded" (e.g. PING), but doesn"t...
'

: '
  PORT CHAINS: ICMP INPUT TYPES
  -----------------------------
'

for T in $ICMP_T_BXS
do filter -A ICMP_T_BX	-j ACCEPT	-p "$ICMP" --"$ICMP"-type "$T"
done

for T in $ICMP_T_BX
do filNEW -A ICMP_T_BX	-j ACCEPT 	-p "$ICMP" --"$ICMP"-type "$T"
done

for T in $ICMP_T_BLS
do filter -A ICMP_T_BL -j ACCEPT 	-p "$ICMP" --"$ICMP"-type "$T"
done

for L in "$LNET"
do case "$L" in '') continue;; esac
  filter -A ICMP_T_B	-j ICMP_T_BL	-p "$ICMP" -s "$L"
  case "$IP" in 'v6')
    filter -A ICMP_T_B	-j ICMP_T_BL	-p "$ICMP" -s "$UNI_LINK";;
  esac
  case "$MUL_LINK" in ?*)
    filter -A ICMP_T_B	-j ICMP_T_BL	-p "$ICMP" -d "$MUL_LINK";;
  esac
done
filter -A ICMP_T_B	-j ICMP_T_BX	-p "$ICMP"

: '
  PORT CHAINS: ICMP OUTPUT TYPES
  ------------------------------
'

for T in $ICMP_T_OXS
do filter -A ICMP_T_OX	-j ACCEPT	-p "$ICMP" --"$ICMP"-type "$T"
done
for T in $ICMP_T_OX
do filNEW -A ICMP_T_OX	-j ACCEPT	-p "$ICMP" --"$ICMP"-type "$T"
done

for T in $ICMP_T_OLS
do filter -A ICMP_T_OL -j ACCEPT	-p "$ICMP" --"$ICMP"-type "$T"
done

for L in "$LNET"
do case "$L" in '') continue;; esac
  filter -A ICMP_T_O	-j ICMP_T_OL	-p "$ICMP" -d "$L"
  filter -A ICMP_T_O	-j ICMP_T_BL	-p "$ICMP" -d "$L"
done
filter -A ICMP_T_O	-j ICMP_T_OX	-p "$ICMP"
filter -A ICMP_T_O	-j ICMP_T_BX	-p "$ICMP"

: ---------------------------------------------------------------------

if ALLOW_H323
then
  for P in '30000:30020'
  do
    filNEW -A TCP_P_BX3	-j ACCEPT	-p tcp --dp "$P"
  done

  for P in '5000:5020'
  do
    filter -A UDP_P_BX3	-j ACCEPT	-p udp --dp "$P"
  done

  case "$IP" in v4)
    for P in '5000:5020'
    do
      mangle -I OUTPUT	-j TOS		-p udp --sp "$P" \
					      --set-tos Minimize-Delay
      mangle -I OUTPUT	-j TOS		-p udp --dp "$P" \
					      --set-tos Minimize-Delay
    done;;
  esac
fi

: '
  PORT CHAINS: GAME SESSIONS, TCP AND UDP, INPUT OR OUTPUT
  --------------------------------------------------------
'

if ALLOW_GAMES
then
  case "$IP" in
  v4)
    : '
      Tribes2:
        http://tribes.ntdf.com/Information/information_T2faqs.htm#47-ports
    '

    for P in '6660:6669' '6672:6680'
    do
      filNEW -A TCP_P_OXG	-j ACCEPT	-p tcp --dp "$P"
    done

    for P in '15101,15104,15200,15204,15206,15800'
    do
      filNEW -A TCP_P_OXG	-j ACCEPT	-p tcp -m multiport --dp "$P"
    done

    for P in '27999:29001'
    do
      filter -A UDP_P_OXG	-j ACCEPT	-p udp --dp "$P"
      filREL -A UDP_P_BXG	-j ACCEPT 	-p udp --sp "$P"
    done

    : 'Quake3, Q3F'

    for P in '27950:27952' '27960:27995'
    do
      filter -A UDP_P_OXG	-j ACCEPT	-p udp --dp "$P"
      filREL -A UDP_P_BXG	-j ACCEPT 	-p udp --sp "$P"
    done
    ;;
  esac
fi

: --------------------------------------------------------------------

: '
  PORT CHAINS: CHECK ALL PROTOCOLS
  --------------------------------
'

if STATELESS
then
  : 'Packets on non-new connections.'
  filter -A PTYPE_I	-j TCP_P_IN	-p tcp --tcp-flags \! SYN SYN
  filter -A PTYPE_O	-j TCP_P_ON	-p tcp --tcp-flags \! SYN SYN

  filter -A PTYPE_I	-j UDP_P_IN	-p udp
  filter -A PTYPE_O	-j UDP_P_ON	-p udp
fi

filNEW -A PTYPE_I	-j TCP_P_B	-p tcp --tcp-flags SYN SYN
filNEW -A PTYPE_O	-j TCP_P_B	-p tcp --tcp-flags SYN SYN
filNEW -A PTYPE_O	-j TCP_P_O	-p tcp --tcp-flags SYN SYN

filNEW -A PTYPE_I	-j UDP_P_B	-p udp
filNEW -A PTYPE_O	-j UDP_P_B	-p udp
filNEW -A PTYPE_O	-j UDP_P_O	-p udp

filter -A PTYPE_I	-j ICMP_T_B	-p "$ICMP"
filter -A PTYPE_O	-j ICMP_T_B	-p "$ICMP"
filter -A PTYPE_O	-j ICMP_T_O	-p "$ICMP"

case "$IP" in 'v6')
  : 'Special special case; HBH broadcasts on a link.'
  filter -A PTYPE_I	-j ACCEPT	-s fe80::/16 -d ff02::1;;
esac

if true
then
  filter -A PTYPE_I	-j DROP_L_P	-p tcp
  filter -A PTYPE_I	-j DROP_L_P	-p udp

  filter -A PTYPE_O	-j DROP_L_P	-p tcp
  filter -A PTYPE_O	-j DROP_L_P	-p udp
fi

: #######################################################################

: '
  ACTUAL FILTERING
  ################

  ALL PACKETS ACTION, CHECK IP FORMAT AND ADDRESSES
  -------------------------------------------------
'

: 'CHECK FOR RISKY PACKETS'

filter -A INPUT		-j IP_H_CHK
filter -A OUTPUT	-j IP_H_CHK
filter -A FORWARD	-j IP_H_CHK

: 'CHECK FOR VALID ADDRESSES'

filter -A INPUT		-j IP_A_CHK
filter -A OUTPUT	-j IP_A_CHK
filter -A FORWARD	-j IP_A_CHK

: '
  PROTOCOL BUT BUT NOT PORT DEPENDENT ACTIONS
  -------------------------------------------
'

: '
  SPECIAL CASES

  When a TCP connection starts allow its MSS to be clamped to the MTU.
  We might want sometimes to force the mss size with "--set-mss 1430".
'
case "$IP" in v4)
  filNEW -A OUTPUT	-j TCPMSS	-p tcp \
					  --tcp-flags SYN SYN --clamp-mss-to-pmtu;;
esac

: 'Let out the packets generated by "--reject-with tcp-reset"'
filter -A OUTPUT	-j ACCEPT	-p tcp \
					  --tcp-flags SYN,ACK,FIN,RST ACK,RST
filter -A FORWARD	-j ACCEPT	-p tcp \
					  --tcp-flags SYN,ACK,FIN,RST ACK,RST

: 'PASS THRU PROTOCOLS (TUNNELING MOSTLY)'

case "$IP" in
'v4')	IP_PROTO="$IP4_PROTO";;
'v6')	IP_PROTO="$IP6_PROTO";;
esac

: '
  IPSEC, IPv6-in-IPv4 and other tunnels. We do not let them happen across
  the local network, only to the current machine, so no forwarding.
'
case "$EXDV" in  ?*)
  for P in $IP_PROTO
  do
    filter -A INPUT	-j ACCEPT	-i "$EXDV" -p "$P"
    filter -A OUTPUT	-j ACCEPT	-o "$EXDV" -p "$P"
  done;;
esac

: '
  PACKETS IN A SESSION
  --------------------

  Now we finally do connection handling. How we do this depends
  on whether we have connection tracking and whether we have NAT.

  Connection tracking

    Ideally we only allow inbound and outbound connections to some
    well defined ports (which are different for external and local
    devices/addresses), and the outbound and inbound traffic that is the
    reverse flow for the inbound and outbound connections that we have
    allowed.

    This is easy to achieve with connections tracking: we just allow
    packets ESTABLISHED (for TCP) and/or RELATED (for UDP and ICMP), or
    NEW packets (some we log) for the permitted inbound or outbound ports.

    If we don"t have connection tracking it"s not so easy to see which
    packets belong to sessions set up by a connection. So we have to be
    laxer, and allow any packets to the permitted ports (and we log some
    of them with SYN), and outbound packets from any port.

  NAT

    If we have NAT and it is enabled, connections to inbound services
    are possible only to the gateway, and this means that the gateway
    in effect firewalls the entire local network. If there is no NAT,
    then connections can go thru to any machine on the local network.
'

: ---------------------------------------------------------------------

: '
  Later on we allow inbound and outbound connections to specific ports,
  so here we need only delay with packets not to those ports. This means
  packets that are inbound as a reply to a packet outbound on an allowed
  output port, or outbound as a reply to a packet inbound on an allowed
  input port.
'

if STATELESS
then
  : '
    Without connection tracking we have no direct idea of which packets
    are reply to packets to allowed ports. But we have two bits of
    information: for TCP, reply packets cannot ever be SYN packets,
    because SYN packets can only be sent to the allowed ports. Also,
    we know that in general reply packets are from the user ports, because
    usually connections are between a client that binds to a user port.

    Input here means a reply packet from a remote server to a local client.
    Packets (including SYN ones) from remote clients to local servers are
    handled below.

    Output here means a reply packet from a local client to a remote server.
    Packets (including SYN ones) from local servers to remote clients are
    handled below.

    For forwarding we need to taken into account both the inbound to local
    traffic and the from local to outbound traffic.

    We do all this in the relevant TCP and UDP input and output chains,
    together with checking for valid sessions opening later on, because
    we want address and protocol checking to be performed first.
  '
  :
else
  : '
    Connection tracking knows which packets are replies to which other
    packets, so we can just let it go ahead, it is so much easier. We can
    bypass here all further checking, as the connection trackers ensures
    that protocol, addresses and ports or ICMP types are valid for existing
    sessions, and the checking for valid session opening is done later.

    I think it"s always safe to accept "ESTABLISHED" and "RELATED" packets.
    "iptables" is about sessions, not really packets, and once a session
    has been allowed, all its packets should be as well, unless they are
    ``risky"", and we checked for those above.
  '

  filter -A INPUT	-j ACCEPT	-m state --state ESTABLISHED,RELATED
  filter -A OUTPUT	-j ACCEPT	-m state --state ESTABLISHED,RELATED
  filter -A FORWARD	-j ACCEPT	-m state --state ESTABLISHED,RELATED
fi

: '
  PACKETS CREATING SESSIONS TO/FROM THE LOCAL INTERFACE
  -----------------------------------------------------
'

case "$LNDV" in ?*)

  : 'ADDRESS CHECKING'

  filter -A LNDV_I	-j IP_A_IL_S
  filter -A LNDV_I	-j IP_A_IL_D	

  filter -A LNDV_O	-j IP_A_OL_S	
  filter -A LNDV_O	-j IP_A_OL_D	

  : 'SPECIAL CASE PORTS'

  : 'Ignore annoyingly frequent IPP broadcasts.'
  false && case "$IP;$LNDV;$MSBC" in 'v4;'?*';'?*)
    filter -A LNDV_I	-j DROP		-p udp -d "$MSBC" --dp ipp;;
  esac

  : 'PORT CHECKING'

  filter -A LNDV_I	-j PTYPE_I
  filter -A LNDV_O	-j PTYPE_O

  case "$LNDV;$LNET" in
  "$EXDV"';'?*)
    filter -A INPUT	-j LNDV_I	-i "$LNDV" -s "$LNET"
    filter -A OUTPUT	-j LNDV_O	-o "$LNDV" -d "$LNET";;
  ?*';'*)
    filter -A INPUT	-j LNDV_I	-i "$LNDV"
    filter -A OUTPUT	-j LNDV_O	-o "$LNDV";;
  esac;;
esac

: '
  PACKETS CREATING SESSIONS TO/FROM THE EXTERNAL INTERFACE
  --------------------------------------------------------
'

case "$EXDV" in ?*)

  : 'ADDRESS CHECKING'

  filter -A EXDV_I	-j IP_A_IX_S	
  filter -A EXDV_I	-j IP_A_IX_D	

  filter -A EXDV_O	-j IP_A_OX_S	
  filter -A EXDV_O	-j IP_A_OX_D	

  : 'SPECIAL CASE PORTS'

  : 'We reset "auth" service requests, but only
     incoming from the external device.'
  filNEW -A EXDV_I	-j RESET_L 	-p tcp \
					  --tcp-flags SYN SYN --dp auth

  : 'PORT CHECKING'

  filter -A EXDV_I	-j PTYPE_I
  filter -A EXDV_O	-j PTYPE_O

if true
then
  : 'Special case because of MSBLAST worm: drop silently some SMB packets.'
  case "$EXDV" in ?*)
    filter -A EXDV_I	-j DROP		-p udp --dp netbios-ns
    filter -A EXDV_I	-j DROP		-p tcp --dp loc-srv
    filter -A EXDV_I	-j DROP		-p tcp --dp microsoft-ds;;
  esac
fi

  case "$EXDV;$LNET" in
  "$LNDV"';'?*)
    filter -A INPUT	-j EXDV_I	-i "$EXDV" ! -s "$LNET"
    filter -A OUTPUT	-j EXDV_O	-o "$EXDV" ! -d "$LNET";;
  ?*';'*)
    filter -A INPUT	-j EXDV_I	-i "$EXDV"
    filter -A OUTPUT	-j EXDV_O	-o "$EXDV";;
  esac;;
esac

: ########################################################################

: '
  FORWARD BETWEEN LOCAL AND EXTERNAL INTERFACE
  ############################################

  Note that filtering, including forwarding, happens before the 'nat' table,
  so the addresses we see here are the unmasqueraded original ones. Nice!

  Also note that the both addresses are guaranteed to be on another host,
  so they cannot be "$LNIP" nor "$EXIP".
'

: '
  SPECIAL CASES
  -------------
'

if case "$IP" in 'v4') LNDV_LNET4_ONLY;; 'v6') LNDV_LNET6_ONLY;; esac
then
  : 'Do not add rules to allow transit packets.'
else
  case "$LNDV;$EXDV" in ?*';'?*)

    : '
      ADDRESS CHECKING
      ----------------
    '

    filter -A EXDV_LN	-j IP_A_XL_S	
    filter -A EXDV_LN	-j IP_A_XL_D	

    filter -A LNDV_EX	-j IP_A_LX_S	
    filter -A LNDV_EX	-j IP_A_LX_D	

    : '
      SPECIAL CASES
      -------------
    '

    case "$IP" in v4)
      filter -A FORWARD	-j TCPMSS	-p tcp \
					  --tcp-flags SYN SYN --clamp-mss-to-pmtu;;
    esac

    : 'We reset "auth" service requests, but only incoming
       from the external device.'
    filNEW -A EXDV_LN	-j RESET_L 	-p tcp \
					  --tcp-flags SYN SYN --dp auth

    : '
      PACKET TYPE CHECKING
      --------------------
    '

    filter -A EXDV_LN	-j PTYPE_I
    filter -A LNDV_EX	-j PTYPE_O

    filter -A FORWARD	-j EXDV_LN	-i "$EXDV" -o "$LNDV"
    filter -A FORWARD	-j LNDV_EX	-i "$LNDV" -o "$EXDV"
    ;;
  esac
fi

: ---------------------------------------------------------------------

: '
  REDIRECTING and MASQUERADING
  ############################
'

case "$EXDV" in ?*)
  if false
  then
    : 'Redirect all external connections to TCP port 80 to this computer.'
    nat -A PREROUTING -j DNAT		-p tcp --dp 80 -o "$EXDV" \
					--to-destination "$UCB":3128
    nat -A OUTPUT	-j DNAT		-p tcp --dp 80 -o "$EXDV" \
					--to-destination "$UCB":3128
  fi

  if MASQUERADING
  then
    : '
      Connections from the local network via the external interface
      are masqueraded to look as if they came from the external
      interface address.
    '
    case "$LNET;$EXIP" in ?*';'?*)
      if EXDV_EXIP4_DYNAMIC
      then nat -A POSTROUTING -j MASQUERADE	-o "$EXDV" \! -s "$EXIP"
      else nat -A POSTROUTING -j SNAT		-o "$EXDV" \! -s "$EXIP" \
						    --to-source "$EXIP"
      fi;;
    esac
  fi
esac

: ---------------------------------------------------------------------

: '
  MANGLING
  ########
'

case "$IP" in
v4)
  for P in $TCP_P_MIN_DEL
  do
    mangle -A OUTPUT	-j TOS		-p tcp -m multiport --sp "$P" \
					    --set-tos Minimize-Delay
    mangle -A OUTPUT	-j TOS		-p tcp -m multiport --dp "$P" \
					    --set-tos Minimize-Delay
  done

  for P in $TCP_P_MAX_THR
  do
    mangle -A OUTPUT	-j TOS		-p tcp -m multiport --sp "$P" \
					    --set-tos Maximize-Throughput
    mangle -A OUTPUT	-j TOS		-p tcp -m multiport --dp "$P" \
					    --set-tos Maximize-Throughput
  done

  for P in $TCP_P_MAX_REL
  do
    mangle -A OUTPUT	-j TOS		-p tcp -m multiport --sp "$P" \
					    --set-tos Maximize-Reliability
    mangle -A OUTPUT	-j TOS		-p tcp -m multiport --dp "$P" \
					    --set-tos Maximize-Reliability
  done

  for P in $UDP_P_MIN_DEL
  do
    mangle -A OUTPUT	-j TOS		-p udp -m multiport --sp "$P" \
					    --set-tos Minimize-Delay
    mangle -A OUTPUT	-j TOS		-p udp -m multiport --dp "$P" \
					    --set-tos Minimize-Delay
  done

  for P in $UDP_P_MAX_THR
  do
    mangle -A OUTPUT	-j TOS		-p udp -m multiport --sp "$P" \
					    --set-tos Maximize-Throughput
    mangle -A OUTPUT	-j TOS		-p udp -m multiport --dp "$P" \
					    --set-tos Maximize-Throughput
  done

  for P in $UDP_P_MAX_REL
  do
    mangle -A OUTPUT	-j TOS		-p udp -m multiport --sp "$P" \
					    --set-tos Maximize-Reliability
    mangle -A OUTPUT	-j TOS		-p udp -m multiport --dp "$P" \
					    --set-tos Maximize-Reliability
  done

  mangle -A OUTPUT	-j TOS		-p icmp \
					    --set-tos Maximize-Reliability
  for T in $ICMP_T_MIN_DEL
  do
    mangle -A OUTPUT	-j TOS		-p icmp --icmp-type "$T" \
					    --set-tos Minimize-Delay
  done
  ;;
esac

: ---------------------------------------------------------------------

: '
  LOG UNEXPECTED PACKETS
  #########################
'

if USE_LOG
then

  : '
    This is just to avoid the logging of some multicast packets
  '
  case "$LNDV" in ?*)
    case "$LNBC" in ?*)
      filter -A INPUT	-j DROP		-i "$LNDV" -p udp -d "$LNBC";;
    esac

    case "$IP" in
    'v4')
      filter -A INPUT	-j DROP		-i "$LNDV" -p igmp -d "224.0.0.1"
      filter -A INPUT	-j DROP		-i "$LNDV" -p udp -d "224.0.0.1"
      filter -A INPUT	-j DROP		-i "$LNDV" -p udp -d "224.0.0.251";;
    'v6')
      filter -A INPUT	-j DROP		-i "$LNDV" -p igmp -d "ff02::1"
      filter -A INPUT	-j DROP		-i "$LNDV" -p udp -d "ff02::1"
      filter -A INPUT	-j DROP		-i "$LNDV" -p udp -d "ff02::fb";;
    esac;;
  esac

  {
    filter -A INPUT	-j LOG		-m limit --limit 30/h \
					    --log-level warning \
					    --log-prefix "DROP_I last: "
    filter -A INPUT	-j DROP
  }

  {
    filter -A OUTPUT	-j LOG		-m limit --limit 30/h \
					    --log-level warning \
					    --log-prefix "DROP_O last: "
    filter -A OUTPUT	-j DROP
  }

  {
    filter -A FORWARD	-j LOG		-m limit --limit 30/h \
					    --log-level error \
					    --log-prefix "DROP_F last: "
    filter -A FORWARD	-j DROP
  }
fi

exit $?

: '
  Firewall setup script for iptables/netfilter, IPv4 and IPv6.
  Supports a single (optional) Internet interface with one address,
  and a single (optional) local interface with one address on one subnet.
  It does not support a DMZ, even if that would be fairly easy to add.
  This should cover almost all home/soho gateways/servers.

  Rules should be listed in an order that makes the most commonly hit
  rules first. This can be checked by looking at the rule counters.
'

: '
  Structure:

  Since we have a policy of deny, we first list tings we can deny quickly,
  then those we allow:

    * Drop generally bad stuff:
      - Drop bad headers.
      - Drop bad addresses.
    * Drop IP addresses that are not good for the interfaces and direction.
    * Accept packets belonging to existing sessions.
    * Accept session setup packets:
      - Accept connection packets to local-only TCP ports.
      - Accept connection packets to generally allowed TCP ports. 
      - Accept packets to local-only UDP ports.
      - Accept packets to generally allowed UDP ports. 
      - Accept packets ICMP with local-only types.
      - Accept packets ICMP generally allowed types.
  In each part, if applicable:
   * List IP first, then TCP, then UDP, then ICMP.
   * List INPUT, then OUTPUT, then FORWARD (if any)
   * List ACCEPT first, then OPEN_L, then REJECT_L/RESET_L, then DROP_L.
   * List internal interfaces, then local interfaces, then external interfaces.
   * List source address, then destination address.

  Sometimes we need to check whether two sets of conditions apply together,
  for example whether both the source and destination addresses are in
  set of allowed address ranges.

  Checking the AND of two conditions can be done by generating
  all the possible combinations, but this creates lots of rules. We can
  do it instead by creating two chains, both of which terminate with a
  jump to failure, in which the first checks for the first set, and the
  second for the second set, and we apply them sequentially.

  The order of configuration in the prologue is from more specific (more
  configurable) to less specific (less configurable).

  There is a list of the chains and their meaning at the end of this script
'

: '
  DROP AND UTILITY CHAINS
  #######################

  OPEN_L REJECT_L RESET_L	Log allowed, rejected, reset connections
  DROP_FLAGS DROP_FRAG		Log, drop packets with header issues
  DROP_L_A DROP_L_P		Log, drop packets with address or port issues
  DROP_MANY			Log, drop packets with too high traffic rate 

  HIGH LEVEL CHAINS BY PACKET FLOW
  ################################

  LNDV_I		Incoming into loc. dev.
  EXDV_I		Incoming into ext. dev.
 
  LNDV_O		Outgoing from loc. dev.
  EXDV_O		Outgoing from ext. dev.
 
  LNDV_EX		Forwarded from loc. to ext. dev.
  EXDV_LN		Forwarded from ext. to loc. dev.
'

:'
  IP HEADER CHECK CHAINS
  ######################

   IP_H_CHK		IP header validity
   IP_H_NSYN		IP header not following SYN check
   
   IP_A_CHK		IP address validity
   
    IP_A_IL_D		IP dst. address, incoming into loc. dev.
    IP_A_IL_S		IP src. address, incoming into loc. dev.
    IP_A_IX_D		IP dst. address, incoming into ext. dev.
    IP_A_IX_S		IP src. address, incoming into ext. dev.
 
    IP_A_OL_D		IP dst. address, outgoing from loc. dev.
    IP_A_OL_S		IP src. address, outgoing from loc. dev.
    IP_A_OX_D		IP dst. address, outgoing into ext. dev.
    IP_A_OX_S		IP src. address, outgoing from ext. dev.
 
    IP_A_LX_D		IP dst. address, forwarded from loc. to ext. dev.
    IP_A_LX_S		IP src. address, forwarded from loc. to ext. dev.
    IP_A_XL_D		IP dst. address, forwarded from ext. to loc. dev.
    IP_A_XL_S		IP src. address, forwarded from ext. to loc. dev.
'

: '
  ALLOWED SERVICES CHAINS, BY INCOMING/OUTGOING/FORWARDED
  #######################################################
 
   PTYPE_I		TCP/UDP/ICMP incoming port/type
   
    TCP_P_IN		TCP incoming only ports, all dev., stateless
    TCP_P_B		TCP incoming or outgoing ports, all dev., NEW+RELATED
     TCP_P_BL_S		TCP incoming or outgoing ports, loc. dev., check source
      TCP_P_BL		TCP incoming or outgoing ports, loc. dev.
     TCP_P_BX_L		TCP incoming or outgoing ports, ext. dev., logged
     TCP_P_BX		TCP incoming or outgoing ports, ext. dev.
     TCP_P_BX3		TCP incoming or outgoing ports, ext. dev.
    
    UDP_P_IN		UDP incoming only ports, all dev., stateless
    UDP_P_B		UDP incoming or outgoing ports, all dev., RELATED
     UDP_P_BL_S		UDP incoming or outgoing ports, loc. dev., check source
      UDP_P_BL		UDP incoming or outgoing ports, loc. dev.,
     UDP_P_BX_L		UDP incoming or outgoing ports, ext. dev., logged
     UDP_P_BX		UDP incoming or outgoing ports, ext. dev.
     UDP_P_BX3		UDP incoming or outgoing ports, ext. dev., H323
     UDP_P_BXG		UDP incoming or outgoing ports, ext. dev., games
    
    ICMP_T_B		ICMP incoming or outgoing types, all dev.
     ICMP_T_BLS		ICMP incoming or outgoing types, loc. dev., stateless
     ICMP_T_BXS		ICMP incoming or outgoing types, ext. dev., stateless
     ICMP_T_BX		ICMP incoming or outgoing types, ext. dev.
 
   PTYPE_O		TCP/UDP/ICMP outgoing port/type (also has _B* chains)
 
    TCP_P_ON		TCP outgoing only ports, all dev., stateless
    TCP_P_O		TCP outgoing only ports, all dev., NEW+RELATED
     TCP_P_OL_S		TCP outgoing only ports, loc. dev., check source
       TCP_P_OL		TCP outgoing only ports, loc. dev.
     TCP_P_OX_L		TCP outgoing only ports, ext. dev., logged
     TCP_P_OX		TCP outgoing only ports, ext. dev.
     TCP_P_OXG		TCP outgoing only ports, ext. dev., games
 
    UDP_P_ON		UDP outgoing only ports, all dev., stateless
    UDP_P_O		UDP outgoing only ports, all dev., RELATED
     UDP_P_OL_S		UDP outgoing only ports, loc. dev., check source
      UDP_P_OL		UDP outgoing only ports, loc. dev.
     UDP_P_OX_L		UDP outgoing only ports, ext. dev., logged
     UDP_P_OX		UDP outgoing only ports, ext. dev.
     UDP_P_OXG		UDP outgoing only ports, ext. dev., games
 
    ICMP_T_O		ICMP outgoing only types, all dev.
     ICMP_T_OLS		ICMP outgoing only types, loc. dev., stateless
     ICMP_T_OXS		ICMP outgoing only types, ext. dev., stateless
     ICMP_T_OX		ICMP outgoing only types, ext. dev.
'
