Skip to content

User ACLs logic implementation#1634

Merged
qmonnet merged 17 commits into
mainfrom
pr/qmonnet/user-acls
Jul 20, 2026
Merged

User ACLs logic implementation#1634
qmonnet merged 17 commits into
mainfrom
pr/qmonnet/user-acls

Conversation

@qmonnet

@qmonnet qmonnet commented Jul 13, 2026

Copy link
Copy Markdown
Member

Based on #1618

It will probably require some changes in the way we use the ACLs (similar to the flow-filter reimplementation from #1611).

@qmonnet qmonnet added the area/acl Related to ACLs (Access Control Lists) label Jul 13, 2026
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a Rust ACL filter crate that builds tables from validated overlay ACLs, evaluates packet and flow-scoped rules, formats table state, tests behavior, and integrates configuration updates with dataplane packet processing.

Changes

ACL filter implementation

Layer / File(s) Summary
ACL contracts and matching conversions
Cargo.toml, acl-filter/Cargo.toml, acl/src/reference/table.rs, config/src/external/overlay/*, lpm/Cargo.toml, lpm/src/prefix/*, net/src/ip/mod.rs
Adds the ACL filter workspace crate, ACL validation and accessors, reference-table rule access, protocol normalization, and IP prefix and port-range conversions.
ACL context and lookup tables
acl-filter/src/access.rs, acl-filter/src/context.rs
Builds protocol-specific IPv4/IPv6 ACL tables from validated overlays and provides hot-swappable context handles.
Packet filtering and table display
acl-filter/src/lib.rs, acl-filter/src/display.rs
Extracts packet summaries, evaluates direct and reverse flow-aware matches, applies filtering outcomes, and formats ACL tables.
ACL filter behavior validation
acl-filter/src/tests.rs
Tests defaults, rule ordering, protocol and IPv6 matching, flow scope, reply handling, and NAT pipeline behavior.
Dataplane and management wiring
dataplane/..., mgmt/...
Adds ACL context writers to configuration processing, inserts the ACL filter into the dataplane pipeline, and applies validated ACL updates.

Possibly related PRs

Suggested reviewers: daniel-noland, mvachhar, fredi-raspall

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is related to the main change, though phrased a bit awkwardly.
Description check ✅ Passed The description is clearly related to the ACL dataplane changes in this PR.

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

@qmonnet qmonnet linked an issue Jul 13, 2026 that may be closed by this pull request
@qmonnet qmonnet changed the title User ACL logic implementation User ACLs logic implementation Jul 13, 2026
@qmonnet
qmonnet force-pushed the pr/qmonnet/user-acls branch 2 times, most recently from 53fbdae to 5526612 Compare July 13, 2026 17:10
@qmonnet
qmonnet marked this pull request as ready for review July 13, 2026 17:11
@qmonnet
qmonnet requested a review from a team as a code owner July 13, 2026 17:11
@qmonnet
qmonnet requested review from Fredi-raspall, daniel-noland and mvachhar and removed request for a team July 13, 2026 17:11
Base automatically changed from ema/acl-crd-converters to main July 13, 2026 18:06
@qmonnet
qmonnet force-pushed the pr/qmonnet/user-acls branch from 5526612 to 81463d2 Compare July 14, 2026 10:20

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
acl-filter/src/tests.rs (1)

644-654: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider testing prefix matching against pre-NAT addresses through the real NAT pipeline.

The e2e ACL rule uses empty src/dst patterns (match-all), so this test never validates that a prefix-restricted rule correctly matches the VPC-internal addresses present at ACL-evaluation time (as opposed to the translated/public addresses). Since the module doc comment specifically calls out this address-timing contract, a rule with concrete prefixes (e.g. matching 1.2.3.0/24/192.168.0.0/24) plus a negative case (a prefix that would only match the public/translated addresses) would give confidence the placement is correct rather than just documented.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@acl-filter/src/tests.rs` around lines 644 - 654, Update the build_overlay
test fixture to use concrete source and destination prefixes matching the
pre-NAT VPC addresses, such as 1.2.3.0/24 and 192.168.0.0/24, instead of empty
match-all patterns. Extend the real NAT-pipeline test with a negative case using
prefixes that match only translated/public addresses, while preserving the
existing UDP flow rule and ACL behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@acl-filter/src/context.rs`:
- Around line 97-116: In insert_for_src_and_dst_prefix, correct the non-TCP/UDP
IPv6 match arm to append rules to self.other_v6 instead of self.other_v4, while
preserving the existing TCP/UDP routing and IPv4 behavior.

In `@acl-filter/src/tests.rs`:
- Around line 600-608: Update the end-to-end flow-scope test comment to describe
the ACL filter’s address state as pre-source-NAT and pre-destination-NAT,
replacing the incorrect post-destination-NAT wording while preserving the
surrounding NAT and flow-link explanation.

---

Nitpick comments:
In `@acl-filter/src/tests.rs`:
- Around line 644-654: Update the build_overlay test fixture to use concrete
source and destination prefixes matching the pre-NAT VPC addresses, such as
1.2.3.0/24 and 192.168.0.0/24, instead of empty match-all patterns. Extend the
real NAT-pipeline test with a negative case using prefixes that match only
translated/public addresses, while preserving the existing UDP flow rule and ACL
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ee2311e1-0af3-4640-bc97-5facff832f41

📥 Commits

Reviewing files that changed from the base of the PR and between 79ef5d3 and 81463d2.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • Cargo.toml
  • acl-filter/Cargo.toml
  • acl-filter/src/access.rs
  • acl-filter/src/context.rs
  • acl-filter/src/display.rs
  • acl-filter/src/lib.rs
  • acl-filter/src/tests.rs
  • acl/src/reference/table.rs
  • config/src/external/overlay/acl.rs
  • config/src/external/overlay/vpc.rs
  • dataplane/Cargo.toml
  • dataplane/src/packet_processor/mod.rs
  • dataplane/src/runtime.rs
  • lpm/Cargo.toml
  • lpm/src/prefix/ip.rs
  • lpm/src/prefix/with_ports.rs
  • mgmt/Cargo.toml
  • mgmt/src/processor/proc.rs
  • mgmt/src/tests/mgmt.rs

Comment thread acl-filter/src/context.rs
Comment thread acl-filter/src/tests.rs
Copilot AI review requested due to automatic review settings July 14, 2026 10:39
@qmonnet
qmonnet force-pushed the pr/qmonnet/user-acls branch from 81463d2 to 7f5a882 Compare July 14, 2026 10:39

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

Implements dataplane-side enforcement for the newly introduced user ACLs by adding an acl-filter stage to the packet-processing pipeline, wiring its hot-swappable context into the config processor, and adding the supporting model conversions and validation.

Changes:

  • Add new acl-filter crate implementing AclFilter network function + context building, display, and comprehensive tests.
  • Integrate ACL context publication into mgmt config processing and inject AclFilter stage after flow-filter in the dataplane pipeline.
  • Add match-action lowering helpers in lpm (prefix/port specs) and tighten ACL validation (reject empty ACL rule lists).

Reviewed changes

Copilot reviewed 19 out of 20 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
mgmt/src/tests/mgmt.rs Adds AclFilterContextWriter to test processor setup.
mgmt/src/processor/proc.rs Publishes AclFilterContext on reconfig and applies ACL config during processing.
mgmt/Cargo.toml Adds workspace dependency on acl-filter.
lpm/src/prefix/with_ports.rs Adds RangeSpec<u16> conversions + wildcard const for ACL lowering.
lpm/src/prefix/ip.rs Adds conversions from IPv4/IPv6 prefix types into match_action::PrefixSpec.
lpm/Cargo.toml Adds dependency on match-action.
dataplane/src/runtime.rs Wires ACL context writer into ConfigProcessorParams.
dataplane/src/packet_processor/mod.rs Installs AclFilter stage after flow-filter; creates reader/writer handles.
dataplane/Cargo.toml Adds workspace dependency on acl-filter.
config/src/external/overlay/vpc.rs Exposes validated peering ACL via ValidatedPeering::acl().
config/src/external/overlay/acl.rs Makes AclProtoMatch Copy, tweaks proto accessor, makes Acl::new public, rejects empty ACLs, adds tests.
Cargo.toml Adds acl-filter workspace member + workspace metadata.
Cargo.lock Records new dataplane-acl-filter package and dependency updates.
acl/src/reference/table.rs Adds accessor to read back stored rules for display/debugging.
acl-filter/src/tests.rs Adds unit + end-to-end tests for packet/flow scope behavior and NAT interactions.
acl-filter/src/lib.rs Implements AclFilter network function and packet summary logic.
acl-filter/src/display.rs Adds CLI/display decoding of lowered ACL rules and defaults.
acl-filter/src/context.rs Builds lowered match-action tables and default-action map from validated overlay ACLs.
acl-filter/src/access.rs Implements hot-swappable AclFilterContext reader/writer handles.
acl-filter/Cargo.toml New crate manifest with deps and dev-deps for tests.

Comment thread acl-filter/src/context.rs Outdated
Comment thread acl-filter/src/lib.rs
Comment thread acl-filter/src/lib.rs
Comment thread acl-filter/src/lib.rs
@qmonnet
qmonnet force-pushed the pr/qmonnet/user-acls branch 2 times, most recently from 82cb6ae to f615f3a Compare July 14, 2026 15:08
@qmonnet
qmonnet force-pushed the pr/qmonnet/user-acls branch from f615f3a to d254d56 Compare July 15, 2026 13:47

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@acl-filter/src/tests.rs`:
- Around line 446-474: Update the UDP section of
protocol_any_rule_matches_both_tcp_and_udp to remove the stale
TCP-only/default-deny explanation and replace it with a comment stating that the
same AclProtoMatch::Any rule should also allow UDP traffic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6516df48-699d-43e3-88cf-523e75e2e7e0

📥 Commits

Reviewing files that changed from the base of the PR and between f615f3a and d254d56.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (20)
  • Cargo.toml
  • acl-filter/Cargo.toml
  • acl-filter/src/access.rs
  • acl-filter/src/context.rs
  • acl-filter/src/display.rs
  • acl-filter/src/lib.rs
  • acl-filter/src/tests.rs
  • acl/src/reference/table.rs
  • config/Cargo.toml
  • config/src/converters/k8s/config/acl.rs
  • config/src/external/overlay/acl.rs
  • config/src/external/overlay/vpc.rs
  • dataplane/Cargo.toml
  • dataplane/src/packet_processor/mod.rs
  • dataplane/src/runtime.rs
  • lpm/src/prefix/with_ports.rs
  • mgmt/Cargo.toml
  • mgmt/src/processor/proc.rs
  • mgmt/src/tests/mgmt.rs
  • net/src/ip/mod.rs
🚧 Files skipped from review as they are similar to previous changes (13)
  • acl-filter/Cargo.toml
  • dataplane/Cargo.toml
  • config/src/external/overlay/vpc.rs
  • dataplane/src/runtime.rs
  • acl/src/reference/table.rs
  • acl-filter/src/display.rs
  • mgmt/Cargo.toml
  • mgmt/src/tests/mgmt.rs
  • mgmt/src/processor/proc.rs
  • dataplane/src/packet_processor/mod.rs
  • acl-filter/src/lib.rs
  • Cargo.toml
  • config/src/external/overlay/acl.rs

Comment thread acl-filter/src/tests.rs
manifest_left: &ValidatedManifest,
manifest_right: &ValidatedManifest,
) -> Result<ValidatedAcl, ConfigError> {
if self.rules.is_empty() {

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.

Didn't we say that in the API there would be a default ACL action that would be configurable?
How would this check work in such a case?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The Acl object always has a default "rule" (a default action) if no rule in the list matches, but it's not in self.rules, it's self.default:

pub struct Acl {
    default: AclAction,    <-- default if no other rule matches
    rules: Vec<AclRule>,
}

So we reject the Acl object if there's no other rule than the default one. It's easier for the rest of the processing, and it doesn't really make sense to have an empty list anyway (just remove the ACL, or the peering if you want to block everything).

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.

Ok. Understood. I would not add that restriction, though. If there is a default, that means that there need not be rules. So a config with Acl = {default: whatever, rule = empty} should be a valid one, even if you would achieve the same behavior with other configuration objects (or the lack of them).

match self.proto {
AclProtoMatch::Tcp | AclProtoMatch::Udp => true,
AclProtoMatch::Other(_) | AclProtoMatch::Icmp | AclProtoMatch::Any => {
AclProtoMatch::Other(_) | AclProtoMatch::Any => {

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 think ICMP may actually deserve special treatment since users may want to filter out/allow certain types of errors? Also, there are icmp packets (e.g. errors) that we may want to allow regardless?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

No objection with adding something specific for ICMP at some point.

At the icmp protocol field is not supported in the CRD right now (although it's trivial to add), and even if we add it, we don't need a specific variant internally.

If/when we want additional match options for ICMP in the future, we need to define the API first, update the CRD, and then we can revisit this part in dataplane - but at the moment I see no reason why to keep this variant.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Comment thread acl-filter/src/lib.rs
}
}

fn packet_has_valid_flow<'a>(

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.

Don't we have similar utils around that we could use for this?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Do you know where? I can't remember seeing one, but I can look.

Note: You may have seen something similar in the PR for the new flow-filter implementation? If we keep both (if we don't already have something for this, I mean), I'll move them to a shared location.

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.

probably masquerade / port-forwarding or flow-filter NFs use it

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Flow-filter will after the re-implementation, I haven't been able to find one that fits here in masquerade or port-forwarding

@Fredi-raspall

Copy link
Copy Markdown
Contributor

@qmonnet I'll continue my review once Daniel's changes land.

In preparation for using port ranges with ACL tables, implement trait
From to convert types with ports into RangeSpec<u16> elements.

Signed-off-by: Quentin Monnet <qmo@qmon.net>
@qmonnet qmonnet added ci:+vlab Enable VLAB tests ci:+cross/full labels Jul 17, 2026
@qmonnet

qmonnet commented Jul 17, 2026

Copy link
Copy Markdown
Member Author

I integrated the code changes from Daniel. The code is now the same as in #1639 (except for some cargo update that made it to that other PR, and which I dropped). It should pass CI (it did on the other PR) and should be ready to go. @mvachhar @daniel-noland @Fredi-raspall please take a look when you can (Daniel, please double-check the last commit).

@qmonnet

qmonnet commented Jul 17, 2026

Copy link
Copy Markdown
Member Author

All tests succeeded, but VLAB didn't run because I added the labels too late. Closing/reopening to trigger VLAB tests

@qmonnet qmonnet closed this Jul 17, 2026
@qmonnet qmonnet reopened this Jul 17, 2026

@daniel-noland daniel-noland left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nice work. LGTM

Comment thread acl-filter/src/lib.rs
Comment thread acl-filter/src/lib.rs
return;
}
};
let valid_flow = self.packet_has_valid_flow(packet.meta(), genid);

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.

If the flow has a flow but it is not valid, we should probably drop the packet already?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't think so? If the genid is too old, this means the flow was created from a previous config and is no longer valid; but this doesn't mean this packet is invalid. If it successfully goes through flow-filter without relying on the outdated flow, then gets marked for masquerade or port-forwarding, we can create a new updated flow for this packet?

Comment thread acl-filter/src/access.rs
/// Load the current context for read-only access.
#[must_use]
pub fn load(&self) -> Arc<AclFilterContext> {
self.0.load_full()

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.

Why load_full() and not load() ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Because I couldn't get it to compile with load() for all of the standard case, loom, and shuttle otherwise. I used load_full() like we use in masquerade for the allocator. I can look a bit more into it, do you know what the right solution would be instead?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@Fredi-raspall The alternative I have consists in introducing dedicated features for the crate:

diff --git i/Cargo.lock w/Cargo.lock
index 66dc60c5e598..d49acb88cfc8 100644
--- i/Cargo.lock
+++ w/Cargo.lock
@@ -1241,6 +1241,7 @@ dependencies = [
 name = "dataplane-acl-filter"
 version = "0.22.0"
 dependencies = [
+ "arc-swap",
  "dataplane-acl",
  "dataplane-common",
  "dataplane-concurrency",
diff --git i/acl-filter/Cargo.toml w/acl-filter/Cargo.toml
index ce11065fc465..e60b6fb3c6eb 100644
--- i/acl-filter/Cargo.toml
+++ w/acl-filter/Cargo.toml
@@ -5,8 +5,14 @@ license.workspace = true
 publish.workspace = true
 version.workspace = true
 
+[features]
+loom = ["concurrency/loom"]
+shuttle = ["concurrency/shuttle"]
+shuttle_dfs = ["concurrency/shuttle_dfs", "shuttle"]
+
 [dependencies]
 acl = { workspace = true }
+arc-swap = { workspace = true }
 common = { workspace = true }
 concurrency = { workspace = true }
 config = { workspace = true }
diff --git i/acl-filter/src/access.rs w/acl-filter/src/access.rs
index 0667dda7961a..5c1eb1fd954d 100644
--- i/acl-filter/src/access.rs
+++ w/acl-filter/src/access.rs
@@ -83,9 +83,17 @@ pub struct AclFilterContextReader(Arc<Slot<AclFilterContext>>);
 
 impl AclFilterContextReader {
     /// Load the current context for read-only access.
+    #[cfg(not(any(feature = "loom", feature = "shuttle")))]
+    #[must_use]
+    pub fn load(&self) -> arc_swap::Guard<Arc<AclFilterContext>> {
+        self.0.load()
+    }
+
+    /// Load the current context for read-only access (loom/shuttle).
+    #[cfg(any(feature = "loom", feature = "shuttle"))]
     #[must_use]
     pub fn load(&self) -> Arc<AclFilterContext> {
-        self.0.load_full()
+        self.0.load()
     }
 }
 

If you think it's worth it, I can change.

@daniel-noland or do you know if we have a wrapper for this already? Introducing new crate features just to accommodate for the return type doesn't look optimal, maybe I'm missing something simpler?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Discussed in meeting (without Fredi): Let's keep as it is for now, the perf delta isn't worth troubling ourselves with the new crate features. I added a FIXME: comment in the code to explain that, at some point, we should add a wrapper around the type in the concurrency crate, so we can use .load().

@Fredi-raspall

Copy link
Copy Markdown
Contributor

@qmonnet the PR looks good to me overall. I've pushed small enhancements / nits.
The only point to clarify is the use of load_full() (slower than load()) and whether we should drop packets pointing to non-valid flows.

qmonnet and others added 9 commits July 20, 2026 18:13
Add a new crate containing the implementation for the user ACLs: list of
access control rules that allow or deny specific packets or flow. We
recently introduced support for parsing and validating user ACL objects;
The new pipeline stage builds up context tables from validated ACLs, and
then apply the rules on coming packets.

With the "scope: flow" option, reply traffic for authorized flows is
automatically allowed as well (unless another explicit rule denies it).
This mode should be extended to all NAT modes (including no-NAT) in the
future, but is only supported with masquerade and port forwarding at
this time.

Internally, the new stage uses ACL structures (match-action) backed by
DPDK (rte_acl).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Daniel Noland <daniel@githedgehog.com>
Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Signed-off-by: Quentin Monnet <qmo@qmon.net>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Daniel Noland <daniel@githedgehog.com>
Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Signed-off-by: Quentin Monnet <qmo@qmon.net>
Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Add unit tests to validate the behaviour of the new ACL filter pipeline
stage.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Daniel Noland <daniel@githedgehog.com>
Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-authored-by: Fredi Raspall <fredi@githedgehog.com>
Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Signed-off-by: Quentin Monnet <qmo@qmon.net>
There are several reasons why packets may be dropped: routing,
not allowed by peering, etc. Define new drop reason for the case
where a packet is dropped due to an ACL denying it as this can
help operations.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
The ACL filter builds rte_acl classifiers when configuration is applied,
which needs the EAL memory subsystem up. Initialize a minimal EAL (no
hugepages / no PCI) early in dataplane::main(), before run_mgmt applies
the first config; the guard is held for the process. The real DPDK
datapath driver must eventually take over EAL ownership (there is one
rte_eal_init per process). The mgmt config-processor test starts the EAL
for the same reason.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Signed-off-by: Quentin Monnet <qmo@qmon.net>
Add a pipeline stage with the recently-added ACL filter to the main
pipeline, so that users can enforce ACLs on the traffic.

Signed-off-by: Quentin Monnet <qmo@qmon.net>
Under the loom backend, concurrency::sync::atomic::{AtomicU32,
AtomicU64}::new is not const: each instance registers with the loom
executor, so it cannot initialize a "static". This makes "cargo check
--features loom --workspace --tests --benches" fail with E0015 ("cannot
call non-const associated function ... in statics").

This is a consequence of a recent commit where we flipped acl's default
features from [] to ["dpdk"]. Every atomic static involved lives behind
'feature = "dpdk"' -- the integration tests and bench module, and the
lib's #[cfg(all(test, feature = "dpdk"))] modules. With dpdk off by
default they were compiled out and never seen by the loom backend;
turning dpdk on by default brings them into every build, including the
loom one.

Wrap each in LazyLock<Atomic..> so the initializer runs on first use
instead of at compile time. The value is still the backend atomic, so
"fetch_add" stays loom-instrumented; only the const requirement on
construction is removed. These are process-unique name/sequence counters
on cold paths, so the one-time lazy init is negligible, and on every
non-loom backend LazyLock merely wraps an otherwise-const atomic.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Quentin Monnet <qmo@qmon.net>
Add tracectl and linkme as dependencies and define a new tracing
target "acl-filter". Otherwise the info!()'s used to log the
verdict for a packet would be governed by the default loglevel,
which may not be desirable.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
@qmonnet
qmonnet force-pushed the pr/qmonnet/user-acls branch from 6bf22e6 to 7faebaa Compare July 20, 2026 17:14
@qmonnet

qmonnet commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

the PR looks good to me overall. I've pushed small enhancements / nits. The only point to clarify is the use of load_full() (slower than load()) and whether we should drop packets pointing to non-valid flows.

Fredi, I replied to your comments above. If the reply regarding the packet drop does not address your concern, we can discuss again and address as a follow-up. Regarding the .lookup(), we discussed it at the stand-up, it shouldn't have any impact on x86 and it's acceptable to keep for now.

I'm toggling auto-merge. Thanks for the reviews!

@qmonnet
qmonnet enabled auto-merge July 20, 2026 17:18
@qmonnet
qmonnet added this pull request to the merge queue Jul 20, 2026
Merged via the queue into main with commit fcdf254 Jul 20, 2026
150 of 158 checks passed
@qmonnet
qmonnet deleted the pr/qmonnet/user-acls branch July 20, 2026 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/acl Related to ACLs (Access Control Lists) ci:+cross/full ci:+vlab Enable VLAB tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

User ACL logic implementation

4 participants