Skip to content

feat: Add TC-BPF uSID program and bit-layout library - #281

Merged
privateip merged 3 commits into
mainfrom
pr0-ebpf-prog
Aug 6, 2026
Merged

feat: Add TC-BPF uSID program and bit-layout library#281
privateip merged 3 commits into
mainfrom
pr0-ebpf-prog

Conversation

@privateip

Copy link
Copy Markdown
Contributor

Summary

This is the first PR in a stack that cuts SRv6 ingress over from a per-route netlink mechanism to a shared eBPF/TC-BPF datapath. It lands the lowest-level, dependency-free pieces on their own so the rest of the stack has something to build on.

Adds the compiled TC-BPF usid_ingress program (decodes/decaps/redirects uSID traffic) with its bpf2go-generated Go bindings, and a pure-Go uformat library that encodes/decodes the uFMT 48+16 SID layout (Block / Node-ID / Function / Argument). Both the eBPF datapath and the BGP control plane will depend on uformat so they can never drift on bit positions. Pulls in github.com/cilium/ebpf.

Nothing outside internal/plumbing/ebpf/{prog,uformat} changes here — no call sites exist yet, so this is inert until the next PR in the stack wires it up.

Test plan

  • go build ./...
  • go test ./internal/plumbing/ebpf/... (BPF-map/BPF_PROG_TEST_RUN cases skip without root/CAP_BPF, as expected in this environment)

Part of the eBPF uSID datapath cutover (stack: this PR → codegen toolchain → datapath control-plane packages → CNI cutover → GC/installer wiring → deploy/e2e wiring).

privateip added a commit that referenced this pull request Aug 5, 2026
Removes the legacy per-route netlink SRv6 ingress mechanism
(srv6.RouteIngressAdd/Del, srv6.go) entirely and replaces it with
registration against the eBPF uSID datapath's pinned maps (attach,
usidmap -- #283): the eBPF/TC-BPF datapath is now the only ingress/decap
path for both veth and tap attachments, so there's no dual-path
coexistence to maintain.

