1#[cfg(test)]
13#[allow(clippy::duplicate_mod)]
14#[path = "tests/side_template_index.rs"]
15mod side_template_index_tests;
16use crate::side_transport::{
17 SIDE_TRANSPORT_FLAG_PACKET_NONCE, SIDE_TRANSPORT_KIND_CHUNK, SIDE_TRANSPORT_KIND_COMPACT,
18 SIDE_TRANSPORT_KIND_COMPACT_DELTA, SIDE_TRANSPORT_KIND_COMPACT_SAME_TIMESTAMP,
19 SIDE_TRANSPORT_KIND_FULL, SideChunkAssembly, SideCompactTimestampMode, SideHeaderTemplate,
20 SideTransportFrames, extract_side_header_template, extract_side_header_template_view,
21 parse_side_transport_wrapper, read_uleb128_local, reconstruct_side_compact_frame,
22 wrap_side_transport_frame, write_uleb128_local,
23};
24#[cfg(all(test, feature = "discovery"))]
25#[path = "tests/ack_return_identity.rs"]
26mod ack_return_identity;
27
28#[cfg(all(test, feature = "discovery"))]
29#[path = "tests/discovery_liveness.rs"]
30mod discovery_liveness;
31
32#[cfg(all(test, feature = "discovery"))]
33mod compact_summary_tests {
34 use super::*;
35
36 #[test]
37 fn ordered_pending_window_is_bounded_and_exhaustion_cancels_the_tail() {
38 crate::tests::ensure_common_test_schema();
39 let ep = DataEndpoint::named("RADIO");
40 let ty = crate::config::register_data_type_with_description(
41 "ORDERED_WINDOW_REGRESSION",
42 "bounded ordered window",
43 MessageElement::Static(1, crate::MessageDataType::UInt8, crate::MessageClass::Data),
44 &[ep],
45 crate::ReliableMode::Ordered,
46 200,
47 )
48 .unwrap();
49 let now = Arc::new(core::sync::atomic::AtomicU64::new(0));
50 let clock = now.clone();
51 let router = Router::new_with_clock(
52 RouterConfig::new([]),
53 Box::new(move || clock.load(Ordering::Relaxed)),
54 );
55 let capacity = runtime_reliable_max_end_to_end_pending().max(1);
56 for index in 0..capacity {
57 let item = RouterItem::Packet(
58 Packet::new(
59 ty,
60 &[ep],
61 "SOURCE",
62 index as u64 + 1,
63 Arc::<[u8]>::from([index as u8]),
64 )
65 .unwrap(),
66 );
67 assert_eq!(
68 router.register_end_to_end_reliable_tx(&item).unwrap(),
69 index == 0
70 );
71 }
72 let rejected = RouterItem::Packet(
73 Packet::new(
74 ty,
75 &[ep],
76 "SOURCE",
77 capacity as u64 + 1,
78 Arc::<[u8]>::from([0]),
79 )
80 .unwrap(),
81 );
82 assert!(router.register_end_to_end_reliable_tx(&rejected).is_err());
83 {
84 let mut state = router.state.lock();
85 assert_eq!(state.end_to_end_reliable_tx.len(), capacity);
86 let first = state.end_to_end_reliable_tx_order[0];
87 state
88 .end_to_end_reliable_tx
89 .get_mut(&first)
90 .unwrap()
91 .retries = runtime_reliable_max_retries();
92 }
93 now.store(runtime_reliable_retransmit_ms(), Ordering::Relaxed);
94 assert!(router.process_end_to_end_reliable_timeouts().is_err());
95 assert!(router.state.lock().end_to_end_reliable_tx.is_empty());
96 }
97 #[test]
98 fn compact_reachability_survives_incomplete_detailed_topology() {
99 crate::tests::ensure_common_test_schema();
100 let router = Router::new(RouterConfig::new([]).with_sender("GB"));
101 let endpoint = DataEndpoint::try_named("GROUND_STATION")
102 .or_else(|| DataEndpoint::try_named("RADIO"))
103 .unwrap();
104 router.compact_summary_fixture(endpoint);
105 let state = router.state.lock();
106 let now = router.clock.now_ms();
107 let advertised =
108 router.advertised_discovery_endpoints_for_link_locked(&state, now, true, Some(1));
109 assert!(
110 advertised.contains(&endpoint),
111 "compact subscriber route was lost at the bridge"
112 );
113 let reflected =
114 router.advertised_discovery_endpoints_for_link_locked(&state, now, true, Some(0));
115 assert!(
116 !reflected.contains(&endpoint),
117 "split horizon must still exclude ingress"
118 );
119 assert_eq!(
120 router.advertised_discovery_timesync_sources_for_link_locked(&state, now, Some(1)),
121 vec!["clock-behind-bridge".to_string()]
122 );
123 assert!(
124 router
125 .advertised_discovery_timesync_sources_for_link_locked(&state, now, Some(0))
126 .is_empty()
127 );
128 assert!(
129 !router
130 .advertised_discovery_endpoints_for_link_locked(
131 &state,
132 now + DISCOVERY_ROUTE_TTL_MS + 1,
133 true,
134 Some(1)
135 )
136 .contains(&endpoint)
137 );
138 assert!(
139 router
140 .advertised_discovery_timesync_sources_for_link_locked(
141 &state,
142 now + DISCOVERY_ROUTE_TTL_MS + 1,
143 Some(1)
144 )
145 .is_empty()
146 );
147 }
148}
149
150use crate::config::RuntimeMemoryConfig;
151use crate::diagnostics::{
152 AdaptiveLinkStats, DiscoveryRuntimeStats, QueueRuntimeStats, ReliableRuntimeStats,
153 RouteModeStats, RouteOverrideStats, RoutePriorityStats, RouteWeightStats, RuntimeSideStats,
154 RuntimeStatsSnapshot, RuntimeTypeStats, TypedRouteOverrideStats,
155};
156#[cfg(feature = "discovery")]
157use crate::discovery::{
158 self, ClientStatsSnapshot, DISCOVERY_INCREMENTAL_RETRY_COUNT, DISCOVERY_ROUTE_TTL_MS,
159 DISCOVERY_SLOW_LINK_PING_INTERVAL_MS, DiscoveryCadenceState,
160 TIMESYNC_SLOW_LINK_MIN_INTERVAL_MS, TopologyAnnouncerRoute, TopologyBoardNode,
161 TopologySideRoute, TopologySnapshot,
162};
163use crate::packet::{hash_bytes_u64, sender_address_u32};
164use crate::queue::{BoundedDeque, ByteCost};
165#[cfg(all(not(feature = "std"), target_os = "none"))]
166use crate::seds_error_msg;
167#[cfg(feature = "timesync")]
168use crate::timesync::{
169 INTERNAL_TIMESYNC_SOURCE_ID, LOCAL_TIMESYNC_DATE_SOURCE_ID, LOCAL_TIMESYNC_FULL_SOURCE_ID,
170 LOCAL_TIMESYNC_SUBSEC_SOURCE_ID, LOCAL_TIMESYNC_TOD_SOURCE_ID, NetworkClock,
171 NetworkTimeReading, PartialNetworkTime, SlewedNetworkClock, TimeSyncConfig, TimeSyncLeader,
172 TimeSyncTracker, advance_network_time, compute_network_time_sample, decode_timesync_announce,
173 decode_timesync_request, decode_timesync_response,
174};
175use crate::{
176 E2eEncryptionPolicy, MessageElement, RouteSelectionMode, TelemetryError, TelemetryResult,
177 config::{
178 DataEndpoint, DataType, runtime_device_identifier, runtime_max_handler_retries,
179 runtime_reliable_max_end_to_end_pending, runtime_reliable_max_pending,
180 runtime_reliable_max_retries, runtime_reliable_max_return_routes,
181 runtime_reliable_retransmit_ms,
182 },
183 get_needed_message_size, impl_letype_num, is_reliable_type,
184 lock::{ReentryGate, RouterMutex},
185 message_e2e_encryption_policy, message_meta, message_priority,
186 packet::Packet,
187 reliable_mode, wire_format,
188};
189use alloc::string::{String, ToString};
190use alloc::{
191 borrow::ToOwned,
192 boxed::Box,
193 collections::{BTreeMap, BTreeSet, VecDeque},
194 format,
195 sync::Arc,
196 vec,
197 vec::Vec,
198};
199use core::cell::UnsafeCell;
200use core::fmt;
201use core::fmt::{Debug, Formatter};
202use core::mem::size_of;
203use core::ops::{Deref, DerefMut};
204use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
205#[cfg(feature = "std")]
206use std::time::Instant;
207
208pub type RouterSideId = usize;
210
211const CONTROL_SLOW_LINK_CAPACITY_BPS: u64 = 512;
212pub const IPV4_LIKE_COMPACT_HEADER_TARGET_BYTES: usize = 20;
213pub const IPV6_LIKE_COMPACT_HEADER_TARGET_BYTES: usize = 40;
214pub const DEFAULT_SIDE_TRANSPORT_TEMPLATE_LIMIT: usize = 64;
215static ROUTER_INSTANCE_SEQ: AtomicU32 = AtomicU32::new(1);
216
217#[derive(Clone, Copy, Debug, PartialEq, Eq)]
218pub enum SideTransportProfile {
219 Canonical,
220 Template,
221 Ipv6Like,
222 Ipv4Like,
223}
224
225impl SideTransportProfile {
226 #[inline]
227 pub const fn as_str(self) -> &'static str {
228 match self {
229 Self::Canonical => "canonical",
230 Self::Template => "template",
231 Self::Ipv6Like => "ipv6_like",
232 Self::Ipv4Like => "ipv4_like",
233 }
234 }
235
236 #[cfg(feature = "discovery")]
237 #[inline]
238 pub const fn discovery_code(self) -> u8 {
239 match self {
240 Self::Canonical => discovery::LINK_PROFILE_CANONICAL,
241 Self::Template => discovery::LINK_PROFILE_TEMPLATE,
242 Self::Ipv6Like => discovery::LINK_PROFILE_IPV6_LIKE,
243 Self::Ipv4Like => discovery::LINK_PROFILE_IPV4_LIKE,
244 }
245 }
246}
247
248#[cfg(test)]
249mod chunk_assembly_memory_tests {
250 use super::*;
251 #[test]
252 fn full_chunk_budget_reclaims_other_partial_transfers_before_active_schema() {
253 let mut state = SideTransportState::default();
254 for id in 1..=4 {
255 let mut assembly = SideChunkAssembly {
256 total: 28,
257 last_seen_ms: id as u64,
258 ..Default::default()
259 };
260 for index in 0..23 {
261 assembly.received.insert(
262 index,
263 crate::shared_bytes::convert(Arc::<[u8]>::from([id as u8; 112])),
264 );
265 }
266 state.rx_chunks.insert(id, assembly);
267 }
268 state.rx_chunks.get_mut(&1).unwrap().received.insert(
269 23,
270 crate::shared_bytes::convert(Arc::<[u8]>::from([1u8; 56])),
271 );
272 SideChunkAssembly::make_room(&mut state.rx_chunks, 4, 23, 28, 112, 16384).unwrap();
274 assert!(!state.rx_chunks.contains_key(&1));
275 assert_eq!(state.rx_chunks[&4].received.len(), 23);
276 for index in 23..28 {
277 SideChunkAssembly::make_room(&mut state.rx_chunks, 4, index, 28, 112, 16384).unwrap();
278 state.rx_chunks.get_mut(&4).unwrap().received.insert(
279 index,
280 crate::shared_bytes::convert(Arc::<[u8]>::from([4u8; 112])),
281 );
282 }
283 assert_eq!(
284 state.rx_chunks.remove(&4).unwrap().assemble().unwrap(),
285 vec![4u8; 28 * 112]
286 );
287 assert!(
288 state
289 .rx_chunks
290 .values()
291 .flat_map(|a| a.received.values())
292 .map(|p| p.len() + 64)
293 .sum::<usize>()
294 <= 16384
295 );
296 }
297
298 #[test]
299 fn duplicate_chunk_keeps_partial_transfers_but_oversize_transfer_is_rejected() {
300 let mut state = SideTransportState::default();
301 let mut assembly = SideChunkAssembly {
302 total: 2,
303 ..Default::default()
304 };
305 assembly.received.insert(
306 0,
307 crate::shared_bytes::convert(Arc::<[u8]>::from([1u8; 112])),
308 );
309 state.rx_chunks.insert(1, assembly.clone());
310 state.rx_chunks.insert(2, assembly);
311 SideChunkAssembly::make_room(&mut state.rx_chunks, 1, 0, 2, 112, 352).unwrap();
312 assert_eq!(state.rx_chunks.len(), 2);
313 assert!(SideChunkAssembly::make_room(&mut state.rx_chunks, 1, 1, 2, 400, 352).is_err());
314 assert!(!state.rx_chunks.contains_key(&1));
315 assert!(state.rx_chunks.contains_key(&2));
316 }
317
318 #[test]
319 fn schema_sized_reassembly_allocates_exactly_and_preserves_order() {
320 let payload: Vec<u8> = (0..3652).map(|i| (i % 251) as u8).collect();
321 let mut assembly = SideChunkAssembly::default();
322 for (index, chunk) in payload.chunks(56).enumerate().rev() {
323 assembly.received.insert(
324 index as u16,
325 crate::shared_bytes::convert(Arc::<[u8]>::from(chunk)),
326 );
327 }
328 assembly.total = assembly.received.len() as u16;
329 let out = assembly.assemble().unwrap();
330 assert_eq!(out, payload);
331 assert_eq!(
332 out.capacity(),
333 payload.len(),
334 "do not double discovery reassembly capacity"
335 );
336 }
337 #[test]
338 fn incomplete_or_noncontiguous_chunks_are_rejected() {
339 let mut assembly = SideChunkAssembly {
340 total: 2,
341 ..Default::default()
342 };
343 assembly
344 .received
345 .insert(0, crate::shared_bytes::convert(Arc::<[u8]>::from([1u8])));
346 assert!(assembly.clone().assemble().is_err());
347 assembly
348 .received
349 .insert(2, crate::shared_bytes::convert(Arc::<[u8]>::from([2u8])));
350 assert!(assembly.assemble().is_err());
351 }
352}
353
354#[derive(Clone, Debug, Default)]
355struct SideTransportState {
356 tx_chunk_progress: Option<Box<crate::side_transport::SideTxProgress>>,
357 tx_template_ids: BTreeMap<u64, u32>,
358 tx_templates: BTreeMap<u64, SideHeaderTemplate>,
359 tx_last_timestamps: BTreeMap<u32, u64>,
360 tx_compact_uses: BTreeMap<u32, u8>,
361 rx_templates: BTreeMap<u64, u32>,
362 rx_templates_by_id: BTreeMap<u32, SideHeaderTemplate>,
363 rx_last_timestamps: BTreeMap<u32, u64>,
364 rx_chunks: BTreeMap<u32, SideChunkAssembly>,
365}
366
367impl SideTransportState {
368 fn refresh_transmit_header_templates(&mut self) {
369 for uses in self.tx_compact_uses.values_mut() {
373 *uses = u8::MAX;
374 }
375 }
378
379 fn tx_template_count(&self) -> usize {
380 self.tx_template_ids.len()
381 }
382
383 fn rx_template_count(&self) -> usize {
384 self.rx_templates_by_id.len()
385 }
386
387 fn trim_tx_templates_to(&mut self, limit: usize) -> usize {
388 let mut removed = 0usize;
389 while self.tx_template_ids.len() > limit {
390 let Some(old_hash) = self.tx_template_ids.keys().next().copied() else {
391 break;
392 };
393 if let Some(old_id) = self.tx_template_ids.remove(&old_hash) {
394 self.tx_last_timestamps.remove(&old_id);
395 self.tx_compact_uses.remove(&old_id);
396 }
397 self.tx_templates.remove(&old_hash);
398 removed += 1;
399 }
400 removed
401 }
402
403 fn insert_tx_template(
404 &mut self,
405 template: SideHeaderTemplate,
406 template_id: u32,
407 max_templates: usize,
408 ) -> bool {
409 if max_templates == 0 {
410 return false;
411 }
412 let evicted = !self.tx_template_ids.contains_key(&template.hash)
413 && self.trim_tx_templates_to(max_templates.saturating_sub(1)) > 0;
414 self.tx_template_ids.insert(template.hash, template_id);
415 self.tx_templates.insert(template.hash, template);
416 self.tx_compact_uses.insert(template_id, 0);
417 evicted
418 }
419
420 fn insert_rx_template(
421 &mut self,
422 template_id: u32,
423 template: SideHeaderTemplate,
424 max_templates: usize,
425 ) -> bool {
426 if max_templates == 0 {
427 return false;
428 }
429 if let Some(old_id) = self.rx_templates.get(&template.hash).copied()
432 && old_id != template_id
433 {
434 self.rx_templates_by_id.remove(&old_id);
435 self.rx_last_timestamps.remove(&old_id);
436 self.rx_templates.remove(&template.hash);
437 }
438 if let Some(previous) = self.rx_templates_by_id.get(&template_id)
439 && previous.hash != template.hash
440 {
441 self.rx_templates.remove(&previous.hash);
442 self.rx_last_timestamps.remove(&template_id);
443 }
444 let mut evicted = false;
445 if self.rx_templates_by_id.len() >= max_templates
446 && !self.rx_templates_by_id.contains_key(&template_id)
447 && let Some((&old_hash, &old_id)) = self.rx_templates.first_key_value()
450 {
451 self.rx_templates.remove(&old_hash);
452 self.rx_templates_by_id.remove(&old_id);
453 self.rx_last_timestamps.remove(&old_id);
454 evicted = true;
455 }
456 self.rx_templates.insert(template.hash, template_id);
457 self.rx_templates_by_id.insert(template_id, template);
458 evicted
459 }
460}
461
462#[derive(Clone, Debug, PartialEq, Eq)]
463pub struct CompactTimestampOmissionPolicy {
464 all: bool,
465 types: Option<Arc<[DataType]>>,
466}
467impl CompactTimestampOmissionPolicy {
468 pub const fn none() -> Self {
469 Self {
470 all: false,
471 types: None,
472 }
473 }
474 pub const fn all() -> Self {
475 Self {
476 all: true,
477 types: None,
478 }
479 }
480 pub fn with_data_type(mut self, ty: DataType) -> Self {
481 self.insert(ty);
482 self
483 }
484 pub fn insert(&mut self, ty: DataType) {
485 if self.contains(ty) {
486 return;
487 }
488 let current = self.types.as_deref().unwrap_or(&[]);
489 let mut types = Vec::with_capacity(current.len() + 1);
490 types.extend_from_slice(current);
491 let index = types.binary_search(&ty).unwrap_err();
492 types.insert(index, ty);
493 self.types = Some(Arc::from(types));
494 }
495 pub fn contains(&self, ty: DataType) -> bool {
496 self.all
497 || self
498 .types
499 .as_deref()
500 .is_some_and(|types| types.binary_search(&ty).is_ok())
501 }
502 pub fn is_empty(&self) -> bool {
503 !self.all && self.types.is_none()
504 }
505}
506
507impl Default for CompactTimestampOmissionPolicy {
508 #[inline]
509 fn default() -> Self {
510 Self::none()
511 }
512}
513
514#[derive(Clone, Debug, PartialEq, Eq)]
515pub enum RouterItem {
516 Packet(Packet),
517 Packed(crate::SharedBytes),
518}
519
520#[cfg(feature = "compact-packet-store")]
521impl RouterItem {
522 fn park_for_queue(&mut self) -> TelemetryResult<()> {
523 match self {
524 Self::Packet(packet) => packet.park_for_queue()?,
525 Self::Packed(bytes) => bytes.park_for_queue()?,
526 }
527 Ok(())
528 }
529}
530
531#[derive(Clone, Debug, PartialEq, Eq)]
532struct RouterRxItem {
533 src: Option<RouterSideId>,
534 data: RouterItem,
535 priority: u8,
536}
537
538#[derive(Clone, Debug, PartialEq, Eq)]
539enum RouterTxItem {
540 Broadcast(RouterItem),
541 EndToEndReplay {
542 packet_id: u64,
543 },
544 ToSide {
545 src: Option<RouterSideId>,
546 dst: RouterSideId,
547 data: RouterItem,
548 },
549 ReliableReplay {
550 dst: RouterSideId,
551 bytes: crate::SharedBytes,
552 },
553}
554
555impl ByteCost for RouterRxItem {
556 #[cfg(feature = "compact-packet-store")]
557 fn park_for_queue(&mut self) -> TelemetryResult<()> {
558 self.data.park_for_queue()
559 }
560 #[inline]
561 fn byte_cost(&self) -> usize {
562 match &self.data {
563 RouterItem::Packet(pkt) => pkt.byte_cost(),
564 RouterItem::Packed(bytes) => size_of::<crate::SharedBytes>() + bytes.len(),
565 }
566 }
567}
568
569impl ByteCost for RouterTxItem {
570 #[cfg(feature = "compact-packet-store")]
571 fn park_for_queue(&mut self) -> TelemetryResult<()> {
572 match self {
573 Self::Broadcast(item) | Self::ToSide { data: item, .. } => item.park_for_queue(),
574 Self::ReliableReplay { bytes, .. } => bytes.park_for_queue(),
575 _ => Ok(()),
576 }
577 }
578 #[inline]
579 fn byte_cost(&self) -> usize {
580 match self {
581 RouterTxItem::Broadcast(data) => match data {
582 RouterItem::Packet(pkt) => pkt.byte_cost(),
583 RouterItem::Packed(bytes) => size_of::<crate::SharedBytes>() + bytes.len(),
584 },
585 RouterTxItem::EndToEndReplay { .. } => size_of::<u64>(),
586 RouterTxItem::ToSide { data, .. } => match data {
587 RouterItem::Packet(pkt) => pkt.byte_cost(),
588 RouterItem::Packed(bytes) => size_of::<crate::SharedBytes>() + bytes.len(),
589 },
590 RouterTxItem::ReliableReplay { bytes, .. } => {
591 size_of::<crate::SharedBytes>() + bytes.len()
592 }
593 }
594 }
595}
596
597#[derive(Clone, Debug, PartialEq, Eq)]
601struct TxQueued {
602 item: RouterTxItem,
603 ignore_local: bool,
604 priority: u8,
605 retry_after_ms: u64,
606}
607
608const MAX_PRIORITY_BURST: u8 = 8;
613
614impl ByteCost for TxQueued {
616 #[cfg(feature = "compact-packet-store")]
617 fn park_for_queue(&mut self) -> TelemetryResult<()> {
618 self.item.park_for_queue()
619 }
620 #[inline]
622 fn byte_cost(&self) -> usize {
623 self.item.byte_cost() + size_of::<bool>() + size_of::<u8>() + size_of::<u64>()
624 }
625}
626
627impl ByteCost for u64 {
629 #[inline]
631 fn byte_cost(&self) -> usize {
632 size_of::<u64>()
633 }
634}
635
636#[derive(Debug, Clone)]
639struct ReliableTxState {
640 next_seq: u32,
641 sent_order: VecDeque<u32>,
642 sent: BTreeMap<u32, ReliableSent>,
643}
644
645#[derive(Debug, Clone)]
646struct ReliableSent {
647 bytes: crate::SharedBytes,
648 last_send_ms: u64,
649 retries: u32,
650 queued: bool,
651 partial_acked: bool,
652}
653
654#[derive(Debug, Clone)]
655struct EndToEndReliableSent {
656 ordered_type: Option<u32>,
657 waiting_for_predecessor: bool,
658 data: RouterItem,
659 pending_destinations: BTreeMap<u64, RouterSideId>,
660 tracked_destinations: bool,
661 last_send_ms: u64,
662 retries: u32,
663 queued: bool,
664}
665
666#[derive(Debug, Clone)]
667struct ReliableRxState {
668 expected_seq: u32,
669 buffered: BTreeMap<u32, crate::SharedBytes>,
670}
671
672#[derive(Debug, Clone)]
673struct ReliableReturnRouteState {
674 side: RouterSideId,
675 last_replay_ms: u64,
676 upstream_contract: Option<(u64, u64)>,
680}
681
682#[cfg(feature = "discovery")]
683#[derive(Debug, Clone, Default, PartialEq, Eq)]
684struct DiscoverySenderState {
685 advertised_address: Option<u32>,
688 reachable: Vec<DataEndpoint>,
689 advertised_reachable: Vec<DataEndpoint>,
690 reachable_network_variables: Vec<DataType>,
691 reachable_timesync_sources: Vec<String>,
692 advertised_reachable_timesync_sources: Vec<String>,
693 link_capabilities: Option<discovery::LinkCapabilities>,
694 topology_boards: Vec<TopologyBoardNode>,
695 has_full_topology: bool,
696 has_schema: bool,
697 last_seen_ms: u64,
698}
699
700#[cfg(feature = "discovery")]
701#[derive(Debug, Clone, Default, PartialEq, Eq)]
702struct DiscoverySideState {
703 reachable: Vec<DataEndpoint>,
704 reachable_network_variables: Vec<DataType>,
705 requested_network_variables: Vec<(DataType, u64)>,
706 reachable_timesync_sources: Vec<String>,
707 last_seen_ms: u64,
708 announcers: BTreeMap<String, DiscoverySenderState>,
709}
710
711#[cfg(feature = "discovery")]
712#[derive(Debug, Clone, Default)]
713struct DiscoverySideThrottleState {
714 next_topology_request_ms: u64,
715 next_schema_request_ms: u64,
716 next_ping_ms: u64,
717 last_liveness_tx_ms: u64,
719 pending_incremental: bool,
720 incremental_retries_remaining: u8,
721 has_sent_full: bool,
722 last_topology: Vec<TopologyBoardNode>,
723}
724
725#[cfg(feature = "discovery")]
726impl DiscoverySideThrottleState {
727 fn liveness_due(&self, now_ms: u64) -> bool {
728 now_ms >= self.next_ping_ms
729 && now_ms.saturating_sub(self.last_liveness_tx_ms)
730 >= DISCOVERY_SLOW_LINK_PING_INTERVAL_MS
731 }
732}
733
734#[cfg(all(feature = "discovery", feature = "timesync"))]
735#[derive(Debug, Clone, Default)]
736struct TimeSyncSideThrottleState {
737 next_allowed_ms: u64,
738}
739
740#[cfg(feature = "discovery")]
741#[derive(Debug, Clone, Copy, PartialEq, Eq)]
742enum DiscoveryAdvertiseLevel {
743 MinimalPing,
744 Incremental,
745 Full,
746}
747
748#[derive(Debug, Clone, Default)]
749struct AdaptiveRouteStats {
750 estimated_bandwidth_bps: u64,
751 peak_bandwidth_bps: u64,
752 last_observed_ms: u64,
753 last_slow_observed_ms: u64,
754 sample_count: u64,
755 window_started_ms: u64,
756 window_bytes: u64,
757 peak_usage_bps: u64,
758}
759
760impl AdaptiveRouteStats {
761 #[inline]
762 fn observe(&mut self, bytes: usize, sample_bps: u64, now_ms: u64) {
763 self.estimated_bandwidth_bps = if self.estimated_bandwidth_bps == 0 {
764 sample_bps
765 } else if sample_bps >= self.estimated_bandwidth_bps {
766 self.estimated_bandwidth_bps
767 .saturating_mul(3)
768 .saturating_add(sample_bps.saturating_mul(5))
769 / 8
770 } else {
771 self.estimated_bandwidth_bps
772 .saturating_mul(7)
773 .saturating_add(sample_bps)
774 / 8
775 };
776 self.peak_bandwidth_bps = self.peak_bandwidth_bps.max(sample_bps);
777 self.last_observed_ms = now_ms;
778 if sample_bps > 0 && sample_bps <= CONTROL_SLOW_LINK_CAPACITY_BPS {
779 self.last_slow_observed_ms = now_ms;
780 }
781 self.sample_count = self.sample_count.saturating_add(1);
782 if self.window_started_ms == 0 || now_ms.saturating_sub(self.window_started_ms) > 1_000 {
783 self.window_started_ms = now_ms;
784 self.window_bytes = 0;
785 }
786 self.window_bytes = self.window_bytes.saturating_add(bytes as u64);
787 self.peak_usage_bps = self.peak_usage_bps.max(self.current_usage_bps(now_ms));
788 }
789
790 #[inline]
791 fn current_usage_bps(&self, now_ms: u64) -> u64 {
792 if self.window_started_ms == 0 {
793 return 0;
794 }
795 let elapsed_ms = now_ms.saturating_sub(self.window_started_ms).max(1);
796 (u128::from(self.window_bytes).saturating_mul(1000) / u128::from(elapsed_ms))
797 .min(u128::from(u64::MAX)) as u64
798 }
799
800 #[inline]
801 fn available_headroom_bps(&self, now_ms: u64) -> u64 {
802 let capacity = self
803 .estimated_bandwidth_bps
804 .max(self.peak_bandwidth_bps)
805 .max(1);
806 capacity.saturating_sub(self.current_usage_bps(now_ms))
807 }
808
809 #[inline]
810 fn weight(&self, now_ms: u64) -> u64 {
811 self.available_headroom_bps(now_ms).max(1)
812 }
813
814 #[inline]
815 fn snapshot(&self, now_ms: u64, auto_balancing_enabled: bool) -> AdaptiveLinkStats {
816 let current_usage_bps = self.current_usage_bps(now_ms);
817 let estimated_capacity_bps = self.estimated_bandwidth_bps.max(1);
818 let peak_capacity_bps = self.peak_bandwidth_bps.max(estimated_capacity_bps);
819 let available_headroom_bps = peak_capacity_bps.saturating_sub(current_usage_bps);
820 AdaptiveLinkStats {
821 auto_balancing_enabled,
822 estimated_capacity_bps,
823 peak_capacity_bps,
824 current_usage_bps,
825 peak_usage_bps: self.peak_usage_bps.max(current_usage_bps),
826 available_headroom_bps,
827 effective_weight: available_headroom_bps.max(1),
828 last_observed_ms: self.last_observed_ms,
829 sample_count: self.sample_count,
830 }
831 }
832}
833
834#[derive(Debug, Clone, Default)]
835struct TypeRuntimeStatsInner {
836 tx_packets: u64,
837 tx_bytes: u64,
838 rx_packets: u64,
839 rx_bytes: u64,
840 relayed_tx_packets: u64,
841 relayed_tx_bytes: u64,
842 relayed_rx_packets: u64,
843 relayed_rx_bytes: u64,
844 tx_retries: u64,
845 handler_failures: u64,
846}
847
848#[derive(Debug, Clone, Default)]
849struct SideRuntimeStatsInner {
850 tx_packets: u64,
851 tx_bytes: u64,
852 rx_packets: u64,
853 rx_bytes: u64,
854 relayed_tx_packets: u64,
855 relayed_tx_bytes: u64,
856 relayed_rx_packets: u64,
857 relayed_rx_bytes: u64,
858 local_delivery_packets: u64,
859 tx_retries: u64,
860 tx_handler_failures: u64,
861 local_handler_failures: u64,
862 total_handler_retries: u64,
863 side_transport_full_frames: u64,
864 side_transport_compact_frames: u64,
865 side_transport_compact_delta_frames: u64,
866 side_transport_compact_omitted_timestamp_frames: u64,
867 side_transport_chunk_frames: u64,
868 side_transport_raw_bytes: u64,
869 side_transport_wire_bytes: u64,
870 side_transport_bytes_saved: u64,
871 side_transport_min_compact_overhead_bytes: Option<usize>,
872 side_transport_max_compact_overhead_bytes: Option<usize>,
873 side_transport_compact_target_misses: u64,
874 side_transport_template_evictions: u64,
875 data_types: BTreeMap<u32, TypeRuntimeStatsInner>,
876}
877
878impl SideRuntimeStatsInner {
879 fn type_stats_mut(&mut self, ty: DataType) -> &mut TypeRuntimeStatsInner {
880 self.data_types.entry(ty.as_u32()).or_default()
881 }
882
883 fn note_tx(&mut self, ty: DataType, bytes: usize, relayed: bool, retries: usize) {
884 self.tx_packets = self.tx_packets.saturating_add(1);
885 self.tx_bytes = self.tx_bytes.saturating_add(bytes as u64);
886 self.tx_retries = self.tx_retries.saturating_add(retries as u64);
887 self.total_handler_retries = self.total_handler_retries.saturating_add(retries as u64);
888 if relayed {
889 self.relayed_tx_packets = self.relayed_tx_packets.saturating_add(1);
890 self.relayed_tx_bytes = self.relayed_tx_bytes.saturating_add(bytes as u64);
891 }
892 let stats = self.type_stats_mut(ty);
893 stats.tx_packets = stats.tx_packets.saturating_add(1);
894 stats.tx_bytes = stats.tx_bytes.saturating_add(bytes as u64);
895 stats.tx_retries = stats.tx_retries.saturating_add(retries as u64);
896 if relayed {
897 stats.relayed_tx_packets = stats.relayed_tx_packets.saturating_add(1);
898 stats.relayed_tx_bytes = stats.relayed_tx_bytes.saturating_add(bytes as u64);
899 }
900 }
901
902 fn note_rx(&mut self, ty: DataType, bytes: usize, relayed: bool) {
903 self.rx_packets = self.rx_packets.saturating_add(1);
904 self.rx_bytes = self.rx_bytes.saturating_add(bytes as u64);
905 if relayed {
906 self.relayed_rx_packets = self.relayed_rx_packets.saturating_add(1);
907 self.relayed_rx_bytes = self.relayed_rx_bytes.saturating_add(bytes as u64);
908 }
909 let stats = self.type_stats_mut(ty);
910 stats.rx_packets = stats.rx_packets.saturating_add(1);
911 stats.rx_bytes = stats.rx_bytes.saturating_add(bytes as u64);
912 if relayed {
913 stats.relayed_rx_packets = stats.relayed_rx_packets.saturating_add(1);
914 stats.relayed_rx_bytes = stats.relayed_rx_bytes.saturating_add(bytes as u64);
915 }
916 }
917
918 fn note_local_delivery(&mut self, ty: DataType) {
919 self.local_delivery_packets = self.local_delivery_packets.saturating_add(1);
920 let stats = self.type_stats_mut(ty);
921 stats.rx_packets = stats.rx_packets.saturating_add(1);
922 }
923
924 fn note_local_handler_failure(&mut self, ty: DataType, retries: usize) {
925 self.local_handler_failures = self.local_handler_failures.saturating_add(1);
926 self.total_handler_retries = self.total_handler_retries.saturating_add(retries as u64);
927 let stats = self.type_stats_mut(ty);
928 stats.handler_failures = stats.handler_failures.saturating_add(1);
929 }
930
931 fn note_tx_failure(&mut self, ty: DataType, retries: usize) {
932 self.tx_handler_failures = self.tx_handler_failures.saturating_add(1);
933 self.total_handler_retries = self.total_handler_retries.saturating_add(retries as u64);
934 self.tx_retries = self.tx_retries.saturating_add(retries as u64);
935 let stats = self.type_stats_mut(ty);
936 stats.handler_failures = stats.handler_failures.saturating_add(1);
937 stats.tx_retries = stats.tx_retries.saturating_add(retries as u64);
938 }
939
940 fn note_side_transport_full(&mut self, raw_bytes: usize, wire_bytes: usize) {
941 self.side_transport_full_frames = self.side_transport_full_frames.saturating_add(1);
942 self.note_side_transport_bytes(raw_bytes, wire_bytes);
943 }
944
945 fn note_side_transport_compact(
946 &mut self,
947 raw_bytes: usize,
948 wire_bytes: usize,
949 compact_overhead_bytes: usize,
950 used_timestamp_delta: bool,
951 omitted_timestamp: bool,
952 ) {
953 self.side_transport_compact_frames = self.side_transport_compact_frames.saturating_add(1);
954 if used_timestamp_delta {
955 self.side_transport_compact_delta_frames =
956 self.side_transport_compact_delta_frames.saturating_add(1);
957 }
958 if omitted_timestamp {
959 self.side_transport_compact_omitted_timestamp_frames = self
960 .side_transport_compact_omitted_timestamp_frames
961 .saturating_add(1);
962 }
963 self.note_side_transport_bytes(raw_bytes, wire_bytes);
964 self.side_transport_min_compact_overhead_bytes = Some(
965 self.side_transport_min_compact_overhead_bytes
966 .map_or(compact_overhead_bytes, |v| v.min(compact_overhead_bytes)),
967 );
968 self.side_transport_max_compact_overhead_bytes = Some(
969 self.side_transport_max_compact_overhead_bytes
970 .map_or(compact_overhead_bytes, |v| v.max(compact_overhead_bytes)),
971 );
972 }
973
974 fn note_side_transport_chunks(&mut self, chunks: usize) {
975 self.side_transport_chunk_frames = self
976 .side_transport_chunk_frames
977 .saturating_add(chunks as u64);
978 }
979
980 fn note_side_transport_bytes(&mut self, raw_bytes: usize, wire_bytes: usize) {
981 self.side_transport_raw_bytes = self
982 .side_transport_raw_bytes
983 .saturating_add(raw_bytes as u64);
984 self.side_transport_wire_bytes = self
985 .side_transport_wire_bytes
986 .saturating_add(wire_bytes as u64);
987 if raw_bytes > wire_bytes {
988 self.side_transport_bytes_saved = self
989 .side_transport_bytes_saved
990 .saturating_add((raw_bytes - wire_bytes) as u64);
991 }
992 }
993
994 fn note_side_transport_compact_target_miss(&mut self) {
995 self.side_transport_compact_target_misses =
996 self.side_transport_compact_target_misses.saturating_add(1);
997 }
998
999 fn note_side_transport_template_eviction(&mut self) {
1000 self.side_transport_template_evictions =
1001 self.side_transport_template_evictions.saturating_add(1);
1002 }
1003}
1004
1005#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1006enum RouteSelectionOrigin {
1007 Flood,
1008 Discovered,
1009}
1010
1011type PacketHandlerFn = dyn Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static;
1014
1015type PackedHandlerFn = dyn Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static;
1017type PrioritizedPackedHandlerFn = dyn Fn(&[u8], u8) -> TelemetryResult<()> + Send + Sync + 'static;
1018
1019#[derive(Clone)]
1025pub enum EndpointHandlerFn {
1026 Packet(Arc<PacketHandlerFn>),
1027 Packed(Arc<PackedHandlerFn>),
1028}
1029
1030pub struct EndpointHandler {
1032 endpoint: DataEndpoint,
1033 handler: EndpointHandlerFn,
1034}
1035
1036impl Debug for EndpointHandlerFn {
1038 #[inline]
1039 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1040 match self {
1041 EndpointHandlerFn::Packet(_) => f.write_str("EndpointHandlerFn::Packet(<handler>)"),
1042 EndpointHandlerFn::Packed(_) => f.write_str("EndpointHandlerFn::Packed(<handler>)"),
1043 }
1044 }
1045}
1046
1047#[derive(Clone)]
1049pub enum RouterTxHandlerFn {
1050 Packed(Arc<PackedHandlerFn>),
1051 PackedWithPriority(Arc<PrioritizedPackedHandlerFn>),
1052 Packet(Arc<PacketHandlerFn>),
1053}
1054
1055impl Debug for RouterTxHandlerFn {
1056 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1057 match self {
1058 RouterTxHandlerFn::Packed(_) => f.debug_tuple("Packed").field(&"<handler>").finish(),
1059 RouterTxHandlerFn::PackedWithPriority(_) => f
1060 .debug_tuple("PackedWithPriority")
1061 .field(&"<handler>")
1062 .finish(),
1063 RouterTxHandlerFn::Packet(_) => f.debug_tuple("Packet").field(&"<handler>").finish(),
1064 }
1065 }
1066}
1067
1068#[derive(Clone, Debug)]
1069pub struct RouterSideOptions {
1070 pub reliable_enabled: bool,
1081 pub link_local_enabled: bool,
1083 pub header_template_enabled: bool,
1090 pub max_frame_bytes: usize,
1096 pub compact_header_target_bytes: usize,
1103 pub max_side_transport_templates: usize,
1109 pub omit_unchanged_compact_timestamps: bool,
1115 pub compact_timestamp_omission_types: CompactTimestampOmissionPolicy,
1120 pub side_transport_profile: SideTransportProfile,
1122 pub ingress_enabled: bool,
1124 pub egress_enabled: bool,
1126}
1127
1128impl Default for RouterSideOptions {
1129 fn default() -> Self {
1130 Self {
1131 reliable_enabled: false,
1132 link_local_enabled: false,
1133 header_template_enabled: false,
1134 max_frame_bytes: 0,
1135 compact_header_target_bytes: 0,
1136 max_side_transport_templates: DEFAULT_SIDE_TRANSPORT_TEMPLATE_LIMIT,
1137 omit_unchanged_compact_timestamps: false,
1138 compact_timestamp_omission_types: CompactTimestampOmissionPolicy::none(),
1139 side_transport_profile: SideTransportProfile::Canonical,
1140 ingress_enabled: true,
1141 egress_enabled: true,
1142 }
1143 }
1144}
1145
1146impl RouterSideOptions {
1147 #[inline]
1152 pub fn with_small_packet_transport(mut self, max_frame_bytes: usize) -> Self {
1153 self.header_template_enabled = true;
1154 self.max_frame_bytes = max_frame_bytes;
1155 self.compact_header_target_bytes = IPV6_LIKE_COMPACT_HEADER_TARGET_BYTES;
1156 self.side_transport_profile = SideTransportProfile::Ipv6Like;
1157 self
1158 }
1159
1160 #[inline]
1161 pub fn with_ipv4_like_compact_header_target(mut self) -> Self {
1162 self.header_template_enabled = true;
1163 self.compact_header_target_bytes = IPV4_LIKE_COMPACT_HEADER_TARGET_BYTES;
1164 self.omit_unchanged_compact_timestamps = true;
1165 self.side_transport_profile = SideTransportProfile::Ipv4Like;
1166 self
1167 }
1168
1169 #[inline]
1170 pub fn with_ipv6_like_compact_header_target(mut self) -> Self {
1171 self.header_template_enabled = true;
1172 self.compact_header_target_bytes = IPV6_LIKE_COMPACT_HEADER_TARGET_BYTES;
1173 self.side_transport_profile = SideTransportProfile::Ipv6Like;
1174 self
1175 }
1176
1177 #[inline]
1178 pub fn with_template_transport(mut self) -> Self {
1179 self.header_template_enabled = true;
1180 self.side_transport_profile = SideTransportProfile::Template;
1181 self
1182 }
1183
1184 #[inline]
1185 pub fn with_omitted_unchanged_compact_timestamps(mut self) -> Self {
1186 self.header_template_enabled = true;
1187 self.omit_unchanged_compact_timestamps = true;
1188 self
1189 }
1190
1191 #[inline]
1192 pub fn with_omitted_unchanged_compact_timestamps_for_type(mut self, ty: DataType) -> Self {
1193 self.header_template_enabled = true;
1194 self.compact_timestamp_omission_types.insert(ty);
1195 self
1196 }
1197
1198 #[inline]
1199 pub fn effective_transport_profile(&self) -> SideTransportProfile {
1200 if !self.header_template_enabled && self.max_frame_bytes == 0 {
1201 SideTransportProfile::Canonical
1202 } else if self.side_transport_profile == SideTransportProfile::Canonical {
1203 SideTransportProfile::Template
1204 } else {
1205 self.side_transport_profile
1206 }
1207 }
1208
1209 #[cfg(feature = "discovery")]
1210 #[inline]
1211 pub fn link_capabilities(&self) -> discovery::LinkCapabilities {
1212 let mut flags = discovery::LINK_CAPABILITY_END_TO_END_RELIABILITY;
1213 if self.header_template_enabled {
1214 flags |= discovery::LINK_CAPABILITY_HEADER_TEMPLATES;
1215 }
1216 if self.max_frame_bytes != 0 {
1217 flags |= discovery::LINK_CAPABILITY_CHUNKING;
1218 }
1219 if self.reliable_enabled {
1220 flags |= discovery::LINK_CAPABILITY_RELIABILITY;
1221 }
1222 if self.omit_unchanged_compact_timestamps
1223 || !self.compact_timestamp_omission_types.is_empty()
1224 {
1225 flags |= discovery::LINK_CAPABILITY_OMIT_UNCHANGED_TIMESTAMPS;
1226 }
1227 #[cfg(feature = "cryptography")]
1228 {
1229 flags |= discovery::LINK_CAPABILITY_CRYPTO;
1230 }
1231 discovery::LinkCapabilities {
1232 version: 1,
1233 flags,
1234 profile: self.effective_transport_profile().discovery_code(),
1235 max_frame_bytes: self.max_frame_bytes.min(u32::MAX as usize) as u32,
1236 compact_header_target_bytes: self.compact_header_target_bytes.min(u32::MAX as usize)
1237 as u32,
1238 max_side_transport_templates: self.max_side_transport_templates.min(u32::MAX as usize)
1239 as u32,
1240 }
1241 }
1242}
1243
1244#[derive(Clone, Debug)]
1246pub struct RouterSide {
1247 pub name: Arc<str>,
1248 pub tx_handler: RouterTxHandlerFn,
1249 pub opts: RouterSideOptions,
1250}
1251
1252impl Debug for EndpointHandler {
1254 #[inline]
1255 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1256 f.debug_struct("EndpointHandler")
1257 .field("endpoint", &self.endpoint)
1258 .field("handler", &self.handler)
1259 .finish()
1260 }
1261}
1262
1263#[inline]
1264pub(crate) const fn endpoint_is_router_internal(endpoint: DataEndpoint) -> bool {
1265 #[cfg(feature = "timesync")]
1266 if matches!(endpoint, DataEndpoint::TimeSync) {
1267 return true;
1268 }
1269 #[cfg(feature = "discovery")]
1270 {
1271 discovery::is_discovery_endpoint(endpoint)
1272 }
1273 #[cfg(not(feature = "discovery"))]
1274 {
1275 let _ = endpoint;
1276 false
1277 }
1278}
1279
1280impl EndpointHandler {
1281 #[inline]
1285 pub fn new_packet_handler<F>(endpoint: DataEndpoint, f: F) -> Self
1286 where
1287 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
1288 {
1289 assert!(
1290 !endpoint_is_router_internal(endpoint),
1291 "reserved internal endpoint handlers must not be user-registered"
1292 );
1293 #[cfg(feature = "std")]
1294 crate::config::ensure_endpoint_id(endpoint, false)
1295 .expect("endpoint handler endpoint registration failed");
1296 Self {
1297 endpoint,
1298 handler: EndpointHandlerFn::Packet(Arc::new(f)),
1299 }
1300 }
1301
1302 #[inline]
1304 #[cfg(feature = "std")]
1305 pub fn new_packet_handler_for<F>(endpoint: crate::config::OwnedEndpointDefinition, f: F) -> Self
1306 where
1307 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
1308 {
1309 Self::new_packet_handler(endpoint.id, f)
1310 }
1311
1312 #[cfg(not(feature = "std"))]
1314 #[inline]
1315 pub fn new_packet_handler_for<F>(endpoint: crate::config::EndpointDefinition, f: F) -> Self
1316 where
1317 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
1318 {
1319 Self::new_packet_handler(endpoint.id, f)
1320 }
1321
1322 #[cfg(feature = "std")]
1324 #[inline]
1325 pub fn new_packet_handler_by_name<F>(endpoint_name: &str, f: F) -> TelemetryResult<Self>
1326 where
1327 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
1328 {
1329 let endpoint = crate::config::endpoint_definition_by_name(endpoint_name)
1330 .ok_or(TelemetryError::BadArg)?;
1331 Ok(Self::new_packet_handler(endpoint.id, f))
1332 }
1333
1334 #[inline]
1338 pub fn new_packed_handler<F>(endpoint: DataEndpoint, f: F) -> Self
1339 where
1340 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
1341 {
1342 assert!(
1343 !endpoint_is_router_internal(endpoint),
1344 "reserved internal endpoint handlers must not be user-registered"
1345 );
1346 #[cfg(feature = "std")]
1347 crate::config::ensure_endpoint_id(endpoint, false)
1348 .expect("endpoint handler endpoint registration failed");
1349 Self {
1350 endpoint,
1351 handler: EndpointHandlerFn::Packed(Arc::new(f)),
1352 }
1353 }
1354
1355 #[inline]
1357 pub fn new_packed_handler_for<F>(endpoint: crate::config::EndpointDefinition, f: F) -> Self
1358 where
1359 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
1360 {
1361 Self::new_packed_handler(endpoint.id, f)
1362 }
1363
1364 #[cfg(feature = "std")]
1366 #[inline]
1367 pub fn new_packed_handler_by_name<F>(endpoint_name: &str, f: F) -> TelemetryResult<Self>
1368 where
1369 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
1370 {
1371 let endpoint = crate::config::endpoint_definition_by_name(endpoint_name)
1372 .ok_or(TelemetryError::BadArg)?;
1373 Ok(Self::new_packed_handler(endpoint.id, f))
1374 }
1375
1376 #[inline]
1378 pub fn get_endpoint(&self) -> DataEndpoint {
1379 self.endpoint
1380 }
1381
1382 #[inline]
1384 pub fn get_handler(&self) -> &EndpointHandlerFn {
1385 &self.handler
1386 }
1387}
1388
1389pub trait Clock {
1390 fn now_ms(&self) -> u64;
1392
1393 fn now_ns(&self) -> u64 {
1397 self.now_ms().saturating_mul(1_000_000)
1398 }
1399}
1400
1401impl<T: Fn() -> u64> Clock for T {
1402 #[inline]
1403 fn now_ms(&self) -> u64 {
1404 self()
1405 }
1406}
1407
1408#[cfg(feature = "std")]
1409#[derive(Debug)]
1410struct StdMonotonicClock {
1411 start: Instant,
1412}
1413
1414#[cfg(feature = "std")]
1415impl Default for StdMonotonicClock {
1416 fn default() -> Self {
1417 Self {
1418 start: Instant::now(),
1419 }
1420 }
1421}
1422
1423#[cfg(feature = "std")]
1424impl Clock for StdMonotonicClock {
1425 fn now_ms(&self) -> u64 {
1426 u64::try_from(self.start.elapsed().as_millis()).unwrap_or(u64::MAX)
1427 }
1428
1429 fn now_ns(&self) -> u64 {
1430 u64::try_from(self.start.elapsed().as_nanos()).unwrap_or(u64::MAX)
1431 }
1432}
1433
1434#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1436pub enum RouterE2eEncryptionMode {
1437 Disabled,
1439 RequiredOnly,
1441 Preferred,
1443 ForceAll,
1445}
1446
1447pub type NodeAddress = u32;
1448pub type P2pPort = u16;
1449pub type P2pStreamId = u32;
1450
1451const P2P_STREAM_MAGIC: [u8; 4] = *b"SDSP";
1452const P2P_STREAM_VERSION: u8 = 1;
1453const P2P_STREAM_SYN: u8 = 0x01;
1454const P2P_STREAM_ACK: u8 = 0x02;
1455const P2P_STREAM_FIN: u8 = 0x04;
1456const P2P_STREAM_RST: u8 = 0x08;
1457const P2P_STREAM_DATA: u8 = 0x10;
1458
1459#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1460pub enum AddressAssignmentMode {
1461 Dynamic,
1462 Requested(NodeAddress),
1463 Static(NodeAddress),
1464}
1465
1466impl AddressAssignmentMode {
1467 #[inline]
1468 fn mode_code(self) -> u8 {
1469 match self {
1470 Self::Dynamic => 0,
1471 Self::Requested(_) => 1,
1472 Self::Static(_) => 2,
1473 }
1474 }
1475
1476 #[inline]
1477 fn requested_address(self) -> NodeAddress {
1478 match self {
1479 Self::Dynamic => 0,
1480 Self::Requested(addr) | Self::Static(addr) => addr,
1481 }
1482 }
1483}
1484
1485#[derive(Debug, Clone, PartialEq, Eq)]
1486pub enum AddressChangeReason {
1487 Configured,
1488 DynamicConflict,
1489 RequestedConflict,
1490 StaticConflict,
1491 HostnameConflict,
1492}
1493
1494#[derive(Debug, Clone, PartialEq, Eq)]
1495pub struct AddressChange {
1496 pub old_hostname: Arc<str>,
1497 pub new_hostname: Arc<str>,
1498 pub old_address: NodeAddress,
1499 pub new_address: NodeAddress,
1500 pub reason: AddressChangeReason,
1501}
1502
1503#[derive(Debug, Clone, PartialEq, Eq)]
1504pub struct AddressBookEntry {
1505 pub hostname: Arc<str>,
1506 pub address: NodeAddress,
1507 pub requested_address: NodeAddress,
1508 pub mode: AddressAssignmentMode,
1509 pub birth_ms: u64,
1510 pub owner_hash: u64,
1511 pub last_seen_ms: u64,
1512}
1513
1514pub struct P2pMessage<'a> {
1515 pub source_hostname: &'a str,
1516 pub source_address: NodeAddress,
1517 pub source_port: P2pPort,
1518 pub destination_port: P2pPort,
1519 pub payload: &'a [u8],
1520}
1521
1522#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1523pub enum P2pStreamEventKind {
1524 Accepted,
1525 Connected,
1526 Data,
1527 Closed,
1528 Reset,
1529}
1530
1531pub struct P2pStreamEvent<'a> {
1532 pub kind: P2pStreamEventKind,
1533 pub stream_id: P2pStreamId,
1534 pub peer_stream_id: P2pStreamId,
1535 pub sequence: u32,
1536 pub peer_hostname: &'a str,
1537 pub peer_address: NodeAddress,
1538 pub local_port: P2pPort,
1539 pub peer_port: P2pPort,
1540 pub payload: &'a [u8],
1541}
1542
1543struct P2pDecoded<'a> {
1544 source_hostname: &'a str,
1545 source_address: NodeAddress,
1546 source_port: P2pPort,
1547 destination_port: P2pPort,
1548 payload: &'a [u8],
1549}
1550
1551#[derive(Debug, Clone)]
1552struct P2pStreamDecoded<'a> {
1553 flags: u8,
1554 source_stream_id: P2pStreamId,
1555 destination_stream_id: P2pStreamId,
1556 sequence: u32,
1557 payload: &'a [u8],
1558}
1559
1560type AddressChangeFn = dyn Fn(AddressChange) -> TelemetryResult<()> + Send + Sync + 'static;
1561
1562#[derive(Clone)]
1563struct AddressChangeHandler {
1564 handler: Arc<AddressChangeFn>,
1565}
1566
1567impl Debug for AddressChangeHandler {
1568 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1569 f.write_str("AddressChangeHandler(<handler>)")
1570 }
1571}
1572
1573type P2pPortHandlerFn = dyn Fn(P2pMessage<'_>) -> TelemetryResult<()> + Send + Sync + 'static;
1574
1575#[derive(Clone)]
1576struct P2pPortHandler {
1577 handler: Arc<P2pPortHandlerFn>,
1578}
1579
1580impl Debug for P2pPortHandler {
1581 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1582 f.write_str("P2pPortHandler(<handler>)")
1583 }
1584}
1585
1586type P2pStreamHandlerFn = dyn Fn(P2pStreamEvent<'_>) -> TelemetryResult<()> + Send + Sync + 'static;
1587
1588#[derive(Clone)]
1589struct P2pStreamHandler {
1590 handler: Arc<P2pStreamHandlerFn>,
1591}
1592
1593impl Debug for P2pStreamHandler {
1594 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1595 f.write_str("P2pStreamHandler(<handler>)")
1596 }
1597}
1598
1599#[derive(Debug, Clone)]
1600struct P2pStreamSession {
1601 peer_hostname: Arc<str>,
1602 peer_address: NodeAddress,
1603 local_port: P2pPort,
1604 peer_port: P2pPort,
1605 peer_stream_id: P2pStreamId,
1606 next_sequence: u32,
1607 connected: bool,
1608}
1609
1610#[derive(Debug, Clone)]
1611struct PendingP2pStreamEvent {
1612 handlers: Vec<P2pStreamHandler>,
1613 kind: P2pStreamEventKind,
1614 stream_id: P2pStreamId,
1615 peer_stream_id: P2pStreamId,
1616 peer_hostname: Arc<str>,
1617 peer_address: NodeAddress,
1618 local_port: P2pPort,
1619 peer_port: P2pPort,
1620}
1621
1622#[derive(Debug, Clone)]
1623pub struct RouterConfig {
1624 handlers: Arc<[EndpointHandler]>,
1626 reliable_enabled: bool,
1628 sender: Option<Arc<str>>,
1630 address_mode: AddressAssignmentMode,
1632 address_change_handlers: Arc<[AddressChangeHandler]>,
1634 e2e_encryption: RouterE2eEncryptionMode,
1636 #[cfg_attr(not(feature = "cryptography"), allow(dead_code))]
1638 e2e_key_id: u32,
1639 memory: RuntimeMemoryConfig,
1640 #[cfg(feature = "discovery")]
1641 preferred_discovery_master: Option<Arc<str>>,
1642 #[cfg(feature = "timesync")]
1643 timesync: Option<TimeSyncConfig>,
1644}
1645
1646impl RouterConfig {
1647 pub fn default_e2e_encryption_mode() -> RouterE2eEncryptionMode {
1652 #[cfg(feature = "cryptography")]
1653 {
1654 RouterE2eEncryptionMode::Preferred
1655 }
1656 #[cfg(not(feature = "cryptography"))]
1657 {
1658 RouterE2eEncryptionMode::Disabled
1659 }
1660 }
1661
1662 pub fn new<H>(handlers: H) -> Self
1664 where
1665 H: Into<Arc<[EndpointHandler]>>,
1666 {
1667 let handlers: Arc<[EndpointHandler]> = handlers.into();
1668 assert!(
1669 handlers
1670 .iter()
1671 .all(|handler| !endpoint_is_router_internal(handler.endpoint)),
1672 "reserved internal endpoint handlers must not be user-registered"
1673 );
1674 Self {
1675 handlers,
1676 reliable_enabled: true,
1677 sender: None,
1678 address_mode: AddressAssignmentMode::Dynamic,
1679 address_change_handlers: Arc::from([]),
1680 e2e_encryption: Self::default_e2e_encryption_mode(),
1681 e2e_key_id: 0,
1682 memory: RuntimeMemoryConfig::default(),
1683 #[cfg(feature = "discovery")]
1684 preferred_discovery_master: None,
1685 #[cfg(feature = "timesync")]
1686 timesync: None,
1687 }
1688 }
1689
1690 pub fn with_reliable_enabled(mut self, enabled: bool) -> Self {
1692 self.reliable_enabled = enabled;
1693 self
1694 }
1695
1696 pub fn with_sender<S: AsRef<str>>(mut self, sender: S) -> Self {
1698 self.sender = Some(Arc::from(sender.as_ref()));
1699 self
1700 }
1701
1702 pub fn with_hostname<S: AsRef<str>>(self, hostname: S) -> Self {
1704 self.with_sender(hostname)
1705 }
1706
1707 pub fn with_dynamic_address(mut self) -> Self {
1709 self.address_mode = AddressAssignmentMode::Dynamic;
1710 self
1711 }
1712
1713 pub fn with_requested_address(mut self, address: NodeAddress) -> Self {
1715 self.address_mode = AddressAssignmentMode::Requested(address);
1716 self
1717 }
1718
1719 pub fn with_static_address(mut self, address: NodeAddress) -> Self {
1721 self.address_mode = AddressAssignmentMode::Static(address);
1722 self
1723 }
1724
1725 pub fn on_address_change<F>(mut self, f: F) -> Self
1727 where
1728 F: Fn(AddressChange) -> TelemetryResult<()> + Send + Sync + 'static,
1729 {
1730 let mut handlers = self.address_change_handlers.to_vec();
1731 handlers.push(AddressChangeHandler {
1732 handler: Arc::new(f),
1733 });
1734 self.address_change_handlers = Arc::from(handlers);
1735 self
1736 }
1737
1738 pub fn with_e2e_encryption(mut self, mode: RouterE2eEncryptionMode) -> Self {
1740 self.e2e_encryption = mode;
1741 self
1742 }
1743
1744 pub fn with_e2e_key_id(mut self, key_id: u32) -> Self {
1746 self.e2e_key_id = key_id;
1747 self
1748 }
1749
1750 pub fn with_memory_config(mut self, memory: RuntimeMemoryConfig) -> TelemetryResult<Self> {
1752 memory.validate()?;
1753 self.memory = memory;
1754 Ok(self)
1755 }
1756
1757 #[cfg(feature = "discovery")]
1763 pub fn with_preferred_discovery_master<S: AsRef<str>>(mut self, hostname: S) -> Self {
1764 let hostname = hostname.as_ref();
1765 self.preferred_discovery_master = if hostname.is_empty() {
1766 None
1767 } else {
1768 Some(Arc::from(hostname))
1769 };
1770 self
1771 }
1772
1773 #[cfg(feature = "timesync")]
1774 pub fn with_timesync(mut self, cfg: TimeSyncConfig) -> Self {
1776 self.timesync = Some(cfg);
1777 self
1778 }
1779
1780 #[inline]
1781 fn is_local_endpoint(&self, ep: DataEndpoint) -> bool {
1783 if endpoint_is_router_internal(ep) {
1784 return false;
1785 }
1786 self.handlers.iter().any(|h| h.endpoint == ep)
1787 }
1788
1789 #[inline]
1790 fn reliable_enabled(&self) -> bool {
1791 self.reliable_enabled
1792 }
1793
1794 #[inline]
1795 fn sender(&self) -> String {
1796 self.sender
1797 .as_deref()
1798 .map(ToString::to_string)
1799 .unwrap_or_else(runtime_device_identifier)
1800 }
1801
1802 #[inline]
1803 fn address_mode(&self) -> AddressAssignmentMode {
1804 self.address_mode
1805 }
1806
1807 #[inline]
1808 fn e2e_encryption(&self) -> RouterE2eEncryptionMode {
1809 self.e2e_encryption
1810 }
1811
1812 #[cfg(feature = "cryptography")]
1813 #[inline]
1814 fn e2e_key_id(&self) -> u32 {
1815 self.e2e_key_id
1816 }
1817
1818 #[cfg(feature = "timesync")]
1819 #[inline]
1820 fn timesync_config(&self) -> Option<TimeSyncConfig> {
1821 self.timesync
1822 }
1823
1824 #[inline]
1825 fn memory_config(&self) -> RuntimeMemoryConfig {
1826 self.memory
1827 }
1828}
1829
1830impl Default for RouterConfig {
1831 fn default() -> Self {
1832 Self {
1833 handlers: Arc::from([]),
1834 reliable_enabled: true,
1835 sender: None,
1836 address_mode: AddressAssignmentMode::Dynamic,
1837 address_change_handlers: Arc::from([]),
1838 e2e_encryption: Self::default_e2e_encryption_mode(),
1839 e2e_key_id: 0,
1840 memory: RuntimeMemoryConfig::default(),
1841 #[cfg(feature = "discovery")]
1842 preferred_discovery_master: None,
1843 #[cfg(feature = "timesync")]
1844 timesync: None,
1845 }
1846 }
1847}
1848
1849pub trait LeBytes: Copy + Sized {
1852 const WIDTH: usize;
1853 fn write_le(self, out: &mut [u8]);
1854 fn from_le_slice(bytes: &[u8]) -> Self;
1855}
1856
1857impl_letype_num!(u8, 1);
1858impl_letype_num!(u16, 2);
1859impl_letype_num!(u32, 4);
1860impl_letype_num!(u64, 8);
1861impl_letype_num!(u128, 16);
1862impl_letype_num!(i8, 1);
1863impl_letype_num!(i16, 2);
1864impl_letype_num!(i32, 4);
1865impl_letype_num!(i64, 8);
1866impl_letype_num!(i128, 16);
1867impl_letype_num!(f32, 4);
1868impl_letype_num!(f64, 8);
1869
1870pub(crate) fn encode_slice_le<T: LeBytes>(data: &[T]) -> Arc<[u8]> {
1872 let total = data.len() * T::WIDTH;
1873 let mut buf = vec![0u8; total];
1874
1875 for (i, v) in data.iter().copied().enumerate() {
1876 let start = i * T::WIDTH;
1877 v.write_le(&mut buf[start..start + T::WIDTH]);
1878 }
1879
1880 Arc::from(buf)
1881}
1882
1883fn make_error_payload(msg: &str) -> crate::SharedBytes {
1886 let meta = message_meta(DataType::TelemetryError);
1887 match meta.element {
1888 MessageElement::Static(_, _, _) => {
1889 let max = get_needed_message_size(DataType::TelemetryError);
1890 let bytes = msg.as_bytes();
1891 let n = core::cmp::min(max, bytes.len());
1892 let mut buf = vec![0u8; max];
1893 if n > 0 {
1894 buf[..n].copy_from_slice(&bytes[..n]);
1895 }
1896 crate::shared_bytes::convert(Arc::<[u8]>::from(buf))
1897 }
1898 MessageElement::Dynamic(_, _) => {
1899 crate::shared_bytes::convert(Arc::<[u8]>::from(msg.as_bytes()))
1900 }
1901 }
1902}
1903
1904fn log_raw<T, F>(
1908 sender: &str,
1909 ty: DataType,
1910 data: &[T],
1911 timestamp: u64,
1912 mut tx_function: F,
1913) -> TelemetryResult<()>
1914where
1915 T: LeBytes,
1916 F: FnMut(Packet) -> TelemetryResult<()>,
1917{
1918 let meta = message_meta(ty);
1919 let got = data.len() * T::WIDTH;
1920
1921 match meta.element {
1922 MessageElement::Static(_, _, _) => {
1923 if got != get_needed_message_size(ty) {
1924 return Err(TelemetryError::SizeMismatch {
1925 expected: get_needed_message_size(ty),
1926 got,
1927 });
1928 }
1929 }
1930 MessageElement::Dynamic(_, _) => {
1931 if !got.is_multiple_of(T::WIDTH) {
1933 return Err(TelemetryError::SizeMismatch {
1934 expected: T::WIDTH,
1935 got,
1936 });
1937 }
1938 }
1939 }
1940
1941 let payload = encode_slice_le(data);
1942 let pkt = Packet::new(ty, meta.endpoints_ref(), sender, timestamp, payload)?;
1943 tx_function(pkt)
1944}
1945
1946#[cfg(feature = "std")]
1948fn reliable_trace_enabled() -> bool {
1949 static ENABLED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1950 *ENABLED.get_or_init(|| {
1951 matches!(
1952 std::env::var("SEDSNET_TRACE_RELIABLE").as_deref(),
1953 Ok("1" | "true")
1954 )
1955 })
1956}
1957
1958fn fallback_stdout(_msg: &str) {
1962 #[cfg(feature = "std")]
1963 {
1964 eprintln!("{}", _msg);
1965 }
1966 #[cfg(all(not(feature = "std"), target_os = "none"))]
1967 {
1968 let message = format!("{}\n", _msg);
1969 unsafe {
1970 seds_error_msg(message.as_ptr(), message.len());
1971 }
1972 }
1973}
1974
1975#[derive(Debug, Clone)]
1980struct RouterInner {
1981 memory: RuntimeMemoryConfig,
1982 sides: Vec<Option<RouterSide>>,
1983 route_overrides: BTreeMap<(Option<RouterSideId>, RouterSideId), bool>,
1984 typed_route_overrides: BTreeMap<(Option<RouterSideId>, u32, RouterSideId), bool>,
1985 route_weights: BTreeMap<(Option<RouterSideId>, RouterSideId), u32>,
1986 route_priorities: BTreeMap<(Option<RouterSideId>, RouterSideId), u32>,
1987 source_route_modes: BTreeMap<Option<RouterSideId>, RouteSelectionMode>,
1988 route_selection_cursors: BTreeMap<Option<RouterSideId>, u64>,
1989 adaptive_route_stats: BTreeMap<RouterSideId, AdaptiveRouteStats>,
1990 side_runtime_stats: BTreeMap<RouterSideId, SideRuntimeStatsInner>,
1991 side_transport: BTreeMap<RouterSideId, SideTransportState>,
1992 managed_variable_types: BTreeSet<u32>,
1993 managed_variable_permissions: BTreeMap<u32, NetworkVariablePermissions>,
1994 managed_variable_latest: BTreeMap<u32, ManagedVariableCacheEntry>,
1995 network_variable_update_handlers: BTreeMap<u32, Vec<NetworkVariableUpdateHandler>>,
1996 local_address: AddressBookEntry,
1997 address_book: BTreeMap<String, AddressBookEntry>,
1998 address_by_value: BTreeMap<NodeAddress, String>,
1999 p2p_port_handlers: BTreeMap<P2pPort, Vec<P2pPortHandler>>,
2000 p2p_stream_handlers: BTreeMap<P2pPort, Vec<P2pStreamHandler>>,
2001 p2p_stream_sessions: BTreeMap<P2pStreamId, P2pStreamSession>,
2002 next_p2p_stream_id: P2pStreamId,
2003 received_queue: BoundedDeque<RouterRxItem>,
2004 transmit_queue: BoundedDeque<TxQueued>,
2005 tx_priority_burst: u8,
2006 recent_rx: BoundedDeque<u64>,
2007 reliable_tx: BTreeMap<(RouterSideId, u32), ReliableTxState>,
2008 reliable_rx: BTreeMap<(RouterSideId, u32), ReliableRxState>,
2009 reliable_return_routes: BTreeMap<u64, ReliableReturnRouteState>,
2010 reliable_return_route_order: VecDeque<u64>,
2011 end_to_end_reliable_tx: BTreeMap<u64, EndToEndReliableSent>,
2012 end_to_end_reliable_tx_order: VecDeque<u64>,
2013 total_handler_failures: u64,
2014 total_handler_retries: u64,
2015 #[cfg(feature = "discovery")]
2016 discovery_routes: BTreeMap<RouterSideId, DiscoverySideState>,
2017 #[cfg(feature = "discovery")]
2018 discovery_cadence: DiscoveryCadenceState,
2019 #[cfg(feature = "discovery")]
2020 discovery_side_throttle: BTreeMap<RouterSideId, DiscoverySideThrottleState>,
2021 #[cfg(feature = "discovery")]
2022 preferred_discovery_master: Option<Arc<str>>,
2023 #[cfg(all(feature = "discovery", feature = "timesync"))]
2024 timesync_side_throttle: BTreeMap<RouterSideId, TimeSyncSideThrottleState>,
2025}
2026
2027#[derive(Debug, Clone)]
2028struct ManagedVariableCacheEntry {
2029 packet: Packet,
2030 sender_address: u32,
2031 cached_at_ms: u64,
2032}
2033
2034type NetworkVariableUpdateFn = dyn Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static;
2035
2036#[derive(Clone)]
2037struct NetworkVariableUpdateHandler {
2038 handler: Arc<NetworkVariableUpdateFn>,
2039}
2040
2041impl Debug for NetworkVariableUpdateHandler {
2042 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2043 f.write_str("NetworkVariableUpdateHandler(<handler>)")
2044 }
2045}
2046
2047#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2049pub struct NetworkVariablePermissions {
2050 pub read: bool,
2051 pub write: bool,
2052}
2053
2054impl NetworkVariablePermissions {
2055 pub const NONE: Self = Self {
2056 read: false,
2057 write: false,
2058 };
2059 pub const READ_ONLY: Self = Self {
2060 read: true,
2061 write: false,
2062 };
2063 pub const WRITE_ONLY: Self = Self {
2064 read: false,
2065 write: true,
2066 };
2067 pub const READ_WRITE: Self = Self {
2068 read: true,
2069 write: true,
2070 };
2071}
2072
2073#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2074enum RouterQueueKind {
2075 Received,
2076 Transmit,
2077}
2078
2079impl RouterInner {
2080 #[cfg(feature = "discovery")]
2081 fn topology_board_byte_cost(board: &TopologyBoardNode) -> usize {
2082 board
2083 .sender_id
2084 .len()
2085 .saturating_add(board.reachable_endpoints.len() * size_of::<DataEndpoint>())
2086 .saturating_add(
2087 board
2088 .reachable_timesync_sources
2089 .iter()
2090 .map(|s| s.len())
2091 .sum::<usize>(),
2092 )
2093 .saturating_add(board.connections.iter().map(|s| s.len()).sum::<usize>())
2094 }
2095
2096 #[cfg(feature = "discovery")]
2097 fn discovery_sender_byte_cost(sender: &str, state: &DiscoverySenderState) -> usize {
2098 sender
2099 .len()
2100 .saturating_add(state.reachable.len() * size_of::<DataEndpoint>())
2101 .saturating_add(state.advertised_reachable.len() * size_of::<DataEndpoint>())
2102 .saturating_add(state.reachable_network_variables.len() * size_of::<DataType>())
2103 .saturating_add(
2104 state
2105 .reachable_timesync_sources
2106 .iter()
2107 .map(|s| s.len())
2108 .sum::<usize>(),
2109 )
2110 .saturating_add(
2111 state
2112 .advertised_reachable_timesync_sources
2113 .iter()
2114 .map(|s| s.len())
2115 .sum::<usize>(),
2116 )
2117 .saturating_add(
2118 state
2119 .topology_boards
2120 .iter()
2121 .map(Self::topology_board_byte_cost)
2122 .sum::<usize>(),
2123 )
2124 .saturating_add(size_of::<DiscoverySenderState>())
2125 }
2126
2127 #[cfg(feature = "discovery")]
2128 fn discovery_route_byte_cost(side: RouterSideId, route: &DiscoverySideState) -> usize {
2129 size_of::<RouterSideId>()
2130 .saturating_add(size_of::<DiscoverySideState>())
2131 .saturating_add(route.reachable.len() * size_of::<DataEndpoint>())
2132 .saturating_add(route.reachable_network_variables.len() * size_of::<DataType>())
2133 .saturating_add(
2134 route.requested_network_variables.len()
2135 * (size_of::<DataType>() + size_of::<u64>()),
2136 )
2137 .saturating_add(
2138 route
2139 .reachable_timesync_sources
2140 .iter()
2141 .map(|s| s.len())
2142 .sum::<usize>(),
2143 )
2144 .saturating_add(
2145 route
2146 .announcers
2147 .iter()
2148 .map(|(sender, state)| Self::discovery_sender_byte_cost(sender, state))
2149 .sum::<usize>(),
2150 )
2151 .saturating_add(side.saturating_sub(side))
2152 }
2153
2154 #[cfg(feature = "discovery")]
2155 fn discovery_bytes_used(&self) -> usize {
2156 self.discovery_routes
2157 .iter()
2158 .map(|(side, route)| Self::discovery_route_byte_cost(*side, route))
2159 .sum()
2160 }
2161
2162 #[inline]
2163 fn reliable_rx_buffered_bytes(&self) -> usize {
2164 self.reliable_rx
2165 .values()
2166 .flat_map(|state| state.buffered.values())
2167 .map(|bytes| size_of::<crate::SharedBytes>() + bytes.len())
2168 .sum()
2169 }
2170
2171 fn reliable_rx_buffer_len(&self) -> usize {
2172 self.reliable_rx
2173 .values()
2174 .map(|state| state.buffered.len())
2175 .sum()
2176 }
2177
2178 #[inline]
2179 fn shared_queue_bytes_used(&self) -> usize {
2180 self.received_queue
2181 .bytes_used()
2182 .saturating_add(self.transmit_queue.bytes_used())
2183 .saturating_add(self.recent_rx.max_bytes())
2184 .saturating_add(self.reliable_rx_buffered_bytes())
2185 .saturating_add(crate::config::schema_bytes_used())
2186 .saturating_add({
2187 #[cfg(feature = "discovery")]
2188 {
2189 self.discovery_bytes_used()
2190 }
2191 #[cfg(not(feature = "discovery"))]
2192 {
2193 0
2194 }
2195 })
2196 }
2197
2198 fn pop_shared_queue_item(&mut self, preferred: RouterQueueKind) -> bool {
2199 match preferred {
2200 RouterQueueKind::Received => self.received_queue.pop_lowest_priority().is_some(),
2205 RouterQueueKind::Transmit => self.transmit_queue.pop_lowest_priority().is_some(),
2206 }
2207 }
2208
2209 fn lower_priority_shared_queue(&self, incoming_priority: u8) -> Option<RouterQueueKind> {
2210 let received = self
2211 .received_queue
2212 .lowest_priority(|queued| queued.priority)
2213 .map(|priority| (RouterQueueKind::Received, priority));
2214 let transmit = self
2215 .transmit_queue
2216 .lowest_priority(|queued| queued.priority)
2217 .map(|priority| (RouterQueueKind::Transmit, priority));
2218 [received, transmit]
2219 .into_iter()
2220 .flatten()
2221 .filter(|(_, priority)| {
2222 *priority < incoming_priority
2223 || (incoming_priority != 254 && *priority == incoming_priority)
2224 })
2225 .min_by_key(|(kind, priority)| {
2226 (
2227 *priority,
2228 if *kind == RouterQueueKind::Received {
2229 0
2230 } else {
2231 1
2232 },
2233 )
2234 })
2235 .map(|(kind, _)| kind)
2236 }
2237
2238 fn make_shared_queue_room(
2239 &mut self,
2240 incoming_cost: usize,
2241 incoming_priority: u8,
2242 ) -> TelemetryResult<()> {
2243 if incoming_cost > self.memory.max_queue_budget {
2244 return Err(TelemetryError::PacketTooLarge(
2245 "Item exceeds maximum shared queue budget",
2246 ));
2247 }
2248
2249 while self.shared_queue_bytes_used().saturating_add(incoming_cost)
2250 > self.memory.max_queue_budget
2251 {
2252 let Some(victim) = self.lower_priority_shared_queue(incoming_priority) else {
2253 return Err(TelemetryError::Io("shared priority queue saturated"));
2254 };
2255 if !self.pop_shared_queue_item(victim) {
2256 return Err(TelemetryError::PacketTooLarge(
2257 "Item exceeds maximum shared queue budget",
2258 ));
2259 }
2260 }
2261
2262 Ok(())
2263 }
2264
2265 #[cfg(feature = "discovery")]
2266 fn schema_growth_budget(&self) -> usize {
2267 let protected = self
2270 .recent_rx
2271 .max_bytes()
2272 .saturating_add(self.discovery_bytes_used())
2273 .saturating_add(self.reliable_rx_buffered_bytes());
2274 self.memory.max_queue_budget.saturating_sub(protected)
2275 }
2276
2277 #[cfg(feature = "discovery")]
2278 fn commit_discovery_route(
2279 &mut self,
2280 side: RouterSideId,
2281 route: DiscoverySideState,
2282 ) -> TelemetryResult<()> {
2283 let previous = self
2284 .discovery_routes
2285 .get(&side)
2286 .map_or(0, |route| Self::discovery_route_byte_cost(side, route));
2287 let incoming = Self::discovery_route_byte_cost(side, &route);
2288 let growth = incoming.saturating_sub(previous);
2289 self.make_shared_queue_room(growth, 255)?;
2290 self.discovery_routes.insert(side, route);
2291 Ok(())
2292 }
2293
2294 #[cfg(feature = "discovery")]
2295 fn fit_discovery_budget(&mut self) {
2296 while self.shared_queue_bytes_used() > self.memory.max_queue_budget {
2297 if let Some(victim) = self.lower_priority_shared_queue(255)
2300 && self.pop_shared_queue_item(victim)
2301 {
2302 continue;
2303 }
2304 break;
2307 }
2308 }
2309
2310 fn push_received(&mut self, item: RouterRxItem) -> TelemetryResult<()> {
2311 self.make_shared_queue_room(item.byte_cost(), item.priority)?;
2312 self.received_queue
2313 .push_back_prioritized(item, |queued| queued.priority)
2314 }
2315
2316 fn push_transmit(&mut self, item: TxQueued) -> TelemetryResult<()> {
2317 #[cfg(feature = "discovery")]
2318 if let RouterTxItem::ToSide { dst, data, .. } = &item.item {
2319 if let RouterItem::Packet(incoming) = data
2322 && self.transmit_queue.iter().any(|queued| {
2323 matches!(&queued.item,
2324 RouterTxItem::ToSide { dst: queued_dst, data: RouterItem::Packet(existing), .. }
2325 if queued_dst == dst && discovery::same_schema_snapshot(existing, incoming))
2326 })
2327 {
2328 return Ok(());
2329 }
2330 let incoming = match data {
2331 RouterItem::Packet(pkt) if discovery::is_discovery_type(pkt.data_type()) => {
2332 Some((pkt.data_type(), pkt.sender().to_owned()))
2333 }
2334 RouterItem::Packet(_) | RouterItem::Packed(_) => None,
2335 };
2336 if let Some((incoming_ty, incoming_sender)) = incoming {
2337 self.transmit_queue.retain(|queued| {
2338 let RouterTxItem::ToSide {
2339 dst: queued_dst,
2340 data: queued_data,
2341 ..
2342 } = &queued.item
2343 else {
2344 return true;
2345 };
2346 let same_snapshot = match queued_data {
2347 RouterItem::Packet(pkt) => {
2348 pkt.data_type() == incoming_ty && pkt.sender() == incoming_sender
2349 }
2350 RouterItem::Packed(_) => false,
2351 };
2352 *queued_dst != *dst || !same_snapshot
2353 });
2354 }
2355 }
2356 self.make_shared_queue_room(item.byte_cost(), item.priority)?;
2357 self.transmit_queue
2358 .push_back_prioritized(item, |queued| queued.priority)
2359 }
2360
2361 fn push_recent_rx(&mut self, id: u64) -> TelemetryResult<()> {
2362 while self.recent_rx.len() >= self.memory.max_recent_rx_ids {
2363 let _ = self.recent_rx.pop_front();
2364 }
2365 self.make_shared_queue_room(0, 0)?;
2366 self.recent_rx.push_back(id)
2367 }
2368
2369 #[allow(unused_mut)]
2370 fn buffer_reliable_rx(
2371 &mut self,
2372 side: RouterSideId,
2373 ty: DataType,
2374 seq: u32,
2375 mut bytes: crate::SharedBytes,
2376 ) -> TelemetryResult<()> {
2377 let key = Router::reliable_key(side, ty);
2378 if self
2379 .reliable_rx
2380 .get(&key)
2381 .is_some_and(|state| state.buffered.contains_key(&seq))
2382 {
2383 return Ok(());
2384 }
2385 let cost = size_of::<crate::SharedBytes>() + bytes.len();
2386 self.make_shared_queue_room(cost, crate::transport_priority(ty))?;
2387 #[cfg(feature = "compact-packet-store")]
2388 bytes.park_for_queue()?;
2389 let rx_state = self
2390 .reliable_rx
2391 .entry(key)
2392 .or_insert_with(|| ReliableRxState {
2393 expected_seq: 1,
2394 buffered: BTreeMap::new(),
2395 });
2396 if rx_state.buffered.len() >= runtime_reliable_max_pending() {
2397 let _ = rx_state.buffered.pop_first();
2398 }
2399 rx_state.buffered.insert(seq, bytes);
2400 Ok(())
2401 }
2402}
2403
2404struct IsrRxQueue {
2409 busy: AtomicBool,
2410 q: UnsafeCell<BoundedDeque<RouterRxItem>>,
2411}
2412
2413unsafe impl Send for IsrRxQueue {}
2414unsafe impl Sync for IsrRxQueue {}
2415
2416struct IsrRxQueueGuard<'a> {
2417 owner: &'a IsrRxQueue,
2418}
2419
2420impl Deref for IsrRxQueueGuard<'_> {
2421 type Target = BoundedDeque<RouterRxItem>;
2422
2423 #[inline]
2424 fn deref(&self) -> &Self::Target {
2425 unsafe { &*self.owner.q.get() }
2426 }
2427}
2428
2429impl DerefMut for IsrRxQueueGuard<'_> {
2430 #[inline]
2431 fn deref_mut(&mut self) -> &mut Self::Target {
2432 unsafe { &mut *self.owner.q.get() }
2433 }
2434}
2435
2436impl Drop for IsrRxQueueGuard<'_> {
2437 #[inline]
2438 fn drop(&mut self) {
2439 self.owner.busy.store(false, Ordering::Release);
2440 }
2441}
2442
2443impl IsrRxQueue {
2444 #[inline]
2445 fn new(max_bytes: usize, starting_bytes: usize, grow_mult: f64) -> Self {
2446 Self {
2447 busy: AtomicBool::new(false),
2448 q: UnsafeCell::new(BoundedDeque::new(max_bytes, starting_bytes, grow_mult)),
2449 }
2450 }
2451
2452 #[inline]
2453 fn try_lock(&self) -> TelemetryResult<IsrRxQueueGuard<'_>> {
2454 match self
2455 .busy
2456 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
2457 {
2458 Ok(_) => Ok(IsrRxQueueGuard { owner: self }),
2459 Err(_) => Err(TelemetryError::Io("rx queue busy")),
2460 }
2461 }
2462
2463 #[allow(dead_code)]
2464 #[inline]
2465 fn push_back(&self, item: RouterRxItem) -> TelemetryResult<()> {
2466 let mut g = self.try_lock()?;
2467 g.push_back(item)
2468 }
2469
2470 #[inline]
2471 fn push_back_prioritized(&self, item: RouterRxItem) -> TelemetryResult<()> {
2472 let mut g = self.try_lock()?;
2473 g.push_back_prioritized(item, |queued| queued.priority)
2474 }
2475
2476 #[inline]
2477 fn pop_front(&self) -> TelemetryResult<Option<RouterRxItem>> {
2478 let mut g = self.try_lock()?;
2479 Ok(g.pop_front())
2480 }
2481
2482 #[inline]
2483 fn clear(&self) -> TelemetryResult<()> {
2484 let mut g = self.try_lock()?;
2485 g.clear();
2486 Ok(())
2487 }
2488
2489 #[inline]
2490 fn snapshot(&self) -> Option<(usize, usize)> {
2491 let g = self.try_lock().ok()?;
2492 Some((g.len(), g.bytes_used()))
2493 }
2494}
2495
2496pub struct Router {
2500 sender: RouterMutex<Arc<str>>,
2501 cfg: RouterConfig,
2502 state: RouterMutex<RouterInner>,
2503 isr_rx_queue: IsrRxQueue,
2504 side_tx_gate: ReentryGate,
2505 clock: Box<dyn Clock + Send + Sync>,
2506 #[cfg(feature = "timesync")]
2507 timesync: RouterMutex<TimeSyncRuntime>,
2508}
2509
2510#[cfg(feature = "timesync")]
2511#[derive(Debug, Clone)]
2512struct PendingTimeSyncRequest {
2513 seq: u64,
2514 t1_mono_ms: u64,
2515 source: String,
2516}
2517
2518#[cfg(feature = "timesync")]
2519#[derive(Debug, Clone)]
2520struct RemoteTimeSyncSource {
2521 priority: u64,
2522 last_sample_mono_ms: u64,
2523 sample_unix_ms: u64,
2524}
2525
2526#[cfg(feature = "timesync")]
2527#[derive(Debug, Clone)]
2528struct TimeSyncRuntime {
2529 cfg: Option<TimeSyncConfig>,
2530 tracker: Option<TimeSyncTracker>,
2531 clock: NetworkClock,
2532 disciplined_clock: SlewedNetworkClock,
2533 remote_sources: BTreeMap<String, RemoteTimeSyncSource>,
2534 next_seq: u64,
2535 next_announce_mono_ms: u64,
2536 next_request_mono_ms: u64,
2537 pending_request: Option<PendingTimeSyncRequest>,
2538}
2539
2540#[cfg(feature = "timesync")]
2541impl TimeSyncRuntime {
2542 fn new(cfg: Option<TimeSyncConfig>) -> Self {
2543 Self {
2544 tracker: cfg.map(TimeSyncTracker::new),
2545 cfg,
2546 clock: NetworkClock::default(),
2547 disciplined_clock: SlewedNetworkClock::new(
2548 cfg.map(|c| c.max_slew_ppm)
2549 .unwrap_or(TimeSyncConfig::default().max_slew_ppm),
2550 ),
2551 remote_sources: BTreeMap::new(),
2552 next_seq: 1,
2553 next_announce_mono_ms: 0,
2554 next_request_mono_ms: 0,
2555 pending_request: None,
2556 }
2557 }
2558}
2559
2560enum RemoteSidePlan {
2561 Target(Vec<RouterSideId>),
2562}
2563
2564#[cfg(feature = "discovery")]
2565#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
2566struct DiscoveryCandidateMatch {
2567 side: RouterSideId,
2568 overlap: usize,
2569}
2570
2571#[cfg(feature = "discovery")]
2572#[inline]
2573const fn discovery_is_hop_local_advertisement(ty: DataType) -> bool {
2574 matches!(
2575 ty,
2576 DataType::DiscoveryAnnounce
2577 | DataType::DiscoveryTimeSyncSources
2578 | DataType::DiscoveryTopology
2579 | DataType::DiscoveryAddress
2580 | DataType::DiscoveryLinkCapabilities
2581 )
2582}
2583
2584#[cfg(not(feature = "discovery"))]
2585const fn discovery_is_hop_local_advertisement(_ty: DataType) -> bool {
2586 false
2587}
2588
2589impl Debug for Router {
2590 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2591 let sender = self.sender();
2592 f.debug_struct("Router")
2593 .field("sender", &sender)
2594 .field("cfg", &self.cfg)
2595 .field("state", &"<mutex>")
2596 .field("clock", &"Clock")
2597 .finish()
2598 }
2599}
2600
2601#[inline]
2604fn has_nonlocal_endpoint(eps: &[DataEndpoint], cfg: &RouterConfig) -> bool {
2605 eps.iter().copied().any(|ep| !cfg.is_local_endpoint(ep))
2606}
2607
2608#[inline]
2609fn force_remote_for_type(ty: DataType) -> bool {
2610 matches!(
2611 ty,
2612 DataType::ReliableAck
2613 | DataType::ReliablePartialAck
2614 | DataType::ReliablePacketRequest
2615 | DataType::P2pMessage
2616 ) || {
2617 #[cfg(feature = "timesync")]
2618 {
2619 matches!(
2620 ty,
2621 DataType::TimeSyncAnnounce | DataType::TimeSyncRequest | DataType::TimeSyncResponse
2622 )
2623 }
2624 #[cfg(not(feature = "timesync"))]
2625 {
2626 false
2627 }
2628 }
2629}
2630
2631#[inline]
2632fn is_internal_control_type(ty: DataType) -> bool {
2633 if matches!(
2634 ty,
2635 DataType::ReliableAck
2636 | DataType::ReliablePartialAck
2637 | DataType::ReliablePacketRequest
2638 | DataType::P2pMessage
2639 ) {
2640 return true;
2641 }
2642
2643 #[cfg(feature = "timesync")]
2644 if matches!(
2645 ty,
2646 DataType::TimeSyncAnnounce | DataType::TimeSyncRequest | DataType::TimeSyncResponse
2647 ) {
2648 return true;
2649 }
2650
2651 #[cfg(feature = "discovery")]
2652 if discovery::is_discovery_type(ty) {
2653 return true;
2654 }
2655
2656 let _ = ty;
2657 false
2658}
2659
2660fn with_retries<F>(
2662 this: &Router,
2663 dest: DataEndpoint,
2664 data: &RouterItem,
2665 pkt_for_ctx: Option<&Packet>,
2666 env_for_ctx: Option<&wire_format::TelemetryEnvelope>,
2667 called_from_queue: bool,
2668 run: F,
2669) -> TelemetryResult<()>
2670where
2671 F: Fn() -> TelemetryResult<()>,
2672{
2673 match this.retry_with_attempts(runtime_max_handler_retries(), run) {
2674 Ok(((), attempts)) => {
2675 if attempts > 1 {
2676 let mut st = this.state.lock();
2677 st.total_handler_retries = st
2678 .total_handler_retries
2679 .saturating_add(attempts.saturating_sub(1) as u64);
2680 }
2681 Ok(())
2682 }
2683 Err((e, attempts)) => {
2684 {
2685 let mut st = this.state.lock();
2686 st.total_handler_failures = st.total_handler_failures.saturating_add(1);
2687 st.total_handler_retries = st.total_handler_retries.saturating_add(attempts as u64);
2688 }
2689
2690 this.remove_pkt_id(data);
2692
2693 if let Some(pkt) = pkt_for_ctx {
2695 let _ = this.handle_callback_error(pkt, Some(dest), e, called_from_queue);
2696 } else if let Some(env) = env_for_ctx {
2697 let _ = this.handle_callback_error_from_env(env, Some(dest), e, called_from_queue);
2698 }
2699
2700 Err(TelemetryError::HandlerError("local handler failed"))
2701 }
2702 }
2703}
2704impl Router {
2706 const END_TO_END_ACK_SENDER: &'static str = "E2EACK";
2707 const END_TO_END_ACK_PREFIX: &'static str = "E2EACK:";
2708
2709 #[inline]
2710 fn side_ref(st: &RouterInner, side: RouterSideId) -> TelemetryResult<&RouterSide> {
2711 st.sides
2712 .get(side)
2713 .and_then(|side| side.as_ref())
2714 .ok_or(TelemetryError::HandlerError("router: invalid side id"))
2715 }
2716
2717 fn note_side_tx_success(
2718 &self,
2719 side: RouterSideId,
2720 ty: DataType,
2721 bytes: usize,
2722 relayed: bool,
2723 attempts: usize,
2724 ) {
2725 let mut st = self.state.lock();
2726 #[cfg(feature = "discovery")]
2727 if ty == crate::DataType::DiscoveryAnnounce
2728 && !relayed
2729 && let Some(throttle) = st.discovery_side_throttle.get_mut(&side)
2730 {
2731 throttle.last_liveness_tx_ms = self.clock.now_ms();
2732 }
2733 let entry = st.side_runtime_stats.entry(side).or_default();
2734 entry.note_tx(ty, bytes, relayed, attempts.saturating_sub(1));
2735 }
2736
2737 fn note_side_tx_failure(&self, side: RouterSideId, ty: DataType, attempts: usize) {
2738 let mut st = self.state.lock();
2739 st.total_handler_failures = st.total_handler_failures.saturating_add(1);
2740 st.total_handler_retries = st.total_handler_retries.saturating_add(attempts as u64);
2741 let entry = st.side_runtime_stats.entry(side).or_default();
2742 entry.note_tx_failure(ty, attempts);
2743 }
2744
2745 fn note_side_rx(&self, side: RouterSideId, ty: DataType, bytes: usize, relayed: bool) {
2746 let mut st = self.state.lock();
2747 let entry = st.side_runtime_stats.entry(side).or_default();
2748 entry.note_rx(ty, bytes, relayed);
2749 }
2750
2751 fn note_side_local_delivery(&self, side: RouterSideId, ty: DataType) {
2752 let mut st = self.state.lock();
2753 let entry = st.side_runtime_stats.entry(side).or_default();
2754 entry.note_local_delivery(ty);
2755 }
2756
2757 fn note_side_local_handler_failure(&self, side: RouterSideId, ty: DataType, retries: usize) {
2758 let mut st = self.state.lock();
2759 let entry = st.side_runtime_stats.entry(side).or_default();
2760 entry.note_local_handler_failure(ty, retries);
2761 }
2762
2763 fn cache_managed_variable_packet(
2764 &self,
2765 pkt: &Packet,
2766 notify_handlers: bool,
2767 ) -> TelemetryResult<()> {
2768 let sender_address = pkt
2769 .sender()
2770 .strip_prefix("@addr:")
2771 .and_then(|value| value.parse::<u32>().ok())
2772 .unwrap_or_else(|| sender_address_u32(pkt.sender()));
2773 let handlers = {
2774 let mut st = self.state.lock();
2775 if !st
2776 .managed_variable_types
2777 .contains(&pkt.data_type().as_u32())
2778 {
2779 return Ok(());
2780 }
2781 let existing = st.managed_variable_latest.get(&pkt.data_type().as_u32());
2782 if existing.is_some_and(|entry| {
2789 let current = &entry.packet;
2790 sender_address == entry.sender_address && pkt.timestamp() < current.timestamp()
2791 }) {
2792 return Ok(());
2793 }
2794 let changed = existing.is_none_or(|entry| entry.packet != *pkt);
2795 st.managed_variable_latest.insert(
2796 pkt.data_type().as_u32(),
2797 ManagedVariableCacheEntry {
2798 packet: pkt.clone(),
2799 sender_address,
2800 cached_at_ms: self.clock.now_ms(),
2801 },
2802 );
2803 if notify_handlers && changed {
2804 st.network_variable_update_handlers
2805 .get(&pkt.data_type().as_u32())
2806 .cloned()
2807 .unwrap_or_default()
2808 } else {
2809 Vec::new()
2810 }
2811 };
2812 for handler in handlers {
2813 (handler.handler)(pkt)?;
2814 }
2815 Ok(())
2816 }
2817
2818 fn remember_managed_variable_packet(&self, pkt: &Packet) -> TelemetryResult<()> {
2819 self.cache_managed_variable_packet(pkt, true)
2820 }
2821
2822 fn managed_variable_latest(&self, ty: DataType) -> Option<Packet> {
2823 let st = self.state.lock();
2824 st.managed_variable_latest
2825 .get(&ty.as_u32())
2826 .map(|entry| entry.packet.clone())
2827 }
2828
2829 fn managed_variable_latest_with_age(&self, ty: DataType) -> Option<(Packet, u64)> {
2830 let now_ms = self.clock.now_ms();
2831 let st = self.state.lock();
2832 st.managed_variable_latest.get(&ty.as_u32()).map(|entry| {
2833 (
2834 entry.packet.clone(),
2835 now_ms.saturating_sub(entry.cached_at_ms),
2836 )
2837 })
2838 }
2839
2840 fn is_managed_variable_type(&self, ty: DataType) -> bool {
2841 let st = self.state.lock();
2842 st.managed_variable_types.contains(&ty.as_u32())
2843 }
2844
2845 fn managed_variable_permissions_locked(
2846 st: &RouterInner,
2847 ty: DataType,
2848 ) -> NetworkVariablePermissions {
2849 st.managed_variable_permissions
2850 .get(&ty.as_u32())
2851 .copied()
2852 .unwrap_or(NetworkVariablePermissions::READ_WRITE)
2853 }
2854
2855 fn can_read_managed_variable(&self, ty: DataType) -> bool {
2856 let st = self.state.lock();
2857 Self::managed_variable_permissions_locked(&st, ty).read
2858 }
2859
2860 fn can_write_managed_variable(&self, ty: DataType) -> bool {
2861 let st = self.state.lock();
2862 Self::managed_variable_permissions_locked(&st, ty).write
2863 }
2864
2865 #[inline]
2866 fn ensure_side_ingress_enabled(&self, side: RouterSideId) -> TelemetryResult<()> {
2867 let st = self.state.lock();
2868 let side_ref = Self::side_ref(&st, side)?;
2869 if side_ref.opts.ingress_enabled {
2870 Ok(())
2871 } else {
2872 Err(TelemetryError::HandlerError(
2873 "router: ingress disabled for side id",
2874 ))
2875 }
2876 }
2877
2878 #[inline]
2879 fn default_route_enabled(&self, src: Option<RouterSideId>, dst: RouterSideId) -> bool {
2880 src != Some(dst)
2881 }
2882
2883 #[inline]
2884 fn route_allowed_locked(
2885 &self,
2886 st: &RouterInner,
2887 src: Option<RouterSideId>,
2888 ty: Option<DataType>,
2889 dst: RouterSideId,
2890 ) -> bool {
2891 let Some(dst_side) = st.sides.get(dst).and_then(|side| side.as_ref()) else {
2892 return false;
2893 };
2894 if !dst_side.opts.egress_enabled {
2895 return false;
2896 }
2897 if let Some(src_id) = src {
2898 let Some(src_side) = st.sides.get(src_id).and_then(|side| side.as_ref()) else {
2899 return false;
2900 };
2901 if !src_side.opts.ingress_enabled {
2902 return false;
2903 }
2904 }
2905 let base_allowed = st
2906 .route_overrides
2907 .get(&(src, dst))
2908 .copied()
2909 .unwrap_or_else(|| self.default_route_enabled(src, dst));
2910 if !base_allowed {
2911 return false;
2912 }
2913
2914 let Some(ty) = ty else {
2915 return true;
2916 };
2917 if st
2918 .typed_route_overrides
2919 .keys()
2920 .any(|(typed_src, typed_ty, _)| *typed_src == src && *typed_ty == ty.as_u32())
2921 {
2922 return st
2923 .typed_route_overrides
2924 .get(&(src, ty.as_u32(), dst))
2925 .copied()
2926 .unwrap_or(false);
2927 }
2928 true
2929 }
2930
2931 fn has_typed_route_overrides_locked(
2932 st: &RouterInner,
2933 src: Option<RouterSideId>,
2934 ty: DataType,
2935 ) -> bool {
2936 st.typed_route_overrides
2937 .keys()
2938 .any(|(typed_src, typed_ty, _)| *typed_src == src && *typed_ty == ty.as_u32())
2939 }
2940
2941 #[cfg(feature = "discovery")]
2942 fn endpoint_overlap_count<I>(reachable: I, eps: &[DataEndpoint]) -> usize
2943 where
2944 I: IntoIterator<Item = DataEndpoint>,
2945 {
2946 let mut overlap = 0usize;
2947 for ep in reachable {
2948 if eps.contains(&ep) {
2949 overlap = overlap.saturating_add(1);
2950 }
2951 }
2952 overlap
2953 }
2954
2955 #[inline]
2956 fn preferred_scoring_endpoints<'a>(
2957 &self,
2958 eps: &'a [DataEndpoint],
2959 prefer_nonlocal: bool,
2960 ) -> alloc::borrow::Cow<'a, [DataEndpoint]> {
2961 if !prefer_nonlocal || eps.iter().all(|&ep| !self.cfg.is_local_endpoint(ep)) {
2962 return alloc::borrow::Cow::Borrowed(eps);
2963 }
2964 let nonlocal: Vec<DataEndpoint> = eps
2965 .iter()
2966 .copied()
2967 .filter(|&ep| !self.cfg.is_local_endpoint(ep))
2968 .collect();
2969 if nonlocal.is_empty() {
2970 alloc::borrow::Cow::Borrowed(eps)
2971 } else {
2972 alloc::borrow::Cow::Owned(nonlocal)
2973 }
2974 }
2975
2976 fn pop_next_queued_control_rx_item(&self) -> TelemetryResult<Option<RouterRxItem>> {
2977 {
2978 let mut isr_rx = self.isr_rx_queue.try_lock()?;
2979 let idx = isr_rx.iter().position(Self::queued_rx_item_is_control);
2980 if let Some(idx) = idx {
2981 return Ok(isr_rx.remove_pos(idx));
2982 }
2983 }
2984
2985 let mut st = self.state.lock();
2986 let idx = st
2987 .received_queue
2988 .iter()
2989 .position(Self::queued_rx_item_is_control);
2990 if let Some(idx) = idx {
2991 return Ok(st.received_queue.remove_pos(idx));
2992 }
2993 Ok(None)
2994 }
2995
2996 fn queued_rx_item_is_control(item: &RouterRxItem) -> bool {
2997 let ty = match &item.data {
2998 RouterItem::Packet(pkt) => Some(pkt.data_type()),
2999 RouterItem::Packed(bytes) => wire_format::scheduling_type(bytes.as_ref()),
3000 };
3001 match ty {
3002 Some(
3003 DataType::ReliableAck
3004 | DataType::ReliablePartialAck
3005 | DataType::ReliablePacketRequest,
3006 ) => true,
3007 #[cfg(feature = "discovery")]
3008 Some(ty) => discovery::is_discovery_type(ty),
3009 _ => false,
3010 }
3011 }
3012
3013 fn drain_queued_control_rx_before_tx(
3014 &self,
3015 start: u64,
3016 timeout_ms: u32,
3017 ) -> TelemetryResult<bool> {
3018 let mut did_any = false;
3019 while let Some(item) = self.pop_next_queued_control_rx_item()? {
3020 self.process_rx_queue_item(item)?;
3021 did_any = true;
3022 if timeout_ms != 0 && self.clock.now_ms().wrapping_sub(start) >= u64::from(timeout_ms) {
3025 break;
3026 }
3027 }
3028 Ok(did_any)
3029 }
3030
3031 fn eligible_side_ids_locked(
3032 &self,
3033 st: &RouterInner,
3034 src: Option<RouterSideId>,
3035 ty: Option<DataType>,
3036 restrict_link_local: bool,
3037 ) -> Vec<RouterSideId> {
3038 st.sides
3039 .iter()
3040 .enumerate()
3041 .filter_map(|(side_id, side)| {
3042 let side = side.as_ref()?;
3043 if restrict_link_local && !side.opts.link_local_enabled {
3044 return None;
3045 }
3046 if self.route_allowed_locked(st, src, ty, side_id) {
3047 Some(side_id)
3048 } else {
3049 None
3050 }
3051 })
3052 .collect()
3053 }
3054
3055 fn apply_route_selection_locked(
3056 &self,
3057 st: &mut RouterInner,
3058 src: Option<RouterSideId>,
3059 mut sides: Vec<RouterSideId>,
3060 origin: RouteSelectionOrigin,
3061 ) -> Vec<RouterSideId> {
3062 if sides.len() <= 1 {
3063 return sides;
3064 }
3065
3066 let selection_mode = st.source_route_modes.get(&src).copied();
3067 if selection_mode.is_none() && origin == RouteSelectionOrigin::Discovered {
3068 return self.apply_adaptive_discovery_selection_locked(st, src, sides);
3069 }
3070
3071 match selection_mode.unwrap_or(RouteSelectionMode::Fanout) {
3072 RouteSelectionMode::Fanout => sides,
3073 RouteSelectionMode::Weighted => {
3074 crate::small_sort::sort(&mut sides);
3075 let total_weight = sides.iter().fold(0_u64, |acc, side| {
3076 acc + u64::from(st.route_weights.get(&(src, *side)).copied().unwrap_or(1))
3077 });
3078 if total_weight == 0 {
3079 return Vec::new();
3080 }
3081 let cursor = st.route_selection_cursors.entry(src).or_insert(0);
3082 let pick = *cursor % total_weight;
3083 *cursor = cursor.wrapping_add(1);
3084 let mut remaining = pick;
3085 for side in sides {
3086 let weight =
3087 u64::from(st.route_weights.get(&(src, side)).copied().unwrap_or(1));
3088 if remaining < weight {
3089 return vec![side];
3090 }
3091 remaining -= weight;
3092 }
3093 Vec::new()
3094 }
3095 RouteSelectionMode::Failover => {
3096 sides.sort_by_key(|side| {
3097 (
3098 st.route_priorities.get(&(src, *side)).copied().unwrap_or(0),
3099 *side,
3100 )
3101 });
3102 sides.truncate(1);
3103 sides
3104 }
3105 }
3106 }
3107
3108 fn apply_adaptive_discovery_selection_locked(
3109 &self,
3110 st: &mut RouterInner,
3111 src: Option<RouterSideId>,
3112 mut sides: Vec<RouterSideId>,
3113 ) -> Vec<RouterSideId> {
3114 crate::small_sort::sort(&mut sides);
3115 let mut unmeasured: Vec<_> = sides
3116 .iter()
3117 .copied()
3118 .filter(|side| !st.adaptive_route_stats.contains_key(side))
3119 .collect();
3120 if !unmeasured.is_empty() {
3121 let cursor = st.route_selection_cursors.entry(src).or_insert(0);
3122 let pick = (*cursor as usize) % unmeasured.len();
3123 *cursor = cursor.wrapping_add(1);
3124 return vec![unmeasured.swap_remove(pick)];
3125 }
3126
3127 let now_ms = self.clock.now_ms();
3128 let total_weight = sides.iter().fold(0_u64, |acc, side| {
3129 acc + st
3130 .adaptive_route_stats
3131 .get(side)
3132 .map(|stats| stats.weight(now_ms))
3133 .unwrap_or(1)
3134 });
3135 if total_weight == 0 {
3136 sides.truncate(1);
3137 return sides;
3138 }
3139
3140 let cursor = st.route_selection_cursors.entry(src).or_insert(0);
3141 let pick = *cursor % total_weight;
3142 *cursor = cursor.wrapping_add(1);
3143 let mut remaining = pick;
3144 for side in sides {
3145 let weight = st
3146 .adaptive_route_stats
3147 .get(&side)
3148 .map(|stats| stats.weight(now_ms))
3149 .unwrap_or(1);
3150 if remaining < weight {
3151 return vec![side];
3152 }
3153 remaining -= weight;
3154 }
3155 Vec::new()
3156 }
3157
3158 fn record_side_tx_sample(
3159 &self,
3160 side: RouterSideId,
3161 bytes: usize,
3162 started_ms: u64,
3163 ended_ms: u64,
3164 ) {
3165 let sample_ms = ended_ms.saturating_sub(started_ms).max(1);
3166 let sample_bps = ((bytes as u128).saturating_mul(1000) / u128::from(sample_ms))
3167 .min(u128::from(u64::MAX)) as u64;
3168 let mut st = self.state.lock();
3169 st.adaptive_route_stats
3170 .entry(side)
3171 .or_default()
3172 .observe(bytes, sample_bps, ended_ms);
3173 }
3174
3175 pub fn note_side_link_probe_sample(
3180 &self,
3181 side: RouterSideId,
3182 bytes: usize,
3183 duration_ms: u64,
3184 ) -> TelemetryResult<()> {
3185 {
3186 let st = self.state.lock();
3187 let _ = Self::side_ref(&st, side).map_err(|_| TelemetryError::BadArg)?;
3188 }
3189 let ended_ms = self.clock.now_ms();
3190 self.record_side_tx_sample(side, bytes, ended_ms.saturating_sub(duration_ms), ended_ms);
3191 Ok(())
3192 }
3193
3194 fn router_item_wire_len(data: &RouterItem) -> TelemetryResult<usize> {
3195 match data {
3196 RouterItem::Packet(pkt) => Ok(wire_format::packet_wire_size(pkt)),
3197 RouterItem::Packed(bytes) => Ok(bytes.len()),
3198 }
3199 }
3200
3201 #[inline]
3211 fn reliable_control_target_packet_id(data: &RouterItem) -> TelemetryResult<Option<u64>> {
3212 match data {
3213 RouterItem::Packet(pkt) => {
3214 if !Self::is_end_to_end_ack_packet(pkt) {
3215 return Ok(None);
3216 }
3217 Self::decode_end_to_end_reliable_ack(pkt.payload()).map(Some)
3218 }
3219 RouterItem::Packed(bytes) => {
3220 if wire_format::peek_routing_frame_info(bytes.as_ref())
3221 .ok()
3222 .is_some_and(|frame| {
3223 frame.envelope.ty != DataType::ReliableAck || frame.ack_only()
3224 })
3225 {
3226 return Ok(None);
3227 }
3228 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
3229 if !Self::is_end_to_end_ack_packet(&pkt) {
3230 return Ok(None);
3231 }
3232 Self::decode_end_to_end_reliable_ack(pkt.payload()).map(Some)
3233 }
3234 }
3235 }
3236
3237 fn decode_end_to_end_reliable_ack(payload: &[u8]) -> TelemetryResult<u64> {
3238 if payload.len() != 8 {
3239 return Err(TelemetryError::Unpack("bad reliable e2e ack payload"));
3240 }
3241 Ok(u64::from_le_bytes(payload[0..8].try_into().unwrap()))
3242 }
3243
3244 #[inline]
3245 fn sender_hash(sender: &str) -> u64 {
3246 if let Some(address) = sender
3247 .strip_prefix("@addr:")
3248 .and_then(|v| v.parse::<u32>().ok())
3249 {
3250 return u64::from(address);
3251 }
3252 hash_bytes_u64(0x517C_C1B7_2722_0A95, sender.as_bytes())
3253 }
3254
3255 #[inline]
3256 fn fallback_address_for_hostname(hostname: &str) -> NodeAddress {
3257 let hash = Self::sender_hash(hostname);
3258 let mut address = (hash as u32) ^ ((hash >> 32) as u32);
3259 if address == 0 {
3260 address = 1;
3261 }
3262 address
3263 }
3264
3265 fn canonical_sender_locked(st: &RouterInner, sender: &str) -> String {
3270 let Some(address) = sender
3271 .strip_prefix("@addr:")
3272 .and_then(|value| value.parse::<u32>().ok())
3273 else {
3274 return sender.to_string();
3275 };
3276 Self::hostname_for_address_locked(st, address).unwrap_or_else(|| sender.to_string())
3277 }
3278
3279 fn hostname_for_address_locked(st: &RouterInner, address: NodeAddress) -> Option<String> {
3286 if let Some(hostname) = st.address_by_value.get(&address) {
3287 return Some(hostname.clone());
3288 }
3289
3290 let mut found: Option<String> = None;
3291 let mut collision = false;
3292 let mut consider = |candidate: &str| {
3293 if sender_address_u32(candidate) != address {
3294 return;
3295 }
3296 match found.as_deref() {
3297 None => found = Some(candidate.to_string()),
3298 Some(existing) if existing == candidate => {}
3299 Some(_) => collision = true,
3303 }
3304 };
3305
3306 for entry in st.address_book.values() {
3307 consider(entry.hostname.as_ref());
3308 }
3309 #[cfg(feature = "discovery")]
3310 for route in st.discovery_routes.values() {
3311 for (announcer, state) in &route.announcers {
3312 consider(announcer);
3313 for board in &state.topology_boards {
3314 consider(&board.sender_id);
3315 for peer in &board.connections {
3321 consider(peer);
3322 }
3323 }
3324 }
3325 }
3326 if collision { None } else { found }
3327 }
3328
3329 fn address_mode_from_code(mode: u8, requested: NodeAddress) -> AddressAssignmentMode {
3330 match mode {
3331 2 => AddressAssignmentMode::Static(if requested == 0 { 1 } else { requested }),
3332 1 => AddressAssignmentMode::Requested(if requested == 0 { 1 } else { requested }),
3333 _ => AddressAssignmentMode::Dynamic,
3334 }
3335 }
3336
3337 fn address_winner_pref(entry: &AddressBookEntry) -> (u8, core::cmp::Reverse<u64>, u64) {
3338 let rank = match entry.mode {
3339 AddressAssignmentMode::Static(_) => 2,
3340 AddressAssignmentMode::Requested(_) => 1,
3341 AddressAssignmentMode::Dynamic => 0,
3342 };
3343 (
3344 rank,
3345 core::cmp::Reverse(entry.birth_ms),
3346 u64::MAX - entry.owner_hash,
3347 )
3348 }
3349
3350 fn identity_winner_pref(entry: &AddressBookEntry) -> (core::cmp::Reverse<u64>, u64) {
3351 (
3352 core::cmp::Reverse(entry.birth_ms),
3353 u64::MAX - entry.owner_hash,
3354 )
3355 }
3356
3357 fn allocate_free_address_locked(st: &RouterInner, seed: NodeAddress) -> NodeAddress {
3358 let mut candidate = if seed == 0 { 1 } else { seed };
3359 for _ in 0..u32::MAX {
3360 if candidate != 0 && !st.address_by_value.contains_key(&candidate) {
3361 return candidate;
3362 }
3363 candidate = candidate.wrapping_add(1);
3364 if candidate == 0 {
3365 candidate = 1;
3366 }
3367 }
3368 1
3369 }
3370
3371 fn unique_hostname_locked(st: &RouterInner, base: &str, owner_hash: u64) -> String {
3372 if !st.address_book.contains_key(base) {
3373 return base.to_string();
3374 }
3375 let stem = if base.is_empty() { "node" } else { base };
3376 let mut candidate = format!("{stem}-{owner_hash:08x}");
3377 let mut suffix = 1u32;
3378 while st.address_book.contains_key(&candidate) {
3379 candidate = format!("{stem}-{owner_hash:08x}-{suffix}");
3380 suffix = suffix.saturating_add(1);
3381 }
3382 candidate
3383 }
3384
3385 fn update_local_identity_locked(
3386 &self,
3387 st: &mut RouterInner,
3388 mut local: AddressBookEntry,
3389 reason: AddressChangeReason,
3390 ) -> AddressChange {
3391 let old = st.local_address.clone();
3392 st.address_book.remove(old.hostname.as_ref());
3393 st.address_by_value.remove(&old.address);
3394 local.last_seen_ms = self.clock.now_ms();
3395 st.address_by_value
3396 .insert(local.address, local.hostname.to_string());
3397 st.address_book
3398 .insert(local.hostname.to_string(), local.clone());
3399 st.local_address = local.clone();
3400 *self.sender.lock() = local.hostname.clone();
3401 #[cfg(feature = "discovery")]
3402 Self::note_discovery_topology_change_locked(st, self.clock.now_ms());
3403 AddressChange {
3404 old_hostname: old.hostname,
3405 new_hostname: local.hostname,
3406 old_address: old.address,
3407 new_address: local.address,
3408 reason,
3409 }
3410 }
3411
3412 fn notify_address_change(&self, change: AddressChange) -> TelemetryResult<()> {
3413 for handler in self.cfg.address_change_handlers.iter() {
3414 (handler.handler)(change.clone())?;
3415 }
3416 Ok(())
3417 }
3418
3419 #[cfg(feature = "discovery")]
3420 fn local_address_advertisement(
3421 &self,
3422 reachable_endpoints: Vec<DataEndpoint>,
3423 reachable_network_variables: Vec<DataType>,
3424 reachable_timesync_sources: Vec<String>,
3425 link_capabilities: discovery::LinkCapabilities,
3426 state: u8,
3427 ) -> discovery::AddressAdvertisement {
3428 let st = self.state.lock();
3429 discovery::AddressAdvertisement {
3430 hostname: st.local_address.hostname.to_string(),
3431 address: st.local_address.address,
3432 requested_address: st.local_address.requested_address,
3433 mode: st.local_address.mode.mode_code(),
3434 state,
3435 birth_ms: st.local_address.birth_ms,
3436 owner_hash: st.local_address.owner_hash,
3437 reachable_endpoints,
3438 reachable_network_variables,
3439 reachable_timesync_sources,
3440 link_capabilities,
3441 }
3442 }
3443
3444 #[cfg(feature = "discovery")]
3445 fn ingest_address_advertisement(
3446 &self,
3447 ad: discovery::AddressAdvertisement,
3448 ) -> TelemetryResult<bool> {
3449 let now_ms = self.clock.now_ms();
3450 let mut remote = AddressBookEntry {
3451 hostname: Arc::from(ad.hostname.as_str()),
3452 address: ad.address,
3453 requested_address: ad.requested_address,
3454 mode: Self::address_mode_from_code(ad.mode, ad.requested_address),
3455 birth_ms: ad.birth_ms,
3456 owner_hash: ad.owner_hash,
3457 last_seen_ms: now_ms,
3458 };
3459 let mut change = None;
3460 let mut changed = false;
3461 {
3462 let mut st = self.state.lock();
3463 let local = st.local_address.clone();
3464 let hostname_conflict =
3465 remote.hostname == local.hostname && remote.owner_hash != local.owner_hash;
3466 let address_conflict =
3467 remote.address == local.address && remote.owner_hash != local.owner_hash;
3468
3469 if hostname_conflict
3470 && Self::identity_winner_pref(&remote) > Self::identity_winner_pref(&local)
3471 {
3472 let mut next = local.clone();
3473 next.hostname = Arc::from(Self::unique_hostname_locked(
3474 &st,
3475 local.hostname.as_ref(),
3476 local.owner_hash,
3477 ));
3478 change = Some(self.update_local_identity_locked(
3479 &mut st,
3480 next,
3481 AddressChangeReason::HostnameConflict,
3482 ));
3483 changed = true;
3484 } else if address_conflict
3485 && Self::address_winner_pref(&remote) > Self::address_winner_pref(&local)
3486 {
3487 let mut next = local.clone();
3488 let seed = next
3489 .requested_address
3490 .max(Self::fallback_address_for_hostname(next.hostname.as_ref()));
3491 next.address = Self::allocate_free_address_locked(&st, seed);
3492 let reason = match next.mode {
3493 AddressAssignmentMode::Static(_) => AddressChangeReason::StaticConflict,
3494 AddressAssignmentMode::Requested(_) => AddressChangeReason::RequestedConflict,
3495 AddressAssignmentMode::Dynamic => AddressChangeReason::DynamicConflict,
3496 };
3497 change = Some(self.update_local_identity_locked(&mut st, next, reason));
3498 changed = true;
3499 } else if hostname_conflict {
3500 remote.hostname = Arc::from(Self::unique_hostname_locked(
3501 &st,
3502 remote.hostname.as_ref(),
3503 remote.owner_hash,
3504 ));
3505 changed = true;
3506 } else if address_conflict {
3507 let seed = remote
3508 .requested_address
3509 .max(Self::fallback_address_for_hostname(
3510 remote.hostname.as_ref(),
3511 ));
3512 remote.address = Self::allocate_free_address_locked(&st, seed);
3513 changed = true;
3514 }
3515
3516 st.address_book.remove(remote.hostname.as_ref());
3517 st.address_by_value.remove(&remote.address);
3518 st.address_by_value
3519 .insert(remote.address, remote.hostname.to_string());
3520 st.address_book
3521 .insert(remote.hostname.to_string(), remote.clone());
3522 }
3523 if let Some(change) = change {
3524 self.notify_address_change(change)?;
3525 }
3526 Ok(changed)
3527 }
3528
3529 #[inline]
3530 fn is_end_to_end_ack_sender(sender: &str) -> bool {
3531 sender == Self::END_TO_END_ACK_SENDER || sender.starts_with(Self::END_TO_END_ACK_PREFIX)
3532 }
3533
3534 fn decode_end_to_end_ack_sender_hash(sender: &str) -> Option<u64> {
3535 if let Some(ack_sender) = sender.strip_prefix(Self::END_TO_END_ACK_PREFIX)
3536 && !ack_sender.is_empty()
3537 {
3538 return Some(Self::sender_hash(ack_sender));
3539 }
3540 None
3541 }
3542
3543 fn end_to_end_ack_sender_hash(pkt: &Packet) -> Option<u64> {
3549 if pkt.data_type() != DataType::ReliableAck {
3550 return None;
3551 }
3552 pkt.wire_target_senders()
3553 .first()
3554 .copied()
3555 .or_else(|| Self::decode_end_to_end_ack_sender_hash(pkt.sender()))
3556 }
3557
3558 fn is_end_to_end_ack_packet(pkt: &Packet) -> bool {
3559 Self::end_to_end_ack_sender_hash(pkt).is_some()
3560 && Self::decode_end_to_end_reliable_ack(pkt.payload()).is_ok()
3561 }
3562
3563 fn encode_end_to_end_ack_sender(&self) -> String {
3564 let sender = self.sender_arc();
3565 format!("{}{}", Self::END_TO_END_ACK_PREFIX, sender)
3566 }
3567
3568 #[cfg(feature = "discovery")]
3569 fn is_end_to_end_destination_sender(sender: &str) -> bool {
3570 sender != "RELAY" && !Self::is_end_to_end_ack_sender(sender)
3571 }
3572
3573 fn encode_end_to_end_reliable_ack(packet_id: u64) -> crate::SharedBytes {
3574 let mut payload = Vec::with_capacity(8);
3575 payload.extend_from_slice(&packet_id.to_le_bytes());
3576 crate::shared_bytes::convert(Arc::<[u8]>::from(payload))
3577 }
3578
3579 fn encode_p2p_payload(
3580 src_hostname: &str,
3581 src_address: NodeAddress,
3582 src_port: P2pPort,
3583 dst_port: P2pPort,
3584 payload: &[u8],
3585 ) -> TelemetryResult<crate::SharedBytes> {
3586 let host_len = u16::try_from(src_hostname.len())
3587 .map_err(|_| TelemetryError::Pack("p2p hostname too long"))?;
3588 let payload_len = u32::try_from(payload.len())
3589 .map_err(|_| TelemetryError::Pack("p2p payload too long"))?;
3590 let mut out = Vec::with_capacity(
3591 15usize
3592 .saturating_add(src_hostname.len())
3593 .saturating_add(payload.len()),
3594 );
3595 out.push(1);
3596 out.extend_from_slice(&dst_port.to_le_bytes());
3597 out.extend_from_slice(&src_port.to_le_bytes());
3598 out.extend_from_slice(&src_address.to_le_bytes());
3599 out.extend_from_slice(&host_len.to_le_bytes());
3600 out.extend_from_slice(&payload_len.to_le_bytes());
3601 out.extend_from_slice(src_hostname.as_bytes());
3602 out.extend_from_slice(payload);
3603 Ok(out.into())
3604 }
3605
3606 fn decode_p2p_payload(payload: &[u8]) -> TelemetryResult<P2pDecoded<'_>> {
3607 if payload.len() < 15 {
3608 return Err(TelemetryError::Unpack("p2p frame short"));
3609 }
3610 if payload[0] != 1 {
3611 return Err(TelemetryError::Unpack("p2p frame version"));
3612 }
3613 let destination_port =
3614 u16::from_le_bytes(payload[1..3].try_into().expect("2-byte dst port"));
3615 let source_port = u16::from_le_bytes(payload[3..5].try_into().expect("2-byte src port"));
3616 let source_address = u32::from_le_bytes(payload[5..9].try_into().expect("4-byte address"));
3617 let host_len =
3618 u16::from_le_bytes(payload[9..11].try_into().expect("2-byte host len")) as usize;
3619 let body_len =
3620 u32::from_le_bytes(payload[11..15].try_into().expect("4-byte body len")) as usize;
3621 let host_start = 15usize;
3622 let host_end = host_start.saturating_add(host_len);
3623 let body_end = host_end.saturating_add(body_len);
3624 if host_end > payload.len() || body_end != payload.len() {
3625 return Err(TelemetryError::Unpack("p2p frame length"));
3626 }
3627 let source_hostname = core::str::from_utf8(&payload[host_start..host_end])
3628 .map_err(|_| TelemetryError::Unpack("p2p hostname utf8"))?;
3629 Ok(P2pDecoded {
3630 source_hostname,
3631 source_address,
3632 source_port,
3633 destination_port,
3634 payload: &payload[host_end..body_end],
3635 })
3636 }
3637
3638 fn encode_p2p_stream_payload(
3639 flags: u8,
3640 source_stream_id: P2pStreamId,
3641 destination_stream_id: P2pStreamId,
3642 sequence: u32,
3643 payload: &[u8],
3644 ) -> TelemetryResult<crate::SharedBytes> {
3645 let payload_len = u32::try_from(payload.len())
3646 .map_err(|_| TelemetryError::Pack("p2p stream payload too long"))?;
3647 let mut out = Vec::with_capacity(22usize.saturating_add(payload.len()));
3648 out.extend_from_slice(&P2P_STREAM_MAGIC);
3649 out.push(P2P_STREAM_VERSION);
3650 out.push(flags);
3651 out.extend_from_slice(&source_stream_id.to_le_bytes());
3652 out.extend_from_slice(&destination_stream_id.to_le_bytes());
3653 out.extend_from_slice(&sequence.to_le_bytes());
3654 out.extend_from_slice(&payload_len.to_le_bytes());
3655 out.extend_from_slice(payload);
3656 Ok(out.into())
3657 }
3658
3659 fn decode_p2p_stream_payload(payload: &[u8]) -> TelemetryResult<Option<P2pStreamDecoded<'_>>> {
3660 if !payload.starts_with(&P2P_STREAM_MAGIC) {
3661 return Ok(None);
3662 }
3663 if payload.len() < 22 {
3664 return Err(TelemetryError::Unpack("p2p stream frame short"));
3665 }
3666 if payload[4] != P2P_STREAM_VERSION {
3667 return Err(TelemetryError::Unpack("p2p stream frame version"));
3668 }
3669 let flags = payload[5];
3670 let source_stream_id =
3671 u32::from_le_bytes(payload[6..10].try_into().expect("stream source id"));
3672 let destination_stream_id =
3673 u32::from_le_bytes(payload[10..14].try_into().expect("stream destination id"));
3674 let sequence = u32::from_le_bytes(payload[14..18].try_into().expect("stream sequence"));
3675 let body_len =
3676 u32::from_le_bytes(payload[18..22].try_into().expect("stream body len")) as usize;
3677 let body_end = 22usize.saturating_add(body_len);
3678 if body_end != payload.len() {
3679 return Err(TelemetryError::Unpack("p2p stream frame length"));
3680 }
3681 Ok(Some(P2pStreamDecoded {
3682 flags,
3683 source_stream_id,
3684 destination_stream_id,
3685 sequence,
3686 payload: &payload[22..],
3687 }))
3688 }
3689
3690 fn allocate_p2p_stream_id_locked(st: &mut RouterInner) -> P2pStreamId {
3691 for _ in 0..u32::MAX {
3692 let id = st.next_p2p_stream_id.max(1);
3693 st.next_p2p_stream_id = st.next_p2p_stream_id.wrapping_add(1).max(1);
3694 if !st.p2p_stream_sessions.contains_key(&id) {
3695 return id;
3696 }
3697 }
3698 0
3699 }
3700
3701 fn dispatch_p2p_packet(&self, pkt: &Packet) -> TelemetryResult<()> {
3702 if pkt.data_type() != DataType::P2pMessage {
3703 return Ok(());
3704 }
3705 let decoded = Self::decode_p2p_payload(pkt.payload())?;
3706 if let Some(stream) = Self::decode_p2p_stream_payload(decoded.payload)? {
3707 return self.dispatch_p2p_stream_frame(&decoded, &stream);
3708 }
3709 let handlers = {
3710 let st = self.state.lock();
3711 st.p2p_port_handlers
3712 .get(&decoded.destination_port)
3713 .cloned()
3714 .unwrap_or_default()
3715 };
3716 for handler in handlers {
3717 (handler.handler)(P2pMessage {
3718 source_hostname: decoded.source_hostname,
3719 source_address: decoded.source_address,
3720 source_port: decoded.source_port,
3721 destination_port: decoded.destination_port,
3722 payload: decoded.payload,
3723 })?;
3724 }
3725 Ok(())
3726 }
3727
3728 fn dispatch_p2p_stream_frame(
3729 &self,
3730 msg: &P2pDecoded<'_>,
3731 frame: &P2pStreamDecoded<'_>,
3732 ) -> TelemetryResult<()> {
3733 let mut events: Vec<PendingP2pStreamEvent> = Vec::new();
3734 let mut reply: Option<(AddressBookEntry, P2pPort, P2pPort, crate::SharedBytes)> = None;
3735 {
3736 let mut st = self.state.lock();
3737 if frame.flags & P2P_STREAM_SYN != 0 && frame.flags & P2P_STREAM_ACK == 0 {
3738 let peer_hostname: Arc<str> = Arc::from(msg.source_hostname);
3739 let existing_id = st.p2p_stream_sessions.iter().find_map(|(id, session)| {
3740 (session.peer_stream_id == frame.source_stream_id
3741 && session.local_port == msg.destination_port
3742 && session.peer_port == msg.source_port
3743 && session.peer_address == msg.source_address
3744 && session.peer_hostname.as_ref() == msg.source_hostname)
3745 .then_some(*id)
3746 });
3747 let local_id = if let Some(local_id) = existing_id {
3748 local_id
3749 } else {
3750 let local_id = Self::allocate_p2p_stream_id_locked(&mut st);
3751 if local_id == 0 {
3752 return Err(TelemetryError::Io("p2p stream id exhausted"));
3753 }
3754 st.p2p_stream_sessions.insert(
3755 local_id,
3756 P2pStreamSession {
3757 peer_hostname: peer_hostname.clone(),
3758 peer_address: msg.source_address,
3759 local_port: msg.destination_port,
3760 peer_port: msg.source_port,
3761 peer_stream_id: frame.source_stream_id,
3762 next_sequence: 1,
3763 connected: true,
3764 },
3765 );
3766 let handlers = st
3767 .p2p_stream_handlers
3768 .get(&msg.destination_port)
3769 .cloned()
3770 .unwrap_or_default();
3771 events.push(PendingP2pStreamEvent {
3772 handlers,
3773 kind: P2pStreamEventKind::Accepted,
3774 stream_id: local_id,
3775 peer_stream_id: frame.source_stream_id,
3776 peer_hostname: peer_hostname.clone(),
3777 peer_address: msg.source_address,
3778 local_port: msg.destination_port,
3779 peer_port: msg.source_port,
3780 });
3781 local_id
3782 };
3783 let dst =
3784 st.address_book
3785 .get(msg.source_hostname)
3786 .cloned()
3787 .unwrap_or(AddressBookEntry {
3788 hostname: peer_hostname,
3789 address: msg.source_address,
3790 requested_address: msg.source_address,
3791 mode: AddressAssignmentMode::Dynamic,
3792 birth_ms: self.clock.now_ms(),
3793 owner_hash: Self::sender_hash(msg.source_hostname),
3794 last_seen_ms: self.clock.now_ms(),
3795 });
3796 let payload = Self::encode_p2p_stream_payload(
3797 P2P_STREAM_SYN | P2P_STREAM_ACK,
3798 local_id,
3799 frame.source_stream_id,
3800 0,
3801 &[],
3802 )?;
3803 reply = Some((dst, msg.source_port, msg.destination_port, payload));
3804 } else if frame.flags & P2P_STREAM_SYN != 0 && frame.flags & P2P_STREAM_ACK != 0 {
3805 if let Some(session) = st.p2p_stream_sessions.get_mut(&frame.destination_stream_id)
3806 {
3807 session.peer_stream_id = frame.source_stream_id;
3808 session.connected = true;
3809 let peer_hostname = session.peer_hostname.clone();
3810 let peer_address = session.peer_address;
3811 let local_port = session.local_port;
3812 let peer_port = session.peer_port;
3813 let handlers = st
3814 .p2p_stream_handlers
3815 .get(&local_port)
3816 .cloned()
3817 .unwrap_or_default();
3818 events.push(PendingP2pStreamEvent {
3819 handlers,
3820 kind: P2pStreamEventKind::Connected,
3821 stream_id: frame.destination_stream_id,
3822 peer_stream_id: frame.source_stream_id,
3823 peer_hostname,
3824 peer_address,
3825 local_port,
3826 peer_port,
3827 });
3828 }
3829 } else if frame.flags & (P2P_STREAM_FIN | P2P_STREAM_RST | P2P_STREAM_DATA) != 0 {
3830 let kind = if frame.flags & P2P_STREAM_RST != 0 {
3831 P2pStreamEventKind::Reset
3832 } else if frame.flags & P2P_STREAM_FIN != 0 {
3833 P2pStreamEventKind::Closed
3834 } else {
3835 P2pStreamEventKind::Data
3836 };
3837 let session_id = if frame.destination_stream_id != 0 {
3838 Some(frame.destination_stream_id)
3839 } else {
3840 st.p2p_stream_sessions.iter().find_map(|(id, session)| {
3841 (session.peer_stream_id == frame.source_stream_id
3842 && session.local_port == msg.destination_port
3843 && session.peer_port == msg.source_port
3844 && session.peer_address == msg.source_address
3845 && session.peer_hostname.as_ref() == msg.source_hostname)
3846 .then_some(*id)
3847 })
3848 };
3849 if let Some(session_id) = session_id
3850 && let Some(session) = st.p2p_stream_sessions.get(&session_id)
3851 {
3852 let handlers = st
3853 .p2p_stream_handlers
3854 .get(&session.local_port)
3855 .cloned()
3856 .unwrap_or_default();
3857 events.push(PendingP2pStreamEvent {
3858 handlers,
3859 kind,
3860 stream_id: session_id,
3861 peer_stream_id: frame.source_stream_id,
3862 peer_hostname: session.peer_hostname.clone(),
3863 peer_address: session.peer_address,
3864 local_port: session.local_port,
3865 peer_port: session.peer_port,
3866 });
3867 if matches!(kind, P2pStreamEventKind::Closed | P2pStreamEventKind::Reset) {
3868 st.p2p_stream_sessions.remove(&session_id);
3869 }
3870 }
3871 }
3872 }
3873 if let Some((dst, dst_port, src_port, payload)) = reply {
3874 self.send_p2p_to_entry(dst, dst_port, src_port, &payload)?;
3875 }
3876 for pending in events {
3877 for handler in pending.handlers {
3878 (handler.handler)(P2pStreamEvent {
3879 kind: pending.kind,
3880 stream_id: pending.stream_id,
3881 peer_stream_id: pending.peer_stream_id,
3882 sequence: frame.sequence,
3883 peer_hostname: pending.peer_hostname.as_ref(),
3884 peer_address: pending.peer_address,
3885 local_port: pending.local_port,
3886 peer_port: pending.peer_port,
3887 payload: frame.payload,
3888 })?;
3889 }
3890 }
3891 Ok(())
3892 }
3893
3894 fn note_reliable_return_route(&self, side: RouterSideId, packet_id: u64) {
3900 let mut st = self.state.lock();
3901 let last_replay_ms = if st.recent_rx.contains(&packet_id) {
3905 self.clock
3906 .now_ms()
3907 .saturating_sub(runtime_reliable_retransmit_ms())
3908 } else {
3909 self.clock.now_ms()
3910 };
3911 Self::remember_reliable_return_route_locked(&mut st, packet_id);
3912 st.reliable_return_routes
3913 .entry(packet_id)
3914 .and_modify(|route| route.side = side)
3915 .or_insert(ReliableReturnRouteState {
3916 side,
3917 last_replay_ms,
3918 upstream_contract: None,
3919 });
3920 }
3921
3922 fn remember_reliable_return_route_locked(st: &mut RouterInner, packet_id: u64) {
3928 let cap = runtime_reliable_max_return_routes().max(1);
3929 st.reliable_return_route_order
3930 .retain(|id| st.reliable_return_routes.contains_key(id) && *id != packet_id);
3931 while st.reliable_return_route_order.len() >= cap {
3932 if let Some(oldest) = st.reliable_return_route_order.pop_front() {
3933 st.reliable_return_routes.remove(&oldest);
3934 } else {
3935 break;
3936 }
3937 }
3938 st.reliable_return_route_order.push_back(packet_id);
3939 }
3940
3941 fn remember_end_to_end_reliable_tx_locked(st: &mut RouterInner, packet_id: u64) {
3942 let cap = runtime_reliable_max_end_to_end_pending().max(1);
3943 st.end_to_end_reliable_tx_order
3944 .retain(|id| st.end_to_end_reliable_tx.contains_key(id) && *id != packet_id);
3945 while st.end_to_end_reliable_tx_order.len() >= cap {
3946 if let Some(oldest) = st.end_to_end_reliable_tx_order.pop_front() {
3947 st.end_to_end_reliable_tx.remove(&oldest);
3948 } else {
3949 break;
3950 }
3951 }
3952 st.end_to_end_reliable_tx_order.push_back(packet_id);
3953 }
3954
3955 #[cfg(feature = "discovery")]
3956 fn expected_end_to_end_destinations_locked(
3957 &self,
3958 st: &RouterInner,
3959 data: &RouterItem,
3960 ) -> TelemetryResult<BTreeMap<u64, RouterSideId>> {
3961 let (eps, ty) = self.item_route_info(data)?;
3962 let now_ms = self.clock.now_ms();
3963 let restrict_link_local = Self::endpoints_are_link_local_only(&eps);
3964 if st.managed_variable_types.contains(&ty.as_u32()) {
3973 let mut out = BTreeMap::new();
3974 for (&side, route) in st.discovery_routes.iter() {
3975 if now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
3976 continue;
3977 }
3978 let Some(side_ref) = st.sides.get(side).and_then(Option::as_ref) else {
3979 continue;
3980 };
3981 if restrict_link_local && !side_ref.opts.link_local_enabled {
3982 continue;
3983 }
3984 if !self.route_allowed_locked(st, None, Some(ty), side) {
3985 continue;
3986 }
3987 for (sender_id, sender_state) in route.announcers.iter() {
3988 if now_ms.saturating_sub(sender_state.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS
3989 || !sender_state.reachable_network_variables.contains(&ty)
3990 || !Self::is_end_to_end_destination_sender(sender_id)
3991 {
3992 continue;
3993 }
3994 out.insert(Self::sender_hash(sender_id), side);
3995 if out.len() >= runtime_reliable_max_end_to_end_pending().max(1) {
3996 return Ok(out);
3997 }
3998 }
3999 }
4000 return Ok(out);
4001 }
4002 let prefer_best_overlap =
4003 is_reliable_type(ty) && Self::reliable_control_target_packet_id(data)?.is_none();
4004 let scoring_eps = self.preferred_scoring_endpoints(&eps, prefer_best_overlap);
4005 let mut candidates: Vec<(u64, RouterSideId, usize)> = Vec::new();
4006 let mut best_overlap = 0usize;
4007 let mut out = BTreeMap::new();
4008 for (&side, route) in st.discovery_routes.iter() {
4009 if now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
4010 continue;
4011 }
4012 let Some(side_ref) = st.sides.get(side).and_then(Option::as_ref) else {
4013 continue;
4014 };
4015 if restrict_link_local && !side_ref.opts.link_local_enabled {
4016 continue;
4017 }
4018 if !self.route_allowed_locked(st, None, Some(ty), side) {
4019 continue;
4020 }
4021 for sender_state in route.announcers.values() {
4022 if now_ms.saturating_sub(sender_state.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
4023 continue;
4024 }
4025 for board in sender_state.topology_boards.iter() {
4026 if !Self::is_end_to_end_destination_sender(&board.sender_id) {
4027 continue;
4028 }
4029 let overlap = Self::endpoint_overlap_count(
4030 board.reachable_endpoints.iter().copied(),
4031 &scoring_eps,
4032 );
4033 if overlap > 0 {
4034 if prefer_best_overlap {
4035 best_overlap = best_overlap.max(overlap);
4036 candidates.push((Self::sender_hash(&board.sender_id), side, overlap));
4037 } else {
4038 out.insert(Self::sender_hash(&board.sender_id), side);
4039 if out.len() >= runtime_reliable_max_end_to_end_pending().max(1) {
4040 return Ok(out);
4041 }
4042 }
4043 }
4044 }
4045 }
4046 }
4047
4048 if prefer_best_overlap {
4049 for (sender_hash, side, overlap) in candidates {
4050 if overlap == best_overlap {
4051 out.insert(sender_hash, side);
4052 if out.len() >= runtime_reliable_max_end_to_end_pending().max(1) {
4053 return Ok(out);
4054 }
4055 }
4056 }
4057 }
4058 Ok(out)
4059 }
4060
4061 #[cfg(feature = "discovery")]
4062 #[allow(clippy::too_many_arguments)]
4063 fn discovered_route_candidates_locked(
4064 &self,
4065 st: &RouterInner,
4066 exclude: Option<RouterSideId>,
4067 ty: DataType,
4068 eps: &[DataEndpoint],
4069 target_senders: &[u64],
4070 prefer_nonlocal: bool,
4071 preferred_timesync_source: Option<&str>,
4072 ) -> Vec<DiscoveryCandidateMatch> {
4073 let restrict_link_local = Self::endpoints_are_link_local_only(eps);
4074 let now_ms = self.clock.now_ms();
4075 let scoring_eps = self.preferred_scoring_endpoints(eps, prefer_nonlocal);
4076 let has_variable_owner = st.discovery_routes.iter().any(|(&side, route)| {
4077 exclude != Some(side)
4078 && now_ms.saturating_sub(route.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
4079 && route.reachable_network_variables.contains(&ty)
4080 });
4081 let mut out = Vec::new();
4082 let mut unique_endpoint_fallbacks = Vec::new();
4083
4084 for (&side, route) in st.discovery_routes.iter() {
4085 if exclude == Some(side)
4086 || now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS
4087 {
4088 continue;
4089 }
4090 if restrict_link_local
4091 && st
4092 .sides
4093 .get(side)
4094 .and_then(|s| s.as_ref())
4095 .map(|s| !s.opts.link_local_enabled)
4096 .unwrap_or(true)
4097 {
4098 continue;
4099 }
4100 if !self.route_allowed_locked(st, exclude, Some(ty), side) {
4101 continue;
4102 }
4103 if !target_senders.is_empty()
4104 && !Self::side_matches_target_senders_locked(st, side, target_senders, now_ms)
4105 {
4106 if scoring_eps
4107 .iter()
4108 .copied()
4109 .any(|ep| route.reachable.contains(&ep))
4110 {
4111 unique_endpoint_fallbacks.push(side);
4112 }
4113 continue;
4114 }
4115 if !target_senders.is_empty() {
4116 out.push(DiscoveryCandidateMatch {
4117 side,
4118 overlap: usize::MAX,
4119 });
4120 continue;
4121 }
4122 if has_variable_owner {
4123 if route.reachable_network_variables.contains(&ty) {
4124 out.push(DiscoveryCandidateMatch {
4125 side,
4126 overlap: usize::MAX,
4127 });
4128 }
4129 continue;
4130 }
4131 if preferred_timesync_source
4132 .is_some_and(|source| route.reachable_timesync_sources.iter().any(|s| s == source))
4133 {
4134 out.push(DiscoveryCandidateMatch {
4135 side,
4136 overlap: usize::MAX,
4137 });
4138 continue;
4139 }
4140 let overlap =
4141 Self::endpoint_overlap_count(route.reachable.iter().copied(), &scoring_eps);
4142 if overlap > 0 {
4143 out.push(DiscoveryCandidateMatch { side, overlap });
4144 }
4145 }
4146 if !target_senders.is_empty() && out.is_empty() && unique_endpoint_fallbacks.len() == 1 {
4147 out.push(DiscoveryCandidateMatch {
4148 side: unique_endpoint_fallbacks[0],
4149 overlap: usize::MAX,
4150 });
4151 }
4152 out
4153 }
4154
4155 #[cfg(feature = "discovery")]
4161 fn retain_shortest_discovery_candidates_locked(
4162 st: &RouterInner,
4163 matches: &mut Vec<DiscoveryCandidateMatch>,
4164 endpoints: &[DataEndpoint],
4165 target_senders: &[u64],
4166 now_ms: u64,
4167 ) {
4168 if matches.len() <= 1 || endpoints.is_empty() {
4169 return;
4170 }
4171
4172 let route_distance = |side: RouterSideId, target_sender: Option<u64>| -> Option<usize> {
4173 let route = st.discovery_routes.get(&side)?;
4174 route
4175 .announcers
4176 .iter()
4177 .filter(|(_, state)| {
4178 now_ms.saturating_sub(state.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
4179 })
4180 .filter_map(|(announcer, state)| {
4181 let targets: BTreeSet<&str> = state
4182 .topology_boards
4183 .iter()
4184 .filter(|board| {
4185 target_sender
4186 .map(|target| Self::sender_hash(&board.sender_id) == target)
4187 .unwrap_or_else(|| {
4188 endpoints.iter().any(|endpoint| {
4189 board.reachable_endpoints.contains(endpoint)
4190 })
4191 })
4192 })
4193 .map(|board| board.sender_id.as_str())
4194 .collect();
4195 if targets.is_empty() {
4196 return if let Some(target) = target_sender {
4197 state
4198 .topology_boards
4199 .iter()
4200 .any(|board| Self::sender_hash(&board.sender_id) == target)
4201 .then_some(usize::MAX / 2)
4202 } else {
4203 state
4204 .reachable
4205 .iter()
4206 .any(|endpoint| endpoints.contains(endpoint))
4207 .then_some(usize::MAX / 2)
4208 };
4209 }
4210
4211 let mut distances: BTreeMap<&str, usize> = BTreeMap::new();
4212 let mut pending = VecDeque::from([(announcer.as_str(), 0usize)]);
4213 while let Some((sender, distance)) = pending.pop_front() {
4214 if distances.contains_key(sender) {
4215 continue;
4216 }
4217 distances.insert(sender, distance);
4218 if targets.contains(sender) {
4219 return Some(distance);
4220 }
4221 for board in &state.topology_boards {
4222 if board.sender_id == sender {
4223 for peer in &board.connections {
4224 if !distances.contains_key(peer.as_str()) {
4225 pending.push_back((peer.as_str(), distance + 1));
4226 }
4227 }
4228 } else if board.connections.iter().any(|peer| peer == sender)
4229 && !distances.contains_key(board.sender_id.as_str())
4230 {
4231 pending.push_back((board.sender_id.as_str(), distance + 1));
4232 }
4233 }
4234 }
4235 None
4236 })
4237 .min()
4238 };
4239
4240 let route_targets: Vec<Option<u64>> = if target_senders.is_empty() {
4241 vec![None]
4242 } else {
4243 target_senders.iter().copied().map(Some).collect()
4244 };
4245 let mut selected = BTreeSet::new();
4246 for target in route_targets {
4247 let distances: Vec<(RouterSideId, usize)> = matches
4248 .iter()
4249 .filter_map(|candidate| {
4250 route_distance(candidate.side, target)
4251 .map(|distance| (candidate.side, distance))
4252 })
4253 .collect();
4254 let Some(best) = distances.iter().map(|(_, distance)| *distance).min() else {
4255 continue;
4256 };
4257 selected.extend(
4258 distances
4259 .into_iter()
4260 .filter(|(_, distance)| *distance == best)
4261 .map(|(side, _)| side),
4262 );
4263 }
4264 if !selected.is_empty() {
4265 matches.retain(|candidate| selected.contains(&candidate.side));
4266 }
4267 }
4268
4269 #[cfg(feature = "discovery")]
4270 fn select_discovered_candidate_sides_locked(
4271 &self,
4272 st: &mut RouterInner,
4273 exclude: Option<RouterSideId>,
4274 ty: DataType,
4275 target_senders: &[u64],
4276 prefer_best_overlap: bool,
4277 matches: Vec<DiscoveryCandidateMatch>,
4278 ) -> Vec<RouterSideId> {
4279 let discovered_origin = if Self::has_typed_route_overrides_locked(st, exclude, ty)
4280 || !target_senders.is_empty()
4281 {
4282 RouteSelectionOrigin::Flood
4283 } else {
4284 RouteSelectionOrigin::Discovered
4285 };
4286
4287 let mut matches = matches;
4288 if matches.iter().any(|m| m.overlap == usize::MAX) {
4289 matches.retain(|m| m.overlap == usize::MAX);
4290 }
4291
4292 let selected: Vec<RouterSideId> = if prefer_best_overlap {
4293 let best_overlap = matches.iter().map(|m| m.overlap).max().unwrap_or(0);
4294 matches
4295 .into_iter()
4296 .filter(|m| m.overlap == best_overlap)
4297 .map(|m| m.side)
4298 .collect()
4299 } else {
4300 matches.into_iter().map(|m| m.side).collect()
4301 };
4302
4303 let has_distributed_variable_owners = selected.iter().any(|side| {
4310 st.discovery_routes
4311 .get(side)
4312 .is_some_and(|route| route.reachable_network_variables.contains(&ty))
4313 });
4314 if DataType::try_named("HEARTBEAT") == Some(ty) || has_distributed_variable_owners {
4315 selected
4316 } else {
4317 self.apply_route_selection_locked(st, exclude, selected, discovered_origin)
4318 }
4319 }
4320
4321 #[allow(unused_mut)]
4322 fn register_end_to_end_reliable_tx(&self, data: &RouterItem) -> TelemetryResult<bool> {
4323 let packet_id = Self::get_hash(data);
4324 let now_ms = self.clock.now_ms();
4325 let ty = match data {
4326 RouterItem::Packet(pkt) => pkt.data_type(),
4327 RouterItem::Packed(bytes) => wire_format::peek_routing_envelope(bytes.as_ref())?.ty,
4328 };
4329 let mut st = self.state.lock();
4330 #[cfg(feature = "discovery")]
4331 let mut pending_destinations = self.expected_end_to_end_destinations_locked(&st, data)?;
4332 #[cfg(not(feature = "discovery"))]
4333 let mut pending_destinations = BTreeMap::new();
4334 self.filter_trackable_end_to_end_destinations_locked(&st, ty, &mut pending_destinations);
4335 let tracked_destinations = !pending_destinations.is_empty();
4336 if let Some(existing) = st.end_to_end_reliable_tx.get(&packet_id) {
4337 return Ok(!existing.waiting_for_predecessor);
4338 }
4339 if st.end_to_end_reliable_tx.len() >= runtime_reliable_max_end_to_end_pending().max(1) {
4341 return Err(TelemetryError::HandlerError(
4342 "end-to-end reliable window full",
4343 ));
4344 }
4345 let ordered_type =
4346 matches!(reliable_mode(ty), crate::ReliableMode::Ordered).then_some(ty.as_u32());
4347 let waiting_for_predecessor = ordered_type.is_some()
4348 && st
4349 .end_to_end_reliable_tx
4350 .values()
4351 .any(|sent| sent.ordered_type == ordered_type);
4352 #[cfg(feature = "std")]
4353 if reliable_trace_enabled() {
4354 eprintln!(
4355 "[sedsnet-reliable] register time={now_ms} type={} id={packet_id:#x} waiting={waiting_for_predecessor} targets={pending_destinations:?}",
4356 ty.as_u32()
4357 );
4358 }
4359 let mut retained = data.clone();
4360 #[cfg(feature = "compact-packet-store")]
4361 retained.park_for_queue()?;
4362 Self::remember_end_to_end_reliable_tx_locked(&mut st, packet_id);
4363 st.end_to_end_reliable_tx.insert(
4364 packet_id,
4365 EndToEndReliableSent {
4366 ordered_type,
4367 waiting_for_predecessor,
4368 data: retained,
4369 pending_destinations,
4370 tracked_destinations,
4371 last_send_ms: now_ms,
4372 retries: 0,
4373 queued: false,
4374 },
4375 );
4376 Ok(!waiting_for_predecessor)
4377 }
4378
4379 fn release_ordered_successors(&self) -> TelemetryResult<()> {
4382 let ready = {
4383 let mut st = self.state.lock();
4384 let RouterInner {
4385 end_to_end_reliable_tx_order: order,
4386 end_to_end_reliable_tx: pending,
4387 ..
4388 } = &mut *st;
4389 let mut ready = Vec::new();
4390 for (index, id) in order.iter().copied().enumerate() {
4391 let Some(sent) = pending.get(&id) else {
4392 continue;
4393 };
4394 if !sent.waiting_for_predecessor {
4395 continue;
4396 }
4397 let Some(ty) = sent.ordered_type else {
4398 continue;
4399 };
4400 if order.iter().take(index).any(|earlier| {
4404 pending
4405 .get(earlier)
4406 .is_some_and(|sent| sent.ordered_type == Some(ty))
4407 }) {
4408 continue;
4409 }
4410 let sent = pending.get_mut(&id).expect("retained ordered entry");
4411 sent.waiting_for_predecessor = false;
4412 sent.last_send_ms = self.clock.now_ms();
4413 ready.push(id);
4414 }
4415 ready
4416 };
4417 for id in ready {
4418 self.queue_end_to_end_reliable_retransmit(id)?;
4419 }
4420 Ok(())
4421 }
4422
4423 #[cfg(feature = "discovery")]
4424 fn reconcile_end_to_end_reliable_destinations_locked(
4425 &self,
4426 st: &mut RouterInner,
4427 ) -> TelemetryResult<()> {
4428 if st.end_to_end_reliable_tx.is_empty() {
4429 return Ok(());
4430 }
4431 let active_destinations = self.active_end_to_end_destinations_locked(st);
4432 let packet_ids: Vec<u64> = st.end_to_end_reliable_tx.keys().copied().collect();
4433 let mut completed = Vec::new();
4434
4435 for packet_id in packet_ids {
4436 let Some(data) = st
4437 .end_to_end_reliable_tx
4438 .get(&packet_id)
4439 .map(|sent| sent.data.clone())
4440 else {
4441 continue;
4442 };
4443 let expected = self.expected_end_to_end_destinations_locked(st, &data)?;
4444 let Some(sent) = st.end_to_end_reliable_tx.get_mut(&packet_id) else {
4445 continue;
4446 };
4447 if !sent.tracked_destinations {
4448 continue;
4449 }
4450 if sent.ordered_type.is_some() {
4452 for (sender, side) in &mut sent.pending_destinations {
4453 if let Some(next_side) = expected
4454 .get(sender)
4455 .or_else(|| active_destinations.get(sender))
4456 {
4457 *side = *next_side;
4458 }
4459 }
4460 continue;
4461 }
4462 sent.pending_destinations.retain(|sender_hash, side| {
4463 match (
4464 expected.get(sender_hash),
4465 active_destinations.get(sender_hash),
4466 ) {
4467 (Some(next_side), _) | (None, Some(next_side)) => {
4468 *side = *next_side;
4469 true
4470 }
4471 (None, None) => false,
4472 }
4473 });
4474 if sent.pending_destinations.is_empty() {
4475 completed.push(packet_id);
4476 }
4477 }
4478
4479 for packet_id in completed {
4480 st.end_to_end_reliable_tx.remove(&packet_id);
4481 }
4482
4483 Ok(())
4484 }
4485
4486 #[cfg(feature = "discovery")]
4487 fn active_end_to_end_destinations_locked(
4488 &self,
4489 st: &RouterInner,
4490 ) -> BTreeMap<u64, RouterSideId> {
4491 let now_ms = self.clock.now_ms();
4492 let mut out = BTreeMap::new();
4493 for (&side, route) in st.discovery_routes.iter() {
4494 if now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
4495 continue;
4496 }
4497 for sender_state in route.announcers.values() {
4498 if now_ms.saturating_sub(sender_state.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
4499 continue;
4500 }
4501 for board in sender_state.topology_boards.iter() {
4502 if !Self::is_end_to_end_destination_sender(&board.sender_id) {
4503 continue;
4504 }
4505 out.insert(Self::sender_hash(&board.sender_id), side);
4506 if out.len() >= runtime_reliable_max_end_to_end_pending().max(1) {
4507 return out;
4508 }
4509 }
4510 }
4511 }
4512 out
4513 }
4514
4515 fn side_supports_end_to_end_tracking_locked(st: &RouterInner, side: RouterSideId) -> bool {
4516 matches!(
4517 st.sides
4518 .get(side)
4519 .and_then(Option::as_ref)
4520 .map(|side| &side.tx_handler),
4521 Some(RouterTxHandlerFn::Packed(_) | RouterTxHandlerFn::PackedWithPriority(_))
4522 )
4523 }
4524
4525 fn filter_trackable_end_to_end_destinations_locked(
4526 &self,
4527 st: &RouterInner,
4528 ty: DataType,
4529 pending: &mut BTreeMap<u64, RouterSideId>,
4530 ) {
4531 if DataType::try_named("HEARTBEAT") == Some(ty) {
4537 pending.clear();
4538 return;
4539 }
4540 let now_ms = self.clock.now_ms();
4541 pending.retain(|_, side| {
4542 Self::side_supports_end_to_end_tracking_locked(st, *side)
4543 && (is_reliable_type(ty)
4544 || !self.side_has_multiple_announcers_locked(st, *side, now_ms))
4545 });
4546 }
4547
4548 #[cfg(feature = "discovery")]
4549 fn side_has_multiple_announcers_locked(
4550 &self,
4551 st: &RouterInner,
4552 side: RouterSideId,
4553 now_ms: u64,
4554 ) -> bool {
4555 st.discovery_routes
4556 .get(&side)
4557 .map(|route| {
4558 route
4559 .announcers
4560 .values()
4561 .filter(|sender| {
4562 now_ms.saturating_sub(sender.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
4563 })
4564 .take(2)
4565 .count()
4566 > 1
4567 })
4568 .unwrap_or(false)
4569 }
4570
4571 #[cfg(not(feature = "discovery"))]
4572 fn side_has_multiple_announcers_locked(
4573 &self,
4574 _st: &RouterInner,
4575 _side: RouterSideId,
4576 _now_ms: u64,
4577 ) -> bool {
4578 false
4579 }
4580
4581 fn queue_end_to_end_reliable_ack(
4582 &self,
4583 pkt: &Packet,
4584 called_from_queue: bool,
4585 ) -> TelemetryResult<()> {
4586 self.queue_end_to_end_reliable_ack_for_packet_id(
4587 pkt.packet_id(),
4588 pkt.sender(),
4589 called_from_queue,
4590 )
4591 }
4592
4593 fn queue_end_to_end_reliable_ack_for_packet_id(
4594 &self,
4595 packet_id: u64,
4596 original_sender: &str,
4597 called_from_queue: bool,
4598 ) -> TelemetryResult<()> {
4599 if !self.cfg.reliable_enabled() {
4600 return Ok(());
4601 }
4602 let original_sender_hash = {
4603 let st = self.state.lock();
4604 Self::sender_hash(&Self::canonical_sender_locked(&st, original_sender))
4605 };
4606 self.queue_end_to_end_reliable_ack_for_sender_hash(
4607 packet_id,
4608 original_sender_hash,
4609 called_from_queue,
4610 )
4611 }
4612
4613 fn queue_end_to_end_reliable_ack_for_sender_hash(
4614 &self,
4615 packet_id: u64,
4616 original_sender_hash: u64,
4617 called_from_queue: bool,
4618 ) -> TelemetryResult<()> {
4619 if !self.cfg.reliable_enabled() {
4620 return Ok(());
4621 }
4622 let ack_sender = self.encode_end_to_end_ack_sender();
4623 let ack = Packet::new(
4624 DataType::ReliableAck,
4625 message_meta(DataType::ReliableAck).endpoints_ref(),
4626 ack_sender.as_str(),
4627 self.packet_timestamp_ms(),
4628 crate::shared_bytes::convert(Self::encode_end_to_end_reliable_ack(packet_id)),
4629 )?;
4630 let local_sender = self.sender_arc();
4631 let ack = self.attach_wire_contract_to_item(
4635 RouterItem::Packet(ack),
4636 &[
4637 Self::sender_hash(local_sender.as_ref()),
4638 original_sender_hash,
4639 ],
4640 )?;
4641 self.emit_internal_tx(RouterTxItem::Broadcast(ack), true, called_from_queue)
4642 }
4643
4644 fn emit_internal_tx(
4645 &self,
4646 item: RouterTxItem,
4647 ignore_local: bool,
4648 called_from_queue: bool,
4649 ) -> TelemetryResult<()> {
4650 if called_from_queue {
4651 self.tx_queue_item_with_flags(item, ignore_local)
4652 } else {
4653 self.tx_item_impl(item, ignore_local, false)
4654 }
4655 }
4656
4657 fn emit_internal_tx_with_priority(
4658 &self,
4659 item: RouterTxItem,
4660 ignore_local: bool,
4661 priority: u8,
4662 called_from_queue: bool,
4663 ) -> TelemetryResult<()> {
4664 if called_from_queue {
4665 self.tx_queue_item_with_priority(item, ignore_local, priority)
4666 } else {
4667 self.tx_item_impl(item, ignore_local, false)
4668 }
4669 }
4670
4671 fn queue_end_to_end_reliable_retransmit(&self, packet_id: u64) -> TelemetryResult<()> {
4672 {
4673 let mut st = self.state.lock();
4674 let Some(sent) = st.end_to_end_reliable_tx.get_mut(&packet_id) else {
4675 return Ok(());
4676 };
4677 if sent.queued {
4678 return Ok(());
4679 }
4680 sent.queued = true;
4681 }
4682 let result = self.tx_queue_item_with_priority(
4683 RouterTxItem::EndToEndReplay { packet_id },
4684 true,
4685 Self::router_item_priority_bumped(DataType::ReliableAck),
4686 );
4687 if result.is_err()
4688 && let Some(sent) = self.state.lock().end_to_end_reliable_tx.get_mut(&packet_id)
4689 {
4690 sent.queued = false;
4691 }
4692 result
4693 }
4694
4695 fn end_to_end_retransmit_sides(
4696 &self,
4697 packet_id: u64,
4698 ) -> Option<(RouterItem, Vec<RouterSideId>)> {
4699 let mut st = self.state.lock();
4700 let (data, tracked_destinations, mut sides) = {
4701 let sent = st.end_to_end_reliable_tx.get_mut(&packet_id)?;
4702 #[cfg(feature = "std")]
4703 if reliable_trace_enabled() {
4704 eprintln!(
4705 "[sedsnet-reliable] replay time={} id={packet_id:#x} retries={} targets={:?}",
4706 self.clock.now_ms(),
4707 sent.retries,
4708 sent.pending_destinations
4709 );
4710 }
4711 sent.queued = false;
4712 sent.last_send_ms = self.clock.now_ms();
4713 let data = sent.data.clone();
4714 let tracked_destinations = sent.tracked_destinations;
4715 let sides: Vec<RouterSideId> = sent.pending_destinations.values().copied().collect();
4716 (data, tracked_destinations, sides)
4717 };
4718 if tracked_destinations && sides.is_empty() {
4719 st.end_to_end_reliable_tx.remove(&packet_id);
4720 return None;
4721 }
4722 crate::small_sort::sort(&mut sides);
4723 sides.dedup();
4724 Some((data, sides))
4725 }
4726
4727 fn router_item_priority(data: &RouterItem) -> TelemetryResult<u8> {
4728 let ty = match data {
4729 RouterItem::Packet(pkt) => pkt.data_type(),
4730 RouterItem::Packed(bytes) => wire_format::peek_routing_envelope(bytes.as_ref())?.ty,
4731 };
4732 Ok(crate::scheduler_priority(ty))
4733 }
4734
4735 #[inline]
4736 fn router_item_priority_bumped(ty: DataType) -> u8 {
4737 crate::scheduler_priority(ty).saturating_add(16)
4738 }
4739
4740 #[inline]
4741 fn is_side_tx_busy(err: &TelemetryError) -> bool {
4742 matches!(err, TelemetryError::Io("side tx busy"))
4743 }
4744
4745 #[cfg(feature = "timesync")]
4746 fn timesync_has_usable_time_locked(st: &TimeSyncRuntime, now_mono_ns: u64) -> bool {
4747 st.disciplined_clock.read_unix_ms(now_mono_ns).is_some()
4748 || st
4749 .clock
4750 .current_time(now_mono_ns)
4751 .and_then(|reading| reading.unix_time_ms)
4752 .is_some()
4753 }
4754
4755 #[cfg(feature = "timesync")]
4756 fn reconcile_pending_timesync_request_locked(
4757 st: &mut TimeSyncRuntime,
4758 leader: &Option<TimeSyncLeader>,
4759 now_ms: u64,
4760 ) {
4761 let active_remote = match leader {
4762 Some(TimeSyncLeader::Remote(remote)) => Some(remote.sender.as_str()),
4763 _ => None,
4764 };
4765 let should_clear = st
4766 .pending_request
4767 .as_ref()
4768 .is_some_and(|pending| Some(pending.source.as_str()) != active_remote);
4769 if should_clear {
4770 st.pending_request = None;
4771 st.next_request_mono_ms = now_ms;
4772 }
4773 }
4774
4775 #[inline]
4777 fn enqueue_to_sides(
4778 &self,
4779 data: RouterItem,
4780 exclude: Option<RouterSideId>,
4781 ignore_local: bool,
4782 ) -> TelemetryResult<()> {
4783 let data = self.clear_intermediate_hop_targets(data)?;
4784 let plan = self.remote_side_plan(&data, exclude)?;
4785 let mut st = self.state.lock();
4786 let ty = Self::item_data_type(&data)?;
4787 let priority = if st.managed_variable_types.contains(&ty.as_u32()) {
4788 crate::transport_priority(DataType::ManagedVariableValue)
4789 } else {
4790 Self::router_item_priority(&data)?
4791 };
4792
4793 let RemoteSidePlan::Target(sides) = plan;
4794 for idx in sides {
4795 st.push_transmit(TxQueued {
4796 item: RouterTxItem::ToSide {
4797 src: exclude,
4798 dst: idx,
4799 data: data.clone(),
4800 },
4801 ignore_local,
4802 priority,
4803 retry_after_ms: 0,
4804 })?;
4805 }
4806
4807 Ok(())
4808 }
4809
4810 fn relay_send(
4811 &self,
4812 data: RouterItem,
4813 src: Option<RouterSideId>,
4814 called_from_queue: bool,
4815 ) -> TelemetryResult<()> {
4816 if called_from_queue {
4817 return self.enqueue_to_sides(data, src, true);
4818 }
4819
4820 let data = self.clear_intermediate_hop_targets(data)?;
4821 let RemoteSidePlan::Target(sides) = self.remote_side_plan(&data, src)?;
4822 for side in sides {
4823 self.tx_item_impl(
4824 RouterTxItem::ToSide {
4825 src,
4826 dst: side,
4827 data: data.clone(),
4828 },
4829 true,
4830 false,
4831 )?;
4832 }
4833
4834 Ok(())
4835 }
4836
4837 fn item_route_info(&self, data: &RouterItem) -> TelemetryResult<(Vec<DataEndpoint>, DataType)> {
4838 match data {
4839 RouterItem::Packet(pkt) => {
4840 pkt.validate()?;
4841 let mut eps = pkt.endpoints().to_vec();
4842 crate::small_sort::sort(&mut eps);
4843 eps.dedup();
4844 Ok((eps, pkt.data_type()))
4845 }
4846 RouterItem::Packed(bytes) => {
4847 let env = wire_format::peek_routing_envelope(bytes.as_ref())?;
4848 let mut eps: Vec<DataEndpoint> = env.endpoints().collect();
4849 crate::small_sort::sort(&mut eps);
4850 eps.dedup();
4851 Ok((eps, env.ty))
4852 }
4853 }
4854 }
4855
4856 fn item_data_type(data: &RouterItem) -> TelemetryResult<DataType> {
4857 match data {
4858 RouterItem::Packet(pkt) => Ok(pkt.data_type()),
4859 RouterItem::Packed(bytes) => Ok(wire_format::peek_routing_envelope(bytes.as_ref())?.ty),
4860 }
4861 }
4862
4863 fn e2e_crypto_supported(&self) -> bool {
4864 #[cfg(feature = "cryptography")]
4865 {
4866 self.cfg.e2e_encryption() != RouterE2eEncryptionMode::Disabled
4867 && crate::crypto::registered_crypto_available()
4868 }
4869 #[cfg(not(feature = "cryptography"))]
4870 {
4871 false
4872 }
4873 }
4874
4875 fn should_require_e2e_for_type(&self, ty: DataType) -> bool {
4876 if is_internal_control_type(ty) {
4877 return false;
4878 }
4879 match self.cfg.e2e_encryption() {
4880 RouterE2eEncryptionMode::Disabled => {
4881 message_e2e_encryption_policy(ty) == E2eEncryptionPolicy::RequireOn
4882 }
4883 RouterE2eEncryptionMode::RequiredOnly => {
4884 message_e2e_encryption_policy(ty) == E2eEncryptionPolicy::RequireOn
4885 }
4886 RouterE2eEncryptionMode::Preferred => matches!(
4887 message_e2e_encryption_policy(ty),
4888 E2eEncryptionPolicy::PreferOn | E2eEncryptionPolicy::RequireOn
4889 ),
4890 RouterE2eEncryptionMode::ForceAll => true,
4891 }
4892 }
4893
4894 fn ensure_e2e_policy_supported_for_type(&self, ty: DataType) -> TelemetryResult<()> {
4895 if self.should_require_e2e_for_type(ty) && !self.e2e_crypto_supported() {
4896 return Err(TelemetryError::BadArg);
4897 }
4898 Ok(())
4899 }
4900
4901 #[cfg(feature = "cryptography")]
4902 fn e2e_seal_config_for_type(&self, ty: DataType) -> Option<wire_format::E2eSealConfig> {
4903 if self.should_require_e2e_for_type(ty) && self.e2e_crypto_supported() {
4904 Some(wire_format::E2eSealConfig {
4905 key_id: self.cfg.e2e_key_id(),
4906 })
4907 } else {
4908 None
4909 }
4910 }
4911
4912 #[inline]
4913 fn pack_packet_for_router(
4914 &self,
4915 pkt: &Packet,
4916 reliable: Option<wire_format::ReliableHeader>,
4917 ) -> TelemetryResult<crate::SharedBytes> {
4918 #[cfg(feature = "cryptography")]
4919 if let Some(e2e) = self.e2e_seal_config_for_type(pkt.data_type()) {
4920 return (wire_format::pack_packet_with_wire_contract_e2e(
4921 pkt,
4922 reliable,
4923 pkt.wire_shape(),
4924 pkt.wire_target_senders(),
4925 e2e,
4926 ))
4927 .map(crate::shared_bytes::convert);
4928 }
4929 Ok(match reliable {
4930 Some(hdr) => {
4931 crate::shared_bytes::convert(wire_format::pack_packet_with_reliable(pkt, hdr))
4932 }
4933 None => crate::shared_bytes::convert(wire_format::pack_packet(pkt)),
4934 })
4935 }
4936
4937 #[inline]
4938 fn pack_packet_for_contract(
4939 &self,
4940 pkt: &Packet,
4941 reliable: Option<wire_format::ReliableHeader>,
4942 shape: Option<MessageElement>,
4943 target_senders: &[u64],
4944 ) -> TelemetryResult<crate::SharedBytes> {
4945 #[cfg(feature = "cryptography")]
4946 if let Some(e2e) = self.e2e_seal_config_for_type(pkt.data_type()) {
4947 return (wire_format::pack_packet_with_wire_contract_e2e(
4948 pkt,
4949 reliable,
4950 shape,
4951 target_senders,
4952 e2e,
4953 ))
4954 .map(crate::shared_bytes::convert);
4955 }
4956 wire_format::pack_packet_with_wire_contract(pkt, reliable, shape, target_senders)
4957 .map(crate::shared_bytes::convert)
4958 }
4959
4960 #[cfg(feature = "cryptography")]
4961 #[inline]
4962 fn prepare_packed_for_remote(
4963 &self,
4964 bytes: crate::SharedBytes,
4965 reliable_override: Option<Option<wire_format::ReliableHeader>>,
4966 ) -> TelemetryResult<crate::SharedBytes> {
4967 let frame = wire_format::peek_frame_info(bytes.as_ref())?;
4968 if frame.ack_only() || self.e2e_seal_config_for_type(frame.envelope.ty).is_none() {
4969 return Ok(bytes);
4970 }
4971 let reliable = reliable_override.unwrap_or(frame.reliable);
4972 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
4973 self.pack_packet_for_contract(
4974 &pkt,
4975 reliable,
4976 frame.envelope.wire_shape,
4977 &frame.envelope.target_senders,
4978 )
4979 }
4980
4981 fn item_requires_end_to_end_tracking(
4985 &self,
4986 data: &RouterItem,
4987 ty: DataType,
4988 ) -> TelemetryResult<bool> {
4989 if is_reliable_type(ty) || self.is_managed_variable_type(ty) {
4990 return Ok(true);
4991 }
4992 match data {
4993 RouterItem::Packet(packet) => Ok(!packet.wire_target_senders().is_empty()),
4994 RouterItem::Packed(bytes) => {
4995 let frame = wire_format::peek_routing_frame_info(bytes.as_ref())?;
4996 Ok(frame.reliable.is_some() || frame.envelope.target_senders().len() != 0)
4997 }
4998 }
4999 }
5000
5001 fn item_target_senders<'a>(
5002 &self,
5003 data: &'a RouterItem,
5004 ) -> TelemetryResult<alloc::borrow::Cow<'a, [u64]>> {
5005 match data {
5006 RouterItem::Packet(pkt) => Ok(alloc::borrow::Cow::Borrowed(pkt.wire_target_senders())),
5007 RouterItem::Packed(bytes) => {
5008 let env = wire_format::peek_routing_envelope(bytes.as_ref())?;
5009 if env.target_senders().len() == 0 {
5010 Ok(alloc::borrow::Cow::Borrowed(&[]))
5011 } else {
5012 Ok(alloc::borrow::Cow::Owned(env.target_senders().collect()))
5013 }
5014 }
5015 }
5016 }
5017
5018 fn item_targets_local_sender(&self, data: &RouterItem) -> TelemetryResult<bool> {
5019 let local_hash = Self::sender_hash(self.sender_arc().as_ref());
5020 match data {
5021 RouterItem::Packet(pkt) => Ok(pkt.wire_target_senders().is_empty()
5022 || pkt.wire_target_senders().contains(&local_hash)),
5023 RouterItem::Packed(bytes) => {
5024 let env = wire_format::peek_routing_envelope(bytes.as_ref())?;
5025 Ok(env.target_senders().len() == 0
5026 || env.target_senders().any(|target| target == local_hash))
5027 }
5028 }
5029 }
5030
5031 fn item_targets_router_as_intermediate_hop(
5032 &self,
5033 data: &RouterItem,
5034 endpoints: &[DataEndpoint],
5035 ) -> TelemetryResult<bool> {
5036 let targets = self.item_target_senders(data)?;
5037 if targets.is_empty()
5038 || targets.len() != 1
5043 || endpoints.iter().copied().any(|endpoint| {
5044 self.endpoint_has_packet_handler(endpoint)
5045 || self.endpoint_has_packed_handler(endpoint)
5046 })
5047 {
5048 return Ok(false);
5049 }
5050 let local_sender = self.sender_arc();
5051 Ok(targets.contains(&Self::sender_hash(local_sender.as_ref())))
5052 }
5053
5054 fn clear_intermediate_hop_targets(&self, data: RouterItem) -> TelemetryResult<RouterItem> {
5055 if self.item_target_senders(&data)?.len() != 1 {
5058 return Ok(data);
5059 }
5060 let (endpoints, _) = self.item_route_info(&data)?;
5061 if !self.item_targets_router_as_intermediate_hop(&data, &endpoints)? {
5062 return Ok(data);
5063 }
5064 let pkt = match &data {
5065 RouterItem::Packet(pkt) => pkt.clone(),
5066 RouterItem::Packed(bytes) => wire_format::unpack_packet(bytes.as_ref())?,
5067 };
5068 let reliable = match &data {
5069 RouterItem::Packet(_) => None,
5070 RouterItem::Packed(bytes) => wire_format::peek_frame_info(bytes.as_ref())?.reliable,
5071 };
5072 let shape = match &data {
5073 RouterItem::Packet(pkt) => pkt.wire_shape(),
5074 RouterItem::Packed(bytes) => {
5075 wire_format::peek_routing_envelope(bytes.as_ref())?.wire_shape
5076 }
5077 };
5078 let forwarded = self.pack_packet_for_contract(&pkt, reliable, shape, &[])?;
5079 let upstream_id = match &data {
5080 RouterItem::Packet(pkt) => pkt.packet_id(),
5081 RouterItem::Packed(bytes) => wire_format::packet_id_from_wire(bytes.as_ref())?,
5082 };
5083 let downstream_id = wire_format::packet_id_from_wire(forwarded.as_ref())?;
5084 {
5085 let mut st = self.state.lock();
5086 if let Some(mut route) = st.reliable_return_routes.get(&upstream_id).cloned() {
5087 route.upstream_contract = Some((
5088 upstream_id,
5089 Self::sender_hash(&Self::canonical_sender_locked(&st, pkt.sender())),
5090 ));
5091 Self::remember_reliable_return_route_locked(&mut st, downstream_id);
5092 st.reliable_return_routes.insert(downstream_id, route);
5093 }
5094 }
5095 Ok(RouterItem::Packed(forwarded))
5096 }
5097
5098 #[cfg(feature = "discovery")]
5099 fn side_matches_target_senders_locked(
5100 st: &RouterInner,
5101 side: RouterSideId,
5102 target_senders: &[u64],
5103 now_ms: u64,
5104 ) -> bool {
5105 st.discovery_routes
5106 .get(&side)
5107 .map(|route| {
5108 if now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
5109 return false;
5110 }
5111 route.announcers.iter().any(|(announcer, sender_state)| {
5112 if now_ms.saturating_sub(sender_state.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
5113 return false;
5114 }
5115 target_senders.contains(&Self::sender_hash(announcer))
5116 || sender_state
5117 .advertised_address
5118 .is_some_and(|address| target_senders.contains(&u64::from(address)))
5119 || sender_state.topology_boards.iter().any(|board| {
5120 target_senders.contains(&Self::sender_hash(&board.sender_id))
5121 })
5122 })
5123 })
5124 .unwrap_or(false)
5125 }
5126
5127 fn attach_wire_contract_to_item(
5128 &self,
5129 data: RouterItem,
5130 target_senders: &[u64],
5131 ) -> TelemetryResult<RouterItem> {
5132 match data {
5133 RouterItem::Packet(pkt) => {
5134 let reliable = if is_reliable_type(pkt.data_type()) {
5135 Some(wire_format::ReliableHeader {
5136 flags: wire_format::RELIABLE_FLAG_UNSEQUENCED,
5137 seq: 0,
5138 ack: 0,
5139 })
5140 } else {
5141 None
5142 };
5143 let bytes = self.pack_packet_for_contract(
5144 &pkt,
5145 reliable,
5146 Some(message_meta(pkt.data_type()).element),
5147 target_senders,
5148 )?;
5149 Ok(RouterItem::Packed(bytes))
5150 }
5151 RouterItem::Packed(bytes) => Ok(RouterItem::Packed(bytes)),
5152 }
5153 }
5154
5155 fn endpoints_are_link_local_only(eps: &[DataEndpoint]) -> bool {
5156 !eps.is_empty() && eps.iter().all(|ep| ep.is_link_local_only())
5157 }
5158
5159 fn should_route_remote(
5160 &self,
5161 data: &RouterItem,
5162 exclude: Option<RouterSideId>,
5163 ) -> TelemetryResult<bool> {
5164 #[cfg(feature = "discovery")]
5165 {
5166 let RemoteSidePlan::Target(sides) = self.remote_side_plan(data, exclude)?;
5167 Ok(!sides.is_empty())
5168 }
5169
5170 #[cfg(not(feature = "discovery"))]
5171 {
5172 let (eps, ty) = self.item_route_info(data)?;
5173 if !(has_nonlocal_endpoint(&eps, &self.cfg) || force_remote_for_type(ty)) {
5174 return Ok(false);
5175 }
5176 let st = self.state.lock();
5177 Ok(!self
5178 .eligible_side_ids_locked(
5179 &st,
5180 exclude,
5181 Some(ty),
5182 Self::endpoints_are_link_local_only(&eps),
5183 )
5184 .is_empty())
5185 }
5186 }
5187
5188 #[cfg(feature = "discovery")]
5189 fn has_explicit_route_policy_locked(
5190 st: &RouterInner,
5191 src: Option<RouterSideId>,
5192 ty: DataType,
5193 ) -> bool {
5194 st.route_overrides
5195 .keys()
5196 .any(|(route_src, _)| *route_src == src)
5197 || Self::has_typed_route_overrides_locked(st, src, ty)
5198 }
5199
5200 fn remote_side_plan(
5201 &self,
5202 data: &RouterItem,
5203 exclude: Option<RouterSideId>,
5204 ) -> TelemetryResult<RemoteSidePlan> {
5205 #[cfg(feature = "discovery")]
5206 {
5207 let (eps, ty) = self.item_route_info(data)?;
5208 let target_senders = self.item_target_senders(data)?;
5209 let resolve_beyond_local_hop =
5210 self.item_targets_router_as_intermediate_hop(data, &eps)?;
5211 let preferred_packet_id = Self::reliable_control_target_packet_id(data)?;
5212 let routing_target_senders: &[u64] = if preferred_packet_id.is_some() {
5213 target_senders.get(1..).unwrap_or(&[])
5216 } else if resolve_beyond_local_hop {
5217 &[]
5218 } else {
5219 target_senders.as_ref()
5220 };
5221 if discovery::is_discovery_type(ty) {
5222 let mut st = self.state.lock();
5223 let sides = self.eligible_side_ids_locked(&st, exclude, Some(ty), false);
5224 return Ok(RemoteSidePlan::Target(self.apply_route_selection_locked(
5225 &mut st,
5226 exclude,
5227 sides,
5228 RouteSelectionOrigin::Flood,
5229 )));
5230 }
5231 #[cfg(feature = "timesync")]
5232 if ty == DataType::TimeSyncAnnounce {
5233 let mut st = self.state.lock();
5240 let sides = self.eligible_side_ids_locked(&st, exclude, Some(ty), false);
5241 let sides =
5242 Self::filter_timesync_sides_locked(&mut st, ty, self.clock.now_ms(), sides);
5243 return Ok(RemoteSidePlan::Target(self.apply_route_selection_locked(
5244 &mut st,
5245 exclude,
5246 sides,
5247 RouteSelectionOrigin::Flood,
5248 )));
5249 }
5250 let mut st = self.state.lock();
5251 let now_ms = self.clock.now_ms();
5256 let has_remote_variable_owner = st.managed_variable_types.contains(&ty.as_u32())
5257 && st.discovery_routes.iter().any(|(&side, route)| {
5258 exclude != Some(side)
5259 && now_ms.saturating_sub(route.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
5260 && route.reachable_network_variables.contains(&ty)
5261 });
5262 if !(has_nonlocal_endpoint(&eps, &self.cfg)
5263 || force_remote_for_type(ty)
5264 || !target_senders.is_empty()
5265 || has_remote_variable_owner)
5266 {
5267 return Ok(RemoteSidePlan::Target(Vec::new()));
5268 }
5269
5270 #[cfg(feature = "timesync")]
5271 let preferred_timesync_source = self.preferred_timesync_route_source(data, ty)?;
5272 #[cfg(not(feature = "timesync"))]
5273 let preferred_timesync_source: Option<String> = None;
5274
5275 if let Some(packet_id) = preferred_packet_id {
5276 let target_side = st
5277 .reliable_return_routes
5278 .get(&packet_id)
5279 .map(|route| route.side);
5280 if let Some(side) = target_side
5281 .filter(|side| self.route_allowed_locked(&st, exclude, Some(ty), *side))
5282 {
5283 #[cfg(feature = "timesync")]
5284 if !Self::timesync_allowed_for_side_locked(
5285 &mut st,
5286 side,
5287 ty,
5288 self.clock.now_ms(),
5289 ) {
5290 return Ok(RemoteSidePlan::Target(Vec::new()));
5291 }
5292 return Ok(RemoteSidePlan::Target(vec![side]));
5293 }
5294 }
5300 let restrict_link_local = Self::endpoints_are_link_local_only(&eps);
5301 let prefer_best_overlap = is_reliable_type(ty)
5302 && routing_target_senders.is_empty()
5303 && preferred_packet_id.is_none();
5304 if st.discovery_routes.is_empty() {
5305 let mut fallback =
5306 self.eligible_side_ids_locked(&st, exclude, Some(ty), restrict_link_local);
5307 #[cfg(feature = "timesync")]
5308 {
5309 fallback = Self::filter_timesync_sides_locked(
5310 &mut st,
5311 ty,
5312 self.clock.now_ms(),
5313 fallback,
5314 );
5315 }
5316 return Ok(RemoteSidePlan::Target(if fallback.len() == 1 {
5317 fallback
5318 } else {
5319 Vec::new()
5320 }));
5321 }
5322 let mut matches = self.discovered_route_candidates_locked(
5323 &st,
5324 exclude,
5325 ty,
5326 &eps,
5327 routing_target_senders,
5328 prefer_best_overlap,
5329 preferred_timesync_source.as_deref(),
5330 );
5331 #[cfg(feature = "timesync")]
5332 {
5333 matches =
5334 Self::filter_timesync_matches_locked(&mut st, ty, self.clock.now_ms(), matches);
5335 }
5336 let has_distributed_variable_owners = matches.iter().any(|candidate| {
5342 st.discovery_routes
5343 .get(&candidate.side)
5344 .is_some_and(|route| route.reachable_network_variables.contains(&ty))
5345 });
5346 if DataType::try_named("HEARTBEAT") != Some(ty) && !has_distributed_variable_owners {
5347 Self::retain_shortest_discovery_candidates_locked(
5348 &st,
5349 &mut matches,
5350 &eps,
5351 routing_target_senders,
5352 self.clock.now_ms(),
5353 );
5354 }
5355
5356 if !matches.is_empty() {
5357 Ok(RemoteSidePlan::Target(
5358 self.select_discovered_candidate_sides_locked(
5359 &mut st,
5360 exclude,
5361 ty,
5362 routing_target_senders,
5363 prefer_best_overlap,
5364 matches,
5365 ),
5366 ))
5367 } else if preferred_packet_id.is_some() {
5368 let sides = self.eligible_side_ids_locked(&st, exclude, Some(ty), false);
5374 Ok(RemoteSidePlan::Target(
5375 if exclude.is_some() && sides.len() == 1 {
5376 sides
5377 } else {
5378 Vec::new()
5379 },
5380 ))
5381 } else if prefer_best_overlap {
5382 Ok(RemoteSidePlan::Target(Vec::new()))
5383 } else {
5384 if Self::has_explicit_route_policy_locked(&st, exclude, ty) {
5385 let mut sides =
5386 self.eligible_side_ids_locked(&st, exclude, Some(ty), restrict_link_local);
5387 #[cfg(feature = "timesync")]
5388 {
5389 sides = Self::filter_timesync_sides_locked(
5390 &mut st,
5391 ty,
5392 self.clock.now_ms(),
5393 sides,
5394 );
5395 }
5396 Ok(RemoteSidePlan::Target(self.apply_route_selection_locked(
5397 &mut st,
5398 exclude,
5399 sides,
5400 RouteSelectionOrigin::Flood,
5401 )))
5402 } else {
5403 Ok(RemoteSidePlan::Target(Vec::new()))
5404 }
5405 }
5406 }
5407 #[cfg(not(feature = "discovery"))]
5408 {
5409 let (_, ty) = self.item_route_info(data)?;
5410 let mut st = self.state.lock();
5411 if let Some(packet_id) = Self::reliable_control_target_packet_id(data)? {
5412 let target_side = st
5413 .reliable_return_routes
5414 .get(&packet_id)
5415 .map(|route| route.side);
5416 if let Some(side) = target_side
5417 .filter(|side| self.route_allowed_locked(&st, exclude, Some(ty), *side))
5418 {
5419 return Ok(RemoteSidePlan::Target(vec![side]));
5420 }
5421 let sides = self.eligible_side_ids_locked(&st, exclude, Some(ty), false);
5422 return Ok(RemoteSidePlan::Target(
5423 if exclude.is_some() && sides.len() == 1 {
5424 sides
5425 } else {
5426 Vec::new()
5427 },
5428 ));
5429 }
5430 let sides = self.eligible_side_ids_locked(&st, exclude, Some(ty), false);
5431 Ok(RemoteSidePlan::Target(self.apply_route_selection_locked(
5432 &mut st,
5433 exclude,
5434 sides,
5435 RouteSelectionOrigin::Flood,
5436 )))
5437 }
5438 }
5439
5440 #[cfg(feature = "discovery")]
5441 fn local_discovery_endpoints(&self, st: &RouterInner) -> Vec<DataEndpoint> {
5442 let mut eps: Vec<DataEndpoint> = self.cfg.handlers.iter().map(|h| h.endpoint).collect();
5443 let _ = st;
5449 #[cfg(feature = "timesync")]
5450 if self.cfg.timesync_config().is_some() {
5451 eps.push(DataEndpoint::TimeSync);
5452 }
5453 eps.retain(|ep| !discovery::is_discovery_endpoint(*ep));
5454 crate::small_sort::sort(&mut eps);
5455 eps.dedup();
5456 eps
5457 }
5458
5459 #[cfg(feature = "discovery")]
5460 fn local_discovery_timesync_sources(&self, now_ms: u64) -> Vec<String> {
5461 #[cfg(feature = "timesync")]
5462 {
5463 let st = self.timesync.lock();
5464 if let Some(tracker) = st.tracker.as_ref()
5465 && tracker.should_serve(
5466 now_ms,
5467 Self::timesync_has_usable_time_locked(&st, self.monotonic_now_ns()),
5468 )
5469 {
5470 return vec![self.sender_arc().to_string()];
5471 }
5472 }
5473 Vec::new()
5474 }
5475
5476 #[cfg(all(feature = "discovery", feature = "timesync"))]
5477 fn preferred_timesync_route_source(
5478 &self,
5479 data: &RouterItem,
5480 ty: DataType,
5481 ) -> TelemetryResult<Option<String>> {
5482 if !matches!(
5483 ty,
5484 DataType::TimeSyncAnnounce | DataType::TimeSyncRequest | DataType::TimeSyncResponse
5485 ) {
5486 return Ok(None);
5487 }
5488
5489 match data {
5490 RouterItem::Packet(pkt) => match ty {
5491 DataType::TimeSyncRequest => {
5492 let local_sender = self.sender_arc();
5493 if pkt.sender() == local_sender.as_ref() {
5494 Ok(self.timesync.lock().tracker.as_ref().and_then(|tracker| {
5495 tracker.current_source().map(|src| src.sender.clone())
5496 }))
5497 } else {
5498 Ok(None)
5499 }
5500 }
5501 DataType::TimeSyncAnnounce | DataType::TimeSyncResponse => {
5502 Ok(Some(pkt.sender().to_owned()))
5503 }
5504 _ => Ok(None),
5505 },
5506 RouterItem::Packed(bytes) => {
5507 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
5508 self.preferred_timesync_route_source(&RouterItem::Packet(pkt), ty)
5509 }
5510 }
5511 }
5512
5513 #[cfg(feature = "discovery")]
5514 fn note_discovery_topology_change_locked(st: &mut RouterInner, now_ms: u64) {
5515 st.discovery_cadence.on_topology_change(now_ms);
5516 for side_id in st
5521 .sides
5522 .iter()
5523 .enumerate()
5524 .filter_map(|(side_id, side)| side.as_ref().map(|_| side_id))
5525 {
5526 let throttle = st.discovery_side_throttle.entry(side_id).or_default();
5527 throttle.pending_incremental = true;
5528 throttle.incremental_retries_remaining = DISCOVERY_INCREMENTAL_RETRY_COUNT;
5529 }
5530 for transport in st.side_transport.values_mut() {
5534 transport.refresh_transmit_header_templates();
5535 }
5536 }
5537
5538 #[cfg(feature = "discovery")]
5539 fn sender_topology_board_mut<'a>(
5540 sender_state: &'a mut DiscoverySenderState,
5541 sender_id: &str,
5542 ) -> &'a mut TopologyBoardNode {
5543 if let Some(idx) = sender_state
5544 .topology_boards
5545 .iter()
5546 .position(|board| board.sender_id == sender_id)
5547 {
5548 return &mut sender_state.topology_boards[idx];
5549 }
5550 sender_state.topology_boards.push(TopologyBoardNode {
5551 sender_id: sender_id.to_string(),
5552 reachable_endpoints: Vec::new(),
5553 reachable_timesync_sources: Vec::new(),
5554 connections: Vec::new(),
5555 });
5556 sender_state
5557 .topology_boards
5558 .last_mut()
5559 .expect("board inserted above")
5560 }
5561
5562 #[cfg(feature = "discovery")]
5563 fn refresh_sender_topology_state(sender_state: &mut DiscoverySenderState) {
5564 discovery::normalize_topology_boards(&mut sender_state.topology_boards);
5565 let (mut reachable, mut reachable_timesync_sources) =
5566 discovery::summarize_topology_boards(&sender_state.topology_boards);
5567 reachable.extend(sender_state.advertised_reachable.iter().copied());
5568 crate::small_sort::sort(&mut reachable);
5569 reachable.dedup();
5570 reachable_timesync_sources.extend(
5571 sender_state
5572 .advertised_reachable_timesync_sources
5573 .iter()
5574 .cloned(),
5575 );
5576 reachable_timesync_sources.sort_unstable();
5577 reachable_timesync_sources.dedup();
5578 sender_state.reachable = reachable;
5579 sender_state.reachable_timesync_sources = reachable_timesync_sources;
5580 }
5581
5582 #[cfg(feature = "discovery")]
5583 fn recompute_discovery_side_state(route: &mut DiscoverySideState) {
5584 let mut reachable = Vec::new();
5585 let mut reachable_network_variables = Vec::new();
5586 let mut reachable_timesync_sources = Vec::new();
5587 let mut last_seen_ms = 0u64;
5588 for sender in route.announcers.values() {
5589 reachable.extend(sender.reachable.iter().copied());
5590 reachable_network_variables.extend(sender.reachable_network_variables.iter().copied());
5591 reachable_timesync_sources.extend(sender.reachable_timesync_sources.iter().cloned());
5592 last_seen_ms = last_seen_ms.max(sender.last_seen_ms);
5593 }
5594 reachable_network_variables
5595 .extend(route.requested_network_variables.iter().map(|(ty, _)| *ty));
5596 last_seen_ms = last_seen_ms.max(
5597 route
5598 .requested_network_variables
5599 .iter()
5600 .map(|(_, last_seen_ms)| *last_seen_ms)
5601 .max()
5602 .unwrap_or(0),
5603 );
5604 crate::small_sort::sort(&mut reachable);
5605 reachable.dedup();
5606 crate::small_sort::sort(&mut reachable_network_variables);
5607 reachable_network_variables.dedup();
5608 reachable_timesync_sources.sort_unstable();
5609 reachable_timesync_sources.dedup();
5610 route.reachable = reachable;
5611 route.reachable_network_variables = reachable_network_variables;
5612 route.reachable_timesync_sources = reachable_timesync_sources;
5613 route.last_seen_ms = last_seen_ms;
5614 }
5615
5616 #[cfg(feature = "discovery")]
5617 fn local_discovery_topology_board(
5618 &self,
5619 st: &RouterInner,
5620 now_ms: u64,
5621 link_local_enabled: bool,
5622 ) -> TopologyBoardNode {
5623 let mut reachable_endpoints = self.local_discovery_endpoints(st);
5624 if !link_local_enabled {
5625 reachable_endpoints.retain(|ep| !ep.is_link_local_only());
5626 }
5627 let mut connections = Vec::new();
5628 for route in st.discovery_routes.values() {
5629 if now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
5630 continue;
5631 }
5632 for (sender, sender_state) in route.announcers.iter() {
5633 if now_ms.saturating_sub(sender_state.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS {
5634 connections.push(sender.clone());
5635 }
5636 }
5637 }
5638 connections.sort_unstable();
5639 connections.dedup();
5640 let sender = self.sender_arc();
5641 TopologyBoardNode {
5642 sender_id: sender.to_string(),
5643 reachable_endpoints,
5644 reachable_timesync_sources: self.local_discovery_timesync_sources(now_ms),
5645 connections,
5646 }
5647 }
5648
5649 #[cfg(feature = "discovery")]
5650 fn advertised_discovery_topology_for_link_locked(
5651 &self,
5652 st: &RouterInner,
5653 now_ms: u64,
5654 link_local_enabled: bool,
5655 exclude_side: Option<RouterSideId>,
5656 ) -> Vec<TopologyBoardNode> {
5657 let mut boards = vec![self.local_discovery_topology_board(st, now_ms, link_local_enabled)];
5658 for (&route_side, route) in st.discovery_routes.iter() {
5659 if exclude_side == Some(route_side) {
5660 continue;
5661 }
5662 if now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
5663 continue;
5664 }
5665 for (announcer, sender_state) in route.announcers.iter() {
5666 if now_ms.saturating_sub(sender_state.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
5667 continue;
5668 }
5669 if sender_state.topology_boards.is_empty() {
5670 let sender = self.sender_arc();
5671 let fallback = TopologyBoardNode {
5672 sender_id: announcer.clone(),
5673 reachable_endpoints: sender_state.reachable.clone(),
5674 reachable_timesync_sources: sender_state.reachable_timesync_sources.clone(),
5675 connections: vec![sender.to_string()],
5676 };
5677 discovery::merge_topology_boards(&mut boards, core::slice::from_ref(&fallback));
5678 } else {
5679 discovery::merge_topology_boards(&mut boards, &sender_state.topology_boards);
5682 if let Some(board) = boards
5683 .iter_mut()
5684 .find(|board| board.sender_id == *announcer)
5685 {
5686 let sender = self.sender_arc();
5687 if !board.connections.iter().any(|peer| peer == sender.as_ref()) {
5688 board.connections.push(sender.to_string());
5689 }
5690 }
5691 }
5692 }
5693 }
5694 if !link_local_enabled {
5695 for board in &mut boards {
5696 board
5697 .reachable_endpoints
5698 .retain(|ep| !ep.is_link_local_only());
5699 }
5700 }
5701 discovery::normalize_topology_boards(&mut boards);
5702 boards
5703 }
5704
5705 #[cfg(feature = "discovery")]
5706 fn prune_discovery_routes_locked(st: &mut RouterInner, now_ms: u64) -> bool {
5707 let mut changed = false;
5710 st.discovery_routes.retain(|_, route| {
5711 let before_peers = route.announcers.len();
5712 let before_requests = route.requested_network_variables.len();
5713 route.announcers.retain(|_, sender| {
5714 now_ms.saturating_sub(sender.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
5715 });
5716 route
5717 .requested_network_variables
5718 .retain(|(_, last_seen_ms)| {
5719 now_ms.saturating_sub(*last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
5720 });
5721 if before_peers != route.announcers.len()
5722 || before_requests != route.requested_network_variables.len()
5723 {
5724 Self::recompute_discovery_side_state(route);
5725 changed = true;
5726 }
5727 let keep =
5728 !route.announcers.is_empty() || !route.requested_network_variables.is_empty();
5729 changed |= !keep;
5730 keep
5731 });
5732 changed
5733 }
5734
5735 #[cfg(feature = "discovery")]
5736 fn advertised_summaries_from_topology_locked(
5737 &self,
5738 st: &RouterInner,
5739 now_ms: u64,
5740 link_local_enabled: bool,
5741 exclude_side: Option<RouterSideId>,
5742 topology: &[TopologyBoardNode],
5743 ) -> (Vec<crate::DataEndpoint>, Vec<String>) {
5744 let (mut endpoints, mut sources) = discovery::summarize_topology_boards(topology);
5745 for (&side, route) in &st.discovery_routes {
5746 if exclude_side == Some(side)
5747 || now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS
5748 {
5749 continue;
5750 }
5751 for peer in route.announcers.values() {
5752 if now_ms.saturating_sub(peer.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
5753 continue;
5754 }
5755 for endpoint in &peer.advertised_reachable {
5756 if !discovery::is_discovery_endpoint(*endpoint)
5757 && (link_local_enabled || !endpoint.is_link_local_only())
5758 && !endpoints.contains(endpoint)
5759 {
5760 endpoints.push(*endpoint);
5761 }
5762 }
5763 for source in &peer.advertised_reachable_timesync_sources {
5764 if !sources.contains(source) {
5765 sources.push(source.clone());
5766 }
5767 }
5768 }
5769 }
5770 endpoints.retain(|endpoint| {
5771 !discovery::is_discovery_endpoint(*endpoint)
5772 && (link_local_enabled || !endpoint.is_link_local_only())
5773 });
5774 crate::small_sort::sort(&mut endpoints);
5775 sources.sort_unstable();
5776 (endpoints, sources)
5777 }
5778
5779 #[cfg(feature = "discovery")]
5780 fn advertised_discovery_endpoints_for_link_locked(
5781 &self,
5782 st: &RouterInner,
5783 now_ms: u64,
5784 link_local_enabled: bool,
5785 exclude_side: Option<RouterSideId>,
5786 ) -> Vec<DataEndpoint> {
5787 let (mut reachable_endpoints, _) = discovery::summarize_topology_boards(
5788 &self.advertised_discovery_topology_for_link_locked(
5789 st,
5790 now_ms,
5791 link_local_enabled,
5792 exclude_side,
5793 ),
5794 );
5795 for (&side, route) in &st.discovery_routes {
5798 if exclude_side == Some(side)
5799 || now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS
5800 {
5801 continue;
5802 }
5803 for sender in route.announcers.values() {
5804 if now_ms.saturating_sub(sender.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS {
5805 reachable_endpoints.extend(sender.advertised_reachable.iter().copied());
5806 }
5807 }
5808 }
5809 crate::small_sort::sort(&mut reachable_endpoints);
5810 reachable_endpoints.dedup();
5811 reachable_endpoints
5812 .into_iter()
5813 .filter(|ep| {
5814 !discovery::is_discovery_endpoint(*ep)
5815 && (link_local_enabled || !ep.is_link_local_only())
5816 })
5817 .collect()
5818 }
5819
5820 #[cfg(all(test, feature = "discovery"))]
5821 fn compact_summary_fixture(&self, endpoint: DataEndpoint) {
5822 let mut sender = DiscoverySenderState {
5823 advertised_reachable: vec![endpoint],
5824 advertised_reachable_timesync_sources: vec!["clock-behind-bridge".to_string()],
5825 last_seen_ms: self.clock.now_ms(),
5826 topology_boards: vec![TopologyBoardNode {
5827 sender_id: "bridge".to_string(),
5828 reachable_endpoints: Vec::new(),
5829 reachable_timesync_sources: Vec::new(),
5830 connections: Vec::new(),
5831 }],
5832 ..Default::default()
5833 };
5834 Self::refresh_sender_topology_state(&mut sender);
5835 let mut route = DiscoverySideState::default();
5836 route.announcers.insert("bridge".to_string(), sender);
5837 Self::recompute_discovery_side_state(&mut route);
5838 self.state.lock().discovery_routes.insert(0, route);
5839 }
5840
5841 #[cfg(feature = "discovery")]
5842 fn advertised_discovery_timesync_sources_for_link_locked(
5843 &self,
5844 st: &RouterInner,
5845 now_ms: u64,
5846 exclude_side: Option<RouterSideId>,
5847 ) -> Vec<String> {
5848 let (_, mut sources) = discovery::summarize_topology_boards(
5849 &self.advertised_discovery_topology_for_link_locked(st, now_ms, true, exclude_side),
5850 );
5851 for (&side, route) in &st.discovery_routes {
5852 if exclude_side == Some(side)
5853 || now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS
5854 {
5855 continue;
5856 }
5857 for sender in route.announcers.values() {
5858 if now_ms.saturating_sub(sender.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS {
5859 sources.extend(sender.advertised_reachable_timesync_sources.iter().cloned());
5860 }
5861 }
5862 }
5863 sources.sort_unstable();
5864 sources.dedup();
5865 sources
5866 }
5867
5868 #[cfg(feature = "discovery")]
5869 fn advertised_network_variables_for_link_locked(
5870 &self,
5871 st: &RouterInner,
5872 now_ms: u64,
5873 exclude_side: Option<RouterSideId>,
5874 ) -> Vec<DataType> {
5875 let mut types: Vec<DataType> = st
5876 .managed_variable_types
5877 .iter()
5878 .copied()
5879 .map(DataType)
5880 .collect();
5881 for (&route_side, route) in st.discovery_routes.iter() {
5882 if exclude_side == Some(route_side) {
5883 continue;
5884 }
5885 if now_ms.saturating_sub(route.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS {
5886 types.extend(route.reachable_network_variables.iter().copied());
5887 }
5888 }
5889 crate::small_sort::sort(&mut types);
5890 types.dedup();
5891 types
5892 }
5893
5894 #[cfg(feature = "discovery")]
5895 fn preferred_discovery_master_present_locked<'a>(
5896 &self,
5897 st: &'a RouterInner,
5898 now_ms: u64,
5899 ) -> Option<&'a str> {
5900 if let Some(preferred) = st.preferred_discovery_master.as_deref()
5901 && (preferred == self.sender_arc().as_ref()
5902 || st
5903 .discovery_routes
5904 .values()
5905 .filter(|route| {
5906 now_ms.saturating_sub(route.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
5907 })
5908 .any(|route| {
5909 route
5910 .announcers
5911 .iter()
5912 .filter(|(_, peer)| {
5913 now_ms.saturating_sub(peer.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
5914 })
5915 .any(|(name, peer)| {
5916 name == preferred
5917 || peer.topology_boards.iter().any(|board| {
5918 board.sender_id == preferred
5919 || board
5920 .connections
5921 .iter()
5922 .any(|name| name == preferred)
5923 })
5924 })
5925 }))
5926 {
5927 return Some(preferred);
5930 }
5931 None
5932 }
5933
5934 #[cfg(feature = "discovery")]
5935 fn discovery_master_sender_locked(&self, st: &RouterInner, now_ms: u64) -> String {
5936 if let Some(preferred) = self.preferred_discovery_master_present_locked(st, now_ms) {
5937 return preferred.to_string();
5938 }
5939 let boards = self.advertised_discovery_topology_for_link_locked(st, now_ms, true, None);
5940 discovery::elect_discovery_master(self.sender_arc().as_ref(), &boards)
5941 }
5942
5943 #[cfg(all(feature = "discovery", feature = "timesync"))]
5944 #[inline]
5945 fn side_is_slow_control_link_locked(
5946 st: &RouterInner,
5947 side_id: RouterSideId,
5948 now_ms: u64,
5949 ) -> bool {
5950 st.adaptive_route_stats.get(&side_id).is_some_and(|stats| {
5951 let recent_slow = stats.last_slow_observed_ms > 0
5952 && now_ms.saturating_sub(stats.last_slow_observed_ms)
5953 <= discovery::DISCOVERY_SLOW_LINK_FULL_INTERVAL_MS;
5954 stats.sample_count > 0
5955 && ((stats.estimated_bandwidth_bps > 0
5956 && stats.estimated_bandwidth_bps <= CONTROL_SLOW_LINK_CAPACITY_BPS)
5957 || recent_slow)
5958 })
5959 }
5960
5961 #[cfg(feature = "discovery")]
5962 fn discovery_level_for_side_locked(
5963 st: &mut RouterInner,
5964 side_id: RouterSideId,
5965 now_ms: u64,
5966 ) -> Option<DiscoveryAdvertiseLevel> {
5967 let throttle = st.discovery_side_throttle.entry(side_id).or_default();
5968 if !throttle.has_sent_full {
5969 throttle.has_sent_full = true;
5970 throttle.pending_incremental = false;
5971 throttle.incremental_retries_remaining = 0;
5972 throttle.next_ping_ms = now_ms.saturating_add(DISCOVERY_SLOW_LINK_PING_INTERVAL_MS);
5973 return Some(DiscoveryAdvertiseLevel::Full);
5974 }
5975 if throttle.pending_incremental {
5976 throttle.incremental_retries_remaining =
5977 throttle.incremental_retries_remaining.saturating_sub(1);
5978 throttle.pending_incremental = throttle.incremental_retries_remaining > 0;
5979 return Some(DiscoveryAdvertiseLevel::Incremental);
5980 }
5981 if throttle.liveness_due(now_ms) {
5984 throttle.next_ping_ms = now_ms.saturating_add(DISCOVERY_SLOW_LINK_PING_INTERVAL_MS);
5985 return Some(DiscoveryAdvertiseLevel::MinimalPing);
5986 }
5987 None
5988 }
5989
5990 #[cfg(all(feature = "discovery", feature = "timesync"))]
5991 #[inline]
5992 fn is_timesync_type(ty: DataType) -> bool {
5993 matches!(
5994 ty,
5995 DataType::TimeSyncAnnounce | DataType::TimeSyncRequest | DataType::TimeSyncResponse
5996 )
5997 }
5998
5999 #[cfg(all(feature = "discovery", feature = "timesync"))]
6000 fn timesync_allowed_for_side_locked(
6001 st: &mut RouterInner,
6002 side_id: RouterSideId,
6003 ty: DataType,
6004 now_ms: u64,
6005 ) -> bool {
6006 if !Self::is_timesync_type(ty) {
6007 return true;
6008 }
6009 if !Self::side_is_slow_control_link_locked(st, side_id, now_ms) {
6010 st.timesync_side_throttle.remove(&side_id);
6011 return true;
6012 }
6013
6014 let throttle = st.timesync_side_throttle.entry(side_id).or_default();
6015 if now_ms >= throttle.next_allowed_ms {
6016 throttle.next_allowed_ms = now_ms.saturating_add(TIMESYNC_SLOW_LINK_MIN_INTERVAL_MS);
6017 return true;
6018 }
6019 false
6020 }
6021
6022 #[cfg(all(feature = "discovery", feature = "timesync"))]
6023 fn filter_timesync_sides_locked(
6024 st: &mut RouterInner,
6025 ty: DataType,
6026 now_ms: u64,
6027 sides: Vec<RouterSideId>,
6028 ) -> Vec<RouterSideId> {
6029 sides
6030 .into_iter()
6031 .filter(|side| Self::timesync_allowed_for_side_locked(st, *side, ty, now_ms))
6032 .collect()
6033 }
6034
6035 #[cfg(all(feature = "discovery", feature = "timesync"))]
6036 fn filter_timesync_matches_locked(
6037 st: &mut RouterInner,
6038 ty: DataType,
6039 now_ms: u64,
6040 matches: Vec<DiscoveryCandidateMatch>,
6041 ) -> Vec<DiscoveryCandidateMatch> {
6042 matches
6043 .into_iter()
6044 .filter(|m| Self::timesync_allowed_for_side_locked(st, m.side, ty, now_ms))
6045 .collect()
6046 }
6047
6048 #[cfg(feature = "discovery")]
6049 fn refresh_known_discovery_liveness(
6050 &self,
6051 pkt: &Packet,
6052 side: RouterSideId,
6053 ) -> TelemetryResult<bool> {
6054 let empty_ping =
6055 pkt.data_type() == crate::DataType::DiscoveryAnnounce && pkt.payload().is_empty();
6056 if !empty_ping && pkt.data_type() != crate::DataType::DiscoveryAddress {
6057 return Ok(false);
6058 }
6059 crate::memory_admission::check(
6060 if empty_ping {
6061 512
6062 } else {
6063 1024 + pkt.payload().len().saturating_mul(4)
6064 },
6065 pkt.payload().len().saturating_mul(2).max(128),
6066 )?;
6067 let mut address = if empty_ping {
6068 None
6069 } else {
6070 Some(discovery::decode_discovery_address(pkt)?)
6071 };
6072 let now = self.clock.now_ms();
6073 let mut st = self.state.lock();
6074 if let Some(ad) = &mut address {
6075 let link_local = st
6076 .sides
6077 .get(side)
6078 .and_then(|entry| entry.as_ref())
6079 .is_some_and(|entry| entry.opts.link_local_enabled);
6080 if !link_local {
6081 ad.reachable_endpoints.retain(|ep| !ep.is_link_local_only());
6082 }
6083 let Some(entry) = st.address_book.get(ad.hostname.as_str()) else {
6084 return Ok(false);
6085 };
6086 if entry.address != ad.address
6087 || entry.requested_address != ad.requested_address
6088 || entry.mode != Self::address_mode_from_code(ad.mode, ad.requested_address)
6089 || entry.owner_hash != ad.owner_hash
6090 {
6091 return Ok(false);
6092 }
6093 if entry.birth_ms != ad.birth_ms
6094 && st.address_book.iter().any(|(hostname, other)| {
6095 hostname != &ad.hostname
6096 && [ad.address, ad.requested_address].iter().any(|candidate| {
6097 *candidate != 0
6098 && (other.address == *candidate
6099 || other.requested_address == *candidate)
6100 })
6101 })
6102 {
6103 return Ok(false);
6107 }
6108 }
6109 let canonical;
6110 let sender = if let Some(ad) = &address {
6111 ad.hostname.as_str()
6112 } else {
6113 canonical = Self::canonical_sender_locked(&st, pkt.sender());
6114 canonical.as_str()
6115 };
6116 let Some(route) = st.discovery_routes.get_mut(&side) else {
6117 return Ok(false);
6118 };
6119 let Some(peer) = route.announcers.get_mut(sender) else {
6120 return Ok(false);
6121 };
6122 if let Some(ad) = &address
6123 && (peer.advertised_address != Some(ad.address)
6124 || peer.advertised_reachable != ad.reachable_endpoints
6125 || peer.advertised_reachable_timesync_sources != ad.reachable_timesync_sources
6126 || peer.reachable_network_variables != ad.reachable_network_variables
6127 || peer.link_capabilities != Some(ad.link_capabilities))
6128 {
6129 return Ok(false);
6130 }
6131 peer.last_seen_ms = now;
6134 route.last_seen_ms = route.last_seen_ms.max(now);
6135 if let Some(ad) = &address
6136 && let Some(entry) = st.address_book.get_mut(ad.hostname.as_str())
6137 {
6138 entry.birth_ms = ad.birth_ms;
6141 entry.last_seen_ms = now;
6142 }
6143 Ok(true)
6144 }
6145
6146 #[cfg(feature = "discovery")]
6147 fn queue_pressure_keepalives(
6148 &self,
6149 requested_side: Option<RouterSideId>,
6150 ) -> TelemetryResult<()> {
6151 let now = self.clock.now_ms();
6152 let sender = self.sender_arc();
6153 crate::memory_admission::check(
6154 256 + sender.len().saturating_mul(4),
6155 sender.len().max(256),
6156 )?;
6157 let side_count = self.state.lock().sides.len();
6158 for side in 0..side_count {
6159 let due = {
6160 let st = self.state.lock();
6161 requested_side.is_none_or(|wanted| wanted == side)
6162 && st.sides[side].is_some()
6163 && self.route_allowed_locked(
6164 &st,
6165 None,
6166 Some(crate::DataType::DiscoveryAnnounce),
6167 side,
6168 )
6169 && st
6170 .discovery_side_throttle
6171 .get(&side)
6172 .is_none_or(|throttle| now >= throttle.next_ping_ms)
6173 };
6174 if !due {
6175 continue;
6176 }
6177 let summary = {
6180 let st = self.state.lock();
6181 let extra = st
6182 .discovery_routes
6183 .iter()
6184 .filter(|(id, _)| **id != side)
6185 .fold(0usize, |total, (_, route)| {
6186 total
6187 .saturating_add(route.reachable.len().saturating_mul(32))
6188 .saturating_add(
6189 route.reachable_network_variables.len().saturating_mul(32),
6190 )
6191 .saturating_add(
6192 route
6193 .reachable_timesync_sources
6194 .iter()
6195 .map(|source| source.len().saturating_add(32).saturating_mul(4))
6196 .sum::<usize>(),
6197 )
6198 });
6199 if crate::memory_admission::check(4096usize.saturating_add(extra), 2048).is_ok() {
6200 let opts = st.sides[side].as_ref().expect("checked above").opts.clone();
6201 let mut endpoints = self.local_discovery_endpoints(&st);
6202 let mut variables =
6203 self.advertised_network_variables_for_link_locked(&st, now, Some(side));
6204 let mut sources = self.local_discovery_timesync_sources(now);
6205 for (&route_side, route) in &st.discovery_routes {
6206 if route_side == side {
6207 continue;
6208 }
6209 for peer in route.announcers.values() {
6210 if now.saturating_sub(peer.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS {
6211 endpoints.extend(peer.reachable.iter().copied());
6212 sources.extend(peer.reachable_timesync_sources.iter().cloned());
6213 }
6214 }
6215 }
6216 endpoints.retain(|ep| {
6217 !discovery::is_discovery_endpoint(*ep)
6218 && (opts.link_local_enabled || !ep.is_link_local_only())
6219 });
6220 crate::small_sort::sort(&mut endpoints);
6221 endpoints.dedup();
6222 crate::small_sort::sort(&mut variables);
6223 variables.dedup();
6224 sources.sort_unstable();
6225 sources.dedup();
6226 Some((endpoints, variables, sources, opts.link_capabilities()))
6227 } else {
6228 None
6229 }
6230 };
6231 let pkt = if let Some((endpoints, variables, sources, capabilities)) = summary {
6232 let address = self.local_address_advertisement(
6233 endpoints,
6234 variables,
6235 sources,
6236 capabilities,
6237 discovery::ADDRESS_STATE_REQUEST,
6238 );
6239 discovery::build_discovery_address(sender.as_ref(), now, &address)?
6240 } else {
6241 discovery::build_discovery_announce(sender.as_ref(), now, &[])?
6242 };
6243 self.emit_internal_tx(
6244 RouterTxItem::ToSide {
6245 src: None,
6246 dst: side,
6247 data: RouterItem::Packet(pkt),
6248 },
6249 true,
6250 true,
6251 )?;
6252 self.state
6253 .lock()
6254 .discovery_side_throttle
6255 .entry(side)
6256 .or_default()
6257 .next_ping_ms = now.saturating_add(discovery::DISCOVERY_SLOW_INTERVAL_MS);
6258 }
6259 Ok(())
6260 }
6261
6262 #[cfg(feature = "discovery")]
6263 fn emit_discovery_snapshot(
6264 &self,
6265 called_from_queue: bool,
6266 include_schema: bool,
6267 include_topology: bool,
6268 skip_in_flight_sides: bool,
6269 ) -> TelemetryResult<()> {
6270 self.emit_discovery_snapshot_on_side(
6271 called_from_queue,
6272 include_schema,
6273 include_topology,
6274 skip_in_flight_sides,
6275 None,
6276 )
6277 }
6278
6279 #[cfg(feature = "discovery")]
6280 fn emit_discovery_snapshot_on_side(
6281 &self,
6282 called_from_queue: bool,
6283 include_schema: bool,
6284 include_topology: bool,
6285 skip_in_flight_sides: bool,
6286 requested_side: Option<RouterSideId>,
6287 ) -> TelemetryResult<()> {
6288 if requested_side.is_none() {
6289 let count = self.state.lock().sides.len();
6290 for side in 0..count {
6291 if self.state.lock().sides[side].is_some() {
6292 self.emit_discovery_snapshot_on_side(
6293 called_from_queue,
6294 include_schema,
6295 include_topology,
6296 skip_in_flight_sides,
6297 Some(side),
6298 )?;
6299 }
6300 }
6301 return Ok(());
6302 }
6303 let (scratch, largest) = if include_schema {
6304 (8192, 2048)
6305 } else {
6306 let now = self.clock.now_ms();
6307 let sender = self.sender_arc();
6308 let st = self.state.lock();
6309 let election_required = self
6310 .preferred_discovery_master_present_locked(&st, now)
6311 .is_none();
6312 let snapshot = discovery::topology_snapshot_scratch(
6313 sender.as_ref(),
6314 self.cfg.handlers.len(),
6315 st.managed_variable_types.len().saturating_add(
6316 st.discovery_routes
6317 .iter()
6318 .filter(|(side, _)| Some(**side) != requested_side)
6319 .map(|(_, r)| r.reachable_network_variables.len())
6320 .sum::<usize>(),
6321 ),
6322 4,
6323 true,
6324 st.discovery_routes
6325 .iter()
6326 .filter(|(side, route)| {
6327 (Some(**side) != requested_side)
6328 && now.saturating_sub(route.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
6329 })
6330 .flat_map(|(_, route)| route.announcers.iter())
6331 .filter(|(_, peer)| {
6332 now.saturating_sub(peer.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
6333 })
6334 .map(|(name, peer)| {
6335 (
6336 name.as_str(),
6337 peer.reachable.as_slice(),
6338 peer.reachable_timesync_sources.as_slice(),
6339 peer.topology_boards.as_slice(),
6340 )
6341 }),
6342 );
6343 if !election_required {
6344 snapshot
6345 } else {
6346 let election = discovery::topology_snapshot_scratch(
6347 sender.as_ref(),
6348 self.cfg.handlers.len(),
6349 0,
6350 2,
6351 false,
6352 st.discovery_routes
6353 .values()
6354 .filter(|route| {
6355 now.saturating_sub(route.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
6356 })
6357 .flat_map(|route| route.announcers.iter())
6358 .filter(|(_, peer)| {
6359 now.saturating_sub(peer.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
6360 })
6361 .map(|(name, peer)| {
6362 (
6363 name.as_str(),
6364 peer.reachable.as_slice(),
6365 peer.reachable_timesync_sources.as_slice(),
6366 peer.topology_boards.as_slice(),
6367 )
6368 }),
6369 );
6370 (snapshot.0.max(election.0), snapshot.1.max(election.1))
6371 }
6372 };
6373 while let Err(err) = crate::memory_admission::check(scratch, largest) {
6378 if matches!(err, TelemetryError::Io("memory pressure"))
6379 && crate::memory_admission::check(512, 256).is_ok()
6380 {
6381 let mut st = self.state.lock();
6382 if let Some(victim) = st.lower_priority_shared_queue(199)
6383 && st.pop_shared_queue_item(victim)
6384 {
6385 continue;
6386 }
6387 }
6388 if !include_schema && matches!(err, TelemetryError::Io("memory pressure")) {
6389 return self.queue_pressure_keepalives(requested_side);
6390 }
6391 return Err(err);
6392 }
6393 let now_ms = self.clock.now_ms();
6394 let per_side = {
6395 let mut st = self.state.lock();
6396 if Self::prune_discovery_routes_locked(&mut st, now_ms) {
6397 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
6398 Self::note_discovery_topology_change_locked(&mut st, now_ms);
6399 }
6400 st.fit_discovery_budget();
6401 let local_is_master =
6402 self.discovery_master_sender_locked(&st, now_ms) == self.sender_arc().as_ref();
6403 let mut per_side = Vec::new();
6404 for side_id in 0..st.sides.len() {
6405 let Some(side) = st.sides[side_id].as_ref() else {
6406 continue;
6407 };
6408 let opts = side.opts.clone();
6409 let link_local_enabled = opts.link_local_enabled;
6410 if requested_side.is_some_and(|requested| requested != side_id) {
6411 continue;
6412 }
6413 let discovery_in_flight = skip_in_flight_sides
6414 && st.reliable_tx.iter().any(|((pending_side, ty), tx_state)| {
6415 *pending_side == side_id
6416 && !tx_state.sent.is_empty()
6417 && DataType::try_from_u32(*ty).is_some_and(discovery::is_discovery_type)
6418 });
6419 if !self.route_allowed_locked(&st, None, Some(DataType::DiscoveryAnnounce), side_id)
6420 {
6421 continue;
6422 }
6423 let level = if discovery_in_flight {
6427 let throttle = st.discovery_side_throttle.entry(side_id).or_default();
6428 if !throttle.liveness_due(now_ms) {
6429 None
6430 } else {
6431 throttle.next_ping_ms =
6432 now_ms.saturating_add(DISCOVERY_SLOW_LINK_PING_INTERVAL_MS);
6433 Some(DiscoveryAdvertiseLevel::MinimalPing)
6434 }
6435 } else {
6436 Self::discovery_level_for_side_locked(&mut st, side_id, now_ms)
6437 };
6438 let Some(level) = level else {
6439 continue;
6440 };
6441 let include_side_topology = include_topology
6442 && self.route_allowed_locked(
6443 &st,
6444 None,
6445 Some(DataType::DiscoveryTopology),
6446 side_id,
6447 );
6448 let capabilities = opts.link_capabilities();
6449 if level == DiscoveryAdvertiseLevel::MinimalPing {
6450 per_side.push((
6451 side_id,
6452 level,
6453 Vec::new(),
6454 Vec::new(),
6455 Vec::new(),
6456 Vec::new(),
6457 Vec::new(),
6458 capabilities,
6459 local_is_master,
6460 ));
6461 continue;
6462 }
6463 let current_topology = if include_side_topology {
6464 self.advertised_discovery_topology_for_link_locked(
6465 &st,
6466 now_ms,
6467 link_local_enabled,
6468 Some(side_id),
6469 )
6470 } else {
6471 Vec::new()
6472 };
6473 let (endpoints, timesync_sources) = if include_side_topology {
6474 self.advertised_summaries_from_topology_locked(
6475 &st,
6476 now_ms,
6477 link_local_enabled,
6478 Some(side_id),
6479 ¤t_topology,
6480 )
6481 } else {
6482 (
6483 self.advertised_discovery_endpoints_for_link_locked(
6484 &st,
6485 now_ms,
6486 link_local_enabled,
6487 Some(side_id),
6488 ),
6489 self.advertised_discovery_timesync_sources_for_link_locked(
6490 &st,
6491 now_ms,
6492 Some(side_id),
6493 ),
6494 )
6495 };
6496 let (topology, removed_topology) = match level {
6497 DiscoveryAdvertiseLevel::Full => (current_topology.clone(), Vec::new()),
6498 DiscoveryAdvertiseLevel::Incremental => {
6499 let previous = &st
6500 .discovery_side_throttle
6501 .get(&side_id)
6502 .expect("discovery throttle exists")
6503 .last_topology;
6504 discovery::topology_delta(previous, ¤t_topology)
6505 }
6506 DiscoveryAdvertiseLevel::MinimalPing => (Vec::new(), Vec::new()),
6507 };
6508 let throttle = st
6509 .discovery_side_throttle
6510 .get_mut(&side_id)
6511 .expect("discovery throttle exists");
6512 if level == DiscoveryAdvertiseLevel::Full
6513 || (level == DiscoveryAdvertiseLevel::Incremental
6514 && throttle.incremental_retries_remaining == 0)
6515 {
6516 throttle.last_topology = current_topology;
6517 }
6518 per_side.push((
6519 side_id,
6520 level,
6521 endpoints,
6522 self.advertised_network_variables_for_link_locked(&st, now_ms, Some(side_id)),
6523 timesync_sources,
6524 topology,
6525 removed_topology,
6526 capabilities,
6527 local_is_master,
6528 ));
6529 }
6530 per_side
6531 };
6532 for (
6533 side_id,
6534 level,
6535 endpoints,
6536 network_variables,
6537 timesync_sources,
6538 topology,
6539 removed_topology,
6540 capabilities,
6541 local_is_master,
6542 ) in per_side
6543 {
6544 let sender = self.sender_arc();
6545 if matches!(
6546 level,
6547 DiscoveryAdvertiseLevel::Full | DiscoveryAdvertiseLevel::Incremental
6548 ) {
6549 let address = self.local_address_advertisement(
6550 endpoints.clone(),
6551 network_variables.clone(),
6552 timesync_sources.clone(),
6553 capabilities,
6554 if local_is_master {
6555 discovery::ADDRESS_STATE_APPROVED
6556 } else {
6557 discovery::ADDRESS_STATE_REQUEST
6558 },
6559 );
6560 let pkt = discovery::build_discovery_address(sender.as_ref(), now_ms, &address)?;
6561 self.emit_internal_tx(
6562 RouterTxItem::ToSide {
6563 src: None,
6564 dst: side_id,
6565 data: RouterItem::Packet(pkt),
6566 },
6567 true,
6568 called_from_queue,
6569 )?;
6570 }
6571 if level == DiscoveryAdvertiseLevel::MinimalPing {
6572 let pkt = discovery::build_discovery_announce(
6573 sender.as_ref(),
6574 now_ms,
6575 endpoints.as_slice(),
6576 )?;
6577 self.emit_internal_tx(
6578 RouterTxItem::ToSide {
6579 src: None,
6580 dst: side_id,
6581 data: RouterItem::Packet(pkt),
6582 },
6583 true,
6584 called_from_queue,
6585 )?;
6586 }
6587 if include_topology
6588 && (!topology.is_empty() || !removed_topology.is_empty())
6589 && level != DiscoveryAdvertiseLevel::MinimalPing
6590 {
6591 let pkt = if level == DiscoveryAdvertiseLevel::Full {
6592 discovery::build_discovery_topology(sender.as_ref(), now_ms, &topology)?
6593 } else {
6594 discovery::build_discovery_topology_delta(
6595 sender.as_ref(),
6596 now_ms,
6597 &topology,
6598 &removed_topology,
6599 )?
6600 };
6601 self.emit_internal_tx(
6602 RouterTxItem::ToSide {
6603 src: None,
6604 dst: side_id,
6605 data: RouterItem::Packet(pkt),
6606 },
6607 true,
6608 called_from_queue,
6609 )?;
6610 }
6611 drop((
6614 endpoints,
6615 network_variables,
6616 timesync_sources,
6617 topology,
6618 removed_topology,
6619 ));
6620 if include_schema && level == DiscoveryAdvertiseLevel::Full {
6625 let pkt = discovery::build_discovery_schema(sender.as_ref(), now_ms)?;
6626 self.emit_internal_tx(
6627 RouterTxItem::ToSide {
6628 src: None,
6629 dst: side_id,
6630 data: RouterItem::Packet(pkt),
6631 },
6632 true,
6633 called_from_queue,
6634 )?;
6635 }
6636 }
6637 Ok(())
6638 }
6639
6640 #[cfg(feature = "discovery")]
6641 fn queue_discovery_announce(
6642 &self,
6643 include_schema: bool,
6644 skip_in_flight_sides: bool,
6645 ) -> TelemetryResult<()> {
6646 let now_ms = self.clock.now_ms();
6647 {
6648 let mut st = self.state.lock();
6649 if Self::prune_discovery_routes_locked(&mut st, now_ms) {
6650 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
6651 Self::note_discovery_topology_change_locked(&mut st, now_ms);
6652 }
6653 st.fit_discovery_budget();
6654 if st.sides.iter().all(|side| side.is_none()) {
6655 return Ok(());
6656 }
6657 if include_schema {
6660 for throttle in st.discovery_side_throttle.values_mut() {
6661 throttle.has_sent_full = false;
6662 }
6663 }
6664 st.discovery_cadence.on_announce_sent(now_ms);
6665 }
6666 self.emit_discovery_snapshot(true, include_schema, true, skip_in_flight_sides)
6667 }
6668
6669 #[cfg(feature = "discovery")]
6670 fn note_schema_received(&self, side: RouterSideId, sender: &str) {
6671 let mut st = self.state.lock();
6672 let address = sender
6676 .strip_prefix("@addr:")
6677 .and_then(|value| value.parse::<u32>().ok());
6678 if let Some(route) = st.discovery_routes.get_mut(&side) {
6679 for (name, peer) in &mut route.announcers {
6680 if name == sender
6681 || address.is_some_and(|address| {
6682 peer.advertised_address == Some(address)
6683 || sender_address_u32(name) == address
6684 })
6685 {
6686 peer.has_schema = true;
6687 break;
6688 }
6689 }
6690 }
6691 }
6692
6693 #[cfg(feature = "discovery")]
6694 fn poll_discovery_announce(&self) -> TelemetryResult<bool> {
6695 let now_ms = self.clock.now_ms();
6696 let recovery_sides = {
6700 let mut st = self.state.lock();
6701 let mut due = Vec::new();
6702 for side in 0..st.sides.len() {
6703 let missing = st.discovery_routes.get(&side).is_some_and(|route| {
6704 route.announcers.iter().any(|(name, peer)| {
6705 !peer.has_full_topology
6706 && now_ms.saturating_sub(peer.last_seen_ms) < DISCOVERY_ROUTE_TTL_MS
6707 && !route.announcers.values().any(|bridge| {
6708 bridge.has_full_topology
6709 && now_ms.saturating_sub(bridge.last_seen_ms)
6710 < DISCOVERY_ROUTE_TTL_MS
6711 && bridge
6712 .topology_boards
6713 .iter()
6714 .any(|board| board.sender_id == *name)
6715 })
6716 })
6717 });
6718 if !missing {
6719 continue;
6720 }
6721 let throttle = st.discovery_side_throttle.entry(side).or_default();
6722 if throttle.next_topology_request_ms == 0 {
6723 throttle.next_topology_request_ms =
6724 now_ms.saturating_add(discovery::DISCOVERY_BASELINE_GRACE_MS);
6725 } else if now_ms >= throttle.next_topology_request_ms {
6726 throttle.next_topology_request_ms =
6727 now_ms.saturating_add(discovery::DISCOVERY_BASELINE_RETRY_MS);
6728 due.push(side);
6729 }
6730 }
6731 due
6732 };
6733 let recovering = !recovery_sides.is_empty();
6734 for side in recovery_sides {
6735 let pkt =
6736 discovery::build_discovery_topology_request(self.sender_arc().as_ref(), now_ms)?;
6737 self.emit_internal_tx(
6738 RouterTxItem::ToSide {
6739 src: None,
6740 dst: side,
6741 data: RouterItem::Packet(pkt),
6742 },
6743 true,
6744 true,
6745 )?;
6746 }
6747 let schema_recovery_sides = {
6748 let mut st = self.state.lock();
6749 let mut due = Vec::new();
6750 for side in 0..st.sides.len() {
6751 let missing = st.discovery_routes.get(&side).is_some_and(|route| {
6752 route.announcers.values().any(|peer| {
6753 peer.has_full_topology
6754 && !peer.has_schema
6755 && now_ms.saturating_sub(peer.last_seen_ms) < DISCOVERY_ROUTE_TTL_MS
6756 })
6757 });
6758 if !missing {
6759 continue;
6760 }
6761 let throttle = st.discovery_side_throttle.entry(side).or_default();
6762 if throttle.next_schema_request_ms == 0 {
6763 throttle.next_schema_request_ms =
6764 now_ms.saturating_add(discovery::DISCOVERY_BASELINE_GRACE_MS);
6765 } else if now_ms >= throttle.next_schema_request_ms {
6766 throttle.next_schema_request_ms =
6767 now_ms.saturating_add(discovery::DISCOVERY_BASELINE_RETRY_MS);
6768 due.push(side);
6769 }
6770 }
6771 due
6772 };
6773 let recovering = recovering || !schema_recovery_sides.is_empty();
6774 for side in schema_recovery_sides {
6775 let pkt =
6776 discovery::build_discovery_schema_request(self.sender_arc().as_ref(), now_ms)?;
6777 self.emit_internal_tx(
6778 RouterTxItem::ToSide {
6779 src: None,
6780 dst: side,
6781 data: RouterItem::Packet(pkt),
6782 },
6783 true,
6784 true,
6785 )?;
6786 }
6787 let due = {
6788 let mut st = self.state.lock();
6789 let removed = Self::prune_discovery_routes_locked(&mut st, now_ms);
6790 if removed {
6791 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
6792 Self::note_discovery_topology_change_locked(&mut st, now_ms);
6793 }
6794 st.fit_discovery_budget();
6795 let has_any = st.sides.iter().enumerate().any(|(side_id, side)| {
6796 let Some(side) = side.as_ref() else {
6797 return false;
6798 };
6799 if !self.route_allowed_locked(&st, None, Some(DataType::DiscoveryAnnounce), side_id)
6800 {
6801 return false;
6802 }
6803 let _ = side;
6804 let in_flight = st.reliable_tx.iter().any(|((pending_side, ty), tx_state)| {
6805 *pending_side == side_id
6806 && !tx_state.sent.is_empty()
6807 && DataType::try_from_u32(*ty).is_some_and(discovery::is_discovery_type)
6808 });
6809 st.discovery_side_throttle
6810 .get(&side_id)
6811 .is_none_or(|throttle| {
6812 (!in_flight && (!throttle.has_sent_full || throttle.pending_incremental))
6813 || throttle.liveness_due(now_ms)
6814 })
6815 });
6816 if st.sides.is_empty() || !has_any {
6817 return Ok(recovering);
6818 }
6819 st.discovery_cadence.due(now_ms)
6820 };
6821 if !due {
6822 return Ok(recovering);
6823 }
6824 self.queue_discovery_announce(false, true)?;
6833 Ok(true)
6834 }
6835
6836 #[cfg(feature = "discovery")]
6837 fn learn_discovery_packet(
6838 &self,
6839 pkt: &Packet,
6840 src: Option<RouterSideId>,
6841 called_from_queue: bool,
6842 ) -> TelemetryResult<bool> {
6843 if !discovery::is_discovery_type(pkt.data_type()) {
6844 return Ok(false);
6845 }
6846 let Some(side) = src else {
6847 return Ok(true);
6848 };
6849 if self.refresh_known_discovery_liveness(pkt, side)? {
6850 return Ok(true);
6851 }
6852 #[cfg(not(feature = "std"))]
6853 if pkt.data_type() == DataType::DiscoverySchema
6854 && discovery::discovery_schema_payload_is_fully_known(pkt.payload())?
6855 {
6856 self.note_schema_received(side, pkt.sender());
6857 return Ok(true);
6858 }
6859 if matches!(
6863 pkt.data_type(),
6864 DataType::DiscoveryTopologyRequest | DataType::DiscoverySchemaRequest
6865 ) {
6866 crate::memory_admission::check(1024, 256)?;
6867 if let Some(transport) = self.state.lock().side_transport.get_mut(&side) {
6872 transport.refresh_transmit_header_templates();
6873 }
6874 }
6875 if matches!(
6876 pkt.data_type(),
6877 DataType::DiscoveryTopologyRequest | DataType::DiscoverySchemaRequest
6878 ) {
6879 let now_ms = self.clock.now_ms();
6880 {
6881 let mut st = self.state.lock();
6882 if Self::prune_discovery_routes_locked(&mut st, now_ms) {
6883 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
6884 Self::note_discovery_topology_change_locked(&mut st, now_ms);
6885 }
6886 st.discovery_side_throttle
6887 .entry(side)
6888 .or_default()
6889 .has_sent_full = false;
6890 }
6891 self.emit_discovery_snapshot_on_side(
6896 called_from_queue,
6897 pkt.data_type() == DataType::DiscoverySchemaRequest,
6898 true,
6899 false,
6900 Some(side),
6901 )?;
6902 return Ok(true);
6903 }
6904 let retained_summary = if matches!(
6905 pkt.data_type(),
6906 DataType::DiscoveryAddress
6907 | DataType::DiscoveryTopology
6908 | DataType::DiscoveryAnnounce
6909 | DataType::DiscoveryTimeSyncSources
6910 | DataType::ManagedVariableRequest
6911 ) {
6912 self.state
6913 .lock()
6914 .discovery_routes
6915 .get(&side)
6916 .map_or(0, |route| {
6917 RouterInner::discovery_route_byte_cost(side, route)
6918 })
6919 } else {
6920 0
6921 };
6922 let (decode_bytes, largest_block) = if pkt.data_type() == DataType::ManagedVariableRequest {
6923 let ty = discovery::decode_managed_variable_request(pkt)?;
6924 let existing = self
6925 .state
6926 .lock()
6927 .discovery_routes
6928 .get(&side)
6929 .is_some_and(|route| {
6930 route
6931 .requested_network_variables
6932 .iter()
6933 .any(|(id, _)| *id == ty)
6934 });
6935 if existing {
6936 (1024, 256)
6937 } else {
6938 (
6941 2048usize.saturating_add(retained_summary.saturating_mul(2)),
6942 retained_summary
6943 .checked_next_power_of_two()
6944 .unwrap_or(usize::MAX)
6945 .max(1024),
6946 )
6947 }
6948 } else {
6949 let scratch = if pkt.data_type() == DataType::DiscoveryAddress {
6950 4096usize.saturating_add(retained_summary)
6951 } else {
6952 8192usize.saturating_add(retained_summary)
6953 };
6954 let decode_bytes = pkt
6955 .payload()
6956 .len()
6957 .saturating_mul(4)
6958 .saturating_add(scratch);
6959 let largest_block = pkt.payload().len().saturating_mul(2).max(2048);
6960 (decode_bytes, largest_block)
6961 };
6962 while let Err(err) = crate::memory_admission::check(decode_bytes, largest_block) {
6963 if !matches!(err, TelemetryError::Io("memory pressure")) {
6964 return Err(err);
6965 }
6966 let mut st = self.state.lock();
6967 let Some(victim) = st.lower_priority_shared_queue(255) else {
6968 return Err(err);
6969 };
6970 if !st.pop_shared_queue_item(victim) {
6971 return Err(err);
6972 }
6973 }
6974 if pkt.data_type() == DataType::DiscoveryAddress {
6975 let mut ad = discovery::decode_discovery_address(pkt)?;
6976 let identity = ad.clone();
6977 let mut changed = false;
6978 let sender_id = if ad.hostname.is_empty() {
6984 pkt.sender()
6985 } else {
6986 ad.hostname.as_str()
6987 };
6988 let now_ms = self.clock.now_ms();
6989 let mut st = self.state.lock();
6990 let side_link_local_enabled = st
6991 .sides
6992 .get(side)
6993 .and_then(|entry| entry.as_ref())
6994 .map(|side_ref| side_ref.opts.link_local_enabled)
6995 .unwrap_or(false);
6996 if !side_link_local_enabled {
6997 ad.reachable_endpoints.retain(|ep| !ep.is_link_local_only());
6998 }
6999 let mut route = st.discovery_routes.get(&side).cloned().unwrap_or_default();
7000 let previously_reachable_network_variables = route.reachable_network_variables.clone();
7001 if pkt.sender() != sender_id {
7002 route.announcers.remove(pkt.sender());
7003 }
7004 let mut sender_state =
7005 core::mem::take(route.announcers.entry(sender_id.to_string()).or_default());
7006 if sender_state.advertised_address != Some(ad.address) {
7012 sender_state.advertised_address = Some(ad.address);
7013 changed = true;
7014 }
7015 if sender_state.advertised_reachable != ad.reachable_endpoints {
7016 sender_state.advertised_reachable = ad.reachable_endpoints;
7017 changed = true;
7018 }
7019 if sender_state.advertised_reachable_timesync_sources != ad.reachable_timesync_sources {
7020 sender_state.advertised_reachable_timesync_sources = ad.reachable_timesync_sources;
7021 changed = true;
7022 }
7023 if sender_state.reachable_network_variables != ad.reachable_network_variables {
7024 sender_state.reachable_network_variables = ad.reachable_network_variables;
7025 changed = true;
7026 }
7027 if sender_state.link_capabilities != Some(ad.link_capabilities) {
7028 sender_state.link_capabilities = Some(ad.link_capabilities);
7029 changed = true;
7030 }
7031 Self::refresh_sender_topology_state(&mut sender_state);
7032 sender_state.last_seen_ms = now_ms;
7033 route.announcers.insert(sender_id.to_string(), sender_state);
7034 Self::recompute_discovery_side_state(&mut route);
7035 let newly_reachable_network_variables: Vec<DataType> = route
7036 .reachable_network_variables
7037 .iter()
7038 .copied()
7039 .filter(|ty| !previously_reachable_network_variables.contains(ty))
7040 .collect();
7041 st.commit_discovery_route(side, route)?;
7042 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
7043 if changed {
7044 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
7045 }
7046 let replay: Vec<Packet> = newly_reachable_network_variables
7052 .iter()
7053 .filter(|ty| Self::managed_variable_permissions_locked(&st, **ty).write)
7054 .filter_map(|ty| {
7055 st.managed_variable_latest
7056 .get(&ty.as_u32())
7057 .map(|entry| entry.packet.clone())
7058 })
7059 .collect();
7060 drop(st);
7061 self.ingest_address_advertisement(identity)?;
7062 for value in replay {
7063 self.emit_internal_tx_with_priority(
7064 RouterTxItem::ToSide {
7065 src: None,
7066 dst: side,
7067 data: RouterItem::Packet(value),
7068 },
7069 true,
7070 crate::transport_priority(DataType::ManagedVariableValue),
7071 called_from_queue,
7072 )?;
7073 }
7074 return Ok(true);
7075 }
7076 let mut decoded_topology = if pkt.data_type() == DataType::DiscoveryTopology {
7077 Some(discovery::decode_discovery_topology_update(pkt)?)
7078 } else {
7079 None
7080 };
7081 let packet_sender = {
7082 let st = self.state.lock();
7083 let canonical = Self::canonical_sender_locked(&st, pkt.sender());
7084 if canonical != pkt.sender() {
7085 canonical
7086 } else if let Some(address) = pkt
7087 .sender()
7088 .strip_prefix("@addr:")
7089 .and_then(|value| value.parse::<u32>().ok())
7090 {
7091 decoded_topology
7097 .as_ref()
7098 .and_then(|update| {
7099 update
7100 .boards
7101 .iter()
7102 .find(|board| sender_address_u32(&board.sender_id) == address)
7103 })
7104 .map(|board| board.sender_id.clone())
7105 .unwrap_or(canonical)
7106 } else {
7107 canonical
7108 }
7109 };
7110 let local_sender = self.sender_arc();
7111 if packet_sender == local_sender.as_ref() {
7112 return Ok(true);
7113 }
7114
7115 if pkt.data_type() == DataType::ManagedVariableRequest {
7116 let ty = discovery::decode_managed_variable_request(pkt)?;
7117 {
7123 let now_ms = self.clock.now_ms();
7124 let mut st = self.state.lock();
7125 let existing = st.discovery_routes.get_mut(&side).and_then(|route| {
7129 let (_, last_seen_ms) = route
7130 .requested_network_variables
7131 .iter_mut()
7132 .find(|(requested, _)| *requested == ty)?;
7133 *last_seen_ms = now_ms;
7134 route.last_seen_ms = route.last_seen_ms.max(now_ms);
7135 Some(())
7136 });
7137 let newly_reachable = existing.is_none();
7138 if newly_reachable {
7139 let mut route = st.discovery_routes.get(&side).cloned().unwrap_or_default();
7140 route.requested_network_variables.push((ty, now_ms));
7141 Self::recompute_discovery_side_state(&mut route);
7142 st.commit_discovery_route(side, route)?;
7143 }
7144 if newly_reachable {
7145 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
7146 Self::note_discovery_topology_change_locked(&mut st, now_ms);
7147 }
7148 }
7149 if !self.can_write_managed_variable(ty) {
7150 return Ok(false);
7155 }
7156 if let Some(value) = self.managed_variable_latest(ty) {
7157 self.emit_internal_tx(
7158 RouterTxItem::ToSide {
7159 src: None,
7160 dst: side,
7161 data: RouterItem::Packet(value),
7162 },
7163 true,
7164 called_from_queue,
7165 )?;
7166 return Ok(true);
7167 }
7168 return Ok(false);
7171 }
7172 if pkt.data_type() == DataType::DiscoverySchema {
7173 let snapshot = discovery::decode_discovery_schema(pkt)?;
7174 let mut st = self.state.lock();
7183 let budget = st.schema_growth_budget();
7184 let report = crate::config::merge_owned_schema_snapshot_with_budget(snapshot, budget)?;
7185 if report.changed() {
7186 st.fit_discovery_budget();
7187 }
7188 drop(st);
7189 self.note_schema_received(side, &packet_sender);
7190 return Ok(true);
7191 }
7192 if pkt.data_type() == DataType::DiscoveryLinkCapabilities {
7193 let _ = discovery::decode_discovery_link_capabilities(pkt)?;
7194 return Ok(true);
7195 }
7196 let mut announce = if pkt.data_type() == DataType::DiscoveryAnnounce {
7197 Some(discovery::decode_discovery_announce(pkt)?)
7198 } else {
7199 None
7200 };
7201 let mut time_sources = if pkt.data_type() == DataType::DiscoveryTimeSyncSources {
7202 Some(discovery::decode_discovery_timesync_sources(pkt)?)
7203 } else {
7204 None
7205 };
7206 let mut st = self.state.lock();
7207 let now_ms = self.clock.now_ms();
7208 if pkt.data_type() == DataType::DiscoveryLeave {
7209 let leaving = packet_sender.as_str();
7210 let before = st.discovery_routes.clone();
7211 for route in st.discovery_routes.values_mut() {
7212 route.announcers.remove(leaving);
7213 for sender_state in route.announcers.values_mut() {
7214 sender_state
7215 .topology_boards
7216 .retain(|board| board.sender_id != leaving);
7217 for board in sender_state.topology_boards.iter_mut() {
7218 board.connections.retain(|peer| peer != leaving);
7219 }
7220 Self::refresh_sender_topology_state(sender_state);
7221 }
7222 Self::recompute_discovery_side_state(route);
7223 }
7224 st.discovery_routes
7225 .retain(|_, route| !route.announcers.is_empty());
7226 if st.discovery_routes != before {
7227 Self::note_discovery_topology_change_locked(&mut st, now_ms);
7228 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
7229 }
7230 return Ok(true);
7231 }
7232 if let Some(update) = decoded_topology.as_mut() {
7234 for board in update.boards.iter_mut() {
7235 board.sender_id = Self::canonical_sender_locked(&st, &board.sender_id);
7236 for peer in board.connections.iter_mut() {
7237 *peer = Self::canonical_sender_locked(&st, peer);
7238 }
7239 }
7240 }
7241 let mut route = st.discovery_routes.get(&side).cloned().unwrap_or_default();
7242 let alias_state = if packet_sender != pkt.sender() {
7243 route.announcers.remove(pkt.sender())
7244 } else {
7245 None
7246 };
7247 let side_link_local_enabled = st
7248 .sides
7249 .get(side)
7250 .and_then(|entry| entry.as_ref())
7251 .map(|side_ref| side_ref.opts.link_local_enabled)
7252 .unwrap_or(false);
7253 let mut sender_state = route
7254 .announcers
7255 .get_mut(&packet_sender)
7256 .map(core::mem::take)
7257 .or(alias_state)
7258 .unwrap_or_default();
7259 let changed = match pkt.data_type() {
7260 DataType::DiscoveryAnnounce => {
7261 let mut reachable = announce.take().expect("decoded above");
7262 if reachable.is_empty() {
7267 false
7268 } else {
7269 if !side_link_local_enabled {
7270 reachable.retain(|ep| !ep.is_link_local_only());
7271 }
7272 let board = Self::sender_topology_board_mut(&mut sender_state, &packet_sender);
7273 let changed = board.reachable_endpoints != reachable;
7274 board.reachable_endpoints = reachable;
7275 Self::refresh_sender_topology_state(&mut sender_state);
7276 changed
7277 }
7278 }
7279 DataType::DiscoveryTimeSyncSources => {
7280 let sources = time_sources.take().expect("decoded above");
7281 let board = Self::sender_topology_board_mut(&mut sender_state, &packet_sender);
7282 let changed = board.reachable_timesync_sources != sources;
7283 board.reachable_timesync_sources = sources;
7284 Self::refresh_sender_topology_state(&mut sender_state);
7285 changed
7286 }
7287 DataType::DiscoveryTopology => {
7288 let mut update = decoded_topology
7289 .take()
7290 .expect("topology packet was decoded before route selection");
7291 if !side_link_local_enabled {
7292 for board in update.boards.iter_mut() {
7293 board
7294 .reachable_endpoints
7295 .retain(|ep| !ep.is_link_local_only());
7296 }
7297 }
7298 if !update.incremental {
7299 sender_state.has_full_topology = true;
7300 }
7301 let changed =
7302 discovery::apply_topology_update(&mut sender_state.topology_boards, update);
7303 Self::refresh_sender_topology_state(&mut sender_state);
7304 changed
7305 }
7306 DataType::DiscoverySchema => false,
7307 _ => false,
7308 };
7309 sender_state.last_seen_ms = now_ms;
7310 route.announcers.insert(packet_sender, sender_state);
7311 Self::recompute_discovery_side_state(&mut route);
7312 st.commit_discovery_route(side, route)?;
7313 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
7314 if changed {
7315 Self::note_discovery_topology_change_locked(&mut st, now_ms);
7316 }
7317 Ok(true)
7318 }
7319
7320 #[cfg(not(feature = "discovery"))]
7321 fn queue_discovery_announce(&self) -> TelemetryResult<()> {
7322 Ok(())
7323 }
7324
7325 #[cfg(not(feature = "discovery"))]
7326 fn poll_discovery_announce(&self) -> TelemetryResult<bool> {
7327 Ok(false)
7328 }
7329
7330 #[cfg(not(feature = "discovery"))]
7331 fn learn_discovery_packet(
7332 &self,
7333 _pkt: &Packet,
7334 _src: Option<RouterSideId>,
7335 _called_from_queue: bool,
7336 ) -> TelemetryResult<bool> {
7337 Ok(false)
7338 }
7339
7340 #[inline]
7341 fn reliable_key(side: RouterSideId, ty: DataType) -> (RouterSideId, u32) {
7342 (side, ty.as_u32())
7343 }
7344
7345 fn reliable_tx_state_mut<'a>(
7346 &'a self,
7347 st: &'a mut RouterInner,
7348 side: RouterSideId,
7349 ty: DataType,
7350 ) -> &'a mut ReliableTxState {
7351 let key = Self::reliable_key(side, ty);
7352 st.reliable_tx
7353 .entry(key)
7354 .or_insert_with(|| ReliableTxState {
7355 next_seq: 1,
7356 sent_order: VecDeque::new(),
7357 sent: BTreeMap::new(),
7358 })
7359 }
7360
7361 fn reliable_rx_state_mut<'a>(
7362 &'a self,
7363 st: &'a mut RouterInner,
7364 side: RouterSideId,
7365 ty: DataType,
7366 ) -> &'a mut ReliableRxState {
7367 let key = Self::reliable_key(side, ty);
7368 st.reliable_rx
7369 .entry(key)
7370 .or_insert_with(|| ReliableRxState {
7371 expected_seq: 1,
7372 buffered: BTreeMap::new(),
7373 })
7374 }
7375
7376 fn reliable_control_packet(
7377 &self,
7378 control_ty: DataType,
7379 ty: DataType,
7380 seq: u32,
7381 ) -> TelemetryResult<Packet> {
7382 let sender = self.sender_arc();
7383 Packet::new(
7384 control_ty,
7385 message_meta(control_ty).endpoints_ref(),
7386 sender.as_ref(),
7387 self.packet_timestamp_ms(),
7388 encode_slice_le(&[ty.as_u32(), seq]),
7389 )
7390 }
7391
7392 fn queue_reliable_ack(
7393 &self,
7394 side: RouterSideId,
7395 ty: DataType,
7396 seq: u32,
7397 called_from_queue: bool,
7398 ) -> TelemetryResult<()> {
7399 let pkt = self.reliable_control_packet(DataType::ReliableAck, ty, seq)?;
7400 self.emit_internal_tx_with_priority(
7401 RouterTxItem::ToSide {
7402 src: None,
7403 dst: side,
7404 data: RouterItem::Packet(pkt),
7405 },
7406 true,
7407 message_priority(DataType::ReliableAck),
7408 called_from_queue,
7409 )
7410 }
7411
7412 fn queue_reliable_packet_request(
7413 &self,
7414 side: RouterSideId,
7415 ty: DataType,
7416 seq: u32,
7417 called_from_queue: bool,
7418 ) -> TelemetryResult<()> {
7419 let pkt = self.reliable_control_packet(DataType::ReliablePacketRequest, ty, seq)?;
7420 self.emit_internal_tx_with_priority(
7421 RouterTxItem::ToSide {
7422 src: None,
7423 dst: side,
7424 data: RouterItem::Packet(pkt),
7425 },
7426 true,
7427 message_priority(DataType::ReliablePacketRequest),
7428 called_from_queue,
7429 )
7430 }
7431
7432 fn queue_reliable_partial_ack(
7433 &self,
7434 side: RouterSideId,
7435 ty: DataType,
7436 seq: u32,
7437 called_from_queue: bool,
7438 ) -> TelemetryResult<()> {
7439 let pkt = self.reliable_control_packet(DataType::ReliablePartialAck, ty, seq)?;
7440 self.emit_internal_tx_with_priority(
7441 RouterTxItem::ToSide {
7442 src: None,
7443 dst: side,
7444 data: RouterItem::Packet(pkt),
7445 },
7446 true,
7447 message_priority(DataType::ReliablePartialAck),
7448 called_from_queue,
7449 )
7450 }
7451
7452 fn handle_reliable_ack(&self, side: RouterSideId, ty: DataType, ack: u32) {
7453 let mut st = self.state.lock();
7454 let tx_state = self.reliable_tx_state_mut(&mut st, side, ty);
7455 if matches!(reliable_mode(ty), crate::ReliableMode::Unordered) {
7456 tx_state.sent.remove(&ack);
7457 tx_state.sent_order.retain(|seq| *seq != ack);
7458 return;
7459 }
7460
7461 while let Some(seq) = tx_state.sent_order.front().copied() {
7462 if seq > ack {
7463 break;
7464 }
7465 tx_state.sent_order.pop_front();
7466 tx_state.sent.remove(&seq);
7467 }
7468 }
7469
7470 fn handle_reliable_partial_ack(&self, side: RouterSideId, ty: DataType, seq: u32) {
7471 let mut st = self.state.lock();
7472 let tx_state = self.reliable_tx_state_mut(&mut st, side, ty);
7473 if let Some(sent) = tx_state.sent.get_mut(&seq) {
7474 sent.partial_acked = true;
7475 }
7476 }
7477
7478 fn queue_reliable_retransmit(
7479 &self,
7480 side: RouterSideId,
7481 ty: DataType,
7482 seq: u32,
7483 called_from_queue: bool,
7484 ) -> TelemetryResult<()> {
7485 let mut queued = None;
7486 {
7487 let mut st = self.state.lock();
7488 let tx_state = self.reliable_tx_state_mut(&mut st, side, ty);
7489 if let Some(sent) = tx_state.sent.get_mut(&seq)
7490 && !sent.queued
7491 {
7492 sent.queued = true;
7493 sent.partial_acked = false;
7494 queued = Some(sent.bytes.clone());
7495 }
7496 }
7497
7498 if let Some(bytes) = queued {
7499 if called_from_queue {
7500 self.tx_queue_item_with_priority(
7501 RouterTxItem::ReliableReplay { dst: side, bytes },
7502 true,
7503 Self::router_item_priority_bumped(ty),
7504 )?;
7505 } else {
7506 self.tx_item_impl(
7507 RouterTxItem::ReliableReplay { dst: side, bytes },
7508 true,
7509 false,
7510 )?;
7511 }
7512 }
7513
7514 Ok(())
7515 }
7516
7517 fn send_reliable_raw_to_side(
7518 &self,
7519 side: RouterSideId,
7520 bytes: crate::SharedBytes,
7521 relayed: bool,
7522 ) -> TelemetryResult<()> {
7523 let handler = {
7524 let st = self.state.lock();
7525 let side_ref = Self::side_ref(&st, side)?;
7526 if !side_ref.opts.egress_enabled {
7527 return Ok(());
7528 }
7529 (side_ref.tx_handler.clone(), side_ref.opts.clone())
7530 };
7531
7532 let (handler, opts) = handler;
7533
7534 let Some(_side_tx_guard) = self.try_enter_side_tx() else {
7535 return Err(TelemetryError::Io("side tx busy"));
7536 };
7537 let started_ms = self.clock.now_ms();
7538 let ty = wire_format::peek_routing_envelope(bytes.as_ref())
7539 .map(|env| env.ty)
7540 .unwrap_or(DataType::ReliableAck);
7541 let priority = self.effective_transport_priority(ty);
7542 let result = match handler {
7543 packed @ (RouterTxHandlerFn::Packed(_) | RouterTxHandlerFn::PackedWithPriority(_)) => {
7544 let frames = self.encode_side_transport_frames(side, opts, bytes.clone())?;
7545 let mut attempts_total = 0usize;
7546 let mut sent_bytes = 0usize;
7547 self.send_side_frames_resumable(side, frames, |frame| {
7548 match self.retry_with_attempts(runtime_max_handler_retries(), || {
7549 Self::packed_handler_call(&packed, frame, priority)
7550 }) {
7551 Ok((_, attempts)) => {
7552 attempts_total = attempts_total.saturating_add(attempts);
7553 sent_bytes = sent_bytes.saturating_add(frame.len());
7554 }
7555 Err((err, attempts)) => {
7556 self.note_side_tx_failure(
7557 side,
7558 ty,
7559 attempts_total.saturating_add(attempts),
7560 );
7561 return Err(err);
7562 }
7563 }
7564
7565 Ok(())
7566 })?;
7567 self.record_side_tx_sample(side, sent_bytes, started_ms, self.clock.now_ms());
7568 self.note_side_tx_success(side, ty, sent_bytes, relayed, attempts_total);
7569 return Ok(());
7570 }
7571 RouterTxHandlerFn::Packet(f) => {
7572 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
7573 self.retry_with_attempts(runtime_max_handler_retries(), || f(&pkt))
7574 }
7575 };
7576 match result {
7577 Ok((_, attempts)) => {
7578 self.record_side_tx_sample(side, bytes.len(), started_ms, self.clock.now_ms());
7579 self.note_side_tx_success(side, ty, bytes.len(), relayed, attempts);
7580 Ok(())
7581 }
7582 Err((err, attempts)) => {
7583 self.note_side_tx_failure(side, ty, attempts);
7584 Err(err)
7585 }
7586 }
7587 }
7588
7589 #[allow(unused_mut)]
7590 fn send_reliable_to_side(
7591 &self,
7592 side: RouterSideId,
7593 data: RouterItem,
7594 relayed: bool,
7595 ) -> TelemetryResult<()> {
7596 let end_to_end_ordered = !self.item_target_senders(&data)?.is_empty()
7597 && matches!(
7598 reliable_mode(self.item_route_info(&data)?.1),
7599 crate::ReliableMode::Ordered
7600 );
7601 let (handler, opts, hop_reliable_enabled) = {
7602 let st = self.state.lock();
7603 let side_ref = Self::side_ref(&st, side)?;
7604 let opts = side_ref.opts.clone();
7605 let hop_reliable_enabled = opts.reliable_enabled
7606 && !end_to_end_ordered
7607 && self.cfg.reliable_enabled()
7608 && !self.side_has_multiple_announcers_locked(&st, side, self.clock.now_ms());
7609 (side_ref.tx_handler.clone(), opts, hop_reliable_enabled)
7610 };
7611
7612 if !matches!(
7613 &handler,
7614 RouterTxHandlerFn::Packed(_) | RouterTxHandlerFn::PackedWithPriority(_)
7615 ) {
7616 return self.call_side_tx_handler(side, &handler, &data, relayed);
7617 }
7618
7619 if !hop_reliable_enabled {
7620 let mut adjusted_opts = opts;
7621 adjusted_opts.reliable_enabled = false;
7622 let preserve_end_to_end_ack = true;
7627 if let Some(adjusted) =
7628 self.adjust_reliable_for_side(adjusted_opts, data, preserve_end_to_end_ack)?
7629 {
7630 return self.call_side_tx_handler(side, &handler, &adjusted, relayed);
7631 }
7632 return Ok(());
7633 }
7634
7635 let ty = match &data {
7636 RouterItem::Packet(pkt) => pkt.data_type(),
7637 RouterItem::Packed(bytes) => {
7638 wire_format::peek_routing_frame_info(bytes.as_ref())?
7639 .envelope
7640 .ty
7641 }
7642 };
7643 let priority = self.effective_transport_priority(ty);
7644
7645 if !is_reliable_type(ty) {
7646 if let Some(adjusted) = self.adjust_reliable_for_side(opts, data, true)? {
7647 self.call_side_tx_handler(side, &handler, &adjusted, relayed)?;
7648 }
7649 return Ok(());
7650 }
7651
7652 let (seq, flags) = {
7653 let mut st = self.state.lock();
7654 let tx_state = self.reliable_tx_state_mut(&mut st, side, ty);
7655 if tx_state.sent.len() >= runtime_reliable_max_pending() {
7656 return Err(TelemetryError::PacketTooLarge(
7657 "router reliable history full",
7658 ));
7659 }
7660 let seq = tx_state.next_seq;
7661 let next = tx_state.next_seq.wrapping_add(1);
7662 tx_state.next_seq = if next == 0 { 1 } else { next };
7663 let flags = match reliable_mode(ty) {
7664 crate::ReliableMode::Unordered => wire_format::RELIABLE_FLAG_UNORDERED,
7665 _ => 0,
7666 };
7667 (seq, flags)
7668 };
7669
7670 let mut bytes: crate::SharedBytes = match data {
7671 RouterItem::Packet(pkt) => self.pack_packet_for_router(
7672 &pkt,
7673 Some(wire_format::ReliableHeader { flags, seq, ack: 0 }),
7674 )?,
7675 RouterItem::Packed(bytes) => {
7676 #[cfg(feature = "cryptography")]
7677 if self.e2e_seal_config_for_type(ty).is_some() {
7678 self.prepare_packed_for_remote(
7679 bytes,
7680 Some(Some(wire_format::ReliableHeader { flags, seq, ack: 0 })),
7681 )?
7682 } else {
7683 let Some(rewritten) =
7684 wire_format::rewrite_reliable_header_owned(bytes.as_ref(), flags, seq, 0)?
7685 else {
7686 let Some(_side_tx_guard) = self.try_enter_side_tx() else {
7687 return Err(TelemetryError::Io("side tx busy"));
7688 };
7689 let started_ms = self.clock.now_ms();
7690 let frames =
7691 self.encode_side_transport_frames(side, opts, bytes.clone())?;
7692 let mut attempts_total = 0usize;
7693 let mut sent_bytes = 0usize;
7694 self.send_side_frames_resumable(side, frames, |frame| {
7695 match self.retry_with_attempts(runtime_max_handler_retries(), || {
7696 Self::packed_handler_call(&handler, frame, priority)
7697 }) {
7698 Ok((_, attempts)) => {
7699 attempts_total = attempts_total.saturating_add(attempts);
7700 sent_bytes = sent_bytes.saturating_add(frame.len());
7701 }
7702 Err((err, attempts)) => {
7703 self.note_side_tx_failure(
7704 side,
7705 ty,
7706 attempts_total.saturating_add(attempts),
7707 );
7708 return Err(err);
7709 }
7710 }
7711
7712 Ok(())
7713 })?;
7714 self.record_side_tx_sample(
7715 side,
7716 sent_bytes,
7717 started_ms,
7718 self.clock.now_ms(),
7719 );
7720 self.note_side_tx_success(side, ty, sent_bytes, relayed, attempts_total);
7721 return Ok(());
7722 };
7723 crate::shared_bytes::convert(rewritten)
7724 }
7725 #[cfg(not(feature = "cryptography"))]
7726 {
7727 let Some(rewritten) =
7728 wire_format::rewrite_reliable_header_owned(bytes.as_ref(), flags, seq, 0)?
7729 else {
7730 let Some(_side_tx_guard) = self.try_enter_side_tx() else {
7731 return Err(TelemetryError::Io("side tx busy"));
7732 };
7733 let started_ms = self.clock.now_ms();
7734 let frames =
7735 self.encode_side_transport_frames(side, opts, bytes.clone())?;
7736 let mut attempts_total = 0usize;
7737 let mut sent_bytes = 0usize;
7738 self.send_side_frames_resumable(side, frames, |frame| {
7739 match self.retry_with_attempts(runtime_max_handler_retries(), || {
7740 Self::packed_handler_call(&handler, frame, priority)
7741 }) {
7742 Ok((_, attempts)) => {
7743 attempts_total = attempts_total.saturating_add(attempts);
7744 sent_bytes = sent_bytes.saturating_add(frame.len());
7745 }
7746 Err((err, attempts)) => {
7747 self.note_side_tx_failure(
7748 side,
7749 ty,
7750 attempts_total.saturating_add(attempts),
7751 );
7752 return Err(err);
7753 }
7754 }
7755
7756 Ok(())
7757 })?;
7758 self.record_side_tx_sample(
7759 side,
7760 sent_bytes,
7761 started_ms,
7762 self.clock.now_ms(),
7763 );
7764 self.note_side_tx_success(side, ty, sent_bytes, relayed, attempts_total);
7765 return Ok(());
7766 };
7767 rewritten.into()
7768 }
7769 }
7770 };
7771
7772 let Some(_side_tx_guard) = self.try_enter_side_tx() else {
7773 return Err(TelemetryError::Io("side tx busy"));
7774 };
7775 let started_ms = self.clock.now_ms();
7776 let frames = self.encode_side_transport_frames(side, opts, bytes.clone())?;
7777 let mut attempts_total = 0usize;
7778 let mut sent_bytes = 0usize;
7779 self.send_side_frames_resumable(side, frames, |frame| {
7780 match self.retry_with_attempts(runtime_max_handler_retries(), || {
7781 Self::packed_handler_call(&handler, frame, priority)
7782 }) {
7783 Ok((_, attempts)) => {
7784 attempts_total = attempts_total.saturating_add(attempts);
7785 sent_bytes = sent_bytes.saturating_add(frame.len());
7786 }
7787 Err((err, attempts)) => {
7788 self.note_side_tx_failure(side, ty, attempts_total.saturating_add(attempts));
7789 return Err(err);
7790 }
7791 }
7792
7793 Ok(())
7794 })?;
7795 self.record_side_tx_sample(side, sent_bytes, started_ms, self.clock.now_ms());
7796 self.note_side_tx_success(side, ty, sent_bytes, relayed, attempts_total);
7797
7798 #[cfg(feature = "compact-packet-store")]
7799 {
7800 let _ = bytes.park_for_queue();
7801 }
7802
7803 {
7804 let mut st = self.state.lock();
7805 let tx_state = self.reliable_tx_state_mut(&mut st, side, ty);
7806 tx_state.sent_order.push_back(seq);
7807 tx_state.sent.insert(
7808 seq,
7809 ReliableSent {
7810 bytes: bytes.clone(),
7811 last_send_ms: self.clock.now_ms(),
7812 retries: 0,
7813 queued: false,
7814 partial_acked: false,
7815 },
7816 );
7817 }
7818
7819 Ok(())
7820 }
7821
7822 fn run_side_chunk_transfer<T>(
7823 &self,
7824 side: RouterSideId,
7825 key: u64,
7826 send: impl FnOnce(&mut usize) -> TelemetryResult<T>,
7827 ) -> TelemetryResult<T> {
7828 let mut next = {
7829 let mut st = self.state.lock();
7830 let transport = st
7831 .side_transport
7832 .get_mut(&side)
7833 .ok_or(TelemetryError::BadArg)?;
7834 if transport.tx_chunk_progress.is_none() {
7835 crate::memory_admission::check(
7836 512,
7837 core::mem::size_of::<crate::side_transport::SideTxProgress>(),
7838 )?;
7839 transport.tx_chunk_progress =
7840 Some(Box::new(crate::side_transport::SideTxProgress::default()));
7841 }
7842 transport
7843 .tx_chunk_progress
7844 .as_mut()
7845 .expect("initialized")
7846 .begin(key, self.clock.now_ms())?
7847 };
7848 let result = send(&mut next);
7849 if let Some(progress) = self
7850 .state
7851 .lock()
7852 .side_transport
7853 .get_mut(&side)
7854 .and_then(|transport| transport.tx_chunk_progress.as_mut())
7855 {
7856 progress.finish(key, next, self.clock.now_ms(), &result);
7857 }
7858 result
7859 }
7860
7861 fn send_side_frames_resumable(
7862 &self,
7863 side: RouterSideId,
7864 mut frames: SideTransportFrames,
7865 mut send: impl FnMut(&[u8]) -> TelemetryResult<()>,
7866 ) -> TelemetryResult<()> {
7867 let bootstraps_template = frames.bootstraps_template();
7868 let result = if frames.len() == 1 {
7869 frames.send_resuming(&mut 0, send)
7870 } else {
7871 let key = frames.progress_key();
7872 self.run_side_chunk_transfer(side, key, |next| frames.send_resuming(next, &mut send))
7873 };
7874 if result.is_err() && bootstraps_template {
7875 if let Some(state) = self.state.lock().side_transport.get_mut(&side) {
7880 state.refresh_transmit_header_templates();
7881 }
7882 }
7883 result
7884 }
7885
7886 fn split_side_transport_frame(
7887 &self,
7888 _side: RouterSideId,
7889 frame: crate::SharedBytes,
7890 max_frame_bytes: usize,
7891 ) -> TelemetryResult<SideTransportFrames> {
7892 SideTransportFrames::split(frame, max_frame_bytes, self.sender_arc().as_bytes())
7893 }
7894
7895 fn encode_side_transport_frames(
7896 &self,
7897 side: RouterSideId,
7898 opts: RouterSideOptions,
7899 raw: crate::SharedBytes,
7900 ) -> TelemetryResult<SideTransportFrames> {
7901 if !opts.header_template_enabled && opts.max_frame_bytes == 0 {
7902 return Ok(SideTransportFrames::single(raw));
7903 }
7904
7905 let raw_len = raw.len();
7906 if opts.max_frame_bytes != 0 && raw_len > opts.max_frame_bytes {
7907 let frames = self.split_side_transport_frame(side, raw, opts.max_frame_bytes)?;
7910 let mut st = self.state.lock();
7911 let stats = st.side_runtime_stats.entry(side).or_default();
7912 stats.note_side_transport_full(raw_len, frames.wire_len());
7913 stats.note_side_transport_chunks(frames.len());
7914 return Ok(frames);
7915 }
7916
7917 if opts.header_template_enabled {
7918 let frame = wire_format::peek_routing_frame_info(raw.as_ref())?;
7922 let without_dictionary = opts.max_side_transport_templates == 0 || {
7923 let st = self.state.lock();
7924 self.side_has_multiple_announcers_locked(&st, side, self.clock.now_ms())
7925 };
7926 let reliable = is_reliable_type(frame.envelope.ty)
7927 || frame.reliable.is_some()
7928 || matches!(
7929 frame.envelope.ty,
7930 crate::DataType::ReliableAck
7931 | crate::DataType::ReliablePartialAck
7932 | crate::DataType::ReliablePacketRequest
7933 );
7934 if without_dictionary || reliable {
7935 let mut st = self.state.lock();
7936 if without_dictionary {
7937 let removed = st
7938 .side_transport
7939 .get_mut(&side)
7940 .ok_or(TelemetryError::BadArg)?
7941 .trim_tx_templates_to(0);
7942 for _ in 0..removed {
7943 st.side_runtime_stats
7944 .entry(side)
7945 .or_default()
7946 .note_side_transport_template_eviction();
7947 }
7948 }
7949 st.side_runtime_stats
7950 .entry(side)
7951 .or_default()
7952 .note_side_transport_full(raw_len, raw_len);
7953 return Ok(SideTransportFrames::single(raw));
7954 }
7955 }
7956
7957 let mut compact_payload_len = None;
7958 let mut used_compact = false;
7959 let used_timestamp_delta = false;
7960 let omitted_timestamp = false;
7961 let wrapped = if opts.header_template_enabled {
7962 let (template, _ty, flags, data_size, timestamp, nonce, reliable_seq_ack, payload) =
7963 extract_side_header_template_view(raw.as_ref())?;
7964 if is_reliable_type(_ty)
7968 || reliable_seq_ack.is_some()
7969 || matches!(
7970 _ty,
7971 DataType::ReliableAck
7972 | DataType::ReliablePartialAck
7973 | DataType::ReliablePacketRequest
7974 )
7975 {
7976 let frames = if opts.max_frame_bytes != 0 && raw_len > opts.max_frame_bytes {
7977 self.split_side_transport_frame(side, raw, opts.max_frame_bytes)?
7978 } else {
7979 SideTransportFrames::single(raw)
7980 };
7981 let wire_len = frames.wire_len();
7982 let mut st = self.state.lock();
7983 let stats = st.side_runtime_stats.entry(side).or_default();
7984 stats.note_side_transport_full(raw_len, wire_len);
7985 if frames.len() > 1 {
7986 stats.note_side_transport_chunks(frames.len());
7987 }
7988 return Ok(frames);
7989 }
7990 let (template_id, use_compact, _previous_timestamp) = {
7991 let mut st = self.state.lock();
7992 let mut effective_template_limit = opts.max_side_transport_templates;
7997 #[cfg(feature = "discovery")]
7998 {
7999 if let Some(route) = st.discovery_routes.get(&side) {
8000 let active_peers = route
8001 .announcers
8002 .values()
8003 .filter(|sender| {
8004 self.clock.now_ms().saturating_sub(sender.last_seen_ms)
8005 <= DISCOVERY_ROUTE_TTL_MS
8006 })
8007 .count();
8008 if active_peers > 1 {
8016 effective_template_limit = 0;
8017 } else {
8018 effective_template_limit = route
8019 .announcers
8020 .values()
8021 .filter_map(|sender| sender.link_capabilities)
8022 .filter(|caps| {
8023 caps.flags & discovery::LINK_CAPABILITY_HEADER_TEMPLATES != 0
8024 && caps.max_side_transport_templates > 0
8025 })
8026 .map(|caps| caps.max_side_transport_templates as usize)
8027 .fold(effective_template_limit, core::cmp::min);
8028 }
8029 }
8030 }
8031 let trimmed = st
8032 .side_transport
8033 .get_mut(&side)
8034 .ok_or(TelemetryError::BadArg)?
8035 .trim_tx_templates_to(effective_template_limit);
8036 for _ in 0..trimmed {
8037 st.side_runtime_stats
8038 .entry(side)
8039 .or_default()
8040 .note_side_transport_template_eviction();
8041 }
8042 let side_state = st
8043 .side_transport
8044 .get_mut(&side)
8045 .ok_or(TelemetryError::BadArg)?;
8046 if let Some(id) = side_state.tx_template_ids.get(&template.hash).copied() {
8047 let previous = side_state.tx_last_timestamps.get(&id).copied();
8048 const FULL_REFRESH_AFTER_COMPACT_FRAMES: u8 = 8;
8051 let compact_uses = side_state.tx_compact_uses.entry(id).or_default();
8052 #[cfg(feature = "discovery")]
8054 let discovery_refresh = discovery::is_discovery_type(_ty);
8055 #[cfg(not(feature = "discovery"))]
8056 let discovery_refresh = false;
8057 let use_compact =
8058 *compact_uses < FULL_REFRESH_AFTER_COMPACT_FRAMES && !discovery_refresh;
8059 if use_compact {
8060 *compact_uses = compact_uses.saturating_add(1);
8061 } else {
8062 *compact_uses = 0;
8063 }
8064 (id, use_compact, previous)
8065 } else {
8066 let template_id = ((template.hash >> 32) as u32 ^ template.hash as u32).max(1);
8072 let evicted = side_state.insert_tx_template(
8073 template.into_owned(),
8074 template_id,
8075 effective_template_limit,
8076 );
8077 if evicted {
8078 st.side_runtime_stats
8079 .entry(side)
8080 .or_default()
8081 .note_side_transport_template_eviction();
8082 }
8083 if let Some(side_state) = st.side_transport.get_mut(&side)
8084 && effective_template_limit > 0
8085 {
8086 side_state.tx_last_timestamps.insert(template_id, timestamp);
8087 }
8088 (template_id, false, None)
8089 }
8090 };
8091 if use_compact {
8092 used_compact = true;
8093 compact_payload_len = Some(payload.len());
8094 let timestamp_field = Some(timestamp);
8097 let mut body = Vec::with_capacity(payload.len() + 32);
8098 body.push(flags);
8099 write_uleb128_local(u64::from(template_id), &mut body);
8100 write_uleb128_local(data_size, &mut body);
8101 if let Some(timestamp_field) = timestamp_field {
8102 write_uleb128_local(timestamp_field, &mut body);
8103 }
8104 if (flags & SIDE_TRANSPORT_FLAG_PACKET_NONCE) != 0 {
8105 write_uleb128_local(u64::from(nonce), &mut body);
8106 }
8107 if let Some((seq, ack)) = reliable_seq_ack {
8108 write_uleb128_local(u64::from(seq), &mut body);
8109 write_uleb128_local(u64::from(ack), &mut body);
8110 }
8111 body.extend_from_slice(payload);
8112 {
8113 let mut st = self.state.lock();
8114 if let Some(side_state) = st.side_transport.get_mut(&side) {
8115 side_state.tx_last_timestamps.insert(template_id, timestamp);
8116 }
8117 }
8118 let kind = if omitted_timestamp {
8119 SIDE_TRANSPORT_KIND_COMPACT_SAME_TIMESTAMP
8120 } else if used_timestamp_delta {
8121 SIDE_TRANSPORT_KIND_COMPACT_DELTA
8122 } else {
8123 SIDE_TRANSPORT_KIND_COMPACT
8124 };
8125 wrap_side_transport_frame(kind, &body)
8126 } else {
8127 let mut body = Vec::with_capacity(raw.len() + 4);
8128 write_uleb128_local(u64::from(template_id), &mut body);
8129 body.extend_from_slice(raw.as_ref());
8130 wrap_side_transport_frame(SIDE_TRANSPORT_KIND_FULL, &body)
8131 }
8132 } else {
8133 wrap_side_transport_frame(SIDE_TRANSPORT_KIND_FULL, raw.as_ref())
8134 };
8135
8136 let frames = if opts.max_frame_bytes != 0 && wrapped.len() > opts.max_frame_bytes {
8137 self.split_side_transport_frame(side, wrapped, opts.max_frame_bytes)
8138 } else {
8139 Ok(SideTransportFrames::single(wrapped))
8140 }?;
8141 let wire_len = frames.wire_len();
8142 let mut st = self.state.lock();
8143 let stats = st.side_runtime_stats.entry(side).or_default();
8144 if used_compact {
8145 let overhead = compact_payload_len
8146 .map(|payload_len| wire_len.saturating_sub(payload_len))
8147 .unwrap_or(wire_len);
8148 stats.note_side_transport_compact(
8149 raw_len,
8150 wire_len,
8151 overhead,
8152 used_timestamp_delta,
8153 omitted_timestamp,
8154 );
8155 if opts.compact_header_target_bytes != 0 && overhead > opts.compact_header_target_bytes
8156 {
8157 stats.note_side_transport_compact_target_miss();
8158 }
8159 } else {
8160 stats.note_side_transport_full(raw_len, wire_len);
8161 }
8162 if frames.len() > 1 {
8163 stats.note_side_transport_chunks(frames.len());
8164 }
8165 Ok(frames)
8166 }
8167
8168 fn expire_side_chunks(&self) {
8169 let now = self.clock.now_ms();
8170 let mut st = self.state.lock();
8171 for side in st.side_transport.values_mut() {
8172 side.rx_chunks
8173 .retain(|_, assembly| now.saturating_sub(assembly.last_seen_ms) <= 2000);
8174 }
8175 }
8176
8177 #[cfg(feature = "discovery")]
8181 fn refresh_ingress_peer_liveness(&self, side: RouterSideId, bytes: &[u8]) {
8182 let Ok(frame) = wire_format::peek_routing_frame_info(bytes) else {
8183 return;
8184 };
8185 let address = frame.envelope.source_address;
8186 self.refresh_peer_liveness(side, address);
8187 }
8188
8189 #[cfg(feature = "discovery")]
8190 #[cfg_attr(not(feature = "std"), inline(never))]
8191 fn refresh_peer_liveness(&self, side: RouterSideId, address: u32) {
8192 let now = self.clock.now_ms();
8193 let mut st = self.state.lock();
8194 let Some(route) = st.discovery_routes.get_mut(&side) else {
8195 return;
8196 };
8197 let mut found = None;
8198 for (name, peer) in &mut route.announcers {
8199 if peer.advertised_address == Some(address) || sender_address_u32(name) == address {
8200 if found.is_some() {
8202 return;
8203 }
8204 found = Some(peer);
8205 }
8206 }
8207 let Some(peer) = found else {
8208 return;
8209 };
8210 peer.last_seen_ms = now;
8211 route.last_seen_ms = route.last_seen_ms.max(now);
8212 }
8213
8214 #[cfg(all(feature = "discovery", not(feature = "std")))]
8215 fn consume_known_packed_schema(
8216 &self,
8217 side: RouterSideId,
8218 bytes: &[u8],
8219 ) -> TelemetryResult<bool> {
8220 let Some((address, payload)) = wire_format::peek_plain_schema_payload(bytes)? else {
8221 return Ok(false);
8222 };
8223 if !discovery::discovery_schema_payload_is_fully_known(payload)? {
8224 return Ok(false);
8225 }
8226 let mut st = self.state.lock();
8229 if let Some(route) = st.discovery_routes.get_mut(&side) {
8230 for (name, peer) in &mut route.announcers {
8231 if peer.advertised_address == Some(address) || sender_address_u32(name) == address {
8232 peer.has_schema = true;
8233 break;
8234 }
8235 }
8236 }
8237 Ok(true)
8238 }
8239
8240 #[cfg(feature = "discovery")]
8243 fn cached_transit_ready(&self, side: RouterSideId, bytes: &[u8]) -> TelemetryResult<bool> {
8244 if bytes.len() > 128 || bytes.first().is_none_or(|flags| flags & 0x15 != 0) {
8245 return Ok(false);
8246 }
8247 let frame = wire_format::peek_routing_frame_info(bytes)?;
8248 let ty = frame.envelope.ty;
8249 if is_internal_control_type(ty)
8250 || frame.envelope.is_reliable
8251 || frame.reliable.is_some()
8252 || frame.envelope.target_senders().len() != 0
8253 || !(1..=3).contains(&frame.envelope.endpoints().take(4).count())
8254 {
8255 return Ok(false);
8256 }
8257 if self.is_managed_variable_type(ty)
8258 || frame.envelope.endpoints().any(|ep| {
8259 self.endpoint_has_packet_handler(ep) || self.endpoint_has_packed_handler(ep)
8260 })
8261 {
8262 return Ok(false);
8263 }
8264
8265 let (template, ..) = extract_side_header_template_view(bytes)?;
8266 let st = self.state.lock();
8267 let active: usize = st.sides.iter().filter(|entry| entry.is_some()).count();
8268 if active != 2 {
8269 return Ok(false);
8270 }
8271 for (id, entry) in st
8272 .sides
8273 .iter()
8274 .enumerate()
8275 .filter_map(|(id, entry)| entry.as_ref().map(|e| (id, e)))
8276 {
8277 if entry.opts.reliable_enabled
8278 || !matches!(
8279 entry.tx_handler,
8280 RouterTxHandlerFn::Packed(_) | RouterTxHandlerFn::PackedWithPriority(_)
8281 )
8282 || !st
8283 .side_runtime_stats
8284 .get(&id)
8285 .is_some_and(|stats| stats.data_types.contains_key(&ty.as_u32()))
8286 {
8287 return Ok(false);
8288 }
8289 if id == side {
8290 continue;
8291 }
8292 #[cfg(feature = "discovery")]
8293 if !st.discovery_routes.get(&id).is_some_and(|route| {
8294 self.clock.now_ms().saturating_sub(route.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
8295 && frame
8296 .envelope
8297 .endpoints()
8298 .any(|ep| route.reachable.contains(&ep))
8299 }) {
8300 return Ok(false);
8301 }
8302 if entry.opts.header_template_enabled
8303 && entry.opts.max_frame_bytes != 0
8304 && entry.opts.max_side_transport_templates != 0
8305 && !self.side_has_multiple_announcers_locked(&st, id, self.clock.now_ms())
8306 && !st.side_transport.get(&id).is_some_and(|transport| {
8307 transport.tx_template_ids.contains_key(&template.hash)
8308 && transport.tx_templates.contains_key(&template.hash)
8309 })
8310 {
8311 return Ok(false);
8312 }
8313 }
8314 Ok(true)
8315 }
8316 #[cfg(not(feature = "discovery"))]
8317 fn cached_transit_ready(&self, _side: RouterSideId, _bytes: &[u8]) -> TelemetryResult<bool> {
8318 Ok(false)
8319 }
8320 fn admit_received_frame(&self, side: RouterSideId, bytes: &[u8]) -> TelemetryResult<()> {
8321 if crate::memory_admission::supports_work_kinds()
8322 && self.cached_transit_ready(side, bytes)?
8323 {
8324 crate::memory_admission::check_cached_transit(bytes.len())
8325 } else {
8326 let mut protected = None;
8327 loop {
8328 match crate::memory_admission::check_receive(bytes) {
8329 Err(TelemetryError::Io("memory pressure")) => {
8330 let protected = *protected.get_or_insert_with(|| {
8331 wire_format::peek_routing_frame_info(bytes)
8332 .ok()
8333 .is_some_and(|frame| message_priority(frame.envelope.ty) >= 200)
8334 });
8335 if !protected {
8336 return Err(TelemetryError::Io("memory pressure"));
8337 }
8338 let mut st = self.state.lock();
8342 let reclaimed = st
8343 .lower_priority_shared_queue(199)
8344 .is_some_and(|queue| st.pop_shared_queue_item(queue));
8345 if !reclaimed {
8346 return Err(TelemetryError::Io("memory pressure"));
8347 }
8348 }
8349 result => return result,
8350 }
8351 }
8352 }
8353 }
8354
8355 fn decode_side_transport_frame(
8356 &self,
8357 side: RouterSideId,
8358 bytes: &[u8],
8359 ) -> TelemetryResult<Option<crate::SharedBytes>> {
8360 self.expire_side_chunks();
8362 let Some((kind, body)) = parse_side_transport_wrapper(bytes)? else {
8363 #[cfg(feature = "discovery")]
8364 self.refresh_ingress_peer_liveness(side, bytes);
8365 #[cfg(all(feature = "discovery", not(feature = "std")))]
8366 if self.consume_known_packed_schema(side, bytes)? {
8367 return Ok(None);
8368 }
8369 self.admit_received_frame(side, bytes)?;
8370 return Ok(Some(crate::shared_bytes::convert(Arc::<[u8]>::from(bytes))));
8371 };
8372 match kind {
8373 SIDE_TRANSPORT_KIND_FULL => {
8374 let header_templates_enabled = self
8375 .state
8376 .lock()
8377 .sides
8378 .get(side)
8379 .and_then(|entry| entry.as_ref())
8380 .map(|side| side.opts.header_template_enabled)
8381 .unwrap_or(false);
8382 let mut off = 0usize;
8383 let template_id = if header_templates_enabled {
8384 Some(
8385 u32::try_from(read_uleb128_local(body, &mut off)?)
8386 .map_err(|_| TelemetryError::Unpack("side template id too large"))?,
8387 )
8388 } else {
8389 None
8390 };
8391 #[cfg(feature = "discovery")]
8392 self.refresh_ingress_peer_liveness(side, &body[off..]);
8393 #[cfg(all(feature = "discovery", not(feature = "std")))]
8394 if self.consume_known_packed_schema(side, &body[off..])? {
8395 return Ok(None);
8396 }
8397 self.admit_received_frame(side, &body[off..])?;
8398 let raw = Arc::<[u8]>::from(&body[off..]);
8399 if let (Some(template_id), Ok((template, _, _, _, timestamp, _, _, _))) =
8400 (template_id, extract_side_header_template(raw.as_ref()))
8401 {
8402 let mut st = self.state.lock();
8403 let max_templates = st
8404 .sides
8405 .get(side)
8406 .and_then(|side| side.as_ref())
8407 .map(|side| side.opts.max_side_transport_templates)
8408 .unwrap_or(DEFAULT_SIDE_TRANSPORT_TEMPLATE_LIMIT);
8409 let evicted = st.side_transport.get_mut(&side).is_some_and(|side_state| {
8410 let evicted =
8411 side_state.insert_rx_template(template_id, template, max_templates);
8412 if max_templates > 0 {
8413 side_state.rx_last_timestamps.insert(template_id, timestamp);
8414 }
8415 evicted
8416 });
8417 if evicted {
8418 st.side_runtime_stats
8419 .entry(side)
8420 .or_default()
8421 .note_side_transport_template_eviction();
8422 }
8423 }
8424 Ok(Some(crate::shared_bytes::convert(raw)))
8425 }
8426 SIDE_TRANSPORT_KIND_COMPACT
8427 | SIDE_TRANSPORT_KIND_COMPACT_DELTA
8428 | SIDE_TRANSPORT_KIND_COMPACT_SAME_TIMESTAMP => {
8429 if body.is_empty() {
8430 return Err(TelemetryError::Unpack("short side compact frame"));
8431 }
8432 let mut off = 1usize;
8433 let template_id = u32::try_from(read_uleb128_local(body, &mut off)?)
8434 .map_err(|_| TelemetryError::Unpack("side template id too large"))?;
8435 let (template, timestamp_base) = {
8436 let st = self.state.lock();
8437 let state = st.side_transport.get(&side);
8438 let template = state
8439 .and_then(|state| state.rx_templates_by_id.get(&template_id))
8440 .cloned();
8441 let timestamp_base = if matches!(
8442 kind,
8443 SIDE_TRANSPORT_KIND_COMPACT_DELTA
8444 | SIDE_TRANSPORT_KIND_COMPACT_SAME_TIMESTAMP
8445 ) {
8446 state
8447 .and_then(|state| state.rx_last_timestamps.get(&template_id))
8448 .copied()
8449 } else {
8450 None
8451 };
8452 (template, timestamp_base)
8453 };
8454 let Some(template) = template else {
8458 return Ok(None);
8459 };
8460 #[cfg(feature = "discovery")]
8463 if let Some(address) = template.source_address() {
8464 self.refresh_peer_liveness(side, address);
8465 }
8466 let reconstructed_bound = body
8467 .len()
8468 .saturating_add(template.prefix.len())
8469 .saturating_add(template.between.len())
8470 .saturating_add(32);
8471 let protected = template
8475 .data_type()
8476 .is_some_and(|ty| message_priority(ty) >= 200);
8477 let ack = template.is_ack();
8478 loop {
8479 match crate::memory_admission::check_frame(reconstructed_bound, ack) {
8480 Err(TelemetryError::Io("memory pressure")) if protected => {
8481 let mut st = self.state.lock();
8482 let reclaimed = st
8483 .lower_priority_shared_queue(199)
8484 .is_some_and(|queue| st.pop_shared_queue_item(queue));
8485 if !reclaimed {
8486 return Err(TelemetryError::Io("memory pressure"));
8487 }
8488 }
8489 result => {
8490 result?;
8491 break;
8492 }
8493 }
8494 }
8495 let timestamp_mode = match kind {
8496 SIDE_TRANSPORT_KIND_COMPACT_DELTA => SideCompactTimestampMode::Delta,
8497 SIDE_TRANSPORT_KIND_COMPACT_SAME_TIMESTAMP => SideCompactTimestampMode::Omitted,
8498 _ => SideCompactTimestampMode::Absolute,
8499 };
8500 let (frame, timestamp) = reconstruct_side_compact_frame(
8501 &template,
8502 body[0],
8503 &body[off..],
8504 timestamp_mode,
8505 timestamp_base,
8506 )?;
8507 let mut st = self.state.lock();
8508 if let Some(side_state) = st.side_transport.get_mut(&side) {
8509 side_state.rx_last_timestamps.insert(template_id, timestamp);
8510 }
8511 Ok(Some(frame))
8512 }
8513 SIDE_TRANSPORT_KIND_CHUNK => {
8514 crate::memory_admission::check_receive(bytes)?;
8515 if body.len() < 8 {
8516 return Err(TelemetryError::Unpack("short side chunk frame"));
8517 }
8518 let transfer_id = u32::from_le_bytes([body[0], body[1], body[2], body[3]]);
8519 let index = u16::from_le_bytes([body[4], body[5]]);
8520 let total = u16::from_le_bytes([body[6], body[7]]);
8521 if total == 0 || index >= total {
8522 return Err(TelemetryError::Unpack("invalid side chunk index"));
8523 }
8524 let assembled = {
8525 let mut st = self.state.lock();
8526 let budget = st.memory.max_queue_budget;
8527 let now_ms = self.clock.now_ms();
8528 let side_state = st
8529 .side_transport
8530 .get_mut(&side)
8531 .ok_or(TelemetryError::BadArg)?;
8532 side_state
8533 .rx_chunks
8534 .retain(|_, assembly| now_ms.saturating_sub(assembly.last_seen_ms) <= 2000);
8535 if !side_state.rx_chunks.contains_key(&transfer_id)
8536 && side_state.rx_chunks.len() >= 4
8537 && let Some(oldest) = side_state
8538 .rx_chunks
8539 .iter()
8540 .min_by_key(|(_, a)| a.last_seen_ms)
8541 .map(|(id, _)| *id)
8542 {
8543 side_state.rx_chunks.remove(&oldest);
8544 }
8545 SideChunkAssembly::make_room(
8546 &mut side_state.rx_chunks,
8547 transfer_id,
8548 index,
8549 total,
8550 body.len() - 8,
8551 budget,
8552 )?;
8553 let entry = side_state.rx_chunks.entry(transfer_id).or_default();
8554 entry.last_seen_ms = now_ms;
8555 if entry.total == 0 {
8556 entry.total = total;
8557 } else if entry.total != total {
8558 side_state.rx_chunks.remove(&transfer_id);
8559 return Err(TelemetryError::Unpack("side chunk total mismatch"));
8560 }
8561 entry.received.entry(index).or_insert_with(|| {
8562 crate::shared_bytes::convert(Arc::<[u8]>::from(&body[8..]))
8563 });
8564 if entry.received.len() == usize::from(total) {
8565 let entry = side_state
8566 .rx_chunks
8567 .remove(&transfer_id)
8568 .ok_or(TelemetryError::Unpack("side chunk missing"))?;
8569 Some(entry.assemble()?)
8570 } else {
8571 None
8572 }
8573 };
8574 match assembled {
8575 Some(frame) => self.decode_side_transport_frame(side, frame.as_ref()),
8576 None => Ok(None),
8577 }
8578 }
8579 _ => Err(TelemetryError::Unpack("unknown side transport frame")),
8580 }
8581 }
8582
8583 fn packed_handler_call(
8584 handler: &RouterTxHandlerFn,
8585 bytes: &[u8],
8586 priority: u8,
8587 ) -> TelemetryResult<()> {
8588 match handler {
8589 RouterTxHandlerFn::Packed(handler) => handler(bytes),
8590 RouterTxHandlerFn::PackedWithPriority(handler) => handler(bytes, priority),
8591 RouterTxHandlerFn::Packet(_) => Err(TelemetryError::BadArg),
8592 }
8593 }
8594
8595 fn effective_transport_priority(&self, ty: DataType) -> u8 {
8596 if self.is_managed_variable_type(ty) {
8597 crate::transport_priority(DataType::ManagedVariableValue)
8598 } else {
8599 crate::transport_priority(ty)
8600 }
8601 }
8602
8603 fn call_side_tx_handler(
8604 &self,
8605 side: RouterSideId,
8606 handler: &RouterTxHandlerFn,
8607 data: &RouterItem,
8608 relayed: bool,
8609 ) -> TelemetryResult<()> {
8610 let opts = {
8611 let st = self.state.lock();
8612 Self::side_ref(&st, side)?.opts.clone()
8613 };
8614 let Some(_side_tx_guard) = self.try_enter_side_tx() else {
8615 return Err(TelemetryError::Io("side tx busy"));
8616 };
8617 let started_ms = self.clock.now_ms();
8618 let ty = match data {
8619 RouterItem::Packet(pkt) => pkt.data_type(),
8620 RouterItem::Packed(bytes) => wire_format::peek_routing_envelope(bytes.as_ref())?.ty,
8621 };
8622 let priority = self.effective_transport_priority(ty);
8623 let result = match (handler, data) {
8624 (
8625 packed @ (RouterTxHandlerFn::Packed(_) | RouterTxHandlerFn::PackedWithPriority(_)),
8626 RouterItem::Packed(bytes),
8627 ) => {
8628 #[cfg(feature = "cryptography")]
8629 let send_bytes = self.prepare_packed_for_remote(bytes.clone(), None)?;
8630 #[cfg(not(feature = "cryptography"))]
8631 let send_bytes = bytes.clone();
8632 let frames = self.encode_side_transport_frames(side, opts, send_bytes)?;
8633 let mut attempts_total = 0usize;
8634 let mut sent_bytes = 0usize;
8635 self.send_side_frames_resumable(side, frames, |frame| {
8636 match self.retry_with_attempts(runtime_max_handler_retries(), || {
8637 Self::packed_handler_call(packed, frame, priority)
8638 }) {
8639 Ok((_, attempts)) => {
8640 attempts_total = attempts_total.saturating_add(attempts);
8641 sent_bytes = sent_bytes.saturating_add(frame.len());
8642 }
8643 Err((err, attempts)) => {
8644 self.note_side_tx_failure(
8645 side,
8646 ty,
8647 attempts_total.saturating_add(attempts),
8648 );
8649 return Err(err);
8650 }
8651 }
8652
8653 Ok(())
8654 })?;
8655 self.record_side_tx_sample(side, sent_bytes, started_ms, self.clock.now_ms());
8656 self.note_side_tx_success(side, ty, sent_bytes, relayed, attempts_total);
8657 return Ok(());
8658 }
8659 (RouterTxHandlerFn::Packet(f), RouterItem::Packet(pkt)) => {
8660 self.retry_with_attempts(runtime_max_handler_retries(), || f(pkt))
8661 }
8662 (
8663 packed @ (RouterTxHandlerFn::Packed(_) | RouterTxHandlerFn::PackedWithPriority(_)),
8664 RouterItem::Packet(pkt),
8665 ) => {
8666 if ty == DataType::DiscoverySchema
8670 && opts.max_frame_bytes != 0
8671 && pkt.payload().len() > opts.max_frame_bytes
8672 {
8673 let mut attempts_total = 0usize;
8674 let mut sent_bytes = 0usize;
8675 let key = crate::packet::hash_bytes_u64(
8676 pkt.packet_id(),
8677 &opts.max_frame_bytes.to_le_bytes(),
8678 );
8679 let result = self.run_side_chunk_transfer(side, key, |next| {
8680 crate::side_transport::send_packet_chunks(
8681 pkt,
8682 opts.max_frame_bytes,
8683 self.sender_arc().as_bytes(),
8684 next,
8685 |frame| match self
8686 .retry_with_attempts(runtime_max_handler_retries(), || {
8687 Self::packed_handler_call(packed, frame, priority)
8688 }) {
8689 Ok((_, attempts)) => {
8690 attempts_total += attempts;
8691 sent_bytes += frame.len();
8692 Ok(())
8693 }
8694 Err((err, attempts)) => {
8695 attempts_total += attempts;
8696 Err(err)
8697 }
8698 },
8699 )
8700 });
8701 let (raw_len, wire_len, chunks) = match result {
8702 Ok(stats) => stats,
8703 Err(err) => {
8704 self.note_side_tx_failure(side, ty, attempts_total);
8705 return Err(err);
8706 }
8707 };
8708 {
8709 let mut st = self.state.lock();
8710 let stats = st.side_runtime_stats.entry(side).or_default();
8711 stats.note_side_transport_full(raw_len, wire_len);
8712 if chunks > 1 {
8713 stats.note_side_transport_chunks(chunks);
8714 }
8715 }
8716 self.record_side_tx_sample(side, sent_bytes, started_ms, self.clock.now_ms());
8717 self.note_side_tx_success(side, ty, sent_bytes, relayed, attempts_total);
8718 return Ok(());
8719 }
8720 let owned = self.pack_packet_for_router(pkt, None)?;
8721 let frames = self.encode_side_transport_frames(side, opts, owned)?;
8722 let mut attempts_total = 0usize;
8723 let mut sent_bytes = 0usize;
8724 self.send_side_frames_resumable(side, frames, |frame| {
8725 match self.retry_with_attempts(runtime_max_handler_retries(), || {
8726 Self::packed_handler_call(packed, frame, priority)
8727 }) {
8728 Ok((_, attempts)) => {
8729 attempts_total = attempts_total.saturating_add(attempts);
8730 sent_bytes = sent_bytes.saturating_add(frame.len());
8731 }
8732 Err((err, attempts)) => {
8733 self.note_side_tx_failure(
8734 side,
8735 ty,
8736 attempts_total.saturating_add(attempts),
8737 );
8738 return Err(err);
8739 }
8740 }
8741
8742 Ok(())
8743 })?;
8744 self.record_side_tx_sample(side, sent_bytes, started_ms, self.clock.now_ms());
8745 self.note_side_tx_success(side, ty, sent_bytes, relayed, attempts_total);
8746 return Ok(());
8747 }
8748 (RouterTxHandlerFn::Packet(f), RouterItem::Packed(bytes)) => {
8749 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
8750 self.retry_with_attempts(runtime_max_handler_retries(), || f(&pkt))
8751 }
8752 };
8753 match result {
8754 Ok((_, attempts)) => {
8755 if let Ok(bytes) = Self::router_item_wire_len(data) {
8756 self.record_side_tx_sample(side, bytes, started_ms, self.clock.now_ms());
8757 self.note_side_tx_success(side, ty, bytes, relayed, attempts);
8758 }
8759 Ok(())
8760 }
8761 Err((err, attempts)) => {
8762 self.note_side_tx_failure(side, ty, attempts);
8763 Err(err)
8764 }
8765 }
8766 }
8767
8768 fn adjust_reliable_for_side(
8769 &self,
8770 opts: RouterSideOptions,
8771 data: RouterItem,
8772 preserve_end_to_end_ack: bool,
8773 ) -> TelemetryResult<Option<RouterItem>> {
8774 if opts.reliable_enabled {
8775 return Ok(Some(data));
8776 }
8777
8778 match data {
8779 RouterItem::Packed(bytes) => {
8780 let frame = wire_format::peek_routing_frame_info(bytes.as_ref())?;
8781 if is_reliable_type(frame.envelope.ty)
8782 && let Some(hdr) = frame.reliable
8783 {
8784 if (hdr.flags & wire_format::RELIABLE_FLAG_ACK_ONLY) != 0 {
8785 return Ok(None);
8786 }
8787 if (hdr.flags & wire_format::RELIABLE_FLAG_UNSEQUENCED) == 0 {
8788 let Some(rewritten) = wire_format::rewrite_reliable_header_owned(
8789 bytes.as_ref(),
8790 wire_format::RELIABLE_FLAG_UNSEQUENCED,
8791 hdr.seq,
8792 0,
8793 )?
8794 else {
8795 return Ok(Some(RouterItem::Packed(bytes)));
8796 };
8797 return Ok(Some(RouterItem::Packed(crate::shared_bytes::convert(
8798 rewritten,
8799 ))));
8800 }
8801 }
8802 Ok(Some(RouterItem::Packed(bytes)))
8803 }
8804 RouterItem::Packet(pkt) => {
8805 if matches!(
8806 pkt.data_type(),
8807 DataType::ReliableAck
8808 | DataType::ReliablePartialAck
8809 | DataType::ReliablePacketRequest
8810 ) {
8811 if preserve_end_to_end_ack && Self::is_end_to_end_ack_packet(&pkt) {
8812 return Ok(Some(RouterItem::Packet(pkt)));
8813 }
8814 return Ok(None);
8815 }
8816 Ok(Some(RouterItem::Packet(pkt)))
8817 }
8818 }
8819 }
8820
8821 fn process_reliable_timeouts(&self) -> TelemetryResult<()> {
8822 let now = self.clock.now_ms();
8823 let mut requeue: Vec<(RouterSideId, DataType, u32)> = Vec::new();
8824
8825 {
8826 let mut st = self.state.lock();
8827 if st.reliable_tx.is_empty() {
8828 return Ok(());
8829 }
8830
8831 for ((side, ty_u32), tx_state) in st.reliable_tx.iter_mut() {
8832 let Some(ty) = DataType::try_from_u32(*ty_u32) else {
8833 continue;
8834 };
8835 let sent_order: Vec<u32> = tx_state.sent_order.iter().copied().collect();
8836 for seq in sent_order {
8837 let Some(sent) = tx_state.sent.get_mut(&seq) else {
8838 continue;
8839 };
8840 if sent.queued
8841 || now.wrapping_sub(sent.last_send_ms) < runtime_reliable_retransmit_ms()
8842 {
8843 continue;
8844 }
8845 if sent.partial_acked {
8846 sent.partial_acked = false;
8853 sent.last_send_ms = now;
8854 continue;
8855 }
8856 if sent.retries >= runtime_reliable_max_retries() {
8857 tx_state.sent.remove(&seq);
8858 tx_state.sent_order.retain(|existing| *existing != seq);
8859 continue;
8860 }
8861 sent.retries += 1;
8862 requeue.push((*side, ty, seq));
8863 }
8864 }
8865 }
8866
8867 for (side, ty, seq) in requeue {
8868 self.queue_reliable_retransmit(side, ty, seq, true)?;
8869 }
8870
8871 Ok(())
8872 }
8873
8874 fn process_end_to_end_reliable_timeouts(&self) -> TelemetryResult<()> {
8875 let now = self.clock.now_ms();
8876 let mut requeue = Vec::new();
8877 let mut failed_ordered_types = BTreeSet::new();
8878
8879 {
8880 let mut st = self.state.lock();
8881 #[cfg(feature = "discovery")]
8882 {
8883 if Self::prune_discovery_routes_locked(&mut st, now) {
8884 Self::note_discovery_topology_change_locked(&mut st, now);
8885 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
8886 }
8887 }
8892 st.end_to_end_reliable_tx.retain(|&packet_id, sent| {
8895 if sent.waiting_for_predecessor
8896 || sent.queued
8897 || now.wrapping_sub(sent.last_send_ms) < runtime_reliable_retransmit_ms()
8898 {
8899 return true;
8900 }
8901 if sent.retries >= runtime_reliable_max_retries() {
8902 if let Some(ty) = sent.ordered_type {
8903 failed_ordered_types.insert(ty);
8904 }
8905 return false;
8906 }
8907 sent.retries += 1;
8908 requeue.push(packet_id);
8909 true
8910 });
8911 st.end_to_end_reliable_tx.retain(|_, sent| {
8913 !sent
8914 .ordered_type
8915 .is_some_and(|ty| failed_ordered_types.contains(&ty))
8916 });
8917 }
8918
8919 for packet_id in requeue {
8920 self.queue_end_to_end_reliable_retransmit(packet_id)?;
8921 }
8922 self.release_ordered_successors()?;
8923 if !failed_ordered_types.is_empty() {
8924 return Err(TelemetryError::HandlerError(
8925 "ordered end-to-end delivery exhausted retries",
8926 ));
8927 }
8928 Ok(())
8929 }
8930
8931 #[cfg(feature = "timesync")]
8932 #[inline]
8933 fn monotonic_now_ns(&self) -> u64 {
8934 self.clock.now_ns()
8935 }
8936
8937 #[cfg(feature = "timesync")]
8938 #[inline]
8939 fn monotonic_now_ms(&self) -> u64 {
8940 self.clock.now_ms()
8941 }
8942
8943 #[cfg(feature = "timesync")]
8944 fn refresh_timesync_state(&self, now_mono_ms: u64) {
8945 let now_mono_ns = self.monotonic_now_ns();
8946 let mut st = self.timesync.lock();
8947 st.clock.prune_expired(now_mono_ms);
8948 let timeout_ms = st.cfg.map(|cfg| cfg.source_timeout_ms).unwrap_or(0);
8949 st.remote_sources
8950 .retain(|_, src| now_mono_ms.saturating_sub(src.last_sample_mono_ms) <= timeout_ms);
8951 let has_usable_time = Self::timesync_has_usable_time_locked(&st, now_mono_ns);
8952 let leader = if let Some(tracker) = st.tracker.as_mut() {
8953 let _ = tracker.refresh(now_mono_ms);
8954 tracker.leader(now_mono_ms, has_usable_time)
8955 } else {
8956 None
8957 };
8958 Self::reconcile_pending_timesync_request_locked(&mut st, &leader, now_mono_ms);
8959 if let Some(TimeSyncLeader::Remote(remote)) = leader.as_ref() {
8960 let target_ms = st
8961 .remote_sources
8962 .get(remote.sender.as_str())
8963 .map(|src| src.sample_unix_ms);
8964 if let Some(target_ms) = target_ms {
8965 st.disciplined_clock.steer_unix_ms(now_mono_ns, target_ms);
8966 }
8967 }
8968 }
8969
8970 #[cfg(feature = "timesync")]
8971 pub fn update_network_time_source(
8973 &self,
8974 source: &str,
8975 priority: u64,
8976 time: PartialNetworkTime,
8977 ttl_ms: Option<u64>,
8978 ) {
8979 let now_ms = self.monotonic_now_ms();
8980 let now_ns = self.monotonic_now_ns();
8981 let mut st = self.timesync.lock();
8982 st.clock
8983 .update_source(source, priority, time, now_ms, now_ns, ttl_ms);
8984 if let Some(unix_ms) = time.to_network_time().and_then(|t| t.as_unix_ms()) {
8985 st.disciplined_clock.steer_unix_ms(now_ns, unix_ms);
8986 }
8987 }
8988
8989 #[cfg(feature = "timesync")]
8990 fn set_network_time_source_impl(
8991 &self,
8992 source: &str,
8993 priority: u64,
8994 time: PartialNetworkTime,
8995 ttl_ms: Option<u64>,
8996 ) {
8997 let observed_mono_ms = self.monotonic_now_ms();
8998 let observed_mono_ns = self.monotonic_now_ns();
8999 let mut st = self.timesync.lock();
9000 let commit_mono_ms = self.monotonic_now_ms();
9001 let commit_mono_ns = self.monotonic_now_ns();
9002 let adjusted = if let Some(base) = time.to_network_time() {
9003 let elapsed_ns = commit_mono_ns.saturating_sub(observed_mono_ns);
9004 advance_network_time(base, elapsed_ns)
9005 .map(PartialNetworkTime::from)
9006 .unwrap_or(time)
9007 } else {
9008 time
9009 };
9010 let adjusted_mono_ms =
9011 observed_mono_ms.saturating_add(commit_mono_ms.saturating_sub(observed_mono_ms));
9012 st.clock.update_source(
9013 source,
9014 priority,
9015 adjusted,
9016 commit_mono_ms.max(adjusted_mono_ms),
9017 commit_mono_ns,
9018 ttl_ms,
9019 );
9020 if let Some(unix_ms) = adjusted.to_network_time().and_then(|t| t.as_unix_ms()) {
9021 st.disciplined_clock.steer_unix_ms(commit_mono_ns, unix_ms);
9022 }
9023 }
9024
9025 #[cfg(feature = "timesync")]
9026 fn local_network_time_priority(&self) -> u64 {
9027 let st = self.timesync.lock();
9028 st.cfg.map(|cfg| cfg.priority).unwrap_or(0)
9029 }
9030
9031 #[cfg(feature = "timesync")]
9032 pub fn set_local_network_time(&self, time: PartialNetworkTime) {
9034 let priority = self.local_network_time_priority();
9035 if time.is_complete_date() && time.is_complete_time() {
9036 self.set_network_time_source_impl(LOCAL_TIMESYNC_FULL_SOURCE_ID, priority, time, None);
9037 let mut st = self.timesync.lock();
9038 st.clock.remove_source(LOCAL_TIMESYNC_DATE_SOURCE_ID);
9039 st.clock.remove_source(LOCAL_TIMESYNC_TOD_SOURCE_ID);
9040 st.clock.remove_source(LOCAL_TIMESYNC_SUBSEC_SOURCE_ID);
9041 return;
9042 }
9043
9044 {
9045 let mut st = self.timesync.lock();
9046 st.clock.remove_source(LOCAL_TIMESYNC_FULL_SOURCE_ID);
9047 }
9048
9049 if time.year.is_some() || time.month.is_some() || time.day.is_some() {
9050 self.set_network_time_source_impl(
9051 LOCAL_TIMESYNC_DATE_SOURCE_ID,
9052 priority,
9053 PartialNetworkTime {
9054 year: time.year,
9055 month: time.month,
9056 day: time.day,
9057 ..Default::default()
9058 },
9059 None,
9060 );
9061 }
9062
9063 if time.hour.is_some() || time.minute.is_some() || time.second.is_some() {
9064 self.set_network_time_source_impl(
9065 LOCAL_TIMESYNC_TOD_SOURCE_ID,
9066 priority,
9067 PartialNetworkTime {
9068 hour: time.hour,
9069 minute: time.minute,
9070 second: time.second,
9071 nanosecond: time.nanosecond,
9072 ..Default::default()
9073 },
9074 None,
9075 );
9076 }
9077
9078 if time.nanosecond.is_some() {
9079 self.set_network_time_source_impl(
9080 LOCAL_TIMESYNC_SUBSEC_SOURCE_ID,
9081 priority,
9082 PartialNetworkTime {
9083 nanosecond: time.nanosecond,
9084 ..Default::default()
9085 },
9086 None,
9087 );
9088 }
9089 }
9090
9091 #[cfg(feature = "timesync")]
9092 pub fn clear_local_network_time(&self) {
9094 let mut st = self.timesync.lock();
9095 st.clock.remove_source(LOCAL_TIMESYNC_FULL_SOURCE_ID);
9096 st.clock.remove_source(LOCAL_TIMESYNC_DATE_SOURCE_ID);
9097 st.clock.remove_source(LOCAL_TIMESYNC_TOD_SOURCE_ID);
9098 st.clock.remove_source(LOCAL_TIMESYNC_SUBSEC_SOURCE_ID);
9099 }
9100
9101 #[cfg(feature = "timesync")]
9102 pub fn set_local_network_date(&self, year: i32, month: u8, day: u8) {
9104 self.set_local_network_time(PartialNetworkTime {
9105 year: Some(year),
9106 month: Some(month),
9107 day: Some(day),
9108 ..Default::default()
9109 });
9110 }
9111
9112 #[cfg(feature = "timesync")]
9113 pub fn set_local_network_time_hm(&self, hour: u8, minute: u8) {
9115 self.set_local_network_time(PartialNetworkTime {
9116 hour: Some(hour),
9117 minute: Some(minute),
9118 ..Default::default()
9119 });
9120 }
9121
9122 #[cfg(feature = "timesync")]
9123 pub fn set_local_network_time_hms(&self, hour: u8, minute: u8, second: u8) {
9125 self.set_local_network_time(PartialNetworkTime {
9126 hour: Some(hour),
9127 minute: Some(minute),
9128 second: Some(second),
9129 ..Default::default()
9130 });
9131 }
9132
9133 #[cfg(feature = "timesync")]
9134 pub fn set_local_network_time_hms_millis(
9136 &self,
9137 hour: u8,
9138 minute: u8,
9139 second: u8,
9140 millisecond: u16,
9141 ) {
9142 self.set_local_network_time(PartialNetworkTime {
9143 hour: Some(hour),
9144 minute: Some(minute),
9145 second: Some(second),
9146 nanosecond: Some((millisecond as u32).saturating_mul(1_000_000)),
9147 ..Default::default()
9148 });
9149 }
9150
9151 #[cfg(feature = "timesync")]
9152 pub fn set_local_network_time_hms_nanos(
9154 &self,
9155 hour: u8,
9156 minute: u8,
9157 second: u8,
9158 nanosecond: u32,
9159 ) {
9160 self.set_local_network_time(PartialNetworkTime {
9161 hour: Some(hour),
9162 minute: Some(minute),
9163 second: Some(second),
9164 nanosecond: Some(nanosecond),
9165 ..Default::default()
9166 });
9167 }
9168
9169 #[cfg(feature = "timesync")]
9170 pub fn set_local_network_datetime(
9172 &self,
9173 year: i32,
9174 month: u8,
9175 day: u8,
9176 hour: u8,
9177 minute: u8,
9178 second: u8,
9179 ) {
9180 self.set_local_network_time(PartialNetworkTime {
9181 year: Some(year),
9182 month: Some(month),
9183 day: Some(day),
9184 hour: Some(hour),
9185 minute: Some(minute),
9186 second: Some(second),
9187 ..Default::default()
9188 });
9189 }
9190
9191 #[cfg(feature = "timesync")]
9192 #[allow(clippy::too_many_arguments)]
9193 pub fn set_local_network_datetime_millis(
9195 &self,
9196 year: i32,
9197 month: u8,
9198 day: u8,
9199 hour: u8,
9200 minute: u8,
9201 second: u8,
9202 millisecond: u16,
9203 ) {
9204 self.set_local_network_time(PartialNetworkTime {
9205 year: Some(year),
9206 month: Some(month),
9207 day: Some(day),
9208 hour: Some(hour),
9209 minute: Some(minute),
9210 second: Some(second),
9211 nanosecond: Some((millisecond as u32).saturating_mul(1_000_000)),
9212 });
9213 }
9214
9215 #[cfg(feature = "timesync")]
9216 #[allow(clippy::too_many_arguments)]
9217 pub fn set_local_network_datetime_nanos(
9219 &self,
9220 year: i32,
9221 month: u8,
9222 day: u8,
9223 hour: u8,
9224 minute: u8,
9225 second: u8,
9226 nanosecond: u32,
9227 ) {
9228 self.set_local_network_time(PartialNetworkTime {
9229 year: Some(year),
9230 month: Some(month),
9231 day: Some(day),
9232 hour: Some(hour),
9233 minute: Some(minute),
9234 second: Some(second),
9235 nanosecond: Some(nanosecond),
9236 });
9237 }
9238
9239 #[cfg(feature = "timesync")]
9240 pub fn clear_network_time_source(&self, source: &str) {
9242 let mut st = self.timesync.lock();
9243 st.clock.remove_source(source);
9244 }
9245
9246 #[cfg(feature = "timesync")]
9247 pub fn set_timesync_config(&self, cfg: Option<TimeSyncConfig>) {
9249 let mut st = self.timesync.lock();
9250 let stale_remote_sources: Vec<String> = st.remote_sources.keys().cloned().collect();
9251 st.cfg = cfg;
9252 st.tracker = cfg.map(TimeSyncTracker::new);
9253 st.disciplined_clock = SlewedNetworkClock::new(
9254 cfg.map(|c| c.max_slew_ppm)
9255 .unwrap_or(TimeSyncConfig::default().max_slew_ppm),
9256 );
9257 st.remote_sources.clear();
9258 st.next_seq = 1;
9259 st.next_announce_mono_ms = 0;
9260 st.next_request_mono_ms = 0;
9261 st.pending_request = None;
9262 st.clock.remove_source(INTERNAL_TIMESYNC_SOURCE_ID);
9263 for source in stale_remote_sources {
9264 st.clock.remove_source(&source);
9265 }
9266 st.clock.remove_source(LOCAL_TIMESYNC_FULL_SOURCE_ID);
9267 st.clock.remove_source(LOCAL_TIMESYNC_DATE_SOURCE_ID);
9268 st.clock.remove_source(LOCAL_TIMESYNC_TOD_SOURCE_ID);
9269 st.clock.remove_source(LOCAL_TIMESYNC_SUBSEC_SOURCE_ID);
9270 }
9271
9272 #[cfg(feature = "timesync")]
9273 pub fn network_time(&self) -> Option<NetworkTimeReading> {
9275 let now_ms = self.monotonic_now_ms();
9276 let now_ns = self.monotonic_now_ns();
9277 self.refresh_timesync_state(now_ms);
9278 let st = self.timesync.lock();
9279 if let Some(unix_ms) = st.disciplined_clock.read_unix_ms(now_ns) {
9280 return Some(NetworkTimeReading {
9281 time: PartialNetworkTime::from_unix_ms(unix_ms),
9282 unix_time_ms: Some(unix_ms),
9283 });
9284 }
9285 st.clock.current_time(now_ns)
9286 }
9287
9288 #[cfg(feature = "timesync")]
9289 pub fn network_time_ms(&self) -> Option<u64> {
9291 self.network_time().and_then(|t| t.unix_time_ms)
9292 }
9293
9294 #[cfg(feature = "timesync")]
9295 fn packet_timestamp_ms(&self) -> u64 {
9296 self.network_time_ms()
9297 .unwrap_or_else(|| self.monotonic_now_ms())
9298 }
9299
9300 #[cfg(not(feature = "timesync"))]
9301 fn packet_timestamp_ms(&self) -> u64 {
9302 self.clock.now_ms()
9303 }
9304
9305 #[cfg(feature = "timesync")]
9306 fn queue_internal_timesync_request(
9307 &self,
9308 seq: u64,
9309 t1_mono_ms: u64,
9310 called_from_queue: bool,
9311 ) -> TelemetryResult<()> {
9312 let pkt_ts = self.packet_timestamp_ms();
9313 if called_from_queue {
9314 self.log_queue_ts(DataType::TimeSyncRequest, pkt_ts, &[seq, t1_mono_ms])
9315 } else {
9316 self.log_ts(DataType::TimeSyncRequest, pkt_ts, &[seq, t1_mono_ms])
9317 }
9318 }
9319
9320 #[cfg(feature = "timesync")]
9321 fn queue_internal_timesync_response(
9322 &self,
9323 seq: u64,
9324 t1_mono_ms: u64,
9325 t2_network_ms: u64,
9326 t3_network_ms: u64,
9327 dst: Option<RouterSideId>,
9328 called_from_queue: bool,
9329 ) -> TelemetryResult<()> {
9330 let pkt_ts = self.packet_timestamp_ms();
9331 let payload = encode_slice_le(&[seq, t1_mono_ms, t2_network_ms, t3_network_ms]);
9332 let sender = self.sender_arc();
9333 let pkt = Packet::new(
9334 DataType::TimeSyncResponse,
9335 &[DataEndpoint::TimeSync],
9336 sender.as_ref(),
9337 pkt_ts,
9338 payload,
9339 )?;
9340 match dst {
9341 Some(dst) => self.emit_internal_tx(
9342 RouterTxItem::ToSide {
9343 src: None,
9344 dst,
9345 data: RouterItem::Packet(pkt),
9346 },
9347 true,
9348 called_from_queue,
9349 ),
9350 None => self.emit_internal_tx(
9351 RouterTxItem::Broadcast(RouterItem::Packet(pkt)),
9352 true,
9353 called_from_queue,
9354 ),
9355 }
9356 }
9357
9358 #[cfg(feature = "timesync")]
9359 pub fn poll_timesync(&self) -> TelemetryResult<bool> {
9361 let now_ms = self.monotonic_now_ms();
9362 let now_ns = self.monotonic_now_ns();
9363 let mut queued_any = false;
9364 let mut announce_priority = None;
9365 let mut request = None;
9366
9367 {
9368 let mut st = self.timesync.lock();
9369 st.clock.prune_expired(now_ms);
9370 let timeout_ms = st.cfg.map(|cfg| cfg.source_timeout_ms).unwrap_or(0);
9371 st.remote_sources
9372 .retain(|_, src| now_ms.saturating_sub(src.last_sample_mono_ms) <= timeout_ms);
9373 let Some(cfg) = st.cfg else {
9374 return Ok(false);
9375 };
9376
9377 let has_usable_time = Self::timesync_has_usable_time_locked(&st, now_ns);
9378 let (leader, announce_prio) = if let Some(tracker) = st.tracker.as_mut() {
9379 let _ = tracker.refresh(now_ms);
9380 (
9381 tracker.leader(now_ms, has_usable_time),
9382 tracker.local_announce_priority(now_ms, has_usable_time),
9383 )
9384 } else {
9385 (None, None)
9386 };
9387 Self::reconcile_pending_timesync_request_locked(&mut st, &leader, now_ms);
9388
9389 if let Some(TimeSyncLeader::Remote(remote)) = leader.as_ref() {
9390 let target_ms = st
9391 .remote_sources
9392 .get(&remote.sender)
9393 .map(|src| src.sample_unix_ms);
9394 if let Some(target_ms) = target_ms {
9395 st.disciplined_clock.steer_unix_ms(now_ns, target_ms);
9396 }
9397 }
9398
9399 if let Some(priority) = announce_prio
9400 && now_ms >= st.next_announce_mono_ms
9401 {
9402 announce_priority = Some(priority);
9403 st.next_announce_mono_ms = now_ms.saturating_add(cfg.announce_interval_ms);
9404 }
9405
9406 if let Some(TimeSyncLeader::Remote(remote)) = leader
9407 && now_ms >= st.next_request_mono_ms
9408 && st.pending_request.is_none()
9409 {
9410 let seq = st.next_seq;
9411 let next = st.next_seq.wrapping_add(1);
9412 st.next_seq = if next == 0 { 1 } else { next };
9413 st.next_request_mono_ms = now_ms.saturating_add(cfg.request_interval_ms);
9414 st.pending_request = Some(PendingTimeSyncRequest {
9415 seq,
9416 t1_mono_ms: now_ms,
9417 source: remote.sender,
9418 });
9419 request = Some((seq, now_ms));
9420 }
9421 }
9422
9423 if let Some(priority) = announce_priority {
9424 let time_ms = self.packet_timestamp_ms();
9425 self.log_queue_ts(DataType::TimeSyncAnnounce, time_ms, &[priority, time_ms])?;
9426 queued_any = true;
9427 }
9428 if let Some((seq, t1_mono_ms)) = request {
9429 self.queue_internal_timesync_request(seq, t1_mono_ms, true)?;
9430 queued_any = true;
9431 }
9432
9433 Ok(queued_any)
9434 }
9435
9436 #[cfg(feature = "timesync")]
9437 fn handle_internal_timesync_packet(
9438 &self,
9439 pkt: &Packet,
9440 src: Option<RouterSideId>,
9441 called_from_queue: bool,
9442 ) -> TelemetryResult<bool> {
9443 let Some(cfg) = self.cfg.timesync_config() else {
9444 if self.should_route_remote(&RouterItem::Packet(pkt.clone()), src)? {
9445 self.relay_send(RouterItem::Packet(pkt.clone()), src, called_from_queue)?;
9446 }
9447 return Ok(true);
9448 };
9449
9450 let now_mono_ms = self.monotonic_now_ms();
9451 let now_mono_ns = self.monotonic_now_ns();
9452 let mut response = None;
9453 let mut poll_after = false;
9454
9455 {
9456 let mut st = self.timesync.lock();
9457 st.clock.prune_expired(now_mono_ms);
9458 let timeout_ms = st.cfg.map(|cfg| cfg.source_timeout_ms).unwrap_or(0);
9459 st.remote_sources
9460 .retain(|_, src| now_mono_ms.saturating_sub(src.last_sample_mono_ms) <= timeout_ms);
9461 let has_usable_time = Self::timesync_has_usable_time_locked(&st, now_mono_ns);
9462 if st.tracker.is_none() {
9463 return Ok(true);
9464 }
9465
9466 match pkt.data_type() {
9467 DataType::TimeSyncAnnounce => {
9468 let ann = decode_timesync_announce(pkt)?;
9469 let should_steer = {
9470 let tracker = st.tracker.as_mut().expect("tracker checked above");
9471 let _ = tracker.handle_announce(pkt, now_mono_ms)?;
9472 matches!(
9473 tracker.leader(now_mono_ms, has_usable_time),
9474 Some(TimeSyncLeader::Remote(ref remote)) if remote.sender == pkt.sender()
9475 )
9476 };
9477 st.remote_sources.insert(
9478 pkt.sender().to_owned(),
9479 RemoteTimeSyncSource {
9480 priority: ann.priority,
9481 last_sample_mono_ms: now_mono_ms,
9482 sample_unix_ms: ann.time_ms,
9483 },
9484 );
9485 st.clock.update_source(
9486 pkt.sender(),
9487 ann.priority,
9488 PartialNetworkTime::from_unix_ms(ann.time_ms),
9489 now_mono_ms,
9490 now_mono_ns,
9491 Some(cfg.source_timeout_ms),
9492 );
9493 if should_steer {
9494 st.disciplined_clock.steer_unix_ms(now_mono_ns, ann.time_ms);
9495 }
9496 poll_after = true;
9497 }
9498 DataType::TimeSyncRequest => {
9499 let should_serve = {
9500 let tracker = st.tracker.as_ref().expect("tracker checked above");
9501 tracker.should_serve(now_mono_ms, has_usable_time)
9502 };
9503 if should_serve {
9504 let req = decode_timesync_request(pkt)?;
9505 let network_now = st
9506 .disciplined_clock
9507 .read_unix_ms(now_mono_ns)
9508 .or_else(|| {
9509 st.clock
9510 .current_time(now_mono_ns)
9511 .and_then(|t| t.unix_time_ms)
9512 })
9513 .unwrap_or(now_mono_ms);
9514 let t2 = network_now;
9515 let t3 = network_now;
9516 response = Some((req.seq, req.t1_ms, t2, t3, src));
9517 }
9518 }
9519 DataType::TimeSyncResponse => {
9520 let resp = decode_timesync_response(pkt)?;
9521 let pending = st.pending_request.clone();
9522 if let Some(pending) = pending
9523 && pending.seq == resp.seq
9524 && pending.source == pkt.sender()
9525 {
9526 let source_priority = {
9527 let tracker = st.tracker.as_ref().expect("tracker checked above");
9528 tracker
9529 .best_active_source(now_mono_ms)
9530 .map(|s| s.priority)
9531 .or_else(|| st.remote_sources.get(pkt.sender()).map(|s| s.priority))
9532 .unwrap_or(cfg.priority)
9533 };
9534 let (estimate_ms, _delay_ms) = compute_network_time_sample(
9535 pending.t1_mono_ms,
9536 resp.t2_ms,
9537 resp.t3_ms,
9538 now_mono_ms,
9539 );
9540 st.remote_sources.insert(
9541 pkt.sender().to_owned(),
9542 RemoteTimeSyncSource {
9543 priority: source_priority,
9544 last_sample_mono_ms: now_mono_ms,
9545 sample_unix_ms: estimate_ms,
9546 },
9547 );
9548 st.clock.update_source(
9549 pkt.sender(),
9550 source_priority,
9551 PartialNetworkTime::from_unix_ms(estimate_ms),
9552 now_mono_ms,
9553 now_mono_ns,
9554 Some(cfg.source_timeout_ms),
9555 );
9556 st.disciplined_clock.steer_unix_ms(now_mono_ns, estimate_ms);
9557 st.pending_request = None;
9558 }
9559 }
9560 _ => {}
9561 }
9562 }
9563
9564 if let Some((seq, t1, t2, t3, dst)) = response {
9565 self.queue_internal_timesync_response(seq, t1, t2, t3, dst, called_from_queue)?;
9566 }
9567 if poll_after {
9568 let _ = crate::memory_admission::allow_drain(self.poll_timesync())?;
9569 }
9570
9571 if self.should_route_remote(&RouterItem::Packet(pkt.clone()), src)? {
9572 self.relay_send(RouterItem::Packet(pkt.clone()), src, called_from_queue)?;
9573 }
9574
9575 Ok(true)
9576 }
9577
9578 #[cfg(feature = "std")]
9580 pub fn new(cfg: RouterConfig) -> Self {
9581 Self::new_with_clock(cfg, Box::new(StdMonotonicClock::default()))
9582 }
9583
9584 pub fn new_with_clock(cfg: RouterConfig, clock: Box<dyn Clock + Send + Sync>) -> Self {
9586 #[cfg(feature = "timesync")]
9587 let timesync_cfg = cfg.timesync_config();
9588 #[cfg(feature = "discovery")]
9589 let preferred_discovery_master = cfg.preferred_discovery_master.clone();
9590 let memory = cfg.memory_config();
9591 let hostname: Arc<str> = Arc::from(cfg.sender());
9592 let address_mode = cfg.address_mode();
9593 let instance_seq = u64::from(ROUTER_INSTANCE_SEQ.fetch_add(1, Ordering::Relaxed));
9594 let owner_hash = Self::sender_hash(hostname.as_ref()) ^ instance_seq;
9595 let fallback = Self::fallback_address_for_hostname(hostname.as_ref());
9596 let requested = address_mode.requested_address();
9597 let address = if requested == 0 { fallback } else { requested };
9598 let local_address = AddressBookEntry {
9599 hostname: hostname.clone(),
9600 address,
9601 requested_address: requested,
9602 mode: address_mode,
9603 birth_ms: clock
9604 .now_ms()
9605 .saturating_mul(1_000_000)
9606 .saturating_add(instance_seq),
9607 owner_hash,
9608 last_seen_ms: clock.now_ms(),
9609 };
9610 let mut address_book = BTreeMap::new();
9611 address_book.insert(hostname.to_string(), local_address.clone());
9612 let mut address_by_value = BTreeMap::new();
9613 address_by_value.insert(address, hostname.to_string());
9614 Self {
9615 sender: RouterMutex::new(hostname),
9616 cfg,
9617 state: RouterMutex::new(RouterInner {
9618 memory,
9619 sides: Vec::new(),
9620 route_overrides: BTreeMap::new(),
9621 typed_route_overrides: BTreeMap::new(),
9622 route_weights: BTreeMap::new(),
9623 route_priorities: BTreeMap::new(),
9624 source_route_modes: BTreeMap::new(),
9625 route_selection_cursors: BTreeMap::new(),
9626 adaptive_route_stats: BTreeMap::new(),
9627 side_runtime_stats: BTreeMap::new(),
9628 side_transport: BTreeMap::new(),
9629 managed_variable_types: BTreeSet::new(),
9630 managed_variable_permissions: BTreeMap::new(),
9631 managed_variable_latest: BTreeMap::new(),
9632 network_variable_update_handlers: BTreeMap::new(),
9633 local_address,
9634 address_book,
9635 address_by_value,
9636 p2p_port_handlers: BTreeMap::new(),
9637 p2p_stream_handlers: BTreeMap::new(),
9638 p2p_stream_sessions: BTreeMap::new(),
9639 next_p2p_stream_id: 1,
9640 received_queue: BoundedDeque::new(
9641 memory.max_queue_budget,
9642 memory.starting_queue_size,
9643 memory.queue_grow_step,
9644 ),
9645 transmit_queue: BoundedDeque::new(
9646 memory.max_queue_budget,
9647 memory.starting_queue_size,
9648 memory.queue_grow_step,
9649 ),
9650 tx_priority_burst: 0,
9651 recent_rx: BoundedDeque::new(
9652 memory.recent_rx_queue_bytes(),
9653 memory.recent_rx_queue_bytes(),
9654 memory.queue_grow_step,
9655 ),
9656 reliable_tx: BTreeMap::new(),
9657 reliable_rx: BTreeMap::new(),
9658 reliable_return_routes: BTreeMap::new(),
9659 reliable_return_route_order: VecDeque::new(),
9660 end_to_end_reliable_tx: BTreeMap::new(),
9661 end_to_end_reliable_tx_order: VecDeque::new(),
9662 total_handler_failures: 0,
9663 total_handler_retries: 0,
9664 #[cfg(feature = "discovery")]
9665 discovery_routes: BTreeMap::new(),
9666 #[cfg(feature = "discovery")]
9667 discovery_cadence: DiscoveryCadenceState::default(),
9668 #[cfg(feature = "discovery")]
9669 discovery_side_throttle: BTreeMap::new(),
9670 #[cfg(feature = "discovery")]
9671 preferred_discovery_master,
9672 #[cfg(all(feature = "discovery", feature = "timesync"))]
9673 timesync_side_throttle: BTreeMap::new(),
9674 }),
9675 isr_rx_queue: IsrRxQueue::new(
9676 memory.max_queue_budget,
9677 memory.starting_queue_size,
9678 memory.queue_grow_step,
9679 ),
9680 side_tx_gate: ReentryGate::new(),
9681 clock,
9682 #[cfg(feature = "timesync")]
9683 timesync: RouterMutex::new(TimeSyncRuntime::new(timesync_cfg)),
9684 }
9685 }
9686
9687 #[inline]
9688 fn sender_arc(&self) -> Arc<str> {
9689 self.sender.lock().clone()
9690 }
9691
9692 #[inline]
9693 pub fn sender(&self) -> Arc<str> {
9694 self.sender_arc()
9695 }
9696
9697 pub fn current_address(&self) -> NodeAddress {
9698 self.state.lock().local_address.address
9699 }
9700
9701 pub fn hostname(&self) -> Arc<str> {
9702 self.sender_arc()
9703 }
9704
9705 pub fn set_preferred_discovery_master(&self, hostname: Option<&str>) {
9710 #[cfg(feature = "discovery")]
9711 {
9712 let preferred = hostname
9713 .filter(|value| !value.is_empty())
9714 .map(Arc::<str>::from);
9715 let mut st = self.state.lock();
9716 if st.preferred_discovery_master != preferred {
9717 st.preferred_discovery_master = preferred;
9718 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
9719 }
9720 }
9721 #[cfg(not(feature = "discovery"))]
9722 let _ = hostname;
9723 }
9724
9725 pub fn preferred_discovery_master(&self) -> Option<Arc<str>> {
9727 #[cfg(feature = "discovery")]
9728 {
9729 return self.state.lock().preferred_discovery_master.clone();
9730 }
9731 #[cfg(not(feature = "discovery"))]
9732 None
9733 }
9734
9735 pub fn address_book(&self) -> Vec<AddressBookEntry> {
9736 self.state.lock().address_book.values().cloned().collect()
9737 }
9738
9739 pub fn resolve_hostname(&self, hostname: &str) -> Option<AddressBookEntry> {
9740 let now_ms = self.clock.now_ms();
9741 #[allow(unused_mut)]
9742 let mut st = self.state.lock();
9743 #[cfg(feature = "discovery")]
9744 if Self::prune_discovery_routes_locked(&mut st, now_ms) {
9745 Self::note_discovery_topology_change_locked(&mut st, now_ms);
9746 }
9747 if let Some(entry) = st.address_book.get(hostname) {
9748 return Some(entry.clone());
9749 }
9750 let address = sender_address_u32(hostname);
9756 if Self::hostname_for_address_locked(&st, address).as_deref() != Some(hostname) {
9757 return None;
9758 }
9759 Some(AddressBookEntry {
9760 hostname: Arc::from(hostname),
9761 address,
9762 requested_address: 0,
9763 mode: AddressAssignmentMode::Dynamic,
9764 birth_ms: 0,
9765 owner_hash: Self::sender_hash(hostname),
9766 last_seen_ms: now_ms,
9767 })
9768 }
9769
9770 pub fn resolve_address(&self, address: NodeAddress) -> Option<AddressBookEntry> {
9771 let st = self.state.lock();
9772 let direct = st
9773 .address_by_value
9774 .get(&address)
9775 .and_then(|hostname| st.address_book.get(hostname))
9776 .cloned();
9777 if direct.is_some() {
9778 return direct;
9779 }
9780
9781 let hostname = Self::hostname_for_address_locked(&st, address)?;
9782 if let Some(entry) = st.address_book.get(&hostname) {
9783 return Some(entry.clone());
9784 }
9785
9786 let hostname: Arc<str> = Arc::from(hostname);
9791 Some(AddressBookEntry {
9792 hostname: hostname.clone(),
9793 address,
9794 requested_address: 0,
9795 mode: AddressAssignmentMode::Dynamic,
9796 birth_ms: 0,
9797 owner_hash: Self::sender_hash(hostname.as_ref()),
9798 last_seen_ms: self.clock.now_ms(),
9799 })
9800 }
9801
9802 pub fn bind_p2p_port<F>(&self, port: P2pPort, f: F) -> TelemetryResult<()>
9803 where
9804 F: Fn(P2pMessage<'_>) -> TelemetryResult<()> + Send + Sync + 'static,
9805 {
9806 if port == 0 {
9807 return Err(TelemetryError::BadArg);
9808 }
9809 let mut st = self.state.lock();
9810 st.p2p_port_handlers
9811 .entry(port)
9812 .or_default()
9813 .push(P2pPortHandler {
9814 handler: Arc::new(f),
9815 });
9816 Ok(())
9817 }
9818
9819 pub fn clear_p2p_port(&self, port: P2pPort) {
9820 self.state.lock().p2p_port_handlers.remove(&port);
9821 }
9822
9823 pub fn bind_p2p_stream_port<F>(&self, port: P2pPort, f: F) -> TelemetryResult<()>
9824 where
9825 F: Fn(P2pStreamEvent<'_>) -> TelemetryResult<()> + Send + Sync + 'static,
9826 {
9827 if port == 0 {
9828 return Err(TelemetryError::BadArg);
9829 }
9830 let mut st = self.state.lock();
9831 st.p2p_stream_handlers
9832 .entry(port)
9833 .or_default()
9834 .push(P2pStreamHandler {
9835 handler: Arc::new(f),
9836 });
9837 Ok(())
9838 }
9839
9840 pub fn clear_p2p_stream_port(&self, port: P2pPort) {
9841 self.state.lock().p2p_stream_handlers.remove(&port);
9842 }
9843
9844 fn send_p2p_to_entry(
9845 &self,
9846 dst: AddressBookEntry,
9847 dst_port: P2pPort,
9848 src_port: P2pPort,
9849 payload: &[u8],
9850 ) -> TelemetryResult<()> {
9851 if dst_port == 0 {
9852 return Err(TelemetryError::BadArg);
9853 }
9854 let local = self.state.lock().local_address.clone();
9855 let payload = Self::encode_p2p_payload(
9856 local.hostname.as_ref(),
9857 local.address,
9858 src_port,
9859 dst_port,
9860 payload,
9861 )?;
9862 let pkt = Packet::new(
9863 DataType::P2pMessage,
9864 &[DataEndpoint::Discovery],
9865 local.hostname.as_ref(),
9866 self.packet_timestamp_ms(),
9867 crate::shared_bytes::convert(payload),
9868 )?;
9869 let target = Self::sender_hash(dst.hostname.as_ref());
9870 let item = self.attach_wire_contract_to_item(RouterItem::Packet(pkt.clone()), &[target])?;
9871 if target == Self::sender_hash(self.sender_arc().as_ref()) {
9872 self.dispatch_p2p_packet(&pkt)?;
9873 return Ok(());
9874 }
9875 self.tx_item_impl(RouterTxItem::Broadcast(item), true, false)
9876 }
9877
9878 pub fn send_p2p_to_hostname(
9879 &self,
9880 hostname: &str,
9881 dst_port: P2pPort,
9882 src_port: P2pPort,
9883 payload: &[u8],
9884 ) -> TelemetryResult<()> {
9885 let dst = self
9886 .resolve_hostname(hostname)
9887 .ok_or(TelemetryError::BadArg)?;
9888 self.send_p2p_to_entry(dst, dst_port, src_port, payload)
9889 }
9890
9891 pub fn send_p2p_to_address(
9892 &self,
9893 address: NodeAddress,
9894 dst_port: P2pPort,
9895 src_port: P2pPort,
9896 payload: &[u8],
9897 ) -> TelemetryResult<()> {
9898 let dst = self
9899 .resolve_address(address)
9900 .ok_or(TelemetryError::BadArg)?;
9901 self.send_p2p_to_entry(dst, dst_port, src_port, payload)
9902 }
9903
9904 fn open_p2p_stream_to_entry(
9905 &self,
9906 dst: AddressBookEntry,
9907 dst_port: P2pPort,
9908 src_port: P2pPort,
9909 ) -> TelemetryResult<P2pStreamId> {
9910 if dst_port == 0 || src_port == 0 {
9911 return Err(TelemetryError::BadArg);
9912 }
9913 let stream_id = {
9914 let mut st = self.state.lock();
9915 let stream_id = Self::allocate_p2p_stream_id_locked(&mut st);
9916 if stream_id == 0 {
9917 return Err(TelemetryError::Io("p2p stream id exhausted"));
9918 }
9919 st.p2p_stream_sessions.insert(
9920 stream_id,
9921 P2pStreamSession {
9922 peer_hostname: dst.hostname.clone(),
9923 peer_address: dst.address,
9924 local_port: src_port,
9925 peer_port: dst_port,
9926 peer_stream_id: 0,
9927 next_sequence: 1,
9928 connected: false,
9929 },
9930 );
9931 stream_id
9932 };
9933 let payload = Self::encode_p2p_stream_payload(P2P_STREAM_SYN, stream_id, 0, 0, &[])?;
9934 self.send_p2p_to_entry(dst, dst_port, src_port, &payload)?;
9935 Ok(stream_id)
9936 }
9937
9938 pub fn open_p2p_stream_to_hostname(
9939 &self,
9940 hostname: &str,
9941 dst_port: P2pPort,
9942 src_port: P2pPort,
9943 ) -> TelemetryResult<P2pStreamId> {
9944 let dst = self
9945 .resolve_hostname(hostname)
9946 .ok_or(TelemetryError::BadArg)?;
9947 self.open_p2p_stream_to_entry(dst, dst_port, src_port)
9948 }
9949
9950 pub fn open_p2p_stream_to_address(
9951 &self,
9952 address: NodeAddress,
9953 dst_port: P2pPort,
9954 src_port: P2pPort,
9955 ) -> TelemetryResult<P2pStreamId> {
9956 let dst = self
9957 .resolve_address(address)
9958 .ok_or(TelemetryError::BadArg)?;
9959 self.open_p2p_stream_to_entry(dst, dst_port, src_port)
9960 }
9961
9962 fn send_p2p_stream_control(
9963 &self,
9964 stream_id: P2pStreamId,
9965 flags: u8,
9966 payload: &[u8],
9967 ) -> TelemetryResult<()> {
9968 let (dst, dst_port, src_port, peer_stream_id, seq, refresh_syn) = {
9969 let mut st = self.state.lock();
9970 let Some(session) = st.p2p_stream_sessions.get_mut(&stream_id) else {
9971 return Err(TelemetryError::BadArg);
9972 };
9973 let peer_hostname = session.peer_hostname.clone();
9974 let peer_address = session.peer_address;
9975 let peer_port = session.peer_port;
9976 let local_port = session.local_port;
9977 let peer_stream_id = session.peer_stream_id;
9978 let refresh_syn = peer_stream_id == 0 && flags & P2P_STREAM_SYN == 0;
9979 let seq = session.next_sequence;
9980 session.next_sequence = session.next_sequence.wrapping_add(1).max(1);
9981 let dst = st
9982 .address_book
9983 .get(peer_hostname.as_ref())
9984 .cloned()
9985 .unwrap_or(AddressBookEntry {
9986 hostname: peer_hostname.clone(),
9987 address: peer_address,
9988 requested_address: peer_address,
9989 mode: AddressAssignmentMode::Dynamic,
9990 birth_ms: self.clock.now_ms(),
9991 owner_hash: Self::sender_hash(peer_hostname.as_ref()),
9992 last_seen_ms: self.clock.now_ms(),
9993 });
9994 (dst, peer_port, local_port, peer_stream_id, seq, refresh_syn)
9995 };
9996 if refresh_syn {
9997 let syn_payload =
9998 Self::encode_p2p_stream_payload(P2P_STREAM_SYN, stream_id, 0, 0, &[])?;
9999 self.send_p2p_to_entry(dst.clone(), dst_port, src_port, &syn_payload)?;
10000 }
10001 let stream_payload =
10002 Self::encode_p2p_stream_payload(flags, stream_id, peer_stream_id, seq, payload)?;
10003 self.send_p2p_to_entry(dst, dst_port, src_port, &stream_payload)?;
10004 if flags & (P2P_STREAM_FIN | P2P_STREAM_RST) != 0 {
10005 self.state.lock().p2p_stream_sessions.remove(&stream_id);
10006 }
10007 Ok(())
10008 }
10009
10010 pub fn send_p2p_stream(&self, stream_id: P2pStreamId, payload: &[u8]) -> TelemetryResult<()> {
10011 self.send_p2p_stream_control(stream_id, P2P_STREAM_DATA, payload)
10012 }
10013
10014 pub fn close_p2p_stream(&self, stream_id: P2pStreamId) -> TelemetryResult<()> {
10015 self.send_p2p_stream_control(stream_id, P2P_STREAM_FIN, &[])
10016 }
10017
10018 pub fn reset_p2p_stream(&self, stream_id: P2pStreamId) -> TelemetryResult<()> {
10019 self.send_p2p_stream_control(stream_id, P2P_STREAM_RST, &[])
10020 }
10021
10022 pub fn set_sender<S: AsRef<str>>(&self, sender: S) {
10023 let hostname: Arc<str> = Arc::from(sender.as_ref());
10024 let mut st = self.state.lock();
10025 let mut local = st.local_address.clone();
10026 local.hostname = hostname.clone();
10027 local.owner_hash = Self::sender_hash(hostname.as_ref());
10028 if matches!(local.mode, AddressAssignmentMode::Dynamic) {
10029 local.address = Self::fallback_address_for_hostname(hostname.as_ref());
10030 local.requested_address = 0;
10031 }
10032 let change =
10033 self.update_local_identity_locked(&mut st, local, AddressChangeReason::Configured);
10034 drop(st);
10035 let _ = self.notify_address_change(change);
10036 }
10037
10038 pub fn set_address_assignment(
10039 &self,
10040 mode: AddressAssignmentMode,
10041 ) -> TelemetryResult<AddressChange> {
10042 let mut st = self.state.lock();
10043 let mut local = st.local_address.clone();
10044 local.mode = mode;
10045 local.requested_address = mode.requested_address();
10046 local.address = match mode {
10047 AddressAssignmentMode::Dynamic => {
10048 Self::fallback_address_for_hostname(local.hostname.as_ref())
10049 }
10050 AddressAssignmentMode::Requested(address) | AddressAssignmentMode::Static(address) => {
10051 if address == 0 {
10052 1
10053 } else {
10054 address
10055 }
10056 }
10057 };
10058 let change =
10059 self.update_local_identity_locked(&mut st, local, AddressChangeReason::Configured);
10060 drop(st);
10061 self.notify_address_change(change.clone())?;
10062 Ok(change)
10063 }
10064
10065 pub fn add_side_packed<N, F>(&self, name: N, tx: F) -> RouterSideId
10074 where
10075 N: AsRef<str>,
10076 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
10077 {
10078 self.add_side_packed_with_options(name, tx, RouterSideOptions::default())
10079 }
10080
10081 pub fn add_side_packed_small_packets<N, F>(
10085 &self,
10086 name: N,
10087 tx: F,
10088 max_frame_bytes: usize,
10089 ) -> RouterSideId
10090 where
10091 N: AsRef<str>,
10092 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
10093 {
10094 self.add_side_packed_with_options(
10095 name,
10096 tx,
10097 RouterSideOptions::default().with_small_packet_transport(max_frame_bytes),
10098 )
10099 }
10100
10101 pub fn add_side_packed_with_options<N, F>(
10110 &self,
10111 name: N,
10112 tx: F,
10113 opts: RouterSideOptions,
10114 ) -> RouterSideId
10115 where
10116 N: AsRef<str>,
10117 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
10118 {
10119 let mut st = self.state.lock();
10120 let side = Some(RouterSide {
10121 name: Arc::from(name.as_ref()),
10122 tx_handler: RouterTxHandlerFn::Packed(Arc::new(tx)),
10123 opts,
10124 });
10125 let id = if let Some(id) = st.sides.iter().position(Option::is_none) {
10126 st.sides[id] = side;
10127 id
10128 } else {
10129 let id = st.sides.len();
10130 st.sides.push(side);
10131 id
10132 };
10133 st.side_runtime_stats
10134 .insert(id, SideRuntimeStatsInner::default());
10135 st.side_transport.insert(id, SideTransportState::default());
10136 #[cfg(feature = "discovery")]
10137 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
10138 id
10139 }
10140
10141 pub fn add_side_packed_with_priority_and_options<N, F>(
10150 &self,
10151 name: N,
10152 tx: F,
10153 opts: RouterSideOptions,
10154 ) -> RouterSideId
10155 where
10156 N: AsRef<str>,
10157 F: Fn(&[u8], u8) -> TelemetryResult<()> + Send + Sync + 'static,
10158 {
10159 let mut st = self.state.lock();
10160 let side = Some(RouterSide {
10161 name: Arc::from(name.as_ref()),
10162 tx_handler: RouterTxHandlerFn::PackedWithPriority(Arc::new(tx)),
10163 opts,
10164 });
10165 let id = if let Some(id) = st.sides.iter().position(Option::is_none) {
10166 st.sides[id] = side;
10167 id
10168 } else {
10169 let id = st.sides.len();
10170 st.sides.push(side);
10171 id
10172 };
10173 st.side_runtime_stats
10174 .insert(id, SideRuntimeStatsInner::default());
10175 st.side_transport.insert(id, SideTransportState::default());
10176 #[cfg(feature = "discovery")]
10177 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
10178 id
10179 }
10180
10181 pub fn add_side_packet<N, F>(&self, name: N, tx: F) -> RouterSideId
10186 where
10187 N: AsRef<str>,
10188 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
10189 {
10190 self.add_side_packet_with_options(name, tx, RouterSideOptions::default())
10191 }
10192
10193 pub fn add_side_packet_with_options<N, F>(
10199 &self,
10200 name: N,
10201 tx: F,
10202 opts: RouterSideOptions,
10203 ) -> RouterSideId
10204 where
10205 N: AsRef<str>,
10206 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
10207 {
10208 let mut st = self.state.lock();
10209 let side = Some(RouterSide {
10210 name: Arc::from(name.as_ref()),
10211 tx_handler: RouterTxHandlerFn::Packet(Arc::new(tx)),
10212 opts,
10213 });
10214 let id = if let Some(id) = st.sides.iter().position(Option::is_none) {
10215 st.sides[id] = side;
10216 id
10217 } else {
10218 let id = st.sides.len();
10219 st.sides.push(side);
10220 id
10221 };
10222 st.side_runtime_stats
10223 .insert(id, SideRuntimeStatsInner::default());
10224 st.side_transport.insert(id, SideTransportState::default());
10225 #[cfg(feature = "discovery")]
10226 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
10227 id
10228 }
10229
10230 pub fn remove_side(&self, side: RouterSideId) -> TelemetryResult<()> {
10235 let now_ms = self.clock.now_ms();
10236 {
10237 let mut st = self.state.lock();
10238 let slot = st.sides.get_mut(side).ok_or(TelemetryError::BadArg)?;
10239 if slot.is_none() {
10240 return Err(TelemetryError::BadArg);
10241 }
10242 *slot = None;
10243 while st.sides.last().is_some_and(Option::is_none) {
10244 st.sides.pop();
10245 }
10246 st.route_overrides
10251 .retain(|(src_side, dst_side), _| *src_side != Some(side) && *dst_side != side);
10252 st.typed_route_overrides
10253 .retain(|(src_side, _, dst_side), _| *src_side != Some(side) && *dst_side != side);
10254 st.route_weights
10255 .retain(|(src_side, dst_side), _| *src_side != Some(side) && *dst_side != side);
10256 st.route_priorities
10257 .retain(|(src_side, dst_side), _| *src_side != Some(side) && *dst_side != side);
10258 st.source_route_modes.remove(&Some(side));
10259 st.route_selection_cursors.remove(&Some(side));
10260 st.adaptive_route_stats.remove(&side);
10261 #[cfg(feature = "discovery")]
10262 st.discovery_side_throttle.remove(&side);
10263 #[cfg(all(feature = "discovery", feature = "timesync"))]
10264 st.timesync_side_throttle.remove(&side);
10265 st.side_runtime_stats.remove(&side);
10266 st.side_transport.remove(&side);
10267 st.reliable_return_routes
10268 .retain(|_, route| route.side != side);
10269 st.transmit_queue.retain(
10270 |queued| {
10271 !matches!(&queued.item, RouterTxItem::ToSide { dst, .. } if *dst == side)
10272 && !matches!(&queued.item, RouterTxItem::ReliableReplay { dst, .. } if *dst == side)
10273 },
10274 );
10275 st.received_queue.retain(|queued| queued.src != Some(side));
10276 st.reliable_tx.retain(|(side_id, _), _| *side_id != side);
10277 st.reliable_rx.retain(|(side_id, _), _| *side_id != side);
10278 #[cfg(feature = "discovery")]
10279 {
10280 st.discovery_routes.remove(&side);
10281 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10282 }
10283 }
10284 let mut isr_rx = self.isr_rx_queue.try_lock()?;
10285 isr_rx.retain(|queued| queued.src != Some(side));
10286 Ok(())
10287 }
10288
10289 pub fn set_side_ingress_enabled(
10294 &self,
10295 side: RouterSideId,
10296 enabled: bool,
10297 ) -> TelemetryResult<()> {
10298 let now_ms = self.clock.now_ms();
10299 let mut st = self.state.lock();
10300 let side_ref = st
10301 .sides
10302 .get_mut(side)
10303 .and_then(|side| side.as_mut())
10304 .ok_or(TelemetryError::BadArg)?;
10305 side_ref.opts.ingress_enabled = enabled;
10306 #[cfg(feature = "discovery")]
10307 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10308 Ok(())
10309 }
10310
10311 pub fn set_side_egress_enabled(
10315 &self,
10316 side: RouterSideId,
10317 enabled: bool,
10318 ) -> TelemetryResult<()> {
10319 let now_ms = self.clock.now_ms();
10320 let mut st = self.state.lock();
10321 let side_ref = st
10322 .sides
10323 .get_mut(side)
10324 .and_then(|side| side.as_mut())
10325 .ok_or(TelemetryError::BadArg)?;
10326 side_ref.opts.egress_enabled = enabled;
10327 if !enabled {
10328 st.transmit_queue.retain(
10329 |queued| {
10330 !matches!(&queued.item, RouterTxItem::ToSide { dst, .. } if *dst == side)
10331 && !matches!(&queued.item, RouterTxItem::ReliableReplay { dst, .. } if *dst == side)
10332 },
10333 );
10334 }
10335 #[cfg(feature = "discovery")]
10336 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10337 Ok(())
10338 }
10339
10340 pub fn set_source_route_mode(
10346 &self,
10347 src: Option<RouterSideId>,
10348 mode: RouteSelectionMode,
10349 ) -> TelemetryResult<()> {
10350 let now_ms = self.clock.now_ms();
10351 let mut st = self.state.lock();
10352 if let Some(src) = src {
10353 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10354 }
10355 st.source_route_modes.insert(src, mode);
10359 st.route_selection_cursors.remove(&src);
10360 #[cfg(feature = "discovery")]
10361 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10362 Ok(())
10363 }
10364
10365 pub fn clear_source_route_mode(&self, src: Option<RouterSideId>) -> TelemetryResult<()> {
10367 let now_ms = self.clock.now_ms();
10368 let mut st = self.state.lock();
10369 if let Some(src) = src {
10370 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10371 }
10372 st.source_route_modes.remove(&src);
10373 st.route_selection_cursors.remove(&src);
10374 #[cfg(feature = "discovery")]
10375 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10376 Ok(())
10377 }
10378
10379 pub fn set_route_weight(
10384 &self,
10385 src: Option<RouterSideId>,
10386 dst: RouterSideId,
10387 weight: u32,
10388 ) -> TelemetryResult<()> {
10389 let now_ms = self.clock.now_ms();
10390 let mut st = self.state.lock();
10391 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
10392 if let Some(src) = src {
10393 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10394 }
10395 st.route_weights.insert((src, dst), weight);
10396 st.route_selection_cursors.remove(&src);
10397 #[cfg(feature = "discovery")]
10398 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10399 Ok(())
10400 }
10401
10402 pub fn clear_route_weight(
10404 &self,
10405 src: Option<RouterSideId>,
10406 dst: RouterSideId,
10407 ) -> TelemetryResult<()> {
10408 let now_ms = self.clock.now_ms();
10409 let mut st = self.state.lock();
10410 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
10411 if let Some(src) = src {
10412 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10413 }
10414 st.route_weights.remove(&(src, dst));
10415 st.route_selection_cursors.remove(&src);
10416 #[cfg(feature = "discovery")]
10417 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10418 Ok(())
10419 }
10420
10421 pub fn set_route_priority(
10426 &self,
10427 src: Option<RouterSideId>,
10428 dst: RouterSideId,
10429 priority: u32,
10430 ) -> TelemetryResult<()> {
10431 let now_ms = self.clock.now_ms();
10432 let mut st = self.state.lock();
10433 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
10434 if let Some(src) = src {
10435 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10436 }
10437 st.route_priorities.insert((src, dst), priority);
10438 #[cfg(feature = "discovery")]
10439 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10440 Ok(())
10441 }
10442
10443 pub fn clear_route_priority(
10445 &self,
10446 src: Option<RouterSideId>,
10447 dst: RouterSideId,
10448 ) -> TelemetryResult<()> {
10449 let now_ms = self.clock.now_ms();
10450 let mut st = self.state.lock();
10451 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
10452 if let Some(src) = src {
10453 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10454 }
10455 st.route_priorities.remove(&(src, dst));
10456 #[cfg(feature = "discovery")]
10457 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10458 Ok(())
10459 }
10460
10461 pub fn set_route(
10466 &self,
10467 src: Option<RouterSideId>,
10468 dst: RouterSideId,
10469 enabled: bool,
10470 ) -> TelemetryResult<()> {
10471 let now_ms = self.clock.now_ms();
10472 let mut st = self.state.lock();
10473 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
10474 if let Some(src) = src {
10475 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10476 }
10477 st.route_overrides.insert((src, dst), enabled);
10478 #[cfg(feature = "discovery")]
10479 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10480 Ok(())
10481 }
10482
10483 pub fn set_typed_route(
10488 &self,
10489 src: Option<RouterSideId>,
10490 ty: DataType,
10491 dst: RouterSideId,
10492 enabled: bool,
10493 ) -> TelemetryResult<()> {
10494 let now_ms = self.clock.now_ms();
10495 let mut st = self.state.lock();
10496 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
10497 if let Some(src) = src {
10498 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10499 }
10500 st.typed_route_overrides
10501 .insert((src, ty.as_u32(), dst), enabled);
10502 #[cfg(feature = "discovery")]
10503 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10504 Ok(())
10505 }
10506
10507 pub fn clear_typed_route(
10509 &self,
10510 src: Option<RouterSideId>,
10511 ty: DataType,
10512 dst: RouterSideId,
10513 ) -> TelemetryResult<()> {
10514 let now_ms = self.clock.now_ms();
10515 let mut st = self.state.lock();
10516 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
10517 if let Some(src) = src {
10518 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10519 }
10520 st.typed_route_overrides.remove(&(src, ty.as_u32(), dst));
10521 #[cfg(feature = "discovery")]
10522 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10523 Ok(())
10524 }
10525
10526 pub fn clear_route(&self, src: Option<RouterSideId>, dst: RouterSideId) -> TelemetryResult<()> {
10528 let now_ms = self.clock.now_ms();
10529 let mut st = self.state.lock();
10530 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
10531 if let Some(src) = src {
10532 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10533 }
10534 st.route_overrides.remove(&(src, dst));
10535 #[cfg(feature = "discovery")]
10536 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10537 Ok(())
10538 }
10539
10540 #[cfg(feature = "discovery")]
10542 pub fn announce_discovery(&self) -> TelemetryResult<()> {
10543 self.queue_discovery_announce(true, false)
10544 }
10545
10546 #[cfg(feature = "discovery")]
10548 pub fn announce_leave(&self) -> TelemetryResult<()> {
10549 let sender = self.sender_arc();
10550 let pkt = discovery::build_discovery_leave(sender.as_ref(), self.clock.now_ms())?;
10551 self.tx_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
10552 }
10553
10554 #[cfg(feature = "discovery")]
10556 pub fn poll_discovery(&self) -> TelemetryResult<bool> {
10557 self.poll_discovery_announce()
10558 }
10559
10560 #[cfg(feature = "discovery")]
10561 pub fn request_topology(&self) -> TelemetryResult<()> {
10562 let sender = self.sender_arc();
10563 let pkt =
10564 discovery::build_discovery_topology_request(sender.as_ref(), self.clock.now_ms())?;
10565 self.tx_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
10566 }
10567
10568 #[cfg(feature = "discovery")]
10569 pub fn request_schema(&self) -> TelemetryResult<()> {
10570 let sender = self.sender_arc();
10571 let pkt = discovery::build_discovery_schema_request(sender.as_ref(), self.clock.now_ms())?;
10572 self.tx_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
10573 }
10574
10575 pub fn enable_managed_variable(&self, ty: DataType) -> TelemetryResult<()> {
10582 self.enable_network_variable(ty, NetworkVariablePermissions::READ_WRITE)
10583 }
10584
10585 pub fn enable_network_variable(
10590 &self,
10591 ty: DataType,
10592 permissions: NetworkVariablePermissions,
10593 ) -> TelemetryResult<()> {
10594 if is_internal_control_type(ty) {
10595 return Err(TelemetryError::InvalidType);
10596 }
10597 let mut st = self.state.lock();
10598 let newly_advertised = st.managed_variable_types.insert(ty.as_u32());
10599 st.managed_variable_permissions
10600 .insert(ty.as_u32(), permissions);
10601 #[cfg(feature = "discovery")]
10602 if newly_advertised {
10603 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
10604 }
10605 Ok(())
10606 }
10607
10608 pub fn on_network_variable_update<F>(&self, ty: DataType, f: F) -> TelemetryResult<()>
10614 where
10615 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
10616 {
10617 if is_internal_control_type(ty) {
10618 return Err(TelemetryError::InvalidType);
10619 }
10620 let mut st = self.state.lock();
10621 let newly_advertised = st.managed_variable_types.insert(ty.as_u32());
10622 st.network_variable_update_handlers
10623 .entry(ty.as_u32())
10624 .or_default()
10625 .push(NetworkVariableUpdateHandler {
10626 handler: Arc::new(f),
10627 });
10628 #[cfg(feature = "discovery")]
10629 if newly_advertised {
10630 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
10631 }
10632 Ok(())
10633 }
10634
10635 pub fn disable_managed_variable(&self, ty: DataType) {
10636 let mut st = self.state.lock();
10637 let was_advertised = st.managed_variable_types.remove(&ty.as_u32());
10638 st.managed_variable_permissions.remove(&ty.as_u32());
10639 st.managed_variable_latest.remove(&ty.as_u32());
10640 st.network_variable_update_handlers.remove(&ty.as_u32());
10641 #[cfg(feature = "discovery")]
10642 if was_advertised {
10643 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
10644 }
10645 }
10646
10647 pub fn seed_managed_variable(&self, pkt: Packet) -> TelemetryResult<()> {
10648 if is_internal_control_type(pkt.data_type()) {
10649 return Err(TelemetryError::InvalidType);
10650 }
10651 pkt.validate()?;
10652 {
10653 let mut st = self.state.lock();
10654 let newly_advertised = st.managed_variable_types.insert(pkt.data_type().as_u32());
10655 #[cfg(feature = "discovery")]
10656 if newly_advertised {
10657 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
10658 }
10659 }
10660 self.cache_managed_variable_packet(&pkt, false)
10661 }
10662
10663 pub fn cached_managed_variable(&self, ty: DataType) -> Option<Packet> {
10664 self.managed_variable_latest(ty)
10665 }
10666
10667 pub fn set_network_variable(&self, pkt: Packet) -> TelemetryResult<()> {
10673 if is_internal_control_type(pkt.data_type()) {
10674 return Err(TelemetryError::InvalidType);
10675 }
10676 pkt.validate()?;
10677 let ty = pkt.data_type();
10678 {
10679 let mut st = self.state.lock();
10680 st.managed_variable_types.insert(ty.as_u32());
10681 let perms = Self::managed_variable_permissions_locked(&st, ty);
10682 if !perms.write {
10683 return Err(TelemetryError::PermissionDenied);
10684 }
10685 }
10686 self.cache_managed_variable_packet(&pkt, false)?;
10687 #[cfg(feature = "discovery")]
10688 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
10689 let item = RouterTxItem::Broadcast(RouterItem::Packet(pkt));
10690 if self.side_tx_active() {
10691 return self.tx_queue_item_with_priority(
10692 item,
10693 false,
10694 crate::transport_priority(DataType::ManagedVariableValue),
10695 );
10696 }
10697 self.tx_item(item)
10698 }
10699
10700 #[cfg(feature = "discovery")]
10707 pub fn get_network_variable(
10708 &self,
10709 ty: DataType,
10710 stale_after_ms: Option<u64>,
10711 ) -> TelemetryResult<Option<Packet>> {
10712 if is_internal_control_type(ty) {
10713 return Err(TelemetryError::InvalidType);
10714 }
10715 {
10716 let mut st = self.state.lock();
10717 st.managed_variable_types.insert(ty.as_u32());
10718 let perms = Self::managed_variable_permissions_locked(&st, ty);
10719 if !perms.read {
10720 return Err(TelemetryError::PermissionDenied);
10721 }
10722 }
10723 let cached = self.managed_variable_latest_with_age(ty);
10724 let needs_refresh = match (cached.as_ref(), stale_after_ms) {
10725 (None, _) => true,
10726 (Some((_pkt, age_ms)), Some(max_age_ms)) => *age_ms > max_age_ms,
10727 (Some(_), None) => false,
10728 };
10729 if needs_refresh {
10730 self.request_managed_variable(ty)?;
10731 }
10732 Ok(cached.map(|(pkt, _age_ms)| pkt))
10733 }
10734
10735 pub fn get_cached_network_variable(&self, ty: DataType) -> TelemetryResult<Option<Packet>> {
10737 if is_internal_control_type(ty) {
10738 return Err(TelemetryError::InvalidType);
10739 }
10740 if !self.can_read_managed_variable(ty) {
10741 return Err(TelemetryError::PermissionDenied);
10742 }
10743 Ok(self.managed_variable_latest(ty))
10744 }
10745
10746 #[cfg(feature = "discovery")]
10747 pub fn request_managed_variable(&self, ty: DataType) -> TelemetryResult<()> {
10748 if is_internal_control_type(ty) {
10749 return Err(TelemetryError::InvalidType);
10750 }
10751 if !self.can_read_managed_variable(ty) {
10752 return Err(TelemetryError::PermissionDenied);
10753 }
10754 let sender = self.sender_arc();
10755 let pkt =
10756 discovery::build_managed_variable_request(sender.as_ref(), self.clock.now_ms(), ty)?;
10757 self.tx_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
10758 }
10759
10760 #[cfg(feature = "discovery")]
10761 pub fn request_managed_variable_by_name(&self, ty_name: &str) -> TelemetryResult<()> {
10762 let ty = DataType::try_named(ty_name).ok_or(TelemetryError::InvalidType)?;
10763 self.request_managed_variable(ty)
10764 }
10765
10766 #[cfg(feature = "discovery")]
10768 pub fn export_topology(&self) -> TopologySnapshot {
10769 let now_ms = self.clock.now_ms();
10770 let mut st = self.state.lock();
10771 if Self::prune_discovery_routes_locked(&mut st, now_ms) {
10772 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10773 }
10774 let routes = st
10775 .discovery_routes
10776 .iter()
10777 .filter_map(|(&side_id, route)| {
10778 let side = st.sides.get(side_id)?.as_ref()?;
10779 let announcers = route
10780 .announcers
10781 .iter()
10782 .map(|(sender_id, sender_state)| TopologyAnnouncerRoute {
10783 sender_id: sender_id.clone(),
10784 reachable_endpoints: sender_state
10785 .reachable
10786 .iter()
10787 .copied()
10788 .filter(|ep| !discovery::is_router_control_endpoint(*ep))
10789 .collect(),
10790 reachable_network_variables: sender_state
10791 .reachable_network_variables
10792 .clone(),
10793 reachable_timesync_sources: sender_state.reachable_timesync_sources.clone(),
10794 routers: sender_state.topology_boards.clone(),
10795 last_seen_ms: sender_state.last_seen_ms,
10796 age_ms: now_ms.saturating_sub(sender_state.last_seen_ms),
10797 })
10798 .collect();
10799 Some(TopologySideRoute {
10800 side_id,
10801 side_name: side.name.to_string(),
10802 reachable_endpoints: route
10803 .reachable
10804 .iter()
10805 .copied()
10806 .filter(|ep| !discovery::is_router_control_endpoint(*ep))
10807 .collect(),
10808 reachable_network_variables: route.reachable_network_variables.clone(),
10809 reachable_timesync_sources: route.reachable_timesync_sources.clone(),
10810 announcers,
10811 last_seen_ms: route.last_seen_ms,
10812 age_ms: now_ms.saturating_sub(route.last_seen_ms),
10813 })
10814 })
10815 .collect();
10816 let routers = self.advertised_discovery_topology_for_link_locked(&st, now_ms, true, None);
10817 let advertised_endpoints =
10818 self.advertised_discovery_endpoints_for_link_locked(&st, now_ms, true, None);
10819 let advertised_timesync_sources =
10820 self.advertised_discovery_timesync_sources_for_link_locked(&st, now_ms, None);
10821 let links = discovery::topology_links_from_boards(&routers);
10822 TopologySnapshot {
10823 advertised_endpoints,
10824 advertised_timesync_sources,
10825 routers,
10826 links,
10827 routes,
10828 current_announce_interval_ms: st.discovery_cadence.current_interval_ms,
10829 next_announce_ms: st.discovery_cadence.next_announce_ms,
10830 }
10831 }
10832
10833 #[cfg(feature = "discovery")]
10834 pub fn client_stats(&self, sender_id: &str) -> Option<ClientStatsSnapshot> {
10835 let now_ms = self.clock.now_ms();
10836 let st = self.state.lock();
10837 let mut side_ids = Vec::new();
10838 let mut side_names = Vec::new();
10839 let mut last_seen_ms = None::<u64>;
10840 let mut reachable_endpoints = Vec::new();
10841 let mut reachable_timesync_sources = Vec::new();
10842 let mut packets_sent = 0u64;
10843 let mut packets_received = 0u64;
10844 let mut bytes_sent = 0u64;
10845 let mut bytes_received = 0u64;
10846
10847 for (side_id, route) in &st.discovery_routes {
10848 let Some(sender_state) = route.announcers.get(sender_id) else {
10849 continue;
10850 };
10851 side_ids.push(*side_id);
10852 if let Some(side_name) = st
10853 .sides
10854 .get(*side_id)
10855 .and_then(|side| side.as_ref())
10856 .map(|side| side.name.clone())
10857 {
10858 side_names.push(side_name.to_string());
10859 }
10860 last_seen_ms = Some(last_seen_ms.unwrap_or(0).max(sender_state.last_seen_ms));
10861 reachable_endpoints.extend(sender_state.reachable.iter().copied());
10862 reachable_timesync_sources
10863 .extend(sender_state.reachable_timesync_sources.iter().cloned());
10864 if let Some(stats) = st.side_runtime_stats.get(side_id) {
10865 packets_sent = packets_sent.saturating_add(stats.tx_packets);
10866 packets_received = packets_received.saturating_add(stats.rx_packets);
10867 bytes_sent = bytes_sent.saturating_add(stats.tx_bytes);
10868 bytes_received = bytes_received.saturating_add(stats.rx_bytes);
10869 }
10870 }
10871
10872 if side_ids.is_empty() {
10873 return None;
10874 }
10875 reachable_endpoints.retain(|ep| !discovery::is_router_control_endpoint(*ep));
10876 crate::small_sort::sort(&mut reachable_endpoints);
10877 reachable_endpoints.dedup();
10878 reachable_timesync_sources.sort_unstable();
10879 reachable_timesync_sources.dedup();
10880 crate::small_sort::sort(&mut side_ids);
10881 side_ids.dedup();
10882 side_names.sort_unstable();
10883 side_names.dedup();
10884 let age_ms = last_seen_ms.map(|seen| now_ms.saturating_sub(seen));
10885 Some(ClientStatsSnapshot {
10886 sender_id: sender_id.to_string(),
10887 connected: age_ms.is_some_and(|age| age <= DISCOVERY_ROUTE_TTL_MS),
10888 side_ids,
10889 side_names,
10890 last_seen_ms,
10891 age_ms,
10892 reachable_endpoints,
10893 reachable_timesync_sources,
10894 packets_sent,
10895 packets_received,
10896 bytes_sent,
10897 bytes_received,
10898 })
10899 }
10900
10901 pub fn export_runtime_stats(&self) -> RuntimeStatsSnapshot {
10902 let now_ms = self.clock.now_ms();
10903 let isr_rx = self.isr_rx_queue.snapshot().unwrap_or((0, 0));
10904 let st = self.state.lock();
10905
10906 let mut sides = Vec::new();
10907 for (side_id, side) in st.sides.iter().enumerate() {
10908 let Some(side) = side.as_ref() else { continue };
10909 let stats = st
10910 .side_runtime_stats
10911 .get(&side_id)
10912 .cloned()
10913 .unwrap_or_default();
10914 let adaptive = st
10915 .adaptive_route_stats
10916 .get(&side_id)
10917 .cloned()
10918 .unwrap_or_default()
10919 .snapshot(now_ms, true);
10920 let (tx_template_count, rx_template_count) = st
10921 .side_transport
10922 .get(&side_id)
10923 .map(|state| (state.tx_template_count(), state.rx_template_count()))
10924 .unwrap_or((0, 0));
10925 let mut data_types: Vec<RuntimeTypeStats> = stats
10926 .data_types
10927 .into_iter()
10928 .map(|(ty, item)| RuntimeTypeStats {
10929 data_type: DataType(ty),
10930 tx_packets: item.tx_packets,
10931 tx_bytes: item.tx_bytes,
10932 rx_packets: item.rx_packets,
10933 rx_bytes: item.rx_bytes,
10934 relayed_tx_packets: item.relayed_tx_packets,
10935 relayed_tx_bytes: item.relayed_tx_bytes,
10936 relayed_rx_packets: item.relayed_rx_packets,
10937 relayed_rx_bytes: item.relayed_rx_bytes,
10938 tx_retries: item.tx_retries,
10939 handler_failures: item.handler_failures,
10940 })
10941 .collect();
10942 data_types.sort_unstable_by_key(|item| item.data_type.as_u32());
10943 sides.push(RuntimeSideStats {
10944 side_id,
10945 side_name: side.name.to_string(),
10946 reliable_enabled: side.opts.reliable_enabled,
10947 link_local_enabled: side.opts.link_local_enabled,
10948 header_template_enabled: side.opts.header_template_enabled,
10949 max_frame_bytes: side.opts.max_frame_bytes,
10950 compact_header_target_bytes: side.opts.compact_header_target_bytes,
10951 side_transport_profile: side.opts.effective_transport_profile().as_str(),
10952 ingress_enabled: side.opts.ingress_enabled,
10953 egress_enabled: side.opts.egress_enabled,
10954 tx_packets: stats.tx_packets,
10955 tx_bytes: stats.tx_bytes,
10956 rx_packets: stats.rx_packets,
10957 rx_bytes: stats.rx_bytes,
10958 relayed_tx_packets: stats.relayed_tx_packets,
10959 relayed_tx_bytes: stats.relayed_tx_bytes,
10960 relayed_rx_packets: stats.relayed_rx_packets,
10961 relayed_rx_bytes: stats.relayed_rx_bytes,
10962 local_delivery_packets: stats.local_delivery_packets,
10963 tx_retries: stats.tx_retries,
10964 tx_handler_failures: stats.tx_handler_failures,
10965 local_handler_failures: stats.local_handler_failures,
10966 total_handler_retries: stats.total_handler_retries,
10967 side_transport_full_frames: stats.side_transport_full_frames,
10968 side_transport_compact_frames: stats.side_transport_compact_frames,
10969 side_transport_compact_delta_frames: stats.side_transport_compact_delta_frames,
10970 side_transport_compact_omitted_timestamp_frames: stats
10971 .side_transport_compact_omitted_timestamp_frames,
10972 side_transport_chunk_frames: stats.side_transport_chunk_frames,
10973 side_transport_raw_bytes: stats.side_transport_raw_bytes,
10974 side_transport_wire_bytes: stats.side_transport_wire_bytes,
10975 side_transport_bytes_saved: stats.side_transport_bytes_saved,
10976 side_transport_min_compact_overhead_bytes: stats
10977 .side_transport_min_compact_overhead_bytes,
10978 side_transport_max_compact_overhead_bytes: stats
10979 .side_transport_max_compact_overhead_bytes,
10980 side_transport_compact_target_misses: stats.side_transport_compact_target_misses,
10981 side_transport_template_evictions: stats.side_transport_template_evictions,
10982 side_transport_tx_template_count: tx_template_count,
10983 side_transport_rx_template_count: rx_template_count,
10984 max_side_transport_templates: side.opts.max_side_transport_templates,
10985 adaptive,
10986 data_types,
10987 });
10988 }
10989
10990 let mut route_modes: Vec<RouteModeStats> = st
10991 .route_selection_cursors
10992 .iter()
10993 .map(|(src, cursor)| RouteModeStats {
10994 src_side_id: *src,
10995 selection_mode: st.source_route_modes.get(src).copied(),
10996 cursor: *cursor,
10997 })
10998 .collect();
10999 for src in st.source_route_modes.keys() {
11000 if !route_modes.iter().any(|mode| mode.src_side_id == *src) {
11001 route_modes.push(RouteModeStats {
11002 src_side_id: *src,
11003 selection_mode: st.source_route_modes.get(src).copied(),
11004 cursor: 0,
11005 });
11006 }
11007 }
11008 route_modes.sort_unstable_by_key(|mode| mode.src_side_id.unwrap_or(usize::MAX));
11009
11010 let mut route_overrides: Vec<RouteOverrideStats> = st
11011 .route_overrides
11012 .iter()
11013 .map(|((src, dst), enabled)| RouteOverrideStats {
11014 src_side_id: *src,
11015 dst_side_id: *dst,
11016 enabled: *enabled,
11017 })
11018 .collect();
11019 route_overrides.sort_unstable_by_key(|item| {
11020 (item.src_side_id.unwrap_or(usize::MAX), item.dst_side_id)
11021 });
11022
11023 let mut typed_route_overrides: Vec<TypedRouteOverrideStats> = st
11024 .typed_route_overrides
11025 .iter()
11026 .map(|((src, ty, dst), enabled)| TypedRouteOverrideStats {
11027 src_side_id: *src,
11028 data_type: DataType(*ty),
11029 dst_side_id: *dst,
11030 enabled: *enabled,
11031 })
11032 .collect();
11033 typed_route_overrides.sort_unstable_by_key(|item| {
11034 (
11035 item.src_side_id.unwrap_or(usize::MAX),
11036 item.data_type.as_u32(),
11037 item.dst_side_id,
11038 )
11039 });
11040
11041 let mut route_weights: Vec<RouteWeightStats> = st
11042 .route_weights
11043 .iter()
11044 .map(|((src, dst), weight)| RouteWeightStats {
11045 src_side_id: *src,
11046 dst_side_id: *dst,
11047 weight: *weight,
11048 })
11049 .collect();
11050 route_weights.sort_unstable_by_key(|item| {
11051 (item.src_side_id.unwrap_or(usize::MAX), item.dst_side_id)
11052 });
11053
11054 let mut route_priorities: Vec<RoutePriorityStats> = st
11055 .route_priorities
11056 .iter()
11057 .map(|((src, dst), priority)| RoutePriorityStats {
11058 src_side_id: *src,
11059 dst_side_id: *dst,
11060 priority: *priority,
11061 })
11062 .collect();
11063 route_priorities.sort_unstable_by_key(|item| {
11064 (item.src_side_id.unwrap_or(usize::MAX), item.dst_side_id)
11065 });
11066
11067 #[cfg(feature = "discovery")]
11068 let discovery = DiscoveryRuntimeStats {
11069 route_count: st.discovery_routes.len(),
11070 announcer_count: st
11071 .discovery_routes
11072 .values()
11073 .map(|route| route.announcers.len())
11074 .sum(),
11075 current_announce_interval_ms: Some(st.discovery_cadence.current_interval_ms),
11076 next_announce_ms: Some(st.discovery_cadence.next_announce_ms),
11077 };
11078 #[cfg(not(feature = "discovery"))]
11079 let discovery = DiscoveryRuntimeStats {
11080 route_count: 0,
11081 announcer_count: 0,
11082 current_announce_interval_ms: None,
11083 next_announce_ms: None,
11084 };
11085
11086 RuntimeStatsSnapshot {
11087 sides,
11088 route_modes,
11089 route_overrides,
11090 typed_route_overrides,
11091 route_weights,
11092 route_priorities,
11093 queues: QueueRuntimeStats {
11094 rx_len: isr_rx.0.saturating_add(st.received_queue.len()),
11095 rx_bytes: isr_rx.1.saturating_add(st.received_queue.bytes_used()),
11096 tx_len: st.transmit_queue.len(),
11097 tx_bytes: st.transmit_queue.bytes_used(),
11098 replay_len: 0,
11099 replay_bytes: 0,
11100 recent_rx_len: st.recent_rx.len(),
11101 recent_rx_bytes: st.recent_rx.bytes_used(),
11102 reliable_rx_buffered_len: st.reliable_rx_buffer_len(),
11103 reliable_rx_buffered_bytes: st.reliable_rx_buffered_bytes(),
11104 shared_queue_bytes_used: st.shared_queue_bytes_used(),
11105 },
11106 reliable: ReliableRuntimeStats {
11107 reliable_return_route_count: st.reliable_return_routes.len(),
11108 end_to_end_pending_count: st.end_to_end_reliable_tx.len(),
11109 end_to_end_pending_destination_count: st
11110 .end_to_end_reliable_tx
11111 .values()
11112 .map(|sent| sent.pending_destinations.len())
11113 .sum(),
11114 end_to_end_acked_cache_count: 0,
11115 },
11116 discovery,
11117 total_handler_failures: st.total_handler_failures,
11118 total_handler_retries: st.total_handler_retries,
11119 }
11120 }
11121
11122 pub fn export_memory_layout_json(&self) -> String {
11124 let isr_rx = self.isr_rx_queue.snapshot().unwrap_or((0, 0));
11125 let st = self.state.lock();
11126 #[cfg(feature = "discovery")]
11127 let discovery_bytes = st.discovery_bytes_used();
11128 #[cfg(not(feature = "discovery"))]
11129 let discovery_bytes = 0usize;
11130 let schema_bytes = crate::config::schema_bytes_used();
11131 let network_variable_cache_bytes = st
11132 .managed_variable_latest
11133 .values()
11134 .map(|entry| entry.packet.byte_cost())
11135 .sum::<usize>();
11136 let mut out = String::new();
11137 let memory = st.memory;
11138 let _ = fmt::Write::write_fmt(
11139 &mut out,
11140 format_args!(
11141 "{{\"kind\":\"router\",\
11142 \"shared_queue_bytes_used\":{},\"shared_queue_bytes_allocated\":{},\
11143 \"rx_queue_bytes_used\":{},\"rx_queue_bytes_allocated\":{},\"rx_queue_len\":{},\
11144 \"isr_rx_queue_bytes_used\":{},\"isr_rx_queue_bytes_allocated\":{},\"isr_rx_queue_len\":{},\
11145 \"tx_queue_bytes_used\":{},\"tx_queue_bytes_allocated\":{},\"tx_queue_len\":{},\
11146 \"recent_rx_bytes_used\":{},\"recent_rx_bytes_allocated\":{},\"recent_rx_len\":{},\
11147 \"reliable_rx_buffer_bytes_used\":{},\"reliable_rx_buffer_bytes_allocated\":{},\"reliable_rx_buffer_len\":{},\
11148 \"discovery_bytes_used\":{},\"discovery_bytes_allocated\":{},\
11149 \"schema_bytes_used\":{},\"schema_bytes_allocated\":{},\
11150 \"network_variable_cache_bytes_used\":{},\"network_variable_cache_bytes_allocated\":{},\"network_variable_cache_len\":{}}}",
11151 st.shared_queue_bytes_used(),
11152 memory.max_queue_budget,
11153 st.received_queue.bytes_used(),
11154 st.received_queue.max_bytes(),
11155 st.received_queue.len(),
11156 isr_rx.1,
11157 memory.max_queue_budget,
11158 isr_rx.0,
11159 st.transmit_queue.bytes_used(),
11160 st.transmit_queue.max_bytes(),
11161 st.transmit_queue.len(),
11162 st.recent_rx.bytes_used(),
11163 st.recent_rx.max_bytes(),
11164 st.recent_rx.len(),
11165 st.reliable_rx_buffered_bytes(),
11166 memory.max_queue_budget,
11167 st.reliable_rx_buffer_len(),
11168 discovery_bytes,
11169 memory.max_queue_budget,
11170 schema_bytes,
11171 memory.max_queue_budget,
11172 network_variable_cache_bytes,
11173 memory.max_queue_budget,
11174 st.managed_variable_latest.len(),
11175 ),
11176 );
11177 out
11178 }
11179
11180 #[cfg(test)]
11181 pub(crate) fn debug_end_to_end_pending_destination_count(
11182 &self,
11183 packet_id: u64,
11184 ) -> Option<usize> {
11185 let st = self.state.lock();
11186 st.end_to_end_reliable_tx
11187 .get(&packet_id)
11188 .map(|sent| sent.pending_destinations.len())
11189 }
11190
11191 #[cfg(test)]
11192 pub(crate) fn debug_end_to_end_tracked_count(&self) -> usize {
11193 let st = self.state.lock();
11194 st.end_to_end_reliable_tx.len()
11195 }
11196
11197 #[cfg(test)]
11198 pub(crate) fn debug_reliable_return_route_count(&self) -> usize {
11199 let st = self.state.lock();
11200 st.reliable_return_routes.len()
11201 }
11202
11203 #[cfg(test)]
11204 pub(crate) fn debug_side_storage(&self) -> (usize, usize) {
11205 let st = self.state.lock();
11206 (st.sides.len(), st.sides.capacity())
11207 }
11208
11209 #[cfg(test)]
11210 pub(crate) fn debug_queue_lengths(&self) -> (usize, usize, usize) {
11211 let st = self.state.lock();
11212 (
11213 st.received_queue.len(),
11214 st.transmit_queue.len(),
11215 st.recent_rx.len(),
11216 )
11217 }
11218
11219 #[cfg(test)]
11220 pub(crate) fn debug_shared_queue_bytes_used(&self) -> usize {
11221 let st = self.state.lock();
11222 st.shared_queue_bytes_used()
11223 }
11224
11225 #[cfg(test)]
11226 pub(crate) fn debug_recent_rx_capacity(&self) -> (usize, usize) {
11227 let st = self.state.lock();
11228 (st.recent_rx.capacity(), st.recent_rx.max_bytes())
11229 }
11230
11231 fn get_hash(item: &RouterItem) -> u64 {
11235 match item {
11236 RouterItem::Packet(pkt) => pkt.packet_id(),
11237 RouterItem::Packed(bytes) => {
11238 match wire_format::packet_id_from_wire(bytes.as_ref()) {
11239 Ok(id) => id,
11240 Err(_e) => {
11241 let h: u64 = 0x9E37_79B9_7F4A_7C15;
11244 hash_bytes_u64(h, bytes.as_ref())
11245 }
11246 }
11247 }
11248 }
11249 }
11250
11251 fn remove_pkt_id(&self, item: &RouterItem) {
11253 let hash = Self::get_hash(item);
11254 let mut st = self.state.lock();
11255 st.recent_rx.remove_value(&hash);
11256 }
11257
11258 fn is_duplicate_pkt(&self, item: &RouterItem) -> TelemetryResult<bool> {
11261 let id = Self::get_hash(item);
11262 let mut st = self.state.lock();
11263 if st.recent_rx.contains(&id) {
11264 Ok(true)
11265 } else {
11266 st.push_recent_rx(id)?;
11267 Ok(false)
11268 }
11269 }
11270
11271 fn handle_callback_error(
11276 &self,
11277 pkt: &Packet,
11278 dest: Option<DataEndpoint>,
11279 e: TelemetryError,
11280 called_from_queue: bool,
11281 ) -> TelemetryResult<()> {
11282 let device = self.sender_arc();
11283 let error_msg = match dest {
11284 Some(failed_local) => format!(
11285 "Handler for endpoint {:?} failed on device {:?}: {:?}",
11286 failed_local, device, e
11287 ),
11288 None => format!("TX Handler failed on device {:?}: {:?}", device, e),
11289 };
11290
11291 let mut recipients: Vec<DataEndpoint> = pkt
11292 .endpoints()
11293 .iter()
11294 .copied()
11295 .filter(|&ep| self.cfg.is_local_endpoint(ep))
11296 .collect();
11297 crate::small_sort::sort(&mut recipients);
11298 recipients.dedup();
11299
11300 if let Some(failed_local) = dest {
11301 recipients.retain(|&ep| ep != failed_local);
11302 }
11303
11304 if recipients.is_empty() && dest.is_none() {
11308 fallback_stdout(&error_msg);
11309 return Ok(());
11310 }
11311
11312 if recipients.is_empty() {
11315 recipients = pkt.endpoints().to_vec();
11316 crate::small_sort::sort(&mut recipients);
11317 recipients.dedup();
11318 if let Some(failed_local) = dest {
11319 recipients.retain(|&ep| ep != failed_local);
11320 }
11321 }
11322
11323 if recipients.is_empty() {
11324 fallback_stdout(&error_msg);
11325 return Ok(());
11326 }
11327
11328 let payload = make_error_payload(&error_msg);
11329
11330 let sender = self.sender_arc();
11331 let error_pkt = Packet::new(
11332 DataType::TelemetryError,
11333 &recipients,
11334 sender.as_ref(),
11335 self.packet_timestamp_ms(),
11336 crate::shared_bytes::convert(payload),
11337 )?;
11338
11339 self.emit_internal_tx(
11340 RouterTxItem::Broadcast(RouterItem::Packet(error_pkt)),
11341 false,
11342 called_from_queue,
11343 )
11344 }
11345
11346 #[inline]
11350 pub fn process_tx_queue(&self) -> TelemetryResult<()> {
11351 self.process_tx_queue_with_timeout(0)
11352 }
11353
11354 #[inline]
11356 pub fn process_all_queues(&self) -> TelemetryResult<()> {
11357 self.process_all_queues_with_timeout(0)
11358 }
11359
11360 #[inline]
11362 pub fn clear_queues(&self) {
11363 let mut st = self.state.lock();
11364 st.transmit_queue.clear();
11365 st.received_queue.clear();
11366 drop(st);
11367 let _ = self.isr_rx_queue.clear();
11368 }
11369
11370 #[inline]
11372 pub fn clear_rx_queue(&self) {
11373 let mut st = self.state.lock();
11374 st.received_queue.clear();
11375 drop(st);
11376 let _ = self.isr_rx_queue.clear();
11377 }
11378
11379 #[inline]
11381 pub fn clear_tx_queue(&self) {
11382 let mut st = self.state.lock();
11383 st.transmit_queue.clear();
11384 st.tx_priority_burst = 0;
11385 }
11386
11387 fn pop_transmit_queue_locked(st: &mut RouterInner, now_ms: u64) -> Option<TxQueued> {
11388 st.transmit_queue.pop_front_fair_if(
11389 &mut st.tx_priority_burst,
11390 MAX_PRIORITY_BURST,
11391 |item| item.priority,
11392 |item| item.retry_after_ms <= now_ms,
11393 )
11394 }
11395
11396 fn process_tx_queue_with_timeout_impl(&self, timeout_ms: u32) -> TelemetryResult<()> {
11399 self.expire_side_chunks();
11400 let start = self.clock.now_ms();
11401 loop {
11402 let received_control = crate::memory_admission::allow_drain(
11403 self.drain_queued_control_rx_before_tx(start, timeout_ms),
11404 )?;
11405 let control_budget_used = received_control
11406 && timeout_ms != 0
11407 && self.clock.now_ms().wrapping_sub(start) >= u64::from(timeout_ms);
11408 if !control_budget_used {
11411 crate::memory_admission::allow_drain(self.process_reliable_timeouts())?;
11412 crate::memory_admission::allow_drain(self.process_end_to_end_reliable_timeouts())?;
11413 }
11414
11415 let pkt_opt = {
11416 let mut st = self.state.lock();
11417 Self::pop_transmit_queue_locked(&mut st, self.clock.now_ms())
11418 };
11419 let Some(pkt) = pkt_opt else { break };
11420 self.tx_item_impl(pkt.item, pkt.ignore_local, true)?;
11421 if timeout_ms != 0 && self.clock.now_ms().wrapping_sub(start) >= timeout_ms as u64 {
11422 break;
11423 }
11424 }
11425 Ok(())
11426 }
11427
11428 pub fn process_tx_queue_with_timeout(&self, timeout_ms: u32) -> TelemetryResult<()> {
11431 #[cfg(feature = "timesync")]
11432 let _ = crate::memory_admission::allow_drain(self.poll_timesync())?;
11433 #[cfg(feature = "discovery")]
11434 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
11435 self.process_tx_queue_with_timeout_impl(timeout_ms)
11436 }
11437
11438 pub fn dispatch_tx_queue_with_timeout(&self, timeout_ms: u32) -> TelemetryResult<()> {
11444 self.process_tx_queue_with_timeout_impl(timeout_ms)
11445 }
11446
11447 #[inline]
11449 fn process_rx_queue_item(&self, item: RouterRxItem) -> TelemetryResult<()> {
11450 self.rx_item(&item, true)
11451 }
11452
11453 fn process_rx_queue_with_timeout_impl(&self, timeout_ms: u32) -> TelemetryResult<()> {
11456 self.expire_side_chunks();
11457 let start = self.clock.now_ms();
11458 loop {
11459 let item_opt = self.isr_rx_queue.pop_front().unwrap_or(None).or_else(|| {
11460 let mut st = self.state.lock();
11461 st.received_queue.pop_front()
11462 });
11463 let Some(item) = item_opt else { break };
11464 self.process_rx_queue_item(item)?;
11465 if timeout_ms != 0 && self.clock.now_ms().wrapping_sub(start) >= timeout_ms as u64 {
11466 break;
11467 }
11468 }
11469 Ok(())
11470 }
11471
11472 pub fn process_rx_queue_with_timeout(&self, timeout_ms: u32) -> TelemetryResult<()> {
11475 #[cfg(feature = "timesync")]
11476 let _ = crate::memory_admission::allow_drain(self.poll_timesync())?;
11477 #[cfg(feature = "discovery")]
11478 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
11479 self.process_rx_queue_with_timeout_impl(timeout_ms)
11480 }
11481
11482 fn process_all_queues_with_timeout_impl(&self, timeout_ms: u32) -> TelemetryResult<()> {
11485 self.expire_side_chunks();
11486 let start = self.clock.now_ms();
11491 loop {
11492 let received_control = crate::memory_admission::allow_drain(
11493 self.drain_queued_control_rx_before_tx(start, timeout_ms),
11494 )?;
11495 let mut did_any = received_control;
11496 let control_budget_used = received_control
11497 && timeout_ms != 0
11498 && self.clock.now_ms().wrapping_sub(start) >= u64::from(timeout_ms);
11499 if !control_budget_used {
11500 crate::memory_admission::allow_drain(self.process_reliable_timeouts())?;
11501 crate::memory_admission::allow_drain(self.process_end_to_end_reliable_timeouts())?;
11502 }
11503
11504 if let Some(pkt) = {
11505 let mut st = self.state.lock();
11506 let now = self.clock.now_ms();
11507 let inner = &mut *st;
11508 inner.transmit_queue.pop_front_fair_if(
11509 &mut inner.tx_priority_burst,
11510 MAX_PRIORITY_BURST,
11511 |item| item.priority,
11512 |item| {
11513 item.retry_after_ms <= now && (!control_budget_used || item.priority >= 200)
11514 },
11515 )
11516 } {
11517 self.tx_item_impl(pkt.item, pkt.ignore_local, true)?;
11518 did_any = true;
11519 }
11520 if control_budget_used {
11522 return Ok(());
11523 }
11524 if let Some(item) = self.isr_rx_queue.pop_front().unwrap_or(None).or_else(|| {
11525 let mut st = self.state.lock();
11526 st.received_queue.pop_front()
11527 }) {
11528 self.process_rx_queue_item(item)?;
11529 did_any = true;
11530 }
11531 if !did_any
11532 || (timeout_ms != 0
11533 && self.clock.now_ms().wrapping_sub(start) >= u64::from(timeout_ms))
11534 {
11535 break;
11536 }
11537 }
11538
11539 Ok(())
11540 }
11541
11542 pub fn process_all_queues_with_timeout(&self, timeout_ms: u32) -> TelemetryResult<()> {
11545 #[cfg(feature = "timesync")]
11546 let _ = crate::memory_admission::allow_drain(self.poll_timesync())?;
11547 #[cfg(feature = "discovery")]
11548 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
11549 self.process_all_queues_with_timeout_impl(timeout_ms)
11550 }
11551
11552 pub fn drain_queues_with_timeout(&self, timeout_ms: u32) -> TelemetryResult<()> {
11557 self.process_all_queues_with_timeout_impl(timeout_ms)
11558 }
11559
11560 pub fn periodic(&self, timeout_ms: u32) -> TelemetryResult<()> {
11565 #[cfg(feature = "timesync")]
11566 let _ = crate::memory_admission::allow_drain(self.poll_timesync())?;
11567
11568 #[cfg(feature = "discovery")]
11569 {
11570 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
11571 }
11572
11573 self.process_all_queues_with_timeout_impl(timeout_ms)
11574 }
11575
11576 pub fn periodic_no_timesync(&self, timeout_ms: u32) -> TelemetryResult<()> {
11581 #[cfg(feature = "discovery")]
11582 {
11583 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
11584 }
11585
11586 self.process_all_queues_with_timeout_impl(timeout_ms)
11587 }
11588
11589 #[inline]
11591 fn tx_queue_item_with_flags(
11592 &self,
11593 item: RouterTxItem,
11594 ignore_local: bool,
11595 ) -> TelemetryResult<()> {
11596 self.tx_queue_item_with_flags_at(item, ignore_local, 0)
11597 }
11598
11599 fn tx_queue_item_for_retry(
11600 &self,
11601 item: RouterTxItem,
11602 ignore_local: bool,
11603 ) -> TelemetryResult<()> {
11604 self.tx_queue_item_with_flags_at(item, ignore_local, self.clock.now_ms().saturating_add(1))
11605 }
11606
11607 fn tx_queue_item_with_flags_at(
11608 &self,
11609 item: RouterTxItem,
11610 ignore_local: bool,
11611 retry_after_ms: u64,
11612 ) -> TelemetryResult<()> {
11613 let priority = match &item {
11614 RouterTxItem::Broadcast(data) => Self::router_item_priority(data)?,
11615 RouterTxItem::EndToEndReplay { .. } => {
11616 Self::router_item_priority_bumped(DataType::ReliableAck)
11617 }
11618 RouterTxItem::ToSide { data, .. } => Self::router_item_priority(data)?,
11619 RouterTxItem::ReliableReplay { bytes, .. } => {
11620 let ty = wire_format::peek_routing_envelope(bytes.as_ref())?.ty;
11621 Self::router_item_priority_bumped(ty)
11622 }
11623 };
11624 self.tx_queue_item_with_priority_at(item, ignore_local, priority, retry_after_ms)
11625 }
11626
11627 #[inline]
11628 fn tx_queue_item_with_priority(
11629 &self,
11630 item: RouterTxItem,
11631 ignore_local: bool,
11632 priority: u8,
11633 ) -> TelemetryResult<()> {
11634 self.tx_queue_item_with_priority_at(item, ignore_local, priority, 0)
11635 }
11636
11637 fn tx_queue_item_with_priority_at(
11638 &self,
11639 item: RouterTxItem,
11640 ignore_local: bool,
11641 priority: u8,
11642 retry_after_ms: u64,
11643 ) -> TelemetryResult<()> {
11644 let mut st = self.state.lock();
11645 st.push_transmit(TxQueued {
11646 item,
11647 ignore_local,
11648 priority,
11649 retry_after_ms,
11650 })?;
11651 Ok(())
11652 }
11653
11654 #[inline]
11656 fn tx_queue_item(&self, item: RouterTxItem) -> TelemetryResult<()> {
11657 self.tx_queue_item_with_flags(item, false)
11658 }
11659
11660 #[inline]
11661 fn try_enter_side_tx(&self) -> Option<crate::lock::ReentryGuard<'_>> {
11662 self.side_tx_gate.try_enter()
11663 }
11664
11665 #[inline]
11666 fn side_tx_active(&self) -> bool {
11667 self.side_tx_gate.is_active()
11668 }
11669
11670 #[inline]
11674 pub fn process_rx_queue(&self) -> TelemetryResult<()> {
11675 self.process_rx_queue_with_timeout(0)
11676 }
11677
11678 #[inline]
11680 pub fn rx_packed_queue(&self, bytes: &[u8]) -> TelemetryResult<()> {
11681 crate::memory_admission::check_receive(bytes)?;
11682 let data = RouterItem::Packed(crate::shared_bytes::convert(Arc::<[u8]>::from(bytes)));
11683 let priority = Self::router_item_priority(&data)?;
11684 let mut st = self.state.lock();
11685 st.push_received(RouterRxItem {
11686 src: None,
11687 data,
11688 priority,
11689 })?;
11690 Ok(())
11691 }
11692
11693 #[inline]
11698 pub fn rx_packed_queue_isr(&self, bytes: &[u8]) -> TelemetryResult<()> {
11699 crate::memory_admission::check_receive(bytes)?;
11700 let data = RouterItem::Packed(crate::shared_bytes::convert(Arc::<[u8]>::from(bytes)));
11701 let priority = Self::router_item_priority(&data)?;
11702 self.isr_rx_queue.push_back_prioritized(RouterRxItem {
11703 src: None,
11704 data,
11705 priority,
11706 })
11707 }
11708
11709 #[inline]
11711 pub fn rx_queue(&self, pkt: Packet) -> TelemetryResult<()> {
11712 pkt.validate()?;
11713 let data = RouterItem::Packet(pkt);
11714 let priority = Self::router_item_priority(&data)?;
11715 let mut st = self.state.lock();
11716 st.push_received(RouterRxItem {
11717 src: None,
11718 data,
11719 priority,
11720 })?;
11721 Ok(())
11722 }
11723
11724 #[inline]
11729 pub fn rx_queue_isr(&self, pkt: Packet) -> TelemetryResult<()> {
11730 pkt.validate()?;
11731 let data = RouterItem::Packet(pkt);
11732 let priority = Self::router_item_priority(&data)?;
11733 self.isr_rx_queue.push_back_prioritized(RouterRxItem {
11734 src: None,
11735 data,
11736 priority,
11737 })
11738 }
11739
11740 #[inline]
11742 pub fn rx_queue_from_side(&self, pkt: Packet, side: RouterSideId) -> TelemetryResult<()> {
11743 self.ensure_side_ingress_enabled(side)?;
11744 pkt.validate()?;
11745 let data = RouterItem::Packet(pkt);
11746 let priority = Self::router_item_priority(&data)?;
11747 let mut st = self.state.lock();
11748 st.push_received(RouterRxItem {
11749 src: Some(side),
11750 data,
11751 priority,
11752 })?;
11753 Ok(())
11754 }
11755
11756 #[inline]
11761 pub fn rx_queue_from_side_isr(&self, pkt: Packet, side: RouterSideId) -> TelemetryResult<()> {
11762 self.ensure_side_ingress_enabled(side)?;
11763 pkt.validate()?;
11764 let data = RouterItem::Packet(pkt);
11765 let priority = Self::router_item_priority(&data)?;
11766 self.isr_rx_queue.push_back_prioritized(RouterRxItem {
11767 src: Some(side),
11768 data,
11769 priority,
11770 })
11771 }
11772
11773 #[inline]
11775 pub fn rx_packed_queue_from_side(
11776 &self,
11777 bytes: &[u8],
11778 side: RouterSideId,
11779 ) -> TelemetryResult<()> {
11780 self.ensure_side_ingress_enabled(side)?;
11781 let Some(decoded) = self.decode_side_transport_frame(side, bytes)? else {
11782 return Ok(());
11783 };
11784 let data = RouterItem::Packed(decoded);
11785 let priority = Self::router_item_priority(&data)?;
11786 let mut st = self.state.lock();
11787 st.push_received(RouterRxItem {
11788 src: Some(side),
11789 data,
11790 priority,
11791 })?;
11792 Ok(())
11793 }
11794
11795 #[inline]
11800 pub fn rx_packed_queue_from_side_isr(
11801 &self,
11802 bytes: &[u8],
11803 side: RouterSideId,
11804 ) -> TelemetryResult<()> {
11805 self.ensure_side_ingress_enabled(side)?;
11806 crate::memory_admission::check_receive(bytes)?;
11807 let data = RouterItem::Packed(crate::shared_bytes::convert(Arc::<[u8]>::from(bytes)));
11808 let priority = Self::router_item_priority(&data)?;
11809 self.isr_rx_queue.push_back_prioritized(RouterRxItem {
11810 src: Some(side),
11811 data,
11812 priority,
11813 })
11814 }
11815
11816 fn retry_with_attempts<F, T>(
11817 &self,
11818 times: usize,
11819 f: F,
11820 ) -> Result<(T, usize), (TelemetryError, usize)>
11821 where
11822 F: Fn() -> TelemetryResult<T>,
11823 {
11824 let mut last_err = None;
11825 for attempt in 0..times {
11826 match f() {
11827 Ok(v) => return Ok((v, attempt + 1)),
11828 Err(e) if Self::is_side_tx_busy(&e) => return Err((e, attempt + 1)),
11831 Err(e) => last_err = Some((e, attempt + 1)),
11832 }
11833 }
11834 Err(last_err.expect("times > 0"))
11835 }
11836
11837 #[inline]
11839 fn endpoint_has_packet_handler(&self, ep: DataEndpoint) -> bool {
11840 self.cfg
11841 .handlers
11842 .iter()
11843 .any(|h| h.endpoint == ep && matches!(h.handler, EndpointHandlerFn::Packet(_)))
11844 }
11845
11846 #[inline]
11848 fn endpoint_has_packed_handler(&self, ep: DataEndpoint) -> bool {
11849 self.cfg
11850 .handlers
11851 .iter()
11852 .any(|h| h.endpoint == ep && matches!(h.handler, EndpointHandlerFn::Packed(_)))
11853 }
11854
11855 fn packet_has_local_handler(&self, pkt: &Packet) -> bool {
11856 pkt.endpoints()
11857 .iter()
11858 .copied()
11859 .any(|ep| self.endpoint_has_packet_handler(ep) || self.endpoint_has_packed_handler(ep))
11860 }
11861
11862 fn packet_has_local_delivery(&self, pkt: &Packet) -> bool {
11869 self.packet_has_local_handler(pkt) || self.is_managed_variable_type(pkt.data_type())
11870 }
11871
11872 fn call_handler_with_retries(
11878 &self,
11879 dest: DataEndpoint,
11880 handler: &EndpointHandler,
11881 data: Option<&[u8]>,
11882 pkt_for_ctx: Option<&Packet>,
11883 env_for_ctx: Option<&wire_format::TelemetryEnvelope>,
11884 called_from_queue: bool,
11885 ) -> TelemetryResult<()> {
11886 let owned_tmp: Option<RouterItem>;
11887
11888 let item_for_ctx: &RouterItem = match (data, pkt_for_ctx) {
11889 (Some(d), _) => {
11890 owned_tmp = Some(RouterItem::Packed(crate::shared_bytes::convert(
11891 Arc::<[u8]>::from(d),
11892 )));
11893 owned_tmp.as_ref().unwrap()
11894 }
11895 (None, Some(pkt)) => {
11896 owned_tmp = Some(RouterItem::Packet(pkt.clone()));
11897 owned_tmp.as_ref().unwrap()
11898 }
11899 (None, None) => {
11900 debug_assert!(
11901 false,
11902 "call_handler_with_retries called without data or packet context"
11903 );
11904 return Ok(());
11905 }
11906 };
11907
11908 match (&handler.handler, data) {
11909 (EndpointHandlerFn::Packet(f), _) => {
11910 let pkt = pkt_for_ctx.expect("Packet handler requires Packet context");
11911 with_retries(
11912 self,
11913 dest,
11914 item_for_ctx,
11915 pkt_for_ctx,
11916 env_for_ctx,
11917 called_from_queue,
11918 || f(pkt),
11919 )
11920 }
11921
11922 (EndpointHandlerFn::Packed(f), Some(bytes)) => with_retries(
11923 self,
11924 dest,
11925 item_for_ctx,
11926 pkt_for_ctx,
11927 env_for_ctx,
11928 called_from_queue,
11929 || f(bytes),
11930 ),
11931
11932 (EndpointHandlerFn::Packed(_), None) => Ok(()),
11933 }
11934 }
11935
11936 fn handle_callback_error_from_env(
11941 &self,
11942 env: &wire_format::TelemetryEnvelope,
11943 dest: Option<DataEndpoint>,
11944 e: TelemetryError,
11945 called_from_queue: bool,
11946 ) -> TelemetryResult<()> {
11947 let mut recipients: Vec<DataEndpoint> = env
11948 .endpoints
11949 .iter()
11950 .copied()
11951 .filter(|&ep| self.cfg.is_local_endpoint(ep))
11952 .collect();
11953 crate::small_sort::sort(&mut recipients);
11954 recipients.dedup();
11955 if let Some(failed) = dest {
11956 recipients.retain(|&ep| ep != failed);
11957 }
11958
11959 let device = self.sender_arc();
11960 let error_msg = format!(
11961 "Handler for endpoint {:?} failed on device {:?}: {:?}",
11962 dest, device, e
11963 );
11964
11965 if recipients.is_empty() && dest.is_none() {
11966 fallback_stdout(&error_msg);
11967 return Ok(());
11968 }
11969
11970 if recipients.is_empty() {
11971 recipients = env.endpoints.to_vec();
11972 crate::small_sort::sort(&mut recipients);
11973 recipients.dedup();
11974 if let Some(failed) = dest {
11975 recipients.retain(|&ep| ep != failed);
11976 }
11977 }
11978
11979 if recipients.is_empty() {
11980 fallback_stdout(&error_msg);
11981 return Ok(());
11982 }
11983
11984 let payload = make_error_payload(&error_msg);
11985
11986 let error_pkt = Packet::new(
11987 DataType::TelemetryError,
11988 &recipients,
11989 &env.sender.clone(),
11990 env.timestamp_ms,
11991 crate::shared_bytes::convert(payload),
11992 )?;
11993 self.emit_internal_tx(
11994 RouterTxItem::Broadcast(RouterItem::Packet(error_pkt)),
11995 false,
11996 called_from_queue,
11997 )
11998 }
11999
12000 fn handle_internal_reliable_packet(
12001 &self,
12002 pkt: &Packet,
12003 src: Option<RouterSideId>,
12004 called_from_queue: bool,
12005 ) -> TelemetryResult<bool> {
12006 if !matches!(
12007 pkt.data_type(),
12008 DataType::ReliableAck | DataType::ReliablePartialAck | DataType::ReliablePacketRequest
12009 ) {
12010 return Ok(false);
12011 }
12012
12013 let Some(src) = src else {
12014 return Ok(false);
12015 };
12016
12017 if Self::is_end_to_end_ack_packet(pkt)
12018 && let Ok(packet_id) = Self::decode_end_to_end_reliable_ack(pkt.payload())
12019 {
12020 let upstream_contract = {
12021 let st = self.state.lock();
12022 st.reliable_return_routes
12023 .get(&packet_id)
12024 .filter(|route| route.side != src)
12025 .and_then(|route| route.upstream_contract)
12026 };
12027 if let Some((upstream_id, publisher_hash)) = upstream_contract {
12028 self.queue_end_to_end_reliable_ack_for_sender_hash(
12032 upstream_id,
12033 publisher_hash,
12034 called_from_queue,
12035 )?;
12036 return Ok(true);
12037 }
12038 let targets_local_sender = pkt.wire_target_senders().is_empty()
12045 || pkt
12046 .wire_target_senders()
12047 .contains(&Self::sender_hash(self.sender_arc().as_ref()));
12048 let mut st = self.state.lock();
12049 if let Some(sent) = st.end_to_end_reliable_tx.get_mut(&packet_id) {
12050 if sent.waiting_for_predecessor {
12051 return Ok(targets_local_sender);
12052 }
12053 if let Some(sender_hash) = Self::end_to_end_ack_sender_hash(pkt) {
12054 let _removed = sent.pending_destinations.remove(&sender_hash);
12055 #[cfg(feature = "std")]
12056 if reliable_trace_enabled() {
12057 eprintln!(
12058 "[sedsnet-reliable] receipt time={} id={packet_id:#x} sender={sender_hash:#x} matched={} remaining={:?}",
12059 self.clock.now_ms(),
12060 _removed.is_some(),
12061 sent.pending_destinations
12062 );
12063 }
12064 if sent.pending_destinations.is_empty() {
12065 st.end_to_end_reliable_tx.remove(&packet_id);
12066 }
12067 drop(st);
12068 self.release_ordered_successors()?;
12069 return Ok(targets_local_sender);
12070 }
12071 st.end_to_end_reliable_tx.remove(&packet_id);
12072 drop(st);
12073 self.release_ordered_successors()?;
12074 return Ok(targets_local_sender);
12075 }
12076 return Ok(false);
12077 }
12078
12079 let vals = pkt.data_as_u32()?;
12080 if vals.len() != 2 {
12081 return Err(TelemetryError::Unpack("bad reliable control payload"));
12082 }
12083 let ty = DataType::try_from_u32(vals[0]).ok_or(TelemetryError::InvalidType)?;
12084 let seq = vals[1];
12085
12086 match pkt.data_type() {
12087 DataType::ReliableAck => {
12088 self.handle_reliable_ack(src, ty, seq);
12089 Ok(true)
12090 }
12091 DataType::ReliablePartialAck => {
12092 self.handle_reliable_partial_ack(src, ty, seq);
12093 Ok(true)
12094 }
12095 DataType::ReliablePacketRequest => {
12096 self.queue_reliable_retransmit(src, ty, seq, called_from_queue)?;
12097 Ok(true)
12098 }
12099 _ => Ok(false),
12100 }
12101 }
12102
12103 fn rx_item(&self, item: &RouterRxItem, called_from_queue: bool) -> TelemetryResult<()> {
12109 if crate::memory_admission::supports_work_kinds()
12110 && let (Some(side), RouterItem::Packed(bytes)) = (item.src, &item.data)
12111 {
12112 self.admit_received_frame(side, bytes)?;
12113 }
12114 if let Some(src) = item.src {
12115 self.ensure_side_ingress_enabled(src)?;
12116 match &item.data {
12117 RouterItem::Packet(pkt) => {
12118 let bytes = wire_format::packet_wire_size(pkt);
12119 self.note_side_rx(src, pkt.data_type(), bytes, true);
12120 }
12121 RouterItem::Packed(bytes) => {
12122 if let Ok(env) = wire_format::peek_routing_envelope(bytes.as_ref()) {
12123 self.note_side_rx(src, env.ty, bytes.len(), true);
12124 }
12125 }
12126 }
12127 match &item.data {
12128 RouterItem::Packet(pkt) => {
12129 if (is_reliable_type(pkt.data_type()) || !pkt.wire_target_senders().is_empty())
12130 && !is_internal_control_type(pkt.data_type())
12131 {
12132 self.note_reliable_return_route(src, pkt.packet_id());
12133 }
12134 }
12135 RouterItem::Packed(bytes) => {
12136 if let Ok(env) = wire_format::peek_routing_envelope(bytes.as_ref())
12137 && (is_reliable_type(env.ty) || env.target_senders().len() != 0)
12138 && !is_internal_control_type(env.ty)
12139 && let Ok(packet_id) = wire_format::packet_id_from_wire(bytes.as_ref())
12140 {
12141 self.note_reliable_return_route(src, packet_id);
12142 }
12143 }
12144 }
12145 }
12146 match &item.data {
12147 RouterItem::Packet(pkt) => {
12148 if !is_internal_control_type(pkt.data_type()) {
12149 self.remember_managed_variable_packet(pkt)?;
12150 }
12151 }
12152 RouterItem::Packed(bytes) => {
12153 if let Ok(env) = wire_format::peek_routing_envelope(bytes.as_ref())
12154 && !is_internal_control_type(env.ty)
12155 && self.is_managed_variable_type(env.ty)
12156 {
12157 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
12158 pkt.validate()?;
12159 self.remember_managed_variable_packet(&pkt)?;
12160 }
12161 }
12162 }
12163 let mut released_buffered: Vec<crate::SharedBytes> = Vec::new();
12164 if let (Some(src), RouterItem::Packed(bytes)) = (item.src, &item.data) {
12165 let (_opts, handler_is_packed, hop_reliable_enabled) = {
12166 let st = self.state.lock();
12167 let side_ref = Self::side_ref(&st, src)?;
12168 let opts = side_ref.opts.clone();
12169 (
12170 opts.clone(),
12171 matches!(
12172 side_ref.tx_handler,
12173 RouterTxHandlerFn::Packed(_) | RouterTxHandlerFn::PackedWithPriority(_)
12174 ),
12175 opts.reliable_enabled
12176 && self.cfg.reliable_enabled()
12177 && !self.side_has_multiple_announcers_locked(&st, src, self.clock.now_ms()),
12178 )
12179 };
12180
12181 if hop_reliable_enabled && handler_is_packed {
12182 let frame = match wire_format::peek_routing_frame_info(bytes.as_ref()) {
12183 Ok(frame) => frame,
12184 Err(e) => {
12185 if matches!(e, TelemetryError::Unpack(msg) if msg == "crc32 mismatch") {
12186 if let Ok(frame) =
12187 wire_format::peek_frame_info_unchecked(bytes.as_ref())
12188 && is_reliable_type(frame.envelope.ty)
12189 && let Some(hdr) = frame.reliable
12190 {
12191 let unordered =
12192 (hdr.flags & wire_format::RELIABLE_FLAG_UNORDERED) != 0;
12193 let unsequenced =
12194 (hdr.flags & wire_format::RELIABLE_FLAG_UNSEQUENCED) != 0;
12195
12196 if !unsequenced {
12197 let requested = if unordered {
12198 hdr.seq
12199 } else {
12200 let mut st = self.state.lock();
12201 let rx_state = self.reliable_rx_state_mut(
12202 &mut st,
12203 src,
12204 frame.envelope.ty,
12205 );
12206 rx_state.expected_seq.min(hdr.seq)
12207 };
12208 self.queue_reliable_packet_request(
12209 src,
12210 frame.envelope.ty,
12211 requested,
12212 called_from_queue,
12213 )?;
12214 }
12215 }
12216 return Ok(());
12217 }
12218 return Err(e);
12219 }
12220 };
12221 if is_reliable_type(frame.envelope.ty)
12222 && let Some(hdr) = frame.reliable
12223 {
12224 if frame.ack_only() {
12225 self.handle_reliable_ack(src, frame.envelope.ty, hdr.ack);
12226 return Ok(());
12227 }
12228 let unordered = (hdr.flags & wire_format::RELIABLE_FLAG_UNORDERED) != 0;
12229 let unsequenced = (hdr.flags & wire_format::RELIABLE_FLAG_UNSEQUENCED) != 0;
12230
12231 if !unsequenced {
12232 if unordered {
12233 self.queue_reliable_ack(
12234 src,
12235 frame.envelope.ty,
12236 hdr.seq,
12237 called_from_queue,
12238 )?;
12239 } else {
12240 let mut release: Vec<crate::SharedBytes> = Vec::new();
12241 let mut last_delivered = None;
12242 let mut ack_old = None;
12243 let mut request_missing = None;
12244 let mut partial_ack = None;
12245 {
12246 let mut st = self.state.lock();
12247 let rx_state =
12248 self.reliable_rx_state_mut(&mut st, src, frame.envelope.ty);
12249 let expected_seq = rx_state.expected_seq;
12250 if hdr.seq < expected_seq {
12251 ack_old = Some(expected_seq.saturating_sub(1));
12252 } else if hdr.seq > expected_seq {
12253 request_missing = Some(expected_seq);
12254 partial_ack = Some(hdr.seq);
12255 st.buffer_reliable_rx(
12256 src,
12257 frame.envelope.ty,
12258 hdr.seq,
12259 bytes.clone(),
12260 )?;
12261 } else {
12262 release.push(bytes.clone());
12263 last_delivered = Some(hdr.seq);
12264 let mut next_expected = hdr.seq.wrapping_add(1);
12265 while let Some(buf) = rx_state.buffered.remove(&next_expected) {
12266 release.push(buf);
12267 last_delivered = Some(next_expected);
12268 let next = next_expected.wrapping_add(1);
12269 next_expected = if next == 0 { 1 } else { next };
12270 }
12271 rx_state.expected_seq = next_expected;
12272 }
12273 }
12274
12275 if let Some(ack_seq) = ack_old {
12276 self.queue_reliable_ack(
12277 src,
12278 frame.envelope.ty,
12279 ack_seq,
12280 called_from_queue,
12281 )?;
12282 return Ok(());
12283 }
12284 if let Some(request_seq) = request_missing {
12285 if let Some(partial_seq) = partial_ack {
12286 self.queue_reliable_partial_ack(
12287 src,
12288 frame.envelope.ty,
12289 partial_seq,
12290 called_from_queue,
12291 )?;
12292 }
12293 self.queue_reliable_packet_request(
12294 src,
12295 frame.envelope.ty,
12296 request_seq,
12297 called_from_queue,
12298 )?;
12299 return Ok(());
12300 }
12301
12302 if let Some(ack_seq) = last_delivered {
12303 self.queue_reliable_ack(
12304 src,
12305 frame.envelope.ty,
12306 ack_seq,
12307 called_from_queue,
12308 )?;
12309 }
12310
12311 released_buffered.extend(release.into_iter().skip(1));
12312 }
12313 }
12314 }
12315 } else {
12316 match wire_format::peek_routing_frame_info(bytes.as_ref()) {
12317 Ok(frame) => {
12318 if frame.ack_only() {
12319 return Ok(());
12320 }
12321 }
12322 Err(e) => {
12323 if matches!(e, TelemetryError::Unpack(msg) if msg == "crc32 mismatch") {
12324 return Ok(());
12325 }
12326 return Err(e);
12327 }
12328 }
12329 }
12330 }
12331
12332 if self.is_duplicate_pkt(&item.data)? {
12333 if (item.src.is_some()
12336 && self
12337 .item_target_senders(&item.data)?
12338 .iter()
12339 .any(|target| *target != Self::sender_hash(self.sender_arc().as_ref())))
12340 || (item.src.is_some() && {
12341 let (endpoints, _) = self.item_route_info(&item.data)?;
12342 self.item_targets_router_as_intermediate_hop(&item.data, &endpoints)?
12343 })
12344 {
12345 let id = Self::get_hash(&item.data);
12346 let replay = {
12347 let mut st = self.state.lock();
12348 st.reliable_return_routes.get_mut(&id).is_some_and(|route| {
12349 let now = self.clock.now_ms();
12350 if now.saturating_sub(route.last_replay_ms)
12351 < runtime_reliable_retransmit_ms()
12352 {
12353 return false;
12354 }
12355 route.last_replay_ms = now;
12356 true
12357 })
12358 };
12359 if replay {
12360 self.relay_send(item.data.clone(), item.src, called_from_queue)?;
12361 }
12362 }
12363 if item.src.is_some() {
12364 let local_sender = self.sender_arc();
12365 match &item.data {
12366 RouterItem::Packet(pkt)
12367 if (is_reliable_type(pkt.data_type())
12368 || !pkt.wire_target_senders().is_empty())
12369 && pkt.sender() != local_sender.as_ref()
12370 && self.item_targets_local_sender(&item.data)?
12371 && self.packet_has_local_delivery(pkt) =>
12372 {
12373 self.queue_end_to_end_reliable_ack(pkt, called_from_queue)?;
12374 }
12375 RouterItem::Packed(bytes) => {
12376 if let Ok(pkt) = wire_format::unpack_packet(bytes.as_ref())
12377 && (is_reliable_type(pkt.data_type())
12378 || !pkt.wire_target_senders().is_empty())
12379 && pkt.sender() != local_sender.as_ref()
12380 && self.item_targets_local_sender(&item.data)?
12381 && self.packet_has_local_delivery(&pkt)
12382 {
12383 let packet_id = wire_format::packet_id_from_wire(bytes.as_ref())
12384 .unwrap_or_else(|_| pkt.packet_id());
12385 self.queue_end_to_end_reliable_ack_for_packet_id(
12386 packet_id,
12387 pkt.sender(),
12388 called_from_queue,
12389 )?;
12390 }
12391 }
12392 _ => {}
12393 }
12394 }
12395 return Ok(());
12396 }
12397
12398 self.dispatch_rx_data(item, called_from_queue)?;
12399
12400 for release_bytes in released_buffered {
12401 let release_data = RouterItem::Packed(release_bytes.clone());
12402 if self.is_duplicate_pkt(&release_data)? {
12403 continue;
12404 }
12405 let release_item = RouterRxItem {
12406 src: item.src,
12407 priority: Self::router_item_priority(&release_data)?,
12408 data: release_data,
12409 };
12410 self.dispatch_rx_data(&release_item, called_from_queue)?;
12411 }
12412
12413 Ok(())
12414 }
12415
12416 fn dispatch_rx_data(
12417 &self,
12418 item: &RouterRxItem,
12419 called_from_queue: bool,
12420 ) -> TelemetryResult<()> {
12421 match &item.data {
12422 RouterItem::Packet(pkt) => {
12423 pkt.validate()?;
12424 #[cfg(feature = "discovery")]
12425 if let Some(side) = item.src {
12426 self.refresh_peer_liveness(side, sender_address_u32(pkt.sender()));
12427 }
12428
12429 if self.handle_internal_reliable_packet(pkt, item.src, called_from_queue)? {
12430 return Ok(());
12431 }
12432
12433 if pkt.data_type() == DataType::P2pMessage {
12434 if self.item_targets_local_sender(&item.data)? {
12435 self.dispatch_p2p_packet(pkt)?;
12436 if item.src.is_some() {
12437 self.queue_end_to_end_reliable_ack(pkt, called_from_queue)?;
12438 }
12439 }
12440 if self.should_route_remote(&item.data, item.src)? {
12441 self.relay_send(
12442 RouterItem::Packet(pkt.to_owned()),
12443 item.src,
12444 called_from_queue,
12445 )?;
12446 }
12447 return Ok(());
12448 }
12449
12450 #[cfg(feature = "timesync")]
12451 if matches!(
12452 pkt.data_type(),
12453 DataType::TimeSyncAnnounce
12454 | DataType::TimeSyncRequest
12455 | DataType::TimeSyncResponse
12456 ) {
12457 self.handle_internal_timesync_packet(pkt, item.src, called_from_queue)?;
12458 return Ok(());
12459 }
12460
12461 if self.learn_discovery_packet(pkt, item.src, called_from_queue)? {
12462 if !discovery_is_hop_local_advertisement(pkt.data_type())
12467 && self.should_route_remote(&item.data, item.src)?
12468 {
12469 self.relay_send(
12470 RouterItem::Packet(pkt.to_owned()),
12471 item.src,
12472 called_from_queue,
12473 )?;
12474 }
12475 return Ok(());
12476 }
12477
12478 let mut eps: Vec<DataEndpoint> = pkt.endpoints().to_vec();
12479 crate::small_sort::sort(&mut eps);
12480 eps.dedup();
12481 let had_local_handler = eps.iter().copied().any(|ep| {
12482 self.endpoint_has_packet_handler(ep) || self.endpoint_has_packed_handler(ep)
12483 });
12484 let had_local_delivery =
12485 had_local_handler || self.is_managed_variable_type(pkt.data_type());
12486
12487 let has_remote = self.should_route_remote(&item.data, item.src)?;
12488 let targets_local = self.item_targets_local_sender(&item.data)?;
12489
12490 let has_packed_local = eps
12491 .iter()
12492 .copied()
12493 .any(|ep| self.endpoint_has_packed_handler(ep));
12494 let bytes_opt = if has_packed_local {
12495 Some(wire_format::pack_packet(pkt))
12496 } else {
12497 None
12498 };
12499
12500 if targets_local {
12501 for dest in eps {
12502 for h in self.cfg.handlers.iter().filter(|h| h.endpoint == dest) {
12503 let result = match (&h.handler, &bytes_opt) {
12504 (EndpointHandlerFn::Packed(_), Some(bytes)) => self
12505 .call_handler_with_retries(
12506 dest,
12507 h,
12508 Some(bytes.as_ref()),
12509 Some(pkt),
12510 None,
12511 called_from_queue,
12512 ),
12513 (EndpointHandlerFn::Packed(_), None) => {
12514 let bytes = wire_format::pack_packet(pkt);
12515 self.call_handler_with_retries(
12516 dest,
12517 h,
12518 Some(bytes.as_ref()),
12519 Some(pkt),
12520 None,
12521 called_from_queue,
12522 )
12523 }
12524 (EndpointHandlerFn::Packet(_), _) => self
12525 .call_handler_with_retries(
12526 dest,
12527 h,
12528 None,
12529 Some(pkt),
12530 None,
12531 called_from_queue,
12532 ),
12533 };
12534 if result.is_err()
12535 && let Some(src) = item.src
12536 {
12537 self.note_side_local_handler_failure(
12538 src,
12539 pkt.data_type(),
12540 runtime_max_handler_retries(),
12541 );
12542 }
12543 }
12544 }
12545 }
12546
12547 if let Some(src) = item.src
12548 && had_local_delivery
12549 && targets_local
12550 {
12551 self.note_side_local_delivery(src, pkt.data_type());
12552 }
12553
12554 if item.src.is_some()
12555 && had_local_delivery
12556 && targets_local
12557 && (is_reliable_type(pkt.data_type()) || !pkt.wire_target_senders().is_empty())
12558 {
12559 self.queue_end_to_end_reliable_ack(pkt, called_from_queue)?;
12560 }
12561
12562 if has_remote {
12563 let relay_item = RouterItem::Packet(pkt.to_owned());
12564 self.relay_send(relay_item, item.src, called_from_queue)?;
12565 }
12566
12567 Ok(())
12568 }
12569 RouterItem::Packed(bytes) => {
12570 let routing = wire_format::peek_routing_envelope(bytes.as_ref())?;
12574 if !is_internal_control_type(routing.ty)
12575 && !routing.is_reliable
12576 && routing.target_senders().len() == 0
12577 && !self.is_managed_variable_type(routing.ty)
12578 && !routing.endpoints().any(|ep| {
12579 self.endpoint_has_packet_handler(ep) || self.endpoint_has_packed_handler(ep)
12580 })
12581 {
12582 return self.relay_send(item.data.clone(), item.src, called_from_queue);
12586 }
12587
12588 let env = wire_format::peek_envelope(bytes.as_ref())?;
12589
12590 if matches!(
12591 env.ty,
12592 DataType::ReliableAck
12593 | DataType::ReliablePartialAck
12594 | DataType::ReliablePacketRequest
12595 ) {
12596 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
12597 pkt.validate()?;
12598 let handled =
12599 self.handle_internal_reliable_packet(&pkt, item.src, called_from_queue)?;
12600 if !handled && self.should_route_remote(&item.data, item.src)? {
12603 self.relay_send(RouterItem::Packet(pkt), item.src, called_from_queue)?;
12604 }
12605 return Ok(());
12606 }
12607
12608 if env.ty == DataType::P2pMessage {
12609 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
12610 pkt.validate()?;
12611 if self.item_targets_local_sender(&item.data)? {
12612 self.dispatch_p2p_packet(&pkt)?;
12613 if item.src.is_some() {
12614 let packet_id = wire_format::packet_id_from_wire(bytes.as_ref())
12615 .unwrap_or_else(|_| pkt.packet_id());
12616 self.queue_end_to_end_reliable_ack_for_packet_id(
12617 packet_id,
12618 pkt.sender(),
12619 called_from_queue,
12620 )?;
12621 }
12622 }
12623 if self.should_route_remote(&item.data, item.src)? {
12624 self.relay_send(RouterItem::Packet(pkt), item.src, called_from_queue)?;
12625 }
12626 return Ok(());
12627 }
12628
12629 #[cfg(feature = "timesync")]
12630 if matches!(
12631 env.ty,
12632 DataType::TimeSyncAnnounce
12633 | DataType::TimeSyncRequest
12634 | DataType::TimeSyncResponse
12635 ) {
12636 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
12637 pkt.validate()?;
12638 self.handle_internal_timesync_packet(&pkt, item.src, called_from_queue)?;
12639 return Ok(());
12640 }
12641
12642 #[cfg(feature = "discovery")]
12643 if discovery::is_discovery_type(env.ty) {
12644 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
12645 pkt.validate()?;
12646 let _ = self.learn_discovery_packet(&pkt, item.src, called_from_queue)?;
12647 if !discovery_is_hop_local_advertisement(env.ty)
12648 && self.should_route_remote(&item.data, item.src)?
12649 {
12650 self.relay_send(RouterItem::Packet(pkt), item.src, called_from_queue)?;
12651 }
12652 return Ok(());
12653 }
12654
12655 let any_packet_needed = env
12656 .endpoints
12657 .iter()
12658 .copied()
12659 .any(|ep| self.endpoint_has_packet_handler(ep));
12660
12661 let mut pkt_opt = if any_packet_needed {
12662 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
12663 pkt.validate()?;
12664 Some(pkt)
12665 } else {
12666 None
12667 };
12668
12669 let mut eps: Vec<DataEndpoint> = env.endpoints.iter().copied().collect();
12670 crate::small_sort::sort(&mut eps);
12671 eps.dedup();
12672 let had_local_handler = eps.iter().copied().any(|ep| {
12673 self.endpoint_has_packet_handler(ep) || self.endpoint_has_packed_handler(ep)
12674 });
12675 let had_local_delivery = had_local_handler || self.is_managed_variable_type(env.ty);
12676
12677 let has_remote = self.should_route_remote(&item.data, item.src)?;
12678 let targets_local = self.item_targets_local_sender(&item.data)?;
12679
12680 if targets_local {
12681 for dest in eps {
12682 for h in self.cfg.handlers.iter().filter(|h| h.endpoint == dest) {
12683 let result = match &h.handler {
12684 EndpointHandlerFn::Packed(_) => self.call_handler_with_retries(
12685 dest,
12686 h,
12687 Some(bytes.as_ref()),
12688 pkt_opt.as_ref(),
12689 Some(&env),
12690 called_from_queue,
12691 ),
12692 EndpointHandlerFn::Packet(_) => {
12693 if pkt_opt.is_none() {
12694 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
12695 pkt.validate()?;
12696 pkt_opt = Some(pkt);
12697 }
12698 let pkt_ref = pkt_opt.as_ref().expect("just set");
12699 self.call_handler_with_retries(
12700 dest,
12701 h,
12702 None,
12703 Some(pkt_ref),
12704 Some(&env),
12705 called_from_queue,
12706 )
12707 }
12708 };
12709 if result.is_err()
12710 && let Some(src) = item.src
12711 {
12712 self.note_side_local_handler_failure(
12713 src,
12714 env.ty,
12715 runtime_max_handler_retries(),
12716 );
12717 }
12718 }
12719 }
12720 }
12721
12722 if item.src.is_some()
12723 && had_local_delivery
12724 && targets_local
12725 && (is_reliable_type(env.ty) || !env.target_senders.is_empty())
12726 {
12727 let packet_id =
12728 wire_format::packet_id_from_wire(bytes.as_ref()).or_else(|_| {
12729 pkt_opt
12730 .as_ref()
12731 .map(Packet::packet_id)
12732 .ok_or(TelemetryError::Unpack("missing packet id"))
12733 })?;
12734 self.queue_end_to_end_reliable_ack_for_packet_id(
12735 packet_id,
12736 &env.sender,
12737 called_from_queue,
12738 )?;
12739 }
12740
12741 if has_remote {
12742 let relay_item = match pkt_opt {
12743 Some(ref p) => RouterItem::Packet(p.clone()),
12744 None => RouterItem::Packed(bytes.clone()),
12745 };
12746 self.relay_send(relay_item, item.src, called_from_queue)?;
12747 }
12748
12749 Ok(())
12750 }
12751 }
12752 }
12753
12754 fn dispatch_local_for_item(
12755 &self,
12756 item: &RouterItem,
12757 called_from_queue: bool,
12758 ) -> TelemetryResult<()> {
12759 match item {
12760 RouterItem::Packet(pkt) => {
12761 pkt.validate()?;
12762 if is_internal_control_type(pkt.data_type()) {
12763 return Ok(());
12764 }
12765 self.ensure_e2e_policy_supported_for_type(pkt.data_type())?;
12766 if !self.item_targets_local_sender(item)? {
12767 return Ok(());
12768 }
12769
12770 let mut eps: Vec<DataEndpoint> = pkt.endpoints().to_vec();
12771 crate::small_sort::sort(&mut eps);
12772 eps.dedup();
12773
12774 let has_packed_local = eps
12775 .iter()
12776 .copied()
12777 .any(|ep| self.endpoint_has_packed_handler(ep));
12778 let bytes_opt = if has_packed_local {
12779 Some(wire_format::pack_packet(pkt))
12780 } else {
12781 None
12782 };
12783
12784 for dest in eps {
12785 for h in self.cfg.handlers.iter().filter(|h| h.endpoint == dest) {
12786 match (&h.handler, &bytes_opt) {
12787 (EndpointHandlerFn::Packed(_), Some(bytes)) => {
12788 self.call_handler_with_retries(
12789 dest,
12790 h,
12791 Some(bytes.as_ref()),
12792 Some(pkt),
12793 None,
12794 called_from_queue,
12795 )?;
12796 }
12797 (EndpointHandlerFn::Packed(_), None) => {
12798 let bytes = wire_format::pack_packet(pkt);
12799 self.call_handler_with_retries(
12800 dest,
12801 h,
12802 Some(bytes.as_ref()),
12803 Some(pkt),
12804 None,
12805 called_from_queue,
12806 )?;
12807 }
12808 (EndpointHandlerFn::Packet(_), _) => {
12809 self.call_handler_with_retries(
12810 dest,
12811 h,
12812 None,
12813 Some(pkt),
12814 None,
12815 called_from_queue,
12816 )?;
12817 }
12818 }
12819 }
12820 }
12821 }
12822 RouterItem::Packed(bytes) => {
12823 let env = wire_format::peek_envelope(bytes.as_ref())?;
12824 if is_internal_control_type(env.ty) {
12825 return Ok(());
12826 }
12827 self.ensure_e2e_policy_supported_for_type(env.ty)?;
12828 if !self.item_targets_local_sender(item)? {
12829 return Ok(());
12830 }
12831
12832 let any_packet_needed = env
12833 .endpoints
12834 .iter()
12835 .copied()
12836 .any(|ep| self.endpoint_has_packet_handler(ep));
12837
12838 let mut pkt_opt = if any_packet_needed {
12839 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
12840 pkt.validate()?;
12841 Some(pkt)
12842 } else {
12843 None
12844 };
12845
12846 let mut eps: Vec<DataEndpoint> = env.endpoints.iter().copied().collect();
12847 crate::small_sort::sort(&mut eps);
12848 eps.dedup();
12849
12850 for dest in eps {
12851 for h in self.cfg.handlers.iter().filter(|h| h.endpoint == dest) {
12852 match &h.handler {
12853 EndpointHandlerFn::Packed(_) => {
12854 self.call_handler_with_retries(
12855 dest,
12856 h,
12857 Some(bytes.as_ref()),
12858 pkt_opt.as_ref(),
12859 Some(&env),
12860 called_from_queue,
12861 )?;
12862 }
12863 EndpointHandlerFn::Packet(_) => {
12864 if pkt_opt.is_none() {
12865 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
12866 pkt.validate()?;
12867 pkt_opt = Some(pkt);
12868 }
12869 let pkt_ref = pkt_opt.as_ref().expect("just set");
12870 self.call_handler_with_retries(
12871 dest,
12872 h,
12873 None,
12874 Some(pkt_ref),
12875 Some(&env),
12876 called_from_queue,
12877 )?;
12878 }
12879 }
12880 }
12881 }
12882 }
12883 }
12884
12885 Ok(())
12886 }
12887
12888 fn tx_item_impl(
12894 &self,
12895 item: RouterTxItem,
12896 ignore_local: bool,
12897 called_from_queue: bool,
12898 ) -> TelemetryResult<()> {
12899 match item {
12900 RouterTxItem::Broadcast(data) => {
12901 self.ensure_e2e_policy_supported_for_type(Self::item_data_type(&data)?)?;
12902 if let RouterItem::Packet(pkt) = &data
12903 && !is_internal_control_type(pkt.data_type())
12904 {
12905 self.remember_managed_variable_packet(pkt)?;
12906 }
12907 #[cfg(feature = "discovery")]
12908 let is_discovery = matches!(&data, RouterItem::Packet(pkt) if discovery::is_discovery_type(pkt.data_type()))
12909 || matches!(&data, RouterItem::Packed(bytes)
12910 if wire_format::peek_envelope(bytes.as_ref())
12911 .map(|env| discovery::is_discovery_type(env.ty))
12912 .unwrap_or(false));
12913 if !ignore_local {
12914 if self.is_duplicate_pkt(&data)? {
12915 return Ok(());
12916 }
12917 #[cfg(feature = "discovery")]
12918 if !is_discovery
12919 && !matches!(&data, RouterItem::Packet(pkt) if is_internal_control_type(pkt.data_type()))
12920 && !matches!(&data, RouterItem::Packed(bytes)
12921 if wire_format::peek_routing_envelope(bytes.as_ref())
12922 .map(|env| is_internal_control_type(env.ty))
12923 .unwrap_or(false))
12924 {
12925 self.dispatch_local_for_item(&data, called_from_queue)?;
12926 }
12927 #[cfg(not(feature = "discovery"))]
12928 if !matches!(&data, RouterItem::Packet(pkt) if is_internal_control_type(pkt.data_type()))
12929 && !matches!(&data, RouterItem::Packed(bytes)
12930 if wire_format::peek_routing_envelope(bytes.as_ref())
12931 .map(|env| is_internal_control_type(env.ty))
12932 .unwrap_or(false))
12933 {
12934 self.dispatch_local_for_item(&data, called_from_queue)?;
12935 }
12936 }
12937
12938 let send_remote = match &data {
12939 RouterItem::Packet(pkt) => {
12940 pkt.validate()?;
12941 self.should_route_remote(&data, None)?
12942 }
12943 RouterItem::Packed(bytes) => {
12944 let _ = wire_format::peek_envelope(bytes.as_ref())?;
12945 self.should_route_remote(&data, None)?
12946 }
12947 };
12948
12949 if !send_remote {
12950 let (endpoints, ty) = self.item_route_info(&data)?;
12951 if !ignore_local
12952 && !is_internal_control_type(ty)
12953 && is_reliable_type(ty)
12954 && has_nonlocal_endpoint(&endpoints, &self.cfg)
12955 && !self
12956 .state
12957 .lock()
12958 .managed_variable_types
12959 .contains(&ty.as_u32())
12960 {
12961 self.remove_pkt_id(&data);
12965 return Err(TelemetryError::Io("no discovered route"));
12966 }
12967 return Ok(());
12968 }
12969 let mut data = data;
12970 let ty = match &data {
12971 RouterItem::Packet(pkt) => pkt.data_type(),
12972 RouterItem::Packed(bytes) => {
12973 wire_format::peek_routing_envelope(bytes.as_ref())?.ty
12974 }
12975 };
12976 if !ignore_local
12977 && !is_internal_control_type(ty)
12978 && self.item_requires_end_to_end_tracking(&data, ty)?
12979 {
12980 #[cfg(feature = "discovery")]
12981 {
12982 let pending = {
12983 let st = self.state.lock();
12984 let mut pending =
12985 self.expected_end_to_end_destinations_locked(&st, &data)?;
12986 self.filter_trackable_end_to_end_destinations_locked(
12987 &st,
12988 ty,
12989 &mut pending,
12990 );
12991 pending
12992 };
12993 if !pending.is_empty() {
12994 let original_data = data.clone();
12995 let mut targets: Vec<u64> = pending.keys().copied().collect();
12996 crate::small_sort::sort(&mut targets);
12997 targets.dedup();
12998 data = self.attach_wire_contract_to_item(data, &targets)?;
12999 match self.register_end_to_end_reliable_tx(&data) {
13000 Ok(false) => return Ok(()),
13001 Ok(true) => {}
13002 Err(error) => {
13003 self.remove_pkt_id(&original_data);
13004 return Err(error);
13005 }
13006 }
13007 }
13008 }
13009 }
13010 let RemoteSidePlan::Target(sides) = self.remote_side_plan(&data, None)?;
13011 for side in sides.iter().copied() {
13012 if let Err(e) = self.send_reliable_to_side(side, data.clone(), false) {
13013 if Self::is_side_tx_busy(&e) {
13014 self.tx_queue_item_for_retry(
13015 RouterTxItem::ToSide {
13016 src: None,
13017 dst: side,
13018 data: data.clone(),
13019 },
13020 true,
13021 )?;
13022 continue;
13023 }
13024 match &data {
13025 RouterItem::Packet(pkt) => {
13026 let _ = self.handle_callback_error(pkt, None, e, called_from_queue);
13027 }
13028 RouterItem::Packed(bytes) => {
13029 if let Ok(env) = wire_format::peek_envelope(bytes.as_ref()) {
13030 let _ = self.handle_callback_error_from_env(
13031 &env,
13032 None,
13033 e,
13034 called_from_queue,
13035 );
13036 }
13037 }
13038 }
13039 return Err(TelemetryError::HandlerError("tx handler failed"));
13040 }
13041 }
13042 }
13043 RouterTxItem::ToSide { src, dst, data } => {
13044 self.ensure_e2e_policy_supported_for_type(Self::item_data_type(&data)?)?;
13045 if let RouterItem::Packet(pkt) = &data
13046 && !is_internal_control_type(pkt.data_type())
13047 {
13048 self.remember_managed_variable_packet(pkt)?;
13049 }
13050 if !ignore_local {
13051 if self.is_duplicate_pkt(&data)? {
13052 return Ok(());
13053 }
13054 let suppress_local = matches!(&data, RouterItem::Packet(pkt) if is_internal_control_type(pkt.data_type()))
13055 || matches!(&data, RouterItem::Packed(bytes)
13056 if wire_format::peek_routing_envelope(bytes.as_ref())
13057 .map(|env| is_internal_control_type(env.ty))
13058 .unwrap_or(false));
13059 if !suppress_local {
13060 self.dispatch_local_for_item(&data, called_from_queue)?;
13061 }
13062 }
13063 let allowed = {
13064 let mut st = self.state.lock();
13065 let ty = match &data {
13066 RouterItem::Packet(pkt) => Some(pkt.data_type()),
13067 RouterItem::Packed(bytes) => {
13068 Some(wire_format::peek_routing_envelope(bytes.as_ref())?.ty)
13069 }
13070 };
13071 let route_allowed = self.route_allowed_locked(&st, src, ty, dst);
13072 #[cfg(all(feature = "discovery", feature = "timesync"))]
13073 let timesync_allowed = ty
13074 .map(|ty| {
13075 Self::timesync_allowed_for_side_locked(
13076 &mut st,
13077 dst,
13078 ty,
13079 self.clock.now_ms(),
13080 )
13081 })
13082 .unwrap_or(true);
13083 #[cfg(not(all(feature = "discovery", feature = "timesync")))]
13084 let timesync_allowed = true;
13085 route_allowed && timesync_allowed
13086 };
13087 if !allowed {
13088 return Ok(());
13089 }
13090 if let Err(e) = self.send_reliable_to_side(dst, data.clone(), src.is_some()) {
13091 if Self::is_side_tx_busy(&e) {
13092 self.tx_queue_item_for_retry(
13093 RouterTxItem::ToSide { src, dst, data },
13094 true,
13095 )?;
13096 return Ok(());
13097 }
13098 match &data {
13099 RouterItem::Packet(pkt) => {
13100 let _ = self.handle_callback_error(pkt, None, e, called_from_queue);
13101 }
13102 RouterItem::Packed(bytes) => {
13103 if let Ok(env) = wire_format::peek_envelope(bytes.as_ref()) {
13104 let _ = self.handle_callback_error_from_env(
13105 &env,
13106 None,
13107 e,
13108 called_from_queue,
13109 );
13110 }
13111 }
13112 }
13113 return Err(TelemetryError::HandlerError("tx handler failed"));
13114 }
13115 }
13116 RouterTxItem::EndToEndReplay { packet_id } => {
13117 let Some((data, mut sides)) = self.end_to_end_retransmit_sides(packet_id) else {
13118 return Ok(());
13119 };
13120 if sides.is_empty() {
13121 let RemoteSidePlan::Target(fallback_sides) =
13122 self.remote_side_plan(&data, None)?;
13123 sides = fallback_sides;
13124 }
13125 for side in sides.iter().copied() {
13126 if let Err(e) = self.send_reliable_to_side(side, data.clone(), false) {
13127 if Self::is_side_tx_busy(&e) {
13128 self.tx_queue_item_for_retry(
13129 RouterTxItem::ToSide {
13130 src: None,
13131 dst: side,
13132 data: data.clone(),
13133 },
13134 true,
13135 )?;
13136 continue;
13137 }
13138 match &data {
13139 RouterItem::Packet(pkt) => {
13140 let _ = self.handle_callback_error(pkt, None, e, called_from_queue);
13141 }
13142 RouterItem::Packed(bytes) => {
13143 if let Ok(env) = wire_format::peek_envelope(bytes.as_ref()) {
13144 let _ = self.handle_callback_error_from_env(
13145 &env,
13146 None,
13147 e,
13148 called_from_queue,
13149 );
13150 }
13151 }
13152 }
13153 return Err(TelemetryError::HandlerError("tx handler failed"));
13154 }
13155 }
13156 }
13157 RouterTxItem::ReliableReplay { dst, bytes } => {
13158 let frame = wire_format::peek_frame_info(bytes.as_ref())?;
13159 let ty = frame.envelope.ty;
13160 let Some(hdr) = frame.reliable else {
13161 return Ok(());
13162 };
13163 {
13164 let mut st = self.state.lock();
13165 let tx_state = self.reliable_tx_state_mut(&mut st, dst, ty);
13166 if !tx_state.sent.contains_key(&hdr.seq) {
13167 return Ok(());
13168 }
13169 }
13170 if let Err(e) = self.send_reliable_raw_to_side(dst, bytes.clone(), false) {
13171 if Self::is_side_tx_busy(&e) {
13172 self.tx_queue_item_for_retry(
13173 RouterTxItem::ReliableReplay { dst, bytes },
13174 true,
13175 )?;
13176 return Ok(());
13177 }
13178 return Err(e);
13179 }
13180 let mut st = self.state.lock();
13181 let tx_state = self.reliable_tx_state_mut(&mut st, dst, ty);
13182 if let Some(sent) = tx_state.sent.get_mut(&hdr.seq) {
13183 sent.last_send_ms = self.clock.now_ms();
13184 sent.queued = false;
13185 }
13186 }
13187 }
13188
13189 Ok(())
13190 }
13191
13192 #[inline]
13194 fn tx_item(&self, item: RouterTxItem) -> TelemetryResult<()> {
13195 self.tx_item_impl(item, false, false)
13196 }
13197
13198 #[inline]
13205 pub fn rx_packed(&self, bytes: &[u8]) -> TelemetryResult<()> {
13206 if self.side_tx_active() {
13207 return self.rx_packed_queue(bytes);
13208 }
13209 crate::memory_admission::check_receive(bytes)?;
13210 let data = RouterItem::Packed(crate::shared_bytes::convert(Arc::<[u8]>::from(bytes)));
13211 let item = RouterRxItem {
13212 src: None,
13213 priority: Self::router_item_priority(&data)?,
13214 data,
13215 };
13216 self.rx_item(&item, false)
13217 }
13218
13219 #[inline]
13224 pub fn rx(&self, pkt: &Packet) -> TelemetryResult<()> {
13225 if self.side_tx_active() {
13226 return self.rx_queue(pkt.clone());
13227 }
13228 let data = RouterItem::Packet(pkt.clone());
13229 let item = RouterRxItem {
13230 src: None,
13231 priority: Self::router_item_priority(&data)?,
13232 data,
13233 };
13234 self.rx_item(&item, false)
13235 }
13236
13237 #[inline]
13242 pub fn rx_from_side(&self, pkt: &Packet, side: RouterSideId) -> TelemetryResult<()> {
13243 if self.side_tx_active() {
13244 return self.rx_queue_from_side(pkt.clone(), side);
13245 }
13246 self.ensure_side_ingress_enabled(side)?;
13247 let data = RouterItem::Packet(pkt.clone());
13248 let item = RouterRxItem {
13249 src: Some(side),
13250 priority: Self::router_item_priority(&data)?,
13251 data,
13252 };
13253 self.rx_item(&item, false)
13254 }
13255
13256 #[inline]
13261 pub fn rx_packed_from_side(&self, bytes: &[u8], side: RouterSideId) -> TelemetryResult<()> {
13262 if self.side_tx_active() {
13263 return self.rx_packed_queue_from_side(bytes, side);
13264 }
13265 self.ensure_side_ingress_enabled(side)?;
13266 let Some(decoded) = self.decode_side_transport_frame(side, bytes)? else {
13267 return Ok(());
13268 };
13269 let data = RouterItem::Packed(decoded);
13270 let item = RouterRxItem {
13271 src: Some(side),
13272 priority: Self::router_item_priority(&data)?,
13273 data,
13274 };
13275 self.rx_item(&item, false)
13276 }
13277
13278 #[inline]
13285 pub fn tx(&self, pkt: Packet) -> TelemetryResult<()> {
13286 #[cfg(feature = "discovery")]
13287 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
13288 if self.side_tx_active() {
13289 return self.tx_queue_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)));
13290 }
13291 self.tx_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
13292 }
13293
13294 #[inline]
13299 pub fn tx_packed(&self, pkt: Arc<[u8]>) -> TelemetryResult<()> {
13300 #[cfg(feature = "discovery")]
13301 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
13302 if self.side_tx_active() {
13303 return self.tx_queue_item(RouterTxItem::Broadcast(RouterItem::Packed(
13304 crate::shared_bytes::convert(pkt),
13305 )));
13306 }
13307 self.tx_item(RouterTxItem::Broadcast(RouterItem::Packed(
13308 crate::shared_bytes::convert(pkt),
13309 )))
13310 }
13311
13312 #[inline]
13318 pub fn tx_queue(&self, pkt: Packet) -> TelemetryResult<()> {
13319 self.tx_queue_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
13320 }
13321
13322 #[inline]
13324 pub fn tx_packed_queue(&self, data: Arc<[u8]>) -> TelemetryResult<()> {
13325 self.tx_queue_item(RouterTxItem::Broadcast(RouterItem::Packed(
13326 crate::shared_bytes::convert(data),
13327 )))
13328 }
13329
13330 #[inline]
13337 pub fn log<T: LeBytes>(&self, ty: DataType, data: &[T]) -> TelemetryResult<()> {
13338 #[cfg(feature = "discovery")]
13339 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
13340 if self.side_tx_active() {
13341 return self.log_queue(ty, data);
13342 }
13343 let sender = self.sender_arc();
13344 log_raw(
13345 sender.as_ref(),
13346 ty,
13347 data,
13348 self.packet_timestamp_ms(),
13349 |pkt| self.tx_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt))),
13350 )
13351 }
13352
13353 #[inline]
13355 pub fn log_queue<T: LeBytes>(&self, ty: DataType, data: &[T]) -> TelemetryResult<()> {
13356 let sender = self.sender_arc();
13357 log_raw(
13358 sender.as_ref(),
13359 ty,
13360 data,
13361 self.packet_timestamp_ms(),
13362 |pkt| self.tx_queue_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt))),
13363 )
13364 }
13365
13366 #[inline]
13368 pub fn log_ts<T: LeBytes>(
13369 &self,
13370 ty: DataType,
13371 timestamp: u64,
13372 data: &[T],
13373 ) -> TelemetryResult<()> {
13374 #[cfg(feature = "discovery")]
13375 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
13376 if self.side_tx_active() {
13377 return self.log_queue_ts(ty, timestamp, data);
13378 }
13379 let sender = self.sender_arc();
13380 log_raw(sender.as_ref(), ty, data, timestamp, |pkt| {
13381 self.tx_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
13382 })
13383 }
13384
13385 #[inline]
13387 pub fn log_queue_ts<T: LeBytes>(
13388 &self,
13389 ty: DataType,
13390 timestamp: u64,
13391 data: &[T],
13392 ) -> TelemetryResult<()> {
13393 let sender = self.sender_arc();
13394 log_raw(sender.as_ref(), ty, data, timestamp, |pkt| {
13395 self.tx_queue_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
13396 })
13397 }
13398}
13399
13400#[cfg(all(test, feature = "std", feature = "discovery"))]
13401mod queued_schema_identity_tests {
13402 use super::*;
13403
13404 fn catalog(timestamp: u64, byte: u8) -> Packet {
13405 Packet::new(
13406 crate::DataType::DiscoverySchema,
13407 &[DataEndpoint::Discovery],
13408 "SCHEMA_PEER",
13409 timestamp,
13410 crate::shared_bytes::convert(Arc::<[u8]>::from(vec![byte; 1024])),
13411 )
13412 .unwrap()
13413 }
13414
13415 #[test]
13416 fn repeated_schema_preserves_partial_transfer_but_updates_and_other_sides_do_not_coalesce() {
13417 let router = Router::new_with_clock(RouterConfig::default(), Box::new(|| 0u64));
13418 let side = router.add_side_packed("A", |_| Ok(()));
13419 let other_side = router.add_side_packed("B", |_| Ok(()));
13420 let original = catalog(1, 42);
13421 let duplicate = catalog(2, 42);
13422 assert_ne!(original.packet_id(), duplicate.packet_id());
13423 let mut st = router.state.lock();
13424 st.transmit_queue.clear();
13425 let queued = |dst, pkt| TxQueued {
13426 item: RouterTxItem::ToSide {
13427 src: None,
13428 dst,
13429 data: RouterItem::Packet(pkt),
13430 },
13431 ignore_local: true,
13432 priority: 255,
13433 retry_after_ms: 123,
13434 };
13435 st.push_transmit(queued(side, original.clone())).unwrap();
13436 st.push_transmit(queued(side, duplicate.clone())).unwrap();
13437 assert_eq!(st.transmit_queue.len(), 1);
13438 let pending = st.transmit_queue.iter().next().unwrap();
13439 assert_eq!(pending.retry_after_ms, 123);
13440 assert!(
13441 matches!(&pending.item, RouterTxItem::ToSide { data: RouterItem::Packet(pkt), .. }
13442 if pkt.packet_id() == original.packet_id())
13443 );
13444 st.push_transmit(queued(other_side, duplicate)).unwrap();
13445 assert_eq!(st.transmit_queue.len(), 2);
13446 let changed = catalog(3, 43);
13447 st.push_transmit(queued(side, changed.clone())).unwrap();
13448 assert_eq!(st.transmit_queue.len(), 2);
13449 assert!(st.transmit_queue.iter().any(|queued| matches!(&queued.item,
13450 RouterTxItem::ToSide { dst, data: RouterItem::Packet(pkt), .. }
13451 if *dst == side && pkt.packet_id() == changed.packet_id())));
13452 }
13453
13454 #[test]
13455 fn repeated_announcements_complete_one_fragment_per_dispatch() {
13456 use core::sync::atomic::{AtomicUsize, Ordering};
13457 let allowance = Arc::new(AtomicUsize::new(0));
13458 let accepted = Arc::new(std::sync::Mutex::new(Vec::<Arc<[u8]>>::new()));
13459 let available = allowance.clone();
13460 let frames = accepted.clone();
13461 let node = Router::new_with_clock(RouterConfig::default(), Box::new(|| 1u64));
13462 let side = node.add_side_packed_with_options(
13463 "bounded CAN",
13464 move |bytes| {
13465 if available
13466 .try_update(Ordering::Relaxed, Ordering::Relaxed, |count| {
13467 count.checked_sub(1)
13468 })
13469 .is_err()
13470 {
13471 return Err(TelemetryError::Io("side tx busy"));
13472 }
13473 frames.lock().unwrap().push(Arc::<[u8]>::from(bytes));
13474 Ok(())
13475 },
13476 RouterSideOptions::default().with_small_packet_transport(128),
13477 );
13478 let handler = node.state.lock().sides[side]
13479 .as_ref()
13480 .unwrap()
13481 .tx_handler
13482 .clone();
13483 let mut complete = false;
13484 for timestamp in 1..100 {
13485 let mut state = 0x517cc1b7u32;
13487 let payload: Vec<u8> = (0..3600)
13488 .map(|_| {
13489 state ^= state << 13;
13490 state ^= state >> 17;
13491 state ^= state << 5;
13492 state as u8
13493 })
13494 .collect();
13495 let packet = Packet::new(
13496 crate::DataType::DiscoverySchema,
13497 &[crate::DataEndpoint::Discovery],
13498 "SCHEMA_PEER",
13499 timestamp,
13500 crate::shared_bytes::convert(Arc::<[u8]>::from(payload)),
13501 )
13502 .unwrap();
13503 node.state
13504 .lock()
13505 .push_transmit(TxQueued {
13506 item: RouterTxItem::ToSide {
13507 src: None,
13508 dst: side,
13509 data: RouterItem::Packet(packet),
13510 },
13511 ignore_local: true,
13512 priority: 255,
13513 retry_after_ms: 0,
13514 })
13515 .unwrap();
13516 let data = match &node.state.lock().transmit_queue.iter().next().unwrap().item {
13517 RouterTxItem::ToSide { data, .. } => data.clone(),
13518 _ => panic!("unexpected item"),
13519 };
13520 allowance.store(1, Ordering::Relaxed);
13521 match node.call_side_tx_handler(side, &handler, &data, false) {
13522 Ok(()) => {
13523 complete = true;
13524 break;
13525 }
13526 Err(TelemetryError::Io(_)) => {}
13527 other => panic!("unexpected result: {other:?}"),
13528 }
13529 }
13530 assert!(
13531 complete,
13532 "new announcement timestamps must not replay the prefix forever"
13533 );
13534 let received = accepted.lock().unwrap();
13535 assert!(
13538 received.len() > 1,
13539 "fixture must exercise fragmentation, not a compressed single frame"
13540 );
13541 let first = &received[0];
13542 assert_eq!(&first[..4], &[83, 68, 84, 3]);
13543 assert_eq!(
13544 u16::from_le_bytes([first[10], first[11]]) as usize,
13545 received.len()
13546 );
13547 let transfer_id = &first[4..8];
13548 for (index, frame) in received.iter().enumerate() {
13549 assert_eq!(&frame[4..8], transfer_id);
13550 assert_eq!(u16::from_le_bytes([frame[8], frame[9]]) as usize, index);
13551 }
13552 }
13553 #[test]
13554 fn schema_receipt_resolves_assigned_numeric_address_without_adding_peers() {
13555 let router = Router::new_with_clock(RouterConfig::default(), Box::new(|| 0u64));
13556 let side = router.add_side_packed("A", |_| Ok(()));
13557 let assigned = 0x1234u32;
13558 assert_ne!(sender_address_u32("SCHEMA_PEER"), assigned);
13559 {
13560 let mut st = router.state.lock();
13561 st.discovery_routes
13562 .entry(side)
13563 .or_default()
13564 .announcers
13565 .insert(
13566 "SCHEMA_PEER".into(),
13567 DiscoverySenderState {
13568 advertised_address: Some(assigned),
13569 ..DiscoverySenderState::default()
13570 },
13571 );
13572 }
13573 router.note_schema_received(side, "@addr:4660");
13574 router.note_schema_received(side, "@addr:4661");
13575 let st = router.state.lock();
13576 let peers = &st.discovery_routes.get(&side).unwrap().announcers;
13577 assert_eq!(peers.len(), 1);
13578 assert!(peers["SCHEMA_PEER"].has_schema);
13579 }
13580 #[test]
13581 fn busy_handler_returns_after_one_attempt_and_other_errors_keep_retry_budget() {
13582 let router = Router::new_with_clock(RouterConfig::default(), Box::new(|| 0u64));
13583 let attempts = core::cell::Cell::new(0);
13584 let result: Result<((), usize), _> = router.retry_with_attempts(8, || {
13585 attempts.set(attempts.get() + 1);
13586 Err(TelemetryError::Io("side tx busy"))
13587 });
13588 assert!(matches!(
13589 result,
13590 Err((TelemetryError::Io("side tx busy"), 1))
13591 ));
13592 assert_eq!(attempts.get(), 1);
13593 attempts.set(0);
13594 let result: Result<((), usize), _> = router.retry_with_attempts(3, || {
13595 attempts.set(attempts.get() + 1);
13596 Err(TelemetryError::Io("permanent handler failure"))
13597 });
13598 assert!(matches!(
13599 result,
13600 Err((TelemetryError::Io("permanent handler failure"), 3))
13601 ));
13602 assert_eq!(attempts.get(), 3);
13603 }
13604 #[test]
13605 fn rejected_first_template_frame_is_retried_self_describing() {
13606 crate::tests::ensure_common_test_schema();
13607 let ty = crate::config::register_data_type_with_description(
13608 "ROUTER_BUSY_FIRST_TEMPLATE",
13609 "",
13610 crate::MessageElement::Static(
13611 1,
13612 crate::MessageDataType::UInt8,
13613 crate::MessageClass::Data,
13614 ),
13615 &[crate::DataEndpoint::named("RADIO")],
13616 crate::ReliableMode::None,
13617 1,
13618 )
13619 .unwrap();
13620 let tx = Router::new_with_clock(RouterConfig::default(), Box::new(|| 0u64));
13621 let rx = Router::new_with_clock(RouterConfig::default(), Box::new(|| 0u64));
13622 let opts = RouterSideOptions::default().with_small_packet_transport(0);
13623 let a = tx.add_side_packed_with_options("wire", |_| Ok(()), opts.clone());
13624 let b = rx.add_side_packed_with_options("wire", |_| Ok(()), opts.clone());
13625 let packet = Packet::new(
13626 ty,
13627 &[crate::DataEndpoint::named("RADIO")],
13628 "BUSY_PEER",
13629 1,
13630 crate::shared_bytes::convert(Arc::<[u8]>::from([42u8])),
13631 )
13632 .unwrap();
13633 let raw = wire_format::pack_packet(&packet);
13634 let first = tx
13636 .encode_side_transport_frames(
13637 a,
13638 opts.clone(),
13639 crate::shared_bytes::convert(raw.clone()),
13640 )
13641 .unwrap();
13642 assert!(
13643 tx.send_side_frames_resumable(a, first, |_| Err(TelemetryError::Io("side tx busy")))
13644 .is_err()
13645 );
13646 let retry = tx
13647 .encode_side_transport_frames(
13648 a,
13649 opts.clone(),
13650 crate::shared_bytes::convert(raw.clone()),
13651 )
13652 .unwrap();
13653 let mut received = None;
13654 tx.send_side_frames_resumable(a, retry, |frame| {
13655 assert_eq!(
13656 frame[3], SIDE_TRANSPORT_KIND_FULL,
13657 "retry requires the unaccepted dictionary bootstrap"
13658 );
13659 received = rx.decode_side_transport_frame(b, frame)?;
13660 Ok(())
13661 })
13662 .unwrap();
13663 assert_eq!(received, Some(crate::shared_bytes::convert(raw.clone())));
13664 let next = tx
13666 .encode_side_transport_frames(a, opts, crate::shared_bytes::convert(raw.clone()))
13667 .unwrap();
13668 tx.send_side_frames_resumable(a, next, |frame| {
13669 assert_eq!(frame[3], SIDE_TRANSPORT_KIND_COMPACT);
13670 assert_eq!(
13671 rx.decode_side_transport_frame(b, frame)?,
13672 Some(crate::shared_bytes::convert(raw.clone()))
13673 );
13674 Ok(())
13675 })
13676 .unwrap();
13677 }
13678 #[test]
13679 fn already_queued_receipt_precedes_ordered_retry_exhaustion() {
13680 crate::tests::ensure_common_test_schema();
13681 let ep = DataEndpoint::named("RADIO");
13682 let ty = crate::config::register_data_type_with_description(
13683 "QUEUED_RECEIPT_DEADLINE",
13684 "",
13685 MessageElement::Static(1, crate::MessageDataType::UInt8, crate::MessageClass::Data),
13686 &[ep],
13687 crate::ReliableMode::Ordered,
13688 1,
13689 )
13690 .unwrap();
13691 for isr in [false, true] {
13692 let now = Arc::new(core::sync::atomic::AtomicU64::new(0));
13693 let clock = now.clone();
13694 let router = Router::new_with_clock(
13695 RouterConfig::new([]).with_hostname("FC"),
13696 Box::new(move || clock.load(Ordering::Relaxed)),
13697 );
13698 let side = router.add_side_packed("CAN", |_| Ok(()));
13699 let packet = Packet::new(ty, &[ep], "FC", 1, Arc::<[u8]>::from([42u8])).unwrap();
13700 let id = packet.packet_id();
13701 let owner = Router::sender_hash("GS");
13702 let mut destinations = BTreeMap::new();
13703 destinations.insert(owner, side);
13704 router.state.lock().end_to_end_reliable_tx.insert(
13705 id,
13706 EndToEndReliableSent {
13707 ordered_type: Some(ty.as_u32()),
13708 waiting_for_predecessor: false,
13709 data: RouterItem::Packet(packet),
13710 pending_destinations: destinations,
13711 tracked_destinations: true,
13712 last_send_ms: 0,
13713 retries: runtime_reliable_max_retries(),
13714 queued: false,
13715 },
13716 );
13717 let ack = Packet::new_with_wire_contract(
13718 DataType::ReliableAck,
13719 message_meta(DataType::ReliableAck).endpoints_ref(),
13720 "E2EACK:GS",
13721 1,
13722 1,
13723 Arc::<[u8]>::from(id.to_le_bytes()),
13724 None,
13725 Arc::from([owner, Router::sender_hash("FC")]),
13726 )
13727 .unwrap();
13728 let packed = wire_format::pack_packet(&ack);
13729 if isr {
13730 router.rx_packed_queue_from_side_isr(&packed, side).unwrap();
13731 } else {
13732 router.rx_packed_queue_from_side(&packed, side).unwrap();
13733 }
13734 now.store(runtime_reliable_retransmit_ms(), Ordering::Relaxed);
13735 router
13736 .drain_queues_with_timeout(1)
13737 .expect("a received ACK must be processed before cancelling its ordered delivery");
13738 assert!(!router.state.lock().end_to_end_reliable_tx.contains_key(&id));
13739 }
13740 }
13741 #[test]
13742 fn receipt_budget_does_not_starve_transmit() {
13743 use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
13744 crate::tests::ensure_common_test_schema();
13745 let now = Arc::new(AtomicU64::new(0));
13746 let clock = now.clone();
13747 let count = Arc::new(AtomicUsize::new(0));
13748 let accepted = count.clone();
13749 let node = Router::new_with_clock(
13750 RouterConfig::default(),
13751 Box::new(move || clock.fetch_add(1, Ordering::Relaxed)),
13752 );
13753 let side = node.add_side_packed("wire", move |_| {
13754 accepted.fetch_add(1, Ordering::Relaxed);
13755 Ok(())
13756 });
13757 node.clear_queues();
13758 let packet = Packet::new(
13759 crate::DataType::ReliableAck,
13760 &[crate::DataEndpoint::TelemetryError],
13761 "E2EACK:PEER",
13762 1,
13763 Arc::<[u8]>::from([0u8; 8]),
13764 )
13765 .unwrap();
13766 node.state
13767 .lock()
13768 .push_transmit(TxQueued {
13769 item: RouterTxItem::ToSide {
13770 src: None,
13771 dst: side,
13772 data: RouterItem::Packet(packet.clone()),
13773 },
13774 ignore_local: true,
13775 priority: 250,
13776 retry_after_ms: 0,
13777 })
13778 .unwrap();
13779 node.rx_packed_queue_from_side(
13780 &wire_format::pack_packet(
13781 &Packet::new(
13782 crate::DataType::ReliableAck,
13783 &[crate::DataEndpoint::TelemetryError],
13784 "RX_PEER",
13785 2,
13786 Arc::<[u8]>::from([0u8; 8]),
13787 )
13788 .unwrap(),
13789 ),
13790 side,
13791 )
13792 .unwrap();
13793 node.drain_queues_with_timeout(1).unwrap();
13794 assert_eq!(
13795 count.load(Ordering::Relaxed),
13796 1,
13797 "control RX exhausting its cooperative budget must still allow one queued TX"
13798 );
13799 }
13800}
13801
13802#[cfg(all(test, feature = "discovery", feature = "std"))]
13803fn untemplated_test_node() -> Router {
13804 Router::new_with_clock(RouterConfig::default(), Box::new(|| 0))
13805}
13806#[cfg(all(test, feature = "discovery", feature = "std"))]
13807fn untemplated_test_options() -> RouterSideOptions {
13808 RouterSideOptions::default().with_small_packet_transport(64)
13809}
13810#[cfg(all(test, feature = "discovery", feature = "std"))]
13811#[allow(clippy::duplicate_mod)]
13812#[path = "tests/untemplated_transport.rs"]
13813mod untemplated_transport_tests;
13814
13815#[cfg(all(test, feature = "std", feature = "discovery"))]
13816mod queued_producer_maintenance_tests {
13817 use super::*;
13818 #[test]
13819 fn queued_publication_defers_discovery_to_worker_maintenance() {
13820 crate::tests::ensure_common_test_schema();
13821 let ty = DataType::named("GPS_DATA");
13822 for variant in 0..4 {
13823 let sent = Arc::new(std::sync::Mutex::new(Vec::new()));
13824 let output = sent.clone();
13825 let router = Router::new_with_clock(RouterConfig::default(), Box::new(|| 0));
13826 router.add_side_packed("wire", move |bytes| {
13827 output
13828 .lock()
13829 .unwrap()
13830 .push(wire_format::unpack_packet(bytes)?.data_type());
13831 Ok(())
13832 });
13833 router.clear_queues();
13834 let packet =
13835 Packet::from_f32_slice(ty, &[1.0, 2.0, 3.0], message_meta(ty).endpoints_ref(), 42)
13836 .unwrap();
13837 match variant {
13838 0 => router.tx_queue(packet).unwrap(),
13839 1 => router
13840 .tx_packed_queue(wire_format::pack_packet(&packet))
13841 .unwrap(),
13842 2 => router.log_queue(ty, &[1.0f32, 2.0, 3.0]).unwrap(),
13843 _ => router.log_queue_ts(ty, 42, &[1.0f32, 2.0, 3.0]).unwrap(),
13844 }
13845 assert!(
13846 sent.lock().unwrap().is_empty(),
13847 "producer invoked a transport"
13848 );
13849 assert_eq!(
13850 router.state.lock().transmit_queue.len(),
13851 1,
13852 "queued API performed discovery on the producer stack (variant {variant})"
13853 );
13854 router.periodic(0).unwrap();
13855 let sent = sent.lock().unwrap();
13856 assert!(sent.contains(&ty));
13857 assert!(
13858 sent.iter().copied().any(discovery::is_discovery_type),
13859 "worker maintenance must still announce the board"
13860 );
13861 }
13862 }
13863}