I run an NTP on a pfsense server and while it works well, the state table fills up with unneeded cruft. I know that on Linux one can use iptables to bypass the conntrack and not fill up the Linux equivalent table.
Any idea what the FreeBSD equivalent of that is? I’m not even certain what to search for.
I have only experience using opnsense (a fork of pfsense and also built on FreeBSD).
I can set “State Type” for each firewall rule to “None”, thereby disabling connection tracking.
This is done through GUI, dont know the CLI command.
Maybe more information is available on the FreeBSD website: pf.conf(5)
Thanks. That seems like it should be the right setting, but when I apply the change and restart the firewall there’s no subsequent decrease in the size of the state table.
I’m serving up several thousand NTP requests per second, which leaves my state tables with around 200,000 - 300,000 entries. It’s not hurting anything that I can see, but I’d rather the CPU didn’t spend energy doing something so fruitless.
The pfsense box is serving NTP clients, and not merely routing NTP traffic?
Not to imply I have any idea what I’m talking about so pardon the wild speculation, but if that’s the case, then perhaps it actually needs to keep track of UPD connections when it itself is the final destination of it?
Not sure what makes you think this might be the case, so apologies if the following doesn’t address that. But in general, the state that the protocols keep, and the state of the firewall connection tracking system are independent of each other. And the firewall connection tracking system always sees packets just passing by, independent of whether they are being forwarded, or whether they are being terminated on the local system.
In a way, the firewall connection tracking system tries to “reverse engineer” the actual protocol state from the packets it sees. The difference between forwarded and locally terminated packets is mostly that owing to the (partially) different paths they take through the networking stack, the locations within the networking code where they are being seen (“hooks”), and where they can be affected in some way (filtered, modified, …), are somewhat different between the two. E.g., iptables chains INPUT and OUTPUT vs. chain FORWARD in the “filter” table, relating to the similarly named Netfilter hooks.
In fact, in case of UDP, there isn’t even real “connection” state on the protocol level (in the sense of state that, e.g., TCP or SCTP, maintain). Rather, the “connection” state that the firewall keeps for UDP “connections” is “made up” just for filtering purposes (and, e.g., in the Linux kernel connection tracking system, is sometimes referred to as “stream” rather than “connection”). Along the lines that “if there was a packet out, it is likely to see some mirror packet(s) not too long after, so we assume they are legitimate reply packets”.
But that is just an assumption, just as the timeout value used is a “one-size-fits-all” value derived from assumptions regarding “typical” behavior of UDP-based applications. Thus, the timeout value is usually configurable by the user who might know better how actual applications relevant to them actually behave. E.g., the timeout could be shortened to be closer to the (typical) lifetime of rather short-lived “connections”, such as many/most NTP packet exchanges, reducing the number of entries that need to be kept at any one time.
I sometimes find it helpful to use the quick keyword for this sort of thing with pf to logically put “bypass the rules”, uh, rules first. (pf is “last match wins”). Be sure to pass in quickandpass out quick, and do it before any rules that create state of course.
I moved the rule to the floating set, put it in first place, UDP, In&Out, and added the quick option; no joy.
I did some more digging using the CLI to examine the state table (the browser couldn’t handle displaying it considering its size).
What I found was a lot of this:
all udp 73.65.80.137:123 -> 45.16.71.105:45754 SINGLE:NO_TRAFFIC
age 00:00:58, expires in 00:00:02, 1:0 pkts, 76:0 bytes, rule 102, allow-opts, log
I have no rule with that description in the UI, so I again used pfctl to find it:
pfctl -vvsr | grep '@102\b' -A3
@102 pass out route-to (igc0 73.65.80.1) inet from 73.65.80.137 to ! 73.65.80.0/22 flags S/SA keep state allow-opts label "let out anything from firewall host itself" ridentifier 1000004761
[ Evaluations: 3530871129 Packets: 1248772941 Bytes: 7333085712 States: 468849]
[ Inserted: uid 0 pid 76842 State Creations: 1065956]
[ Last Active Time: Wed May 21 14:44:14 2025 ]
That’s certainly the culprit. But I don’t understand why it’s getting hit when my floating quick rule is rule zero. Shouldn’t that allow the traffic and then stop processing rules?