Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ members = [
"dpdk-sysroot-helper",
"dpdk-test-macros",
"errno",
"fixed-size",
"flow-entry",
"flow-filter",
"hardware",
Expand All @@ -23,6 +24,7 @@ members = [
"k8s-less",
"left-right-tlcache",
"lifecycle",
"lookup",
"lpm",
"mgmt",
"nat",
Expand Down Expand Up @@ -72,6 +74,7 @@ dpdk-sysroot-helper = { path = "./dpdk-sysroot-helper", package = "dataplane-dpd
dpdk-test-macros = { path = "./dpdk-test-macros", package = "dataplane-dpdk-test-macros", features = [] }
dplane-rpc = { git = "https://github.com/githedgehog/dplane-rpc.git", branch = "pr/daniel-noland/bumps", features = [] }
errno = { path = "./errno", package = "dataplane-errno", features = [] }
fixed-size = { path = "./fixed-size", package = "dataplane-fixed-size", 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 = [] }
Expand All @@ -82,6 +85,7 @@ k8s-intf = { path = "./k8s-intf", package = "dataplane-k8s-intf", default-featur
k8s-less = { path = "./k8s-less", package = "dataplane-k8s-less", features = [] }
left-right-tlcache = { path = "./left-right-tlcache", package = "dataplane-left-right-tlcache", features = [] }
lifecycle = { path = "./lifecycle", package = "dataplane-lifecycle", features = [] }
lookup = { path = "./lookup", package = "dataplane-lookup", features = [] }
lpm = { path = "./lpm", package = "dataplane-lpm", features = [] }
mgmt = { path = "./mgmt", package = "dataplane-mgmt", features = [] }
nat = { path = "./nat", package = "dataplane-nat", features = [] }
Expand Down Expand Up @@ -283,6 +287,11 @@ package = "dataplane-dpdk-sys"
miri = false # hopeless + pointless
wasm = false # hopeless + pointless

[workspace.metadata.package.fixed-size]
package = "dataplane-fixed-size"
miri = true
wasm = true

[workspace.metadata.package.flow-entry]
package = "dataplane-flow-entry"
miri = true
Expand Down Expand Up @@ -318,6 +327,11 @@ package = "dataplane-k8s-less"
miri = true
wasm = false # split

[workspace.metadata.package.lookup]
package = "dataplane-lookup"
miri = true
wasm = false # split (std collections)

[workspace.metadata.package.mgmt]
package = "dataplane-mgmt"
miri = false
Expand Down
8 changes: 8 additions & 0 deletions fixed-size/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "dataplane-fixed-size"
edition.workspace = true
license.workspace = true
publish.workspace = true
version.workspace = true

[dependencies]
67 changes: 67 additions & 0 deletions fixed-size/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Open Network Fabric Authors

#![no_std]
#![deny(
unsafe_code,
clippy::all,
clippy::pedantic,
clippy::unwrap_used,
clippy::expect_used,
clippy::panic
)]

use core::net::{Ipv4Addr, Ipv6Addr};
pub trait FixedSize: Copy {
const SIZE: usize;
fn write_be(&self, out: &mut [u8]);
}
Comment on lines +15 to +18

impl FixedSize for u8 {
const SIZE: usize = 1;
fn write_be(&self, out: &mut [u8]) {
out[0] = *self;
}
}

impl FixedSize for u16 {
const SIZE: usize = 2;
fn write_be(&self, out: &mut [u8]) {
out[..Self::SIZE].copy_from_slice(&self.to_be_bytes());
}
}

impl FixedSize for u32 {
const SIZE: usize = 4;
fn write_be(&self, out: &mut [u8]) {
out[..Self::SIZE].copy_from_slice(&self.to_be_bytes());
}
}

impl FixedSize for u64 {
const SIZE: usize = 8;
fn write_be(&self, out: &mut [u8]) {
out[..Self::SIZE].copy_from_slice(&self.to_be_bytes());
}
}

impl FixedSize for u128 {
const SIZE: usize = 16;
fn write_be(&self, out: &mut [u8]) {
out[..Self::SIZE].copy_from_slice(&self.to_be_bytes());
}
}

impl FixedSize for Ipv4Addr {
const SIZE: usize = 4;
fn write_be(&self, out: &mut [u8]) {
out[..Self::SIZE].copy_from_slice(&self.octets());
}
}

impl FixedSize for Ipv6Addr {
const SIZE: usize = 16;
fn write_be(&self, out: &mut [u8]) {
out[..Self::SIZE].copy_from_slice(&self.octets());
}
}
8 changes: 8 additions & 0 deletions lookup/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "dataplane-lookup"
edition.workspace = true
license.workspace = true
publish.workspace = true
version.workspace = true

[dependencies]
190 changes: 190 additions & 0 deletions lookup/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Open Network Fabric Authors

#![deny(
unsafe_code,
clippy::all,
clippy::pedantic,
clippy::unwrap_used,
clippy::expect_used,
clippy::panic
)]
#![allow(missing_docs)]

