userspace: add port mirror snapshot foundation#1392
Conversation
There was a problem hiding this comment.
Pull request overview
Adds the initial Go-side “port mirroring” snapshot representation for the userspace dataplane, deriving per-ingress mirror mappings from forwarding-options port-mirroring while keeping capability admission fail-closed until runtime clone/inject support exists.
Changes:
- Extend the userspace control-plane snapshot protocol with
mirror_configsentries (ingress ifindex, output ifindex, sampling rate). - Build mirror config snapshots from config + resolved interface ifindexes, rejecting duplicate ingress ifindexes and skipping unresolved outputs.
- Add Go tests for JSON wire stability and mirror snapshot building; keep the userspace capability gate rejecting port mirroring.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| pkg/dataplane/userspace/snapshot.go | Adds mirror snapshot derivation and wires it into buildSnapshot(). |
| pkg/dataplane/userspace/protocol.go | Adds MirrorConfigs to ConfigSnapshot and defines MirrorConfigSnapshot. |
| pkg/dataplane/userspace/protocol_test.go | Adds JSON round-trip / wire-key pin test for mirror_configs. |
| pkg/dataplane/userspace/manager_test.go | Adds tests for fail-closed capability gating and mirror snapshot builder behavior. |
| out = append(out, MirrorConfigSnapshot{ | ||
| IngressIfindex: ingressIfindex, | ||
| OutputIfindex: outputIfindex, | ||
| Rate: uint32(inst.InputRate), | ||
| }) |
| func mustBuildMirrorConfigSnapshots(cfg *config.Config, interfaces []InterfaceSnapshot) []MirrorConfigSnapshot { | ||
| mirrors, err := buildMirrorConfigSnapshots(cfg, interfaces) |
|
|
||
| func TestConfigSnapshotMirrorConfigsRoundTrip(t *testing.T) { | ||
| in := ConfigSnapshot{ | ||
| Version: 1, |
Claude round-1 review on
|
Round-1 review consolidated on
|
| Reviewer | Verdict |
|---|---|
| Claude | MERGE-READY (foundation) |
| Codex | MERGE-NEEDS-MINOR |
| Copilot | (no review yet on this HEAD) |
Codex MINOR — Test coverage thinner than scope
"protocol_test.go:63 only proves Go self round-trip for
mirror_configs. Current RustConfigSnapshothas nomirror_configsfield and nodeny_unknown_fields, so older/current xpfd should ignore the added JSON field, but this is not pinned by a Rust compatibility test."
And:
"manager_test.go:1903 tests
buildMirrorConfigSnapshotsdirectly, not the real buildSnapshot integration point."
Same "production publish path coverage" gap pattern as #1396. The mirror_configs field is built by the helper but not necessarily wired into the publishing path. A regression that stops wiring MirrorConfigs into the published snapshot wouldn't be caught.
What Codex verified clean
- Capability gate stays closed: manager.go:775 rejects port-mirroring configs ✓
- Missing-reference fail-closed: missing output/input ifindexes produce warn + skip, duplicate ingress errors. ✓
- Diff scope: exactly 4 files, +236/-0. ✓
Recommendation
Block on: nothing structural.
Strongly consider: add a Rust-side decode test confirming mirror_configs is tolerated by current ConfigSnapshot (forward-compat), and an integration test through buildSnapshot (not just buildMirrorConfigSnapshots helper).
Defer to follow-up PRs: Rust mirror egress path, UMEM buffer-doubling accounting, backpressure semantics, failover (no clone-state sync), capability gate removal.
Codex task: task-mp9559w2-zumig4. Not merging — author's decision.
|
Round-2 update pushed in ec4e9cb. Closed the review coverage gap with:
Validation:
|
Claude round-2 review on
|
Round-2 review consolidated on
|
| Reviewer | Verdict |
|---|---|
| Claude | MERGE-READY |
| Codex | MERGE-READY |
Round-1 MINOR (test coverage gap) closed cleanly. 3 files, +90/-0:
- Rust round-trip + forward-compat test for
mirror_configs✓ - Real
buildSnapshotintegration test ✓ - Capability gate unchanged at
manager.go:775✓
Codex finding: no new MAJORs.
Recommendation: MERGE-READY. Defer to follow-up PRs: Rust mirror egress path, UMEM buffer-doubling accounting, backpressure semantics, failover, capability gate removal.
Codex task: task-mp96mlkx-0s1hx5. Not merging — author's decision.
ec4e9cb to
4586233
Compare
Rebased after #1395 mergeRebased the port-mirror snapshot PR onto current What changed in the conflict resolution:
Validation run on
Both passed. Cargo still emits the existing warning set. |
|
@copilot review\n\nPlease re-review current HEAD after the latest fix-forward/rebase push. |
| func mustBuildMirrorConfigSnapshots(cfg *config.Config, interfaces []InterfaceSnapshot) []MirrorConfigSnapshot { | ||
| mirrors, err := buildMirrorConfigSnapshots(cfg, interfaces) | ||
| if err != nil { | ||
| // The mirror table contract is one output per ingress ifindex. Keep | ||
| // snapshot publication fail-closed by omitting an ambiguous mirror table | ||
| // if this helper is called on an invalid config. | ||
| slog.Warn("userspace snapshot: invalid port-mirroring config skipped", "err", err) | ||
| return nil | ||
| } | ||
| return mirrors | ||
| } |
| out = append(out, MirrorConfigSnapshot{ | ||
| IngressIfindex: ingressIfindex, | ||
| OutputIfindex: outputIfindex, | ||
| Rate: uint32(inst.InputRate), | ||
| }) | ||
| } |
Agent-Logs-Url: https://github.com/psaab/xpf/sessions/11ea982a-ae2c-47db-b452-b224abc2589b Co-authored-by: psaab <196946+psaab@users.noreply.github.com>
Agent-Logs-Url: https://github.com/psaab/xpf/sessions/11ea982a-ae2c-47db-b452-b224abc2589b Co-authored-by: psaab <196946+psaab@users.noreply.github.com>
Agent-Logs-Url: https://github.com/psaab/xpf/sessions/11ea982a-ae2c-47db-b452-b224abc2589b Co-authored-by: psaab <196946+psaab@users.noreply.github.com>
Summary
References #1376.
Tests
Remaining gaps