Using IPFW Rulesets with BSD Firewalls

IPFW makes an excellent network firewall and I intend to illustrate its ease of use. I will cover the general use of IPFW and ruleset structure. The essence of IP filtering is a subject of great debate and the very best way to learn effective filtering is through research and experience. I'd like to remind users that this tutorial details my preferred methods of working with IPFW and alternate methods exist for most tasks.

Start IPFW

Define the variable in /etc/rc.conf to start IPFW upon boot.

firewall_enable="yes"

Now start IPFW from the command line by loading the kernel module.

# kldload ipfw

Rules File

Create a rules file and define the variable to load the rules upon boot.

firewall_type="/path/to/file"

Load the rules with the ipfw command.

# ipfw /path/to/file

The ipfw command is my preferred method for loading rules. I make changes to the rules file, then reload the entire ruleset. Disabling the firewall during load provides that the firewall will not block proper traffic as the ruleset changes. This is important in the event a typo causes rule load failure. It is also necessary to flush the old rules before loading the new. A rules file addressing these conventions may look like:

disable firewall
[tables]
-f flush
[rules]
enable firewall

Tables

Tables provide a method of referencing multiple network addresses in IPFW rules. To configure tables in the rules file, first flush the table:

Structure

Undoubtedly, the most overlooked aspect of free-form firewall design is structure. Cisco IOS enforces excellent firewall structure through the binding of ACLs to an interface and direction. The flexibility afforded by IPFW includes no mechanism for enforcing structure. Instead, the skipto action suffices. Consider a router with two interfaces, eth0 (internal) and eth1 (external).

[rules applied to all interfaces; for example, chk-state/frags]
add set 1 skipto 10000 ip from any to me in
add set 2 skipto 15000 ip from me to any out
add set 3 skipto 20000 ip from any to any in via eth1
add set 4 skipto 25000 ip from any to any out via eth1
add set 5 skipto 30000 ip from any to any in via eth0
add allow ip from any to any # default rule
### local host - in #################################
add 10000 set 1 count ip from any to any
[rules]
add set 1 deny ip from any to any # default rule
###############################################
### local host - out ################################
add 15000 set 2 count ip from any to any
[rules]
add set 2 allow ip from any to any keep-state # default rule
###############################################
### external interface - in ###########################
add 20000 set 3 count ip from any to any
[rules]
add set 3 deny ip from any to any # default rule
###############################################
### external interface - out ##########################
add 25000 set 4 count ip from any to any
[rules]
add set 4 allow ip from any to any keep-state # default rule
###############################################
### internal interface - in ###########################
add 30000 set 5 count ip from any to any
[rules]
add set 5 allow ip from any to any # default rule
###############################################

The ruleset consists of a series of skipto rules that reference a series of rule modules. Modularity makes a ruleset easier to comprehend and provides finer traffic controls. It is important to skip to the local host modules before any other modules, or traffic may match an improper rule. A modular design provides several default rules throughout the firewall. A striking difference of modularity is that very few iterations reach the default rule.

This example ruleset also demonstrates IPFW sets, which handle rules as a group. For example, you may disable an entire set. Sets also provide for comprehensible output when executing ipfw -S show or ipfw -S list.

Troubleshooting

If something seems to not work properly, start by using tcpdump. It's very easy to use and provides excellent troubleshooting information. Append a bpf filter to view only the interesting traffic.

# tcpdump -i eth0 ip

Sometimes a firewall can block the loopback interface. This is not desirable. ping is useful to test for such a blockage.

# ping localhost

Spoofing can be a problem, and an improperly configured firewall may allow spoofed traffic. The best course of action is to spoof your own firewall with hping. hping is not part of the base system, so install it from the ports collection.

First, tcpdump the loopback interface:

# tcpdump -i lo0

Now, send a spoofed ping from a remote host:

# hping -icmp -a [firewall host IP] [firewall host IP]

If tcpdump indicates ping replies on the loopback interface, then you know that the firewall is spoofable.