Ipfw-HOWTO

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

Summary.

1. General Introduction to Packet Filters
2. Enabling Ipfirewall(4)

2.1. rc.firewall and OPEN firewalls
2.2. Loading Rulesets

2.2.1. Pre-defined Firewall Types
2.2.2. Custom Firewall Types

3. Basic Ipfw(8) Rule Syntax

3.1. Listing Rules
3.2. Basic Commands and Actions
3.3. Specifying Protocols
3.4. Specifying the Source and Destination Addresses

3.4.1. Introduction to Bitmasks and Netmasks
3.4.2. Specifying Ports and Port Ranges

4. Advanced ipfw(8) Rule Syntax

4.1. "unreach" Action
4.2. Interface and Flow Control
4.3. Matching specific ICMP and TCP Packet Types

4.3.1. icmptypes
4.3.2. tcpflags, setup and established
4.3.3. ipoptions

4.4. Catching Fragmented Packets
4.5. UID and GID Based Filtering

5. Logging

5.1. Logging Issues
5.2. System Logging Configuration

5.2.1. Kernel Options
5.2.2. Configuring syslog(8) for Logging
5.2.3. Configuring newsyslog(8) for Log Rotation

5.3. Rule Logging Configuration

6. Introduction to Stateless and Stateful Filtering

6.1. Basic Stateful Configuration
6.2. Advanced Stateful Configuration
6.3. Anatomy of a Dynamic Rule

7. Traffic Shaping

7.1. Probability Matching
7.2. Dummynet

7.2.1. Pipe Queues
7.2.2. Pipe Masks
7.2.3. Pipe Packet Reinjection

8. Traffic Flow

Appendix A: Example Firewall Configurations

1. General Introduction to Packet Filters

Ipfw(8), the command frontend to ipfirewall(4), is the most common
IP filtering and traffic shaping facility in FreeBSD, and the one for
which FreeBSD is ready to handle by default (although the firewall itself
is disabled by default in the kernel). The logical operation if its rules
is similar to many other packet filters, with the exception of IPFilter,
whose default operation in handling rules is rather less efficient and
requires greater care to tune it (if you're familiar with it, note the
'quick' keyword required for ipf(8) not to traverse the entire ruleset
every time, etc). This is not to minimize the power of ipf(8), which has
its own advantages. The ultimate decision as to which packet filtering
facility one uses is a personal choice, unless one requires particular
functionality not available in one or the other, although, we will delve
into a rough comparison of the two later on.

As indicated above, ipfirewall(4) is a packeting filtering
firewall, which means that it acts by inspecting connexions on a
packet-by-packet basis, and as of FreeBSD 4.0, can also perform
rudimentary connexion oriented (stateful) filtering. On either count, it
acts by filtering packets through one or more network interfaces. This
behaviour is always transparent, that is, one will probably not be aware
that a firewall is present until something is blocked.

Firewall designs take on many shapes, but all can be broken down
into two general policies: open and closed. The open firewall approach
lets all packets through by default and only blocks that which is NOT
desired, while on the other hand, the closed approach blocks all packets
by default, and only lets through was IS desired. The latter allows for a
much tighter firewall configuration, but is much trickier to setup because
one can easily block out traffic that one's net requires, but one isn't
aware of.

2. Enabling Ipfirewall(4)

Ipfirewall(4) can be enabled in two ways: add the appropriate
options into your kernel configuration file and rebuild the kernel, or use
kldload(8) to dynamically load the basic ipfw.ko module into the kernel.
Either approach works well for enabling basic ipfirewall(4) operations,
however, only the former allows you to add additional configuration
options, such as logging.

To dynamically load ipfw one can simply issue the following
command:

(root@nu)~># kldload ipfw
(root@nu)~>#

Enabling ipfirewall(4) statically, the equivalent would be to add
the following line into your kernel configuration file:

options IPFIREWALL

Then, rebuilding and rebooting would enable ipfirewall(4) in the
kernel statically.

However, things are not as simple as they may seem; one can only
do things exactly as above when one is in front of the console. Additional
options are necessary to make the box usable. If you recall the discussion
above concerning firewall policies (open and closed), you will understand
why things can get very messy when you realize that the default firewall
policy is closed. As such, if you simply enable ipfirewall(4) without any
further actions, all network traffic will be blocked. This can become a
nightmare when enabling ipfirewall(4) remotely. Diasaster can be avoided,
although, it is never recommened to remotely enable ipfirewall(4), either
dynamically or statically.

If you wish to dynamically load ipfirewall(4) remotely, anyhow,
the follow command is recommended:

(root@nu)~># kldload ipfw && \
ipfw -q add 65000 allow all from any to any
(root@nu)~>#

This will automatically set a rule to allow all traffic instead of
blocking it, so you don't cut yourself off from your remote box. Likewise,
this is recommended on local boxes as well if they are connected to a
network and you don't want to lose your connexion.

Enabling ipfirewall(4) in the kernel statically in a remote box
takes a little extra work. Once one has added the kernel option specified
earlier, in the kernel configuration file, and rebuilt the kernel, one has
to then set at least two ipfirewall(4) options in rc.conf so that when the
box reboots, it will not be locked out by its own firewall with the
default-to-close policy.

firewall_enable="YES"
firewall_type="OPEN"

There are other firewall types defined in /etc/rc.firewall, but we
will concern ourselves with those later. For now, a default open policy is
good practice for the beginner. Alternatively, one can enable a default
open policy for ipfw(8) in the kernel, instead, by also adding the
following option into the kernel configuration file:

options IPFIREWALL_DEFAULT_TO_ACCEPT

In this case, the rc.conf options noted above will not be
*necessary* as we will not *need* to use /etc/rc.firewall to enable an
open policy because it will be enabled by default in the kernel. However,
even if one chooses to enable this in the kernel, it is good practice to
enable the /etc/rc.conf options anyhow, because later we will be using
/etc/rc.firewall to load custom rulesets. This also applies if one loads
the kernel dynamically, because eventually one will reboot, and the kernel
ipfw.ko module will not be automatically loaded. The /etc/rc.conf firewall
enabling functions allow for a convenient place to load the ipfw.ko
module.

With the additional options for ipfirewall(4) that are available
for enabling statically in the kernel, one soon realizes this is the
better method for enabling ipfirewall(4) in earlier versions of FreeBSD.
Aside from IPFIREWALL_DEFAULT_TO_ACCEPT, we also have:

options IPFIREWALL_VERBOSE
options IPFIREWALL_FORWARD
options IPFIREWALL_VERBOSE_LIMIT=#

IPFIREWALL_VERBOSE allows one to log traffic with ipfirewall(4) by
verbosely printing packet activity to syslogd(8) for every rule that has
the "log" keyword. This will be more clearly explained later.

IPFIREWALL_FORWARD allows one to forward packets to other hosts
with the 'fwd' command for ipfirewall(4), which will be dealt with in more
depth later.

IPFIREWALL_VERBOSE_LIMIT=# specifies a limit to logging packets
from a particular rule. With this, syslogd(8) and one's console (unless
disabled in /etc/syslog.conf, as will be shown later) will not be
overwhelmed with messages from ipfirewall(4) activity. The "#" is
replaced with the number of consecutive times one wishes to log a given
rule being activated.

For the most recent 4.x builds of FreeBSD, additional sysctl variables
have been added which makes enabling ipfirewall(4) options with sysctl more
practical. For the corresponding options enabled above in the kernel
configuration file, one can issue the following sysctl commands:

sysctl -w net.inet.ip.fw.verbose=1
sysctl -w net.inet.ip.fw.verbose_limit=#
sysctl -w net.inet.ip.forwarding=1

If one has IPv6 enabled, then the following rules will apply to
the corresponding firewall actions on IPv6 packets:

options IPV6FIREWALL
options IPV6FIREWALL_VERBOSE
options IPV6FIREWALL_VERBOSE_LIMIT=100
options IPV6FIREWALL_DEFAULT_TO_ACCEPT

OR

sysctl -w net.inet6.ip6.forwarding=1

Note that there are no sysctl variables for controlling inet6
ipfirewall(4) verbosity.

There are additional options associated with ipfirewall(4) that
can be enabled in the kernel, but will not be discussed at this moment
as they are not necessary for the basic firewall activities, and involve
more complex routing situations.

2.1. rc.firewall and OPEN firewalls

Whether one specifies a firewall type or not, once
firewall_enable="YES" is put into rc.conf and the system reboots,
/etc/rc.firewall is started from rc.conf, and the following two commands
are issued to ipfw(8) from within it:

${fwcmd} add 100 pass all from any to any via lo0
${fwcmd} add 200 deny all from any to 127.0.0.0/8

{fwcmd} is defined early on in the rc.firewall script, depending
on whether one specified that ipfw(8) run quietly (with the -q option) or
not. The first rule allows all traffic via the loopback device (lo0) to
pass, and the second rule blocks all traffic aimed at the localhost
(127.0.0.0) network. The first rule is necessary to allow local IPC
(inter-process communication) traffic, and the second rule is necessary so
that no external packets can ever be allowed to reach the localhost
address, which is the address on the loopback device, thus protecting
one's local traffic. If these rules are missing, and the firewall defaults
to a closed policy, you will see RPC(3) services break during startup,
among other things.

Next, when one specifies a firewall type of "OPEN" in rc.conf, the
following line in rc.firewall is activated:

${fwcmd} add 65000 pass all from any to any