ComputeSID (internal/plumbing/srv6/usid.go) is rewritten onto the
shared uFMT 48+16 bit layout (internal/plumbing/ebpf/uformat -- #281)
instead of its previous ad hoc NodeID/VRFID/Function suffix, so the BGP
control plane and the eBPF dataplane can never drift on bit positions.

The CNI ADD path no longer derives the VRFID straight from the
VPCAttachment identifier (vrfIDFromAttachment); it now allocates a
12-bit uFMT Argument per-node from live BGPVRFInstance CRD state
(allocateArgument), with a collision check (checkArgumentCollision)
covering the allocate-then-create race between two concurrent ADDs.
registerEBPFDatapath/unregisterEBPFDatapath write and roll back the
three eBPF map entries (locator_table, function_table, vrf_table) for
each attachment.

Stacked on #283 (eBPF datapath control-plane packages) and #284
(BGPAdvertisement prefix-merge fix, needed for this PR's
publishBGPStateK8s changes to apply cleanly).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@privateip
privateip marked this pull request as ready for review August 5, 2026 01:57
@privateip
privateip requested a review from a team as a code owner August 5, 2026 01:57
@privateip
privateip requested a review from mksinghtx August 5, 2026 01:57
@ecv

ecv commented Aug 5, 2026

Copy link
Copy Markdown

Apologies up front — ecv ran this review through an AI shell rather than reading it line by line himself. Please forgive him. Everything below is a question, not a verdict. (Rewritten after reading the addressing spec in datum-cloud/enhancements#740, which answered some of what I'd originally asked and sharpened the rest.)

First, the part that checks out: uformat validates cleanly against #740's srv6.md. Node-ID GIB 0x0001–0xDFFF, Function 0xE/0xF, Argument 0x001–0xFFF with 0x000 reserved, and the no-shift reading of RFC 9800 §4.2.7 — every constant and the reasoning behind them match the spec. Building the test packets through uformat.Encode so the C and Go key arithmetic cross-validate is a nice touch.

  1. function_value.behavior is written by the registration path but never read — a function_table hit proceeds to DT46 decap regardless of what's in it. #740 makes 0xE and 0xF fully independent service universes: 0xE is a VRF ID and an L3 route lookup, 0xF is an EVI ID and a Bridge Domain MAC lookup. So a DT2 entry reaching this code path isn't a near-miss, it's an L2 frame parsed as an inner IP packet against the wrong table entirely. Since behavior is exactly the discriminator the spec calls for, is there a reason not to gate on it before step 5 and drop anything that isn't BEHAVIOR_END_DT46? Would need its own drop reason, but that seems cheaper than the failure it prevents.

  2. Map sizing looks internally inconsistent against the spec's scaling story. locator_table holds 64 blocks and function_table 128 (= 64 blocks x 2 functions), but vrf_table is 8192 — and #740 pins 4,095 usable Instance IDs per uSID Block, with a second /48 block as the documented path past 4k. That's ~2 blocks of VRF capacity, or 1 block mid-migration under R8's 2x make-before-break, against 64 blocks' worth of locator/function space. A third block would exhaust vrf_table at registration and surface as a failed CNI ADD rather than a datapath drop. The comment already says "tune alongside R7 multi-Block sizing later" — does #740's per-block ceiling make it worth sizing now, or is 2 blocks a deliberate near-term cap?

  3. The vrf_table hit counters bump at step 6, ahead of five drop paths (unexpected nexthdr, malformed inner, strip failure, FIB failure, redirect failure), and drop_reasons has no Block/Argument dimension. So a VRF dropping everything still shows healthy per-Argument packets/bytes, and no counter says which tenant is dropping. For R8's zero-hits migration gate, counting claims looks like the right semantic — is that the intent, and if so should the Prometheus series be named and documented as claimed rather than forwarded traffic?

  4. BPF_FIB_LKUP_RET_FRAG_NEEDED becomes TC_ACT_SHOT plus a counter. The static-route SEG6 decap path this replaces would have let the kernel emit ICMPv6 Packet Too Big. Is losing PMTUD for inner flows an accepted cost of the cutover, or is an ICMP-generation path planned later in the stack? Either way it feels like a known-constraint entry.

  5. There's no bpf_skb_pull_data — every read is direct packet access against data_end. GRO normally pulls tunnel headers into the linear area, so this probably holds in practice, but on a non-linear skb the post-decap inner-header read fails its bounds check and lands as malformed_inner, i.e. legitimate traffic dropped under a misleading reason. Has that been exercised on the containerlab fabric with GRO on?

  6. The drop-reason enum exists three times: usid.c, dropreason.go, and usid_test.go's unexported copy. The test file is package prog, so its copy is redundant — any reason not to delete it and use the exported constants, so there are two places to drift instead of three?

  7. The BPF ELF license string is Dual BSD/GPL while the file's own SPDX header is AGPL-3.0-or-later. The comment cites Cilium, but Cilium's BPF is deliberately dual-licensed, so the precedent doesn't obviously carry — here the two statements in one file disagree, and the effect is offering the compiled datapath under BSD. None of the helpers called are gpl_only, so a plain GPL string would load fine. Is the permissive string intentional, and has whoever owns licensing signed off?

Smaller things, take or leave: Encode's f.Function<<4 | byte(f.Argument>>8)&0x0F is correct but leans on Go precedence — worth parens. The package doc says there's no bit-shift of the address anywhere, while Block() does >> NodeIDBits. dropreason.go is stamped 2026, everything else 2025. The bytes counter uses skb->len pre-decap, so it includes the outer v6 header and Ethernet.

One coverage note rather than a question: every test that gets past vrf_table terminates in a FIB failure, so TC_ACT_REDIRECT is never returned by any test, and the whole file is requireRoot-gated so it all skips today. The root job lands in #282, which only triggers on PRs targeting main — so until this one merges, usid.c has no executed coverage in CI at all. Not a blocker for this PR, just worth not losing track of across the stack.

Separately: #280 looks superseded by this one — the usid.c in both is byte-identical. Worth closing it so reviews land in one place.

@ecv ecv left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i haff questions

@privateip

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough pass — going through in order:

1. Behavior gating — real bug, fixed. usid_ingress now checks fn->behavior == BEHAVIOR_END_DT46 right after the function_table hit and drops anything else (new DROP_REASON_UNSUPPORTED_BEHAVIOR) before Argument/vrf_table ever run. Added TestUsidIngress_UnsupportedBehaviorDropsCounted to cover it.

2. vrf_table sizing — deliberate near-term cap, not an oversight: tightened the comment on vrf_table's max_entries to say so explicitly (~2 uSID Blocks' worth of Argument space; a 3rd Block fails CNI ADD at registration, not a datapath drop, until R7's multi-Block work revisits this map alongside locator_table/function_table). No size change in this PR.

3. Counter semantics — you're right that "claimed" packets showing healthy while everything actually gets dropped was a real blind spot. Added dropped_packets to vrf_value, bumped at every drop site from step 6 onward (nexthdr, malformed inner, strip, FIB, redirect), so packets - dropped_packets is what actually left via step 9. Documented the claimed-vs-forwarded distinction on both packets and the new field. Agreed on Prometheus naming — flagging for Milestone 4 (metrics collector) that the series should be named/documented as _claimed_ rather than implying forwarded traffic.

4. PMTUD loss — accepted as a known cost of this cutover for now; added it to docs/agents/ARCHITECTURE.md's Known Constraints rather than leaving it implicit. Generating ICMPv6 PTB from the datapath is real future work, not scheduled yet.

5. No bpf_skb_pull_data — added bpf_skb_pull_data(skb, 0) unconditionally at the top of usid_ingress, so every direct read is safe regardless of the skb's arriving layout rather than relying on GRO behavior holding. Failure there fails open (TC_ACT_OK), consistent with R6, since the program hasn't determined the packet is even a uSID packet yet. Haven't run the GRO-on containerlab check separately since this removes the dependency on that assumption either way.

6. Duplicate drop-reason enum — removed usid_test.go's local copy; it now uses dropreason.go's exported DropReason* constants directly (same package). Two copies to keep in sync instead of three.

7. License string — changed the ELF license to "AGPL-3.0-or-later", matching the file's own SPDX header, rather than borrowing a whitelisted GPL/BSD string the file doesn't actually carry. Rewrote the surrounding comment to explain the tradeoff explicitly: the kernel's license_is_gpl_compatible() check only recognizes a fixed whitelist, so this doesn't match it — fine today since nothing called here is gpl_only, but the first gpl_only helper added to this file will need this decision revisited.

Smaller things — parenthesized Encode's bit composition; reworded the uformat package doc's "no shift" claim to scope it correctly (a field's own right-justifying shift, like Block()'s >> NodeIDBits, was never what R2 forbids — only shifting to relocate a field into another field's frame); normalized dropreason.go's copyright year to 2025, matching the rest of the PR; documented that vrf_value.bytes intentionally counts the whole tunneled frame (outer header + Ethernet), not just inner payload.

Coverage note — agreed, tracked and not addressed here; will keep visible once #282's root CI job lands.

#280 — agreed, it's superseded by this one; closing it.

privateip and others added 2 commits August 5, 2026 07:59
Adds the compiled TC-BPF usid_ingress program (uFMT 48+16 uSID
decode/decap/redirect pipeline) with bpf2go-generated Go bindings, and
the pure-Go uformat bit-layout library (Block/Node-ID/Function/Argument
encode/decode) used by both the eBPF control plane and the BGP control
plane so they never drift on bit positions.

Pulls in github.com/cilium/ebpf.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fixes and clarifications from ecv's review:

- Gate on function_value.behavior before decap: a function_table hit
  whose behavior isn't BEHAVIOR_END_DT46 is now dropped (new
  DROP_REASON_UNSUPPORTED_BEHAVIOR) instead of falling through to DT46
  decap, since #740 makes 0xE/0xF independent service universes.
- Add vrf_value.dropped_packets, bumped at every post-vrf_table-match
  drop site, so packets-dropped_packets reflects actually-forwarded
  traffic instead of just claimed traffic.
- Add bpf_skb_pull_data(skb, 0) at program entry so direct data/
  data_end reads are safe regardless of the skb's arriving layout.
- Change the BPF ELF license string from "Dual BSD/GPL" to
  "AGPL-3.0-or-later" to match the file's own SPDX header, with a
  comment on the gpl_only-helper tradeoff that follows from it.
- Remove usid_test.go's duplicate drop-reason enum in favor of
  dropreason.go's exported constants.
- Tighten the vrf_table max_entries comment to state the ~2-Block cap
  explicitly.
- Document docs/agents/ARCHITECTURE.md's PMTUD/ICMPv6 PTB gap as a
  known constraint.
- Minor: parenthesize Encode's bit composition, correct uformat's
  "no shift" package doc claim, fix dropreason.go's copyright year,
  document vrf_value.bytes's pre-decap semantics.

Note: usid_bpfel.o/usid_bpfeb.o and their generated Go bindings still
need `go generate ./internal/plumbing/ebpf/prog/...` (requires clang)
before these usid.c changes take effect in the compiled datapath.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@privateip
privateip requested a review from ecv August 5, 2026 12:09
privateip added a commit that referenced this pull request Aug 5, 2026
Teaches task build to regenerate the TC-BPF usid program's Go bindings
(bpf2go via go generate, split into a new build:ebpf step ahead of
build:binaries) and the CNI container image to do the same at
image-build time, so clang/llvm is a build-time-only dependency and
never ships in the runtime image.

Adds a root-privileged rerun of the unit test suite in CI
(test-unit-root) so tests gated on requireRoot(t) -- the
BPF_PROG_TEST_RUN cases in usid_test.go, plus later root-gated tests --
actually execute instead of skipping, and a step that diffs the
regenerated eBPF artifacts against the committed ones so usid.c and its
compiled output can't silently drift apart.

Stacked on #281 (adds the usid.c program this generates from).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ecv
ecv previously approved these changes Aug 5, 2026
privateip added a commit that referenced this pull request Aug 5, 2026
Teaches task build to regenerate the TC-BPF usid program's Go bindings
(bpf2go via go generate, split into a new build:ebpf step ahead of
build:binaries) and the CNI container image to do the same at
image-build time, so clang/llvm is a build-time-only dependency and
never ships in the runtime image.

Adds a root-privileged rerun of the unit test suite in CI
(test-unit-root) so tests gated on requireRoot(t) -- the
BPF_PROG_TEST_RUN cases in usid_test.go, plus later root-gated tests --
actually execute instead of skipping, and a step that diffs the
regenerated eBPF artifacts against the committed ones so usid.c and its
compiled output can't silently drift apart.

Stacked on #281 (adds the usid.c program this generates from).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
privateip added a commit that referenced this pull request Aug 5, 2026
Teaches task build to regenerate the TC-BPF usid program's Go bindings
(bpf2go via go generate, split into a new build:ebpf step ahead of
build:binaries) and the CNI container image to do the same at
image-build time, so clang/llvm is a build-time-only dependency and
never ships in the runtime image.

Adds a root-privileged rerun of the unit test suite in CI
(test-unit-root) so tests gated on requireRoot(t) -- the
BPF_PROG_TEST_RUN cases in usid_test.go, plus later root-gated tests --
actually execute instead of skipping, and a step that diffs the
regenerated eBPF artifacts against the committed ones so usid.c and its
compiled output can't silently drift apart.

Stacked on #281 (adds the usid.c program this generates from).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ef73ddf changed the BPF ELF license section from "Dual BSD/GPL" to
"AGPL-3.0-or-later" on the premise that "none of the helpers this
program calls today are gpl_only." That premise is false: the VRF
FIB lookup already calls bpf_fib_lookup(), which the kernel marks
gpl_only (net/core/filter.c's bpf_fib_lookup_proto). The kernel's
license_is_gpl_compatible() check only recognizes a fixed whitelist
of exact strings, and "AGPL-3.0-or-later" isn't on it, so the
program now fails to load at all: "cannot call GPL-restricted
function from non-GPL compatible program". This surfaced as PR
#282's new CI jobs failing -- the Build job's drift check flagged
the stale committed .o/.go (usid.c had changed but they hadn't been
regenerated since), and once regenerated, Unit Tests (root) then hit
the verifier rejection above on every FIB-lookup-path test.

Set the license string to plain "GPL" -- not "Dual BSD/GPL"; this
program isn't itself dual-licensed, so it declares the license it
actually means rather than a disjunction it doesn't. Correct the
surrounding comment accordingly: the ELF license section governs
which helpers the verifier allows and is independent of (doesn't
relicense) the file's own AGPL-3.0-or-later SPDX header, same as
Cilium, Katran, and every other AGPL/Apache/BSD-licensed project
embedding a BPF datapath. Regenerate usid_bpfel.o/usid_bpfeb.o and
their Go bindings from the corrected usid.c (also picking up the
vrf_value.dropped_packets field ef73ddf added, which needed the
same regeneration its own commit message flagged as still pending).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
privateip added a commit that referenced this pull request Aug 5, 2026
Teaches task build to regenerate the TC-BPF usid program's Go bindings
(bpf2go via go generate, split into a new build:ebpf step ahead of
build:binaries) and the CNI container image to do the same at
image-build time, so clang/llvm is a build-time-only dependency and
never ships in the runtime image.

Adds a root-privileged rerun of the unit test suite in CI
(test-unit-root) so tests gated on requireRoot(t) -- the
BPF_PROG_TEST_RUN cases in usid_test.go, plus later root-gated tests --
actually execute instead of skipping, and a step that diffs the
regenerated eBPF artifacts against the committed ones so usid.c and its
compiled output can't silently drift apart.

Stacked on #281 (adds the usid.c program this generates from).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@privateip
privateip requested a review from ecv August 5, 2026 17:05
@privateip
privateip merged commit 04ac1d5 into main Aug 6, 2026
9 checks passed
privateip added a commit that referenced this pull request Aug 6, 2026
Teaches task build to regenerate the TC-BPF usid program's Go bindings
(bpf2go via go generate, split into a new build:ebpf step ahead of
build:binaries) and the CNI container image to do the same at
image-build time, so clang/llvm is a build-time-only dependency and
never ships in the runtime image.

Adds a root-privileged rerun of the unit test suite in CI
(test-unit-root) so tests gated on requireRoot(t) -- the
BPF_PROG_TEST_RUN cases in usid_test.go, plus later root-gated tests --
actually execute instead of skipping, and a step that diffs the
regenerated eBPF artifacts against the committed ones so usid.c and its
compiled output can't silently drift apart.

Stacked on #281 (adds the usid.c program this generates from).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
privateip added a commit that referenced this pull request Aug 6, 2026
Removes the legacy per-route netlink SRv6 ingress mechanism
(srv6.RouteIngressAdd/Del, srv6.go) entirely and replaces it with
registration against the eBPF uSID datapath's pinned maps (attach,
usidmap -- #283): the eBPF/TC-BPF datapath is now the only ingress/decap
path for both veth and tap attachments, so there's no dual-path
coexistence to maintain.

ComputeSID (internal/plumbing/srv6/usid.go) is rewritten onto the
shared uFMT 48+16 bit layout (internal/plumbing/ebpf/uformat -- #281)
instead of its previous ad hoc NodeID/VRFID/Function suffix, so the BGP
control plane and the eBPF dataplane can never drift on bit positions.

The CNI ADD path no longer derives the VRFID straight from the
VPCAttachment identifier (vrfIDFromAttachment); it now allocates a
12-bit uFMT Argument per-node from live BGPVRFInstance CRD state
(allocateArgument), with a collision check (checkArgumentCollision)
covering the allocate-then-create race between two concurrent ADDs.
registerEBPFDatapath/unregisterEBPFDatapath write and roll back the
three eBPF map entries (locator_table, function_table, vrf_table) for
each attachment.

Stacked on #283 (eBPF datapath control-plane packages) and #284
(BGPAdvertisement prefix-merge fix, needed for this PR's
publishBGPStateK8s changes to apply cleanly).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
privateip added a commit that referenced this pull request Aug 6, 2026
Teaches task build to regenerate the TC-BPF usid program's Go bindings
(bpf2go via go generate, split into a new build:ebpf step ahead of
build:binaries) and the CNI container image to do the same at
image-build time, so clang/llvm is a build-time-only dependency and
never ships in the runtime image.

Adds a root-privileged rerun of the unit test suite in CI
(test-unit-root) so tests gated on requireRoot(t) -- the
BPF_PROG_TEST_RUN cases in usid_test.go, plus later root-gated tests --
actually execute instead of skipping, and a step that diffs the
regenerated eBPF artifacts against the committed ones so usid.c and its
compiled output can't silently drift apart.

Stacked on #281 (adds the usid.c program this generates from).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
privateip added a commit that referenced this pull request Aug 6, 2026
Teaches task build to regenerate the TC-BPF usid program's Go bindings
(bpf2go via go generate, split into a new build:ebpf step ahead of
build:binaries) and the CNI container image to do the same at
image-build time, so clang/llvm is a build-time-only dependency and
never ships in the runtime image.

Adds a root-privileged rerun of the unit test suite in CI
(test-unit-root) so tests gated on requireRoot(t) -- the
BPF_PROG_TEST_RUN cases in usid_test.go, plus later root-gated tests --
actually execute instead of skipping, and a step that diffs the
regenerated eBPF artifacts against the committed ones so usid.c and its
compiled output can't silently drift apart.

Stacked on #281 (adds the usid.c program this generates from).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
privateip added a commit that referenced this pull request Aug 6, 2026
Adds the Go control-plane library for the TC-BPF uSID datapath:

- preflight: kernel capability gate (BTF, HASH maps, SCHED_CLS,
  bpf_fib_lookup with VRF-tbid support) run before attempting to load
  the program at all.
- usidmap: typed read/write/reconcile API for the three kernel maps
  (locator_table, function_table, vrf_table) the program consults.
- attach: load/pin/attach/detach/watch lifecycle for the TC-BPF
  ingress hook, including netlink-driven re-attachment on interface or
  route change, gated by the new GALACTIC_CNI_EBPF_INTERFACES env var
  (internal/config) for multi-homed nodes where auto-detection is
  ambiguous.
- metrics: Prometheus collector plus load/attach event counters.

Every package here is independently unit-testable against fakes
(faketable_test.go, a mock kernel prober, fake closers) -- nothing
outside this tree calls any of it yet.

Stacked on #281 (usid.c/uformat) and #282 (codegen toolchain).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
privateip added a commit that referenced this pull request Aug 6, 2026
Removes the legacy per-route netlink SRv6 ingress mechanism
(srv6.RouteIngressAdd/Del, srv6.go) entirely and replaces it with
registration against the eBPF uSID datapath's pinned maps (attach,
usidmap -- #283): the eBPF/TC-BPF datapath is now the only ingress/decap
path for both veth and tap attachments, so there's no dual-path
coexistence to maintain.

ComputeSID (internal/plumbing/srv6/usid.go) is rewritten onto the
shared uFMT 48+16 bit layout (internal/plumbing/ebpf/uformat -- #281)
instead of its previous ad hoc NodeID/VRFID/Function suffix, so the BGP
control plane and the eBPF dataplane can never drift on bit positions.

The CNI ADD path no longer derives the VRFID straight from the
VPCAttachment identifier (vrfIDFromAttachment); it now allocates a
12-bit uFMT Argument per-node from live BGPVRFInstance CRD state
(allocateArgument), with a collision check (checkArgumentCollision)
covering the allocate-then-create race between two concurrent ADDs.
registerEBPFDatapath/unregisterEBPFDatapath write and roll back the
three eBPF map entries (locator_table, function_table, vrf_table) for
each attachment.

Stacked on #283 (eBPF datapath control-plane packages) and #284
(BGPAdvertisement prefix-merge fix, needed for this PR's
publishBGPStateK8s changes to apply cleanly).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
privateip added a commit that referenced this pull request Aug 6, 2026
Adds the Go control-plane library for the TC-BPF uSID datapath:

- preflight: kernel capability gate (BTF, HASH maps, SCHED_CLS,
  bpf_fib_lookup with VRF-tbid support) run before attempting to load
  the program at all.
- usidmap: typed read/write/reconcile API for the three kernel maps
  (locator_table, function_table, vrf_table) the program consults.
- attach: load/pin/attach/detach/watch lifecycle for the TC-BPF
  ingress hook, including netlink-driven re-attachment on interface or
  route change, gated by the new GALACTIC_CNI_EBPF_INTERFACES env var
  (internal/config) for multi-homed nodes where auto-detection is
  ambiguous.
- metrics: Prometheus collector plus load/attach event counters.

Every package here is independently unit-testable against fakes
(faketable_test.go, a mock kernel prober, fake closers) -- nothing
outside this tree calls any of it yet.

Stacked on #281 (usid.c/uformat) and #282 (codegen toolchain).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
privateip added a commit that referenced this pull request Aug 6, 2026
Removes the legacy per-route netlink SRv6 ingress mechanism
(srv6.RouteIngressAdd/Del, srv6.go) entirely and replaces it with
registration against the eBPF uSID datapath's pinned maps (attach,
usidmap -- #283): the eBPF/TC-BPF datapath is now the only ingress/decap
path for both veth and tap attachments, so there's no dual-path
coexistence to maintain.

ComputeSID (internal/plumbing/srv6/usid.go) is rewritten onto the
shared uFMT 48+16 bit layout (internal/plumbing/ebpf/uformat -- #281)
instead of its previous ad hoc NodeID/VRFID/Function suffix, so the BGP
control plane and the eBPF dataplane can never drift on bit positions.

The CNI ADD path no longer derives the VRFID straight from the
VPCAttachment identifier (vrfIDFromAttachment); it now allocates a
12-bit uFMT Argument per-node from live BGPVRFInstance CRD state
(allocateArgument), with a collision check (checkArgumentCollision)
covering the allocate-then-create race between two concurrent ADDs.
registerEBPFDatapath/unregisterEBPFDatapath write and roll back the
three eBPF map entries (locator_table, function_table, vrf_table) for
each attachment.

Stacked on #283 (eBPF datapath control-plane packages) and #284
(BGPAdvertisement prefix-merge fix, needed for this PR's
publishBGPStateK8s changes to apply cleanly).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@privateip
privateip deleted the pr0-ebpf-prog branch August 6, 2026 21:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants