You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
AI tools were used to create at least part of the text submitted herewith.
If AI was used, please disclose:
Model used: Claude
Extent of AI involvement: Proposed hypotheses and suggested debug steps; authored summary after debug session
Summary
In a two-node OPNsense CARP/pfsync HA pair, one node (opnsense-primary) never transmits a single pfsync frame, in either the initial bulk state-table transfer or ongoing incremental sync. This has been confirmed at four independent layers on the affected node — kernel counters, a capture on the physical interface, a capture on the underlying Proxmox host tap, and a capture on the pfsync0 pseudo-interface itself — all showing zero output.
The other node (opnsense-backup) sends pfsync normally — its outbound frames are confirmed reaching primary via matching netstat counters and packet-for-packet matching tcpdump captures on both ends. Backup's receive path, however, has never actually been observed working: netstat -s -p pfsync on backup shows 0 packets received (IPv4), total, since boot. Given there are only two nodes in this pair, that's fully consistent with — and not independent evidence against — primary's transmit failure: backup has nothing to receive because primary has never sent it anything. So only one direction of this link (backup → primary) has actually been demonstrated healthy; the reverse direction is unconfirmed, not confirmed-and-broken-elsewhere. The failure survives a full VM restart (not just an in-OS reboot) and is not explained by anything in the pf ruleset, HA config, ARP, ARP/FDB entries, EVPN control-plane state, or per-queue NIC driver statistics, all of which check out clean and symmetric between the two nodes.
Because the bulk transfer fails every time, CARP demotion never fully clears and accumulates a large stuck negative value on the master node, which in turn caused CARP Maintenance Mode to fail to force a failover (consistent with the existing report at opnsense/core#5930 re: maintenance mode not reliably working in virtualized environments) — a manual CARP disable was required to force mastership over to the healthy node.
Environment
OPNsense version: OPNsense 26.1.11_10 (amd64)
Platform: Proxmox VE, QEMU/KVM (meta: creation-qemu=11.0.2)
Two single-socket, 8-core, 8GB VMs, one per physical Proxmox host (pve-a, pve-b)
NIC model: virtio, queues=8 on every interface, including the pfsync-dedicated NIC
pfsync interface: vtnet5 (OPNsense-internal name opt3), dedicated point-to-point /30 (169.254.10.0/30, primary=.1, backup=.2), MTU 8930
The sync bridge on the Proxmox side is not a plain Linux bridge — it's a VXLAN/EVPN overlay (VNI 3) under a VRF, configured nolearning, with FRR handling MAC/IP route distribution via BGP EVPN
pfsyncversion (config token) / protocol version: 1400 (PFSYNCv5 in captures)
hasync config block matches exactly between nodes apart from the peer IP (verified via /conf/config.xml)
Symptom
Master node (opnsense-primary) accumulates a large state table over time (thousands of states); backup accumulates only what it creates for its own local services (dozens).
netstat -s -p pfsync on primary shows normal, climbing receive counters (states arriving from backup) but completely static send counters — state inserts sent, end of frame marks sent, etc. do not move at all across a full working session: multiple curl-triggered new states, a pfctl/HA config reapply, an ifconfig pfsync0 create re-parameterization, and a full restart of the VM.
Every pfsync bulk transfer attempt (triggered at boot, at manual pfsync0 reconfiguration, and at HA config reapply) fails with carp: demoted by -240 to X (pfsync bulk fail) in dmesg. The interval between pfsync bulk start and pfsync bulk fail is consistently ~65–90 kernel ticks, observed identically across multiple independent trigger events on both the master role and — earlier in the pair's life, on the other node while it briefly held the requester role — meaning the ~65–90 tick timeout itself is not new or unique to this node; what's unique to this node is that it never actually gets a frame onto the wire in the first place, request or otherwise.
Reproduction
Bring up a two-node OPNsense 25.x HA/CARP pair on Proxmox VMs, one VM per physical host, connected via a dedicated pfsync interface on a VXLAN/EVPN-backed bridge.
Let the pair run under normal traffic for some time (state churn on both nodes).
On the node that will exhibit the bug (in our case, whichever node was master at initial CARP bring-up): netstat -s -p pfsync | grep "state inserts sent".
Trigger new state creation (e.g. curl to any external host).
Re-check the same counter. Expected: it increases. Observed: no change, ever, regardless of how the state was created or how much time passes.
Confirm nothing crosses the wire for proto 240 from this node's pfsync IP, at any of: the guest's physical interface (tcpdump -ni vtnet5 -n proto 240 and src host <this-node-ip>), the Proxmox host's tap device for that NIC (tcpdump -ni tapNNNiM proto 240), and the pfsync0 pseudo-interface itself (tcpdump -ni pfsync0 -n).
Force a fresh bulk attempt (ifconfig pfsync0 create syncdev <if> syncpeer <peer-ip> maxupd 128 up, or toggle/reapply HA settings) — dmesg logs a pfsync bulk start followed ~65–90 ticks later by pfsync bulk fail, with zero packets observed in any of the three captures above during that window.
Fully restart the VM (host-level qm stop/qm start, not an in-guest reboot) — the behavior is identical post-restart. Send counters remain frozen from first boot.
What's been ruled out
Working through this collaboratively (see note on process below), the following were checked and are not the cause:
ARP — resolves correctly and is cached (arp -an shows a valid, current entry for the peer).
Physical interface health — netstat -i shows 0 input/output errors on the pfsync NIC across millions of packets of other traffic.
pfsync0 binding — ifconfig pfsync0 reports the correct syncdev, syncpeer, maxupd, and version, matching the saved config exactly (syncok: 1 throughout).
HA/pfsync config file — <hasync> block in /conf/config.xml is symmetric between both nodes (same pfsyncinterface, pfsyncversion; peer IPs correctly cross-referenced).
pf ruleset on the pfsync interface — no block/quick rule shadows the interface; all pass rules for that interface show normal, expected evaluation counts unrelated to pfsync.
Multiqueue/RSS on the virtio NIC — per-queue sysctl dev.vtnet.N.txqM.opackets shows all 8 transmit queues actively moving packets for other traffic sharing the same physical interface (an rsync process, unrelated to pfsync), ruling out a stuck-queue/hash-collision explanation for why proto 240 specifically never appears.
EVPN control plane / bridge FDB — bridge fdb show on both Proxmox hosts shows correct, mirrored extern_learn entries for each node's pfsync MAC pointing at the correct peer VTEP address; the overlay's control plane is healthy and symmetric.
VM/NIC configuration drift — qm config for both VMs shows identical NIC model, queue count, and bridge attachment for the pfsync interface; the only structural difference is which physical Proxmox host each VM runs on.
A one-off wedged runtime state — ruled out by the VM restart test; the exact same failure signature (frozen send counters, identical bulk-fail timing) reappeared from first boot.
CARP role / demotion arithmetic causing an apparent "won't fail over" symptom — this part is expected behavior, not a bug: the affected node's stuck demotion value (advskew + demotion) was large enough to keep it winning CARP elections regardless of Maintenance Mode, consistent with Enter Persistent CARP Maintenance Mode doesn't do anything core#5930's notes on maintenance mode being unreliable in virtualized environments; a manual disable CARP was needed to force failover. This is a downstream consequence of the pfsync bug, not a separate issue.
Current understanding / ask
At this point the failure appears to be entirely inside the FreeBSD kernel's carp/pfsync send path on the affected node — something prevents pfsync_out/the carp bulk-request logic from ever constructing or queuing an outbound frame, upstream of the network stack, the driver, and everything below it. We don't have kernel-debugging tooling (DTrace/ktrace on the relevant kqueue functions) applied yet, and would appreciate guidance on:
Any known FreeBSD/OPNsense issue matching "pfsync send counters frozen from boot, receive path unaffected, survives full VM restart"
Whether there's a known interaction between pfsync's output path and any of: VXLAN/EVPN-backed bridges specifically (as opposed to a plain L2 bridge or physical NIC), the nolearning bridge flag on the underlying overlay, or virtio-net on recent FreeBSD/OPNsense releases
What kernel-level tracing (ktrace, DTrace probes on carp/pfsync, or a debug sysctl) would help pin down where in the send path the frame is being dropped or never generated
Happy to gather ktrace/DTrace output, pfsync_undefer/related sysctl values, or anything else that would help narrow this further.
Note on how this report was produced
This write-up documents a diagnostic session conducted collaboratively between the system owner (who ran every command and supplied all raw output below) and Claude (Anthropic's AI assistant), which proposed hypotheses, suggested each diagnostic command, and interpreted results in real time. In the interest of full transparency:
All commands were run by the human operator, on real production hardware, and all output quoted or summarized above is genuine, unedited system output.
The AI proposed the diagnostic sequence and several intermediate theories, some of which were wrong and were later disproven by further testing — including, at various points, an incorrect read of packet-capture directionality, a since-abandoned theory that the OPNsense virtio NIC's multiqueue/RSS hashing was dropping proto 240 traffic onto a stalled queue, and an incorrect prediction that a full VM restart would resolve the issue. Each of these was corrected once contradicting evidence came in, and this report reflects only the conclusions that survived that process, not the discarded ones.
The final conclusion — a primary-side, boot-persistent, kernel-internal pfsync transmit failure — is the product of that iterative process, not a single diagnosis. Anyone acting on this report should treat the ruled-out list as reasonably solid (each item was checked against direct command output) but should independently verify anything critical before relying on it.
Important notices
Before you add a new report, we ask you kindly to acknowledge the following:
If AI was used, please disclose:
Summary
In a two-node OPNsense CARP/pfsync HA pair, one node (
opnsense-primary) never transmits a single pfsync frame, in either the initial bulk state-table transfer or ongoing incremental sync. This has been confirmed at four independent layers on the affected node — kernel counters, a capture on the physical interface, a capture on the underlying Proxmox host tap, and a capture on thepfsync0pseudo-interface itself — all showing zero output.The other node (
opnsense-backup) sends pfsync normally — its outbound frames are confirmed reaching primary via matchingnetstatcounters and packet-for-packet matching tcpdump captures on both ends. Backup's receive path, however, has never actually been observed working:netstat -s -p pfsyncon backup shows0 packets received (IPv4), total, since boot. Given there are only two nodes in this pair, that's fully consistent with — and not independent evidence against — primary's transmit failure: backup has nothing to receive because primary has never sent it anything. So only one direction of this link (backup → primary) has actually been demonstrated healthy; the reverse direction is unconfirmed, not confirmed-and-broken-elsewhere. The failure survives a full VM restart (not just an in-OS reboot) and is not explained by anything in the pf ruleset, HA config, ARP, ARP/FDB entries, EVPN control-plane state, or per-queue NIC driver statistics, all of which check out clean and symmetric between the two nodes.Because the bulk transfer fails every time, CARP demotion never fully clears and accumulates a large stuck negative value on the master node, which in turn caused CARP Maintenance Mode to fail to force a failover (consistent with the existing report at opnsense/core#5930 re: maintenance mode not reliably working in virtualized environments) — a manual CARP disable was required to force mastership over to the healthy node.
Environment
meta: creation-qemu=11.0.2)pve-a,pve-b)virtio,queues=8on every interface, including the pfsync-dedicated NICvtnet5(OPNsense-internal nameopt3), dedicated point-to-point /30 (169.254.10.0/30, primary=.1, backup=.2), MTU 8930syncbridge on the Proxmox side is not a plain Linux bridge — it's a VXLAN/EVPN overlay (VNI 3) under a VRF, configurednolearning, with FRR handling MAC/IP route distribution via BGP EVPNpfsyncversion(config token) / protocol version: 1400 (PFSYNCv5 in captures)hasyncconfig block matches exactly between nodes apart from the peer IP (verified via/conf/config.xml)Symptom
opnsense-primary) accumulates a large state table over time (thousands of states); backup accumulates only what it creates for its own local services (dozens).netstat -s -p pfsyncon primary shows normal, climbing receive counters (states arriving from backup) but completely static send counters —state inserts sent,end of frame marks sent, etc. do not move at all across a full working session: multiple curl-triggered new states, apfctl/HA config reapply, anifconfig pfsync0 createre-parameterization, and a full restart of the VM.pfsync0reconfiguration, and at HA config reapply) fails withcarp: demoted by -240 to X (pfsync bulk fail)in dmesg. The interval betweenpfsync bulk startandpfsync bulk failis consistently ~65–90 kernel ticks, observed identically across multiple independent trigger events on both the master role and — earlier in the pair's life, on the other node while it briefly held the requester role — meaning the ~65–90 tick timeout itself is not new or unique to this node; what's unique to this node is that it never actually gets a frame onto the wire in the first place, request or otherwise.Reproduction
netstat -s -p pfsync | grep "state inserts sent".curlto any external host).tcpdump -ni vtnet5 -n proto 240 and src host <this-node-ip>), the Proxmox host's tap device for that NIC (tcpdump -ni tapNNNiM proto 240), and thepfsync0pseudo-interface itself (tcpdump -ni pfsync0 -n).ifconfig pfsync0 create syncdev <if> syncpeer <peer-ip> maxupd 128 up, or toggle/reapply HA settings) — dmesg logs apfsync bulk startfollowed ~65–90 ticks later bypfsync bulk fail, with zero packets observed in any of the three captures above during that window.qm stop/qm start, not an in-guest reboot) — the behavior is identical post-restart. Send counters remain frozen from first boot.What's been ruled out
Working through this collaboratively (see note on process below), the following were checked and are not the cause:
arp -anshows a valid, current entry for the peer).netstat -ishows 0 input/output errors on the pfsync NIC across millions of packets of other traffic.pfsync0binding —ifconfig pfsync0reports the correctsyncdev,syncpeer,maxupd, andversion, matching the saved config exactly (syncok: 1throughout).<hasync>block in/conf/config.xmlis symmetric between both nodes (samepfsyncinterface,pfsyncversion; peer IPs correctly cross-referenced).block/quickrule shadows the interface; all pass rules for that interface show normal, expected evaluation counts unrelated to pfsync.sysctl dev.vtnet.N.txqM.opacketsshows all 8 transmit queues actively moving packets for other traffic sharing the same physical interface (an rsync process, unrelated to pfsync), ruling out a stuck-queue/hash-collision explanation for why proto 240 specifically never appears.bridge fdb showon both Proxmox hosts shows correct, mirroredextern_learnentries for each node's pfsync MAC pointing at the correct peer VTEP address; the overlay's control plane is healthy and symmetric.qm configfor both VMs shows identical NIC model, queue count, and bridge attachment for the pfsync interface; the only structural difference is which physical Proxmox host each VM runs on.advskew + demotion) was large enough to keep it winning CARP elections regardless of Maintenance Mode, consistent with Enter Persistent CARP Maintenance Mode doesn't do anything core#5930's notes on maintenance mode being unreliable in virtualized environments; a manualdisable CARPwas needed to force failover. This is a downstream consequence of the pfsync bug, not a separate issue.Current understanding / ask
At this point the failure appears to be entirely inside the FreeBSD kernel's carp/pfsync send path on the affected node — something prevents
pfsync_out/the carp bulk-request logic from ever constructing or queuing an outbound frame, upstream of the network stack, the driver, and everything below it. We don't have kernel-debugging tooling (DTrace/ktrace on the relevant kqueue functions) applied yet, and would appreciate guidance on:nolearningbridge flag on the underlying overlay, or virtio-net on recent FreeBSD/OPNsense releasescarp/pfsync, or a debug sysctl) would help pin down where in the send path the frame is being dropped or never generatedHappy to gather ktrace/DTrace output,
pfsync_undefer/related sysctl values, or anything else that would help narrow this further.Note on how this report was produced
This write-up documents a diagnostic session conducted collaboratively between the system owner (who ran every command and supplied all raw output below) and Claude (Anthropic's AI assistant), which proposed hypotheses, suggested each diagnostic command, and interpreted results in real time. In the interest of full transparency: