#!/bin/bash
cat <<EOTEXT1 >/dev/null
	(The above line allows me to put the documentation right in the
script... Cool, eh?  Yes, I know I should break it up....)

>>>>>>>>>>>>>>>If you read nothing else, 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.
	As the GNU GPL states, 
    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.

	Mason builds a wall ... what else?
	Copyright 1998 William Stearns

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

	The impatient can find a "Quick Start" section below.


Background and motivation:
	The built-in firewall features of the Linux kernel offer a
powerful set of packet filtering features that can be used to build a
firewall.  The various pieces of available documentation provide an
introduction on how to configure the firewall for simple setups, but can't
possibly explain how to configure a firewall for more complex setups,
including fine-grained allow and deny lists.  This is especially obvious
when trying to create a firewall with a default policy of deny. 
	Someone looking to configure a linux firewall is simultaneously
hit with the complexity of trying to understand the ipfwadm syntax, trying
to understand the structure of TCP/IP connections, and trying to create
and implement a security policy.  No wonder firewalls are daunting!

	The mason application you're looking at attempts to handle the
first two problems by dynamically creating the firewall based on the
traffic flowing through it.  For example, if you start up a telnet session
through your firewall from a machine on your LAN to a machine out on the
WAN while mason is running, mason will create all the rules necessary to
allow this traffic.
	Conversely, if you're looking to block incoming NFS requests,
simply launch mason, make the NFS connection, and change the output rules
to deny rules.  When the firewall is restarted, voila!  No more incoming
NFS. 
	Creating a firewall no longer requires understanding the ipfwadm
syntax.  Even novices can create a firewall under Linux.  _HOWEVER_,
creating a _good_ firewall _still_ requires some understanding of TCP/IP
protocols and packet filtering.  Many good books cover this.  Check out
O'Reilly and Associates (www.ora.com or www.oreilly.com) for some
excellent general coverage of firewall designs. 


Basic theory of operation:
	Before starting, if the user has some rules that he or she knows
should be used in this machine, the user can implement them.  The last
rules are the ones that log all other packets to /var/log/messages.  The
"tail" command is used to feed the log messages in this script.  The
script converts each log entry into the corresponding command necessary to
allow that kind of traffic.  In the previous telnet example, 6 different
firewall rules would be created on the firewall, three for the original
request packet, 3 for the response back from the server:

pkt 1:	Allow telnet request in from LAN
pkt 1:	Forward request through system
pkt 1:	Allow request to exit to WAN
pkt 2:	Allow telnet response back into machine from WAN
pkt 2:	Forward response through system
pkt 2:	Allow response to exit back to the original machine on the LAN.

	All packets from 3 on are handled by these rules.  There may be a
short delay in the initial connection.
	The script creates the actual ipfwadm commands to accomodate the
packet flow.  When the command is executed the new rule is inserted at the
head of the existing rules so that future packets of this type no longer
reach the logging rule at the bottom. 
	The rules are also echoed to the console so that you can see the
rules as they are executed or redirect them to a file.  I prefer the
latter; this allows me to go back later to review the rules and massage
them. 


Special considerations - PLEASE read:
Kernel:
	IP firewalling and firewall packet logging have to be compiled
