netem_trace/
lib.rs

1//! This crate provides a set of tools to generate traces for network emulation.
2//!
3//! ## Examples
4//!
5//! If you want to use the pre-defined models, please enable the `model` or `bw-model` feature.
6//!
7//! And if you want read configuration from file, `serde` feature should also be enabled.
8//! We else recommend you to enable `human` feature to make the configuration file more human-readable.
9//!
10//! An example to build model from configuration:
11//!
12//! ```
13//! # use netem_trace::model::StaticBwConfig;
14//! # use netem_trace::{Bandwidth, Duration, BwTrace};
15//! let mut static_bw = StaticBwConfig::new()
16//!     .bw(Bandwidth::from_mbps(24))
17//!     .duration(Duration::from_secs(1))
18//!     .build();
19//! assert_eq!(static_bw.next_bw(), Some((Bandwidth::from_mbps(24), Duration::from_secs(1))));
20//! assert_eq!(static_bw.next_bw(), None);
21//! ```
22//!
23//! A more common use case is to build model from a configuration file (e.g. json file):
24//!
25//! ```
26//! # use netem_trace::model::{StaticBwConfig, BwTraceConfig};
27//! # use netem_trace::{Bandwidth, Duration, BwTrace};
28//! # #[cfg(feature = "human")]
29//! # let config_file_content = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":\"12Mbps\",\"duration\":\"1s\"}},{\"StaticBwConfig\":{\"bw\":\"24Mbps\",\"duration\":\"1s\"}}],\"count\":2}}";
30//! // The content would be "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":12000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":24000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}"
31//! // if the `human` feature is not enabled.
32//! # #[cfg(not(feature = "human"))]
33//! let config_file_content = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":12000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":24000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}";
34//! let des: Box<dyn BwTraceConfig> = serde_json::from_str(config_file_content).unwrap();
35//! let mut model = des.into_model();
36//! assert_eq!(
37//!     model.next_bw(),
38//!     Some((Bandwidth::from_mbps(12), Duration::from_secs(1)))
39//! );
40//! assert_eq!(
41//!     model.next_bw(),
42//!     Some((Bandwidth::from_mbps(24), Duration::from_secs(1)))
43//! );
44//! assert_eq!(
45//!     model.next_bw(),
46//!     Some((Bandwidth::from_mbps(12), Duration::from_secs(1)))
47//! );
48//! assert_eq!(
49//!     model.next_bw(),
50//!     Some((Bandwidth::from_mbps(24), Duration::from_secs(1)))
51//! );
52//! assert_eq!(model.next_bw(), None);
53//! ```
54//!
55//! ## Make your own model
56//!
57//! Here is an simple example of how to do this. For more complicated examples, please refer to our pre-defined models.
58//!
59//! ```
60//! use netem_trace::BwTrace;
61//! use netem_trace::{Bandwidth, Duration};
62//!
63//! struct MyStaticBw {
64//!    bw: Bandwidth,
65//!    duration: Option<Duration>,
66//! }
67//!
68//! impl BwTrace for MyStaticBw {
69//!     fn next_bw(&mut self) -> Option<(Bandwidth, Duration)> {
70//!         if let Some(duration) = self.duration.take() {
71//!             if duration.is_zero() {
72//!                 None
73//!             } else {
74//!                 Some((self.bw, duration))
75//!             }
76//!         } else {
77//!             None
78//!         }
79//!     }
80//! }
81//! ```
82//!
83//! This is almost the same as how this library implements the [`model::StaticBw`] model.
84//!
85//! ## Features
86//!
87//! ### Model Features
88//!
89//! - `model`: Enable this feature if you want to use all pre-defined models.
90//!     - `bw-model`: Enable this feature if you want to use the pre-defined [`BwTrace`] models.
91//!     - `truncated-normal`: Enable this feature if you want to use truncated normal distribution in [`model::NormalizedBw`] models.
92//!
93//! ### Trace Format Features
94//!
95//! - `mahimahi`: Enable this feature if you want to load or output traces in [mahimahi](https://github.com/ravinet/mahimahi) format.
96//!
97//! ### Trace Extension Features
98//!
99//! - `trace-ext`: Enable this feature to use the series expansion and export functionality for plotting and visualization. See [`series`] module for details.
100//!
101//! ### Other Features
102//!
103//! - `serde`: Enable this features if you want some structs to be serializable/deserializable. Often used with model features.
104//! - `human`: Enable this feature if you want to use human-readable format in configuration files. Often used with model features.
105
106#[cfg(feature = "mahimahi")]
107pub mod mahimahi;
108#[cfg(feature = "mahimahi")]
109pub use mahimahi::{load_mahimahi_trace, Mahimahi, MahimahiExt};
110
111#[cfg(any(
112    feature = "bw-model",
113    feature = "delay-model",
114    feature = "loss-model",
115    feature = "duplicate-model",
116    feature = "model",
117))]
118pub mod model;
119
120#[cfg(feature = "trace-ext")]
121pub mod series;
122
123pub use bandwidth::Bandwidth;
124pub use std::time::Duration;
125
126/// The delay describes how long a packet is delayed when going through.
127pub type Delay = std::time::Duration;
128
129/// The loss_pattern describes how the packets are dropped when going through.
130///
131/// The loss_pattern is a sequence of conditional probabilities describing how packets are dropped.
132/// The probability is a f64 between 0 and 1.
133///
134/// The meaning of the loss_pattern sequence is as follows:
135///
136/// - The probability on index 0 describes how likely a packet will be dropped **if the previous packet was not lost**.
137/// - The probability on index 1 describes how likely a packet will be dropped **if the previous packet was lost**.
138/// - The probability on index 2 describes how likely a packet will be dropped **if the previous 2 packet was lost**.
139/// - ...
140///
141/// For example, if the loss_pattern is [0.1, 0.2], and packet 100 is not lost,
142/// then the probability of packet 101 being lost is 0.1.
143///
144/// If the packet 101 is lost, then the probability of packet 102 being lost is 0.2.
145/// If the packet 101 is not lost, then the probability of packet 102 being lost is still 0.1.
146pub type LossPattern = Vec<f64>;
147
148/// The duplicate_pattern describes how the packets are duplicated.
149///
150/// The duplicate_pattern is a sequence of conditional probabilities describing how packets are duplicated.
151/// The probability is a f64 between 0 and 1.
152///
153/// The meaning of the duplicate_pattern sequence is:
154///
155/// - The probability on index 0 describes how likely a packet will be duplicated
156///   **if the previous packet was transmitted normally**.
157/// - The probability on index 1 describes how likely a packet will be duplicated
158///   **if the previous packet was duplicated**.
159/// - ...
160///
161/// For example, if the duplicate_pattern is [0.8, 0.1], and packet 100 is not duplicated, then the
162/// probability of packet 101 being duplicated is 0.8.
163///
164/// If the packet 101 is duplicated, the the probability of packet 102 being duplicated is 0.1; if
165/// the packet 101 is not duplicated, then the probability of packet 102 being duplicated is still 0.8.
166///
167/// If both packet 101 and 102 were duplicated, then the probability of packet 103 being duplicated
168/// is still 0.1, and as long as the packets were duplicated, the probability of the next packet
169/// being duplicated is always the last element - in this case, 0.1.
170pub type DuplicatePattern = Vec<f64>;
171
172/// This is a trait that represents a trace of bandwidths.
173///
174/// The trace is a sequence of `(bandwidth, duration)` pairs.
175/// The bandwidth describes how many bits can be sent per second.
176/// The duration is the time that the bandwidth lasts.
177///
178/// For example, if the sequence is [(1Mbps, 1s), (2Mbps, 2s), (3Mbps, 3s)],
179/// then the bandwidth will be 1Mbps for 1s, then 2Mbps for 2s, then 3Mbps for 3s.
180///
181/// The next_bw function either returns **the next bandwidth and its duration**
182/// in the sequence, or **None** if the trace goes to end.
183pub trait BwTrace: Send {
184    fn next_bw(&mut self) -> Option<(Bandwidth, Duration)>;
185}
186
187/// This is a trait that represents a trace of delays.
188///
189/// The trace is a sequence of `(delay, duration)` pairs.
190/// The delay describes how long a packet is delayed when going through.
191/// The duration is the time that the delay lasts.
192///
193/// For example, if the sequence is [(10ms, 1s), (20ms, 2s), (30ms, 3s)],
194/// then the delay will be 10ms for 1s, then 20ms for 2s, then 30ms for 3s.
195///
196/// The next_delay function either returns **the next delay and its duration**
197/// in the sequence, or **None** if the trace goes to end.
198pub trait DelayTrace: Send {
199    fn next_delay(&mut self) -> Option<(Delay, Duration)>;
200}
201
202/// This is a trait that represents a trace of per-packet delays.
203///
204/// The trace is a sequence of `delay`.
205/// The delay describes how long the packet is delayed when going through.
206///
207/// For example, if the sequence is [10ms, 20ms, 30ms],
208/// then the delay will be 10ms for the first packet, then 20ms for second, then 30ms for third.
209///
210/// The next_delay function either returns **the next delay**
211/// in the sequence, or **None** if the trace goes to end.
212pub trait DelayPerPacketTrace: Send {
213    fn next_delay(&mut self) -> Option<Delay>;
214}
215
216/// This is a trait that represents a trace of loss patterns.
217///
218/// The trace is a sequence of `(loss_pattern, duration)` pairs.
219/// The loss_pattern describes how packets are dropped when going through.
220/// The duration is the time that the loss_pattern lasts.
221///
222/// The next_loss function either returns **the next loss_pattern and its duration**
223/// in the sequence, or **None** if the trace goes to end.
224pub trait LossTrace: Send {
225    fn next_loss(&mut self) -> Option<(LossPattern, Duration)>;
226}
227
228/// This is a trait that represents a trace of duplicate patterns.
229///
230/// The trace is a sequence of `(duplicate_pattern, duration)` pairs.
231/// The duplicate_pattern describes how packets are duplicated when going through.
232/// The duration is the time that the duplicate_pattern lasts.
233///
234/// The next_duplicate function either returns **the next duplicate_pattern and its duration** in
235/// the sequence, or **None** if the trace goes to end.
236pub trait DuplicateTrace: Send {
237    fn next_duplicate(&mut self) -> Option<(DuplicatePattern, Duration)>;
238}
239
240#[cfg(test)]
241mod test {
242    use model::TraceBwConfig;
243
244    use self::model::bw::Forever;
245
246    use super::*;
247    #[cfg(feature = "serde")]
248    use crate::model::RepeatedBwPatternConfig;
249    use crate::model::{BwTraceConfig, NormalizedBwConfig, SawtoothBwConfig, StaticBwConfig};
250
251    #[test]
252    fn test_static_bw_model() {
253        let mut static_bw = StaticBwConfig::new()
254            .bw(Bandwidth::from_mbps(24))
255            .duration(Duration::from_secs(1))
256            .build();
257        assert_eq!(
258            static_bw.next_bw(),
259            Some((Bandwidth::from_mbps(24), Duration::from_secs(1)))
260        );
261    }
262
263    #[test]
264    fn test_normalized_bw_model() {
265        let mut normal_bw = NormalizedBwConfig::new()
266            .mean(Bandwidth::from_mbps(12))
267            .std_dev(Bandwidth::from_mbps(1))
268            .duration(Duration::from_secs(1))
269            .step(Duration::from_millis(100))
270            .seed(42)
271            .build();
272        assert_eq!(
273            normal_bw.next_bw(),
274            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
275        );
276        assert_eq!(
277            normal_bw.next_bw(),
278            Some((Bandwidth::from_bps(12132938), Duration::from_millis(100)))
279        );
280        let mut normal_bw = NormalizedBwConfig::new()
281            .mean(Bandwidth::from_mbps(12))
282            .std_dev(Bandwidth::from_mbps(1))
283            .duration(Duration::from_secs(1))
284            .step(Duration::from_millis(100))
285            .seed(42)
286            .upper_bound(Bandwidth::from_kbps(12100))
287            .lower_bound(Bandwidth::from_kbps(11900))
288            .build();
289        assert_eq!(
290            normal_bw.next_bw(),
291            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
292        );
293        assert_eq!(
294            normal_bw.next_bw(),
295            Some((Bandwidth::from_bps(12100000), Duration::from_millis(100)))
296        );
297    }
298
299    #[test]
300    fn test_sawtooth_bw_model() {
301        let mut sawtooth_bw = SawtoothBwConfig::new()
302            .bottom(Bandwidth::from_mbps(12))
303            .top(Bandwidth::from_mbps(16))
304            .duration(Duration::from_secs(1))
305            .step(Duration::from_millis(100))
306            .interval(Duration::from_millis(500))
307            .duty_ratio(0.8)
308            .build();
309        assert_eq!(
310            sawtooth_bw.next_bw(),
311            Some((Bandwidth::from_mbps(12), Duration::from_millis(100)))
312        );
313        assert_eq!(
314            sawtooth_bw.next_bw(),
315            Some((Bandwidth::from_mbps(13), Duration::from_millis(100)))
316        );
317        assert_eq!(
318            sawtooth_bw.next_bw(),
319            Some((Bandwidth::from_mbps(14), Duration::from_millis(100)))
320        );
321        assert_eq!(
322            sawtooth_bw.next_bw(),
323            Some((Bandwidth::from_mbps(15), Duration::from_millis(100)))
324        );
325        assert_eq!(
326            sawtooth_bw.next_bw(),
327            Some((Bandwidth::from_mbps(16), Duration::from_millis(100)))
328        );
329        assert_eq!(
330            sawtooth_bw.next_bw(),
331            Some((Bandwidth::from_mbps(12), Duration::from_millis(100)))
332        );
333        assert_eq!(
334            sawtooth_bw.next_bw(),
335            Some((Bandwidth::from_mbps(13), Duration::from_millis(100)))
336        );
337        assert_eq!(
338            sawtooth_bw.next_bw(),
339            Some((Bandwidth::from_mbps(14), Duration::from_millis(100)))
340        );
341        assert_eq!(
342            sawtooth_bw.next_bw(),
343            Some((Bandwidth::from_mbps(15), Duration::from_millis(100)))
344        );
345        let mut sawtooth_bw = SawtoothBwConfig::new()
346            .bottom(Bandwidth::from_mbps(12))
347            .top(Bandwidth::from_mbps(16))
348            .duration(Duration::from_secs(1))
349            .step(Duration::from_millis(100))
350            .interval(Duration::from_millis(500))
351            .duty_ratio(0.8)
352            .std_dev(Bandwidth::from_mbps(5))
353            .upper_noise_bound(Bandwidth::from_mbps(1))
354            .lower_noise_bound(Bandwidth::from_kbps(500))
355            .build();
356        assert_eq!(
357            sawtooth_bw.next_bw(),
358            Some((Bandwidth::from_bps(12347139), Duration::from_millis(100)))
359        );
360        assert_eq!(
361            sawtooth_bw.next_bw(),
362            Some((Bandwidth::from_bps(13664690), Duration::from_millis(100)))
363        );
364        assert_eq!(
365            sawtooth_bw.next_bw(),
366            Some((Bandwidth::from_mbps(15), Duration::from_millis(100)))
367        );
368        assert_eq!(
369            sawtooth_bw.next_bw(),
370            Some((Bandwidth::from_bps(14500000), Duration::from_millis(100)))
371        );
372    }
373
374    #[test]
375    fn test_trace_bw() {
376        let mut trace_bw = TraceBwConfig::new()
377            .pattern(vec![
378                (
379                    Duration::from_millis(1),
380                    vec![
381                        Bandwidth::from_kbps(29123),
382                        Bandwidth::from_kbps(41242),
383                        Bandwidth::from_kbps(7395),
384                    ],
385                ),
386                (
387                    Duration::from_millis(2),
388                    vec![Bandwidth::from_mbps(1), Bandwidth::from_kbps(8542)],
389                ),
390            ])
391            .build();
392
393        assert_eq!(
394            trace_bw.next_bw(),
395            Some((Bandwidth::from_bps(29123000), Duration::from_millis(1)))
396        );
397        assert_eq!(
398            trace_bw.next_bw(),
399            Some((Bandwidth::from_bps(41242000), Duration::from_millis(1)))
400        );
401        assert_eq!(
402            trace_bw.next_bw(),
403            Some((Bandwidth::from_bps(7395000), Duration::from_millis(1)))
404        );
405        assert_eq!(
406            trace_bw.next_bw(),
407            Some((Bandwidth::from_bps(1000000), Duration::from_millis(2)))
408        );
409        assert_eq!(
410            trace_bw.next_bw(),
411            Some((Bandwidth::from_bps(8542000), Duration::from_millis(2)))
412        );
413        assert_eq!(trace_bw.next_bw(), None);
414    }
415
416    #[test]
417    #[cfg(feature = "serde")]
418    fn test_model_serde() {
419        let a = vec![
420            Box::new(
421                StaticBwConfig::new()
422                    .bw(Bandwidth::from_mbps(12))
423                    .duration(Duration::from_secs(1)),
424            ) as Box<dyn BwTraceConfig>,
425            Box::new(
426                StaticBwConfig::new()
427                    .bw(Bandwidth::from_mbps(24))
428                    .duration(Duration::from_secs(1)),
429            ) as Box<dyn BwTraceConfig>,
430        ];
431        let ser =
432            Box::new(RepeatedBwPatternConfig::new().pattern(a).count(2)) as Box<dyn BwTraceConfig>;
433        let ser_str = serde_json::to_string(&ser).unwrap();
434        #[cfg(not(feature = "human"))]
435        let des_str = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":12000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":24000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}";
436        #[cfg(feature = "human")]
437        let des_str = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":\"12Mbps\",\"duration\":\"1s\"}},{\"StaticBwConfig\":{\"bw\":\"24Mbps\",\"duration\":\"1s\"}}],\"count\":2}}";
438        assert_eq!(ser_str, des_str);
439        let des: Box<dyn BwTraceConfig> = serde_json::from_str(des_str).unwrap();
440        let mut model = des.into_model();
441        assert_eq!(
442            model.next_bw(),
443            Some((Bandwidth::from_mbps(12), Duration::from_secs(1)))
444        );
445    }
446
447    #[test]
448    fn test_forever() {
449        let mut normal_bw = NormalizedBwConfig::new()
450            .mean(Bandwidth::from_mbps(12))
451            .std_dev(Bandwidth::from_mbps(1))
452            .duration(Duration::from_millis(200))
453            .step(Duration::from_millis(100))
454            .seed(42)
455            .build();
456        assert_eq!(
457            normal_bw.next_bw(),
458            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
459        );
460        assert_eq!(
461            normal_bw.next_bw(),
462            Some((Bandwidth::from_bps(12132938), Duration::from_millis(100)))
463        );
464        assert_eq!(normal_bw.next_bw(), None);
465        let normal_bw_config = NormalizedBwConfig::new()
466            .mean(Bandwidth::from_mbps(12))
467            .std_dev(Bandwidth::from_mbps(1))
468            .duration(Duration::from_millis(200))
469            .step(Duration::from_millis(100))
470            .seed(42);
471        let normal_bw_repeated = normal_bw_config.forever();
472        let mut model = Box::new(normal_bw_repeated).into_model();
473        assert_eq!(
474            model.next_bw(),
475            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
476        );
477        assert_eq!(
478            model.next_bw(),
479            Some((Bandwidth::from_bps(12132938), Duration::from_millis(100)))
480        );
481        assert_eq!(
482            model.next_bw(),
483            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
484        );
485        assert_eq!(
486            model.next_bw(),
487            Some((Bandwidth::from_bps(12132938), Duration::from_millis(100)))
488        );
489    }
490
491    #[test]
492    #[cfg(feature = "human")]
493    fn test_compatibility_with_figment() {
494        use figment::{
495            providers::{Format, Json},
496            Figment,
497        };
498
499        let config = r##"
500{
501   "RepeatedBwPatternConfig":{
502      "pattern":[
503         {
504            "TraceBwConfig":{
505               "pattern":[
506                  [
507                     "25ms",
508                     [
509                        "10Mbps",
510                        "20Mbps"
511                     ]
512                  ],
513                  [
514                     "2ms",
515                     [
516                        "11Mbps",
517                        "23Mbps"
518                     ]
519                  ]
520               ]
521            }
522         },
523         {
524            "SawtoothBwConfig":{
525               "bottom":"10Mbps",
526               "top":"20Mbps",
527               "step":"1ms",
528               "interval":"10ms",
529               "duty_ratio":0.5
530            }
531         }
532      ],
533      "count":0
534   }
535}"##;
536
537        let trace: Box<dyn BwTraceConfig> = Figment::new()
538            .merge(Json::string(config))
539            .extract()
540            .unwrap();
541
542        let mut model = trace.into_model();
543
544        assert_eq!(
545            model.next_bw(),
546            Some((Bandwidth::from_mbps(10), Duration::from_millis(25)))
547        );
548        assert_eq!(
549            model.next_bw(),
550            Some((Bandwidth::from_mbps(20), Duration::from_millis(25)))
551        );
552        assert_eq!(
553            model.next_bw(),
554            Some((Bandwidth::from_mbps(11), Duration::from_millis(2)))
555        );
556        assert_eq!(
557            model.next_bw(),
558            Some((Bandwidth::from_mbps(23), Duration::from_millis(2)))
559        );
560        assert_eq!(
561            model.next_bw(),
562            Some((Bandwidth::from_mbps(10), Duration::from_millis(1)))
563        );
564        assert_eq!(
565            model.next_bw(),
566            Some((Bandwidth::from_mbps(12), Duration::from_millis(1)))
567        );
568        assert_eq!(
569            model.next_bw(),
570            Some((Bandwidth::from_mbps(14), Duration::from_millis(1)))
571        );
572        assert_eq!(
573            model.next_bw(),
574            Some((Bandwidth::from_mbps(16), Duration::from_millis(1)))
575        );
576        assert_eq!(
577            model.next_bw(),
578            Some((Bandwidth::from_mbps(18), Duration::from_millis(1)))
579        );
580    }
581}