Contributed by Peter N. M. Hansteen on 2026-01-16 from the raising my family dept.
Seasoned networkers will know to tell you that legacy IPv4 and modern IPv6 are, in fact, not directly compatible, and shipping traffic between IPv4 and IPv6 networks requires address family translation
On our favorite operating system and its siblings, that special case has been handled via the af-to option and special case rules since back in the OpenBSD 5.1 days.
But that special case has always felt a bit awkward to some, and now David Gwynne ( dlg@ ) is airing a patch on tech@ with a view to making af-to "less magical".
In the message titled pf: make af-to less magical, David explains the motivation,
List: openbsd-tech Subject: pf: make af-to less magical From: David Gwynne
this difference means that af-to is special cased in pf a lot. i'm arguing that it would be better if we made af-to less special, which is what this diff does. the important bit here is af-to no longer forces forwarding of packets. this is implemented by having pf_test called by ip_input push the translated packet into ip6_input instead of ip6_forward. this leads to a problem where we need to skip processing by pf_test in this nested ip6_input call after the packet has been translated. this is the same problem we already have in ip_output and ip6_output when we redirect or reroute outgoing packets, which is solved using a little state machine with the PF_TAG_GENERATED and PF_TAG_REROUTE flags on mbufs. this diff makes this state machine work on the ip_input and ip6_input side to support af-to. because the translated packets are now handed to the other ip_input/ip6_input handler, it allows the local tcp/udp/etc stack to handle these translated packets like we do for rdr-to. we can also use this same semantic to support translation for "pass out" rules. this allows us to translate ipv4 or ipv6 packets from the local network stack to the other address family, and it Just Works(tm). there is a downside from an operator point of view though. because af-to no longer forces forwarding, forwarded connections now require rules to allow the outgoing traffic to pass. ie, this worked before: block pass in on em0 inet proto udp \ from 192.168.1.1 to 192.168.2.1 \ af-to from 2407::1 to 2407:2::1 but now you need to add this rule too: pass out inet6 proto udp \ from 2407::1 to 2407:2::1 however, the code is so much simpler. we no longer have to special case and swap key indices around to cope with there only being one af-to state to cover both sides of a forwarded connection anymore. thoughts? i've only done minimal testing. my interest is mostly in making the code simpler, so i'd appreciate feedback from people who actually use af-to about whether it still works or not.
followed by the patch that implements the change.
If you handle network environments where address family translation is something you do or if it is on your horizon, please offer your input after evaluating the proposed patch.
Would the changes David proposes make your life easier, or how could this be improved?