Skip to content
Draft

clock #1756

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
819cb7d
feat(clock): Read the clock through a facade, and lint for it
daniel-noland Aug 18, 2026
242ceea
test(nat): Test flow expiry on a clock the test drives
daniel-noland Aug 18, 2026
a425bf9
test(nat): Drive port forwarding with configuration-relative packets
daniel-noland Aug 18, 2026
e584d5b
test(net): Test the flow expiry state machine as an algebra
daniel-noland Aug 18, 2026
47b74ec
fix(masquerade): Keep both halves of a flow pair alive
daniel-noland Aug 18, 2026
e6ab131
test(stats): Test the exponentially weighted moving average
daniel-noland Aug 18, 2026
fed69e3
test(stats): Test the per-vpc statistics store
daniel-noland Aug 18, 2026
c8a3d71
test(stats): Test the time-slice apportioning in the collector
daniel-noland Aug 18, 2026
b7b78d8
test(routing): Test the stale window on a clock the test drives
daniel-noland Aug 18, 2026
8a46ab0
test(routing): Drive the router IO loop through its own sockets
daniel-noland Aug 18, 2026
0dc1bf5
test(routing): Stand in for frr-agent and test the frrmi lifecycle
daniel-noland Aug 18, 2026
d427257
test(routing): Follow a route from the socket to the fib
daniel-noland Aug 18, 2026
b255001
test(net): Close every mutant flow_info's properties were missing
daniel-noland Aug 18, 2026
3a51f8b
test(masquerade): Pin the flow state machine, exhaustively
daniel-noland Aug 19, 2026
18f522d
fix(nat): Keep both halves of a port-forwarded pair alive
daniel-noland Aug 27, 2026
0113a84
style(routing): Take rustfmt's answer, and route the last Instant import
daniel-noland Aug 27, 2026
f99739f
fix(nat): Let the port-forwarding properties be selected
daniel-noland Aug 27, 2026
9d6177d
fix(net): Make a flow's deadline refresh one atomic operation
daniel-noland Aug 27, 2026
143a550
test(nat): Drop the absolute floor here too
daniel-noland Aug 27, 2026
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
13 changes: 13 additions & 0 deletions .cargo/mutants.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

exclude_re = [
"<impl Display for",
"<impl Debug for",
"<impl std::fmt::Display for",
"<impl std::fmt::Debug for",

"contract::",
]

exclude_globs = [
"sysfs/**",
]
25 changes: 25 additions & 0 deletions .semgrep/rules/no-std-time-direct.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
rules:
- id: rust-no-direct-clock-read
languages: [rust]
severity: ERROR
message: |
Read the clock via `clock::now()` (or `clock::system_now()` for wall
time), not `Instant::now()` / `SystemTime::now()`. The workspace's
`clock` facade reads `std` in production and tokio's pausable clock
under the `virtual` feature; reading `std` directly pins the deadline
to the wall clock while the timer waiting on it follows tokio's, so a
test that advances time sees the two diverge.

