Skip to main content

FreeBSD Firewall Configuration with IPFW

Published on:
.
20 min read
.
For German Version

IPFW is an IPv4 and IPv6-compatible stateful firewall designed for FreeBSD. It consists of the kernel firewall filter rule processor with its integrated packet accounting facility, the logging facility, NAT, the dummynet traffic shaper, a forward facility, a bridge facility, and an ipstealth facility.

Authors and maintainers of IPFW are FreeBSD volunteer staff members. Its syntax permits the use of sophisticated filtering capabilities, enabling users to meet complex requirements.

Mac OS X's native firewall was ipfw until Mac OS X 10.7 Lion in 2011, when it was superseded by the OpenBSD project's PF. ipfw, like FreeBSD, is open source. It is used in numerous firewall products based on FreeBSD, including m0n0wall and FreeNAS. Since Linux 1.1, a port of an early version of ipfw was used as the first firewall implementation for Linux, until it was superseded by ipchains.[4] A modern port of ipfw and the dummynet traffic shaper is available for Linux (including a prebuilt OpenWrt package) and Microsoft Windows.

FreeBSD includes an example ruleset in /etc/rc.firewall that specifies many firewall types for typical circumstances to aid inexperienced users in developing suitable rules. IPFW offers a robust syntax that enables expert users to create customized rulesets that satisfy the security needs of a specific environment.

This article explains how to activate IPFW, offers an overview of its rule syntax, and displays many rulesets for typical setup circumstances.

BEST PRACTICE

In addition the its effective L4 packet filtering and routing features, FreeBSD also provides next-generation firewall capabilities such as web control and application control. This is provided by an external tool called Zenarmor.

Zenarmor NGFW extension allows you to easily upgrade your firewall to a Next Generation Firewall in seconds. NG Firewalls empower you to combat modern-day cyber attacks that are becoming more sophisticated every day.

Some of the capabilities are layer-7 application/user aware blocking, granular filtering policies, commercial-grade web filtering utilizing cloud-delivered AI-based Threat Intelligence, parental controls, and the industry's best network analytics and reporting.

Zenarmor Free Edition is available at no cost for all FreeBSD users.

Get Started with Zenarmor Today For Free

How to Enable IPFW?

IPFW is provided as a kernel-loadable module in the default FreeBSD installation, therefore a special kernel is not required to activate IPFW.

To configure your FreeBSD node to boot with IPFW enabled, add firewall_enable="YES" to the /etc/rc.conf file by running the following command:

sysrc firewall_enable="YES"

To utilize one of the FreeBSD default firewall types, add another line that defines the type by running next command:

sysrc firewall type="open"

These are the possible types:

  • open: allows all traffic to pass.

  • client: just protects this computer.

  • simple: Protects the whole network.

  • closed: disables all IP communication with the exception of the loopback interface.

  • workstation: Using stateful rules, protects just this computer.

  • UNKNOWN: prevents firewall rules from loading.

  • filename: the whole path to the file that contains the firewall ruleset.

If firewall_type is set to client or simple, customize the default rules in /etc/rc.firewall to match the system's settings.

Note that the filename type is utilized to load an individualized ruleset.

Setting the firewall script variable to the absolute path of an executable script that contains IPFW commands is an alternative method for loading a custom ruleset. The examples in this section presume that /etc/ipfw.rules is the firewall script:

sysrc firewall_script="/etc/ipfw.rules"

To enable logging using syslogd, add the following line:

sysrc firewall_logging="YES"
tip

Only rules with the log option enabled will be logged. This option is not included in the default rules; it must be introduced separately. Therefore, it is recommended to modify the default ruleset for logging. In addition, if logs are saved in a separate file, log rotation may be desirable.

There is no variable in /etc/rc.conf for setting logging limits. To set the maximum number of times a rule is logged per connection attempt, add the related configuration line to /etc/sysctl.conf by running the next command:

echo "net.inet.ip.fw.verbose_limit=5" >> /etc/sysctl.conf

To enable logging through a dedicated interface called ipfw0, add the related configuration line to /etc/rc.conf by running the next command:

sysrc firewall_logif="YES"

Then, use tcpdump to view the logged information by running the next command::

tcpdump -t -n -i ipfw0

Start the firewall and enable logging limits by setting the sysctl value specified above:

service ipfw start
sysctl net.inet.ip.fw.verbose_limit=5

What is IPFW Rule Syntax?

When a packet enters the IPFW firewall, it is compared to the first rule in the ruleset and then to each subsequent rule in order, from top to bottom. When a packet fits the selection criteria of a rule, the rule's action is carried out and the search of the ruleset for that packet is concluded. This is known as "first match wins". If the packet does not fit any of the rules, it is intercepted by IPFW default rule number 65535, which refuses and discards all packets quietly. The search continues, however, if the packet fits a rule containing the count, skipto, or tee keywords.

Keywords must be provided in the following sequence when constructing an IPFW rule: Certain keywords are required, while others are optional. The words in uppercase indicate a variable, whereas the words in lowercase must precede the variable that follows. The ``sign marks the beginning of a remark and may come at the conclusion of a rule or on a separate line. Blank lines are ignored.

CMD RULE_NUMBER set SET_NUMBER ACTION log LOG_AMOUNT PROTO from SRC SRC_PORT to DST DST_PORT OPTIONS