This allows all external traffic in (except to the localhost), and
all internal traffic out. It fulfills the same task as enabling the
IPFIREWALL_DEFAULT_TO_ACCEPT in the kernel. If the open policy is enabled
in the kernel, then rule # 65535 will be automatically set to "allow ip
from any to any" instead of "deny ip from any to any," thus making rule #
65000 as set in rc.firewall for the open policy redundant. As such, it is
more apropos to indicate firewall type "UNKNOWN" if one enables an open
policy in the kernel, and does not wish to enable any other rules. For one
wishes to, for instance, simply enable the firewall to play with and see
how it work,s or simply block packets from a single host, then leaving the
firewall type open like this is sufficient and one can safely skip to
section 3.

However, if one wishes to use one of the pre-built rulesets in
rc.firewall, or create one's own custom rulesets, then neither options
(OPEN or UNKNOWN) are sufficient.

2.2. Loading Rulesets

There are two generally different things that can be done
concerning rulesets: use one of the pre-built ones in rc.firewall, or
create your own. The author recommends the latter for two reasons:

- You can customize the firewall rules to your liking and needs
without touching rc.firewall, which can be kept as a general reference.

- You will be forced harder to become familiarized with
ipfw(8) syntax, and as such, will become more comfortable with using
ipfw(8).

2.2.1. Pre-defined Firewall Types

Of course, the final decision is the administrator's. If one
wishes to use one of the pre-built rulesets, then one should read through
each of them in rc.firewall to become familiar with them before activating
any of them. Which ruleset is loaded is controlled by the
firewall_type="" option in rc.conf. Aside from the "OPEN" type, there are
three more predefined types available:

"CLIENT" This ruleset enables some basic rules. It allows all
traffic from the local network (which could be a private network behind
NAT) to itself. It blocks fragmented packets. It allows mail, DNS and NTP
packets in and out of the network, and does not allow any host outside the
private network to initiate TCP connexions with internal hosts. This would
already be impossible if the network was behind a vanilla NAT
configuration without any special proxies. This configuration will work
with both a default open or closed policy.