`Duration` needs no facade -- it is a plain value with no clock in it,
and `clock` re-exports it only for convenience.
paths:
exclude:
- .codeql/tests/
- clock/src/
- concurrency/tests/
pattern-either:
- pattern: Instant::now()
- pattern: std::time::Instant::now()
- pattern: SystemTime::now()
- pattern: std::time::SystemTime::now()
- pattern: tokio::time::Instant::now()
19 changes: 19 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"acl-filter",
"args",
"cli",
"clock",
"common",
"concurrency",
"concurrency-macros",
Expand Down Expand Up @@ -71,6 +72,7 @@ acl-filter = { path = "./acl-filter", package = "dataplane-acl-filter", features
args = { path = "./args", package = "dataplane-args", features = [] }
cli = { path = "./cli", package = "dataplane-cli", features = [] }
common = { path = "./common", package = "dataplane-common", features = [] }
clock = { path = "./clock", package = "dataplane-clock", features = [] }
concurrency = { path = "./concurrency", package = "dataplane-concurrency", features = [] }
concurrency-macros = { path = "./concurrency-macros", package = "dataplane-concurrency-macros", features = [] }
config = { path = "./config", package = "dataplane-config", features = [] }
Expand Down
2 changes: 2 additions & 0 deletions acl-filter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ version.workspace = true

[dependencies]
acl = { workspace = true }
clock = { workspace = true }
common = { workspace = true }
concurrency = { workspace = true }
config = { workspace = true }
Expand All @@ -22,6 +23,7 @@ tracectl = { workspace = true }
tracing = { workspace = true }

[dev-dependencies]
clock = { workspace = true, features = ["virtual"] }
# The reference (linear-scan) ACL backend is the differential-test oracle and drives the fast,
# EAL-free semantic suite. It is `cfg(test)`-gated in the source, so it is never part of a
# production build; this dev-dep just makes `acl::reference` available to test builds.
Expand Down
4 changes: 2 additions & 2 deletions acl-filter/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use concurrency::sync::Arc;
use pipeline::NetworkFunction;

use std::net::{Ipv4Addr, Ipv6Addr};
use std::time::{Duration, Instant};
use std::time::Duration;

// VNIs and IP ranges used by the standard two-VPC peering (vpc1 <-> vpc2). The manifest names
// ("vpc1"/"vpc2") double as the ACL rule `from`/`to` endpoints.
Expand Down Expand Up @@ -670,7 +670,7 @@ fn ipv6_allow_and_default_deny() {
// reply's weak `related` reference can still be upgraded.
fn attach_related_flow(reply: &mut Packet<TestBuffer>, fwd_key: FlowKey) -> Arc<FlowInfo> {
let reply_key = FlowKey::try_from(&*reply).unwrap();
let expiry = Instant::now() + Duration::from_secs(60);
let expiry = clock::now() + Duration::from_secs(60);
let (fwd_flow, reply_flow) = FlowInfo::related_pair(
expiry,
fwd_key,
Expand Down
2 changes: 2 additions & 0 deletions acl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ reference = []

[dependencies]
arrayvec = { workspace = true, default-features = true }
clock = { workspace = true }
concurrency = { workspace = true, features = [] }
dpdk = { workspace = true, optional = true }
lookup = { workspace = true, features = [] }
Expand All @@ -20,6 +21,7 @@ net = { workspace = true, features = [] }
thiserror = { workspace = true }

[dev-dependencies]
clock = { workspace = true, features = ["virtual"] }
# Enable the "reference" backend for this crate's own tests and benches. It is a non-default feature
# (production links only the rte_acl backend), but the integration tests and benches
# differential-test against it, so make it available whenever test/bench targets are built.
Expand Down
3 changes: 1 addition & 2 deletions acl/src/dpdk/dyn_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,6 @@ mod tests {
use lookup::Lookup;
use match_action::{Erased, ExactSpec, MaskSpec, MatchKey, PrefixSpec, RangeSpec};
use std::hint::black_box;
use std::time::Instant;

#[derive(MatchKey, Debug, Clone, Copy)]
struct FiveTuple {
Expand Down Expand Up @@ -969,7 +968,7 @@ mod tests {
let rules = make(n);
let max = NonZero::new(u32::try_from(n).unwrap()).unwrap();
let rss_before = rss_kb();
let t = Instant::now();
let t = clock::now();
let res: Result<DpdkAclLookup<RemoteKey<Ipv4Addr>, u32>, _> =
install_table(&unique_name("cap"), max, rules);
let dt = t.elapsed().as_secs_f64() * 1e3;
Expand Down
13 changes: 13 additions & 0 deletions clock/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "dataplane-clock"
edition.workspace = true
license.workspace = true
publish.workspace = true
version.workspace = true

[features]
default = []
virtual = ["dep:tokio"]

[dependencies]
tokio = { workspace = true, optional = true, features = ["test-util", "time"] }
48 changes: 48 additions & 0 deletions clock/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Open Network Fabric Authors

#![deny(clippy::all, clippy::pedantic)]
#![deny(rustdoc::all)]
#![deny(unsafe_code)]

pub use std::time::{Duration, Instant, SystemTime, SystemTimeError, TryFromFloatSecsError};

#[must_use]
pub fn now() -> Instant {
#[cfg(feature = "virtual")]
{
tokio::time::Instant::now().into_std()
}
#[cfg(not(feature = "virtual"))]
{
Instant::now()
}
}

#[must_use]
pub fn system_now() -> SystemTime {
SystemTime::now()
}

#[cfg(test)]
mod tests {
use super::{Duration, now, system_now};

#[test]
fn now_is_monotonic() {
let first = now();
let second = now();
assert!(second >= first, "the monotonic clock went backwards");
}

#[test]
fn now_works_with_no_runtime() {
let _ = now();
let _ = system_now();
}

#[test]
fn durations_are_plain_values() {
assert_eq!(Duration::from_secs(1).as_millis(), 1000);
}
}
2 changes: 2 additions & 0 deletions config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ bolero = ["dep:bolero", "lpm/bolero"]

[dependencies]
# internal
clock = { workspace = true }
common = { workspace = true }
concurrency = { workspace = true }
k8s-intf = { workspace = true }
Expand All @@ -33,6 +34,7 @@ linkme = { workspace = true }
tracectl = { workspace = true }

[dev-dependencies]
clock = { workspace = true, features = ["virtual"] }
# internal
pipeline = { workspace = true } # should be removed w/ NAT
lpm = { workspace = true, features = ["bolero", "testing"] }
Expand Down
Loading
Loading