>Synopsis: pf af-to states count both directions in
packets[0]/bytes[0]
>Category: kernel
>Environment:
System : OpenBSD 8.0
Details : OpenBSD 8.0 (GENERIC.MP) #84: Tue Oct 6 23:13:49 MDT 2026
[email protected]:/usr/src/sys/arch/amd64/compile/GENERIC.MP
Architecture: OpenBSD.amd64
Machine : amd64
>Description:
For states created by an af-to rule, all traffic is counted
in the first of the two state counters. The second one
stays at zero, so pfctl -vvss shows "N:0 pkts, N:0 bytes".
>How-To-Repeat:
Taking three VMM instances inside <-> router <-> outside
Outside is configured with IPv4-only
Inside is configured with IPv6-only
Router performs NAT64 between them.
router# uname -a
OpenBSD router.example.com 8.0 GENERIC#82 amd64
router# cat /etc/hostname.vio0
inet 10.0.1.1 255.255.255.0
router# cat /etc/hostname.vio1
inet6 fec0::1 64
router# grep forwarding /etc/sysctl.conf
net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1
router# pfctl -sr
block return all
pass all flags S/SA
pass in on vio1 inet6 from fec0::/64 to 64:ff9b::/96 flags S/SA af-to
inet from (vio0) round-robin
router# pfctl -z
inside# ping6 -c 5 64:ff9b::10.0.1.2
PING 64:ff9b::10.0.1.2 (64:ff9b::a00:102): 56 data bytes
64 bytes from 64:ff9b::a00:102: icmp_seq=0 hlim=63 time=27.565 ms
64 bytes from 64:ff9b::a00:102: icmp_seq=1 hlim=63 time=49.198 ms
64 bytes from 64:ff9b::a00:102: icmp_seq=2 hlim=63 time=44.837 ms
64 bytes from 64:ff9b::a00:102: icmp_seq=3 hlim=63 time=56.343 ms
64 bytes from 64:ff9b::a00:102: icmp_seq=4 hlim=63 time=57.229 ms
--- 64:ff9b::10.0.1.2 ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 27.565/47.034/57.229/10.762 ms
router# pfctl -vsr
block return all
[ Evaluations: 1 Packets: 0 Bytes: 0 States: 0 ]
[ Inserted: uid 0 pid 70082 State Creations: 0 ]
pass all flags S/SA
[ Evaluations: 1 Packets: 0 Bytes: 0 States: 0 ]
[ Inserted: uid 0 pid 70082 State Creations: 0 ]
pass in on vio1 inet6 from fec0::/64 to 64:ff9b::/96 flags S/SA af-to
inet from (vio0) round-robin
[ Evaluations: 1 Packets: 10 Bytes: 940 States: 1 ]
[ Inserted: uid 0 pid 70082 State Creations: 1 ]
router# pfctl -vvss
all ipv6-icmp 10.0.1.1:53742 (fec0::2[42513]) -> 10.0.1.2:8
(64:ff9b::a00:102[53742]) 0:0
age 00:00:05, expires in 00:00:09, 10:0 pkts, 940:0 bytes, rule 2
id: 6ae66863000000e1 creatorid: 70d89c11
>Fix:
This reporter is not fluent in OpenBSD kernel, so he consulted a tool
from Anthropic.
Feel free to disregard its advice, pasted below:
Choose the counter by address family for af-to states, as
pf_icmp_state_lookup()
already does in sys/net/pf.c:6303-6317. Diff against -current:
--- sys/net/pf.c
+++ sys/net/pf.c
@@ -8278,7 +8278,15 @@
sni->sn->packets[dirndx]++;
sni->sn->bytes[dirndx] += pd->tot_len;
}
- dirndx = (pd->dir == st->direction) ? 0 : 1;
+ /*
+ * With af-to both directions enter on the rule's interface
+ * side, so pd->dir always equals st->direction; use the
+ * address family to tell the directions apart instead.
+ */
+ if (st->key[PF_SK_WIRE]->af != st->key[PF_SK_STACK]->af)
+ dirndx = (pd->af == st->key[PF_SK_WIRE]->af) ? 0
: 1;
+ else
+ dirndx = (pd->dir == st->direction) ? 0 : 1;
st->packets[dirndx]++;
st->bytes[dirndx] += pd->tot_len;
pf_counters_inc() runs before pf_translate_af(), so pd->af
is still the family the packet arrived with. With this
change, pfctl shows the counts split by direction, and
pflow exports the second (reverse) record for af-to states
without any change to if_pflow.c.
--
Pim van Pelt
PBVP1-RIPE - https://ipng.ch/