"SIMPLE" This ruleset is somewhat of any oxymoron - it is more
complex than the CLIENT configuration and requires some knowledge of
internet RFCs to make sense of at first glance. It will attempt to stop
spoofing by not allowing in external packets that have return addresses
the same as that of any internal host. It will block all
private-net-addressesed packets as defined by RFC 1918 from leaking in or
out, and will block all additional non-routable networks as defined in the
Manning internet draft
(http://www.ietf.org/internet-drafts/draft-manning-dsua-03.txt). It will
allow mail, www, DNS, NTP traffic and fragmented packets to pass through,
and will not only block attempts for connexions to be initiated by outside
hosts, like CLIENT, but will also log these attempts.

"CLOSED" This is technically not a rulest, because it does not
enable any rules. In fact, it does everything we've been warning not to
do: allow the default closed policy to take hold over all traffic (except
for traffic via lo0 as controlled by the rules explained earlier). It will
essentially disable all IP services, unless, one enabled the default open
policy in the kernel. Don't do this.

2.2.2. Custom Firewall Types

If one has decided to instead load one's oen ruleset(s), then one
should specify a file instead of one of the above types in the
firewall_type option in rc.conf. For instance, one might have the
following in their rc.conf:

firewall_enable="YES"
firewall_type="/etc/rc.firewall.rules"

This will allow one to define one's custom ipfirewall(4) ruleset
in /etc/rc.firewall.rules and have it run everytime during bootup.
Furthermore, if one wanted to have the rules start quietly, one could also
include the following in rc.conf:

firewall_quiet="YES"

The format of the ruleset in this file will be slightly different
from that which is encountered in rc.firewall. This is because rc.firewall
is an sh(1) script designed to run on its own. The ipfirewall(4) rule file
is there solely to be processed by ipfw(8). The primary difference will be
that where the shell variable {fwcmd} is invoked in rc.firewall, you will
see nothing corresponding to it invoked in the ipfirewall(4) rule file -
simply the rules on their own. Later on, when we construct a sample rule
file, we will go through this step by step.

3. Basic Ipfw(8) Rule Syntax

The rule syntax for ipfw(8) is pretty simple. Any rule can be
enabled from the console with the ipfw(8) command. Before we delve into
the rule syntax, however, we will quickly overview how to list the
ipfirewall(4) rules that have been activated.

3.1. Listing Rules

In its simplest form, we can list the rules with:

ipfw list

This will list all of the rules ordered by their rule number. To
also list the timestamp of the last moment a packet was matched on a
specific rule, the following command will accomplish this:

ipfw -t list

Finally, if we wish to list the packet count for matched rules
along with the rules themselves, we can issue the following:

ipfw -a list

OR

ipfw show

Both will display the same information in the same way. The first
column is the rule number, followed by the number of outgoing matched
packets, followed by the number of incoming matched packets, and finally
followed by the rule itself.

3.2. Basic Commands and Actions

We will now gradually go through the various options available for
the construction of a stateless filtering ruleset. In our examples we will
only state the rule not including the firewall control utility
(/sbin/ipfw) which must precede each one if we're manually setting these
rules from the command prompt; otherwise, if we're construction a rule
file to be passed to ipfw(8) we can use the sample lines as-is.

add 1000 allow all from any to any

This is the most benign example of a rule. We have already
encountered the same rule, except for the rule #, in section 2.1 when
discussing the OPEN firewall type. Note: the "pass" parameter used in that
rule, as written in rc.firewall, is synonym for "allow" and "permit" -
they are interchangable. In this rule, "all" packets from "any" source to
"any" destination are allowed to pass.

With ipfirewall(4), under most circumstances, the moment a rule
matches a particular packet, then ruleset examination halts there.

As we see, the simplest syntax for ipfw(4) is:

[] 
from to

The important commands are "add" and "delete." They are
self-explanatory. Rule numbers start count at 0 and end at 65535. The last
rule number is always defined by the default firewall policy in the
kernel. Even if you have an open policy defined in rc.conf, the last rule
will always reflect the kernel policy. This is fine because ruleset search
halts at the first matching rule (usually), so if the penultimate (second
to last) rule is number 65000 and defined by rc.firewall to allow all
packets, all packets will be allowed by default even if the last rule
(65535) defines a closed kernel firewall policy, because the last rule
will never be reached.

"action" can be one of a number of things:

"allow" | "pass" | "permit" - Any packets matching a rule with
this action are allowed to pass through the firewall, and search of
ruleset terminates.

"deny" | "drop" - Any packets matching a rule with this action are
silently blocked by the firewall and search of ruleset terminates.

add 1100 deny all from any to any

This would deny all packets from anywhere to anywhere.

"reset" - Any packets matching a rule with this action are blocked
and the ipfirewall(4) attempts to send a TCP reset (RST) notice to the
source. The ruleset search is terminated. Naturally, because this only
applies for TCP packets, the protocol must be "tcp," which matches only
TCP packets, and not "all," which matches all IP packets.

This action is sometimes useful for fooling network scanners that
would otherwise be able to detect a service behind a filtered port. On the
other hand, it can become a liability if one is flooded at a particular IP
and port for which ipfirewall(4) is set to reply with a RST packet, thus
doubling the usage of your bandwidth.

add 1200 reset tcp from any to any

This would deny all TCP packets from any to anywhere, and sent a
TCP RST responce packet to the source for each.

"count" - Any packets matching a rule with this action will prompt
ipfirewall(4) to increment its packet counter. Search through the ruleset
continues.

add 1300 count all from any to any

This would increment the packet counter for this rule, which
matches all packets coming from anywhere and going anywhere.

"skipto " - Any packets matching a rule with this action
will prompt ipfirewall(4) to continue its search through the ruleset
starting with the rule number equal to or greater than that which is
indicated by .

add 1400 skipto 1800 all from any to any

This would skip ruleset search to rule 1800 for any packets that
matched this rule in the first place.

3.3. Specifying Protocols

The "proto" is the protocol that is desired to be matched. The
keywords "ip" or "all" are catch-alls that match all protocols. The
commonly matched packet procotols are icmp, udp, and tcp, although, that
is by no means an exhaustive list. For the complete list of possible
protocols one can match, 'more /etc/protocols'.

3.4. Specifying the Source and Destination Addresses

The "source" and "destination" both take on the same format. They
can be a name, as defined in /etc/hosts or through DNS, an IP address, a
network address with bitmask (or netmask), and can be optionally followed
by one or more ports numbers if the protocol is udp or tcp. Using names or
IPs is straightforward, for instance:

add 1000 allow all from myhost to hishost
add 1100 deny all from 10.0.0.5 to any

The first rule will allow all traffic from "myhost" to "hishost,"
and the second rule will deny all traffic from 10.0.0.5 to any host. Once
a packet matches one of these, ruleset examination for that packet ceases,
and it is either passed or dropped, according to the action specified in
the rule it matched. This is a simple example of host-based filtering;
that is, of filtering according to which hosts a packet is destined for,
or arriving from. Network-based filtering works similarly, and the network
notation there utilizes either bitmasks or netmasks, for instance:

add 2000 allow all from 192.168.0.0/16 to any
add 2100 deny all from any to 10.0.0.0:255.0.0.0

The first rule allows all traffic from the network whose IP range
is 192.168.0.0-192.168.255.255. It uses a bitmask to indicate this. A
bitmask specifies how many bits from the network address (192.168.0.0)
should remain the same for matching packets. In this instance, the first
16 bits out of the 32 bit address will remain the same, and as the first
16 bits happen to be the first two octets, 192.168, all addresses whose
source addresses have the first two octets as 192.168 will be matched by
this rule. The second rule accomplishes a similar thing using netmasks.
The netmask indicate how many bits from the indicated network address
should be used for rule matching. In the above example, for rule two, the
netmask is 255.0.0.0. Its first octet is set with high bits; in other
words, the first 8 bits are set high. This indicates to ipfw(8) that only
packets with the first 8 bits of the network address (10.0.0.0) should be
matched. As the first 8 bits of the network address equal 10, then all
packets whose destination address have a 10 for the first octet (all
addresses between 10.0.0.0 and 10.255.255.255) will be matched by this
rule, and then dropped, as indicated by the action.

Rule matches can also be inverted with the "not" keyword. For
instance, in the following ipfw(8) commands, all packets not from
192.168.0.3 are dropped:

add 1000 deny all from not 192.168.0.3

3.4.1. Introduction to Bitmasks and Netmasks

The principle behind bitmasks and netmasks is simple, but often
confusing to new users, as it requires knowledge of binary numbers. It
makes far more sense if one worked with IP addresses in their binary form,
however, the confounding of decimal and binary concepts easily throws
newcomers off. For a quick reference, the following table illustrates what
network ranges are indicated by the corresponding bitmasks/netmasks up to
a default class C netmask and a couple quick examples of additional
bitmask/netmask entries for larger networks:

Bitmask Netmask Total IPs / Usable IPs

32 255.255.255.255 1 1
31 255.255.255.254 2 1
30 255.255.255.252 4 2
29 255.255.255.248 8 6
28 255.255.255.240 16 14
27 255.255.255.224 32 30
26 255.255.255.192 64 62
25 255.255.255.128 128 126
24 255.255.255.0 256 254

...

22 255.255.192.0 16320 16318
20 255.255.128.0 32768 32766
16 255.255.0.0 65536 65534
12 255.128.0.0 8.388608+e6 8.388606+e6
8 255.0.0.0 256^3 (256^3)-2
0 0.0.0.0 (all IPs) 256^4 (256^4)-2

As you can see, there is a definite pattern. The number if total
IPs always doubles, and the number of usable IPs is always total - 2. This
is because for every IP network/subnet there are two IPs reserved for the
network and broadcast addresses. The netmask's last octet starts at 255
and constantly decreases by multiples of 2, while the bitmask decreases by
multiples of 1, because in binary, each shift over to the left halves the
number, not divides by ten, like in the decimal number system. This same
pattern goes for all possible netmasks and bitmasks.

For a quick example in using the above table/pattern, let us
figure out the IP range for the subnet indicated by:

172.16.100.32/28

First we notice that the network address is 172.16.100.32, so we
know that the subnet begins with this address. Second, we notice that the
bitmask of 28 indicates that the last 4 bits (32-28) are set low and 28
bits set high. Because there are far less bits set low, it'll be easier to
compute this using them. Because each bit has two possible values, 2^4
indicates how many hosts are referenced by this bitmask. In this case, 16.
172.16.100.32 + 16 = 172.16.100.48, so the IP range is 172.16.100.32 -
172.16.100.48. Looking at the table, we see that 16 IPs correspond to a
bitmask of 28, so we could've used that to add to our network address and
avoided the other math, but it's so much better to know how to do it all
on your own - learn once and use always.

3.4.2. Specifying Ports and Port Ranges

One can also do port-based filtering along with host and
network-based filtering. Ports can be simply specified following the
address of either a source of destination. Port ranges can be specified
with a dash, be comma-separated, or use a bitmask to specify a range. Most
importantly, one can not use the "all" protocol when specifying ports
because not all protocols are port-sensitive.

add 1000 allow tcp from any to 172.16.0.5 25
add 1100 allow tcp from any to 172.16.0.5 1021-1023
add 1200 allow tcp from any to 172.16.0.5 21,22,23
add 1300 deny udp from any to 192.168.0.5 1024:8

In the first rule all TCP packets which are destined for port 25
on 172.16.0.5 are matched. In the second rule, all TCP packets which are
destined for ports 1021 through 1023, inclusive, on host 172.16.0.5 are
matched. In the third rule, all TCP packets which are destined for ports
21, 22 or 23 on host 172.15.0.5 are matched; and finally, in the fourth
rule, all UDP packets which are destined for ports 1024 through 1028 on
host 172.16.0.5 are matched. The last rule can be tricky as it uses a
bitmask on the port to make matches. The port 1024 contains 10 bits. The
bitmask indicates that all hosts matching the last 8 bits on that port,
destined for host 192.168.0.5, are matched. 10 - 8 gives one 2 bits which
can be anything. 2^2 = 4, so we have 4 port numbers, starting with 1024,
that can be the destination ports for packets aiming for that host, and
will result in a match.

Bitmasks for ports are rarely used and are even trickier than
bitmasks or netmasks for IP addresses, because the number of bits in a
port varies depending on the port specified before the mask. As such, it
is recommended that one stick to specifying port ranges with a dash ( -
) or separate the list of ports with commas.

4. Advanced ipfw(8) Rule Syntax

Although the above overview of ipfw(8) rule creation will cover
many of the simple scenarios, it sorely falls short for many more complex
situations, such as when a system has more than one network interface, one
wishes to make special responces to certain matches, or one wants more
control over the direction of traffic flow. We will first expand the
template for the ipfw(8) syntax to the follow:

[] [log [logamount ]]
from to [] []

Everything in brackets comprises new functionality we will discuss
in this section. We will also cover an additional "action" that was not
covered earlier. The syntax may suddenly seem daunting, but we will take
it slowly, and add each part as we go along, so as not to overwhelm you.

4.1. "unreach" Action

Firstly, we will introduce a new "action:"

"unreach " - Any packet which matches a rule with this
action will reply with an ICMP unreach code, after which time the ruleset
search will terminate. The possible unreach codes can be indicated by
number or name. The following is a quick list of ICMP unreach codes and
corresponding names. If you don't know what these are used for, you won't
have a reason to use them:

net 0 net-prohib 9
host 1 host-prohib 10
protocol 2 tosnet 11
port 3 toshost 12
needfrag 4 filter-prohib 13
srcfail 5 host-precedence 14
net-unknown 6 precedence-cutoff 15
host-unknown 7
isolated 8

4.2. Interface and Flow Control

One important functionality missing from the basic description of
ipfw(8) syntax in part 3 was interface and flow control; that is, the
ability to match packets according to which interface (if you have a
multihomed system) packets are moving through, and in which direction
they're moving. Up until now, direction was only loosely gauged by using
the source and destination addresses, but using just them to guesstimate
whether a packet is really coming or going when it moves through the
firewall is unreliable. If you wish to match packets only coming in or
going out, the keywords "in" and "out" can be used. Both correspond to the
"interface-spec" area of the syntax template given earlier, and therefore,
are placed near the end of every rule, prior to any posible options. For
instance, if we wish to match all packets coming in from anywhere and
going anywhere, we could have:

add 1000 allow all from any to any in

To match packets going through a particular interface, use the
"via" option followed by the interface name. For instance, if you are
using a PCI 3Com 3c59x, then your interface device will be xl0. To match
all packets coming in through that interface specifically, sourced from
anywhere and destined anywhere, the following would suffice:

add 1100 allow all from any to any in via xl0

Or, perhaps, if one has a multihomed system and wishes to match
any packets coming from anywhere and going anywhere at least moving
outside through *some* interface, he can do the following:

add 1200 allow all from any to any out via any

One will notice, when listing firewall rules, that when using
either "in" or "out" in combination with "via" the rule as it actually
looks does not contain a "via" but either "recv" or "xmit," depending on
whether an "in" or "out" was specified, respectively. For instance:

(root@nu)~># ipfw add 7000 allow all from any to any out via xl0
(root@nu)~># ipfw list | grep 7000
07000 allow ip from any to any out xmit xl0
(root@nu)~>#

Indeed, one can use either "recv" or "xmit" in place of "via" when using
"in" or "out," however, doing so is not required, and can add to some
confusion for the newcomer.

In all, these options allow a lot more control over network
traffic on a multihomed system and any system in general, by allowing one
to filter packets specifically coming into the firewall, exiting it, and
moving through a specified interface.

4.3. Matching specific ICMP and TCP Packet Types

ICMP, TCP, and IP packets come in various types. These types are
defined by the various flags that each of those packets sets. We can match
each of those types by using one of the following ipfw(8) options at the
end of our rules.

4.3.1. icmptypes

"icmptypes " - This will match the specified ICMP packet

, and conversely, if a '!' is put before the then all ICMP
packets that are not of this type are match. There are currently 15
different ICMP packet types that can be matched; each is specified by the
correct number. Ranges can be specified with dashes or be comma-separated.
The 15 possible ICMP types are:

0 - Echo Reply
3 - Destination Unreachable
4 - Source Quench
5 - Redirect
8 - Echo Request
9 - Router Advertisement
10 - Router Silicitation
11 - Time-to-Live Exceeded
12 - IP header bad
13 - Timestamp Request
14 - Timestamp Reply
15 - Information Request
16 - Information Reply
17 - Address Mask Request
18 - Address Mask Reply

If one is curious how these ICMP type, specifically type 3,
correspond with the Unreach codes that can be generated with the "unreach"
action, then, simply type 3 matches any of those Unreach codes. Filtering
ICMP packet types can be very useful for controlling ping; specifically,
for allowing internal hosts to ping out while blocking outside hosts from
pinging the gateway or any other host. The following three rules can
accomplish this easily:

1000 allow icmp from any to any out icmptypes 8
1100 allow icmp from any to any in icmptypes 0
1200 deny icmp from any to any in icmptypes 8

The first rule allows all icmp packets of type 8 (echo request) to
go out. The second rule allows all icmp packets of type 0 (echo reply) in,
and the final rule blocks all icmp packets of type 8 from entering. In
short, it allows echo requests to go out and echo replies to come in, but
blocks echo requests from coming in. As such, hosts behind the firewall
can ping anyone on the outside, while hosts on the outside can't ping
anyone behind the firewall. Naturally, this option can only be specified
when the indicated procotol is "icmp."

4.3.2. tcpflags, setup and established

"tcpflags " - This will match any TCP packet whose header
contains one of the following flags, or conversely, if '!' is presented
before the , match all TCP packets that do not have the set:

fin - Request for connexion termination
syn - Request for connexion initiation
rst - Reset Connexion
psh - Push Flag
ack - Acknowledgement
urg - Indicate Urgent OOB data

The SYN flag is of most interest as it is sent for initiation of
TCP connecions. Because it is so important, there is a separate
ipfw(8) option dedicated specifically for matching TCP packets with the
SYN flag set. This is called "setup." Naturally, this option can only be
specified when the indicated protocol is "tcp."

"setup" - Any rule containing this option will match any TCP
packet with the SYN flag set. For instance, if we wished to deny all
incoming TCP SYN packets, we coul issue the following:

add deny tcp from any to any in tcpflags syn

OR

add deny tcp from any to any in setup

On either count, the same action is performed: all TCP SYN packets
from "any" destined to "any" will be matched, and denied. As stated above
for "tcpflags", this option can only be used for rules when the indicated
protocol is "tcp."

"established" - Just as there is a special option for indicating
the request for TCP connexion initiation ("setup") there is a special
option for matching an already established TCP connexion. Because it is of
paramount important to easily control TCP connexions, "established" and
"setup" are available for quick rule formation. Given these options (or
their corresponding "tcpflags" incarnations) we can have some simplistic
control of TCP connexion activity. This is the very base of stateful
firewall functionality, which shall be dealt with in more detail later.

4.3.3. ipoptions

"ipoptions " - Finally, we can match for some specific IP
packet flags, namely, for SSRR (Strict Source Route), LSRR Loose Source
Route, RR Record Packet Route, and TS (Timestamp) flags. If you do not
know what any of these IP options do then you will not need to match for
them specifically.

4.4. Catching Fragmented Packets

Fragmented packets are matched with the "frag" ipfw(8) option.
Under most circumstances fragmented packets should be blocked. Receiving
many fragmented packets may indicate a DoS (Denial of Service) attack,
although FreeBSD and most other UNIX and UNIX-like systems will not be
phased by such attacks, Windows systems are often quite vulnerable. As
such, if one has one or more Windows systems on their network behind the
firewall, it is avisable to block fragmented packets.

When using the "frag" option to match [and block] fragmented
packets, there are a couple guidelines that must be followed. Firstly, one
can not use the "frag" option when also specifying "tcpflags." Secondly,
one can not use "frag" if also specifying any TCP or UDP ports. Given
these guidelines, we can easily issue a rule to block all incoming
fragmented packets:

add deny all from any to any in frag

4.5. UID and GID Based Filtering

One powerful function that IPFilter does not have is UID/GID-based
filtering. Ipfirewall(4) is able to filter packets according to the UID
and/or the GID of the process from which they are arriving. Naturally,
this can only be used to match packets which originate from processes on
the local host, however, it still offers a powerful functionality. The
options to be used are "uid" and "gid" followed by the UID/GID or name of
the user or group by which we will be filtering.

One potential use is to restrict the use of IP vhosts on a shell
server. If one wants to ensure that one or more vhosts can not be used by
anyone else, one can easily use UID-based filtering to block everyone's
but one's own traffic from that vhost:

add allow tcp from any to 172.16.0.10 in
add allow tcp from 172.16.0.10 to any out uid george
add deny tcp from 172.168.0.10 to any

With the above rules, only user george would be able to use the
aliased IP (IP vhost) 172.168.0.10 to establish TCP connexions to the
outside. No one else would be able to bind bots, IRC chat clients, or what
have you, to that IP and establish connexions with anything that required
TCP (most things). Likewise, the UDP protocol can be used with
UID/GID-based filtering, however, no other protocol can be.

Another possible use of UID/GID-based filtering would be to enable
bandwidth limiting on a per-user basis, as opposed to per-host or
per-network basis. As such, one could, for example, have a group of users
that all have fast FTP accounts, and only moderately limit their GID,
while on the other hand, have another group of shell users, who don't
require much bandwidth, and therefore significantly cap the bandwidth on
the GID they all belong to. Such GID-based bandwidth capping will be
illustrated later, once we cover the traffic shaping facilities of
ipfirewall(8).

For security purposes, one may wish to log the traffic of a
particular user, and here too UID-based filtering would come in handy. In
short, whenever one would wish to conduct firewall behaviour differently
for one or more users, UID/GID-based filtering would come in
handy. Because, in general, once a rule is matched, search through the
ruleset stops, UID/GID matching rules must be invoked before other
sweeping rules can match the traffic. So, when creating one's ruleset, one
must take this into careful account if one wishes to enable UID/GID-based
filtering.

5. Logging

5.1. Logging Issues

The virtues of logging are obvious. The ability to go back and see
what what connexions had been dropped, what addresses they came from,
where they were going, whether they were composed of many fragmented
packets (indicative of many DoS attacks), and so on gives you a
significant edge in both knowing where connexions are being made, by whom,
and when. Especially in the case of tracking down crackers and the like,
firewall logs may give one the all-important edge.

Logging also has a down-side. If you're not careful, you can both
lose yourself in the abundant data (for lack of proper log analyzing
strategies) and lose your HD space to growing log files. DoS attacks that
fill up HDs are one of the oldest around, and still just as dangerous to
the imprudent administrator. Although, they more often strike the poorly
configure email server, they are just as much a threat to any system
keeping extensive log data. It is important that you have sufficient HD
space and rotate logs prudently, so they do not grow indefinitely.

On the other hand, what many newcomers experience that once they
enable ipfirewall(4) logging their terminal is overwhelmed with messages
concerning packet activity. This is the result of any combination of:

- logging too many often-matched rules
- not disabling logging to console & root terminals (bad idea!)
- not controlling logging with the IPFIREWALL_VERBOSE_LIMIT

5.2. System Logging Configuration

5.2.1. Kernel Options

To Enable ipfirewall(4) logging in FreeBSD, one has to include at
least the following option in the kernel (don't forget to reboot after
you build the new kernel):

options IPFIREWALL_VERBOSE

In addition, one may wish to enable the following option:

options IPFIREWALL_VERBOSE_LIMIT=#

We have already mentioned this option early on when reviewing
firewall activation. This kernel option limits the number of consecutive
messages sent to the system logger, syslogd(8), concerning the activation
of a given rule. When this option is enabled in the kernel, the number of
consecutive messages concerning a particular connexion are capped to the
number specified. For instance, consider the following:

options IPFIREWALL_VERBOSE_LIMIT=10

With this, only 10 consecutive messages concerning a particular
connexion would be logged to syslogd(8), with the remainder being subsumed
under a general statement like:

Jan 29 03:26:55 myserver last message repeated 45 times

These messages are generally logged to /var/log/messages in
addition to the console. If one wishes to modify this behaviour, one will
have to edit the /etc/syslog.conf. syslog.conf(5) offers considerable
flixibility in how syslogd(8) will deal with system messages. The limit
which one defines for IPFIREWALL_VERBOSE_LIMIT is up to the administrator,
but values above 10 or so on a busy server may still sorely populate the
console. On the other hand, if one wishes to disable kernel messages to
the console altogether and log everything to a separate file (not the
default /var/log/messages) this can be done also. Indeed, in this
configuration, one need not specify IPFIREWALL_VERBOSE_LIMIT at all if one
is certain that HD space is sufficient and log rotation will keep things
tidy.

5.2.2. Configuring syslog(8) for Logging

One can setup syslogd(8) to log ipfirewall(4) messages to a
separate file in three relatively simple steps:

1) Create your log file, and alternative, log directory. For
instance, if you wish to have all of your ipfirewall(4) logs in
/var/log/ipfw/ , create the directory, and then inside, create the log
file. You may call it ipfw.log, for instance. You can easily do this with
the touch(1) command:

