#!/bin/bash
set -e
#WARNING - add "-e" to the above line before distribution!!!!!!!!!!

cat <<EOTEXT1 >/dev/null
	(The above line allows me to put the documentation right in the
script... Cool, eh?)

>>>>>>>>>>>>>>>If you read nothing else, please read this<<<<<<<<<<<<<<<<

	This program offers an aid to creating firewall rules.  It offers
ABSOLUTELY NO intelligence in deciding what should be allowed or
disallowed.  It has ABSOLUTELY NO ability to understand your security
policy and implement it.  YOU are responsible for reviewing the rules and
massaging them to fit your needs.
	While the documentation in mason.txt attempts to provide some
general guidelines on how to use Mason, please remember:  the author has
no knowledge of what you want your firewall to do and has not tailored the
documentation or program to specially fit your needs.  If there is ever a
discrepancy between your needs and the program output or your needs and
the documentation, the program and/or documentation are _dead_ _wrong_. 


Copyleft:
	Mason interactively creates a Linux packet filtering firewall.
	Copyright (C) 1998, 1999 William Stearns <wstearns@pobox.com>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	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., 675 Mass Ave, Cambridge, MA 02139, USA.

	The author can also be reached at:
        William Stearns
email:  wstearns@pobox.com				(preferred)
web:    http://www.pobox.com/~wstearns
snail:  544 Winchester Place
        Colchester VT, 05446, USA


	This code is entirely owned by William Stearns
(wstearns@pobox.com) and has no relation to any employer or employer
sponsored project.  

------------------------------ Mason ------------------------------
	The Mason script interactively builds a (fire)wall on a Linux
machine. For more details about how this is done, please see mason.txt,
which gives background, theory of operation, a quick start, and additional
documentation on firewalls and firewall gotchas. 
	mason.txt and related documentation should have been installed to
/usr/doc/mason-{version}/ .  If they are missing or you would like to make
sure you have the latest version, please go to
http://www.pobox.com/~wstearns/mason/ . 
	All configuration of this program is done in the /etc/masonrc
file.  This script should probably not be directly edited.

- Bill Stearns

The EOTEXT line is the end of the text and the start of the code. 
EOTEXT1



	MASONDIR=${MASONDIR:-"/var/lib/mason/"}
	MASONCONF=${MASONCONF:-"/etc/masonrc"}
	MASONLIB=${MASONLIB:-"/var/lib/mason/masonlib"}
	if [ -f $MASONLIB ]; then
		. $MASONLIB
	else
		echo Missing $MASONLIB library file.  Please get a complete copy of Mason from >/dev/stderr
		echo http://www.pobox.com/~wstearns/mason/ .  Exiting. >/dev/stderr
		exit
	fi

#-------------------------------------------------------------------------
# Start setting up.
#-------------------------------------------------------------------------

LOGCHAINSEXIST="yes"

if [ "$DOCOMMAND" = "ipchains" ]; then
	ALLCHAINS="`/sbin/ipchains -L -n | grep '^Chain ' | awk '{print $2}'`"
	if [ -n "$NOLOGSUFFIX" ]; then
		for ONECHAIN in $ALLCHAINS ; do
			case $ONECHAIN in
			*${NOLOGSUFFIX})	:								;;
			*)	# For each chain (except for the nolog chains themselves) check that a corresponding nolog chain exists.
				#Do not use -n - it does not work for this.
				if ! /sbin/ipchains -L ${ONECHAIN}${NOLOGSUFFIX} >/dev/null 2>/dev/null ; then  #If nolog chain does not exist
					LOGCHAINSEXIST="no"
					echo The $ONECHAIN chain exists without a corresponding ${ONECHAIN}${NOLOGSUFFIX} >/dev/stderr
					echo chain.  For the moment, the "nolog" feature will revert to sticking the rules >/dev/stderr
					echo at the top of the original chain. >/dev/stderr
				fi												;;
			esac
		done
		if [ "$LOGCHAINSEXIST" = "no" ]; then $NOLOGSUFFIX = "" ; fi
	fi
fi

LAST1="" ; LAST2="" ; LAST3="" ; LAST4="" ; LAST5="" ; CURRENT=""
EXITMASON=${EXITMASON:-"NO"}

trap sigexitscript SIGHUP
trap loadconf SIGUSR1


#---------- Start of Main code ----------
echo "---- Mason firewall builder for Linux                         ----" >/dev/stderr
echo "---- see http://www.pobox.com/~wstearns/mason/ for more info. ----" >/dev/stderr
echo "---- William Stearns <wstearns@pobox.com>                     ----" >/dev/stderr 
loadconf
SIGGED="NO"

