hdds_micro/transport/mesh/
router.rs1use super::header::MeshHeader;
7use super::neighbor::NeighborTable;
8use super::seen::SeenCache;
9use super::{MeshStats, DEFAULT_TTL, MAX_TTL};
10
11#[derive(Debug, Clone, Copy)]
13pub struct MeshConfig {
14 pub default_ttl: u8,
16 pub relay_enabled: bool,
18 pub relay_broadcast: bool,
20 pub min_relay_rssi: i16,
22 pub neighbor_lifetime: u32,
24 pub seen_lifetime: u32,
26}
27
28impl Default for MeshConfig {
29 fn default() -> Self {
30 Self {
31 default_ttl: DEFAULT_TTL,
32 relay_enabled: true,
33 relay_broadcast: true,
34 min_relay_rssi: -100, neighbor_lifetime: 300, seen_lifetime: 60, }
38 }
39}
40
41impl MeshConfig {
42 pub fn relay_node() -> Self {
44 Self {
45 relay_enabled: true,
46 relay_broadcast: true,
47 ..Default::default()
48 }
49 }
50
51 pub fn endpoint() -> Self {
53 Self {
54 relay_enabled: false,
55 relay_broadcast: false,
56 ..Default::default()
57 }
58 }
59
60 pub fn gateway() -> Self {
62 Self {
63 default_ttl: MAX_TTL,
64 relay_enabled: true,
65 relay_broadcast: true,
66 ..Default::default()
67 }
68 }
69}
70
71#[derive(Debug, Clone)]
73pub enum RelayDecision {
74 Deliver,
76 Relay(MeshHeader),
78 Drop,
80}
81
82pub struct MeshRouter<const NEIGHBORS: usize, const SEEN: usize> {
84 node_id: u8,
86 config: MeshConfig,
88 neighbors: NeighborTable<NEIGHBORS>,
90 seen: SeenCache<SEEN>,
92 stats: MeshStats,
94}
95
96impl<const NEIGHBORS: usize, const SEEN: usize> MeshRouter<NEIGHBORS, SEEN> {
97 pub fn new(node_id: u8, config: MeshConfig) -> Self {
99 Self {
100 node_id,
101 config,
102 neighbors: NeighborTable::new(config.neighbor_lifetime),
103 seen: SeenCache::new(config.seen_lifetime),
104 stats: MeshStats::default(),
105 }
106 }
107
108 pub fn config(&self) -> &MeshConfig {
110 &self.config
111 }
112
113 pub fn neighbors(&self) -> &NeighborTable<NEIGHBORS> {
115 &self.neighbors
116 }
117
118 pub fn stats(&self) -> MeshStats {
120 self.stats
121 }
122
123 pub fn tick(&mut self) {
125 self.neighbors.tick();
126 self.seen.tick();
127 self.neighbors.expire_old();
128 }
129
130 pub fn process_received(&mut self, header: &MeshHeader, rssi: Option<i16>) -> RelayDecision {
132 if let Some(rssi) = rssi {
134 self.neighbors.update(header.src, rssi);
135 }
136
137 let msg_id = header.message_id();
139 if self.seen.check_and_mark(msg_id) {
140 self.stats.rx_duplicate += 1;
141 return RelayDecision::Drop;
142 }
143
144 let is_for_us = header.dst == self.node_id || header.dst == 0xFF;
146
147 let should_relay = self.should_relay(header, rssi);
149
150 if should_relay {
151 if let Some(relay_header) = header.for_relay() {
152 self.stats.tx_relayed += 1;
153
154 if is_for_us {
155 self.stats.rx_delivered += 1;
156 }
157
158 return RelayDecision::Relay(relay_header);
159 }
160
161 self.stats.rx_ttl_expired += 1;
163
164 if is_for_us {
165 self.stats.rx_delivered += 1;
166 return RelayDecision::Deliver;
167 }
168
169 return RelayDecision::Drop;
170 }
171
172 if is_for_us {
173 self.stats.rx_delivered += 1;
174 RelayDecision::Deliver
175 } else {
176 RelayDecision::Drop
177 }
178 }
179
180 fn should_relay(&self, header: &MeshHeader, rssi: Option<i16>) -> bool {
182 if header.src == self.node_id {
184 return false;
185 }
186
187 if header.dst == self.node_id {
189 return false;
190 }
191
192 if !self.config.relay_enabled {
194 return false;
195 }
196
197 if header.is_broadcast() && !self.config.relay_broadcast {
199 return false;
200 }
201
202 if let Some(rssi) = rssi {
204 if rssi < self.config.min_relay_rssi {
205 return false;
206 }
207 }
208
209 if header.ttl == 0 {
211 return false;
212 }
213
214 true
215 }
216
217 pub fn record_originated(&mut self, header: &MeshHeader) {
219 self.seen.mark_seen(header.message_id());
220 self.stats.tx_originated += 1;
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn test_router_creation() {
230 let router: MeshRouter<8, 32> = MeshRouter::new(1, MeshConfig::default());
231 assert_eq!(router.config().default_ttl, DEFAULT_TTL);
232 }
233
234 #[test]
235 fn test_relay_decision_deliver() {
236 let mut router: MeshRouter<8, 32> = MeshRouter::new(1, MeshConfig::endpoint());
237
238 let header = MeshHeader::new(2, 1, 100, 3);
240 let decision = router.process_received(&header, Some(-70));
241
242 match decision {
243 RelayDecision::Deliver => {}
244 _ => panic!("Expected Deliver"),
245 }
246 }
247
248 #[test]
249 fn test_relay_decision_broadcast() {
250 let mut router: MeshRouter<8, 32> = MeshRouter::new(1, MeshConfig::relay_node());
251
252 let header = MeshHeader::broadcast(2, 100, 3);
254 let decision = router.process_received(&header, Some(-70));
255
256 match decision {
257 RelayDecision::Relay(new_header) => {
258 assert_eq!(new_header.ttl, 2); assert_eq!(new_header.hop_count, 1); }
261 _ => panic!("Expected Relay"),
262 }
263 }
264
265 #[test]
266 fn test_relay_decision_duplicate() {
267 let mut router: MeshRouter<8, 32> = MeshRouter::new(1, MeshConfig::relay_node());
268
269 let header = MeshHeader::broadcast(2, 100, 3);
270
271 let decision1 = router.process_received(&header, Some(-70));
273 assert!(matches!(decision1, RelayDecision::Relay(_)));
274
275 let decision2 = router.process_received(&header, Some(-70));
277 assert!(matches!(decision2, RelayDecision::Drop));
278
279 assert_eq!(router.stats().rx_duplicate, 1);
280 }
281
282 #[test]
283 fn test_relay_decision_ttl_expired() {
284 let mut router: MeshRouter<8, 32> = MeshRouter::new(1, MeshConfig::relay_node());
285
286 let header = MeshHeader::new(2, 3, 100, 0);
288 let decision = router.process_received(&header, Some(-70));
289
290 assert!(matches!(decision, RelayDecision::Drop));
292 }
293
294 #[test]
295 fn test_relay_disabled() {
296 let mut router: MeshRouter<8, 32> = MeshRouter::new(1, MeshConfig::endpoint());
297
298 let header = MeshHeader::broadcast(2, 100, 3);
300 let decision = router.process_received(&header, Some(-70));
301
302 match decision {
304 RelayDecision::Deliver => {}
305 _ => panic!("Expected Deliver (not Relay)"),
306 }
307 }
308
309 #[test]
310 fn test_own_message_not_relayed() {
311 let mut router: MeshRouter<8, 32> = MeshRouter::new(1, MeshConfig::relay_node());
312
313 let header = MeshHeader::broadcast(1, 100, 3);
315 let decision = router.process_received(&header, Some(-70));
316
317 match decision {
319 RelayDecision::Deliver => {}
320 _ => panic!("Expected Deliver"),
321 }
322 }
323
324 #[test]
325 fn test_rssi_threshold() {
326 let config = MeshConfig {
327 min_relay_rssi: -80,
328 relay_enabled: true,
329 relay_broadcast: true,
330 ..Default::default()
331 };
332 let mut router: MeshRouter<8, 32> = MeshRouter::new(1, config);
333
334 let header1 = MeshHeader::broadcast(2, 100, 3);
336 let decision1 = router.process_received(&header1, Some(-70));
337 assert!(matches!(decision1, RelayDecision::Relay(_)));
338
339 let header2 = MeshHeader::broadcast(3, 101, 3);
341 let decision2 = router.process_received(&header2, Some(-90));
342 assert!(matches!(decision2, RelayDecision::Deliver));
343 }
344
345 #[test]
346 fn test_neighbor_tracking() {
347 let mut router: MeshRouter<8, 32> = MeshRouter::new(1, MeshConfig::relay_node());
348
349 let h1 = MeshHeader::broadcast(2, 100, 3);
351 let h2 = MeshHeader::broadcast(3, 101, 3);
352 let h3 = MeshHeader::broadcast(4, 102, 3);
353
354 router.process_received(&h1, Some(-70));
355 router.process_received(&h2, Some(-80));
356 router.process_received(&h3, Some(-60));
357
358 assert_eq!(router.neighbors().len(), 3);
359
360 let best = router.neighbors().best_neighbor().unwrap();
361 assert_eq!(best.node_id, 4); }
363
364 #[test]
365 fn test_stats_tracking() {
366 let mut router: MeshRouter<8, 32> = MeshRouter::new(1, MeshConfig::relay_node());
367
368 let h1 = MeshHeader::broadcast(2, 100, 3);
370 router.process_received(&h1, Some(-70));
371
372 router.process_received(&h1, Some(-70));
374
375 let h2 = MeshHeader::new(3, 1, 101, 3);
377 router.process_received(&h2, Some(-70));
378
379 let stats = router.stats();
380 assert_eq!(stats.tx_relayed, 1);
381 assert_eq!(stats.rx_duplicate, 1);
382 assert_eq!(stats.rx_delivered, 2); }
384
385 #[test]
386 fn test_config_presets() {
387 let relay = MeshConfig::relay_node();
388 assert!(relay.relay_enabled);
389 assert!(relay.relay_broadcast);
390
391 let endpoint = MeshConfig::endpoint();
392 assert!(!endpoint.relay_enabled);
393
394 let gateway = MeshConfig::gateway();
395 assert_eq!(gateway.default_ttl, MAX_TTL);
396 }
397}