(root@nu)~># mkdir /var/log/ipfw && touch /var/log/ipfw/ipfw.log
(root@nu)~>#

Make sure the directory and file are not world-readable so other
users can not poke around.

2) Configure syslog.conf(5) to send ipfirewall(4) messages to
/var/log/ipfw/ipfw.log. In its most basic configuration, this can be done
easily by added the following two lines to the bottom of syslog.conf(5).

!ipfw
*.* /var/log/ipfw/ipfw.log

Make sure that you use tabs and not spaces in this file. Although
tabs are not required in FreeBSD for syslog.conf, it is good practice in
case you work with other UNIX systems, which may not accept spaces in
their syslog.conf(5). So, eventhough you can ignore the scary message at
the top of /etc/syslog.conf, it is wiser to obide by it for the sake of
maintaining a safe habit.

One should note that the "last messages repeated #" messages will be
logged to this file and also any other file that is specified in
syslog.conf(5) to log *.err, kern.debug, *.notice messages, and so on. In
addition, the console will always receive these messages as defined by:

*.err;kern.debug;auth.notice;mail.crit /dev/console

Remember, the the console is ttyv0 (ALT + F1). Messaging to
virtual and pseudo terminals will behave differently. Virtual and psudeo
terminals are controlled by the following lines in the default
syslog.conf(5):

*.err root
*.notice;news.err root
*.alert root
*.emerg *