into the kernel.  To see if IP firewalling is compiled into your kernel,
type the command: 
ipfwadm -F -l -e<Enter>		(while you're root)
	If you get "command not found", you're either not root or you
haven't installed the ipfwadm package.  See if it's included in your
distribution.  It's definitely included in RedHat 4.1 and higher.  ipfwadm
2.3.0 has been around for a long time and is stable. 
	If you get something like "/proc/net/ip_forward not found", your
kernel does not support ipfwadm firewalling.  One of the following three
is true: 

- Your kernel is too old.  It appears that linux firewalling switched from
the old "ipfw" firewalling in 1.3.66, but some features require 2.0.0.

- Your kernel is too new.  Somewhere between 2.1.101 and 2.1.104 the
kernel switched over from "ipfwadm" firewalling to "ipchains" firewalling. 
At some point I'll add a command line option to choose ipfwadm or
ipchains. 

- You have the right version of the kernel, but firewalling is not
enabled.  You must recompile the kernel and turn on firewalling.  See the
HOWTO's at http://sunsite.unc.edu/linux/HOWTO to see how this is done.  In
particular, see the masquerading and kernel HOWTO's. 

	To see if firewall packet logging is enabled in your kernel, type
the following command: 
ipfwadm -a deny -F -S 127.12.2.3/32 -D 127.12.2.4/32 -o<Enter>
	The "-o" at the end tells the kernel to log this particular packet
type (one which should never show up).  If your kernel does not support
logging, I _think_ you would get an error.  On the other hand, I've never
had a kernel that has firewalling but does not have logging. 


DNS:
	If the firewall or one of the machines behind it is a DNS server,
you have a situation where mason issues a steady flow of DNS requests to
resolve the machine names and each DNS request requires a new rule, which
in turn requires more DNS requests... ugh.
	There are a couple of ways around this:

- Write the correct rules for DNS lookup for your particular setup.

- Use trusted external DNS servers, ie servers that do not have to resolve
through your firewall.  This reduces the number of rules necessary to
handle DNS to just a few.

- Put the names of all relevant hosts in /etc/hosts.  Mason uses this file
before going out to DNS to try to minimize the snowball effect when it
occurs. 

- Comment out the DNS lookup code in Mason.  At some point this will be 
command line configurable, but not quite yet.

- Insert general rules for DNS in your rule set.  If you're not sure what
these should be, the following six rules should allow all DNS requests
from anywhere to anywhere.  Please note that depending on your security
policy, this may be too general and should be reviewed. 

	ipfwadm -I -i accept -S 0/0 53 -P udp
	ipfwadm -I -i accept -D 0/0 53 -P udp
	ipfwadm -O -i accept -S 0/0 53 -P udp
	ipfwadm -O -i accept -D 0/0 53 -P udp
	ipfwadm -F -i accept -S 0/0 53 -P udp
	ipfwadm -F -i accept -D 0/0 53 -P udp

	Note that I don't include tcp dns zone transfers here.  These
transfers are generally rare and between a few machines.  These are good
candidates for having explicit rules added by mason.


Rule order:
	When a packet needs to be processed (at entry, forwarding, or
exit), the firewall scans the existing list of rules to decide whether to
allow, deny or reject the packet.  As this scans stops at the first rule
that matches the packet, the order in which your final firewall rules are
executed can make a difference.  This document does not cover how to order
your rules - sorry.  The best place to find out about this is in the
O'Reilly and associates books. 

Generalization:
	When a rule is created, it covers packet transfer between specific
ports on specific machines.  For example: 

ipfwadm -i accept -W ppp0 -I -P tcp -S linux.kernel.org/32 ftp -D \
devel1.goober.net/32 1024:65535 # ftp/tcp

	If you wanted to also allow this machine to reach any other ftp
server in the world, you might want to edit this rule and remove "-S
linux.kernel.org/32" (remember that a missing source or destination port
or address are the equivalent of "anywhere".  If you simply wanted to
allow more than one machine to get to linux.kernel.org, you could replace
"-D devel1.goober.net/32" with "-D 210.134.12.0/24" (the network address
block that covers the machines in question). 
	This generalization could also be for protocols.  If you wanted
the users on devel1.goober.net to be able to reach linux.kernel.org with
any protocol, simply remove the "ftp" keyword in the command.  The port
used at the goober.net end would still have to be one of the high ports
(1024 to 65535), but they could talk to any port (ftp, smtp, http, telnet,
etc.) at the linux.kernel.org end. 

Router or end node:
	This program was originally intended for use on a traditional
firewall - a router (linux box that connects 2 or more networks through
one or more interfaces) that does packet filtering.  It works equally well
on Linux boxes with only one interface.  These could be workstations on a
LAN, servers outside of your firewall, or even slip or ppp connected
workstations.  The number of interfaces and their type and speed are
irrelevant to the firewall creation process. 

Slow machines or fast nics
	As a shell script, Mason is much less efficient at its work than a
C app would be.  On my 486sx-33, it can take a couple of seconds from the
time the log entry is fed into it until the ipfwadm rule is implemented. 
If the system is slow or if it has a lot of packets traveling through it,
it can take Mason a long time to catch up.  If this is the case, start
slow. Try one connection type at a time and give the system a chance to
settle before you move on. 
	In the step by step instructions below, I suggest turning on
packet logging for input, forward, and output filters at the same time
(see the three almost identical ipfwadm lines below with the "-o" at the
end).  If Mason is having trouble keeping up, you might want to start off
with just one of those lines (say "input" or "-I").  Run Mason for a
little while to build up the input rules.  Then turn on the "forward" 
rules, etc.  If you take this approach, make sure you run the same kinds
of traffic through the system at every step. 


Active hacking while mason running:
	If at all possible, try to set up these rules in a controlled
environment.  Hook up your firewall to machines that simulate the routers
and networks that will be used in its final location.  It is not a good
idea to create a firewall in an environment not completely under your
control. 
	If you must create the firewall rules in a live environment be
warned: Mason simply creates rules based on what traffic is passing
through it.  IT CANNOT DISTINGUISH BETWEEN THE TRAFFIC YOU'RE CREATING TO
TEACH IT AND SOMEONE ACTIVELY TRYING TO HACK THROUGH YOUR FIREWALL.  IF
THIS HAPPENS, MASON WILL CREATE RULES THAT _SPECIFICALLY_ _ALLOW_ PEOPLE
TO GET BACK IN LATER.  _Please_ read and try to understand the rules
before you put them to use in a production environment.
	(I hate all caps too, but the "boldface" button on my keyboard is
jammed :-).
	The "hacker" mentioned above does not need to be a computer
criminal in a far-off country.  This individual could be someone in
accounting that is (without malicious intent) connecting to an Internet
IRC server, even though this doesn't fit in the security policy you're
trying to implement.  If you don't read and understand the rules Mason
spits out, you may very well leave an explicit opening for this user's
future IRC use.


Doesn't teach what to block or allow:
	One more time: Mason _does_ _not_ understand the traffic flowing
through your firewall; it just creates the rules that you can later use to
specifically allow or disallow this traffic.


Masquerading:
	One of the common uses for Linux firewalling is to act not only as
a packet filter but also as a masquerading host, allowing multiple
machines to share a single IP address.  
	In this setup, simply create the masquerading forwarding rule on
the firewall before you run Mason.  Mason will simply accept the
forwarding specification you've made and create input and output rules
around it.  Mason is actually even more useful in this case as the packet
rules are even more confusing when packets are being rewritten under you.
	See the masquerading HOWTO to see how this rule is created.


Offline creation:
	If you are especially cautious, you might not want Mason actively
creating rules on your production server.  Or maybe you think you've
created a good firewall, but keep getting log messages and don't know how
to create the matching rules.  Or perhaps your CPU can't keep up.  In all
of the above circumstances, Mason can create the commands without actually
being fed the log messages live.  For example, if you have packet logging
entries in /var/log/messages, try this:

cat /var/log/messages | grep 'IP fw-' | mason<Enter>

	The output can, of course, be tee'd, redirected to a file, piped
to less, etc.  "... | sort | uniq" can be useful too when you're not
converting it live. 
	Obviously, the source file can be one that has been transferred
from another machine. 
	One final use for this technique is creating the rules when you're
not root.  Simply edit the script to change DOCOMMAND="YES" to
DOCOMMAND="NO" and the script will still output the appropriate ipfwadm
commands but won't try to execute them, allowing non-root users to create
the firewall rules.  Note that you still need to be root long enough to
turn on some kind of logging, or /var/log/messages will never contain any
entries to convert.  Root privileges are also required to implement the
rules once you've created them. 


/etc/services:
	Mason converts the protocol number and type (i.e. 53, udp) into
the more common name (domain, in this example).  It uses the /etc/services
file to do make this conversion.  Before you start, make sure all the
protocols you will work with are listed there.  If a particular protocol
is not in that file, Mason will simply leave it as a numeric entry. 
	Having this entry is especially important if you are working with
services whose ports are >= 1024 (nfs, X, squid, irc, vdolive, etc.).  If
a service >= 1024 is not found in /etc/services, it will be automatically
(and incorrectly) generalized to the port range of 1024-65535.  If your
favourite service isn't in there, simply edit the file and add it in the
same format as the other entries. 
	If you plan to do the conversion on one machine and actually run
the firewall on another, make sure all of the protocols used are listed in
the /etc/services on both machines. 


ftp:
	Ahhh, yes, ftp.  The scourge of firewall creators everywhere.  
	Ftp starts off well because the client opens a connection from a
high port (1024-65535) to the ftp control port 21.  This part of the
connection follows the same model as other tcp protocols: client uses a
random high port and connects to a fixed low port. 
	The problem arises when it's time to actually transmit data.  The
client and server exchange directory listings and files over additional
tcp connections that are between a random high port at the client end and
a random high port at the server end.
	Remember that packet filtering firewalls depend on being able to
identify connections by their (generally low) server port.  Here we have
connections that need to be allowed if ftp is going to work, but can't be
identified.
	It really comes down to a choice: does the firewall allow ftp
traffic (leaving at least one high to high rule which is a generally
considered a security risk), or do we block ftp?  You'll need to decide. 

	Mason creates these rules as transparently as any others.  It
opens up the ports for the control channel and the high to high rule
(called the data channel).  A single ftp connection could therefore open
12 rules.


Insert vs. append:
	Ipfwadm has two ways of adding rules: at the beginning of the rule
list using insert ("-i"), or at the end of the list using append ("-a"). 
The usual way of creating the firewall is to flush the existing rules and
then add each of the rules using append so they will be kept in the same
order in which they were run.  For this reason, the rules that Mason spits
out to stdout use "append" so they can easily be put in a rule set.
	Mason needs some way to tell the kernel to not log already logged
packets anymore.  The way to do this is to put a matching rule before the
logging rule.  Unfortunately, that means one of two things: deleting the
logging rule at the end, implementing the new rule at the end, and
reinstating the logging rule, or simply inserting the new rule at the top
of the list.  The first option is tricky to do well. It's also a bad
choice because the user using mason may not be logging everything, so
mason doesn't know what logging rule to reinstate.  That leaves using "-i"
to insert the rule at the very top of the list.
	The end effect is that the rules that Mason displays use "-a" to
match how that would be put into a rule file, but the rules that are
actually implemented while Mason is running use "-i" to avoid relogging
those packets in the future. 

	It's my understanding that ipchains allows one to insert rules
into a specific location in the list.  This would get around this
quirk of ipfwadm.


Allow versus deny and reject:
	To be written; allow user to change on the fly with a flag file.


Input, Output, and Forwarding:
	To implement packet filtering, the Linux kernel needs to inspect
each packet at at least one of the following three times: when the packet
enters the system, as it passes through the system on the way to its exit
interface, and as it leaves the system. 
	At each of those three times, the kernel can decide to allow or
deny/reject the packet.  The rules can be different at each stage - it's
perfectly legal to, for example, allow it in, allow it to be forwarded,
but then block it at the last second before it leaves the system.
	A simple firewall could be implemented using just, say, input
rules(*).  It's when you get complex firewalls that having rules at all
three stages is useful.  You might want to allow hosts from eth0 to get to
a pop-3 server on eth1, but not allow hosts from eth2 to get to the same
server.  This kind of restriction might be impossible to do without
forwarding rules, especially if eth2 hosts _should_ be allowed to get to a
pop-3 server on eth0.
	Anyways, if you want less than the imposing granduer of a firewall
ruleset that goes on for pages and pages, Mason can accomodate you.  If
you just want input rules, follow the instructions below, but only run the
logging rule for input ("ipfwadm -I -a deny -o").  If you just want input
and forwarding rules, just log input and forwarded packets. 
	You might even take an approach such as allowing unrestricted
access between the hosts on eth0 and eth1 and the firewall itself, but
wanting tight control over what passes in and out of eth2.  Instead of
taking one of the canned logging rules below, make up your own, such as: 

ipfwadm -I -W eth2 -a deny -o
ipfwadm -O -W eth2 -a deny -o

	Mason will only create rules corresponding to what you're actively
logging, allowing you room to be a firewall artist.

(*) The exceptions to this are the special rules for redirecting packets
(which must be done as an input rule), and masquerading packets, (which
must be done as a forwarding rule).  Even in the cases where you wish to
use these facilities, it's still legal to implement packet filtering using
another rule type. 


Limitations and Ideas:

- Mason only does ipfwadm rules.  At some point I'll add a command line
option to do ipchain rules as these will be the default in kernel 2.2. 
Don't hold your breath for ipfw rules; is there anyone that is actively
creating firewalls based on these?

- I don't implement flags as I don't understand how they get logged.  I
would like to implement -y or -k flags to further restrict the direction
in which connections get created.

- I do not use "-b" (bidirectional) rules so that I can later implement
the -y and -k rules. 

- run newrules, then append to it

- group local machines into subnet rule (configurable)

- group foreign machines into additional rule?

- pad if and proto fields to line up

- have flag file with current policy and quick scripts to change it so the
current policy can be changed on the fly. 


Quick start:
- compile a kernel with firewall and firewall logging enabled, boot into it.

- run the following ONLY IF you want to start with an empty set of rules
(if you simply want to fill in the gaps of your current rule set, do NOT
run the next three commands which erase all existing rules): 
	ipfwadm -I -f
	ipfwadm -O -f
	ipfwadm -F -f

- Set any custom firewall rules you wish.  Here is where you might put in
a masquerading forwarding rule if that is one of the uses of this
firewall. 

- If you have not already put in rules for allowing DNS requests through
your firewall, put them in now.  I offer the following rules as a general
way to allow DNS requests through, but as I mentioned above, these may be
way too general for security.  Note that these rules would allow someone
from the outside world to make requests of a nameserver running on your
firewall or on your LAN.  A cracker with root access could actually use
the following rules to get to arbitrary services on your LAN or the
firewall.  Please think about what you really want to accomplish before
using these in the final production environment. 

	ipfwadm -I -i accept -S 0/0 53 -P udp
	ipfwadm -I -i accept -D 0/0 53 -P udp
	ipfwadm -O -i accept -S 0/0 53 -P udp
	ipfwadm -O -i accept -D 0/0 53 -P udp
	ipfwadm -F -i accept -S 0/0 53 -P udp
	ipfwadm -F -i accept -D 0/0 53 -P udp

#	ipfwadm -O -i accept -W ppp0 -P udp -D 0/0 53
#	ipfwadm -I -i accept -W ppp0 -P udp -S 0/0 53


- in one window, do
tail --lines=0 -f /var/log/messages | mason | tee -a newrules

- To actaully start logging packets, switch to another window and run:
	ipfwadm -I -a deny -o
	ipfwadm -F -a deny -o
	ipfwadm -O -a deny -o

(see the section "Input, Output, and Forwarding" for examples of how to
get a smaller set of rules or only rules of a certain type). 


	This will start the kernel logging packets into /var/log/messages. 
Since no other rules (except for masquerading and DNS) have been defined,
_all_ other packets will be logged.  See the above documentation if your
machine is too slow to keep up with the initial flood of logged packets;
you may wish to execute these one at a time, waiting for the load to
settle a bit before implementing the next.
	When a packet is logged, the corresponding ipfwadm rule will be
simultaneously executed, displayed on the terminal, and appended to
newrules for later editing. 

- To watch the fireworks, do:
	tail -f --lines=0 /var/log/messages newrules
in another window.

- At this point, start making the connections of the type that you'd like
to allow in the production environment.  Mason will spit out the
appropriate rules for the traffic you're creating. 

- When you've gotten all the rules you want, press Ctrl-C in the window
running mason.  If you're going to be working on these rules for a while
and don't want to fill up your logs with additional messages, you might
want to either delete the logging rules or flush the firewall entirely. 
These are done, respectively, with the following commands: 
        ipfwadm -I -d deny -o
        ipfwadm -F -d deny -o
        ipfwadm -O -d deny -o
or
        ipfwadm -I -f
        ipfwadm -O -f
        ipfwadm -F -f


- I find the upcoming editing to be easier if similar rules are placed
next to each other.  Here's how:

cat newrules | sort -t '#' +1 >>edited_rules

	Strictly speaking, this sorts the rules according to the comments
at the end of the line.  This has the effect of placing similar rules
together as the comments indicate the rule type.  Make sure you run this
after adding any new rules to "newrules". 
	Edit the edited_rules file to generalize and reorder the rules. 
If this is the complete set of rules for your environment, you might be
able to get away with creating the following wrapper script to create a
full firewall: 

---- cut here ---- firewall.run ---- cut here ----
#!/bin/bash
/sbin/ipfwadm -I -f
/sbin/ipfwadm -O -f
/sbin/ipfwadm -F -f
/sbin/ipfwadm -I -p def_policy*
/sbin/ipfwadm -O -p def_policy*
/sbin/ipfwadm -F -p def_policy*

[place any rules you want to appear before the generated rules here. 
These might include the masquerading rule and/or DNS rules]

. ./edited_rules

[place any rules you want to appear after the generated rules here.  These
might include any logging rules]

---- cut here ---- firewall.run ---- cut here ----

* def_policy is either deny or accept, as appropriate for your firewall.

	All three of the files should be hidden from public view by
executing: 
chmod 700 firewall.run edited_rules newrules<Enter>
	The firewall can be started automatically at boot time by adding
the following line to /etc/rc.d/rc.local (or the appropriate boot script
for your linux): 

/path/to/firewall.run


- Bill Stearns

"read" bash function syntax
       read [-r] [name ...]
              One  line  is read from the standard input, and the
              first word is assigned to the first name, the  sec-
              ond  word to the second name, and so on, with left-
              over words assigned to the  last  name.   Only  the
              characters  in  IFS  are  recognized as word delim-
              iters.  If no names are supplied, the line read  is
              assigned to the variable REPLY.  The return code is
              zero, unless end-of-file is encountered.  If the -r
              option  is  given,  a backslash-newline pair is not
              ignored, and the backslash is considered to be part
              of the line.

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

#DEBUG="YES"
DEBUG="NO"
#ECHOCOMMAND="NO"
#DOCOMMAND="NO"
ECHOCOMMAND="YES"
DOCOMMAND="YES"
LAST1=""
LAST2=""
LAST3=""
LAST4=""
LAST5=""
CURRENT=""

#FIXME - break up into read field1, field2, etc.
read MESSAGE
while [ "${MESSAGE}" != "" ]; do
    unset COMMENT DESTHOST DESTIP DESTPORT DIR IF PROTO SRCHOST SRCIP SRCPORT
    MESSAGE=`echo ${MESSAGE} | grep ' kernel\: ' \
     | sed -e 's/.* kernel\: //' \
     | grep '^IP fw\-'`

    if [ "${DEBUG}" = "YES" ]; then echo message= ${MESSAGE} ; fi
    if [ "${MESSAGE}" != "" ]; then

        case `echo ${MESSAGE} | awk '{print $2}'` in
        fw-out)	DIR='O'							;;
        fw-in)	DIR='I'							;;
        fw-fwd)	DIR='F'							;;
        *)	echo Huh?  X`echo ${MESSAGE} | awk '{print $2}'`X	;;
        esac

              IF=`echo ${MESSAGE} | awk '{print $4}'`
           PROTO=`echo ${MESSAGE} | awk '{print $5}' | tr A-Z a-z`
           SRCIP=`echo ${MESSAGE} | awk '{print $6}' | sed -e 's/\:.*//'`
         SRCPORT=`echo ${MESSAGE} | awk '{print $6}' | sed -e 's/.*\://'`
          DESTIP=`echo ${MESSAGE} | awk '{print $7}' | sed -e 's/\:.*//'`
        DESTPORT=`echo ${MESSAGE} | awk '{print $7}' | sed -e 's/.*\://'`

	if [ `echo ${IF} | wc --bytes` -lt 5 ]; then
	    IF=`echo "${IF}     " | cut -b 1-4`
	fi

