Connection over the Internet is the lifeblood of many of today's businesses,
but it also leaves the door open for malicious crackers. To protect their
networks, systems administrators employ a variety of tactics to restrict
access to only the machines and services deemed necessary. The most widely
used method to tighten access permissions is packet filtering and firewalling.
Often filtering happens at the company's main connection points to the
Internet, but the savvy systems administrator defends not only the routers but
individual hosts as well.
Various products, such as Sun's SunScreen, Check Point FireWall-1,
iptables, ipfw, OpenBSD pf, and IP Filter exist to
perform a combination of stateful packet filtering and Network Address
Translation (NAT). IP Filter, written by Darren Reed, provides a good balance of features and OS portability for sites that need to protect heterogeneous environments.
An Overview
IP Filter or ipfilter, also known as ipf, runs on the Solaris Operating System (SPARC and x86 Platform Editions), SunOS, FreeBSD, NetBSD, OpenBSD, HP-UX, Tru64, Irix, and several other operating
systems. The portion that interacts with the kernel can be compiled into the
kernel itself, compiled as a standalone kernel module, or both (but only one
at a time), depending on the OS. Under the Solaris OS, ipfilter is loaded as a
kernel module with the modload command. Because of the drastic differences in the Linux kernel design, however, very few versions of Linux
will support ipfilter (see the IP Filter FAQ).
The ipfilter package consists of several programs:
ipf: This binary allows the user to specify a set of filter
rules, either from a file or on the command line, which are then added to the
kernel's current filter list. The ipf program also allows the user to enable or disable filtering entirely, delete individual rules or the entire ruleset,
and reset the statistics counters.
ipnat: This binary functions as the NAT-controlling
counterpart to ipf. It reads in rules from standard in or a file and applies
them to the existing NAT table. In addition, ipnat allows the user to individually delete or entirely flush NAT rulesets and monitor NAT
statistics.
ipfs: This binary saves and restores information for NAT and state tables.
ipmon: This binary, as the name suggests, monitors both ipf and ipnat by reading the log device (the default is /dev/ipl) and
sending data to standard out, a file, or directly to syslog.
ipfstat: This binary allows the users to query the kernel to monitor statistics on ipf's packet processing. ipfstat takes a number of
different flags, resulting in finer or coarser statistic output.
ipftest: This binary reads a ruleset from standard in or a
file and then applies sample IP packets to said rules. This allows the user to
test and debug new rulesets before making them an active part of the actual
firewall ruleset.
iptest: This binary contains a set of test "programs" that send out a series of IP packets aimed at testing the strength of the TCP/IP
stack of the target host. Running iptest against a target machine may crash it.
ipsend: This binary generates arbitrary IP packets for
Ethernet-connected machines.
ipresend: This binary reads in a data file of saved IP
packets from a program like snoop, tcpdump, or
etherfind, and replays the session. This is useful for debugging
rulesets with the exact same set of data every time.
Obtaining and Installing IP Filter
IP Filter needs little in the way of supporting software but does require a
64-bit-capable compiler if the target machine runs a 64-bit kernel. The
UltraSPARC III processors, for example, no longer support 32-bit kernel applications and necessitate building 64-bit binaries. Solaris users can install gcc version 3.3.2 (known to produce 64-bit safe code) or later, or they can opt to use Sun's
own commercial compiler. Also, ensure that make is defined
as /usr/ccs/bin/make and not GNU make, regardless of the compiler vendor. The last two steps below automatically create a Solaris package,
`/usr/bin/uname -p`-`/usr/bin/uname -r`/ipf.pkg, which can be
copied to other hosts for installation. If ipfilter is compiled on a 64-bit
machine, this package will include both 64- and 32-bit versions of select
binaries and the kernel module.
wget http://coombs.anu.edu.au/~avalon/ip-fil3.4.33pre1.tar.gz
gtar zxf ip-fil3.4.33pre1.tar.gz
cd ip_fil3.4.33pre1/
make solaris
cd SunOS5
make package
The resulting package installs the binaries ipf,
ipnat, ipfstat, and ipfs into
/usr/sbin/sparcv7 and /usr/sbin/sparcv9 and links
their counterparts in /usr/sbin with
/usr/lib/isaexec for ease of use. The loadable kernel modules
reside in /usr/kernel/drv/sparcv9/ipf and
/usr/kernel/drv/ipf. The module loaded depends on whether the
machine booted the 64- or 32-bit kernel. Architecture-independent binaries
install into /opt/ipf/bin, man pages into
/opt/ipf/man, header files into
/usr/include/netinet, examples into
/opt/ipf/examples, and the system init file into
/etc/init.d/ipfboot (symlinked to by
/etc/rc2.d/S65ipfboot).
After creating the package, add it to each desired host with the pkgadd command:
This installs the software and loads the kernel module, but does not enable
any filtering or NAT rulesets. The next step requires building a filter
and/or NAT ruleset based on the company's access policy.
Configuration
IP Filter configuration rules reside in the files /etc/opt/ipf/ipnat.conf for
NAT and /etc/opt/ipf/ipf.conf for filtering. If
/etc/opt/ipf/ipnat.conf exists, the
/etc/init.d/ipfboot startup script will run ipnat,
and if /etc/opt/ipf/ipf.conf exists, the script will run
ipf. The two files are independent of each other, and
ipf and ipnat may be run singly or together on any given machine.
Configuring NAT
ipnat provides Network and Port Translation (NPAT), allowing the user to
change any of the source and destination IP addresses and ports. Four directives are used in /etc/opt/ipf/ipnat.conf to accomplish
this:
map - Map one address or network to another using a round-robin algorithm.
map-block - Set up static IP address-based translation. The
mapping is based on an algorithm designed to force the addresses to be
translated into the destination range.
bimap - Set up bidirectional NAT between an external and an internal IP address.
rdr - Redirect packets destined for one IP address and port pair to another address and port pair.
The grammar for ipnat rulesets is described in BNF notation in the
ipnat(5) man page. The grammar listed is incomplete and
outdated, but the use of ipnat is actually quite simple. The
most commonly used directive is map, and the most basic example
is mapping all packets from one address or range of addresses to another
address.
The following example is known as a many-to-one mapping, or IP masquerading,
and is most often used by small organizations that use only one Internet-facing
server. These two rules map any packets originating from the class C sized
network 192.168.1.0/24 and destined for the hme1 interface to the IP address
10.1.1.1. The first rule remaps the port number for each TCP and UDP
connection so that multiple hosts can send data to the same ports without
collisions. The second rule changes the appearance of the packet's source
address.
Often the notation 0/32 replaces the actual IP address of the machine
running ipnat, and the auto keyword replaces the port range so that ipnat determines which ports are free on its own. The preceding example, running on the
machine with the hme1 "external" interface of 10.1.1.1, would be written as the
following instead:
The preceding example is a many-to-one mapping, but sometimes a many-to-many
mapping is desired instead. This kind of mapping becomes useful when a small
set of statically assigned addresses for services in a DMZ needs to be mapped
to internal addresses in use behind a router. The following example, using
the directive map-block, remaps the addresses in the class C sized network 192.168.1.0/24 to the class C sized network 10.1.1.0/24:
map-block hme1 192.168.1.0/24 -> 10.1.1.0/24 auto
The last type of NAT is a one-to-one mapping, accomplished in the
ipnat.conf file with the bimap directive. The following example
makes the interface with the IP of 192.168.1.1 look like it has the IP address
of 10.1.1.1:
bimap hme1 192.168.1.1/32 -> 10.1.1.1/32
The rdr directive usually comes into play when trying to
provide transparent proxying for a service. The following example redirects
connections TCP on port 80 of 10.1.1.1 to port 8080 of 192.168.1.1, allowing a
web server to be hidden inside the internal network and running on a
non-standard port:
rdr 10.1.1.1/32 port 80 -> 192.168.1.1 port 8080 tcp
A lengthier explanation and more examples of each of the ipnat
directives can be found in the ipnat(5) man page and in the ipf HOWTO
document.
Configuring Filtering
The /etc/opt/ipf/ipf.conf file contains all of the filtering
rules for a host and is read and matched from top to bottom. People usually
choose one of two policies: block everything and then allow certain
packets, or pass everything and block only certain packets. The
former is more restrictive and therefore more secure, but may not be
appropriate in all situations. The default filtering rules should be based on
the policy at the site in question.
The filter rules use eight action keywords which are applied to packets
matching the parameters of the rule. In general, the first three are the most
heavily used in any filtering configuration:
block - Block a packet.
pass - Pass a packet.
log - Log the action taken on the packet. The following
qualifiers can be appended to the log action. If more than one
qualifier is used, they must be specified in the following order:
body - Log the first 128 bytes of the packet contents after logging the headers.
first - If being used in conjunction with the keep option (described below), log only the first packet in the session and ignore other packets sharing the state information.
on-block - If the filter is unable to block the packet (if the log reader is too slow, for example), treat the rule as if the action were block.
level <loglevel> - Use the given priority and, if given, facility to log information about this packet using ipmon -s.
count - Include the packet in the accounting statistics kept by ipfstat.
call - Call the named function in the kernel. This action is not well documented and is only for use by experienced hackers.
skip <n> - Skip over the next n filter
rules.
auth - Hold the packet in an internal buffer and call an
external program to determine whether or not the packet is valid. An example
external program might request authentication, such as a password, from the
user before allowing the packet through.
preauth - Look at the pre-authenticated list for a match. If
no match is found, drop the packet. If a match is found, use the result from
that ruleset instead.
After specifying the action to take on the packet, the next word must be
either in or out, defining whether the rule is for
packets inbound to the kernel (just received on an interface) or outbound from
the kernel (processed by the stack and on its way to an interface). The next
keywords are optional, but when used must be listed in the order below:
log - Log the packet if this rule is the last that matches.
The qualifiers mentioned above in the description of the log
action may also be specified here under the log keyword.
quick - If the packet matches this rule, stop processing the
packet and take whatever action this rule lists.
on <iface> - Make the rule apply only to the specified
interface.
dup-to <iface> - Copy the packet and send the duplicate
to the specified interface. This option is useful for logging on remote hosts
or when using a network sniffer.
to <iface> - Move the packet to the outbound queue on
the specified interface. Use this option to circumvent further kernel
processing, making the machine on which ipfilter runs appear to be a
transparently filtering hub or switch rather than a router.
After specifying any options, the rest of the rule consists of keyword
parameters that determine whether or not the packet matches the rule. If
more than one parameter is listed in a rule, they must be specified in the
following order:
tos - Filter based on the packet's Type Of Service. The TOS
mask can be expressed as a hexadecimal or decimal number.
ttl - Filter based on the packet's Time-To-Live value. The
decimal value specified in the rule must exactly match that of the
packet.
proto - Filter based on the packet's protocol. Protocol
names listed in /etc/protocols or the decimal number of the
protocol may be used.
from/to - Filter based on the source or
destination IP address and/or port number. IP addresses can be specified as
an address/mask pair, a dotted quad, a valid hostname, or the special keyword
any which matches all. Ports can be specified by name as listed
in /etc/services, or by decimal number.
with - Filter based on the listed IP option. The
with parameter may be prepended with the not or
no options to indicate that the rule should match only if the
listed option is not present.
flags - Filter based on the listed TCP flag. The
flags parameter is therefore only useful with TCP rules. The list
of possible options to the flags parameter includes:
F - FIN
S - SYN
R - RST
P - PUSH
A - ACK
U - URG
These options can be ANDed together and masked to produce various
combinations. For example, flags SA becomes flags
SA/AUPRFSC and will match and packet with only the SYN and ACK flags
set. On the other hand, specifying flags S/SA will only match a
packet with just the SYN flag set out of the SYN-ACK pair.
icmp-type - Filter based on the ICMP type. This parameter
should only be used when proto icmp is specified and should not
be used in conjunction with flags.
keep state - Keep information about the session to which the
packet is attached. State can be kept for TCP, UDP, and ICMP packets.
keep frags - Keep information about fragmented packets to
later apply to other fragments.
head <number> - Create a new grouping of filter rules,
denoted by the tag <number>. Multiple rule groupings allow for a
hierarchy of rules and shorter searches through the lists. For example, the two rule groupings could be for proto tcp and proto
udp. If a packet is UDP, it will first fail the head rule of the tcp
group and then ignore the rest of the rules in that group. The processed
packet therefore skips all other rules that apply only to TCP packets.
group <number> - Add the rule to the group
number.
Filtering Examples
The following examples illustrate some common uses of ipf. These examples
provide a good starting place for filtering configurations, but are by no
means complete. If using these rulesets as a starting base, be sure to tailor
them to the specific needs of the site.
This first case shows a machine acting as a router between the RFC 1918
address space 192.168.1.0/24 and the outside world. This host follows a
mostly closed policy and begins by blocking and logging everything and then
allowing specified services out.
This example is a mostly closed configuration which resides on a host with one network connection. This machine hosts a high-volume web server, and
the rules for ports 80 and 443 come first to lessen the number of rules
searched for most packets.
This example shows a mostly open configuration on a host with one interface
that just blocks a few hosts and ports.
Troubleshooting and Tuning
Occasionally packets that you think should make it past your filter are
blocked, or some mapping doesn't appear to be translated in the manner you
think it should. When this happens, there are several tools available for
debugging and fine tuning. The first of these is the NAT/filter log file, the
contents of which is controlled by the ipmon program. Make sure
that logging occurs for each rule that processes the packet in question.
The other useful debugging tools are ipnat and
ipfstat. The ipnat program provides NAT statistics as well as
controlling the application or rulesets. If you are encountering a NAT issue,
use ipnat -l, ipnat -v, and ipnat -s.
If the issue occurs with the filtering rules, the ipfstat program
provides a wealth of information about packet authentication, fragment
information, per rule or protocol and overall statistics, state information,
and a top like facility (ipfstat -t) for doing
real-time monitoring.
Some common problems that occur with NAT and filtering involve timeouts for
idle or partially closed connections and running out of table space. For the
former, the following entries in /etc/system may prove useful
(note that the timers run "two ticks to a second", so the 172800 ticks for
ipf:fr_tcpidletimeout listed below translates to 24 hours). A reboot is
required for the changes to take effect.
* ipf: adjust the default tcp timeouts downward so that
* idle (dead) and half closed states get killed off quicker.
set ipf:fr_tcpidletimeout = 172800
set ipf:fr_tcphalfclosed = 7200
On servers that see a large amount of traffic where the state is kept as
part of the rulesets, it's sometimes necessary to increase the state table
size to accommodate the load. It's also wise to add flags S to
each rule where state is kept, so that only the first packet of the session is
recorded. To adjust the state table size so that there are enough buckets for
large servers, add the following to /etc/system and reboot the machine:
* ipf: adjust the state table sizes so we have enough buckets.
* IPSTATE_MAX (=fr_statemax) should be ~70% of IPSTATE_SIZE
* IPSTATE_SIZE (=fr_statesize) has to be a prime number
set ipf:fr_statemax = 7000
set ipf:fr_statesize = 10009
To increase the table size for NAT, add the following to
/etc/system and reboot the machine:
* ipf: adjust the NAT table sizes so we have enough buckets.
* generally you have fewer than 127 rules in ipnat.conf
* so no need to waste memory for more.
set ipf:ipf_nattable_sz = 10009
set ipf:ipf_natrules_sz = 127
set ipf:ipf_rdrrules_sz = 127
Resources
Many useful online resources exist for ipfilter on a variety of platforms.
The sites listed here provide a wealth of examples as well as technical help
from other ipfilter users and the author himself.