Aaron's IPFW Patches for FreeBSD

FreeBSD's IPFW IP filtering software is a requirement for any FreeBSD box I administer. However, the statefule IP filtering in IPFW has one significant limitation that I dislike. A dynamic rule that is created to statefully pass UDP or TCP traffic eventually will expire based on the state of the traffic stream and the settings of several global sysctl variables. Unfortunately, as an administrator who likes to follow the "everything that is not explicitly permitted is denied" ethic, I must increase the global timeouts in order to pass traffic statefully for just a very few protocols (SSH and telnet among them) that have sessions that tend to idle longer than the default timeout settings.

The solution to this dilemma is the ability to override the default sysctl expiration lifetime settings on a per-rule basis. And thus the patches below were born.

Thanks to Crist J. Clark for suggesting how to make the patches more efficient. The patches include his suggested improvements since 6 July 2001.

IPFW Patch Sets:

PLEASE NOTE: The patches are listed below in reverse chronological order, newest at the top. Please remember that there are two sets of patches, the lifetime set (adds per-rule lifetime expiration control), and the state set (adds stricter expiration of dynamic TCP rules when unusual states/conditions occur). For FreeBSD 4.x, there are separate versions of these two patch sets for ipfw and ipfw2.

FreeBSD Version (Date) IPFW IPFW2 Notes
Lifetime State Lifetime State
8.0-STABLE (14 MAR 2010) patch patch 14-MAR-2010: These are the patches I've been using for FreeBSD version 8.
7.1-STABLE (31 MAR 2009) patch patch 31-MAR-2009: As of RELENG_7 today, the ipfw2 patch no longer applied cleanly, as part of the ipfw2.c file was moved to the ipfw2.h file. This update means both patches should once again apply cleanly.
6.3-STABLE (28 MAR 2008) patch patch 28-MAR-2008: No changes were needed. The patches applied cleanly to 6.3-STABLE (as of 28 Mar. 2008) and I presume they'll apply cleanly to 6.3-RELEASE too.
6.2-STABLE (29 MAR 2007) patch patch 29-MAR-2007: I had to update the patches for 6.2-STABLE to adapt for the variable name changes in sys/netinet/ip_fw2.c from time_second to time_uptime, A quick search-and-replace made the update easy.
6.2-RELEASE (15 JAN 2007) patch patch 16-AUG-2006:he patches for 6.0R work just fine for 6.1R. No changes were made. I just forgot to make a change to this web page until today. However, for 6.1-STABLE as of 16 August 2006, a few changes were necessary.
6.1-STABLE (16 AUG 2006)   N/A   N/A   patch   patch
6.1-RELEASE   N/A   N/A   patch   patch
6.0-RELEASE   N/A   N/A   patch   patch
5.3-RELEASE   N/A   N/A   patch   patch
5.2-RELEASE   N/A   N/A   patch   patch
5.1-RELEASE   N/A   N/A   patch   patch
5.0-RC1 (21 DEC 2002)   N/A   N/A   patch   patch
4.10-RELEASE   patch   patch   patch   patch
4.9-RELEASE   patch   patch   patch   patch
4.8-STABLE (14 AUG 2003)   patch   patch   patch   patch
4.8-RELEASE   patch   patch   patch   patch
4.7-STABLE (19 NOV 2002)   patch   patch   patch   patch
4.7-RELEASE   patch   patch   patch   patch
4.6-STABLE (01 AUG 2002)   patch   patch   patch   patch
4.6-RELEASE   patch   patch   N/A   N/A
4.5-RELEASE   patch   N/A   N/A   N/A
4.4-STABLE (19 DEC 2001)   patch   N/A   N/A   N/A
4.4-STABLE (14 NOV 2001)   patch   N/A   N/A   N/A
4.4-STABLE (31 OCT 2001)   patch   N/A   N/A   N/A
4.4-STABLE (25 SEP 2001)   patch   N/A   N/A   N/A
4.4-RELEASE   patch   N/A   N/A   N/A
4.3-STABLE (06 JUL 2001)   patch   N/A   N/A   N/A

Patch Change History:

I originally wrote the ipfw lifetime patches for FreeBSD 4.0-STABLE, submitting the first version to the freebsd-net@freebsd.org mailing list on 5 June 2000, subsequently filing FreeBSD PR #22065 offering the patches to add this feature to FreeBSD (and later PR #28713).

02 NOV 2005: No real updates to the patches were needed from 5.3 to 6.0. Nevertheless, I updated them to reduce the delta in future changes. Only offsets changed.

04 NOV 2004: Originally I was going to abandon these patches, switching from ipfw to pf on my systems when I upgraded for 5.3-RELEASE. I have too many systems using ipfw to make that realistic, so I'll keep maintaining these patches it looks like. Also, ipfw, while missing some of the very nice state tracking, modulation, and scrubbing abilities of pf, and missing in-kernel NAT, it has a well-defined flow so you know exactly what happens to each packet and when. With ipfw I can exactly control when network address translation happens with precision, as well as control when stateful dynamic rule matching is performed. Useful stuff!

20 JAN 2004: I finally got around to updating my home system from 5.1 to 5.2. The patches for 5.1-RELEASE still applied cleanly to 5.2-RELEASE and everything appears to be working fine.

30 OCT 2003: The patches for 4.8 appear to apply cleanly to 4.9-RELEASE, no changes required. I'm now doing a buildworld/installworld to make sure everything works well.

14 AUG 2003: Just a quick update for 4.8-STABLE so that the ipfw2 patch will again apply cleanly. And I thought I was done with patching the 4.x line... *chuckle*

09 JUN 2003: Now that 5.1-RELEASE is here and quite stable (at least on the system I installed 5.0-RC1 on back in December and then tracked 5.x, updating periodically), I probably won't be updating the patches for the 4.x line anymore. Actually I haven't had to modify the patches for 4.x since 4.6-STABLE in Sept. of 2002. Hopefully no modifications will be necessary until 5.x becomes the new -STABLE line.

21 DEC 2002: Finally I took the plunge and installed 5.0-RC1 to begin familiarizing myself with 5.0. So far it's been quite stable for me, in spite of a few minor rough edges (to be expected in a release candiatate on the -CURRENT tree). Hopefully the 21 Dec. 2002 patches for the 5.0 tree will apply cleanly to RC1. They do apply cleanly to -CURRENT as of 21 Dec. 2002.

09 September 2002: Updated patches for -STABLE for both IPFW and IPFW2 systems and switched to a table format to list patch versions available.

01 August 2002: Added new patch sets for Luigi Rizzo's IPFW2 system for 4.6-STABLE.

17 June 2002: I've added another ipfw patch set that fixes what I personally consider a deficiency in the ipfw stateful handing machine where certain packets should be blocked. Situations where this actually occur are VERY RARE, so most people probably don't need the new ipfwstate patches at all, or may even disagree with what they do. I personally think that it is a cleaner, clearer way of handling invalid TCP states within ipfw with a log message. This new patch set is useful when playing with a complex ipfw and network translation set-up where a misconfiguration can cause the middle (second) packet of a 3-way TCP handshake to "miss" or bypass a rule. In such cases, a log entry appears in the logs, prompting the administrator to fix the ruleset flow. Likewise, if for some reason an outside hacker sends a TCP SYN packet followed by a TCP ACK packet, pretending that he/she received the middle TCP SYN+ACK, with this patch, the subsequent ACK packet is dropped with a log message and the dynamic rule is expired. That just makes sense to me. If I'm using dynamic rules, my ipfw machine MUST be "in the middle" of all TCP sessions utilizing those rules, so if a packet is missiong from the three-way handshake, I want the dynamic rules associated with that session to expire as soon as the invalid state is detected, and I want the offending packet dropped.

Patch Notes:

