1use crate::channel::{QueuedDatagram, SimChannel};
2use rand::{Rng, rng};
3use std::net::SocketAddr;
4use std::time::{Duration, Instant};
5
6#[derive(Debug, Clone)]
8pub struct NetworkConfig {
9 pub latency: Duration,
11 pub jitter: Duration,
13 pub loss_rate: f64,
15 pub burst_loss_prob: f64,
17 pub reorder_rate: f64,
19 pub bandwidth: Option<u64>,
21 pub nat_rebind_prob: f64,
23}
24
25impl Default for NetworkConfig {
26 fn default() -> Self {
27 Self {
28 latency: Duration::from_millis(10),
29 jitter: Duration::from_millis(0),
30 loss_rate: 0.0,
31 burst_loss_prob: 0.0,
32 reorder_rate: 0.0,
33 bandwidth: None,
34 nat_rebind_prob: 0.0,
35 }
36 }
37}
38
39pub struct SimulatedNetwork {
41 config: NetworkConfig,
42 channel: SimChannel,
43 in_burst_loss: bool,
44 current_src_port_offset: u16,
45}
46
47impl SimulatedNetwork {
48 pub fn new(config: NetworkConfig) -> Self {
50 Self {
51 config,
52 channel: SimChannel::new(),
53 in_burst_loss: false,
54 current_src_port_offset: 0,
55 }
56 }
57
58 pub fn send(&mut self, payload: Vec<u8>, mut src: SocketAddr, dst: SocketAddr, now: Instant) {
60 let mut rng = rng();
61
62 if self.in_burst_loss {
64 if rng.random::<f64>() > self.config.burst_loss_prob {
65 self.in_burst_loss = false;
66 } else {
67 return; }
69 } else if rng.random::<f64>() < self.config.loss_rate {
70 if rng.random::<f64>() < self.config.burst_loss_prob {
71 self.in_burst_loss = true;
72 }
73 return; }
75
76 let mut delay = self.config.latency;
78 if self.config.jitter > Duration::ZERO {
79 let jitter_ms = rng.random_range(0..=self.config.jitter.as_millis() as u64);
80 delay += Duration::from_millis(jitter_ms);
81 }
82
83 if rng.random::<f64>() < self.config.reorder_rate {
85 delay += Duration::from_millis(rng.random_range(10..50));
86 }
87
88 if rng.random::<f64>() < self.config.nat_rebind_prob {
90 self.current_src_port_offset = self.current_src_port_offset.wrapping_add(1);
91 }
92
93 src.set_port(src.port().wrapping_add(self.current_src_port_offset));
94
95 let delivery_time = now + delay;
96
97 self.channel.push(QueuedDatagram {
98 delivery_time,
99 payload,
100 src,
101 dst,
102 });
103 }
104
105 pub fn receive(&mut self, now: Instant) -> Option<QueuedDatagram> {
107 self.channel.pop_ready(now)
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_latency_delay() {
117 let mut net = SimulatedNetwork::new(NetworkConfig {
118 latency: Duration::from_millis(50),
119 ..Default::default()
120 });
121
122 let src: SocketAddr = "127.0.0.1:1000".parse().unwrap();
123 let dst: SocketAddr = "127.0.0.1:2000".parse().unwrap();
124 let now = Instant::now();
125
126 net.send(vec![1, 2, 3], src, dst, now);
127
128 assert!(net.receive(now).is_none());
129 assert!(net.receive(now + Duration::from_millis(20)).is_none());
130 assert!(net.receive(now + Duration::from_millis(50)).is_some());
131 }
132
133 #[test]
134 fn test_packet_loss_dropping() {
135 let mut net = SimulatedNetwork::new(NetworkConfig {
136 loss_rate: 1.0,
137 ..Default::default()
138 });
139
140 let src: SocketAddr = "127.0.0.1:1000".parse().unwrap();
141 let dst: SocketAddr = "127.0.0.1:2000".parse().unwrap();
142 let now = Instant::now();
143
144 net.send(vec![1, 2, 3], src, dst, now);
145
146 assert!(net.receive(now + Duration::from_secs(10)).is_none());
148 }
149
150 #[test]
151 fn test_reordering() {
152 let mut net = SimulatedNetwork::new(NetworkConfig {
153 latency: Duration::from_millis(10),
154 reorder_rate: 1.0, ..Default::default()
156 });
157
158 let src: SocketAddr = "127.0.0.1:1000".parse().unwrap();
159 let dst: SocketAddr = "127.0.0.1:2000".parse().unwrap();
160 let now = Instant::now();
161
162 net.send(vec![1], src, dst, now);
163
164 assert!(net.receive(now + Duration::from_millis(10)).is_none());
166
167 assert!(net.receive(now + Duration::from_millis(100)).is_some());
169 }
170}