This section gives a summary of these keywords and their associated alternatives. This list does not include every potential option.

  • CMD: Each rule must start with ipfw add.

  • RULE NUMBER: Each rule is assigned a number between 1 and 65534. The number is used to denote the processing order of rules. Multiple rules with the same number may be applied in the order in which they were introduced.

  • SET NUMBER: Each rule is assigned a number between 0 and 31. Sets may be individually deactivated or reactivated, allowing for rapid addition or deletion of rule sets. If no SET NUMBER is provided, the rule is added to set 0.

  • ACTION: One of the following may be related to a rule. The stated action will be carried out if the packet meets the rule's selection criteria.

  • allow | accept | pass | permit: these terms are interchangeable and permit packets that fit the rule.

  • check-state: a function that compares the packet to the dynamic state database. Execute the action associated with the rule that created this dynamic rule if a match is detected; otherwise, go to the next rule. A check-state rule lacks criteria for selection. If there is no check-state rule in the ruleset, the first keep-state or limit rule in the dynamic rules table is examined.

  • count: updates the counts for all matching packets. The search proceeds under the following rule.

  • deny or drop: either cause packets that meet this criteria to be quietly discarded.

  • LOG AMOUNT: When a packet meets a rule with the log keyword, a message with the facility name SECURITY is recorded to syslogd. Logging happens only if the number of packets reported for this rule does not exceed the LOG AMOUNT value. If no LOG AMOUNT is supplied, the value of net.inet.ip.fw.verbose_limit is used as the limit. The logging limit is eliminated when the value is set to zero. Once the limit is reached, logging may be re-enabled by using ipfw resetlog to erase the logging counter or packet counter for that rule.

After all other packet matching requirements have been satisfied and prior to doing the final action on the packet, logging is performed. The administrator determines which rules permit login.

  • PROTO: This variable may be used to define any protocol listed in /etc/protocols.

  • SRC: The source address or a keyword representing the source address must follow the from keyword. An address may be represented by any, me, me6, or table followed by the number of a lookup table containing a list of addresses. Optionally, an IP address might be followed by its CIDR mask or subnet mask when specified.

  • SRC_PORT: An optional source port may be specified using the port number or name from /etc/services.

  • DST: The destination address or a keyword representing the destination address must follow the to keyword. The same keywords and locations used to define the source may also be used to specify the target.

  • DST_PORT: An optional destination port may be specified using the port number or name from /etc/services.

  • OPTIONS: Multiple keywords may be placed between the source and destination. As implied by its name, OPTIONS are optional. Commonly used parameters are in or out, which determine packet flow direction, icmptypes followed by the kind of ICMP message, and keep-state.

When a keep-state rule is met, the firewall generates a dynamic rule that matches bidirectional traffic with the same protocol between the source and destination addresses and ports.

The dynamic rules facility is susceptible to resource exhaustion as a result of an SYN-flood assault that would open an enormous number of dynamic rules. Use limit to defend against this sort of attack with IPFW. This option restricts the number of concurrent sessions by examining the open dynamic rules and calculating the number of occurrences of this rule and IP address. If this number exceeds the limit value, the packet is rejected.

How to Define IPFW Ruleset?

This section illustrates how to write /etc/ipfw.rules, an example stateful firewall ruleset script. In this example, all connection rules specify the direction using in or out. Additionally, they employ interface-name to identify the interface over which the packet is flowing.

tip

When designing or testing a firewall ruleset for the first time, consider temporarily configuring this variable:

net.inet.ip.fw.default_to_accept="1"

This modifies the default policy of ipfw to be more liberal than the default deny ip from any to any, making it somewhat more challenging to be locked out of the system immediately after a reboot.

The firewall script starts by identifying itself as a Bourne shell script and flushing any current rules. It then generates the cmd variable so that ipfw add is not required at the start of each rule. Additionally, it specifies the pif variable, which indicates the name of the Internet-connected interface.

!/bin/sh
# Flush out the list before we begin.
ipfw -q -f flush

# Set rules command prefix
cmd="ipfw -q add"
pif="dc0" # interface name of NIC attached to Internet

The first two rules permit all traffic on the trusted internal and loopback interfaces:

# Change xl0 to LAN NIC interface name
$cmd 00005 allow all from any to any via xl0

# No restrictions on Loopback Interface
$cmd 00010 allow all from any to any via lo0

The subsequent rule permits the packet to pass if it matches an existing item in the table of dynamic rules.

$cmd 00101 check-state

The following set of rules specifies which stateful connections internal systems may establish with Internet hosts:

# Allow access to public DNS
# Replace x.x.x.x with the IP address of a public DNS server
# and repeat for each DNS server in /etc/resolv.conf

$cmd 00110 allow tcp from any to x.x.x.x 53 out via $pif setup keep-state
$cmd 00111 allow udp from any to x.x.x.x 53 out via $pif keep-state



# Allow access to ISP's DHCP server for cable/DSL configurations.
# Use the first rule and check log for IP address.
# Then, uncomment the second rule, input the IP address, and delete the first rule

$cmd 00120 allow log udp from any to any 67 out via $pif keep-state
$cmd 00120 allow udp from any to x.x.x.x 67 out via $pif keep-state

# Allow outbound HTTP and HTTPS connections

$cmd 00200 allow tcp from any to any 80 out via $pif setup keep-state
$cmd 00220 allow tcp from any to any 443 out via $pif setup keep-state

# Allow outbound email connections
$cmd 00230 allow tcp from any to any 25 out via $pif setup keep-state
$cmd 00231 allow tcp from any to any 110 out via $pif setup keep-state

# Allow outbound ping
$cmd 00250 allow icmp from any to any out via $pif keep-state

# Allow outbound NTP
$cmd 00260 allow udp from any to any 123 out via $pif keep-state

# Allow outbound SSH
$cmd 00280 allow tcp from any to any 22 out via $pif setup keep-state