The lifetime patch set modifies two kernel files (so you have to rebuild your kernel) as well as the ipfw command itself (and the ipfw man page too). Just copy the patch file to your /usr/src directory, use it to patch the files, copy a header file, then rebuild your kernel and the ipfw command:

patch -p < ipfw-patch-4.4-STABLE-2001-09-25.txt
cp /usr/src/sys/netinet/ip_fw.h /usr/include/netinet/
cd /usr/src/sbin/ipfw
make && make install
cd /usr/src/
make buildkernel KERNCONF=YOURKERNELCONFIGFILE
make installkernel KERNCONF=YOURKERNELCONFIGFILE
reboot

Once installed, you can now override the global dynamic rule lifetime expiration times controlled by various sysctl variables on a rule-by rule basis (or you can NOT override it and use the default ipfw behavior). For example, this lets me allow HTTP connections to port 80 the usual way using the default behavior while overriding the timeout on SSH (port 22) TCP connections so that my SSH sessions don't timeout after 5 minutes of inactivity. Here's an example based on firewall rules for a single workstation:

...initial rules...

# Check the dynamic ruleset so legitimate established traffic
# will be allowed:
ipfw add check-state

# Block any supposedly "established" TCP traffic since legitimate
# traffic will match the above rule with a dynamic rule:
ipfw add deny tcp from any to any established

# Permit outgoing SSH connections from my workstation and override
# the default dynamic rule lifetime with a 2-hour timeout:
ipfw add permit tcp from me to any 22 out setup keep-state lifetime 7200

# Permit outgoing HTTP connections from my workstation (just use
# the default expiration times):
ipfw add permit tcp from me to any 80 out setup keep-state

...more rules...

# Drop other TCP traffic:
ipfw add deny log tcp from any to any

...more rules...

# DNS queries use the default sysctl dyn_*_lifetime timeout:
ipfw add permit udp from me to any 53 out keep-state

# Give ICQ traffic a 10-minute timeout:
ipfw add permit udp from me to ${icqnets} 4000 out keep-state lifetime 600

...more rules...

# Drop remaining UDP traffic:
ipfw add deny log udp from any to any

...the rest of the rules...

See the ipfw man page (after you apply the patches and install it) for more documentation, in particular the stateful rule section where the lifetime extension that the patches add is covered.

I have been running these patches for well over a year now on several firewalls and also on several moderate-to-high traffic hosts on the Internet with no trouble at all.

Since Luigi Rizzo add TCP keepalive packet generation to ipfw2 certain applications no longer require these patches. If, however, you do not want your firewall to generate TCP keepalive traffic--and there are cases where this may be desired--these patches remain useful. Even with ipfw2, non-TCP IP protocols still benefit from finer grained per-rule lifetime control.

To turn off TCP keepalive packet generation under ipfw2 do this:

sysctl net.inet.ip.fw.dyn_keepalive=0

Or set the same variable to zero in your /etc/sysctl.conf file.

Or you can do it on the fly using the ipfw disable dyn_keepalive on the command-line.

I personally prefer that my firewall NOT generate TCP keepalive traffic. One reason is that with TCP keepalive traffic being automatically generated by the firewall, if a TCP session terminates abnormally (due to a reboot or perhaps a network problem) and the near side has this feature enabled, and the other side has a very picky silent-discard rule-set, that other side, when it is back online, will continue to see TCP keepalive traffic periodically even though there are no established TCP sessions until the dynamic rule finally expires on the near side. I suppose this could be alleviated by not silently dropping "bogus" established TCP traffic on the remote side firewall and instead sending out a RST to terminate the left-over dynamic rule on the near side, but why bother. I just hate having extraneous and unnecessary traffic for any reason on my network.

If you do rely on ipfw2's TCP keepalive feature, you have to make sure that your rule sets allow that traffic to be sent. It's easy to accidentally block or deny it without realizing.

Let Me Know

IF YOU USE THESE PATCHES please drop me a note on my Contact Me web page and let me know how you use the patches.