PF How to kill states related to anchor

Hello!
Is there a way to kill states that created by rules from specified anchor on FreeBSD 12.1?
I tried pfctl -a "anchor_name" -F states, but it flushes all of states. Also pfctl -a "anchor_name" -s states shows all states, not anchor's ones.
Is it a bug?
 
Last edited by a moderator:
Is there a way to kill states that created by rules from specified anchor on FreeBSD 12.1?
I think your best bet is to use the label option on the rules that create the states (See pf.conf(5) for details). Maybe it's even possible to use label on the entire anchor, but I haven't tested it. Then you could use pfctl(8) to kill all states matching the given label:
Code:
             It is also possible to kill states by rule label or state ID.  In
             this mode the first -k argument is used to specify the type of
             the second argument.  The following command would kill all states
             that have been created from rules carrying the label “foobar”:

                   # pfctl -k label -k foobar
 
I've tested using of label on the entire anchor, but killing of states doesn't work. As I understood from documentation, PF's optimization doesn't work on rules with labels so I wouldn't like to use labels.
I can write my own state killer using PF's ioctl api, but it would be nice to have such functionality 'out of the box'.
 
I've tested using of label on the entire anchor, but killing of states doesn't work. As I understood from documentation, PF's optimization doesn't work on rules with labels so I wouldn't like to use labels.
I guess if PF's ruleset optimization substantially changes your ruleset then maybe you should reconsider your ruleset design.
 
I'm making a wrapper of PF for customers and I'm not responsible for rules that they'll create, so I rely on PF's optimizations.
Speaking of killing states by anchor, it would be useful for other projects, for example fail2ban https://github.com/fail2ban/fail2ban/issues/1924
pfctl -s states -vv displays (in numeric form) which anchor and rule within that anchor created the state as well as a unique state ID which can also be used to kill specific state entries by using pfctl -k id -k <StateID>. You would have to know what number is associated to your anchor though, which could prove somewhat problematic as it might change when the ruleset changes.
 
pfctl -s states -vv displays (in numeric form) which anchor and rule within that anchor created the state as well as a unique state ID which can also be used to kill specific state entries by using pfctl -k id -k <StateID>. You would have to know what number is associated to your anchor though, which could prove somewhat problematic as it might change when the ruleset changes.
I thought about this way but I couldn't get id of anchor and it didn't look quite fast. It seems that writing my own PF client in C is only solution.
Anyway I'm still want to know how pfctl -a "anchor_name" -F states should work. If there is a bug I could fix it in my free time, although it might take a time to dive into pfctl :)
 
I thought about this way but I couldn't get id of anchor and it didn't look quite fast. It seems that writing my own PF client in C is only solution.
Anyway I'm still want to know how pfctl -a "anchor_name" -F states should work. If there is a bug I could fix it in my free time, although it might take a time to dive into pfctl :)
I'd say that's a bit of a grey zone. The documentation states:
Code:
     -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.
This could suggest that something like pfctl -a anch -s states should work, but then again it only mentions rules which states are clearly not.

Using the ioctl interface in C code it should not be that hard to determine the numerical ID for a given anchor name, then search the state table for matching entries that were created by rules within that anchor. and finally kill all states previously matched.
 
Using the ioctl interface in C code it should not be that hard to determine the numerical ID for a given anchor name, then search the state table for matching entries that were created by rules within that anchor. and finally kill all states previously matched.
I've looked up in man and C headers but haven't tried to implement yet. I think it won't be so difficult.
Thanks a lot :)
 
I've done some research:

1. It's impossible to kill states by anchor's id because anchors don't have unique ids. In fact, anchor's id is an ordinal number in the anchor list of parent anchor. So two states with the same anchor id can relate to different anchors.

2. When nested anchor is added some inner anchor is added too. For example:
Code:
PfAnchor1
PfAnchor1/_1
PfAnchor1/_1/PfAnchor3
I haven't found any information about that.

3. pfctl -F all doesn't delete anchors from PF's memory. If do pfctl -a anchor -F all after, it removes anchor from the memory and displays
Code:
rules cleared
nat cleared
pfctl: Anchor or Ruleset does not exist.
 
1. It's impossible to kill states by anchor's id because anchors don't have unique ids. In fact, anchor's id is an ordinal number in the anchor list of parent anchor. So two states with the same anchor id can relate to different anchors.
Yes, I noticed that anchor numbers are just like rule numbers, i.e. an ascending number within the corresponding context/anchor. As long as you dont nest anchors that should not become a problem however. States on the other hand do have a unique ID that can be used to kill exactly that state that is referenced by it. The following sure aint pretty, can probably be done in a more elegant fashion and most definately will not work with nested anchors but otherwise it should do:
Code:
#! /bin/sh                                                                                                          
                                                                                                                    