# deny and log all other outbound connections
$cmd 00299 deny log all from any to any out via $pif

The subsequent set of rules regulates Internet host connections to the internal network. It begins by rejecting packets that are often linked with assaults, and then expressly permits certain sorts of connections. All Internet-based permitted services have a rate restriction to avoid flooding.

# Deny all inbound traffic from non-routable reserved address spaces
$cmd 00300 deny all from 192.168.0.0/16 to any in via $pif RFC 1918 private IP
$cmd 00301 deny all from 172.16.0.0/12 to any in via $pif RFC 1918 private IP
$cmd 00302 deny all from 10.0.0.0/8 to any in via $pif RFC 1918 private IP
$cmd 00303 deny all from 127.0.0.0/8 to any in via $pif loopback
$cmd 00304 deny all from 0.0.0.0/8 to any in via $pif loopback
$cmd 00305 deny all from 169.254.0.0/16 to any in via $pif DHCP auto-config
$cmd 00306 deny all from 192.0.2.0/24 to any in via $pif reserved for docs
$cmd 00307 deny all from 204.152.64.0/23 to any in via $pif Sun cluster interconnect
$cmd 00308 deny all from 224.0.0.0/3 to any in via $pif Class D & E multicast

# Deny public pings
$cmd 00310 deny icmp from any to any in via $pif

# Deny ident
$cmd 00315 deny tcp from any to any 113 in via $pif

# Deny all Netbios services.
$cmd 00320 deny tcp from any to any 137 in via $pif
$cmd 00321 deny tcp from any to any 138 in via $pif
$cmd 00322 deny tcp from any to any 139 in via $pif
$cmd 00323 deny tcp from any to any 81 in via $pif

#Deny fragments
$cmd 00330 deny all from any to any frag in via $pif

#Deny ACK packets that did not match the dynamic rule table
$cmd 00332 deny tcp from any to any established in via $pif

#Allow traffic from ISP's DHCP server.
#Replace x.x.x.x with the same IP address used in rule 00120.
$cmd 00360 allow udp from any to x.x.x.x 67 in via $pif keep-state

#Allow HTTP connections to internal web server
$cmd 00400 allow tcp from any to me 80 in via $pif setup limit src-addr 2

#Allow inbound SSH connections
$cmd 00410 allow tcp from any to me 22 in via $pif setup limit src-addr 2

#Reject and log all other incoming connections
$cmd 00499 deny log all from any to any in via $pif

The last rule records all packets that meet none of the preceding rules:

#Everything else is denied and logged
$cmd 00999 deny log all from any to any

How to Implement NAT?

The IPFW firewall on FreeBSD has two NAT implementations:

  • the userland implementation natd(8)

  • the more modern in-kernel NAT implementation.

Both operate in combination with IPFW to translate network addresses. This may be used to offer a solution for Internet Connection Sharing, allowing several internal computers to connect to the Internet using a single public IP address.

In order to do this, the FreeBSD system connected to the Internet must serve as a gateway. This system must have two NICs, one of which must be linked to the Internet and the other to the local area network (LAN). According to RFC 1918, each computer connected to the LAN shall be issued an IP address in the private network space.

IPFW requires further settings in order to activate its in-kernel NAT capability. To enable NAT functionality in the kernel at boot time, the following must be configured in /etc/rc.conf:

gateway_enable="YES"
firewall_enable="YES"
firewall_nat_enable="YES"
tip

When firewall_nat_enable is set without firewall_enable, it has no impact and does nothing. Given that the in-kernel NAT solution is only compatible with IPFW, this is the case.

When the ruleset incorporates stateful rules, the placement of the NAT rule is crucial, and the skipto action is used. The skipto action needs a rule number to identify the target rule. The next example expands upon the firewall regulation presented in the preceding section. In order to setup the firewall for in-kernel NAT, it adds some extra entries and alters certain existing rules. It begins by adding variables representing the rule to skip to, the keep-state option, and a list of TCP ports that will be utilized to decrease the number of rules.

!/bin/sh
ipfw -q -f flush
cmd="ipfw -q add"
skip="skipto 1000"
pif=dc0
ks="keep-state"
good_tcpo="22,25,37,53,80,443,110"

Due to the design of libalias, a library built as a kernel module that provides the in-kernel NAT capability of IPFW, TCP segmentation offloading (TSO) must be disabled while using in-kernel NAT. TSO may be deactivated per network interface using ifconfig or globally using sysctl. To deactivate TSO globally, the following parameters must be specified in /etc/sysctl.conf:

net.inet.tcp.tso="0"

Additionally, a NAT instance will be established. Multiple NAT instances, each with its unique settings, are feasible. For this example, just one NAT instance, NAT instance number 1, is required. The configuration can include several options, such as:

  • if, which specifies the public interface,

  • same_ports, which ensures that aliased ports and local port numbers are mapped the same,

  • unreg_only, which will cause only unregistered (private) address spaces to be processed by the NAT instance,

  • reset, which will keep the NAT instance operational even if the public IP address of the IPFW machine changes.

Consult ipfw for a list of all parameters that may be supplied to a single NAT instance setup. When designing a stateful NATing firewall, it is required to permit reinsertion of translated packets for further processing. This is possible by deactivating the one_pass behavior at the beginning of the firewall script.

ipfw disable one_pass
ipfw -q nat 1 config if $pif same_ports unreg_only reset