#NAMECACHE Support has been disabled
##Hmmm... I _hate_ overwriting /etc/passwd...
#if [ -L $NAMECACHE ]; then
#	rm -f $NAMECACHE
#fi
#
##Create the name cache file if it doesn't exist
#if [ ! -e $NAMECACHE ]; then
#	touch $NAMECACHE
#	#This is a security-related program.  I don't want to let people know who we're even talking to.
#	chmod og-rwx $NAMECACHE
#fi

for ONEIF in $DYNIF ; do
	cat <<EOTEXT
$CMNT ${CMNT}export ${ONEIF}ADDR="0/0"		${CMNT}Use this version if you want to match any IP address.
export ${ONEIF}ADDR="\`ifconfig $ONEIF | grep 'inet addr' | awk '{print \$2}' | sed -e 's/.*://'\`${SINGLEMACHSPEC}"
EOTEXT
	eval ${ONEIF}ADDR=`ifconfig $ONEIF | grep 'inet addr' | awk '{print $2}' | sed -e 's/.*://'`
done

#ipchains-save is under development; use at your own risk. ;-)
if [ "$ECHOCOMMAND" = "ipchains-save" ]; then
	for ONECHAIN in input forward output ; do
		echo ':'$ONECHAIN $DEFAULTPOLICY
	done
	if [ -n "$NOLOGSUFFIX" ]; then
		for ONECHAIN in input forward output ; do
			echo ':'${ONECHAIN}${NOLOGSUFFIX} '-'
		done
	fi
fi

#Get the first log entry
unset ACK COMMENT CURRCHAIN DEST DESTHOST DESTIP DESTPORT DIR DIRLETTER \
DOACK ECHOACK IF IGNOREPORT ISLOGLINE \
J1 J2 J3 J4 J5 J6 J7 J8 J9 J10 J11 J12 J13 J14 J15 J16 J17 J18 J19 \
                  O7 O8 O9 O10 O11 O12 O13 \
                  A7 A8 A9 A10 A11 A12 A13 \
LINEHASDYNAMIC PROTO SRC SRCHOST SRCIP SRCPORT TAIL TOS || /bin/true
#unset DFFLAG FFLAG FOFLAG IFLAG LFLAG MESSPOL SFLAG TFLAG || /bin/true			#These fields are not used at this time.
TOS=""
read J1 J2 J3 J4 J5 J6 J7 J8 J9 J10 J11 J12 J13 J14 J15 J16 J17 J18 J19

while [ "$SIGGED" = "YES" ]; do
	SIGGED="NO"
	read J1 J2 J3 J4 J5 J6 J7 J8 J9 J10 J11 J12 J13 J14 J15 J16 J17 J18 J19
done

while [ "$EXITMASON" != "YES" ] && [ -n "$J1" ]; do
#Only do the work if the line is a firewalling log entry.
	if [ "$J6 $J7" = "Packet log:" ]; then		#Load variables from ipchains log entry
		INFORMAT="ipchains-log"
		case $J8 in
		output)		DIR='output ' ;	DIRLETTER='O'				;;
		input)		DIR='input  ' ;	DIRLETTER='I'				;;
		forward)	DIR='forward' ;	DIRLETTER='F'				;;
		*)	echo Unknown direction X${J8}X >/dev/stderr			;;
		esac
		IF="$J10"
		protonum2name "${J11##PROTO=}"
		SRC="$J12" ; DEST="$J13"
		SRCPORT=${SRC##*:}
		DESTPORT=${DEST##*:}
		SRCIP=${SRC%%:*}
		DESTIP=${DEST%%:*}
		#MESSPOL="$J9" ; LFLAG="$J14" ; SFLAG="$J15" ; IFLAG="$J16" ; FFLAG="$J17" ; TFLAG="$J18"		#Unused
		ISLOGLINE="YES"
	elif [ "$J5 $J6" = "kernel: IP" ]; then	#Load variables from ipfwadm log entry
		INFORMAT="ipfwadm-log"
		case $J7 in				#Formerly [ "`echo $J7 | cut -b 1-3`" = "fw-" ]
		fw-out|fw-in|fw-fwd)
			case $J7 in
			fw-out)	DIR='output ' ;	DIRLETTER='O'					;;
			fw-in)	DIR='input  ' ;	DIRLETTER='I'					;;
			fw-fwd)	DIR='forward' ;	DIRLETTER='F'					;;
			*)	echo Unknown direction X${J7}X >/dev/stderr			;;
			esac
			IF="$J9"
			PROTO=`echo $J10 | tr A-Z a-z`
			SRC="$J11" ; DEST="$J12"
			SRCIP=${SRC%%:*}
			DESTIP=${DEST%%:*}
			#MESSPOL="$J8" ; LFLAG="$J13" ; SFLAG="$J14" ; IFLAG="$J15" ; FOFLAG="$J16" ; TFLAG="$J17" ; DFFLAG="$J18"		#Unused
