Skip to content

(12) feat(acl): rte_acl backend + differential-vs-reference + benches#1576

Open
daniel-noland wants to merge 7 commits into
mainfrom
pr/daniel-noland/acl-dpdk
Open

(12) feat(acl): rte_acl backend + differential-vs-reference + benches#1576
daniel-noland wants to merge 7 commits into
mainfrom
pr/daniel-noland/acl-dpdk

Conversation

@daniel-noland

@daniel-noland daniel-noland commented May 31, 2026

Copy link
Copy Markdown
Collaborator

Stack (12). Base: pr/daniel-noland/acl-reference.

The rte_acl backend and everything proving it matches the reference oracle.
This is the largest PR -- the irreducible DPDK lowering complexity:

  • feat(acl/dpdk): layout planner + rule lowering.
  • feat(acl/dpdk): install + DpdkAclLookup + EAL smoke.
  • feat(acl/dpdk): DynDpdkLookup + shape-fuzz property test.
  • test(acl): differential property (reference vs DPDK) + Headers/metadata
    projection demos.
  • feat(acl): criterion benchmarks + nix bench-builder.

Review stack (merge bottom -> top):

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a DPDK rte_acl-powered backend for the dataplane-acl crate, plus differential/property tests and criterion benchmarks to validate correctness against the existing software reference backend and measure performance.

Changes:

  • Introduces acl::dpdk backend (layout planning, rule lowering/splicing, install/build, typed + dynamic lookup APIs).
  • Adds DPDK-gated integration/property tests (reference-vs-DPDK differential checks, dynamic-shape fuzzing, header/metadata projection examples).
  • Adds criterion benchmarks and Nix/Just plumbing to build and run bench binaries.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
justfile Adds bench recipe to build and execute bench binaries.
default.nix Adds bench-builder derivation and exports benches.
Cargo.toml Adds workspace dependency on criterion.
Cargo.lock Locks new benchmark dependency graph (criterion + transitive deps).
acl/tests/property_predicate.rs Differential property test for typed 5-tuple (v4/v6) against reference.
acl/tests/property_dyn_shape.rs Differential property test for dynamic shapes (DPDK vs reference).
acl/tests/net_field_types.rs Demonstrates net newtypes classification through DPDK and reference.
acl/tests/metadata_projection.rs Demonstrates Projection-based classification using headers + metadata.
acl/tests/eal_install_classify.rs EAL smoke test for install + classify + batch classify.
acl/tests/eal_classify_via_projection.rs EAL smoke test classifying a real packet via projection.
acl/src/lib.rs Exposes dpdk module and adds dpdk_table_alias! helper macro under feature gate.
acl/src/dpdk/mod.rs Defines DPDK backend module surface.
acl/src/dpdk/rule.rs Implements DPDK rule lowering + field splicing + related errors/tests.
acl/src/dpdk/lookup.rs Implements typed DPDK lookup and batch classify helpers.
acl/src/dpdk/layout.rs Implements layout planning + const extents for compile-time type aliases.
acl/src/dpdk/install.rs Implements table installation/building into an AclContext.
acl/src/dpdk/dyn_table.rs Implements dynamic-shape install + byte-key lookup path.
acl/Cargo.toml Adds dpdk feature, DPDK/criterion dev-deps, and bench targets.
acl/benches/table_build.rs Benchmarks table build cost (reference vs DPDK, v4/v6).
acl/benches/reference_five_tuple.rs Benchmarks reference backend lookup patterns (v4/v6).
acl/benches/dpdk_five_tuple.rs Benchmarks DPDK backend lookups (single + batch, v4/v6).

Comment thread Cargo.toml Outdated
Comment thread acl/Cargo.toml Outdated
Comment thread acl/src/lib.rs
Comment thread acl/src/dpdk/lookup.rs
Comment thread acl/src/dpdk/lookup.rs
Comment thread acl/src/dpdk/dyn_table.rs
Comment thread justfile Outdated
@daniel-noland daniel-noland force-pushed the pr/daniel-noland/acl-reference branch from 3220e66 to 710b08b Compare June 3, 2026 19:50
@daniel-noland daniel-noland force-pushed the pr/daniel-noland/acl-dpdk branch from 16e2188 to 01d98b2 Compare June 3, 2026 19:50
@daniel-noland daniel-noland force-pushed the pr/daniel-noland/acl-dpdk branch from 01d98b2 to ddbc607 Compare June 15, 2026 20:18
@daniel-noland daniel-noland force-pushed the pr/daniel-noland/acl-reference branch from 710b08b to 598ba60 Compare June 15, 2026 20:18
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 18fa049d-a03b-4223-b1da-5f3eb5313ffb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands and usage tips.