The inbound NAT rule is put after the two rules that permit all traffic on the trusted and loopback interfaces, after the reassemble rule, and before the check-state rule. Importantly, the chosen rule number for this NAT rule, in this case, 100, must be more than the previous three rules and less than the check-state rule. In addition, due to the behavior of in-kernel NAT, it is recommended to insert a reassemble rule immediately before the initial NAT rule and after the rules that permit traffic on the trusted interface. When dealing with IPSEC/ESP/GRE tunneling traffic, IP fragmentation may occur, necessitating the reassembly of pieces prior to transmitting the whole packet to the in-kernel NAT facility.

tip

The reassemble rule was not required with userland natd since the IPFW divert action already reassembles packets before delivering them to the socket.

The NAT instance and rule number used in this example do not match those established by rc.firewall. rc.firewall is a FreeBSD script that sets up the default firewall rules.

$cmd 005 allow all from any to any via xl0 exclude LAN traffic
$cmd 010 allow all from any to any via lo0 exclude loopback traffic
$cmd 099 reass all from any to any in reassemble inbound packets
$cmd 100 nat 1 ip from any to any in via $pif NAT any inbound packets

#Allow the packet through if it has an existing entry in the dynamic rules table
$cmd 101 check-state

Replace the allow action in the outbound rules with the $skip variable to indicate that rule processing will resume at rule 1000. The seven TCP rules have been replaced with rule 125 since the $good_tcpo variable includes the seven permitted outbound ports.

tip

Remember that IPFW's performance is mostly controlled by the ruleset's size.

#Authorized outbound packets
$cmd 120 $skip udp from any to x.x.x.x 53 out via $pif $ks
$cmd 121 $skip udp from any to x.x.x.x 67 out via $pif $ks
$cmd 125 $skip tcp from any to any $good_tcpo out via $pif setup $ks
$cmd 130 $skip icmp from any to any out via $pif $ks

Except for the very last rule, which eliminates the through $pif in order to capture both incoming and outgoing rules, the inbound rules stay unchanged. The NAT rule must be placed after the previous outgoing rule, have a higher rule number, and the rule number must be referred to by the skipto action. In this ruleset, rule number 1000 is responsible for sending all packets to the defined NAT instance. The following rule permits any packet that has been processed by NAT to flow.

$cmd 999 deny log all from any to any
$cmd 1000 nat 1 ip from any to any out via $pif skipto location for outbound stateful rules
$cmd 1001 allow ip from any to any

In this example, rules 100, 101, 125, 1000, and 1001 govern the address translation of outgoing and incoming packets such that the dynamic state table entries always record the private LANIP address.

Consider an internal web browser that begins a new outward HTTP connection on port 80. When the first outbound packet reaches the firewall, it does not meet rule 100 due to its outward direction. Rule 101 is satisfied since this is the first packet and it has not yet been added to the dynamic state database. The packet ultimately satisfies rule 125 since it is outgoing on a permitted port and originates from the local area network. When this rule is met, two actions occur. First, the keep-state action appends an item to the dynamic state table, followed by the execution of the provided action, skipto rule 1000. The packet then traverses NAT before being delivered to the Internet. This packet is forwarded to the destination web server, which generates and returns a response packet. This new packet enters the ruleset at the top. It matches rule 100 and its destination IP address has been remapped to its original internal address. It is subsequently processed by the check-state rule, identified as an existing session in the database, and released to the LAN.

On the incoming side, the ruleset must reject invalid packets and only permit permitted services. When a packet meets an incoming rule, it is added to the dynamic state table and sent to the LAN. The response packet created by the check-state rule is identified as belonging to an existing session. The packet is subsequently sent to rule 1000 for NAT processing before being released to the outgoing interface.

tip

Although the transition from userland natd to in-kernel NAT may look smooth, there is a slight hitch. While firewall_nat_enable is set in /etc/rc.conf, IPFW will load the libalias.ko kernel module when running the GENERIC kernel. The libalias.ko kernel module only offers rudimentary NAT capability, but the userland implementation natd has complete NAT capabilities accessible without further settings in its userland library. All functionality pertains to the following kernel modules that may be loaded in addition to the usual libalias.ko kernel module using the kld list directive in /etc/rc.conf: alias_ftp.ko, alias_bbt.ko, skinny.ko, irc.ko, alias_pptp.ko, and alias_smedia.ko. Using the LIBALIAS options, the complete functionality of the userland library may be built into the kernel if a custom kernel is used.

How to Perform Port Redirection?

The disadvantage of NAT is that LAN clients cannot be accessed from the Internet. Clients on the LAN can establish outgoing connections to the rest of the world, but they cannot accept incoming connections. This is a problem if Internet services are attempted on one of the LAN client PCs. A straightforward workaround is to reroute certain Internet ports on the NAT server to a LAN client.

For example, client A hosts an IRC server while client B has a web server. For this to function correctly, incoming connections on ports 6667 (IRC) and 80 (HTTP) must be forwarded to the appropriate computers.

All setup for in-kernel NAT is performed in the NAT instance configuration. Consult ipfw for a complete list of settings that an in-kernel NAT instance may employ. The syntax of IPFW follows that of natd. The syntax for redirect_port is given below:

redirect_port proto targetIP:targetPORT[-targetPORT]
[aliasIP:]aliasPORT[-aliasPORT]
[remoteIP[:remotePORT[-remotePORT]]]

To configure the above example setup, the arguments should be as given below:

redirect_port tcp 192.168.0.2:6667 6667
redirect_port tcp 192.168.0.3:80 80

After adding these parameters to the setup of NAT instance 1 in the preceding ruleset, the TCP ports will be routed to the LAN client PCs running IRC and HTTP.