Both lines that contain *.err and *.notice will log kernel
messages concerning rule matches to the terminal on which the user
specified to the right is logged in. Notice that this user is root; the
best way to avoid annoying messages is to not log in as root constantly.
Logging in as root and su'ing will not save you from the messages, either.
It is strongly recommended that these lines not be commented out - having
such messages logged to any terminal in which root is logged in is very
useful as a quick notification of something possibly wrong happening.
Indeed, you may wish to log these or other messages to other UIDs that you
use often. Instead setup your rules appropriately so only important rule
matches are logged and log in with your personal account, not root. Keep a
spare virtual terminal logged in as root and check it occasionally.

To sum up, when dealing with syslog.conf(5) configuration and
terminal notification:

- Add the two aforementioned lines in syslog.conf(5) to log rule
matching messages to a separate file for easy examination at a later time.
- Do not worry about root terminal messages, instead do not do
your normal activity as root. This is a good all-around piece of security
advice. Instead, keep a spare terminal logged in as root and check it
occasionally.

3) Send a Hangup signal to the syslogd(8) process. The easiest way
is with the following command:

(root@nu)~># killall -HUP syslogd
(root@nu)~>#

5.2.3. Configuring newsyslog(8) for Log Rotation

Now that ipfirewall(4) logging is enabled, one should consider
configuring newsyslog.conf(5) so that newsyslog(8) rotates your
ipfirewall(4) logs; or, alternatively, use some other log rotation
mechanism. For a full explanation of all possibler configuration options
one should consult the newsyslog.conf(5) man page, however, the following
entry should suffice for most occasions (in this example, we are using the
example log file name as was presented earlier):

/var/log/ipfw/ipfw.log 600 10 * $W0D2 Z

This entry can be added to the bottom of newsyslog.conf(5). The
first part is self-explanatory. The second comprises the permission bits
for the rotated file The third the number of log files to keep rotating
back until the oldest is deleted. The fourth is a wildcard instead of a
specific size which to use as a signal to rotate the files. The fifth part
is the time at which to rotate the logs, and the sixth is a special
option. As one may have already surmised, log rotation can be done by one
of two criteria: size, and time/date. We can specify to rotate a certain
log based on when it reaches a particular size, or instead, based on what
time/date it is. In the above entry, we used the time/date criterion. We
specified to rotate the log at 2 AM every sunday morning, and then to
gzip(1) the archive (the Z option), only allowing for a 10 archive history
(10 weeks). If one wishes to hold their logs longer than this, one need
only change the third parameter from 10 to whatever they like.

Once newsyslog.conf(5) has been configured, log rotation is all
done. Because newsyslog(8) is a program run from cron(8) there is no
daemon that needs to be sent the Hangup signal as was necessary for
syslogd(8). There are other ways of enabling log rotation. One can write
and run one's own log-rotation script, or use a friend's that you've been
impressed with. Either way, it is important that on such potentially large
log files, there be some method of archiving and controlling their growth.

5.3. Rule Logging Configuration

Once the system is fully configured to handle the ipfirewall(4)
logging, we can begin specifying which rules, when matched, should be
logged. There are two simple parameters to be used in conjunction with
rule formation to enable logging for the given rule; they are:

"log" - Logs everytime the rule containing this keyword is matched
by a packet. The keyword, if present, must follow the "action". Make sure
you do NOT put this in wide sweeping rules such as:

add 0500 allow log all from any to any

Unless there is some extensive filtering occuring prior to this
rule, most traffic will be matched to it and one's log files will grow
very large very fast. On the other hand, it could well be safe to enable
logging on a sweeping deny rule such as:

add 65000 deny log all from any to any

This rule is much safer because it is both near the end (rule #
65000 versus 500) and in rulesets that fulfill a closed firewall policy,
there are generally many rules prior to the last deny rule that allow
through important traffic, which should comprise much of the total traffic
experienced, except in unusual circumstances - although it can still be
risky. Take careful consideration into which rules should be logged and
which shouldn't.

"logamount " - This parameter, following the "log"
parameter, specifies the maximum number of matches that a rule can
experience before logging halts. This gives the administrator some extra
control over logging. If, for instance, he enables 10000 matches for a
given rule, and then reset the counter once a day, he can alleviate
potentially large logs if someone tries to flood the server and is blocked
by the given rule. After 10000 matches, the logging for that rule will
stop, and the flooder's attack will not swell the log file. Alternatively,
one may wish to log *everything* and not use this parameter. In FreeBSD
4.x, 3.4+, and 2.2.x this is allowed, however, in earlier versions of
FreeBSD 3.x, if this keyword is not used, a default "logamount" is set to
10.

When rules are logged, the following information will be saved:

- Date & Time
- Rule number
- Action
- Source & Destination IP addresses
- Source & Destination Port numbers
- Direction flow
- Device over which this occured

For instance, a firewall log line may look like this:

Jun 12 13:55:59 mybox1 /kernel: ipfw: 65000 Deny TCP
172.16.0.1:62307 192.168.0.1:23 in via xl0

6. Introduction to Stateless and Stateful Filtering

Stateful and stateless filtering are two terms often encountered
in debates between proponents of ipfilter and ipfirewall(4). Stateless
filtering treats each packet going through it as an individual that has no
association with the other traffic going through the given interface. This
type of filtering is easier to implement and can be used effectively to:

- filter corrupt (fragmented) packets
- filter particular protocol packets (icmp, udp, igmp, etc)
- do host-based filtering (filtering according to where a packet
is destined, or where it came from)

Stateful filtering is more complex to implement. It treats traffic
not as an aggregate of individual, independent packets, but as composed of
connexions. All communication via any of the protocols uses sequence
numbers to indicate in which order packets should be read on a socket(2).
A stateful firewall would be aware of these sequence numbers. Connexion
oriented protocols such as TCP also have special packets which indicate
connexion initation (SYN) and termination (FIN), and a stateful firewall
would also be aware of these. In short, it would:

- know what state a connexion is in
- be able to determine if a connexion is following valid
proceedure, or is breaking the rules, and would be able to filter the
packets in these connections accordingly.

Stateful firewalls create dynamic rules for live connexions, and
clear out these rules when the connexions time out. All of this allows for
a more intelligent awareness of the higher level activity of network
traffic by the firewall. On the downside, statefull firewalls are unable
to treat each packet individually, because special dynamic rules are built
to allow entire connexions through, precluding the examination of packets
in those rules except in the context of whether they are behaving properly
in the overall connexion. As such, it is wise to mix and match stateful
and stateless rules in any firewall configuration, so one is able to
benefit from both.

Almost all of the example rules that have been given so far have
been stateless. The only exceptions are the rules concerning the option
"tcpflags," "setup," and "established" which allow one to check the state
of a TCP connexion. Indeed a combination of these will be used in the
first stateful ruleset examples shortly. Using these options to make
primitive stateful rulesets has been functionality that has been available
in ipfirewall(4) for a long time, however, because of its very limited
stateful capabilities, ipfirewall(4) has long been regarded as a stateless
firewall, with IPFilter the stateful alternative. Starting with FreeBSD
4.0, ipfirewall(4) has been enabled with more extensive stateful
functionality, with more promised to come.

6.1. Basic Stateful Configuration

For our first example, we will use the older, more basic, stateful
capabilities of ipfirewall(4). Many people, following after the example in
~rc.firewall, where all of the pre-defined rulesets use this most basic of
stateful functionality, make heavy use of the "setup" and "established"
keywords for controlling TCP connexions. Indeed, this can only be used to
control TCP connexions in a stateful manner, showing its limiting nature
on at least one count. In our example, we will create a simple stateful
firewall rulest that only allows ssh connexions through:

add 1000 allow tcp from any to any established
add 2000 allow tcp from any to any 22 in setup

Presuming a closed firewall policy (firewall_type is NOT "OPEN"
and kernel does NOT have IPFIREWALL_DEFAULT_TO_ACCEPT set) the above two
lines will first allow all established TCP connexions to pass through.
Specifically, any packets part of an established TCP connexion will match
rule 1000 and then ruleset searching will cease for those packets. If, on
the other hand, any packet is not part of an established TCP connexion,
rule 1000 will not match it, and ruleset analysis will move to rule 2000,
where, if the packet is a SYN TCP packet destined for port 22 (port for
ssh), the rule will match it and and let pass. Subsequent packets for that
connexion will be passed with rule 1000. In this manner, the above rules
are stateful as they are aware of the existence of a TCP connexion at
large and not just individual packets. One could have easily accomplished
the same with stateless rules; for instance:

add 1000 allow tcp from any to any out
add 2000 allow tcp from any to any 22 in

In this example, all packets moving out from any to anywhere are
allowed to pass through the firewall, and all packets moving to port 22 of
anywhere from anywhere in, are passed through. In this case, the rules are
not aware of the TCP connexions - they do not test for initiation and
established TCP connexions, but instead, let all TCP packets move out, no
matter what they are, and let all TCP packets in to port 22, no matter
what they are.

This is the essence of stateful behaviour in ipfirewall(4) using
the "setup" and "established" flags: pass through TCP setup requests to
specified addresses:ports and then let these established connexions
through.

Let us look at a more involved example that handles ssh, email,
FTP, and DNS queries for network 172.16.0.0/27:

add 1000 allow tcp from any to any established
add 2000 allow tcp from any to 172.16.0.0/27 21,22,25 setup
add 3000 allow udp from 172.16.0.0/27 to any 53
add 3100 allow udp from any 53 to 172.16.0.0/27