@daniel-noland daniel-noland force-pushed the pr/daniel-noland/acl-dpdk branch 2 times, most recently from 19ca1a1 to 29232af Compare June 15, 2026 22:08
@daniel-noland daniel-noland force-pushed the pr/daniel-noland/acl-reference branch from 598ba60 to 08b4c60 Compare June 15, 2026 22:08
Comment thread acl/src/dpdk/layout.rs Outdated
Comment thread acl/src/dpdk/lookup.rs
}
let mut results: ArrayVec<u32, MAX_BATCH> = ArrayVec::new();
for _ in 0..keys.len() {
let _ = results.try_push(0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I saw this paradigm before, surely there is a better way to get keys to be 0 while checking that it is large enough to have all of keys?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please fix this before merge.

Comment thread acl/src/dpdk/layout.rs Outdated
Comment thread acl/src/dpdk/layout.rs
Comment thread acl/src/dpdk/layout.rs
Comment thread acl/src/dpdk/lookup.rs Outdated
Comment thread acl/src/dpdk/dyn_table.rs
Introduces the dataplane-acl crate with the software reference
classifier.  The DPDK rte_acl backend lands behind a feature gate in
a follow-up PR.

The reference backend is a linear-scan software classifier built on
the canonical FieldPredicate form from match-action
(rule.into_backend_fields::<Erased>()), so it speaks the same four
predicate kinds (Prefix / Mask / Range / Exact) as every other
backend.  Two roles:

1. Differential-testing oracle against rte_acl (a future PR's
   differential property tests pit both backends against the same
   random rule + packet draws).
2. Non-lossy substrate for a small-delta cascade front over a slow
   tail backend.

Layout:

- src/lib.rs declares the crate-level docs and re-exports the
  reference module.  The dpdk feature gate and dpdk_table_alias!
  macro land alongside the rte_acl backend itself in the next PR.
- src/reference/table.rs is the typed surface: ReferenceTable<K, A>
  parameterised by a MatchKey and an action; RefRule wraps the
  lowered Erased predicates plus an action.  Inline unit tests cover
  positional precedence (first match wins) and the four predicate
  kinds.
- src/reference/dyn_table.rs is the runtime-shape twin:
  DynReferenceTable carries its FieldSpec layout at runtime so
  property tests can fuzz the schema itself.  Returns DynShapeError
  on shape mismatch.

just fmt; cargo check --workspace --all-targets passes.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
@daniel-noland daniel-noland force-pushed the pr/daniel-noland/acl-dpdk branch from 29232af to fbcf630 Compare June 18, 2026 19:34
@daniel-noland daniel-noland force-pushed the pr/daniel-noland/acl-reference branch from 08b4c60 to 7a7d109 Compare June 18, 2026 19:34
daniel-noland and others added 6 commits June 18, 2026 14:16
Lands the static type machinery for the DPDK `rte_acl` backend
behind the new `dpdk` feature gate: how a MatchKey's FIELD_SPECS maps
into rte_acl's per-field FieldDef array, and how the four
match-action *Spec predicate kinds lower into IntoBackendField for
the `Dpdk` backend marker.  The runtime install / classify path
(install.rs, lookup.rs) and the dpdk_table_alias! macro land next.

acl/src/dpdk/:

- mod.rs declares the two submodules; carries a temporary
  #![allow(dead_code)] because the layout's `stride` field and the
  rule.rs RuleSpec fields are consumed only once install / lookup
  arrive in the next PR.  The allow goes away then.
- layout.rs has the rte_acl field planner: group fields by
  input_index (rte_acl requires the first field to be one byte,
  remaining fields grouped into <= 4-byte buckets), insert padding
  for gaps, and yield a DpdkLayout { field_defs: [FieldDef; N],
  stride, user_to_dpdk }.  const_extents() is const fn so a const
  alias can derive N / STRIDE from K::FIELD_SPECS without unstable
  generic_const_exprs.  Wide fields (Ipv6Addr, u128) decompose into
  four u32 sub-fields the way l3fwd-acl does.
- rule.rs holds the Dpdk backend marker, the AclWord trait (blanket
  impl over FixedSize via chunks()), the IntoBackendField impls
  carrying each *Spec into a backend-typed AclField group, the
  RuleSpec rule-field envelope, and splice_user_fields_to_dpdk for
  reordering user-declared fields into rte_acl's layout-driven
  ordering.

acl/src/lib.rs picks up the #[cfg(feature = "dpdk")] gate on
pub mod dpdk; (no macro yet -- the dpdk_table_alias! macro lands with
its lookup-side referent next PR).

acl/Cargo.toml grows the dpdk feature and the optional dpdk
workspace dep.  No dev-deps yet.

just fmt; cargo check --workspace --all-targets and
cargo clippy -p dataplane-acl --features dpdk -- -D warnings pass.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Wires the layout planner and rule lowering from the previous PR into
a working DPDK backend: build an AclContext from a MatchKey plus its
rules, wrap it in a DpdkAclLookup, and classify packets through it.
First EAL-touching PR in the acl stack.

src/dpdk/:

- install.rs is the from-K-plus-rules constructor: take a MatchKey,
  call plan_layout to get the rte_acl FieldDefs, build an
  AclContext, splice each user RuleSpec through layout's
  user_to_dpdk map into rte_acl's column order, hand the rules to
  the context, build, and wrap the built context in a
  DpdkAclLookup<K, N, STRIDE, A>.
- lookup.rs is DpdkAclLookup itself: stack-packed key bytes
  (MAX_USER_KEY_BYTES sentinel feeds the compile-time guard in
  dpdk_table_alias!), the impl Lookup<K, A> single-shot path, and a
  batched classify_batch over a slice of K returning aligned actions.
- mod.rs picks up pub mod install / pub mod lookup and drops the
  temporary #![allow(dead_code)] from the previous PR -- RuleSpec
  fields and DpdkLayout.stride now have readers.

src/lib.rs gains the dpdk_table_alias! macro: dpdk_table_alias!(pub
type FiveTupleTable<Verdict> = FiveTuple); yields a DpdkAclLookup<K,
N, STRIDE, A> with N / STRIDE derived from K::FIELD_SPECS via
const_extents.  A const _: () = assert!(KEY_SIZE <=
MAX_USER_KEY_BYTES) guards against keys that wouldn't fit the
stack scratch buffer.  The hidden __match_action module re-exports
MatchKey so the macro resolves without a caller-side import.

tests/eal_install_classify.rs is the smoke: derive a MatchKey, install
two rules with priority precedence, classify via the single-shot path
and the batch path, assert userdata.

acl/Cargo.toml grows a single dev-dep -- self-overriding dpdk with the
`test` feature on so #[with_eal] from dpdk-test-macros works.

just fmt; cargo check --workspace --all-targets and
cargo clippy -p dataplane-acl --features dpdk -- -D warnings pass.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Adds the runtime-shape twin of DpdkAclLookup -- DynDpdkLookup carries
its FieldSpec layout at runtime instead of in const generics -- and
the shape-fuzz oracle that proves the byte-level pipeline agrees
between the reference oracle and rte_acl over an unconstrained
schema.

src/dpdk/dyn_table.rs is DynDpdkLookup<A>:

- new(name, max_rule_num, field_specs) plans the rte_acl layout from
  a Vec<FieldSpec> at runtime, builds an empty AclContext, and
  returns a typed lookup keyed by an Erased FieldPredicate vector.
- add_rules takes Vec<DynRuleSpec> -- the runtime-shape rule
  carrier (priority, category_mask, lowered fields, action) -- and
  splices each rule's field bytes through the user_to_dpdk map into
  rte_acl's column order, then builds.
- impl Lookup<Vec<FieldBytes>, A>: pack the probe bytes onto the
  stack scratch buffer in the layout's column order, hand them to
  rte_acl_classify, and translate the userdata hit back to &A.

src/dpdk/mod.rs picks up pub mod dyn_table; alongside the typed path.

tests/property_dyn_shape.rs is the schema fuzz:

- bolero TypeGenerator yields a random Vec<FieldSpec>, a single rule
  matching that shape, and packet seeds.
- For each shape: install the same rule into a DynReferenceTable
  (oracle) and a DynDpdkLookup, then probe both with both a
  hit-byte seed and a miss-byte seed.  Assert agreement on every
  probe.
- No MatchKey types involved -- exercises the byte-level pipeline
  end-to-end and catches drift in layout planning, the splice map,
  and rte_acl's per-predicate semantics simultaneously.

acl/Cargo.toml gains bolero + match-action[bolero] dev-deps the
test needs.

just fmt; cargo check --workspace --all-targets and
cargo clippy -p dataplane-acl --features dpdk -- -D warnings pass.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Pure test broadening; no src changes.  Adds the single-rule v4/v6
differential against the reference oracle and three Headers /
metadata projection demos that exercise classify / classify_opt
against real net::HeadersView packets.

tests/property_predicate.rs is the differential.  For a random
5-tuple rule + random hit/miss byte seeds drawn via match-action's
FieldHit / FieldMiss generators, both the reference oracle and the
DPDK backend must accept every hits() draw and reject every misses()
draw.  Parameterised over the address width via a sealed IpAddress
trait so a single body covers v4 (Ipv4Addr) and v6 (Ipv6Addr) -- the
DPDK wide-field split (one 16-byte address -> four 4-byte
sub-fields) is exercised end-to-end by the v6 invocation.  Single
rule only; multi-rule differential is deferred (positional
precedence vs numeric Priority).

tests/eal_classify_via_projection.rs is the end-to-end projection
demo: a real packet -> HeadersView -> Projection<FiveTuple> -> DPDK
Lookup<FiveTuple, _> -> action.  Shows Lookup::classify runs the
projection and the lookup as a single call -- the call site reads
table.classify(\&headers) and doesn't see the intermediate key
construction.

tests/metadata_projection.rs is the partial-projection demo.  Header
fields live in Headers; VRF / VNI live in PacketMeta.  A projection
source bundles &HeadersView with &PacketMeta and projects to
Option<K>: the header part is total (shape proves presence), the
metadata part narrows from its Option with ?.  Missing metadata
projects to None and Lookup::classify_opt turns that into a table
miss with no explicit branch in user code.

tests/net_field_types.rs uses net wire newtypes (TcpPort, UdpPort,
Vni, UnicastIpv4Addr) directly as MatchKey fields with no acl-side
AclWord impl, leaning on net's FixedSize impls (PR 2a) and the DPDK
backend's blanket AclWord-over-FixedSize impl.

acl/Cargo.toml grows the net[test_buffer, builder] dev-dep these
projection demos need.

just fmt; cargo check --workspace --all-targets and
cargo clippy -p dataplane-acl --features dpdk -- -D warnings pass.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Adds criterion benchmarks for both backends at v4 and v6 widths,
plus the nix / just plumbing to produce bench binaries from a
sandboxed build.

acl/benches/:

- reference_five_tuple.rs sweeps a deep miss (full per-rule scan)
  and an early hit through the reference's O(rules * fields) linear
  scan.  Both widths.
- dpdk_five_tuple.rs is the rte_acl companion: trie walk cost (close
  to flat in rule count), miss vs hit, single-shot vs SIMD batch.
  v6 exercises the wide-field split (one 16-byte address -> four
  4-byte sub-fields).  Requires a live EAL.
- table_build.rs measures construction cost vs rule count: reference
  (lower + Vec wrap) and DPDK (rte_acl_build, the update-latency
  cost).  Both widths.  iter_batched so teardown is excluded.

acl/Cargo.toml gets the criterion dev-dep and three harness = false
[[bench]] entries.  Workspace Cargo.toml gets the criterion = 0.5.1
shared dep entry.

default.nix adds a bench-builder derivation: cargo bench --no-run
under the profile-appropriate DPDK sysroot, then copies each
compiled benchmark into $out/bin (stripping cargo's -<hash>
suffix).  Linked against the optimized DPDK when profile = release.

justfile adds a bench recipe that builds the benches package and
runs every binary under results/benches/bin/ in turn.

just fmt; cargo check --workspace --all-targets and
cargo clippy -p dataplane-acl --features dpdk -- -D warnings pass.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
DpdkAclLookup<K,A> drops N_FIELDS/STRIDE and holds Box<dyn DynClassifier>; install_table<K,A> dispatches the field count at runtime via build_classifier_n shared with the dyn install path; adds DynClassifier::classify_batch so the typed batch path keeps real rte_acl batching through the type erasure; dispatch ceiling raised to MAX_FIELDS (64); keys packed into a single runtime-layout.stride arena (batch path drops the redundant per-key copy); removes lookup_via_bytes/dpdk_key_bytes and simplifies the dpdk_table_alias! macro (no const_extents).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Daniel Noland <daniel@githedgehog.com>
@daniel-noland daniel-noland force-pushed the pr/daniel-noland/acl-dpdk branch from c795ed9 to 19de724 Compare June 18, 2026 20:16
Base automatically changed from pr/daniel-noland/acl-reference to main June 18, 2026 20:50
@mvachhar mvachhar self-requested a review June 18, 2026 21:46
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.

3 participants