#Break up ipfwadmin's habit of gluing the icmp port onto the protocol.
			if [ "${PROTO%%/*}" = "icmp" ]; then
				SRCPORT=${PROTO##*/}
				PROTO="icmp"
				DESTPORT=""
				if [ "$DEBUG" = "YES" ]; then echo proto= $PROTO srcport= $SRCPORT destport= $DESTPORT >/dev/stderr ; fi
			else
				SRCPORT=${SRC##*:}
				DESTPORT=${DEST##*:}
			fi
			ISLOGLINE="YES"
																;;
		esac
	elif [ "$J1" = "Chain" ]; then	#Load variables from ipchains -Lv listing; this is a header line.
		CURRCHAIN=$J2
		INFORMAT="ipchains-lv"
	elif [ "$J6" = "0xFF" ] || [ "$J6" = "0x01" ]; then	#Load variables from ipchains -Lv listing; this is a data line.
#ZZZZ
		case $J7 in
		0x*)
			INFORMAT="ipchains-lv"
			DIR=$CURRCHAIN
			case $CURRCHAIN in
			o*|O*)	DIRLETTER='O'									;;
			i*|I*)	DIRLETTER='I'									;;
			f*|F*)	DIRLETTER='F'									;;
			*)	echo Unknown direction X${CURRCHAIN}X >/dev/stderr	;;
			esac
#FIXME - double check these, check other flag fields.
			case $J5 in
			-y*)	ACK="! -k"										;;
			!y*)	ACK="-k"										;;
			--*)	ACK="  "										;;
			esac
			IF=$J8
			PROTO=$J4
			if [ "$J6 $J7" != "0xFF 0x00" ]; then TOS=" -t $J6 $J7" ; else TOS="" ; fi
#FIXME - handle case of existing or missing mark and outsize fields
			#SRC="$J11" ; DEST="$J12"	#SRC and DEST are only temporary variables for ipfwadm and ipchains formats to split into SRCIP and SRCPORT in a mo.
			SRCIP=$J9
			DESTIP=$J10

			#MESSPOL="$J8" ; LFLAG="$J13" ; SFLAG="$J14" ; IFLAG="$J15" ; FOFLAG="$J16" ; TFLAG="$J17" ; DFFLAG="$J18"		#Unused

			case $J11 in
			*:*)
				if [ "${J11%%:*}" = "${J11##*:}" ]; then
					SRCPORT=${J11%%:*}
				else
					SRCPORT=$J11
				fi													;;
			*)	SRCPORT=$J11										;;
			esac
			case $J13 in
			*:*)
				if [ "${J13%%:*}" = "${J13##*:}" ]; then
					DESTPORT=${J13%%:*}
				else
					DESTPORT=$J13
				fi													;;
			*)	DESTPORT=$J13										;;
			esac
			ISLOGLINE="YES"
																;;
		esac
		
	fi


	if [ "$ISLOGLINE" = "YES" ]; then
#Handle AUTOMASQ feature.  Masquerade packets forwarded to requested interfaces if source 
#address is a reserved (rfc1918) address.
		if [ "$DIRLETTER" = "F" ] && [ -n "$AUTOMASQIF" ] && [ "$LCPOLICY" = "accept" ] && reservedip $SRCIP ; then
			for ANIF in $AUTOMASQIF ; do
				if [ "$ANIF" = "$IF" ]; then
					UCPOLICY="MASQ  "			#For ipchains 
					LCPOLICY="accept -m"		#For ipfwadm, sorry, it won't line up for the moment.
				fi
			done
		fi #If forwarding and automasq requested.

#Quickly put in a completely specific rule for this packet to stop the flow - it will be deleted later
		#in masonlib: LCPOLICY, UCPOLICY
		#from above: IF, DIR, DIRLETTER, PROTO, SRCIP, SRCPORT, DESTIP, DESTPORT
		#not set? ACK, SRCHOST, DESTHOST, TOS, DOACK
		UNDOSPECIFICRULE=""
		if [ "$DOCOMMAND" = "ipfwadm" ]; then 
			/sbin/ipfwadm -i $LCPOLICY -W $IF -${DIRLETTER} -P $PROTO -S $SRCIP $SRCPORT -D $DESTIP $DESTPORT
			UNDOSPECIFICRULE="/sbin/ipfwadm -d $LCPOLICY -W $IF -${DIRLETTER} -P $PROTO -S $SRCIP $SRCPORT -D $DESTIP $DESTPORT"
		elif [ "$DOCOMMAND" = "ipchains" ]; then
			case $DIRLETTER in
			I)		DODIR="input${NOLOGSUFFIX}"					;;
			O)		DODIR="output${NOLOGSUFFIX}"				;;
			F)		DODIR="forward${NOLOGSUFFIX}"				;;
			esac
			/sbin/ipchains -I $DODIR 1 -i $IF -p $PROTO -s $SRCIP $SRCPORT -d $DESTIP ${DESTPORT} -j $UCPOLICY
			UNDOSPECIFICRULE="/sbin/ipchains -D $DODIR -i $IF -p $PROTO -s $SRCIP $SRCPORT -d $DESTIP ${DESTPORT} -j $UCPOLICY"
			unset DODIR || /bin/true
		fi # no need to handle DOCOMMAND=none :-)

		if [ "$INFORMAT" != "ipchains-lv" ]; then ACK="  " ; fi

		case ${#IF} in
		0)	IF="${IF}     "		;;
		1)	IF="${IF}    "		;;
		2)	IF="${IF}   "		;;
		3)	IF="${IF}  "		;;
		4)	IF="${IF} "			;;
		esac
		#Used to be: if [ ${#IF} -lt 5 ]; then IF=`echo "$IF     " | cut -b 1-4` ; fi

