Skip to content

Feature gap: port mirroring implemented in eBPF, missing from userspace-dp #1376

@psaab

Description

@psaab

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:888struct mirror_config { __u32 output_ifindex; __u32 rate; }
  • bpf/headers/xpf_maps.h:824-826mirror_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-1547compilePortMirroring() populates the BPF map
  • pkg/dataplane/maps.go:831SetMirrorConfig() 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/:

  1. Add MirrorConfig { output_ifindex, rate } snapshot + protocol field
  2. 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
  3. Implement 1-in-N sampling via per-binding counter
  4. Surface mirror packet counters in status
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions