Daniel P. Berrangé wrote:
On Sat, Apr 26, 2025 at 09:42:36AM +0200, Roman Bogorodskiy wrote:
> Implement NAT networking support based on the Packet Filter (pf)
> firewall in FreeBSD. At this point, the implementation is very basic.
> It creates:
>
> - Essential NAT translation rules
> - Basic forwarding rules
>
> Implementation uses pf's anchor feature to group rules. All rules live
> in the "libvirt" anchor and every libvirt's network has its own
> sub-anchor.
>
> Currently there are some assumptions and limitations:
>
> - We assume that a user has created the "libvirt" (nat-)anchors. As
> they cannot be created on fly, it's better not to touch global pf
> configuration and let the user do the changes. If the user doesn't
> have these anchors configured, the rules will still be created in
> sub-anchors, but will not be effective until these anchors are
> activated. Should we check if these anchors are not active to
> give some runtime warning?
So IIUC a PF 'anchor' is essentially a top level namespace
under which all libvirt rules will live, so they are isolated
from other system rules ?
Yes, that's correct.
This sounds similar to how we deal with the new 'nft'
firewall
on Linux, where we have to create top level tables to hold
all our rules
That has thrown up a tricky problem for us though around the
prioritization. A packet has to be accepted by *every* top
level table, for it to be allowed. So even if a libvirt rule
allows it, potentiallly other system rules can still block
it. This is basically unsolvable in the general case. We do
a special hack in the firewalld case to tell it to put the
bridge device into a separate libvirt "zone", which in
turn means none of the other firewalld rules will ever get
applied to those packets.
It depends. In PF, rules could be created with the "quick" keyword.
Quoting the manual page:
Matching filter and translation rules marked with the quick option are
final and abort the evaluation of the rules in other anchors and the main
ruleset. If the anchor itself is marked with the quick option, ruleset
evaluation will terminate when the anchor is exited if the packet is
matched by any rule within the anchor.
I'm using this keyword for some of the pass rules to avoid these packets
occasionally blocked.
Back to PF though. You're saying we can't create anchors on
the fly - so that's saying the only way to create the anchors
is to edit the /etc/ config file, and those get applied on
system boot ?
Yes, that's true. Well, in theory, we can edit the /etc/pf.conf, try to
find an appropriate place for anchors definitions, and reload the rules.
I saw some management software does that, but that seems to be too
invasive for me, at least for now.
We should probably document this in docs/drvnetwork.rst...
which I see does not actually exist :-( Probably good to
start a skeleton of a page and put the FreeBSD specific
notes there, and we can fll out Linux notes later
> - Currently, rule reloading is not smart: it always deletes rules,
> flushes rules and re-creates that. It would be better to do that
> more gracefully.
That's not especially worse than what we do on Linux either
> - IPv6 configurations are currently not supported
Probably worth reporting a fatal error if you see IPv6
config in the XML input, so attempts to use IPV6 are
not silently ignored.
> - For NAT, pf requires explicit IP address or an interface to NAT to.
> We try to obtain that from the network XML definition, and if it's
> not specified, we try to determine interface corresponding to the
> default route.
On Linux the defaults should work based on routing, so that's not
too bad as a default behaviour. The difference is that FreeBSD
would only work based on the single route, while the Linux behaviour
could use all available routes. Not the end of the world, since
most machines only have a single default route. The common exception
being people using VPN clients.
Thanks for feedback, will address the comments in the follow up series.
Roman