ipfw -q nat 1 config if $pif same_ports unreg_only reset \
redirect_port tcp 192.168.0.2:6667 6667 \
redirect_port tcp 192.168.0.3:80 80

With redirect_port, port ranges over individual ports may be specified. As an example, tcp 192.168.0.2:2000-3000. all connections received on ports 2000 to 3000 would be redirected to client A's ports 2000 to 3000.

How to Implement Address Redirection?

Address forwarding is advantageous when many IP addresses are available. Each LAN client may be allocated its own external IP address using ipfw, which then rewrites outgoing packets with the correct external IP address and redirects all incoming traffic on that IP address to the corresponding LAN client. Likewise known as static NAT. If IP addresses 12.0.0.1, 12.0.0.2, and 12.0.0.3 are available, for instance, 12.0.0.1 may be used as the ipfw machine's external IP address, whilst 12.0.0.2 and 12.0.0.3 are forwarded to LAN clients A and B.

The format for redirect_addr is as follows, where localIP is the internal IP address of the LAN client and publicIP is the exterior IP address.

redirect_addr localIP publicIP

In the example, these would be the arguments:

redirect_addr 192.168.0.2 12.0.0.2
redirect_addr 192.168.0.3 12.0.0.3

Like redirect_addr, these parameters are put in the configuration of a NAT instance. With address redirection, port redirection is unnecessary, since all data received on a certain IP address is rerouted.

External IP addresses must be active and aliased to the external interface on the ipfw host.

How to Implement Userspace NAT?

The userspace NAT implementation, natd, has more overhead than in-kernel NAT. In order for natd to translate packets, the packets must be transferred from the kernel to userspace and back, which incurs additional cost compared to in-kernel NAT.

The minimal setup for enabling the userspace NAT daemon natd at boot time is as follows in /etc/rc.conf. Where natd_interface is set to the name of the Internet-connected network interface card (NIC). The rc script of natd will automatically check for the presence of a dynamic IP address and adjust itself accordingly if one is detected.

gateway_enable="YES"
natd_enable="YES"
natd_interface="rl0"

In general, the ruleset described above for in-kernel NAT may also be utilized with natd. Exceptions include the setting of the in-kernel NAT instance (ipfw -q nat 1 config ...), which is not required in conjunction with reassemble rule 99 since its functionality is included into the divert action. Rule numbers 100 and 1000 will need minor modifications as detailed below:

$cmd 100 divert natd ip from any to any in via $pif
$cmd 1000 divert natd ip from any to any out via $pif

To setup port or address redirection, the same syntax is used as with in-kernel NAT. Nevertheless, unlike with in-kernel NAT, the setting of natd should now be specified in a configuration file rather than the ruleset script. This requires passing an additional flag through /etc/rc.conf that indicates the location of the configuration file.

natd_flags="-f /etc/natd.conf" is a flag that specifies the location of the natd configuration file.

The provided file must include a line-by-line list of configuration settings. Two examples are shown below, one per line:

redirect_port tcp 192.168.0.2:6667 6667
redirect_addr 192.168.0.3 12.0.0.3

How to Use ipfw Command?

ipfw may be used to manually add or remove a single rule from the active firewall while it is operating. The issue with this technique is that all modifications are lost upon system restart. Instead of storing all rules in a single file, it is advised to utilize this file to load the rules at boot time and to replace the currently running firewall rules anytime this file is modified.

ipfw is an efficient method for displaying the active firewall rules on the console screen. The IPFW accounting feature generates a dynamic counter for each rule that counts the number of packets that match the rule. In the process of testing a rule, listing the rule with its counter is one method for determining whether or not the rule is performing as planned.

  • To list all running IPFW rules sequentially, you may run the next command:
ipfw list
  • To display all active IPFW rules with the date and time of the rule's most recent match, you may run the next command:
ipfw -t record
  • Along with the rules themselves, the following example provides accounting information and the number of packets for rules that have been matched. The first column contains the rule number, then the total number of matched packets and bytes, and finally the rule itself.
ipfw list -a
  • To include dynamic IPFW rules alongside static rules, run the next command:
ipfw -d list
  • To display expired dynamic IPFW rules, you may run the following command:
ipfw -d -e list
  • To reset counts to zero, run the next command:
ipfw zero
  • To reset the counters for the IPFW rule containing the number NUM, you may run the next command:
ipfw zero NUM

How to Start Logging Firewall Messages?

Even with the logging feature enabled, IPFW will not automatically produce rule logging. The administrator of the firewall determines which rules in the ruleset will be recorded and adds the log keyword to those rules. Typically, only rule denials are documented. Customarily, the "ipfw default deny everything" rule is duplicated with the log keyword added as the last rule in the ruleset. Thus, it is feasible to see all packets that did not satisfy any of the ruleset's criteria.

Logging has both pros and cons. An overabundance of log data or a DoS attack might load the disk with log files if care is not taken. Not only are log messages sent to syslogd, but they are also shown on the root console screen, which quickly becomes unpleasant.

The IPFIREWALL_VERBOSE_LIMIT=5 kernel parameter restricts the amount of successive messages transmitted to syslogd about packets that fit a specific criteria. When this option is enabled in the kernel, the maximum number of successive messages for a certain rule is limited to the value supplied. There is no benefit to receiving 200 identical log messages. With this option set to five, five consecutive messages pertaining to a certain rule would be recorded to syslogd, and the remaining identical consecutive messages would be tallied and uploaded to syslogd with a statement similar to the one below:

last message repeated 94 times

By default, all packet messages are written to /var/log/security, as specified in /etc/syslog.conf.

How to Create IPFW Rule Script?