#Set dynifs here.
#Put the dynamic IP addresses in the current environment.
#FIXME - do we need to do this every time?
		for ONEIF in $DYNIF ; do
			eval ${ONEIF}ADDR=$(eval echo \${$(eval echo ${ONEIF}ADDR)})	#No, really.  That's the IP address.
		done

		if [ "$IGNOREPORT" = "YES" ]; then		#For non tcp/udp/icmp protocols (w/o port numbers), restrict to machine to machine
			HOLDIPCONV="$IPCONV"	#Convert to hostname, but honor Dynamic IP macros.  Temporarily set IPCONV to HOST to do this.
			IPCONV="HOST"
			SRCHOST=`generalizeip $SRCIP`
			DESTHOST=`generalizeip $DESTIP`
			IPCONV="$HOLDIPCONV"
		else
#If this is a DNS request and the server is one of those listed in /etc/resolv.conf, don't generalize the ip address.
#Also, don't generalize if this is one of the Sparse Server or Sparse Client protocols.
			ISDNSSERVER="NO"
			if [ "$SRCPORT" = "53" ] && ( [ "$PROTO" = "udp" ] || [ "$PROTO" = "tcp" ] ) ; then
				for ONEDNSSERVER in $DNSSERVERS ; do
					if [ "$SRCIP" = "$ONEDNSSERVER" ]; then ISDNSSERVER="YES" ; SRCHOST="${SRCIP}${SINGLEMACHSPEC}" ; fi
				done
			fi
			if [ "$ISDNSSERVER" = "NO" ]; then
				SRCHOST=`generalizeip $SRCIP`
				for ONESPARSE in $SSP ; do
					if [ "${SRCPORT}/$PROTO" = "$ONESPARSE" ]; then		SRCHOST="`nameof ${SRCIP}`${SINGLEMACHSPEC}" ; fi
				done
				for ONESPARSE in $SCP ; do
					if [ "${DESTPORT}/$PROTO" = "$ONESPARSE" ]; then	SRCHOST="`nameof ${SRCIP}`${SINGLEMACHSPEC}" ; fi
				done
			fi

			ISDNSSERVER="NO"
			if [ "$DESTPORT" = "53" ] && ( [ "$PROTO" = "udp" ] || [ "$PROTO" = "tcp" ] ) ; then
				for ONEDNSSERVER in $DNSSERVERS ; do
					if [ "$DESTIP" = "$ONEDNSSERVER" ]; then ISDNSSERVER="YES" ; DESTHOST="${DESTIP}${SINGLEMACHSPEC}" ; fi
				done
			fi
			if [ "$ISDNSSERVER" = "NO" ]; then
				DESTHOST=`generalizeip $DESTIP`
				for ONESPARSE in $SSP ; do
					if [ "${DESTPORT}/$PROTO" = "$ONESPARSE" ]; then	DESTHOST="`nameof ${DESTIP}`${SINGLEMACHSPEC}" ; fi
				done
				for ONESPARSE in $SCP ; do
					if [ "${SRCPORT}/$PROTO" = "$ONESPARSE" ]; then		DESTHOST="`nameof ${DESTIP}`${SINGLEMACHSPEC}" ; fi
				done
			fi
		fi


#Clean up protocol type and number fields, visualize source and dest port fields, set ack flag.
#If port not in /etc/services and >=1024, generalize to "high port"
		COMMENT2="" ;	COMMENT="${CMNT}" ; CISCOSERVICE=""
		if [ "$PROTO" = "tcp" ] || [ "$PROTO" = "udp" ]; then
			serverportrange $SRCPORT $PROTO
			SRCSERVICE="$READABLEPORT"
			SRCCOMMENT="$PARTIALCOMMENT"

			serverportrange $DESTPORT $PROTO
			DESTSERVICE="$READABLEPORT"
			DESTCOMMENT="$PARTIALCOMMENT"

			if [ -n "$SRCSERVICE" ] && [ "$PROTO" = "tcp" ] && [ "$INFORMAT" != "ipchains-lv" ]; then
				#The ack flag should be set if port=tcp and source port is a server service.
				#The one tcpdump I've seen of an ftp connection seems to indicate that the 
				#ftp-data connection is from the _server_ to the client - backwards.
				#Passive mode FTP is supposed to reverse this.  Ugh.  And again, I say, Ugh.