In this example, setup of TCP connexions is allowed for ports 21,
22, 25 - FTP, ssh, and email respectively, when packets are destined for
the 172.16.0.0 network. Afterwards, all established TCP connexiond are
allowed to pass. Rules 3000 and 3100 pass UDP packets to port 53 on other
hosts and allow UDP packets from port 53 on other hosts to pass in through
the firewall. Both of these rules are stateless. Port 53 is the port on
which DNS servers run. Rule 1000 must have "from any to any" because TCP
packets for established connexions must be allowed to both originate from
anywhere and to arrive at anywhere.

If one was to write a similar ruleset which was completely
stateless, one would have to keep most TCP ports between 1024 and 65000
open for FTP connexions. FTP is definitely a wild protocol as it randomly
binds to non-reserved ports across a wide range. Allowing active FTP
through a firewall is always difficult; only stateful firewalls offer a
really clean approach to it, without opening massive ranges of
ports. Most notably the shortcoming of opening the ports with a stateless
firewall is that anyone can attempt to connect to any port within that
range. With our setup, only TCP connexions that had been initiated through
rule 2000 can be let through with the "established" option in rule 1000,
effectively limiting random and uncontrolled connexions to the wide range
of ports needed for clean active FTP operation.

6.2. Advanced Stateful Configuration

As has been stated before, stateful firewall configuration with
just the "setup" and "established" options is very limiting. Aside from
only allowing stateful control over TCP connexions, this stateful control
is simplistic at best. Starting with FreeBSD 4.0 the stateful capabilities
of ipfirewall(4) have been greatly augmented. Now, ipfirewall(4) can be
configured for stateful handling of TCP, UDP, ICMP and other packets types
with use of dynamic rules.

Dynamic rules are another new addition to ipfirewall(4) in FreeBSD
4.0. Dynamic rules, as the name suggests, are dynamically generated for
individual connexions. Each dynamic rule, after lack of use past a period
of time, times out. The specific timeout period for TCP connexions can be
controlled by several(8) sysctl variables. This allows control of not only
connexion initiation, but connexion termination; that is, in a manner of
speaking, a ruleset can be designed which is aware of the beginning end
ending of a particular connexion, and that adjusts itself accordingly.

One option and one command are used to control this advanced
stateful behaviour:

"set-state" - Any rule with this option initiates a dynamic rule
whenever it is matched by a packet.

"check-state" - This command allows the firewall search to first
check the dynamic rules. If a rule with this command is missing from all
of the rules in a ruleset, then dynamic rules are checked at the first
instance of the "set-state" option. If a rule with this command is a
match, then the search stops, otherwise the search continues.

Let us return to our earlier example of a ruleset designed to only
allow ssh connexions through, only this time using the advanced stateful
ipfirewall(4) facilities:

add 1000 check-state
add 2000 allow tcp from any to any 22 in setup keep-state

Remember, these rules presume that your firewall policy is a
closed one (the default rule is to deny everything). In the above rules,
the first rule prompts ipfirewall(4) to check the dynamic rules. If the
immediate packet passing through the firewall do not belong to any already
established connexions, that is, they do not match any of the dynamic
rules, then the search continues to rule 2000, where if the packet is a
TCP SYN, then "keep-state" prompts the creation of a dynamic
rule. Subsequent packets through that connexion will be passed through the
dynamic rule, which is checked for with rule 1000. All other packets would
be rejected by the default deny rule.

Eventhough, we have already demonstrated to do this with the older
stateful approach and also a stateless approach, this new approach using
the "set-state" option and "check-state" command has a distinct advantage
above the other approaches. In the older stateful approach, the option
"established" matched any TCP packets from an established TCP connexion,
even if they were spoofed and not from any current legitimate TCP
connexion. This new approach precludes such an occurance. Each dynamic
rule is for a specific connexion between two hosts and their respective
ports. Spoofed TCP packets will be able to spoof the source and
destination IP addresses, but not the correct ports (unless they're very
lucky) on which a legitimate TCP connexion is being maintained, and thus,
rule 1000 with the "check-state" command would fail, after which rule 2000
would fail unless the spoofed packet was a TCP SYN packet, and the packet
would fall to the final deny rule.

In summary, the criteria with which packets are tested against
when trying to match a dynamic rule for them are:

- Protocol
- Source IP & Port
- Destination IP & Port
- Whether the rule has timed out

As noted before, the dynamic rules time out after lack of use over
a particular period of time. Depending on how a dynamic rule is used, the
rule's lifetime is given a particular period. The timeout values
for the different types of rule uses can be viewed by printing the correct
sysctl variables; moreover, one can modify these values. Here is the list
of sysctl variables and their default values:

(root@nu)~># sysctl -a | grep 'dyn.*lifetime'
net.inet.ip.fw.dyn_ack_lifetime: 300
net.inet.ip.fw.dyn_syn_lifetime: 20
net.inet.ip.fw.dyn_fin_lifetime: 20
net.inet.ip.fw.dyn_rst_lifetime: 5
net.inet.ip.fw.dyn_short_lifetime: 5
(root@nu)~>#

The first sysctl variable indicates that the default lifetime
value to which a dynamic rule used by a TCP ACK packet is immediately set
to upon use is 300 seconds. The second variable indicates that the
lifetime value to which a dynamic rule used by a TCP SYN packet is
immediately set to upon use is 20 seconds. The third variable indicates
that 20 seconds is also the value to which the lifetime of a dynamic rule
used by a TCP FIN packet is set to. Dynamic rules used both by TCP RST
packets and any other packets (UDP, ICMP, etc) are set with a 5 second
lifetime, as indicated by the last two sysctl variables.

Let's use an example to clarify how all of this works in action:

1) A legitimate TCP connexion request from host 172.16.0.1 on port
1234 is sent to port 22 on a server 192.168.0.1 behind the firewall. This
connexion request consists of a TCP synchronization packet (TCP SYN).

2) Rule 1000 on the firewall has the firewall check the dynamic
rules and finds none that correspond to any sort of TCP packets coming
from 172.15.0.1:1234 and destined for 192.168.0.1:22.

3) Rule 2000 is checked is a match. The "keep-state" has a dynamic
rule created for a TCP connexion from 172.16.0.1:1234 to 192.168.0.1:22
with the lifetime of 20 seconds (default for TCP SYN packets).

4) Within a second a TCP ACK packet is sent to 192.168.0.1:22 in
responce to the TCP ACK sent from the server to the client confirming the
TCP connexion request.

5) As the packet encounters the firewall, rule 1000 once again has
the dynamic rules checked. This time a dynamic rule still within its
lifetime exists which matches the protocol source IP:port and destination
IP:port, so it is let through the dynamic rule, and the packet passes
through the firewall safely.

6) a spoofed TCP ACK packet from an attacker enters the
firewall which under normal circumstances could knock out the networking
capability of certain unpatched Windows machines behind the firewall.

7) Rule 1000 checks the dynamic rules and finds that the spoofed
packet has a destination IP:port that is an IP:port of an existing dynamic
rule, its return IP:port does not match that of any rule (because it was
randomly generated by the attacker). So, rule 1000 fails and checking
moves to rule 2000.

8) Rule 2000 finds the packet not to be a TCP SYN, so the
check-state option is not run as the rule is not matched.

9) Consequently, the packet falls to the default deny rule and is
lost.

Were this had been a stateful ruleset by the older method, the
spoofed TCP packet would've been accepted with the first rule containing
the "established" option because in our original example the rule accepted
all TCP packets destined for a particular network behind the firewall that
had at least the ACK flag set, and the spoofed packet indeed filfulled
these criteria. This is a first, but powerful reason for why the advanced
stateful operations of ipfirewall(4) are extremely useful, and what
advantages IPFilter has in this regard also.

In our above illustration, it would have gone differently had the
spoofed packet been a TCP SYN packet, but before we describe why, let's
examine a popular use of spoofed TCP SYN packets in computer
attacks, also known as SYN floods.

Spoofed TCP SYN packets are often used in network attacks. The
most common of such attack consists of sending a flood of TCP SYN packets
(SYN floods) to a host so that the entire connexion queue in the prey's
kernel is saturated with open connexion attempts, thus denying the
activation of new legitimate connexions. Eventhough, the FreeBSD TCP/IP
stack is designed to randomly drop TCP connexion attempts from its queue
past a particular threshold of maximum tolerated TCP connexion attempts,
this type of attack can be devastating. If the TCP SYN packets come fast
enough they will initiate fake connexions faster than they can be dropped
low enough to free sufficient room for all legitimate connexions. The
primary variables are speed of the attack and speed of the box in its
ability to process packets moving through its TCP/IP stack. Fortunately,
FreeBSD's TCP/IP stack is faster than that of Linux and many other
systesm, however, it's not always fast enough.

Just as in our previous illustration of spoofed TCP ACK packet
handling with the advanced stateful functionality, randomly spoofed TCP
packets of any sort, SYN or ACK, would be unsuccessful because each new
packet with a random source IP:port would be unable to find a dynamic rule
to pass through. However, if the original TCP SYN and all subsequently
spoofed TCP packets maintain the same source IP:port, then the stream
could open and maintain a TCP connexion based on invalid source:port
information. Also, one must be very careful about too many dynamic rules
being opened by spoofed TCP SYN packets. Although, the TCP/IP stack
wouldn't be overwhelmed by too many connexion attempts, the firewall
dynamic rule queue could be saturated. One way to try to avoid this is to
shorten the lifetime of dynamic rules started by TCP SYN packets with the
appropriate sysctl mentioned earlier and extending the maximum number of
dynamic rules, which is done via another sysctl:

net.inet.ip.fw.dyn_max: 1000 (default)

