pfr_pool_get() in sys/net/pf_table.c hardcodes AF_INET at the
_next_entry label (line 2773) where it should use the af parameter:
pfr_prepare_network(&mask, AF_INET, ke2->pfrke_net);
For IPv6, this fills the wrong struct member (sin.sin_addr instead
of sin6.sin6_addr), producing an all-zeros mask. The nested block
skip fails: the pool wraps instead of advancing. When pfrke_net
is > 32 the shift count goes negative (UB). Every other call to
pfr_prepare_network in the file uses the correct af.
Tested on OpenBSD 7.9-beta amd64 with a /126 table containing a
nested /128, nat-to round-robin, 8 connections. Stock kernel
never reaches the 4th address. Patched kernel uses all four.
Index: sys/net/pf_table.c
===================================================================
--- sys/net/pf_table.c
+++ sys/net/pf_table.c
@@ -2770,7 +2770,7 @@ pfr_pool_get(struct pf_pool *rpool, stru
}
_next_entry:
/* we need to increase the counter past the nested block */
- pfr_prepare_network(&mask, AF_INET, ke2->pfrke_net);
+ pfr_prepare_network(&mask, af, ke2->pfrke_net);
pf_poolmask(addr, addr, SUNION2PF(&mask, af), &pfr_ffaddr, af);
pf_addr_inc(addr, af);
if (!pf_match_addr(0, *raddr, *rmask, addr, af)) {