Skip to content

Clean-up and minor fixes for flow-filter#1358

Merged
qmonnet merged 9 commits intomainfrom
pr/qmonnet/cleanup-flow-filter-lib
Mar 20, 2026
Merged

Clean-up and minor fixes for flow-filter#1358
qmonnet merged 9 commits intomainfrom
pr/qmonnet/cleanup-flow-filter-lib

Conversation

@qmonnet
Copy link
Member

@qmonnet qmonnet commented Mar 19, 2026

Split off from #1341.

Please refer to individual commit logs for details.

qmonnet added 6 commits March 19, 2026 11:05
Keep the main logic at the top. This brings methods
bypass_with_flow_info() and check_packet_flow_info() nearer to similar
functions, lower down in the file, and keeps process_packet() with the
core logic near the top of the file.

Signed-off-by: Quentin Monnet <qmo@qmon.net>
For consistency with the other logs in the rest of the file, use the
network function's name in logs from bypass_with_flow_info().

Signed-off-by: Quentin Monnet <qmo@qmon.net>
Make the function a method of struct FlowFilter, for consistency with
similar functions called by FlowFilter.process_packet().

Signed-off-by: Quentin Monnet <qmo@qmon.net>
Keep restructuring the code in flow-filter/src/lib.rs, by moving
processing function into FlowFilter's implementation.

Signed-off-by: Quentin Monnet <qmo@qmon.net>
Fix comments: there's no such thing as "port masquerading", it's
supposed to be "port forwarding".

Reported-by: Fredi Raspall <fredi@githedgehog.com>
Signed-off-by: Quentin Monnet <qmo@qmon.net>
When looking for an existing flow to determine where to send a packet,
in the case when the flow-filter lookup returned an ambiguous result, we
would discard the packet on finding an expired flow. This is incorrect:
if the flow is expired, then we cannot use it to determine the
destination VPC, but this is not a reason for dropping the packet.
Instead we need to carry on and try to determine the destination VPC
based on other means, potentially going through stateful NAT as a
follow-up step and recreating an up-to-date flow.

Also move the status check closer to the flow availability check itself.
No need to attempt to take the lock to retrieve the destination VPC if
the flow has expired.

Fixes: 2eb6274 ("feat(flow-filter): Update logic for port forwarding + masquerade overlap")
Signed-off-by: Quentin Monnet <qmo@qmon.net>
@qmonnet qmonnet requested a review from Fredi-raspall March 19, 2026 12:38
@qmonnet qmonnet self-assigned this Mar 19, 2026
@qmonnet qmonnet requested a review from a team as a code owner March 19, 2026 12:38
Copilot AI review requested due to automatic review settings March 19, 2026 12:38
@qmonnet qmonnet force-pushed the pr/qmonnet/cleanup-flow-filter-lib branch from c1c3a06 to 63e20c2 Compare March 19, 2026 12:41
Copy link
Contributor

Copilot AI left a comment

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 is a refactor/cleanup of the flow-filter stage’s flow-info handling, extracting common “active flow-info” logic into net::packet::Packet and reorganizing flow-filter helpers for bypassing and NAT requirement derivation.

Changes:

  • Add Packet::active_flow_info() helper in net to centralize “flow-info exists and is Active” checks.
  • Refactor flow-filter to use the new helper, reorganizing the bypass and multiple-match handling paths.
  • Move NAT-requirements-setting helpers into FlowFilter impl and streamline call sites.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
net/src/packet/mod.rs Adds active_flow_info() helper for retrieving only Active flow-info entries.
flow-filter/src/lib.rs Refactors flow-info usage to use active_flow_info(), adjusts bypass logic, and reorganizes NAT requirement helpers.
Comments suppressed due to low confidence (1)

flow-filter/src/lib.rs:29

confidence: 9
tags: [logic]

`FlowInfo`/`Packet::active_flow_info()` uses `concurrency::sync::Arc` (which becomes `shuttle::sync::Arc` under the workspace’s `shuttle` feature), but this file imports `std::sync::Arc` and now uses it in `Option<&Arc<FlowInfo>>` signatures. That will fail to typecheck (and likely to compile) when building with `--features shuttle` (e.g., via `nat`’s shuttle tests), because `PacketMeta.flow_info` is `Option<concurrency::sync::Arc<FlowInfo>>`.