The majority of skilled IPFW users generate a file containing the rules and code them so that they may be executed as a script. The primary advantage of this method is that the firewall rules is updated in bulk without requiring a system restart. This approach is useful for testing new rules since the process is performed as often as necessary. As a script, symbolic substitution is used to replace commonly used values into several rules.

This sample script is compatible with the sh, csh, and tcsh shells syntax. The dollar sign ($) precedes symbolic replacement fields. Symbolic fields are not prefixed with $. The value used to fill the symbolic field must be surrounded by double quotation marks ("").

You may start your IPFW rules file as given below:

# start of example ipfw rules script

ipfw -q -f flush # Delete all rules

# Set defaults
oif="vtnet0" #out interface
odns="192.0.0.1" #ISP's DNS server IP address
cmd="ipfw -q add " #build rule prefix
ks="keep-state" #just too lazy to key this each time
$cmd 00500 check-state
$cmd 00502 deny all from any to any frag
$cmd 00501 deny tcp from any to any established
$cmd 00600 allow tcp from any to any 80 out via $oif setup $ks
$cmd 00610 allow tcp from any to $odns 53 out via $oif setup $ks
$cmd 00611 allow udp from any to $odns 53 out via $oif $ks
#End of example ipfw rules script

The emphasis of this example is how the symbolic substitution fields are filled, not the rules.

If the above example were located in /etc/ipfw.rules, the following command might be used to reload the rules:

sh /etc/ipfw.rules

/etc/ipfw.rules may be placed anywhere and can have any name.

The same result might be achieved by manually executing these commands:

ipfw -q -f flush
ipfw -q add check-state
ipfw -q add deny all from any to any frag
ipfw -q add deny tcp from any to any established
ipfw -q add allow tcp from any to any 80 out via tun0 setup keep-state
ipfw -q add allow tcp from any to 192.0.0.01 53 out via tun0 setup keep-state
ipfw -q add 00611 allow udp from any to 192.0.0.1 53 out via tun0 keep-state

What are the IPFW Ruleset Example for a Workstation?

An example IPFW ruleset that you may use to protect your FreeBSD workstation is given below:


################ Start of IPFW rules file ###############################
#!/bin/sh
# Flush out the list before we begin.
ipfw -q -f flush

# Set rules command prefix
cmd="ipfw -q add"
net="em0" # interface name of NIC attached to Internet
#################################################################
# No restrictions on the Loopback Interface
#################################################################

$cmd 00005 allow ip from any to any via lo0

#################################################################
# Permit the packet to pass if it has been previously added to the "dynamic" rules table by a "allow keep-state" declaration.
#################################################################

$cmd 00010 check-state :default

#################################################################
# Interface facing Public Internet (Outbound Section)
#Examine session start requests coming from behind the firewall on the private network or this gateway server bound for the public Internet.
#################################################################
# Allow out access to my ISP's Domain name server.
# x.x.x.x must be the IP address of your ISP.s DNS
# Dup these lines if your ISP has more than one DNS server
# Get the IP addresses from /etc/resolv.conf file
$cmd 00020 allow tcp from any to 192.168.1.1 53 out via $net setup keep-state
$cmd 00025 allow udp from any to 192.168.1.1 53 out via $net keep-state

# Allow out non-secure standard www function

$cmd 00030 allow tcp from any to any 80 out via $net setup keep-state

# Allow out secure www function https over TLS SSL

$cmd 00035 allow tcp from any to any 443 out via $net setup keep-state

# Allow out send and get email function (SMTP/POP3)

$cmd 00040 allow tcp from any to any 25 out via $net setup keep-state
$cmd 00041 allow tcp from any to any 110 out via $net setup keep-state

# Allow outbound ping

$cmd 00050 allow icmp from any to any out via $net keep-state

# Allow outbound SSH

$cmd 00060 allow tcp from any to any 22 out via $net setup keep-state

# Allow out whois

$cmd 00090 allow tcp from any to any 43 out via $net setup keep-state

# Deny and log everything else that is trying to get out.
# This rule enforces the block all by default logic.

$cmd 00099 deny log all from any to any out via $net

#################################################################
# Interface facing Public Internet (Inbound Section)
# Examine packets from the public Internet that are headed for this gateway server or the private network.
#################################################################
# Deny all inbound traffic from non-routable reserved address spaces

# $cmd 00300 deny all from 192.168.0.0/16 to any in via $net #RFC 1918 private IP
$cmd 00301 deny all from 172.16.0.0/12 to any in via $net #RFC 1918 private IP
$cmd 00302 deny all from 10.0.0.0/8 to any in via $net #RFC 1918 private IP
$cmd 00303 deny all from 127.0.0.0/8 to any in via $net #loopback
$cmd 00304 deny all from 0.0.0.0/8 to any in via $net #loopback
$cmd 00305 deny all from 169.254.0.0/16 to any in via $net #DHCP auto-config
$cmd 00306 deny all from 192.0.2.0/24 to any in via $net #reserved for docs
$cmd 00308 deny all from 224.0.0.0/3 to any in via $net #Class D & E multicast
$cmd 00309 deny all from any to ::1 in via $net
$cmd 00310 deny all from ::1 to any in via $net

# Deny public pings
$cmd 00320 deny icmp from any to any in via $net

# Deny ident
$cmd 00330 deny tcp from any to any 113 in via $pif

# Deny all Netbios services. 137=name, 138=datagram, 139=session
# Netbios is MS/Windows sharing services.
# Block MS/Windows hosts2 name server requests 81
$cmd 00340 deny tcp from any to any 137 in via $net
$cmd 00341 deny tcp from any to any 138 in via $net
$cmd 00342 deny tcp from any to any 139 in via $net
$cmd 00343 deny tcp from any to any 81 in via $net

