Skip to content

Commit f6babf0

Browse files
committed
chore(msg-sim): address review
1 parent 0d3a3de commit f6babf0

File tree

9 files changed

+80
-40
lines changed

9 files changed

+80
-40
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,12 @@ jobs:
5353
with:
5454
cache-on-failure: true
5555

56+
# Install nextest
57+
- name: Install cargo-nextest
58+
uses: taiki-e/install-action@nextest
59+
5660
- name: Run msg-sim tests (requires root for network namespaces)
57-
run: sudo env "PATH=$PATH" "HOME=$HOME" cargo test run -p msg-sim --all-features --no-fail-fast -- --test-threads=1
61+
run: sudo env "PATH=$PATH" "HOME=$HOME" cargo nextest run -p msg-sim --all-features --no-fail-fast --test-threads=1
5862

5963
cargo-lint:
6064
runs-on: ubuntu-latest

Cargo.lock

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

msg-sim/examples/sim_multi_region.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@
1919
//! sudo HOME=$HOME RUST_LOG=info $(which cargo) run --example multi_region -p msg-sim
2020
//! ```
2121
22-
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
23-
use std::time::{Duration, Instant};
24-
25-
use msg_sim::ip::Subnet;
26-
use msg_sim::network::{Link, Network, PeerIdExt};
27-
use msg_sim::tc::impairment::LinkImpairment;
22+
use std::{
23+
net::{IpAddr, Ipv4Addr, SocketAddr},
24+
time::{Duration, Instant},
25+
};
26+
27+
use msg_sim::{
28+
ip::Subnet,
29+
network::{Link, Network, PeerIdExt},
30+
tc::impairment::LinkImpairment,
31+
};
2832
use tracing_subscriber::EnvFilter;
2933

3034
// Peer IDs (assigned sequentially by add_peer)