#FIXME - use the SYN / PENANCE flag Rusty's putting into later 2.2's.  Thanks, Rusty!
				if [ "${SRCPORT}/$PROTO" != "20/tcp" ]; then ACK="-k" ; fi
			fi
			if [ -n "$SRCSERVICE" ] && [ -n "$DESTSERVICE" ]; then	# Both source and destination ports are servers.  Rare, but possible.
				SRCPORT=$SRCSERVICE
				DESTPORT=$DESTSERVICE
				if [ "$INFORMAT" != "ipchains-lv" ]; then ACK="  " ; fi	#In this case, we can't tell in which direction the connection was made - no ack flag.
				#We leave TOS alone here - there's no way to tell on which to base it.
				#What the heck do we do with Cisco in this case?
			elif [ -n "$SRCSERVICE" ]; then								# Source port is a server port.
				DESTPORT=`clientportrange $DESTPORT $SRCPORT ${PROTO}`
				settos $SRCPORT $SRCSERVICE $PROTO
				SRCPORT=$SRCSERVICE	; CISCOSERVICE=" eq $SRCPORT"
			elif [ -n "$DESTSERVICE" ]; then							# Dest port is a server port.
				SRCPORT=`clientportrange $SRCPORT $DESTPORT $PROTO`
				settos $DESTPORT $DESTSERVICE $PROTO
				DESTPORT=$DESTSERVICE ; CISCOSERVICE=" eq $DESTPORT"
			else														# Neither source nor dest is a server port.
				COMMENT2="${CMNT}${CMNT} S=`nameof ${SRCIP}`:${SRCPORT} D=`nameof ${DESTIP}`:${DESTPORT}"
				ORIGSRCPORT=$SRCPORT
				SRCPORT=`clientportrange $SRCPORT $DESTPORT $PROTO`
				DESTPORT=`clientportrange $DESTPORT $ORIGSRCPORT $PROTO`
				unset ORIGSRCPORT
				#if [ "$INFORMAT" != "ipchains-lv" ]; then TOS=" -t 0x01 0x08" ; fi		#Maximize throughput on the assumption that this is FTP data or irc dcc?

				#If we have a high port to high port connection (darn ftp and irc dcc), do _not_ generalize to anywhere
				if [ "$SRCPORT" = "1024:65535" ] && [ "$DESTPORT" = "1024:65535" ]; then
					if [ "$SRCHOST" = "0/0" ]; then		SRCHOST="${SRCIP}${SINGLEMACHSPEC}" ;		fi
					if [ "$DESTHOST" = "0/0" ]; then	DESTHOST="${DESTIP}${SINGLEMACHSPEC}" ;	fi
				fi
			fi
			if [ -n "$SRCCOMMENT" ]; then COMMENT="$COMMENT $SRCCOMMENT" ; fi
			if [ -n "$DESTCOMMENT" ]; then
				if [ "$SRCCOMMENT" != "$DESTCOMMENT" ]; then COMMENT="$COMMENT $DESTCOMMENT" ; fi
			fi
			COMMENT="$COMMENT (${DIRLETTER})"
		elif [ "$PROTO" = "icmp" ]; then	#Handle ICMP comments
#FIXME - handle subcode (in comment2) if dest != 0
			case $SRCPORT in
			0)		COMMENT="$CMNT Echo reply/icmp (${DIRLETTER})"				;;
			3)		COMMENT="$CMNT Dest Unreach/icmp (${DIRLETTER})"			;;
			4)		COMMENT="$CMNT Source Quench/icmp (${DIRLETTER})"			;;
			5)		COMMENT="$CMNT Redirect/icmp (${DIRLETTER})"				;;
			8)		COMMENT="$CMNT Echo req/icmp (${DIRLETTER})"				;;
			11)		COMMENT="$CMNT Time exceeded/icmp (${DIRLETTER})"			;;
			12)		COMMENT="$CMNT Parameter prob/icmp (${DIRLETTER})"			;;
			13)		COMMENT="$CMNT Timestamp req/icmp (${DIRLETTER})"			;;
			14)		COMMENT="$CMNT Timestamp reply/icmp (${DIRLETTER})"			;;
			15)		COMMENT="$CMNT Info req/icmp (${DIRLETTER})"				;;
			16)		COMMENT="$CMNT Info reply/icmp (${DIRLETTER})"				;;
			17)		COMMENT="$CMNT Addr Mask req/icmp (${DIRLETTER})"			;;
			18)		COMMENT="$CMNT Addr Mask reply/icmp (${DIRLETTER})"			;;