if [ $# != 1 ]; then                                                                                              
        echo "USAGE $0 <anchor-name>"                                                                               
        exit 1                                                                                                      
fi                                                                                                                  
                                                                                                                    
anchornum=$(pfctl -s rules -vv | grep "anchor \"$1\"" | cut -d " " -f 1 | colrm 1 1)                                
stateids=$(pfctl -s states -vv | grep -A 1 "anchor ${anchornum}," | grep "id: " | cut -d " " -f 5)                  
                                                                                                                    
for i in $stateids; do                                                                                              
        echo "KILLME: $i"                                                                                           
done
Bonus points will be awarded for providing a functional awk implementation o_O

3. pfctl -F all doesn't delete anchors from PF's memory. If do pfctl -a anchor -F all after, it removes anchor from the memory and displays
Code:
rules cleared
nat cleared
pfctl: Anchor or Ruleset does not exist.
The -F all option includes various things (os fingerprints, states, tables, info) that are global, so effectively you cannot do pfctl -a anchor -F all. Use single options instead like pfctl -a anchor -F rules or pfctl -a anchor -F nat. Anchors are deleted when they are flushed of all rules (including nat and rdr rules) and no other rule references them anymore. From my experience this can take some time though.
 
#! /bin/sh if [ $# != 1 ]; then echo "USAGE $0 <anchor-name>" exit 1 fi anchornum=$(pfctl -s rules -vv | grep "anchor \"$1\"" | cut -d " " -f 1 | colrm 1 1) stateids=$(pfctl -s states -vv | grep -A 1 "anchor ${anchornum}," | grep "id: " | cut -d " " -f 5) for i in $stateids; do echo "KILLME: $i" done
I implemented the same in C :)

The -F all option includes various things (os fingerprints, states, tables, info) that are global, so effectively you cannot do pfctl -a anchor -F all. Use single options instead like pfctl -a anchor -F rules or pfctl -a anchor -F nat. Anchors are deleted when they are flushed of all rules (including nat and rdr rules) and no other rule references them anymore. From my experience this can take some time though.

I noticed that anchors stay in memory when experimented with my own PF client. My program extracts list of all anchors recursively, in this way I knew about hidden anchors. Pfctl doesn't show anchors that have been flushed of rules but my program does. Actually, it isn't a problem, just my notices.
 
I implemented the same in C :)

I noticed that anchors stay in memory when experimented with my own PF client. My program extracts list of all anchors recursively, in this way I knew about hidden anchors. Pfctl doesn't show anchors that have been flushed of rules but my program does. Actually, it isn't a problem, just my notices.
So you got it working then? gz
Out of curiosity, using the C API, did you find a way to extract only a subset of state table entries matching specified criteria, like source address, destination port number or protocol, without having to pull the complete list of states? I had a quick look at the documentation yesterday, but only noticed DIOCNATLOOK which looks like it needs a complete src/dst addr/port pair to work with, which is not suitable for my needs. I have a small script that is used as an external ACL helper in my squid proxy to allow/block requests to windows update and steam client updates when certain states are present in the state table, and i was thinking about reimplementing it in C.

As for the anchors, I think pfctl does list those, given the right options that is. What I found highly confusing at first is that it sometimes can take like ~60sec until an empty anchor finally disappears. I first noticed this behaviour with a script I use to dynamically add/delete firewall modules to open/close the firewall for stuff like locally hosted games.
 
I didn't, I extract all of states. It seems there isn't such opportunity.

I'm not sure but I think it can work without filling of all fields.
Then maybe I will take a closer look at DIOCNATLOOK again. The structure of the argument and the name containing NAT suggested it was made for something other than what I have in mind. I only need to check whether certain connections are currently active or not, i.e. TCP to destination port 6112 or UDP to destination ports 4379+4380, then output either "OK" or "ERR" to use it as an external squid ACL. Not sure if it's worth implementing this in C though. For now I replaced my shell script with an awk script that seems to work quite well.
 
The structure of the argument and the name containing NAT suggested it was made for something other than what I have in mind. I only need to check whether certain connections are currently active or not, i.e. TCP to destination port 6112 or UDP to destination ports 4379+4380, then output either "OK" or "ERR" to use it as an external squid ACL.
As I understand DIOCNATLOOK shows NAT table, so you can achieve your goals. Or you can use ssl_bumb in squid (full interception or reading sni) and filter by url.
 
As I understand DIOCNATLOOK shows NAT table, so you can achieve your goals. Or you can use ssl_bumb in squid (full interception or reading sni) and filter by url.
Those connections are no web connections and therefore do not pass through squid at all. The goal is to block certain web connections in squid when/while any of those connections is active in the firewall's state table.
 
Back
Top