To get a quick count of how many dynamic rules exist, one
should print the value of the sysctl variable:

net.inet.ip.fw.dyn_count

One sure way to avoid the possibility of spoofed packets from
using up all of your dynamic rules is to disallow external hosts from
initiating TCP connexions. This can be done easily with the following
rules, presuming that 192.168.0.0/27 is the net behind the firewall:

add 1000 check-state
add 2000 allow tcp from 192.168.0.0/27 to any out setup \
keep-state

When TCP SYN packets reach rule 2000 they fail to match and are
dropped by the default deny because only TCP SYN packets moving out and
with the source address from the protected net, 192.168.0.0/27 will match
the rule. Incidentally, this is the same type of protection that NAT, and
transparent proxies in general, affords for internal hosts.

Until now most of the discussion has revolved around stateful
handling of TCP connexions. However, using the advanced stateful
functionality one can, as has been stated, control other packet types in a
stateful manner. For instance, during our discussion of the option
"icmptypes" we presented an example of how one could use them to allow
internal hosts to ping external hosts, and deny it the other way around.
This can be easily done with the "check-state" command and "set-state"
option as well:

add 1000 check-state
add 2000 allow icmp from any to any out icmptypes 8 keep-state

This works simply by creating a dynamic rule for every ICMP echo
request sent out. When the reply comes, it uses the dynamic rule and is
passed through. However, if someone tries to send in an ICMP echo, it is
denied, unless they happen to send it during the moment our dynamic rule
is alove. ICMPs use the net.inet.ip.fw.dyn_short_lifetime sysctl variable
which is by default set to 5 seconds. If the ICMP echo reply takes long
than 5 seconds to reply, the dynamic rule's life will timeout and it won't
be able to pass through. If long ping replies are suspecting for being a
possibility, then that sysctl's value should be raised. Most network lag
is under a second, however.

Incidentally, rule 2000 could also be written with the source
address being a network address if there were more than one network behind
the firewall and one wanted to specify which network these rules should
apply to. Likewise, one could limit the rule to a single host address. We
used this format because it was closest to what our first ping controlling
rules used.

6.3. Anatomy of a Dynamic Rule

Let us take a look at what it may exactly look like when one lists
their stateful firewall rules:

00100 allow ip from any to any via lo0
00200 deny ip from any to 127.0.0.0/8
01000 check-state
02000 allow tcp from any to any keep-state
65535 deny ip from any to any
## Dynamic rules:
02000 9 1255 (T 54, # 0) ty 0 tcp, 192.168.0.1 2007 <->
204.71.200.245 80

We are already familiar with the static rules, however, this is
the first time we're showing an example of a dynamic rule. Let us examine
it closely:

The first part of the dynamic rule is the rule # of the static
rule that started it, in this case, rule 2000, which has a "keep-state"
option. The second part is the count of bytes that have been sent out
through that dynamic rule, and the third part is the count of bytes that
have been received through that rule. In the parentheses, the 'T' value is
the timeout value - the rule lifetime - in seconds. In this case, 54
seconds are life for the rule. The hash mark indicates the rule number, in
this case being rule 0. The 'ty 0' part indicates what type of dynamic
rule this is. The rule type corresponds to the flow of the rule - whether
it allows traffic only from source to destination, the other way around,
or both (bidirectional). Currently, only one type is available, which is
the default: bidirectional. This is visually indicated by the "<->" symbol
between the source and destination IP:port. After the type, we see the
protocol that the dynamic rule passes through, followed by the source
IP:port, a bidirectional indicator "<->" as mentioned above, and finally
the destination IP:port.

Even after dynamic rules timeout, you will still see them listed
with "ipfw list," although, with a 0 T value. Once a rule times out, it
will no longer accept packets as it would have normally unless it is
revived with the same static rule with a "keep-state." Also, once they
timeout, they can be replaced by newly activated dynamic rules. Unless all
of the dynamic rules are alive, they will be continuously replaced with
new ones, especially so as the number of dynamic rules approaches the
maximum.

Once many dynamic rules are created it may become somewhat of a
nuisance to list the rules with 'ipfw list' as all of the dynamic rules
will stream off the terminal. To only list the static rules, one can do
something like:

ipfw list | grep -v '[<->#]'

Or, if one wishes to page down all of the rules, both static and
dynamic, one can:

ipfw list | more

7. Traffic Shaping

Traffic shaping refers to the controlling of traffic in various
ways, such as bandwidth capping, delays, flow queues, and so on. It allows
one to control the general intensity, direction and breakup of the
traffic. Since the introduction of dummynet(4) in FreeBSD, extensive
traffic shaping capabilities have been available. Indeed, this is another
region in which IPFilter is unable to offer corresponding functionality.
If one needs traffic shaping capabilities to control individual user
bandwidth consumption, or enable delays in traffic for experimental and
testing purposes, one must use dummynet(4) with ipfirewall(4), with only
one exception: probability matching, which is supported completely by
ipfirewall(4) without dummynet(4).

No traffic shaping rules can use dynamic rules, because as each
dynamic rule is created it will not observe the bandwidth caps, delays,
separate flow queues, etc.

7.1. Probability Matching

ipfirewall(4) supports a useful tool for networking testing by
allowing one to simulate random packet drops at various probability
ranges. This option uses the keyword "prob" followed by a floating point
number between 0 and 1 which corresponds to the probability with which
packets will be passed. So, a "prob 0.9" will pass packets matched by that
rule with a 90% probability, while a "prob 0.1" will do so with a 10%
probability. The following is an extended syntax for ipfw(8) for use with
native ipfw(8) probability matching:

[] [prob ] [log
[logamount ]]
from to 
[] []

For example, if we wished to drop ICMP echo requests 20% of the
time, we could have the following rule:

add 1000 prob 0.8 allow icmp from any to any in icmptypes 8

Or, perhaps, we may wish to deny 50% of TCP SYN packets to the web
server to simulate heavy web traffic via the ep0 interface:

add 1000 prob 0.5 allow tcp from any to any in setup via ep0

7.2. Dummynet

All additional traffic filtering capabilities require dummynet(4),
which was introduced into FreeBSD in version 2.2.8. Before they can be
used, the kernel has to be compiled with the following option:

options DUMMYNET

Once compiled in, one will be able to specify pipes for traffic
control. A pipe is a traffic shaping rule that controls the traffic in the
specified manner, and is created with the ipfw(8) "pipe" command. Traffic
is redirected to pipes with ipfw(8) and the use of the "pipe
"
action. First let us construct a simple pipe (note: each "pipe" command
must be preceeded with a call to /sbin/ipfw: ipfw pipe # ...):

pipe 10 config bw 100Kbit/s

This simple pipe will cap traffic flowing through it to a maximum
of 100 Kilobits per second. There are a several different ways to indicate
bandwidth measure: bit/s, Byte/s, Kbit/s, KByte/s Mbit/s, MByte/s. Each
bandwidth limiting pipe must use the "bw" keyword.

Another way to control traffic is to use a delay, which could be
used to simulate system lag:

pipe 10 config delay 100

The value following "delay" is in milliseconds. In this example,
all traffic moving through this pipe will be delayed 100ms. We could also
accomplish the same thing as the "prob" indicator built into
ipfirewall(4) with the "plr" pipe keyword. For instance, to simulate 20%
packet loss as we did with "prob 0.8," we could construct the following
pipe:

pipe 10 config plr 0.2

"plr" stands for "packet loss rate" so the value indicates at what
rate packets will be lost, while the "prob" keyword for ipfw(8) indicates
the probability with which packets will pass. Therefore, "plr" values are
(1 - "prob") values. To simulate 20% packet loss with "prob" we indicate
that 0.8 of the traffic will make it through; with "plr" we indicate
that 0.2 of the traffic will not make it through. Try not to get confused
by the difference.

7.2.1. Pipe Queues

Next, one may need to control the queue sizes of their pipes,
especially if the MTU of their network device is relatively large. The MTU
of a network device defines the "maximum transmission unit" for that
interface, or, in other words, the maximum size a packet can take on that
interface. To learn the size of the MTU of a given network interface one
need only use ifconfig(8) to view its info; for instance:

(root@nu)~># ifconfig xl0
xl0: flags=8843 mtu 1500
inet 192.168.0.1 netmask 0xffffffe0 broadcast 192.168.0.31
ether 00:70:18:d4:a4:ac
media: 10baseT/UTP (10baseT/UTP )
supported media: 10base5/AUI 10baseT/UTP 10baseT/UTP

10baseT/UTP
(root@nu)~>#

Here we see that the NIC's MTU is 1500 (bytes). Indeed, the MTU
for all ethernet devices is 1500 bytes.

Queues are used by pipes to enforce the configured bandwidth
limitations and delays. Queues can be configured by either specifying
their size in "Kbytes" or slot amounts. Slots correspond to packets; in
other words, specifying that a queue have "10" slots is to specify that it
can only hold 10 packets. The maximum size of the packets is defined by
the MTU of the given network device. For ethernet devices, the MTU is 1500
bytes. This means that if one specifies that a pipe use a queue with 10
slots on an ethernet network, the queue size would be 10 x 1500 bytes, or
15 Kbytes.

This is important to understand because the default queue for
pipes is 50 slots and it may especially be too large on network devices
with a large MTU and a very low bandwidth limitation. 50 was chosen as the
default because it is the typical queue size for ethernet devices. Under
normal circumstances it is imperceptible, however, when small bandwidth
limitations are imposed it'd take a long time to fill the queue and this
would create horrible network delays. For instance, if we set the
following pipe on an ethernet LAN to simulate a 56K modem:

pipe 10 config bw 56Kbit/s

... and we do not set a smaller MTU for the device with
ifconfig(8) or set a smaller queue (preferred approach), the queue into
which packets would be pumped for the pipe to enforce its bandwidth
limitation would be 1500 bytes (12000 bits) x 50 = 600Kbits. For a pipe
which is capping the bandwidth to 56Kbit/s it would take roughly 10.7
seconds (600Kbits / 56Kbit/s) to fill a 600Kbit queue. Such delays would
be a severe monkey wrench in the experiment. To avoid such complications,
it is strongly advisable to manually set the queue sizes that each pipe
uses. As we have noted earlier, the default queue size is set using slots
- 50 of them. The queue can also be set by specifying a size in "Kbits".
This latter approach is safer because using slots to specify the queue
size leaves the open variable of MTU size, which, if one isn't paying
attention, could cause additional complications. The smaller the bandwidth
cap, the smaller the queue size should be. For instance, using our above
example, a reasonable configuration would be:

pipe 10 config bw 56Kbit/s queue 5Kbytes

7.2.2. Pipe Masks

A powerful capability of pipes is to allow multiple queues per
traffic flow. For instance, if one has several boxes behind a firewall and
wants to cap the bandwidth of each one to 100Kbit/s and not just the
aggregate bandwidth of all traffic moving through the gateway, one can
either manually set up a pipe and ipfw rule for each server, or instead,
use dynamic queueing with pipe masks. The masks define which hosts belong
to the same pipe, just as netmasks and bitmasks define which groups of
hosts belong to the same network/subnet.

Masks can be specified in six different ways:

"dst-ip" - mask for the destination IP of the packets being sent
through the pipe.
"src-ip" - mask for the source.
"dst-port" - mask for the destination port.
"src-port" - mask for the source ports.
"proto" - mask for the protocol.
"all" - mask for all hosts; specifies that all bits in all fields
(dst-ip, src-ip, etc) are significant.

For example, let us take the above example of a network behind a
firewall for which all of the hosts are desired a 100Kbit/s bandwidth
cap. If we simply send all traffic through a pipe like described earlier,
the cap will be applied to the aggregate traffic from all of the hosts and
not each one individually. To apply masks to the hosts such that each
host's traffic is sent into a separate queue and applied the bandwidth
limit separately, one could do the following:

pipe 10 config mask src-ip 0x000000ff bw 100Kbit/s queue 10Kbytes
pipe 20 config mask dst-ip 0x000000ff bw 100Kbit/s queue 10Kbytes
add 1000 add pipe 10 all from 192.168.0.0/16 to any out via 
add 2000 add pipe 20 all from 192.168.0.0/16 to any in via

At first glance this may seem confusing. We have also for the
first time included the ipfw(8) rules that divert packets to the pipes. We
did this because the two pipe rules on their own would not make as much
sense without seeing what we are diverting to them. Pipe 10 caps the
traffic passing through it to 100Kbit/s as well as does the pipe 20. Rule
1000 diverts its traffic to pipe 10, and rule 2000 to pipe 20. Rule 1000
matches all traffic moving out and rule 2000 matches all traffic moving
in. There are two reasons to have a pipe for incoming and outgoing
traffic, but one will be addressed later. The primary reason that one
needs to concern oneself with now is that each pipe configures a different
mask. Pipe 10 configures mask 0x000000ff for source addresses; because
rule 1000 diverts traffic leaving the internal network, the mask *must* be
applied to source addresses if we wish to break the flows from each
internal network host to a separate queues. Likewise, for traffic coming
in, the queues must be broken up according to the destination addresses
behind the firewall.

As you noticed, we specified the masks in hexadecimal instead of
decimal. Either should work. The masks work in the exact same manner in
which netmasks work; this becomes clear when we realize they're done in
reverse. When netmasking we are trying to break up hosts into groups so
the high bits are at the beginning, here we are trying to break up a group
into hosts, so the high bits in the mask will be near the end. Observing
this reverse goal, it makes sense that the pipe masks look backwards from
netmasks. The hex mask we specified corresponds to a decimal mask of
0.0.0.255. In simple terms, the last octet indicates that only one host
should be alotted per queue (256 - 255). Thus, a separate queue for
bandwidth control is set aside for each address that has a different host
number (different last octet). This presumes, of course, that there are no
more than 254 hosts on a network. If there are more hosts, then the mask
must be adjusted. For instance, if there are 254^2 hosts in the network
behind the firewall, then the mask would have to be 0.0.255.255 (0000ffff)
to indicate that any address that has a different bit within the last two
octets must get its own queue.

7.2.3. Pipe Packet Reinjection

Under most circumstances, once a packet is diverted to a pipe, the
traffic shaping configured for that pipe takes effect and rule searching
ends. However, one can have the packet become reijected into the firewall,
starting at the next rule, after it passes through the pipe by disabling
the following sysctl:

net.inet.ip.fw.one_pass: 1

8. Traffic Flow

It must be always remembered that rules that do not specify "in"
or "out" flags will be checked for traffic coming in AND out. This has a
number of implications. For instance, pipes to which rules divert traffic
that haven't "in" or "out" flags will be activated twice, once when
packets leave and once when they enter. In addition, not specifying an
interface with the "via" keword can cause unwarranted confusion. If a
multi-homed system does not have its firewall using "via" then traffic
coming both ways across any interface will be treated with the "in" and
"out" keywords. "in" will both match traffic coming from the outside AND
local network, because both are coming "in"-to the gateway box.

Another concern is with half-duplex and full-duplex connexions. If
inward and outward traffic is diverted through the same pipe, then the
pipe will simulate half-duplex traffic, simply because, a pipe can not
simulate traffic going in both directions at the same time. If one is
simulating ethernet traffic, or simply using the pipes to control ethernet
traffic, then this is not a problem, for ethernet is a half-duplex
network. However, many other network connexions are full-duplex; as such,
it is safer to usually setup one pipe for inward traffic and one for
outward. This is the second reason for having two rules, each controlling
a direction, as was mentioned in the previous section on pipe masks.

Appendix A: Example Firewall Configurations

Here follow a number of scenarios requiring firewalling. Each
scenario is answered by a firewall rulest and a quick explanation as to
how it works. For all examples, 12.18.123.0/24 will be used as the local
subnet, xl0 will be used as the external NIC, and xl1 will be used as the
internal NIC.

Q) How do I block external pings, but allow myself to ping out
to any external host?

A) Stateful solution. The dynamic rules for icmp packets use the
net.inet.ip.fw.dyn_short_lifetime setting, which is 5 seconds by default.
The advantage of the stateful solution is that echo replies from only the
specific host you pinged will be accepted.

