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