msg-sim/src/tc/drr.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
//! DRR (Deficit Round Robin) qdisc and class support.
2-
//!
3-
//! DRR is our root qdisc, chosen because it allows an unlimited number of classes
4-
//! to be created dynamically with minimal overhead. Each class can have its own
5-
//! qdisc chain (TBF → netem) for per-destination impairments.
62
73
use rtnetlink::packet_core::{
84
NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REPLACE, NLM_F_REQUEST, NetlinkMessage,
@@ -28,7 +24,7 @@ const TCA_DRR_QUANTUM: u16 = 1;
2824
/// The quantum must be at least as large as the maximum packet size (MTU) to ensure packets
2925
/// can always be dequeued. 1MB is large enough to handle any reasonable packet while still
3026
/// being well within safe bounds.
31-
pub const DRR_DEFAULT_QUANTUM: u32 = u32::MAX; // 1 MB
27+
pub const DRR_DEFAULT_QUANTUM: u32 = u32::MAX; // 4GiB
3228

3329
/// Builder for creating a DRR (Deficit Round Robin) root qdisc.
3430
///
@@ -88,13 +84,13 @@ impl QdiscDrrRequest {
8884
/// # Example
8985
///
9086
/// ```
91-
/// use msg_sim::tc::handle::{CLASS_MINOR_OFFSET, QdiscRequestInner};
87+
/// use msg_sim::tc::handle::{ID_OFFSET, QdiscRequestInner};
9288
/// use msg_sim::tc::drr::DrrClassRequest;
9389
/// use rtnetlink::packet_route::tc::TcHandle;
9490
///
9591
/// let if_index = 1; // Network interface index
9692
/// // Create class for traffic to peer 2
97-
/// let class_minor = CLASS_MINOR_OFFSET + 2; // 12
93+
/// let class_minor = ID_OFFSET + 2; // 12
9894
/// let request = DrrClassRequest::new(
9995
/// QdiscRequestInner::new(if_index)
10096
/// .with_parent(TcHandle::from(0x0001_0000)) // Parent is DRR root (1:0)

msg-sim/src/tc/filter.rs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ const ETH_P_IP: u16 = nix::libc::ETH_P_IP as u16;
2121
/// EtherType for IPv6 packets (0x86DD).
2222
const ETH_P_IPV6: u16 = nix::libc::ETH_P_IPV6 as u16;
2323

24+
/// EtherType for matching all protocols supported by Ethernet.
25+
const ETH_P_ALL: u16 = nix::libc::ETH_P_ALL as u16;
26+
2427
/// Compute an IPv4 netmask from a prefix length.
2528
fn ipv4_mask(prefix_len: u8) -> Ipv4Addr {
2629
if prefix_len == 0 {
@@ -82,9 +85,9 @@ pub struct FlowerFilterRequest {
8285
}
8386

8487
impl FlowerFilterRequest {
85-
/// Create a new flower filter for the given destination IP.
88+
/// Create a new flower filter for the given destination IP
8689
///
87-
/// By default, uses /32 (exact match) for IPv4 or /128 for IPv6.
90+
/// By default, uses /32 (exact match) for IPv4 or /128 for IPv6 and root class id.
8891
pub fn new(inner: QdiscRequestInner, destination: IpAddr) -> Self {
8992
let default_mask = match destination {
9093
IpAddr::V4(_) => 32,
@@ -95,7 +98,7 @@ impl FlowerFilterRequest {
9598
destination,
9699
mask: default_mask,
97100
// Default class ID will be set by caller
98-
class_id: 0,
101+
class_id: u32::MAX, // Root class id
99102
}
100103
}
101104

@@ -213,6 +216,14 @@ struct TcU32Sel {
213216
// Followed by nkeys * tc_u32_key structures
214217
}
215218

219+
impl TcU32Sel {
220+
/// Returns an instance of [`Self`] with all fields set to zero.
221+
#[allow(dead_code)]
222+
fn zero() -> Self {
223+
Self { flags: 0, offmask: 0, offshift: 0, nkeys: 0, off: 0, offoff: 0, hoff: 0, hmask: 0 }
224+
}
225+
}
226+
216227
/// The kernel's `tc_u32_key` structure for u32 matching.
217228
#[derive(Debug, Clone, Copy, Default)]
218229
struct TcU32Key {
@@ -226,6 +237,13 @@ struct TcU32Key {
226237
offmask: i32,
227238
}
228239

240+
impl TcU32Key {
241+
/// Returns an instance of [`Self`] with all fields set to zero.
242+
fn zero() -> Self {
243+
Self { mask: 0, val: 0, off: 0, offmask: 0 }
244+
}
245+
}
246+
229247
/// Builder for creating a u32 catch-all filter.
230248
///
231249
/// The u32 filter with `match u32 0 0` matches all packets. This is used as a catch-all
@@ -241,8 +259,10 @@ struct TcU32Key {
241259
/// # Why u32 Instead of matchall
242260
///
243261
/// The `matchall` filter requires the `cls_matchall` kernel module which may not be
244-
/// available on all systems. The `u32` filter is more universally supported and can
245-
/// achieve the same effect with `match u32 0 0` (which always matches).
262+
/// available on all systems (e.g., minimal/embedded kernels, older distros, or containers
263+
/// without the module loaded). The `u32` filter is part of `cls_u32`, which is almost
264+
/// always built-in, and can achieve the same effect with `match u32 0 0` (mask=0 matches
265+
/// everything).
246266
///
247267
/// # Priority System
248268
///
@@ -280,8 +300,8 @@ impl U32CatchallFilterRequest {
280300
pub fn new(inner: QdiscRequestInner) -> Self {
281301
Self {
282302
inner,
283-
class_id: 0,
284-
priority: 65535, // Lowest priority = checked last
303+
class_id: u32::MAX, // Root class id.
304+
priority: 65535, // Lowest priority = checked last
285305
}
286306
}
287307

@@ -304,10 +324,7 @@ impl U32CatchallFilterRequest {
304324
let mut tc_msg = TcMessage::with_index(self.inner.interface_index);
305325
tc_msg.header.parent = self.inner.parent;
306326
tc_msg.header.handle = TcHandle::from(0u32);
307-
// The info field contains the priority (in upper 16 bits) and protocol (in lower 16 bits)
308-
// ETH_P_ALL (0x0003) matches all protocols
309-
let eth_p_all: u16 = 0x0003;
310-
tc_msg.header.info = ((self.priority as u32) << 16) | (eth_p_all.to_be() as u32);
327+
tc_msg.header.info = ((self.priority as u32) << 16) | (ETH_P_ALL.to_be() as u32);
311328

312329
tc_msg.attributes.push(TcAttribute::Kind("u32".to_string()));
313330

@@ -320,7 +337,7 @@ impl U32CatchallFilterRequest {
320337

321338
// Mask of 0 means "don't care".
322339
// Value doesn't matter when mask is 0
323-
let key = TcU32Key::default();
340+
let key = TcU32Key::zero();
324341

325342
// Serialize selector + key
326343
let mut sel_bytes = Vec::with_capacity(size_of::<TcU32Sel>() + size_of::<TcU32Key>());

msg-sim/src/tc/handle.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ use rtnetlink::packet_route::tc::TcHandle;
77

88
/// The offset added to peer IDs to compute DRR class minor numbers.
99
///
10-
/// For peer ID `N`, the class minor is `CLASS_MINOR_OFFSET + N`.
11-
/// This keeps class 1:1 reserved as the default (unimpaired) class.
12-
pub const CLASS_MINOR_OFFSET: u32 = 10;
10+
/// For peer ID `N`, the class minor/major (depending on the qdisc level) is `ID_OFFSET + N`. This
11+
/// keeps class 1:1 reserved as the default (unimpaired) class.
12+
pub const ID_OFFSET: u32 = 10;
1313

1414
/// Offset for netem qdisc major numbers (separate from TBF to avoid collisions).
1515
pub const NETEM_MAJOR_OFFSET: u32 = 20;
@@ -60,7 +60,7 @@ impl QdiscRequestInner {
6060
/// assert_eq!(drr_class_handle(2), 0x0001_000C); // 1:12
6161
/// ```
6262
pub fn drr_class_handle(dest_peer_id: usize) -> u32 {
63-
let minor = CLASS_MINOR_OFFSET + dest_peer_id as u32;
63+
let minor = ID_OFFSET + dest_peer_id as u32;
6464
(1 << 16) | minor
6565
}
6666

@@ -78,7 +78,7 @@ pub fn drr_class_handle(dest_peer_id: usize) -> u32 {
7878
/// assert_eq!(tbf_handle(2), 0x000C_0000); // 12:0
7979
/// ```
8080
pub fn tbf_handle(dest_peer_id: usize) -> u32 {
81-
let major = CLASS_MINOR_OFFSET + dest_peer_id as u32;
81+
let major = ID_OFFSET + dest_peer_id as u32;
8282
major << 16 // minor must be 0 for qdiscs
8383
}
8484

msg-sim/src/tc/impairment.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub struct LinkImpairment {
5757
///
5858
/// When this limit is reached, additional packets are dropped. Default is 1000,
5959
/// which matches netem's default. Increase for high-bandwidth, high-latency links
60-
/// to avoid artificial drops.
60+
/// to avoid unintended drops.
6161
pub netem_limit: u32,
6262

6363
/// Packet loss percentage (0.0 to 100.0).
@@ -251,10 +251,15 @@ impl LinkImpairment {
251251
if let Some(burst_kib) = self.burst_kib {
252252
burst_kib * 1024
253253
} else if let Some(bandwidth_mbit) = self.bandwidth_mbit_s {
254-
// Compute default: max(bandwidth/8 seconds, 10 MTU packets)
254+
// TBF requires a non-zero burst: it defines how many tokens can accumulate
255+
// in the bucket. With burst=0, no packets could ever be sent.
256+
//
257+
// Default: max(1/8 second of bandwidth, 10 MTU packets). This is large
258+
// enough to handle typical traffic bursts without being so large that
259+
// rate limiting becomes ineffective.
255260
let bandwidth_bytes_per_sec = (bandwidth_mbit * 1_000_000.0 / 8.0) as u32;
256261
let one_eighth_second = bandwidth_bytes_per_sec / 8;
257-
let ten_packets = MTU_ETHERNET * 10; // 10 MTU-sized packets
262+
let ten_packets = MTU_ETHERNET * 10;
258263
std::cmp::max(one_eighth_second, ten_packets)
259264
} else {
260265
// No bandwidth limit, burst is irrelevant
@@ -272,8 +277,7 @@ impl LinkImpairment {
272277
let queue_latency_ms = self.tbf_queue_latency_ms.unwrap_or(200);
273278
let burst_bytes = self.effective_burst_bytes();
274279

275-
if let Some(bandwidth_mbit) = self.bandwidth_mbit_s {
276-
let rate_bytes_per_sec = (bandwidth_mbit * 1_000_000.0 / 8.0) as u32;
280+
if let Some(rate_bytes_per_sec) = self.bandwidth_bytes_per_sec() {
277281
let rate_bytes_per_ms = rate_bytes_per_sec / 1000;
278282
rate_bytes_per_ms * queue_latency_ms + burst_bytes
279283
} else {
@@ -285,7 +289,7 @@ impl LinkImpairment {
285289
/// Compute the bandwidth rate in bytes per second.
286290
///
287291
/// Returns `None` if no bandwidth limit is configured.
288-
pub fn bandwidth_bytes_per_sec(&self) -> Option<u64> {
289-
self.bandwidth_mbit_s.map(|mbit| (mbit * 1_000_000.0 / 8.0) as u64)
292+
pub fn bandwidth_bytes_per_sec(&self) -> Option<u32> {
293+
self.bandwidth_mbit_s.map(|mbit| (mbit * 1_000_000.0 / 8.0) as u32)
290294
}
291295
}

msg-sim/src/tc/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@
5858
//!
5959
//! | Component | Handle | Example (peer_id=2) |
6060
//! |------------------|-----------------|---------------------|
61-
//! | DRR root | `1:0` | `1:0` |
62-
//! | Default class | `1:1` | `1:1` |
61+
//! | DRR root | `1:0` | `N/A` |
62+
//! | Default class | `1:1` | `N/A` |
6363
//! | Per-dest class | `1:(10+id)` | `1:12` |
6464
//! | TBF qdisc | `(10+id):0` | `12:0` |
6565
//! | Netem qdisc | `(20+id):0` | `22:0` |

msg-sim/src/tc/nla.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use rtnetlink::packet_core::{DefaultNla, NLA_HEADER_SIZE};
2121
/// └─────────────────────────────────────────┘
2222
/// ```
2323
///
24+
/// Reference: <linux/netlink.h>
25+
///
2426
/// # Returns
2527
///
2628
/// A byte vector containing the complete NLA (header + value + padding).

0 commit comments

Comments
 (0)