From 14376071253d9791fdb4e54fda394ffca0f62f97 Mon Sep 17 00:00:00 2001 From: Daniel Noland Date: Mon, 23 Mar 2026 13:11:40 -0600 Subject: [PATCH 01/13] chore(config): deny clippy::redundant_closure_for_method_calls Remove `#![allow(clippy::redundant_closure_for_method_calls)]` and replace all redundant closures with method references throughout the config crate. I actually don't love this particular change because I thought it was fine before. That said, I hate having arguments with the linter a lot more. Co-authored-by: Claude Opus 4.6 Signed-off-by: Daniel Noland --- .../src/converters/k8s/status/dataplane_status.rs | 2 +- config/src/display.rs | 2 +- config/src/external/gwgroup.rs | 2 +- config/src/external/overlay/vpcpeering.rs | 14 ++++++++++---- config/src/external/underlay/mod.rs | 2 +- config/src/internal/device/tracecfg.rs | 2 +- config/src/lib.rs | 1 - 7 files changed, 15 insertions(+), 10 deletions(-) diff --git a/config/src/converters/k8s/status/dataplane_status.rs b/config/src/converters/k8s/status/dataplane_status.rs index 78d404dec..e6ab7375c 100644 --- a/config/src/converters/k8s/status/dataplane_status.rs +++ b/config/src/converters/k8s/status/dataplane_status.rs @@ -136,7 +136,7 @@ mod tests { let last_applied_time = time_gen.generate(d)?; let status = d .produce::>>()? - .map(|v| v.take()); + .map(LegalValue::take); let last_collected_time_raw = time_gen.generate(d)?; let last_collected_time = status.as_ref().map(|_| last_collected_time_raw); let last_heartbeat_raw = time_gen.generate(d)?; diff --git a/config/src/display.rs b/config/src/display.rs index 4842ff7ed..09731c563 100644 --- a/config/src/display.rs +++ b/config/src/display.rs @@ -333,7 +333,7 @@ impl Display for GwConfigMeta { let error = self .error .as_ref() - .map_or("none".to_string(), |e| e.to_string()); + .map_or("none".to_string(), std::string::ToString::to_string); let is_rollback = if self.is_rollback { "(rollback)" } else { "" }; diff --git a/config/src/external/gwgroup.rs b/config/src/external/gwgroup.rs index 377657024..67da84393 100644 --- a/config/src/external/gwgroup.rs +++ b/config/src/external/gwgroup.rs @@ -332,7 +332,7 @@ mod test { fn test_bgp_community_setup() { let comtable = sample_community_table(); let mut gwtable = build_sample_gw_groups(); - gwtable.iter_mut().for_each(|group| group.sort_members()); + gwtable.iter_mut().for_each(GwGroup::sort_members); println!("{gwtable}"); println!("{comtable}"); diff --git a/config/src/external/overlay/vpcpeering.rs b/config/src/external/overlay/vpcpeering.rs index 1d7b7d8e7..45eda90b6 100644 --- a/config/src/external/overlay/vpcpeering.rs +++ b/config/src/external/overlay/vpcpeering.rs @@ -249,9 +249,15 @@ impl VpcExpose { // only V4 atm vec![Prefix::root_v4()] } else if let Some(nat) = self.nat.as_ref() { - nat.as_range.iter().map(|p| p.prefix()).collect::>() + nat.as_range + .iter() + .map(PrefixWithOptionalPorts::prefix) + .collect::>() } else { - self.ips.iter().map(|p| p.prefix()).collect::>() + self.ips + .iter() + .map(PrefixWithOptionalPorts::prefix) + .collect::>() } } @@ -442,7 +448,7 @@ impl VpcExpose { fn prefixes_size(prefixes: &PrefixPortsSet) -> PrefixWithPortsSize { prefixes .iter() - .map(|p| p.size()) + .map(PrefixWithOptionalPorts::size) .sum::() } let zero_size = ppsize_zero(); @@ -528,7 +534,7 @@ impl VpcManifest { } #[must_use] pub fn has_host_prefixes(&self) -> bool { - self.exposes.iter().any(|expose| expose.has_host_prefixes()) + self.exposes.iter().any(VpcExpose::has_host_prefixes) } fn validate_expose_collisions(&self) -> ConfigResult { // Check that prefixes in each expose don't overlap with prefixes in other exposes diff --git a/config/src/external/underlay/mod.rs b/config/src/external/underlay/mod.rs index d47954d25..5573550d8 100644 --- a/config/src/external/underlay/mod.rs +++ b/config/src/external/underlay/mod.rs @@ -79,7 +79,7 @@ impl Underlay { self.vrf .interfaces .values() - .try_for_each(|iface| iface.validate())?; + .try_for_each(InterfaceConfig::validate)?; // set vtep information if a vtep interface has been specified in the config self.vtep = self.get_vtep_info()?; diff --git a/config/src/internal/device/tracecfg.rs b/config/src/internal/device/tracecfg.rs index 20851dd3a..c0efc3014 100644 --- a/config/src/internal/device/tracecfg.rs +++ b/config/src/internal/device/tracecfg.rs @@ -37,7 +37,7 @@ impl TracingConfig { } pub fn validate(&self) -> ConfigResult { debug!("Validating tracing configuration.."); - let tags: Vec<&str> = self.tags.keys().map(|k| k.as_str()).collect(); + let tags: Vec<&str> = self.tags.keys().map(String::as_str).collect(); Ok(get_trace_ctl().check_tags(&tags)?) } } diff --git a/config/src/lib.rs b/config/src/lib.rs index 0cd826887..4675d74ea 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -15,7 +15,6 @@ clippy::expect_used, clippy::panic )] -#![allow(clippy::redundant_closure_for_method_calls)] #![allow(clippy::doc_markdown)] #![allow(clippy::missing_errors_doc)] #![allow(clippy::struct_excessive_bools)] From 830ac616ad5cb9b8ffe4295b2072d9f12f40ef08 Mon Sep 17 00:00:00 2001 From: Daniel Noland Date: Mon, 23 Mar 2026 13:12:24 -0600 Subject: [PATCH 02/13] docs(config): deny clippy::doc_markdown Remove `#![allow(clippy::doc_markdown)]` and add backticks around type names in doc comments throughout the config crate. Co-authored-by: Claude Opus 4.6 Signed-off-by: Daniel Noland --- config/src/converters/k8s/status/bgp.rs | 2 +- config/src/external/overlay/validation_tests.rs | 2 +- config/src/external/overlay/vpcpeering.rs | 2 +- config/src/lib.rs | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/config/src/converters/k8s/status/bgp.rs b/config/src/converters/k8s/status/bgp.rs index 979ada8c9..e718badea 100644 --- a/config/src/converters/k8s/status/bgp.rs +++ b/config/src/converters/k8s/status/bgp.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Open Network Fabric Authors -//! Converters for internal BGP status -> K8s GatewayAgentStatusStateBgp CRD. +//! Converters for internal BGP status -> K8s `GatewayAgentStatusStateBgp` CRD. use std::collections::BTreeMap; diff --git a/config/src/external/overlay/validation_tests.rs b/config/src/external/overlay/validation_tests.rs index 9bb8b6c7b..75b2ecf59 100644 --- a/config/src/external/overlay/validation_tests.rs +++ b/config/src/external/overlay/validation_tests.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Open Network Fabric Authors -//! Validation tests for VpcExpose / VpcPeering / Overlay +//! Validation tests for `VpcExpose` / `VpcPeering` / Overlay //! //! These tests cover the expected semantics and restrictions for Expose objects in VPC peerings. //! diff --git a/config/src/external/overlay/vpcpeering.rs b/config/src/external/overlay/vpcpeering.rs index 45eda90b6..414a54f57 100644 --- a/config/src/external/overlay/vpcpeering.rs +++ b/config/src/external/overlay/vpcpeering.rs @@ -701,7 +701,7 @@ impl VpcPeering { } } - /// Create a VpcPeering mapped to a group called "default". + /// Create a `VpcPeering` mapped to a group called "default". /// This should only be used for tests #[must_use] pub fn with_default_group(name: &str, left: VpcManifest, right: VpcManifest) -> Self { diff --git a/config/src/lib.rs b/config/src/lib.rs index 4675d74ea..ef3297114 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -15,7 +15,6 @@ clippy::expect_used, clippy::panic )] -#![allow(clippy::doc_markdown)] #![allow(clippy::missing_errors_doc)] #![allow(clippy::struct_excessive_bools)] From a8c2457ee4b20b826b9b948ec3b2ca5715afcc87 Mon Sep 17 00:00:00 2001 From: Daniel Noland Date: Mon, 23 Mar 2026 13:13:58 -0600 Subject: [PATCH 03/13] docs(config): deny clippy::missing_errors_doc Remove `#![allow(clippy::missing_errors_doc)]` and add errors doc sections to all public functions returning Result throughout the config crate. Co-authored-by: Claude Opus 4.6 Signed-off-by: Daniel Noland --- config/src/external/communities.rs | 4 ++ config/src/external/gwgroup.rs | 11 ++++ config/src/external/mod.rs | 5 ++ config/src/external/overlay/mod.rs | 8 +++ config/src/external/overlay/vpcpeering.rs | 67 ++++++++++++++------- config/src/external/underlay/mod.rs | 5 ++ config/src/gwconfig.rs | 6 +- config/src/internal/device/mod.rs | 5 ++ config/src/internal/device/tracecfg.rs | 5 ++ config/src/internal/interfaces/interface.rs | 5 ++ config/src/internal/mod.rs | 5 ++ config/src/internal/routing/prefixlist.rs | 10 +++ config/src/internal/routing/routemap.rs | 5 ++ config/src/internal/routing/vrf.rs | 4 ++ config/src/lib.rs | 1 - 15 files changed, 121 insertions(+), 25 deletions(-) diff --git a/config/src/external/communities.rs b/config/src/external/communities.rs index 0f2ebbecf..139983723 100644 --- a/config/src/external/communities.rs +++ b/config/src/external/communities.rs @@ -16,6 +16,10 @@ impl PriorityCommunityTable { Self::default() } /// Insert a community + /// + /// # Errors + /// + /// Returns [`ConfigError::DuplicateCommunity`] if the community already exists in the table. pub fn insert(&mut self, order: usize, community: &str) -> Result<(), ConfigError> { if self.0.iter().any(|(_, comm)| comm == community) { return Err(ConfigError::DuplicateCommunity(community.to_string())); diff --git a/config/src/external/gwgroup.rs b/config/src/external/gwgroup.rs index 67da84393..c7b7e2735 100644 --- a/config/src/external/gwgroup.rs +++ b/config/src/external/gwgroup.rs @@ -77,6 +77,12 @@ impl GwGroup { //N.B. we reverse the operands since want the most preferred first self.members.sort_by(|m1, m2| m2.cmp(m1)); } + /// Add a member to the gateway group. + /// + /// # Errors + /// + /// Returns [`ConfigError::DuplicateMember`] if a member with the same name already exists. + /// Returns [`ConfigError::DuplicateMemberAddress`] if a member with the same IP address already exists. pub fn add_member(&mut self, member: GwGroupMember) -> Result<(), ConfigError> { if self.get_member_by_name(&member.name).is_some() { return Err(ConfigError::DuplicateMember(member.name.clone())); @@ -126,6 +132,11 @@ impl GwGroupTable { pub fn new() -> Self { Self::default() } + /// Add a gateway group to the table. + /// + /// # Errors + /// + /// Returns [`ConfigError::DuplicateGroup`] if a group with the same name already exists. pub fn add_group(&mut self, group: GwGroup) -> Result<(), ConfigError> { if self.0.contains_key(group.name()) { return Err(ConfigError::DuplicateGroup(group.name().to_owned())); diff --git a/config/src/external/mod.rs b/config/src/external/mod.rs index e8c2ecc3e..d8be5c668 100644 --- a/config/src/external/mod.rs +++ b/config/src/external/mod.rs @@ -93,6 +93,11 @@ impl ExternalConfig { } Ok(()) } + /// Validate the external configuration. + /// + /// # Errors + /// + /// Returns a [`ConfigError`] if validation fails. pub fn validate(&mut self) -> ConfigResult { self.device.validate()?; self.underlay.validate()?; diff --git a/config/src/external/overlay/mod.rs b/config/src/external/overlay/mod.rs index 525c78283..01fe756fe 100644 --- a/config/src/external/overlay/mod.rs +++ b/config/src/external/overlay/mod.rs @@ -39,6 +39,10 @@ impl Overlay { } /// Validate all peerings, checking if the VPCs they refer to exist in vpc table + /// + /// # Errors + /// + /// Returns an error if a peering references a VPC that does not exist in the VPC table. pub fn validate_peerings(&self) -> ConfigResult { debug!("Validating VPC peerings..."); for peering in self.peering_table.values() { @@ -60,6 +64,10 @@ impl Overlay { } /// Top most validation function for `Overlay` configuration + /// + /// # Errors + /// + /// Returns an error if the overlay configuration is invalid. pub fn validate(&mut self) -> ConfigResult { debug!("Validating overlay configuration..."); diff --git a/config/src/external/overlay/vpcpeering.rs b/config/src/external/overlay/vpcpeering.rs index 414a54f57..d54a7e375 100644 --- a/config/src/external/overlay/vpcpeering.rs +++ b/config/src/external/overlay/vpcpeering.rs @@ -83,11 +83,11 @@ pub struct VpcExpose { pub nat: Option, } impl VpcExpose { - // Make the [`VpcExpose`] use stateless NAT. - // - // # Errors - // - // Returns an error if the [`VpcExpose`] already has a different NAT mode. + /// Make the [`VpcExpose`] use stateless NAT. + /// + /// # Errors + /// + /// Returns an error if the [`VpcExpose`] already has a different NAT mode. pub fn make_stateless_nat(mut self) -> Result { match self.nat.as_mut() { Some(nat) if nat.is_stateless() => Ok(self), @@ -103,12 +103,12 @@ impl VpcExpose { } } - // Make the [`VpcExpose`] use stateful NAT, with the given idle timeout, if provided. - // If the [`VpcExpose`] is already in stateful mode, the idle timeout is overwritten. - // - // # Errors - // - // Returns an error if the [`VpcExpose`] already has a different NAT mode. + /// Make the [`VpcExpose`] use stateful NAT, with the given idle timeout, if provided. + /// If the [`VpcExpose`] is already in stateful mode, the idle timeout is overwritten. + /// + /// # Errors + /// + /// Returns an error if the [`VpcExpose`] already has a different NAT mode. pub fn make_stateful_nat( mut self, idle_timeout: Option, @@ -132,15 +132,15 @@ impl VpcExpose { } } - // Make the [`VpcExpose`] use port forwarding, with the given idle timeout, if provided, and the - // given L4 protocol, if provided. - // - // If the [`VpcExpose`] is already in port forwarding mode, the idle timeout and L4 protocol are - // overwritten. - // - // # Errors - // - // Returns an error if the [`VpcExpose`] already has a different NAT mode. + /// Make the [`VpcExpose`] use port forwarding, with the given idle timeout, if provided, and the + /// given L4 protocol, if provided. + /// + /// If the [`VpcExpose`] is already in port forwarding mode, the idle timeout and L4 protocol are + /// overwritten. + /// + /// # Errors + /// + /// Returns an error if the [`VpcExpose`] already has a different NAT mode. pub fn make_port_forwarding( mut self, idle_timeout: Option, @@ -223,6 +223,11 @@ impl VpcExpose { self.nots.insert(prefix); self } + /// Add a prefix to the NAT `as` range. + /// + /// # Errors + /// + /// Returns an error if the expose has no NAT configuration. pub fn as_range(mut self, prefix: PrefixWithOptionalPorts) -> Result { let nat = self.nat.as_mut().ok_or(ConfigError::MissingParameter( "'as' block requires NAT configuration for the expose", @@ -230,6 +235,11 @@ impl VpcExpose { nat.as_range.insert(prefix); Ok(self) } + /// Add a prefix to the NAT `not as` exclusion set. + /// + /// # Errors + /// + /// Returns an error if the expose has no NAT configuration. pub fn not_as(mut self, prefix: PrefixWithOptionalPorts) -> Result { let nat = self.nat.as_mut().ok_or(ConfigError::MissingParameter( "'not' prefix for 'as' block requires NAT configuration for the expose", @@ -367,7 +377,11 @@ impl VpcExpose { Ok(()) } - /// Validate the [`VpcExpose`]: + /// Validate the [`VpcExpose`]. + /// + /// # Errors + /// + /// Returns an error if the expose configuration is invalid. #[allow(clippy::too_many_lines)] pub fn validate(&self) -> ConfigResult { // Check default exposes and prefixes @@ -608,6 +622,11 @@ impl VpcManifest { pub fn add_exposes(&mut self, exposes: impl IntoIterator) { self.exposes.extend(exposes); } + /// Validate the [`VpcManifest`]. + /// + /// # Errors + /// + /// Returns an error if the manifest configuration is invalid. pub fn validate(&self) -> ConfigResult { if self.name.is_empty() { return Err(ConfigError::MissingIdentifier("Manifest name")); @@ -750,7 +769,11 @@ impl VpcPeeringTable { self.0.is_empty() } - /// Add a [`VpcPeering`] to a [`VpcPeeringTable`] + /// Add a [`VpcPeering`] to a [`VpcPeeringTable`]. + /// + /// # Errors + /// + /// Returns an error if the peering name is missing or a duplicate peering exists. pub fn add(&mut self, peering: VpcPeering) -> ConfigResult { if peering.name.is_empty() { return Err(ConfigError::MissingIdentifier("Peering name")); diff --git a/config/src/external/underlay/mod.rs b/config/src/external/underlay/mod.rs index 5573550d8..3c6f61976 100644 --- a/config/src/external/underlay/mod.rs +++ b/config/src/external/underlay/mod.rs @@ -72,6 +72,11 @@ impl Underlay { } } + /// Validate the underlay configuration. + /// + /// # Errors + /// + /// Returns an error if any interface is invalid or VTEP configuration is wrong. pub fn validate(&mut self) -> ConfigResult { debug!("Validating underlay configuration..."); diff --git a/config/src/gwconfig.rs b/config/src/gwconfig.rs index f0ecf12d1..4b7190502 100644 --- a/config/src/gwconfig.rs +++ b/config/src/gwconfig.rs @@ -109,9 +109,11 @@ impl GwConfig { self.external.genid } - ////////////////////////////////////////////////////////////////// /// Validate a [`GwConfig`]. We only validate the external. - ////////////////////////////////////////////////////////////////// + /// + /// # Errors + /// + /// Returns a [`ConfigError`] if the external configuration fails validation. pub fn validate(&mut self) -> ConfigResult { debug!("Validating external config with genid {} ..", self.genid()); self.external.validate() diff --git a/config/src/internal/device/mod.rs b/config/src/internal/device/mod.rs index 92b7f6797..1f2c33d93 100644 --- a/config/src/internal/device/mod.rs +++ b/config/src/internal/device/mod.rs @@ -22,6 +22,11 @@ impl DeviceConfig { pub fn set_tracing(&mut self, tracing: TracingConfig) { self.tracing = Some(tracing); } + /// Validate the device configuration. + /// + /// # Errors + /// + /// Returns an error if the device configuration is invalid. pub fn validate(&self) -> ConfigResult { debug!("Validating device configuration.."); if let Some(tracing) = &self.tracing { diff --git a/config/src/internal/device/tracecfg.rs b/config/src/internal/device/tracecfg.rs index c0efc3014..60108e375 100644 --- a/config/src/internal/device/tracecfg.rs +++ b/config/src/internal/device/tracecfg.rs @@ -35,6 +35,11 @@ impl TracingConfig { pub fn add_tag(&mut self, tag: &str, level: LevelFilter) { let _ = self.tags.insert(tag.to_string(), level); } + /// Validate the tracing configuration. + /// + /// # Errors + /// + /// Returns an error if any configured trace tag is unknown. pub fn validate(&self) -> ConfigResult { debug!("Validating tracing configuration.."); let tags: Vec<&str> = self.tags.keys().map(String::as_str).collect(); diff --git a/config/src/internal/interfaces/interface.rs b/config/src/internal/interfaces/interface.rs index bca4a9968..2b2e2aa17 100644 --- a/config/src/internal/interfaces/interface.rs +++ b/config/src/internal/interfaces/interface.rs @@ -156,6 +156,11 @@ impl InterfaceConfig { matches!(self.iftype, InterfaceType::Vtep(_)) } + /// Validate the interface configuration. + /// + /// # Errors + /// + /// Returns an error if the interface name is empty. pub fn validate(&self) -> ConfigResult { // name is mandatory if self.name.is_empty() { diff --git a/config/src/internal/mod.rs b/config/src/internal/mod.rs index c16a4035a..f1670c846 100644 --- a/config/src/internal/mod.rs +++ b/config/src/internal/mod.rs @@ -68,6 +68,11 @@ impl InternalConfig { pub fn get_vtep(&self) -> &Option { &self.vtep } + /// Add a VRF configuration to the internal config. + /// + /// # Errors + /// + /// Returns an error if the VRF has duplicate fields (name, table ID, or VNI). pub fn add_vrf_config(&mut self, vrf_cfg: VrfConfig) -> ConfigResult { self.vrfs.add_vrf_config(vrf_cfg) } diff --git a/config/src/internal/routing/prefixlist.rs b/config/src/internal/routing/prefixlist.rs index 088e854dd..b2eddaae2 100644 --- a/config/src/internal/routing/prefixlist.rs +++ b/config/src/internal/routing/prefixlist.rs @@ -66,6 +66,11 @@ impl PrefixList { entries: BTreeMap::new(), } } + /// Add an entry to the prefix list. + /// + /// # Errors + /// + /// Returns an error if the entry has an incompatible IP version or the sequence number is a duplicate. pub fn add_entry(&mut self, seq: Option, mut entry: PrefixListEntry) -> ConfigResult { if !entry.is_version_compatible(self.ipver) { let msg = format!( @@ -94,6 +99,11 @@ impl PrefixList { self.entries.insert(seq, entry); Ok(()) } + /// Add multiple entries to the prefix list. + /// + /// # Errors + /// + /// Returns an error if any entry has an incompatible IP version or a duplicate sequence number. pub fn add_entries( &mut self, entries: impl IntoIterator, diff --git a/config/src/internal/routing/routemap.rs b/config/src/internal/routing/routemap.rs index b55a3b107..439c509f1 100644 --- a/config/src/internal/routing/routemap.rs +++ b/config/src/internal/routing/routemap.rs @@ -101,6 +101,11 @@ impl RouteMap { entries: BTreeMap::new(), } } + /// Add an entry to the route map. + /// + /// # Errors + /// + /// Returns an error if the sequence number is a duplicate. pub fn add_entry(&mut self, seq: Option, entry: RouteMapEntry) -> ConfigResult { let seq = if let Some(n) = seq { n diff --git a/config/src/internal/routing/vrf.rs b/config/src/internal/routing/vrf.rs index dd884fe53..86601af50 100644 --- a/config/src/internal/routing/vrf.rs +++ b/config/src/internal/routing/vrf.rs @@ -137,6 +137,10 @@ impl VrfConfigTable { ////////////////////////////////////////////////////////// /// Store a `VrfConfig` object in this `VrfConfigTable` + /// + /// # Errors + /// + /// Returns an error if the VRF has duplicate fields (name, table ID, or VNI). ////////////////////////////////////////////////////////// pub fn add_vrf_config(&mut self, vrf_cfg: VrfConfig) -> ConfigResult { let name = vrf_cfg.name.clone(); diff --git a/config/src/lib.rs b/config/src/lib.rs index ef3297114..cdc3ab76b 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -15,7 +15,6 @@ clippy::expect_used, clippy::panic )] -#![allow(clippy::missing_errors_doc)] #![allow(clippy::struct_excessive_bools)] pub mod converters; From bda638a1c92afafd3d3f1b8fedf03b29a7074290 Mon Sep 17 00:00:00 2001 From: Daniel Noland Date: Mon, 23 Mar 2026 13:14:56 -0600 Subject: [PATCH 04/13] chore(config): deny clippy::struct_excessive_bools Remove `#![allow(clippy::struct_excessive_bools)]` and add per-struct `#[allow(clippy::struct_excessive_bools)]` annotations on BGP and BMP config structs where booleans directly model protocol flags. The lint removal was actually perfectly fine, but should be done on a per struct level rather than package wide. Co-authored-by: Claude Opus 4.6 Signed-off-by: Daniel Noland --- config/src/internal/routing/bgp.rs | 5 +++++ config/src/internal/routing/bmp.rs | 1 + config/src/lib.rs | 1 - 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/config/src/internal/routing/bgp.rs b/config/src/internal/routing/bgp.rs index a27e6c5ed..f4c796d5a 100644 --- a/config/src/internal/routing/bgp.rs +++ b/config/src/internal/routing/bgp.rs @@ -51,6 +51,7 @@ pub struct AfIpv6Ucast { } #[derive(Clone, Debug, Default)] +#[allow(clippy::struct_excessive_bools)] pub struct AfL2vpnEvpn { pub adv_all_vni: bool, pub adv_default_gw: bool, @@ -64,6 +65,7 @@ pub struct AfL2vpnEvpn { } #[derive(Clone, Debug, Default)] +#[allow(clippy::struct_excessive_bools)] pub struct BgpNeighCapabilities { pub dynamic: bool, pub ext_nhop: bool, @@ -119,6 +121,7 @@ impl BgpNeighAF { #[derive(Clone, Debug, Default)] /// A BGP neighbor config +#[allow(clippy::struct_excessive_bools)] pub struct BgpNeighbor { pub ntype: BgpNeighType, pub remote_as: Option, @@ -159,6 +162,7 @@ pub struct BgpNeighbor { } #[derive(Clone, Debug, Default)] +#[allow(clippy::struct_excessive_bools)] pub struct BgpDefaultsAF { flow_spec: bool, labeled_unicast: bool, @@ -178,6 +182,7 @@ pub struct BgpDefaults { #[derive(Clone, Debug)] /// BGP global configuration options +#[allow(clippy::struct_excessive_bools)] pub struct BgpOptions { pub network_import_check: bool, pub ebgp_requires_policy: bool, diff --git a/config/src/internal/routing/bmp.rs b/config/src/internal/routing/bmp.rs index 126f3249d..5b297ef10 100644 --- a/config/src/internal/routing/bmp.rs +++ b/config/src/internal/routing/bmp.rs @@ -26,6 +26,7 @@ pub enum BmpSource { } #[derive(Clone, Debug)] +#[allow(clippy::struct_excessive_bools)] pub struct BmpOptions { /// Name for `bmp targets ` pub target_name: String, diff --git a/config/src/lib.rs b/config/src/lib.rs index cdc3ab76b..57115bd98 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -15,7 +15,6 @@ clippy::expect_used, clippy::panic )] -#![allow(clippy::struct_excessive_bools)] pub mod converters; pub mod display; From 6335b42e9dfa3a3b9c91b9f80bd0d8cbb85217e9 Mon Sep 17 00:00:00 2001 From: Daniel Noland Date: Mon, 23 Mar 2026 15:30:01 -0600 Subject: [PATCH 05/13] style(net): minor cleanup for net First up, we aren't using kani at the moment so it is just causing clutter. Next, we had some over-broad `allows` running around which Claude and I cleaned up. Finally, we re-organized a few cargo build features to prep for the wasm build. These are pure code quality improvements with no functional change. --- Cargo.lock | 7 --- net/Cargo.toml | 8 +--- net/src/headers/embedded.rs | 67 +++++++++++++++++++-------- net/src/headers/mod.rs | 90 +++++++++++++++++++++++++------------ net/src/icmp6/mod.rs | 2 - net/src/ipv4/mod.rs | 4 -- net/src/ipv6/addr.rs | 1 - net/src/ipv6/mod.rs | 4 -- net/src/tcp/mod.rs | 5 --- net/src/udp/mod.rs | 5 --- net/src/vlan/mod.rs | 6 --- net/src/vxlan/mod.rs | 6 --- 12 files changed, 111 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8756e60ba..d58aad56d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -146,12 +146,6 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" - [[package]] name = "arc-swap" version = "1.9.0" @@ -507,7 +501,6 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98a5782f2650f80d533f58ec339c6dce4cc5428f9c2755894f98156f52af81f2" dependencies = [ - "arbitrary", "bolero-generator-derive", "either", "getrandom 0.3.4", diff --git a/net/Cargo.toml b/net/Cargo.toml index 5cf3dc327..3c73a17d2 100644 --- a/net/Cargo.toml +++ b/net/Cargo.toml @@ -17,7 +17,7 @@ test_buffer = [] arrayvec = { workspace = true, features = ["serde", "std"] } atomic-instant-full = { workspace = true } bitflags = { workspace = true } -bolero = { workspace = true, features = ["alloc", "arbitrary", "std"], optional = true } +bolero = { workspace = true, features = ["std"], optional = true } bytecheck = { workspace = true } concurrency = { workspace = true } derive_builder = { workspace = true, features = ["alloc"] } @@ -35,8 +35,4 @@ tracing = { workspace = true } [dev-dependencies] ahash = { workspace = true, features = ["no-rng"] } -bolero = { workspace = true, features = ["alloc", "arbitrary", "std"] } -etherparse = { workspace = true, default-features = false, features = ["std"] } - -[lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } +bolero = { workspace = true, features = ["std"] } \ No newline at end of file diff --git a/net/src/headers/embedded.rs b/net/src/headers/embedded.rs index cac4780e1..821bcb6bb 100644 --- a/net/src/headers/embedded.rs +++ b/net/src/headers/embedded.rs @@ -50,6 +50,7 @@ pub struct EmbeddedHeaders { impl EmbeddedHeaders { #[cfg(any(test, feature = "bolero"))] + #[must_use] pub fn new( net: Option, transport: Option, @@ -58,8 +59,8 @@ impl EmbeddedHeaders { ) -> Self { Self { net, - transport, net_ext, + transport, full_payload_length, } } @@ -95,25 +96,29 @@ impl EmbeddedHeaders { } } + #[must_use] pub fn net_headers_len(&self) -> u16 { - self.net.as_ref().map(|net| net.size().get()).unwrap_or(0) + self.net.as_ref().map_or(0, |net| net.size().get()) } + #[must_use] pub fn transport_headers_len(&self) -> u16 { self.transport .as_ref() - .map(|transport| transport.size().get()) - .unwrap_or(0) + .map_or(0, |transport| transport.size().get()) } + #[must_use] pub fn is_full_payload(&self) -> bool { self.full_payload_length.is_some() } + #[must_use] pub fn payload_length(&self) -> Option { self.full_payload_length } + #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)] pub fn check_full_payload( &mut self, buf: &[u8], @@ -125,18 +130,22 @@ impl EmbeddedHeaders { match &mut self.transport { None - | Some(EmbeddedTransport::Tcp(TruncatedTcp::PartialHeader(_))) - | Some(EmbeddedTransport::Udp(TruncatedUdp::PartialHeader(_))) - | Some(EmbeddedTransport::Icmp4(TruncatedIcmp4::PartialHeader(_))) - | Some(EmbeddedTransport::Icmp6(TruncatedIcmp6::PartialHeader(_))) => { + | Some( + EmbeddedTransport::Tcp(TruncatedTcp::PartialHeader(_)) + | EmbeddedTransport::Udp(TruncatedUdp::PartialHeader(_)) + | EmbeddedTransport::Icmp4(TruncatedIcmp4::PartialHeader(_)) + | EmbeddedTransport::Icmp6(TruncatedIcmp6::PartialHeader(_)), + ) => { // We couldn't parse the full transport header, of course we don't have the full, // valid payload return; } - Some(EmbeddedTransport::Tcp(TruncatedTcp::FullHeader(_))) - | Some(EmbeddedTransport::Udp(TruncatedUdp::FullHeader(_))) - | Some(EmbeddedTransport::Icmp4(TruncatedIcmp4::FullHeader(_))) - | Some(EmbeddedTransport::Icmp6(TruncatedIcmp6::FullHeader(_))) => { + Some( + EmbeddedTransport::Tcp(TruncatedTcp::FullHeader(_)) + | EmbeddedTransport::Udp(TruncatedUdp::FullHeader(_)) + | EmbeddedTransport::Icmp4(TruncatedIcmp4::FullHeader(_)) + | EmbeddedTransport::Icmp6(TruncatedIcmp6::FullHeader(_)), + ) => { // There's a chance payload is full, keep going } } @@ -467,6 +476,7 @@ pub enum EmbeddedTransport { } impl EmbeddedTransport { + #[must_use] pub fn icmp_identifier(&self) -> Option { match self { EmbeddedTransport::Icmp4(icmp) => icmp.identifier(), @@ -475,6 +485,7 @@ impl EmbeddedTransport { } } + #[must_use] pub fn source(&self) -> Option> { match self { EmbeddedTransport::Tcp(tcp) => Some(tcp.source().into()), @@ -483,6 +494,7 @@ impl EmbeddedTransport { } } + #[must_use] pub fn destination(&self) -> Option> { match self { EmbeddedTransport::Tcp(tcp) => Some(tcp.destination().into()), @@ -491,6 +503,12 @@ impl EmbeddedTransport { } } + /// Set the source port of the embedded transport header. + /// + /// # Errors + /// + /// Returns [`EmbeddedHeaderError::NoPorts`] if the transport header does not have ports + /// (i.e., ICMP). pub fn set_source(&mut self, port: NonZero) -> Result<(), EmbeddedHeaderError> { match self { EmbeddedTransport::Tcp(tcp) => { @@ -505,6 +523,12 @@ impl EmbeddedTransport { } } + /// Set the destination port of the embedded transport header. + /// + /// # Errors + /// + /// Returns [`EmbeddedHeaderError::NoPorts`] if the transport header does not have ports + /// (i.e., ICMP). pub fn set_destination(&mut self, port: NonZero) -> Result<(), EmbeddedHeaderError> { match self { EmbeddedTransport::Tcp(tcp) => { @@ -1145,6 +1169,7 @@ mod tests { // Basic parsing, deparsing checks #[test] + #[allow(clippy::cast_possible_truncation)] fn test_parse_ipv4_with_truncated_tcp() { let buf = create_truncated_ipv4_tcp_packet(); @@ -1162,6 +1187,7 @@ mod tests { } #[test] + #[allow(clippy::cast_possible_truncation)] fn test_parse_ipv4_with_full_udp() { let buf = create_full_ipv4_udp_packet(); @@ -1179,6 +1205,7 @@ mod tests { } #[test] + #[allow(clippy::cast_possible_truncation)] fn test_parse_ipv6_with_truncated_tcp() { let buf = create_truncated_ipv6_tcp_packet(); @@ -1196,6 +1223,7 @@ mod tests { } #[test] + #[allow(clippy::cast_possible_truncation)] fn test_parse_ipv6_with_full_udp() { let buf = create_full_ipv6_udp_packet(); @@ -1592,7 +1620,7 @@ mod tests { #[cfg(any(test, feature = "bolero"))] mod contract { - use super::*; + use super::{EmbeddedHeaders, EmbeddedTransport}; use crate::headers::Net; use crate::ipv4; use crate::ipv6; @@ -1606,17 +1634,18 @@ mod contract { type Output = EmbeddedHeaders; fn generate(&self, driver: &mut D) -> Option { - let (ipv4_next_header, ipv6_next_header, transport) = match driver.produce::()? { - true => ( + let (ipv4_next_header, ipv6_next_header, transport) = if driver.produce::()? { + ( ipv4::CommonNextHeader::Tcp, ipv6::CommonNextHeader::Tcp, EmbeddedTransport::Tcp(driver.produce::()?), - ), - false => ( + ) + } else { + ( ipv4::CommonNextHeader::Udp, ipv6::CommonNextHeader::Udp, EmbeddedTransport::Udp(driver.produce::()?), - ), + ) }; let is_ipv4 = driver.produce::()?; @@ -1680,6 +1709,6 @@ mod tests_fuzzing { None => { unreachable!() } - }) + }); } } diff --git a/net/src/headers/mod.rs b/net/src/headers/mod.rs index 668299567..fae70af3e 100644 --- a/net/src/headers/mod.rs +++ b/net/src/headers/mod.rs @@ -2,7 +2,7 @@ // Copyright Open Network Fabric Authors //! Definition of [`Headers`] and related methods and types. -#![allow(missing_docs, clippy::pedantic)] // temporary +#![allow(missing_docs)] // temporary use crate::checksum::Checksum; use crate::eth::ethtype::EthType; @@ -65,6 +65,7 @@ pub enum Net { } impl Net { + #[must_use] pub fn dst_addr(&self) -> IpAddr { match self { Net::Ipv4(ip) => IpAddr::V4(ip.destination()), @@ -72,6 +73,7 @@ impl Net { } } + #[must_use] pub fn src_addr(&self) -> IpAddr { match self { Net::Ipv4(ip) => IpAddr::V4(ip.source().inner()), @@ -79,6 +81,7 @@ impl Net { } } + #[must_use] pub fn next_header(&self) -> NextHeader { match self { Net::Ipv4(ip) => ip.protocol().into(), @@ -86,6 +89,12 @@ impl Net { } } + /// Sets the source address of the network header. + /// + /// # Errors + /// + /// Returns [`NetError::InvalidIpVersion`] if the IP version of `addr` does not match the + /// IP version of the network header. pub fn try_set_source(&mut self, addr: UnicastIpAddr) -> Result<(), NetError> { match (self, addr) { (Net::Ipv4(ip), UnicastIpAddr::V4(addr)) => { @@ -101,6 +110,12 @@ impl Net { Ok(()) } + /// Sets the destination address of the network header. + /// + /// # Errors + /// + /// Returns [`NetError::InvalidIpVersion`] if the IP version of `addr` does not match the + /// IP version of the network header. pub fn try_set_destination(&mut self, addr: IpAddr) -> Result<(), NetError> { match (self, addr) { (Net::Ipv4(ip), IpAddr::V4(addr)) => { @@ -195,7 +210,7 @@ impl Transport { icmp4 .update_checksum(payload.as_ref()) .unwrap_or_else(|()| unreachable!()); // Updating ICMPv4 checksum never fails - }; + } } (Net::Ipv6(ip), Transport::Icmp6(icmp6)) => { if icmp6.is_error_message() && embedded_headers.is_some() { @@ -315,6 +330,12 @@ impl Transport { Ok(()) } + /// Sets the ICMP identifier field. + /// + /// # Errors + /// + /// Returns [`TransportError::UnsupportedIdentifier`] if the transport header does not + /// support identifiers (i.e., TCP or UDP). pub fn try_set_identifier(&mut self, identifier: u16) -> Result<(), TransportError> { match self { Transport::Icmp4(icmp4) => icmp4 @@ -467,7 +488,7 @@ impl DeParse for Headers { fn size(&self) -> NonZero { // TODO(blocking): Deal with ip{v4,v6} extensions - let eth = self.eth.as_ref().map(|x| x.size().get()).unwrap_or(0); + let eth = self.eth.as_ref().map_or(0, |x| x.size().get()); let vlan = self.vlan.iter().map(|v| v.size().get()).sum::(); let net = match self.net { None => { @@ -482,7 +503,7 @@ impl DeParse for Headers { }; let encap = match self.udp_encap { None => 0, - Some(UdpEncap::Vxlan(vxlan)) => vxlan.size().get(), + Some(UdpEncap::Vxlan(vx)) => vx.size().get(), }; let embedded_ip = self .embedded_ip @@ -582,6 +603,7 @@ pub enum PopVlanError { impl Headers { /// Create a new [`Headers`] with the supplied `Eth` header. + #[must_use] pub fn new() -> Headers { Headers::default() } @@ -622,6 +644,12 @@ impl Headers { /// /// This method will ensure that the `eth` field has its [`EthType`] adjusted to /// [`EthType::VLAN`] if there are no [`Vlan`]s on the stack at the time this method was called. + /// + /// # Errors + /// + /// Returns [`PushVlanError::TooManyVlans`] if there are already [`MAX_VLANS`] VLANs on the + /// stack. + /// Returns [`PushVlanError::NoEthernetHeader`] if no Ethernet header is present. pub fn push_vlan(&mut self, vid: Vid) -> Result<(), PushVlanError> { if self.vlan.len() >= MAX_VLANS { return Err(PushVlanError::TooManyVlans); @@ -646,6 +674,10 @@ impl Headers { /// preserve the structure. /// /// If `None` is returned, the [`Headers`] is not modified. + /// + /// # Errors + /// + /// Returns [`PopVlanError::NoEthernetHeader`] if no Ethernet header is present. pub fn pop_vlan(&mut self) -> Result, PopVlanError> { match &mut self.eth { None => Err(PopVlanError::NoEthernetHeader), @@ -1371,6 +1403,7 @@ mod contract { impl ValueGenerator for CommonHeaders { type Output = Headers; + #[allow(clippy::too_many_lines)] fn generate(&self, driver: &mut D) -> Option { let common_eth_type: CommonEthType = driver.produce()?; let eth = GenWithEthType(common_eth_type.into()).generate(driver)?; @@ -1384,9 +1417,9 @@ mod contract { let tcp: Tcp = driver.produce()?; let headers = Headers { eth: Some(eth), - vlan: Default::default(), + vlan: ArrayVec::default(), net: Some(Net::Ipv4(ipv4)), - net_ext: Default::default(), + net_ext: ArrayVec::default(), transport: Some(Transport::Tcp(tcp)), udp_encap: None, embedded_ip: None, @@ -1403,9 +1436,9 @@ mod contract { }; let headers = Headers { eth: Some(eth), - vlan: Default::default(), + vlan: ArrayVec::default(), net: Some(Net::Ipv4(ipv4)), - net_ext: Default::default(), + net_ext: ArrayVec::default(), transport: Some(Transport::Udp(udp)), udp_encap, embedded_ip: None, @@ -1418,7 +1451,7 @@ mod contract { eth: Some(eth), vlan: ArrayVec::default(), net: Some(Net::Ipv4(ipv4)), - net_ext: Default::default(), + net_ext: ArrayVec::default(), transport: Some(Transport::Icmp4(icmp)), udp_encap: None, embedded_ip: None, @@ -1436,9 +1469,9 @@ mod contract { let tcp: Tcp = driver.produce()?; let headers = Headers { eth: Some(eth), - vlan: Default::default(), + vlan: ArrayVec::default(), net: Some(Net::Ipv6(ipv6)), - net_ext: Default::default(), + net_ext: ArrayVec::default(), transport: Some(Transport::Tcp(tcp)), udp_encap: None, embedded_ip: None, @@ -1455,9 +1488,9 @@ mod contract { }; let headers = Headers { eth: Some(eth), - vlan: Default::default(), + vlan: ArrayVec::default(), net: Some(Net::Ipv6(ipv6)), - net_ext: Default::default(), + net_ext: ArrayVec::default(), transport: Some(Transport::Udp(udp)), udp_encap, embedded_ip: None, @@ -1468,9 +1501,9 @@ mod contract { let icmp6: Icmp6 = driver.produce()?; let headers = Headers { eth: Some(eth), - vlan: Default::default(), + vlan: ArrayVec::default(), net: Some(Net::Ipv6(ipv6)), - net_ext: Default::default(), + net_ext: ArrayVec::default(), transport: Some(Transport::Icmp6(icmp6)), udp_encap: None, embedded_ip: None, @@ -1484,21 +1517,22 @@ mod contract { } } -#[cfg(any(test, kani))] -#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] // fine to unwarp in tests +#[cfg(test)] +#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] // fine to unwrap in tests mod test { use std::net::Ipv4Addr; + use crate::checksum::Checksum; use crate::headers::Headers; use crate::headers::contract::CommonHeaders; use crate::icmp4::Icmp4Checksum; use crate::parse::{DeParse, DeParseError, IntoNonZeroUSize, Parse, ParseError}; - use super::*; + use super::{Net, Transport}; use crate::icmp6::{Icmp6Checksum, Icmp6ChecksumPayload}; use crate::ipv4::{Ipv4Checksum, UnicastIpv4Addr}; - use crate::tcp::{TcpChecksum, TcpChecksumPayload}; - use crate::udp::{UdpChecksum, UdpChecksumPayload}; + use crate::tcp::{TcpChecksum, TcpChecksumPayload, TcpPort}; + use crate::udp::{UdpChecksum, UdpChecksumPayload, UdpPort}; fn parse_back_test(headers: &Headers) { let mut buffer = [0_u8; 1024]; @@ -1521,17 +1555,15 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_back() { - bolero::check!().with_type().for_each(parse_back_test) + bolero::check!().with_type().for_each(parse_back_test); } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_back_common() { bolero::check!() .with_generator(CommonHeaders) - .for_each(parse_back_test) + .for_each(parse_back_test); } mod sample { @@ -1696,6 +1728,7 @@ mod test { } } + #[allow(clippy::too_many_lines)] fn test_checksum(mut headers: Headers) { match &headers.transport { None => {} @@ -1794,7 +1827,7 @@ mod test { // Check incremental updates match &mut headers.transport { - None => {} + None | Some(Transport::Icmp6(_)) => {} Some(Transport::Udp(transport)) => { let net = headers.net.clone().unwrap(); let old_value = transport.source().into(); @@ -1845,9 +1878,8 @@ mod test { .expect("expected valid checksum"); } Net::Ipv6(_) => panic!("unexpected ipv6"), - }; + } } - Some(Transport::Icmp6(_)) => {} } } @@ -1927,7 +1959,7 @@ mod test { _ => unreachable!(), } } - _ => unreachable!(), + Net::Ipv6(_) => unreachable!(), }, _ => unreachable!(), } @@ -1980,7 +2012,7 @@ mod test { _ => unreachable!(), } } - _ => unreachable!(), + Net::Ipv6(_) => unreachable!(), }, _ => unreachable!(), } diff --git a/net/src/icmp6/mod.rs b/net/src/icmp6/mod.rs index f646e257b..e6d460372 100644 --- a/net/src/icmp6/mod.rs +++ b/net/src/icmp6/mod.rs @@ -475,7 +475,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_back() { bolero::check!() .with_type() @@ -483,7 +482,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_arbitrary_bytes() { bolero::check!() .with_type() diff --git a/net/src/ipv4/mod.rs b/net/src/ipv4/mod.rs index d30b53dcf..54e9ac885 100644 --- a/net/src/ipv4/mod.rs +++ b/net/src/ipv4/mod.rs @@ -525,7 +525,6 @@ mod test { const MAX_LEN_USIZE: usize = 60; #[test] - #[cfg_attr(kani, kani::proof)] fn parse_back() { bolero::check!().with_type().for_each(|header: &Ipv4| { let mut buffer = [0u8; MIN_LEN_USIZE]; @@ -540,14 +539,12 @@ mod test { assert_eq!(header.protocol(), parse_back.protocol()); assert_eq!(header.ecn(), parse_back.ecn()); assert_eq!(header.dscp(), parse_back.dscp()); - #[cfg(not(kani))] // remove when we fix options generation assert_eq!(header, &parse_back); assert_eq!(bytes_written, bytes_read); }); } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_arbitrary_bytes() { bolero::check!() .with_type() @@ -561,7 +558,6 @@ mod test { // reserved bit in ipv4 flags should serialize to zero assert_eq!(slice[6] & 0b0111_1111, buf[6]); assert_eq!(&slice[7..MIN_LEN_USIZE], &buf.as_slice()[7..MIN_LEN_USIZE]); - #[cfg(not(kani))] // remove when we fix options generation assert_eq!( &slice[MIN_LEN_USIZE..consumed.into_non_zero_usize().get()], &buf.as_slice()[MIN_LEN_USIZE..consumed.into_non_zero_usize().get()] diff --git a/net/src/ipv6/addr.rs b/net/src/ipv6/addr.rs index f67b2938d..355cc66c7 100644 --- a/net/src/ipv6/addr.rs +++ b/net/src/ipv6/addr.rs @@ -115,7 +115,6 @@ mod test { use crate::ipv6::addr::UnicastIpv6Addr; #[test] - #[cfg_attr(kani, kani::proof)] fn generated_unicast_ipv6_address_is_unicast() { bolero::check!() .with_type() diff --git a/net/src/ipv6/mod.rs b/net/src/ipv6/mod.rs index 86a6988d4..2ab7c190e 100644 --- a/net/src/ipv6/mod.rs +++ b/net/src/ipv6/mod.rs @@ -651,7 +651,6 @@ mod test { const MIN_LEN: usize = Ipv6::MIN_LEN.get() as usize; #[test] - #[cfg_attr(kani, kani::proof)] fn parse_back() { bolero::check!().with_type().for_each(|header: &Ipv6| { let mut buf = [0u8; MIN_LEN]; @@ -664,7 +663,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_arbitrary_bytes() { bolero::check!() .with_type() @@ -697,7 +695,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_arbitrary_bytes_too_short() { bolero::check!() .with_type() @@ -711,7 +708,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_arbitrary_bytes_above_minimum() { bolero::check!() .with_type() diff --git a/net/src/tcp/mod.rs b/net/src/tcp/mod.rs index 60586ac55..4a657e923 100644 --- a/net/src/tcp/mod.rs +++ b/net/src/tcp/mod.rs @@ -410,7 +410,6 @@ mod test { const MIN_LEN: usize = Tcp::MIN_LENGTH.get() as usize; #[test] - #[cfg_attr(kani, kani::proof)] fn parse_back() { bolero::check!().with_type().for_each(|tcp: &Tcp| { let mut buffer = [0u8; 64]; @@ -439,14 +438,12 @@ mod test { assert_eq!(tcp.urg(), parsed.urg()); assert_eq!(tcp.window_size(), parsed.window_size()); assert_eq!(tcp.urgent_pointer(), parsed.urgent_pointer()); - #[cfg(not(kani))] // remove after fixing options assert_eq!(tcp, &parsed); assert_eq!(consumed, consumed2); }); } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_noise() { bolero::check!() .with_type() @@ -477,13 +474,11 @@ mod test { let (parsed_back, consumed3) = Tcp::parse(&slice2[..consumed2.into_non_zero_usize().get()]).unwrap(); assert_eq!(consumed2, consumed3); - #[cfg(not(kani))] // remove after fixing options assert_eq!(parsed, parsed_back); assert_eq!(&slice[..12], &slice2[..12]); // check for reserved bits getting zeroed by `write` (regardless of inputs) assert_eq!(slice[12] & 0b1111_0001, slice2[12]); assert_eq!(&slice[13..MIN_LEN], &slice2[13..MIN_LEN]); - #[cfg(not(kani))] // remove after fixing options assert_eq!( &slice[MIN_LEN..consumed1.into_non_zero_usize().get()], &slice2[MIN_LEN..consumed1.into_non_zero_usize().get()] diff --git a/net/src/udp/mod.rs b/net/src/udp/mod.rs index 02dc34767..d173feecc 100644 --- a/net/src/udp/mod.rs +++ b/net/src/udp/mod.rs @@ -283,7 +283,6 @@ mod test { const MIN_LENGTH_USIZE: usize = 8; #[test] - #[cfg_attr(kani, kani::proof)] fn parse_back() { bolero::check!().with_type().for_each(|input: &Udp| { let mut buffer = [0u8; MIN_LENGTH_USIZE]; @@ -306,7 +305,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_arbitrary_bytes() { bolero::check!() .with_type() @@ -335,7 +333,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn too_short_buffer_parse_fails_gracefully() { bolero::check!() .with_type() @@ -353,7 +350,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn longer_buffer_parses_ok() { bolero::check!() .with_type() @@ -385,7 +381,6 @@ mod test { // evolve an arbitrary source towards an arbitrary target to make sure mutation methods work #[test] - #[cfg_attr(kani, kani::proof)] fn arbitrary_mutation() { bolero::check!() .with_type() diff --git a/net/src/vlan/mod.rs b/net/src/vlan/mod.rs index 572b8cc92..b992f8451 100644 --- a/net/src/vlan/mod.rs +++ b/net/src/vlan/mod.rs @@ -500,7 +500,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn pcp_bounds_respected() { bolero::check!() .with_type() @@ -519,7 +518,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_back() { bolero::check!().with_type().for_each(|vlan: &Vlan| { let mut buf = [0u8; MIN_LENGTH_USIZE]; // vlan headers are always 4 bytes long @@ -538,7 +536,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_noise() { bolero::check!().with_type().for_each(|buf: &[u8; MIN_LENGTH_USIZE]| { let (vlan, consumed) = match Vlan::parse(buf) { @@ -561,7 +558,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_noise_too_short() { bolero::check!().with_type().for_each( |buf: &[u8; MIN_LENGTH_USIZE - 1]| match Vlan::parse(buf) { @@ -575,7 +571,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn arbitrary_mutation() { bolero::check!() .with_type() @@ -595,7 +590,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn deparse_to_insufficient_buffer_is_graceful() { bolero::check!().with_type().for_each(|vlan: &Vlan| { let mut buf = [0u8; MIN_LENGTH_USIZE - 1]; diff --git a/net/src/vxlan/mod.rs b/net/src/vxlan/mod.rs index 6f84a77aa..02c545d39 100644 --- a/net/src/vxlan/mod.rs +++ b/net/src/vxlan/mod.rs @@ -152,7 +152,6 @@ mod test { const MIN_LENGTH_USIZE: usize = 8; #[test] - #[cfg_attr(kani, kani::proof)] fn parse_back() { bolero::check!().with_type().for_each(|vxlan: &Vxlan| { assert_eq!(vxlan.size(), Vxlan::MIN_LENGTH); @@ -167,7 +166,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn creation_identity_check() { bolero::check!().with_type().for_each(|vxlan: &Vxlan| { assert_eq!(vxlan, &Vxlan::new(vxlan.vni())); @@ -176,7 +174,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_noise() { bolero::check!() .with_type() @@ -223,7 +220,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn write_to_insufficient_buffer_fails_gracefully() { bolero::check!().with_type().for_each(|vni: &Vxlan| { let mut too_small_buffer = [0u8; MIN_LENGTH_USIZE - 1]; @@ -238,7 +234,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn parse_of_insufficient_buffer_fails_gracefully() { bolero::check!() .with_type() @@ -254,7 +249,6 @@ mod test { } #[test] - #[cfg_attr(kani, kani::proof)] fn mutation_of_header_preserves_contract() { bolero::check!() .with_type() From eec967e4555607f64174d88ef880d43ee311642d Mon Sep 17 00:00:00 2001 From: Daniel Noland Date: Mon, 23 Mar 2026 15:30:25 -0600 Subject: [PATCH 06/13] style(config): minor cleanup We had some over-broad style violations in the form of package wide `allow`s. - Remove unused import of peering in vpc.rs - Add missing doc comment sections (`# Errors`) for VpcPeering::validate - Fix doc_markdown lint: wrap VpcPeering in backticks These are pure code quality improvements with no functional change. --- config/src/external/overlay/vpc.rs | 1 - config/src/external/overlay/vpcpeering.rs | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/config/src/external/overlay/vpc.rs b/config/src/external/overlay/vpc.rs index 55395fb9c..6107f3da3 100644 --- a/config/src/external/overlay/vpc.rs +++ b/config/src/external/overlay/vpc.rs @@ -12,7 +12,6 @@ use std::collections::BTreeMap; use std::collections::BTreeSet; use tracing::{debug, error, warn}; -use crate::converters::k8s::config::peering; use crate::external::overlay::VpcManifest; use crate::external::overlay::VpcPeeringTable; use crate::external::overlay::vpcpeering::VpcExposeNatConfig; diff --git a/config/src/external/overlay/vpcpeering.rs b/config/src/external/overlay/vpcpeering.rs index d54a7e375..5f207648b 100644 --- a/config/src/external/overlay/vpcpeering.rs +++ b/config/src/external/overlay/vpcpeering.rs @@ -733,7 +733,11 @@ impl VpcPeering { } #[cfg(test)] - /// Validate A VpcPeering. Only used in tests. Dataplane validates `Peerings` + /// Validate A `VpcPeering`. Only used in tests. Dataplane validates `Peerings` + /// + /// # Errors + /// + /// Returns an error if the peering configuration is invalid. pub fn validate(&self) -> ConfigResult { self.left.validate()?; self.right.validate()?; From 55972f6b4aef0a3d78e208b821de28799e4759fb Mon Sep 17 00:00:00 2001 From: Daniel Noland Date: Mon, 23 Mar 2026 15:30:37 -0600 Subject: [PATCH 07/13] build(nix): add wasm32-wasip1 and wasm32-wasip2 rust targets Add WASM/WASI targets to the Rust toolchain configuration in the Nix overlay. This enables cross-compilation to wasm32-wasip1 and wasm32-wasip2 from the development environment. --- nix/overlays/llvm.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nix/overlays/llvm.nix b/nix/overlays/llvm.nix index 5bffc8823..286370ab9 100644 --- a/nix/overlays/llvm.nix +++ b/nix/overlays/llvm.nix @@ -42,6 +42,8 @@ let ]; targets = [ platform.info.target + "wasm32-wasip1" + "wasm32-wasip2" ]; }; rustPlatform' = final.makeRustPlatform { From a01cb27045b6943e3f1135993f35abeb9b58f3a0 Mon Sep 17 00:00:00 2001 From: Daniel Noland Date: Mon, 23 Mar 2026 15:30:49 -0600 Subject: [PATCH 08/13] build(net): prep for wasm32-wasip1 - Move atomic-instant-full to `cfg(x86_64, aarch64)` target deps (requires native atomic instructions not available on wasm32) - Move linux-raw-sys to `cfg(unix)` target deps - Gate flows and packet modules with `#![cfg(unix)]` (depend on linux-specific types and atomic-instant-full) - Gate flows re-export in lib.rs with `#[cfg(unix)]` - Replace linux_raw_sys::if_ether::ETH_MAX_MTU constant with hardcoded 65535 in Mtu, gating the linux_raw_sys import These changes allow the net crate to compile on wasm32-wasip1 while preserving all existing functionality on unix targets. --- net/Cargo.toml | 8 ++++++-- net/src/flows/mod.rs | 1 + net/src/interface/mtu.rs | 4 ++-- net/src/lib.rs | 2 ++ net/src/packet/mod.rs | 2 ++ 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/net/Cargo.toml b/net/Cargo.toml index 3c73a17d2..78d5340fe 100644 --- a/net/Cargo.toml +++ b/net/Cargo.toml @@ -13,9 +13,14 @@ netdevsim = [] bolero = ["dep:bolero", "id/bolero"] test_buffer = [] +[target.'cfg(any(target_arch = "x86_64", target_arch = "aarch64"))'.dependencies] +atomic-instant-full = { workspace = true } + +[target.'cfg(unix)'.dependencies] +linux-raw-sys = { workspace = true, features = ["std", "if_ether"] } + [dependencies] arrayvec = { workspace = true, features = ["serde", "std"] } -atomic-instant-full = { workspace = true } bitflags = { workspace = true } bolero = { workspace = true, features = ["std"], optional = true } bytecheck = { workspace = true } @@ -24,7 +29,6 @@ derive_builder = { workspace = true, features = ["alloc"] } downcast-rs = { workspace = true, features = ["sync"] } etherparse = { workspace = true, features = ["std"] } id = { workspace = true } -linux-raw-sys = { workspace = true, features = ["std", "if_ether"] } multi_index_map = { workspace = true, default-features = false, features = ["serde"] } rapidhash = { workspace = true } rkyv = { workspace = true, features = ["alloc", "bytecheck"]} diff --git a/net/src/flows/mod.rs b/net/src/flows/mod.rs index 32f8d6e42..38a3de242 100644 --- a/net/src/flows/mod.rs +++ b/net/src/flows/mod.rs @@ -3,6 +3,7 @@ //! Definitions for flow keys +#![cfg(unix)] #![allow(missing_docs)] pub mod atomic_instant; diff --git a/net/src/interface/mtu.rs b/net/src/interface/mtu.rs index 2abbf1fba..e24b66383 100644 --- a/net/src/interface/mtu.rs +++ b/net/src/interface/mtu.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Open Network Fabric Authors -use linux_raw_sys::if_ether; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; use std::fmt::{Display, Formatter}; @@ -16,7 +15,8 @@ pub struct Mtu(NonZero); impl Mtu { pub(crate) const MIN_U32: u32 = 1280; // 1280 IPv6 minimum MTU - pub(crate) const MAX_U32: u32 = if_ether::ETH_MAX_MTU; + pub(crate) const MAX_U32: u32 = 65535; + pub(crate) const DEFAULT_U32: u32 = 1500; /// The minimum legal MTU for an IPv6 interface is 1280 bytes. diff --git a/net/src/lib.rs b/net/src/lib.rs index 001df2670..0c8ee416e 100644 --- a/net/src/lib.rs +++ b/net/src/lib.rs @@ -18,6 +18,7 @@ pub mod addr_parse_error; pub mod buffer; pub mod checksum; pub mod eth; +#[cfg(unix)] pub mod flows; pub mod headers; pub mod icmp4; @@ -38,6 +39,7 @@ pub mod vlan; pub mod vxlan; // re-export +#[cfg(unix)] pub use flows::flow_key::{ self, ExtendedFlowKey, FlowKey, FlowKeyData, IcmpProtoKey, IpProtoKey, TcpProtoKey, UdpProtoKey, }; diff --git a/net/src/packet/mod.rs b/net/src/packet/mod.rs index 64e5cdab7..9970cc74e 100644 --- a/net/src/packet/mod.rs +++ b/net/src/packet/mod.rs @@ -3,6 +3,8 @@ //! Packet struct and methods +#![cfg(unix)] + mod display; mod hash; mod meta; From a7b1c9729ac21d1d7002942749c0a2375d175e11 Mon Sep 17 00:00:00 2001 From: Daniel Noland Date: Mon, 23 Mar 2026 15:30:59 -0600 Subject: [PATCH 09/13] build(hardware): make sysfs dependency optional behind feature flag The sysfs crate provides Linux sysfs/procfs access which is not available on wasm32 targets. Make it optional behind a 'sysfs' feature (enabled by default) and gate the nic module that depends on it. This allows the hardware crate's core types (PCI addresses, bridge attributes, etc.) to be used on non-Linux targets without pulling in Linux-specific filesystem dependencies. --- hardware/Cargo.toml | 6 ++++-- hardware/src/lib.rs | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/hardware/Cargo.toml b/hardware/Cargo.toml index 3c889842e..ecc58242a 100644 --- a/hardware/Cargo.toml +++ b/hardware/Cargo.toml @@ -7,18 +7,20 @@ publish.workspace = true version.workspace = true [features] -default = [] +default = ["sysfs"] # Enables property testing / fuzzing support for testing. bolero = ["dep:bolero"] # Enables hardware topology scanning using the `hwlocality` crate. scan = ["dep:hwlocality", "dep:pci-ids"] # Adds serialization support for all types using serde. serde = ["dep:serde", "id/serde"] +# Enables sysfs/procfs support (linux-only; disable for WASM targets). +sysfs = ["dep:sysfs"] [dependencies] # internal id = { workspace = true, features = ["rkyv"] } -sysfs = { workspace = true } +sysfs = { workspace = true, optional = true } # external bolero = { workspace = true, optional = true, features = ["alloc"] } diff --git a/hardware/src/lib.rs b/hardware/src/lib.rs index 70ed25389..4afbb98d4 100644 --- a/hardware/src/lib.rs +++ b/hardware/src/lib.rs @@ -17,6 +17,7 @@ use crate::pci::bridge::BridgeAttributes; pub mod group; pub mod mem; +#[cfg(feature = "sysfs")] pub mod nic; pub mod os; pub mod pci; From 6632bffa1ebd5eaa9a4f07d7ef2e4504ef0b7a9f Mon Sep 17 00:00:00 2001 From: Daniel Noland Date: Mon, 23 Mar 2026 15:31:13 -0600 Subject: [PATCH 10/13] build(k8s-intf): feature-gate K8s client dependencies Introduce a 'client' feature (enabled by default) that gates the heavy, WASM-incompatible dependencies behind an opt-in feature The generated CRD types and their derive dependencies (kube with just `derive`, `kube-core`, `k8s-openapi`, `schemars`, `serde`, `serde_json`, `serde_yaml_ng`) remain always available. Gate the client.rs and utils.rs modules (and the `watch_gateway_agent_crd` re-export) with `#[cfg(feature = "client")]`. This allows crates that only need CRD type definitions (like the validator) to depend on `k8s-intf` with `default-features = false`, avoiding the entire async/crypto/linker dependency tree that prevents wasm32-wasip1 compilation. --- k8s-intf/Cargo.toml | 32 +++++++++++++++++++++++--------- k8s-intf/src/lib.rs | 3 +++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/k8s-intf/Cargo.toml b/k8s-intf/Cargo.toml index 51836cddb..59af332fe 100644 --- a/k8s-intf/Cargo.toml +++ b/k8s-intf/Cargo.toml @@ -6,36 +6,50 @@ publish.workspace = true version.workspace = true [features] +default = ["client"] +# Enables the full K8s client, watcher, and status-reporting functionality. +# Disable this feature (default-features = false) for WASM/WASI builds that +# only need the generated CRD types. +client = [ + "dep:futures", + "dep:linkme", + "dep:rustls", + "dep:tokio", + "dep:tracectl", + "kube/client", + "kube/runtime", + "kube/rustls-tls", +] bolero = ["dep:bolero", "dep:hardware", "dep:net", "dep:lpm", "net/test_buffer", "net/bolero", "hardware/bolero"] [dependencies] # internal -hardware = { workspace = true, optional = true, features = ["bolero"] } +hardware = { workspace = true, optional = true, features = ["bolero", "sysfs"] } lpm = { workspace = true, optional = true } net = { workspace = true, optional = true, features = ["bolero"] } -tracectl = { workspace = true } +tracectl = { workspace = true, optional = true } tracing = { workspace = true } # external bolero = { workspace = true, optional = true } -futures = { workspace = true } -kube = { workspace = true, features = ["client", "derive", "runtime", "rustls-tls"] } +futures = { workspace = true, optional = true } +kube = { workspace = true, features = ["derive"] } kube-core = { workspace = true } k8s-openapi = { workspace = true, features = ["latest", "schemars", "std"] } -linkme = { workspace = true } -rustls = { workspace = true, features = ["aws-lc-rs"] } +linkme = { workspace = true, optional = true } +rustls = { workspace = true, features = ["aws-lc-rs"], optional = true } schemars = { workspace = true, features = ["derive", "std"] } serde = { workspace = true } serde_json = { workspace = true } serde_yaml_ng = { workspace = true } thiserror = { workspace = true } -tokio = { workspace = true } +tokio = { workspace = true, optional = true } [dev-dependencies] bolero = { workspace = true } -hardware = { workspace = true, features = ["bolero"] } +hardware = { workspace = true, features = ["bolero", "sysfs"] } lpm = { workspace = true, features = [] } net = { workspace = true, features = ["bolero", "test_buffer"] } [build-dependencies] -dpdk-sysroot-helper = { workspace = true } +dpdk-sysroot-helper = { workspace = true } \ No newline at end of file diff --git a/k8s-intf/src/lib.rs b/k8s-intf/src/lib.rs index 0d5bad7a1..cf0d486d8 100644 --- a/k8s-intf/src/lib.rs +++ b/k8s-intf/src/lib.rs @@ -7,7 +7,9 @@ #[cfg(any(test, feature = "bolero"))] pub mod bolero; +#[cfg(feature = "client")] pub mod client; +#[cfg(feature = "client")] pub mod utils; #[allow(clippy::all, clippy::pedantic)] @@ -16,4 +18,5 @@ pub mod gateway_agent_crd { include!(concat!(env!("OUT_DIR"), "/gateway_agent_crd.rs")); } +#[cfg(feature = "client")] pub use client::watch_gateway_agent_crd; From 54195e9d8ca6a083ba70251262d63c8e4b8b99d4 Mon Sep 17 00:00:00 2001 From: Daniel Noland Date: Mon, 23 Mar 2026 15:31:26 -0600 Subject: [PATCH 11/13] build(config): gate tracectl and linkme behind `cfg(unix)` Move tracectl and linkme to `[target.'cfg(unix)'.dependencies]` in config/Cargo.toml since both depend on unix-specific functionality: - `tracectl`: uses unix-only tracing infrastructure - `linkme`: uses linker section injection (not available on wasm32) Provide platform-conditional alternatives for non-unix targets: - Use `tracing::metadata::LevelFilter` as a drop-in for `tracectl::LevelFilter` in tracecfg converters - Define `DEFAULT_DEFAULT_LOGLEVEL` constant for non-unix - Provide no-op `TracingConfig::validate()` on non-unix - Add `cfg(unix)` gate on `trace_target!` macro invocation in lib.rs - Add non-unix ConfigError::Tracing variant using String Also use `default-features = false` for hardware and k8s-intf deps, and explicitly add sysfs feature where needed in dev-dependencies. --- config/Cargo.toml | 14 +++++++----- config/src/converters/k8s/config/tracecfg.rs | 3 +++ config/src/errors.rs | 4 ++++ config/src/internal/device/tracecfg.rs | 24 ++++++++++++++++++-- config/src/lib.rs | 2 ++ 5 files changed, 39 insertions(+), 8 deletions(-) diff --git a/config/Cargo.toml b/config/Cargo.toml index 48bc05abd..ff4bb3856 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -9,31 +9,33 @@ version.workspace = true # internal common = { workspace = true } hardware = { workspace = true } +k8s-intf = { workspace = true } net = { workspace = true } lpm = { workspace = true } -tracectl = { workspace = true } -k8s-intf = { workspace = true } # external arc-swap = { workspace = true } chrono = { workspace = true, features = ["alloc", "std"] } -derive_builder = { workspace = true, default-features = false, features = ["default"] } +derive_builder = { workspace = true, features = [] } ipnet = { workspace = true } -linkme = { workspace = true } multi_index_map = { workspace = true, features = ["serde"] } ordermap = { workspace = true, features = ["std"] } thiserror = { workspace = true } tracing = { workspace = true, features = ["attributes"] } +[target.'cfg(unix)'.dependencies] +linkme = { workspace = true } +tracectl = { workspace = true } + [dev-dependencies] # internal pipeline = { workspace = true } # should be removed w/ NAT lpm = { workspace = true, features = ["testing"] } -hardware = { workspace = true, features = ["bolero"] } +hardware = { workspace = true, features = ["bolero", "sysfs"] } k8s-intf = { workspace = true, features = ["bolero"] } # external -bolero = { workspace = true, default-features = false, features = ["alloc"] } +bolero = { workspace = true, features = ["alloc"] } caps = { workspace = true } ipnet = { workspace = true } pretty_assertions = { workspace = true, features = ["std"] } diff --git a/config/src/converters/k8s/config/tracecfg.rs b/config/src/converters/k8s/config/tracecfg.rs index 19f893eb2..6f000ec00 100644 --- a/config/src/converters/k8s/config/tracecfg.rs +++ b/config/src/converters/k8s/config/tracecfg.rs @@ -1,7 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Open Network Fabric Authors +#[cfg(unix)] use tracectl::LevelFilter; +#[cfg(not(unix))] +use tracing::metadata::LevelFilter; use k8s_intf::gateway_agent_crd::GatewayAgentGatewayLogs; diff --git a/config/src/errors.rs b/config/src/errors.rs index f671b61a4..ef5a4221f 100644 --- a/config/src/errors.rs +++ b/config/src/errors.rs @@ -95,8 +95,12 @@ pub enum ConfigError { Invalid(String), // tracing + #[cfg(unix)] #[error("Failed to set tracing configuration: {0}")] Tracing(#[from] tracectl::TraceCtlError), + #[cfg(not(unix))] + #[error("Failed to set tracing configuration: {0}")] + Tracing(String), // Community mappings #[error("Could not assign BGP community to VPC peering {0}")] diff --git a/config/src/internal/device/tracecfg.rs b/config/src/internal/device/tracecfg.rs index 60108e375..d201ec33c 100644 --- a/config/src/internal/device/tracecfg.rs +++ b/config/src/internal/device/tracecfg.rs @@ -7,9 +7,17 @@ use crate::ConfigResult; use ordermap::OrderMap; +use tracing::debug; + +#[cfg(unix)] use tracectl::DEFAULT_DEFAULT_LOGLEVEL; +#[cfg(unix)] use tracectl::{LevelFilter, get_trace_ctl}; -use tracing::debug; + +#[cfg(not(unix))] +use tracing::metadata::LevelFilter; +#[cfg(not(unix))] +const DEFAULT_DEFAULT_LOGLEVEL: LevelFilter = LevelFilter::INFO; #[derive(Clone, Debug)] pub struct TracingConfig { @@ -40,9 +48,21 @@ impl TracingConfig { /// # Errors /// /// Returns an error if any configured trace tag is unknown. + #[cfg(unix)] pub fn validate(&self) -> ConfigResult { debug!("Validating tracing configuration.."); let tags: Vec<&str> = self.tags.keys().map(String::as_str).collect(); Ok(get_trace_ctl().check_tags(&tags)?) } -} + + /// Validate the tracing configuration (no-op on non-unix platforms). + /// + /// # Errors + /// + /// Always returns `Ok(())` on non-unix platforms. + #[cfg(not(unix))] + pub fn validate(&self) -> ConfigResult { + debug!("Validating tracing configuration (no-op on this platform).."); + Ok(()) + } +} \ No newline at end of file diff --git a/config/src/lib.rs b/config/src/lib.rs index 57115bd98..833755edf 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -32,5 +32,7 @@ pub use gwconfig::{GwConfig, GwConfigMeta}; pub use internal::InternalConfig; pub use internal::device::DeviceConfig; +#[cfg(unix)] use tracectl::trace_target; +#[cfg(unix)] trace_target!("mgmt", LevelFilter::DEBUG, &["management"]); From ede2cee970a2ea6f061dfcb572b2005b60628774 Mon Sep 17 00:00:00 2001 From: Daniel Noland Date: Mon, 23 Mar 2026 15:31:42 -0600 Subject: [PATCH 12/13] build: wire up feature flags for wasm32 validator build Workspace Cargo.toml: - Set `default-features = false` for hardware and `k8s-intf` workspace deps so that downstream crates opt-in to platform-specific features - Remove `rt-multi-thread` from tokio workspace default features (now requested explicitly by crates that need it) - Remove codec from tokio-util workspace default features (now requested explicitly by crates that need it) Downstream crate adjustments: - validator: use `k8s-intf`; with `default-features = false` (only needs CRD types, not the full K8s client) - k8s-less: explicitly request `k8s-intf` 'client' feature - mgmt: explicitly request `k8s-intf` 'client' feature - args: explicitly request hardware 'sysfs' feature - init: explicitly request hardware 'sysfs' feature - routing: explicitly request tokio 'rt-multi-thread' feature and tokio-util 'codec' feature With these changes, the validator can be checked against wasm32-wasip1: cargo check --target wasm32-wasip1 -p dataplane-validator --- Cargo.lock | 30 +++++++++++++------------- Cargo.toml | 10 ++++----- args/Cargo.toml | 4 ++-- config/src/internal/device/tracecfg.rs | 2 +- init/Cargo.toml | 2 +- k8s-less/Cargo.toml | 2 +- mgmt/Cargo.toml | 2 +- routing/Cargo.toml | 2 +- validator/Cargo.toml | 2 +- 9 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d58aad56d..ad11f06cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -126,7 +126,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -137,7 +137,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -870,7 +870,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2084,7 +2084,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc 0.2.183", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3890,7 +3890,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4904,7 +4904,7 @@ dependencies = [ "errno", "libc 0.2.183", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5419,7 +5419,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc 0.2.183", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5526,9 +5526,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "symbolic-common" -version = "12.17.2" +version = "12.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "751a2823d606b5d0a7616499e4130a516ebd01a44f39811be2b9600936509c23" +checksum = "52ca086c1eb5c7ee74b151ba83c6487d5d33f8c08ad991b86f3f58f6629e68d5" dependencies = [ "debugid", "memmap2 0.9.10", @@ -5538,9 +5538,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.17.2" +version = "12.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b237cfbe320601dd24b4ac817a5b68bb28f5508e33f08d42be0682cadc8ac9" +checksum = "baa911a28a62823aaf2cc2e074212492a3ee69d0d926cc8f5b12b4a108ff5c0c" dependencies = [ "cpp_demangle", "rustc-demangle", @@ -5605,7 +5605,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5621,12 +5621,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" +checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7b82acb6d..f694115f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,11 +61,11 @@ dplane-rpc = { git = "https://github.com/githedgehog/dplane-rpc.git", rev = "e8f errno = { path = "./errno", package = "dataplane-errno", features = [] } flow-entry = { path = "./flow-entry", package = "dataplane-flow-entry", features = [] } flow-filter = { path = "./flow-filter", package = "dataplane-flow-filter", features = [] } -hardware = { path = "./hardware", package = "dataplane-hardware", features = [] } +hardware = { path = "./hardware", package = "dataplane-hardware", default-features = false, features = [] } id = { path = "./id", package = "dataplane-id", features = [] } init = { path = "./init", package = "dataplane-init", features = [] } interface-manager = { path = "./interface-manager", package = "dataplane-interface-manager", features = [] } -k8s-intf = { path = "./k8s-intf", package = "dataplane-k8s-intf", features = [] } +k8s-intf = { path = "./k8s-intf", package = "dataplane-k8s-intf", default-features = false, features = [] } k8s-less = { path = "./k8s-less", package = "dataplane-k8s-less", features = [] } left-right-tlcache = { path = "./left-right-tlcache", package = "dataplane-left-right-tlcache", features = [] } lpm = { path = "./lpm", package = "dataplane-lpm", features = [] } @@ -141,7 +141,7 @@ multi_index_map = { version = "0.15.1", default-features = false, features = [] n-vm = { git = "https://github.com/githedgehog/testn.git", tag = "v0.0.9", default-features = false, features = [], package = "n-vm" } netdev = { version = "0.41.0", default-features = false, features = [] } nix = { version = "0.31.2", default-features = false, features = [] } -netgauze-bgp-pkt = { version = "0.10.0", features = [] } +netgauze-bgp-pkt = { version = "0.10.0", features = [] } netgauze-bmp-pkt = { version = "0.10.0", features = ["codec"] } num-derive = { version = "0.4.2", default-features = false, features = [] } num-traits = { version = "0.2.19", default-features = false, features = [] } @@ -178,9 +178,9 @@ strum_macros = { version = "0.28.0", default-features = false, features = [] } syn = { version = "2.0.117", default-features = false, features = [] } thiserror = { version = "2.0.18", default-features = false, features = [] } thread_local = { version = "1.1.9", default-features = false, features = [] } -tokio = { version = "1.50.0", default-features = false, features = ["rt-multi-thread"] } +tokio = { version = "1.50.0", default-features = false, features = [] } tracing = { version = "0.1.44", default-features = false, features = [] } -tokio-util = { version = "0.7.18", default-features = false, features = ["codec"] } +tokio-util = { version = "0.7.18", default-features = false, features = [] } tonic = { version = "0.14.5", default-features = false, features = [] } tracing-error = { version = "0.2.1", default-features = false, features = [] } tracing-subscriber = { version = "0.3.23", default-features = false, features = [] } diff --git a/args/Cargo.toml b/args/Cargo.toml index ba9226a76..2bfbdbbbd 100644 --- a/args/Cargo.toml +++ b/args/Cargo.toml @@ -7,7 +7,7 @@ version.workspace = true [dependencies] # internal -hardware = { workspace = true, features = ["serde"] } +hardware = { workspace = true, features = ["serde", "sysfs"] } id = { workspace = true, features = [] } net = { workspace = true, features = [] } @@ -27,7 +27,7 @@ uuid = { workspace = true, features = [] } [dev-dependencies] # internal -hardware = { workspace = true, features = ["serde"] } +hardware = { workspace = true, features = ["serde", "sysfs"] } net = { workspace = true, features = ["test_buffer"] } # external serde_yaml_ng = { workspace = true, features = [] } diff --git a/config/src/internal/device/tracecfg.rs b/config/src/internal/device/tracecfg.rs index d201ec33c..f38806bd3 100644 --- a/config/src/internal/device/tracecfg.rs +++ b/config/src/internal/device/tracecfg.rs @@ -65,4 +65,4 @@ impl TracingConfig { debug!("Validating tracing configuration (no-op on this platform).."); Ok(()) } -} \ No newline at end of file +} diff --git a/init/Cargo.toml b/init/Cargo.toml index cfea1672a..cf0e0ea9c 100644 --- a/init/Cargo.toml +++ b/init/Cargo.toml @@ -11,7 +11,7 @@ sysroot = ["dep:dpdk-sysroot-helper"] [dependencies] # internal -hardware = { workspace = true, features = ["serde", "scan"] } +hardware = { workspace = true, features = ["serde", "scan", "sysfs"] } id = { workspace = true } sysfs = { workspace = true } diff --git a/k8s-less/Cargo.toml b/k8s-less/Cargo.toml index 71250994c..c7c38407e 100644 --- a/k8s-less/Cargo.toml +++ b/k8s-less/Cargo.toml @@ -8,7 +8,7 @@ repository.workspace = true [dependencies] inotify = { workspace = true, features = ["stream"] } -k8s-intf = { workspace = true } +k8s-intf = { workspace = true, features = ["client"] } tokio = { workspace = true, features = ["macros", "rt", "fs"] } tracectl = { workspace = true } tracing = { workspace = true } diff --git a/mgmt/Cargo.toml b/mgmt/Cargo.toml index aed33e2e5..5eccb98ed 100644 --- a/mgmt/Cargo.toml +++ b/mgmt/Cargo.toml @@ -23,7 +23,7 @@ concurrency = { workspace = true } flow-filter = { workspace = true } id = { workspace = true } interface-manager = { workspace = true } -k8s-intf = { workspace = true } +k8s-intf = { workspace = true, features = ["client"] } k8s-less = { workspace = true } lpm = { workspace = true } nat = { workspace = true } diff --git a/routing/Cargo.toml b/routing/Cargo.toml index 508e1f89f..b5ee666bd 100644 --- a/routing/Cargo.toml +++ b/routing/Cargo.toml @@ -39,7 +39,7 @@ netgauze-bmp-pkt = { workspace = true } serde = { workspace = true, features = ["derive"] } strum = { workspace = true } thiserror = { workspace = true } -tokio = { workspace = true, features = ["fs", "io-util", "sync", "rt", "net", "macros"] } +tokio = { workspace = true, features = ["fs", "io-util", "sync", "rt", "net", "macros", "rt-multi-thread"] } tokio-util = { workspace = true, features = ["codec"] } tracing = { workspace = true } diff --git a/validator/Cargo.toml b/validator/Cargo.toml index 7da65435f..b1c1c662d 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -11,7 +11,7 @@ path = "src/main.rs" [dependencies] config = { workspace = true } -k8s-intf = { workspace = true } +k8s-intf = { workspace = true, default-features = false } serde = { workspace = true } serde_yaml_ng = { workspace = true } From b9326ca972d5acb196e7b8dc056cf055b1dcd055 Mon Sep 17 00:00:00 2001 From: Daniel Noland Date: Mon, 23 Mar 2026 15:42:26 -0600 Subject: [PATCH 13/13] Update lib.rs --- net/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/net/src/lib.rs b/net/src/lib.rs index 0c8ee416e..13e89f75b 100644 --- a/net/src/lib.rs +++ b/net/src/lib.rs @@ -12,6 +12,7 @@ clippy::expect_used, clippy::panic )] +#![cfg_attr(not(unix), allow(unused))] // for wasm32 builds #![allow(clippy::should_panic_without_expect)] // we panic in contract checks with simple unwrap() pub mod addr_parse_error;