BSD DevCenter
oreilly.comSafari Books Online.Conferences.


FreeBSD Basics

BSD Firewalls: IPFW Rulesets


In the last article, I had successfully blocked all IP packets from entering or leaving my FreeBSD computer by installing ipfw with a default policy to deny all packets. This week, I want to create a ruleset to be read by ipfw that will allow the IP packets I wish to leave and enter my computer.

Since there is no "correct" way to create a ruleset, and I can't possibly demonstrate how to add rules that will cover every possible scenario, I'll instead demonstrate the logic one goes through when creating a ruleset. I'll also assume that you are familiar enough with the syntax used by ipfw that you can follow along as I create my rules.

If you're new to this series, you might want to first skim through the past articles, starting with TCP Protocol Layers Explained as we'll come across IP behaviour as we try to troubleshoot the ruleset.

When creating your own ruleset, keep in mind that rules are read in numbered order, and as soon as a packet matches a rule, ipfw stops reading the ruleset. This means that if you create two rules, say number 400 and number 800, that could apply to a specific IP packet, rule 400 would always be used and rule 800 would not be read. It's always a good idea to look at your current rules before adding another one to make sure an older rule won't override your new rule.

Also, rules apply to every interface on your computer, that is, anything you can see in the output of ifconfig -a. This isn't a problem if you only use one interface as I'm doing in my example, but can make a difference if you're using multiple interfaces. For example, if one interface is connected to the Internet and another interface is connected to your internal LAN, you'll probably want to apply different security restrictions to each interface and can do so by specifying the interface name in your ipfw rules.

Let's return to my firewall setup. This is a stand-alone computer running FreeBSD 4.2-Release that has one interface cabled to the Internet. Since this is my home computer, I've decided not to place any restrictions on the types of packets I send out to the Internet; however, I only want my computer to accept IP packets that are a valid response to the packets I've sent out.

A good way to accomplish this task is to take advantage of the "dynamic" or "stateful" feature of ipfw. If you're unfamiliar with this term, there is a good explanation here.

If I use "dynamic" rules, when I send out a packet to the Internet, ipfw will add an entry to its "state table." This entry will include the IP address of the computer I sent the packet to, and what port number I made a connection to on that computer. When packets come back from the Internet, they will be discarded if they do not come back from that IP address using that port number. However, dynamic rules only work with TCP packets, as TCP creates a connection that is used for the length of the data transfer. Since UDP doesn't create a virtual connection, it is called a "state-less" protocol and can't use the "state table."

The Examples section of the manpage for ipfw gives the three rules that are used to create this "dynamic" packet filter. Since I've decided to create my ruleset in a separate file that I've called /etc/ipfw.rules, I'll become the superuser, and create that file now with the following lines:

#from man 8 ipfw: allow only outbound TCP connections I've created
add 00300 check-state
add 00301 deny tcp from any to any in established
add 00302 allow tcp from any to any out setup keep-state

Notice that I've decided to start numbering my rules at 300 since rules 100 and 200 are pre-created in the file /etc/rc.firewall. I like to number related rules together, so I've numbered these rules 300, 301, and 302. When I create more, unrelated rules, I'll jump up to 400. Remember, you can number your rules any way you wish as long as the number isn't already in use and an earlier numbered rule won't prevent your new rule from being read.

You'll notice that these three rules contain some key words that are described in man ipfw, which I've quoted here:

  • check-state: Checks the packet against the dynamic ruleset. If a match is found then the search terminates, otherwise we move to the next rule.
  • keep-state: Upon a match, the firewall will create a dynamic rule, whose default behaviour is to matching bidirectional traffic between source and destination IP/port using the same protocol. The rule has a limited lifetime (controlled by a set of sysctl(8) variables), and the lifetime is refreshed every time a matching packet is found.
  • established: TCP packets only. Match packets that have the RST or ACK bits set.
  • setup: TCP packets only. Match packets that have the SYN bit set but no ACK bit.

In other words, when a packet arrives at one of my interfaces, ipfw will first check to see if it is in the state table; if it is, the packet is allowed. (Rule 300 does check-state.) If it's not in the state table and the RST or ACK bits are set, it will deny the packet because it's not a valid response to a connection I've created. (Rule 301 checks for established.) If the ACK flag is not set (meaning it wants to initiate a TCP connection), it is allowed, but only if the packet is outbound; if a packet meets this rule, it will also be added to the state table. (Rule 303 does setup and keep-state.)

Pages: 1, 2, 3

Next Pagearrow

Sponsored by: