>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/

Reply via email to