Use `concurrency::sync::Arc` for `FlowInfo`-related arcs here (you may need to add a direct dependency on `concurrency` in `flow-filter/Cargo.toml`), and if you still need `std::sync::Arc` for `PipelineData`, alias one of them (e.g., `StdArc`).

use std::collections::HashSet;
use std::fmt::Display;
use std::net::IpAddr;
use std::num::NonZero;
use std::sync::Arc;
use tracing::{debug, error};

</details>



---

You can also share your feedback on Copilot code review. [Take the survey](https://www.surveymonkey.com/r/XP6L3XJ).

qmonnet added 3 commits March 20, 2026 14:30
In flow-filter's main logic, both functions check_packet_flow_info() and
bypass_with_flow_info() check for the availability, and then for the
status of flow information for the packet being processed.
And we may yet add another similar occurrence of
these checks in future work.

Let's move the two checks to a dedicated method from struct Packet in an
effort to remove duplicated code.

Note that we do not use the new method to check the status again after
looking for flow availability in set_nat_requirements_from_flow_info(),
because we've just checked it at the two locations where we called this
function. Checking it again in that function:

- Would be useless, as we already check it.
- Might introduce a bug in the logic if the flow expired between the two
  checks, because in that case we'd end up dropping the packet instead
  of acting as if there was no flow (in fact, if the first checked
  returned an active flow and the flow expired after the first check,
  it's still OK to keep going with this packet).

Signed-off-by: Quentin Monnet <qmo@qmon.net>
In an effort to de-duplicate some of the code in the flow-filter
processing, move the code to extract and cast the destination VPC
discriminant from packet's flow information to a dedicated function that
can be called in both check_packet_flow_info() and
bypass_with_flow_info().

Signed-off-by: Quentin Monnet <qmo@qmon.net>
We already implement Display for FlowFilterTable, we don't need it for
the network function object itself.

Signed-off-by: Quentin Monnet <qmo@qmon.net>
@qmonnet qmonnet force-pushed the pr/qmonnet/cleanup-flow-filter-lib branch from 63e20c2 to 9c364e5 Compare March 20, 2026 14:34
@qmonnet
Copy link
Member Author

qmonnet commented Mar 20, 2026

As discussed offline, I dropped the last commit and reverted the change in set_nat_requirements_from_flow_info() (I also added a note in the relevant commit description as to why we don't do the flow status check in that function).

Diff (click to expand)
diff --git c/flow-filter/src/lib.rs w/flow-filter/src/lib.rs
index b7ffade26aec..5a8b712eddcd 100644
--- c/flow-filter/src/lib.rs
+++ w/flow-filter/src/lib.rs
@@ -67,11 +67,9 @@ impl FlowFilter {
     ) {
         let nfi = &self.name;
         let genid = self.pipeline_data.genid();
-        let flow_info = packet.active_flow_info().cloned();
-        let dst_vpcd_from_flow_info = Self::dst_vpcd_from_flow_info(flow_info.as_ref());
 
-        // Bypass flow-filter if packet has flow-info and it is not outdated
-        if self.bypass_with_flow_info(packet, flow_info.as_ref(), genid, dst_vpcd_from_flow_info) {
+        // bypass flow-filter if packet has flow-info and it is not outdated
+        if self.bypass_with_flow_info(packet, genid) {
             return;
         }
 
@@ -112,12 +110,9 @@ impl FlowFilter {
                     "{nfi}: Found multiple matches for destination VPC for flow {tuple}. Checking for a flow table entry..."
                 );
 
-                match self.check_packet_flow_info(flow_info.as_ref(), dst_vpcd_from_flow_info) {
+                match self.check_packet_flow_info(packet) {
                     Ok(Some(dst_vpcd)) => {
-                        if self
-                            .set_nat_requirements_from_flow_info(packet, flow_info.as_ref())
-                            .is_ok()
-                        {
+                        if Self::set_nat_requirements_from_flow_info(packet).is_ok() {
                             Some(dst_vpcd)
                         } else {
                             debug!("{nfi}: Failed to set NAT requirements from flow info");
@@ -172,17 +167,14 @@ impl FlowFilter {
     }
 
     /// Check if flow-info is up-to-date and allows bypassing the main filtering logic.
-    /// Assumes `dst_vpcd_from_flow_info` was retrieved from `flow_info_opt`.
     fn bypass_with_flow_info<Buf: PacketBufferMut>(
         &self,
         packet: &mut Packet<Buf>,
-        flow_info_opt: Option<&Arc<FlowInfo>>,
         genid: i64,
-        dst_vpcd_from_flow_info: Option<VpcDiscriminant>,
     ) -> bool {
         let nfi = &self.name;
 
-        let Some(flow_info) = flow_info_opt else {
+        let Some(flow_info) = packet.active_flow_info() else {
             return false;
         };
         let flow_genid = flow_info.genid();
@@ -191,33 +183,32 @@ impl FlowFilter {
             return false;
         }
 
+        let vpcd = Self::dst_vpcd_from_flow_info(flow_info);
+
         debug!("{nfi}: Packet can bypass filter due to flow {flow_info}");
 
-        if self
-            .set_nat_requirements_from_flow_info(packet, flow_info_opt)
-            .is_err()
-        {
+        if Self::set_nat_requirements_from_flow_info(packet).is_err() {
             debug!("{nfi}: Failed to set nat requirements");
             return false;
         }
-        packet.meta_mut().dst_vpcd = dst_vpcd_from_flow_info;
+        packet.meta_mut().dst_vpcd = vpcd;
         true
     }
 
     /// Attempt to determine destination VPC from packet's flow-info.
-    /// Assumes `dst_vpcd_from_flow_info` was retrieved from `flow_info`.
-    fn check_packet_flow_info(
+    fn check_packet_flow_info<Buf: PacketBufferMut>(
         &self,
-        flow_info: Option<&Arc<FlowInfo>>,
-        dst_vpcd_from_flow_info: Option<VpcDiscriminant>,
+        packet: &mut Packet<Buf>,
     ) -> Result<Option<VpcDiscriminant>, DoneReason> {
         let nfi = &self.name;
 
-        if flow_info.is_none() {
+        let Some(flow_info) = packet.active_flow_info() else {
             return Ok(None);
-        }
+        };
 
-        let Some(dst_vpcd) = dst_vpcd_from_flow_info else {
+        let vpcd = Self::dst_vpcd_from_flow_info(flow_info);
+
+        let Some(dst_vpcd) = vpcd else {
             debug!("{nfi}: No VPC discriminant found, dropping packet");
             return Err(DoneReason::Unroutable);
         };
@@ -337,11 +328,16 @@ impl FlowFilter {
 
     /// Set NAT requirements on the packet based on packet's flow-info, if any.
     fn set_nat_requirements_from_flow_info<Buf: PacketBufferMut>(
-        &self,
         packet: &mut Packet<Buf>,
-        flow_info: Option<&Arc<FlowInfo>>,
     ) -> Result<(), ()> {
-        let locked_info = flow_info.ok_or(())?.locked.read().map_err(|_| ())?;
+        let locked_info = packet
+            .meta()
+            .flow_info
+            .as_ref()
+            .ok_or(())?
+            .locked
+            .read()
+            .map_err(|_| ())?;
         let needs_stateful_nat = locked_info.nat_state.is_some();
         let needs_port_forwarding = locked_info.port_fw_state.is_some();
         drop(locked_info);
@@ -361,8 +357,8 @@ impl FlowFilter {
 
     /// Extract the destination VPC discriminant from a flow-info entry.
     /// Panic if the lock has been poisoned.
-    fn dst_vpcd_from_flow_info(flow_info: Option<&Arc<FlowInfo>>) -> Option<VpcDiscriminant> {
-        flow_info?
+    fn dst_vpcd_from_flow_info(flow_info: &Arc<FlowInfo>) -> Option<VpcDiscriminant> {
+        flow_info
             .locked
             .read()
             .unwrap()

Thanks a lot for the review!

@qmonnet qmonnet added this pull request to the merge queue Mar 20, 2026
Merged via the queue into main with commit cb26398 Mar 20, 2026
21 checks passed
@qmonnet qmonnet deleted the pr/qmonnet/cleanup-flow-filter-lib branch March 20, 2026 15:40
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