#Configurable name lookup?
	
	SRCHOST=`cat /etc/hosts | egrep "^${SRCIP}[^0-9]" | awk '{print $2}'`
	if [ "${SRCHOST}" != "" ]; then
	    SRCIP=${SRCHOST}
	elif [ "${SRCPORT}" != "53" -a "${DESTPORT}" != "53" ]; then
	    SRCHOST=`host ${SRCIP} | grep '^Name' | sed -e 's/.* //'`
	    if [ "${SRCHOST}" != "" ]; then
	        SRCIP=${SRCHOST}
	    fi
	fi

	DESTHOST=`cat /etc/hosts | egrep "^${DESTIP}[^0-9]" | awk '{print $2}'`
	if [ "${DESTHOST}" != "" ]; then
	    DESTIP=${DESTHOST}
	elif [ "${SRCPORT}" != "53" -a "${DESTPORT}" != "53" ]; then
	    DESTHOST=`host ${DESTIP} | grep '^Name' | sed -e 's/.* //'`
	    if [ "${DESTHOST}" != "" ]; then
	        DESTIP=${DESTHOST}
	    fi
	fi

	if [ "`echo ${PROTO} | sed -e 's/\/.*//'`" = "icmp" ]; then
	    SRCPORT=`echo ${PROTO} | sed -e 's/.*\///'`
	    PROTO="icmp"
	    DESTPORT=""
	    if [ "${DEBUG}" = "YES" ]; then echo proto= ${PROTO} srcport= ${SRCPORT} destport= ${DESTPORT} ; fi

	    case ${SRCPORT} in
	    0)		COMMENT="# Echo reply/icmp(${DIR})"		;;
	    3)		COMMENT="# Dest Unreach/icmp(${DIR})"		;;
	    4)		COMMENT="# Source Quench/icmp(${DIR})"		;;
	    5)		COMMENT="# Redirect/icmp(${DIR})"		;;
	    8)		COMMENT="# Echo req/icmp(${DIR})"		;;
	    11)		COMMENT="# Time exceeded/icmp(${DIR})"		;;
	    12)		COMMENT="# Parameter prob/icmp(${DIR})"		;;
	    13)		COMMENT="# Timestamp req/icmp(${DIR})"		;;
	    14)		COMMENT="# Timestamp reply/icmp(${DIR})"	;;
	    15)		COMMENT="# Info req/icmp(${DIR})"		;;
	    16)		COMMENT="# Info reply/icmp(${DIR})"		;;
	    17)		COMMENT="# Addr Mask req/icmp(${DIR})"		;;
	    18)		COMMENT="# Addr Mask reply/icmp(${DIR})"	;;
	    *)		COMMENT="# unknown-${SRCPORT}/icmp(${DIR})"	;;
	    esac
	else
	    COMMENT="#"
	    MATCHSERVICE=`cat /etc/services | grep "${SRCPORT}/${PROTO}"`
	    if [ "${MATCHSERVICE}" != "" ]; then
	        SRCPORT=`echo ${MATCHSERVICE} | awk '{print $1}'`
	    else
	        case ${SRCPORT} in
	        [0-9]|[1-9][0-9]|[1-9][0-9][0-9]|10[0-1][0-9]|102[0-3])	: ;;
	        *)			SRCPORT="1024:65535"		;;
	        esac
	    fi
	    if [ "${SRCPORT}" != "1024:65535" ]; then
		COMMENT="${COMMENT} ${SRCPORT}/${PROTO} "
	    fi

	    MATCHSERVICE=`cat /etc/services | grep "${DESTPORT}/${PROTO}"`
	    if [ "${MATCHSERVICE}" != "" ]; then
	        DESTPORT=`echo ${MATCHSERVICE} | awk '{print $1}'`
	    else
	        case ${DESTPORT} in
	        [0-9]|[1-9][0-9]|[1-9][0-9][0-9]|10[0-1][0-9]|102[0-3]) : ;;
	        *)			DESTPORT="1024:65535"		;;
	        esac
	    fi
	    if [ "${DESTPORT}" != "1024:65535" ]; then
	        if [ "${SRCPORT}" != "${DESTPORT}" ]; then
		    COMMENT="${COMMENT} ${DESTPORT}/${PROTO}"
		fi
	    fi
	    COMMENT="${COMMENT} (${DIR})"
	fi


	case ${PROTO} in
	tcp)		PROTO="tcp "					;;
	udp)		PROTO="udp "					;;
	esac

        CURRENT="ipfwadm -a accept -W ${IF} -${DIR} -P ${PROTO} -S ${SRCIP}/32 ${SRCPORT} -D ${DESTIP}/32 ${DESTPORT}"
	if [ `echo ${CURRENT} | wc --bytes` -lt 101 ]; then
	    CURRENT=`echo "${CURRENT}                                         " | cut -b 1-100`
	fi
	CURRENT="${CURRENT} ${COMMENT}"

	if [ "${DEBUG}" = "YES" ]; then echo current= ${CURRENT} ; fi
	case ${CURRENT} in
	${LAST1})	:	;;
	${LAST2})	:	;;
	${LAST3})	:	;;
	${LAST4})	:	;;
	${LAST5})	:	;;
	*)
            if [ "${ECHOCOMMAND}" = "YES" ]; then 
                echo "${CURRENT}"
            fi

            if [ "${DOCOMMAND}" = "YES" ]; then 
                ipfwadm -i accept -W ${IF} -${DIR} -P ${PROTO} -S ${SRCIP}/32 ${SRCPORT} -D ${DESTIP}/32 ${DESTPORT}
            fi
	    LAST5=${LAST4}
	    LAST4=${LAST3}
	    LAST3=${LAST2}
	    LAST2=${LAST1}
	    LAST1=${CURRENT}	;;
	esac

	if [ "${DEBUG}" = "YES" ]; then 
	    echo ${MESSAGE}
	    echo src= ${SRCIP} ${SRCPORT} dest= ${DESTIP} ${DESTPORT}
	    echo if= ${IF} proto= ${PROTO}
	    echo
	fi
    fi
    read MESSAGE
done