# Deny any late arriving packets
$cmd 00340 deny all from any to any frag in via $net

# Deny ACK packets that did not match the dynamic rule table
$cmd 00345 deny tcp from any to any established in via $net

# Allow in standard www function because we have a web server
$cmd 00400 allow tcp from any to me 80 in via $net setup limit src-addr 2
$cmd 00401 allow tcp from any to me 443 in via $net setup limit src-addr 2

# Allow inbound SSH
# Rather than setting 'any', you should designate which IPs or IP groups may access the network.
$cmd 00450 allow tcp from any to me 22 in via $net setup keep-state

# Reject & Log all incoming connections from the outside
$cmd 00499 deny log all from any to any in via $net

# Everything else is denied by default
# deny and log all packets that fell through to see what they are
$cmd 00999 deny log all from any to any

################ End of IPFW rules file ###############################

This setting permits traffic from the 192.168.0.0 subnet, since this is the network where many home devices will be located. And this is required for services to function. The settings may be modified as follows if one desires to prohibit all LAN communication, including from the "trusted" local network, to prevent compromised workstations from spreading their malicious payloads so readily.

For blocking rules, remove the statement from the 00300 ruleset:

$cmd 00300 deny all from 192.168.0.0/16 to any in via $net #RFC 1918 private IP
$cmd 00301 deny all from 172.16.0.0/12 to any in via $net #RFC 1918 private IP

And if services such as SSH are required from a certain IP address, a rule may be added right above the blocking rule. Remember that IPFW follows the 'first match wins' principle, therefore the first rule that is matched is the one that is applied. Therefore, we must let SSH from the specified IP before barring everyone.

# Allow inbound SSH
$cmd 00250 allow tcp from 192.168.1.100 to me 22 in via $net setup keep-state

Additional services that are required for usage, such as:

# Allow in standard www function because I have an Apache HTTP server
$cmd 00260 allow tcp from any to me 80 in via $net setup limit src-addr 2
$cmd 00265 allow tcp from any to me 443 in via $net setup limit src-addr 2

Ping can also added as incoming packets.

# Allow public pings
$cmd 00270 allow icmp from any to any in via $net limit src-addr 2

These modifications are beneficial when the LAN cannot be trusted. Depending on the environment, demands, and personal preferences of the network or systems administrator, this may be excessive if the FreeBSD server is located in an office. However, this may be required for laptops used in coffee shops, on unreliable customer networks, and the like. Because UNIX allows changes to be quickly implemented by changing text files, you may feel compelled to work on your website's content while connected to one of these untrusted networks. You may modify the following rules to restrict access to just the material served by your local Apache HTTP server.

# Allow in standard www function because I have an Apache HTTP server
$cmd 00260 allow tcp from me to me 80 in via $net setup limit src-addr 1
$cmd 00265 allow tcp from me to me 443 in via $net setup limit src-addr 1

Adding these rule modifications is necessary, but you must also delete the previous ones, such as 00400, 00401, and 00450, so that only the IP addresses listed on the newly modified rules are permitted access, and the rest are banned.

What are the IPFW Ruleset Example for a Web Server?

An example IPFW ruleset that you may use to protect your FreeBSD Web server is given below:

################### Web Server Example ##################################
#!/bin/sh
# Flush out the list before we begin.
ipfw -q -f flush

# Set rules command prefix
cmd="ipfw -q add"
net="vtnet0" # interface name of NIC attached to Internet

#################################################################
# No restrictions on the Loopback Interface
#################################################################

$cmd 00005 allow ip from any to any via lo0

#################################################################
# Allow the packet through if it has previously been added to the
# the "dynamic" rules table by an �allow keep-state� statement.
#################################################################

$cmd 00010 check-state :default

#################################################################
# Interface facing Public Internet (Outbound Section)
#Examine session start requests coming from behind the firewall on the private network or this gateway server bound for the public Internet.
#################################################################

# Allow out non-secure standard www function
$cmd 00030 allow tcp from me to any 80 out via $net setup keep-state

# Allow out secure www function https over TLS SSL
$cmd 00035 allow tcp from me to any 443 out via $net setup keep-state

# Allow out send and get email function
# Uncomment if you need to use these ports to send email from Webserver
# $cmd 00040 allow tcp from me to any 25 out via $net setup keep-state
# $cmd 00041 allow tcp from me to any 110 out via $net setup keep-state

# Allow outbound ping
$cmd 00050 allow icmp from any to any out via $net keep-state

# Allow outbound SSH
# Uncomment this rule if you need to access other servers from this one
# $cmd 00060 allow tcp from any to any 22 out via $net setup keep-state
# Deny and log everything else that is trying to get out.
# This rule enforces the block all by default logic.
$cmd 00099 deny log all from any to any out via $net
#################################################################
# Interface facing Public Internet (Inbound Section)
# Examine packets from the public Internet that are headed for this gateway server or the private network.
#################################################################

# Deny all inbound traffic from non-routable reserved address spaces
$cmd 00300 deny all from 192.168.0.0/16 to any in via $net #RFC 1918 private IP
$cmd 00301 deny all from 172.16.0.0/12 to any in via $net #RFC 1918 private IP
$cmd 00302 deny all from 10.0.0.0/8 to any in via $net #RFC 1918 private IP
$cmd 00303 deny all from 127.0.0.0/8 to any in via $net #loopback
$cmd 00304 deny all from 0.0.0.0/8 to any in via $net #loopback
$cmd 00305 deny all from 169.254.0.0/16 to any in via $net #DHCP auto-config
$cmd 00306 deny all from 192.0.2.0/24 to any in via $net #reserved for docs
$cmd 00307 deny all from 204.152.64.0/23 to any in via $net #Sun cluster interconnect
$cmd 00308 deny all from 224.0.0.0/3 to any in via $net #Class D & E multicast
$cmd 00309 deny all from any to ::1 in via $net
$cmd 00310 deny all from ::1 to any in via $net

