Gap
The eBPF dataplane implements forwarding-options port-mirroring by reading a mirror_config map keyed on ingress ifindex and cloning the packet to the configured output interface with optional 1-in-N sampling. The userspace dataplane has zero implementation. Configs with forwarding-options port-mirroring are explicitly rejected by the userspace capability gate.
eBPF implementation (source of truth)
bpf/headers/xpf_common.h:888 — struct mirror_config { __u32 output_ifindex; __u32 rate; }
bpf/headers/xpf_maps.h:824-826 — mirror_config BPF map definition
bpf/xdp/xdp_forward.c:330-340 — XDP-side ingress mirror: look up mirror_config by ingress_key, redirect clone
bpf/tc/tc_forward.c:73-109 — TC-side mirror with sampling rate, bpf_clone_redirect() to output ifindex
pkg/dataplane/compiler.go:1506-1547 — compilePortMirroring() populates the BPF map
pkg/dataplane/maps.go:831 — SetMirrorConfig() writes a mirror entry
pkg/dataplane/userspace/manager.go:775-777 — capability gate
Userspace-dp gap
Searched: grep -rn 'port.mirror\\|port_mirror\\|PortMirror' userspace-dp/src/ — only generic mirroring-as-prose matches (unrelated to port mirroring), no implementation.
Recommended fix
In userspace-dp/src/afxdp/:
- Add
MirrorConfig { output_ifindex, rate } snapshot + protocol field
- On packet ingress, after policy/forwarding decision: if mirror rule matches, allocate a copy frame from UMEM, build cloned packet, push to output ifindex TX queue
- Implement 1-in-N sampling via per-binding counter
- Surface mirror packet counters in status
- Remove the
cfg.ForwardingOptions.PortMirroring != nil gate in pkg/dataplane/userspace/manager.go:775
Note: UMEM is per-binding queue-bound — mirror to a different physical interface requires either a cross-binding inject (similar to fabric redirect) or attaching a slow-path TUN sink.
Blocker for #1373
This must land before Phase 4 (BPF source removal) of #1373 (retire eBPF dataplane). Currently the userspace capability gate falls back to the eBPF dataplane when this config is set.
Refined contract (added 2026-05-17 after triple-review of #1384)
See docs/pr/1373-retire-ebpf-dataplane/plan-1376-port-mirroring.md for the full implementation contract refined through 4 rounds of Claude+Codex+Gemini Pro 3 review. New since the original issue body:
- Risks called out: mirror backpressure (mirror egress drop must not stall primary forwarding), failover handoff (no mirror-clone state sync between peers — new active mirrors per current config), buffer doubling (mirror clone must not double-charge UMEM accounting).
- Required test added: Go
deriveUserspaceCapabilities() admits port-mirroring configs only after userspace snapshot + Rust runtime support are wired.
Gap
The eBPF dataplane implements
forwarding-options port-mirroringby reading amirror_configmap keyed on ingress ifindex and cloning the packet to the configured output interface with optional 1-in-N sampling. The userspace dataplane has zero implementation. Configs withforwarding-options port-mirroringare explicitly rejected by the userspace capability gate.eBPF implementation (source of truth)
bpf/headers/xpf_common.h:888—struct mirror_config { __u32 output_ifindex; __u32 rate; }bpf/headers/xpf_maps.h:824-826—mirror_configBPF map definitionbpf/xdp/xdp_forward.c:330-340— XDP-side ingress mirror: look upmirror_configbyingress_key, redirect clonebpf/tc/tc_forward.c:73-109— TC-side mirror with sampling rate,bpf_clone_redirect()to output ifindexpkg/dataplane/compiler.go:1506-1547—compilePortMirroring()populates the BPF mappkg/dataplane/maps.go:831—SetMirrorConfig()writes a mirror entrypkg/dataplane/userspace/manager.go:775-777— capability gateUserspace-dp gap
Searched:
grep -rn 'port.mirror\\|port_mirror\\|PortMirror' userspace-dp/src/— only genericmirroring-as-prose matches (unrelated to port mirroring), no implementation.Recommended fix
In
userspace-dp/src/afxdp/:MirrorConfig { output_ifindex, rate }snapshot + protocol fieldcfg.ForwardingOptions.PortMirroring != nilgate inpkg/dataplane/userspace/manager.go:775Note: UMEM is per-binding queue-bound — mirror to a different physical interface requires either a cross-binding inject (similar to fabric redirect) or attaching a slow-path TUN sink.
Blocker for #1373
This must land before Phase 4 (BPF source removal) of #1373 (retire eBPF dataplane). Currently the userspace capability gate falls back to the eBPF dataplane when this config is set.
Refined contract (added 2026-05-17 after triple-review of #1384)
See
docs/pr/1373-retire-ebpf-dataplane/plan-1376-port-mirroring.mdfor the full implementation contract refined through 4 rounds of Claude+Codex+Gemini Pro 3 review. New since the original issue body:deriveUserspaceCapabilities()admits port-mirroring configs only after userspace snapshot + Rust runtime support are wired.