use std::collections::{BTreeMap, HashMap};
use std::hash::Hash;
pub trait Projection<T> {
fn project(self) -> T;
}
impl<K> Projection<Option<K>> for Option<K> {
fn project(self) -> Option<K> {
self
}
}
pub trait Lookup<K, A> {
fn lookup(&self, key: &K) -> Option<&A>;
fn classify<S>(&self, source: S) -> Option<&A>
where
S: Projection<K>,
{
self.lookup(&source.project())
}
fn classify_opt<S>(&self, source: S) -> Option<&A>
where
S: Projection<Option<K>>,
{
source.project().and_then(|key| self.lookup(&key))
}
}

impl<K: Ord, V> Lookup<K, V> for BTreeMap<K, V> {
fn lookup(&self, key: &K) -> Option<&V> {
BTreeMap::get(self, key)
}
}

impl<K: Eq + Hash, V, S: std::hash::BuildHasher> Lookup<K, V> for HashMap<K, V, S> {
fn lookup(&self, key: &K) -> Option<&V> {
HashMap::get(self, key)
}
}

#[cfg(test)]
mod tests {
use super::*;

struct Pkt {
src: u32,
dst: u32,
sport: u16,
dport: u16,
}

impl Projection<(u32, u32)> for &Pkt {
fn project(self) -> (u32, u32) {
(self.src, self.dst)
}
}

impl Projection<(u32, u32, u16, u16)> for &Pkt {
fn project(self) -> (u32, u32, u16, u16) {
(self.src, self.dst, self.sport, self.dport)
}
}

impl<'a> Projection<(&'a u32, &'a u32)> for &'a Pkt {
fn project(self) -> (&'a u32, &'a u32) {
(&self.src, &self.dst)
}
}
impl Projection<Option<(u32, u32)>> for &Pkt {
fn project(self) -> Option<(u32, u32)> {
(self.src != 0).then_some((self.src, self.dst))
}
}

#[derive(Debug, PartialEq, Eq)]
enum Action {
Allow,
Drop,
}

#[test]
fn classify_picks_the_two_tuple_projection_from_the_table_type() {
let mut table: BTreeMap<(u32, u32), Action> = BTreeMap::new();
table.insert((10, 20), Action::Drop);
let pkt = Pkt {
src: 10,
dst: 20,
sport: 22,
dport: 80,
};
assert_eq!(table.classify(&pkt), Some(&Action::Drop));
}

#[test]
fn classify_picks_the_four_tuple_projection_from_the_table_type() {
let mut table: BTreeMap<(u32, u32, u16, u16), Action> = BTreeMap::new();
table.insert((10, 20, 22, 80), Action::Allow);
let pkt = Pkt {
src: 10,
dst: 20,
sport: 22,
dport: 80,
};
assert_eq!(table.classify(&pkt), Some(&Action::Allow));
}

#[test]
fn borrowed_tuple_projection_threads_lifetime() {
let pkt = Pkt {
src: 10,
dst: 20,
sport: 0,
dport: 0,
};
let (src, dst): (&u32, &u32) = (&pkt).project();
assert_eq!(*src, 10);
assert_eq!(*dst, 20);
}

#[test]
fn miss_returns_none() {
let table: BTreeMap<(u32, u32), Action> = BTreeMap::new();
let pkt = Pkt {
src: 1,
dst: 2,
sport: 3,
dport: 4,
};
assert_eq!(table.classify(&pkt), None);
}

#[test]
fn classify_opt_looks_up_when_projection_yields_some() {
let mut table: BTreeMap<(u32, u32), Action> = BTreeMap::new();
table.insert((10, 20), Action::Drop);
let pkt = Pkt {
src: 10,
dst: 20,
sport: 0,
dport: 0,
};
assert_eq!(table.classify_opt(&pkt), Some(&Action::Drop));
}

#[test]
fn classify_opt_short_circuits_when_projection_yields_none() {
let mut table: BTreeMap<(u32, u32), Action> = BTreeMap::new();
table.insert((0, 20), Action::Drop);
let pkt = Pkt {
src: 0,
dst: 20,
sport: 0,
dport: 0,
};
assert_eq!(table.classify_opt(&pkt), None);
}

#[test]
fn classify_opt_accepts_a_computed_option_via_identity() {
let mut table: BTreeMap<(u32, u32), Action> = BTreeMap::new();
table.insert((10, 20), Action::Drop);
let built: Option<(u32, u32)> = Some((10, 20));
assert_eq!(table.classify_opt(built), Some(&Action::Drop));
assert_eq!(table.classify_opt(None::<(u32, u32)>), None);
}

#[test]
fn hashmap_backend_works_the_same_way() {
let mut table: HashMap<(u32, u32), Action> = HashMap::new();
table.insert((10, 20), Action::Drop);
let pkt = Pkt {
src: 10,
dst: 20,
sport: 0,
dport: 0,
};
assert_eq!(table.classify(&pkt), Some(&Action::Drop));
}
}
1 change: 1 addition & 0 deletions net/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ concurrency = { workspace = true }
derive_builder = { workspace = true, features = ["alloc"] }
downcast-rs = { workspace = true, features = ["sync"] }
etherparse = { workspace = true, features = ["std"] }
fixed-size = { workspace = true, features = [] }
id = { workspace = true }
multi_index_map = { workspace = true, default-features = false, features = ["serde"] }
rapidhash = { workspace = true }
Expand Down
Loading
Loading