# Deny any late arriving packets
$cmd 00320 deny all from any to any frag in via $net

# Deny ACK packets that did not match the dynamic rule table
$cmd 00325 deny tcp from any to any established in via $net

# Allow in HTTP and HTTPS connections
$cmd 00330 allow tcp from any to me 80 in via $net setup limit src-addr 2
$cmd 00331 allow tcp from any to me 443 in via $net setup limit src-addr 2

# Deny ident
$cmd 00340 deny tcp from any to any 113 in via $pif

# Deny all Netbios services. 137=name, 138=datagram, 139=session
# Netbios is MS/Windows sharing services.
# Block MS/Windows hosts2 name server requests 81
$cmd 00350 deny tcp from any to any 137 in via $net
$cmd 00351 deny tcp from any to any 138 in via $net
$cmd 00352 deny tcp from any to any 139 in via $net
$cmd 00353 deny tcp from any to any 81 in via $net

# Allow inbound SSH
# Rather than setting 'any', you should designate which IPs or IP groups may access the network.
$cmd 00400 allow tcp from any to me 22 in via $net setup keep-state

# Deny public pings
# Uncomment the rule to deny pings from anywhere
# $cmd 00410 deny icmp from any to any in via $net
# Allow public pings from one source
$cmd 00415 allow icmp from any to any in via $net limit src-addr 1

# Reject & Log all incoming connections from the outside
$cmd 00499 deny log all from any to any in via $net

# Everything else is denied by default
# deny and log all packets that fell through to see what they are
$cmd 00999 deny log all from any to any

################ End of IPFW rules file ###############################

What are the IPFW Ruleset Example for a Gateway?

This is an ipfw configuration attempt for a cluster with a master node and many compute nodes. The master node serves as a gateway for the number of compute nodes in the local area network (192.168.0.0/24).

A jail executing on one of the compute nodes with the address 192.168.0.10 must be accessible from the outside (using redirect_addr 192.168.0.10 22.22.33.33). The IP address 22.22.33.33 is an alias for the WAN interface and is used to divert traffic to this prison.

Incoming traffic on port 2222 should be routed to port 22 on compute node 192.168.0.100.

Neither the master node nor the compute nodes should block outbound traffic.

Ensure that the value of net.inet.ip.fw.one_pass is 0.

cmd="/sbin/ipfw -q"
lanif="vtnet0"
wanif="vtnet1"

# flush existing rules
$cmd -f flush

# incoming nat
$cmd nat 1 config if $wanif \
reset \
same_ports \
unreg_only \
redirect_port tcp 192.168.0.100:22 2222 \
redirect_addr 192.168.0.10 22.22.33.33


# set up loopback
$cmd add allow all from any to any via lo0
$cmd add deny all from any to 127.0.0.0/8
$cmd add deny ip from 127.0.0.0/8 to any


# no restrictions on bridge0 or tun0
$cmd add allow all from any to any via bridge0
$cmd add allow all from any to any via tun0

# no restrictions on lanif
$cmd add allow all from any to any via $lanif

# catch spoofing from outside
$cmd add deny ip from any to any in not antispoof

# incoming traffic that needs nat
$cmd add nat 1 ip4 from any to me in recv $wanif


# this rule must be directly after incoming nat
$cmd add check-state

# outgoing traffic to block here
# allow all other outgoing connections by bypassing the outbound nat rule processing. 10000
$cmd add skipto 10000 tcp from any to any out xmit $wanif setup keep-state
$cmd add skipto 10000 udp from any to any out xmit $wanif keep-state

# incoming
$cmd add allow tcp from any to me 80,443,2222 in recv $wanif setup keep-state

# Rules that let packets to reach services listening on a LAN interface behind the NAT.
$cmd add skipto 10000 tcp from any to any 2222 in recv $wanif setup keep-state

# nat for outgoing packets
$cmd add 10000 nat 1 ip4 from any to any out xmit $wanif
#$cmd add 10000 nat 1 ip4 from 192.168.0.0/24 to any out

# allow anything else
$cmd add allow ip from any to any via $wanif

What are the IPFW Kernel Options?

IPFW may be installed as a kernel module; the following settings are created by default as modules or can be modified dynamically using tunables. Refer to Configuring the FreeBSD Kernel for information on statically compiling IPFW functionality into a custom kernel. For the custom kernel configuration file, the following options are available:

options IPFIREWALL  # enables IPFW
options IPFIREWALL_VERBOSE # enables logging for rules with log keyword to syslogd(8)
options IPFIREWALL_VERBOSE_LIMIT=5 # limits number of logged packets per-entry
options IPFIREWALL_DEFAULT_TO_ACCEPT # sets default policy to pass what is not explicitly denied
options IPFIREWALL_NAT # enables basic in-kernel NAT support
options LIBALIAS # enables full in-kernel NAT support
options IPFIREWALL_NAT64 # enables in-kernel NAT64 support
options IPFIREWALL_NPTV6 # enables in-kernel IPv6 NPT support
options IPFIREWALL_PMOD # enables protocols modification module support
options IPDIVERT # enables NAT through natd(8)