#FIXME - include source and dest IPs for the following?
			*)		COMMENT="$CMNT unknown-${SRCPORT}/icmp (${DIRLETTER})"		;;
			esac
			CISCOSERVICE=" eq $SRCPORT"
		else	#Handle non tcp/udp/icmp protocols
			COMMENT="$CMNT $PROTO (${DIRLETTER})"
			COMMENT2="${CMNT}${CMNT} S=`nameof $SRCIP` D=`nameof $DESTIP`"
		fi
		#if [ "$LINEHASDYNAMIC" = "YES" ]; then COMMENT="$COMMENT DynamicIP" ; fi
		#LINEHASDYNAMIC is not exported because generalizeip is a function.  Not used.

		if [ "$DEBUG" = "YES" ]; then
			echo J1=$J1 J2=$J2 J3=$J3 J4=$J4 J5=$J5 J6=$J6 J7=$J7 J8=$J8 >/dev/stderr 
			echo J9=$J9 J10=$J10 J11=$J11 J12=$J12 J13=$J13 J14=$J14 J15=$J15 J16=$J16 >/dev/stderr
			echo J17=$J17 J18=$J18 J19=$J19 DIR=$DIR DIRLETTER=$DIRLETTER >/dev/stderr
			echo MESSPOL=$MESSPOL IF=$IF PROTO=$PROTO SRC=$SRC DEST=$DEST LFLAG=$LFLAG >/dev/stderr
			echo SFLAG=$SFLAG IFLAG=$IFLAG FOFLAG=$FOFLAG FFLAG=$FFLAG TFLAG=$TFLAG DFFLAG=$DFFLAG TAIL=$TAIL >/dev/stderr
			echo Unused: DFFLAG, FFLAG, FOFLAG, IFLAG, LFLAG, MESSPOL, SFLAG, TFLAG >/dev/stderr
		fi


#Actually create and implement the firewall command to display.  Pad so rules line up.
		case $PROTO in
		ip|IP)																		PROTO="$PROTO  "	;;
		tcp|TCP|udp|UDP|ggp|GGP|pup|PUP|idp|IDP|raw|RAW|egp|EGP|gre|GRE|pim|PIM)	PROTO="$PROTO "		;;
		esac


		DODISPLAY="YES"
		if [ "$IGNOREPORT" = "YES" ]; then SRCPORT="" ; DESTPORT="" ; fi
		case "$ECHOCOMMAND" in
		ipchains)
			case "$ACK" in
			"  ")	ECHOACK="    "									;;
			"-k")	ECHOACK="! -y"									;;
			"! -k")	ECHOACK="-y  "									;;
			esac
#Handle 0/0 host spec or missing port spec - thanks, Rusty!
			case $SRCHOST in
			*/0|*/0.0.0.0)	if [ -n "$SRCPORT" ]; then SRCSPEC="--sport $SRCPORT " ;     else SRCSPEC="" ; fi					;;
			*)				if [ -n "$SRCPORT" ]; then SRCSPEC="-s $SRCHOST $SRCPORT " ; else SRCSPEC="-s $SRCHOST " ; fi		;;
			esac
			case $DESTHOST in
			*/0|*/0.0.0.0)	if [ -n "$DESTPORT" ]; then DESTSPEC="--dport $DESTPORT" ;      else DESTSPEC="" ; fi				;;
			*)				if [ -n "$DESTPORT" ]; then DESTSPEC="-d $DESTHOST $DESTPORT" ; else DESTSPEC="-d $DESTHOST" ; fi	;;	
			esac
			CURRENT="/sbin/ipchains -A $DIR -i $IF -p $PROTO $ECHOACK ${SRCSPEC}${DESTSPEC}${TOS} -j $UCPOLICY" #TOS is either blank or has a leading space
																				;;
		ipchains-save)		#Currently experimental
			case "$ACK" in
			"  ")	ECHOACK="    "									;;
			"-k")	ECHOACK="! -y"									;;
			"! -k")	ECHOACK="-y  "									;;
			esac
			case $SRCHOST in
			*/0|*/0.0.0.0)	SRCSPEC="-s 0.0.0.0/0.0.0.0"											;;
			*)				SRCSPEC="-s ${SRCHOST%%/*}/`bits2mask ${SRCHOST##*/}`"					;;
			esac
			case "$SRCPORT" in		#serverportrange should have returned a numeric port for ipchains-save
			"")				SRCSPEC="$SRCSPEC 0:65535"												;;
			*:*)			SRCSPEC="$SRCSPEC $SRCPORT"												;;
			*)				SRCSPEC="$SRCSPEC $SRCPORT:$SRCPORT"									;;
			esac
			case $DESTHOST in
			*/0|*/0.0.0.0)	DESTSPEC="-s 0.0.0.0/0.0.0.0"											;;
			*)				DESTSPEC="-s ${DESTHOST%%/*}/`bits2mask ${DESTHOST##*/}`"				;;
			esac
			case "$DESTPORT" in
			"")				DESTSPEC="$DESTSPEC 0:65535"											;;
			*:*)			DESTSPEC="$DESTSPEC $DESTPORT"											;;
			*)				DESTSPEC="$DESTSPEC $DESTPORT:$DESTPORT"								;;
			esac
			PROTONUMBER=`cat /etc/protocols | grep -i "^${PROTO}[[:space:]]" | awk '{print $2}'`
			CURRENT="-A $DIR $SRCSPEC $DESTSPEC -i $IF -p ${PROTONUMBER:-${PROTO}} $TOS -j $UCPOLICY $ECHOACK" #TOS is either blank or has a leading space
																				;;
		ipfwadm)
			CURRENT="/sbin/ipfwadm -a $LCPOLICY -W $IF -${DIRLETTER} -P $PROTO $ACK -S $SRCHOST $SRCPORT -D $DESTHOST ${DESTPORT}${TOS}"
																				;;
		cisco)