add 1000 deny icmp from any to 12.18.123.0/24 in via xl0 icmptypes 8
add 1010 check-state
add 1020 allow icmp from 12.18.123.0/24 to any out via xl0
icmptypes 8 keep-state
add 1030 deny icmp from any to any

The reason for having the deny rule before the check-state rule is
that the dynamic rules are bi-directional. As such, during the check-state
echo requests can come from external hosts and they will be answered;
essentially, during the short lives of the dynamic rules, your host will
be pingable. Because of this, echo requests from external hosts are
filtered prior to the check-state rule.

Stateless Solution. The advantage of the stateless solution is
less overhead because of less rules to process; but, overhead in dealing
with occasional pings shouldn't be an for the most part, so this advantage
is negligible.

add 1000 deny icmp from any to 12.18.123.0/24 in via xl0 icmptypes 8
add 1010 allow icmp from 12.18.123.0/24 to any out via xl0 icmptypes 8
add 1020 allow icmp from any to 12.18.123.0/24 in via xl0 icmtypes 0

The disadvantage of the stateless approach is that it will always
accept echo replies from any host, as opposed to the stateful approach
which will only accept echo replies from the specific host that was
pinged.

Q) How do I block private subnets as defined in RFC 1918 from
entering or exitting my network?

A)

add 1000 deny all from 192.168.0.0/16 to any via xl0
add 1010 deny all from any to 192.168.0.0/16 via xl0
add 1020 deny all from 172.16.0.0/12 to any via xl0
add 1030 deny all from any to 172.16.0.0/12 via xl0
add 1040 deny all from 10.0.0.0/8 to any via xl0
add 1050 deny all from any to 10.0.0.0/8 via xl0

Q) How would I enforce rate limiting on each host in my network
individually? I want to enforce an upstream limit of 64Kbit/s and a
downstream of 384Kbit/s for each host; in addition, I want to disallow all
external hosts from initiating connexions with the hosts on my network so
that no one can run any servers.

A) This might be similar to a setup enforced at a university. It
can be easily set with the following rules:

pipe 10 config mask src-ip 0x000000ff bw 64kbit/s queue 8Kbytes
pipe 20 config mask dst-ip 0x000000ff bw 384kbit/s queue 8Kbytes
add 100 deny icmp from any to 12.18.123.0/24 in via xl0 icmptypes 8
add 110 check-state
add 1000 pipe 10 all from 12.18.123.0/24 to any out via xl0
add 1100 pipe 20 all from any to 12.18.123.0/24 in via xl0
add 1200 allow tcp from 12.18.123.0/24 to any out via xl0 setup
keep-state
add 1200 allow udp from 12.18.123.0/24 to any out via xl0 keep-state
add 1300 allow icmp from 12.18.123.0/24 to any out icmptypes 8
keep-state
add 65535 deny all from any to any

转载于:https://my.oschina.net/u/187928/blog/34149

你可能感兴趣的:(Ipfw-HOWTO)