Activation
PF is enabled by default. If you wish to disable it on boot, use the rcctl(8) tool to do so:
# rcctl disable pf
and reboot your system to have it take effect.
You can also manually activate and deactivate PF by using the pfctl(8) program:
# pfctl -e # pfctl -d
to enable and disable, respectively. Note that this just enables or disables PF, it doesn't actually load a ruleset. The ruleset must be loaded separately, either before or after PF is enabled.
Configuration
PF reads its configuration rules from /etc/pf.conf at boot time, as loaded by the rc scripts. Note that while /etc/pf.conf is the default and is loaded by the system rc scripts, it is just a text file loaded and interpreted by pfctl(8) and inserted into pf(4). For some applications, other rulesets may be loaded from other files after boot. As with any well designed Unix application, PF offers great flexibility.
The pf.conf file has multiple parts:
- Macros: User-defined variables that can hold IP addresses, interface names, etc.
- Tables: A structure used to hold lists of IP addresses.
- Options: Various options to control how PF works.
- Filter Rules: Allows the selective filtering or blocking of packets as they pass through any of the interfaces.
Filter rules can be given parameters to specify network address translation (NAT) and packet redirection.
Blank lines are ignored, and lines beginning with # are treated as comments.
Control
After boot, PF operation can be managed using the pfctl(8) program. Some example commands are:
# pfctl -f /etc/pf.conf Load the pf.conf file # pfctl -nf /etc/pf.conf Parse the file, but don't load it # pfctl -sr Show the current ruleset # pfctl -ss Show the current state table # pfctl -si Show filter stats and counters # pfctl -sa Show EVERYTHING it can show
There are a few things that you need to have ready prior to following this how-to. I will outline how to get these things ready so that we can get to the meat-and-potatoes of this article. The first thing that you need is OpenBSD version 3.0 or later. At a mininum, you should have OpenBSD already installed. I have geared this article for OpenBSD 3.3. While you can find many good how-to's on setting up the firewall, my main focus is how to build and configure your firewall config correctly from the ground-up.
First and formost, you should have your firewall plugged into a network (with at least one other computer on it as well) so that it can receive and send traffic. If you do not have this, it will be very difficult to test. Often times if a network is not available, but you have two computers, you can connect the two computers directly to each other (network card to network card) by using a special Cat-5 cable called a "cross-over cable". I will not go into how to make one here, but any decent computer store carries them. If you plan on setting up NAT (Network Address Translation- where multiple computers can share a single IP address), then you should have two NICs (network interface cards) installed.
Few things are more important to have secure than a firewall. While building/configuring/testing your firewall, it should (ideally) NOT be accessible via the internet, and should be on an internal network. Please refer to RFC1918 for a list of private network blocks, but most ineternal networks are either 192.168.0.0/16 or 10.0.0.0/8. You should be comfortable with setting this up, and if you find this difficult, I would recommend that you do NOT set up a firewall at this point.
Setting Things Up
To activate PF, and have it start automatically on boot-up, edit your /etc/rc.conf.local
file so that you add the line pf=YES
to it. Although, you can edit the /etc/rc.conf
file, it contains the system defaults, and should not be touched. Instead you can make your "overridden" changes in /etc/rc.conf.local
, and the system will read that last, and update any changes you had made in it. Here is the contents of my /etc/rc.conf.local
file:
Listing 1. /etc/rc.conf.local
|
Next, create a "pass all" pf.conf so that on boot-up, OpenBSD will read it in. Here is a good template for you to use:
Listing 2. /etc/pf.conf
|
If you look at Listing 2, you will see some place holders beginning with a "##". This will be explained later, but it is good to put it in there now so you know the order in which PF will process the rules file. If you look at the last two lines that begin with "pass..." you can see that it is going to log traffic going in and out of the system. As your system makes or receives connections, it is going to get logged in your /var/log/pflog file. I will show you later how you can read this (it's a binary file, and can not be read in a text editor). Ideally, while you are setting up your firewall, the machine should send and receive little to no traffic unless it is generated by you- it will help you see what is getting logged if you are the one creating traffic. At this point, go ahead and reboot your system for all the changes you have made to take affect, and to make sure everything starts up automatically.
Once you have rebooted, it's time to check to see if everything you made changes to came up successfully. You should try the below section, and make sure that the pflog0 interface is up, and that your rules got loaded.
Listing 3. Checking the pflog0 interface and pfctl -s rules output
|
Here in Listing 3, you can see that pflog0 is "UP". This will allow you to attach TCPdump (explained later) to the interface to allow you to watch the traffic in real-time. Like I said above, if your computer is being heavily used, the information will fly by way too fast for you to read. Also, by running pfctl -s rules
, you can see that your basic ruleset was loaded (not much of a firewall at this point since it's passing ALL traffic. hehe).
Taking it for a Spin
Now, it's time for you to watch some traffic. Although it will seem a bit cryptic, I will show you what to look for. Once you know how to read this, you will be able to better tailor your fireall to handle your network traffic. Listed below is the output of a few lines from TCPdump. Just type TCPdump -n -e -ttt -i pflog0
at the command-prompt to see what your firewall is doing.
Listing 4. Reading the output of TCPdump -n -e -ttt -i pflog0
|
Ok, the IPs 11.22.33.44
, and 55.66.77.88
are fictitious, and have been changed so that the real IPs won't be shown here. The IP 192.168.1.2
is an internal system that is on a private network that is being NATted by the firewall. I will explain why the internal private network is being shown in the logs instead of the external (Internet) IP address later (see the NATting section at the end of this document if you can't wait).
I am going to dissect the below line to show you what each part means:
Sep 17 17:07:37.443421 rule 14/0(match): pass in on fxp0: 55.66.77.88.14373 > 66.92.15.252.22: S 3920978973:3920978973(0) win 5840 (DF)
Sep 17 17:07:37.443421 |
The current date/time including milliseconds. |
rule 14/0(match): |
The ruleset number that caught the packet. You can view what number PF assigned to each rule by typing: pfctl -g -s rules| grep '^@' |
pass in on fxp0: |
This says that the packet passed, coming in on fxp0 (fxp is a Intel Pro100 driver). You may have a different NIC, so fxp will not be there. |
55.66.77.88.14373 > 66.92.15.252.22: S |
This indicates the from-ip:port > to-ip:port that the packet came from, and was destined to go to. The S at the end indicates the flag that the packet was set to (in this case it was a SYN flag). |
3920978973:3920978973(0) win 5840 (DF) |
Ignore this stuff. It is beyond the scope of this document (mainly for more advanced uses). |
This should give you somewhat of a base so that when you start building your ruleset, and by monitoring TCPdump
, you will be able to make additions and/or corrections to your specific environment.
Now, you have a basic working knowlege of TCPdump
and how to read it's output, and have the framework for a basic pf.conf file. Now it's time to give you a primer into the essential pfctl
switches. I am not going to go into all the switches, just the ones you would use 90% of the time.
pfctl -d |
Diable the packet filter |
pfctl -e |
Enable the packet filter |
pfctl -Fa -f /etc/pf.conf |
Flush all (nat, filter, queue, state, info, table) rules and reload from the file /etc/pf.conf |
pfctl -s rules |
Report on the currently loaded filter ruleset. |
pfctl -s nat |
Report on the currently loaded nat ruleset. |
pfctl -s state |
Report on the currently running state table (very useful). |
pfctl -v -n -f /etc/pf.conf |
This does not actually load any rules, but allows you to check for errors in the file before you do load the ruleset. This is obviously good for testing. |
Building Something Useful
Now it's time to roll up our sleeves, and delve into creating a useful PF config file. I would recommend that you leave your current /etc/pf.conf
file alone (just in case you panic, and want to start fresh), and use a new one (we'll use /etc/pf.conf-new
). The key is to start with a simple config, and build from there- if you get too zealous, and build a 60 line pf.conf
file, and it doesn't do what it is supposed to do, you will have a hard time finding where the problem is. So let's start off simple, and build from there once we have a solid foundation.
There are essentially two camps for rules: allow all traffic by default, but explicitly deny what you don't want, and the other is to deny all traffic by default, and explicitly allow what you want. There is a big difference between the two, and can you guess which one I am going to show you? The latter one because it's the most secure.
First off, you should make a list of ports that you want to allow to the machine. At a mininum, allowing ssh (port 22/TCP), auth (port 113/TCP), and ICMP pings are good to start with. If you are running additional services (be VERY selective about what you run on a firewall- ideally, it shouldn't run any), you will obviously want to add those as well. We are going to replace the last two lines from Listing 2 with the following lines. I will explain what each line is later on.
Listing 5. /etc/pf.conf-new
|
We aren't using any of the cool features that PF has (aside from some MACRO definitions), but this is meant to be simple initially, and we can build on it later. One thing that tends to confuse alot of people is the concept of "IN" and "OUT" in a filter rule. The rules are always geared to itself (being the firewall), so when you see an "OUT" in a rule, that means it is leaving the firewall (regardless on what interface), and if you see an "IN" in a rule, then that means it is entering the firewall (regardless on what interface).
One thing that I am a stickler for is keeping the ruleset easy to read. There is some redundancy but it helps keep things grouped together so it's easy to make mental map of what you have- especially when you get to a BIG ruleset. Ok, now for the dissection (I am going to skip over the macro section as it should be pretty self-explanatory).
# Block everything (inbound AND outbound on ALL interfaces) by default (catch-all)
block all
The line above is about as wide open as you can get- it blocks everything on all interfaces and all protocols. This ensures that only what you explicitly allow will be allowed through
block return-rst in log on $EXT_NIC proto TCP all
pass in log quick on $EXT_NIC proto TCP from any to $EXT_IP port 22 flags $SYN_ONLY keep state
pass in log quick on $EXT_NIC proto TCP from any to $EXT_IP port 113 flags $SYN_ONLY keep state
These three lines are the grouping for all TCP-related traffic. The big differerence between the three lines is the word "quick". Without a "quick" option, the rule that matches a packet gets "tagged" with what the rule says to do, but the rule evalutation continues. If no other rules match, than the "tag" sticks, if a following rule matches, then the tag is replaced with what the new rule says. This is a nice way to set default behaviour for whatever you want, and then specify what you specifically want to do in the following rules. Here in this case, the last two lines have the "quick" option, so that if a packet matches either port 22/TCP or 113/TCP (as well as other constraints specified within the rule), then they are allowed through the firewall (hence the word "pass" in the beginning of the rule, and no other processing will be done on the packet. This is a critical piece to understand, so make sure it makes sense to you.
block in log on $EXT_NIC proto udp all
# It's rare to be hosting a service that requires UDP (unless you are hosting
# a dns server for example), so there typically won't be any entries here.
These three lines are much the same as the TCP section above, but this time it is a section focusing on UDP. Like it says in the ruleset, few machines need to listen on incoming UDP unless you are running a DNS server for example. For the most part, you can leave this section like it is. Here you can see that there is no "quick" option for this section- since no other rules match, UDP will be silently dropped. On a side note if a port is closed, the right thing to do is for the server to send back a return-reset if it is a TCP port, but since UDP and ICMP are stateless, you don't have to return anything, so dropping the packet without sending anything back is perfectly legal. I think that hiding the fact that you have a firewall is just as important as having one. No one needs to know you have one because it gives potential attackers more information about what you have. If you have a server hanging on the net, and it receives a UDP packet destined for a port it doesn't listen on, it will drop the packet with no reply, but if it receives a TCP packet, it sends back a tcp-reset packet letting the sender know it's not listening. So, the point is, if your firewall sends back a packet for a closed UDP port, then the attacker can say with a high probability that a firewall sent that, and not a server.
block in log on $EXT_NIC proto icmp all
pass in log quick on $EXT_NIC proto icmp from any to $EXT_IP echoreq keep state
Here is the ICMP section. You should be getting the hang of this stuff by now, so I will keep it short. Although I did say that ICMP is stateless, PF can do some nice tricks to allow for some intelligence when dealing with UDP and ICMP. For example, if you restrict outbound ICMP traffic, but just allow ICMP-pings, if you have a "keep state" flag on that rule, then PF is smart enough to allow ICMP-replies to come back without you needing to create a specific rule for it to come back in. Nice eh?
block out log on $EXT_NIC all
pass out log quick on $EXT_NIC from $EXT_IP to any keep state
Remember that one of the first lines in your new /etc/pf.conf-new
file reads block all
. This means it will block all outbound traffic as well as inbound traffic (ie. you won't be able to send any traffic out of your firewall. The above two lines permit unrestricted traffic to leave your firewall. If you are super-paranoid, you can lock this down further, and only allow specific traffic to leave the firewall (like web access for example).
# Allow the local interface to talk unrestricted
pass in quick on lo0 all
pass out quick on lo0 all
Just like I mentioned in the above paragraph, the block all
line blocks ALL traffic on all interfaces, and that includes your local interface as well. The above two lines allow unrestricted access on your local interface. I am sure you notices that there is no "keep state" option on these two lines- the reason for that is that it requires more cpu processing to manage states, so in a case where all traffic is allowed, it's really pointless to keep state.
Final Touches
At this point, using the pfctl
commands I listed above, fire up your new ruleset, and try things out. Since I like to have a "log" option for all rules, when you run tcpdump
on your pflog0
interface, you should see log entries of connections coming in and out whether it is passed or denied. If you see traffic that is getting blocked, but you want to allow it, you can add that rule to your /etc/pf.conf-new
file, and reload, and you should see it passing.
To go a bit Deeper
At this point, you should have the basics down pretty well, but alot was left out that makes PF so great. In this section, I am going to expand on what we discussed earlier, and show you some of the more popular features. I think the best way to approach this is by going through each section of your new
/etc/pf.conf-new
file, and explain some of these new features.
Macros
The first section is your macro
section. This is just a fancy way of creating names (variables), and assigning values to them. This gets really handy when you have to move to a new set of IP addresses, and if you used macros everywhere, you don't have to find them all in your pf.conf file- you just change the macro definition, reload the config, and your'e done. Keep in mind that you can use macros for substituting anything- such as interface names, ips, ports, parts of rules, etc.
Tables
The next section is your tables
section. This is a very unique feature, and is kinda like macros, but can be changed without reloading your config, and if the table has alot of IPs (like 1000's), PF will able to do some serious optimization that it wouldn't be able to do with macros. Yes, you read that right, you can add/remove IPs from a table from the commandline, and your firewall will automagically know the new config. Very nice! I am not gong to re-create the pf.conf
for you, but I will show you what I have set up for my tables on my firewalls.
## TABLES
table <block_hosts> persist
table <private> const { 10/8, 172.16/12, 192.168/16, 224/8 }
Essentially, you create a name for a table (like "block_hosts"), and you can optionally add a flag like const
or persist
. What const
means is that once the table is loaded, you can not change the values of it from the commandline. For persist
, it means that the table will stay in ram so you can add to it later even if there are no entries in it. Normally, when a table no longer has any data in it, it is unloaded from memory- persist
prevents that from happening. Now, to actually use a table in your rules, you would do something like I have below (which I place at the top of my filter rule section).
# Global filter stuff
block in log quick on $EXT_NIC from <block_hosts> to any
block in log quick on $EXT_NIC from <private> to any
Global Options
This section is where you define generally how PF behaves. You will find a wide variety of options to set, and although I will not go through them all, I will list some of the more interesting ones.
## GLOBAL OPTIONS
set loginterface $EXT_NIC
set block-policy return
The first option (set loginterface $EXT_NIC
) is a must for just about every situation. It allows you to gather some really interesting statistics that normally aren't captured. Here's an example output of one of my firewalls. To get this information, use pfctl -s info
. I don't think I need to explain the below output.
cerberus:~/# pfctl -s info
Status: Enabled for 69 days 07:04:35 Debug: None
Interface Stats for fxp0 IPv4 IPv6
Bytes In 385597759 0
Bytes Out 179194907 0
Packets In
Passed 801631 0
Blocked 190642 0
Packets Out
Passed 713633 0
Blocked 4 0
State Table Total Rate
current entries 16
searches 15272773 2.6/s
inserts 218763 0.0/s
removals 218747 0.0/s
Counters
match 13746071 2.3/s
bad-offset 0 0.0/s
fragment 0 0.0/s
short 0 0.0/s
normalize 0 0.0/s
memory 37403 0.0/s
The next line (set block-policy return
) can be seen as a "catch-all" option. Although in your filter rules section, you specify explicitly how to handle every packet, this should mainly be seen as a saftey-net in case something new comes along that you don't have a rule for.
Traffic Normalization
With PF, it doesn't just pass packets on through, you also have the ability for it to clean up "dirty" packets before it is passed on. To go into detail of what all this means is beyond the scope of this article, but it is generally a good idea to do this. Below is some of the more interesting normalization options that you can use.
## TRAFFIC NORMALIZATION
scrub in on $EXT_NIC all fragment reassemble
scrub out on $EXT_NIC all fragment reassemble random-id no-df
# For NFS
scrub in on $INT_NIC all no-df
scrub out on $INT_NIC all no-df
Notice that I scrub all traffic going in and out of my external interface. The option random-id
is a obscure option that prevents monitoring systems from detecting how many systems your firewall is NATting for. With the random-id
option, they won't be able to detect this. The no-df
option is especially good if you are running nfs over that interface (like I am on my internal interface). Although, this isn't specific to the traffic normalization section (but kinda goes along with it), when you put a modulate state
option at the end of your filter rules, it hides the shortcomings of different network stacks found on some OS's. The benefit is that if your firewall is port-forwarding traffic to different systems (like port 80 traffic going to Linux, and port 25 traffic going to Solaris), external monitoring systems will be able to determine that 1) You are running two different servers one for each port, and 2) What OS is running on those systems. Please note that if you use modulate state
for TCP filter rules, you do not need the keep state
option. Also, modulate state
works with TCP only.
Queueing
Unfortunately, to properly discuss PF's queueing facility, It would probably be best to write up a separate paper discussing this topic. If there is enough interest, I may do that.
Translation (NAT)
Having a firewall that doesn't do NAT is like using a table with only three legs- the two just go hand in hand. Of course, you must have two interfaces in two different subnet masks for this to work. On a typical firewall, your external inteface is connected to your Internet, and your internal interface is connected to a hub or switch for your internal network (refer back to the private network IP addresses listed earlier). With translation (NAT), you can do some really cool stuff, such as: 1) creating a static map where a specific internal IP address is mapped to a specific external IP address (if you have multiple IPs bound to on your external NIC), 2) Forwarding traffic for a specific port coming in on your ext. NIC, and passing it on to a server on your internal network for it to be handled, and 3) Allowing your private network of computers to access the internet via a single IP address. I will go through how to do each one.
rdr on $EXT_NIC proto TCP from any to 33.11.33.55 port 25 -> 192.168.1.33 port 25
binat on $EXT_NIC from 192.168.1.55 to any -> 11.22.44.33
nat on $EXT_NIC from 192.168.0.0/16 to any -> 22.33.11.55
You should recognise the macros in the above three lines. The first line basically says that traffic coming in on the external interface going for IP address 33.11.33.55
port 25 should be passed on to 192.168.1.33
on port 25.
The second line says that the machine at 192.168.1.55
on the internal interface should "own" the IP address 11.22.44.33
on the external interface. What this means is that all outbound traffic leaving 192.168.1.55
will go out through the IP address 11.22.44.33
, and vice-versa. It is important to note that filtering rules still are applied for all traffic.
The last line says that NATting will occur for all machines on the 192.168.0.0/16
network destined to any ip address to appear on the internet as though it is coming from 22.33.11.55
.
NOTE: This is one part that will screw up alot of people. Please note that NAT translation happens before any filtering happens. What this means is that if you do any filtering for a NATted IP address, your filter rule has to specify the NATted IP address, not it's external IP address (which would be needed in your ruleset for the port redirection above to work). I will give you an example below.
pass in log quick on $EXT_NIC proto TCP from any to 192.168.1.33 port 25 keep state
Grouping
Although, this isn't really a section, it's a very nice feature that PF has. Simply put, it allows you to combine similar rules into one rule keeping your ruleset nice and tidy. It can be completely explained in the below examples:
pass in log quick on $EXT_NIC proto tcp from any to 11.22.33.44 port 80 keep state
pass in log quick on $EXT_NIC proto tcp from any to 11.22.33.44 port 443 keep state
Becomes...
pass in log quick on $EXT_NIC proto tcp from any to 11.22.33.44 port { 80, 443 } keep state
and
pass in log quick on $EXT_NIC proto tcp from any to 192.168.1.33 port 53 keep state
pass in log quick on $EXT_NIC proto udp from any to 192.168.1.33 port 53 keep state
Becomes...
pass in log quick on $EXT_NIC proto { tcp, udp } from any to 192.168.1.33 port 53 keep state
Nice eh? This can be a real space-saver, and will make your firewall config much easier to manage. I used the "{ and }" grouping symbols in my table definitions above, and you can use them everywhere in your pf.conf file.
Conclusion
I hope this helped you understand how to set up a PF firewall. While just using these guidelines will build a pretty solid firewall, there are many parts of PF that you should take a deeper look into that you may want to leverage. If you have any comments or suggestions for other articles, please send them to me at the email address listed above.
Resources
- man 5 pf.conf
- man 8 pfctl
- OpenBSD's PF FAQ
PFCTL(8) BSD System Manager's Manual PFCTL(8)
NAME pfctl -- control the packet filter (PF) and network address translation (NAT) device SYNOPSIS pfctl [-AdeghmNnOqRrvz] [-a anchor] [-D macro= value] [-F modifier] [-f file] [-i interface] [-K host | network] [-k host | network] [-o level] [-p device] [-s modifier] [-t table -T command [address ...]] [-x level] DESCRIPTION The pfctl utility communicates with the packet filter device. It allows ruleset and parameter configu-ration configuration ration and retrieval of status information from the packet filter. Packet filtering restricts the types of packets that pass through network interfaces entering or leav-ing leaving ing the host based on filter rules as described in pf.conf(5). The packet filter can also replace addresses and ports of packets. Replacing source addresses and ports of outgoing packets is called NAT (Network Address Translation) and is used to connect an internal network (usually reserved address space) to an external one (the Internet) by making all connections to external hosts appear to come from the gateway. Replacing destination addresses and ports of incoming packets is used to redirect connections to different hosts and/or ports. A combination of both translations, bidirectional NAT, is also supported. Translation rules are described in pf.conf(5). The packet filter does not itself forward packets between interfaces. Forwarding can be enabled by setting the sysctl(8) variables net.inet.ip.forwarding and/or net.inet6.ip6.forwarding to 1. Set them permanently in sysctl.conf(5). The pfctl utility provides several commands. The options are as follows: -A Load only the queue rules present in the rule file. Other rules and options are ignored. -a anchor Apply flags -f, -F, and -s only to the rules in the specified anchor. In addition to the main ruleset, pfctl can load and manipulate additional rulesets by name, called anchors. The main ruleset is the default anchor. Anchors are referenced by name and may be nested, with the various components of the anchor path separated by `/' characters, similar to how file system hierarchies are laid out. The last component of the anchor path is where ruleset operations are performed. Evaluation of anchor rules from the main ruleset is described in pf.conf(5). Private tables can also be put inside anchors, either by having table statements in the pf.conf(5) file that is loaded in the anchor, or by using regular table commands, as in: # pfctl -a foo/bar -t mytable -T add 1.2.3.4 5.6.7.8 When a rule referring to a table is loaded in an anchor, the rule will use the private table if one is defined, and then fall back to the table defined in the main ruleset, if there is one. This is similar to C rules for variable scope. It is possible to create distinct tables with the same name in the global ruleset and in an anchor, but this is often bad design and a warn-ing warning ing will be issued in that case. By default, recursive inline printing of anchors applies only to unnamed anchors specified inline in the ruleset. If the anchor name is terminated with a `*' character, the -s flag will recursively print all anchors in a brace delimited block. For example the following will print the ``authpf'' ruleset recursively: # pfctl -a 'authpf/*' -sr To print the main ruleset recursively, specify only `*' as the anchor name: # pfctl -a '*' -sr -D macro=value Define macro to be set to value on the command line. Overrides the definition of macro in the ruleset. -d Disable the packet filter. -X token Release the pf enable reference represented by the token passed. -e Enable the packet filter. -E Enable the packet filter and increment the pf enable reference count. -F modifier Flush the filter parameters specified by modifier (may be abbreviated): -F nat Flush the NAT rules. -F queue Flush the queue rules. -F rules Flush the filter rules. -F states Flush the state table (NAT and filter). -F Sources Flush the source tracking table. -F info Flush the filter information (statistics that are not bound to rules). -F Tables Flush the tables. -F osfp Flush the passive operating system fingerprints. -F all Flush all of the above. -f file Load the rules contained in file. This file may contain macros, tables, options, and normal-ization, normalization, ization, queueing, translation, and filtering rules. With the exception of macros and tables, the statements must appear in that order. Use of this option, could result in flushing of rules present in the main ruleset added by the system at startup. See /etc/pf.conf for further details. -g Include output helpful for debugging. -h Help. -i interface Restrict the operation to the given interface. -K host | network Kill all of the source tracking entries originating from the specified host or network. A sec-ond second ond -K host or -K network option may be specified, which will kill all the source tracking entries from the first host/network to the second. -k host | network Kill all of the state entries originating from the specified host or network. A second -k host or -k network option may be specified, which will kill all the state entries from the first host/network to the second. For example, to kill all of the state entries originating from ``host'': # pfctl -k host To kill all of the state entries from ``host1'' to ``host2'': # pfctl -k host1 -k host2 To kill all states originating from 192.168.1.0/24 to 172.16.0.0/16: # pfctl -k 192.168.1.0/24 -k 172.16.0.0/16 A network prefix length of 0 can be used as a wildcard. To kill all states with the target ``host2'': # pfctl -k 0.0.0.0/0 -k host2 -m Merge in explicitly given options without resetting those which are omitted. Allows single options to be modified without disturbing the others: # echo "set loginterface fxp0" | pfctl -mf --M -mf-M -M Enable port to name translation while displaying rule. -N Load only the NAT rules present in the rule file. Other rules and options are ignored. -n Do not actually load rules, just parse them. -O Load only the options present in the rule file. Other rules and options are ignored. -o level Control the ruleset optimizer, overriding any rule file settings. -o none Disable the ruleset optimizer. -o basic Enable basic ruleset optimizations. This is the default behaviour. -o profile Enable basic ruleset optimizations with profiling. For further information on the ruleset optimizer, see pf.conf(5). -p device Use the device file device instead of the default /dev/pf. -q Only print errors and warnings. -R Load only the filter rules present in the rule file. Other rules and options are ignored. -r Perform reverse DNS lookups on states when displaying them. -s modifier Show the filter parameters specified by modifier (may be abbreviated): -s nat Show the currently loaded NAT rules. -s queue Show the currently loaded queue rules. When used together with -v, per-queue statistics are also shown. When used together with -v -v, pfctl will loop and show updated queue statistics every five seconds, including measured bandwidth and packets per second. -s rules Show the currently loaded filter rules. When used together with -v, the per-rule perrule rule statistics (number of evaluations, packets and bytes) are also shown. Note that the ``skip step'' optimization done automatically by the kernel will skip evaluation of rules where possible. Packets passed statefully are counted in the rule that created the state (even though the rule isn't evaluated more than once for the entire connection). -s Anchors Show the currently loaded anchors directly attached to the main ruleset. If -a anchor is specified as well, the anchors loaded directly below the given anchor are shown instead. If -v is specified, all anchors attached under the target anchor will be displayed recursively. -s states Show the contents of the state table. -s Sources Show the contents of the source tracking table. -s info Show filter information (statistics and counters). When used together with -v, source tracking statistics are also shown. -s References Show pf-enable reference statistics (pid/name of enabler, token, timestamp). -s labels Show per-rule statistics (label, evaluations, packets total, bytes total, pack-ets packets ets in, bytes in, packets out, bytes out) of filter rules with labels, useful for accounting. -s timeouts Show the current global timeouts. -s memory Show the current pool memory hard limits. -s Tables Show the list of tables. -s osfp Show the list of operating system fingerprints. -s Interfaces Show the list of interfaces and interface drivers available to PF. When used together with -v, it additionally lists which interfaces have skip rules acti-vated. activated. vated. When used together with -vv, interface statistics are also shown. -i can be used to select an interface or a group of interfaces. -s all Show all of the above, except for the lists of interfaces and operating system fingerprints. -T command [address ...] Specify the command (may be abbreviated) to apply to the table. Commands include: -T kill Kill a table. -T flush Flush all addresses of a table. -T add Add one or more addresses in a table. Automatically create a nonexisting table. -T delete Delete one or more addresses from a table. -T expire number Delete addresses which had their statistics cleared more than number seconds ago. For entries which have never had their statistics cleared, number refers to the time they were added to the table. -T replace Replace the addresses of the table. Automatically create a nonexisting table. -T show Show the content (addresses) of a table. -T test Test if the given addresses match a table. -T zero Clear all the statistics of a table. -T load Load only the table definitions from pf.conf(5). This is used in conjunction with the -f flag, as in: # pfctl -Tl -f pf.conf For the add, delete, replace, and test commands, the list of addresses can be specified either directly on the command line and/or in an unformatted text file, using the -f flag. Comments starting with a `#' are allowed in the text file. With these commands, the -v flag can also be used once or twice, in which case pfctl will print the detailed result of the operation for each individual address, prefixed by one of the following letters: A The address/network has been added. C The address/network has been changed (negated). D The address/network has been deleted. M The address matches (test operation only). X The address/network is duplicated and therefore ignored. Y The address/network cannot be added/deleted due to conflicting `!' attributes. Z The address/network has been cleared (statistics). Each table maintains a set of counters that can be retrieved using the -v flag of pfctl. For example, the following commands define a wide open firewall which will keep track of packets going to or coming from the OpenBSD FTP server. The following commands configure the firewall and send 10 pings to the FTP server: # printf "table <test> { ftp.openbsd.org }\n \ pass out to <test>\n" | pfctl -f-# -f# # ping -qc10 ftp.openbsd.org We can now use the table show command to output, for each address and packet direction, the number of packets and bytes that are being passed or blocked by rules referencing the table. The time at which the current accounting started is also shown with the ``Cleared'' line. # pfctl -t test -vTshow 129.128.5.191 Cleared: Thu Feb 13 18:55:18 2003 In/Block: [ Packets: 0 Bytes: 0 ] In/Pass: [ Packets: 10 Bytes: 840 ] Out/Block: [ Packets: 0 Bytes: 0 ] Out/Pass: [ Packets: 10 Bytes: 840 ] Similarly, it is possible to view global information about the tables by using the -v modifier twice and the -s Tables command. This will display the number of addresses on each table, the number of rules which reference the table, and the global packet statistics for the whole ta-ble: table: ble: # pfctl -vvsTables --a-r- test Addresses: 1 Cleared: Thu Feb 13 18:55:18 2003 References: [ Anchors: 0 Rules: 1 ] Evaluations: [ NoMatch: 3496 Match: 1 ] In/Block: [ Packets: 0 Bytes: 0 ] In/Pass: [ Packets: 10 Bytes: 840 ] In/XPass: [ Packets: 0 Bytes: 0 ] Out/Block: [ Packets: 0 Bytes: 0 ] Out/Pass: [ Packets: 10 Bytes: 840 ] Out/XPass: [ Packets: 0 Bytes: 0 ] As we can see here, only one packet - the initial ping request - matched the table, but all packets passing as the result of the state are correctly accounted for. Reloading the table(s) or ruleset will not affect packet accounting in any way. The two ``XPass'' counters are incre-mented incremented mented instead of the ``Pass'' counters when a ``stateful'' packet is passed but doesn't match the table anymore. This will happen in our example if someone flushes the table while the ping(8) command is running. When used with a single -v, pfctl will only display the first line containing the table flags and name. The flags are defined as follows: c For constant tables, which cannot be altered outside pf.conf(5). p For persistent tables, which don't get automatically killed when no rules refer to them. a For tables which are part of the active tableset. Tables without this flag do not really exist, cannot contain addresses, and are only listed if the -g flag is given. i For tables which are part of the inactive tableset. This flag can only be witnessed briefly during the loading of pf.conf(5). r For tables which are referenced (used) by rules. h This flag is set when a table in the main ruleset is hidden by one or more tables of the same name from anchors attached below it. -t table Specify the name of the table. -v Produce more verbose output. A second use of -v will produce even more verbose output includ-ing including ing ruleset warnings. See the previous section for its effect on table commands. A third use of -v will produce additional queue statistics related information. -w wait Show queue statistics at intervals of wait seconds. -x level Set the debug level (may be abbreviated) to one of the following: -x none Don't generate debug messages. -x urgent Generate debug messages only for serious errors. -x misc Generate debug messages for various errors. -x loud Generate debug messages for common conditions. -z Clear per-rule statistics. FILES /etc/pf.conf Packet filter rules file. /etc/pf.os Passive operating system fingerprint database. SEE ALSO pf.conf(5), pf.os(5), sysctl.conf(5), ftp-proxy(8), sysctl(8) HISTORY The pfctl program and the packet filter mechanism first appeared in OpenBSD 3.0. BSD October 11, 2013 BSD