#FIXME handle comments to user for $IF, $DIRLETTER, fix "eq", TOS format? no forwarding rules (maybe in baserules?)
#FIXME - is this screwing up the DOCOMMAND=ipchains parameters?
			if [ "$DIRLETTER" = "F" ]; then
				DODISPLAY="NO" ; echo "Forwarding rule skipped in cisco mode" >/dev/stderr
			elif [ "$IF" = "lo" ]; then
				DODISPLAY="NO" ; echo "Loopback interface skipped in cisco mode" >/dev/stderr
			else
				case "$LCPOLICY" in
				"reject"|"deny  "|"deny")						CISCOPOLICY="deny  "	;;
				"accept"|"accept -m")							CISCOPOLICY="permit"	;;	#Cisco Masq? sorry, do not know how.
				*)												CISCOPOLICY="unknown"	;;
				esac
				case "$ACK" in
				"  ")	ECHOACK="           "							;;
				"-k")	ECHOACK="established"							;;
				"! -k")	ECHOACK="           "							;;
				esac
				if [ -n "$TOS" ]; then CISCOTOS=" tos${TOS}" ; else CISCOTOS="" ; fi
				case $IF in
				eth0)		CISCOIF="E0"	;;
				eth1)		CISCOIF="E1"	;;
				eth2)		CISCOIF="E2"	;;
				eth3)		CISCOIF="E3"	;;
				ppp0)		CISCOIF="S0"	;;
				ppp1)		CISCOIF="S1"	;;
				ppp2)		CISCOIF="S2"	;;
				ppp3)		CISCOIF="S3"	;;
				tr0)		CISCOIF="To0"	;;
				tr1)		CISCOIF="To1"	;;
				tr2)		CISCOIF="To2"	;;
				tr3)		CISCOIF="To3"	;;
				*)			CISCOIF="`echo "$IF" | sed -e 's/^eth/E/' -e 's/^ppp/S/' -e 's/^tr/To/'`"	;;
				esac
				CURRENT="access-list ${DIRLETTER}${CISCOIF} $CISCOPOLICY $PROTO $SRCHOST ${DESTHOST}${CISCOSERVICE}${CISCOTOS} $ECHOACK $LOG"
			fi
																				;;
		esac # No need to handle ECHOCOMMAND=none :-)

		if [ "$ECHOCOMMAND" != "ipchains-save" ]; then		#ipchains-save doesn't need padding or comments
			if [ `echo -n "$CURRENT" | wc -c` -lt 115 ]; then	#We cannot use ${#CURRENT} because it counts multiple spaces as one.
				CURRENT=`echo "$CURRENT                                                   " | cut -b 1-120`	#orig 110 chopped lines, 111 did not pad enough
			fi
		fi

		if [ "$DEBUG" = "YES" ]; then echo current= "$CURRENT" >/dev/stderr ; fi

		if [ -n "$UNDOSPECIFICRULE" ]; then eval "$UNDOSPECIFICRULE" ; fi

#Don't do anything if this is the same as one of the last 5 rules.  This 
#reduces the occurence of repeated rules showing up.
		case $CURRENT in
		$LAST1|$LAST2|$LAST3|$LAST4|$LAST5)
			if [ "$HEARTBEAT" = "YES" ]; then echo -n "-" >/dev/stderr ; NEEDLF="YES" ; fi
																;;
		*)
			if [ "$NEEDLF" = "YES" ]; then echo >/dev/stderr ; NEEDLF="NO" ; fi
			if [ "$DOBEEP" = "YES" ]; then echo -n -e "\a" >/dev/stderr ; fi
			if [ "$DODISPLAY" = "YES" ]; then
				case $ECHOCOMMAND in
				ipchains|ipfwadm|cisco)	echo "$CURRENT $COMMENT $COMMENT2"		;;
				ipchains-save)			echo $CURRENT							;;
				esac
			fi


#Put a real rule in the rule chain so that we don't log it again.  We need to use eval since ${xxxHOST} may be a $DYNIP 
#and may need to be evaluated to its real value.
			if [ "$DOCOMMAND" = "ipfwadm" ]; then 
				eval "/sbin/ipfwadm -i $LCPOLICY -W $IF -${DIRLETTER} -P $PROTO $ACK -S $SRCHOST $SRCPORT -D $DESTHOST ${DESTPORT}${TOS}"
			elif [ "$DOCOMMAND" = "ipchains" ]; then
				case "$ACK" in
				"  ")	DOACK="    "									;;
				"-k")	DOACK="! -y"									;;
				"! -k")	DOACK="-y  "									;;
				esac
				case $DIRLETTER in
				I)		DIR="input${NOLOGSUFFIX}"				;;
				O)		DIR="output${NOLOGSUFFIX}"				;;
				F)		DIR="forward${NOLOGSUFFIX}"				;;
				esac
				#echo X${DIR}X >/dev/stderr
				eval "/sbin/ipchains -I $DIR 1 -i $IF -p $PROTO $DOACK -s $SRCHOST $SRCPORT -d $DESTHOST ${DESTPORT}${TOS} -j $UCPOLICY"
			fi # no need to handle DOCOMMAND=none :-)
			LAST5=$LAST4
			LAST4=$LAST3
			LAST3=$LAST2
			LAST2=$LAST1
			LAST1=$CURRENT										;;
		esac

		if [ "$DEBUG" = "YES" ]; then  echo src= $SRCIP $SRCPORT dest= $DESTIP $DESTPORT if= $IF proto= $PROTO >/dev/stderr ; fi

		if [ "$LCPOLICY" = "accept -m" ]; then	#Reset policy to non-masq for the next rule.
			UCPOLICY="ACCEPT" ; LCPOLICY="accept"
		fi
	fi #if ISLOGLINE

#Get the next log entry and start over.
	unset ACK COMMENT DEST DESTHOST DESTIP DESTPORT DIR DIRLETTER DOACK ECHOACK IF IGNOREPORT ISLOGLINE \
	LINEHASDYNAMIC PROTO SRC SRCHOST SRCIP SRCPORT TAIL TOS || /bin/true
	#unset DFFLAG FFLAG FOFLAG IFLAG LFLAG MESSPOL SFLAG TFLAG || /bin/true		#These are unused.
	TOS=""
	A7=$O7 ; A8=$O8 ; A9=$O9 ; A10=$O10 ; A11=$O11 ; A12=$O12 ; A13=$O13
	O7=$J7 ; O8=$J8 ; O9=$J9 ; O10=$J10 ; O11=$J11 ; O12=$J12 ; O13=$J13
	read J1 J2 J3 J4 J5 J6 J7 J8 J9 J10 J11 J12 J13 J14 J15 J16 J17 J18 J19
#Keep reading until a line with different firewall values is found.
	while { [ "$O7"  = "$J7"  ] && [ "$O8"  = "$J8"  ] && [ "$O9"  = "$J9"  ] && [ "$O10" = "$J10" ] && \
	        [ "$O11" = "$J11" ] && [ "$O12" = "$J12" ] && [ "$O13" = "$J13" ] ; } ||
		  { [ "$A7"  = "$J7"  ] && [ "$A8"  = "$J8"  ] && [ "$A9"  = "$J9"  ] && [ "$A10" = "$J10" ] && \
	        [ "$A11" = "$J11" ] && [ "$A12" = "$J12" ] && [ "$A13" = "$J13" ] ; } ||
		  { [ "$SIGGED" = "YES" ] ; } ; do 
		if [ "$SIGGED" = "YES" ]; then
			SIGGED="NO"
		else
			if [ "$HEARTBEAT" = "YES" ]; then echo -n "." >/dev/stderr ; NEEDLF="YES" ; fi
			A7=$O7 ; A8=$O8 ; A9=$O9 ; A10=$O10 ; A11=$O11 ; A12=$O12 ; A13=$O13
			O7=$J7 ; O8=$J8 ; O9=$J9 ; O10=$J10 ; O11=$J11 ; O12=$J12 ; O13=$J13
		fi
		read J1 J2 J3 J4 J5 J6 J7 J8 J9 J10 J11 J12 J13 J14 J15 J16 J17 J18 J19
	done
done

if [ "$NEEDLF" = "YES" ]; then echo >/dev/stderr ; NEEDLF="NO" ; fi
if [ "$EXITMASON" = "YES" ]; then
	echo Mason is exiting because of a SIGHUP or EXITMASON=YES. >/dev/stderr
else
	echo Mason is exiting because of an end of input data. >/dev/stderr
fi


	#unset J1 J2 J3 J4 J5 J6 J7 J8 J9 J10 J11 J12 J13 J14 J15 J16 J17 J18 J19 \
#	                  O7 O8 O9 O10 O11 O12 O13 \
#	                  A7 A8 A9 A10 A11 A12 A13 || /bin/true
